d247275d8266bad8d22c108df1b40a8665e109bf tdreszer Fri Sep 23 16:16:24 2011 -0700 Another load of subCfg module (scm). Heavy work getting subCbs, matCbs and views all playing nice. diff --git src/hg/js/subCfg.js src/hg/js/subCfg.js new file mode 100644 index 0000000..4a619e6 --- /dev/null +++ src/hg/js/subCfg.js @@ -0,0 +1,953 @@ + +// subCfg the subtrack Configureation module (scm) for hgTrackUi +// +// This module is for subtrack level config embedded dialogs in hgTrackUi. +// Subtrack config dialogs are embedded in the subtrack table and get populated when first +// opened. Composite and view level controls (parents) when updated override related +// subtrack controls (children). Subtrack controls, when updated overide parent controls +// for the one subtrack. Controls have no 'name' attribute until the user alters their value. +// Unnamed controls are not sent to the cart!!! + +// Definitions as used here: +// obj: an input or select style html control which may be named or unnamed +// parentObj: composite or view level obj which has subtrack level childObjs associated +// childObj: subtrack level obj that has composite and or view level parentObjs +// cfg: subtrack level embedded dialog which can be opened or closed (hidden) and isn't +// populated till first opened. Can also be a viewCfg and maybe a compositeCfg +// populate: act of filling a subtrack cfg with controls +// named: control with a name attribute which means it has been updated by the user. +// Unfortunately radio buttons are an exception, since they must keep their names. +// unnamed: control that has not been updated by the user. Will not be sent to cart. + +// TODO: +// 1) SOLVED: Radio buttons work BUT they require name to keep unified set, so rely upon 'changed' class to detect changes. +// 2) SOLVED: checkboxes: working with name = boolshad.{name} FIXME: multishad? +// 3) SOLVED: filterBy,filterComp working: they rely upon id, and id with '.' screwed it all up. So replaced '.' with '_-' +// 4) SOLVED: subCfg not matching parent: solved. +// 5) SOLVED: OpenChromSynth: subtrack filterby needs to be updated by composite filterBy. +// 6) SOLVED: check subtrack to enable/disable fauxVis and wrench +// 7) SOLVED: matCB should effect subCb including enable/disable fauxVis and wrench +// 8) SOLVED: composite/view vis should effect subVis and enable/disable fauxVis and wrench +// 9) SOLVED: inside out: changing subtrack vis should affect subCB and matCB +// 10) SOLVED: Loosing checked tracks!!! Setting vis clears checkboxes? +// 11) TESTED: hui.c #ifdef SUBTRACK_CFG should switch full functionality on/off +// 12) SOLVED: Make "disabled" subCB clickable! +// -) When parent vis makes subs hidden, should they go to unchecked? No, disabled! +// -) Should user be able to click on disabled vis to check the CB? No, not important. +// -) Make vis changes "reshape" composite! NOTE: Do we want to do this??? I am against this as too disruptive. We may want to end reshaping in CGIs as well! +// -) Non-configurable will need to show/change vis independently! (separate vis control but no wrench) +// - Verify all composites work (conservation and SNPs are likely failures) +// - Decide on a name (scm, subCfg, ? ) and then make a module file like ddcl.js. +// - Remove debug code when ready +// NOTE: +// Current implementation relies upon '.' delimiter in name and no '_-' in name. Nothing breaks rule yet... + +var scm = { // subtrack config module. + //mySelf: null, // There is no need for a "mySelf" unless this object is being instantiated. + + // There is one instance and these vars are page wide + compositeId: undefined, + visIndependent: false, + viewIds: [], + + markChange: function (obj) + { // Marks a control as having been changed by the user. Naming will send value to cart. + $(obj).addClass('changed'); + + if(obj.type.indexOf("radio") == 0) // radios must keep their names! + return; + + var oldName = obj.id.replace(/\_\-/g,'.'); // sanitized id replaces '.' with '_-' + $(obj).attr('name',oldName); + + // checkboxes have hidden boolshads which should be marked when unchecked + if(obj.type.indexOf("checkbox") == 0) { + var boolshad = normed($('input#boolshad_-' + obj.id)); + if (boolshad != undefined) { + //if(obj.checked == false) { + var oldName = boolshad.id.replace(/\_\-/g,'.'); // sanitized id replaces '.' with '_-' + $(obj).addClass('changed'); + $(boolshad).attr('name',oldName); + //} + //else + // scm.clearChange(boolshad); + } + } + }, + + clearChange: function (obj) + { // Mark as unchanged + $(obj).removeClass('changed'); + + if(obj.type.indexOf("radio") == 0) // radios must keep their names! + return; + + $(obj).removeAttr('name'); + + // checkboxes have hidden boolshads which should be cleared in tandem + if(obj.type.indexOf("checkbox") == 0) { + var boolshad = normed($('input#boolshad_-' + obj.id)); + if (boolshad != undefined) { + $(boolshad).removeClass('changed'); + $(boolshad).removeAttr('name'); + } + } + }, + + hasChanged: function (obj) + { // Is this object updated (value changed by a user)? + return $(obj).hasClass('changed'); + }, + + unnameIt: function (obj,setId) + { // removes a control's name so that it will not be updated to the cart upon submit + // If setId and obj has a name, will set id to current name + var myName = $(obj).attr('name'); + if (myName && myName.length > 0) { + + // checkboxes have hidden boolshads which should be unamed in tandem + if(obj.type.indexOf("checkbox") == 0) { + var boolshad = normed( $("input[name='boolshad\\."+myName+"']") ); // Not yet sanitized name + if (boolshad != undefined) { + scm.unnameIt(boolshad,setId); // self referencing but boolshad not(':checkbox') + } + } + + if (setId) { + // DEBUG ------------- + if (myName.indexOf('_-') >= 0) + warn("DEBUG: Found control with '_-' in name: "+myName + "<BR>This violates the naming rules and will break javascript code."); + // DEBUG ------------- + var newId = myName.replace(/\./g,'_-'); // sanitized id replaces '.' with '_-' + + // DEBUG ------------- + if (obj.id && obj.id.length > 0 && obj.id != myName && !$(obj).hasClass('filterBy') && !$(obj).hasClass('filterComp')) + warn("DEBUG: Obj is being re-ID'd but it already has an ID. Old:'"+obj.id+"' New:'"+newId+"'"); + // DEBUG ------------- + $(obj).attr('id',newId); + } + } + + scm.clearChange(obj); // actually removes the name! + }, + + compositeCfgFind: function () + { // returns the cfg container for the composite controls + // TODO: write, create a composite cfg div! + }, + + compositeObjFind: function (suffix) + { // returns the composite level control for this suffix + //var compCfg = scm.compositeCfgFind(); // TODO: when there is a composite cfg div! + var compObj; + if (suffix != undefined) { + //compObj = $('#'+ scm.compositeId + '\\.' + suffix); + compObj = normed($('#'+ scm.compositeId + '_-' + suffix)); + if (compObj == undefined) { + compObj = normed($('#'+ scm.compositeId + '_' + suffix)); + } + } else { + compObj = normed($("#"+scm.compositeId)); + } + return compObj; + }, + + viewIdFind: function (childObj) + { // returns the name of the view that is a parent of this subtrack control + var cfg = normed($(childObj).parents('div.subCfg')); + if (cfg == undefined) { + // could be vis outside of cfg div + var subCb = normed($(childObj).closest('tr').find('input.subCB')); + if (subCb != undefined) { + var classList = $(subCb).attr("class").split(" "); + classList = aryRemove(classList,["changed","disabled"]); + return classList[classList.length - 1]; + } + warn("Can't find containing div.subCfg of child '"+$(childObj).attr('id')+"'."); + return undefined; + } + var classList = $( cfg ).attr("class").split(" "); + classList = aryRemove(classList,["subCfg","filled"]); + if (classList.length == 0) { + warn("Subtrack cfg div does not have view class for child '"+$(childObj).attr('id')+"'."); + return undefined; + } else if (classList.length > 1) { + warn("Subtrack cfg div for '"+$(childObj).attr('id')+"' has unexpected class: "+classList); + return undefined; + } + if (classList[0] == 'noView') // valid case + return undefined; + return classList[0]; + }, + + viewCfgFind: function (viewId) + { // returns the cfg container for a given view + var viewCfg = normed($('tr#tr_cfg_'+viewId)); + if (viewCfg == undefined) { + warn('Could not find viewCfg for '+viewId); + } + return viewCfg; + }, + + viewObjFind: function (viewId,suffix) + { // returns the control belonging to this view and suffix + var viewObj; + if (suffix != undefined) { + var viewCfg = scm.viewCfgFind(viewId); + viewObj = normed($(viewCfg).find("[id$='_-"+suffix+"']")); + if (viewObj == undefined) + viewObj = normed($(viewCfg).find("[id$='_"+suffix+"']")); + } else + viewObj = normed($("#"+scm.compositeId+"_-"+viewId+"_-vis")); + + return viewObj; + }, + + childObjsFind: function (viewId,suffix) + { // returns an array of objs for this view and suffix + // Assumes composite wide if viewId is not provided + // Assumes vis if suffix is not provided + + if (viewId != undefined) { + var childCfgs = $('div.subCfg.filled.'+viewId); + } else { + var childCfgs = $('div.subCfg.filled'); + } + if (childCfgs == undefined) + return []; + + var childObjs = []; + if (suffix != undefined) + childObjs = $(childCfgs).find('select,input').filter("[id$='_-"+suffix+"']"); + else + childObjs = $(childCfgs).find('select.visDD'); + + if (childObjs == undefined) + return []; + + return childObjs; + }, + + objSuffixGet: function (obj) + { // returns the identifying suffix of a control. obj can be parent or child + //var nameParts = $(obj).attr('id').split('.'); + var nameParts = $(obj).attr('id').split('_-'); + if (nameParts == undefined || nameParts.length == 0) { + warn("Can't resolve id for '"+$(obj).attr('id')+"'."); + return undefined; + } + if (nameParts.length < 2) + return undefined; + + nameParts.shift(); + if (scm.viewIds.length > 0 && nameParts.length > 1) // FIXME: I expect more problems with this! + nameParts.shift(); + + return nameParts.join('_-'); + }, + + childCfgFind: function (childObj) + { // returns the cfg wrapper for a child vis object + var childCfg = normed($(childObj).parents('div.subCfg.filled')); + if (childCfg == undefined) + warn("Can't find childCfg for "+childObj.id); + return childCfg; + }, + + subCbFind: function (childObj) + { // returns the subCB for a child vis object + // Look directly first + var subCb = normed($(childObj).closest('tr').find('input.subCB')); + if (subCb != undefined) + return subCb; + + warn("DEBUG: Failed to find subCb for "+childObj.id); + var childCfg = scm.childCfgFind(childObj); + if (childCfg == undefined) + return undefined; + + var tr = $(childCfg).parents('tr').first(); + var subtrack = childCfg.id.substring(8); // 'div_cfg_'.length + var subCb = normed($(tr).find("input[name='"+subtrack+"_sel']")); + if (subCb == undefined) + warn("Can't find subCB for subtrack: "+subtrack); + return subCb; + }, + + visChanged: function (childObj) + { // called on change for a child vis control + var subCb = scm.subCbFind(childObj); + if (subCb != undefined) { + if (childObj.selectedIndex > 0) + subCb.checked = true; + matSubCbClick(subCb); + } + $(childObj).attr('name',childObj.id); + }, + + parentsFind: function (childObj) + { // returns array of composite and/or view level parent controls + var myParents = []; + var suffix = scm.objSuffixGet(childObj); + var notRadio = (childObj.type.indexOf("radio") != 0); + + // find view name + var viewId = scm.viewIdFind(childObj); + if (viewId != undefined) { + var viewObj = scm.viewObjFind(viewId,suffix); + if (viewObj != undefined) { + if (notRadio) + myParents[0] = viewObj; + else { + $(viewObj).each(function (i) { // radios will have more than one + if(childObj.value == this.value) { + myParents[0] = this; + return false; // breaks each, but not parentsFind + } + }); + } + } + } + + var compObj = scm.compositeObjFind(suffix); + if (compObj != undefined) { + if (notRadio) + myParents[myParents.length] = compObj; + else { + $(compObj).each(function (i) { // radios will have more than one + if(childObj.value == this.value) { + myParents[myParents.length] = this; + return false; + } + }); + } + } + return myParents; + }, + + childrenFind: function (parentObj) + { // returns array of all currently populated child controls related to this parent control + // parentObj could be composite level or view level + // parent object could be for vis which has special rules + + var isComposite = false; + var isVis = false; + var suffix = scm.objSuffixGet(parentObj); + isVis = (suffix != undefined && suffix == 'vis'); // vis inside of subCfg + + var viewId = undefined; + if (isVis) { // This is a view control + + isComposite = (suffix == undefined); + suffix = undefined; + if (!isComposite) { + var classList = $( parentObj ).attr("class").split(" "); + classList = aryRemove(classList,["viewDD","normalText","changed"]); + if (classList.length != 1) { + warn("Unexpected view vis class list:"+classList); + return []; + } + viewId = classList[0]; + } + } else { // normal obj + + var viewCfg = normed($(parentObj).parents("tr[id^='tr_cfg_']")); + isComposite = (viewCfg == undefined); // is composite + if (!isComposite) { + viewId = viewCfg.id.substring(7); // 'tr_cfg_'.length + } + } + + if (isComposite) { + + // There may be views + if (scm.viewIds.length > 0) { + var allChildren = []; + for (var ix = 0;ix < scm.viewIds.length;ix++) { + viewId = scm.viewIds[ix]; + // Get any view objs first + var viewObj = scm.viewObjFind(viewId,suffix); + if (viewObj != undefined) + allChildren[allChildren.length] = viewObj; + // Now get children + var viewChildren = scm.childObjsFind(viewId,suffix); + if (viewChildren.length > 0) { + if (allChildren.length == 0) + allChildren = viewChildren; + else + allChildren = jQuery.merge( allChildren, viewChildren ); + } + } + return allChildren; + } else { // if no views then just get them all + return scm.childObjsFind(undefined,suffix); + } + + } else { + return scm.childObjsFind(viewId,suffix); + } + }, + + visChildrenFind: function (parentObj) + { // returns array of all currently faux and populated vis child controls (which are not in subCfg div) + // parentObj could be composite level or view level + + var isVis = false; + var suffix = scm.objSuffixGet(parentObj); + isVis = (suffix == undefined || suffix == 'vis'); + var isComposite = (suffix == undefined); + + var subVis = $('.subVisDD'); // select:vis or faux:div + if (isComposite) { + + return subVis; + + } else { + var classList = $( parentObj ).attr("class").split(" "); + classList = aryRemove(classList,["viewDD","normalText","changed"]); + if (classList.length != 1) { + warn("Unexpected view vis class list:"+classList); + return []; + } + return $(subVis).filter('.' + classList[0]); + } + }, + + checkOneSubtrack: function (subCb,check,enable) + { // Handle a single check of a single subCb + // called by changing subVis to/from 'hide' + subCb.checked = check; + subCb.dsabled = enable; + scm.markChange(subCb); + matSubCBsetShadow(subCb); + hideOrShowSubtrack(subCb); + scm.enableCfg(subCb,undefined,check); + }, + + propagateSetting: function (parentObj) + { // propagate composite/view level setting to subtrack children + var children = scm.childrenFind(parentObj); + if(parentObj.type.indexOf("checkbox") == 0) { + var parentChecked = parentObj.checked; + $(children).each(function (i) { + // Note checkbox and boolshad are children. + if (this.type != 'hidden') + this.checked = parentObj.checked; + scm.clearChange(this); + }); + } else if(parentObj.type.indexOf("radio") == 0) { + var parentVal = $(parentObj).val(); + $(children).each(function (i) { + this.checked = ($(this).val() == parentVal); + scm.clearChange(this); + }); + } else {// selects and inputs are easy + var parentVal = $(parentObj).val(); + var updateDdcl = ($(parentObj).hasClass('filterBy') || $(parentObj).hasClass('filterComp')); + $(children).each(function (i) { + $(this).val(parentVal); + scm.clearChange(this); + if (updateDdcl) + ddcl.onComplete(this); + }); + } + }, + + propagateViewVis: function (viewObj,compositeVis) + { // propagate vis from a view limiting with compositeVis + var limitedVis = Math.min(compositeVis,viewObj.selectedIndex); + var visText = 'hide'; + if (limitedVis == 1) + visText = 'dense'; + else if (limitedVis == 2) + visText = 'squish'; + else if (limitedVis == 3) + visText = 'pack'; + else if (limitedVis == 4) + visText = 'full'; + + var children = scm.visChildrenFind(viewObj); + $(children).each(function (i) { + if ($(this).hasClass('fauxInput')) { + $(this).text(visText); + } else { + $(this).attr('selectedIndex',limitedVis); + scm.clearChange(this); + } + }); + }, + + propagateVis: function (parentObj,viewId) + { // propagate vis settings to subtrack children + if (viewId == undefined) { + // Walk through views and set with this + var parentVis = parentObj.selectedIndex; + if (scm.viewIds.length > 0) { + for (var ix=0;ix<scm.viewIds.length;ix++) { + var viewObj = scm.viewObjFind(scm.viewIds[ix]);//,undefined); + if (viewObj != undefined) + scm.propagateViewVis(viewObj,parentVis); + } + } else { // No view so, simple + + var visText = 'hide'; + if (parentVis == 1) + visText = 'dense'; + else if (parentVis == 2) + visText = 'squish'; + else if (parentVis == 3) + visText = 'pack'; + else if (parentVis == 4) + visText = 'full'; + var children = scm.visChildrenFind(parentObj); + $(children).each(function (i) { + if ($(this).hasClass('fauxInput')) { + $(this).text(visText); + } else { + $(this).attr('selectedIndex',parentVis); + scm.clearChange(this); + } + }); + } + } else { + // First get composite vis to limit with + var compObj = scm.compositeObjFind(undefined); + if (compObj == undefined) { + warn('Could not find composite vis object!'); + return false; + } + scm.propagateViewVis(parentObj,compObj.selectedIndex); + } + }, + + inheritSetting: function (childObj,force) + { // update value if parents values override child values. + var myParents = scm.parentsFind(childObj); + if (myParents == undefined || myParents.length < 1) { + // DEBUG ------------- It is probably okay to subCfg without parent cfg, but we need to be sure + warn('DEBUG No parents were found for childObj: '+childObj.id); + // DEBUG ------------- + return true; + } + var isVis = (undefined == scm.objSuffixGet(childObj)); + if (isVis) { + var subCb = scm.subCbFind(childObj); + if (subCb != undefined) { + var limitedVis = 9; + if (myParents.length == 1 && (force || scm.hasChanged(myParents[0]))) + limitedVis = myParents[0].selectedIndex; + else if (myParents.length == 2) { + if (force || scm.hasChanged(myParents[0]) || scm.hasChanged(myParents[1])) + limitedVis = Math.min(myParents[0].selectedIndex,myParents[1].selectedIndex); + } + if (limitedVis < 9) + $(childObj).attr('selectedIndex',limitedVis); + } + } else { + var count = 0; + if(childObj.type.indexOf("checkbox") == 0) { + $(myParents).each(function (i) { + if (force || scm.hasChanged(this)) { + childObj.checked = this.checked; + // can ignore boolshad because this does not change + count++; + } + }); + } else if(childObj.type.indexOf("radio") == 0) { + $(myParents).each(function (i) { + if (force || scm.hasChanged(this)) { + childObj.checked = this.checked; + // parentObj is already "tuned" to the correct radio, so this works + count++; + } + }); + } else {// selects and inputs are easy + $(myParents).each(function (i) { + if (force || scm.hasChanged(this)) { + $(childObj).val($(this).val()); + count++; + } + }); + } + if (count > 1) // if hasChanged() is working, there should never be more than one + warn('Both composite and view are seen as updated! Named update is not working.'); + } + }, + + currentCfg: undefined, // keep track of cfg while ajaxing, man + currentSub: undefined, // keep track of subtrack while ajaxing, dude + + cfgFill: function (content, status) + { // Finishes the population of a subtrack cfg. Called by ajax return. + var cfg = scm.currentCfg; + scm.currentCfg = undefined; + var cleanHtml = content; + var shlurpPattern=/\<script type=\'text\/javascript\' SRC\=\'.*\'\>\<\/script\>/gi; + // DEBUG ------------- + var jsFiles = cleanHtml.match(shlurpPattern); + if (jsFiles && jsFiles.length > 0) + alert("jsFiles:'"+jsFiles+"'\n---------------\n"+cleanHtml); // warn() interprets html, etc. + // DEBUG ------------- + cleanHtml = cleanHtml.replace(shlurpPattern,""); + shlurpPattern=/\<script type=\'text\/javascript\'>.*\<\/script\>/gi; + // DEBUG ------------- + var jsEmbeded = cleanHtml.match(shlurpPattern); + if (jsEmbeded && jsEmbeded.length > 0) + alert("jsEmbeded:'"+jsEmbeded+"'\n---------------\n"+cleanHtml); + // DEBUG ------------- + cleanHtml = cleanHtml.replace(shlurpPattern,""); + shlurpPattern=/\<LINK rel=\'STYLESHEET\' href\=\'.*\' TYPE=\'text\/css\' \/\>/gi; + // DEBUG ------------- + var cssFiles = cleanHtml.match(shlurpPattern); + if (cssFiles && cssFiles.length > 0) + alert("cssFiles:'"+cssFiles+"'\n---------------\n"+cleanHtml); + // DEBUG ------------- + cleanHtml = cleanHtml.replace(shlurpPattern,""); + if (scm.visIndependent) { + ix = cleanHtml.indexOf('</SELECT>'); + if (ix > 0) + cleanHtml = cleanHtml.substring(ix+'</SELECT>'.length); + while(cleanHtml.length > 0) { + ix = cleanHtml.search("<"); + cleanHtml = cleanHtml.substring(ix); + ix = cleanHtml.search(/\<BR\>/i); + if (ix != 0) + break; // Not found or not at start. + else + cleanHtml = cleanHtml.substring(4); // skip past <BR> and continue + } + } else { + var ix = cleanHtml.indexOf('<B>Display mode: </B>'); + if (ix > 0) + cleanHtml = cleanHtml.substring(ix+'<B>Display mode: </B>'.length); + } + //cleanHtml = cleanHtml.substring(ix); + ix = cleanHtml.indexOf('</FORM>'); // start of form already chipped off + if (ix > 0) + cleanHtml = cleanHtml.substring(0,ix - 1); + + //cleanHtml = "<div class='blueBox' style='background-color:#FFF9D2; padding:0.2em 1em 1em; float:left;'><CENTER><B>Subtrack Configuration</B></CENTER><BR>" + cleanHtml + "</div>" + //cleanHtml = "<div class='blueBox' style='background-color:#FFF9D2; padding:0.2em 1em 1em; float:left;'><B>Subtrack visibility:</B> " + cleanHtml + "</div>" + cleanHtml = "<div class='blueBox' style='background-color:#FFF9D2; padding:0.5em 1em 1em;'>" + cleanHtml + "</div>" + $(cfg).html(cleanHtml); + $(cfg).addClass('filled'); + var boxWithin = $(cfg).find('.blueBox'); + if (boxWithin.length > 1) + $(boxWithin[1]).removeClass('blueBox'); + + //$(cfg).html("<div style='font-size:.9em;'>" + cleanHtml + "</div>"); + var subObjs = $(cfg).find('input,select').filter("[name]"); + if (subObjs.length == 0) { + warn('Did not find controls for cfg: ' + cfg.id); + return; + } + $(subObjs).each(function (i) { + if (this.name != undefined) { // The filter("[name]") above didn't do it! + if (this.type != 'hidden') { + scm.unnameIt(this,true); + scm.inheritSetting(this,false); // updates any values that have been changed on this page + // if view vis do more than just name it on change + var suffix = scm.objSuffixGet(this); + if (suffix == undefined) // vis + $(this).bind('change',function (e) { + scm.visChanged(this); + }); + else { + $(this).bind('change',function (e) { + scm.markChange(this); + }); + } + } + } + }); + // finally show + $(cfg).show(); + // Tricks to get this in the size and position I want + $(cfg).css({ position: 'absolute'}); + var myWidth = $(cfg).width(); + var shiftLeft = -1; + if (scm.visIndependent) { + shiftLeft *= ($(cfg).position().left - 125); + var subVis = normed($('div#' + scm.currentSub+'_faux')); + if (subVis != undefined) + scm.replaceWithVis(subVis,scm.currentSub,false); + } + else + shiftLeft *= ($(cfg).position().left - 40); + $(cfg).css({ width: myWidth+'px',position: 'relative', left: shiftLeft + 'px' }); + + // Setting up filterBys must follow show because sizing requires visibility + $(cfg).find('.filterBy,.filterComp').each( function(i) { + if ($(this).hasClass('filterComp')) + ddcl.setup(this); + else + ddcl.setup(this, 'noneIsAll'); + }); + }, + + cfgPopulate: function (cfg,subtrack) + { // Populates a subtrack cfg dialog via ajax and update from composite/view parents + scm.currentCfg = cfg; + scm.currentSub = subtrack; + + $.ajax({ + type: "GET", + url: "../cgi-bin/hgTrackUi?ajax=1&g=" + subtrack + "&hgsid=" + getHgsid() + "&db=" + getDb(), + dataType: "html", + trueSuccess: scm.cfgFill, + success: catchErrorOrDispatch, + error: errorHandler, + cmd: "cfg", + cache: false + }); + }, + + replaceWithVis: function (obj,subtrack,open) + { // Replaces the current fauxVis object with a true visibility selector + + if ($(obj).hasClass('disabled')) + return; + var classList = $( obj ).attr("class").split(" "); + classList = aryRemove(classList,["disabled"]); + var view = classList[classList.length - 1]; // This relies on view being the last class!!! + var selectHtml = "<SELECT id='"+subtrack+"' class='normalText subVisDD "+view+"' style='width:70px;'"; + selectHtml += ">"; + var selected = $(obj).text(); + var visibilities = ['hide','dense','squish','pack','full']; + $(visibilities).each( function (ix) { + selectHtml += "<OPTION" + (visibilities[ix] == selected ? " SELECTED":"") + ">"+visibilities[ix]+"</OPTION>"; + }); + selectHtml += "</SELECT>"; + $(obj).replaceWith(selectHtml); + if (open) { + var newObj = $('select#'+subtrack); + $(newObj).css({'zIndex':'2','vertical-align':'top'}); + $(newObj).attr('size',5); + $(newObj).one('blur',function (e) { + $(this).attr('size',1); + $(this).unbind('blur'); + }); + $(newObj).change(function (e) { + if ($(this).attr('size') > 1) + $(this).attr('size',1); + if (this.selectedIndex == 0) { // setting to hide so uncheck and disable. + // Easiest is to uncheck subCB and reset vis + // so that the reverse action makes sense + var subCb = normed($('input#' + this.id + '_sel')); + if (subCb != undefined) { + scm.checkOneSubtrack(subCb,false,true); + matSubCbClick(subCb); + scm.inheritSetting(this,true); + scm.unnameIt(this); + // DEBUG ------------- + } else { + warn('DEBUG: Cant find subCB for ' + this.id); + // DEBUG ------------- + } + } else + scm.markChange(this); + }); + $(newObj).focus(); + } + }, + + enableCfg: function (subCb,subtrack,setTo) + { // Enables or disables subVis and wrench + if (subCb == undefined) { + if (subtrack == undefined || subtrack.length == 0) { + warn("DEBUG: scm.enableCfg() called without CB or subtrack."); + return false; + } + subCb = normed($("input[name='"+subtrack+"'_sel]")); + if (subCb == undefined) { + warn("DEBUG: scm.enableCfg() could not find CB for subtrack: "+subtrack); + return false; + } + } + + var td = normed($(subCb).parent('td')); + if (td == undefined) { + warn("DEBUG: scm.enableCfg() could not find TD for CB: "+subCb.name); + return false; + } + var subFaux = normed($(td).find('div.subVisDD')); + if (subFaux != undefined) { + if (setTo == true) + $(subFaux).removeClass('disabled'); + else + $(subFaux).addClass('disabled'); + } else { + var subVis = normed($(td).find('select.subVisDD')); + if (subVis != undefined) { + $(subVis).attr('disabled',!setTo); + } + } + var wrench = normed($(td).find('span.clickable')); // TODO tighten this + if (wrench != undefined) { + if (setTo == true) + $(wrench).removeClass('disabled'); + else + $(wrench).addClass('disabled'); + } + }, + + cfgToggle: function (wrench,subtrack) + { // Opens/closes subtrack cfg dialog, populating if empty + var cfg = normed($("div#div_cfg_"+subtrack)); + if (cfg == undefined) { + warn("DEBUG: Can't find div_cfg_"+subtrack); + return false; + } + + if ($(cfg).css('display') == 'none') { + if ($(wrench).hasClass('disabled')) + return; + // Don't allow if this composite is not enabled! + // find the cb + var tr = $(cfg).parents('tr').first(); + var subCb = normed($(tr).find("#"+subtrack+"_sel")); + if (subCb == undefined) { + warn("Can't find subCB for "+subtrack); + return false; + } + //if (subCb.disabled == true) // || subCb.checked == false) + if (isFauxDisabled(subCb,true)) + return false; + + if(metadataIsVisible(subtrack)) + metadataShowHide(subtrack,"",""); + if ($(cfg).hasClass('filled')) + $(cfg).show(); + else + waitOnFunction( scm.cfgPopulate, cfg, subtrack ); + } else + $(cfg).hide(); + return false; // called by link! + }, + + subCbInit: function (subCb) + { // unnames the subCb control and sets up on change envent + scm.unnameIt(subCb,true); + + $(subCb).bind('change',function (e) { // Not 'click' for a reason: other code will trigger 'change' + scm.markChange(subCb); // while 'click' code is exclusive for user click. + }); + }, + + viewInit: function (viewId) + { // unnames all view controls + // iterate through all matching controls and unname + var tr = normed($('tr#tr_cfg_'+viewId)); + if (tr == undefined) { + warn('DEBUG: Did not find view: ' + viewId); + return; + } + var viewObjs = $(tr).find('input,select') + var tempHiddens = $(viewObjs).filter(".filterBy,.filterComp").filter(':hidden'); + viewObjs = $(viewObjs).not(":hidden"); + viewObjs = $(viewObjs).add(tempHiddens); + if (viewObjs.length > 0) { + $(viewObjs).each(function (i) { + scm.unnameIt(this,true); + $(this).bind('change',function (e) { + scm.markChange(this); + scm.propagateSetting(this); + }); + }); + } + + // Now vis control + var viewVis = normed($("select[name='"+scm.compositeId+"\\."+viewId+"\\.vis']")); + if (viewVis == undefined) { + warn('DEBUG: Did not find visibility control for view: ' + viewId); + return; + } + scm.unnameIt(viewVis,true); + $(viewVis).bind('change',function (e) { + scm.markChange(viewVis); + scm.propagateVis(viewVis,viewId); + }); + }, + + initialize: function () + { // unnames all composite controls and then all view controls + // names will be added back in onchange events + // mySelf = this; // There is no need for a "mySelf" unless this object is being instantiated. + + var compVis = $('.visDD'); + if (compVis == undefined || compVis.length < 1) { + warn('DEBUG: Did not find visibility control for composite.'); + return; + } + if (compVis.length > 1) { + warn('DEBUG: Multiple visibility controls for composite???'); + return; + } + + scm.compositeId = $(compVis).attr('name'); + scm.visIndependent = ($('.subVisDD').length > 0); // Can subtracks have their own vis? + + // Unname and set up vis propagation + compVis = compVis[0]; + scm.unnameIt(compVis,true); + $(compVis).bind('change',function (e) { + scm.markChange(compVis); + scm.propagateVis(compVis,undefined); + }); + + // Find all appropriate controls and unname + + // matCBs are easy, they never get named again + var matCbs = $('input.matCB'); + $(matCbs).each(function (i) { + scm.unnameIt(this,false); + }); + + // SubCBs will get renamed and on change will name them back. + var subCbs = $('input.subCB'); + $(subCbs).each(function (i) { + scm.subCbInit(this); + }); + + // iterate through views + var viewVis = $('select.viewDD'); + $(viewVis).each(function (i) { + var classList = $( this ).attr("class").split(" "); + classList = aryRemove(classList,["viewDD","normalText","changed"]); + if (classList.length == 0) + warn('DEBUG: View classlist is missing view class.'); + else if (classList.length > 1) + warn('DEBUG: View classlist contains unexpected classes:' + classList); + else { + scm.viewIds[i] = classList[0]; + scm.viewInit(scm.viewIds[i]); + } + }); + + // Tricky for composite level controls. Could wrap cfg controls in new div. + // DO THIS AFTER Views + // NOTE: excluding sortOrder and showCfg which are special cases we don't care about in scm + var compObjs = $('select,input').filter("[name^='"+scm.compositeId+"\\.'],[name^='"+scm.compositeId+"_']").not(".allOrOnly"); + var tempHiddens = $(compObjs).filter(".filterBy,.filterComp").filter(':hidden'); + compObjs = $(compObjs).not(":hidden"); + compObjs = $(compObjs).add(tempHiddens); + if (compObjs.length > 0) { + $(compObjs).each(function (i) { + // DEBUG ------------- + if (this.id != undefined + && this.id.length > 0 + && $(this).hasClass('filterBy') == false + && $(this).hasClass('filterComp') == false) + warn('DEBUG: Not expected control with name ['+this.name + '], and id #'+this.id); + // DEBUG ------------- + + scm.unnameIt(this,true); + $(this).bind('change',function (e) { + scm.markChange(this); + scm.propagateSetting(this); + }); + }); + } + } +}; +