5a1d1a25ff3a7a7001e5dd98e1e4879b6f3ebe9d
chmalee
  Tue Dec 16 16:15:02 2025 -0800
Fix up jsOnEventById calls to javascript literal encode the id's, so the ids can have parens, single quotes, double quotes, etc in them and still be selected. Fix up the matrix checkbox code to htmlEncode the subgroups so subgroups can have parens, single quotes, etc in them and still work as class names and selectors, refs #36841

diff --git src/hg/js/hui.js src/hg/js/hui.js
index 8e2cd67c18f..ddd9975eb0d 100644
--- src/hg/js/hui.js
+++ src/hg/js/hui.js
@@ -164,38 +164,40 @@
             }
         }
     }
 
     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
 
     var matCBs = $("input.matCB").not(".abc");
     for (var vIx=1;vIx<arguments.length;vIx++) {
+        if (arguments[vIx] !== "")
             matCBs = $( matCBs ).filter("."+arguments[vIx]);  // Successively limit list
     }
     $( matCBs ).each( function (i) {
         this.checked = state;
         matCbComplete(this,true);
     });
     var subCbs = $("input.subCB");
     for (var sIx=1;sIx<arguments.length;sIx++) {
+        if (arguments[sIx] !== "")
             subCbs = $( subCbs ).filter("."+arguments[sIx]);  // Successively limit list
     }
     if (state) { // If clicking [+], further limit to only checked ABCs
         var classes = matAbcCBclasses(false);
         subCbs = objsFilterByClasses(subCbs,"not",classes);  // remove unchecked abcCB classes
     }
     $( subCbs ).each( function (i) {
         if (this.checked !== state) {
             this.checked = state;
             matSubCBsetShadow(this,false);
             $(this).trigger("change");  // NOTE: if "subCfg" then 'change' event will update it
         }
     });
     if (state)
         exposeAll();  // Unhide composite vis?
@@ -237,31 +239,31 @@
 // 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
+        subCBs = subCBs.filter("."+CSS.escape(arguments[vIx]));  // Successively limit list
     }
 
     if (state) { 
         // If checking subCBs, then make sure up to 3 dimensions of matCBs
         // agree with each other on subCB verdict
         var classes = matAbcCBclasses(false);
         if (classes.length > 0)
             subCBs = objsFilterByClasses(subCBs,"not",classes);  // remove unchecked abcCB classes
         if (arguments.length === 1 || arguments.length === 3) { // Requested dimX&Y: check ABC state
             $( subCBs ).each( function (i) { matSubCBcheckOne(this,state); });
         } else {//if (arguments.length === 2) { // Requested dim ABC (or only 1 dim so harmless)
             var matXY = $("input.matCB").not(".abc");  // check X&Y state
             matXY = $( matXY ).filter(":checked");
             if (matXY.length === 0)
                 subCBs.each( function (i) { matSubCBcheckOne(this,state); });
@@ -1312,31 +1314,31 @@
                 continue;
             if (ix === 0) {
                 $(".matCell."+classList[ix]).css({backgroundColor: color });
             } else {
                 $(cell).closest('tr').css({backgroundColor: color }); // faster?
             }
         }
         if (on && cell.title.length === 0) {
             for (var cIx=0;cIx < classList.length;cIx++) {
                 if (classList[cIx] === 'all') { // on a label already
                     cell.title = "";
                     break;
                 }
                 if (cell.title.length > 0)
                     cell.title += " and ";
-                cell.title += $("th."+classList[cIx]).first().text();
+                cell.title += $("th."+CSS.escape(classList[cIx])).first().text();
             }
         }
     },
 
     clearGhostHilites: function ()
     {
         $('.matCell').css({backgroundColor:""});
         $(mat.matrix).find('tr').css({backgroundColor:""});
     },
 
     resizeAngleLabels: function ()
     {   // Sets the height on the angled matrix labels
         var longest = "";
         var up45 = $('div.up45');
         $(up45).each(function (i) {