9086229a906e088fbf13ae102794aa643aafb00f tdreszer Tue Aug 9 15:06:19 2011 -0700 Fixed some reentrant problems when image is dragScrolled and then ajax updated. While much of this is >1x specific, not all is. Also did some renaming to make functionality more clear. diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index bf25fe8..eb01104 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -945,60 +945,61 @@ // 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"); } } ///////////////////////////////////////////////////// // Drag Scroll code -jQuery.fn.panImages = function(imgOffset,imgBoxLeftOffset){ +jQuery.fn.panImages = function(){ // globals across all panImages - var leftLimit = imgBoxLeftOffset*-1; + originalPosition = getOriginalPosition(); + var leftLimit = hgTracks.imgBoxLeftLabel * -1; var rightLimit = (hgTracks.imgBoxPortalWidth - hgTracks.imgBoxWidth + leftLimit); - var only1xScrolling = (hgTracks.imgBoxPortalOffsetX == 0); - var prevX = (imgOffset + imgBoxLeftOffset)*-1; + var only1xScrolling = ((hgTracks.imgBoxWidth - hgTracks.imgBoxPortalWidth) == 0);//< hgTracks.imgBoxLeftLabel); + var prevX = (hgTracks.imgBoxPortalOffsetX + hgTracks.imgBoxLeftLabel) * -1; var portalWidth = 0; var savedPosition; - panAdjustHeight(prevX); 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(); + panAdjustHeight(prevX); // globals to one panImage var newX = 0; var mouseDownX = 0; var mouseIsDown = false; var beyondImage = false; var atEdge = false; initialize(); function initialize(){ if ( !($.browser.msie) ) // IE will override map items cursors as well! $(pan).parents('td.tdData').css('cursor',"url(../images/grabber.cur),w-resize"); pan.mousedown(function(e){ @@ -1060,60 +1061,65 @@ panAdjustHeight(newX); // NOTE: This will dynamically resize image while scrolling. Do we want to? } } } function panMouseUp(e) { // Must be a separate function instead of pan.mouseup event. //if(!e) e = window.event; if(mouseIsDown) { dragMaskClear(); $(document).unbind('mousemove',panner); $(document).unbind('mouseup',panMouseUp); mouseIsDown = false; setTimeout('blockUseMap=false;',50); // Necessary incase the selectEnd was over a map item. select takes precedence. // Outside image? Then abandon. - var curY = e.clientY; + var curY = e.pageY; var imgTbl = $('#imgTbl'); - var imgTop = $(imgTbl).position().top; - if (curY < imgTop || curY > imgTop + $(imgTbl).height()) { + 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); 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)); } 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) { - //if (!only1xScrolling) - // panAdjustHeight(newX); // NOTE: This will resize image after scrolling. Do we want to while scrolling? 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 portalWidthBases = hgTracks.imgBoxPortalEnd - hgTracks.imgBoxPortalStart; var portalScrolledX = (hgTracks.imgBoxPortalOffsetX+hgTracks.imgBoxLeftLabel) + newOffsetX; var recalculate = false; var newPortalStart = hgTracks.imgBoxPortalStart - Math.round(portalScrolledX*hgTracks.imgBoxBasesPerPixel); // As offset goes down, bases seen goes up! if( newPortalStart < hgTracks.chromStart && bounded) { // Stay within bounds newPortalStart = hgTracks.chromStart; @@ -1125,126 +1131,137 @@ newPortalStart = newPortalEnd - portalWidthBases; recalculate = true; } if(newPortalStart > 0) { // XXXX ? hgTracks.imgBoxPortalStart = newPortalStart; // XXXX ? hgTracks.imgBoxPortalEnd = newPortalEnd; var newPos = hgTracks.chromName + ":" + commify(newPortalStart) + "-" + commify(newPortalEnd); setPosition(newPos, (newPortalEnd - newPortalStart + 1)); } if (recalculate && hgTracks.imgBoxBasesPerPixel > 0) { // Need to recalculate X for bounding drag portalScrolledX = (hgTracks.imgBoxPortalStart - newPortalStart) / hgTracks.imgBoxBasesPerPixel; newOffsetX = portalScrolledX - (hgTracks.imgBoxPortalOffsetX+hgTracks.imgBoxLeftLabel); } return newOffsetX; } - function mapTopAndBottom(mapName,left,right) + function mapTopAndBottom(mapName,east,west) { // Find the top and bottom px given left and right boundaries - var span = { top: -10, bottom: -10 }; + 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 < right && aright >= left) { + if(aleft < west && aright >= east) { var atop = parseInt(loc[1]); var abottom = parseInt(loc[3]); - if( span.top < 0 ) { - span.top = atop; - span.bottom = abottom; - } else if(span.top > atop) { - span.top = atop; - } else if(span.bottom < abottom) { - span.bottom = abottom; + 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 span; + 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 left = newOffsetX * -1; - var right = left + portalWidth; + var east = newOffsetX * -1; + var west = east + portalWidth; $(".panImg").each(function(t) { - var mapid = "map_" + this.id.substring(4); + var mapid = this.id.replace('img_','map_'); var hDiv = $(this).parent(); - var top = parseInt($(this).css("top")) * -1; - var bottom = top + $(hDiv).height(); + var north = parseInt($(this).css("top")) * -1; + var south = north + $(hDiv).height(); - var span = mapTopAndBottom(mapid,left,right); - if(span.top > 0) { - var topdif = Math.abs(span.top - top); - var botdif = Math.abs(span.bottom - bottom); + 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( span.bottom - span.top ); - top = span.top * -1; - $(this).css( {'top': top.toString() + "px" }); + $(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(); - top += titlePx; + north += titlePx; } var side = $("#img_side_"+imgId[2]); if( side.length > 0) { - $(side).parent().height( span.bottom - span.top + titlePx); - $(side).css( {'top': top.toString() + "px" }); + $(side).parent().height( mapPortal.bottom - mapPortal.top + titlePx); + $(side).css( {'top': north.toString() + "px" }); } var btn = $("#p_btn_"+imgId[2]); if( btn.length > 0) { - $(btn).height( span.bottom - span.top + titlePx); + $(btn).height( mapPortal.bottom - mapPortal.top + titlePx); } else { btn = $("#img_btn_"+imgId[2]); if( btn.length > 0) { - $(btn).parent().height( span.bottom - span.top + titlePx); + $(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 waitMask to block page manipulation until cleared + 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) { $("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.4,backgroundColor:'gray',zIndex:999}); // temporarily so I can see it } - function dragMaskClear() { // Clears the waitMask + 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) { + $(dragMask).height( $(imgTbl).height() ); + } + } + + function dragMaskClear() { // Clears the dragMask $('body').css('cursor','auto') var dragMask = $('#dragMask'); if( dragMask != undefined ) $(dragMask).hide(); } }; ///////////////////////////////////////////////////// function saveMouseOffset(ev) { // Save the mouse offset associated with this event originalMouseOffset = {x: ev.clientX, y: ev.clientY}; @@ -1437,31 +1454,31 @@ 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(hgTracks.imgBoxPortalOffsetX,hgTracks.imgBoxLeftLabel); + $("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,"",""); }); @@ -2506,46 +2523,56 @@ } 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(hgTracks.imgBoxPortalOffsetX,hgTracks.imgBoxLeftLabel); + if(hgTracks.imgBoxPortal) { + $("div.scroller").panImages(); + } } 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]) { - $('#tr_' + id).html(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 } ); + } + } return true; } else { return 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");