e0403e5eaf910cb8cbe1c9a1962bf36061dab8ef larrym Mon Jul 25 12:14:11 2011 -0700 add some comments; more in place update changes (only live in larrym's tree) diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index d3ae33e..54d6dd6 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -10,30 +10,43 @@ var imgBoxPortal = 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 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 + +/* Data passed in from CGI via the hgTracks object: + * + * string chromName // current chromosome + * int winStart // genomic start coordinate (0-based, half-open) + * int winEnd // genomic end coordinate + * int newWinWidth // new width if user clicks on the top ruler (in bps) + * 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) + */ function initVars(img) { // There are various entry points, so we call initVars in several places to make sure this variables get updated. if(!originalPosition) { // remember initial position and size so we can restore it if user cancels originalPosition = getOriginalPosition(); originalSize = $('#size').text(); originalCursor = jQuery('body').css('cursor'); } } function selectStart(img, selection) { initVars(); @@ -162,66 +175,71 @@ function selectionPixelsToBases(img, selection) // Convert selection x1/x2 coordinates to chromStart/chromEnd. { return pixelsToBases(img, selection.x1, selection.x2, hgTracks.winStart, hgTracks.winEnd); } 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; } - setPositionByCoordinates(hgTracks.chromName, pos.chromStart+1, pos.chromEnd); - return true; + var newPosition = setPositionByCoordinates(hgTracks.chromName, pos.chromStart+1, pos.chromEnd); + return newPosition; } 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'); } } 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.clickClipHeight; + doIt = startDragZoom != null || selection.y1 <= hgTracks.rulerClickHeight; } if(doIt) { // startDragZoom is null if mouse has never been moved - if(updatePosition(img, selection, (selection.x2 == selection.x1) || startDragZoom == null || (now.getTime() - startDragZoom) < 100)) { + newPosition = updatePosition(img, selection, (selection.x2 == selection.x1) || startDragZoom == null || (now.getTime() - startDragZoom) < 100); + if(newPosition != undefined) { + if(inPlaceUpdate) { + navigateInPlace("position=" + newPosition); + } else { jQuery('body').css('cursor', 'wait'); document.TrackHeaderForm.submit(); } + } } 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; } $(window).load(function () { jQuery.each(jQuery.browser, function(i, val) { @@ -233,50 +251,115 @@ if(browser == "safari" && 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"; } // 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). // // Chrome used to have this problem too, but this problem seems to have gone away as of // Chrome 5.0.335.1 (or possibly earlier). mapIsUpdateable = browser != "safari"; + inPlaceUpdate = hgTracks.inPlaceUpdate && mapIsUpdateable; loadImgAreaSelect(true); if($('#hgTrackUiDialog')) $('#hgTrackUiDialog').hide(); // 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(); } 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! + if(inPlaceUpdate) { + // Larry's experimental version of 1x panning (aka cheap panning). + var originalLeft; + var originalClientX; + var withinTrackImgTbl; + var last; + trackImgTbl.mousedown( + function (e) { + originalClientX = e.clientX; + withinTrackImgTbl = true; + last = new Date(); + jQuery('body').css('cursor', 'w-resize'); + $("img.dataImg").each( function(index, ele) { + var left = $(ele).css('left'); + originalLeft = left.substring(0, left.length - 2) + // we only need to look at one element, so bail out now. + return false; + }); + jQuery(document).one('mouseup', function (e) { + trackImgTbl.css('cursor', ''); + if(originalLeft != undefined) { + if(withinTrackImgTbl && Math.abs(originalClientX - e.clientX) > 20) { + originalLeft = undefined; + var imgWidth = trackImgTbl.width() - hgTracks.insideX; + var mult = (originalClientX - e.clientX) / imgWidth; + var width = hgTracks.winEnd - hgTracks.winStart; + hgTracks.winStart = hgTracks.winStart + Math.floor(width * mult); + hgTracks.winEnd = hgTracks.winEnd + Math.floor(width * mult); + setPositionByCoordinates(hgTracks.chromName, hgTracks.winStart + 1, hgTracks.winEnd) + navigateInPlace("position=" + encodeURIComponent(hgTracks.chromName + ":" + (hgTracks.winStart + 1) + "-" + hgTracks.winEnd)); + // return true in case of ajax error. + return true; + } else { + $("img.dataImg").each( function(index, ele) { + $(ele).css( {'left': originalLeft + "px" }); + }); + originalLeft = undefined; + return true; + } + } else { + return true; + } + }); + return true; + } + ); + trackImgTbl.mouseenter(function(e) { withinTrackImgTbl = true; }); + trackImgTbl.mouseleave(function(e) { withinTrackImgTbl = false; }); + trackImgTbl.mousemove( + function (e) { + var now = new Date(); + // date logic is an attempt to fix IE related weirdness + if(originalLeft != undefined && (now - last) > 100) { + last = now; + trackImgTbl.css('cursor', 'w-resize'); + $("img.dataImg").each( function(index, ele) { + var newPos = originalLeft - (originalClientX - e.clientX) + "px"; + $(ele).css( {'left': newPos }); + }); + } + return true; + } + ); + } } else { loadContextMenu(trackImg); trackImg.mousemove( function (e) { mapEvent(e); } ); trackImg.mousedown( function (e) { mapMouseDown(e); } ); } } }); @@ -2498,30 +2581,32 @@ if(!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 } } var json = scrapeVariable(response, "hgTracks"); if(json != undefined) { hgTracks.chromName = json.chromName; hgTracks.winStart = json.winStart; hgTracks.winEnd = json.winEnd; $("input[name='c']").val(json.chromName); $("input[name='l']").val(json.winStart); $("input[name='r']").val(json.winEnd); hgTracks.newWinWidth = json.newWinWidth; setPositionByCoordinates(hgTracks.chromName, hgTracks.winStart + 1, hgTracks.winEnd); + originalPosition = undefined; + initVars(); } else { showWarning("Couldn't parse out new position info"); } afterImgTblReload(); } 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);