8c908f948b09826c6cb4452ee5b282aca41be85e
galt
  Tue Dec 8 21:52:59 2015 -0800
Multi-region (exonMostly). This work allows people to look at virtual chromosomes from a list of regions and then navigate and perform all of the usual functions on it.

diff --git src/hg/js/utils.js src/hg/js/utils.js
index 73e83d2..6394fe2 100644
--- src/hg/js/utils.js
+++ src/hg/js/utils.js
@@ -781,36 +781,45 @@
                 } else {                        //     subtrack vis should be inherited.
                     urlData[name+"_sel"] = 1;
                     urlData[name]        = val;
                 }
             } else {
                 if ($.isArray( val) && val.length > 1) {
                     urlData[name] = "[" + val.toString() + "]";
                 } else
                     urlData[name] = val;
             }
         }
     });
     return urlData;
 }
 
+function debugDumpFormCollection(collectionName,vars)
+{ // dumps form vars collection in an alert
+    var debugStr = ""; 
+    for (var thisVar in vars) {
+	debugStr += thisVar + "==" + vars[thisVar]+"\n";
+    }
+    alert("DEBUG "+ collectionName + ":\n"+debugStr);  
+}
+
 function varHashChanges(newVars,oldVars)
 {   // Returns a hash of all vars that are changed between old and new hash.
     // New vars not found in old are changed.
     var changedVars = {};
     for (var newVar in newVars) {
-        if (!oldVars[newVar] || oldVars[newVar] !== newVars[newVar])
+        if (oldVars[newVar] === null || oldVars[newVar] !== newVars[newVar])
             changedVars[newVar] = newVars[newVar];
     }
     return changedVars;
 }
 
 function varHashToQueryString(varHash)
 {
 // return a CGI QUERY_STRING for name/vals in given object
     var retVal = "";
     var count = 0;
     for (var aVar in varHash) {
         if (count++ > 0) {
             retVal += "&";
         }
         var val = varHash[aVar];
@@ -1324,45 +1333,45 @@
         else if (endToken.length > 0)
             insideEnd = someString.indexOf(endToken,ixBeg);
         if (ixBeg <= insideBeg && insideBeg <= insideEnd && insideEnd <= ixEnd)
             return {start : insideBeg, stop : insideEnd};
 
         return {start : -1, stop : -1};
     },
 
     inside: function (begToken,endToken,someString,ixBeg,ixEnd)
     { // returns the inside bounds of 2 tokens within a string
     // Note ixBeg and ixEnd are optional bounds already established within string
     // Pattern match can be used instead of literal token if a regexp is passed in for the tokens
         var bounds = bindings._raw(begToken,endToken,someString,ixBeg,ixEnd);
         if (bounds.start > -1) {
             if (jQuery.type(begToken) === "regexp")
-                bounds.start += someString.match(begToken).length;
+                bounds.start += someString.match(begToken)[0].length;
             else
                 bounds.start += begToken.length;
         }
         return bounds;
     },
 
     outside: function (begToken,endToken,someString,ixBeg,ixEnd)
     { // returns the outside bounds of 2 tokens within a string
     // Note ixBeg and ixEnd are optional bounds already established within string
     // Pattern match can be used instead of literal token if a regexp is passed in for the tokens
         var bounds = bindings._raw(begToken,endToken,someString,ixBeg,ixEnd);
         if (bounds.start > -1) {
             if (jQuery.type(endToken) === "regexp") 
-                bounds.stop  += someString.match(endToken).length;
+                bounds.stop  += someString.match(endToken)[0].length;
             else
                 bounds.stop  += endToken.length;
         }
         return bounds;
     },
 
     insideOut: function (begToken,endToken,someString,ixBeg,ixEnd)
     { // returns what falls between begToken and endToken as found in the string provided
     // Note ixBeg and ixEnd are optional bounds already established within string
         var bounds = bindings.inside(begToken,endToken,someString,ixBeg,ixEnd);
         if (bounds.start < bounds.stop)
             return someString.slice(bounds.start,bounds.stop);
 
         return '';
     }
@@ -1434,30 +1443,61 @@
             if (debug)
                 alert("jsEmbedded:'"+jsEmbeded+"'\n---------------\n"+cleanHtml);
         } else {
             var warnMsg = bindings.insideOut('<li>','</li>',cleanHtml,bounds.start,bounds.stop);
             if (warnMsg.length > 0) {
                 warn(warnMsg);
                 if (whatWeDid)
                     whatWeDid.warnMsg = warnMsg;
             }
         }
         cleanHtml = cleanHtml.slice(0,bounds.start) + cleanHtml.slice(bounds.stop);
     }
     return stripHgErrors(cleanHtml, whatWeDid); // Certain early errors are not called via warnBox
 }
 
+function stripMainMenu(returnedHtml, debug, whatWeDid)
+{ // strips main menu div from html returned by ajax
+  // NOTE: any warnBox style errors will be put into the warnBox
+  // If whatWeDid !== null, we use it to return info about
+  // what we stripped out and processed (current just warnMsg).
+    var cleanHtml = returnedHtml;
+    // embedded javascript?
+    while (cleanHtml.length > 0) {
+	
+        var begPattern = '<div id="main-menu-whole">';
+        var endPattern = '</div><!-- end main-menu-whole -->';
+        var bounds = bindings.outside(begPattern,endPattern,cleanHtml);
+        if (bounds.start === -1)
+            break;
+        var mainMenu = cleanHtml.slice(bounds.start,bounds.stop);
+        if (-1 === mainMenu.indexOf("showWarnBox")) {
+            if (debug)
+                alert("mainMenu:'"+mainMenu+"'\n---------------\n"+cleanHtml);
+        } else {
+            var warnMsg = bindings.insideOut('<li>','</li>',cleanHtml,bounds.start,bounds.stop);
+            if (warnMsg.length > 0) {
+                warn(warnMsg);
+                if (whatWeDid)
+                    whatWeDid.warnMsg = warnMsg;
+            }
+        }
+        cleanHtml = cleanHtml.slice(0,bounds.start) + cleanHtml.slice(bounds.stop);
+    }
+    return stripHgErrors(cleanHtml, whatWeDid); // Certain early errors are not called via warnBox
+}
+
 function visTriggersHiddenSelect(obj)
 { // SuperTrack child changing vis should trigger superTrack reshaping.
   // This is done by setting hidden input "_sel"
     var trackName_Sel = $(obj).attr('name') + "_sel";
     var theForm = $(obj).closest("form");
     var visible = (obj.selectedIndex !== 0);
     if (visible) {
         updateOrMakeNamedVariable(theForm,trackName_Sel,"1");
     } else
         disableNamedVariable(theForm,trackName_Sel);
     return true;
 }
 
 function setCheckboxList(list, value)
 { // set value of all checkboxes in semicolon delimited list