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;
-    }
 }