afbb996b456d1b16740c5251f2605cf0490fc267 tdreszer Mon May 19 13:38:57 2014 -0700 Polished for jshint, which uncovered a couple minor bugs. Minimal reformatting. Plan to check in a reformatted version of the file soon. diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index 69e6f62..7c7bfd9 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -1,71 +1,74 @@ // Javascript for use in hgTracks CGI // "use strict"; +// Don't complain about line break before '||' etc: +/* jshint -W014 */ + var debug = false; 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 revCmplDisp // true if we are in reverse display * int insideX // width of side-bar (in pixels) * int rulerClickHeight // height of ruler (in pixels) - zero if ruler is hidden * boolean inPlaceUpdate // true if in-place-update is turned on * int imgBox* // various drag-scroll values * boolean measureTiming // true if measureTiming is on * Object trackDb // hash of trackDb entries for tracks which are visible on current page */ function initVars() { // There are various entry points, so we call initVars in several places to make sure all is well - if (typeof(hgTracks) != "undefined" && !genomePos.original) { + if (typeof(hgTracks) !== "undefined" && !genomePos.original) { // remember initial position and size so we can restore it if user cancels genomePos.original = genomePos.getOriginalPos(); genomePos.originalSize = $('#size').text(); 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); + // imageV2.enabled === true unless: advancedJavascript===false, or trackSearch, or config pg + imageV2.enabled = (imageV2.imgTbl && 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) { + 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"; } 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). imageV2.mapIsUpdateable = false; - var reg = new RegExp("Version\/(\[0-9]+\.\[0-9]+) Safari"); + 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) { imageV2.mapIsUpdateable = true; } } } } imageV2.inPlaceUpdate = hgTracks.inPlaceUpdate && imageV2.mapIsUpdateable; } } ///////////////////////////////////// ////////// Genomic position ///////// @@ -86,109 +89,112 @@ } } }, setByCoordinates: function (chrom, start, end) { var newPosition = chrom + ":" + commify(start) + "-" + commify(end); genomePos.set(newPosition, commify(end - start + 1)); return newPosition; }, getElement: function () { // Return position box object var tags = document.getElementsByName("position"); - // There are multiple tags with name == "position" (the visible position text input + // 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") { + if (ele.id !== "positionHidden") { return ele; } } return null; }, get: function () { // Return current value of position box var ele = genomePos.getElement(); - if(ele != null) { + if (ele) { return ele.value; } return null; }, getOriginalPos: function () { return genomePos.original || genomePos.get(); }, revertToOriginalPos: function () { // undo changes to position (i.e. after user aborts a drag-and-select). this.set(this.original, this.originalSize); this.original = this.originalSize = null; // not sure if this is necessary. }, 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" + // 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($('#positionDisplay').length) { $('#positionDisplay').text(position); } if(size) { $('#size').text(size); } var pos = parsePosition(position); if(pos) { // fixup external static links on page' // Example ensembl link: http://www.ensembl.org/Homo_sapiens/contigview?chr=21&start=33031934&end=33041241 genomePos.linkFixup(pos, "ensemblLink", new RegExp("(.+start=)[0-9]+"), "end"); // Example NCBI link: http://www.ncbi.nlm.nih.gov/mapview/maps.cgi?taxid=9606&CHR=21&BEG=33031934&END=33041241 genomePos.linkFixup(pos, "ncbiLink", new RegExp("(.+BEG=)[0-9]+"), "END"); // Example medaka link: http://utgenome.org/medakabrowser_ens_jump.php?revision=version1.0&chr=chromosome18&start=14435198&end=14444829 genomePos.linkFixup(pos, "medakaLink", new RegExp("(.+start=)[0-9]+"), "end"); + var link; + var reg; + var a; 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); + link = $('#wormbaseLink').attr('href'); + reg = new RegExp("(.+:)[0-9]+"); + 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); + link = $('#dnaLink').attr('href'); + reg = new RegExp("(.+&o=)[0-9]+.+&db=[^&]+(.*)"); + 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); } } } if (!imageV2.backSupport) 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(); @@ -203,44 +209,46 @@ 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 + var x1; if(hgTracks.revCmplDisp) { - var x1 = Math.min(imgWidth, selStart); + x1 = Math.min(imgWidth, selStart); startDelta = Math.floor(mult * (imgWidth - x1)); } else { - var x1 = Math.max(hgTracks.insideX, selStart); + x1 = Math.max(hgTracks.insideX, selStart); startDelta = Math.floor(mult * (x1 - hgTracks.insideX)); } var endDelta; + var x2; if(hgTracks.revCmplDisp) { endDelta = startDelta; - var x2 = Math.min(imgWidth, selEnd); + x2 = Math.min(imgWidth, selEnd); startDelta = Math.floor(mult * (imgWidth - x2)); } else { - var x2 = Math.max(hgTracks.insideX, selEnd); + 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}; }, selectionPixelsToBases: function (img, selection) { // Convert selection x1/x2 coordinates to chromStart/chromEnd. return genomePos.pixelsToBases(img, selection.x1, selection.x2, hgTracks.winStart, hgTracks.winEnd); }, @@ -250,170 +258,170 @@ 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 + ")"); + var json = JSON.parse(response); genomePos.set(json.pos); }, changeAssemblies: function (ele) // UNUSED? Larry's experimental code { // code to update page when user changes assembly select list. $.ajax({ type: "GET", url: "../cgi-bin/hgApi", data: cart.varsToUrlData({ 'cmd': 'defaultPos', 'db': getDb() }), dataType: "html", trueSuccess: genomePos.handleChange, success: catchErrorOrDispatch, error: errorHandler, cache: true }); return false; } -} +}; ///////////////////////////////////// //// Creating items by dragging ///// ///////////////////////////////////// var makeItemsByDrag = { end: function (img, selection) { var image = $(img); var imageId = image.attr('id'); var trackName = imageId.substring('img_data_'.length); var pos = genomePos.selectionPixelsToBases(image, selection); var command = document.getElementById('hgt_doJsCommand'); command.value = "makeItems " + trackName + " " + hgTracks.chromName; command.value += " " + pos.chromStart + " " + pos.chromEnd; document.TrackHeaderForm.submit(); return true; }, 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) { + if (img && 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})); } }, load: function () { for (var id in hgTracks.trackDb) { var rec = hgTracks.trackDb[id]; - if(rec.type != null && rec.type.indexOf("makeItems") == 0) { + if (rec && rec.type && rec.type.indexOf("makeItems") === 0) { this.init(id); } } } -} +}; /////////////////// ///// 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) { + if (arguments.length === 2) { var num = Number(arguments[1]); if(isNaN(num)) { - if ( arguments[1].toLowerCase() == "x" ) + if ( arguments[1].toLowerCase() === "x" ) return (movedX > minPixels || movedX < (minPixels * -1)); - if ( arguments[1].toLowerCase() == "y" ) + 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)); } -} +}; ///////////////// //// posting //// ///////////////// var posting = { blockUseMap: false, blockMapClicks: function () // Blocks clicking on map items when in effect. Drag opperations frequently call this. { posting.blockUseMap=true; }, allowMapClicks:function () // reallows map clicks. Called after operations that compete with clicks (e.g. dragging) { $('body').css('cursor', ''); // Explicitly remove wait cursor. posting.blockUseMap=false; }, mapClicksAllowed: function () // Verify that click-competing operation (e.g. dragging) isn't currently occurring. { - return (posting.blockUseMap == false); + return (posting.blockUseMap === false); }, blockTheMapOnMouseMove: function (ev) { if (!posting.blockUseMap && mouse.hasMoved(ev)) { posting.blockUseMap=true; } }, mapClk: function () { var done = false; if(false && imageV2.inPlaceUpdate) { // XXXX experimental and only turned on in larrym's tree. // Use in-place update if the map item just modifies the current position (this is nice because it's faster // and it preserves the users current relative position in the track image). // // First test handles next/prev item. - var str = "/cgi-bin/hgTracks\\?position=([^:]+):(.+)&hgsid=(\\d+)&(hgt\.(next|prev)Item=[^&]+)"; + var str = "/cgi-bin/hgTracks\\?position=([^:]+):(.+)&hgsid=(\\d+)&(hgt.(next|prev)Item=[^&]+)"; var reg = new RegExp(str); var a = reg.exec(this.href); - if(a && a[1] && a[1] == hgTracks.chromName) { + if (a && a[1] && a[1] === hgTracks.chromName) { imageV2.navigateInPlace("position=" + encodeURIComponent(a[1] + ":" + a[2]) + "&" + a[4], null, true); done = true; } else { // handle next/prev exon str = "/cgi-bin/hgTracks\\?position=([^:]+):(.+)&hgsid=(\\d+)$"; reg = new RegExp(str); a = reg.exec(this.href); if(a && a[1]) { imageV2.navigateInPlace("position=" + encodeURIComponent(a[1] + ":" + a[2]), null, true); done = true; } else { // handle toggle visibility. Request may include a track set, so we cannot use requestImgUpdate. str = "/cgi-bin/hgTracks\\?(position=[^:]+:.+&hgsid=\\d+&([^=]+)=([^&]+))$"; reg = new RegExp(str); a = reg.exec(this.href); @@ -421,78 +429,76 @@ imageV2.navigateInPlace(a[1], null, true); // imageV2.requestImgUpdate(a[1], a[1] + "=" + a[2], "", a[2]); done = true; } } } } if(done) return false; else return posting.saveSettings(this); }, saveSettings: function (obj) { - if(posting.blockUseMap==true) { + if (posting.blockUseMap === true) { return false; } - if(obj == undefined || obj.href == undefined) // called directly with obj + if (!obj || !obj.href) // called directly with obj obj = this; // and from callback without obj if ($(obj).hasClass('noLink')) // TITLE_BUT_NO_LINK return false; 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) { + var thisForm = normed($(obj).parents('form').first()); + if (!thisForm) + thisForm = normed($("FORM").first()); + if (thisForm) { //alert("posting form:"+$(thisForm).attr('name')); return postTheForm($(thisForm).attr('name'),cart.addUpdatesToUrl(obj.href)); } return true; } -} +}; ///////////////////////// //// cart updating ///// /////////////////////// var cart = { // Controls queuing and ultimately updating cart variables vis ajax or submit. Queued vars // are held in an object with unique keys preventing duplicate updates and ensuring last update // takes precedence. WARNING: be careful creating an object with variables on the fly: // cart.setVarsObj({track: vis}) is invalid but cart.setVarsObj({'knownGene': vis}) is ok! updateQueue: {}, updatesWaiting: function () { // returns TRUE if updates are waiting. return objNotEmpty(cart.updateQueue); }, addUpdatesToUrl: function (url) { // adds any outstanding cart updates to the url, then clears the queue if (cart.updatesWaiting()) { //console.log('cart.addUpdatesToUrl: '+objKeyCount(cart.updateQueue)+' vars'); var updates = cart.varsToUrlData(); // clears the queue - if (typeof url === 'undefined' || url.length === 0) + if (!url || url.length === 0) return updates; if (updates.length > 0) { var dataOnly = (url.indexOf("cgi-bin") === -1); // all urls should be to our cgis if (!dataOnly && url.lastIndexOf("?") === -1) url += "?" + updates; else url += '&' + updates; } } return url; }, beforeUnload: function () { // named function that can be bound and unbound to beforeunload event @@ -545,74 +551,73 @@ // NOTE: could update in background, however, failing to hit "refresh" is a user choice // first in queue, schedule background update if (objKeyCount(cart.updateQueue) === 1) { // By unbind/bind, we assure that there is only one instance bound $(window).unbind('beforeunload', cart.beforeUnload); $(window).bind( 'beforeunload', cart.beforeUnload); } } } }, addVarsToQueue: function (names,values) { // creates a string of updates to save for ajax batch or a submit cart.queueVarsObj(arysToObj(names,values)); - }, - } +}; /////////////////////////////////////////////// //// 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=" + escapeJQuerySelectorChars(track) + "]").each(function(t) { - $(this).attr('class', visibility == 'hide' ? 'hiddenText' : 'normalText'); + $(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 { return vis.enumOrder[rec.visibility]; } } else { return null; } }, makeTrackVisible: function (track) { - if(track != null && vis.get(track) != "full") { + if (track && vis.get(track) !== "full") { vis.update(track, '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); }, @@ -621,98 +626,98 @@ { // 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(); $(".toggleButton[id$='_button']").each( function (i) { // works for old img type AND new BUTTONS_BY_CSS vis.toggleForGroup(this,this.id.substring(0,this.id.length - 7),newState); // clip '_button' suffix }); return false; }, initForAjax: function() { // To better support the back-button, it is good to eliminate extraneous form puts // Towards that end, we support visBoxes making ajax calls to update cart. var sels = $('select.normalText,select.hiddenText'); $(sels).change(function() { var track = $(this).attr('name'); - if ($(this).val() == 'hide') { + if ($(this).val() === 'hide') { var rec = hgTracks.trackDb[track]; if(rec) rec.visibility = 0; // else Would be nice to hide subtracks as well but that may be overkill $(document.getElementById('tr_' + track)).remove(); imageV2.highlightRegion(); $(this).attr('class', 'hiddenText'); } else $(this).attr('class', 'normalText'); cart.addVarsToQueue([track], [$(this).val()]); imageV2.markAsDirtyPage(); return false; }); // Now we can rid the submt of the burden of all those vis boxes var form = $('form#TrackForm'); $(form).submit(function () { $('select.normalText,select.hiddenText').attr('disabled',true); }); $(form).attr('method','get'); }, restoreFromBackButton: function() // Re-enabling vis dropdowns is necessarty because intiForAjax() disables them on submit. { $('select.normalText,select.hiddenText').attr('disabled',false); } +}; -} //////////////////////////////////////////////////////////// // dragSelect is also known as dragZoom or shift-dragZoom // //////////////////////////////////////////////////////////// var dragSelect = { areaSelector: null, // formerly "imgAreaSelect". jQuery element used for imgAreaSelect originalCursor: null, startTime: null, selectStart: function (img, selection) { initVars(); if(rightClick.menu) { rightClick.menu.hide(); } var now = new Date(); dragSelect.startTime = now.getTime(); posting.blockMapClicks(); }, selectChange: function (img, selection) { - if(selection.x1 != selection.x2) { + if (selection.x1 !== selection.x2) { if (genomePos.check(img, selection)) { genomePos.update(img, selection, false); jQuery('body').css('cursor', dragSelect.originalCursor); } else { jQuery('body').css('cursor', 'not-allowed'); } } return true; }, highlightThisRegion: function(newPosition) // set highlighting newPosition in server-side cart and apply the highlighting in local UI. { var start, end; - if (arguments.length == 2) { + if (arguments.length === 2) { start = arguments[0]; end = arguments[1]; } else { var pos = parsePosition(newPosition); start = pos.start; end = pos.end; } hgTracks.highlight = getDb() + "." + hgTracks.chromName + ":" + start + "-" + end; hgTracks.highlight += '#AAFFFF'; // Also include highlight color // we include enableHighlightingDialog because it may have been changed by the dialog cart.setVarsObj({ 'highlight': hgTracks.highlight, 'enableHighlightingDialog': hgTracks.enableHighlightingDialog ? 1 : 0 }); imageV2.highlightRegion(); }, @@ -732,31 +737,31 @@ title: "Drag-and-select", closeOnEscape: true, resizable: false, autoOpen: false, revertToOriginalPos: true, minWidth: 400, buttons: { "Zoom In": function() { // Zoom to selection $(this).dialog("option", "revertToOriginalPos", false); if ($("#disableDragHighlight").attr('checked')) hgTracks.enableHighlightingDialog = false; if (imageV2.inPlaceUpdate) { var params = "position=" + newPosition; if (!hgTracks.enableHighlightingDialog) - params += "&enableHighlightingDialog=0" + params += "&enableHighlightingDialog=0"; imageV2.navigateInPlace(params, null, true); } else { $('body').css('cursor', 'wait'); if (!hgTracks.enableHighlightingDialog) cart.setVarsObj({'enableHighlightingDialog': 0 }); document.TrackHeaderForm.submit(); } $(this).dialog("close"); }, "Highlight": function() { // Highlight selection $(imageV2.imgTbl).imgAreaSelect({hide:true}); if ($("#disableDragHighlight").attr('checked')) hgTracks.enableHighlightingDialog = false; dragSelect.highlightThisRegion(newPosition); @@ -777,154 +782,154 @@ genomePos.revertToOriginalPos(); if($("#disableDragHighlight").attr('checked')) $(this).remove(); else $(this).hide(); $('body').css('cursor', ''); // Occasionally wait cursor got left behind } }); $(dragSelectDialog).dialog('open'); }, selectEnd: function (img, selection) { var now = new Date(); var doIt = false; - if(dragSelect.originalCursor != null) + if (dragSelect.originalCursor) jQuery('body').css('cursor', dragSelect.originalCursor); // ignore releases outside of the image rectangle (allowing a 10 pixel slop) if (genomePos.check(img, selection)) { // ignore single clicks that aren't in the top of the image // (this happens b/c the clickClipHeight test in dragSelect.selectStart // doesn't occur when the user single clicks). - doIt = dragSelect.startTime != null || selection.y1 <= hgTracks.rulerClickHeight; + 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 + 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 (newPosition) { if (hgTracks.enableHighlightingDialog) dragSelect.selectionEndDialog(newPosition); else { $(imageV2.imgTbl).imgAreaSelect({hide:true}); if (imageV2.inPlaceUpdate) { imageV2.navigateInPlace("position=" + newPosition, null, true); } else { jQuery('body').css('cursor', 'wait'); document.TrackHeaderForm.submit(); } } } } else { $(imageV2.imgTbl).imgAreaSelect({hide:true}); genomePos.revertToOriginalPos(); } dragSelect.startTime = null; // blockMapClicks/allowMapClicks() is necessary if selectEnd was over a map item. - setTimeout('posting.allowMapClicks();',50); + setTimeout(posting.allowMapClicks,50); return true; }, load: function (firstTime) { var imgHeight = 0; if (imageV2.enabled) imgHeight = imageV2.imgTbl.innerHeight() - 1; // last little bit makes border look ok // No longer disable without ruler, because shift-drag still works - if(typeof(hgTracks) != "undefined") { + if (typeof(hgTracks) !== "undefined") { - if (hgTracks.rulerClickHeight == undefined || hgTracks.rulerClickHeight == null) + if (hgTracks.rulerClickHeight === undefined || hgTracks.rulerClickHeight === null) hgTracks.rulerClickHeight = 0; // will be zero if no ruler track var heights = hgTracks.rulerClickHeight; dragSelect.areaSelector = jQuery((imageV2.imgTbl).imgAreaSelect({ selectionColor: 'blue', outerColor: '', minHeight: imgHeight, maxHeight: imgHeight, onSelectStart: dragSelect.selectStart, onSelectChange: dragSelect.selectChange, onSelectEnd: dragSelect.selectEnd, autoHide: false, // gets hidden after possible dialog movable: false, clickClipHeight: heights })); } } -} +}; ///////////////////////////////////// //// Chrom Drag/Zoom/Expand code //// ///////////////////////////////////// jQuery.fn.chromDrag = function(){ this.each(function(){ // Plan: // mouseDown: determine where in map: convert to img location: pxDown // mouseMove: flag drag // 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 pxDown = 0; // pix X location of mouseDown var chrImg = $(this); var mouseIsDown = false; var mouseHasMoved = false; var hilite = null; initialize(); function initialize(){ findDimensions(); - if(chr.top == -1) + if (chr.top === -1) warn("chromIdeo(): failed to register "+this.id); else { hiliteSetup(); $('area.cytoBand').unbind('mousedown'); // Make sure this is only bound once $('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 + 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)) { + 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 @@ -956,163 +961,165 @@ if ( isWithin(chr.right,pxUp,img.width + 20) ) pxUp = chr.right; 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 + 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) + if (mouseHasMoved === false) { // Update highlight by converting bp back to pix + pxDown = convertFromBases(selRange.beg); + pxUp = convertFromBases(selRange.end); hiliteShow(pxDown,pxUp); } //if ((selRange.end - selRange.beg) < 50000) // dontAsk = true; if (dontAsk || confirm("Jump to new position:\n\n"+chr.name+":"+commify(selRange.beg)+ "-"+commify(selRange.end)+" size:"+commify(selRange.width)) ) { - genomePos.setByCoordinates(chr.name, selRange.beg, selRange.end) + genomePos.setByCoordinates(chr.name, selRange.beg, selRange.end); $('area.cytoBand').mousedown( function(e) { return false; }); // Stop the presses :0) if (imageV2.backSupport) { imageV2.navigateInPlace("position=" + encodeURIComponent(genomePos.get().replace(/,/g,'')), null, true); hiliteCancel(); } else 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('posting.allowMapClicks();',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) { + 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) chr.left = myLeft; if (chr.right < parseInt(loc[2])) chr.right = parseInt(loc[2]); } - var range = this.title.substr(this.title.lastIndexOf(':')+1) + var range = this.title.substr(this.title.lastIndexOf(':')+1); var pos = range.split('-'); - if(pos.length == 2) { - if (chr.name.length == 0) { + if (pos.length === 2) { + 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(':')) + this.title.lastIndexOf(':')); } else { if (chr.beg > parseInt(pos[0])) chr.beg = parseInt(pos[0]); } if (chr.end < parseInt(pos[1])) { chr.end = parseInt(pos[1]); - if(lastX == -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) + } 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) { + if (loc.length === 4) { var myLeft = parseInt(loc[0]); var myRight = parseInt(loc[2]); - if(cyto.left == -1 || cyto.left > myLeft) { + var range; + var pos; + if (cyto.left === -1 || cyto.left > myLeft) { 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) { + range = this.title.substr(this.title.lastIndexOf(':')+1); + 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 (cyto.right === -1 || cyto.right < 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) { + range = this.title.substr(this.title.lastIndexOf(':')+1); + 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; } @@ -1146,352 +1153,352 @@ begX = down + img.left; wide = (cur - down); } $(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) - if (hilite == null) { // setup only once + if (hilite === null) { // setup only once hilite = jQuery("
"); $(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 //// /////////////////////////// var dragReorder = { setOrder: function (table) { // Sets the 'order' value for the image table after a drag reorder var varsToUpdate = {}; $("tr.imgOrd").each(function (i) { - if ($(this).attr('abbr') != $(this).attr('rowIndex').toString()) { + if ($(this).attr('abbr') !== $(this).attr('rowIndex').toString()) { $(this).attr('abbr',$(this).attr('rowIndex').toString()); var name = this.id.substring('tr_'.length) + '_imgOrd'; varsToUpdate[name] = $(this).attr('abbr'); } }); if (objNotEmpty(varsToUpdate)) { cart.setVarsObj(varsToUpdate); imageV2.markAsDirtyPage(); } }, sort: function (table) { // Sets the table row order to match the order of the abbr attribute. // This is needed for back-button, and for visBox changes combined with refresh. var tbody = $(table).find('tbody')[0]; - if(tbody == undefined) + if (!tbody) tbody = table; // Do we need to sort? var trs = tbody.rows; var needToSort = false; $(trs).each(function(ix) { - if ($(this).attr('abbr') != $(this).attr('rowIndex').toString()) { + if ($(this).attr('abbr') !== $(this).attr('rowIndex').toString()) { needToSort = true; return false; // break for each() loops } }); if (!needToSort) return false; // Create array of tr holders to sort - var ary = new Array(); + var ary = []; $(trs).each(function(ix) { // using sortTable found in utils.js ary.push(new sortTable.field(parseInt($(this).attr('abbr')),false,this)); }); // Sort the array ary.sort(sortTable.fieldCmp); // most efficient reload of sorted rows I have found var sortedRows = jQuery.map(ary, function(ary, i) { return ary.row; }); $(tbody).append( sortedRows ); // removes tr from current position and adds to end. return true; }, showCenterLabel: function (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) + var center = normed($(tr).find(".sliceDiv.cntrLab")); + if (!center) return; - var seen = ($(center).css('display') != 'none'); - if(show == seen) + var 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 btn = normed($(tr).find("p.btn")); + var side = normed($(tr).find(".sliceDiv.sideLab")); + if (btn && side) { + var sideImg = normed($(side).find("img")); + if (sideImg) { 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(); } } } }, getContiguousRowSet: function (row) { // Returns the set of rows that are of the same class and contiguous - if(row == null) + if (!row) return null; var btn = $( row ).find("p.btn"); - if (btn.length == 0) + 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) + if (btn.length === 0) break; classList = $( btn ).attr("class").split(" "); - if (classList[0] != matchClass) + if (classList[0] !== matchClass) break; startIndex = ix; } // Find end index - for(var ix=endIndex;ix