420f3d256e332531c49df90c76fd4a303d2d8137
tdreszer
  Mon Nov 29 14:04:39 2010 -0800
Made filterComposite work with matrix.
diff --git src/hg/js/hui.js src/hg/js/hui.js
index 8c6c6fd..dea5442 100644
--- src/hg/js/hui.js
+++ src/hg/js/hui.js
@@ -15,40 +15,40 @@
 
 // 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') );
+        classesHidden = classesHidden.concat( matAbcCBclasses(false) );
     } else {
         // Make main display dropdown show full if currently hide
         compositeName = obj.name.substring(0,obj.name.indexOf(".")); // {trackName}.{view}.vis
         exposeAll();
         matSubCBsEnable(true,view);
 
         // Needed for later
         classesHidden = matViewClasses('hidden');
-        classesHidden = classesHidden.concat( matAbcCBclasses('unchecked') );
+        classesHidden = classesHidden.concat( matAbcCBclasses(false) );
 
         // 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
@@ -99,31 +99,31 @@
     // Make main display dropdown show full if currently hide
     var visDD = $("select.visDD"); // limit to hidden
     if ($(visDD).length == 1 && $(visDD).attr('selectedIndex') == 0)   // limit to hidden
         $(visDD).attr('selectedIndex',$(visDD).children('option').length - 1);
 }
 
 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') );
+    classes = classes.concat( matAbcCBclasses(false) );
     var matCB = matCbFindFromSubCb( subCB );
     if( matCB != undefined ) {
         matChkBoxNormalize( matCB, classes );
     }
     //var abcCB = matAbcCbFindFromSubCb( subCB );
     //if( abcCB != undefined ) {
     //    matChkBoxNormalize( abcCB, classes );
     //}
 
     if(subCB.checked)
         exposeAll();  // Unhide composite vis?
 
     matSubCBsSelected();
 }
 
@@ -143,157 +143,167 @@
        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') );
+                    classes = classes.concat( matAbcCBclasses(false) );
                     $( matCBs ).each( function (i) { matChkBoxNormalize( this, classes ); });
                 }
             }
         }
     }
 
     if(matCB.checked)
         exposeAll();  // Unhide composite vis?
     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');
+        var classes = matAbcCBclasses(false);
         subCDs = objsFilterByClasses(subCDs,"not",classes);  // remove unchecked abcCB classes
     }
     $( subCDs ).each( function (i) {
         this.checked = state;
         matSubCBsetShadow(this);
     });
     if(state)
         exposeAll();  // Unhide composite vis?
     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]");
+        setCartVar($(obj).attr('name'),"[empty]");  // FIXME: setCartVar conflicts with "submit" button paradigm
     }
 
     // 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?
+        // Filter for Matrix CBs too
+        if (subCBsToSelect.length > 0) {
+            var matCb = $("input.matCb").filter(":checked");
+            if (matCb.length > 0) {
+                var matchClasses = "";
+                $( matCb ).each( function(i) {
+                    var filterClasses = $( this ).attr("class").split(" ");
+                    filterClasses = aryRemove(filterClasses,"matCB","halfVis","abc");
+                    matchClasses = matchClasses + ",." + filterClasses.join('.');
+                });
+                if (matchClasses.length > 0) {
+                    matchClasses = matchClasses.substring(1); // Skip past leading comma: ",.HepG2.ZBTB33,.GM12878.CTCF"
+                    subCBsToSelect = $(subCBsToSelect).filter(matchClasses);     // Keep any that should be selected
+                }
+            }
+        }
 
-        // What to do now?
+        // Now we have subCBs that should be checked.  Exclude already checked, then do it
         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');
+        var classes = matAbcCBclasses(false);
         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); });
 
@@ -462,45 +472,47 @@
     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)
+function matAbcCBclasses(wantSelected)
 {// 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') {
+        if (!wantSelected) {
             abcCBs = abcCBs.not(":checked");
-        } else if(limitTo == 'checked') {
+        } else {
             abcCBs = abcCBs.filter(":checked");
         }
         $(abcCBs).each( function (i) {
             var classList = $( this ).attr("class").split(" ");
             classList = aryRemove(classList,"matCB","abc");
             classes.push( classList[0] );
         });
+    } else { // No abcCBs so look for filterBox classes
+        return filterCompositeClasses(wantSelected);
     }
     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 ////////////////
@@ -1225,61 +1237,78 @@
         $(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);
+        setCartVars(vars,vals);// FIXME: setCartVar conflicts with "submit" button paradigm
     }
     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?
+    // Matrix CBs need to be considered too
+    if (subCBs.length > 0) {
+        var matCb = $("input.matCb").filter(":checked");
+        if (matCb.length == 0)
+            oneEmpty = true;
+        else {//if (matCb.length > 0) {
+            var matchClasses = "";
+            $( matCb ).each( function(i) {
+                var filterClasses = $( this ).attr("class").split(" ");
+                filterClasses = aryRemove(filterClasses,"matCB","halfVis","abc");
+                matchClasses = matchClasses + ",." + filterClasses.join('.');
+            });
+            if (matchClasses.length > 0) {
+                matchClasses = matchClasses.substring(1); // Skip past leading comma: ",.HepG2.ZBTB33,.GM12878.CTCF"
+                subCBs = $(subCBs).filter(matchClasses);
+            }
+        }
+    }
 
     // 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);
     }
@@ -1290,30 +1319,57 @@
     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 filterCompositeClasses(wantSelected)
+{// returns an array of classes from the dim ABC filterComp classes: converts "matCB abc rep1"[]s to "rep1","rep2"
+    var classes = new Array;
+    var abcFBs = $("select.filterComp");
+    if(abcFBs.length > 0) {
+        $(abcFBs).each( function(i) {
+            // Need to walk through list of options
+            var ix=0;
+            var allAreSelected = false;
+            if (this.options[ix].value == "All") {
+                allAreSelected = this.options[ix].selected;
+                ix++;
+            }
+            for(;ix<this.length;ix++) {
+                if (allAreSelected || this.options[ix].selected) {
+                    if (wantSelected)
+                        classes.push(this.options[ix].value);
+                } else {
+                    if (!wantSelected)
+                        classes.push(this.options[ix].value);
+                }
+            }
+        });
+    }
+    return classes;
+}
+
 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();