07c318cffa6769b6a4b650a88a7887ae01de608d
jcasper
  Wed Feb 4 20:07:42 2026 -0800
Impose an overall limit on active track count for featured composites,
and allow alternate labels for the data type selectors, refs #36320

diff --git src/hg/js/facetedComposite.js src/hg/js/facetedComposite.js
index e9da72619a2..859669f32fb 100644
--- src/hg/js/facetedComposite.js
+++ src/hg/js/facetedComposite.js
@@ -88,36 +88,37 @@
         });
     }
 
     function initDataTypeSelector() {
         // Skip if no dataTypes defined or empty object
         if (!embeddedData.dataTypes || Object.keys(embeddedData.dataTypes).length === 0) {
             return;
         }
 
         const selector = document.getElementById("dataTypeSelector");
         selector.appendChild(Object.assign(document.createElement("label"), {
             innerHTML: "<b>Data type</b>",
         }));
         Object.keys(embeddedData.dataTypes).forEach(name => {
             const label = document.createElement("label");
+            const dataType = embeddedData.dataTypes[name];
             label.innerHTML = `
-                <input type="checkbox" class="cbgroup" value="${name}">${name}`;
+                <input type="checkbox" class="cbgroup" value="${name}">${dataType.title}`;
             selector.appendChild(label);
         });
         const selectedDataTypes = new Set(  // get dataTypes selected initially
-            Object.entries(embeddedData.dataTypes).filter(([_, val]) => val === 1)
+            Object.entries(embeddedData.dataTypes).filter(([_, val]) => val.active === 1)
                 .map(([key]) => key)
         );
         // initialize data type checkboxes (using class instead of 'name')
         document.querySelectorAll("input.cbgroup")
             .forEach(cb => { cb.checked = selectedDataTypes.has(cb.value); });
 
         // Capture initial data type state
         initialState.dataTypes = new Set(selectedDataTypes);
     }
 
     function initTable(allData) {
         const { metadata, rowToIdx, colNames } = allData;
 
         const ordinaryColumns = colNames.map(key => ({  // all but checkboxes
             data: key,
@@ -346,30 +347,46 @@
                     if (cb.checked) {
                         currentDataTypes.push(cb.value);
                     }
                 });
                 // Require at least one data type when the selector exists
                 if (currentDataTypes.length === 0) {
                     alert("Please select at least one data type.");
                     return;  // abort submission
                 }
             }
 
             // Get current data element selections
             const currentDataElements = table.rows({selected: true}).data().toArray()
                 .map(obj => obj[primaryKey]);
 
+            // Enforce an upper bound on the number of tracks on at the same time.
+            // This is imperfect when data types are present - some combinations might
+            // have been manually hidden by the user.  But it should be a good ballpark.
+            const trackLimit = 1000;
+            if (hasDataTypes) {
+                if (currentDataTypes.length * currentDataElements.length > trackLimit) {
+                    alert("You have turned on too many subtracks (over 1000) - please uncheck some.");
+                    return;  // abort submission
+                }
+            } else {
+                if (currentDataElements.length > trackLimit) {
+                    alert("You have turned on too many subtracks (over 1000) - please uncheck some.");
+                    return;  // abort submission
+                }
+            }
+
             // Build the parameters for the cart update
             const uriForUpdate = new URLSearchParams({
                 "cartDump.metaDataId": mdid,
                 "noDisplay": 1
             });
 
             // Data elements: was and now
             if (initialState.dataElements.size > 0) {
                 initialState.dataElements.forEach(de =>
                     uriForUpdate.append(`${mdid}.de_was`, de));
             } else {
                 uriForUpdate.append(`${mdid}.de_was`, "");
             }
             if (currentDataElements.length > 0) {
                 currentDataElements.forEach(de =>