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>↓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>↓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++; }