70ef68d210fa4b76c16f7b8e8142a8180f75052a tdreszer Fri Jun 17 20:14:06 2011 -0700 Big checkin of jquery 1.5.1, jquery-ui 1.8 and ui-dropdownchecklist 1.3. Needs more testing and CSS work. DDCL is working on FF, Chrome and IE. Both Chrome and IE support required tiny changes to the DDCL wigit code. DDCL may still have problems with IE. Other jquery wigits have been tested at a cursory level and work fine. Ajax calls may need an extrat dataType: html param. Dialog (popup) needs CSS tuning. diff --git src/hg/js/utils.js src/hg/js/utils.js index 5e57f03..4486aae 100644 --- src/hg/js/utils.js +++ src/hg/js/utils.js @@ -1748,91 +1748,99 @@ if(newVar != undefined && a && a[1]) { var num = a[1]; if ($('#advancedTab').length == 1 && $('#filesTab').length == 1) { $("select.mdbVar[name='hgt_mdbVar"+num+"'][value!='"+newVar+"']").val(newVar); } var cgiVars = "db=" + getDb() + "&cmd=hgt_mdbVal" + num + "&var=" + newVar; if (document.URL.search('hgFileSearch') != -1) cgiVars += "&fileSearch=1"; else cgiVars += "&fileSearch=0"; $.ajax({ type: "GET", url: "../cgi-bin/hgApi", data: cgiVars, + dataType: 'html', trueSuccess: findTracksHandleNewMdbVals, success: catchErrorOrDispatch, error: errorHandler, cache: true, cmd: "hgt_mdbVal" + num, // NOTE must match METADATA_VALUE_PREFIX in hg/hgTracks/searchTracks.c num: num }); } - //findTracksSearchButtonsEnable(true); + // NOTE: with newJquery, the response is getting a new error (missing ; before statement) + // There were also several XML parsing errors. + // This error is fixed with the addition of "dataType: 'html'," above. } function findTracksHandleNewMdbVals(response, status) { // Handle ajax response (repopulate a metadata val select) // This handles the currnet case when 2 vars have the same name (e.g. advanced, files tabs) var td = $('td#' + this.cmd ); if (td != undefined) { var usesFilterBy = ($("select.mdbVar[name='hgt_mdbVar"+this.num+"']").hasClass('noMulti') == false); td.empty(); td.append(response); var inp = $(td).find('.mdbVal'); var tdIsLike = $('td#isLike'+this.num); if (inp != undefined && tdIsLike != undefined) { if ($(inp).hasClass('freeText')) { $(tdIsLike).text('contains'); } else if (usesFilterBy && $(inp).hasClass('filterBy')) { $(tdIsLike).text('is among'); } else { $(tdIsLike).text('is'); } } $(td).find('.filterBy').each( function(i) { // Do this by 'each' to set noneIsAll individually if (usesFilterBy) { + if (newJQuery) + ddclSetup(this,'noneIsAll'); + else $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true, maxDropHeight: filterByMaxHeight(this) }); } else { $(this).attr("multiple",false); $(this).removeClass('filterBy'); $(this).show(); } }); } updateMetaDataHelpLinks(this.num); } function findTracksMdbValChanged(obj) { // Keep all tabs with same selects in sync TODO: Change from name to id based identification and only have one set of inputs in form // This handles the currnet case when 2 vars have the same name (e.g. advanced, files tabs) findTracksClearFound(); // Changing values so abandon what has been found if ($('#advancedTab').length == 1 && $('#filesTab').length == 1) { var newVal = $(obj).val(); var a = /hgt_mdbVal(\d+)/.exec(obj.name); // NOTE must match METADATA_NAME_PREFIX in hg/hgTracks/searchTracks.c if(newVal != undefined && a && a[1]) { var num = a[1]; $("input.mdbVal[name='hgt_mdbVal"+num+"'][value!='"+newVal+"']").val(newVal); $("select.mdbVal[name='hgt_mdbVal"+num+"'][value!='"+newVal+"']").each( function (i) { $(this).val(newVal); if ($(this).hasClass('filterBy')) { - //$(this).dropdownchecklist("refresh"); // requires v1.1 $(this).dropdownchecklist("destroy"); + if (newJQuery) + ddclSetup(this,'noneIsAll'); + else $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true, maxDropHeight: filterByMaxHeight(this) }); } }); } } //findTracksSearchButtonsEnable(true); } function findTracksChangeVis(seenVis) { // called by onchange of vis var visName = $(seenVis).attr('id'); var trackName = visName.substring(0,visName.length - "_id".length) var hiddenVis = $("input[name='"+trackName+"']"); var tdb = tdbGetJsonRecord(trackName); if($(seenVis).val() != "hide") @@ -1999,42 +2007,49 @@ 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; + return maxHeight; } function findTracksClear() {// Clear found tracks and all input controls findTracksClearFound(); $('input[type="text"]').val(''); // This will always be found //$('select.mdbVar').attr('selectedIndex',0); // Do we want to set the first two to cell/antibody? $('select.mdbVal').attr('selectedIndex',0); // Should be 'Any' $('select.filterBy').each( function(i) { // Do this by 'each' to set noneIsAll individually //$(this).dropdownchecklist("refresh"); // requires v1.1 $(this).dropdownchecklist("destroy"); + $(this).show(); + if (newJQuery) + ddclSetup(this,'noneIsAll'); + else $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true, maxDropHeight: filterByMaxHeight(this) }); }); $('select.groupSearch').attr('selectedIndex',0); $('select.typeSearch').attr('selectedIndex',0); //findTracksSearchButtonsEnable(false); return false; } function findTracksSortNow(obj) {// Called by radio button to sort tracks if( $('#sortIt').length == 0 ) $('form#trackSearch').append(""); else $('#sortIt').val($(obj).val()); @@ -2075,32 +2090,47 @@ $(thisForm).attr('action',"../cgi-bin/hgTrackUi?hgt_tSearch=Search&g="+name); $(thisForm).find('input.viewBtn').click(); } function findTracksMdbSelectPlusMinus(obj, rowNum) { // Now [+][-] mdb var rows with javascript rather than cgi roundtrip // Will remove row or clone new one. Complication is that 'advanced' and 'files' tab duplicate the tables! var objId = $(obj).attr('id'); rowNum = objId.substring(objId.length - 1); if ($(obj).val() == '+') { var buttons = $("input#plusButton"+rowNum); // Two tabs may have the exact same buttons! if (buttons.length > 0) { $(buttons).each(function (i) { var tr = $(this).parents('tr.mdbSelect')[0]; - if (tr != undefined) + if (tr != undefined) { + if(newJQuery) { + var newTr = $(tr).clone(); + var element = $(newTr).find("select.mdbVar")[0]; + if (element != undefined) + $(element).attr('selectedIndex',-1); + + element = $(newTr).find("td[id^='hgt_mdbVal']")[0]; + if (element != undefined) + $(element).empty(); + element = $(newTr).find("td[id^='isLike']")[0]; + if (element != undefined) + $(element).empty(); + $(tr).after( newTr ); + } else $(tr).after( $(tr).clone() ); + } findTracksMdbSelectRowsNormalize($(tr).parents('table')[0]); // magic is in this function }); return false; } } else { // == '-' var buttons = $("input#minusButton"+rowNum); // Two tabs may have the exact same buttons! if (buttons.length > 0) { var remaining = 0; $(buttons).each(function (i) { var tr = $(this).parents('tr')[0]; var table = $(tr).parents('table')[0]; if (tr != undefined) $(tr).remove(); remaining = findTracksMdbSelectRowsNormalize(table); // Must renormalize since 2nd of 3 rows may have been removed }); @@ -2159,30 +2189,34 @@ $(element).attr('id','isLike' + rowNum); element = $(this).find("td[id^='hgt_mdbVal']")[0]; if (element != undefined) $(element).attr('id','hgt_mdbVal' + rowNum); }); return mdbSelectRows.length; } return 0; } function findTracksSwitchTabs(ui) { // switching tabs on findTracks page if( ui.panel.id == 'simpleTab' && $('div#found').length < 1) { setTimeout("$('input#simpleSearch').focus();",20); // delay necessary, since select event not afterSelect event + } else if( ui.panel.id == 'advancedTab') { + // Advanced tab has DDCL wigets which were sized badly because the hidden width was unknown + // delay necessary, since select event not afterSelect event + setTimeout("ddclReinit($('div#advancedTab').find('select.filterBy'),false);",20); } 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(); } } @@ -2340,18 +2374,313 @@ 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; } + +/////////// DDCL: drop-down checkbox-list wrapper code ///////////// + +function textOfObjWrappedInStyle(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 = ""; + text += $(obj).text(); + if (style != undefined && style.length > 0) + text += ""; + + return text; +} + +function ddclTextOfCurrentSelections(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 = textOfObjWrappedInStyle(chosen[0]); + } else if(chosenCount == options.length) { + msg = textOfObjWrappedInStyle(options[0]); + } else { + for(var ix=0;ix 0) + msg += "
"; + msg += textOfObjWrappedInStyle(chosen[ix]); + } + } + return msg; +} + +function ddclLabelSet(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('
').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); +} + +function ddclOnOpen(event) +{ // Called by a DDCL onClick event (when the drop list is opened) + + // Set the label + var control = $(this).parent(); + ddclLabelSet(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 selector = $('#' + id); + var allCheckboxes = $(dropWrapper).find("input.active"); + var selectOptions = selector[0].options; + + // Special juice to handle "exclude" options based upon competing filterBoxes + try { + if(($(selector).hasClass('filterComp') && filterCompositeExcludeOptions(selector)) + || ($(selector).hasClass('filterTable') && filterTableExcludeOptions(selector))) { + + // "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. + +} + +function ddclOnComplete(selector) +{ // 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 = $(selector).attr('id'); + + // If no options are selected, may have to force all + if ($(selector).attr('noneIsAll') == 'true') { + var chosen = $(selector).find('option:selected'); + if (chosen.length == 0) { + //$(selector).first('option').first().attr('selected',true); + selector.options[0].selected = true; + // How to check the first item? + var dropWrapper = $('#ddcl-' + id + '-ddw'); + $(dropWrapper).find("input").first().attr("checked",true); + } + } + + + var msg = ddclTextOfCurrentSelections(selector.options); + + var control = this.controlWrapper; + if (control == null || control == undefined) { // caller is not constant + control = $('#ddcl-' + id); + } + var newColor = ''; + if ($(selector).find('option:selected').length == 0) + newColor = '#AA0000'; // red + //else if (msg.search(/color:/i) == -1) + // newColor = 'black'; + ddclLabelSet(control,msg,newColor,'Click to select...'); +} + +function ddclReinit(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 + if (!force) { // condition on bad dimensions + var id = $(this).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 + } + } + $(this).dropdownchecklist("destroy"); + $(this).show(); // necessary to get dimensions + if (newJQuery) + ddclSetup(this,'noneIsAll'); + else + $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true, maxDropHeight: filterByMaxHeight(this) }); + }); +} + +function ddclSetup(obj) +{ // Initialize the multiselect as a DDCL (drop-down checkbox-list) + + // 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 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: ddclOnComplete + }); + $(obj).attr('noneIsAll',myNoneIsAll); // Declare this as none selected same as all selected + ddclOnComplete(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('ddclSetup('+id+') failed to create drop-down checkbox-list'); + return; + } + var controlSelector = $(control).find(".ui-dropdownchecklist-selector"); + $(controlSelector).click(ddclOnOpen); + $(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('ddclSetup('+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]); + } + } + + // TODO: + // - Chrome multi-select required changing ddcl code as per issue 176. + // - package these changes: Could keep this in utils or could make a wrapper like DDCL itself. + // - test test test + // Works on FF: track search, hgFileUi, hgTrackUi filterComp and filterBy, popup + // Works on Chrom: track search, hgFileUi, hgTrackUi filterComp and filterBy, popup + // Mostly works on IE: track search, hgFileUi, hgTrackUi filterComp and filterBy, popup + // - IE needed special code to block window.resize event in DDCL. + // - Have seen scripting timeouts on IE but I am not sure this is still an issue. + // *** v1.4 has been released which works with jquery 1.6.1 *** +}