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/ddcl.js src/hg/js/ddcl.js index f870f99..9b1e5e9 100644 --- src/hg/js/ddcl.js +++ src/hg/js/ddcl.js @@ -80,31 +80,31 @@ // Set the label var control = $(controlSelector).parent(); ddcl.labelSet(control,"Select multiple...",'#000088','Selecting...'); // Find the active 'items' and original 'options' var id = $(control).attr('id').substring('ddcl-'.length); var dropWrapper = $('#ddcl-' + id + '-ddw');//.first(); var multiSelect = $(document.getElementById(id)); var allCheckboxes = $(dropWrapper).find("input.active"); var selectOptions = multiSelect[0].options; // Special juice to handle "exclude" options based upon competing filterBoxes try { if(($(multiSelect).hasClass('filterComp') && filterCompositeExcludeOptions(multiSelect)) - || ($(multiSelect).hasClass('filterTable') && filterTableExcludeOptions(multiSelect))) { + || ($(multiSelect).hasClass('filterTable') && filterTable.excludeOptions(multiSelect))) { // "exclude" items based upon the exclude tag of the true options allCheckboxes.each(function(index) { var item = $(this).parent(); if($(selectOptions[index]).hasClass('excluded')) { $(item).addClass("ui-state-excluded"); } else //if($(item).hasClass("ui-state-excluded")) $(item).removeClass("ui-state-excluded"); }); } } catch (err) {} // OK if filterCompositeExcludeOptions is not defined. // Show only first as selected if it is selected if (allCheckboxes[0].checked == true) { @@ -311,19 +311,387 @@ } } }, start: function () { // necessary to delay startup till all selects get ids. $('.filterBy,.filterComp').each( function(i) { if ($(this).hasClass('filterComp')) ddcl.setup(this); // Not nonIsAll else ddcl.setup(this,'noneIsAll') }); } }; + /////////////////////// + //// Filter Tables //// +/////////////////////// +var filterTable = { + // Filter Tables are HTML tables that can be filtered by drop-down-checkbox-lists (ddcl) controls + // To use this feature trs must have class 'filterble' and contain tds with classes matching category and value + // eg: <TR class='filterable'><TD class='cell 8988T'>8988T</td> + // and ddcl 'filterBy' controls with additional classes 'filterTable' and the category, and contains options + // with the the value to be filtered. Also, the ddcl objects onChange event must call filterTable.filter(this) + // eg: <SELECT name='cell' MULTIPLE class='filterTable cell filterBy' onchange='filterTable.filter(this);' style='font-size:.9em;'> + // <OPTION SELECTED VALUE='All'>All</OPTION> + // <OPTION VALUE='8988T'>8988T</OPTION> + // Multiple filterTable controls can be defined, but only one table can be the target of filtering. + + variable: function (filter) + { // returns the variable associated with a filter + + 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 deal with slow javascript. + + _byClassesIterative: function (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; + }, + + _byClassesComplete: function (args) + { // Continues after filterTable._byClassesIterative + // 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(filterTable._filterIterative,filterTable._complete,filtersStruct); + //warnSince("Really complete classes."); + }, + + _filterIterative: function (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 = filterTable.variable(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(filterTable._byClassesIterative,filterTable._byClassesComplete,classesStruct); + return -1; // Stops itteration now, but will be resumed in filterTable._byClassesComplete + } 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; + }, + + _complete: function (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."); + }, + + _filterWithYielding: function () + { // 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(filterTable._filterIterative,filterTable._complete,filtersStruct); + }, + */ + + applyOneFilter: function (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 = filterTable.variable(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; + }, + + trsSurviving: function (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 = filterTable.applyOneFilter(filters[ix],showTrs) + } + return showTrs; + }, + + _filter: function () + { // Called by filter onchange event. Will show/hide trs based upon all filters + var showTrs = filterTable.trsSurviving(); + //$('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'); + }, + + trigger: function () + { // 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._filter); + }, + + done: function (event) + { // Called by custom 'done' event + event.stopImmediatePropagation(); + $(this).unbind( event ); + filterTable.trigger(); + }, + + filter: function (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',filterTable.done); + return; + //} + } else + filterTable.trigger(); + }, + + excludeOptions: function (filter) + { // bound to 'click' event inside ddcl.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 = filterTable.variable(filter); + if (filterVar == undefined) + return false; + + // Look at list of visible trs. + var visibleTrs = filterTable.trsSurviving(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; + } +} + $(document).ready(function() { setTimeout('ddcl.start();',2); // necessary to delay startup till all selects get ids. }); +