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 ? "↓":"↑"); 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;