eed2810ed6d1f108513493f532288fd9aac238a7 Merge parents db18564 bdb00f3 tdreszer Wed Oct 19 16:42:29 2011 -0700 Fix up a merge diff --cc src/hg/js/hgTracks.js index 6ba0bce,77fe196..04c39ac --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@@ -1,3082 -1,3127 +1,3127 @@@ // Javascript for use in hgTracks CGI - // "use strict"; + "use strict"; var debug = false; - var originalPosition; - var originalSize; - var originalCursor; - var originalMouseOffset = {x:0, y:0}; - var startDragZoom = null; - var imageV2 = false; - var blockUseMap = false; - var mapItems; - var trackImg; // jQuery element for the track image - var trackImgTbl; // jQuery element used for image table under imageV2 - var imgAreaSelect; // jQuery element used for imgAreaSelect - var originalImgTitle; - var autoHideSetting = true; // Current state of imgAreaSelect autoHide setting - var selectedMenuItem; // currently choosen context menu item (via context menu). - var browser; // browser ("msie", "safari" etc.) - var mapIsUpdateable = true; - var currentMapItem; - var lastMapItem; // this is used to try to keep what the user last was looking at in view after in-place updates. - var floatingMenuItem; - var visibilityStrsOrder = new Array("hide", "dense", "full", "pack", "squish"); // map browser numeric visibility codes to strings - var supportZoomCodon = false; // turn on experimental zoom-to-codon functionality (currently only on in larrym's tree). - var inPlaceUpdate = false; // modified based on value of hgTracks.inPlaceUpdate and mapIsUpdateable - var contextMenu; - var lastMatchLast; + var browser; // browser ("msie", "safari" etc.) // move to utils.js? /* Data passed in from CGI via the hgTracks object: * - * string cgiVersion // CGI_VERSION - * string chromName // current chromosome - * int winStart // genomic start coordinate (0-based, half-open) - * int winEnd // genomic end coordinate - * int newWinWidth // new width (in bps) if user clicks on the top ruler - * boolean dragSelection // true if we should allow drag and select - * boolean revCmplDisp // true if we are in reverse display - * int insideX // width of side-bar (in pixels) - * int rulerClickHeight // height of ruler (in pixels) - * boolean dragSelection // true if drag-and-select turned on - * 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 + * string cgiVersion // CGI_VERSION + * string chromName // current chromosome + * int winStart // genomic start coordinate (0-based, half-open) + * int winEnd // genomic end coordinate + * int newWinWidth // new width (in bps) if user clicks on the top ruler + * boolean dragSelection // true if we should allow drag and select + * 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 dragSelection // true if drag-and-select turned on + * 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 this variables get updated. - if(!originalPosition) { + { // There are various entry points, so we call initVars in several places to make sure all is well + if (!genomePos.original) { // remember initial position and size so we can restore it if user cancels - originalPosition = getOriginalPosition(); - originalSize = $('#size').text(); - originalCursor = jQuery('body').css('cursor'); + genomePos.original = genomePos.getOriginalPos(); + genomePos.originalSize = $('#size').text(); + dragSelect.originalCursor = jQuery('body').css('cursor'); + + imageV2.imgTbl = $('#imgTbl'); + // imageV2.enabled == true unless: advancedJavascript==false, or trackSearch, or config pg + imageV2.enabled = (imageV2.imgTbl != undefined && imageV2.imgTbl.length > 0); jQuery.each(jQuery.browser, function(i, val) { if(val) { browser = i; } }); // jQuery load function with stuff to support drag selection in track img if(browser == "safari") { if(navigator.userAgent.indexOf("Chrome") != -1) { - // Handle the fact that (as of 1.3.1), jQuery.browser reports "safari" when the browser is in fact Chrome. - browser = "chrome"; + // Handle the fact that (as of 1.3.1), jQuery.browser reports "safari" when the browser is in fact Chrome. + browser = "chrome"; } else { // 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 the most common). // // Early versions of Chrome had this problem too, but this problem went away as of Chrome 5.0.335.1 (or possibly earlier). - mapIsUpdateable = false; + imageV2.mapIsUpdateable = false; var reg = new RegExp("Version\/(\[0-9]+\.\[0-9]+) Safari"); var a = reg.exec(navigator.userAgent); if(a && a[1]) { var version = Number(a[1]); if(version >= 5.1) { - mapIsUpdateable = true; + imageV2.mapIsUpdateable = true; } } } } - inPlaceUpdate = hgTracks.inPlaceUpdate && mapIsUpdateable; + imageV2.inPlaceUpdate = hgTracks.inPlaceUpdate && imageV2.mapIsUpdateable; } } - function selectStart(img, selection) - { - initVars(); - if(contextMenu) { - contextMenu.hide(); - } - var now = new Date(); - startDragZoom = now.getTime(); - blockUseMap = true; - // vvvvvvv Should be obsolete since maps items are ignored when startDragZoom is set - // if(imageV2 == false) { - // jQuery.each(jQuery.browser, function(i, val) { - // if(i=="msie" && val) { - // // Very hacky way to solve following probem specif to IE: - // // If the user ends selection with the mouse in a map box item, the map item - // // is choosen instead of the selection; to fix this, we remove map box items - // // during the mouse selection process. - // mapHtml = $('#map').html(); - // $('#map').empty(); - // } - // }); - // } - // ^^^^^^^^ Should be obsolete since maps items are ignored when startDragZoom is set - } + ///////////////////////////////////// + ////////// Genomic position ///////// + ///////////////////////////////////// + var genomePos = { - function setPositionByCoordinates(chrom, start, end) - { - var newPosition = chrom + ":" + commify(start) + "-" + commify(end); - setPosition(newPosition, commify(end - start + 1)); - return newPosition; - } + original: null, + originalSize: 0, - function getPositionElement() - { - // 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; + linkFixup: function (pos, name, reg, endParamName) + { // fixup external links (e.g. ensembl) + if($('#' + name).length) { + var link = $('#' + name).attr('href'); + var a = reg.exec(link); + if(a && a[1]) { + $('#' + name).attr('href', a[1] + pos.start + "&" + endParamName + "=" + pos.end); } - } - return null; - } + } + }, - function getPosition() - { - // Return current value of position box - var ele = getPositionElement(); - if(ele != null) { - return ele.value; - } - return null; - } + setByCoordinates: function (chrom, start, end) + { + var newPosition = chrom + ":" + commify(start) + "-" + commify(end); + genomePos.set(newPosition, commify(end - start + 1)); + return newPosition; + }, - function getOriginalPosition() - { - return originalPosition || getPosition(); - } + 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; + } + } + return null; + }, - function markAsDirtyPage() - { // Page is marked as dirty so that the backbutton can be overridden - var dirty = $('#dirty'); - if (dirty != undefined && dirty.length != 0) - $(dirty).val('true'); - } + get: function () + { + // Return current value of position box + var ele = genomePos.getElement(); + if(ele != null) { + return ele.value; + } + return null; + }, - function isDirtyPage() - { // returns true if page was marked as dirty - // This will allow the backbutton to be overridden + getOriginalPos: function () + { + return genomePos.original || genomePos.get(); + }, + + 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. + 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; + } + } + if(size) { + $('#size').text(size); + } + var pos = parsePosition(position); + if(pos) { + // fixup external static links on page' - var dirty = $('#dirty'); - if (dirty != undefined && dirty.length > 0) { - if ($(dirty).val() == 'true') - return true; - } - return false; - } + // 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"); - function linkFixup(pos, name, reg, endParamName) - { - // fixup external links (e.g. ensembl) - if($('#' + name).length) { - var link = $('#' + name).attr('href'); - var a = reg.exec(link); - if(a && a[1]) { - $('#' + name).attr('href', a[1] + pos.start + "&" + endParamName + "=" + pos.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"); + + if($('#wormbaseLink').length) { + // e.g. http://www.wormbase.org/db/gb2/gbrowse/c_elegans?name=II:14646301-14667800 + var link = $('#wormbaseLink').attr('href'); + var reg = new RegExp("(.+:)[0-9]+"); + var a = reg.exec(link); + if(a && a[1]) { + $('#wormbaseLink').attr('href', a[1] + pos.start + "-" + pos.end); + } + } + // Fixup DNA link; e.g.: hgc?hgsid=2999470&o=114385768&g=getDna&i=mixed&c=chr7&l=114385768&r=114651696&db=panTro2&hgsid=2999470 + if($('#dnaLink').length) { + var link = $('#dnaLink').attr('href'); + var reg = new RegExp("(.+&o=)[0-9]+.+&db=[^&]+(.*)"); + var a = reg.exec(link); + if(a && a[1]) { + var url = a[1] + (pos.start - 1) + "&g=getDna&i=mixed&c=" + pos.chrom; + url += "&l=" + (pos.start - 1) + "&r=" + pos.end + "&db=" + getDb() + a[2]; + $('#dnaLink').attr('href', url); + } + } } - } - } + imageV2.markAsDirtyPage(); + }, + + check: function (img, selection) + { // return true if user's selection is still w/n the img (including some slop). + var imgWidth = jQuery(img).width(); + var imgHeight = jQuery(img).height(); + var imgOfs = jQuery(img).offset(); + var slop = 10; + + // We ignore clicks in the gray tab and track title column + // (we really should suppress all drag activity there, + // but I don't know how to do that with imgAreaSelect). + var leftX = hgTracks.revCmplDisp ? imgOfs.left - slop : + imgOfs.left + hgTracks.insideX - slop; + var rightX = hgTracks.revCmplDisp ? imgOfs.left + imgWidth - hgTracks.insideX + slop : + imgOfs.left + imgWidth + slop; + + return ( (selection.event.pageX >= leftX) + && (selection.event.pageX < rightX) + && (selection.event.pageY >= (imgOfs.top - slop)) + && (selection.event.pageY < (imgOfs.top + imgHeight + slop))); + }, + + pixelsToBases: function (img, selStart, selEnd, winStart, winEnd) + { // Convert image coordinates to chromosome coordinates + var imgWidth = jQuery(img).width() - hgTracks.insideX; + var width = hgTracks.winEnd - hgTracks.winStart; + var mult = width / imgWidth; // mult is bp/pixel multiplier + var startDelta; // startDelta is how many bp's to the right/left + if(hgTracks.revCmplDisp) { + var x1 = Math.min(imgWidth, selStart); + startDelta = Math.floor(mult * (imgWidth - x1)); + } else { + var x1 = Math.max(hgTracks.insideX, selStart); + startDelta = Math.floor(mult * (x1 - hgTracks.insideX)); + } + var endDelta; + if(hgTracks.revCmplDisp) { + endDelta = startDelta; + var x2 = Math.min(imgWidth, selEnd); + startDelta = Math.floor(mult * (imgWidth - x2)); + } else { + var 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}; + }, - function setPosition(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. - 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; - } - } - if(size) { - $('#size').text(size); - } - var pos = parsePosition(position); - if(pos) { - // fixup external static links on page' + 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; + } + var newPosition = genomePos.setByCoordinates(hgTracks.chromName, + pos.chromStart+1, pos.chromEnd); + return newPosition; + }, + + handleChange: function (response, status) + { + var json = eval("(" + response + ")"); + genomePos.set(json.pos); + }, - // Example ensembl link: http://www.ensembl.org/Homo_sapiens/contigview?chr=21&start=33031934&end=33041241 - linkFixup(pos, "ensemblLink", new RegExp("(.+start=)[0-9]+"), "end"); + 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: "cmd=defaultPos&db=" + getDb(), + dataType: "html", + trueSuccess: genomePos.handleChange, + success: catchErrorOrDispatch, + error: errorHandler, + cache: true + }); + return false; + } + } - // Example NCBI link: http://www.ncbi.nlm.nih.gov/mapview/maps.cgi?taxid=9606&CHR=21&BEG=33031934&END=33041241 - linkFixup(pos, "ncbiLink", new RegExp("(.+BEG=)[0-9]+"), "END"); + ///////////////////////////////////// + //// Creating items by dragging ///// + ///////////////////////////////////// + var makeItemsByDrag = { - // Example medaka link: http://utgenome.org/medakabrowser_ens_jump.php?revision=version1.0&chr=chromosome18&start=14435198&end=14444829 - linkFixup(pos, "medakaLink", new RegExp("(.+start=)[0-9]+"), "end"); + 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; + command.value += " " + pos.chromStart + " " + pos.chromEnd; + document.TrackHeaderForm.submit(); + return true; + }, - if($('#wormbaseLink').length) { - // e.g. http://www.wormbase.org/db/gb2/gbrowse/c_elegans?name=II:14646301-14667800 - var link = $('#wormbaseLink').attr('href'); - var reg = new RegExp("(.+:)[0-9]+"); - var a = reg.exec(link); - if(a && a[1]) { - $('#wormbaseLink').attr('href', a[1] + pos.start + "-" + pos.end); - } - } - // Fixup DNA link; e.g.: hgc?hgsid=2999470&o=114385768&g=getDna&i=mixed&c=chr7&l=114385768&r=114651696&db=panTro2&hgsid=2999470 - if($('#dnaLink').length) { - var link = $('#dnaLink').attr('href'); - var reg = new RegExp("(.+&o=)[0-9]+.+&db=[^&]+(.*)"); - var a = reg.exec(link); - if(a && a[1]) { - $('#dnaLink').attr('href', a[1] + (pos.start - 1) + "&g=getDna&i=mixed&c=" + pos.chrom + "&l=" + (pos.start - 1) + "&r=" + pos.end + "&db=" + getDb() + a[2]); + init: function (trackName) + { + // Set up so that they can drag out to define a new item on a makeItems track. + var img = $("#img_data_" + trackName); + if(img != undefined && img.length != 0) { + var imgHeight = imageV2.imgTbl.height(); + jQuery(img.imgAreaSelect( { selectionColor: 'green', outerColor: '', + minHeight: imgHeight, maxHeight: imgHeight, onSelectEnd: makeItemsByDrag.end, + autoHide: true, movable: false})); + } + } + } + + /////////////////// + ///// mouse ///// + /////////////////// + var mouse = { + + savedOffset: {x:0, y:0}, + + saveOffset: function (ev) + { // Save the mouse offset associated with this event + mouse.savedOffset = {x: ev.clientX, y: ev.clientY}; + }, + + hasMoved: function (ev) + { // return true if mouse has moved a significant amount + var minPixels = 10; + var movedX = ev.clientX - mouse.savedOffset.x; + var movedY = ev.clientY - mouse.savedOffset.y; + if (arguments.length == 2) { + var num = Number(arguments[1]); + if(isNaN(num)) { + if ( arguments[1].toLowerCase() == "x" ) + return (movedX > minPixels || movedX < (minPixels * -1)); + if ( arguments[1].toLowerCase() == "y" ) + return (movedY > minPixels || movedY < (minPixels * -1)); } + else + minPixels = num; } + return ( movedX > minPixels || movedX < (minPixels * -1) + || movedY > minPixels || movedY < (minPixels * -1)); } - markAsDirtyPage(); } - function checkPosition(img, selection) - { - // return true if user's selection is still w/n the img (including some slop). - var imgWidth = jQuery(img).width(); - var imgHeight = jQuery(img).height(); - var imgOfs = jQuery(img).offset(); - var slop = 10; - - // We ignore clicks in the gray tab and track title column (we really should suppress all drag activity there, - // but I don't know how to do that with imgAreaSelect). - var leftX = hgTracks.revCmplDisp ? imgOfs.left - slop : imgOfs.left + hgTracks.insideX - slop; - var rightX = hgTracks.revCmplDisp ? imgOfs.left + imgWidth - hgTracks.insideX + slop : imgOfs.left + imgWidth + slop; - - return (selection.event.pageX >= leftX) && (selection.event.pageX < rightX) - && (selection.event.pageY >= (imgOfs.top - slop)) && (selection.event.pageY < (imgOfs.top + imgHeight + slop)); - } + ///////////////// + //// posting //// + ///////////////// + var posting = { - function pixelsToBases(img, selStart, selEnd, winStart, winEnd) - // Convert image coordinates to chromosome coordinates - { - var imgWidth = jQuery(img).width() - hgTracks.insideX; - var width = hgTracks.winEnd - hgTracks.winStart; - var mult = width / imgWidth; // mult is bp/pixel multiplier - var startDelta; // startDelta is how many bp's to the right/left - if(hgTracks.revCmplDisp) { - var x1 = Math.min(imgWidth, selStart); - startDelta = Math.floor(mult * (imgWidth - x1)); - } else { - var x1 = Math.max(hgTracks.insideX, selStart); - startDelta = Math.floor(mult * (x1 - hgTracks.insideX)); - } - var endDelta; - if(hgTracks.revCmplDisp) { - endDelta = startDelta; - var x2 = Math.min(imgWidth, selEnd); - startDelta = Math.floor(mult * (imgWidth - x2)); - } else { - var 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}; - } + blockUseMap: false, - function selectionPixelsToBases(img, selection) - // Convert selection x1/x2 coordinates to chromStart/chromEnd. - { - return pixelsToBases(img, selection.x1, selection.x2, hgTracks.winStart, hgTracks.winEnd); - } + blockMapClicks: function () { posting.blockUseMap=true; }, + allowMapClicks: function () { posting.blockUseMap=false; }, + mapClicksAllowed: function () { return (posting.blockUseMap == false); }, - function updatePosition(img, selection, singleClick) - { - var pos = 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; - } - var newPosition = setPositionByCoordinates(hgTracks.chromName, pos.chromStart+1, pos.chromEnd); - return newPosition; - } + blockTheMapOnMouseMove: function (ev) + { + if (!posting.blockUseMap && mouse.hasMoved(ev)) { + posting.blockUseMap=true; + } + }, - function selectChange(img, selection) - { - initVars(); - if(selection.x1 != selection.x2) { // TODO: Larry could you examine this? - if(checkPosition(img, selection)) { - updatePosition(img, selection, false); - jQuery('body').css('cursor', originalCursor); - } else { - jQuery('body').css('cursor', 'not-allowed'); + mapClk: function () + { + return posting.saveSettings(this); + }, + + saveSettings: function (obj) + { + if(posting.blockUseMap==true) { + return false; + } + if(obj == undefined || obj.href == undefined) // called directly with obj + obj = this; // and from callback without obj + if (obj.href.match('#') || obj.target.length > 0) { + //alert("Matched # ["+obj.href+"] or has target:"+obj.target); + return true; + } + var thisForm=$(obj).parents('form'); + if(thisForm == undefined || $(thisForm).length == 0) + thisForm=$("FORM"); + if($(thisForm).length > 1 ) + thisForm=$(thisForm)[0]; + if(thisForm != undefined && $(thisForm).length == 1) { + //alert("posting form:"+$(thisForm).attr('name')); + return postTheForm($(thisForm).attr('name'),obj.href); } + return true; } - return true; } - function selectEnd(img, selection) - { - var now = new Date(); - var doIt = false; - if(originalCursor != null) - jQuery('body').css('cursor', originalCursor); - // ignore releases outside of the image rectangle (allowing a 10 pixel slop) - if(autoHideSetting && checkPosition(img, selection)) { - // ignore single clicks that aren't in the top of the image (this happens b/c the clickClipHeight test in selectStart - // doesn't occur when the user single clicks). - doIt = startDragZoom != null || selection.y1 <= hgTracks.rulerClickHeight; - } - if(doIt) { - // startDragZoom is null if mouse has never been moved - var singleClick = (selection.x2 == selection.x1) || startDragZoom == null || (now.getTime() - startDragZoom) < 100; - var newPosition = updatePosition(img, selection, singleClick); - if(newPosition != undefined) { - if(inPlaceUpdate) { - navigateInPlace("position=" + newPosition, null, true); + /////////////////////////////////////////////// + //// visibility (mixed with group toggle) ///// + /////////////////////////////////////////////// + var vis = { + + enumOrder: new Array("hide", "dense", "full", "pack", "squish"), // map cgi enum visibility codes to strings + + update: function (track, visibility) + { // Updates visibility state in hgTracks.trackDb and any visible elements on the page. + // returns true if we modify at least one select in the group list + var rec = hgTracks.trackDb[track]; + var selectUpdated = false; + $("select[name=" + track + "]").each(function(t) { + $(this).attr('class', visibility == 'hide' ? 'hiddenText' : 'normalText'); + $(this).val(visibility); + selectUpdated = true; + }); + if(rec) { + rec.localVisibility = visibility; + } + return selectUpdated; + }, + + get: function (track) + { // return current visibility for given track + var rec = hgTracks.trackDb[track]; + if(rec) { + if(rec.localVisibility) { + return rec.localVisibility; } else { - jQuery('body').css('cursor', 'wait'); - document.TrackHeaderForm.submit(); + return vis.enumOrder[rec.visibility]; } - } - } else { - setPosition(originalPosition, originalSize); - originalPosition = originalSize = null; - // if(mapHtml) { - // $('#map').append(mapHtml); - // } - } - // mapHtml = null; - startDragZoom = null; - setTimeout('blockUseMap=false;',50); // Necessary incase the selectEnd was over a map item. select takes precedence. - return true; - } + } else { + return null; + } + }, - $(window).load(function () { - jQuery.each(jQuery.browser, function(i, val) { - if(val) { - browser = i; + makeTrackVisible: function (track) + { + if(track != null && vis.get(track) != "full") { + vis.update(track, 'pack'); + $("<input type='hidden' name='" + track + "'value='pack'>").appendTo( + $(document.TrackHeaderForm)); } + }, + + toggleForGroup: function (button, prefix) + { // toggle visibility of a track group; prefix is the prefix of all the id's of tr's in the + // relevant group. This code also modifies the corresponding hidden fields and the gif of the +/- img tag. + imageV2.markAsDirtyPage(); + if(arguments.length > 2) + return setTableRowVisibility(button, prefix, "hgtgroup", "group",false,arguments[2]); + else + return setTableRowVisibility(button, prefix, "hgtgroup", "group",false); + }, + + expandAllGroups: function (newState) + { // Set visibility of all track groups to newState (true means expanded). + // This code also modifies the corresponding hidden fields and the gif's of the +/- img tag. + imageV2.markAsDirtyPage(); + $("img[id$='_button']").each( function (i) { + if(this.src.indexOf("/remove") > 0 || this.src.indexOf("/add") > 0) + vis.toggleForGroup(this,this.id.substring(0,this.id.length - 7),newState); // clip '_button' suffix }); - initVars(); + return false; + } - loadImgAreaSelect(true); - if($('#hgTrackUiDialog')) - $('#hgTrackUiDialog').hide(); + } + //////////////////////////////////////////////////////////// + // dragSelect is also known as dragZoom or shift-dragZoom // + //////////////////////////////////////////////////////////// + var dragSelect = { - // Don't load contextMenu if jquery.contextmenu.js hasn't been loaded - if(trackImg && jQuery.fn.contextMenu) { - $('#hgTrackUiDialog').hide(); - if(imageV2) { - $("map[name!=ideoMap]").each( function(t) { parseMap($(this,false));}); - } else { - // XXXX still under debate whether we have to remove the map - parseMap($('#map'),true); - $('#map').empty(); + 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(); + }, - originalImgTitle = trackImg.attr("title"); - if(imageV2) { - loadContextMenu(trackImgTbl); - //$(".trDraggable,.nodrop").each( function(t) { loadContextMenu($(this)); }); - // FIXME: why isn't rightClick for sideLabel working??? Probably because there is no link! - } else { - loadContextMenu(trackImg); - trackImg.mousemove( - function (e) { - mapEvent(e); - } - ); - trackImg.mousedown( - function (e) { - mapMouseDown(e); + 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; + }, + + 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 + // 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(imageV2.inPlaceUpdate) { + imageV2.navigateInPlace("position=" + newPosition, null, true); + } else { + jQuery('body').css('cursor', 'wait'); + document.TrackHeaderForm.submit(); } - ); + } + } else { // what is this doing? + genomePos.set(genomePos.original, genomePos.originalSize); + genomePos.original = genomePos.originalSize = null; } - } - }); + dragSelect.startTime = null; + setTimeout('posting.allowMapClicks();',50); // Necessary incase the dragSelect.selectEnd was over a map item. select takes precedence. + return true; + }, - function loadImgAreaSelect(firstTime) - { - // disable if ruler is not visible. - if(typeof(hgTracks) != "undefined" && hgTracks.dragSelection && hgTracks.rulerClickHeight > 0) { + load: function (firstTime) + { var imgHeight = 0; - trackImg = $('#img_data_ruler'); + if (imageV2.enabled) + imgHeight = imageV2.imgTbl.height(); - if(trackImg == undefined || trackImg.length == 0) { // Revert to old imageV1 - trackImg = $('#trackMap'); - imgHeight = jQuery(trackImg).height(); - } else { - imageV2 = true; - trackImgTbl = $('#imgTbl'); - imgHeight = trackImg.height(); - // XXXX Tim, I think we should get height from trackImgTbl, b/c it automatically adjusts as we add/delete items. - imgHeight = trackImgTbl.height(); - } - var heights = hgTracks.rulerClickHeight; - imgAreaSelect = jQuery((trackImgTbl || trackImg).imgAreaSelect({ selectionColor: 'blue', outerColor: '', - minHeight: imgHeight, maxHeight: imgHeight, - onSelectStart: selectStart, onSelectChange: selectChange, onSelectEnd: selectEnd, - autoHide: autoHideSetting, movable: false, - clickClipHeight: heights})); - } - } + // No longer disable without ruler, because shift-drag still works + if(typeof(hgTracks) != "undefined" && hgTracks.dragSelection) { - function makeItemsEnd(img, selection) - { - var image = $(img); - var imageId = image.attr('id'); - var trackName = imageId.substring('img_data_'.length); - var pos = selectionPixelsToBases(image, selection); - var command = document.getElementById('hgt_doJsCommand'); - command.value = "makeItems " + trackName + " " + hgTracks.chromName + " " + pos.chromStart + " " + pos.chromEnd; - document.TrackHeaderForm.submit(); - return true; - } + if (hgTracks.rulerClickHeight == undefined || hgTracks.rulerClickHeight == null) + hgTracks.rulerClickHeight = 0; // will be zero if no ruler track + var heights = hgTracks.rulerClickHeight; - function setUpMakeItemsDrag(trackName) - { - // Set up so that they can drag out to define a new item on a makeItems track. - var img = $("#img_data_" + trackName); - if(img != undefined && img.length != 0) { - var trackImgTbl = $('#imgTbl'); - var imgHeight = trackImgTbl.height(); - jQuery(img.imgAreaSelect( { selectionColor: 'green', outerColor: '', - minHeight: imgHeight, maxHeight: imgHeight, onSelectEnd: makeItemsEnd, - autoHide: true, movable: false})); + 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, + movable: false, + clickClipHeight: heights + })); + } } } - function toggleTrackGroupVisibility(button, prefix) - { - // toggle visibility of a track group; prefix is the prefix of all the id's of tr's in the - // relevant group. This code also modifies the corresponding hidden fields and the gif of the +/- img tag. - markAsDirtyPage(); - if(arguments.length > 2) - return setTableRowVisibility(button, prefix, "hgtgroup", "group", false, arguments[2]); - else - return setTableRowVisibility(button, prefix, "hgtgroup", "group", false); - } - - function setAllTrackGroupVisibility(newState) - { - // Set visibility of all track groups to newState (true means expanded). - // This code also modifies the corresponding hidden fields and the gif's of the +/- img tag. - markAsDirtyPage(); - $("img[id$='_button']").each( function (i) { - if(this.src.indexOf("/remove") > 0 || this.src.indexOf("/add") > 0) - toggleTrackGroupVisibility(this,this.id.substring(0,this.id.length - 7),newState); // clip '_button' suffix - }); - return false; - } - - ///////////////////////////////////////////////////// - // Chrom Drag/Zoom/Expand code + ///////////////////////////////////// + //// 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 // mouseUp: if no drag, then create href centered on bpDown loc with current span // if drag, then create href from bpDown to bpUp // if ctrlKey then expand selection to containing cytoBand(s) var img = { top: -1, scrolledTop: -1, height: -1, left: -1, scrolledLeft: -1, width: -1 }; // Image dimensions all in pix - var chr = { name: "", reverse: false, beg: -1, end: -1, size: -1, top: -1, bottom: -1, left: -1, right: -1, width: -1 }; // chrom Dimenaions beg,end,size in bases, rest in pix + var chr = { name: "", reverse: false, beg: -1, end: -1, size: -1, + top: -1, bottom: -1, left: -1, right: -1, width: -1 }; // chrom Dimenaions beg,end,size in bases, rest in pix var pxDown = 0; // pix X location of mouseDown var chrImg = $(this); var mouseIsDown = false; var mouseHasMoved = false; var hilite = jQuery('<div></div>'); initialize(); function initialize(){ findDimensions(); if(chr.top == -1) warn("chromIdeo(): failed to register "+this.id); else { hiliteSetup(); $('area.cytoBand').mousedown( function(e) { // mousedown on chrom portion of image only (map items) updateImgOffsets(); pxDown = e.clientX - img.scrolledLeft; var pxY = e.clientY - img.scrolledTop; if(mouseIsDown == false && isWithin(chr.left,pxDown,chr.right) && isWithin(chr.top,pxY,chr.bottom)) { mouseIsDown = true; mouseHasMoved = false; $(document).bind('mousemove',chromMove); $(document).bind( 'mouseup', chromUp); hiliteShow(pxDown,pxDown); return false; } }); } } function chromMove(e) { // If mouse was down, determine if dragged, then show hilite if ( mouseIsDown ) { var pxX = e.clientX - img.scrolledLeft; var relativeX = (pxX - pxDown); if(mouseHasMoved || (mouseHasMoved == false && Math.abs(relativeX) > 2)) { mouseHasMoved = true; if(isWithin(chr.left,pxX,chr.right)) hiliteShow(pxDown,pxX); else if(pxX < chr.left) hiliteShow(pxDown,chr.left); else hiliteShow(pxDown,chr.right); } } } function chromUp(e) { // If mouse was down, handle final selection $(document).unbind('mousemove',chromMove); $(document).unbind('mouseup',chromUp); chromMove(e); // Just in case if(mouseIsDown) { updateImgOffsets(); var bands; var pxUp = e.clientX - img.scrolledLeft; var pxY = e.clientY - img.scrolledTop; //warn("chromIdeo("+chr.name+") selected range (pix):"+pxDown+"-"+pxUp+" chrom range (pix):"+chr.left+"-"+chr.right+" chrom range (bp):"+chr.name+":"+chr.beg+"-"+chr.end); if(isWithin(0,pxY,img.height)) { // within vertical range or else cancel var selRange = { beg: -1, end: -1, width: -1 }; var dontAsk = true; if(e.ctrlKey) { bands = findCytoBand(pxDown,pxUp); if(bands.end > -1) { pxDown = bands.left; pxUp = bands.right; mouseHasMoved = true; dontAsk = false; selRange.beg = bands.beg; selRange.end = bands.end; hiliteShow(pxDown,pxUp); } } else if(mouseHasMoved) { - if( isWithin(-20,pxUp,chr.left) ) // bounded by chrom dimensions: but must remain within image! + if ( isWithin(-20,pxUp,chr.left) ) // bounded by chrom dimensions: but must remain within image! pxUp = chr.left; - if( isWithin(chr.right,pxUp,img.width + 20) ) + if ( isWithin(chr.right,pxUp,img.width + 20) ) pxUp = chr.right; - if( isWithin(chr.left,pxUp,chr.right+1) ) { + if ( isWithin(chr.left,pxUp,chr.right+1) ) { selRange.beg = convertToBases(pxDown); selRange.end = convertToBases(pxUp); if(Math.abs(selRange.end - selRange.beg) < 20) mouseHasMoved = false; // Drag so small: treat as simple click else dontAsk = false; } //else warn("chromIdeo("+chr.name+") NOT WITHIN HORIZONTAL RANGE\n selected range (pix):"+pxDown+"-"+pxUp+" chrom range (pix):"+chr.left+"-"+chr.right); } if(mouseHasMoved == false) { // Not else because small drag turns this off hiliteShow(pxUp,pxUp); var curWidth = hgTracks.winEnd - hgTracks.winStart; selRange.beg = convertToBases(pxUp) - Math.round(curWidth/2); // Notice that beg is based upon up position selRange.end = selRange.beg + curWidth; } if(selRange.end > -1) { // prompt, then submit for new position selRange = rangeNormalizeToChrom(selRange,chr); 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) + //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)) ) { - setPositionByCoordinates(chr.name, selRange.beg, selRange.end) + 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) $('area.cytoBand').mousedown( function(e) { return false; }); // Stop the presses :0) document.TrackHeaderForm.submit(); return true; // Make sure the setTimeout below is not called. } } } //else warn("chromIdeo("+chr.name+") NOT WITHIN VERTICAL RANGE\n selected range (pix):"+pxDown+"-"+pxUp+" chrom range (pix):"+chr.left+"-"+chr.right+"\n cytoTop-Bottom:"+chr.top +"-"+chr.bottom); hiliteCancel(); - setTimeout('blockUseMap=false;',50); + setTimeout('posting.allowMapClicks();',50); } mouseIsDown = false; mouseHasMoved = false; } function isWithin(beg,here,end) { // Simple utility return ( beg <= here && here < end ); } function convertToBases(pxX) { // Simple utility to convert pix to bases var offset = (pxX - chr.left)/chr.width; if(chr.reverse) offset = 1 - offset; return Math.round(offset * chr.size); } function convertFromBases(bases) { // Simple utility to convert bases to pix var offset = bases/chr.size; if(chr.reverse) offset = 1 - offset; return Math.round(offset * chr.width) + chr.left; } function findDimensions() { // Called at init: determine the dimensions of chrom from 'cytoband' map items var lastX = -1; $('area.cytoBand').each(function(ix) { var loc = this.coords.split(","); if(loc.length == 4) { var myLeft = parseInt(loc[0]); var myRight = parseInt(loc[2]); - if( chr.top == -1) { + if (chr.top == -1) { chr.left = myLeft; chr.right = myRight; chr.top = parseInt(loc[1]); chr.bottom = parseInt(loc[3]); } else { - if( chr.left > myLeft) + if (chr.left > myLeft) chr.left = myLeft; - if( chr.right < parseInt(loc[2])) + if (chr.right < parseInt(loc[2])) chr.right = parseInt(loc[2]); } var range = this.title.substr(this.title.lastIndexOf(':')+1) var pos = range.split('-'); if(pos.length == 2) { - if( chr.name.length == 0) { + if (chr.name.length == 0) { chr.beg = parseInt(pos[0]); //chr.end = parseInt(pos[1]); - chr.name = this.title.substring(this.title.lastIndexOf(' ')+1,this.title.lastIndexOf(':')) + chr.name = this.title.substring(this.title.lastIndexOf(' ')+1, + this.title.lastIndexOf(':')) } else { - if( chr.beg > parseInt(pos[0])) + if (chr.beg > parseInt(pos[0])) chr.beg = parseInt(pos[0]); } - if( chr.end < parseInt(pos[1])) { + if (chr.end < parseInt(pos[1])) { chr.end = parseInt(pos[1]); if(lastX == -1) lastX = myRight; else if(lastX > myRight) chr.reverse = true; // end is advancing, but X is not, so reverse } else if(lastX != -1 && lastX < myRight) chr.reverse = true; // end is not advancing, but X is, so reverse } $(this).css( 'cursor', 'text'); $(this).attr("href",""); } }); chr.size = (chr.end - chr.beg ); chr.width = (chr.right - chr.left); } function findCytoBand(pxDown,pxUp) { // Called when mouseup and ctrl: Find the bounding cytoband dimensions, both in pix and bases var cyto = { left: -1, right: -1, beg: -1, end: -1 }; $('area.cytoBand').each(function(ix) { var loc = this.coords.split(","); if(loc.length == 4) { var myLeft = parseInt(loc[0]); var myRight = parseInt(loc[2]); if(cyto.left == -1 || cyto.left > myLeft) { - if( isWithin(myLeft,pxDown,myRight) || isWithin(myLeft,pxUp,myRight) ) { + if ( isWithin(myLeft,pxDown,myRight) || isWithin(myLeft,pxUp,myRight) ) { cyto.left = myLeft; var range = this.title.substr(this.title.lastIndexOf(':')+1) var pos = range.split('-'); if(pos.length == 2) { cyto.beg = (chr.reverse ? parseInt(pos[1]) : parseInt(pos[0])); } } } if(cyto.right == -1 || cyto.right < myRight) { - if( isWithin(myLeft,pxDown,myRight) || isWithin(myLeft,pxUp,myRight) ) { + if ( isWithin(myLeft,pxDown,myRight) || isWithin(myLeft,pxUp,myRight) ) { cyto.right = myRight; var range = this.title.substr(this.title.lastIndexOf(':')+1) var pos = range.split('-'); if(pos.length == 2) { cyto.end = (chr.reverse ? parseInt(pos[0]) : parseInt(pos[1])); } } } } }); return cyto; } function rangeNormalizeToChrom(selection,chrom) { // Called before presenting or using base range: make sure chrom selection is within chrom range if(selection.end < selection.beg) { var tmp = selection.end; selection.end = selection.beg; selection.beg = tmp; } selection.width = (selection.end - selection.beg); selection.beg += 1; - if( selection.beg < chrom.beg) { + if (selection.beg < chrom.beg) { selection.beg = chrom.beg; selection.end = chrom.beg + selection.width; } - if( selection.end > chrom.end) { + if (selection.end > chrom.end) { selection.end = chrom.end; selection.beg = chrom.end - selection.width; - if( selection.beg < chrom.beg) { // spans whole chrom + if (selection.beg < chrom.beg) { // spans whole chrom selection.width = (selection.end - chrom.beg); selection.beg = chrom.beg + 1; } } return selection; } function hiliteShow(down,cur) { // Called on mousemove, mouseup: set drag hilite dimensions var topY = img.top; var high = img.height; var begX = -1; var wide = -1; if(cur < down) { begX = cur + img.left; wide = (down - cur); } else { begX = down + img.left; wide = (cur - down); } - $(hilite).css({ left: begX + 'px', width: wide + 'px', top: topY + 'px', height: high + 'px', display:'' }); + $(hilite).css({ left: begX + 'px', width: wide + 'px', top: topY + 'px', + height: high + 'px', display:'' }); $(hilite).show(); } function hiliteCancel(left,width,top,height) { // Called on mouseup: Make green drag hilite disappear when no longer wanted $(hilite).hide(); $(hilite).css({ left: '0px', width: '0px', top: '0px', height: '0px' }); } function hiliteSetup() { // Called on init: setup of drag region hilite (but don't show yet) - $(hilite).css({ backgroundColor: 'green', opacity: 0.4, borderStyle: 'solid', borderWidth: '1px', bordercolor: '#0000FF' }); + $(hilite).css({ backgroundColor: 'green', opacity: 0.4, borderStyle: 'solid', + borderWidth: '1px', bordercolor: '#0000FF' }); $(hilite).css({ display: 'none', position: 'absolute', overflow: 'hidden', zIndex: 1 }); jQuery($(chrImg).parents('body')).append($(hilite)); return hilite; } function updateImgOffsets() { // Called on mousedown: Gets the current offsets var offs = $(chrImg).offset(); img.top = Math.round(offs.top ); img.left = Math.round(offs.left); img.scrolledTop = img.top - $("body").scrollTop(); img.scrolledLeft = img.left - $("body").scrollLeft(); if($.browser.msie) { img.height = $(chrImg).outerHeight(); img.width = $(chrImg).outerWidth(); } else { img.height = $(chrImg).height(); img.width = $(chrImg).width(); } return img; } }); } - ///////////////////////////////////////////////////// - // Drag Reorder Code - function imgTblSetOrder(table) - { - // Sets the 'order' value for the image table after a drag reorder - var names = []; - var values = []; - $("tr.imgOrd").each(function (i) { - if ($(this).attr('abbr') != $(this).attr('rowIndex').toString()) { - $(this).attr('abbr',$(this).attr('rowIndex').toString()); - var name = this.id.substring('tr_'.length) + '_imgOrd'; - names.push(name); - values.push($(this).attr('abbr')); - } - }); - if(names.length > 0) { - setCartVars(names,values); - markAsDirtyPage(); - } - } - - function imgTblTrackShowCenterLabel(tr, show) - { // Will show or hide centerlabel as requested - // adjust button, sideLabel height, sideLabelOffset and centerlabel display - if(!$(tr).hasClass('clOpt')) - return; - var center = $(tr).find(".sliceDiv.cntrLab"); - if($(center) == undefined) - return; - seen = ($(center).css('display') != 'none'); - if(show == seen) - return; - - var centerHeight = $(center).height(); - - var btn = $(tr).find("p.btn"); - var side = $(tr).find(".sliceDiv.sideLab"); - if($(btn) != undefined && $(side) != undefined) { - var sideImg = $(side).find("img"); - if($(sideImg) != undefined) { - var top = parseInt($(sideImg).css('top')); - if(show) { - $(btn).css('height',$(btn).height() + centerHeight); - $(side).css('height',$(side).height() + centerHeight); - top += centerHeight; // top is a negative number - $(sideImg).css( {'top': top.toString() + "px" }); - $( center ).show(); - } else if(!show) { - $(btn).css('height',$(btn).height() - centerHeight); - $(side).css('height',$(side).height() - centerHeight); - top -= centerHeight; // top is a negative number - $(sideImg).css( {'top': top.toString() + "px" }); - $( center ).hide(); + /////////////////////////// + //// Drag Reorder Code //// + /////////////////////////// + var dragReorder = { + + setOrder: function (table) + { // Sets the 'order' value for the image table after a drag reorder + var names = []; + var values = []; + $("tr.imgOrd").each(function (i) { + if ($(this).attr('abbr') != $(this).attr('rowIndex').toString()) { + $(this).attr('abbr',$(this).attr('rowIndex').toString()); + var name = this.id.substring('tr_'.length) + '_imgOrd'; + names.push(name); + values.push($(this).attr('abbr')); } + }); + if(names.length > 0) { + setCartVars(names,values); + imageV2.markAsDirtyPage(); } - } - } - - function imgTblContiguousRowSet(row) - { // Returns the set of rows that are of the same class and contiguous - if(row == null) - return null; - var btn = $( row ).find("p.btn"); - if( btn.length == 0) - return null; - var classList = $( btn ).attr("class").split(" "); - var matchClass = classList[0]; - var table = $(row).parents('table#imgTbl')[0]; - var rows = $(table).find('tr'); - - // Find start index - var startIndex = $(row).attr('rowIndex'); - var endIndex = startIndex; - for(var ix=startIndex-1;ix>=0;ix--) { - btn = $( rows[ix] ).find("p.btn"); - if( btn.length == 0) - break; - classList = $( btn ).attr("class").split(" "); - if (classList[0] != matchClass) - break; - startIndex = ix; - } + }, - // Find end index - for(var ix=endIndex;ix<rows.length;ix++) { - btn = $( rows[ix] ).find("p.btn"); - if( btn.length == 0) - break; - classList = $( btn ).attr("class").split(" "); - if (classList[0] != matchClass) - break; - endIndex = ix; - } - return rows.slice(startIndex,endIndex+1); // endIndex is 1 based! - } + showCenterLabel: function (tr, show) + { // Will show or hide centerlabel as requested + // adjust button, sideLabel height, sideLabelOffset and centerlabel display - function imgTblCompositeSet(row) - { // Returns the set of rows that are of the same class and contiguous - if(row == null) - return null; - var rowId = $(row).attr('id').substring('tr_'.length); - var rec = hgTracks.trackDb[rowId]; - if (tdbIsSubtrack(rec) == false) - return null; + if (!$(tr).hasClass('clOpt')) + return; + var center = $(tr).find(".sliceDiv.cntrLab"); + if($(center) == undefined) + return; + seen = ($(center).css('display') != 'none'); + if(show == seen) + return; - var rows = $('tr.trDraggable:has(p.' + rec.parentTrack+')'); - return rows; - } + var centerHeight = $(center).height(); + + var btn = $(tr).find("p.btn"); + var side = $(tr).find(".sliceDiv.sideLab"); + if($(btn) != undefined && $(side) != undefined) { + var sideImg = $(side).find("img"); + if($(sideImg) != undefined) { + var top = parseInt($(sideImg).css('top')); + if(show) { + $(btn).css('height',$(btn).height() + centerHeight); + $(side).css('height',$(side).height() + centerHeight); + top += centerHeight; // top is a negative number + $(sideImg).css( {'top': top.toString() + "px" }); + $( center ).show(); + } else if (!show) { + $(btn).css('height',$(btn).height() - centerHeight); + $(side).css('height',$(side).height() - centerHeight); + top -= centerHeight; // top is a negative number + $(sideImg).css( {'top': top.toString() + "px" }); + $( center ).hide(); + } + } + } + }, - function imgTblZipButtons(table) - { - // Goes through the image and binds composite track buttons when adjacent - var rows = $(table).find('tr'); - var lastClass=""; - var lastBtn; - var lastMatchesLast=false; - var lastBlue=true; - var altColors=false; - var count=0; - var countN=0; - for(var ix=0;ix<rows.length;ix++) { // Need to have buttons in order - var btn = $( rows[ix] ).find("p.btn"); - if( btn.length == 0) - continue; + getContiguousRowSet: function (row) + { // Returns the set of rows that are of the same class and contiguous + if(row == null) + return null; + var btn = $( row ).find("p.btn"); + if (btn.length == 0) + return null; var classList = $( btn ).attr("class").split(" "); - var curMatchesLast=(classList[0] == lastClass); - - // centerLabels may be conditionally seen - if($( rows[ix] ).hasClass('clOpt')) { - if(curMatchesLast && $( rows[ix - 1] ).hasClass('clOpt')) - imgTblTrackShowCenterLabel(rows[ix],false); // if same composite and previous is also centerLabel optional then hide center label - else - imgTblTrackShowCenterLabel(rows[ix],true); + var matchClass = classList[0]; + var table = $(row).parents('table#imgTbl')[0]; + var rows = $(table).find('tr'); + + // Find start index + var startIndex = $(row).attr('rowIndex'); + var endIndex = startIndex; + for(var ix=startIndex-1;ix>=0;ix--) { + btn = $( rows[ix] ).find("p.btn"); + if (btn.length == 0) + break; + classList = $( btn ).attr("class").split(" "); + if (classList[0] != matchClass) + break; + startIndex = ix; } - // On with buttons + // Find end index + for(var ix=endIndex;ix<rows.length;ix++) { + btn = $( rows[ix] ).find("p.btn"); + if (btn.length == 0) + break; + classList = $( btn ).attr("class").split(" "); + if (classList[0] != matchClass) + break; + endIndex = ix; + } + return rows.slice(startIndex,endIndex+1); // endIndex is 1 based! + }, + + getCompositeSet: function (row) + { // Returns the set of rows that are of the same class and contiguous + if(row == null) + 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; + var lastMatchesLast=false; + var lastBlue=true; + var altColors=false; + var count=0; + var countN=0; + for(var ix=0;ix<rows.length;ix++) { // Need to have buttons in order + var btn = $( rows[ix] ).find("p.btn"); + if (btn.length == 0) + continue; + var classList = $( btn ).attr("class").split(" "); + var curMatchesLast=(classList[0] == lastClass); + + // centerLabels may be conditionally seen + if($( rows[ix] ).hasClass('clOpt')) { + if(curMatchesLast && $( rows[ix - 1] ).hasClass('clOpt')) + dragReorder.showCenterLabel(rows[ix],false); // if same composite and previous is also centerLabel optional then hide center label + else + dragReorder.showCenterLabel(rows[ix],true); + } + + // On with buttons + if(lastBtn != undefined) { + $( lastBtn ).removeClass('btnN btnU btnL btnD'); + if(curMatchesLast && lastMatchesLast) { + $( lastBtn ).addClass('btnL'); + } else if(lastMatchesLast) { + $( lastBtn ).addClass('btnU'); + } else if(curMatchesLast) { + $( lastBtn ).addClass('btnD'); + } else { + $( lastBtn ).addClass('btnN'); + countN++; + } + count++; + if(altColors) { + lastBlue = (lastMatchesLast == lastBlue); // lastMatch and lastBlue or not lastMatch and notLastBlue + if(lastBlue) // Too smart by 1/3rd + $( lastBtn ).addClass( 'btnBlue' ); + else + $( lastBtn ).removeClass( 'btnBlue' ); + } + } + lastMatchesLast = curMatchesLast; + lastClass = classList[0]; + lastBtn = btn; + } if(lastBtn != undefined) { $( lastBtn ).removeClass('btnN btnU btnL btnD'); - if(curMatchesLast && lastMatchLast) { - $( lastBtn ).addClass('btnL'); - } else if(lastMatchLast) { - $( lastBtn ).addClass('btnU'); - } else if(curMatchesLast) { - $( lastBtn ).addClass('btnD'); + if(lastMatchesLast) { + $( btn).addClass('btnU'); } else { $( lastBtn ).addClass('btnN'); countN++; } - count++; if(altColors) { - lastBlue = (lastMatchLast == lastBlue); // lastMatch and lastBlue or not lastMatch and notLastBlue - if(lastBlue) // Too smart by 1/3rd - $( lastBtn ).addClass( 'btnBlue' ); - else - $( lastBtn ).removeClass( 'btnBlue' ); + lastBlue = (lastMatchesLast == lastBlue); // lastMatch and lastBlue or not lastMatch and notLastBlue + if(lastBlue) // Too smart by 1/3rd + $( lastBtn ).addClass( 'btnBlue' ); + else + $( lastBtn ).removeClass( 'btnBlue' ); } + count++; } - lastMatchLast = curMatchesLast; - lastClass = classList[0]; - lastBtn = btn; - } - if(lastBtn != undefined) { - $( lastBtn ).removeClass('btnN btnU btnL btnD'); - if(lastMatchLast) { - $( btn).addClass('btnU'); - } else { - $( lastBtn ).addClass('btnN'); - countN++; + //warn("Zipped "+count+" buttons "+countN+" are independent."); + }, + + dragHandleMouseOver: function () + { // Highlights a single row when mouse over a dragHandle column (sideLabel and buttons) + if(jQuery.tableDnD == undefined) { + //var handle = $("td.dragHandle"); + //$(handle) + // .unbind('mouseenter')//, jQuery.tableDnD.mousemove); + // .unbind('mouseleave');//, jQuery.tableDnD.mouseup); + return; } - if(altColors) { - lastBlue = (lastMatchLast == lastBlue); // lastMatch and lastBlue or not lastMatch and notLastBlue - if(lastBlue) // Too smart by 1/3rd - $( lastBtn ).addClass( 'btnBlue' ); - else - $( lastBtn ).removeClass( 'btnBlue' ); + if (jQuery.tableDnD.dragObject == null) { + $( this ).parents("tr.trDraggable").addClass("trDrag"); + } + }, + + dragHandleMouseOut: function () + { // Ends row highlighting by mouse over + $( this ).parents("tr.trDraggable").removeClass("trDrag"); + }, + + buttonMouseOver: function () + { // Highlights a composite set of buttons, regarless of whether tracks are adjacent + if(jQuery.tableDnD == undefined || jQuery.tableDnD.dragObject == null) { + var classList = $( this ).attr("class").split(" "); + var btns = $( "p." + classList[0] ); + $( btns ).removeClass('btnGrey'); + $( btns ).addClass('btnBlue'); + if (jQuery.tableDnD != undefined) { + var rows = dragReorder.getContiguousRowSet($(this).parents('tr.trDraggable')[0]); + if (rows) + $( rows ).addClass("trDrag"); + } } - count++; - } - //warn("Zipped "+count+" buttons "+countN+" are independent."); - } - - function initImgTblButtons() - { - // Make side buttons visible (must also be called when updating rows in the imgTbl). - var btns = $("p.btn"); - if(btns.length > 0) { - imgTblZipButtons($('#imgTbl')); - $(btns).mouseenter( imgTblButtonMouseOver ); - $(btns).mouseleave( imgTblButtonMouseOut ); - $(btns).show(); - } - var handle = $("td.dragHandle"); - if(handle.length > 0) { - $(handle).mouseenter( imgTblDragHandleMouseOver ); - $(handle).mouseleave( imgTblDragHandleMouseOut ); - } - - // setup mouse callbacks for the area tags - $(".area").each( function(t) { - this.onmouseover = mapItemMouseOver; - this.onmouseout = mapItemMouseOut; - this.onclick = mapClk; - }); - } - - function imgTblDragHandleMouseOver() - { - // Highlights a single row when mouse over a dragHandle column (sideLabel and buttons) - if(jQuery.tableDnD == undefined) { - //var handle = $("td.dragHandle"); - //$(handle) - // .unbind('mouseenter')//, jQuery.tableDnD.mousemove); - // .unbind('mouseleave');//, jQuery.tableDnD.mouseup); - return; - } - if (jQuery.tableDnD.dragObject == null) { - $( this ).parents("tr.trDraggable").addClass("trDrag"); - } - } + }, - function imgTblDragHandleMouseOut() - { - // Ends row highlighting by mouse over - $( this ).parents("tr.trDraggable").removeClass("trDrag"); - } - - function imgTblButtonMouseOver() - { - // Highlights a composite set of buttons, regarless of whether tracks are adjacent - if(jQuery.tableDnD == undefined || jQuery.tableDnD.dragObject == null) { + buttonMouseOut: function () + { // Ends composite highlighting by mouse over var classList = $( this ).attr("class").split(" "); var btns = $( "p." + classList[0] ); - $( btns ).removeClass('btnGrey'); - $( btns ).addClass('btnBlue'); + $( btns ).removeClass('btnBlue'); + $( btns ).addClass('btnGrey'); if (jQuery.tableDnD != undefined) { - var rows = imgTblContiguousRowSet($(this).parents('tr.trDraggable')[0]); + var rows = dragReorder.getContiguousRowSet($(this).parents('tr.trDraggable')[0]); if (rows) - $( rows ).addClass("trDrag"); + $( rows ).removeClass("trDrag"); } - } - } + }, - function imgTblButtonMouseOut() - { - // Ends composite highlighting by mouse over - var classList = $( this ).attr("class").split(" "); - var btns = $( "p." + classList[0] ); - $( btns ).removeClass('btnBlue'); - $( btns ).addClass('btnGrey'); - if (jQuery.tableDnD != undefined) { - var rows = imgTblContiguousRowSet($(this).parents('tr.trDraggable')[0]); - if (rows) - $( rows ).removeClass("trDrag"); + trMouseOver: function (e) // LARRY: is this the right thing? + { + // Trying to make sure there is always a imageV2.lastTrack so that we know where we are + var id = ''; + var a = /tr_(.*)/.exec($(this).attr('id')); // voodoo + if(a && a[1]) { + id = a[1]; + } + if (id.length > 0) { + if (imageV2.lastTrack == null || imageV2.lastTrack.id != id) + imageV2.lastTrack = rightClick.makeMapItem(id); // currentMapItem gets set by mapItemMapOver + } // This is just backup + }, + + mapItemMouseOver: function () + { + // Record data for current map area item + id = this.id; + if (id == undefined || id == null || id.length == 0) { + id = ''; + var tr = $( this ).parents('tr.imgOrd'); + if ( $(tr).length == 1 ) { + var a = /tr_(.*)/.exec($(tr).attr('id')); // voodoo + if(a && a[1]) { + id = a[1]; + } + } + } + if (id.length > 0) { + rightClick.currentMapItem = rightClick.makeMapItem(id); + if(rightClick.currentMapItem != null) { + rightClick.currentMapItem.href = this.href; + rightClick.currentMapItem.title = this.title; + } + } + }, + + mapItemMouseOut: function () + { + imageV2.lastTrack = rightClick.currentMapItem; // Just a backup + rightClick.currentMapItem = null; + }, + + init: function () + { // Make side buttons visible (must also be called when updating rows in the imgTbl). + var btns = $("p.btn"); + if(btns.length > 0) { + dragReorder.zipButtons($('#imgTbl')); + $(btns).mouseenter( dragReorder.buttonMouseOver ); + $(btns).mouseleave( dragReorder.buttonMouseOut ); + $(btns).show(); + } + var handle = $("td.dragHandle"); + if(handle.length > 0) { + $(handle).mouseenter( dragReorder.dragHandleMouseOver ); + $(handle).mouseleave( dragReorder.dragHandleMouseOut ); + } + + // setup mouse callbacks for the area tags + $(imageV2.imgTbl).find("tr").mouseover( dragReorder.trMouseOver ); + + $(".area").each( function(t) { + this.onmouseover = dragReorder.mapItemMouseOver; + this.onmouseout = dragReorder.mapItemMouseOut; + this.onclick = posting.mapClk; + }); } } - ///////////////////////////////////////////////////// - // Drag Scroll code + ////////////////////////// + //// Drag Scroll code //// + ////////////////////////// jQuery.fn.panImages = function(){ // globals across all panImages - originalPosition = getOriginalPosition(); + genomePos.original = genomePos.getOriginalPos(); 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; 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(); // 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 $(this).css('cursor',""); // normal pointer when not over clickable item //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(contextMenu) { - contextMenu.hide(); + if(rightClick.menu) { + rightClick.menu.hide(); } mouseIsDown = true; mouseDownX = e.clientX; 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 (!e) e = window.event; if ( mouseIsDown ) { var relativeX = (e.clientX - mouseDownX); if(relativeX != 0) { - if (blockUseMap == false) { + if (posting.mapClicksAllowed()) { // need to throw up a z-index div. Wait mask? - savedPosition = getPosition(); + savedPosition = genomePos.get(); dragMaskShow(); - blockUseMap = true; + posting.blockMapClicks(); } var decelerator = 1; //var wingSize = 1000; // 0 stops the scroll at the edges. // Remeber that offsetX (prevX) is negative newX = prevX + relativeX; if ( newX >= leftLimit ) { // scrolled all the way to the left if(atEdge) { // Do not drag straight off edge. Force second drag beyondImage = true; newX = leftLimit + (newX - leftLimit)/decelerator;// slower //if (newX >= leftLimit + wingSize) // Don't go too far over the edge! // newX = leftLimit + wingSize; } else newX = leftLimit; } else if ( newX < rightLimit ) { // scrolled all the way to the right 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 } ); 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 (!e) e = window.event; if(mouseIsDown) { dragMaskClear(); $(document).unbind('mousemove',panner); $(document).unbind('mouseup',panMouseUp); mouseIsDown = false; - setTimeout('blockUseMap=false;',50); // Necessary incase the selectEnd was over a map item. select takes precedence. + 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) - setPosition(savedPosition,null); + genomePos.set(savedPosition,null); var oldPos = prevX.toString() + "px"; $(".panImg").css( {'left': oldPos }); $('.tdData').css( {'backgroundPosition': oldPos } ); return true; } // Do we need to fetch anything? if(beyondImage) { - if(inPlaceUpdate) { - var pos = parsePosition(getPosition()); - navigateInPlace("position=" + encodeURIComponent(pos.chrom + ":" + pos.start + "-" + pos.end), null, true); + 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. } // Just a normal scroll within a >1X image if(prevX != newX) { prevX = newX; if (!only1xScrolling) { //panAdjustHeight(newX); // NOTE: This will resize image after scrolling. Do we want to while scrolling? // This is important, since AJAX could lead to reinit after this within bounds scroll hgTracks.imgBoxPortalOffsetX = (prevX * -1) - hgTracks.imgBoxLeftLabel; hgTracks.imgBoxPortalLeft = newX.toString() + "px"; } } } } }); // end of this.each(function(){ function panUpdatePosition(newOffsetX,bounded) { // Updates the 'position/search" display with change due to panning var closedPortalStart = hgTracks.imgBoxPortalStart + 1; // Correction for half open portal coords var portalWidthBases = hgTracks.imgBoxPortalEnd - closedPortalStart; - var portalScrolledX = (hgTracks.imgBoxPortalOffsetX+hgTracks.imgBoxLeftLabel) + newOffsetX; + var portalScrolledX = hgTracks.imgBoxPortalOffsetX+hgTracks.imgBoxLeftLabel + newOffsetX; var recalculate = false; var newPortalStart = 0; if (hgTracks.revCmplDisp) - newPortalStart = closedPortalStart + Math.round(portalScrolledX*hgTracks.imgBoxBasesPerPixel); // As offset goes down, so do bases seen. + newPortalStart = closedPortalStart + + Math.round(portalScrolledX*hgTracks.imgBoxBasesPerPixel); // As offset goes down, so do bases seen. else - newPortalStart = closedPortalStart - Math.round(portalScrolledX*hgTracks.imgBoxBasesPerPixel); // As offset goes down, bases seen goes up! - if( newPortalStart < hgTracks.chromStart && bounded) { // Stay within bounds + newPortalStart = closedPortalStart - + Math.round(portalScrolledX*hgTracks.imgBoxBasesPerPixel); // As offset goes down, bases seen goes up! + if (newPortalStart < hgTracks.chromStart && bounded) { // Stay within bounds newPortalStart = hgTracks.chromStart; recalculate = true; } var newPortalEnd = newPortalStart + portalWidthBases; - if( newPortalEnd > hgTracks.chromEnd && bounded) { + if (newPortalEnd > hgTracks.chromEnd && bounded) { newPortalEnd = hgTracks.chromEnd; newPortalStart = newPortalEnd - portalWidthBases; recalculate = true; } if(newPortalStart > 0) { - var newPos = hgTracks.chromName + ":" + commify(newPortalStart) + "-" + commify(newPortalEnd); - setPosition(newPos, 0); // 0 means no need to change the size + var newPos = hgTracks.chromName + ":" + + commify(newPortalStart) + "-" + commify(newPortalEnd); + genomePos.set(newPos, 0); // 0 means 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) { var loc = this.coords.split(","); var aleft = parseInt(loc[0]); var aright = parseInt(loc[2]); if(aleft < west && aright >= east) { var atop = parseInt(loc[1]); var abottom = parseInt(loc[3]); - if( mapPortal.top < 0 ) { + if (mapPortal.top < 0 ) { mapPortal.top = atop; mapPortal.bottom = abottom; } else if(mapPortal.top > atop) { mapPortal.top = atop; } else if(mapPortal.bottom < abottom) { mapPortal.bottom = abottom; } } }); } return mapPortal; } function panAdjustHeight(newOffsetX) { // Adjust the height of the track data images so that bed items scrolled off screen // do not waste vertical real estate // Is the > 1x? if (only1xScrolling) return; var east = newOffsetX * -1; var west = east + portalWidth; $(".panImg").each(function(t) { var mapid = this.id.replace('img_','map_'); var hDiv = $(this).parent(); var north = parseInt($(this).css("top")) * -1; var south = north + $(hDiv).height(); var mapPortal = mapTopAndBottom(mapid,east,west); if(mapPortal.top > 0) { var topdif = Math.abs(mapPortal.top - north); var botdif = Math.abs(mapPortal.bottom - south); if(topdif > 2 || botdif > 2) { $(hDiv).height( mapPortal.bottom - mapPortal.top ); north = mapPortal.top * -1; $(this).css( {'top': north.toString() + "px" }); // Need to adjust side label height as well! var imgId = this.id.split("_"); var titlePx = 0; var center = $("#img_center_"+imgId[2]); if(center.length > 0) { titlePx = $(center).parent().height(); north += titlePx; } var side = $("#img_side_"+imgId[2]); - if( side.length > 0) { + if (side.length > 0) { $(side).parent().height( mapPortal.bottom - mapPortal.top + titlePx); $(side).css( {'top': north.toString() + "px" }); } var btn = $("#p_btn_"+imgId[2]); - if( btn.length > 0) { + if (btn.length > 0) { $(btn).height( mapPortal.bottom - mapPortal.top + titlePx); } else { btn = $("#img_btn_"+imgId[2]); - if( btn.length > 0) { + if (btn.length > 0) { $(btn).parent().height( mapPortal.bottom - mapPortal.top + titlePx); $(btn).css( {'top': top.toString() + "px" }); } } } } }); dragMaskResize(); // Resizes the dragMask to match current image size } function dragMaskShow() { // Sets up the dragMask to show grabbing cursor within image and not allowed north and south of image var imgTbl = $('#imgTbl'); // Find or create the waitMask (which masks the whole page) - var dragMask = $('div#dragMask'); - if( dragMask == undefined || dragMask.length == 0) { + var dragMask = $('div#dragMask'); + if (dragMask == undefined || dragMask.length == 0) { $("body").prepend("<div id='dragMask' class='waitMask'></div>"); dragMask = $('div#dragMask'); } $('body').css('cursor','not-allowed'); $(dragMask).css('cursor',"url(../images/grabbing.cur),w-resize"); - $(dragMask).css({opacity:0.0,display:'block',top: $(imgTbl).position().top.toString() + 'px', height: $(imgTbl).height().toString() + 'px' }); + $(dragMask).css({opacity:0.0,display:'block', + top: $(imgTbl).position().top.toString() + 'px', + height: $(imgTbl).height().toString() + 'px' }); //$(dragMask).css({opacity:0.4,backgroundColor:'gray',zIndex:999}); // temporarily so I can see it } function dragMaskResize() { // Resizes dragMask (called when image is dynamically resized in >1x scrolling) var imgTbl = $('#imgTbl'); // Find or create the waitMask (which masks the whole page) - var dragMask = $('div#dragMask'); - if( dragMask != undefined && dragMask.length >= 1) { + 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 ) + if (dragMask != undefined ) $(dragMask).hide(); } }; - ///////////////////////////////////////////////////// - - function saveMouseOffset(ev) - { // Save the mouse offset associated with this event - originalMouseOffset = {x: ev.clientX, y: ev.clientY}; - } - - function mouseHasMoved(ev) - { // return true if mouse has moved a significant amount - var minPixels = 10; - var movedX = ev.clientX - originalMouseOffset.x; - var movedY = ev.clientY - originalMouseOffset.y; - if ( arguments.length == 2) { - var num = Number(arguments[1]); - if(isNaN(num)) { - if ( arguments[1].toLowerCase() == "x" ) - return (movedX > minPixels || movedX < (minPixels * -1)); - if ( arguments[1].toLowerCase() == "y" ) - return (movedY > minPixels || movedY < (minPixels * -1)); - } - else - minPixels = num; - } - return ( movedX > minPixels || movedX < (minPixels * -1) - || movedY > minPixels || movedY < (minPixels * -1)); - } - - function blockTheMap(e) - { - blockUseMap=true; - } - - function blockTheMapOnMouseMove(ev) - { - if (!blockUseMap && mouseHasMoved(ev)) { - blockUseMap=true; - } - } - - // wait for jStore to prepare the storage engine (this token reload code is experimental and currently dead). - jQuery.jStore && jQuery.jStore.ready(function(engine) { - // alert(engine.jri); - // wait for the storage engine to be ready. - engine.ready(function(){ - var engine = this; - var newToken = hgTracks.time; - if(newToken) { - var oldToken = engine.get("token"); - if(oldToken && oldToken == newToken) { - // user has hit the back button. - jQuery('body').css('cursor', 'wait'); - window.location = "../cgi-bin/hgTracks?hgsid=" + getHgsid(); - } - } - engine.set("token", newToken); - }); - }); - - function mapClk() - { - return postToSaveSettings(this); - } - - function postToSaveSettings(obj) - { - if(blockUseMap==true) { - return false; - } - if(obj == undefined || obj.href == undefined) // called directly with obj and from callback without obj - obj = this; - if( obj.href.match('#') || obj.target.length > 0) { - //alert("Matched # ["+obj.href+"] or has target:"+obj.target); - return true; - } - var thisForm=$(obj).parents('form'); - if(thisForm == undefined || $(thisForm).length == 0) - thisForm=$("FORM"); - if($(thisForm).length > 1 ) - thisForm=$(thisForm)[0]; - if(thisForm != undefined && $(thisForm).length == 1) { - //alert("posting form:"+$(thisForm).attr('name')); - return postTheForm($(thisForm).attr('name'),obj.href); - } - return true; - } - - function loadRemoteTracks() - { - if(typeof(hgTracks.trackDb) != "undefined" && hgTracks.trackDb != null) { - for (var id in hgTracks.trackDb) { + /////////////////////////////////////// + //// 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). + + makeMapItem: function (id) + { // Create a dummy mapItem on the fly (for objects that don't have corresponding entry in the map). + if(id && id.length > 0 + && typeof(hgTracks.trackDb) != "undefined" && hgTracks.trackDb != null) { + var title; var rec = hgTracks.trackDb[id]; - if(rec.type == "remote") { - if($("#img_data_" + id).length > 0) { - // load the remote track renderer via jsonp - rec.loadingId = showLoadingImage("tr_" + id); - var script = document.createElement('script'); - var pos = parsePosition(getPosition()); - var name = rec.remoteTrack || id; - script.setAttribute('src', rec.url + "?track=" + name + "&jsonp=remoteTrackCallback&position=" + - encodeURIComponent(pos.chrom + ":" + pos.start + "-" + pos.end) + - "&pix=" + $('#imgTbl').width()); - document.getElementsByTagName('head')[0].appendChild(script); - } - } - } - } - } - - $(document).ready(function() - { - // The page may be reached via browser history (back button) - // If so, then this code should detect if the image has been changed via js/ajax - // and will reload the image if necessary. - // NOTE: this is needed for IE but other browsers can detect the dirty page much earlier - if (isDirtyPage()) { - // mark as non dirty to avoid infinite loop in chrome. - $('#dirty').val('false'); - jQuery('body').css('cursor', 'wait'); - window.location = "../cgi-bin/hgTracks?hgsid=" + getHgsid(); - return false; - } - initVars(); - var db = getDb(); - if(jQuery.fn.autocomplete && $('input#suggest') && db) { - if(jQuery.fn.Watermark) { - $('#suggest').Watermark("gene"); - } - $('input#suggest').autocomplete({ - delay: 500, - minLength: 2, - source: ajaxGet(function () {return getDb();}, new Object), - open: function(event, ui) { - var pos = $(this).offset().top + $(this).height(); - if (!isNaN(pos)) { - var maxHeight = $(window).height() - pos - 30; // take off a little more because IE needs it - var auto = $('.ui-autocomplete'); - var curHeight = $(auto).children().length * 21; - if (curHeight > maxHeight) - $(auto).css({maxHeight: maxHeight+'px',overflow:'scroll'}); - else - $(auto).css({maxHeight: 'none',overflow:'hidden'}); - } - }, - select: function (event, ui) { - setPosition(ui.item.id, commify(getSizeFromCoordinates(ui.item.id))); - makeSureSuggestTrackIsVisible(); - // jQuery('body').css('cursor', 'wait'); - // document.TrackHeaderForm.submit(); - } - }); - - // I want to set focus to the suggest element, but unforunately that prevents PgUp/PgDn from - // working, which is a major annoyance. - // $('input#suggest').focus(); - } - - if(jQuery.jStore) { - // Experimental code to handle "user hits back button" problem by reloading the page based on the user's cart - if(jQuery.browser.msie && jQuery.browser.version < 8) { - // IE 7 requires flash to support jStore. - jQuery.extend(jQuery.jStore.defaults, { - project: 'hgTracks', - engine: 'flash', - flash: '/jStore.Flash.html' - }); - } - jQuery.jStore.load(); - } - - - // Convert map AREA gets to post the form, ensuring that cart variables are kept up to date (but turn this off for search form). - if($("FORM").length > 0 && $('#trackSearch').length == 0) { - var allLinks = $('a'); - $( allLinks ).unbind('click'); - $( allLinks ).click( postToSaveSettings ); - } - if($('#pdfLink').length == 1) { - $('#pdfLink').click(function(i) { - var thisForm=$('#TrackForm'); - if(thisForm != undefined && $(thisForm).length == 1) { - //alert("posting form:"+$(thisForm).attr('name')); - updateOrMakeNamedVariable($(thisForm),'hgt.psOutput','on'); - return postTheForm($(thisForm).attr('name'),this.href); + if(rec) { + title = rec.shortLabel; + } else { + title = id; } - return true; - }); - } - if($('#imgTbl').length == 1) { - imageV2 = true; - initImgTblButtons(); - // Make imgTbl allow draw reorder of imgTrack rows - var imgTable = $(".tableWithDragAndDrop"); - if($(imgTable).length > 0) { - $(imgTable).tableDnD({ - onDragClass: "trDrag", - dragHandle: "dragHandle", - scrollAmount: 40, - onDragStart: function(ev, table, row) { - saveMouseOffset(ev); - $(document).bind('mousemove',blockTheMapOnMouseMove); - - // Can drag a contiguous set of rows if dragging blue button - table.tableDnDConfig.dragObjects = [ row ]; // defaults to just the one - var btn = $( row ).find('p.btnBlue'); // btnBlue means cursor over left button - if (btn.length == 1) { - table.tableDnDConfig.dragObjects = imgTblContiguousRowSet(row); - var compositeSet = imgTblCompositeSet(row); - if (compositeSet && compositeSet.length > 0) - $( compositeSet ).find('p.btn').addClass('blueButtons'); // blue persists - } - }, - onDrop: function(table, row, dragStartIndex) { - var compositeSet = imgTblCompositeSet(row); - if (compositeSet && compositeSet.length > 0) - $( compositeSet ).find('p.btn').removeClass('blueButtons'); // blue persists - if($(row).attr('rowIndex') != dragStartIndex) { - // NOTE Even if dragging a contiguous set of rows, - // still only need to check the one under the cursor. - if(imgTblSetOrder) { - imgTblSetOrder(table); - } - imgTblZipButtons( table ); - } - $(document).unbind('mousemove',blockTheMapOnMouseMove); - setTimeout('blockUseMap=false;',50); // Necessary incase the onDrop was over a map item. onDrop takes precedence. - } - }); - } - if(hgTracks.imgBoxPortal) { - // Turn on drag scrolling. - $("div.scroller").panImages(); - } - //$("#zoomSlider").slider({ min: -4, max: 3, step: 1 });//, handle: '.ui-slider-handle' }); - - // Temporary warning while new imageV2 code is being worked through - if($('#map').children("AREA").length > 0) { - warn('Using imageV2, but old map is not empty!'); - } - - // 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); - updateTrackImg(trackName,"",""); - }); - } - loadRemoteTracks(); - } - if($('img#chrom').length == 1) { - if($('area.cytoBand').length > 1) { - $('img#chrom').chromDrag(); - } - } - - if($("#tabs").length > 0) { - // Search page specific code - - var val = $('#currentTab').val(); - $("#tabs").tabs({ - show: function(event, ui) { - $('#currentTab').val(ui.panel.id); - }, - select: function(event, ui) { findTracksSwitchTabs(ui); } - }); - $('#tabs').show(); - $("#tabs").tabs('option', 'selected', '#' + val); - if(val =='simpleTab' && $('div#found').length < 1) { - $('input#simpleSearch').focus(); - } - $("#tabs").css('font-family', jQuery('body').css('font-family')); - $("#tabs").css('font-size', jQuery('body').css('font-size')); - $('.submitOnEnter').keydown(searchKeydown); - findTracksNormalize(); - updateMetaDataHelpLinks(0); - } - }); - - function rulerModeToggle (ele) - { - autoHideSetting = !ele.checked; - var obj = imgAreaSelect.data('imgAreaSelect'); - obj.setOptions({autoHide : autoHideSetting}); - } - - function makeMapItem(id) - { - // Create a dummy mapItem on the fly (for objects that don't have corresponding entry in the map). - if(id && id.length > 0 - && typeof(hgTracks.trackDb) != "undefined" && hgTracks.trackDb != null) { - var title; - var rec = hgTracks.trackDb[id]; - if(rec) { - title = rec.shortLabel; + return {id: id, title: "configure " + title}; } else { - title = id; + return null; } - return {id: id, title: "configure " + title}; - } else { - return null; - } - } + }, - function mapItemMouseOver() - { - // Record data for current map area item - currentMapItem = makeMapItem(this.id); - if(currentMapItem != null) { - currentMapItem.href = this.href; - currentMapItem.title = this.title; - } - } - - function mapItemMouseOut(obj) - { - lastMapItem = currentMapItem; - currentMapItem = null; - } + findMapItem: function (e) + { // Find mapItem for given event; returns item object or null if none found. - function findMapItem(e) - { - // Find mapItem for given event; returns item object or null if none found. - - if(currentMapItem) { - return currentMapItem; - } + if(rightClick.currentMapItem) { + return rightClick.currentMapItem; + } - // rightClick for non-map items that can be resolved to their parent tr and then trackName (e.g. items in gray bar) - if(e.target.tagName.toUpperCase() != "AREA") { + // rightClick for non-map items that can be resolved to their parent tr and then trackName (e.g. items in gray bar) var tr = $( e.target ).parents('tr.imgOrd'); - if( $(tr).length == 1 ) { + if ( $(tr).length == 1 ) { var a = /tr_(.*)/.exec($(tr).attr('id')); // voodoo if(a && a[1]) { var id = a[1]; - return makeMapItem(id); + return rightClick.makeMapItem(id); } } - } + warn("ASSERT: findMapItem failed.");// Should not ever get to this point + return null; + }, - // FIXME: do we really need to worry about non-imageV2 ? - // Yeah, I think the rest of this is (hopefully) dead code - - var x,y; - if(imageV2) { - // It IS appropriate to use coordinates relative to the img WHEN we have a hit in the right-hand side, but NOT - // when we have a hit in the left hand elements (which do not have relative coordinates). - // XXXX still trying to figure this out. - var pos = $(e.target).position(); - if(e.target.tagName == "IMG") { - // msie - // warn("img: x: " + x + ", y:" + y); - // warn("pageX: " + e.pageX + "; offsetLeft: " + pos.left); - x = e.pageX - pos.left; - y = e.pageY - pos.top; - // warn("x: " + x + "; y: " + y); - } else { - x = e.pageX - trackImg.attr("offsetLeft"); - y = e.pageY - trackImg.attr("offsetTop"); - } - // console.log(trackImg.attr("offsetLeft"), trackImg.attr("offsetTop")); - // console.log("findMapItem:", x, y); - // console.dir(mapItems); - } else { - x = e.pageX - e.target.offsetLeft; - y = e.pageY - e.target.offsetTop; - } + windowOpenFailedMsg: function () + { + warn("Your web browser prevented us from opening a new window.\n\nPlease change your browser settings to allow pop-up windows from " + document.domain + "."); + }, - var retval = -1; - // console.log(e.target.tagName + "; " + e.target.id); - for(var i=0;i<mapItems.length;i++) + handleZoomCodon: function (response, status) { - if(mapItems[i].obj && e.target === mapItems[i].obj) { - // e.target is AREA tag under FF and Safari; - // This never occurs under IE - // console.log("Found match by objects comparison"); - retval = i; - break; - } else if (!imageV2 || browser == "msie") { - // Under IE, target is the IMG tag for the map's img and y is relative to the top of that, - // so we must use the target's src to make sure we are looking at the right map. - // We start falling through to here under safari under imageV2 once something has been modified - // (that's a bug I still haven't figured out how to fix). - if(mapItems[i].r.contains(x, y) && mapItems[i].src == $(e.target).attr('src')) { - retval = i; - break; - } + var json = eval("(" + response + ")"); + if(json.pos) { + genomePos.set(json.pos, 3); + if(document.TrackForm) + document.TrackForm.submit(); + else + document.TrackHeaderForm.submit(); + } else { + alert(json.error); } - } - // showWarning(x + " " + y + " " + retval + " " + e.target.tagName + " " + $(e.target).attr('src')); - // console.log("findMapItem:", e.clientX, e.clientY, x, y, pos.left, pos.top, retval, mapItems.length, e.target.tagName); - // console.log(e.clientX, pos); - if(retval >= 0) { - return mapItems[retval]; - } else { - return null; - } - } + }, - function mapEvent(e) - { - var o = findMapItem(e); - if(o) { - e.target.title = o.title; - } else { - // XXXX this doesn't work. - // $('#myMenu').html("<ul id='myMenu' class='contextMenu'><li class='edit'><a href='#img'>Get Image</a></li></ul>"); - e.target.title = originalImgTitle; - } - } + handleViewImg: function (response, status) + { // handles view image response, which must get new image without imageV2 gimmickery + jQuery('body').css('cursor', ''); + var str = "<IMG[^>]*SRC='([^']+)'"; + var reg = new RegExp(str); + a = reg.exec(response); + if(a && a[1]) { + if(window.open(a[1]) == null) { + rightClick.windowOpenFailedMsg(); + } + return; + } + showWarning("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("<div id = 'myPrompt'><div id='dialog' title='Basic dialog'><form>" + + msg + "<input id='myPromptText' value=''></form>"); + $("#myPrompt").dialog({ + modal: true, + closeOnEscape: true, + buttons: { "OK": function() { + var myPromptText = $("#myPromptText").val(); + $(this).dialog("close"); + callback(myPromptText); + } + } + }); + }, - function mapMouseDown(e) - { - // XXXX Is rightclick logic necessary? - var rightclick = e.which ? (e.which == 3) : (e.button == 2); - if(rightclick) + hit: function (menuItemClicked, menuObject, cmd, args) { - return false; - } else { - var o = findMapItem(e); - if(o) { - // XXXX Why does href get changed to "about://" on IE? - window.location = o.href; + 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; + if(menuObject.shown) { + // showWarning("Spinning: menu is still shown"); + setTimeout(function() { rightClick.hitFinish(menuItemClicked, menuObject, cmd); }, 10); + return; } - return true; - } - } - - function contextMenuHit(menuItemClicked, menuObject, cmd, args) - { - setTimeout(function() { contextMenuHitFinish(menuItemClicked, menuObject, cmd, args); }, 1); - } - - function contextMenuHitFinish(menuItemClicked, menuObject, cmd, args) - { - // dispatcher for context menu hits - var id = selectedMenuItem.id; - if(menuObject.shown) { - // showWarning("Spinning: menu is still shown"); - setTimeout(function() { contextMenuHitFinish(menuItemClicked, menuObject, cmd); }, 10); - return; - } - if(cmd == 'selectWholeGene' || cmd == 'getDna') { - // bring whole gene into view or redirect to DNA screen. - var href = 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]) - // XXXX does chromStart have to be incremented by 1? - chromStart = a[1]; - a = /hgg_end=(\d+)/.exec(href); - if(a && a[1]) - chromEnd = 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); - if(a && a[1]) - chromStart = parseInt(a[1]) + 1; - a = /t=(\d+)/.exec(href); - if(a && a[1]) - chromEnd = parseInt(a[1]); - } - if(chrom == null || chromStart == null || chromEnd == null) { - showWarning("couldn't parse out genomic coordinates"); - } else { - if(cmd == 'getDna') - { - if(window.open("../cgi-bin/hgc?g=getDna&i=mixed&c=" + chrom + "&l=" + (chromStart - 1) + "&r=" + chromEnd) == null) { - windowOpenFailedMsg(); - } + if(cmd == 'selectWholeGene' || cmd == 'getDna') { + // 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]) + // XXXX does chromStart have to be incremented by 1? + chromStart = a[1]; + a = /hgg_end=(\d+)/.exec(href); + if(a && a[1]) + chromEnd = 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); + if(a && a[1]) + chromStart = parseInt(a[1]) + 1; + a = /t=(\d+)/.exec(href); + if(a && a[1]) + chromEnd = parseInt(a[1]); + } + if(chrom == null || chromStart == null || chromEnd == null) { + showWarning("couldn't parse out genomic coordinates"); } else { - var newPosition = setPositionByCoordinates(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]; + if(cmd == 'getDna') + { + if(window.open("../cgi-bin/hgc?g=getDna&i=mixed&c=" + + chrom + "&l=" + (chromStart - 1) + "&r=" + chromEnd + ) == null) { + rightClick.windowOpenFailedMsg(); + } } else { - reg = new RegExp("[&?]i=([^&]+)"); - a = reg.exec(href); + 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]; + } + } + if(imageV2.inPlaceUpdate) { + // XXXX This attempt to "update whole track image in place" didn't work for a variety of reasons + // (e.g. safari doesn't parse map when we update on the client side), so this is currently dead code. + // However, this now works in all other browsers, so we may turn this on for non-safari browsers + // (see redmine #4667). + jQuery('body').css('cursor', ''); + var data = "hgt.trackImgOnly=1&hgt.ideogramToo=1&position=" + + newPosition + "&hgsid=" + getHgsid(); + if(name) + data += "&hgFind.matches=" + name; + $.ajax({ + type: "GET", + url: "../cgi-bin/hgTracks", + data: data, + dataType: "html", + trueSuccess: imageV2.updateImgAndMap, + success: catchErrorOrDispatch, + error: errorHandler, + cmd: cmd, + loadingId: showLoadingImage("imgTbl"), + cache: false + }); + } else { + // do a full page refresh to update hgTracks image + jQuery('body').css('cursor', 'wait'); + var ele; + if(document.TrackForm) + ele = document.TrackForm; + else + ele = document.TrackHeaderForm; + if(name) + $(ele).append("<input type='hidden' name='hgFind.matches' " + + "value='" + name + "'>"); + ele.submit(); } - } - if(inPlaceUpdate) { - // XXXX This attempt to "update whole track image in place" didn't work for a variety of reasons - // (e.g. safari doesn't parse map when we update on the client side), so this is currently dead code. - // However, this now works in all other browsers, so we may turn this on for non-safari browsers - // (see redmine #4667). - jQuery('body').css('cursor', ''); - var data = "hgt.trackImgOnly=1&hgt.ideogramToo=1&position=" + newPosition + "&hgsid=" + getHgsid(); - if(name) - data += "&hgFind.matches=" + name; - $.ajax({ - type: "GET", - url: "../cgi-bin/hgTracks", - data: data, - dataType: "html", - trueSuccess: handleUpdateTrackMap, - success: catchErrorOrDispatch, - error: errorHandler, - cmd: cmd, - loadingId: showLoadingImage("imgTbl"), - cache: false - }); - } else { - // do a full page refresh to update hgTracks image - jQuery('body').css('cursor', 'wait'); - var ele; - if(document.TrackForm) - ele = document.TrackForm; - else - ele = document.TrackHeaderForm; - if(name) - $(ele).append("<input type='hidden' name='hgFind.matches' value='" + name + "'>"); - ele.submit(); } } - } - } else if (cmd == 'zoomCodon' || cmd == 'zoomExon') { - var num, ajaxCmd, msg; - if(cmd == 'zoomCodon') { - msg = "Please enter the codon number to jump to:"; - ajaxCmd = 'codonToPos'; - } else { - msg = "Please enter the exon number to jump to:"; - ajaxCmd = 'exonToPos'; - } - myPrompt(msg, function(results) { - $.ajax({ - type: "GET", - url: "../cgi-bin/hgApi", - data: "db=" + getDb() + "&cmd=" + ajaxCmd + "&num=" + results + "&table=" + args.table + "&name=" + args.name, - trueSuccess: handleZoomCodon, - success: catchErrorOrDispatch, - error: errorHandler, - cache: true - }); - }); - } else if (cmd == 'hgTrackUi_popup') { - - hgTrackUiPopUp( selectedMenuItem.id, false ); // Launches the popup but shields the ajax with a waitOnFunction - - } 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_'+ selectedMenuItem.id ).children('a'); // The button already has the ref - if( $(link) != undefined) - url = $(link).attr('href'); - else - url += selectedMenuItem.id; - } - location.assign(url); - - } else if (cmd == 'dragZoomMode') { - autoHideSetting = true; - var obj = imgAreaSelect.data('imgAreaSelect'); - obj.setOptions({autoHide : true, movable: false}); - } else if (cmd == 'hilightMode') { - autoHideSetting = false; - var obj = imgAreaSelect.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). - var data = "hgt.imageV1=1&hgt.trackImgOnly=1&hgsid=" + getHgsid(); - jQuery('body').css('cursor', 'wait'); - $.ajax({ - type: "GET", - url: "../cgi-bin/hgTracks", - data: data, - dataType: "html", - trueSuccess: handleViewImg, - success: catchErrorOrDispatch, - error: errorHandler, - cmd: cmd, - cache: false - }); - } else if (cmd == 'openLink' || cmd == 'followLink') { - var href = selectedMenuItem.href; - var vars = new Array("c", "l", "r", "db"); - var valNames = new Array("chromName", "winStart", "winEnd"); - for (var i in vars) { - // make sure the link contains chrom and window width info (necessary b/c we are stripping hgsid and/or the cart may be empty); - // but don't add chrom to wikiTrack links (see redmine #2476). - var v = vars[i]; - var val; - if(v == "db") { - val = getDb(); + } else if (cmd == 'zoomCodon' || cmd == 'zoomExon') { + var num, ajaxCmd, msg; + if(cmd == 'zoomCodon') { + msg = "Please enter the codon number to jump to:"; + ajaxCmd = 'codonToPos'; } else { - val = hgTracks[valNames[i]]; + msg = "Please enter the exon number to jump to:"; + ajaxCmd = 'exonToPos'; } - if(val && id != "wikiTrack" && (href.indexOf("?" + v + "=") == -1) && (href.indexOf("&" + v + "=") == -1)) { - href = href + "&" + v + "=" + val; - } - } - if(cmd == 'followLink') { - // XXXX This is blocked by Safari's popup blocker (without any warning message). - location.assign(href); - } else { - // Remove hgsid to force a new session (see redmine ticket 1333). - href = removeHgsid(href); - if(window.open(href) == null) { - windowOpenFailedMsg(); + rightClick.myPrompt(msg, function(results) { + $.ajax({ + type: "GET", + url: "../cgi-bin/hgApi", + data: "db=" + getDb() + "&cmd=" + ajaxCmd + "&num=" + results + + "&table=" + args.table + "&name=" + args.name, + trueSuccess: rightClick.handleZoomCodon, + success: catchErrorOrDispatch, + error: errorHandler, + cache: true + }); + }); + } else if (cmd == 'hgTrackUi_popup') { + + popUp.hgTrackUi( rightClick.selectedMenuItem.id, false ); // Launches the popup but shields the ajax with a waitOnFunction + + } 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; } - } - } else if (cmd == 'float') { - if(floatingMenuItem && floatingMenuItem == id) { - $.floatMgr.FOArray = new Array(); - floatingMenuItem = null; - } else { - if(floatingMenuItem) { - // This doesn't work. - $('#img_data_' + floatingMenuItem).parent().restartFloat(); - // This does work - $.floatMgr.FOArray = new Array(); + 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). + var data = "hgt.imageV1=1&hgt.trackImgOnly=1&hgsid=" + getHgsid(); + jQuery('body').css('cursor', 'wait'); + $.ajax({ + type: "GET", + url: "../cgi-bin/hgTracks", + data: data, + dataType: "html", + trueSuccess: rightClick.handleViewImg, + success: catchErrorOrDispatch, + error: errorHandler, + cmd: cmd, + cache: false + }); + } else if (cmd == 'openLink' || cmd == 'followLink') { + var href = rightClick.selectedMenuItem.href; + var vars = new Array("c", "l", "r", "db"); + var valNames = new Array("chromName", "winStart", "winEnd"); + for (var i in vars) { + // make sure the link contains chrom and window width info (necessary b/c we are stripping hgsid and/or the cart may be empty); + // but don't add chrom to wikiTrack links (see redmine #2476). + var v = vars[i]; + var val; + if(v == "db") { + val = getDb(); + } else { + val = hgTracks[valNames[i]]; + } + if (val + && id != "wikiTrack" + && (href.indexOf("?" + v + "=") == -1) + && (href.indexOf("&" + v + "=") == -1)) { + href = href + "&" + v + "=" + val; + } } - floatingMenuItem = id; - reloadFloatingItem(); - updateTrackImg(id, "hgt.transparentImage=0", ""); - } - } else if (cmd == 'hideSet') { - var row = $( 'tr#tr_' + id ); - var rows = imgTblContiguousRowSet(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(cmd == 'followLink') { + // XXXX This is blocked by Safari's popup blocker (without any warning message). + location.assign(href); + } else { + // Remove hgsid to force a new session (see redmine ticket 1333). + href = removeHgsid(href); + if(window.open(href) == null) { + rightClick.windowOpenFailedMsg(); + } } - if (vars.length > 0) { - setCartVars( vars, vals ); - initImgTblButtons(); - loadImgAreaSelect(false); + } else if (cmd == 'float') { + if(rightClick.floatingMenuItem && rightClick.floatingMenuItem == id) { + $.floatMgr.FOArray = new Array(); + rightClick.floatingMenuItem = null; + } else { + if(rightClick.floatingMenuItem) { + // This doesn't work. + $('#img_data_' + rightClick.floatingMenuItem).parent().restartFloat(); + // This does work + $.floatMgr.FOArray = new Array(); + } + rightClick.floatingMenuItem = id; + rightClick.reloadFloatingItem(); + imageV2.requestImgUpdate(id, "hgt.transparentImage=0", ""); } - markAsDirtyPage(); - } - } else if (cmd == 'hideComposite') { - var rec = hgTracks.trackDb[id]; - if (tdbIsSubtrack(rec)) { + } else if (cmd == 'hideSet') { var row = $( 'tr#tr_' + id ); - var rows = imgTblCompositeSet(row); + 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(); } - var selectUpdated = updateVisibility(rec.parentTrack, 'hide'); - setCartVar(rec.parentTrack, 'hide' ); - initImgTblButtons(); - loadImgAreaSelect(false); - markAsDirtyPage(); + if (vars.length > 0) { + setCartVars( vars, vals ); + dragReorder.init(); + dragSelect.load(false); + } + imageV2.markAsDirtyPage(); } - } - //else - // warn('What went wrong?'); - } 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 = updateVisibility(id, cmd); - - // Now change the track image - if(imageV2 && 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' ); - $('#tr_' + id).remove(); - initImgTblButtons(); - loadImgAreaSelect(false); - markAsDirtyPage(); - } else if (!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); - $("<input type='hidden' name='" + id + "'value='" + cmd + "'>").appendTo(form); - document.TrackHeaderForm.submit(); + } 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(); + } } - } else { - var data = "hgt.trackImgOnly=1&" + id + "=" + cmd + "&hgsid=" + getHgsid(); // this will update vis in remote cart - if(imageV2) { - data += "&hgt.trackNameFilter=" + id; + //else + // warn('What went wrong?'); + } 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' ); + $('#tr_' + id).remove(); + dragReorder.init(); + dragSelect.load(false); + imageV2.markAsDirtyPage(); + } 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); + $("<input type='hidden' name='"+id+"'value='"+cmd+"'>").appendTo(form); + document.TrackHeaderForm.submit(); + } + } else { + imageV2.requestImgUpdate(id, id+"="+cmd, ""); + /*var data = "hgt.trackImgOnly=1&" + id + "=" + cmd + "&hgsid=" + getHgsid(); // this will update vis in remote cart + if(imageV2.enabled) { + data += "&hgt.trackNameFilter=" + id; + } + //var center = $("#img_data_" + id); + //center.attr('src', "../images/loading.gif") + //center.attr('style', "text-align: center; display: block;"); + //warn("hgTracks?"+data); // Uesful to cut and paste the url + var loadingId = showLoadingImage("tr_" + id); + $.ajax({ + type: "GET", + url: "../cgi-bin/hgTracks", + data: data, + dataType: "html", + trueSuccess: imageV2.updateImgAndMap, + success: catchErrorOrDispatch, + error: errorHandler, + cmd: cmd, + newVisibility: cmd, + id: id, + loadingId: loadingId, + cache: false + });*/ } - //var center = $("#img_data_" + id); - //center.attr('src', "../images/loading.gif") - //center.attr('style', "text-align: center; display: block;"); - //warn("hgTracks?"+data); // Uesful to cut and paste the url - var loadingId = showLoadingImage("tr_" + id); - $.ajax({ - type: "GET", - url: "../cgi-bin/hgTracks", - data: data, - dataType: "html", - trueSuccess: handleUpdateTrackMap, - success: catchErrorOrDispatch, - error: errorHandler, - cmd: cmd, - newVisibility: cmd, - id: id, - loadingId: loadingId, - cache: false - }); } - } - } + }, - function myPrompt(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("<div id = 'myPrompt'><div id='dialog' title='Basic dialog'><form>" + msg + "<input id='myPromptText' value=''></form>"); - $("#myPrompt").dialog({ - modal: true, - closeOnEscape: true, - buttons: { "OK": function() { - var myPromptText = $("#myPromptText").val(); - $(this).dialog("close"); - callback(myPromptText); - } - } - }); - } + makeHitCallback: function (title) + { // stub to avoid problem with a function closure w/n a loop + return function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, title); return true; + }; + }, - function makeContextMenuHitCallback(title) - { - // stub to avoid problem with a function closure w/n a loop - return function(menuItemClicked, menuObject) { - contextMenuHit(menuItemClicked, menuObject, title); return true; - }; - } + reloadFloatingItem: function () + { // currently dead (experimental code) + if(rightClick.floatingMenuItem) { + $('#img_data_' + rightClick.floatingMenuItem).parent().makeFloat( + {x:"current",y:"current", speed: 'fast', alwaysVisible: true, alwaysTop: true}); + } + }, - function makeImgTag(img) - { - // Return img tag with explicit dimensions for img (dimensions are currently hardwired). - // This fixes the "weird shadow problem when first loading the right-click menu" seen in FireFox 3.X, - // which occurred b/c FF doesn't actually fetch the image until the menu is being shown. - return "<img style='width:16px; height:16px; border-style:none;' src='../images/" + img + "' />"; - } + makeImgTag: function (img) + { // Return img tag with explicit dimensions for img (dimensions are currently hardwired). + // This fixes the "weird shadow problem when first loading the right-click menu" seen in FireFox 3.X, + // which occurred b/c FF doesn't actually fetch the image until the menu is being shown. + return "<img style='width:16px; height:16px; border-style:none;' src='../images/" + + img + "' />"; + }, - function loadContextMenu(img) - { - contextMenu = img.contextMenu( - function() { - popUpBoxCleanup(); // Popup box is not getting closed properly so must do it here - - var menu = []; - var selectedImg = makeImgTag("greenChecksm.png"); - var blankImg = makeImgTag("invisible16.png"); - var done = false; - if(selectedMenuItem && selectedMenuItem.id != null) { - var href = selectedMenuItem.href; - var isHgc, isGene; - if(href) { - isGene = href.match("hgGene"); - isHgc = href.match("hgc"); - } - var id = selectedMenuItem.id; - var rec = hgTracks.trackDb[id]; - var offerHideSubset = false; - var offerHideComposite = false; - var offerSingles = true; - var row = $( 'tr#tr_' + id ); - if (row) { - var btn = $(row).find('p.btnBlue'); // btnBlue means cursor over left button - if (btn.length == 1) { - var compositeSet = imgTblCompositeSet(row); - if (compositeSet && compositeSet.length > 0) { // There is a composite set - offerHideComposite = true; - $( compositeSet ).find('p.btn').addClass('blueButtons'); // blue persists - var subSet = imgTblContiguousRowSet(row); - if (subSet && subSet.length > 1) { - offerSingles = false; - if(subSet.length < compositeSet.length) { - offerHideSubset = true; - $( subSet ).addClass("greenRows"); // green persists + load: function (img) + { + rightClick.menu = img.contextMenu( + function() { + popUp.cleanup(); // Popup box is not getting closed properly so must do it here + + var menu = []; + var selectedImg = rightClick.makeImgTag("greenChecksm.png"); + var blankImg = rightClick.makeImgTag("invisible16.png"); + var done = false; + if(rightClick.selectedMenuItem && rightClick.selectedMenuItem.id != null) { + var href = rightClick.selectedMenuItem.href; + var isHgc, isGene; + if(href) { + isGene = href.match("hgGene"); + isHgc = href.match("hgc"); + } + var id = rightClick.selectedMenuItem.id; + var rec = hgTracks.trackDb[id]; + var offerHideSubset = false; + var offerHideComposite = false; + var offerSingles = true; + var row = $( 'tr#tr_' + id ); + if (row) { + var btn = $(row).find('p.btnBlue'); // btnBlue means cursor over left button + if (btn.length == 1) { + var compositeSet = dragReorder.getCompositeSet(row); + if (compositeSet && compositeSet.length > 0) { // There is a composite set + offerHideComposite = true; + $( compositeSet ).find('p.btn').addClass('blueButtons'); // blue persists + + var subSet = dragReorder.getContiguousRowSet(row); + if (subSet && subSet.length > 1) { + offerSingles = false; + if(subSet.length < compositeSet.length) { + offerHideSubset = true; + $( subSet ).addClass("greenRows"); // green persists + } } } } } - } - // First option is hide sets - if (offerHideComposite) { - if (offerHideSubset) { + // First option is hide sets + if (offerHideComposite) { + if (offerHideSubset) { + var o = new Object(); + o[blankImg + " hide track subset (green)"] = { + onclick: rightClick.makeHitCallback('hideSet')}; + //o[rightClick.makeImgTag("highliteGreenX.png") + " hide track subset"] = {onclick: rightClick.makeHitCallback('hideSet')}; + menu.push(o); + } + var o = new Object(); - o[blankImg + " hide track subset (green)"] = {onclick: makeContextMenuHitCallback('hideSet')}; - //o[makeImgTag("highliteGreenX.png") + " hide track subset"] = {onclick: makeContextMenuHitCallback('hideSet')}; + var str = blankImg + " hide track set"; + if (offerHideSubset) + str += " (blue)"; + o[str] = {onclick: rightClick.makeHitCallback('hideComposite')}; + //o[rightClick.makeImgTag("btnBlueX.png") + " hide track set"] = {onclick: rightClick.makeHitCallback('hideComposite')}; menu.push(o); } - var o = new Object(); - var str = blankImg + " hide track set"; - if (offerHideSubset) - str += " (blue)"; - o[str] = {onclick: makeContextMenuHitCallback('hideComposite')}; - //o[makeImgTag("btnBlueX.png") + " hide track set"] = {onclick: makeContextMenuHitCallback('hideComposite')}; - menu.push(o); - } - - // Second set of options: visibility for single track - if (offerSingles) { - if (offerHideComposite) - menu.push($.contextMenu.separator); - - // XXXX what if select is not available (b/c trackControlsOnMain is off)? - // Move functionality to a hidden variable? - var select = $("select[name=" + id + "]"); - if (select.length > 1) // Not really needed if $('#hgTrackUiDialog').html(""); has worked - select = [ $(select)[0] ]; - var cur = $(select).val(); - if(cur) { - $(select).children().each(function(index, o) { - var title = $(this).val(); - var str = blankImg + " " + title; - if(title == cur) - str = selectedImg + " " + title; - var o = new Object(); - o[str] = {onclick: function (menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, title); return true;}}; - menu.push(o); - }); - done = true; - } else { - if(rec) { - // XXXX check current state from a hidden variable. - var visibilityStrs = new Array("hide", "dense", "squish", "pack", "full"); - for (var i in visibilityStrs) { - // XXXX use maxVisibility and change hgTracks so it can hide subtracks + // Second set of options: visibility for single track + if (offerSingles) { + if (offerHideComposite) + menu.push($.contextMenu.separator); + + // XXXX what if select is not available (b/c trackControlsOnMain is off)? + // Move functionality to a hidden variable? + var select = $("select[name=" + id + "]"); + if (select.length > 1) // Not really needed if $('#hgTrackUiDialog').html(""); has worked + select = [ $(select)[0] ]; + var cur = $(select).val(); + if(cur) { + $(select).children().each(function(index, o) { + var title = $(this).val(); + var str = blankImg + " " + title; + if(title == cur) + str = selectedImg + " " + title; var o = new Object(); - var str = blankImg + " " + visibilityStrs[i]; - if(rec.canPack || (visibilityStrs[i] != "pack" && visibilityStrs[i] != "squish")) { - if(rec.localVisibility) { - if(visibilityStrs[i] == rec.localVisibility) { - str = selectedImg + " " + visibilityStrs[i]; + o[str] = {onclick: function (menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, title); + return true;}}; + menu.push(o); + }); + done = true; + } else { + if(rec) { + // XXXX check current state from a hidden variable. + var visStrings = new Array("hide","dense","squish","pack","full"); + for (var i in visStrings) { + // XXXX use maxVisibility and change hgTracks so it can hide subtracks + var o = new Object(); + var str = blankImg + " " + visStrings[i]; + if (rec.canPack + || (visStrings[i] != "pack" && visStrings[i] != "squish")) { + if(rec.localVisibility) { + if(visStrings[i] == rec.localVisibility) { + str = selectedImg + " " + visStrings[i]; + } + } else if(visStrings[i] == vis.enumOrder[rec.visibility]) { + str = selectedImg + " " + visStrings[i]; } - } else if(visibilityStrs[i] == visibilityStrsOrder[rec.visibility]) { - str = selectedImg + " " + visibilityStrs[i]; + o[str] = { onclick: + rightClick.makeHitCallback(visStrings[i]) + }; + menu.push(o); } - o[str] = {onclick: makeContextMenuHitCallback(visibilityStrs[i])}; - menu.push(o); } + done = true; } - done = true; } } - } - if(done) { - var o = new Object(); - var any = false; - var title = selectedMenuItem.title || "feature"; - if(isGene || isHgc || id == "wikiTrack") { - // Add "Open details..." item - var displayItemFunctions = false; - if(rec) { - 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[makeImgTag("magnify.png") + " Zoom to " + title] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "selectWholeGene"); return true; }}; - if(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]; - } + if(done) { + var o = new Object(); + var any = false; + var title = rightClick.selectedMenuItem.title || "feature"; + if(isGene || isHgc || id == "wikiTrack") { + // Add "Open details..." item + var displayItemFunctions = false; + if(rec) { + 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 { - // 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 - var reg = new RegExp("i=([^&]+)"); + displayItemFunctions = true; + } + } + if(displayItemFunctions) { + o[rightClick.makeImgTag("magnify.png") + " Zoom to " + title] = { + onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, + "selectWholeGene"); 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("g=([^&]+)"); + 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 + var reg = new RegExp("i=([^&]+)"); + var a = reg.exec(href); + if(a && a[1]) { + name = a[1]; + reg = new RegExp("g=([^&]+)"); + a = reg.exec(href); + if(a && a[1]) { + table = a[1]; + } + } + } + if(name && table) { + o[rightClick.makeImgTag("magnify.png")+" Zoom to codon"] = + { onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, + "zoomCodon", + {name: name, table: table}); + return true;} + }; + o[rightClick.makeImgTag("magnify.png")+" Zoom to exon"] = { + onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, + "zoomExon", + {name: name, table: table}); + return true; } + }; } } - if(name && table) { - o[makeImgTag("magnify.png") + " Zoom to codon"] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "zoomCodon", {name: name, table: table}); return true; }}; - o[makeImgTag("magnify.png") + " Zoom to exon"] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "zoomExon", {name: name, table: table}); return true; }}; - } + o[rightClick.makeImgTag("dnaIcon.png")+" Get DNA for "+title] = { + onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, "getDna"); + return true; } + }; } - o[makeImgTag("dnaIcon.png") + " Get DNA for " + title] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "getDna"); return true; }}; - } - o[makeImgTag("bookOut.png") + " Open details page in new window..."] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "openLink"); return true; }}; - any = true; - } - if(href != undefined && href.length > 0) { - // Add "Show details..." item - if(title.indexOf("Click to alter ") == 0) { - ; // suppress the "Click to alter..." items - } else if(selectedMenuItem.href.indexOf("cgi-bin/hgTracks") != -1) { - ; // suppress menu items for hgTracks links (e.g. Next/Prev map items). - } else { - var item; - if(title == "zoomInMore") - // avoid showing menu item that says "Show details for zoomInMore..." (redmine 2447) - item = makeImgTag("book.png") + " Show details..."; - else - item = makeImgTag("book.png") + " Show details for " + title + "..."; - o[item] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "followLink"); return true; }}; + o[rightClick.makeImgTag("bookOut.png")+ + " Open details page in new window..."] = { + onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, "openLink"); + return true; } + }; any = true; } + if(href != undefined && href.length > 0) { + // Add "Show details..." item + if(title.indexOf("Click to alter ") == 0) { + ; // suppress the "Click to alter..." items + } else if(rightClick.selectedMenuItem.href.indexOf("cgi-bin/hgTracks") != -1) { + ; // suppress menu items for hgTracks links (e.g. Next/Prev map items). + } else { + var item; + if(title == "zoomInMore") + // avoid showing menu item that says "Show details for zoomInMore..." (redmine 2447) + item = rightClick.makeImgTag("book.png") + " Show details..."; + else + 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(any) { - menu.push($.contextMenu.separator); + } + 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(!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. + + if(rightClick.selectedMenuItem && rec && rec["configureBy"] != 'none') { + // Add cfg options at just shy of end... var o = new Object(); - var str = "drag-and-zoom mode"; - if(autoHideSetting) { - str += selectedImg; - // menu[str].className = 'context-menu-checked-item'; - } - o[str] = { onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "dragZoomMode"); return true; }}; - menu.push(o); - o = new Object(); - // console.dir(ele); - str = "hilight mode"; - if(!autoHideSetting) { - str += selectedImg; + if(tdbIsLeaf(rec)) { + o[rightClick.makeImgTag("wrench.png")+" Configure "+rec.shortLabel] = { + onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, "hgTrackUi_popup"); + return true; } + }; + if(rec.parentTrack != undefined) + o[rightClick.makeImgTag("folderWrench.png")+" Configure "+ + rec.parentLabel + " track set..."] = { + onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked,menuObject,"hgTrackUi_follow"); + return true; } + }; + } else + o[rightClick.makeImgTag("folderWrench.png")+" Configure "+rec.shortLabel + + " track set..."] = { + onclick: function(menuItemClicked, menuObject) { + rightClick.hit(menuItemClicked, menuObject, "hgTrackUi_follow"); + 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; } + }; } - o[str] = { onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "hilightMode"); return true; }}; + menu.push($.contextMenu.separator); menu.push(o); } - //menu.push({"view image": {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "viewImg"); return true; }}}); - } - if(selectedMenuItem && rec) { - // Add cfg options at just shy of end... - // configureBy==none, subtracks get folder, stand-alones get nothing - // configureBy==clickThrough, subtracks get folder, stand-alones get wrench but it will not be a popup - // configureBy==popup subtracks get wrench and folder, stand-alones get wrench as popup + // Add view image at end var o = new Object(); - if(tdbIsLeaf(rec)) { - if ( rec["configureBy"] != 'none' - && (rec["configureBy"] != 'clickThrough' || rec.parentTrack == undefined)) - o[makeImgTag("wrench.png") + " Configure " + rec.shortLabel] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "hgTrackUi_popup"); return true; }}; - if(rec.parentTrack != undefined) - o[makeImgTag("folderWrench.png") + " Configure " + rec.parentLabel + " track set..."] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "hgTrackUi_follow"); return true; }}; - } else if (rec["configureBy"] != 'none') - o[makeImgTag("folderWrench.png") + " Configure " + rec.shortLabel + " track set..."] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "hgTrackUi_follow"); return true; }}; - if(jQuery.floatMgr) { - o[(selectedMenuItem.id == floatingMenuItem ? selectedImg : blankImg) + " float"] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "float"); return true; }}; - } + 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); - } - // Add view image at end - var o = new Object(); - o[makeImgTag("eye.png") + " View image"] = {onclick: function(menuItemClicked, menuObject) { contextMenuHit(menuItemClicked, menuObject, "viewImg"); return true; }}; - menu.push($.contextMenu.separator); - menu.push(o); - - return menu; - }, - { - beforeShow: function(e) { - // console.log(mapItems[selectedMenuItem]); - selectedMenuItem = findMapItem(e); - // XXXX? blockUseMap = true; - return true; + return menu; }, - hideTransition:'hide', // hideCallback fails if these are not defined. - hideSpeed:10, - hideCallback: function() { - $('p.btn.blueButtons').removeClass('blueButtons'); - $('tr.trDraggable.greenRows').removeClass('greenRows'); - } - }); - return; - } - - function parseMap(ele, reset) - { - // Parse the jQuery <map> object into returned mapItems array (ele needn't be the element attached to current document). - if(reset || !mapItems) { - mapItems = new Array(); - } - if(ele) { - var i = mapItems.length; - // src is necessary under msie - var src = ele.next().attr('src'); - ele.children().each(function() { - if (this.coords != undefined) { - mapItems[i++] = { - r : new Rectangle(this.coords), - href : this.href, - title : this.title, - id : this.id, - src : src, - obj : this - }; - } - }); + { + 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'); + $('tr.trDraggable.greenRows').removeClass('greenRows'); + } + }); + return; } - return mapItems; - } - function updateTrackImg(trackName,extraData,loadingId) - { - var data = "hgt.trackImgOnly=1&hgsid=" + getHgsid() + "&hgt.trackNameFilter=" + trackName; - if(extraData != undefined && extraData != "") - data += "&" + extraData; - if(loadingId == undefined || loadingId == "") - loadingId = showLoadingImage("tr_" + trackName); - $.ajax({ - type: "GET", - url: "../cgi-bin/hgTracks", - data: data, - dataType: "html", - trueSuccess: handleUpdateTrackMap, - success: catchErrorOrDispatch, - error: errorHandler, - cmd: 'refresh', - loadingId: loadingId, - id: trackName, - cache: false - }); } - var popUpTrackName = ""; - var popUpTrackDescriptionOnly = false; - var popSaveAllVars = null; - - function popUpBoxCleanup() - { // Clean out the popup box on close - if ($('#hgTrackUiDialog').html().length > 0 ) { - $('#hgTrackUiDialog').html(""); // clear out html after close to prevent problems caused by duplicate html elements - popUpTrackName = ""; //set to defaults - popUpTrackDescriptionOnly = false; - popSaveAllVars = null; - } - } + ////////////////////////////////// + //// popup (aka modal dialog) //// + ////////////////////////////////// + var popUp = { - function _hgTrackUiPopUp(trackName,descriptionOnly) - { // popup cfg dialog - popUpTrackName = trackName; - var myLink = "../cgi-bin/hgTrackUi?g=" + trackName + "&hgsid=" + getHgsid() + "&db=" + getDb(); - popUpTrackDescriptionOnly = descriptionOnly; - if(popUpTrackDescriptionOnly) - myLink += "&descriptionOnly=1"; - - var rec = hgTracks.trackDb[trackName]; - if(!descriptionOnly && rec != null && rec["configureBy"] != null) { - if (rec["configureBy"] == 'none') - return; - else if (rec["configureBy"] == 'clickThrough') { - jQuery('body').css('cursor', 'wait'); - window.location = myLink; - return; - } // default falls through to configureBy popup - } - myLink += "&ajax=1"; - $.ajax({ - type: "GET", - url: myLink, - dataType: "html", - trueSuccess: handleTrackUi, - success: catchErrorOrDispatch, - error: errorHandler, - cmd: selectedMenuItem, - cache: false - }); - } + trackName: "", + trackDescriptionOnly: false, + saveAllVars: null, - function hgTrackUiPopUp(trackName,descriptionOnly) - { - waitOnFunction( _hgTrackUiPopUp, trackName, descriptionOnly ); // Launches the popup but shields the ajax with a waitOnFunction - } + cleanup: function () + { // Clean out the popup box on close + if ($('#hgTrackUiDialog').html().length > 0 ) { + $('#hgTrackUiDialog').html(""); // clear out html after close to prevent problems caused by duplicate html elements + popUp.trackName = ""; //set to defaults + popUp.trackDescriptionOnly = false; + popUp.saveAllVars = null; + } + }, - function hgTrackUiPopCfgOk(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; // If subtrack then vis rules differ - var allVars = getAllVars($('#pop'), subtrack ); - var changedVars = varHashChanges(allVars,popSaveAllVars); - //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) { + _uiDialigRequest: 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 != null && rec["configureBy"] != null) { + if (rec["configureBy"] == 'none') + return; + else if (rec["configureBy"] == 'clickThrough') { + jQuery('body').css('cursor', 'wait'); + window.location = myLink; + return; + } // default falls through to configureBy popup + } + myLink += "&ajax=1"; + $.ajax({ + type: "GET", + url: myLink, + dataType: "html", + trueSuccess: popUp.uiDialog, + success: catchErrorOrDispatch, + error: errorHandler, + cmd: rightClick.selectedMenuItem, + cache: false + }); + }, + + hgTrackUi: function (trackName,descriptionOnly) + { + waitOnFunction( popUp._uiDialigRequest, trackName, descriptionOnly ); // Launches the popup but shields the ajax with a waitOnFunction + }, + + 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; // If subtrack then vis rules differ + var allVars = getAllVars($('#pop'), subtrack ); + 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); - $('#tr_' + trackName).remove(); - initImgTblButtons(); - loadImgAreaSelect(false); - } else { - // Keep local state in sync if user changed visibility - if(newVis != null) { - updateVisibility(trackName, newVis); - } - var urlData = varHashToQueryString(changedVars); - if(urlData.length > 0) { - if(mapIsUpdateable) { - updateTrackImg(trackName,urlData,""); - } else { - window.location = "../cgi-bin/hgTracks?" + urlData + "&hgsid=" + getHgsid(); + //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); + $('#tr_' + trackName).remove(); + dragReorder.init(); + dragSelect.load(false); + } 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(); + } } } } + }, + + 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(/<a /ig, "<a target='_blank' "); + + // TODO: Shlurp up any javascript files from the response and load them with $.getScript() + // example <script type='text/javascript' SRC='../js/tdreszer/jquery.contextmenu-1296177766.js'></script> + var cleanHtml = response; + var shlurpPattern=/\<script type=\'text\/javascript\' SRC\=\'.*\'\>\<\/script\>/gi; + var jsFiles = cleanHtml.match(shlurpPattern); + cleanHtml = cleanHtml.replace(shlurpPattern,""); + shlurpPattern=/\<script type=\'text\/javascript\'>.*\<\/script\>/gi; + var jsEmbeded = cleanHtml.match(shlurpPattern); + cleanHtml = cleanHtml.replace(shlurpPattern,""); + //<LINK rel='STYLESHEET' href='../style/ui.dropdownchecklist-1276528376.css' TYPE='text/css' /> + shlurpPattern=/\<LINK rel=\'STYLESHEET\' href\=\'.*\' TYPE=\'text\/css\' \/\>/gi; + var cssFiles = cleanHtml.match(shlurpPattern); + cleanHtml = cleanHtml.replace(shlurpPattern,""); + + $('#hgTrackUiDialog').html("<div id='pop' style='font-size:.9em;'>"+ cleanHtml +"</div>"); + + // Strategy for poups 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. + + /* //in open ? Will load of css work this way? + $(cssFiles).each(function (i) { + bix = "<LINK rel='STYLESHEET' href='".length; + eix = this.lastIndexOf("' TYPE='text/css' />"); + file = this.substring(bix,eix); + $.getScript(file); // Should protect against already loaded files. + }); */ + /* //in open ? Loads fine, but then dialog gets confused + $(jsFiles).each(function (i) { + bix = "<script type='text/javascript' SRC='".length; + eix = this.lastIndexOf("'></script>"); + file = this.substring(bix,eix); + //$.getScript(file,function(data) { warn(data.substring(0,20) + " loaded")}); + });*/ + + if ( ! popUp.trackDescriptionOnly ) { + var subtrack = tdbIsSubtrack(hgTracks.trackDb[popUp.trackName]) ? popUp.trackName :""; // If subtrack then vis rules differ + popUp.saveAllVars = getAllVars( $('#hgTrackUiDialog'), subtrack ); // Saves the vars that may get changed by the popup cfg. + + // -- 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 + 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, + height: (popUp.trackDescriptionOnly ? popMaxHeight : 'auto'), // Let description scroll vertically + width: popWidth, + minHeight: 200, + minWidth: 700, + maxHeight: popMaxHeight, + maxWidth: popMaxWidth, + modal: true, + closeOnEscape: true, + autoOpen: false, + buttons: { "OK": function() { + if ( ! popUp.trackDescriptionOnly ) + popUp.uiDialogOk($('#pop'), popUp.trackName); + $(this).dialog("close"); + }}, + // popup.ready() doesn't seem to work in open. So there is no need for open at this time. + //open: function() { + // var subtrack = tdbIsSubtrack(hgTracks.trackDb[popUp.trackName]) ? popUp.trackName :""; // If subtrack then vis rules differ + // popUp.saveAllVars = getAllVars( $('#pop'), subtrack ); + //}, + open: function () { + if ( ! popUp.trackDescriptionOnly ) { + $('#hgTrackUiDialog').find('.filterBy,.filterComp').each( + function(i) { + if ($(this).hasClass('filterComp')) + ddcl.setup(this); + else + ddcl.setup(this, 'noneIsAll'); + } + ); + } + }, + close: function() { + popUp.cleanup(); + } + }); + // FIXME: Why are open and close no longer working!!! + if(popUp.trackDescriptionOnly) { + var myWidth = $(window).width() - 300; + if(myWidth > 900) + myWidth = 900; + $('#hgTrackUiDialog').dialog("option", "maxWidth", myWidth); + $('#hgTrackUiDialog').dialog("option", "width", myWidth); + $('#hgTrackUiDialog').dialog('option' , 'title' , + hgTracks.trackDb[popUp.trackName].shortLabel+" Track Description"); + $('#hgTrackUiDialog').dialog('open'); + var buttOk = $('button.ui-state-default'); + if($(buttOk).length == 1) + $(buttOk).focus(); + } else { + $('#hgTrackUiDialog').dialog('option' , 'title' , + hgTracks.trackDb[popUp.trackName].shortLabel+" Track Settings"); + $('#hgTrackUiDialog').dialog('open'); + } } } - function handleTrackUi(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(/<a /ig, "<a target='_blank' "); - - // TODO: Shlurp up any javascript files from the response and load them with $.getScript() - // example <script type='text/javascript' SRC='../js/tdreszer/jquery.contextmenu-1296177766.js'></script> - var cleanHtml = response; - cleanHtml = stripJsFiles(cleanHtml, false); // true means show error msg - cleanHtml = stripCssFiles(cleanHtml, false); - cleanHtml = stripJsEmbedded(cleanHtml,false);// NOTE: embeded warnBox msgs will be put into the warnbox + /////////////////////////////// + //// imageV2 (aka imgTbl) //// + /////////////////////////////// + var imageV2 = { - $('#hgTrackUiDialog').html("<div id='pop' style='font-size:.9em;'>" + cleanHtml + "</div>"); + enabled: false, // Will be set to true unless advancedJavascriptFeatures is turned off OR if track search of config page + imgTbl: null, // formerly "trackImgTbl" The imgTbl or null if non-imageV2. + inPlaceUpdate: false, // modified based on value of hgTracks.inPlaceUpdate and mapIsUpdateable + mapIsUpdateable:true, + lastTrack: null, // formerly (lastMapItem) this is used to try to keep what the last track the cursor passed. - // Strategy for poups 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. + markAsDirtyPage: function () + { // Page is marked as dirty so that the backbutton can be overridden + var dirty = $('#dirty'); + if (dirty != undefined && dirty.length != 0) + $(dirty).val('true'); + }, - if( ! popUpTrackDescriptionOnly ) { - var subtrack = tdbIsSubtrack(hgTracks.trackDb[popUpTrackName]) ? popUpTrackName :""; // If subtrack then vis rules differ - popSaveAllVars = getAllVars( $('#hgTrackUiDialog'), subtrack ); // Saves the vars that may get changed by the popup cfg. + isDirtyPage: function () + { // returns true if page was marked as dirty + // This will allow the backbutton to be overridden - // -- 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 - 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, - height: (popUpTrackDescriptionOnly ? popMaxHeight : 'auto'), // Let description scroll vertically - width: popWidth, - minHeight: 200, - minWidth: 700, - maxHeight: popMaxHeight, - maxWidth: popMaxWidth, - modal: true, - closeOnEscape: true, - autoOpen: false, - buttons: { "OK": function() { - if( ! popUpTrackDescriptionOnly ) - hgTrackUiPopCfgOk($('#pop'), popUpTrackName); - $(this).dialog("close"); - }}, - // popup.ready() doesn't seem to work in open. So there is no need for open at this time. - //open: function() { - // var subtrack = tdbIsSubtrack(hgTracks.trackDb[popUpTrackName]) ? popUpTrackName :""; // If subtrack then vis rules differ - // popSaveAllVars = getAllVars( $('#pop'), subtrack ); - //}, - open: function () { - if( ! popUpTrackDescriptionOnly ) { - $('#hgTrackUiDialog').find('.filterBy,.filterComp').each( function(i) { - if ($(this).hasClass('filterComp')) - ddcl.setup(this); - else - ddcl.setup(this, 'noneIsAll'); - }); - } - }, - close: function() { - popUpBoxCleanup(); - } - }); - // FIXME: Why are open and close no longer working!!! - if(popUpTrackDescriptionOnly) { - var myWidth = $(window).width() - 300; - if(myWidth > 900) - myWidth = 900; - $('#hgTrackUiDialog').dialog("option", "maxWidth", myWidth); - $('#hgTrackUiDialog').dialog("option", "width", myWidth); - $('#hgTrackUiDialog').dialog('option' , 'title' , hgTracks.trackDb[popUpTrackName].shortLabel + " Track Description"); - $('#hgTrackUiDialog').dialog('open'); - var buttOk = $('button.ui-state-default'); - if($(buttOk).length == 1) - $(buttOk).focus(); - } else { - $('#hgTrackUiDialog').dialog('option' , 'title' , hgTracks.trackDb[popUpTrackName].shortLabel + " Track Settings"); - $('#hgTrackUiDialog').dialog('open'); - } - } - - function afterImgTblReload() - { - // Reload various UI widgets after updating imgTbl map. - parseMap(null, true); - $("map[name!=ideoMap]").each( function(t) { parseMap($(this, false));}); - initImgTblButtons(); - loadImgAreaSelect(false); - // Do NOT reload context menu (otherwise we get the "context menu sticks" problem). - // loadContextMenu($('#tr_' + id)); - if(trackImgTbl.tableDnDUpdate) - trackImgTbl.tableDnDUpdate(); - reloadFloatingItem(); - // Turn on drag scrolling. - if(hgTracks.imgBoxPortal) { - $("div.scroller").panImages(); - } - loadRemoteTracks(); - markAsDirtyPage(); - } + var dirty = $('#dirty'); + if (dirty != undefined && dirty.length > 0) { + if ($(dirty).val() == 'true') + return true; + } + return false; + }, + + updateTiming: function (response) + { // update measureTiming text on current page based on what's in the response + var reg = new RegExp("(<span class='timing'>.+?</span>)", "g"); + var strs = []; + for(var a = reg.exec(response); a != null && a[1] != null; a = reg.exec(response)) { + strs.push(a[1]); + } + if(strs.length > 0) { + $('.timing').remove(); + for(var i = strs.length; i > 0; i--) { + $('body').prepend(strs[i - 1]); + } + } + reg = new RegExp("(<span class='trackTiming'>[\\S\\s]+?</span>)"); + a = reg.exec(response); + if(a != null && a[1] != null) { + $('.trackTiming').replaceWith(a[1]); + } + }, + + afterReload: function () + { // 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(); + } + imageV2.loadRemoteTracks(); + imageV2.markAsDirtyPage(); + }, - function updateTrackImgForId(html, id) - { - // update row in imgTbl for given id. - // return true if we successfully pull slice for id and update it in imgTrack. - var str = "<TR id='tr_" + id + "'[^>]*>([\\s\\S]+?)</TR>"; - var reg = new RegExp(str); - var a = reg.exec(html); - if(a && a[1]) { - var tr = $('#tr_' + id); - $(tr).html(a[1]); - // NOTE: Want to examine the png? Uncomment: - //var img = $('#tr_' + id).find("img[id^='img_data_']").attr('src'); - //warn("Just parsed image:<BR>"+img); - - // >1x dragScrolling needs some extra care. - if(hgTracks.imgBoxPortal && (hgTracks.imgBoxWidth > hgTracks.imgBoxPortalWidth)) { - if (hgTracks.imgBoxPortalLeft != undefined) { - $(tr).find('.panImg').css({'left': hgTracks.imgBoxPortalLeft }); - $(tr).find('.tdData').css( {'backgroundPosition': hgTracks.imgBoxPortalLeft } ); + updateImgForId: function (html, id) + { // update row in imgTbl for given id. + // return true if we successfully pull slice for id and update it in imgTrack. + var str = "<TR id='tr_" + id + "'[^>]*>([\\s\\S]+?)</TR>"; + var reg = new RegExp(str); + var a = reg.exec(html); + if(a && a[1]) { + var tr = $('#tr_' + id); + if (tr.length > 0) { + $(tr).html(a[1]); + // NOTE: Want to examine the png? Uncomment: + //var img = $('#tr_' + id).find("img[id^='img_data_']").attr('src'); + //warn("Just parsed image:<BR>"+img); + + // >1x dragScrolling needs some extra care. + if(hgTracks.imgBoxPortal && (hgTracks.imgBoxWidth > hgTracks.imgBoxPortalWidth)) { + if (hgTracks.imgBoxPortalLeft != undefined) { + $(tr).find('.panImg').css({'left': hgTracks.imgBoxPortalLeft }); + $(tr).find('.tdData').css( + {'backgroundPosition': hgTracks.imgBoxPortalLeft}); + } + } + return true; } } - return true; - } else { return false; - } - } + }, - function updateTiming(response) - { - // update measureTiming text on current page based on what's in the response - var reg = new RegExp("(<span class='timing'>.+?</span>)", "g"); - var strs = []; - for(var a = reg.exec(response); a != null && a[1] != null; a = reg.exec(response)) { - strs.push(a[1]); - } - if(strs.length > 0) { - $('.timing').remove(); - for(var i = strs.length; i > 0; i--) { - $('body').prepend(strs[i - 1]); - } - } - reg = new RegExp("(<span class='trackTiming'>[\\S\\s]+?</span>)"); - a = reg.exec(response); - if(a != null && a[1] != null) { - $('.trackTiming').replaceWith(a[1]); - } - } + requestImgUpdate: function (trackName,extraData,loadingId) + { + var data = "hgt.trackImgOnly=1&hgsid=" + getHgsid() + "&hgt.trackNameFilter=" + trackName; + if(extraData != undefined && extraData != "") + data += "&" + extraData; + if(loadingId == undefined || 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: data, + dataType: "html", + trueSuccess: imageV2.updateImgAndMap, + success: catchErrorOrDispatch, + error: errorHandler, + cmd: 'refresh', + loadingId: loadingId, + id: trackName, + cache: false + }); + }, - function handleUpdateTrackMap(response, status) - { - // Handle ajax response with an updated trackMap image, map and optional ideogram. - // - // 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. - - // update local hgTracks.trackDb to reflect possible side-effects of ajax request. - var json = scrapeVariable(response, "hgTracks"); - var oldTrackDb = hgTracks.trackDb; - if(json == undefined) { - showWarning("hgTracks object is missing from the response"); - } else { - if(this.id != null) { - if(json.trackDb[this.id]) { - var visibility = visibilityStrsOrder[json.trackDb[this.id].visibility]; - var limitedVis; - if(json.trackDb[this.id].limitedVis) - limitedVis = visibilityStrsOrder[json.trackDb[this.id].limitedVis]; - if(this.newVisibility && limitedVis && this.newVisibility != limitedVis) - // see redmine 1333#note-9 - alert("There are too many items to display the track in " + this.newVisibility + " mode."); - var rec = hgTracks.trackDb[this.id]; - rec.limitedVis = json.trackDb[this.id].limitedVis; - updateVisibility(this.id, visibility); + updateImgAndMap: function (response, status) + { // Handle ajax response with an updated trackMap image, map and optional ideogram. + // + // 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. + + // update local hgTracks.trackDb to reflect possible side-effects of ajax request. + var json = scrapeVariable(response, "hgTracks"); + var oldTrackDb = hgTracks.trackDb; + if(json == undefined) { + showWarning("hgTracks object is missing from the response"); + } else { + if(this.id != null) { + if(json.trackDb[this.id]) { + var visibility = vis.enumOrder[json.trackDb[this.id].visibility]; + var limitedVis; + if(json.trackDb[this.id].limitedVis) + limitedVis = vis.enumOrder[json.trackDb[this.id].limitedVis]; + if(this.newVisibility && limitedVis && this.newVisibility != limitedVis) + // see redmine 1333#note-9 + alert("There are too many items to display the track in " + + this.newVisibility + " mode."); + var rec = hgTracks.trackDb[this.id]; + rec.limitedVis = json.trackDb[this.id].limitedVis; + vis.update(this.id, visibility); + } else { + showWarning("Invalid hgTracks.trackDb received from the server"); + } } else { - showWarning("Invalid hgTracks.trackDb received from the server"); + hgTracks.trackDb = json.trackDb; } - } else { - hgTracks.trackDb = json.trackDb; } - } - if(imageV2 && this.id && this.cmd && this.cmd != 'wholeImage' && this.cmd != 'selectWholeGene') { - // Extract <TR id='tr_ID'>...</TR> 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(updateTrackImgForId(response, id)) { - afterImgTblReload(); - } else { - showWarning("Couldn't parse out new image for id: " + id); - //alert("Couldn't parse out new image for id: " + id+"BR"+response); // Very helpful - } - } else { - if(imageV2) { - // Implement in-place updating of hgTracks image - setPositionByCoordinates(json.chromName, json.winStart + 1, json.winEnd); - $("input[name='c']").val(json.chromName); - $("input[name='l']").val(json.winStart); - $("input[name='r']").val(json.winEnd); - if(json.cgiVersion != hgTracks.cgiVersion) { - // 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. - jQuery('body').css('cursor', 'wait'); - document.TrackHeaderForm.submit(); + if(imageV2.enabled + && this.id + && this.cmd + && this.cmd != 'wholeImage' + && this.cmd != 'selectWholeGene') { + // Extract <TR id='tr_ID'>...</TR> 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)) { + imageV2.afterReload(); } else { - // We update rows one at a time (updating the whole imgTable at one time doesn't work in IE). - for (var id in hgTracks.trackDb) { - if(hgTracks.trackDb[id].type != "remote" && !updateTrackImgForId(response, id)) { - showWarning("Couldn't parse out new image for id: " + id); - //alert("Couldn't parse out new image for id: " + id+"BR"+response); // Very helpful + showWarning("Couldn't parse out new image for id: " + id); + //alert("Couldn't parse out new image for id: " + id+"BR"+response); // Very helpful + } + } else { + if(imageV2.enabled) { + // Implement in-place updating of hgTracks image + genomePos.setByCoordinates(json.chromName, json.winStart + 1, json.winEnd); + $("input[name='c']").val(json.chromName); + $("input[name='l']").val(json.winStart); + $("input[name='r']").val(json.winEnd); + if(json.cgiVersion != hgTracks.cgiVersion) { + // 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. + jQuery('body').css('cursor', 'wait'); + document.TrackHeaderForm.submit(); + } else { + // We update rows one at a time (b/c updating the whole imgTable at one time doesn't work in IE). + for (var id in hgTracks.trackDb) { + // handle case where invisible items may be in the trackDb list (see redmine #5670). + if(hgTracks.trackDb[id].type != "remote" + && hgTracks.trackDb[id].visibility > 0 // && $('#tr_' + id).length > 0 + && !imageV2.updateImgForId(response, id)) { + showWarning("Couldn't parse out new image for id: " + id); + } } + /* This (disabled) code handles dynamic addition of tracks: + for (var id in hgTracks.trackDb) { + if(oldTrackDb[id] == undefined) { + // XXXX Tim, what s/d abbr attribute be? + $('#imgTbl').append("<tr id='tr_" + id + "' class='imgOrd trDraggable'></tr>"); + imageV2.updateImgForId(response, id); + vis.update(id, vis.enumOrder[hgTracks.trackDb[id].visibility]); + } + } + */ + hgTracks = json; + genomePos.original = undefined; + initVars(); + imageV2.afterReload(); } - /* This (disabled) code handles dynamic addition of tracks: - for (var id in hgTracks.trackDb) { - if(oldTrackDb[id] == undefined) { - // XXXX Tim, what s/d abbr attribute be? - $('#imgTbl').append("<tr id='tr_" + id + "' class='imgOrd trDraggable'></tr>"); - updateTrackImgForId(response, id); - updateVisibility(id, visibilityStrsOrder[hgTracks.trackDb[id].visibility]); + } else { + warn("ASSERT: Attempt to update track without advanced javascript features."); + } + // now pull out and parse the map. + //a = /<MAP id='map' Name=map>([\s\S]+)<\/MAP>/.exec(response); + //if(!a[1]) + // showWarning("Couldn't parse out map"); + } + // Parse out new ideoGram url (if available) + // e.g.: <IMG SRC = "../trash/hgtIdeo/hgtIdeo_hgwdev_larrym_61d1_8b4a80.gif" BORDER=1 WIDTH=1039 HEIGHT=21 USEMAP=#ideoMap id='chrom'> + // We do this last b/c it's least important. + var a = /<IMG([^>]+SRC[^>]+id='chrom'[^>]*)>/.exec(response); + if(a && a[1]) { + b = /SRC\s*=\s*"([^")]+)"/.exec(a[1]); + if(b[1]) { + $('#chrom').attr('src', b[1]); + } + } + if(hgTracks.measureTiming) { + imageV2.updateTiming(response); + } + if(this.disabledEle) { + this.disabledEle.attr('disabled', ''); + } + if(this.loadingId) { + hideLoadingImage(this.loadingId); + } + jQuery('body').css('cursor', ''); + if(this.currentId) { + var top = $("#tr_" + this.currentId).position().top; + $(window).scrollTop(top - this.currentIdYOffset); + } + }, + + loadRemoteTracks: function () + { + if(typeof(hgTracks.trackDb) != "undefined" && hgTracks.trackDb != null) { + for (var id in hgTracks.trackDb) { + var rec = hgTracks.trackDb[id]; + if(rec.type == "remote") { + if($("#img_data_" + id).length > 0) { + // load the remote track renderer via jsonp + rec.loadingId = showLoadingImage("tr_" + id); + var script = document.createElement('script'); + var pos = parsePosition(genomePos.get()); + var name = rec.remoteTrack || id; + script.setAttribute('src', + rec.url + "?track=" + name + + "&jsonp=imageV2.remoteTrackCallback&position=" + + encodeURIComponent(pos.chrom + ":" + pos.start + "-" + pos.end) + + "&pix=" + $('#imgTbl').width() + ); + document.getElementsByTagName('head')[0].appendChild(script); } } - */ - hgTracks = json; - originalPosition = undefined; - initVars(); - afterImgTblReload(); } + } + }, + + remoteTrackCallback: function (rec) + // jsonp callback to load a remote track. + { + if(rec.error) { + alert("retrieval from remote site failed with error: " + rec.error) } else { - a= /<IMG([^>]+SRC[^>]+id='trackMap[^>]*)>/.exec(response); - // Deal with a is null - if(a[1]) { - var b = /WIDTH\s*=\s*['"]?(\d+)['"]?/.exec(a[1]); - var width = b[1]; - b = /HEIGHT\s*=\s*['"]?(\d+)['"]?/.exec(a[1]); - var height = b[1]; - b = /SRC\s*=\s*"([^")]+)"/.exec(a[1]); - var src = b[1]; - $('#trackMap').attr('src', src); - var obj = imgAreaSelect.data('imgAreaSelect'); - if(width) { - trackImg.attr('width', width); + var remoteTrack = rec.track; + for (var track in hgTracks.trackDb) { + if(hgTracks.trackDb[track].remoteTrack == remoteTrack) { + $('#img_data_' + track).attr('style', "left:-116px; top: -23px;"); + $('#img_data_' + track).attr('height', rec.height); + // XXXX use width in some way? + // $('#img_data_' + track).attr('width', rec.width); + $('#img_data_' + track).attr('width', $('#img_data_ruler').width()); + $('#img_data_' + track).attr('src', rec.img); + $('#td_data_' + track + ' > div').each(function(index) { + if(index == 1) { + var style = $(this).attr('style'); + style = style.replace(/height:\s*\d+/i, "height:" + rec.height); + $(this).attr('style', style); + } + }); + var style = $('#p_btn_' + track).attr('style'); + style = style.replace(/height:\s*\d+/i, "height:" + rec.height); + $('#p_btn_' + track).attr('style', style); + if(hgTracks.trackDb[track].loadingId) { + hideLoadingImage(hgTracks.trackDb[track].loadingId); } - if(height) { - trackImg.attr('height', height); - - // obj.setOptions({minHeight : height, maxHeight: height}); - // obj.getOptions().minHeight = height; - // obj.getOptions().maxHeight = height; - // XXX doesn't work obj.options.maxHeight = height; - // This doesn't work: obj.windowResize(); - // This works, but causes weird error IF we also change minHeight and maxHeight. - // jQuery(window).triggerHandler("resize"); - - // After much debugging, I found the best way to have imgAreaSelect continue to work - // was to reload it: - loadImgAreaSelect(false); - // XXX this doesn't work (i.e. does't make the re-sized row draggable under safari). - trackImgTbl.tableDnD(); - } - } else { - showWarning("Couldn't parse out new image"); + } } } - // now pull out and parse the map. - a = /<MAP id='map' Name=map>([\s\S]+)<\/MAP>/.exec(response); - if(a[1]) { - var $map = $('<map>' + a[1] + '</map>'); - parseMap($map, true); + }, + + navigateButtonClick: function (ele) // called from hgTracks.c + { // code to update just the imgTbl in response to navigation buttons (zoom-out etc.). + // This is currently experimental code (controlled by IN_PLACE_UPDATE in imageV2.h). + if(imageV2.inPlaceUpdate) { + var params = ele.name + "=" + ele.value; + $(ele).attr('disabled', 'disabled'); + // dinking navigation needs additional data + if(ele.name == "hgt.dinkLL" || ele.name == "hgt.dinkLR") { + params += "&dinkL=" + $("input[name='dinkL']").val(); + } else if(ele.name == "hgt.dinkRL" || ele.name == "hgt.dinkRR") { + params += "&dinkR=" + $("input[name='dinkR']").val(); + } + imageV2.navigateInPlace(params, $(ele), false); + return false; } else { - showWarning("Couldn't parse out map"); + return true; } - } - // Parse out new ideoGram url (if available) - // e.g.: <IMG SRC = "../trash/hgtIdeo/hgtIdeo_hgwdev_larrym_61d1_8b4a80.gif" BORDER=1 WIDTH=1039 HEIGHT=21 USEMAP=#ideoMap id='chrom'> - // We do this last b/c it's least important. - var a = /<IMG([^>]+SRC[^>]+id='chrom'[^>]*)>/.exec(response); - if(a && a[1]) { - b = /SRC\s*=\s*"([^")]+)"/.exec(a[1]); - if(b[1]) { - $('#chrom').attr('src', b[1]); + }, + + updateButtonClick: function (ele) // UNUSED? + { // code to update the imgTbl based on changes in the track controls. + // This is currently experimental code and is dead in the main branch. + if(imageV2.mapIsUpdateable) { + var data = ""; + $("select").each(function(index, o) { + var cmd = $(this).val(); + if(cmd == "hide") { + if(hgTracks.trackDb[this.name] != undefined) { + alert("Need to implement hide"); + } + } else { + if(hgTracks.trackDb[this.name] == undefined + || cmd != vis.enumOrder[hgTracks.trackDb[this.name].visibility]) { + if(data.length > 0) { + data = data + "&"; + } + data = data + this.name + "=" + cmd; + } + } + }); + if(data.length > 0) { + imageV2.navigateInPlace(data, null, false); + } + return false; + } else { + return true; } - } - if(hgTracks.measureTiming) { - updateTiming(response); - } - if(this.disabledEle) { - this.disabledEle.attr('disabled', ''); - } - if(this.loadingId) { - hideLoadingImage(this.loadingId); - } - jQuery('body').css('cursor', ''); - if(this.currentId) { - var top = $("#tr_" + this.currentId).position().top; - $(window).scrollTop(top - this.currentIdYOffset); - } - } + }, + + jumpButtonOnClick: function () // called from hgTracks.c + { // onClick handler for the "jump" button. + // Handles situation where user types a gene name into the gene box and immediately hits the jump button, + // expecting the browser to jump to that gene. + var gene = $('#suggest').val(); + var db = getDb(); + if(gene + && gene.length > 0 + && gene != "gene" + && db + && (genomePos.getOriginalPos() == genomePos.get() || genomePos.get().length == 0)) { + pos = lookupGene(db, gene); + if(pos) { + genomePos.set(pos, null); + } else { + // turn this into a full text search. + genomePos.set(gene, null); + } + } + return true; + }, - function handleViewImg(response, status) - { // handles view image response, which must get new image without imageV2 gimmickery - jQuery('body').css('cursor', ''); - var str = "<IMG[^>]*SRC='([^']+)'"; - var reg = new RegExp(str); - a = reg.exec(response); - if(a && a[1]) { - if(window.open(a[1]) == null) { - windowOpenFailedMsg(); + navigateInPlace: function (params, disabledEle, keepCurrentTrackVisible) + { + // request an hgTracks image, using params + // disabledEle is optional; this element will be enabled when update is complete + // If keepCurrentTrackVisible is true, we try to maintain relative position of the item under the mouse after the in-place update. + jQuery('body').css('cursor', ''); + var currentId, currentIdYOffset; + if(keepCurrentTrackVisible) { + var item = rightClick.currentMapItem || imageV2.lastTrack; + if(item) { + var top = $("#tr_" + item.id).position().top; + if(top >= $(window).scrollTop() + || top < $(window).scrollTop() + $(window).height()) { + // don't bother if the item is not currently visible. + currentId = item.id; + currentIdYOffset = top - $(window).scrollTop(); + } + } } - return; + $.ajax({ + type: "GET", + 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 + }); } - showWarning("Couldn't parse out img src"); + } - function jumpButtonOnClick() - { - // onClick handler for the "jump" button. - // Handles situation where user types a gene name into the gene box and immediately hits the jump button, - // expecting the browser to jump to that gene. - var gene = $('#suggest').val(); - var db = getDb(); - if(gene && gene != "gene" && db && gene.length > 0 && (getOriginalPosition() == getPosition() || getPosition().length == 0)) { - pos = lookupGene(db, gene); - if(pos) { - setPosition(pos, null); - } else { - // turn this into a full text search. - setPosition(gene, null); + //////////////////////////////////// + //// suggest (aka gene search) //// + //////////////////////////////////// + var suggestBox = { + + init: function (db) + { + if(jQuery.fn.autocomplete && $('input#suggest') && db) { + if(jQuery.fn.Watermark) { + $('#suggest').Watermark("gene"); + } + $('input#suggest').autocomplete({ + delay: 500, + minLength: 2, + source: ajaxGet(function () {return getDb();}, new Object, true), + open: function(event, ui) { + var pos = $(this).offset().top + $(this).height(); + if (!isNaN(pos)) { + var maxHeight = $(window).height() - pos - 30; // take off a little more because IE needs it + var auto = $('.ui-autocomplete'); + var curHeight = $(auto).children().length * 21; + if (curHeight > maxHeight) + $(auto).css({maxHeight: maxHeight+'px',overflow:'scroll'}); + else + $(auto).css({maxHeight: 'none',overflow:'hidden'}); + } + }, + select: function (event, ui) { + genomePos.set(ui.item.id, commify(getSizeFromCoordinates(ui.item.id))); + vis.makeTrackVisible($("#suggestTrack").val()); + // jQuery('body').css('cursor', 'wait'); + // document.TrackHeaderForm.submit(); + } + }); + + // I want to set focus to the suggest element, but unforunately that prevents PgUp/PgDn from + // working, which is a major annoyance. + // $('input#suggest').focus(); } } - return true; } - function remoteTrackCallback(rec) - // jsonp callback to load a remote track. - { - if(rec.error) { - alert("retrieval from remote site failed with error: " + rec.error) - } else { - var remoteTrack = rec.track; - for (var track in hgTracks.trackDb) { - if(hgTracks.trackDb[track].remoteTrack == remoteTrack) { - $('#img_data_' + track).attr('style', "left:-116px; top: -23px;"); - $('#img_data_' + track).attr('height', rec.height); - // XXXX use width in some way? - // $('#img_data_' + track).attr('width', rec.width); - $('#img_data_' + track).attr('width', $('#img_data_ruler').width()); - $('#img_data_' + track).attr('src', rec.img); - $('#td_data_' + track + ' > div').each(function(index) { - if(index == 1) { - var style = $(this).attr('style'); - style = style.replace(/height:\s*\d+/i, "height:" + rec.height); - $(this).attr('style', style); - } - }); - var style = $('#p_btn_' + track).attr('style'); - style = style.replace(/height:\s*\d+/i, "height:" + rec.height); - $('#p_btn_' + track).attr('style', style); - if(hgTracks.trackDb[track].loadingId) { - hideLoadingImage(hgTracks.trackDb[track].loadingId); - } + ////////////////////// + //// 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"). + $("input[name=hgt_tsPage]").val(0); // NOTE: must match TRACK_SEARCH_PAGER in hg/inc/searchTracks.h + $('#trackSearch').submit(); + // This doesn't work with IE or Safari. + // $('#searchSubmit').click(); + } + }, + + init: function () + { + // Track search uses tabs + if($("#tabs").length > 0) { + // Search page specific code + + var val = $('#currentTab').val(); + $("#tabs").tabs({ + show: function(event, ui) { + $('#currentTab').val(ui.panel.id); + }, + select: function(event, ui) { findTracksSwitchTabs(ui); } + }); + $('#tabs').show(); + $("#tabs").tabs('option', 'selected', '#' + val); + if(val =='simpleTab' && $('div#found').length < 1) { + $('input#simpleSearch').focus(); } + $("#tabs").css('font-family', jQuery('body').css('font-family')); + $("#tabs").css('font-size', jQuery('body').css('font-size')); + $('.submitOnEnter').keydown(trackSearch.searchKeydown); + findTracksNormalize(); + updateMetaDataHelpLinks(0); } } } - function searchKeydown(event) - { - if (event.which == 13) { - // Required to fix problem on IE and Safari where value of hgt_tSearch is "-" (i.e. not "Search"). - $("input[name=hgt_tsPage]").val(0); // NOTE: must match TRACK_SEARCH_PAGER in hg/inc/searchTracks.h - $('#trackSearch').submit(); - // This doesn't work with IE or Safari. - // $('#searchSubmit').click(); - } - } - function windowOpenFailedMsg() - { - alert("Your web browser prevented us from opening a new window.\n\nPlease change your browser settings to allow pop-up windows from " + document.domain + "."); - } + /* + + // wait for jStore to prepare the storage engine (this token reload code is experimental and currently dead). + jQuery.jStore && jQuery.jStore.ready(function(engine) { + // alert(engine.jri); + // wait for the storage engine to be ready. + engine.ready(function(){ + var engine = this; + var newToken = hgTracks.time; + if(newToken) { + var oldToken = engine.get("token"); + if(oldToken && oldToken == newToken) { + // user has hit the back button. + jQuery('body').css('cursor', 'wait'); + window.location = "../cgi-bin/hgTracks?hgsid=" + getHgsid(); + } + } + engine.set("token", newToken); + }); + }); + + */ - function updateVisibility(track, visibility) + /////////////// + //// READY //// + /////////////// + $(document).ready(function() { - // Updates visibility state in hgTracks.trackDb and any visible elements on the page. - // returns true if we modify at least one select in the group list - var rec = hgTracks.trackDb[track]; - var selectUpdated = false; - $("select[name=" + track + "]").each(function(t) { - $(this).attr('class', visibility == 'hide' ? 'hiddenText' : 'normalText'); - $(this).val(visibility); - selectUpdated = true; - }); - if(rec) { - rec.localVisibility = visibility; + // The page may be reached via browser history (back button) + // If so, then this code should detect if the image has been changed via js/ajax + // and will reload the image if necessary. + // NOTE: this is needed for IE but other browsers can detect the dirty page much earlier + if (imageV2.isDirtyPage()) { + // mark as non dirty to avoid infinite loop in chrome. + $('#dirty').val('false'); + jQuery('body').css('cursor', 'wait'); + window.location = "../cgi-bin/hgTracks?hgsid=" + getHgsid(); + return false; } - return selectUpdated; - } + initVars(); - function getVisibility(track) - { - // return current visibility for given track - var rec = hgTracks.trackDb[track]; - if(rec) { - if(rec.localVisibility) { - return rec.localVisibility; - } else { - return visibilityStrsOrder[rec.visibility]; + var db = getDb(); + suggestBox.init(db); + + if(jQuery.jStore) { + // Experimental code to handle "user hits back button" problem by reloading the page based on the user's cart + if(jQuery.browser.msie && jQuery.browser.version < 8) { + // IE 7 requires flash to support jStore. + jQuery.extend(jQuery.jStore.defaults, { + project: 'hgTracks', + engine: 'flash', + flash: '/jStore.Flash.html' + }); } - } else { - return null; + jQuery.jStore.load(); } - } - function makeSureSuggestTrackIsVisible() - { - // make sure to show knownGene/refGene track is in at least pack (redmine #3484). - var track = $("#suggestTrack").val(); - if(track != null && getVisibility(track) != "full") { - updateVisibility(track, 'pack'); - $("<input type='hidden' name='" + track + "'value='pack'>").appendTo($(document.TrackHeaderForm)); - } - } - function reloadFloatingItem() - { - // currently dead (experimental code) - if(floatingMenuItem) { - $('#img_data_' + floatingMenuItem).parent().makeFloat({x:"current",y:"current", speed: 'fast', alwaysVisible: true, alwaysTop: true}); + // Convert map AREA gets to post the form, ensuring that cart variables are kept up to date (but turn this off for search form). + if($("FORM").length > 0 && $('#trackSearch').length == 0) { + var allLinks = $('a'); + $( allLinks ).unbind('click'); + $( allLinks ).click( posting.saveSettings ); } - } - - function handleZoomCodon(response, status) - { - var json = eval("(" + response + ")"); - if(json.pos) { - setPosition(json.pos, 3); - if(document.TrackForm) - document.TrackForm.submit(); - else - document.TrackHeaderForm.submit(); - } else { - alert(json.error); + if($('#pdfLink').length == 1) { + $('#pdfLink').click(function(i) { + var thisForm=$('#TrackForm'); + if(thisForm != undefined && $(thisForm).length == 1) { + //alert("posting form:"+$(thisForm).attr('name')); + updateOrMakeNamedVariable($(thisForm),'hgt.psOutput','on'); + return postTheForm($(thisForm).attr('name'),this.href); + } + return true; + }); } - } - function navigateButtonClick(ele) - { - // code to update just the imgTbl in response to navigation buttons (zoom-out etc.). - // This is currently experimental code (controlled by IN_PLACE_UPDATE in imageV2.h). - if(inPlaceUpdate) { - var params = ele.name + "=" + ele.value; - $(ele).attr('disabled', 'disabled'); - // dinking navigation needs additional data - if(ele.name == "hgt.dinkLL" || ele.name == "hgt.dinkLR") { - params += "&dinkL=" + $("input[name='dinkL']").val(); - } else if(ele.name == "hgt.dinkRL" || ele.name == "hgt.dinkRR") { - params += "&dinkR=" + $("input[name='dinkR']").val(); - } - navigateInPlace(params, $(ele), false); - return false; - } else { - return true; - } - } + if(imageV2.enabled) { - function navigateInPlace(params, disabledEle, keepCurrentTrackVisible) - { - // request an hgTracks image, using params - // disabledEle is optional; this element will be enabled when update is complete - // If keepCurrentTrackVisible is true, we try to maintain relative position of the item under the mouse after the in-place update. - jQuery('body').css('cursor', ''); - var currentId, currentIdYOffset; - if(keepCurrentTrackVisible) { - var item = currentMapItem || lastMapItem; - if(item) { - var top = $("#tr_" + item.id).position().top; - if(top >= $(window).scrollTop() || top < $(window).scrollTop() + $(window).height()) { - // don't bother if the item is not currently visible. - currentId = item.id; - currentIdYOffset = top - $(window).scrollTop(); - } + // Make imgTbl allow drag reorder of imgTrack rows + dragReorder.init(); + var imgTable = $(".tableWithDragAndDrop"); + if($(imgTable).length > 0) { + $(imgTable).tableDnD({ + onDragClass: "trDrag", + dragHandle: "dragHandle", + scrollAmount: 40, + onDragStart: function(ev, table, row) { + mouse.saveOffset(ev); + $(document).bind('mousemove',posting.blockTheMapOnMouseMove); + + // Can drag a contiguous set of rows if dragging blue button + table.tableDnDConfig.dragObjects = [ row ]; // defaults to just the one + var btn = $( row ).find('p.btnBlue'); // btnBlue means cursor over left button + if (btn.length == 1) { + table.tableDnDConfig.dragObjects = dragReorder.getContiguousRowSet(row); + var compositeSet = dragReorder.getCompositeSet(row); + if (compositeSet && compositeSet.length > 0) + $( compositeSet ).find('p.btn').addClass('blueButtons'); // blue persists + } + }, + onDrop: function(table, row, dragStartIndex) { + var compositeSet = dragReorder.getCompositeSet(row); + if (compositeSet && compositeSet.length > 0) + $( compositeSet ).find('p.btn').removeClass('blueButtons'); // blue persists + if($(row).attr('rowIndex') != dragStartIndex) { + // NOTE Even if dragging a contiguous set of rows, + // still only need to check the one under the cursor. + if(dragReorder.setOrder) { + dragReorder.setOrder(table); + } + dragReorder.zipButtons( table ); + } + $(document).unbind('mousemove',posting.blockTheMapOnMouseMove); + setTimeout('posting.allowMapClicks();',50); // Necessary incase the onDrop was over a map item. onDrop takes precedence. + } + }); } + + // Drag scroll init + if(hgTracks.imgBoxPortal) { + // 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(); } - $.ajax({ - type: "GET", - url: "../cgi-bin/hgTracks", - data: params + "&hgt.trackImgOnly=1&hgt.ideogramToo=1&hgsid=" + getHgsid(), - dataType: "html", - trueSuccess: handleUpdateTrackMap, - success: catchErrorOrDispatch, - error: errorHandler, - cmd: 'wholeImage', - loadingId: showLoadingImage("imgTbl"), - disabledEle: disabledEle, - currentId: currentId, - currentIdYOffset: currentIdYOffset, - cache: false - }); - } - function updateButtonClick(ele) - { - // code to update the imgTbl based on changes in the track controls. - // This is currently experimental code and is dead in the main branch. - if(mapIsUpdateable) { - var data = ""; - $("select").each(function(index, o) { - var cmd = $(this).val(); - if(cmd == "hide") { - if(hgTracks.trackDb[this.name] != undefined) { - alert("Need to implement hide"); - } - } else { - if(hgTracks.trackDb[this.name] == undefined || cmd != visibilityStrsOrder[hgTracks.trackDb[this.name].visibility]) { - if(data.length > 0) { - data = data + "&"; - } - data = data + this.name + "=" + cmd; - } - } - }); - if(data.length > 0) { - navigateInPlace(data, null, false); + // Drag select in chromIdeogram + if($('img#chrom').length == 1) { + if($('area.cytoBand').length > 1) { + $('img#chrom').chromDrag(); } - return false; - } else { - return true; } - } - function handleChangePosition(response, status) - { - var json = eval("(" + response + ")"); - setPosition(json.pos); - } + // Track search uses tabs + trackSearch.init(); - function changeAssemblies(ele) - { - // code to update page when user changes assembly select list. - $.ajax({ - type: "GET", - url: "../cgi-bin/hgApi", - data: "cmd=defaultPos&db=" + getDb(), - dataType: "html", - trueSuccess: handleChangePosition, - success: catchErrorOrDispatch, - error: errorHandler, - cache: true - }); - return false; - } + // Drag select initialize + if (imageV2.enabled) { // moved from window.load(). + dragSelect.load(true); + + if($('#hgTrackUiDialog')) + $('#hgTrackUiDialog').hide(); + + // Don't load contextMenu if jquery.contextmenu.js hasn't been loaded + if (jQuery.fn.contextMenu) { + rightClick.load(imageV2.imgTbl); + } + } -}); ++});