e1ba0aaa1baec345d56cc8a518b8450c3e5d74c3
tdreszer
Wed Jan 22 14:31:22 2014 -0800
Checking in new feature 'drag-select highlight', which was originally coded by Larry. Redmine #709 (been on the shelf for awhile).
diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index b4b2af5..514edb2 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -351,33 +351,48 @@
else
minPixels = num;
}
return ( movedX > minPixels || movedX < (minPixels * -1)
|| movedY > minPixels || movedY < (minPixels * -1));
}
}
/////////////////
//// posting ////
/////////////////
var posting = {
blockUseMap: false,
- blockMapClicks: function () { posting.blockUseMap=true; },
- allowMapClicks: function () { posting.blockUseMap=false; },
- mapClicksAllowed: function () { return (posting.blockUseMap == false); },
+ blockMapClicks: function ()
+ // Blocks clicking on map items when in effect. Drag opperations frequently call this.
+ {
+ posting.blockUseMap=true;
+ },
+
+ allowMapClicks:function ()
+ // reallows map clicks. Called after operations that compete with clicks (e.g. dragging)
+ {
+ $('body').css('cursor', ''); // Explicitly remove wait cursor.
+ posting.blockUseMap=false;
+ },
+
+ mapClicksAllowed: function ()
+ // Verify that click-competing operation (e.g. dragging) isn't currently occurring.
+ {
+ return (posting.blockUseMap == false);
+ },
blockTheMapOnMouseMove: function (ev)
{
if (!posting.blockUseMap && mouse.hasMoved(ev)) {
posting.blockUseMap=true;
}
},
mapClk: function ()
{
var done = false;
if(false && imageV2.inPlaceUpdate) {
// XXXX experimental and only turned on in larrym's tree.
// Use in-place update if the map item just modifies the current position (this is nice because it's faster
// and it preserves the users current relative position in the track image).
@@ -533,121 +548,208 @@
var form = $('form#TrackForm');
$(form).submit(function () {
$('select.normalText,select.hiddenText').attr('disabled',true);
});
$(form).attr('method','get');
}
}
////////////////////////////////////////////////////////////
// dragSelect is also known as dragZoom or shift-dragZoom //
////////////////////////////////////////////////////////////
var dragSelect = {
areaSelector: null, // formerly "imgAreaSelect". jQuery element used for imgAreaSelect
- autoHideSetting: true, // Current state of imgAreaSelect autoHide setting
originalCursor: null,
startTime: null,
- rulerModeToggle: function (ele) // UNUSED?
- {
- dragSelect.autoHideSetting = !ele.checked;
- var obj = dragSelect.areaSelector.data('imgAreaSelect');
- obj.setOptions({autoHide : dragSelect.autoHideSetting});
- },
-
selectStart: function (img, selection)
{
initVars();
if(rightClick.menu) {
rightClick.menu.hide();
}
var now = new Date();
dragSelect.startTime = now.getTime();
posting.blockMapClicks();
},
selectChange: function (img, selection)
{
if(selection.x1 != selection.x2) {
if (genomePos.check(img, selection)) {
genomePos.update(img, selection, false);
jQuery('body').css('cursor', dragSelect.originalCursor);
} else {
jQuery('body').css('cursor', 'not-allowed');
}
}
return true;
},
+ highlightThisRegion: function(newPosition)
+ // set highlighting newPosition in server-side cart and apply the highlighting in local UI.
+ {
+ var start, end;
+ if (arguments.length == 2) {
+ start = arguments[0];
+ end = arguments[1];
+ } else {
+ var pos = parsePosition(newPosition);
+ start = pos.start;
+ end = pos.end;
+ }
+ hgTracks.highlight = getDb() + "." + hgTracks.chromName + ":" + start + "-" + end;
+ hgTracks.highlight += '#AAFFFF'; // Also include highlight color
+ // we include enableHighlightingDialog because it may have been changed by the dialog
+ setCartVars(['highlight', 'enableHighlightingDialog'],
+ [hgTracks.highlight, hgTracks.enableHighlightingDialog ? 1 : 0]);
+ imageV2.highlightRegion();
+ },
+
+ selectionEndDialog: function (newPosition)
+ // Let user choose between zoom-in and highlighting.
+ {
+ var dragSelectDialog = $("#dragSelectDialog")[0];
+ if (!dragSelectDialog) {
+ $("body").append("
" + newPosition +
+ "
" +
+ "Don't show this dialog again and always zoom. " +
+ "(Re-enable highlight via the 'configure' menu at any time.)
");
+ dragSelectDialog = $("#dragSelectDialog")[0];
+ }
+ $(dragSelectDialog).dialog({
+ modal: true,
+ title: "Drag-and-select",
+ closeOnEscape: true,
+ resizable: false,
+ autoOpen: false,
+ revertToOriginalPos: true,
+ minWidth: 400,
+ buttons: {
+ "Zoom In": function() {
+ // Zoom to selection
+ $(this).dialog("option", "revertToOriginalPos", false);
+ if (imageV2.inPlaceUpdate) {
+ var params = "position=" + newPosition;
+ if ($("#disableDragHighlight").attr('checked')) {
+ hgTracks.enableHighlightingDialog = false;
+ params += "&enableHighlightingDialog=0"
+ }
+ imageV2.navigateInPlace(params, null, true);
+ } else {
+ $('body').css('cursor', 'wait');
+ document.TrackHeaderForm.submit();
+ }
+ $(this).dialog("close");
+ },
+ "Highlight": function() {
+ // Highlight selection
+ $(imageV2.imgTbl).imgAreaSelect({hide:true});
+ if ($("#disableDragHighlight").attr('checked'))
+ hgTracks.enableHighlightingDialog = false;
+ dragSelect.highlightThisRegion(newPosition);
+ $(this).dialog("close");
+ },
+ "Cancel": function() {
+ $(this).dialog("close");
+ }
+ },
+ open: function () { // Make zoom the focus/default action
+ $(this).parents('.ui-dialog-buttonpane button:eq(0)').focus();
+ },
+
+ close: function() {
+ // All exits to dialog should go through this
+ $(imageV2.imgTbl).imgAreaSelect({hide:true});
+ if($(this).dialog("option", "revertToOriginalPos"))
+ genomePos.revertToOriginalPos();
+ if($("#disableDragHighlight").attr('checked'))
+ $(this).remove();
+ else
+ $(this).hide();
+ $('body').css('cursor', ''); // Occasionally wait cursor got left behind
+ }
+ });
+ $(dragSelectDialog).dialog('open');
+ },
+
selectEnd: function (img, selection)
{
var now = new Date();
var doIt = false;
if(dragSelect.originalCursor != null)
jQuery('body').css('cursor', dragSelect.originalCursor);
// ignore releases outside of the image rectangle (allowing a 10 pixel slop)
- if(dragSelect.autoHideSetting && genomePos.check(img, selection)) {
- // ignore single clicks that aren't in the top of the image (this happens b/c the clickClipHeight test in dragSelect.selectStart
+ if (genomePos.check(img, selection)) {
+ // ignore single clicks that aren't in the top of the image
+ // (this happens b/c the clickClipHeight test in dragSelect.selectStart
// doesn't occur when the user single clicks).
doIt = dragSelect.startTime != null || selection.y1 <= hgTracks.rulerClickHeight;
}
if(doIt) {
// dragSelect.startTime is null if mouse has never been moved
var singleClick = ( (selection.x2 == selection.x1)
|| dragSelect.startTime == null
|| (now.getTime() - dragSelect.startTime) < 100);
var newPosition = genomePos.update(img, selection, singleClick);
if(newPosition != undefined) {
+ if (hgTracks.enableHighlightingDialog)
+ dragSelect.selectionEndDialog(newPosition);
+ else {
+ $(imageV2.imgTbl).imgAreaSelect({hide:true});
if (imageV2.inPlaceUpdate) {
imageV2.navigateInPlace("position=" + newPosition, null, true);
} else {
jQuery('body').css('cursor', 'wait');
document.TrackHeaderForm.submit();
}
}
+ }
} else {
+ $(imageV2.imgTbl).imgAreaSelect({hide:true});
genomePos.revertToOriginalPos();
}
dragSelect.startTime = null;
- setTimeout('posting.allowMapClicks();',50); // Necessary incase the dragSelect.selectEnd was over a map item. select takes precedence.
+ // blockMapClicks/allowMapClicks() is necessary if selectEnd was over a map item.
+ setTimeout('posting.allowMapClicks();',50);
return true;
},
load: function (firstTime)
{
var imgHeight = 0;
if (imageV2.enabled)
- imgHeight = imageV2.imgTbl.height();
+ imgHeight = imageV2.imgTbl.innerHeight() - 1; // last little bit makes border look ok
// No longer disable without ruler, because shift-drag still works
if(typeof(hgTracks) != "undefined") {
if (hgTracks.rulerClickHeight == undefined || hgTracks.rulerClickHeight == null)
hgTracks.rulerClickHeight = 0; // will be zero if no ruler track
var heights = hgTracks.rulerClickHeight;
dragSelect.areaSelector = jQuery((imageV2.imgTbl).imgAreaSelect({
selectionColor: 'blue',
outerColor: '',
minHeight: imgHeight,
maxHeight: imgHeight,
onSelectStart: dragSelect.selectStart,
onSelectChange: dragSelect.selectChange,
onSelectEnd: dragSelect.selectEnd,
- autoHide: dragSelect.autoHideSetting,
+ autoHide: false, // gets hidden after possible dialog
movable: false,
clickClipHeight: heights
}));
}
}
}
/////////////////////////////////////
//// Chrom Drag/Zoom/Expand code ////
/////////////////////////////////////
jQuery.fn.chromDrag = function(){
this.each(function(){
// Plan:
// mouseDown: determine where in map: convert to img location: pxDown
// mouseMove: flag drag
@@ -1308,51 +1410,53 @@
}
//////////////////////////
//// Drag Scroll code ////
//////////////////////////
jQuery.fn.panImages = function(){
// globals across all panImages
genomePos.original = genomePos.getOriginalPos(); // XXXX what is this for? (this already happened in initVars).
var leftLimit = hgTracks.imgBoxLeftLabel * -1;
var rightLimit = (hgTracks.imgBoxPortalWidth - hgTracks.imgBoxWidth + leftLimit);
var only1xScrolling = ((hgTracks.imgBoxWidth - hgTracks.imgBoxPortalWidth) == 0);//< hgTracks.imgBoxLeftLabel);
var prevX = (hgTracks.imgBoxPortalOffsetX + hgTracks.imgBoxLeftLabel) * -1;
var portalWidth = 0;
var savedPosition;
+ var highlightArea = null; // Used to ensure dragSelect highlight will scroll.
this.each(function(){
var pic;
var pan;
if ( $(this).is("img") ) {
pan = $(this).parent("div");
pic = $(this);
}
else if ( $(this).is("div.scroller") ) {
pan = $(this);
pic = $(this).children("img#panImg"); // Get the real pic
}
if(pan == undefined || pic == undefined) {
throw "Not a div with a child image! 'panImages' can only be used with divs contain images.";
}
// globals across all panImages
portalWidth = $(pan).width();
+ portalAbsoluteX = $(pan).offset().left;
// globals to one panImage
var newX = 0;
var mouseDownX = 0;
var mouseIsDown = false;
var beyondImage = false;
var atEdge = false;
initialize();
function initialize(){
$(pan).parents('td.tdData').mousemove(function(e) {
if (e.shiftKey)
$(this).css('cursor',"crosshair"); // shift-dragZoom
else if ( $.browser.msie ) // IE will override map item cursors if this gets set
@@ -1360,30 +1464,31 @@
//else // NOTE: Open hand cursor is being removed because it makes vis toggling less obvious
// $(this).css('cursor',"url(../images/grabber.cur),w-resize"); // dragScroll
});
panAdjustHeight(prevX);
pan.mousedown(function(e){
if (e.which > 1 || e.button > 1 || e.shiftKey)
return true;
if(mouseIsDown == false) {
if(rightClick.menu) {
rightClick.menu.hide();
}
mouseIsDown = true;
mouseDownX = e.clientX;
+ highlightArea = $('#highlightItem')[0];
atEdge = (!beyondImage && (prevX >= leftLimit || prevX <= rightLimit));
$(document).bind('mousemove',panner);
$(document).bind( 'mouseup', panMouseUp); // Will exec only once
return false;
}
});
}
function panner(e) {
//if (!e) e = window.event;
if ( mouseIsDown ) {
var relativeX = (e.clientX - mouseDownX);
if(relativeX != 0) {
if (posting.mapClicksAllowed()) {
@@ -1409,58 +1514,61 @@
if(atEdge) { // Do not drag straight off edge. Force second drag
beyondImage = true;
newX = rightLimit - (rightLimit - newX)/decelerator;// slower
//if (newX < rightLimit - wingSize) // Don't go too far over the edge!
// newX = rightLimit - wingSize;
} else
newX = rightLimit;
} else if(newX >= rightLimit && newX < leftLimit)
beyondImage = false; // could have scrolled back without mouse up
newX = panUpdatePosition(newX,true);
var nowPos = newX.toString() + "px";
$(".panImg").css( {'left': nowPos });
$('.tdData').css( {'backgroundPosition': nowPos } );
+ scrollHighlight(relativeX);
if (!only1xScrolling)
panAdjustHeight(newX); // NOTE: This will dynamically resize image while scrolling. Do we want to?
}
}
}
function panMouseUp(e) { // Must be a separate function instead of pan.mouseup event.
//if (!e) e = window.event;
if(mouseIsDown) {
dragMaskClear();
$(document).unbind('mousemove',panner);
$(document).unbind('mouseup',panMouseUp);
mouseIsDown = false;
setTimeout('posting.allowMapClicks();',50); // Necessary incase the dragSelect.selectEnd was over a map item. select takes precedence.
// Outside image? Then abandon.
var curY = e.pageY;
var imgTbl = $('#imgTbl');
var north = $(imgTbl).offset().top;
var south = north + $(imgTbl).height();
if (curY < north || curY > south) {
atEdge = false;
beyondImage = false;
if (savedPosition != undefined)
genomePos.set(savedPosition,null);
var oldPos = prevX.toString() + "px";
$(".panImg").css( {'left': oldPos });
$('.tdData').css( {'backgroundPosition': oldPos } );
+ if (highlightArea)
+ imageV2.highlightRegion();
return true;
}
// Do we need to fetch anything?
if(beyondImage) {
if(imageV2.inPlaceUpdate) {
var pos = parsePosition(genomePos.get());
imageV2.navigateInPlace("position=" +
encodeURIComponent(pos.chrom + ":" + pos.start + "-" + pos.end),
null, true);
} else {
document.TrackHeaderForm.submit();
}
return true; // Make sure the setTimeout below is not called.
}
@@ -1618,30 +1726,48 @@
var imgTbl = $('#imgTbl');
// Find or create the waitMask (which masks the whole page)
var dragMask = $('div#dragMask');
if (dragMask != undefined && dragMask.length >= 1) {
$(dragMask).height( $(imgTbl).height() );
}
}
function dragMaskClear() { // Clears the dragMask
$('body').css('cursor','auto')
var dragMask = $('#dragMask');
if (dragMask != undefined )
$(dragMask).hide();
}
+ function scrollHighlight(relativeX)
+ // Scrolls the highlight region if one exists
+ {
+ if (highlightArea) {
+ // Best to have a left and right, then min/max the edges, then set width
+ var hiOffset = $(highlightArea).offset();
+ var hiDefinedLeft = $(highlightArea).data('leftPixels');
+ var hiDefinedWidth = $(highlightArea).data('widthPixels');
+ hiOffset.left = Math.max(hiDefinedLeft + relativeX,
+ portalAbsoluteX);
+ var right = Math.min(hiDefinedLeft + hiDefinedWidth + relativeX,
+ portalAbsoluteX + portalWidth);
+ var newWidth = Math.max(right - hiOffset.left,0);
+ if (hiDefinedWidth != newWidth)
+ $(highlightArea).width(newWidth);
+ $(highlightArea).offset(hiOffset);
+ }
+ }
};
///////////////////////////////////////
//// rightClick (aka context menu) ////
///////////////////////////////////////
var rightClick = {
menu: null,
selectedMenuItem: null, // currently choosen context menu item (via context menu).
floatingMenuItem: null,
currentMapItem: null,
supportZoomCodon: false, // turn on experimental zoom-to-codon functionality (currently only on in larry's tree).
@@ -1693,31 +1819,31 @@
genomePos.set(json.pos, 3);
if(document.TrackForm)
document.TrackForm.submit();
else
document.TrackHeaderForm.submit();
} else {
alert(json.error);
}
},
handleViewImg: function (response, status)
{ // handles view image response, which must get new image without imageV2 gimmickery
jQuery('body').css('cursor', '');
var str = "]*SRC='([^']+)'";
var reg = new RegExp(str);
- a = reg.exec(response);
+ var a = reg.exec(response);
if(a && a[1]) {
if(window.open(a[1]) == null) {
rightClick.windowOpenFailedMsg();
}
return;
}
warn("Couldn't parse out img src");
},
myPrompt: function (msg, callback)
{ // replacement for prompt; avoids misleading/confusing security warnings which are caused by prompt in IE 7+
// callback is called if user presses "OK".
$("body").append("
");
$("#myPrompt").dialog({
@@ -1736,31 +1862,31 @@
{
setTimeout( function() {
rightClick.hitFinish(menuItemClicked, menuObject, cmd, args);
}, 1);
},
hitFinish: function (menuItemClicked, menuObject, cmd, args)
{ // dispatcher for context menu hits
var id = rightClick.selectedMenuItem.id;
var url = null;
if(menuObject.shown) {
// warn("Spinning: menu is still shown");
setTimeout(function() { rightClick.hitFinish(menuItemClicked, menuObject, cmd); }, 10);
return;
}
- if(cmd == 'selectWholeGene' || cmd == 'getDna') {
+ if (cmd == 'selectWholeGene' || cmd == 'getDna' || cmd == 'highlightItem') {
// bring whole gene into view or redirect to DNA screen.
var href = rightClick.selectedMenuItem.href;
var chromStart, chromEnd;
var a = /hgg_chrom=(\w+)&/.exec(href);
// Many links leave out the chrom (b/c it's in the server side cart as "c")
var chrom = hgTracks.chromName;
if(a) {
if(a && a[1])
chrom = a[1];
a = /hgg_start=(\d+)/.exec(href);
if(a && a[1])
chromStart = parseInt(a[1]) + 1;
a = /hgg_end=(\d+)/.exec(href);
if(a && a[1])
chromEnd = parseInt(a[1]);
@@ -1775,30 +1901,32 @@
a = /t=(\d+)/.exec(href);
if(a && a[1])
chromEnd = parseInt(a[1]);
}
if(chrom == null || chromStart == null || chromEnd == null) {
warn("couldn't parse out genomic coordinates");
} else {
if(cmd == 'getDna') {
// NOTE: this should be shared with URL generation for getDna blue bar menu
url = "../cgi-bin/hgc?g=getDna&i=mixed&c=" + chrom;
url += "&l=" + (chromStart - 1) + "&r=" + chromEnd;
url += "&db=" + getDb() + "&hgsid=" + getHgsid();
if (window.open(url) === null) {
rightClick.windowOpenFailedMsg();
}
+ } else if (cmd == 'highlightItem') {
+ dragSelect.highlightThisRegion(parseInt(chromStart), parseInt(chromEnd));
} else {
var newPosition = genomePos.setByCoordinates(chrom, chromStart, chromEnd);
var reg = new RegExp("hgg_gene=([^&]+)");
var a = reg.exec(href);
var name;
// pull item name out of the url so we can set hgFind.matches (redmine 3062)
if(a && a[1]) {
name = a[1];
} else {
reg = new RegExp("[&?]i=([^&]+)");
a = reg.exec(href);
if(a && a[1]) {
name = a[1];
}
}
@@ -1867,38 +1995,30 @@
} else if (cmd == 'hgTrackUi_follow') {
var url = "hgTrackUi?hgsid=" + getHgsid() + "&g=";
var rec = hgTracks.trackDb[id];
if (tdbHasParent(rec) && tdbIsLeaf(rec))
url += rec.parentTrack
else {
var link = $( 'td#td_btn_'+ rightClick.selectedMenuItem.id ).children('a'); // The button already has the ref
if ($(link) != undefined)
url = $(link).attr('href');
else
url += rightClick.selectedMenuItem.id;
}
location.assign(url);
- } else if (cmd == 'dragZoomMode') {
- dragSelect.autoHideSetting = true;
- var obj = dragSelect.areaSelector.data('imgAreaSelect');
- obj.setOptions({autoHide : true, movable: false});
- } else if (cmd == 'hilightMode') {
- dragSelect.autoHideSetting = false;
- var obj = dragSelect.areaSelector.data('imgAreaSelect');
- obj.setOptions({autoHide : false, movable: true});
} else if (cmd == 'viewImg') {
// Fetch a new copy of track img and show it to the user in another window. This code assume we have updated
// remote cart with all relevant chages (e.g. drag-reorder).
/* Here's how to do this more directly with hgRenderTracks:
if(window.open("../cgi-bin/hgRenderTracks?hgt.internal=1&hgsid=" + getHgsid()) == null) {
rightClick.windowOpenFailedMsg();
}
return;
*/
var data = "hgt.imageV1=1&hgt.trackImgOnly=1&hgsid=" + getHgsid();
jQuery('body').css('cursor', 'wait');
$.ajax({
type: "GET",
url: "../cgi-bin/hgTracks",
data: data,
@@ -1960,75 +2080,87 @@
var rows = dragReorder.getContiguousRowSet(row);
if (rows && rows.length > 0) {
var vars = new Array();
var vals = new Array();
for (var ix=rows.length - 1; ix >= 0; ix--) { // from bottom, just in case remove screws with us
var rowId = $(rows[ix]).attr('id').substring('tr_'.length);
//if (tdbIsSubtrack(hgTracks.trackDb[rowId]) == false)
// warn('What went wrong?');
vars.push(rowId, rowId+'_sel'); // Remove subtrack level vis and explicitly uncheck.
vals.push('[]', 0);
$(rows[ix]).remove();
}
if (vars.length > 0) {
setCartVars( vars, vals );
- dragReorder.init();
- dragSelect.load(false);
}
- imageV2.markAsDirtyPage();
+ imageV2.afterImgChange(true);
}
} else if (cmd == 'hideComposite') {
var rec = hgTracks.trackDb[id];
if (tdbIsSubtrack(rec)) {
var row = $( 'tr#tr_' + id );
var rows = dragReorder.getCompositeSet(row);
if (rows && rows.length > 0) {
for (var ix=rows.length - 1; ix >= 0; ix--) { // from bottom, just in case remove screws with us
$(rows[ix]).remove();
}
var selectUpdated = vis.update(rec.parentTrack, 'hide');
setCartVar(rec.parentTrack, 'hide' );
- dragReorder.init();
- dragSelect.load(false);
- imageV2.markAsDirtyPage();
+ imageV2.afterImgChange(true);
}
}
//else
// warn('What went wrong?');
+ } else if (cmd == 'zoomToHighlight') { // If highlight exists for this assembly, zoom to it
+ if (hgTracks.highlight) {
+ var pos = parsePositionWithDb(hgTracks.highlight);
+ if (pos && pos.db == getDb()) {
+ if (imageV2.inPlaceUpdate) {
+ var params = "position=" + pos.chrom + ':' + pos.start + '-' + pos.end;
+ imageV2.navigateInPlace(params, null, true);
+ } else {
+ genomePos.setByCoordinates(pos.chrom, pos.start, pos.end);
+ jQuery('body').css('cursor', 'wait');
+ document.TrackHeaderForm.submit();
+ }
+ }
+ }
+ } else if (cmd == 'removeHighlight') {
+ hgTracks.highlight = null;
+ setCartVars(['highlight'], ['[]']);
+ imageV2.highlightRegion();
} else { // if ( cmd in 'hide','dense','squish','pack','full','show' )
// Change visibility settings:
//
// First change the select on our form:
var rec = hgTracks.trackDb[id];
var selectUpdated = vis.update(id, cmd);
// Now change the track image
if(imageV2.enabled && cmd == 'hide')
{
// Hide local display of this track and update server side cart.
// Subtracks controlled by 2 settings so del vis and set sel=0. Others, just set vis hide.
if(tdbIsSubtrack(rec))
setCartVars( [ id, id+"_sel" ], [ '[]', 0 ] ); // Remove subtrack level vis and explicitly uncheck.
else if(tdbIsFolderContent(rec))
setCartVars( [ id, id+"_sel" ], [ 'hide', 0 ] ); // supertrack children need to have _sel set to trigger superttrack reshaping
else
setCartVar(id, 'hide' );
$(document.getElementById('tr_' + id)).remove();
- dragReorder.init();
- dragSelect.load(false);
- imageV2.markAsDirtyPage();
+ imageV2.afterImgChange(true);
} else if (!imageV2.mapIsUpdateable) {
jQuery('body').css('cursor', 'wait');
if(selectUpdated) {
// assert(document.TrackForm);
document.TrackForm.submit();
} else {
// add a hidden with new visibility value
var form = $(document.TrackHeaderForm);
$("").appendTo(form);
document.TrackHeaderForm.submit();
}
} else {
imageV2.requestImgUpdate(id, id + "=" + cmd, "", cmd);
}
}
@@ -2189,30 +2321,37 @@
if(rec.type.indexOf("wig") == 0 || rec.type.indexOf("bigWig") == 0 || id == "wikiTrack") {
displayItemFunctions = false;
} else if(rec.type.indexOf("expRatio") == 0) {
displayItemFunctions = title != "zoomInMore";
} else {
displayItemFunctions = true;
}
}
if(displayItemFunctions) {
o[rightClick.makeImgTag("magnify.png") + " Zoom to " + title] = {
onclick: function(menuItemClicked, menuObject) {
rightClick.hit(menuItemClicked, menuObject,
"selectWholeGene"); return true;
}
};
+ o[rightClick.makeImgTag("highlight.png") + " Highlight " + title] =
+ { onclick: function(menuItemClicked, menuObject) {
+ rightClick.hit(menuItemClicked, menuObject,
+ "highlightItem");
+ return true;
+ }
+ };
if(rightClick.supportZoomCodon && rec.type.indexOf("genePred") != -1) {
// http://hgwdev-larrym.cse.ucsc.edu/cgi-bin/hgGene?hgg_gene=uc003tqk.2&hgg_prot=P00533&hgg_chrom=chr7&hgg_start=55086724&hgg_end=55275030&hgg_type=knownGene&db=hg19&c=chr7
var name, table;
var reg = new RegExp("hgg_gene=([^&]+)");
var a = reg.exec(href);
if(a && a[1]) {
name = a[1];
reg = new RegExp("hgg_type=([^&]+)");
a = reg.exec(href);
if(a && a[1]) {
table = a[1];
}
} else {
// http://hgwdev-larrym.cse.ucsc.edu/cgi-bin/hgc?o=55086724&t=55275031&g=refGene&i=NM_005228&c=chr7
// http://hgwdev-larrym.cse.ucsc.edu/cgi-bin/hgc?o=55086713&t=55270769&g=wgEncodeGencodeManualV4&i=ENST00000455089&c=chr7
@@ -2273,59 +2412,30 @@
item = rightClick.makeImgTag("book.png")+" Show details for "+
title + "...";
o[item] = {onclick: function(menuItemClicked, menuObject) {
rightClick.hit(menuItemClicked,menuObject,"followLink");
return true; }
};
any = true;
}
}
if(any) {
menu.push($.contextMenu.separator);
menu.push(o);
}
}
}
- if (!done) {
- if(false) {
- // Currently toggling b/n drag-and-zoom mode and hilite mode is disabled b/c we don't know how to keep hilite mode from disabling the
- // context menus.
- var o = new Object();
- var str = "drag-and-zoom mode";
- if(dragSelect.autoHideSetting) {
- str += selectedImg;
- // menu[str].className = 'context-menu-checked-item';
- }
- o[str] = { onclick: function(menuItemClicked, menuObject) {
- rightClick.hit(menuItemClicked, menuObject, "dragZoomMode");
- return true; }
- };
- menu.push(o);
- o = new Object();
- // console.dir(ele);
- str = "hilight mode";
- if (!dragSelect.autoHideSetting) {
- str += selectedImg;
- }
- o[str] = { onclick: function(menuItemClicked, menuObject) {
- rightClick.hit(menuItemClicked, menuObject, "hilightMode");
- return true; }
- };
- menu.push(o);
- }
- //menu.push({"view image": {onclick: function(menuItemClicked, menuObject) { rightClick.hit(menuItemClicked, menuObject, "viewImg"); return true; }}});
- }
if(rightClick.selectedMenuItem && rec) {
// Add cfg options at just shy of end...
var o = new Object();
if(tdbIsLeaf(rec)) {
if (rec["configureBy"] != 'none'
&& (!tdbIsCompositeSubtrack(rec) || rec["configureBy"] != 'clickThrough')) {
// Note that subtracks never do clickThrough because
// parentTrack cfg is the desired clickThrough
o[rightClick.makeImgTag("wrench.png")+" Configure "+rec.shortLabel] = {
onclick: function(menuItemClicked, menuObject) {
rightClick.hit(menuItemClicked, menuObject, "hgTrackUi_popup");
return true; }
};
@@ -2347,38 +2457,58 @@
return true; }
};
}
if(jQuery.floatMgr) {
o[(rightClick.selectedMenuItem.id == rightClick.floatingMenuItem ?
selectedImg : blankImg) + " float"] = {
onclick: function(menuItemClicked, menuObject) {
rightClick.hit(menuItemClicked, menuObject, "float");
return true; }
};
}
menu.push($.contextMenu.separator);
menu.push(o);
}
+ menu.push($.contextMenu.separator);
+ if(hgTracks.highlight) {
+ var o;
+ if (hgTracks.highlight.search(getDb() + '.') == 0) {
+ o = new Object();
+ o[rightClick.makeImgTag("highlightZoom.png") +
+ " Zoom to highlighted region"] =
+ { onclick:function(menuItemClicked, menuObject) {
+ rightClick.hit(menuItemClicked, menuObject, "zoomToHighlight");
+ return true;
+ }
+ };
+ o[rightClick.makeImgTag("highlightRemove.png") + " Remove highlighting"] =
+ { onclick:function(menuItemClicked, menuObject) {
+ rightClick.hit(menuItemClicked, menuObject, "removeHighlight");
+ return true;
+ }
+ };
+ menu.push(o);
+ }
+ }
// Add view image at end
var o = new Object();
o[rightClick.makeImgTag("eye.png") + " View image"] = {
onclick: function(menuItemClicked, menuObject) {
rightClick.hit(menuItemClicked, menuObject, "viewImg");
return true; }
};
- menu.push($.contextMenu.separator);
menu.push(o);
return menu;
},
{
beforeShow: function(e) {
// console.log(mapItems[rightClick.selectedMenuItem]);
rightClick.selectedMenuItem = rightClick.findMapItem(e);
// XXXX? posting.blockUseMap = true;
return true;
},
hideTransition:'hide', // hideCallback fails if these are not defined.
hideSpeed:10,
hideCallback: function() {
$('p.btn.blueButtons').removeClass('blueButtons');
@@ -2451,32 +2581,31 @@
var rec = hgTracks.trackDb[trackName];
var subtrack = tdbIsSubtrack(rec) ? trackName :undefined; // If subtrack then vis rules differ
var allVars = getAllVars($('#hgTrackUiDialog'), subtrack );// For unknown reasons IE8 fails to find $('#pop'), occasionally
var changedVars = varHashChanges(allVars,popUp.saveAllVars);
//warn("cfgVars:"+varHashToQueryString(changedVars));
var newVis = changedVars[trackName];
var hide = (newVis != null && (newVis == 'hide' || newVis == '[]')); // subtracks do not have "hide", thus '[]'
if($('#imgTbl') == undefined) { // On findTracks or config page
setVarsFromHash(changedVars);
//if(hide) // TODO: When findTracks or config page has cfg popup, then vis change needs to be handled in page here
}
else { // On image page
if(hide) {
setVarsFromHash(changedVars);
$(document.getElementById('tr_' + trackName)).remove();
- dragReorder.init();
- dragSelect.load(false);
+ imageV2.afterImgChange(true);
} else {
// Keep local state in sync if user changed visibility
if(newVis != null) {
vis.update(trackName, newVis);
}
var urlData = varHashToQueryString(changedVars);
if(urlData.length > 0) {
if(imageV2.mapIsUpdateable) {
imageV2.requestImgUpdate(trackName,urlData,"");
} else {
window.location = "../cgi-bin/hgTracks?" + urlData +
"&hgsid=" + getHgsid();
}
}
}
@@ -2646,53 +2775,64 @@
},
function (position) {
genomePos.set(position, commify(getSizeFromCoordinates(position)));
});
// Make sure suggestTrack is visible when user chooses something via gene select (#3484).
if($("#suggestTrack").length) {
$(document.TrackForm || document.TrackHeaderForm).submit(function(event) {
if($('#hgFindMatches').length) {
vis.makeTrackVisible($("#suggestTrack").val());
}
});
}
}
},
+ afterImgChange: function (dirty)
+ { // Standard things to do when manipulations change image without ajax update.
+ dragReorder.init();
+ dragSelect.load(false);
+ imageV2.highlightRegion();
+ if (dirty)
+ imageV2.markAsDirtyPage();
+ },
+
afterReload: function (id)
{ // Reload various UI widgets after updating imgTbl map.
dragReorder.init();
dragSelect.load(false);
// Do NOT reload context menu (otherwise we get the "context menu sticks" problem).
// rightClick.load($('#tr_' + id));
if(imageV2.imgTbl.tableDnDUpdate)
imageV2.imgTbl.tableDnDUpdate();
rightClick.reloadFloatingItem();
// Turn on drag scrolling.
if(hgTracks.imgBoxPortal) {
$("div.scroller").panImages();
}
if (imageV2.backSupport) {
if (id) { // The remainder is only needed for full reload
imageV2.markAsDirtyPage(); // vis of cfg change
+ imageV2.highlightRegion();
return;
}
}
imageV2.loadRemoteTracks();
makeItemsByDrag.load();
imageV2.loadSuggestBox();
+ imageV2.highlightRegion();
if (imageV2.backSupport) {
imageV2.setInHistory(false); // Set this new position into History stack
} else {
imageV2.markAsDirtyPage();
}
},
updateImgForId: function (html, id, fullImageReload, newJsonRec)
{ // update row in imgTbl for given id.
// return true if we successfully pull slice for id and update it in imgTrack.
var str = "
]*>([\\s\\S]+?)
";
var reg = new RegExp(str);
var a = reg.exec(html);
if(a && a[1]) {
@@ -3067,30 +3207,80 @@
url: "../cgi-bin/hgTracks",
data: params + "&hgt.trackImgOnly=1&hgt.ideogramToo=1&hgsid=" + getHgsid(),
dataType: "html",
trueSuccess: imageV2.updateImgAndMap,
success: catchErrorOrDispatch,
error: errorHandler,
cmd: 'wholeImage',
loadingId: showLoadingImage("imgTbl"),
disabledEle: disabledEle,
currentId: currentId,
currentIdYOffset: currentIdYOffset,
cache: false
});
},
+ highlightRegion: function()
+ // highlight vertical region in imgTbl based on hgTracks.highlight (#709).
+ {
+ var pos;
+ var hexColor = '#FFAAAA'
+ $('#highlightItem').remove();
+ if(hgTracks.highlight) {
+ pos = parsePositionWithDb(hgTracks.highlight);
+ if(pos) {
+ pos.start--; // make start 0-based to match hgTracks.winStart
+ if (pos.color)
+ hexColor = pos.color;
+ }
+ }
+ if(pos != null && pos.chrom == hgTracks.chromName && pos.db == getDb()
+ && pos.start <= hgTracks.imgBoxPortalEnd && pos.end >= hgTracks.imgBoxPortalStart) {
+ var portalWidthBases = hgTracks.imgBoxPortalEnd - hgTracks.imgBoxPortalStart;
+ var portal = $('#imgTbl td.tdData')[0];
+ var leftPixels = $(portal).offset().left + 3; // 3 for borders and cgi item calcs ??
+ var pixelsPerBase = ($(portal).width() - 2) / portalWidthBases;
+ var clippedStartBases = Math.max(pos.start, hgTracks.imgBoxPortalStart);
+ var clippedEndBases = Math.min(pos.end, hgTracks.imgBoxPortalEnd);
+ var widthPixels = (clippedEndBases - clippedStartBases) * pixelsPerBase;
+ if(hgTracks.revCmplDisp)
+ leftPixels += (hgTracks.imgBoxPortalEnd - clippedEndBases) * pixelsPerBase - 1;
+ else
+ leftPixels += (clippedStartBases - hgTracks.imgBoxPortalStart) * pixelsPerBase;
+ // Impossible to get perfect... Okay to overrun by a pixel on each side
+ leftPixels = Math.floor(leftPixels);
+ widthPixels = Math.ceil(widthPixels);
+ if (widthPixels < 2) {
+ widthPixels = 3;
+ leftPixels -= 1;
+ }
+
+ var area = jQuery("");
+ $(area).css({ backgroundColor: hexColor, // display: 'none'
+ left: leftPixels + 'px', top: $('#imgTbl').offset().top + 1 + 'px',
+ width: widthPixels + 'px',
+ height: $('#imgTbl').css('height') });
+ $(area).data({leftPixels: leftPixels, widthPixels: widthPixels});// needed for dragScroll
+
+ // Larry originally appended to imgTbl, but discovered that doesn't work on IE 8 and 9.
+ $('body').append($(area));
+ // z-index is done in css class, so highlight is beneath transparent data images.
+ // NOTE: ideally highlight would be below transparent blue-lines, but THAT is a
+ // background-image so z-index can't get below it! PS/PDF looks better for blue-lines!
+ }
+ },
+
backSupport: (window.History.enabled != undefined), // support of r back button via:
history: null, // jquery.history.js and HTML5 history API
setupHistory: function ()
{ // Support for back-button using jquery.history.js.
// Sets up the history and initializes a state.
// Since ajax updates leave the browser cached pages different from the server state,
// simple back-button fails. Using a 'dirty flag' we had forced an update from server,
// whenever the back button was hit, meaning there was no going back from server-state!
// NOW using the hitsory API, the back-button triggers a 'statechange' event which can
// contain data. We save the position in the data and ajax update the image when the
// back-button is pressed. This works great for going back through ajax-updated position
// changes, but is a bit messier when going back past a full-page retrieved state (as
// described below).
@@ -3330,30 +3520,31 @@
// Turn on drag scrolling.
$("div.scroller").panImages();
}
//$("#zoomSlider").slider({ min: -4, max: 3, step: 1 });//, handle: '.ui-slider-handle' });
// Retrieve tracks via AJAX that may take too long to draw initialliy (i.e. a remote bigWig)
var retrievables = $('#imgTbl').find("tr.mustRetrieve")
if($(retrievables).length > 0) {
$(retrievables).each( function (i) {
var trackName = $(this).attr('id').substring(3);
imageV2.requestImgUpdate(trackName,"","");
});
}
imageV2.loadRemoteTracks();
makeItemsByDrag.load();
+ imageV2.highlightRegion();
}
// Drag select in chromIdeogram
if($('img#chrom').length == 1) {
if($('area.cytoBand').length >= 1) {
$('img#chrom').chromDrag();
}
}
// Track search uses tabs
trackSearch.init();
// Drag select initialize
if (imageV2.enabled) { // moved from window.load().
dragSelect.load(true);