26f05ca6ac226db0020575e47182622a6e66639b
tdreszer
  Thu Sep 16 12:30:39 2010 -0700
Larry suggested where my history bug might be.  Now subtrack cfg popups in hgTrackUi does not affect history.
diff --git src/hg/js/hui.js src/hg/js/hui.js
index c5238a4..c07ef40 100644
--- src/hg/js/hui.js
+++ src/hg/js/hui.js
@@ -1,1379 +1,1379 @@
 // JavaScript Especially for hui.c
 // $Header: /projects/compbio/cvsroot/kent/src/hg/js/hui.js,v 1.59 2010/06/03 20:27:26 tdreszer Exp $
 
 var compositeName = "";
 //var browser;                // browser ("msie", "safari" etc.)
 //var now = new Date();
 //var start = now.getTime();
 //$(window).load(function () {
 //    if(start != null) {
 //        now = new Date();
 //        alert("Loading took "+(now.getTime() - start)+" msecs.");
 //    }
 //});
 
 
 // The 'mat*' functions are especially designed to support subtrack configuration by 2D matrix of controls
 
 function _matSelectViewForSubTracks(obj,view)
 {
 // viewDD:onchange Handle any necessary changes to subtrack checkboxes when the view changes
 // views are "select" drop downs on a subtrack configuration page
     var classesHidden = ""; // Needed for later
 
     if( obj.selectedIndex == 0) { // hide
         matSubCBsEnable(false,view);
         hideConfigControls(view);
 
         // Needed for later
         classesHidden = matViewClasses('hidden');
         classesHidden = classesHidden.concat( matAbcCBclasses('unchecked') );
     } else {
         // Make main display dropdown show full if currently hide
         compositeName = obj.name.substring(0,obj.name.indexOf(".")); // {trackName}.{view}.vis
         exposeComposite(compositeName);
         matSubCBsEnable(true,view);
 
         // Needed for later
         classesHidden = matViewClasses('hidden');
         classesHidden = classesHidden.concat( matAbcCBclasses('unchecked') );
 
         // If hide => show then check all subCBs matching view and matCBs
         // If show => show than just enable subCBs for the view.
         if (obj.lastIndex == 0) { // From hide to show
             // If there are matCBs then we will check some subCBs if we just went from hide to show
             var matCBs = $("input.matCB:checked");
             if (matCBs.length > 0) {
                 // Get list of all checked abc classes first
                 var classesAbcChecked = new Array();
                 matCBs.filter(".abc").each( function (i) {
                     var classList = $( this ).attr("class").split(" ");
                     classesAbcChecked.push( aryRemove(classList,"matCB","halfVis","abc") );
                 });
 
                 // Walk through checked non-ABC matCBs and sheck related subCBs
                 var subCBs = $("input.subCB").filter("."+view).not(":checked");
                 matCBs.not(".abc").each( function (i) {
                     var classList = $( this ).attr("class").split(" ");
                     classList = aryRemove(classList,"matCB","halfVis");
                     var subCBsMatching = objsFilterByClasses(subCBs,"and",classList);
                     if (classesAbcChecked.length>0)
                         subCBsMatching = objsFilterByClasses(subCBsMatching,"or",classesAbcChecked);
                     // Check the subCBs that belong to this view and checked matCBs
                     subCBsMatching.each( function (i) {
                         this.checked = true;
                         matSubCBsetShadow(this);
                         hideOrShowSubtrack(this);
                     });
                 });
             } // If no matrix, then enabling is all that was needed.
 
             // fix 3-way which may need to go from unchecked to .halfVis
             var matCBs = $("input.matCB").not(".abc").not(".halfVis").not(":checked");
             if(matCBs.length > 0) {
                 $( matCBs ).each( function (i) { matChkBoxNormalize( this, classesHidden ); });
             }
         }
     }
     // fix 3-way matCBs which may need to go from halfVis to checked or unchecked depending
     var matCBs = $("input.matCB").not(":checked").not(".halfVis");
     var matCBs = matCBsWhichAreComplete(false);
     if(matCBs.length > 0) {
         if($("select.viewDD").not("[selectedIndex=0]").length = 0) { // No views visible so nothing is inconsistent
             $( matCBs ).each( function (i) { matCbComplete( this, true ); });
         } else {
             $( matCBs ).each( function (i) { matChkBoxNormalize( this, classesHidden ); });
         }
     }
     matSubCBsSelected();
     obj.lastIndex = obj.selectedIndex;
 }
 
 function matSelectViewForSubTracks(obj,view)
 {
     waitOnFunction( _matSelectViewForSubTracks, obj,view);
 }
 
 function exposeComposite(compositeName)
 {
     // Make main display dropdown show full if currently hide
     var compositeDD = $("select[name='"+compositeName+"']");
     if($(compositeDD).attr('selectedIndex') < 1) { // Composite vis display is HIDE
         var maxVis = ($(compositeDD).children('option').length - 1);
         $(compositeDD).attr('selectedIndex',maxVis);
     }
 }
 
 function matSubCbClick(subCB)
 {
 // subCB:onclick  When a subtrack checkbox is clicked, it may result in
 // Clicking/unclicking the corresponding matrix CB.  Also the
 // subtrack may be hidden as a result.
     matSubCBsetShadow(subCB);
     hideOrShowSubtrack(subCB);
     // When subCBs are clicked, 3-state matCBs may need to be set
     var classes = matViewClasses('hidden');
     classes = classes.concat( matAbcCBclasses('unchecked') );
     var matCB = matCbFindFromSubCb( subCB );
     if( matCB != undefined ) {
         matChkBoxNormalize( matCB, classes );
     }
     //var abcCB = matAbcCbFindFromSubCb( subCB );
     //if( abcCB != undefined ) {
     //    matChkBoxNormalize( abcCB, classes );
     //}
     matSubCBsSelected();
 }
 
 function matCbClick(matCB)
 {
 // matCB:onclick  When a matrix CB is clicked, the set of subtracks checked may change
 // Also called indirectly by matButton:onclick via matSetMatrixCheckBoxes
 
     var classList = $( matCB ).attr("class").split(" ");
     var isABC = (aryFind(classList,"abc") != -1);
     classList = aryRemove(classList,"matCB","halfVis","abc");
     if(classList.length == 0 )
        matSubCBsCheck(matCB.checked);
     else if(classList.length == 1 )
        matSubCBsCheck(matCB.checked,classList[0]);               // dimX or dimY or dim ABC
     else if(classList.length == 2 )
        matSubCBsCheck(matCB.checked,classList[0],classList[1]);  // dimX and dimY
     else
         warn("ASSERT in matCbClick(): There should be no more than 2 entries in list:"+classList)
 
     if(!isABC)
         matCbComplete(matCB,true); // No longer partially checked
 
     if(isABC) {  // if dim ABC then we may have just made indeterminate X and Ys determinate
         if(matCB.checked == false) { // checking new dim ABCs cannot change indeterminate state.   IS THIS TRUE ?  So far.
             var matCBs = matCBsWhichAreComplete(false);
             if(matCBs.length > 0) {
                 if($("input.matCB.abc:checked").length == 0) // No dim ABC checked, so leave X&Y checked but determined
                     $( matCBs ).each( function (i) { matCbComplete( this, true ); });
                 else {
                     var classes = matViewClasses('hidden');
                     classes = classes.concat( matAbcCBclasses('unchecked') );
                     $( matCBs ).each( function (i) { matChkBoxNormalize( this, classes ); });
                 }
             }
         }
     }
     matSubCBsSelected();
 }
 
 function matSetMatrixCheckBoxes(state)
 {
 // matButtons:onclick Set all Matrix checkboxes to state.  If additional arguments are passed in, the list of CBs will be narrowed by the classes
     //jQuery(this).css('cursor', 'wait');
     var matCBs = $("input.matCB").not(".abc");
     for(var vIx=1;vIx<arguments.length;vIx++) {
         matCBs = $( matCBs ).filter("."+arguments[vIx]);  // Successively limit list by additional classes.
     }
     $( matCBs ).each( function (i) {
         this.checked = state;
         matCbComplete(this,true);
     });
     subCDs = $("input.subCB");
     for(var vIx=1;vIx<arguments.length;vIx++) {
         subCDs = $( subCDs ).filter("."+arguments[vIx]);  // Successively limit list by additional classes.
     }
     if(state) { // If clicking [+], further limit to only checked ABCs
         var classes = matAbcCBclasses('unchecked');
         subCDs = objsFilterByClasses(subCDs,"not",classes);  // remove unchecked abcCB classes
     }
     $( subCDs ).each( function (i) {
         this.checked = state;
         matSubCBsetShadow(this);
     });
     showOrHideSelectedSubtracks();
     matSubCBsSelected();
     //jQuery(this).css('cursor', '');
     return true;
 }
 
 function filterCompositeSelectionChanged(obj)
 { // filterComposite:onchange Set subtrack selection based upon the changed filter  [Not called for filterBy]
 
     if($(obj).val() != undefined
     && $(obj).val().toString().indexOf("All,") != -1) {
         $(obj).val("All");
         $(obj).attr('selectedIndex',0);
     }
     // Get list of values which should match classes of subtracks
     var classes = $(obj).val();  // It is already an array!
     if(classes == null) { // Nothing is selected.  Mark with "[empty]"
         classes = "";
         setCartVar($(obj).attr('name'),"[empty]");
         //$(obj).val("[empty]");
     }
 
     // For all subtracks that DO NOT match selected classes AND are checked: uncheck
     var subCBs = $("input.subCB");
     var subCBsToUnselect = subCBs;
     if (classes.length > 0 && classes[0] != "All")
         subCBsToUnselect = objsFilterByClasses(subCBsToUnselect,"not",classes);  // remove unchecked classes
     if (subCBsToUnselect.length > 0)
         subCBsToUnselect = $(subCBsToUnselect).filter(":checked");
     if (subCBsToUnselect.length > 0)
         subCBsToUnselect.each( function (i) { matSubCBcheckOne(this,false); });
 
     // For all subtracks that DO match a selected class AND are NOT checked:
     //  Figure out if other selection criteria match.  If so: check
     //var subCBs = $("input.subCB");
     var subCBsToSelect = subCBs;
     if(classes.length > 0) {
         if(classes.length > 0 && classes[0] != "All")
             subCBsToSelect = objsFilterByClasses(subCBsToSelect,"or",classes);     // Keep any that should be selected
         // Now deal with all the others
         if (subCBsToSelect.length > 0) {
             var filterComp = $("select.filterComp").not("[name='"+obj.name+"']"); // Exclude self from list
             for(var ix=0;ix<filterComp.length && subCBsToSelect.length > 0;ix++) {
                 var filterClasses = $(filterComp[ix]).val();
                 //alert(filterClasses);
                 if(filterClasses.length > 0 && filterClasses[0] != "All")
                     subCBsToSelect = objsFilterByClasses(subCBsToSelect,"or",filterClasses);     // Keep any that should be selected
                 else if(filterClasses.length == 0)
                     subCBsToSelect.length = 0;
             }
             //alert("filterComp:"+filterComp.length);
         }
         // FIXME: What about mat dimensions?
 
         // What to do now?
         if (subCBsToSelect.length > 0)
             subCBsToSelect = $(subCBsToSelect).not(":checked");
         if (subCBsToSelect.length > 0)
             subCBsToSelect.each( function (i) { matSubCBcheckOne(this,true); });
     }
     //alert("Subtracks:"+subCBs.length+"  To be selected:"+subCBsToSelect.length+"  unselected:"+subCBsToUnselect.length)
     matSubCBsSelected();
     //$(obj).children("option:even").disabled = true;
 }
 
 ///////////// CB support routines ///////////////
 // Terms:
 // viewDD - view drop-down control
 // matButton: the [+][-] button controls associated with the matrix
 // matCB - matrix dimX and dimY CB controls (in some cases this set contains abcCBs as well because they are part of the matrix)
 // abcCB - matrix dim (ABC) CB controls
 // subCB - subtrack CB controls
 // What does work
 // 1) 4 state subCBs: checked/unchecked enabled/disabled (which is visible/hidden)
 // 2) 3 state matCBs for dimX and Y but not for Z (checked,unchecked,indeterminate (incomplete set of subCBs for this matCB))
 // 3) cart vars for viewDD, abcCBs and subCBs but matCBs set by the state of those 3
 // What is awkward or does not work
 // A) Awkward: matCB could be 5 state (all,none,subset,superset,excusive non-matching set)
 function matSubCBsCheck(state)
 {
 // Set all subtrack checkboxes to state.  If additional arguments are passed in, the list of CBs will be narrowed by the classes
 // called by matCB clicks (matCbClick()) !
     var subCBs = $("input.subCB");
     for(var vIx=1;vIx<arguments.length;vIx++) {
         subCBs = subCBs.filter("."+arguments[vIx]);  // Successively limit list by additional classes.
     }
 
     if(state) { // If checking subCBs, then make sure up to 3 dimensions of matCBs agree with each other on subCB verdict
         var classes = matAbcCBclasses('unchecked');
         subCBs = objsFilterByClasses(subCBs,"not",classes);  // remove unchecked abcCB classes
         if(arguments.length == 1 || arguments.length == 3) { // Requested dimX&Y: check dim ABC state
             $( subCBs ).each( function (i) { matSubCBcheckOne(this,state); });
         } else {//if(arguments.length == 2) { // Requested dim ABC (or only 1 dimension so this code is harmless)
             var matXY = $("input.matCB").not(".abc");  // check X&Y state
             matXY = $( matXY ).filter(":checked");
             for(var mIx=0;mIx<matXY.length;mIx++) {
                 var classes = $(matXY[mIx]).attr("class").split(' ');
                 classes = aryRemove(classes,"matCB","halfVis");
                 $( subCBs ).filter('.'+classes.join(".")).each( function (i) { matSubCBcheckOne(this,state); });
             }
         }
     } else  // state not checked so no filtering by other matCBs needed
         subCBs.each( function (i) { matSubCBcheckOne(this,state); });
 
     return true;
 }
 
 function matSubCBsEnable(state)
 {
 // Enables/Disables subtracks checkboxes.  If additional arguments are passed in, the list of CBs will be narrowed by the classes
     var subCBs = $("input.subCB");
     for(var vIx=1;vIx<arguments.length;vIx++) {
         if(arguments[vIx].length > 0)
             subCBs = subCBs.filter("."+arguments[vIx]);  // Successively limit list by additional classes.
     }
     subCBs.each( function (i) {
         if(state) {
             $(this).parent().attr('title','');
             $(this).parent().attr('cursor','pointer');
         } else {
             $(this).parent().attr('title','view is hidden');
             $(this).parent().attr('cursor','pointer');
         }
         this.disabled = !state;
         matSubCBsetShadow(this);
         hideOrShowSubtrack(this);
     });
 
     return true;
 }
 
 function matSubCBcheckOne(subCB,state)
 {
 // setting a single subCB may cause it to appear/disappear
     subCB.checked = state;
     matSubCBsetShadow(subCB);
     hideOrShowSubtrack(subCB);
 }
 
 function matSubCBsetShadow(subCB)
 {
 // Since CBs only get into cart when enabled/checked, the shadow control enables cart to know other states
     var shadowState = 0;
     if(subCB.checked)
         shadowState = 1;
     if(subCB.disabled)
         shadowState -= 2;
     $("#"+subCB.name+"_4way").val(shadowState);
 }
 
 function matChkBoxNormalize(matCB)
 {
 // Makes sure matCBs are in one of 3 states (checked,unchecked,indeterminate) based on matching set of subCBs
     var classList = $( matCB ).attr("class").split(" ");
     var isABC = (aryFind(classList,"abc") != -1);
     if(isABC)
         alert("ASSERT: matChkBoxNormalize() called for dim ABC!");
     classList = aryRemove(classList,"matCB","halfVis");
 
     var classes = '.' + classList.join(".");// created string filter of classes converting "matCB K562 H3K4me1" as ".K562.H3K4me1"
     var subCBs = $("input.subCB").filter(classes); // All subtrack CBs that match matrix CB
 
     if(arguments.length > 1 && arguments[1].length > 0) { // dim ABC NOT classes
         subCBs = objsFilterByClasses(subCBs,"not",arguments[1]);
     }
 
     if(subCBs.length > 0) {
         var CBsChecked = subCBs.filter(":checked");
         if(!isABC) {
             if(CBsChecked.length == subCBs.length) {
                 matCbComplete(matCB,true);
                 $(matCB).attr('checked',true);
             } else if(CBsChecked.length == 0) {
                 matCbComplete(matCB,true);
                 $(matCB).attr('checked',false);
             } else {
                 matCbComplete(matCB,false);
                 $(matCB).attr('checked',true);
             }
         }
     }
     else
         matCbComplete(matCB,true); // If no subs match then this is determined !
 }
 
 function matCbComplete(matCB,complete)
 {
 // Makes or removes the 3rd (indeterminate) matCB state
     // Too many options:
     // 1) addClass()/removeClass() (which does not directly support title)
     // 2) wrap div which could contain border, color, content.  content is not on one line: size is difficult
     // 3) wrap font which could contain border, color, content.  content is on one line: size is difficult
     // 4) *[ ]* ?[ ]?  *[ ]*  No text seems right;  borders? colors? opacity? Yes, minimum
     if(complete) {
         $(matCB).css('opacity', '1');  // For some reason IE accepts direct change but isn't happy with simply adding class!
         $(matCB).removeClass('halfVis');
         $(matCB).attr("title","");
     } else {
         $(matCB).css('opacity', '0.5');
         $(matCB).addClass('halfVis');
         $(matCB).attr("title","Not all associated subtracks have been selected");
         $('.halfVis').css('opacity', '0.5');
     }
 }
 
 function matCBsWhichAreComplete(complete)
 {
 // Returns a list of currently indeterminate matCBs.  This is encapsulated to keep consistent with matCbComplete()
     if(complete)
         return $("input.matCB").not(".halfVis");
     else
         return $("input.matCB.halfVis");
 }
 
 function matCbFindFromSubCb(subCB)
 {
 // returns the one matCB associated with a subCB (or undefined)
     var classList =  $( subCB ).attr("class").split(" ");
     classes = '.' + classList.slice(1,3).join('.');   // How to get only X and Y classes?  Assume they are the first 2 ("subCB GM12878 H3K4me3 rep1 p1" we only want ".GM12878.H3K4me3")
     // At this point classes has been converted from "subCB 1GM12878 CTCF rep1 cHot" to ".1GM12878.CTCF"
     var matCB = $("input.matCB"+classes); // NOte, this works for filtering multiple classes because we want AND
     if(matCB.length == 1)
         return matCB;
     else
         return undefined;
 }
 
 function matAbcCBfindFromSubCb(subCB)
 {
 // returns the abcCBs associated with a subCB (or undefined)
     var abcCBs = $("input.matCB.abc");
     if( abcCBs.length > 0 ) {
         var classList =  $( subCB ).attr("class").split(" ");
         classList = aryRemove(classList,"subCB");
         classList.shift(); // Gets rid of X and Y associated classes (first 2 after subCB)
         classList.shift();
         classList.pop();   // gets rid of view associated class (at end)
         if(classList.length >= 1) {
             var abcCB = $(abcCBs).filter('.'+classList.join("."));
             if(abcCB.length >= 1)
                 return abcCB;
         }
     }
     return undefined;
 }
 
 function objsFilterByClasses(objs,keep,classes)
 {
 // Accepts an obj list and an array of classes, then filters successively by that list
     if( classes != undefined && classes.length > 0 ) {
         if(keep == "and") {
             objs = $( objs ).filter( '.' + classes.join('.') ); // Must belong to all
         } else if(keep == "or") {
             objs = $( objs ).filter( '.' + classes.join(',.') ); // Must belong to one or more
         } else if(keep == "not") {
             for(var cIx=classes.length-1;cIx>-1;cIx--) {
                 objs = $( objs ).not( '.' + classes[cIx] ); // not('class1.class2') is different from not('.class1').not('.class2')
             }
         }
     }
     return objs;
 }
 
 function matViewClasses(limitTo)
 {
 // returns an array of classes from the ViewDd: converts "viewDD normalText SIG"[]s to "SIG","zRAW"
     var classes = new Array;
     var viewDDs = $("select.viewDD");//.filter("[selectedIndex='0']");
     if(limitTo == 'hidden') {
         viewDDs = $(viewDDs).filter("[selectedIndex=0]");
     } else if(limitTo == 'visible') {
         viewDDs = $(viewDDs).not("[selectedIndex=0]");
     }
     $(viewDDs).each( function (i) {
         var classList = $( this ).attr("class").split(" ");
         classList = aryRemove(classList,"viewDD","normalText");
         classes.push( classList[0] );
     });
     return classes;
 }
 
 function matAbcCBclasses(limitTo)
 {// returns an array of classes from the dim ABC CB classes: converts "matCB abc rep1"[]s to "rep1","rep2"
     var classes = new Array;
     var abcCBs = $("input.matCB.abc");
     if(abcCBs.length > 0) {
         if(limitTo == 'unchecked') {
             abcCBs = abcCBs.not(":checked");
         } else if(limitTo == 'checked') {
             abcCBs = abcCBs.filter(":checked");
         }
         $(abcCBs).each( function (i) {
             var classList = $( this ).attr("class").split(" ");
             classList = aryRemove(classList,"matCB","abc");
             classes.push( classList[0] );
         });
     }
     return classes;
 }
 
 function matSubCBsSelected()
 {
 // Displays visible and checked track count
     var counter = $('.subCBcount');
     if(counter != undefined) {
         var subCBs =  $("input.subCB");
         $(counter).text($(subCBs).filter(":enabled:checked").length + " of " +$(subCBs).length+ " selected");
     }
 }
 
 /////////////////// subtrack configuration support ////////////////
 function compositeCfgUpdateSubtrackCfgs(inp)
 {
 // Updates all subtrack configuration values when the composite cfg is changed
     // If view association then find it:
     var view = "";
     var daddy = $(inp).parents(".blueBox");
     if(daddy.length == 1) {
         var classList = $(daddy[0]).attr("class").split(" ");
         if(classList.length == 2) {
             view = classList[1];
         }
     }
     var suffix = inp.name.substring(inp.name.indexOf("."));  // Includes '.'
     //if(suffix.length==0)
     //    suffix = inp.name.substring(inp.name.indexOf("_"));
     if(suffix.length==0) {
         warn("Unable to parse '"+inp.name+"'");
         return true;
     }
     if(inp.type.indexOf("select") == 0) {
         var list = $("select[name$='"+suffix+"']").not("[name='"+inp.name+"']"); // Exclude self from list
         if(view != "") { list =  $(list).filter(function(index) { return $(this).parents(".blueBox." + view).length == 1; });}
         if($(list).length>0) {
             if(inp.multiple != true)
                 $(list).attr('selectedIndex',inp.selectedIndex);
             else {
                 $(list).each(function(view) {  // for all dependent (subtrack) multi-selects
                     sel = this;
                     if(view != "") {
                         var hasClass = $(this).parents(".blueBox." + view);
                         if(hasClass.length != 0)
                             return;
                     }
                     $(this).children('option').each(function() {  // for all options of dependent mult-selects
                         $(this).attr('selected',$(inp).children('option:eq('+this.index+')').attr('selected')); // set selected state to independent (parent) selected state
                     });
                     $(this).attr('size',$(inp).attr('size'));
                 });
             }
         }
     }
     else if(inp.type.indexOf("checkbox") == 0) {
         var list = $("checkbox[name$='"+suffix+"']").not("[name='"+inp.name+"']"); // Exclude self from list
         if(view != "") { list =  $(list).filter(function(index) { return $(this).parents(".blueBox." + view).length == 1; });}
         if($(list).length>0)
             $(list).attr("checked",$(inp).attr("checked"));
     }
     else if(inp.type.indexOf("radio") == 0) {
         var list = $("input:radio[name$='"+suffix+"']").not("[name='"+inp.name+"']");
         list = $(list).filter("[value='"+inp.value+"']")
         if(view != "") { list =  $(list).filter(function(index) { return $(this).parents(".blueBox." + view).length == 1; });}
         if($(list).length>0)
             $(list).attr("checked",true);
     }
     else {  // Various types of inputs
         var list = $("input[name$='"+suffix+"']").not("[name='"+inp.name+"']");//.not("[name^='boolshad.']"); // Exclude self from list
         if(view != "") { list =  $(list).filter(function(index) { return $(this).parents(".blueBox." + view).length == 1; });}
         if($(list).length>0)
             $(list).val(inp.value);
         //else {
         //    alert("Unsupported type of multi-level cfg setting type='"+inp.type+"'");
         //    return false;
         //}
     }
     return true;
 }
 
 function compositeCfgRegisterOnchangeAction(prefix)
 {
 // After composite level cfg settings written to HTML it is necessary to go back and
 // make sure that each time they change, any matching subtrack level cfg setting are changed.
     var list = $("input[name^='"+prefix+".']").not("[name$='.vis']");
     $(list).change(function(){compositeCfgUpdateSubtrackCfgs(this);});
 
     var list = $("select[name^='"+prefix+".']").not("[name$='.vis']");
     $(list).change(function(){compositeCfgUpdateSubtrackCfgs(this);});
 }
 
 function registerViewOnchangeAction(viewTrackName)
 {
 // After composite level view settings are written to HTML it is necessary to go back and
 // make sure that each time they change, the change is ajaxed over
     var list = $("input[name^='"+viewTrackName+"\.']");
     $(list).each(function(){setIdRemoveName(this);});
     $(list).change(function(){setCartVarFromObjId(this);});
 
     list = $("select[name^='"+viewTrackName+"\.']"); // includes composite.view.vis
     $(list).each(function(){setIdRemoveName(this);});
     $(list).change(function(){setCartVarFromObjId(this);});
 
     list = $("select[name='"+viewTrackName+"']"); // is 'composite' vis
     $(list).each(function(){setIdRemoveName(this);});
     $(list).change(function(){setCartVarFromObjId(this);});
 }
 
 function registerFormSubmit(formName)
 {
     $('form[name="'+formName+'"]').each(function(i) { formSubmitWaitOnAjax(this)});
 }
 
 function subtrackCfgHideAll(table)
 {
 // hide all the subtrack configuration stuff
     $("div[id $= '_cfg']").each( function (i) {
         $( this ).css('display','none');
         $( this ).children("input[name$='.childShowCfg']").val("off");
     });
 }
 
 var popUpTrackName;
 var popUpTitle = "";
 var popSaveAllVars = null;
 function popUpCfgOk(popObj, trackName)
 { // Kicks off a Modal Dialog for the provided content.
     var allVars = getAllVars(popObj, trackName );   // always subtrack cfg
     var changedVars = varHashChanges(allVars,popSaveAllVars);
     //warn("cfgVars:"+varHashToQueryString(changedVars));
     setVarsFromHash(changedVars);
     var newVis = changedVars[trackName];
     if(newVis != null) {
         var sel = $('input[name="'+trackName+'_sel"]:checkbox');
         var checked = (newVis != 'hide' && newVis != '[]');  // subtracks do not have "hide", thus '[]'
         if( $(sel) != undefined ) {
             $(sel).each( function (i) { matSubCBcheckOne(this,checked); });  // Though there is only one, the each works but the direct call does not!
             matSubCBsSelected();
         }
     }
 }
 
 function popUpCfg(content, status)
 { // Kicks off a Modal Dialog for the provided content.
     // Set up the modal dialog
     var popit = $('#popit');
     $(popit).html("<div style='font-size:80%'>" + content + "</div>");
     $(popit).dialog({
         ajaxOptions: { cache: true }, // This doesn't work
         resizable: false,
         height: 'auto',
         width: 'auto',
         minHeight: 200,
         minWidth: 700,
         modal: true,
         closeOnEscape: true,
         autoOpen: false,
         buttons: { "Ok": function() {
             popUpCfgOk(this,popUpTrackName);
             $(this).dialog("close");
         }},
         open: function() { popSaveAllVars = getAllVars( this, popUpTrackName ); }, // always subtrack cfg
         close: function() { $('#popit').empty(); }
     });
     // Apparently the options above to dialog take only once, so we set title explicitly.
     if(popUpTitle != undefined && popUpTitle.length > 0)
         $(popit).dialog('option' , 'title' , popUpTitle );
     else
         $(popit).dialog('option' , 'title' , "Please Respond");
     $(popit).dialog('open');
 }
 
 function _popUpSubrackCfg(trackName,label)
 { // popup cfg dialog
     popUpTrackName = trackName;
     popUpTitle = label;
 
-    // FIXME: Avoid this getting into history and making the back button not work!
     $.ajax({
         type: "GET",
         url: "../cgi-bin/hgTrackUi?ajax=1&g=" + trackName + "&hgsid=" + getHgsid() + "&db=" + getDb(),
         dataType: "html",
         trueSuccess: popUpCfg,
         success: catchErrorOrDispatch,
         cmd: "cfg",
         cache: false
     });
 }
 
 function popUpSubtrackCfg(trackName,label)
 {
     waitOnFunction( _popUpSubrackCfg, trackName, label );  // Launches the popup but shields the ajax with a waitOnFunction
+    return false;
 }
 
 function subtrackCfgShow(tableName)
 {
 // Will show subtrack specific configuration controls
 // Config controls not matching name will be hidden
     var divit = $("#div_"+tableName+"_cfg");
     if($(divit).css('display') == 'none')
         $("#div_"+tableName+"_meta").hide();
     // Could have all inputs commented out, then uncommented when clicked:
     // But would need to:
     // 1) be able to find composite view level input
     // 2) know if subtrack input is non-default (if so then subtrack value overrides composite view level value)
     // 3) know whether so composite view level value has changed since hgTrackUi displayed (if so composite view level value overrides)
     $(divit).toggle();
     return false;
 }
 
 function enableViewCfgLink(enable,view)
 {
 // Enables or disables a single configuration link.
     var link = $('#a_cfg_'+view);
     if(enable)
         $(link).attr('href','#'+$(link).attr('id'));
     else
         $(link).removeAttr('href');
 }
 
 function enableAllViewCfgLinks()
 {
     $( ".viewDD").each( function (i) {
         var view = this.name.substring(this.name.indexOf(".") + 1,lastIndexOf(".vis"));
         enableViewCfgLink((this.selectedIndex > 0),view);
     });
 }
 
 function hideConfigControls(view)
 {
 // Will hide the configuration controls associated with one name
     $("input[name$='"+view+".showCfg']").val("off");      // Set cart variable
     $("tr[id^='tr_cfg_"+view+"']").css('display','none'); // Hide controls
 }
 
 function showConfigControls(name)
 {
 // Will show configuration controls for name= {tableName}.{view}
 // Config controls not matching name will be hidden
     var trs  = $("tr[id^='tr_cfg_']")
     $("input[name$='.showCfg']").val("off"); // Turn all off
     $( trs ).each( function (i) {
         if( this.id == 'tr_cfg_'+name && this.style.display == 'none') {
             $( this ).css('display','');
 	    $("input[name$='."+name+".showCfg']").val("on");
         }
         else if( this.style.display == '') {
             $( this ).css('display','none');
         }
     });
 
     // Close the cfg controls in the subtracks
     $("table[id^='subtracks.']").each( function (i) { subtrackCfgHideAll(this);} );
     return true;
 }
 
 function trAlternateColors(table,cellIx)
 {
 // Will alternate colors each time the contents of the column(s) change
     var lastContent = "not";
     var bgColor1 = "#FFFEE8";
     var bgColor2 = "#FFF9D2";
     var curColor = bgColor1;
     var lastContent = "start";
     var cIxs = new Array();
 
     for(var aIx=1;aIx<arguments.length;aIx++) {   // multiple columns
         cIxs[aIx-1] = arguments[aIx];
     }
     if (document.getElementsByTagName)
     {
         for (var trIx=0;trIx<table.rows.length;trIx++) {
             var curContent = "";
             if(table.rows[trIx].style.display == 'none')
                 continue;
             for(var ix=0;ix<cIxs.length;ix++) {
                 if(table.rows[trIx].cells[cIxs[ix]]) {
                     curContent = (table.rows[trIx].cells[cIxs[ix]].abbr != "" ?
                                   table.rows[trIx].cells[cIxs[ix]].abbr       :
                                   table.rows[trIx].cells[cIxs[ix]].innerHTML  );
                 }
             }
             if( lastContent != curContent ) {
                 lastContent  = curContent;
                 if( curColor == bgColor1)
                     curColor =  bgColor2;
                 else
                     curColor =  bgColor1;
             }
             table.rows[trIx].bgColor = curColor;
         }
     }
 }
 
 //////////// Sorting ////////////
 
 function tableSort(table,fnCompare)
 {
 // Sorts table based upon rules passed in by function reference
     //alert("tableSort("+table.id+") is beginning.");
     subtrackCfgHideAll(table);
     var trs=0,moves=0;
     var colOrder = new Array();
     var cIx=0;
     var trTopIx,trCurIx,trBottomIx=table.rows.length - 1;
     for(trTopIx=0;trTopIx < trBottomIx;trTopIx++) {
         trs++;
         var topRow = table.rows[trTopIx];
         for(trCurIx = trTopIx + 1; trCurIx <= trBottomIx; trCurIx++) {
             var curRow = table.rows[trCurIx];
             var compared = fnCompare(topRow,curRow,arguments[2]);
             if(compared < 0) {
                 table.insertBefore(table.removeChild(curRow), topRow);
                 topRow = curRow; // New top!
                 moves++;
             }
         }
     }
     if(fnCompare != trComparePriority)
         tableSetPositions(table);
     //alert("tableSort("+table.id+") examined "+trs+" rows and made "+moves+" moves.");
 }
 
 // Sorting a table by columns relies upon the sortColumns structure
 // The sortColumns structure looks like:
 //{
 //    char *  tags[];     // a list of trackDb.subGroupN tags in sort order
 //    boolean reverse[];  // the sort direction for that subGroup
 //    int     cellIxs[];  // The indexes of the columns in the table to be sorted
 //}
 ///// Following functions are for Sorting by columns:
 function trCompareColumnAbbr(tr1,tr2,sortColumns)
 {
 // Compares a set of columns based upon the contents of their abbr
     for(var ix=0;ix < sortColumns.cellIxs.length;ix++) {
         //if(tr1.cells[sortColumns.cellIxs[ix]].abbr == undefined) {
         //    if(tr1.cells[sortColumns.cellIxs[ix]].value < tr2.cells[sortColumns.cellIxs[ix]].value)
         //        return (sortColumns.reverse[ix] ? -1: 1);
         //    else if(tr1.cells[sortColumns.cellIxs[ix]].value > tr2.cells[sortColumns.cellIxs[ix]].value)
         //        return (sortColumns.reverse[ix] ? 1: -1);
         //} else {
             if(tr1.cells[sortColumns.cellIxs[ix]].abbr < tr2.cells[sortColumns.cellIxs[ix]].abbr)
                 return (sortColumns.reverse[ix] ? -1: 1);
             else if(tr1.cells[sortColumns.cellIxs[ix]].abbr > tr2.cells[sortColumns.cellIxs[ix]].abbr)
                 return (sortColumns.reverse[ix] ? 1: -1);
         //}
     }
     return 0;
 }
 
 
 function tableSortByColumns(table,sortColumns)
 {
 // Will sort the table based on the abbr values on a et of <TH> colIds
     if (document.getElementsByTagName)
     {
         tableSort(table,trCompareColumnAbbr,sortColumns);//cellIxs,columns.colRev);
                         var columns = new sortColumnsGetFromTable(table);
         if(sortColumns.tags.length>1)
             trAlternateColors(table,sortColumns.cellIxs[sortColumns.tags.length-2]);
 
     }
 }
 
 function sortOrderFromColumns(sortColumns)
 {// Creates the trackDB setting entry sortOrder subGroup1=+ ... from a sortColumns structure
     var sortOrder ="";
     for(ix=0;ix<sortColumns.tags.length;ix++) {
         sortOrder += sortColumns.tags[ix] + "=" + (sortColumns.reverse[ix] ? "-":"+") + " ";
     }
     if(sortOrder.length > 0)
         sortOrder = sortOrder.substr(0,sortOrder.length-1);
     return sortOrder;
 }
 
 function sortOrderFromTr(tr)
 {// Looks up the sortOrder input value from a *.sortTr header row of a sortable table
     var inp = tr.getElementsByTagName('input');
     for(var ix=0;ix<inp.length;ix++) {
         var offset = inp[ix].id.lastIndexOf(".sortOrder");
         if(offset > 0 && offset == inp[ix].id.length - 10)
             return inp[ix].value;
     }
     return "";
 }
 function sortColumnsGetFromSortOrder(sortOrder)
 {// Creates sortColumns struct (without cellIxs[]) from a trackDB.sortOrder setting string
     this.tags = new Array();
     this.reverse = new Array();
     var order = sortOrder;
     for(var ix=0;ix<12;ix++) {
         if(order.indexOf("=") <= 0)
             break;
         this.tags[ix]    = order.substring(0,order.indexOf("="));
         this.reverse[ix] = (order.substr(this.tags[ix].length+1,1) != '+');
         if(order.length < (this.tags[ix].length+2))
             break;
         order = order.substring(this.tags[ix].length+3);
     }
 }
 function sortColumnsGetFromTr(tr)
 {// Creates a sortColumns struct from the entries in the '*.sortTr' heading row of a sortable table
     this.inheritFrom = sortColumnsGetFromSortOrder;
     var inp = tr.getElementsByTagName('input');
     var ix;
     for(ix=0;ix<inp.length;ix++) {
         var offset = inp[ix].id.lastIndexOf(".sortOrder");
         if(offset > 0 && offset == inp[ix].id.length - 10) {
             this.inheritFrom(inp[ix].value);
             break;
         }
     }
     if(ix == inp.length)
         return;
 
     // Add an additional array
     this.cellIxs = new Array();
     var cols = tr.getElementsByTagName('th');
     for(var tIx=0;tIx<this.tags.length;tIx++) {
         var colIdTag = this.tags[tIx] + ".sortTh";
         for(ix=0; ix<cols.length; ix++) {
             var offset = cols[ix].id.lastIndexOf(colIdTag);
             if(offset > 0 && offset == cols[ix].id.length - colIdTag.length) {
                 this.cellIxs[tIx] = cols[ix].cellIndex;
             }
         }
     }
 }
 function sortColumnsGetFromTable(table)
 {// Creates a sortColumns struct from the contents of a '*.sortable' table
     this.inheritNow = sortColumnsGetFromTr;
     var ix;
     for(ix=0;ix<table.rows.length;ix++) {
         var offset = table.rows[ix].id.lastIndexOf(".sortTr");
         if(offset > 0 && offset == table.rows[ix].id.length - 7) {
             this.inheritNow(table.rows[ix]);
             break;
         }
     }
 }
 
 
 function tableSortUsingSortColumns(table)
 {// Sorts a table body based upon the marked columns
     var columns = new sortColumnsGetFromTable(table);
     tbody = table.getElementsByTagName("tbody")[0];
     tableSortByColumns(tbody,columns);
 }
 
 function _tableSortAtButtonPressEncapsulated(anchor,tagId)
 {// Updates the sortColumns struct and sorts the table when a column header has been pressed
  // If the current primary sort column is pressed, its direction is toggled then the table is sorted
  // If a secondary sort column is pressed, it is moved to the primary spot and sorted in fwd direction
     var th=anchor.parentNode;
     var sup=th.getElementsByTagName("sup")[0];
     var tr=th.parentNode;
     var inp = tr.getElementsByTagName('input');
     var iIx;
     for(iIx=0;iIx<inp.length;iIx++) {
         var offset = inp[iIx].id.lastIndexOf(".sortOrder");
         if(offset > 0 && offset == inp[iIx].id.length - 10)
             break;
     }
     var theOrder = new sortColumnsGetFromTr(tr);
     var oIx;
     for(oIx=0;oIx<theOrder.tags.length;oIx++) {
         if(theOrder.tags[oIx] == tagId)
             break;
     }
     if(oIx > 0) { // Need to reorder
         var newOrder = new sortColumnsGetFromTr(tr);
         var nIx=0;
         newOrder.tags[nIx] = theOrder.tags[oIx];
         newOrder.reverse[nIx] = false;  // When moving to the first position sort forward
         newOrder.cellIxs[nIx] = theOrder.cellIxs[ oIx];
         sups = tr.getElementsByTagName("sup");
         sups[newOrder.cellIxs[nIx]-1].innerHTML = "&darr;1";
         for(var ix=0;ix<theOrder.tags.length;ix++) {
             if(ix != oIx) {
                 nIx++;
                 newOrder.tags[nIx]    = theOrder.tags[ix];
                 newOrder.reverse[nIx] = theOrder.reverse[ix];
                 newOrder.cellIxs[nIx] = theOrder.cellIxs[ix];
                 var dir = sups[newOrder.cellIxs[nIx]-1].innerHTML.substring(0,1);
                 sups[newOrder.cellIxs[nIx]-1].innerHTML = dir + (nIx+1);
             }
         }
         theOrder = newOrder;
     } else { // need to reverse directions
         theOrder.reverse[oIx] = (theOrder.reverse[oIx] == false);
         var ord = sup.innerHTML.substring(1);
         sup.innerHTML = (theOrder.reverse[oIx] == false ? "&darr;":"&uarr;");
         if(theOrder.tags.length>1)
             sup.innerHTML += ord;
     }
     //alert("tableSortAtButtonPress(): count:"+theOrder.tags.length+" tag:"+theOrder.tags[0]+"="+(theOrder.reverse[0]?"-":"+"));
     var newSortOrder = sortOrderFromColumns(theOrder);
     inp[iIx].value = newSortOrder;
     var thead=tr.parentNode;
     var table=thead.parentNode;
     tbody = table.getElementsByTagName("tbody")[0];
     tableSortByColumns(tbody,theOrder);
     return;
 
 }
 
 function tableSortAtButtonPress(anchor,tagId)
 {
     waitOnFunction( _tableSortAtButtonPressEncapsulated, anchor, tagId);
 }
 
 function tableSortAtStartup()
 {
     //alert("tableSortAtStartup() called");
     var list = document.getElementsByTagName('table');
     for(var ix=0;ix<list.length;ix++) {
         var offset = list[ix].id.lastIndexOf(".sortable");  // TODO: replace with class and jQuery
         if(offset > 0 && offset == list[ix].id.length - 9) {
             tableSortUsingSortColumns(list[ix]);
         }
     }
 }
 
 function hintOverSortableColumnHeader(th)
 {// Upodates the sortColumns struct and sorts the table when a column headder has been pressed
     //th.title = "Click to make this the primary sort column, or toggle direction";
     //var tr=th.parentNode;
     //th.title = "Current Sort Order: " + sortOrderFromTr(tr);
 }
 
 ///// Following functions are for Sorting by priority
 function tableSetPositions(table)
 {
 // Sets the value for the *.priority input element of a table row
 // This gets called by sort or dradgndrop in order to allow the new order to affect hgTracks display
     if (table.getElementsByTagName)
     {
         for(var trIx=0;trIx<table.rows.length;trIx++) {
             if(table.rows[trIx].id.indexOf("tr_cb_") == 0) {
                 var inp = table.rows[trIx].getElementsByTagName('input');
                 for(var ix=0;ix<inp.length;ix++) {
                     var offset = inp[ix].name.lastIndexOf(".priority");
                     if( offset > 0 && offset == (inp[ix].name.length - 9)) {
                         inp[ix].value = table.rows[trIx].rowIndex;
                         break;
                     }
                 }
             }
         }
     }
 }
 function trFindPosition(tr)
 {
 // returns the position (*.priority) of a sortable table row
     var inp = tr.getElementsByTagName('input');
     for(var ix=0;ix<inp.length;ix++) {
         var offset = inp[ix].name.indexOf(".priority");
         if(offset > 0 && offset == (inp[ix].name.length - 9)) {
             return inp[ix].value;
         }
     }
     return "unknown";
 }
 
 function trComparePriority(tr1,tr2)
 {
 // Compare routine for sorting by *.priority
     var priority1 = 999999;
     var priority2 = 999999;
     var inp1 = tr1.getElementsByTagName('input');
     var inp2 = tr2.getElementsByTagName('input');
     for(var ix=0;ix<inp1.length;ix++) { // should be same length
         if(inp1[ix].name.indexOf(".priority") == (inp1[ix].name.length - 9))
             priority1 = inp1[ix].value;
         if(inp2[ix].name.indexOf(".priority") == (inp2[ix].name.length - 9))
             priority2 = inp2[ix].value;
         if(priority1 < 999999 && priority2 < 999999)
             break;
     }
     return priority2 - priority1;
 }
 
 ///// Following functions support column reorganization
 function trReOrderCells(tr,cellIxFrom,cellIxTo)
 {
 // Reorders cells in a table row: removes cell from one spot and inserts it before another
     //alert("tableSort("+table.id+") is beginning.");
     if(cellIxFrom == cellIxTo)
         return;
 
     var tdFrom = tr.cells[cellIxFrom];
     var tdTo   = tr.cells[cellIxTo];
     if((cellIxTo - cellIxFrom) == 1) {
         tdFrom = tr.cells[cellIxTo];
         tdTo   = tr.cells[cellIxFrom];
     } else if((cellIxTo - cellIxFrom) > 1)
         tdTo   = tr.cells[cellIxTo + 1];
 
     tr.insertBefore(tr.removeChild(tdFrom), tdTo);
 }
 
 function tableReOrderColumns(table,cellIxFrom,cellIxTo)
 {
 // Reorders cells in all the rows of a table row, thus reordering columns
     if (table.getElementsByTagName) {
         for(var ix=0;ix<table.rows.length;ix++) {
             var offset = table.rows[ix].id.lastIndexOf(".sortTr");
             if(offset > 0 && offset == table.rows[ix].id.length - 7) {
                 trReOrderCells(table.rows[ix],cellIxFrom,cellIxTo);
                 break;
             }
         }
         tbody = table.getElementsByTagName('tbody');
         for(var ix=0;ix<tbody[0].rows.length;ix++) {
             trReOrderCells(tbody[0].rows[ix],cellIxFrom,cellIxTo);
         }
     }
 }
 
 function showOrHideSelectedSubtracks(inp)
 {
 // Show or Hide subtracks based upon radio toggle
     var showHide;
     if(arguments.length > 0)
         showHide=inp;
     else {
         var onlySelected = $("input[name='displaySubtracks']");
         if(onlySelected.length > 0)
             showHide = onlySelected[0].checked;
         else
             return;
     }
     showSubTrackCheckBoxes(showHide);
     var list = $("table.tableSortable")
     for(var ix=0;ix<list.length;ix++) {
         var columns = new sortColumnsGetFromTable(list[ix]);
         var tbody = list[ix].getElementsByTagName('tbody');
         if(columns.tags.length>1) {
             if(columns.tags.length==2)
                 trAlternateColors(tbody[0],columns.cellIxs[0]);
             else if(columns.tags.length==3)
                 trAlternateColors(tbody[0],columns.cellIxs[0],columns.cellIxs[1]);
             else
                 trAlternateColors(tbody[0],columns.cellIxs[0],columns.cellIxs[1],columns.cellIxs[2]);
         }
     }
 }
 
 ///// Following functions called on page load
 function matInitializeMatrix()
 {
 // Called at Onload to coordinate all subtracks with the matrix of check boxes
 //var start = startTiming();
 jQuery('body').css('cursor', 'wait');
     if (document.getElementsByTagName) {
         matSubCBsSelected();
         showOrHideSelectedSubtracks();
     }
 jQuery('body').css('cursor', '');
 //showTiming(start,"matInitializeMatrix()");
 }
 
 function multiSelectLoad(div,sizeWhenOpen)
 {
     //var div = $(obj);//.parent("div.multiSelectContainer");
     var sel = $(div).children("select:first");
     if(div != undefined && sel != undefined && sizeWhenOpen <= $(sel).length) {
         $(div).css('width', ( $(sel).clientWidth ) +"px");
         $(div).css('overflow',"hidden");
         $(div).css('borderRight',"2px inset");
     }
     $(sel).show();
 }
 
 function multiSelectCollapseToSelectedOnly(obj)
 {
     var items = $(obj).children("option");
     if(items.length > 0) {
         $(items).not(":selected").hide();
     }
     $(obj).attr("size",$(items).filter(":selected").length);
 }
 
 function multiSelectBlur(obj)
 { // Closes a multiselect and colapse it to a single option when appropriate
     if($(obj).val() == undefined || $(obj).val() == "") {
         $(obj).val("All");
         $(obj).attr('selectedIndex',0);
     }
     //if(obj.value == "All") // Close if selected index is 1
     if($(obj).attr('selectedIndex') == 0) // Close if selected index is 1
         $(obj).attr('size',1);
     else
         multiSelectCollapseToSelectedOnly(obj);
 
     /*else if($.browser.msie == false && $(obj).children('option[selected]').length==1) {
         var ix;
         for(ix=0;ix<obj.options.length;ix++) {
             if(obj.options[ix].value == obj.value) {
                 //obj.options[ix].selected = true;
                 obj.selectedIndex = ix;
                 obj.size=1;
                 $(obj).trigger('change');
                 break;
             }
         }
     }*/
 }
 
 function filterCompositeSet(obj,all)
 { // Will set all filter composites via [+] or [-] buttons
 
     matSubCBsCheck(all);
     var vars = [];
     var vals = [];
     var filterComp = $("select.filterComp").not(".filterBy");
     if(all) {
         $(filterComp).each(function(i) {
             $(this).trigger("checkAll");
             $(this).val("All");
             //vars.push($(this).attr('name'));  // Don't bother ajaxing this over
             //vals.push($(this).val());
         });
     } else {
         $(filterComp).each(function(i) {
             $(this).trigger("uncheckAll");
             $(this).val("");
             vars.push($(this).attr('name'));
             vals.push("[empty]");
         });
     }
     if(vars.length > 0) {
         setCartVars(vars,vals);
     }
     matSubCBsSelected(); // Be sure to update the counts!
 }
 
 function filterCompositeExcludeOptions(obj)
 { // Will mark all options in one filterComposite boxes that are inconsistent with the current
   // selections in other filterComposite boxes.  Mark is class ".excluded"
 
     if($(obj).hasClass('filterBy'))
         return false;
 
     // Walk through all other dimensions and narrow list of subCBs that are selected
     // NOTE: narrowing the list should by each dimension other than current one is the same as
     // getting the selected CB list if the current dimension selected all
     var allSelectedTags = [ ];  // empty array
     var oneEmpty = false;
     var subCBs = $("input.subCB");
     var filterComp = $("select.filterComp").not("#"+$(obj).attr('id')); // Exclude self from list
     $( filterComp ).each( function (i)  {
         if( $( subCBs ).length > 0 ) {
             var selectedTags = $( this ).val();
             if( !selectedTags || selectedTags.length == 0)
                 oneEmpty = true;
             else if( selectedTags && selectedTags.length > 0 && selectedTags[0] != "All" ) {
                 subCBs = objsFilterByClasses(subCBs,"or",selectedTags);  // must belong to one or more
                 allSelectedTags = allSelectedTags.concat(selectedTags);
             }
         }
     });
     // FIXME: What about mat dimensions?
 
     // Walk through all selected subCBs to get other related tags
     var possibleSelections = [ ];  // empty array
     if(!oneEmpty) {
         $( subCBs ).each( function (i)  {
 
             var possibleClasses = $( this ).attr("class").split(" ");
             if( $(possibleClasses).length > 0)
                 possibleClasses = aryRemoveVals(possibleClasses,[ "subCB" ]);
             if( $(possibleClasses).length > 0)
                 possibleSelections = possibleSelections.concat(possibleClasses);
         });
         if( $ (possibleSelections).length > 0) // clean out tags from other dimensions
             possibleSelections = aryRemoveVals(possibleSelections,allSelectedTags);
     }
 
     // Walk through all options in this filterBox to set excluded class
     //warn(possibleSelections,toString());
     var updated = false;
     var opts = $(obj).children("option");
     for(var ix = 1;ix < $(opts).length;ix++) { // All is always allowwed
         if(!oneEmpty && possibleSelections.length > 0 && aryFind(possibleSelections,opts[ix].value) == -1) {
             if($(opts[ix]).hasClass('excluded') == false) {
                 $(opts[ix]).addClass('excluded');
                 updated = true;
             }
         } else if($(opts[ix]).hasClass('excluded')) {
             $(opts[ix]).removeClass('excluded');
             updated = true;
         }
     }
     return updated;
 }
 
 function multiSelectFocus(obj,sizeWhenOpen)
 { // Opens multiselect whenever it gets focus
     if($(obj).attr('size') != sizeWhenOpen) {
     $(obj).children("option").show();
     $(obj).attr('size',sizeWhenOpen);
     //warn("Selected:"+$(obj).children("option").filter(":selected").length);
     }
     //return false;
 }
 
 function multiSelectClick(obj,sizeWhenOpen)
 { // Opens multiselect whenever it is clicked
     if($(obj).attr('size') != sizeWhenOpen) {
         multiSelectFocus(obj,sizeWhenOpen);
         //$(obj).children("option").show();
         //$(obj).attr('size',sizeWhenOpen);
         return false;
     } else if($(obj).attr('selectedIndex') == 0)
         $(obj).attr('size',1);
 return true;
 }
 
 function navigationLinksSetup()
 { // Navigation links let you jump to places in the document
   // If they exist, then they need to be well placed to fit window dimensions
 
     // Put navigation links in top corner
     var navDown = $("span#navDown");
     if(navDown != undefined) {
         var winWidth = ($(window).width() - 30) + "px"; // Room for borders
         $('.windowSize').css({maxWidth: winWidth,width: winWidth});
         var sectTtl = $("#sectTtl").parents("td");
         if(sectTtl != undefined) {
             $(sectTtl).css({clear: 'none'});
             if($.browser.msie)
                 $(sectTtl).prepend($(navDown));
             else
                 $(sectTtl).append($(navDown));
         }
         $(navDown).css({display:''});
         $(navDown).show();
     }
 
     // Decide if top links are needed
     var navUp = $('span.navUp');
     if($(navUp) != undefined && $(navUp).length > 0) {
         $(navUp).each(function(i) {
             var offset = $(this).parent().offset();
             if(offset.top  > $(window).height()) {
                 $(this).css({display:''});
                 $(this).show();
             }
         });
     }
 }
 
 // The following js depends upon the jQuery library
 $(document).ready(function()
 {
     //jQuery.each(jQuery.browser, function(i, val) {
     //    if(val) {
     //        browser = i;
     //    }
     //});
     //matInitializeMatrix();
     //$("div.multiSelectContainer").each( function (i) {
     //    var sel = $(this).children("select:first");
     //    multiSelectLoad(this,sel.openSize);
     //});
 
     // Allows rows to have their positions updated after a drag event
     var tblDnd = $(".tableWithDragAndDrop");
     if($(tblDnd).length > 0) {
         $(tblDnd).tableDnD({
             onDragClass: "trDrag",
             dragHandle: "dragHandle",
             onDrop: function(table, row, dragStartIndex) {
                     if(tableSetPositions) {
                         tableSetPositions(table);
                     }
                 }
             });
         $(".dragHandle").hover(
             function(){ $(this).parent('tr').addClass('trDrag'); },
             function(){ $(this).parent('tr').removeClass('trDrag'); }
         );
     }
     $('.halfVis').css('opacity', '0.5'); // The 1/2 opacity just doesn't get set from cgi!
 
     $('.filterComp').each( function(i) { // Do this by 'each' to set noneIsAll individually
         $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: $(this).hasClass('filterBy') });
     });
 
     // Put navigation links in top corner
     navigationLinksSetup();
 });