07040f485bb7b6a1eb5f6202a32fc6107a508d1f
tdreszer
  Wed Dec 15 14:59:40 2010 -0800
Fixed some sort issues.
diff --git src/hg/js/utils.js src/hg/js/utils.js
index 16254c8..b1e43a8 100644
--- src/hg/js/utils.js
+++ src/hg/js/utils.js
@@ -82,56 +82,41 @@
             }
         }
     } else {
         // NS 4.x - I gave up trying to get this to work.
         if(debugLevel>2)
            alert("arrayOfInputsThatMatch is unimplemented for this browser");
     }
     return found;
 }
 function showSubTrackCheckBoxes(onlySelected)
 {
 // If a Subtrack configuration page has show "only selected subtracks" option,
 // This can show/hide tablerows that contain the checkboxes
 // Containing <tr>'s must be id'd with 'tr_' + the checkbox id,
 // while checkbox id must have 'cb_' prefix (ie: 'tr_cb_checkThis' & 'cb_checkThis')
-   if (document.getElementsByTagName)
-   {
-        var list = document.getElementsByTagName('tr');
-        for (var ix=0;ix<list.length;ix++) {
-            var tblRow = list[ix];
-            if(tblRow.id.indexOf("tr_cb_") >= 0) {  // marked as tr containing a cb
-                if(!onlySelected) {
-                    tblRow.style.display = ''; //'table-row' doesn't work in some browsers (ie: IE)
-                } else {
-                    var associated_cb = tblRow.id.substring(3,tblRow.id.length);
-                    chkBox = document.getElementById(associated_cb);
-                    if(chkBox!=undefined && chkBox.checked && chkBox.disabled == false)
-                        tblRow.style.display = '';
+    var trs = $('table.subtracks').children('tbody').children('tr');
+    if(!onlySelected)
+        $(trs).show();
+    else {
+        $(trs).each(function (ix) {
+            var subCB = $(this).find('input.subCB');
+            if (subCB.length > 0 && subCB[0].checked && subCB[0].disabled == false)
+                $(this).show();
                     else
-                        tblRow.style.display = 'none';  // hides
-                }
-            }
-        }
-   }
-   else if (document.all) {
-        if(debug)
-            alert("showSubTrackCheckBoxes is unimplemented for this browser");
-   } else {
-        // NS 4.x - I gave up trying to get this to work.
-        if(debug)
-           alert("showSubTrackCheckBoxes is unimplemented for this browser");
+                $(this).hide();
+        });
    }
 }
 
 function hideOrShowSubtrack(obj)
 {
 // This can show/hide a tablerow that contains a specific object
 // Containing <tr>'s must be id'd with 'tr_' + obj.id
 // Also, this relies upon the "displaySubtracks" radio button control
     var tblRow = document.getElementById("tr_"+obj.id);
 
     if(!obj.checked || obj.disabled)
     {
         var list = document.getElementsByName("displaySubtracks");
         for (var ix=0;ix<list.length;ix++) {
             if(list[ix].value == "selected") {
@@ -429,34 +414,33 @@
                 }
             }
         }
         return true;
     }
 }
 
 function metadataShowHide(trackName,showLonglabel,showShortLabel)
 {
 // Will show subtrack specific configuration controls
 // Config controls not matching name will be hidden
     var divit = $("#div_"+trackName+"_meta");
     if($(divit).css('display') == 'none') {
         $("#div_"+trackName+"_cfg").hide();  // Hide any configuration when opening metadata
 
-        if($(divit).find('table').length == 0) {
+        if($(divit).find('table').length == 0)
             lookupMetadata(trackName,showLonglabel,showShortLabel);
         }
-    }
     $(divit).toggle();  // jQuery hide/show
     return false;
 }
 
 function warnBoxJsSetup()
 {   // Sets up warnBox if not already established.  This is duplicated from htmshell.c
     var html = "";
     html += "<center>";
     html += "<div id='warnBox' style='display:none; background-color:Beige; ";
     html += "border: 3px ridge DarkRed; width:640px; padding:10px; margin:10px; ";
     html += "text-align:left;'>";
     html += "<CENTER><B id='warnHead' style='color:DarkRed;'></B></CENTER>";
     html += "<UL id='warnList'></UL>";
     html += "<CENTER><button id='warnOK' onclick='hideWarnBox();return false;'></button></CENTER>";
     html += "</div></center>";
@@ -973,192 +957,177 @@
 
 //////////// Sorting ////////////
 // Sorting a table by columns relies upon the sortColumns structure
 
 // The sortColumns structure looks like:
 //{
 //    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
 
-function tableSort(tbody,fnCompare,sortColumns)
+function sortField(value,index)
+{
+this.value=value;
+this.index=index;
+}
+
+function sortRow(tr,sortColumns,row)  // UNUSED: sortField 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
+{
+    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)
+{
+    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)
+{
+    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)
 {// 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;
-    var cellIx = sortColumns.cellIxs[0];
-    var useAbbr = sortColumns.useAbbr[0];
-    $(trs).each(function(i) {
-        var th = this.cells[cellIx];
-        if(useAbbr)
-            cols.push(th.abbr);
+    $(trs).each(function(ix) {
+        //cols.push(new sortRow(this,sortColumns,$(this).clone()));
+        var th = this.cells[sortColumns.cellIxs[0]];
+        if(sortColumns.useAbbr[0])
+            cols.push(new sortField(th.abbr,sortColumns.reverse[0],$(this).clone())); // When jQuery >= v1.4, use detach() insterad of clone()
         else
-            cols.push($(th).text());
+            cols.push(new sortField($(th).text(),sortColumns.reverse[0],$(this).clone()));
     });
 
     // Sort the array
-    cols.sort();
-    if(sortColumns.reverse[0])
-        cols.reverse();
+    //cols.sort(sortRowCmp);
+    cols.sort(sortFieldCmp);
 
     // Now reorder the table
     for(var cIx=0;cIx<cols.length;cIx++) {
-        trs = tbody.rows;
-        var match = false;
-        for(var rIx=0;rIx<trs.length;rIx++) {
-            var th = trs[rIx].cells[cellIx];
-            if(useAbbr)
-                match = (th.abbr == cols[cIx]);
-            else
-                match = ($(th).text() == cols[cIx]);
-            if(match) {
-                tbody.appendChild(tbody.removeChild(trs[rIx])); // Always append to end in order
-                break;
-            }
-        }
+        $(tbody.rows[cIx]).replaceWith(cols[cIx].row);
     }
 
-   /* This is obslete and inefficient code.
-    var trs=0,moves=0;
-    var colOrder = new Array();
-    var cIx=0;
-    var trTopIx,trCurIx,trBottomIx=tbody.rows.length - 1;
-    for(trTopIx=0;trTopIx < trBottomIx;trTopIx++) {
-        trs++;
-        var topRow = tbody.rows[trTopIx];
-        for(trCurIx = trTopIx + 1; trCurIx <= trBottomIx; trCurIx++) {
-            var curRow = tbody.rows[trCurIx];
-            var compared = fnCompare(topRow,curRow);
-            if (compared < 0) {
-                tbody.insertBefore(tbody.removeChild(curRow), topRow);
-                topRow = curRow; // New top!
-                moves++;
-            }
-        }
-    }
-    */
     gTbody=tbody;
     gSortColumns=sortColumns;
     setTimeout('tableSortFinish(gTbody,gSortColumns)',5); // Avoid javascript timeouts!
 }
 
 function tableSortFinish(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);
     if ($(tbody).hasClass('altColors'))
         sortedTableAlternateColors(tbody,sortColumns);
-     $(tbody).show();
-}
-
-///// Following compare functions are not currentl;y used since sorting rows must be done indirectly
-function trCompareColumnInnerHtml(tr1,tr2) // NOT USED and will not be until sorting directly by row is avaiable
-{// Compares a set of columns based upon the contents of their first sortField's innerHTML
-    if (tr1.cells[gSortColumns.cellIxs[0]].innerHTML < tr2.cells[gSortColumns.cellIxs[0]].innerHTML)
-        return (gSortColumns.reverse[0] ? -1: 1);
-    else if (tr1.cells[gSortColumns.cellIxs[0]].innerHTML > tr2.cells[gSortColumns.cellIxs[0]].innerHTML)
-        return (gSortColumns.reverse[0] ? 1: -1);
-    return 0;
-}
-
-function trCompareColumnAbbr(tr1,tr2) // NOT USED and will not be until sorting directly by row is avaiable
-{// Compares a set of columns based upon the contents of their first sortField's abbr
-    if (tr1.cells[gSortColumns.cellIxs[0]].abbr < tr2.cells[gSortColumns.cellIxs[0]].abbr)
-        return (gSortColumns.reverse[0] ? -1: 1);
-    else if (tr1.cells[gSortColumns.cellIxs[0]].abbr > tr2.cells[gSortColumns.cellIxs[0]].abbr)
-        return (gSortColumns.reverse[0] ? 1: -1);
-    return 0;
-}
-
-function trCompareByColumn(tr1,tr2) // NOT USED and will not be until sorting directly by row is avaiable
-{// Compares a set of columns based upon the contents of their first sortField's abbr
-    if (sortColumns.useAbbr[0])
-        return trCompareColumnAbbr(tr1,tr2);
-    else
-        return trCompareColumnInnerHtml(tr1,tr2);
+    //$(tbody).show();
+    $(tbody).removeClass('sorting');
 }
 
 function tableSortByColumns(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).hide();
+    $(tbody).addClass('sorting');
     gTbody=tbody;
     gSortColumns=sortColumns;
-    setTimeout('tableSort(gTbody,trCompareByColumn,gSortColumns)',5); // This allows hiding the rows while sorting!
+    setTimeout('tableSort(gTbody,gSortColumns)',5); // This allows hiding the rows while sorting!
 }
 
 function trAlternateColors(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).find('tr:visible').each( function(i) {
+        $(tbody).children('tr:visible').each( function(i) {
             if (curCount == 0 ) {
                 curCount  = rowGroup;
                 darker = (!darker);
             }
-            //$(this).css('backgroundColor', curColor );
             if (darker) {
                 $(this).removeClass("bgLevel1");
                 $(this).addClass(   "bgLevel2");
             } else {
                 $(this).removeClass("bgLevel2");
                 $(this).addClass(   "bgLevel1");
             }
             curCount--;
         });
 
     } else {
 
         var lastContent = "startWithChange";
         var cIxs = new Array();
         for(var aIx=2;aIx<arguments.length;aIx++) {   // multiple columns
             cIxs[aIx-2] = arguments[aIx];
         }
-        $(tbody).find('tr:visible').each( function(i) {
+        $(tbody).children('tr:visible').each( function(i) {
             curContent = "";
             for(var ix=0;ix<cIxs.length;ix++) {
                 if (this.cells[cIxs[ix]]) {
                     curContent += (this.cells[cIxs[ix]].abbr != "" ?
                                 this.cells[cIxs[ix]].abbr       :
                                 this.cells[cIxs[ix]].innerHTML );
                 }
             }
             if (lastContent != curContent ) {
                 lastContent  = curContent;
                 darker = (!darker);
             }
-            //$(this).css('backgroundColor', curColor );
             if (darker) {
                 $(this).removeClass("bgLevel1");
                 $(this).addClass(   "bgLevel2");
             } else {
                 $(this).removeClass("bgLevel2");
                 $(this).addClass(   "bgLevel1");
             }
         });
     }
 }
 
 function sortedTableAlternateColors(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;
@@ -1203,33 +1172,33 @@
         addSuperscript = false;
     if ($(table).is('tbody'))
         table = $(table).parent();
     var tr = $(table).find('tr.sortable')[0];
     if (tr) {
         //warn("sortOrderUpdate("+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 class = classList.pop();
-                    if (class.indexOf("sort") == 0)
-                        $(this).removeClass(class);
+                    var aClass = classList.pop();
+                    if (aClass.indexOf("sort") == 0)
+                        $(this).removeClass(aClass);
                 }
 
                 // Now add current sort classes
                 $(this).addClass("sort"+(cIx+1));
                 if (sortColumns.reverse[cIx])
                     $(this).addClass("sortRev");
 
                 // update any superscript
                 sup = $(this).find('sup')[0];
                 if (sup || addSuperscript) {
                     var content = (sortColumns.reverse[cIx] == false ? "&darr;":"&uarr;");
 
                     if (sortColumns.cellIxs.length>1) { // Number only if more than one
                         if (cIx < 5)  // Show numbering and direction only for the first 5
                             content += (cIx+1);
@@ -1256,37 +1225,37 @@
     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;
             while( classList.length > 0 ) {
-                var class = classList.pop();
-                if (class.indexOf("sort") == 0) {
-                    if (class == "sortRev")
+                var aClass = classList.pop();
+                if (aClass.indexOf("sort") == 0) {
+                    if (aClass == "sortRev")
                         reverse = true;
                     else {
-                        class = class.substring(4);  // clip off the "sort" portion
-                        var ix = parseInt(class);
+                        aClass = aClass.substring(4);  // clip off the "sort" portion
+                        var ix = parseInt(aClass);
                         if (ix != NaN) {
                             sortIx = ix;
                         }
                     }
                 }
             }
             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)
@@ -1307,41 +1276,42 @@
             this.tags.push(pair[0]);
             this.reverse.push(pair[1] != '+');
         }
     }
 }
 function sortColumnsGetFromTr(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);
     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 an additional array
+    // 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])
-                this.cellIxs[tIx] = ths[ix].cellIndex;
-            else if (ths[ix].cellIndex == this.tags[tIx])
+            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 = (ths[ix].abbr.length > 0);
+                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)
 {// Creates a sortColumns struct from the contents of a 'table.sortable'
     this.inheritNow = sortColumnsGetFromTr;
     var tr = $(table).find('tr.sortable')[0];
     //if (tr == undefined && debug) warn("Couldn't find 'tr.sortable' rows:"+table.rows.length);
     this.inheritNow(tr);
 }
@@ -1358,36 +1328,38 @@
     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 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!
         var tbody = $(table).find("tbody.sortable")[0];
         //if (tbody == undefined && debug) warn("Couldn't find 'tbody.sortable' 5");
         tableSortByColumns(tbody,theOrder);
     }
     return;