497d85f0a8f8ca48592955b0edd40468305a5be3
tdreszer
  Tue Sep 27 17:08:37 2011 -0700
Major rework of subCfg module to no longer rely upon removing name at initialization.
diff --git src/hg/js/subCfg.js src/hg/js/subCfg.js
index e071ea7..c080de9 100644
--- src/hg/js/subCfg.js
+++ src/hg/js/subCfg.js
@@ -1,959 +1,885 @@
-
 // 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!!!
+// for the one subtrack.  Controls wil get class 'changed' added when changes are made.
+// When the form is submitted, all controls not marked as "changed" will be unnamed and will
+// therefore not make it into the cart.
 
 // Definitions as used here:
-// obj: an input or select style html control which may be named or unnamed
+// obj: an input or select style html control which may be marked as "changed"
 //   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.
+// fauxVis: fake control for subtrack visDD, which will be replaced with true vis when clicked.
 
 // 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!
-// 13) SOLVED: PROBLEM is fauxDisabled.  SOLUTION is convert fauxDisabled to true disabled on form.submit()
-//  -) 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)
+// - SOLVED: checkboxes: working with name = boolshad.{name}   FIXME: multishad?
+// - SOLVED: filterBy,filterComp working: they rely upon a unique id, and id with '.' screwed it all up.  So replaced '.' with '_-'
+// - SOLVED: OpenChromSynth: subtrack filterby needs to be updated by composite filterBy.
+// - SOLVED: check subtrack to enable/disable fauxVis and wrench
+// - SOLVED: matCB should effect subCb including enable/disable fauxVis and wrench
+// - SOLVED: composite/view vis should effect subVis and enable/disable fauxVis and wrench
+// - SOLVED: inside out: changing subtrack vis should affect subCB and matCB
+// - SOLVED: Loosing checked tracks!!!  Setting vis clears checkboxes?
+// - TESTED: hui.c #ifdef SUBTRACK_CFG should switch full functionality on/off
+// - SOLVED: Make "disabled" subCB clickable!
+// - SOLVED: PROBLEM is fauxDisabled.  SOLUTION is convert fauxDisabled to true disabled on form.submit()
+// - DECIDED: When parent vis makes subs hidden, should they go to unchecked?   No, disabled!
+// - DECIDED: Should user be able to click on disabled vis to check the CB?  No, not important.
+// - DECIDED: 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!
+// - DECIDED: Decide on a name (scm, subCfg, ? ) and then make a module file like ddcl.js.
+// - SOLVED: subtrack selected count is wrong!
+//  - Non-configurable will need to show/change vis independently! (separate vis control but no wrench)
+//  - Speed up massive composites!  HAIB TFBS SYDH TFBS
 // - 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.
+var subCfg = { // 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,
+    compositeName: undefined,
     visIndependent: false,
-    viewIds: [],
+    viewTags: [],
 
-    markChange: function (obj)
+    markChange: function (eventObj, obj)
     { // Marks a control as having been changed by the user.  Naming will send value to cart.
-
+      // Note this is often called directly as the onchange event function
+        if (obj == undefined)
+            obj = this;
         $(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(obj.type === "checkbox") {
+            var boolshad = normed($("input.cbShadow[name='boolshad\\." + obj.name+"']"));
             if (boolshad != undefined) {
-                var oldName = boolshad.id.replace(/\_\-/g,'.');   // sanitized id replaces '.' with '_-'
-                $(obj).addClass('changed');
-                $(boolshad).attr('name',oldName);
+                $(boolshad).addClass('changed');
             }
         }
     },
 
     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(obj.type === "checkbox") {
+            var boolshad = normed($("input.cbShadow[name='boolshad\\." + obj.name+"']"));
             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 compCfg = subCfg.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));
+            compObj = normed($("[name='"+subCfg.compositeName + "\\." + suffix+"']"));
+            if (compObj === undefined) {
+                compObj = normed($("[name='"+subCfg.compositeName + "_" + suffix+"']"));
             }
         } else {
-            compObj = normed($("#"+scm.compositeId));
+            compObj = normed($("[name='"+subCfg.compositeName+"']"));
         }
         return compObj;
     },
 
-    viewIdFind: function (childObj)
+    viewTagFind: 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
+            // Child obj could be vis itself...
             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')+"'.");
+            // could be vis outside of cfg div
+            warn("DEBUG: Can't find containing div.subCfg of child '"+childObj.name+"'.");
             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')+"'.");
+            warn("DEBUG: Subtrack cfg div does not have view class for child '"+childObj.name+"'.");
             return undefined;
         } else if (classList.length > 1) {
-            warn("Subtrack cfg div for '"+$(childObj).attr('id')+"' has unexpected class: "+classList);
+            warn("DEBUG: Subtrack cfg div for '"+childObj.name+"' has unexpected class: "+classList);
             return undefined;
         }
         if (classList[0] == 'noView') // valid case
             return undefined;
         return classList[0];
     },
 
-    viewCfgFind: function (viewId)
+    viewCfgFind: function (viewTag)
     { // returns the cfg container for a given view
-        var viewCfg = normed($('tr#tr_cfg_'+viewId));
+        var viewCfg = normed($('tr#tr_cfg_'+viewTag));
         if (viewCfg == undefined) {
-            warn('Could not find viewCfg for '+viewId);
+            warn('DEBUG: Could not find viewCfg for '+viewTag);
         }
         return viewCfg;
     },
 
-    viewObjFind: function (viewId,suffix)
+    viewObjFind: function (viewTag,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+"']"));
+            var viewCfg = subCfg.viewCfgFind(viewTag);
+            viewObj = normed($(viewCfg).find("[name$='\\."+suffix+"']"));
             if (viewObj == undefined)
-                viewObj = normed($(viewCfg).find("[id$='_"+suffix+"']"));
+                viewObj = normed($(viewCfg).find("[name$='_"+suffix+"']"));
         } else
-            viewObj = normed($("#"+scm.compositeId+"_-"+viewId+"_-vis"));
+            viewObj = normed($("[name='"+subCfg.compositeName+"\\."+viewTag+"\\.vis']"));
 
         return viewObj;
     },
 
-    childObjsFind: function (viewId,suffix)
+    childObjsFind: function (viewTag,suffix)
     { // returns an array of objs for this view and suffix
-      // Assumes composite wide if viewId is not provided
+      // Assumes composite wide if viewTag is not provided
       // Assumes vis if suffix is not provided
 
-        if (viewId != undefined) {
-            var childCfgs = $('div.subCfg.filled.'+viewId);
+        if (viewTag != undefined) {
+            var childCfgs = $('div.subCfg.filled.'+viewTag);
         } else {
             var childCfgs = $('div.subCfg.filled');
         }
         if (childCfgs == undefined)
             return [];
 
         var childObjs = [];
         if (suffix != undefined)
-            childObjs = $(childCfgs).find('select,input').filter("[id$='_-"+suffix+"']");
+            childObjs = $(childCfgs).find('select,input').filter("[name$='\\."+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('_-');
+        var nameParts = obj.name.split('.');
         if (nameParts == undefined || nameParts.length == 0) {
-            warn("Can't resolve id for '"+$(obj).attr('id')+"'.");
+            warn("DEBUG: Can't resolve name for '"+obj.name+"'.");
             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!
+        if (subCfg.viewTags.length > 0 && nameParts.length > 1) // FIXME: I expect more problems with this!
             nameParts.shift();
 
-        return nameParts.join('_-');
+        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);
+            warn("DEBUG: Can't find childCfg for "+childObj.name);
         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);
+        warn("DEBUG: Failed to find subCb for "+childObj.name);
+        var childCfg = subCfg.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);
+            warn("DEBUG: 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);
+        var suffix = subCfg.objSuffixGet(childObj);
+        //var notRadio = (childObj.type.indexOf("radio") != 0);
+        var notRadio = (childObj.type !== "radio");
 
         // find view name
-        var viewId = scm.viewIdFind(childObj);
-        if (viewId != undefined) {
-            var viewObj = scm.viewObjFind(viewId,suffix);
+        var viewTag = subCfg.viewTagFind(childObj);
+        if (viewTag != undefined) {
+            var viewObj = subCfg.viewObjFind(viewTag,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);
+        var compObj = subCfg.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);
+        var suffix = subCfg.objSuffixGet(parentObj);
         isVis = (suffix != undefined && suffix == 'vis');  // vis inside of subCfg
 
-        var viewId = undefined;
+        var viewTag = 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);
+                    warn("DEBUG: Unexpected view vis class list:"+classList);
                     return [];
                 }
-                viewId = classList[0];
+                viewTag = 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
+                viewTag = viewCfg.id.substring(7); // 'tr_cfg_'.length
             }
         }
 
         if (isComposite) {
 
             // There may be views
-            if (scm.viewIds.length > 0) {
+            if (subCfg.viewTags.length > 0) {
                 var allChildren = [];
-                for (var ix = 0;ix < scm.viewIds.length;ix++) {
-                    viewId = scm.viewIds[ix];
+                for (var ix = 0;ix < subCfg.viewTags.length;ix++) {
+                    viewTag = subCfg.viewTags[ix];
                     // Get any view objs first
-                    var viewObj = scm.viewObjFind(viewId,suffix);
+                    var viewObj = subCfg.viewObjFind(viewTag,suffix);
                     if (viewObj != undefined)
                         allChildren[allChildren.length] = viewObj;
                     // Now get children
-                    var viewChildren = scm.childObjsFind(viewId,suffix);
+                    var viewChildren = subCfg.childObjsFind(viewTag,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);
+                return subCfg.childObjsFind(undefined,suffix);
             }
 
         } else {
-            return scm.childObjsFind(viewId,suffix);
+            return subCfg.childObjsFind(viewTag,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);
+        var suffix = subCfg.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);
+                warn("DEBUG: 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;
         if (enable)
             fauxDisable(subCb,false,"");
         else
             fauxDisable(subCb,true, "View is hidden");
-        scm.markChange(subCb);
-        matSubCBsetShadow(subCb);
-        hideOrShowSubtrack(subCb);
-        scm.enableCfg(subCb,undefined,check);
+        subCfg.markChange(null,subCb);
+        subCfg.enableCfg(subCb,undefined,check);
+        matSubCbClick(subCb); // needed to mark matCBs, shadow and show/hide
     },
 
     propagateSetting: function (parentObj)
     { // propagate composite/view level setting to subtrack children
-        var children = scm.childrenFind(parentObj);
-        if(parentObj.type.indexOf("checkbox") == 0) {
+        var children = subCfg.childrenFind(parentObj);
+        if(parentObj.type === "checkbox") {
             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);
+                subCfg.clearChange(this);
             });
-        } else if(parentObj.type.indexOf("radio") == 0) {
+        //} else if(parentObj.type.indexOf("radio") == 0) {
+        } else if(parentObj.type === "radio") {
             var parentVal = $(parentObj).val();
             $(children).each(function (i) {
                 this.checked = ($(this).val() == parentVal);
-                scm.clearChange(this);
+                subCfg.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);
+                subCfg.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);
+        var children = subCfg.visChildrenFind(viewObj);
         $(children).each(function (i) {
             if ($(this).hasClass('fauxInput')) {
                 $(this).text(visText);
             } else {
                 $(this).attr('selectedIndex',limitedVis);
-                scm.clearChange(this);
+                subCfg.clearChange(this);
             }
         });
     },
 
-    propagateVis: function (parentObj,viewId)
+    propagateVis: function (parentObj,viewTag)
     { // propagate vis settings to subtrack children
-        if (viewId == undefined) {
+        if (viewTag == 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 (subCfg.viewTags.length > 0) {
+                for (var ix=0;ix<subCfg.viewTags.length;ix++) {
+                    var viewObj = subCfg.viewObjFind(subCfg.viewTags[ix]);//,undefined);
                     if (viewObj != undefined)
-                        scm.propagateViewVis(viewObj,parentVis);
+                        subCfg.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);
+                var children = subCfg.visChildrenFind(parentObj);
                 $(children).each(function (i) {
                     if ($(this).hasClass('fauxInput')) {
                         $(this).text(visText);
                     } else {
                         $(this).attr('selectedIndex',parentVis);
-                        scm.clearChange(this);
+                        subCfg.clearChange(this);
                     }
                 });
             }
         } else {
             // First get composite vis to limit with
-            var compObj = scm.compositeObjFind(undefined);
+            var compObj = subCfg.compositeObjFind(undefined);
             if (compObj == undefined) {
-                warn('Could not find composite vis object!');
+                warn('DEBUG: Could not find composite vis object!');
                 return false;
             }
-            scm.propagateViewVis(parentObj,compObj.selectedIndex);
+            subCfg.propagateViewVis(parentObj,compObj.selectedIndex);
         }
     },
 
     inheritSetting: function (childObj,force)
     { // update value if parents values override child values.
-        var myParents = scm.parentsFind(childObj);
+        var myParents = subCfg.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 -------------
+            warn('DEBUG: No parents were found for childObj: '+childObj.name);
             return true;
         }
-        var isVis = (undefined == scm.objSuffixGet(childObj));
+        var isVis = (undefined == subCfg.objSuffixGet(childObj));
         if (isVis) {
-            var subCb = scm.subCbFind(childObj);
+            var subCb = subCfg.subCbFind(childObj);
             if (subCb != undefined) {
                 var limitedVis = 9;
-                if (myParents.length == 1 && (force || scm.hasChanged(myParents[0])))
+                if (myParents.length == 1 && (force || subCfg.hasChanged(myParents[0])))
                     limitedVis = myParents[0].selectedIndex;
                 else if (myParents.length == 2) {
-                    if (force || scm.hasChanged(myParents[0]) || scm.hasChanged(myParents[1]))
+                    if (force || subCfg.hasChanged(myParents[0]) || subCfg.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) {
+            if(childObj.type === "checkbox") {
                 $(myParents).each(function (i) {
-                    if (force || scm.hasChanged(this)) {
+                    if (force || subCfg.hasChanged(this)) {
                         childObj.checked = this.checked;
-                        // can ignore boolshad because this does not change
                         count++;
                     }
                 });
-            } else if(childObj.type.indexOf("radio") == 0) {
+            //} else if(childObj.type.indexOf("radio") == 0) {
+            } else if(childObj.type === "radio") {
                 $(myParents).each(function (i) {
-                    if (force || scm.hasChanged(this)) {
+                    if (force || subCfg.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)) {
+                    if (force || subCfg.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.');
+                warn('DEBUG: 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 cfg = subCfg.currentCfg;
+        subCfg.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) {
+        if (subCfg.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&nbsp;mode:&nbsp;</B>');
             if (ix > 0)
-                cleanHtml = cleanHtml.substring(ix+'<B>Display&nbsp;mode:&nbsp;</B>'.length);
+                cleanHtml = cleanHtml.substring(ix+'<B>Display&nbsp;mode:&nbsp;</B>'.length); // Excludes vis!
         }
             //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>&nbsp;" + 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);
+            warn('DEBUG: 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
+                    subCfg.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);
-                        });
-                    }
+                    var suffix = subCfg.objSuffixGet(this);
+                    if (suffix != undefined)
+                        $(this).change( subCfg.markChange );
+                    else
+                        warn("DEBUG: couldn't find suffix for subtrack control: "+this.name);
                 }
             }
         });
         // 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) {
+        if (subCfg.visIndependent) {
             shiftLeft *= ($(cfg).position().left - 125);
-            var subVis = normed($('div#' + scm.currentSub+'_faux'));
+            var subVis = normed($('div#' + subCfg.currentSub+'_faux'));
             if (subVis != undefined)
-                scm.replaceWithVis(subVis,scm.currentSub,false);
+                subCfg.replaceWithVis(subVis,subCfg.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) {
+            this.id = this.name.replace(/\./g,'_-'); // Must have id unique to page!
             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;
+        subCfg.currentCfg = cfg;
+        subCfg.currentSub = subtrack;
 
         $.ajax({
             type: "GET",
             url: "../cgi-bin/hgTrackUi?ajax=1&g=" + subtrack + "&hgsid=" + getHgsid() + "&db=" + getDb(),
             dataType: "html",
-            trueSuccess: scm.cfgFill,
+            trueSuccess: subCfg.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;'";
+        var selectHtml = "<SELECT name='"+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);
+            var newObj = $("select[name='"+subtrack+"']");
             $(newObj).css({'zIndex':'2','vertical-align':'top'});
             $(newObj).attr('size',5);
             $(newObj).one('blur',function (e) {
                 $(this).attr('size',1);
+                $(this).unbind('click');
+            });
+            $(newObj).one('click',function (e) {
+                $(this).attr('size',1);
                 $(this).unbind('blur');
             });
             $(newObj).change(function (e) {
-                if ($(this).attr('size') > 1)
+                if ($(this).attr('size') > 1) {
                     $(this).attr('size',1);
+                    $(this).unbind('blur');
+                    $(this).unbind('click');
+                }
                 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'));
+                    var subCb = normed($("input[name='" + this.name + "_sel']"));
                     if (subCb != undefined) {
-                        scm.checkOneSubtrack(subCb,false,true);
-                        matSubCbClick(subCb);
-                        scm.inheritSetting(this,true);
-                        scm.unnameIt(this);
-                    // DEBUG -------------
+                        subCfg.checkOneSubtrack(subCb,false,true);
+                        subCfg.inheritSetting(this,true);
                     } else {
-                        warn('DEBUG: Cant find subCB for ' + this.id);
-                    // DEBUG -------------
+                        warn('DEBUG: Cant find subCB for ' + this.name);
                     }
                 } else
-                    scm.markChange(this);
+                    subCfg.markChange(e,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.");
+                warn("DEBUG: subCfg.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);
+                warn("DEBUG: subCfg.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);
+            warn("DEBUG: subCfg.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"));
+            var subCb = normed($(tr).find("input[name='"+subtrack+"_sel']"));
             if (subCb == undefined) {
-                warn("Can't find subCB for "+subtrack);
+                warn("DEBUG: 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 );
+                waitOnFunction( subCfg.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)
+    viewInit: function (viewTag)
     { // unnames all view controls
         // iterate through all matching controls and unname
-        var tr = normed($('tr#tr_cfg_'+viewId));
+        var tr = normed($('tr#tr_cfg_'+viewTag));
         if (tr == undefined) {
-            warn('DEBUG: Did not find view: ' + viewId);
+            warn('DEBUG: Did not find view: ' + viewTag);
             return;
         }
-        var viewObjs = $(tr).find('input,select')
-        var tempHiddens = $(viewObjs).filter(".filterBy,.filterComp").filter(':hidden');
-        viewObjs = $(viewObjs).not(":hidden");
-        viewObjs = $(viewObjs).add(tempHiddens);
+        var viewObjs = $(tr).find('input,select');
         if (viewObjs.length > 0) {
             $(viewObjs).each(function (i) {
-                scm.unnameIt(this,true);
+                if (this.type != 'hidden') {
                 $(this).bind('change',function (e) {
-                    scm.markChange(this);
-                    scm.propagateSetting(this);
+                        subCfg.markChange(e,this);
+                        subCfg.propagateSetting(this);
                 });
+                }
             });
         }
 
         // Now vis control
-        var viewVis = normed($("select[name='"+scm.compositeId+"\\."+viewId+"\\.vis']"));
+        var viewVis = normed($("select[name='"+subCfg.compositeName+"\\."+viewTag+"\\.vis']"));
         if (viewVis == undefined) {
-            warn('DEBUG: Did not find visibility control for view: ' + viewId);
+            warn('DEBUG: Did not find visibility control for view: ' + viewTag);
             return;
         }
-        scm.unnameIt(viewVis,true);
         $(viewVis).bind('change',function (e) {
-            scm.markChange(viewVis);
-            scm.propagateVis(viewVis,viewId);
+            subCfg.markChange(e,viewVis);
+            subCfg.propagateVis(viewVis,viewTag);
         });
     },
 
     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?
+        subCfg.compositeName = $(compVis).attr('name');
+        subCfg.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);
+            subCfg.markChange(e,compVis);
+            subCfg.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);
-        });
+        $(subCbs).change( subCfg.markChange );
 
         // 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]);
+                subCfg.viewTags[i] = classList[0];
+                subCfg.viewInit(subCfg.viewTags[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);
+        // NOTE: excluding sortOrder and showCfg which are special cases we don't care about in subCfg
+        var compObjs = $('select,input').filter("[name^='"+subCfg.compositeName+"\\.'],[name^='"+subCfg.compositeName+"_']").not(".allOrOnly");
         if (compObjs.length > 0) {
             $(compObjs).each(function (i) {
+                if (this.type != 'hidden') {
                 // 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);
+                    $(this).change(function (e) {
+                        subCfg.markChange(e,this);
+                        subCfg.propagateSetting(this);
                 });
+                }
             });
         }
 
         // Because of fauxDisabled subCBs, it is necessary to truly disable them before submitting.
         $("FORM").submit(function (i) {
             $('input.subCB.changed.disabled').attr('disabled',true);
+
+            // Names will be removed for all controls that have not changed
+            $('select,input').filter("[name]").not(".allOrOnly").not('.changed').each( function (i) {
+                if (this.type != 'hidden' || $(this).hasClass('trPos') || $(this).hasClass('cbShadow')) {
+                    //this.disabled = true;   // FIXME: which is faster: name or disabled ?
+                    this.name = "";  // Unname goes straight to the point: doesn't send in the form
+                }
+            });
+            // to do: other hiddens?
+            // matCb uncheck failed to update cart properly
         });
     }
 };