6239b81a7033f90749f836e73703c65eabd0de9c
jcasper
  Fri Apr 10 15:56:31 2026 -0700
Moved js and css includes for faceted composites into hgTrackUi for better detection
of file changes.  Dynamically change the track visibility based on selected subtracks (hiding
it if everything is deselected, making it visible when something is selected).  refs #36320

diff --git src/hg/js/facetedComposite.js src/hg/js/facetedComposite.js
index 4df32b24c4f..b0818970768 100644
--- src/hg/js/facetedComposite.js
+++ src/hg/js/facetedComposite.js
@@ -1,726 +1,752 @@
 // SPDX-License-Identifier: MIT; (c) 2025 Andrew D Smith (author)
 /* jshint esversion: 11 */
 $(function() {
     /* ADS: Uncomment below to force confirm on unload/reload */
     // window.addEventListener("beforeunload", function (e) {
     //     e.preventDefault(); e.returnValue = ""; });
     const DEFAULT_MAX_CHECKBOXES = 20;  // ADS: without default, can get crazy
 
-    // ADS: need "matching" versions for the plugins
-    const DATATABLES_URL = "../js/dataTables-2.2.2.min.js";
-    const DATATABLES_SELECT_URL = "../js/dataTables.select-3.0.0.min.js";
-    const CSS_URLS = [
-        "../style/dataTables-2.2.2.min.css",  // dataTables CSS
-        "../style/dataTables.select-3.0.0.min.css",  // dataTables Select CSS
-        "../style/facetedComposite.css",  // Local metadata table CSS
-    ];
-
     const isValidColorMap = obj =>  // check the whole thing and ignore if invalid
           typeof obj === "object" && obj !== null && !Array.isArray(obj) &&
           Object.values(obj).every(x =>
               typeof x === "object" && x !== null && !Array.isArray(x) &&
                   Object.values(x).every(value => typeof value === "string"));
 
     // fetch file dynamically
     const loadOptional = (url, hgsid, track) =>  { // load if possible otherwise carry on
         if (!url) return Promise.resolve(null);
         let fetchBody = `fileUrl=${url}&track=${track}`;
         if (hgsid !== null) {
             fetchBody = fetchBody + `&hgsid=${hgsid}`;
         }
         const fetchUrl = `/cgi-bin/hgTrackUi?${fetchBody}`;
         const req = (fetchUrl.length > 2048 || embeddedData.udcTimeout) ?
             fetch("/cgi-bin/hgTrackUi", {
                 method: "POST",
                 headers: { "Content-Type": "application/x-www-form-urlencoded" },
                 body: fetchBody,
             })
             : fetch(fetchUrl, {
                 method: "GET",
                 headers: { "Content-Type": "application/x-www-form-urlencoded" },
             });
         return req.then(r => r.ok ? r.json() : null).catch(() => null);
     };
 
-    const loadIfMissing = (condition, url, callback) =>  // for missing plugins
-          condition ?
-          document.head.appendChild(Object.assign(
-              document.createElement("script"), { src: url, onload: callback }))
-          : callback();
-
     const toTitleCase = str =>
           str.toLowerCase()
           .split(/[_\s-]+/)  // Split on underscore, space, or hyphen
           .map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
 
     const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
 
     const embeddedData = (() => {
         // get data that was embedded in the HTML here to use them as globals
         const dataTag = document.getElementById("app-data");
         return dataTag ? JSON.parse(dataTag.innerText) : "";
     })();
 
     // Store initial checkbox states for delta computation on server
     const initialState = {
         dataElements: new Set(),
         dataTypes: new Set()
     };
 
     function generateHTML() {
         const container = document.createElement("div");
         container.id = "myTag";
         container.innerHTML = `
         <div id="dataTypeSelector"></div>
         <div id="container">
             <div id="filters"></div>
             <table id="theMetaDataTable">
                 <thead></thead>
                 <tfoot></tfoot>
             </table>
         </div>
         `;
         // Instead of appending to body, append into the placeholder div
         document.getElementById("metadata-placeholder").appendChild(container);
     }
 
     function updateVisibilities(uriForUpdate, submitBtnEvent) {
         // get query params from URL
         const paramsFromUrl = new URLSearchParams(window.location.search);
         const db = paramsFromUrl.get("db");
         const hgsid = paramsFromUrl.get("hgsid");
         let body = `${uriForUpdate}`;
         if (db !== null) {
             body = body + `&db=${db}`;
         }
         if (hgsid !== null) {
             body = body + `&hgsid=${hgsid}`;
         }
         fetch("/cgi-bin/cartDump", {
             method: "POST",
             headers: { "Content-Type": "application/x-www-form-urlencoded" },
             body: body,
         }).then(() => {
             // 'disable' any CSS named elements here to them keep out of cart
             const dtLength = submitBtnEvent.
                   target.form.querySelector("select[name$='_length']");
             if (dtLength) {
                 dtLength.disabled = true;
             }
             submitBtnEvent.target.form.submit();  // release submit event
         });
     }
 
     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>Subtrack types enabled:</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}">${dataType.title}`;
             selector.appendChild(label);
         });
         const selectedDataTypes = new Set(  // get dataTypes selected initially
             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,
             title: toTitleCase(key.replace(/^_/, "")),
         }));
 
         const checkboxColumn = {
             data: null,
             orderable: false,
             defaultContent: "",
             title: `
             <label title="Select all visible rows">
             <input type="checkbox" id="select-all"/></label>`,
             // no render function needed
         };
 
         const hasDataTypes = embeddedData.dataTypes &&
                              Object.keys(embeddedData.dataTypes).length > 0;
         const itemLabel = hasDataTypes ? "samples" : "tracks";
         const singularLabel = itemLabel.slice(0, -1);
 
         const columns = [checkboxColumn, ...ordinaryColumns];
         const table = $("#theMetaDataTable").DataTable({
             data: metadata,
             deferRender: true,    // seems faster
             columns: columns,
             columnDefs: [ { targets:0, render: DataTable.render.select() } ],
             responsive: true,
             layout: {
                 topStart: 'pageLength',
                 topEnd: null,        // omit global search
                 bottomStart: 'info',
                 bottomEnd: 'paging'
             },
             order: [[1, "asc"]],  // sort by the first data column, not checkbox
             pageLength: 25,       // show 25 rows per page by default
             lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
             language: {
                 lengthMenu: `Show _MENU_ ${itemLabel}`,
                 select: {
                     rows: {
                         0: "",
                         1: `1 ${singularLabel} selected`,
                         _: `%d ${itemLabel} selected`
                     }
                 },
                 info: `Showing _START_ to _END_ of _TOTAL_ ${itemLabel}`,
                 infoFiltered: `(filtered from _MAX_ total ${itemLabel})`,
             },
             select: { style: "multi", selector: "td:not(:has(a))" },
             initComplete: function() {  // Check appropriate boxes
                 const api = this.api();
                 embeddedData.dataElements.forEach(rowName => {
                     const rowIndex = rowToIdx[rowName];
                     if (rowIndex !== undefined) {
                         api.row(rowIndex).select();
                     }
                 });
                 // Capture initial data element state
                 initialState.dataElements = new Set(embeddedData.dataElements);
             },
             drawCallback: function() {
                 updateSelectAllCheckbox(this.api());
             },
         });
 
         function updateSelectAllCheckbox(api) {
             const filteredCount = api.rows({ search: "applied" }).count();
             const selectedCount = api.rows({ search: "applied", selected: true }).count();
             $("#select-all")
                 .prop("checked", filteredCount > 0 && selectedCount === filteredCount)
                 .prop("indeterminate", selectedCount > 0 && selectedCount < filteredCount);
         }
-        table.on("select deselect", () => updateSelectAllCheckbox(table));
+        // Find the Display Mode dropdown rendered by C code
+        const visDropdown = document.querySelector(
+            'select[name="' + embeddedData.track + '"]');
+
+        // Track preferred non-hide visibility for auto-restore
+        let preferredVis = "full";
+        if (visDropdown && visDropdown.value !== "hide") {
+            preferredVis = visDropdown.value;
+        }
+
+        // Track previous selection count for detecting 0<->nonzero transitions
+        let prevSelCount = table.rows({selected: true}).count();
+
+        // Update preferredVis when user manually changes the dropdown
+        if (visDropdown) {
+            visDropdown.addEventListener("change", function() {
+                if (this.value !== "hide") {
+                    preferredVis = this.value;
+                }
+            });
+        }
+
         updateSelectAllCheckbox(table);  // set initial state after pre-selections
 
         // Create "show only selected" toggle in the toolbar
         const lengthDiv = document.querySelector(
             "#theMetaDataTable_wrapper .dt-length");
         const toggleWrapper = document.createElement("div");
         toggleWrapper.id = "selected-filter";
         const toggleLabel = document.createElement("label");
         toggleLabel.classList.add("toggle-switch");
         const toggleCheckbox = document.createElement("input");
         toggleCheckbox.type = "checkbox";
         toggleCheckbox.dataset.selectFilter = "true";
         toggleLabel.appendChild(toggleCheckbox);
         toggleLabel.appendChild(Object.assign(
             document.createElement("span"), {className: "toggle-slider"}));
         toggleWrapper.appendChild(toggleLabel);
         const toggleText = Object.assign(
             document.createElement("span"), {id: "selected-filter-text"});
         toggleWrapper.appendChild(toggleText);
         lengthDiv.appendChild(toggleWrapper);
 
+        // Disable toggle initially if no rows are selected
+        toggleCheckbox.disabled = (prevSelCount === 0);
+
         function updateSelectedText() {
             const selCount = table.rows({selected: true}).count();
             const totalCount = table.rows().count();
             toggleText.textContent =
                 `Show only selected ${itemLabel} (${selCount} of ${totalCount} selected)`;
         }
         updateSelectedText();
-        table.on("select deselect", updateSelectedText);
+
+        // Unified handler for selection changes
+        function onSelectionChanged() {
+            const selCount = table.rows({selected: true}).count();
+
+            // Disable toggle when nothing is selected; auto-uncheck if count hits 0
+            toggleCheckbox.disabled = (selCount === 0);
+            if (selCount === 0 && toggleCheckbox.checked) {
+                toggleCheckbox.checked = false;
+                table.draw();
+            }
+
+            // Auto-switch Display Mode on 0<->nonzero transitions
+            if (visDropdown) {
+                if (selCount === 0 && prevSelCount > 0) {
+                    visDropdown.value = "hide";
+                } else if (selCount > 0 && prevSelCount === 0) {
+                    visDropdown.value = preferredVis;
+                }
+            }
+
+            updateSelectAllCheckbox(table);
+            updateSelectedText();
+            prevSelCount = selCount;
+        }
+        table.on("select deselect", onSelectionChanged);
 
         // Create active-filters chip bar (hidden when empty)
         const activeFiltersDiv = document.createElement("div");
         activeFiltersDiv.id = "active-filters";
         activeFiltersDiv.style.display = "none";
         const tableEl = document.getElementById("theMetaDataTable");
         tableEl.parentNode.insertBefore(activeFiltersDiv, tableEl);
 
         // define inputs for search functionality for each column in the table
         const row = document.querySelector("#theMetaDataTable thead").insertRow();
         columns.forEach((col) => {
             const cell = row.insertCell();
             if (col.data === null) {
                 // left empty; toggle is now in the toolbar
             } else if (col.data && col.data.startsWith("__")) {
                 // no search box for double-underscore columns
             } else {
                 const input = document.createElement("input");
                 input.type = "text";
                 input.placeholder = "Search...";
                 input.style.width = "100%";
                 cell.appendChild(input);
             }
         });
 
         // behaviors for the column-based search functionality
         $("#theMetaDataTable thead input[type='text']")
             .on("keyup change", function () {
                 const dtColIdx = $(this).parent().index();
                 const colName = colNames[dtColIdx - 1];  // offset for checkbox col
                 if (this.value) {
                     textFilters.set(colName, this.value.toLowerCase());
                 } else {
                     textFilters.delete(colName);
                 }
                 table.column(dtColIdx).search(this.value).draw();
             });
         $.fn.dataTable.ext.search.push(function (_, data, dataIndex) {
             const filterInput =
                   document.querySelector("input[data-select-filter]");
             if (!filterInput?.checked) {  // If checkbox not checked, show all rows
                 return true;
             }
             // Otherwise, only show selected rows
             const row = table.row(dataIndex);
             return row.select && row.selected();
         });
         $("#selected-filter input[data-select-filter]")
             .on("change", function () { table.draw(); });
 
         // implement the 'select all' at the top of the checkbox column
         $("#select-all").closest("label").attr(
             "title", `Select all filtered ${itemLabel}`);
         $("#theMetaDataTable thead").on("click", "#select-all", function () {
             const rowIsChecked = this.checked;
             if (rowIsChecked) {
                 table.rows({ search: "applied" }).select();
             } else {
                 table.rows({ search: "applied" }).deselect();
             }
         });
         return table;
     }  // end initTable
 
 
     // Map of colName -> Map of unescapedValue -> spanElement, for dynamic counts
     const countSpans = new Map();
     // Filter state for cross-facet count computation
     const checkboxFilters = new Map();  // colName -> Set<string> (raw values)
     const textFilters = new Map();      // colName -> lowercase string
 
     function updateFacetCounts(metadata) {
         // For each facet, count values among rows that pass all OTHER filters
         // (excluding this facet's own checkbox filter). This way, unchecked
         // values show how many rows would be added if you checked them.
         for (const [facetCol, valMap] of countSpans) {
             const counts = new Map();  // lowercased value -> count
             for (const row of metadata) {
                 let passes = true;
                 for (const [col, valueSet] of checkboxFilters) {
                     if (col === facetCol) continue;
                     if (!valueSet.has(row[col]?.toLowerCase())) {
                         passes = false; break;
                     }
                 }
                 if (passes) {
                     for (const [col, text] of textFilters) {
                         if (!row[col]?.toLowerCase().includes(text)) {
                             passes = false; break;
                         }
                     }
                 }
                 if (passes) {
                     const val = row[facetCol]?.toLowerCase();
                     counts.set(val, (counts.get(val) ?? 0) + 1);
                 }
             }
             for (const [val, span] of valMap) {
                 span.textContent = `(${counts.get(val.toLowerCase()) ?? 0})`;
             }
         }
     }
 
     function initFilters(table, allData) {
         const { metadata, colorMap, colNames } = allData;
 
         // iterate once over entire data not separately per attribute
         // Case-insensitive: merge variants, keep first-seen casing as display form
         const possibleValues = {};  // key -> Map<lowerVal, [displayVal, count]>
         for (const entry of metadata) {
             for (const [key, val] of Object.entries(entry)) {
                 if (!possibleValues[key]) {
                     possibleValues[key] = new Map();
                 }
                 const map = possibleValues[key];
                 const lower = val.toLowerCase();
                 const existing = map.get(lower);
                 if (existing) {
                     existing[1]++;
                 } else {
                     map.set(lower, [val, 1]);
                 }
             }
         }
 
         let { maxCheckboxes, primaryKey } = embeddedData;
         if (maxCheckboxes === null || maxCheckboxes === undefined) {
             maxCheckboxes = DEFAULT_MAX_CHECKBOXES;
         }
         const excludeCheckboxes = [primaryKey];
 
         const filtersDiv = document.getElementById("filters");
         colNames.forEach((key, colIdx) => {
             // skip attributes if they should be excluded from checkbox sets
             if (excludeCheckboxes.includes(key) || key.startsWith("_")) {
                 return;
             }
 
             // possibleValues[key] is Map<lower, [displayVal, count]>; extract [displayVal, count]
             const sortedPossibleVals = Array.from(possibleValues[key].values());
             sortedPossibleVals.sort((a, b) => b[1] - a[1]);  // sort by count descending
 
             // Use 'maxCheckboxes' most frequent items (if they appear > 1 time)
             let topToShow = sortedPossibleVals
                 .filter(([val, count]) =>
                     val.trim().toUpperCase() !== "NA" && count > 1)
                 .slice(0, maxCheckboxes);
 
             // Any "other/Other/OTHER" entry will be put at the end
             let otherKey = null, otherValue = null;
             topToShow = topToShow.filter(([val, value]) => {
                 if (val.toLowerCase() === "other") {
                     otherKey = val;
                     otherValue = value;
                     return false;
                 }
                 return true;
             });
             if (otherValue !== null) {
                 topToShow.push([otherKey, otherValue]);
             }
             if (topToShow.length <= 1) {  // no point if there's only one group
                 excludeCheckboxes.push(key);
                 return;
             }
 
             // --- Build the facet group with collapsible structure ---
             const facetDiv = document.createElement("div");
             facetDiv.classList.add("facet-group");
 
             // Clickable heading that toggles collapse
             const heading = Object.assign(document.createElement("strong"), {
                 textContent: toTitleCase(key),
                 className: "facet-heading",
             });
             facetDiv.appendChild(heading);
 
             // Collapsible body: holds Clear button + all checkboxes
             const facetBody = document.createElement("div");
             facetBody.classList.add("facet-body");
 
             // Clear button — built here so it lives inside the collapsible body
             const clearBtn = document.createElement("button");
             clearBtn.textContent = "Clear";
             clearBtn.type = "button";
             facetBody.appendChild(clearBtn);
 
             // Build checkbox labels
             const cboxes = [];
             const rawValues = [];  // parallel to cboxes: unescaped values
             if (!countSpans.has(key)) countSpans.set(key, new Map());
             const colSpans = countSpans.get(key);
             topToShow.forEach(([val, count]) => {
                 const label = document.createElement("label");
                 const checkbox = document.createElement("input");
                 checkbox.type = "checkbox";
                 checkbox.value = escapeRegex(val);
                 label.appendChild(checkbox);
                 if (colorMap && key in colorMap) {
                     const colorBox = document.createElement("span");
                     colorBox.classList.add("color-box");
                     if (val in colorMap[key]) {
                         colorBox.style.backgroundColor = colorMap[key][val];
                     }
                     label.appendChild(colorBox);
                 }
                 label.appendChild(document.createTextNode(`${val} `));
                 const countSpan = document.createElement("span");
                 countSpan.textContent = `(${count})`;
                 label.appendChild(countSpan);
                 colSpans.set(val, countSpan);
                 facetBody.appendChild(label);
                 cboxes.push(checkbox);
                 rawValues.push(val);
             });
 
             facetDiv.appendChild(facetBody);
             filtersDiv.appendChild(facetDiv);
 
             // --- Wire up collapse toggle ---
             heading.addEventListener("click", () => {
                 const isCollapsed = facetBody.classList.toggle("collapsed");
                 heading.classList.toggle("collapsed", isCollapsed);
             });
 
             // --- Wire up checkbox filtering (same logic as before) ---
             // colIdx is the 0-based index into colNames; DataTable column is
             // colIdx + 1 because column 0 is the select-checkbox column.
             const dtColIdx = colIdx + 1;
             cboxes.forEach(cb => {
                 cb.addEventListener("change", () => {
                     const checked = cboxes.filter(c => c.checked).map(c => c.value);
                     const query = checked.length ? "^(" + checked.join("|") + ")$" : "";
                     // Track lowercased values for cross-facet counting
                     const checkedRaw = new Set();
                     cboxes.forEach((c, i) => {
                         if (c.checked) checkedRaw.add(rawValues[i].toLowerCase());
                     });
                     if (checkedRaw.size) {
                         checkboxFilters.set(key, checkedRaw);
                     } else {
                         checkboxFilters.delete(key);
                     }
                     table.column(dtColIdx).search(query, true, false).draw();
                     updateActiveFilters();
                 });
             });
 
             // --- Wire up Clear button ---
             clearBtn.addEventListener("click", () => {
                 cboxes.forEach(cb => cb.checked = false);
                 checkboxFilters.delete(key);
                 table.column(dtColIdx).search("", true, false).draw();
                 updateActiveFilters();
             });
         });  // done creating collapsible checkbox filters for each column
 
         // Update facet counts whenever the table is redrawn (filtering, search, etc.)
         table.on("draw", () => updateFacetCounts(metadata));
 
         return table;  // to chain calls
     }  // end initFilters
 
     function updateActiveFilters() {
         const container = document.getElementById("active-filters");
         if (!container) return;
         container.innerHTML = "";
 
         const checked = document.querySelectorAll(
             "#filters input[type='checkbox']:checked");
         if (checked.length === 0) {
             container.style.display = "none";
             return;
         }
 
         // Group by facet name
         const groups = new Map();
         checked.forEach(cb => {
             const facetGroup = cb.closest(".facet-group");
             if (!facetGroup) return;
             const heading = facetGroup.querySelector(".facet-heading");
             if (!heading) return;
             const facetName = heading.textContent.trim();
             // Get the display text from the label (strip the count suffix)
             const label = cb.parentElement;
             const labelText = label.textContent.trim();
             if (!groups.has(facetName)) groups.set(facetName, []);
             groups.get(facetName).push({ labelText, checkbox: cb });
         });
 
         groups.forEach((chips, facetName) => {
             const groupLabel = document.createElement("span");
             groupLabel.className = "filter-chip-group-label";
             groupLabel.textContent = facetName + ":";
             container.appendChild(groupLabel);
 
             chips.forEach(({ labelText, checkbox }) => {
                 const chip = document.createElement("span");
                 chip.className = "filter-chip";
                 chip.appendChild(document.createTextNode(labelText + " "));
                 const removeBtn = document.createElement("button");
                 removeBtn.className = "remove-chip";
                 removeBtn.type = "button";
                 removeBtn.textContent = "\u00d7";
                 removeBtn.addEventListener("click", () => {
                     checkbox.checked = false;
                     checkbox.dispatchEvent(new Event("change"));
                 });
                 chip.appendChild(removeBtn);
                 container.appendChild(chip);
             });
         });
 
         container.style.display = "flex";
     }
 
     function initSubmit(table) {  // logic for the submit event
         const { mdid, primaryKey } = embeddedData;  // mdid: metadata identifier
         const hasDataTypes = embeddedData.dataTypes &&
                              Object.keys(embeddedData.dataTypes).length > 0;
         document.getElementById("Submit").addEventListener("click", (submitBtnEvent) => {
             submitBtnEvent.preventDefault();  // hold the submit button event
 
             const currentDataTypes = [];
             if (hasDataTypes) {
                 // Get current data type selections
                 document.querySelectorAll("input.cbgroup").forEach(cb => {
                     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 =>
                     uriForUpdate.append(`${mdid}.de_now`, de));
             } else {
                 uriForUpdate.append(`${mdid}.de_now`, "");
             }
 
             if (hasDataTypes) {
             // Data types: was and now
                 if (initialState.dataTypes.size > 0) {
                     initialState.dataTypes.forEach(dt => {
                         uriForUpdate.append(`${mdid}.dt_was`, dt);});
                 } else {
                     uriForUpdate.append(`${mdid}.dt_was`, "");
                 }
                 if (currentDataTypes.length > 0) {
                     currentDataTypes.forEach(dt => {
                         uriForUpdate.append(`${mdid}.dt_now`, dt);});
                 } else {
                     uriForUpdate.append(`${mdid}.dt_now`, "");
                 }
             }
             // No ${mdid}.dt* variables indicates that the composite doesn't use data types
 
             updateVisibilities(uriForUpdate, submitBtnEvent);
         });
     }  // end initSubmit
 
     function initAll(dataForTable) {
         initDataTypeSelector();
         const table = initTable(dataForTable);
         initFilters(table, dataForTable);
         initSubmit(table);
     }
 
     function loadDataAndInit() {  // load data and call init functions
         const { mdid, primaryKey, metadataUrl, colorSettingsUrl, track } = embeddedData;
 
         const paramsFromUrl = new URLSearchParams(window.location.search);
         const hgsid = paramsFromUrl.get("hgsid");
         let fetchBody = `fileUrl=${metadataUrl}&track=${track}`;
         if (hgsid !== null) {
             fetchBody = fetchBody + `&hgsid=${hgsid}`;
         }
 
         // fetch file dynamically
         const fetchUrl = "/cgi-bin/hgTrackUi?" + fetchBody;
         const req = (fetchUrl.length > 2048 || embeddedData.udcTimeout) ?
             fetch("/cgi-bin/hgTrackUi", {
                 method: "POST",
                 headers: { "Content-Type": "application/x-www-form-urlencoded" },
                 body: fetchBody,
             })
             : fetch(fetchUrl, {
                 method: "GET",
                 headers: { "Content-Type": "application/x-www-form-urlencoded" },
             });
         req.then(response => {
             if (!response.ok) {  // a 404 will look like plain text
                 throw new Error(`HTTP Status: ${response.status}`);
             }
             return response.text();
             })
             .then(tsvText => {  // metadata table is a TSV file to parse
                 loadOptional(colorSettingsUrl, hgsid, track).then(colorMap => {
                     const rows = tsvText.trim().split("\n");
                     const colNames = rows[0].split("\t");
                     const metadata = rows.slice(1).map(row => {
                         const values = row.split("\t");
                         const obj = {};
                         colNames.forEach((attrib, i) => { obj[attrib] = values[i]; });
                         return obj;
                     });
                     const rowToIdx = Object.fromEntries(
                         metadata.map((row, i) => [row[primaryKey], i])
                     );
                     colorMap = isValidColorMap(colorMap) ? colorMap : null;
                     const freshData = { metadata, rowToIdx, colNames, colorMap };
 
                     initAll(freshData);
                 });
             })
             .catch(err => {
                 const table = document.getElementById("theMetaDataTable");
                 if (table) {
                     table.innerHTML =
                         `<tr><td style="padding:20px;color:#a00;">` +
                         `Error loading metadata: ${err.message}</td></tr>`;
                 }
             });
     }  // end loadDataAndInit
 
-    CSS_URLS.map(href =>  // load all the CSS
-        document.head.appendChild(Object.assign(
-            document.createElement("link"), { rel: "stylesheet", href })));
-
     document.addEventListener("keydown", e => {  // block accidental submit
         if (e.key === "Enter") { e.preventDefault(); e.stopPropagation(); }
     }, true);
 
-    // ADS: only load plugins if they are not already loaded
-    loadIfMissing(!$.fn.DataTable, DATATABLES_URL, () => {
-        loadIfMissing(!$.fn.dataTable.select, DATATABLES_SELECT_URL, () => {
     generateHTML();
     loadDataAndInit();
-        });
-    });
+
 });