e0c9cb5c5b9536dc3771e3fe09f3afb6ad5ad9ff
tdreszer
  Tue Jul 5 18:32:21 2011 -0700
Major performance improvements for IE javascript.  parent() and add() both dragged the JSript engine to it's knees.  Debug code is being checked in but not called.
diff --git src/hg/js/utils.js src/hg/js/utils.js
index a594c63..0882f5a 100644
--- src/hg/js/utils.js
+++ src/hg/js/utils.js
@@ -538,30 +538,43 @@
     if( warnList == undefined || $(warnList).length == 0 ) {
         warnBoxJsSetup();
         warnList = $('#warnList');
     }
     if( $(warnList).length == 0 )
         alert(msg);
     else {
         $( warnList ).append('<li>'+msg+'</li>');
         if(showWarnBox != undefined)
             showWarnBox();
         else
             alert(msg);
     }
 }
 
+var gWarnSinceMSecs = 0;
+function warnSince(msg)
+{   // Warn messages with msecs since last warnSince msg
+    // This is necessary because IE Developer tools are hanging
+    var now = new Date();
+    var msecs = now.getTime();
+    var since = 0;
+    if (gWarnSinceMSecs > 0)
+        since = msecs - gWarnSinceMSecs;
+    gWarnSinceMSecs = msecs;
+    warn('['+since+'] '+msg);
+}
+
 function cgiBooleanShadowPrefix()
 // Prefix for shadow variable set with boolean variables.
 // Exact copy of code in cheapcgi.c
 {
     return "boolshad.";
 }
 
 function getAllVars(obj,subtrackName)
 {
 // Returns a hash for all inputs and selects in an obj.
 // If obj is undefined then obj is document!
     var urlData = new Object();
     if($(obj) == undefined)
         obj = $('document');
     var inp = $(obj).find('input');
@@ -915,39 +928,40 @@
 { // Clears the waitMask
     var  waitMask = $('#waitMask');
     if( waitMask != undefined )
         $(waitMask).hide();
 }
 
 function waitMaskSetup(timeOutInMs)
 { // Sets up the waitMask to block page manipulation until cleared
 
     // Find or create the waitMask (which masks the whole page)
     var  waitMask = $('#waitMask');
     if( waitMask == undefined || waitMask.length != 1) {
         // create the waitMask
         $("body").append("<div id='waitMask' class='waitMask');'></div>");
         waitMask = $('#waitMask');
-        // Special for IE
+        // Special for IE, since it takes so long, make mask obvious
         if ($.browser.msie)
-            $(waitMask).css('filter','alpha(opacity= 0)');
+            $(waitMask).css({opacity:0.4,backgroundColor:'gray'});
     }
     $(waitMask).css('display','block');
 
     // Things could fail, so always have a timeout.
     if(timeOutInMs == undefined || timeOutInMs <=0)
-        timeOutInMs = 5000; // Don't ever leave this as infinite
+        timeOutInMs = 30000; // IE can take forever!
+
     setTimeout('waitMaskClear();',timeOutInMs); // Just in case
 }
 
 function _launchWaitOnFunction()
 { // should ONLY be called by waitOnFunction()
   // Launches the saved function
     var func = gWaitFunc;
     gWaitFunc = null;
     var funcArgs = gWaitFuncArgs;
     gWaitFuncArgs = [];
 
     if(func == undefined || !jQuery.isFunction(func))
         warn("_launchWaitOnFunction called without a function");
     else {
         if(funcArgs.length == 0)
@@ -993,30 +1007,61 @@
         if(arguments[1].type == 'button' && $(arguments[1]).hasClass('inOutButton')) {
             $(arguments[1]).css( 'borderStyle',"inset");
         }
     }
 
     // Build up the aruments array
     for(var aIx=1;aIx<arguments.length;aIx++) {
         gWaitFuncArgs.push(arguments[aIx])
     }
     gWaitFunc = func;
 
     setTimeout('_launchWaitOnFunction();',50);
 
 }
 
+// --- yielding iterator ---
+function _yieldingIteratorObject(yieldingFunc)
+{ // This is the "recusive object" or ro which is instantiated in waitOnIteratingFunction
+  // yieldingFunc is passed in from waitOnIteratingFunction
+  // and will recurse which recursively calls an iterator
+    this.step = function(msecs,args) {
+        setTimeout(function() { yieldingFunc(args); }, msecs); // recursive timeouts
+        return;
+    }
+}
+
+function yieldingIterator(interatingFunc,continuingFunc,args)
+{   // Will run interatingFunc function with "yields", then run continuingFunc
+    // Based upon design by Guido Tapia, PicNet
+    // interatingFunc must return number of msecs to pause before next interation.
+    //                return 0 ends iteration with call to continuingFunc
+    //                return < 0 ends iteration with no call to continuingFunc
+    // Both interatingFunc and continuingFunc will receive the single "args" param.
+    // Hint. for multiple args, create a single struct object
+
+    var ro = new _yieldingIteratorObject(function() {
+            var msecs = interatingFunc(args);
+            if (msecs > 0)
+                ro.step(msecs,args);      // recursion
+            else if (msecs == 0)
+                continuingFunc(args);     // completion
+            // else (msec < 0) // abandon
+        });
+    ro.step(1,args);                      // kick-off
+}
+
 function showLoadingImage(id)
 {
 // Show a loading image above the given id; return's id of div added (so it can be removed when loading is finished).
 // This code was mostly directly copied from hgHeatmap.js, except I also added the "overlay.appendTo("body");"
     var loadingId = id + "LoadingOverlay";
     var overlay = $("<div></div>").attr("id", loadingId).css("position", "absolute");
     overlay.appendTo("body");
     overlay.css("top", $('#'+ id).position().top);
     var divLeft = $('#'+ id).position().left + 2;
     overlay.css("left",divLeft);
     var width = $('#'+ id).width() - 5;
     var height = $('#'+ id).height();
     overlay.width(width);
     overlay.height(height);
     overlay.css("background", "white");
@@ -2240,120 +2285,285 @@
     if($(filter).hasClass('filterBy') == false)
         return undefined;
     if($(filter).hasClass('filterTable') == false)
         return undefined;
 
     // Find the var for this filter
     var classes = $(filter).attr("class").split(' ');
     classes = aryRemove(classes,"filterBy","filterTable","noneIsAll");
     if (classes.length > 1 ) {
         warn('Too many classes for filterBy: ' + classes);
         return undefined;
     }
     return classes.pop();
 }
 
-function filterTablesTrsSurviving(filterClass)
-// returns a list of trs that satisfy all filters
-// If defined, will exclude filter identified by filterClass
-{
-    // find all filterable table rows
-    var showTrs = $('tr.filterable'); // Default all
-    if (showTrs.length == 0)
-        return undefined;
+function _filterTableByClassesIterative(args)
+{ // Applies a single class filter to a filterTable TRs
+  // Called via yieldingIterator
+    if (args.curIx >= args.classes.length)
+        return 0;
 
-    // Find all filters
-    var filters = $("select.filterBy");
-    if (filters.length == 0)
-        return undefined;
+    var tds = $(args.tdsRemaining).filter('.' + args.classes[args.curIx]);
+    if (tds.length > 0) {
+        if (args.tdsFiltered == null)
+            args.tdsFiltered = tds;
+        else
+            args.tdsFiltered = jQuery.merge( args.tdsFiltered, tds );  // This one takes too long in IE!
+    }
+    //warnSince("Iterating class:"+args.curIx);
+    args.curIx++;
+    if (args.curIx >= args.classes.length)
+        return 0;
+    return 1;
+}
 
-    // Exclude one if requested.
-    if (filterClass != undefined && filterClass.length > 0)
-        filters = $(filters).not('.' + filterClass);
+function _filterTableByClassesComplete(args)
+{ // Continues after filterTableByClassesIterative
+  // Called via yieldingIterator
+    var filtersStruct = args.filtersStruct;
 
-    // For each filter, filter the list of trs that matches that filter
-    $(filters).each(function (i) {
-        var val = $(this).val();
-        if (val == null || val.length == 0)
-            return;
-        val = val.join();
-        if(val.indexOf("All") == 0)
-            return;
+    //warnSince("Completing classes...");
+    if (args.tdsFiltered == null)
+        filtersStruct.trsRemaining = null;
+    else {
+        //filtersStruct.trsRemaining = $(args.tdsFiltered).parent(); // Very slow in IE!!!
+        var tds = args.tdsFiltered;
+        var trs = [];
+        $(tds).each(function (ix) {
+            trs[ix] = this.parentNode;
+        });
+        filtersStruct.trsRemaining = trs;
+    }
+    //warnSince("Mostly complete classes...");
+    filtersStruct.curIx++;
+    yieldingIterator(_filterTableIterative,_filterTableComplete,filtersStruct);
+    //warnSince("Really complete classes.");
+}
 
-        // Get the filter variable
-        var filterVar = filterTableFilterVar(this);
-        if (filterVar == undefined)
-            return;
+function _filterTableIterative(args)
+{ // Applies a single filter to a filterTable TRs
+  // Called via yieldingIterator
 
-        // Get the selected values for this filter
-        var classes = $(this).val();
-        if (classes.length == 0)
-            return;
+    //warnSince("Filter "+args.curIx+" iterating...");
+    if (args.curIx >= args.filters.length)
+        return 0;
 
-        var varTds = $(showTrs).children('td.' + filterVar);
-        var filteredTrs = new Array;
-        var ix =0;
-        for(;ix<classes.length;ix++) {
+    var filter = args.filters[args.curIx];
+
+    var classes = $(filter).val();
+    if (classes == null || classes.length == 0)
+        {
+        args.trsRemaining = null;
+        return 0; // Nothing selected so exclude all rows
+        }
+
+    if(classes[0] != 'All') { // nothing excluded by this filter
+        // Get the filter variable
+        var filterVar = filterTableFilterVar(filter);
+        if (filterVar != undefined) {// No filter variable?!
+            //if ($.browser.msie) {   // Special for IE, since it takes so long
+            //    var classesStruct = new Object;
+            //    classesStruct.filtersStruct = args;
+            //    classesStruct.classes       = classes;
+            //    classesStruct.curIx         = 0;
+            //    classesStruct.tdsRemaining = $(args.trsRemaining).children('td.' + filterVar);
+            //    classesStruct.tdsFiltered = null;
+            //    yieldingIterator(_filterTableByClassesIterative,_filterTableByClassesComplete,classesStruct);
+            //    return -1; // Stops itteration now, but will be resumed in _filterTableByClassesComplete
+            //} else {
+                var varTds = $(args.trsRemaining).children('td.' + filterVar);
+                var filteredTrs = null;
+                for(var ix=0;ix<classes.length;ix++) {
             var tds = $(varTds).filter('.' + classes[ix]);
             if (tds.length > 0) {
-                if (filteredTrs.length == 0)
-                    filteredTrs = $(tds).parent('tr').get();
+                        var trs = [];
+                        $(tds).each(function (ix) {
+                            trs[ix] = this.parentNode;
+                        });
+                        if (filteredTrs == null)
+                            filteredTrs = trs; // $(tds).parent('tr'); // parent() takes too long in IE
                 else
-                    filteredTrs = filteredTrs.concat( $(tds).parent('tr').get() );
+                            filteredTrs = jQuery.merge( filteredTrs, trs );// $(tds).parent() );  // takes too long in IE!
+                    }
             }
+                args.trsRemaining = filteredTrs;
+            //}
+        }
+    }
+    args.curIx++;
+    if (args.curIx >= args.filters.length)
+        return 0;
+    return 1;
         }
-        // The following tighter code may take too long
-        //var filterVals = 'td.'+filterVar+'.' + classes.join(',td.'+filterVar+'.');   // "td.cell.GM12878,td.cell.K562,td.cell.H1-hESC"
-        //var filteredTrs = $(showTrs).filter(":has(" + filterVals + ")");
 
-        if (filteredTrs.length > 0)
-            showTrs = filteredTrs;
-    });
-    return showTrs;
+function _filterTableComplete(args)
+{ // Continuation after all the filters have been applied
+  // Called via yieldingIterator
+
+    //warnSince("Completing...");
+    //$('tr.filterable').hide();  // <========= This is what is taking so long!
+    $('tr.filterable').css('display', 'none');
+
+    if (args.trsRemaining != null) {
+        //$(args.trsRemaining).show();
+        $(args.trsRemaining).css('display', '');
+
+        // Update count
+        var counter = $('.filesCount');
+        if(counter != undefined)
+            $(counter).text($(args.trsRemaining).length + " / ");
+    } else {
+        var counter = $('.filesCount');
+        if(counter != undefined)
+            $(counter).text(0 + " / ");
 }
 
-function _filterTable()
+    var tbody = $( $('tr.filterable')[0] ).parent('tbody.sorting');
+    if (tbody != undefined)
+         $(tbody).removeClass('sorting');
+    //warnSince("Really complete.");
+}
+
+function _filterTableOld()
 { // Called by filter onchange event.  Will show/hide trs based upon all filters
     var showTrs = filterTablesTrsSurviving();
-    if (showTrs == undefined)
-        return;
     //$('tr.filterable').hide();  // <========= This is what is taking so long!
     $('tr.filterable').css('display', 'none')
 
-    $(showTrs).show();
+    if (showTrs != undefined && showTrs.length > 0) {
+        //$(showTrs).show();
+        $(showTrs).css('display', '');
 
     // Update count
     var counter = $('.filesCount');
     if(counter != undefined)
         $(counter).text($(showTrs).length + " / ");
+    } else {
+        var counter = $('.filesCount');
+        if(counter != undefined)
+            $(counter).text(0 + " / ");
+    }
 
     var tbody = $( $('tr.filterable')[0] ).parent('tbody.sorting');
     if (tbody != undefined)
          $(tbody).removeClass('sorting');
 }
 
+function _filterTable()
+{ // Called by filter onchange event.  Will show/hide trs based upon all filters
+    var trsAll = $('tr.filterable'); // Default all
+    if (trsAll.length == 0)
+        return undefined;
+
+    // Find all filters
+    var filters = $("select.filterBy");
+    if (filters.length == 0)
+        return undefined;
+
+    var filtersStruct = new Object;
+    filtersStruct.filters = filters;
+    filtersStruct.curIx = 0;
+    filtersStruct.trsRemaining = trsAll;
+
+    yieldingIterator(_filterTableIterative,_filterTableComplete,filtersStruct);
+}
+
+
+function filterTablesApplyOneFilter(filter,remainingTrs)
+{ // Applies a single filter to a filterTables TRs
+    var classes = $(filter).val();
+    if (classes == null || classes.length == 0)
+        return null; // Nothing selected so exclude all rows
+
+    if(classes[0] == 'All')
+        return remainingTrs;  // nothing excluded by this filter
+
+    // Get the filter variable
+    var filterVar = filterTableFilterVar(filter);
+    if (filterVar == undefined)
+        return null;
+
+    var varTds = $(remainingTrs).children('td.' + filterVar);
+    var filteredTrs = null;
+    var ix =0;
+    for(;ix<classes.length;ix++) {
+        var tds = $(varTds).filter('.' + classes[ix]);
+        if (tds.length > 0) {
+            var trs = [];
+            $(tds).each(function (ix) {
+                trs[ix] = this.parentNode;
+            });
+
+            if (filteredTrs == null)
+                filteredTrs = trs;
+            else
+                filteredTrs = jQuery.merge( filteredTrs, trs );  // This one takes too long in IE!
+        }
+    }
+    return filteredTrs;
+}
+
+function filterTablesTrsSurviving(filterClass)
+// returns a list of trs that satisfy all filters
+// If defined, will exclude filter identified by filterClass
+{
+    // find all filterable table rows
+    var showTrs = $('tr.filterable'); // Default all
+    if (showTrs.length == 0)
+        return undefined;
+
+    // Find all filters
+    var filters = $("select.filterBy");
+    if (filters.length == 0)
+        return undefined;
+
+    // Exclude one if requested.
+    if (filterClass != undefined && filterClass.length > 0)
+        filters = $(filters).not('.' + filterClass);
+
+    for(var ix =0;showTrs != null && ix < filters.length;ix++) {
+        showTrs = filterTablesApplyOneFilter(filters[ix],showTrs)
+    }
+    return showTrs;
+}
+
 function filterTableTrigger()
 { // Called by filter onchange event.  Will show/hide trs based upon all filters
     var tbody = $( $('tr.filterable')[0] ).parent('tbody');
     if (tbody != undefined)
          $(tbody).addClass('sorting');
 
-    setTimeout('_filterTable();',10); // Just in case
+    setTimeout('_filterTableOld();',2); // Just in case
+}
+
+function filterTableDone(event)
+{ // Called by custom 'done' event
+    event.stopImmediatePropagation();
+    $(this).unbind( event );
+    waitOnFunction(filterTableTrigger);
 }
 
-function filterTable()
+function filterTable(selector)
 { // Called by filter onchange event.  Will show/hide trs based upon all filters
+    // IE takes tooo long, so this should be called only when leaving the filterBy box
+    if ( $('tr.filterable').length > 300) {
+        //if ($.browser.msie) { // IE takes tooo long, so this should be called only when leaving the filterBy box
+            $(selector).one('done',filterTableDone);
+            return;
+        //}
+    } else
     waitOnFunction(filterTableTrigger );
 }
 
 function filterTableExcludeOptions(filter)
 { // bound to 'click' event inside ui.dorpdownchecklist.js.
   // Will mark all options in one filterBy box that are inconsistent with the current
   // selections in other filterBy boxes.  Mark with class ".excluded"
 
     // Find the var for this filter
     var filterVar = filterTableFilterVar(filter);
     if (filterVar == undefined)
         return false;
 
     // Look at list of visible trs.
     var visibleTrs = filterTablesTrsSurviving(filterVar);
@@ -2526,30 +2736,33 @@
         });
     }
 
     var msg = ddclTextOfCurrentSelections(selector.options);
 
     var control = this.controlWrapper;
     if (control == null || control == undefined) { // caller is not constant
         control = $('#ddcl-' + id);
     }
     var newColor = '';
     if ($(selector).find('option:selected').length == 0)
         newColor = '#AA0000'; // red
     //else if (msg.search(/color:/i) == -1)
     //    newColor = 'black';
     ddclLabelSet(control,msg,newColor,'Click to select...');
+
+    // Notice special handling for a custom event
+    $(selector).trigger('done',selector);
 }
 
 function ddclReinit(filterBys,force)
 { // ReInitialize the DDCLs (drop-down checkbox-list)
   // This is done when the track search with tabs gets switched to advanced tab
   // because the DDCLs were setup on hidden filterBys and dimensiuons are wrong.
   // if not force, then only reinit when the dimensions are suspect
 
     if (filterBys.length < 1)
         return;
 
     $(filterBys).each( function(i) { // Do this by 'each' to set noneIsAll individually
         if (!force) { // condition on bad dimensions
             var id = $(this).attr('id');
             control = $('#ddcl-' + id);