8c908f948b09826c6cb4452ee5b282aca41be85e
galt
Tue Dec 8 21:52:59 2015 -0800
Multi-region (exonMostly). This work allows people to look at virtual chromosomes from a list of regions and then navigate and perform all of the usual functions on it.
diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index ad65569..eba5e3a 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -16,31 +16,31 @@
* int newWinWidth // new width (in bps) if user clicks on the top ruler
* boolean revCmplDisp // true if we are in reverse display
* int insideX // width of side-bar (in pixels)
* int rulerClickHeight // height of ruler (in pixels) - zero if ruler is hidden
* boolean inPlaceUpdate // true if in-place-update is turned on
* int imgBox* // various drag-scroll values
* boolean measureTiming // true if measureTiming is on
* Object trackDb // hash of trackDb entries for tracks which are visible on current page
*/
function initVars()
{ // There are various entry points, so we call initVars in several places to make sure all is well
if (typeof(hgTracks) !== "undefined" && !genomePos.original) {
// remember initial position and size so we can restore it if user cancels
genomePos.original = genomePos.getOriginalPos();
- genomePos.originalSize = $('#size').text();
+ genomePos.originalSize = $('#size').text().replace(/,/g, ""); // strip out any commas
dragSelect.originalCursor = jQuery('body').css('cursor');
imageV2.imgTbl = $('#imgTbl');
// imageV2.enabled === true unless: advancedJavascript===false, or trackSearch, or config pg
imageV2.enabled = (imageV2.imgTbl && imageV2.imgTbl.length > 0);
// jQuery load function with stuff to support drag selection in track img
if (theClient.isSafari()) {
// Safari has the following bug: if we update the hgTracks map dynamically,
// the browser ignores the changes (even though if you look in the DOM the changes
// are there). So we have to do a full form submission when the user changes
// visibility settings or track configuration.
// As of 5.0.4 (7533.20.27) this is problem still exists in safari.
// As of 5.1 (7534.50) this problem appears to have been fixed - unfortunately,
// logs for 7/2011 show vast majority of safari users are pre-5.1 (5.0.5 is by far
@@ -72,32 +72,32 @@
linkFixup: function (pos, id, reg, endParamName)
{ // fixup external links (e.g. ensembl)
var ele = $(document.getElementById(id));
if (ele.length) {
var link = ele.attr('href');
var a = reg.exec(link);
if (a && a[1]) {
ele.attr('href', a[1] + pos.start + "&" + endParamName + "=" + pos.end);
}
}
},
setByCoordinates: function (chrom, start, end)
{
- var newPosition = chrom + ":" + commify(start) + "-" + commify(end);
- genomePos.set(newPosition, commify(end - start + 1));
+ var newPosition = chrom + ":" + start + "-" + end;
+ genomePos.set(newPosition, end - start + 1);
return newPosition;
},
getElement: function ()
{
// Return position box object
var tags = document.getElementsByName("position");
// There are multiple tags with name === "position" (the visible position text input
// and a hidden with id='positionHidden'); we return value of visible element.
for (var i = 0; i < tags.length; i++) {
var ele = tags[i];
if (ele.id !== "positionHidden") {
return ele;
}
}
@@ -114,50 +114,214 @@
return null;
},
getOriginalPos: function ()
{
return genomePos.original || genomePos.get();
},
revertToOriginalPos: function ()
{
// undo changes to position (i.e. after user aborts a drag-and-select).
this.set(this.original, this.originalSize);
this.original = this.originalSize = null; // not sure if this is necessary.
},
+ undisguisePosition: function(position) // UN-DISGUISE VMODE
+ { // find the virt position
+ // position should be real chrom span
+ //warn("undisguisePosition position="+position); // DEBUG REMOVE
+ var pos = parsePosition(position);
+ if (!pos)
+ return position; // some parsing error, return original
+ var start = pos.start - 1;
+ var end = pos.end;
+ var chromName = hgTracks.windows[0].chromName;
+ if (pos.chrom !== chromName)
+ return position; // return original
+ var newStart = -1;
+ var newEnd = -1;
+ var lastW = null;
+ var windows = null;
+ //warn("start="+start+" end="+end); // DEBUG REMOVE
+ for (j=0; j < 3; ++j) {
+ if (j === 0) windows = hgTracks.windowsBefore;
+ if (j === 1) windows = hgTracks.windows;
+ if (j === 2) windows = hgTracks.windowsAfter;
+ for (i=0,len=windows.length; i < len; ++i) {
+ var w = windows[i];
+ // double check chrom is same thoughout all windows, otherwise warning, return original value
+ if (w.chromName != chromName) {
+ return position; // return original
+ }
+ // check that the regions are ascending and non-overlapping
+ if (lastW && w.winStart < lastW.winEnd) {
+ return position; // return original
+ }
+ // overlap with position?
+ // if intersection,
+ if (w.winEnd > start && end > w.winStart) {
+ var s = Math.max(start, w.winStart);
+ var e = Math.min(end, w.winEnd);
+ var cs = s - w.winStart + w.virtStart;
+ var ce = e - w.winStart + w.virtStart;
+ //warn("cs="+cs+" ce="+ce); // DEBUG REMOVE
+ if (newStart === -1)
+ newStart = cs;
+ newEnd = ce;
+ }
+ lastW = w;
+ }
+ }
+ // return new virt undisguised position as a string
+ var newPos = "virt:" + (newStart+1) + "-" + newEnd;
+ //warn("undisguisePosition newPos="+newPos); // DEBUG REMOVE
+ return newPos;
+ },
+
+ disguiseSize: function(position) // DISGUISE VMODE
+ { // find the real size of the windows spanned
+ // position should be a real chrom span
+ //warn("disguisePosition position="+position); // DEBUG REMOVE
+ var pos = parsePosition(position);
+ if (!pos)
+ return 0;
+ var start = pos.start - 1;
+ var end = pos.end;
+ var newSize = 0;
+ var windows = null;
+ //warn("start="+start+" end="+end); // DEBUG REMOVE
+ for (j=0; j < 3; ++j) {
+ if (j === 0) windows = hgTracks.windowsBefore;
+ if (j === 1) windows = hgTracks.windows;
+ if (j === 2) windows = hgTracks.windowsAfter;
+ for (i=0,len=windows.length; i < len; ++i) {
+ var w = windows[i];
+ // overlap with position?
+ // if intersection,
+ if (w.winEnd > start && end > w.winStart) {
+ var s = Math.max(start, w.winStart);
+ var e = Math.min(end, w.winEnd);
+ //warn("s="+s+" e="+e); // DEBUG REMOVE
+ newSize += (e - s);
+ }
+ }
+ }
+ // return real size of the disguised position
+ //warn("disguiseSize newSize="+newSize); // DEBUG REMOVE
+ return newSize;
+ },
+
+ disguisePosition: function(position) // DISGUISE VMODE
+ { // find the single-chrom range spanned
+ // position should be virt
+ //warn("disguisePosition position="+position); // DEBUG REMOVE
+ var pos = parsePosition(position);
+ if (!pos)
+ return position; // some parsing error, return original
+ var start = pos.start - 1;
+ var end = pos.end;
+ var chromName = hgTracks.windows[0].chromName;
+ var newStart = -1;
+ var newEnd = -1;
+ var lastW = null;
+ var windows = null;
+ //warn("start="+start+" end="+end); // DEBUG REMOVE
+ for (j=0; j < 3; ++j) {
+ if (j === 0) windows = hgTracks.windowsBefore;
+ if (j === 1) windows = hgTracks.windows;
+ if (j === 2) windows = hgTracks.windowsAfter;
+ for (i=0,len=windows.length; i < len; ++i) {
+ var w = windows[i];
+ // double check chrom is same thoughout all windows, otherwise warning, return original value
+ if (w.chromName != chromName) {
+ return position; // return undisguised original
+ }
+ // check that the regions are ascending and non-overlapping
+ if (lastW && w.winStart < lastW.winEnd) {
+ return position; // return undisguised original
+ }
+ // overlap with position?
+ // if intersection,
+ if (w.virtEnd > start && end > w.virtStart) {
+ var s = Math.max(start, w.virtStart);
+ var e = Math.min(end, w.virtEnd);
+ var cs = s - w.virtStart + w.winStart;
+ var ce = e - w.virtStart + w.winStart;
+ //warn("cs="+cs+" ce="+ce); // DEBUG REMOVE
+ if (newStart === -1)
+ newStart = cs;
+ newEnd = ce;
+ }
+ lastW = w;
+ }
+ }
+ // return new non-virt disguised position as a string
+ var newPos = chromName + ":" + (newStart+1) + "-" + newEnd;
+ //warn("disguisePosition newPos="+newPos); // DEBUG REMOVE
+ return newPos;
+ },
+
set: function (position, size)
{ // Set value of position and size (in hiddens and input elements).
// We assume size has already been commified.
// Either position or size may be null.
+
+ // stack dump // DEBUG
+ //console.trace();
+ // NOT work on safari
+ //var obj = {};
+ //Error.captureStackTrace(obj);
+ //warn("genomePos.set() called "+obj.stack);
+
+ position = position.replace(/,/g, ""); // strip out any commas
+
+ if (position) {
+ // DISGUISE VMODE
+ //warn("genomePos.set() called, position = "+position);
+ if (hgTracks.virtualSingleChrom && (position.search("virt:")===0)) {
+ var newPosition = genomePos.disguisePosition(position);
+ //warn("genomePos.set() position = "+position+", newPosition = "+newPosition);
+ position = newPosition;
+ }
+ }
if (position) {
// There are multiple tags with name === "position"
// (one in TrackHeaderForm and another in TrackForm).
var tags = document.getElementsByName("position");
for (var i = 0; i < tags.length; i++) {
var ele = tags[i];
ele.value = position;
}
}
+ var pos = parsePosition(position);
if ($('#positionDisplay').length) {
- $('#positionDisplay').text(position);
+ // add commas to positionDisplay
+ var commaPosition = position;
+ if (pos)
+ commaPosition = pos.chrom+":"+commify(pos.start)+"-"+commify(pos.end);
+ $('#positionDisplay').text(commaPosition);
}
if (size) {
- $('#size').text(size);
+ if (hgTracks.virtualSingleChrom && (position.search("virt:")!==0)) {
+ var newSize = genomePos.disguiseSize(position);
+ //warn("genomePos.set() position = "+position+", newSize = "+newSize);
+ if (newSize > 0)
+ size = newSize;
+ }
+ $('#size').text(commify(size)); // add commas
}
- var pos = parsePosition(position);
if (pos) {
// fixup external static links on page'
// Example ensembl link:
// http://www.ensembl.org/Homo_sapiens/contigview?chr=21&start=33031934&end=33041241
genomePos.linkFixup(pos, "ensemblLink", new RegExp("(.+start=)[0-9]+"), "end");
// Example NCBI link:
// http://www.ncbi.nlm.nih.gov/mapview/maps.cgi?taxid=9606&CHR=21&BEG=33031934&END=33041241
genomePos.linkFixup(pos, "ncbiLink", new RegExp("(.+BEG=)[0-9]+"), "END");
// Example medaka link:
// http://utgenome.org/medakabrowser_ens_jump.php?revision=version1.0&chr=chromosome18&start=14435198&end=14444829
genomePos.linkFixup(pos, "medakaLink", new RegExp("(.+start=)[0-9]+"), "end");
@@ -231,71 +395,209 @@
endDelta = startDelta;
x2 = Math.min(imgWidth, selEnd);
startDelta = Math.floor(mult * (imgWidth - x2));
} else {
x2 = Math.max(hgTracks.insideX, selEnd);
endDelta = Math.floor(mult * (x2 - hgTracks.insideX));
}
var newStart = hgTracks.winStart + startDelta;
var newEnd = hgTracks.winStart + 1 + endDelta;
if (newEnd > winEnd) {
newEnd = winEnd;
}
return {chromStart : newStart, chromEnd : newEnd};
},
+ chromToVirtChrom: function (chrom, chromStart, chromEnd)
+ { // Convert regular chromosome position to virtual chrom coordinates using hgTracks.windows list
+ // Consider the first contiguous set of overlapping regions to define the match (for now).
+ // only works for regions covered by the current hgTracks.windows
+ var virtStart = -1, virtEnd = -1;
+ var s,e;
+ var i, len;
+ //warn("length of hgTracks.windows = "+hgTracks.windows.length); // DEBUG REMOVE
+ for (i = 0, len = hgTracks.windows.length; i < len; ++i) {
+ var w = hgTracks.windows[i];
+ var overlap = (chrom == w.chromName && chromEnd > w.winStart && w.winEnd > chromStart);
+ //warn("w.chromName="+w.chromName+" w.winStart="+w.winStart+" w.winEnd="+w.winEnd+" overlap?="+overlap+" virtStart="+virtStart); // DEBUG REMOVE
+ if (virtStart == -1) {
+ if (overlap) {
+ // when they overlap the first time
+ s = Math.max(chromStart, w.winStart);
+ e = Math.min(chromEnd, w.winEnd);
+ virtStart = w.virtStart + (s - w.winStart);
+ virtEnd = w.virtStart + (e - w.winStart);
+ //warn("s="+s+" e="+e+" virtStart="+virtStart+" virtEnd="+virtEnd); // DEBUG REMOVE
+ } else {
+ // until they overlap
+ // do nothing
+ }
+ } else {
+ if (overlap) {
+ // while they continue to overlap, extend
+ e = Math.min(chromEnd, w.winEnd);
+ virtEnd = w.virtStart + (e - w.winStart);
+ //warn("extend virtEnd="+virtEnd); // DEBUG REMOVE
+ } else {
+ // when they do not overlap anymore, stop
+ break;
+ }
+ }
+ }
+ return {chromStart : virtStart, chromEnd : virtEnd};
+ },
+
selectionPixelsToBases: function (img, selection)
{ // Convert selection x1/x2 coordinates to chromStart/chromEnd.
return genomePos.pixelsToBases(img, selection.x1, selection.x2,
hgTracks.winStart, hgTracks.winEnd);
},
update: function (img, selection, singleClick)
{
var pos = genomePos.pixelsToBases(img, selection.x1, selection.x2,
hgTracks.winStart, hgTracks.winEnd);
// singleClick is true when the mouse hasn't moved (or has only moved a small amount).
if (singleClick) {
var center = (pos.chromStart + pos.chromEnd)/2;
pos.chromStart = Math.floor(center - hgTracks.newWinWidth/2);
pos.chromEnd = pos.chromStart + hgTracks.newWinWidth;
+ // clip
+ if (pos.chromStart < hgTracks.chromStart)
+ pos.chromStart = hgTracks.chromStart; // usually 1
+ if (pos.chromEnd > hgTracks.chromEnd)
+ pos.chromEnd = hgTracks.chromEnd; // usually virt chrom size
+
+ // save current position so that that it may be restored after highlight or cancel.
+ genomePos.original = genomePos.getOriginalPos();
+ genomePos.originalSize = $('#size').text().replace(/,/g, ""); // strip out any commas
+
}
var newPosition = genomePos.setByCoordinates(hgTracks.chromName,
pos.chromStart+1, pos.chromEnd);
return newPosition;
},
handleChange: function (response, status)
{
var json = JSON.parse(response);
genomePos.set(json.pos);
},
changeAssemblies: function (ele) // UNUSED? Larry's experimental code
{ // code to update page when user changes assembly select list.
$.ajax({
type: "GET",
url: "../cgi-bin/hgApi",
data: cart.varsToUrlData({ 'cmd': 'defaultPos', 'db': getDb() }),
dataType: "html",
trueSuccess: genomePos.handleChange,
success: catchErrorOrDispatch,
error: errorHandler,
cache: true
});
return false;
+ },
+
+ convertedVirtCoords : {chromStart : -1, chromEnd : -1},
+
+ handleConvertChromPosToVirtCoords: function (response, status)
+ {
+ var virtStart = -1, virtEnd = -1;
+ var newJson = scrapeVariable(response, "convertChromToVirtChrom");
+ if (!newJson) {
+ warn("convertChromToVirtChrom object is missing from the response");
+ } else {
+ virtStart = newJson.virtWinStart;
+ virtEnd = newJson.virtWinEnd;
+ }
+ genomePos.convertedVirtCoords = {chromStart : virtStart, chromEnd : virtEnd};
+ },
+
+ convertChromPosToVirtCoords: function (chrom, chromStart, chromEnd)
+ { // code to convert chrom position to virt coords
+ genomePos.convertedVirtCoords = {chromStart : -1, chromEnd : -1}; // reset
+ var pos = chrom+":"+(chromStart+1)+"-"+chromEnd; // easier to pass 1 parameter than 3
+ $.ajax({
+ type: "GET",
+ async: false, // wait for result
+ url: "../cgi-bin/hgTracks",
+ data: cart.varsToUrlData({ 'hgt.convertChromToVirtChrom': pos, 'hgt.trackImgOnly' : 1, 'hgsid': getHgsid() }),
+ dataType: "html",
+ trueSuccess: genomePos.handleConvertChromPosToVirtCoords,
+ success: catchErrorOrDispatch,
+ error: errorHandler,
+ cache: false
+ });
+ return genomePos.convertedVirtCoords;
+ },
+
+ positionDisplayDialog: function ()
+ // Show the virtual and real positions of the windows
+ {
+ var position = genomePos.get();
+ //warn("positionDisplayDialog position="+position); // DEBUG REMOVE
+ var positionDialog = $("#positionDialog")[0];
+ if (!positionDialog) {
+ $("body").append("
\n";
+ }
+ $("#positionDisplayPosition").html(str);
+ } else {
+ $("#positionDisplayPosition").html(position);
+ }
+ $(positionDialog).dialog({
+ modal: true,
+ title: "Window-Positions",
+ closeOnEscape: true,
+ resizable: false,
+ autoOpen: false,
+ minWidth: 400,
+ minHeight: 40, // DEBUG GALT
+ buttons: {
+ "OK": function() {
+ $(this).dialog("close");
+ }
+ },
+
+ open: function () { // Make OK 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});
+ $(this).hide();
+ $('body').css('cursor', ''); // Occasionally wait cursor got left behind
+ }
+ });
+ $(positionDialog).dialog('open');
}
+
};
/////////////////////////////////////
//// Creating items by dragging /////
/////////////////////////////////////
var makeItemsByDrag = {
end: function (img, selection)
{
var image = $(img);
var imageId = image.attr('id');
var trackName = imageId.substring('img_data_'.length);
var pos = genomePos.selectionPixelsToBases(image, selection);
var command = document.getElementById('hgt_doJsCommand');
command.value = "makeItems " + trackName + " " + hgTracks.chromName;
@@ -694,97 +996,162 @@
{
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
+ var start = pos.start;
+ var end = pos.end;
+ //warn("highlightThisRegion: newPosition="+newPosition); // DEBUG REMOVE
+ hgTracks.highlight = getDb() + "." + pos.chrom + ":" + start + "-" + end + '#AAFFFF';
+ hgTracks.highlight = imageV2.disguiseHighlight(hgTracks.highlight);
// we include enableHighlightingDialog because it may have been changed by the dialog
- cart.setVarsObj({ 'highlight': hgTracks.highlight,
- 'enableHighlightingDialog': hgTracks.enableHighlightingDialog ? 1 : 0 });
+ var cartSettings = { 'highlight': hgTracks.highlight,
+ 'enableHighlightingDialog': hgTracks.enableHighlightingDialog ? 1 : 0 };
+
+ if (hgTracks.windows && !hgTracks.virtualSingleChrom) {
+ var nonVirtChrom = "";
+ var nonVirtStart = -1;
+ var nonVirtEnd = -1;
+ for (i=0,len=hgTracks.windows.length; i < len; ++i) {
+ var w = hgTracks.windows[i];
+ // overlap with new position?
+ if (w.virtEnd > start && end > w.virtStart) {
+ var s = Math.max(start, w.virtStart);
+ var e = Math.min(end, w.virtEnd);
+ var cs = s - w.virtStart + w.winStart;
+ var ce = e - w.virtStart + w.winStart;
+ if (nonVirtChrom === "") {
+ nonVirtChrom = w.chromName;
+ nonVirtStart = cs;
+ nonVirtEnd = ce;
+ } else {
+ if (w.chromName === nonVirtChrom) {
+ nonVirtEnd = Math.max(ce, nonVirtEnd);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (nonVirtChrom !== "")
+ cartSettings.nonVirtHighlight = getDb() + '.' + nonVirtChrom + ':' + nonVirtStart + '-' + (nonVirtEnd+1) + '#AAFFFF';
+ } else if (hgTracks.windows && hgTracks.virtualSingleChrom) {
+ cartSettings.nonVirtHighlight = hgTracks.highlight;
+ }
+ // TODO if not virt, do we need to erase cart nonVirtHighlight ?
+ cart.setVarsObj(cartSettings);
imageV2.highlightRegion();
},
selectionEndDialog: function (newPosition)
// Let user choose between zoom-in and highlighting.
{
var dragSelectDialog = $("#dragSelectDialog")[0];
if (!dragSelectDialog) {
- $("body").append("
" + newPosition +
+ $("body").append("
" +
"
" +
"Don't show this dialog again and always zoom. " +
"(Re-enable highlight via the 'configure' menu at any time.)
");
dragSelectDialog = $("#dragSelectDialog")[0];
}
+ if (hgTracks.windows) {
+ var i,len;
+ var newerPosition = newPosition;
+ if (hgTracks.virtualSingleChrom && (newPosition.search("virt:")===0)) {
+ newerPosition = genomePos.disguisePosition(newPosition);
+ }
+ var str = newerPosition + " \n";
+ var str2 = " \n";
+ str2 += "
\n";
+ var pos = parsePosition(newPosition);
+ var start = pos.start - 1;
+ var end = pos.end;
+ var selectedRegions = 0;
+ for (i=0,len=hgTracks.windows.length; i < len; ++i) {
+ var w = hgTracks.windows[i];
+ // overlap with new position?
+ if (w.virtEnd > start && end > w.virtStart) {
+ var s = Math.max(start, w.virtStart);
+ var e = Math.min(end, w.virtEnd);
+ var cs = s - w.virtStart + w.winStart;
+ var ce = e - w.virtStart + w.winStart;
+ str2 += "
" + w.chromName + ":" + (cs+1) + "-" + ce + "
\n";
+ selectedRegions += 1;
+ }
+ }
+ str2 += "
\n";
+ if (!(hgTracks.virtualSingleChrom && (selectedRegions === 1))) {
+ str += str2;
+ }
+ $("#dragSelectPosition").html(str);
+ } else {
+ $("#dragSelectPosition").html(newPosition);
+ }
$(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 ($("#disableDragHighlight").attr('checked'))
hgTracks.enableHighlightingDialog = false;
if (imageV2.inPlaceUpdate) {
+ if (hgTracks.virtualSingleChrom && (newPosition.search("virt:")===0)) {
+ newPosition = genomePos.disguisePosition(newPosition); // DISGUISE
+ }
var params = "position=" + newPosition;
if (!hgTracks.enableHighlightingDialog)
params += "&enableHighlightingDialog=0";
imageV2.navigateInPlace(params, null, true);
} else {
$('body').css('cursor', 'wait');
if (!hgTracks.enableHighlightingDialog)
cart.setVarsObj({'enableHighlightingDialog': 0 });
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
}
@@ -991,31 +1358,32 @@
if (mouseHasMoved === false) { // Update highlight by converting bp back to pix
pxDown = convertFromBases(selRange.beg);
pxUp = convertFromBases(selRange.end);
hiliteShow(pxDown,pxUp);
}
//if ((selRange.end - selRange.beg) < 50000)
// dontAsk = true;
if (dontAsk
|| confirm("Jump to new position:\n\n"+chr.name+":"+commify(selRange.beg)+
"-"+commify(selRange.end)+" size:"+commify(selRange.width)) ) {
genomePos.setByCoordinates(chr.name, selRange.beg, selRange.end);
// Stop the presses :0)
$('area.cytoBand').mousedown( function(e) { return false; });
if (imageV2.backSupport) {
imageV2.navigateInPlace("position=" +
- encodeURIComponent(genomePos.get().replace(/,/g,'')),null,true);
+ encodeURIComponent(genomePos.get().replace(/,/g,'')) +
+ "&findNearest=1",null,true);
hiliteCancel();
} else
document.TrackHeaderForm.submit();
return true; // Make sure the setTimeout below is not called.
}
}
}
hiliteCancel();
setTimeout(posting.allowMapClicks,50);
}
mouseIsDown = false;
mouseHasMoved = false;
}
function isWithin(beg,here,end)
@@ -1337,84 +1705,93 @@
return null;
var rowId = $(row).attr('id').substring('tr_'.length);
var rec = hgTracks.trackDb[rowId];
if (tdbIsSubtrack(rec) === false)
return null;
var rows = $('tr.trDraggable:has(p.' + rec.parentTrack+')');
return rows;
},
zipButtons: function (table)
{ // Goes through the image and binds composite track buttons when adjacent
var rows = $(table).find('tr');
var lastClass="";
var lastBtn = null;
+ var lastSide = null;
var lastMatchesLast=false;
var lastBlue=true;
var altColors=false;
var count=0;
var countN=0;
for (var ix=0; ix south) {
atEdge = false;
beyondImage = false;
if (savedPosition)
- genomePos.set(savedPosition,null);
+ genomePos.set(savedPosition);
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);
@@ -1743,33 +2120,32 @@
Math.round(portalScrolledX*hgTracks.imgBoxBasesPerPixel);
else
newPortalStart = closedPortalStart - // As offset goes down, bases seen goes up!
Math.round(portalScrolledX*hgTracks.imgBoxBasesPerPixel);
if (newPortalStart < hgTracks.chromStart && bounded) { // Stay within bounds
newPortalStart = hgTracks.chromStart;
recalculate = true;
}
var newPortalEnd = newPortalStart + portalWidthBases;
if (newPortalEnd > hgTracks.chromEnd && bounded) {
newPortalEnd = hgTracks.chromEnd;
newPortalStart = newPortalEnd - portalWidthBases;
recalculate = true;
}
if (newPortalStart > 0) {
- var newPos = hgTracks.chromName + ":" +
- commify(newPortalStart) + "-" + commify(newPortalEnd);
- genomePos.set(newPos, 0); // 0 means no need to change the size
+ var newPos = hgTracks.chromName + ":" + newPortalStart + "-" + newPortalEnd;
+ genomePos.set(newPos); // no need to change the size
}
if (recalculate && hgTracks.imgBoxBasesPerPixel > 0) {
// Need to recalculate X for bounding drag
portalScrolledX = (closedPortalStart - newPortalStart) / hgTracks.imgBoxBasesPerPixel;
newOffsetX = portalScrolledX - (hgTracks.imgBoxPortalOffsetX+hgTracks.imgBoxLeftLabel);
}
return newOffsetX;
}
function mapTopAndBottom(mapName,east,west)
{
// Find the top and bottom px given left and right boundaries
var mapPortal = { top: -10, bottom: -10 };
var items = $("map[name='"+mapName+"']").children();
if ($(items).length>0) {
$(items).each(function(t) {
@@ -2016,34 +2392,35 @@
var id = rightClick.selectedMenuItem.id;
var url = null; // TODO: Break this giant routine with shared vars into some sub-functions
var href = null;
var rec = null;
var row = null;
var rows = null;
var selectUpdated = null;
if (menuObject.shown) {
// warn("Spinning: menu is still shown");
setTimeout(function() { rightClick.hitFinish(menuItemClicked, menuObject, cmd); }, 10);
return;
}
if (cmd === 'selectWholeGene' || cmd === 'getDna' || cmd === 'highlightItem') {
// bring whole gene into view or redirect to DNA screen.
href = rightClick.selectedMenuItem.href;
- var chromStart, chromEnd;
- var a = /hgg_chrom=(\w+)&/.exec(href);
+ var chrom, chromStart, chromEnd;
// Many links leave out the chrom (b/c it's in the server side cart as "c")
- var chrom = hgTracks.chromName;
+ // var chrom = hgTracks.chromName; // This is no longer acceptable
+ // with multi-window capability drawing multiple positions on multiple chroms.
+ var a = /hgg_chrom=(\w+)&/.exec(href);
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]);
} else {
// a = /hgc.*\W+c=(\w+)/.exec(href);
a = /hgc.*\W+c=(\w+)/.exec(href);
if (a && a[1])
chrom = a[1];
a = /o=(\d+)/.exec(href);
@@ -2053,31 +2430,52 @@
if (a && a[1])
chromEnd = parseInt(a[1]);
}
if (!chrom || chrom.length === 0 || !chromStart || !chromEnd) {// 1-based chromStart
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) ) {
rightClick.windowOpenFailedMsg();
}
} else if (cmd === 'highlightItem') {
- dragSelect.highlightThisRegion(parseInt(chromStart), parseInt(chromEnd));
+ if (hgTracks.windows && !hgTracks.virtualSingleChrom) {
+ //warn("hitFinish highlightItem chrom="+chrom+" chromStart="+chromStart+" chromEnd="+chromEnd); // DEBUG REMOVE
+ // orig way only worked if the entire item was visible in the windows.
+ //var result = genomePos.chromToVirtChrom(chrom, parseInt(chromStart-1), parseInt(chromEnd));
+
+ var result = genomePos.convertChromPosToVirtCoords(chrom, parseInt(chromStart-1), parseInt(chromEnd));
+
+ //warn("result.chromStart="+result.chromStart+" result.chromEnd="+result.chromEnd); // DEBUG REMOVE
+ if (result.chromStart != -1)
+ {
+ var newPos2 = hgTracks.chromName+":"+(result.chromStart+1)+"-"+result.chromEnd;
+ dragSelect.highlightThisRegion(newPos2);
+ }
+
+ } else {
+ var newChrom = hgTracks.chromName;
+ if (hgTracks.windows && hgTracks.virtualSingleChrom) {
+ newChrom = hgTracks.windows[0].chromName;
+ }
+ var newPos3 = newChrom+":"+(parseInt(chromStart))+"-"+parseInt(chromEnd);
+ dragSelect.highlightThisRegion(newPos3);
+ }
} else {
var newPosition = genomePos.setByCoordinates(chrom, chromStart, chromEnd);
var reg = new RegExp("hgg_gene=([^&]+)");
var b = reg.exec(href);
var name;
// pull item name out of the url so we can set hgFind.matches (redmine 3062)
if (b && b[1]) {
name = b[1];
} else {
reg = new RegExp("[&?]i=([^&]+)");
b = reg.exec(href);
if (b && b[1]) {
name = b[1];
}
}
@@ -2702,88 +3100,239 @@
if ((maxSize===0) || (winSize < maxSize))
{
var url = "hgTracks?hgsid="+getHgsid()+"&hgt.redirectTool="+toolId;
var onclick = "$('#extToolDialog').dialog('close');";
htmlLines.push("
'+shortLabel+": "+longLabel+note);
}
}
htmlLines.push("");
content = htmlLines.join("");
- var title = hgTracks.chromName + ":" + (hgTracks.winStart+1) + "-" + hgTracks.winEnd + " on another website";
+ var title = hgTracks.chromName + ":" + (hgTracks.winStart+1) + "-" + hgTracks.winEnd;
+ if (hgTracks.nonVirtPosition)
+ title = hgTracks.nonVirtPosition;
+ title += " on another website";
$("body").append("
" + content + "
");
// copied from the hgTrackUi function below
var popMaxHeight = ($(window).height() - 40);
var popMaxWidth = ($(window).width() - 40);
var popWidth = 600;
if (popWidth > popMaxWidth)
popWidth = popMaxWidth;
// also copied from the hgTrackUi code below
$('#extToolDialog').dialog({
resizable: true, // Let description scroll vertically
height: popMaxHeight,
width: popWidth,
minHeight: 200,
minWidth: 600,
maxHeight: popMaxHeight,
maxWidth: popMaxWidth,
modal: true,
closeOnEscape: true,
autoOpen: false,
buttons: { "Close": function() {
$(this).dialog("close");
}},
});
$('#extToolDialog').dialog('open');
}
-// a function for the keyboard shortcut:
-// Jump to the "Get DNA" page
+ /////////////////////////////////////////////////////////
+ //// popupHgt popup for hgTracks (aka modal dialog) ////
+/////////////////////////////////////////////////////////
+var popUpHgt = {
+
+ whichHgTracksMethod: "",
+ title: "",
+
+ cleanup: function ()
+ { // Clean out the popup box on close
+ if ($('#hgTracksDialog').html().length > 0 ) {
+ // clear out html after close to prevent problems caused by duplicate html elements
+ $('#hgTracksDialog').html("");
+ popUpHgt.whichHgTracksMethod = "";
+ popUpHgt.title = "";
+ }
+ },
+
+ _uiDialogRequest: function (whichHgTracksMethod)
+ { // popup cfg dialog
+ popUpHgt.whichHgTracksMethod = whichHgTracksMethod;
+ var myLink = "../cgi-bin/hgTracks?hgsid=" + getHgsid() + "&db=" + getDb();
+ if (popUpHgt.whichHgTracksMethod === "multi-region config") {
+ myLink += "&hgTracksConfigMultiRegionPage=multi-region";
+ popUpHgt.title = "Configure Multi-Region View";
+ }
+
+ $.ajax({
+ type: "GET",
+ url: cart.addUpdatesToUrl(myLink),
+ dataType: "html",
+ trueSuccess: popUpHgt.uiDialog,
+ success: catchErrorOrDispatch,
+ error: errorHandler,
+ cache: false
+ });
+ },
+
+ hgTracks: function (whichHgTracksMethod)
+ { // Launches the popup but shields the ajax with a waitOnFunction
+ waitOnFunction( popUpHgt._uiDialogRequest, whichHgTracksMethod);
+ },
+
+ uiDialogOk: function (popObj)
+ { // When hgTracks Cfg popup closes with ok, then update cart and refresh parts of page
+
+ },
+
+ uiDialog: function (response, status)
+ {
+ // Take html from hgTracks and put it up as a modal dialog.
+
+ // make sure all links (e.g. help links) open up in a new window
+ response = response.replace(/"+ cleanHtml +"
");
+
+ // Strategy for popups with js:
+ // - jsFiles and CSS should not be included in html. Here they are shluped out.
+ // - The resulting files ought to be loadable dynamically (with getScript()),
+ // but this was not working nicely with the modal dialog
+ // Therefore include files must be included with hgTracks CGI !
+ // - embedded js should not be in the popup box.
+ // - Somethings should be in a popup.ready() function, and this is emulated below,
+ // as soon as the cleanHtml is added
+ // Since there are many possible popup cfg dialogs, the ready should be all inclusive.
+
+ // -- popup.ready() -- Here is the place to do things that might otherwise go
+ // into a $('#pop').ready() routine!
+
+ // Searching for some semblance of size suitability
+ var popMaxHeight = ($(window).height() - 40);
+ var popMaxWidth = ($(window).width() - 40);
+ var popWidth = 740;
+ if (popWidth > popMaxWidth)
+ popWidth = popMaxWidth;
+
+ $('#hgTracksDialog').dialog({
+ ajaxOptions: {
+ // This doesn't work
+ cache: true
+ },
+ resizable: true, // Let scroll vertically
+ height: 'auto',
+ width: popWidth,
+ minHeight: 200,
+ minWidth: 700,
+ maxHeight: popMaxHeight,
+ maxWidth: popMaxWidth,
+ modal: true,
+ closeOnEscape: true,
+ autoOpen: false,
+ buttons: {
+ /* NOT NOW
+ "OK": function() {
+ popUpHgt.uiDialogOk($('#pop'));
+ $(this).dialog("close");
+ }
+ */
+ },
+ // popup.ready() doesn't seem to work in open.
+
+ //create: function () {
+ //$(this).siblings().find(".ui-dialog-title").html('Test ');
+ //$(this).siblings().find(".ui-dialog-title").html('');
+ //},
+
+ open: function () {
+ $('#hgTracksDialog').find('.filterBy,.filterComp').each(
+ function(i) { // ddcl.js is dropdown checklist lib support
+ if ($(this).hasClass('filterComp'))
+ ddcl.setup(this);
+ else
+ ddcl.setup(this, 'noneIsAll');
+ }
+ );
+ },
+
+ close: function() {
+ popUpHgt.cleanup();
+ }
+ });
+
+
+ $('#hgTracksDialog').dialog('option' , 'title' , popUpHgt.title);
+ $('#hgTracksDialog').dialog('open');
+
+ }
+};
+
+// A function for the keyboard shortcut:
+// View DNA
function gotoGetDnaPage() {
- var url = "hgc?hgsid="+getHgsid()+"&g=getDna&i=mixed&c="+hgTracks.chromName+"&l="+hgTracks.winStart+"&r="+hgTracks.winEnd+"&db="+getDb();
+ var position = hgTracks.chromName+":"+hgTracks.winStart+"-"+hgTracks.winEnd;
+ if (hgTracks.virtualSingleChrom && (pos.chrom.search("virt") === 0)) {
+ position = genomePos.get().replace(/,/g,'');
+ } else if (hgTracks.windows && hgTracks.nonVirtPosition) {
+ position = hgTracks.nonVirtPosition;
+ }
+ var pos = parsePosition(position);
+ if (pos) {
+ var url = "hgc?hgsid="+getHgsid()+"&g=getDna&i=mixed&c="+pos.chrom+"&l="+pos.start+"&r="+pos.end+"&db="+getDb();
window.location.href = url;
}
+ return false;
+}
//////////////////////////////////
//// popup (aka modal dialog) ////
//////////////////////////////////
var popUp = {
trackName: "",
trackDescriptionOnly: false,
saveAllVars: null,
cleanup: function ()
{ // Clean out the popup box on close
if ($('#hgTrackUiDialog').html().length > 0 ) {
// clear out html after close to prevent problems caused by duplicate html elements
$('#hgTrackUiDialog').html("");
popUp.trackName = ""; //set to defaults
popUp.trackDescriptionOnly = false;
popUp.saveAllVars = null;
}
},
- _uiDialigRequest: function (trackName,descriptionOnly)
+ _uiDialogRequest: function (trackName,descriptionOnly)
{ // popup cfg dialog
popUp.trackName = trackName;
var myLink = "../cgi-bin/hgTrackUi?g=" + trackName + "&hgsid=" + getHgsid() +
"&db=" + getDb();
popUp.trackDescriptionOnly = descriptionOnly;
if (popUp.trackDescriptionOnly)
myLink += "&descriptionOnly=1";
var rec = hgTracks.trackDb[trackName];
if (!descriptionOnly && rec && rec.configureBy) {
if (rec.configureBy === 'none')
return;
else if (rec.configureBy === 'clickThrough') {
jQuery('body').css('cursor', 'wait');
window.location = cart.addUpdatesToUrl(myLink);
@@ -2793,40 +3342,57 @@
myLink += "&ajax=1";
$.ajax({
type: "GET",
url: cart.addUpdatesToUrl(myLink),
dataType: "html",
trueSuccess: popUp.uiDialog,
success: catchErrorOrDispatch,
error: errorHandler,
cmd: rightClick.selectedMenuItem,
cache: false
});
},
hgTrackUi: function (trackName,descriptionOnly)
{ // Launches the popup but shields the ajax with a waitOnFunction
- waitOnFunction( popUp._uiDialigRequest, trackName, descriptionOnly );
+ waitOnFunction( popUp._uiDialogRequest, trackName, descriptionOnly );
},
uiDialogOk: function (popObj, trackName)
{ // When hgTrackUi Cfg popup closes with ok, then update cart and refresh parts of page
var rec = hgTracks.trackDb[trackName];
var subtrack = tdbIsSubtrack(rec) ? trackName : undefined; // subtrack vis rules differ
// For unknown reasons IE8 fails to find $('#pop'), occasionally
var allVars = getAllVars($('#hgTrackUiDialog'), subtrack );
+ // Since 2010, when Tim changed this to only report changed vars instead of all form vars,
+ // it no longer matches the behavior of hgTrackUi when called the non-popup way.
+ // A few places in the hgTracks C code have been patched to explicitly set the cart vars
+ // for some default checkboxes. So now this still means that QA must explicitly test
+ // both paths through the code: as a separate full hgTracksUi page, and as a popup config window.
+ // hgTrackUi always sends in its form all variables causing them to be explicitly set in the cart.
+ // The popup only sends things that have changed, causing those changes to appear explicitly
+ // and even then it skips over disabled form items.
+ // There is some C code that was written before the popup config,
+ // and it expects all the variables are set or none are.
+ // If just some are set, and not the default ones, it gets confused.
+ // I fixed just such a bug in the code that handles refSeq.
+ // See commit daf92c0f9eb331ea60740e6802aabd241d4be363.
var changedVars = varHashChanges(allVars,popUp.saveAllVars);
+ // DEBUG REMOVE
+ //debugDumpFormCollection("saveAllVars", popUp.saveAllVars);
+ //debugDumpFormCollection("allVars", allVars);
+ //debugDumpFormCollection("changedVars", changedVars);
var newVis = changedVars[trackName];
// subtracks do not have "hide", thus '[]'
var hide = (newVis && (newVis === 'hide' || newVis === '[]'));
if ( ! normed($('#imgTbl')) ) { // On findTracks or config page
if (objNotEmpty(changedVars))
cart.setVarsObj(changedVars);
}
else { // On image page
if (hide) {
if (objNotEmpty(changedVars))
cart.setVarsObj(changedVars);
$(document.getElementById('tr_' + trackName)).remove();
imageV2.afterImgChange(true);
} else {
// Keep local state in sync if user changed visibility
@@ -2846,51 +3412,51 @@
},
uiDialog: function (response, status)
{
// Take html from hgTrackUi and put it up as a modal dialog.
// make sure all links (e.g. help links) open up in a new window
response = response.replace(/"+ cleanHtml +"
");
- // Strategy for poups with js:
+ // Strategy for popups with js:
// - jsFiles and CSS should not be included in html. Here they are shluped out.
// - The resulting files ought to be loadable dynamically (with getScript()),
// but this was not working nicely with the modal dialog
// Therefore include files must be included with hgTracks CGI !
// - embedded js should not be in the popup box.
// - Somethings should be in a popup.ready() function, and this is emulated below,
// as soon as the cleanHtml is added
// Since there are many possible popup cfg dialogs, the ready should be all inclusive.
if ( ! popUp.trackDescriptionOnly ) {
// If subtrack then vis rules differ
var subtrack = tdbIsSubtrack(hgTracks.trackDb[popUp.trackName]) ? popUp.trackName :"";
// Saves the original vars (and vals) that may get changed by the popup cfg.
popUp.saveAllVars = getAllVars( $('#hgTrackUiDialog'), subtrack );
// -- popup.ready() -- Here is the place to do things that might otherwise go
// into a $('#pop').ready() routine!
}
- // Searching for some selblance of size suitability
+ // Searching for some semblance of size suitability
var popMaxHeight = ($(window).height() - 40);
var popMaxWidth = ($(window).width() - 40);
var popWidth = 740;
if (popWidth > popMaxWidth)
popWidth = popMaxWidth;
$('#hgTrackUiDialog').dialog({
ajaxOptions: {
// This doesn't work
cache: true
},
resizable: true, // Let description scroll vertically
height: (popUp.trackDescriptionOnly ? popMaxHeight : 'auto'),
width: popWidth,
minHeight: 200,
@@ -3000,37 +3566,40 @@
$('.timing').remove();
for (var ix = strs.length; ix > 0; ix--) {
$('body').prepend(strs[ix - 1]);
}
}
reg = new RegExp("([\\S\\s]+?)");
a = reg.exec(response);
if (a && a[1]) {
$('.trackTiming').replaceWith(a[1]);
}
},
loadSuggestBox: function ()
{
if ($('#positionInput').length) {
- suggestBox.init(getDb(), $("#suggestTrack").length > 0,
+ if (!suggestBox.initialized) { // only call init once
+ suggestBox.init(getDb(),
+ $("#suggestTrack").length > 0,
function (item) {
- genomePos.set(item.id, commify(getSizeFromCoordinates(item.id)));
+ genomePos.set(item.id, getSizeFromCoordinates(item.id));
},
function (position) {
- genomePos.set(position, commify(getSizeFromCoordinates(position)));
+ genomePos.set(position, getSizeFromCoordinates(position));
});
+ }
// Make sure suggestTrack is visible when user chooses 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);
@@ -3179,30 +3748,45 @@
var $oldMap = $("map[name='ideoMap']");
var $container = $oldMap.parent();
$oldMap.remove();
$container.append(ideoMapMatch[0]);
}
if (imageV2.backSupport) {
// Reinit chrom dragging.
if ($('area.cytoBand').length >= 1) {
$('img#chrom').chromDrag();
}
}
}
}
},
+ updateBackground: function (response)
+ {
+ // Added by galt to update window separators
+ // Parse out background image url
+ // background-image:url("../trash/hgt/blueLines1563-118-12_hgwdev_galt_9df9_e33b30.png")
+
+ var a = /background-image:url\("(..\/trash\/hgt\/winSeparators[^"]+[.]png)"\)/.exec(response);
+ if (a && a[1]) {
+ //warn("updateBackground called! winSepartors"+a[1]);
+ $('td.tdData').css("background-image", "url("+a[1]+")");
+ }
+
+ },
+
+
requestImgUpdate: function (trackName,extraData,loadingId,newVisibility)
{
// extraData, loadingId and newVisibility are optional
var data = "hgt.trackImgOnly=1&hgsid=" + getHgsid() + "&hgt.trackNameFilter=" + trackName;
if (extraData && extraData !== "")
data += "&" + extraData;
if (!loadingId || loadingId === "")
loadingId = showLoadingImage("tr_" + trackName);
var getOrPost = "GET";
if ((data.length) > 2000) // extraData could contain a bunch of changes from the cfg dialog
getOrPost = "POST";
$.ajax({
type: getOrPost,
url: "../cgi-bin/hgTracks",
data: cart.addUpdatesToUrl(data),
@@ -3227,36 +3811,44 @@
var url = cart.addUpdatesToUrl(window.location.href);
if (extraData) {
if ( url.lastIndexOf("?") === -1)
url += "?" + extraData;
else
url += '&' + extraData;
}
window.location.assign(url);
return false;
}
document.TrackHeaderForm.submit();
},
updateImgAndMap: function (response, status)
{ // Handle ajax response with an updated trackMap image, map and optional ideogram.
- //
+ // and maybe the redLines background too.
// this.cmd can be used to figure out which menu item triggered this.
// this.id === appropriate track if we are retrieving just a single track.
+ //warn("updateImgAndMap got here 0"); // DEBUG REMOVE GALT
+
// update local hgTracks.trackDb to reflect possible side-effects of ajax request.
+
+ //alert(response); //DEBUG REMOVE GALT
+
var newJson = scrapeVariable(response, "hgTracks");
+
+ //alert(JSON.stringify(newJson)); // DEBUG REMOVE GALT
+
var oldJson = hgTracks;
var valid = false;
if (!newJson) {
var stripped = {};
stripJsEmbedded(response, true, stripped);
if ( ! stripped.warnMsg )
warn("hgTracks object is missing from the response");
} else {
if (this.id) {
if (newJson.trackDb[this.id]) {
var visibility = vis.enumOrder[newJson.trackDb[this.id].visibility];
var limitedVis;
if (newJson.trackDb[this.id].limitedVis)
limitedVis = vis.enumOrder[newJson.trackDb[this.id].limitedVis];
if (this.newVisibility && limitedVis && this.newVisibility !== limitedVis)
@@ -3267,62 +3859,66 @@
rec.limitedVis = newJson.trackDb[this.id].limitedVis;
vis.update(this.id, visibility);
valid = true;
} else {
warn("Invalid hgTracks.trackDb received from the server");
}
} else {
valid = true;
}
}
if (valid) {
if (imageV2.enabled
&& this.id
&& this.cmd
&& this.cmd !== 'wholeImage'
- && this.cmd !== 'selectWholeGene') {
+ && this.cmd !== 'selectWholeGene'
+ && !newJson.virtChromChanged) {
// Extract
...
and update appropriate row in imgTbl;
// this updates src in img_left_ID, img_center_ID and img_data_ID
// and map in map_data_ID
var id = this.id;
if (imageV2.updateImgForId(response, id, false)) {
imageV2.afterReload(id);
+ imageV2.updateBackground(response); // Added by galt to update window separators
} else {
warn("Couldn't parse out new image for id: " + id);
// Very helpful when debugging and alert doesn't render the html:
//alert("Couldn't parse out new image for id: " + id+"BR"+response);
}
} else {
if (imageV2.enabled) {
// Implement in-place updating of hgTracks image
- genomePos.setByCoordinates(newJson.chromName, newJson.
- winStart + 1, newJson.winEnd);
+ // GALT delaying this until after newJson updated in hgTracks so disguising works
+ //genomePos.setByCoordinates(newJson.chromName, newJson.winStart + 1, newJson.winEnd);
$("input[name='c']").val(newJson.chromName);
$("input[name='l']").val(newJson.winStart);
$("input[name='r']").val(newJson.winEnd);
- if (newJson.cgiVersion !== oldJson.cgiVersion) {
+ if (newJson.cgiVersion !== oldJson.cgiVersion || newJson.virtChromChanged) {
// Must reload whole page because of a new version on the server;
// this should happen very rarely. Note that we have already updated
// position based on the user's action.
imageV2.fullReload();
} else {
// Will rebuild image adding new, removing old and resorting tracks
imageV2.updateImgForAllIds(response,oldJson,newJson);
imageV2.updateChromImg(response);
+ imageV2.updateBackground(response); // Added by galt to update window separators
hgTracks = newJson;
genomePos.original = undefined;
+ genomePos.setByCoordinates(hgTracks.chromName, hgTracks.winStart + 1, hgTracks.winEnd); // MOVED HERE GALT
initVars();
imageV2.afterReload();
}
} else {
warn("ASSERT: Attempt to update track without advanced javascript features.");
}
}
if (hgTracks.measureTiming) {
imageV2.updateTiming(response);
}
}
if (this.disabledEle) {
this.disabledEle.removeAttr('disabled');
}
if (this.loadingId) {
@@ -3480,38 +4076,76 @@
data: cart.addUpdatesToUrl(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
});
},
+ disguiseHighlight: function(position)
+ // disguise highlight position
+ {
+ pos = parsePositionWithDb(position);
+ // DISGUISE
+ if (hgTracks.virtualSingleChrom && (pos.chrom.search("virt") === 0)) {
+ var positionStr = pos.chrom+":"+pos.start+"-"+pos.end;
+ var newPosition = genomePos.disguisePosition(positionStr);
+ var newPos = parsePosition(newPosition);
+ pos.chrom = newPos.chrom;
+ pos.start = newPos.start;
+ pos.end = newPos.end;
+ }
+ return pos.db+"."+pos.chrom+":"+pos.start+"-"+pos.end+pos.color;
+ },
+
+ undisguiseHighlight: function(pos)
+ // undisguise highlight pos
+ {
+ // UN-DISGUISE
+ //console.warn("undisguiseHighlight: got here 0"); // DEBUG REMOVE
+ if (hgTracks.virtualSingleChrom && (pos.chrom.search("virt") !== 0)) {
+ var position = pos.chrom+":"+pos.start+"-"+pos.end;
+ var newPosition = genomePos.undisguisePosition(position);
+ //console.warn("undisguiseHighlight: newPosition="+newPosition); // DEBUG REMOVE
+ var newPos = parsePosition(newPosition);
+ if (newPos) {
+ pos.chrom = newPos.chrom;
+ pos.start = newPos.start;
+ pos.end = newPos.end;
+ }
+ }
+ },
+
highlightRegion: function()
// highlight vertical region in imgTbl based on hgTracks.highlight (#709).
{
var pos;
var hexColor = '#FFAAAA';
$('#highlightItem').remove();
if (hgTracks.highlight) {
+ //console.warn("highlightRegion: hgTracks.highlight="+hgTracks.highlight); // DEBUG REMOVE
pos = parsePositionWithDb(hgTracks.highlight);
+ //console.warn("highlightRegion: pos.chrom="+pos.chrom); // DEBUG REMOVE
+ // UN-DISGUISE
+ imageV2.undisguiseHighlight(pos);
if (pos) {
pos.start--; // make start 0-based to match hgTracks.winStart
if (pos.color)
hexColor = pos.color;
}
}
if (pos && 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;
@@ -3530,165 +4164,168 @@
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 by 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:
+ backSupport: (window.History.enabled !== undefined), // support of our 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
+ // NOW using the history 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).
// NOTE: many things besides position could be ajax updated (e.g. track visibility). We are
// using the back-button to keep track of position only. Since the image should be updated
// every-time the back button is pressed, all track settings should persist (not go back).
// What will occasionally fail is vis box state and group expansion state. This is because
// the back-button goes to a browser cached page and then the image alone is updated.
imageV2.history = window.History;
// The 'statechange' function triggerd by the back-button.
// Whenever the position changes, then use ajax-update to refetch the position
imageV2.history.Adapter.bind(window,'statechange',function(){
+ var prevDbPos = imageV2.history.getState().data.lastDbPos;
var prevPos = imageV2.history.getState().data.position;
- var curPos = encodeURIComponent(genomePos.get().replace(/,/g,''));
- if (prevPos && prevPos !== curPos) {
- // NOTE: this function is NOT called when backing passed a full retrieval boundary
+ var curDbPos = hgTracks.lastDbPos;
+ if (prevDbPos && prevDbPos !== curDbPos) {
+ // NOTE: this function is NOT called when backing past a full retrieval boundary
genomePos.set(decodeURIComponent(prevPos));
- imageV2.navigateInPlace("position=" + prevPos, null, false);
+ imageV2.navigateInPlace("" + prevDbPos, null, false);
}
});
- // TODO: move elsewhere?
// With history support it is best that most position changes will ajax-update the image
// This ensures that the 'go' and 'refresh' button will do so unless the chrom changes.
$("input[value='go'],input[value='refresh']").click(function () {
var newPos = genomePos.get().replace(/,/g,'');
+ var newDbPos = hgTracks.lastDbPos;
if ( ! imageV2.manyTracks() ) {
var newChrom = newPos.split(':')[0];
var oldChrom = genomePos.getOriginalPos().split(':')[0];
if (newChrom === oldChrom) {
imageV2.markAsDirtyPage();
- imageV2.navigateInPlace("position="+encodeURIComponent(newPos), null, false);
+ imageV2.navigateInPlace("position=" + newPos, null, false);
window.scrollTo(0,0);
return false;
}
}
// If not just image update AND there are vis updates waiting...
if (cart.updatesWaiting()) {
- var url = "../cgi-bin/hgTracks?" + cart.varsToUrlData({ 'db': getDb(),
- 'position': newPos, 'hgsid': getHgsid() });
+ var url = "../cgi-bin/hgTracks?position=" + newPos + "&" + cart.varsToUrlData({ 'db': getDb(), 'hgsid': getHgsid() });
window.location.assign(url);
return false;
}
return true;
});
// Have vis box changes update cart through ajax. This helps keep page/cart in sync.
vis.initForAjax();
// We reach here from these possible paths:
// A) Forward: Full page retrieval: hgTracks is first navigated to (or chrom change)
// B) Back-button past a full retrieval (B in: ->A,->b,->c(full page),->d,<-c,<-B(again))
// B1) Dirty page: at least one non-position change (e.g. 1 track vis changed in b)
// B2) Clean page: only position changes from A->b->|
var curPos = encodeURIComponent(genomePos.get().replace(/,/g,''));
+ var curDbPos = hgTracks.lastDbPos;
var cachedPos = imageV2.history.getState().data.position;
+ var cachedDbPos = imageV2.history.getState().data.lastDbPos;
// A) Forward: Full page retrieval: hgTracks is first navigated to (or chrom change)
- if (!cachedPos) { // Not a back-button operation
+ if (!cachedDbPos) { // Not a back-button operation
// set the current position into history outright (will replace). No img update needed
imageV2.setInHistory(true);
} else { // B) Back-button past a full retrieval
genomePos.set(decodeURIComponent(cachedPos));
// B1) Dirty page: at least one non-position change
if (imageV2.isDirtyPage()) {
imageV2.markAsCleanPage();
// Only forcing a full page refresh if chrom changes
var cachedChrom = decodeURIComponent(cachedPos).split(':')[0];
var curChrom = decodeURIComponent( curPos).split(':')[0];
if (cachedChrom === curChrom) {
- imageV2.navigateInPlace("db="+getDb()+"&position=" + cachedPos, null, false);
+ imageV2.navigateInPlace("db="+getDb()+"&"+cachedDbPos, null, false);
} else {
imageV2.fullReload();
}
} else {
// B2) Clean page: only position changes from a->b
- if (cachedPos !== curPos) {
- imageV2.navigateInPlace("db="+getDb()+"&position=" + cachedPos, null, false);
+ if (cachedDbPos !== curDbPos) {
+ imageV2.navigateInPlace("db="+getDb()+"&"+cachedDbPos, null, false);
}
}
// Special because FF is leaving vis drop-downs disabled
vis.restoreFromBackButton();
}
},
setInHistory: function (fullPageLoad)
{ // Keep a position history and allow the back-button to work (sort of)
// replaceState on initial page load, pushState on each advance
// When call triggered by back button, the lastPos===newPos, so no action.
- var lastPos = imageV2.history.getState().data.position;
+ var lastDbPos = imageV2.history.getState().data.lastDbPos;
var newPos = encodeURIComponent(genomePos.get().replace(/,/g,'')); // no commas
+ var newDbPos = hgTracks.lastDbPos;
// A full page load could be triggered by back-button, but then there will be a lastPos
// if this is the case then don't set the position in history again!
- if (fullPageLoad && lastPos)
+ if (fullPageLoad && lastDbPos)
return;
- if (!lastPos || lastPos !== newPos) {
+ if (!lastDbPos || lastDbPos !== newDbPos) {
// Swap the position into the title
var title = $('TITLE')[0].text;
var ttlWords = title.split(' ');
if (ttlWords.length >= 2) {
for (var i=1; i < ttlWords.length; i++) {
if (ttlWords[i].indexOf(':') >= 0) {
ttlWords[i] = genomePos.get();
}
}
title = ttlWords.join(' ');
} else
title = genomePos.get();
- var sid = getHgsid(); // Wish you were here! Come on, someone must catch this.
+ var sid = getHgsid();
if (fullPageLoad) {
// Should only be on initial set-up: first navigation to page
- imageV2.history.replaceState({position: newPos, hgsid: + sid },title,
- "hgTracks?db=" + getDb() + "&position=" + newPos + "&hgsid="+sid);
+ imageV2.history.replaceState({lastDbPos: newDbPos, position: newPos, hgsid: + sid },title,
+ "hgTracks?db="+getDb()+"&"+newDbPos+"&hgsid="+sid);
} else {
// Should be when advancing (not-back-button)
- imageV2.history.pushState({position: newPos, hgsid: + getHgsid()},title,
- "hgTracks?db=" + getDb() + "&position=" + newPos + "&hgsid="+sid);
+ imageV2.history.pushState({lastDbPos: newDbPos, position: newPos, hgsid: + sid },title,
+ "hgTracks?db="+getDb()+"&"+newDbPos+"&hgsid="+sid);
}
}
}
};
//////////////////////
//// track search ////
//////////////////////
var trackSearch = {
searchKeydown: function (event)
{
if (event.which === 13) {
// Required to fix problem on IE and Safari where value of hgt_tSearch is "-"
// (i.e. not "Search").