4c15f3e254d1e9c7cb3c4dca17f2808c07dbdb4f tdreszer Thu Jul 7 17:52:25 2011 -0700 Moved all ddcl extension code into a ddcl object in ddcl.js. Reworked filterComp code to be more like filterTable code since IE was getting timeouts on HAIB TFBS. diff --git src/hg/js/ddcl.js src/hg/js/ddcl.js new file mode 100644 index 0000000..894873b --- /dev/null +++ src/hg/js/ddcl.js @@ -0,0 +1,329 @@ +// The "ddcl" object that contains all the DDCL extension/support code. +// DDCL: drop-down checkbox-list wrapper code to ui.dropdownchecklist.js jQuery widget. +// The reason for extension/support code beyond the jquery widget is that +// We have changed some widget functionality: +// - Closed control show all currently selected options in multiple lines with CSS +// - Open control can gray out invalid choices + +// NOTE: +// Currently tied to ui.dropdownchecklist.js v1.3, with 2 "UCSC" changes: +// - Chrome multi-select required changing ddcl code as per issue 176. +// - IE needed special code to block window.resize event in DDCL. +// *** v1.4 has been released which works with jquery 1.6.1 *** + +// Some useful names defined: +// ddcl: This object just contains these supporting functions in one package. +// multiSelect: original <SELECT multiple> that ddcl wraps around +// (AKA filterBy: all mutiSelects are of class .filterBy [may also be .filterComp, .filterTable]) +// selectOptions: original multiSelect.options (get updated, sent to cart, etc.) +// control: The whole gaggle of elements that get created when one multiSelect becomes a ddcl +// controlLabel: Just the text inside the closed control +// controlSelector: the closed box waiting to be clicked +// dropWrapper: The div that contains all the checkboxes and is only seen when the DDCL is open +// allCheckboxes: one to one correspondence with selectOptions + +var ddcl = { + mySelf: null, + + textOfObjWrappedInStyle: function (obj) + { // returns the obj text and if there is obj style, the text gets span wrapped with it + var text = ''; + var style = $(obj).attr('style'); + if (style != undefined && style.length > 0) + text = "<span style='"+style+"'>"; + text += $(obj).text(); + if (style != undefined && style.length > 0) + text += "</span>"; + + return text; + }, + + textOfCurrentSelections: function (options) { + // Generates a multi-line string of currently selected options + var chosen = $(options).filter(':selected'); // Works with FF and Chrome but not IE! + if (chosen.length == 0 && $.browser.msie) + chosen = $(options).find(':selected'); // Works with IE but not FF and Chrome! + var chosenCount = $(chosen).length; + var msg = ''; + if(chosenCount == 0) { + msg = 'Please select...'; + } else if(chosenCount == 1) { + msg = mySelf.textOfObjWrappedInStyle(chosen[0]); + } else if(chosenCount == options.length) { + msg = mySelf.textOfObjWrappedInStyle(options[0]); + } else { + for(var ix=0;ix<chosenCount;ix++) { + if (ix > 0) + msg += "<BR>"; + msg += mySelf.textOfObjWrappedInStyle(chosen[ix]); + } + } + return msg; + }, + + labelSet: function (control,msg,newTextColor,newTitle) { + // Sets the label text (as opposed to the drop-down options) + var controlLabel = $(control).find(".ui-dropdownchecklist-text"); + var controlSelector = $(control).find(".ui-dropdownchecklist-selector"); + var newHeight = msg.split('<BR>').length * 20; + //$(control).css('height',newHeight + 'px'); + $(controlSelector).css({height: newHeight + 'px', background: '#fff'}); + $(controlLabel).attr('title',newTitle); + $(controlLabel).css({height: newHeight + 'px'}); + $(controlLabel).css('color',newTextColor ); // could be empty string, thus removing the color + $(controlLabel).html(msg); + }, + + onOpen: function (event) { + // Called by a DDCL onClick event (when the drop list is opened) + var controlSelector = this; + + // Set the label + var control = $(controlSelector).parent(); + mySelf.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 = $('#' + 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))) { + + // "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) { + allCheckboxes.each(function(index) { + if (index > 0) + $(this).attr('checked',false); + }); + } + }, + + onComplete: function (multiSelect) { + // Called by ui.dropdownchecklist.js when selections have been made + // Also called at init to fill the selector with current choices + + // Warning: In IE this gets called when still selecting! + + var id = $(multiSelect).attr('id'); + + // If no options are selected, may have to force all + var chosen = $(multiSelect).find('option:selected'); + if (chosen.length == 0) { + if ($(multiSelect).hasClass('noneIsAll')) { + //$(multiSelect).first('option').first().attr('selected',true); + multiSelect.options[0].selected = true; + // How to check the first item? + var dropWrapper = $('#ddcl-' + id + '-ddw'); + $(dropWrapper).find("input").first().attr("checked",true); + } + } else if (chosen.length == $(multiSelect).find('option').length) { + // If all are chosen then select only the first! + $(chosen).each(function(index) { + if (index > 0) + $(this).attr('selected',false); + }); + } + + var msg = ddcl.textOfCurrentSelections(multiSelect.options); + + var control = $('#ddcl-' + id); + var newColor = ''; + if ($(multiSelect).find('option:selected').length == 0) + newColor = '#AA0000'; // red + //else if (msg.search(/color:/i) == -1) + // newColor = 'black'; + mySelf.labelSet(control,msg,newColor,'Click to select...'); + + // Notice special handling for a custom event + $(multiSelect).trigger('done',multiSelect); + }, + + reinit: function (filterBys,force) { + // ReInitialize the DDCLs (drop-down checkbox-list) + // This is done when the track search with tabs gets switched to advanced tab + // because the DDCLs were setup on hidden filterBys and dimensiuons are wrong. + // if not force, then only reinit when the dimensions are suspect + + if (filterBys.length < 1) + return; + + $(filterBys).each( function(i) { // Do this by 'each' to set noneIsAll individually + var multiSelect = this; + if (!force) { // condition on bad dimensions + var id = $(multiSelect).attr('id'); + control = $('#ddcl-' + id); + if (control != null && control != undefined) { + var controlSelector = $(control).find(".ui-dropdownchecklist-selector"); + if ($(controlSelector).width() > 20) + return; // Dimensions look okay + } + } + $(multiSelect).dropdownchecklist("destroy"); + $(multiSelect).show(); // necessary to get dimensions + if (newJQuery) + ddclSetup(multiSelect,'noneIsAll'); + else + $(multiSelect).dropdownchecklist({ firstItemChecksAll: true, + noneIsAll: true, + maxDropHeight: filterByMaxHeight(multiSelect) }); + }); + }, + + setup: function (obj) { + // Initialize the multiselect as a DDCL (drop-down checkbox-list) + mySelf = this; + + // Defaults + var myFirstIsAll = true; + var myNoneIsAll = false; + var myIcon = null; + var myEmptyText = 'Select...'; + var myClose = 'close '; + var myDropHeight = filterByMaxHeight(obj); + // parse optional args + for(var vIx=1;vIx<arguments.length;vIx++) { + switch(arguments[vIx]) { + case 'noneIsAll': myNoneIsAll = true; + break; + case 'firstNotAll': myFirstIsAll = false; + break; + case 'arrows': myIcon = {}; + break; + case 'noClose': myClose = null; + break; + case 'label': vIx++; + if (vIx<arguments.length) + myEmptyText = arguments[vIx]; + break; + default: warn('ddcl.setup() unexpected argument: '+arguments[vIx]); + break; + } + } + if (myFirstIsAll == false) + myNoneIsAll = false; + + // Make sure there is an id! + var id = $(obj).attr('id'); + if (id == null || id.length == 0) { + var name = $(obj).attr('name'); + if (name != null && name.length > 0) + id = 'dd-' + name; + else { + var ix = $('select').index(obj); + id = 'ix' + ix; + } + $(obj).attr('id',id); + } + + // These values can only be taken from the select before it becomes a DDCL + var maxWidth = $(obj).width(); + var minWidth = $(obj).css('min-width'); + if (minWidth != undefined && minWidth.length > 0) { + minWidth = parseInt(maxWidth); + if (maxWidth < minWidth) + maxWidth = minWidth; + } + maxWidth = (Math.ceil(maxWidth / 10) * 10) + 10; // Makes for more even boxes + var style = $(obj).attr('style'); + + // The magic starts here: + $(obj).dropdownchecklist({ + firstItemChecksAll: true, + noneIsAll: myNoneIsAll, + maxDropHeight: myDropHeight, + icon: myIcon, + emptyText: myEmptyText, + explicitClose: myClose, + textFormatFunction: function () { return 'selecting...'; } , + onComplete: mySelf.onComplete + }); + if (myNoneIsAll) + $(obj).addClass('noneIsAll'); // Declare this as none selected same as all selected + mySelf.onComplete(obj); // shows selected items in multiple lines + + // Set up the selector (control seen always and replacing select) + control = $('#ddcl-' + id); + if (control == null || control == undefined) { + warn('ddcl.setup('+id+') failed to create drop-down checkbox-list'); + return; + } + var controlSelector = $(control).find(".ui-dropdownchecklist-selector"); + $(controlSelector).click(mySelf.onOpen); + $(controlSelector).css({width:maxWidth+'px'}); + var controlText = $(control).find(".ui-dropdownchecklist-text"); + $(controlText).css({width:maxWidth+'px'}); + + // Set up the drop list (control seen only on fucus and with items to choose) + var dropWrapper = $('#ddcl-' + id + '-ddw'); + if (dropWrapper == null || dropWrapper == undefined) { + warn('ddcl.setup('+id+') failed to create drop-down checkbox-list'); + return; + } + // Individual items need styling + var itemHeight = 22; + // Exclude the close button + var dropItems = $(dropWrapper).find(".ui-dropdownchecklist-item");//.not('.ui-dropdownchecklist-close'); + $(dropItems).hover(function () {$(this).css({backgroundColor:'#CCFFCC'});}, + function () {$(this).css({backgroundColor:'white'});}); + var dropItems = $(dropItems).not('.ui-dropdownchecklist-close'); + $(dropItems).css({background:'white', borderStyle:'none', height:itemHeight+'px'}); + var itemCount = dropItems.length; + if (myClose != null) { // target the close button + var dropClose = $(dropWrapper).find(".ui-dropdownchecklist-close"); + $(dropClose).css({height:(itemHeight - 1)+'px',textAlign:'center'}); + itemCount++; + } + + // The whole droplist needs styling + var dropContainerDiv = dropWrapper.find(".ui-dropdownchecklist-dropcontainer"); + var maxHeight = (itemHeight*itemCount) + 1; // extra prevents unwanted vertical scrollbar + var divHeight = dropContainerDiv.outerHeight(); + if (divHeight > maxHeight) { + $(dropContainerDiv).css({height:maxHeight+'px'}); + $(dropWrapper).css({height:maxHeight+'px'}); + } + maxWidth += 30; // extra avoids horizontal scrollBar when vertical one is included + $(dropContainerDiv).css({width:(maxWidth)+'px'}); + $(dropWrapper).css({width:maxWidth+'px'}); + + // Finally we can get style from the original select and apply it to the whole control (hopefully) + if (style != undefined && style.length > 0) { + var styles = style.split(';'); + for(var ix = 0;ix < styles.length;ix++) { + var aStyleDef = styles[ix].split(':'); + aStyleDef[0] = aStyleDef[0].replace(' ',''); // no spaces + if (aStyleDef[0] != 'display') // WARNING: Need to see if other styles should be restricted. + $(control).css(aStyleDef[0],aStyleDef[1]); + if (aStyleDef[0].substring(0,4) == 'font') // Fonts should be applied too + $(dropItems).css(aStyleDef[0],aStyleDef[1]); + } + } + + } +}; + +$(document).ready(function() { + $('.filterBy,.filterComp').each( function(i) { + if ($(this).hasClass('filterComp')) + ddcl.setup(this); // Not nonIsAll + else + ddcl.setup(this,'noneIsAll') + }); +});