3622f98b426357389568faa6c53a463918b19281 larrym Wed Jul 20 11:26:17 2011 -0700 more work on in-place updating (redmine #4667); also some code and comments cleanup diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index a6cb875..d3ae33e 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -9,31 +9,31 @@ var imageV2 = false; 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; +var supportZoomCodon = false; // turn on experimental zoom-to-codon functionality (currently only on in larrym's tree). 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(); @@ -226,30 +226,33 @@ $(window).load(function () { 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" && 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"; 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 @@ -1614,83 +1617,85 @@ } 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]; } else { reg = new RegExp("[&?]i=([^&]+)"); a = reg.exec(href); if(a && a[1]) { name = a[1]; } } - if(browser == "safari" || imageV2) { - // We need to parse out more stuff to support resetting the position under imageV2 via ajax, but it's probably possible. - // See comments below on safari problems. - 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 { - // XXXX This attempt to "update whole track image in place" didn't work for a variety of reasons, so this is dead code, but - // I'm leaving it in case we try to implement this functionality in the future. + if(false && mapIsUpdateable) { + // 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; + var num, ajaxCmd, msg; if(cmd == 'zoomCodon') { - num = prompt("Please enter the codon number to jump to:"); + msg = "Please enter the codon number to jump to:"; ajaxCmd = 'codonToPos'; } else { - num = prompt("Please enter the exon number to jump to:"); + msg = "Please enter the exon number to jump to:"; ajaxCmd = 'exonToPos'; } - if(num) { + myPrompt(msg, function(results) { $.ajax({ type: "GET", url: "../cgi-bin/hgApi", - data: "db=" + getDb() + "&cmd=" + ajaxCmd + "&num=" + num + "&table=" + args.table + "&name=" + args.name, + 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 = trackDbJson[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 @@ -1846,30 +1851,47 @@ 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); + } + } + }); +} + 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; }; } 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 + "' />"; } @@ -2413,39 +2435,30 @@ //var img = $('#tr_' + id).find("img[id^='img_data_']").attr('src'); //warn("Just parsed image:<BR>"+img); return true; } else { return false; } } function handleUpdateTrackMap(response, status) { // Handle ajax response with an updated trackMap image (gif or png) and map. // // 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. - // 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'> - 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]); - } - } // update local trackDbJson to reflect possible side-effects of ajax request. var re = /<\!-- trackDbJson -->\n<script>var trackDbJson = ([\S\s]+)<\/script>\n<\!-- trackDbJson -->/m; a = re.exec(response); if(a && a[1]) { var json = eval("(" + a[1] + ")"); if(json) { if(this.id != null) { if(json[this.id]) { var visibility = visibilityStrsOrder[json[this.id].visibility]; var limitedVis; if(json[this.id].limitedVis) limitedVis = visibilityStrsOrder[json[this.id].limitedVis]; if(this.newVisibility && limitedVis && this.newVisibility != limitedVis) alert("There are too many items to display the track in " + this.newVisibility + " mode."); var rec = trackDbJson[this.id]; @@ -2466,43 +2479,49 @@ if(this.loadingId) { hideLoadingImage(this.loadingId); } 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) { - // We update row's one at a time (updating the whole imgTable at one time doesn't work in IE). + // Implement in-place updating of hgTracks image + // + // We update rows one at a time (updating the whole imgTable at one time doesn't work in IE). for (id in trackDbJson) { 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(json.chromName, hgTracks.winStart + 1, hgTracks.winEnd); + setPositionByCoordinates(hgTracks.chromName, hgTracks.winStart + 1, hgTracks.winEnd); } 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); @@ -2528,30 +2547,40 @@ 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); } else { 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]); + } + } jQuery('body').css('cursor', ''); } 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(); } return; } @@ -2600,32 +2629,30 @@ $('#p_btn_' + track).attr('style', style); } } 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 + "."); } function updateVisibility(track, visibility) { // Updates visibility state in trackDbJson and any visible elements on the page. // returns true if we modify at least one select in the group list var rec = trackDbJson[track]; var selectUpdated = false; $("select[name=" + track + "]").each(function(t) { $(this).attr('class', visibility == 'hide' ? 'hiddenText' : 'normalText'); $(this).val(visibility); selectUpdated = true; @@ -2676,33 +2703,46 @@ setPosition(json.pos, 3); if(document.TrackForm) document.TrackForm.submit(); else document.TrackHeaderForm.submit(); } else { alert(json.error); } } function navigateButtonClick(ele) { // code to update just the imgTbl in response to navigation buttons (zoom-out etc.). // currently experimental code (live only in larrym's tree). if(mapIsUpdateable) { + var params = ele.name + "=" + ele.value; + // 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); + return false; + } else { + return true; + } +} + +function navigateInPlace(params) +{ +// request an hgTracks image, using params jQuery('body').css('cursor', ''); $.ajax({ type: "GET", url: "../cgi-bin/hgTracks", - data: ele.name + "=" + ele.value + "&hgt.trackImgOnly=1&hgt.ideogramToo=1&hgsid=" + getHgsid(), + data: params + "&hgt.trackImgOnly=1&hgt.ideogramToo=1&hgsid=" + getHgsid(), dataType: "html", trueSuccess: handleUpdateTrackMap, success: catchErrorOrDispatch, error: errorHandler, cmd: 'wholeImage', loadingId: showLoadingImage("imgTbl"), cache: false }); - return false; - } else { - return true; - } }