0df62533ce94e914b0eb5d4490744d219eb721e5
tdreszer
  Tue Dec 20 14:28:42 2011 -0800
Objectified sortTable and filterTable but no functional change is here.
diff --git src/hg/js/utils.js src/hg/js/utils.js
index 8c360e8..af81020 100644
--- src/hg/js/utils.js
+++ src/hg/js/utils.js
@@ -1159,170 +1159,256 @@
 }
 
 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
+    var names = list.split(";");
+    for(var i=0;i<names.length;i++) {
+        $("input[name='" + names[i] + "']").attr('checked', value);
+    }
+}
+
+function calculateHgTracksWidth()
+{
+// return appropriate width for hgTracks image given users current window width
+    return $(window).width() - 20;
+}
+
+function hgTracksSetWidth()
+{
+    var winWidth = calculateHgTracksWidth();
+    if($("#imgTbl").length == 0) {
+        // XXXX what's this code for?
+        $("#TrackForm").append('<input type="hidden" name="pix" value="' + winWidth + '"/>');
+        //$("#TrackForm").submit();
+    } else {
+        $("input[name=pix]").val(winWidth);
+    }
+}
+
+function filterByMaxHeight(multiSel)
+{   // Setting a max height to scroll dropdownchecklists but
+    // multiSel is hidden when this is done, so it's position and height must be estimated.
+    var pos = $(multiSel).closest(':visible').offset().top + 30;
+    if (pos <= 0)
+        pos = 260;
+
+    // Special mess since the filterBy's on non-current tabs will calculate pos badly.
+    var tabbed = $('input#currentTab');
+    if (tabbed != undefined) {
+        var tabDiv = $(multiSel).parents('div#'+ $(tabbed).attr('value'));
+        if (tabDiv == null || tabDiv == undefined || $(tabDiv).length == 0) {
+            pos = 360;
+        }
+    }
+    var maxHeight = $(window).height() - pos;
+    var selHeight = $(multiSel).children().length * 21;
+    if (maxHeight > selHeight)
+        maxHeight = null;
+    //else if($.browser.msie && maxHeight > 500)  // DDCL bug on IE only.
+    //    maxHeight = 500;          // Seems to be solved by disbling DDCL's window.resize event for IE
+
+    return maxHeight;
+}
+
 //////////// Drag and Drop ////////////
 function tableDragAndDropRegister(thisTable)
 {// Initialize a table with tableWithDragAndDrop
     if ($(thisTable).hasClass("tableWithDragAndDrop") == false)
         return;
 
     $(thisTable).tableDnD({
         onDragClass: "trDrag",
         dragHandle: "dragHandle",
         onDrop: function(table, row, dragStartIndex) {
                 if (row.rowIndex != dragStartIndex) {
-                    if(tableSetPositions) {
-                        tableSetPositions(table);
+                    if (sortTable.savePositions) {
+                        sortTable.savePositions(table);
                     }
                 }
             }
     });
     $(thisTable).find("td.dragHandle").hover(
         function(){ $(this).closest('tr').addClass('trDrag'); },
         function(){ $(this).closest('tr').removeClass('trDrag'); }
     );
 }
 
-//////////// Sorting ////////////
-// Sorting a table by columns relies upon the sortColumns structure
+  ///////////////////////////////
+ ////////// Sort Table /////////
+///////////////////////////////
+var sortTable = {
+    // The sortTable object handles sorting HTML tables on columns.
+    // Just add the 'sortable' class to your table and in ready() call sortTable.initialize($('table.sortable')).
+    //
+    // Details you don't need to know until you want to do something fancy.
+    // A sortable table requires:
+    //     TABLE.sortable: TABLE class='sortable' containing a THEAD header and sortable TBODY filled with the rows to sort.
+    //     THEAD.sortable: (NOTE: created if not found) The THEAD can contain multiple rows must contain:
+    //       TR.sortable: exactly 1 header TH (table row) class='sortable' which will declare the sort columns:
+    //       TH.sortable: 1 or more TH (table column headers) with class='sortable sort1 [sortRev]' (or sort2, sort3) declaring sort order and whether reversed
+    //         e.g. <TH id='factor' class='sortable sortRev sort3' nowrap>...</TH>  (this means that factor is currently the third sort column and reverse sorted)
+    //         (NOTE: If no TH.sortable is found, then every th in the TR.sortable will be converted for you and will be in sort1,2,3 order.)
+    //         ONCLICK: Each TH.sortable must call sortTable.sortOnButtonPress(this) directly or indirectly in the onclick event :
+    //           e.g. <TH id='factor' class='sortable sortRev sort3' nowrap title='Sort list on this column' onclick="return sortTable.sortOnButtonPress(this);">
+    //           (NOTE: If no onclick function is found in a TH.sortable, then it will automatically be added.)
+    //         SUP: Each TH.sortable *may* contain a <sup> which will be filled with an up or down arrow and the column's sort order: e.g. <sup>&darr;2</sup>
+    //           (NOTE: The sup can be added by using the addSuperscript option to the sortTable.initialize() function.
+    //     TBODY.sortable: (NOTE: created if not found) The TBODY class='sortable' contains the table rows that get sorted:
+    //       TBODY->TR & ->TD: Each row contains a TD for each sortable column. The innerHTML (entire contents) of the cell will be used for sorting.
+    //       TRICK: You can use the 'abbr' field to subtly alter the sortable contents.  Otherwise sorts on td contents ($(td).text()).
+    //              Use the abbr field to make case-insensitive sorts or force exceptions to alpha-text order (e.g. ZCTRL vs Control forcing controls to bottom)
+    //              e.g. <TD id='wgEncodeBroadHistoneGm12878ControlSig_factor' nowrap abbr='ZCTRL' align='left'>Control</TD>
+    //              This is also the method to ensure a numeric sort (e.g. <td align="right" abbr="000003416800354">3.2 GB</td>).
+    //              IMPORTANT: You must add abbr='use' to the TH.sortable definitions.
+    //     Finally if you want the tableSort to alternate the table row colors (using #FFFEE8 and #FFF9D2) then TBODY.sortable should also have class 'altColors'
+    //     NOTE: This class can be added by using the altColors option to the sortTable.initialize() function.
+    //
+    //     PRESERVING TO CART: To send the sort column on a form 'submit', the header tr (TR.sortable) needs a named hidden input of class='sortOrder' as:
+    //       e.g.: <INPUT TYPE=HIDDEN NAME='wgEncodeBroadHistone.sortOrder' class='sortOrder' VALUE="factor=- cell=+ view=+">
+    //       AND each sortable column header (TH.sortable) must have id='{name}' which is the name of the sortable field (e.g. 'factor', 'shortLabel')
+    //       The value preserves the column sort order and direction based upon the id={name} of each sort column.
+    //       In the example, while 'cell' may be the first column, the table is currently reverse ordered by 'factor', then by cell and view.
+    //     And to send the sorted row orders on form 'submit', each TBODY->TR will need a named hidden input field of class='trPos':
+    //       e.g. <INPUT TYPE=HIDDEN NAME='wgEncodeHaibTfbsA549ControlPcr2xDexaRawRep1.priority' class='trPos' VALUE="2">
+    //       A reason to preserve the order in the cart is if the order will affect other cgis.  For instance: sort subtracks and see that order in the hgTracks image.
 
-// The sortColumns structure looks like:
+    // Sorting a table by columns relies upon the columns obj, whose C equivalent would look like:
+    //struct column
 //{
 //    char *  tags[];     // a list of field names in sort order (e.g. 'cell', 'shortLabel')
 //    boolean reverse[];  // the sort direction for each sort field
 //    int     cellIxs[];  // The indexes of the columns in the table to be sorted
-//    boolean useAbbr[];  // Compare on Abbr or on innerHtml?
-//}
-// These 2 globals are used by setTimeout, so that rows can be hidden while sorting and javascript timeout is less likely
-var gSortColumns;
-var gTbody
+    //    boolean useAbbr[];  // Compare on Abbr or on text()?
+    //    };
 
-function sortField(value,index)
-{
-this.value=value;
-this.index=index;
-}
+    // These 2 globals are used during setTimeout, so that rows can be hidden while sorting
+    // and javascript timeout on slow (IE) browsers is less likely
+    columns: null,
+    tbody: null,
 
-function sortRow(tr,sortColumns,row)  // UNUSED: sortField works fine
+    row: function (tr,sortColumns,row)  // UNUSED: sortTable.fieldCmp works fine
 {
     this.fields  = new Array();
     this.reverse = new Array();
     this.row     = row;
     for(var ix=0;ix<sortColumns.cellIxs.length;ix++)
         {
         var th = tr.cells[sortColumns.cellIxs[ix]];
         this.fields[ix]  = (sortColumns.useAbbr[ix] ? th.abbr : $(th).text()).toLowerCase(); // case insensitive sorts
         this.reverse[ix] = sortColumns.reverse[ix];
         }
-}
+    },
 
-function sortRowCmp(a,b)  // UNUSED: sortField works fine
+    rowCmp: function (a,b)  // UNUSED: sortTable.fieldCmp works fine
 {
     for(var ix=0;ix<a.fields.length;ix++) {
         if (a.fields[ix] > b.fields[ix])
             return (a.reverse[ix] ? -1:1);
         else if (a.fields[ix] < b.fields[ix])
             return (a.reverse[ix] ? 1:-1);
     }
     return 0;
-}
+    },
 
-function sortField(value,reverse,row)
+    field: function (value,reverse,row)
 {
     this.value   = value.toLowerCase(); // case insensitive sorts NOTE: Do not need to define every field
     this.reverse = reverse;
     this.row     = row;
-}
-function sortFieldCmp(a,b)
+    },
+
+    fieldCmp: function (a,b)
 {
     if (a.value > b.value)
         return (a.reverse ? -1:1);
     else if (a.value < b.value)
         return (a.reverse ? 1:-1);
     return 0;
-}
+    },
 
-function tableSort(tbody,sortColumns)
+    sort: function (tbody,sortColumns)
 {// Sorts table based upon rules passed in by function reference
  // Expects tbody to not sort thead, but could take table
 
     // The sort routine available is javascript array.sort(), which cannot sort rows directly
     // Until we have jQuery >=v1.4, we cannot easily convert tbody.rows[] inot a javascript array
     // So we will make our own array, sort, then then walk through the table and reorder
     // FIXME: Until better methods are developed, only sortOrder based sorts are supported and fnCompare is obsolete
 
     // Create array of the primary sort column's text
     var cols = new Array();
     var trs = tbody.rows;
     $(trs).each(function(ix) {
         var th = this.cells[sortColumns.cellIxs[0]];
         if(sortColumns.useAbbr[0])
-            cols.push(new sortField(th.abbr,sortColumns.reverse[0],this));
+                cols.push(new sortTable.field(th.abbr,sortColumns.reverse[0],this));
         else
-            cols.push(new sortField($(th).text(),sortColumns.reverse[0],this));
+                cols.push(new sortTable.field($(th).text(),sortColumns.reverse[0],this));
     });
 
     // Sort the array
-    cols.sort(sortFieldCmp);
+        cols.sort(sortTable.fieldCmp);
 
     var topIndex = tbody.rows[0].rowIndex; // This could vary depending upon header rows
     for(var cIx=cols.length-1;cIx>=0;cIx--) {
         if (cols[cIx].row.rowIndex > topIndex) // Inserting before onesself will delete row
             $( cols[cIx].row ).insertBefore( tbody.rows[0] );
     }
 
-    gTbody=tbody;
-    gSortColumns=sortColumns;
-    setTimeout('tableSortFinish(gTbody,gSortColumns)',5); // Avoid javascript timeouts!
-}
+        sortTable.tbody=tbody;
+        sortTable.columns=sortColumns;
+        setTimeout('sortTable.sortFinish(sortTable.tbody,sortTable.columns)',5); // Avoid javascript timeouts!
+    },
 
-function tableSortFinish(tbody,sortColumns)
+    sortFinish: function (tbody,sortColumns)
 {// Additional sort cleanup.
  // This is in a separate function to allow calling with setTimeout() which will prevent javascript timeouts (I hope)
-    tableSetPositions(tbody);
+        sortTable.savePositions(tbody);
     if ($(tbody).hasClass('altColors'))
-        sortedTableAlternateColors(tbody,sortColumns);
+            sortTable.alternateColors(tbody,sortColumns);
     $(tbody).parents("table.tableWithDragAndDrop").each(function (ix) {
         tableDragAndDropRegister(this);
     });
     //$(tbody).show();
     $(tbody).removeClass('sorting');
-}
+    },
 
-function tableSortByColumns(tbody,sortColumns)
+    sortByColumns: function (tbody,sortColumns)
 {// Will sort the table based on the abbr values on a set of <TH> colIds
  // Expects tbody to not sort thead, but could take table
     //$(tbody).hide();
     $(tbody).addClass('sorting');
-    gTbody=tbody;
-    gSortColumns=sortColumns;
-    setTimeout('tableSort(gTbody,gSortColumns)',50); // This allows hiding the rows while sorting!
-}
+        sortTable.tbody=tbody;
+        sortTable.columns=sortColumns;
+        setTimeout('sortTable.sort(sortTable.tbody,sortTable.columns)',50); // This allows hiding the rows while sorting!
+    },
 
-function trAlternateColors(tbody,rowGroup,cellIx)
+    trAlternateColors: function (tbody,rowGroup,cellIx)
 {// Will alternate colors for visible table rows.
  // If cellIx(s) provided then color changes when the column(s) abbr or els innerHtml changes
  // If no cellIx is provided then alternates on rowGroup (5= change color 5,10,15,...)
  // Expects tbody to not color thead, but could take table
     var darker   = false; // == false will trigger first row to be change color = darker
 
     if (arguments.length<3) { // No columns to check so alternate on rowGroup
 
         if (rowGroup == undefined || rowGroup == 0)
             rowGroup = 1;
         var curCount = 0; // Always start with a change
         $(tbody).children('tr:visible').each( function(i) {
             if (curCount == 0 ) {
                 curCount  = rowGroup;
                 darker = (!darker);
@@ -1354,80 +1440,80 @@
                 }
             }
             if (lastContent != curContent ) {
                 lastContent  = curContent;
                 darker = (!darker);
             }
             if (darker) {
                 $(this).removeClass("bgLevel1");
                 $(this).addClass(   "bgLevel2");
             } else {
                 $(this).removeClass("bgLevel2");
                 $(this).addClass(   "bgLevel1");
             }
         });
     }
-}
+    },
 
-function sortedTableAlternateColors(tbody)
+    alternateColors: function (tbody)
 { // Will alternate colors based upon sort columns (which may be passed in as second arg, or discovered)
 // Expects tbody to not color thead, but could take table
     var sortColumns;
     if (arguments.length > 1)
         sortColumns = arguments[1];
     else {
         var table = tbody;
         if ($(table).is('tbody'))
             table = $(tbody).parent();
-        sortColumns = new sortColumnsGetFromTable(table);
+            sortColumns = new sortTable.columnsFromTable(table);
     }
 
     if (sortColumns) {
         if (sortColumns.cellIxs.length==1)
-            trAlternateColors(tbody,0,sortColumns.cellIxs[0]);
+                sortTable.trAlternateColors(tbody,0,sortColumns.cellIxs[0]);
         else if (sortColumns.cellIxs.length==2)
-            trAlternateColors(tbody,0,sortColumns.cellIxs[0],sortColumns.cellIxs[1]);
+                sortTable.trAlternateColors(tbody,0,sortColumns.cellIxs[0],sortColumns.cellIxs[1]);
         else // Three columns is plenty
-            trAlternateColors(tbody,0,sortColumns.cellIxs[0],sortColumns.cellIxs[1],sortColumns.cellIxs[2]);
+                sortTable.trAlternateColors(tbody,0,sortColumns.cellIxs[0],sortColumns.cellIxs[1],sortColumns.cellIxs[2]);
     } else {
-        trAlternateColors(tbody,5); // alternates every 5th row
-    }
+            sortTable.trAlternateColors(tbody,5); // alternates every 5th row
 }
+    },
 
-function sortOrderFromColumns(sortColumns)
+    orderFromColumns: function (sortColumns)
 {// Creates the trackDB setting entry sortOrder subGroup1=+ ... from a sortColumns structure
     fields = new Array();
     for(var ix=0;ix < sortColumns.cellIxs.length;ix++) {
         if (sortColumns.tags[ix] != undefined && sortColumns.tags[ix].length > 0)
             fields[ix] = sortColumns.tags[ix] + "=" + (sortColumns.reverse[ix] ? "-":"+");
         else
             fields[ix] = sortColumns.cellIxs[ix] + "=" + (sortColumns.reverse[ix] ? "-":"+");
     }
     var sortOrder = fields.join(' ');
-    //warn("sortOrderFromColumns("+sortColumns.cellIxs.length+"):["+sortOrder+"]");
+        //warn("sortTable.orderFromColumns("+sortColumns.cellIxs.length+"):["+sortOrder+"]");
     return sortOrder;
-}
+    },
 
-function sortOrderUpdate(table,sortColumns,addSuperscript)
+    orderUpdate: function (table,sortColumns,addSuperscript)
 {// Updates the sortOrder in a sortable table
     if (addSuperscript == undefined)
         addSuperscript = false;
     if ($(table).is('tbody'))
         table = $(table).parent();
     var tr = $(table).find('tr.sortable')[0];
     if (tr) {
-        //warn("sortOrderUpdate("+sortColumns.cellIxs.length+")");
+            //warn("sortTable.orderUpdate("+sortColumns.cellIxs.length+")");
         for(cIx=0;cIx<sortColumns.cellIxs.length;cIx++) {
             var th = tr.cells[sortColumns.cellIxs[cIx]];
             $(th).each(function(i) {
                 // First remove old sort classes
                 var classList = $( this ).attr("class").split(" ");
                 if (classList.length < 2) // assertable
                     return;
                 classList = aryRemove(classList,["sortable"]);
                 while( classList.length > 0 ) {
                     var aClass = classList.pop();
                     if (aClass.indexOf("sort") == 0)
                         $(this).removeClass(aClass);
                 }
 
                 // Now add current sort classes
@@ -1445,38 +1531,38 @@
                             content += (cIx+1);
                         else
                             content = "";
                     }
 
                     if (sup)
                         sup.innerHTML = content;
                     else
                         $(th).append("<sup>"+content+"</sup>");
                 }
             });
         }
         // There may be a hidden input that gets updated to the cart
         var inp = $(tr).find('input.sortOrder')[0];
         if (inp) {
-            $(inp).val(sortOrderFromColumns(sortColumns));
+                $(inp).val(sortTable.orderFromColumns(sortColumns));
             if (!addSuperscript && typeof(subCfg) !== "undefined")
                 subCfg.markChange(null,inp);     // use instead of change() because type=hidden!
         }
     }
-}
+    },
 
-function sortOrderFromTr(tr)
+    orderFromTr: function (tr)
 {// Looks up the sortOrder input value from a *.sortable header row of a sortable table
     var inp = $(tr).find('input.sortOrder')[0];
     if (inp)
         return $(inp).val();
     else {
         // create something like "cellType=+ rep=+ protocol=+ treatment=+ factor=+ view=+"
         var fields = new Array();
         var cells = $(tr).find('th.sortable');
         $(cells).each(function (i) {
             var classList = $( this ).attr("class").split(" ");
             if (classList.length < 2) // assertable
                 return;
             classList = aryRemove(classList,["sortable"]);
             var reverse = false;
             var sortIx = -1;
@@ -1496,230 +1582,221 @@
             }
             if (sortIx >= 0) {
                 if (this.id != undefined && this.id.length > 0)
                     fields[sortIx] = this.id + "=" + (reverse ? "-":"+");
                 else
                     fields[sortIx] = this.cellIndex + "=" + (reverse ? "-":"+");
             }
         });
         if (fields.length > 0) {
             if (fields[0] == undefined)
                 fields.shift();  // 1 based sort ix and 0 based fields ix
             return fields.join(' ');
         }
     }
     return "";
-}
-function sortColumnsGetFromSortOrder(sortOrder)
+    },
+
+    columnsFromSortOrder: function (sortOrder)
 {// Creates sortColumns struct (without cellIxs[]) from a trackDB.sortOrder setting string
     this.tags = new Array();
     this.reverse = new Array();
     var fields = sortOrder.split(" "); // sortOrder looks like: "cell=+ factor=+ view=+"
     while(fields.length > 0) {
         var pair = fields.shift().split("=");  // Take first and split into
         if (pair.length == 2) {
             this.tags.push(pair[0]);
             this.reverse.push(pair[1] != '+');
         }
     }
-}
-function sortColumnsGetFromTr(tr,silent)
+    },
+
+    columnsFromTr: function (tr,silent)
 {// Creates a sortColumns struct from the entries in the 'tr.sortable' heading row of a sortable table
-    this.inheritFrom = sortColumnsGetFromSortOrder;
-    var sortOrder = sortOrderFromTr(tr);
+        this.inheritFrom = sortTable.columnsFromSortOrder;
+        var sortOrder = sortTable.orderFromTr(tr);
     if (sortOrder.length == 0 && silent == undefined) {
         warn("Unable to obtain sortOrder from sortable table.");   // developer needs to know something is wrong
         return;
     }
 
     this.inheritFrom(sortOrder);
     // Add two additional arrays
     this.cellIxs = new Array();
     this.useAbbr = new Array();
     var ths = $(tr).find('th.sortable');
     for(var tIx=0;tIx<this.tags.length;tIx++) {
         for(ix=0; ix<ths.length; ix++) {
             if ((ths[ix].id != undefined && ths[ix].id == this.tags[tIx])
             ||  (ths[ix].cellIndex == this.tags[tIx]))
             {
                 this.cellIxs[tIx] = ths[ix].cellIndex;
                 this.useAbbr[tIx] = (ths[ix].abbr.length > 0);
             }
         }
     }
     if (this.cellIxs.length == 0 && silent == undefined) {
         warn("Unable to find any sortOrder.cells for sortable table.  ths.length:"+ths.length + " tags.length:"+this.tags.length + " sortOrder:["+sortOrder+"]");
         return;
     }
-}
+    },
 
-function sortColumnsGetFromTable(table)
+    columnsFromTable: function (table)
 {// Creates a sortColumns struct from the contents of a 'table.sortable'
-    this.inheritNow = sortColumnsGetFromTr;
+        this.inheritNow = sortTable.columnsFromTr;
     var tr = $(table).find('tr.sortable')[0];
     //if (tr == undefined && debug) warn("Couldn't find 'tr.sortable' rows:"+table.rows.length);
     this.inheritNow(tr);
-}
-
+    },
 
-function _tableSortOnButtonPressEncapsulated(anchor)
+    _sortOnButtonPress: function (anchor)
 {// Updates the sortColumns struct and sorts the table when a column header has been pressed
  // If the current primary sort column is pressed, its direction is toggled then the table is sorted
  // If a secondary sort column is pressed, it is moved to the primary spot and sorted in fwd direction
     var th=$(anchor).closest('th')[0];  // Note that anchor is <a href> within th, not th
     var tr=$(th).parent();
-    var theOrder = new sortColumnsGetFromTr(tr);
+        var theOrder = new sortTable.columnsFromTr(tr);
     var oIx = th.cellIndex;
     for(oIx=0;oIx<theOrder.cellIxs.length;oIx++) {
         if (theOrder.cellIxs[oIx] == th.cellIndex)
             break;
     }
     if (oIx == theOrder.cellIxs.length) {
         warn("Failure to find '"+th.id+"' in sort columns."); // Developer must be warned that something is wrong with sortable table setup
         return;
     }
     // assert(th.id == theOrder.tags[oIx] || th.id == undefined);
     if (oIx > 0) { // Need to reorder
-        var newOrder = new sortColumnsGetFromTr(tr);
+            var newOrder = new sortTable.columnsFromTr(tr);
         var nIx=0; // button pushed puts this 'tagId' column first in new order
         newOrder.tags[nIx] = theOrder.tags[oIx];
         newOrder.reverse[nIx] = false;  // When moving to the first position sort forward
         newOrder.cellIxs[nIx] = theOrder.cellIxs[oIx];
         newOrder.useAbbr[nIx] = theOrder.useAbbr[oIx];
         for(var ix=0;ix<theOrder.cellIxs.length;ix++) {
             if (ix != oIx) {
                 nIx++;
                 newOrder.tags[nIx]    = theOrder.tags[ix];
                 newOrder.reverse[nIx] = theOrder.reverse[ix];
                 newOrder.cellIxs[nIx] = theOrder.cellIxs[ix];
                 newOrder.useAbbr[nIx] = theOrder.useAbbr[ix];
             }
         }
         theOrder = newOrder;
     } else { // if (oIx == 0) {   // need to reverse directions
         theOrder.reverse[oIx] = (theOrder.reverse[oIx] == false);
     }
     var table=$(tr).closest("table.sortable")[0];
     if (table) { // assertable
-        sortOrderUpdate(table,theOrder);  // Must update sortOrder first!
+            sortTable.orderUpdate(table,theOrder);  // Must update sortOrder first!
         var tbody = $(table).find("tbody.sortable")[0];
         //if (tbody == undefined && debug) warn("Couldn't find 'tbody.sortable' 5");
-        tableSortByColumns(tbody,theOrder);
+            sortTable.sortByColumns(tbody,theOrder);
     }
     return;
+    },
 
-}
-
-function tableSortOnButtonPress(anchor,tagId)
-{
+    sortOnButtonPress: function (anchor,tagId)
+    {   // wrapper for the real worker: _sortOnButtonPress()
     var table = $( anchor ).closest("table.sortable")[0];
     if (table) {
-        waitOnFunction( _tableSortOnButtonPressEncapsulated, anchor, tagId);
+            waitOnFunction( sortTable._sortOnButtonPress, anchor, tagId);
     }
     return false;  // called by link so return false means don't try to go anywhere
-}
+    },
 
-function tableSortUsingSortColumns(table) // NOT USED
+    sortUsingColumns: function (table) // NOT USED
 {// Sorts a table body based upon the marked columns
-    var columns = new sortColumnsGetFromTable(table);
+        var columns = new sortTable.columnsFromTable(table);
     tbody = $(table).find("tbody.sortable")[0];
     if (tbody)
-        tableSortByColumns(tbody,columns);
-}
+            sortTable.sortByColumns(tbody,columns);
+    },
 
-function hintOverSortableColumnHeader(th) // NOT USED
+    hintOnColumnHeader: function (th) // NOT USED
 {// Upodates the sortColumns struct and sorts the table when a column headder has been pressed
     //th.title = "Click to make this the primary sort column, or toggle direction";
     //var tr=th.parentNode;
-    //th.title = "Current Sort Order: " + sortOrderFromTr(tr);
-}
+        //th.title = "Current Sort Order: " + sortTable.orderFromTr(tr);
+    },
 
-function tableSetPositions(table)
+    savePositions: function (table)
 {// Sets the value for the input.trPos of a table row.  Typically this is a "priority" for a track
  // This gets called by sort or dragAndDrop in order to allow the new order to affect hgTracks display
     var inputs = $(table).find("input.trPos");
     $( inputs ).each( function(i) {
         var tr = $( this ).closest('tr')[0];
         var trIx = $( tr ).attr('rowIndex').toString();
         if ($( this ).val() != trIx) {
             $( this ).val( trIx );
             if (typeof(subCfg) !== "undefined")  // NOTE: couldn't get $(this).change() to work.
                 subCfg.markChange(null,this);    //       probably because this is input type=hidden!
         }
     });
-}
+    },
 
 ///// Following functions are for Sorting by priority
-function trFindPosition(tr)
+    trPriorityFind: function (tr)
 {
 // returns the position (*.priority) of a sortable table row
     var inp = $(tr).find('input.trPos')[0];
     if (inp)
         return $(inp).val();
     return 999999;
-}
+    },
 
-function trComparePriority(tr1,tr2)  // UNUSED FUNCTION
+    trPriorityCmp: function (tr1,tr2)  // UNUSED FUNCTION
 {
 // Compare routine for sorting by *.priority
-    var priority1 = trFindPosition(tr1);
-    var priority2 = trFindPosition(tr2);
+        var priority1 = sortTable.trPriorityFind(tr1);
+        var priority2 = sortTable.trPriorityFind(tr2);
     return priority2 - priority1;
-}
+    },
 
-function tablesSortAtStartup()
+    tablesSortAtStartup: function ()
 {// Called at startup if you want javascript to initialize and sort all your class='sortable' tables
  // IMPORTANT: This function WILL ONLY sort by first column.
  // If there are multiple sort columns, please presort the list for accurtacy!!!
     var tables = $("table.sortable");
     $(tables).each(function(i) {
-        sortTableInitialize(this,true); // Will initialize superscripts
-        tableSortUsingSortColumns(this);
+            sortTable.initialize(this,true); // Will initialize superscripts
+            sortTable.sortUsingColumns(this);
     });
-}
+    },
 
-function sortTableInitialize(table,addSuperscript,altColors)
+    initialize: function (table,addSuperscript,altColors)
 {// Called if you want javascript to initialize your class='sortable' table.
  // A sortable table requires:
  // TABLE.sortable: TABLE class='sortable' containing a THEAD header and sortable TBODY filled with the rows to sort.
  // THEAD.sortable: (NOTE: created if not found) The THEAD can contain multiple rows must contain:
  //   TR.sortable: exactly 1 header TH (table row) class='sortable' which will declare the sort columnns:
  //   TH.sortable: 1 or more TH (table column headers) with class='sortable sort1 [sortRev]' (or sort2, sort3) declaring sort order and whether reversed
  //     e.g. <TH id='factor' class='sortable sortRev sort3' nowrap>...</TH>  (this means that factor is currently the third sort column and reverse sorted)
  //     (NOTE: If no TH.sortable is found, then every th in the TR.sortable will be converted for you and will be in sort1,2,3 order.)
- //     ONCLICK: Each TH.sortable must call tableSortOnButtonPress(this) directly or indirectly in the onclick event :
- //       e.g. <TH id='factor' class='sortable sortRev sort3' nowrap title='Sort list on this column' onclick="return tableSortOnButtonPress(this);">
+    //     ONCLICK: Each TH.sortable must call sortTable.sortOnButtonPress(this) directly or indirectly in the onclick event :
+    //       e.g. <TH id='factor' class='sortable sortRev sort3' nowrap title='Sort list on this column' onclick="return sortTable.sortOnButtonPress(this);">
  //       (NOTE: If no onclick function is found in a TH.sortable, then it will automatically be added.)
  //     SUP: Each TH.sortable *may* contain a <sup> which will be filled with an up or down arrow and the column's sort order: e.g. <sup>&darr;2</sup>
  //       (NOTE: If no sup is found but addSuperscript is requested, then they will be added.)
  // TBODY.sortable: (NOTE: created if not found) The TBODY class='sortable' contains the table rows that get sorted:
  //   TBODY->TR & ->TD: Each row contains a TD for each sortable column. The innerHTML (entire contents) of the cell will be used for sorting.
  //   TRICK: You can use the 'abbr' field to subtly alter the sortable contents.  Otherwise sorts on td contents ($(td).text()).
  //          Use the abbr field to make case-insensitive sorts or force exceptions to alpha-text order (e.g. ZCTRL vs Control forcing controls to bottom)
  //          e.g. <TD id='wgEncodeBroadHistoneGm12878ControlSig_factor' nowrap abbr='ZCTRL' align='left'>Control</TD>
  //          IMPORTANT: You must add abbr='use' to the TH.sortable definitions.
  // Finally if you want the tableSort to alternate the table row colors (using #FFFEE8 and #FFF9D2) then TBODY.sortable should also have class 'altColors'
  // NOTE: This class can be added by using the altColors option to this function
- //
- // PRESERVING TO CART: To send the sort column on a form 'submit', the header tr (TR.sortable) needs a named hidden input of class='sortOrder' as:
- //   e.g.: <INPUT TYPE=HIDDEN NAME='wgEncodeBroadHistone.sortOrder' class='sortOrder' VALUE="factor=- cell=+ view=+">
- //   AND each sortable column header (TH.sortable) must have id='{name}' which is the name of the sortable field (e.g. 'factor', 'shortLabel')
- //   The value preserves the column sort order and direction based upon the id={name} of each sort column.
- //   In the example, while 'cell' may be the first column, the table is currently reverse ordered by 'factor', then by cell and view.
- // And to send the sorted row orders on form 'submit', each TBODY->TR will need a named hidden input field of class='trPos':
- //   e.g. <INPUT TYPE=HIDDEN NAME='wgEncodeHaibTfbsA549ControlPcr2xDexaRawRep1.priority' class='trPos' VALUE="2">
- //   A reason to preserve the order in ther cart is if the order will affect other cgis.  For instance: sort subtracks and see that order in the hgTracks image.
 
     if ($(table).hasClass('sortable') == false) {
         warn('Table is not sortable');
         return;
     }
     var tr = $(table).find('tr.sortable')[0];
     if(tr == undefined) {
         tr = $(table).find('tr')[0];
         if(tr == undefined) {
             warn('Sortable table has no rows');
             return;
         }
         $(tr).addClass('sortable');
         //warn('Made first row tr.sortable');
     }
@@ -1747,139 +1824,94 @@
     $(tbody).hide();
 
     // If not THEAD is found, then create, wrapping first row.
     thead = $(table).find('thead')[0];
     if(thead == undefined) {
         $(tr).wrapAll("<THEAD class='sortable' />")
         thead = $(table).find('thead')[0];
         $(thead).insertBefore(tbody);
         //warn('Wrapped tr.sortable with thead.sortable');
     }
     if ($(thead).hasClass('sortable') == false) {
         $(thead).addClass('sortable');
         //warn('Added sortable class to thead');
     }
 
-    var sortColumns = new sortColumnsGetFromTr(tr,"silent");
+        var sortColumns = new sortTable.columnsFromTr(tr,"silent");
     if (sortColumns == undefined || sortColumns.cellIxs.length == 0) {
         // could mark all columns as sortable!
         $(tr).find('th').each(function (ix) {
             $(this).addClass('sortable');
             $(this).addClass('sort'+(ix+1));
             //warn("Added class='sortable sort"+(ix+1)+"' to th:"+this.innerHTML);
         });
-        sortColumns = new sortColumnsGetFromTr(tr,"silent");
+            sortColumns = new sortTable.columnsFromTr(tr,"silent");
         if (sortColumns == undefined || sortColumns.cellIxs.length == 0) {
             warn("sortable table's header row contains no sort columns.");
             return;
         }
     }
     // Can wrap all columnn headers with link
     $(tr).find("th.sortable").each(function (ix) {
         //if ( $(this).queue('click').length == 0 ) {
         if ( $(this).attr('onclick') == undefined ) {
-            $(this).click( function () { tableSortOnButtonPress(this);} );
+                $(this).click( function () { sortTable.sortOnButtonPress(this);} );
         }
         if ($.browser.msie) { // Special case for IE since CSS :hover doesn't work (note pointer and hand because older IE calls it hand)
             $(this).hover(
                 function () { $(this).css( { backgroundColor: '#CCFFCC', cursor: 'hand' } ); },
                 function () { $(this).css( { backgroundColor: '#FCECC0', cursor: '' } ); }
             );
         }
         if ( $(this).attr('title').length == 0) {
             var title = $(this).text().replace(/[^a-z0-9 ]/ig,'');
             if (title.length > 0 && $(this).find('sup'))
                 title = title.replace(/[0-9]$/g,'');
             if (title.length > 0)
                 $(this).attr('title',"Sort list on '" + title + "'." );
             else
                 $(this).attr('title',"Sort list on column." );
         }
     })
     // Now update all of those cells
-    sortOrderUpdate(table,sortColumns,addSuperscript);
+        sortTable.orderUpdate(table,sortColumns,addSuperscript);
 
     // Alternate colors if requested
     if(altColors != undefined)
-        sortedTableAlternateColors(tbody);
+            sortTable.alternateColors(tbody);
 
     // Highlight rows?  But on subtrack list, this will mess up the "..." coloring.  So just exclude tables with drag and drop
     if ($(table).hasClass('tableWithDragAndDrop') == false) {
         $('tbody.sortable').find('tr').hover(
             function(){ $(this).addClass('bgLevel3');
                         $(this).find('table').addClass('bgLevel3'); },      // Will highlight the rows, including '...'
             function(){ $(this).removeClass('bgLevel3');
                         $(this).find('table').removeClass('bgLevel3'); }
         );
     }
 
     // Finally, make visible
     $(tbody).removeClass('sorting');
     $(tbody).show();
 }
-
-function setCheckboxList(list, value)
-{
-// set value of all checkboxes in semicolon delimited list
-    var names = list.split(";");
-    for(var i=0;i<names.length;i++) {
-        $("input[name='" + names[i] + "']").attr('checked', value);
-    }
 }
 
-function calculateHgTracksWidth()
-{
-// return appropriate width for hgTracks image given users current window width
-    return $(window).width() - 20;
-}
-
-function hgTracksSetWidth()
-{
-    var winWidth = calculateHgTracksWidth();
-    if($("#imgTbl").length == 0) {
-        // XXXX what's this code for?
-        $("#TrackForm").append('<input type="hidden" name="pix" value="' + winWidth + '"/>');
-        //$("#TrackForm").submit();
-    } else {
-        $("input[name=pix]").val(winWidth);
-    }
-}
-
-function filterByMaxHeight(multiSel)
-{   // Setting a max height to scroll dropdownchecklists but
-    // multiSel is hidden when this is done, so it's position and height must be estimated.
-    var pos = $(multiSel).closest(':visible').offset().top + 30;
-    if (pos <= 0)
-        pos = 260;
-
-    // Special mess since the filterBy's on non-current tabs will calculate pos badly.
-    var tabbed = $('input#currentTab');
-    if (tabbed != undefined) {
-        var tabDiv = $(multiSel).parents('div#'+ $(tabbed).attr('value'));
-        if (tabDiv == null || tabDiv == undefined || $(tabDiv).length == 0) {
-            pos = 360;
-        }
+function sortTableInitialize(table,addSuperscript,altColors)
+{   // legacy in case some static pages still initialize the table the old way
+    sortTable.initialize(table,addSuperscript,altColors);
     }
-    var maxHeight = $(window).height() - pos;
-    var selHeight = $(multiSel).children().length * 21;
-    if (maxHeight > selHeight)
-        maxHeight = null;
-    //else if($.browser.msie && maxHeight > 500)  // DDCL bug on IE only.
-    //    maxHeight = 500;          // Seems to be solved by disbling DDCL's window.resize event for IE
 
-    return maxHeight;
-}
 
  //////////////////////////////
 //// findTracks functions ////
 /////////////////////////////
 var findTracks = {
 
     updateMdbHelp: function (index)
     {
     // update the metadata help links based on currently selected values.
     // If index == 0 we update all help items, otherwise we only update the one == index.
         var db = getDb();
         var disabled = {  // blackList
             'accession': 1, 'dataType':        1, 'dataVersion': 1, 'geoSample':    1, 'grant':     1,
             'lab':       1, 'labExpId':        1, 'labVersion':  1, 'origAssembly': 1, 'replicate': 1,
             'setType':   1, 'softwareVersion': 1, 'subId':       1, 'view':         1
@@ -2357,385 +2389,30 @@
         if( $('div#filesFound').length == 1) {
             if( ui.panel.id == 'filesTab')
                 $('div#filesFound').show();
             else
                 $('div#filesFound').hide();
         }
         if( $('div#found').length == 1) {
             if( ui.panel.id != 'filesTab')
                 $('div#found').show();
             else
                 $('div#found').hide();
         }
     }
 }
 
-/////////////////////////////////////////////////////
-// filterTable functions
-
-function filterTableFilterVar(filter)
-{ // returns the var associated with a fileFilter
-
-    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();
-}
-
-/* // This version of filterTable() uses the yieldingIterator methods.
-   // These methods and the yieldingIterator were developed because IE was so slow.
-   // HOWEVER: IE was sped up by avoiding .add() and .parent(),
-   // so I reverted to simpler code but wish to retain this example for
-   // future reference of how to deael with slow javascript.
-
-function _filterTableByClassesIterative(args)
-{ // Applies a single class filter to a filterTable TRs
-  // Called via yieldingIterator
-    if (args.curIx >= args.classes.length)
-        return 0;
-
-    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;
-}
-
-function _filterTableByClassesComplete(args)
-{ // Continues after filterTableByClassesIterative
-  // Called via yieldingIterator
-    var filtersStruct = args.filtersStruct;
-
-    //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.");
-}
-
-function _filterTableIterative(args)
-{ // Applies a single filter to a filterTable TRs
-  // Called via yieldingIterator
-
-    //warnSince("Filter "+args.curIx+" iterating...");
-    if (args.curIx >= args.filters.length)
-        return 0;
-
-    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) {
-                        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 = 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;
-}
-
-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 + " / ");
-    }
-
-    var tbody = $( $('tr.filterable')[0] ).parent('tbody.sorting');
-    if (tbody != undefined)
-         $(tbody).removeClass('sorting');
-    //warnSince("Really complete.");
-}
-
-function _filterTableYielding()
-{ // 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 _filterTable()
-{ // Called by filter onchange event.  Will show/hide trs based upon all filters
-    var showTrs = filterTablesTrsSurviving();
-    //$('tr.filterable').hide();  // <========= This is what is taking so long!
-    $('tr.filterable').css('display', 'none')
-
-    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 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');
-
-    waitOnFunction(_filterTable);
-}
-
-function filterTableDone(event)
-{ // Called by custom 'done' event
-    event.stopImmediatePropagation();
-    $(this).unbind( event );
-    filterTableTrigger();
-}
-
-function filterTable(multiSelect)
-{ // 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
-            $(multiSelect).one('done',filterTableDone);
-            return;
-        //}
-    } else
-        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"
-
-    // Compare to the list of all trs
-    var allTrs = $('tr.filterable'); // Default all
-    if (allTrs.length == 0)
-        return false;
-
-    if ($.browser.msie && $(allTrs).length > 300) // IE takes tooo long, so this should be called only when leaving the filterBy box
-        return false;
-
-    // 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);
-    if (visibleTrs == undefined)
-        return false;
-
-    //if ($.browser.msie && $(visibleTrs).length > 300) // IE takes tooo long, so this should be called only when leaving the filterBy box
-    //    return false;
-
-    if (allTrs.length == visibleTrs.length) {
-        $(filter).children('option.excluded').removeClass('excluded');   // remove .excluded" from all
-        return true;  // Nothing more to do.  All are already excluded
-    }
-
-    // Find the tds that belong to this var
-    var tds = $(visibleTrs).find('td.'+filterVar);
-    if (tds.length == 0) {
-        $(filter).children('option').addClass('excluded');   // add .excluded" to all
-        return true;
-    }
-
-    // Find the val classes
-    var classes = new Array();
-    $(tds).each(function (i) {
-        var someClass = $(this).attr("class").split(' ');
-        someClass = aryRemove(someClass,[filterVar]);
-        var val = someClass.pop()
-        if (aryFind(classes,val) == -1)
-            classes.push(val);
-    });
-    if (classes.length == 0) {
-        $(filter).children('option').addClass('excluded');   // add .excluded" to all
-        return true;
-    }
-
-    // Find all options with those classes
-    $(filter).children('option').each(function (i) {
-        if (aryFind(classes,$(this).val()) != -1)
-            $(this).removeClass('excluded'); // remove .excluded from matching
-        else
-            $(this).addClass('excluded');    // add .excluded" to non-matching
-    });
-
-    // If all options except "all" are included then all should nt be excluded
-    var excluded = $(filter).children('option.excluded');
-    if (excluded.length == 1) {
-        var text = $(excluded[0]).text();
-        if (text == 'All' || text == 'Any')
-            $(excluded[0]).removeClass('excluded');
-    }
-    return true;
-}
-
 function escapeJQuerySelectorChars(str)
 {
     // replace characters which are reserved in jQuery selectors (surprisingly jQuery does not have a built in function to do this).
     return str.replace(/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~"])/g,'\\$1');
 }
 
 var preloadImages = new Array()
 var preloadImageCount = 0;
 function preloadImg(url)
 {
 // force an image to be loaded (e.g. for images in menus or dialogs).
     preloadImages[preloadImageCount] = new Image();
     preloadImages[preloadImageCount].src = url;
     preloadImageCount++;
 }