668110379c7c18bc75dd2db8747821b387fb28d6
chmalee
  Fri Jan 27 10:22:57 2023 -0800
Redraw barChart svgs on the client so the column widths can be wide enough for labels, refs #28439

diff --git src/hg/js/hgc.js src/hg/js/hgc.js
index 64df3d1..3c7f630 100644
--- src/hg/js/hgc.js
+++ src/hg/js/hgc.js
@@ -1,168 +1,218 @@
 // "use strict";
 
 // insert a node after the reference node
 function insertAfter(newNode, referenceNode) {
     referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
 }
 
 // a generic table with no headers or sortable rows:
 function makeGenericTable(data) {
     var table = document.createElement("table");
     table.classList.add("jsonTable");
     var val, i, j;
     var keys = Object.keys(data);
     for (i = 0; i < keys.length; i++) {
         key = keys[i];
         val = data[key];
         var row = table.insertRow();
         if (Array.isArray(val)) {
             for (j = 0; j < val.length; j++) {
                 cell = row.insertCell();
                 if (j < val.length) {
                     cell.appendChild(document.createTextNode(val[j]));
                 } else {
                     cell.appendChild(document.createTextNode(""));
                 }
             }
         } else {
             var rowLabel = document.createTextNode(key);
             var labelCell = row.insertCell();
             var cell;
             labelCell.appendChild(rowLabel);
 
             if (typeof(val) == "object" && !Array.isArray(val)) {
                 cell = row.insertCell();
                 cell.appendChild(makeGenericTable(val));
             } else {
                 if (val === null) {
                     cell = row.insertCell();
                     cell.appendChild(document.createTextNode(""));
                 } else {
                     cell = row.insertCell();
                     cell.appendChild(document.createTextNode(val));
                 }
             }
         }
     }
     return table;
 }
 
 // make the vep table, which is limited to 2x2 for the vep fields
 function makeVepTable(data) {
     var table = document.createElement("table");
     table.classList.add("jsonTable");
     var gene;
     for (gene in data) {
         var geneRow = table.insertRow();
         var newLabel = document.createTextNode(gene);
         var labelCell = geneRow.insertCell();
         labelCell.appendChild(newLabel);
 
         // a 2x2 sub table for the vep info:
         var subTable = document.createElement("table");
         subTable.classList.add("jsonTable");
         var annot;
         for (annot in data[gene]) {
             var annotRow = subTable.insertRow();
             var annotLabel = document.createTextNode(annot);
             var annotCell = annotRow.insertCell();
             annotCell.appendChild(annotLabel);
             annotCell = annotRow.insertCell();
             var dataLabel = document.createTextNode(data[gene][annot].join(", "));
             annotCell.appendChild(dataLabel);
         }
         labelCell = geneRow.insertCell();
         labelCell.appendChild(subTable);
     }
     return table;
 }
 
 // make the pop frequencies or haplotype frequencies table
 function makePopTable(data) {
     var table = document.createElement("table");
     table.classList.add("jsonTable");
     var pop;
     var thead, tfoot, tbody;
     for (pop in data) {
         if (pop === "Populations" || pop === "Haplogroup") {
             thead = table.createTHead();
             theadRow = thead.insertRow();
             th = theadRow.insertCell();
             th.appendChild(document.createTextNode(pop));
             var header;
             for (header in data[pop]) {
                 th = theadRow.insertCell();
                 th.appendChild(document.createTextNode(data[pop][header]));
             }
         } else if (pop === "Total") {
             tfoot = table.createTFoot();
             tfootRow = tfoot.insertRow();
             th = tfootRow.insertCell();
             th.appendChild(document.createTextNode(pop));
             var footer;
             for (footer in data[pop]) {
                 th = tfootRow.insertCell();
                 th.appendChild(document.createTextNode(data[pop][footer]));
             }
         } else {
             if (table.tBodies.length === 0) {
                 tbody = table.createTBody();
             }
             var popRow = tbody.insertRow();
             var newLabel = document.createTextNode(pop);
             var labelCell = popRow.insertCell();
             labelCell.appendChild(newLabel);
 
             // a 5x5 sub table for the population freqs or 6x6 for haplotypes:
             var vals;
             for (vals in data[pop]) {
                 var valsLabel = document.createTextNode(data[pop][vals]);
                 var valsCell = popRow.insertCell();
                 valsCell.appendChild(valsLabel);
             }
         }
     }
     return table;
 }
 
 // turn a json object into an html table
 function dataToTable(label, data) {
     var subTable = document.createElement("table");
     subTable.classList.add("jsonTable");
     var subRow = subTable.insertRow();
     var newTableNode;
     if (label === "Variant Effect Predictor")
         newTableNode  = makeVepTable(data);
     else if (label === "Population Frequencies" || label === "Haplotype Frequencies")
         newTableNode  = makePopTable(data);
     else
         newTableNode  = makeGenericTable(data);
     return newTableNode ;
 }
 
 // on page load initialize VEP, Population Frequency and Haplotype Tables
 // for gnomAD v3.1.1 track
 $(document).ready(function() {
-    if (_jsonHgcLabels !== null) {
+    if ($("#svgTable") !== null) {
+        // redraw the svg with appropriate widths for all columns
+        // swatchWidth and columnSpacer are taken from svgBarChart() in hgc/barChartClick.c
+        // they should probably be dynamically determined
+        var swatchWidth = 20.0;
+        var columnSpacer = 4.0;
+        var maxSampleWidth = 0.0;
+
+        // determine the size taken up by the sample names
+        $(".sampleLabel").each(function(s) {
+            if ((sampleLength = this.getComputedTextLength()) >= maxSampleWidth) {
+                maxSampleWidth = sampleLength;
+            }
+        });
+
+        // determine the size taken up by the 'N' counts
+        var maxStatsWidth = 0.0;
+        $(".statsLabel").each(function(s) {
+            if ((statWidth = this.getComputedTextLength()) >= maxStatsWidth) {
+                maxStatsWidth = statWidth;
+            }
+        });
+
+        // the stat is right aligned so take into account it's width as well
+        statsRightOffset = swatchWidth + maxSampleWidth + (2 * columnSpacer) + maxStatsWidth;
+
+        // The white band that separates every other row needs to be resized
+        $(".sampleBand").each(function(s) {
+            this.setAttribute("width", statsRightOffset - swatchWidth);
+        });
+
+        // now move the stat number
+        $(".statsLabel").each(function(s) {
+            this.setAttribute("x", statsRightOffset);
+        });
+
+        // now shift the actual bars (plus value) over if necessary
+        $(".valueLabel").each(function(s) {
+            barName = "#bar" + s;
+            var barWidth = 0;
+            var newX = statsRightOffset + (2 * columnSpacer);
+            if ($(barName).length > 0) {
+                barWidth = parseInt($(barName)[0].getAttribute("width"));
+                $(barName)[0].setAttribute("x", newX);
+                this.setAttribute("x", newX + barWidth + 2 * columnSpacer);
+            } else { // the header label only
+                this.setAttribute("x", newX + barWidth);
+            }
+        });
+    }
+    if (typeof _jsonHgcLabels !== "undefined") {
         var obj, o;
         for (obj in _jsonHgcLabels) {
             // build up the new table:
             var newTable = document.createElement("table");
             var newRow = newTable.insertRow();
             var newCell = newRow.insertCell();
             var label = _jsonHgcLabels[obj].label;
             var data = _jsonHgcLabels[obj].data;
             var newText = document.createTextNode(label);
             newCell.appendChild(newText);
             newCell = newRow.insertCell();
             newCell.appendChild(dataToTable(label, data));
             // find the last details table and add a new table on:
             var currTbl = $(".bedExtraTbl");
             l = currTbl.length;
             var last = currTbl[l-1];
             insertAfter(newTable, last);
             newTable.classList.add("bedExtraTbl");
             last.parentNode.insertBefore(document.createElement("br"), newTable);
         }
     }
 });