From 204d56c808d6fd43a30cbf691cbd554d14db5d40 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Tue, 25 Nov 2025 16:33:21 -0500 Subject: [PATCH 01/17] Word wrap long legend entries --- html/gui/js/PlotlyChartWrapper.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/html/gui/js/PlotlyChartWrapper.js b/html/gui/js/PlotlyChartWrapper.js index 5473cdd448..a0df9d929b 100644 --- a/html/gui/js/PlotlyChartWrapper.js +++ b/html/gui/js/PlotlyChartWrapper.js @@ -101,6 +101,12 @@ XDMoD.utils.createChart = function (chartOptions, extraHandlers) { baseChartOptions.layout[axis].tickvals.forEach((tick, index, tickvals) => { tickvals[index] = moment.tz(tick, CCR.xdmod.timezone).format(); }); + + // Wrap long legends + baseChartOptions.data.forEach((trace) => { + trace.name = trace.name.replace(/(.{60})/g, '$1
'); + }); + } } From d665e0e7e24f6d5e2c90a73ed7c5db044d0ef095 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Wed, 3 Dec 2025 13:23:05 -0500 Subject: [PATCH 02/17] Add word wrap to legends --- .../Visualization/AggregateChart.php | 2 ++ .../Visualization/TimeseriesChart.php | 2 ++ html/gui/js/PlotlyChartWrapper.js | 13 +++++++------ html/gui/js/libraries/PlotlyUtilities.js | 15 ++++++++++++++- html/gui/js/modules/Usage.js | 2 +- .../js/modules/metric_explorer/MetricExplorer.js | 2 +- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/classes/DataWarehouse/Visualization/AggregateChart.php b/classes/DataWarehouse/Visualization/AggregateChart.php index 15215ea334..076bc6a160 100644 --- a/classes/DataWarehouse/Visualization/AggregateChart.php +++ b/classes/DataWarehouse/Visualization/AggregateChart.php @@ -1134,6 +1134,7 @@ public function configure( $trace = array_merge($trace, array( 'automargin'=> $data_description->display_type == 'pie' ? true : null, + 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => true @@ -1500,6 +1501,7 @@ protected function buildErrorDataSeries( // create the data series description: $error_trace = array_merge($trace, array( + 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => false diff --git a/classes/DataWarehouse/Visualization/TimeseriesChart.php b/classes/DataWarehouse/Visualization/TimeseriesChart.php index 67a7b982c0..e4f032de67 100644 --- a/classes/DataWarehouse/Visualization/TimeseriesChart.php +++ b/classes/DataWarehouse/Visualization/TimeseriesChart.php @@ -579,6 +579,7 @@ public function configure( } $trace = array( + 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => true @@ -877,6 +878,7 @@ public function configure( $visible = $data_description->visibility->{$dsn}; } $trendline_trace = array( + 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => false, diff --git a/html/gui/js/PlotlyChartWrapper.js b/html/gui/js/PlotlyChartWrapper.js index a0df9d929b..c8a0eac3cb 100644 --- a/html/gui/js/PlotlyChartWrapper.js +++ b/html/gui/js/PlotlyChartWrapper.js @@ -101,13 +101,14 @@ XDMoD.utils.createChart = function (chartOptions, extraHandlers) { baseChartOptions.layout[axis].tickvals.forEach((tick, index, tickvals) => { tickvals[index] = moment.tz(tick, CCR.xdmod.timezone).format(); }); - - // Wrap long legends - baseChartOptions.data.forEach((trace) => { - trace.name = trace.name.replace(/(.{60})/g, '$1
'); - }); - } + + // Wrap long legends + baseChartOptions.data.forEach((trace) => { + if (trace.oname) { + trace.name = trace.oname.replace(/(.{120})/g, '$1
'); + } + }); } const chart = Plotly.newPlot(baseChartOptions.renderTo, baseChartOptions.data, baseChartOptions.layout, configs); diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index c05e6cf9e3..d79e9acd69 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -353,9 +353,22 @@ function adjustSubtitle(layout, subtitleIndex, legendTopCenter, firstRender) { * @return {Object} update - Layout object passed to Plotly.relayout */ /* exported relayoutChart */ -function relayoutChart(chartDiv, adjHeight, firstRender = false, isExport = false) { +function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExport = false) { let update = {}; if (chartDiv._fullLayout.annotations.length > 0) { + // Wrap long titles based on width + const traceNameUpdates = { name: [] }; + const traceIndices = []; + const wordWrapLimit = parseInt((adjWidth / chartDiv.clientWidth) * 60); + const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); + chartDiv.data.forEach((trace, index) => { + if (trace.oname) { + traceNameUpdates.name.push(trace.oname.replace(regex, '$1
')); + traceIndices.push(index); + } + }); + Plotly.restyle(chartDiv, traceNameUpdates, traceIndices); + const topCenter = isTopLegend(chartDiv._fullLayout); const marginRight = chartDiv._fullLayout._size.r; const marginLeft = chartDiv._fullLayout._size.l; diff --git a/html/gui/js/modules/Usage.js b/html/gui/js/modules/Usage.js index 18426e80d8..309632d505 100644 --- a/html/gui/js/modules/Usage.js +++ b/html/gui/js/modules/Usage.js @@ -2612,7 +2612,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { const chartDiv = document.getElementById(this.chartId); if (chartDiv) { Plotly.relayout(this.chartId, { width: adjWidth, height: adjHeight }); - const update = relayoutChart(chartDiv, adjHeight, false); + const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); Plotly.relayout(this.chartId, update); } } diff --git a/html/gui/js/modules/metric_explorer/MetricExplorer.js b/html/gui/js/modules/metric_explorer/MetricExplorer.js index 4985c9e1c9..80cea609b3 100644 --- a/html/gui/js/modules/metric_explorer/MetricExplorer.js +++ b/html/gui/js/modules/metric_explorer/MetricExplorer.js @@ -6335,7 +6335,7 @@ Ext.extend(XDMoD.Module.MetricExplorer, XDMoD.PortalModule, { const chartDiv = document.getElementById(`plotly-panel${this.id}`); if (chartDiv) { Plotly.relayout(`plotly-panel${this.id}`, { width: adjWidth, height: adjHeight }); - const update = relayoutChart(chartDiv, adjHeight, false); + const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); Plotly.relayout(`plotly-panel${this.id}`, update); } } //onResize From 76385cff7beb71611aead5a9914a27e01eac218e Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Thu, 4 Dec 2025 15:37:43 -0500 Subject: [PATCH 03/17] remove unneeded section --- html/gui/js/PlotlyChartWrapper.js | 7 ------- html/gui/js/libraries/PlotlyUtilities.js | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/html/gui/js/PlotlyChartWrapper.js b/html/gui/js/PlotlyChartWrapper.js index c8a0eac3cb..5473cdd448 100644 --- a/html/gui/js/PlotlyChartWrapper.js +++ b/html/gui/js/PlotlyChartWrapper.js @@ -102,13 +102,6 @@ XDMoD.utils.createChart = function (chartOptions, extraHandlers) { tickvals[index] = moment.tz(tick, CCR.xdmod.timezone).format(); }); } - - // Wrap long legends - baseChartOptions.data.forEach((trace) => { - if (trace.oname) { - trace.name = trace.oname.replace(/(.{120})/g, '$1
'); - } - }); } const chart = Plotly.newPlot(baseChartOptions.renderTo, baseChartOptions.data, baseChartOptions.layout, configs); diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index d79e9acd69..a8f4887b35 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -359,11 +359,11 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp // Wrap long titles based on width const traceNameUpdates = { name: [] }; const traceIndices = []; - const wordWrapLimit = parseInt((adjWidth / chartDiv.clientWidth) * 60); + const wordWrapLimit = Number.parseInt((adjWidth / chartDiv.clientWidth) * 60); const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); chartDiv.data.forEach((trace, index) => { if (trace.oname) { - traceNameUpdates.name.push(trace.oname.replace(regex, '$1
')); + traceNameUpdates.name.push(trace.oname.replaceAll(regex, '$1
')); traceIndices.push(index); } }); From 12c16f98087e622c3073f48252543d6c5e3cfda3 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Thu, 4 Dec 2025 15:41:09 -0500 Subject: [PATCH 04/17] add number base --- html/gui/js/libraries/PlotlyUtilities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index a8f4887b35..2af035addc 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -359,7 +359,7 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp // Wrap long titles based on width const traceNameUpdates = { name: [] }; const traceIndices = []; - const wordWrapLimit = Number.parseInt((adjWidth / chartDiv.clientWidth) * 60); + const wordWrapLimit = Number.parseInt((adjWidth / chartDiv.clientWidth) * 60, 10); const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); chartDiv.data.forEach((trace, index) => { if (trace.oname) { From 351cf5679a47bf89c874ae154dffe0143989ff5b Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Thu, 4 Dec 2025 16:17:07 -0500 Subject: [PATCH 05/17] Make legend wrapping more robust --- html/gui/js/libraries/PlotlyUtilities.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index 2af035addc..0b137d607c 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -359,7 +359,18 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp // Wrap long titles based on width const traceNameUpdates = { name: [] }; const traceIndices = []; - const wordWrapLimit = Number.parseInt((adjWidth / chartDiv.clientWidth) * 60, 10); + const chartRatioChange = adjWidth / chartDiv.clientWidth; + let characterLimit = 150; + if (adjWidth < 400) { + characterLimit = 20; + } else if (adjWidth < 650) { + characterLimit = 40; + } else if (adjWidth < 850) { + characterLimit = 70; + } else if (adjWidth < 1250) { + characterLimit = 100; + } + const wordWrapLimit = Number.parseInt(chartRatioChange * characterLimit, 10); const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); chartDiv.data.forEach((trace, index) => { if (trace.oname) { From cb0c293aebf43661d543e4c07802cb8a3ca7b2b5 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Fri, 5 Dec 2025 07:32:28 -0500 Subject: [PATCH 06/17] Add in missing steps --- classes/DataWarehouse/Access/Usage.php | 3 ++- html/gui/js/PlotlyChartWrapper.js | 4 ++-- html/gui/js/modules/Usage.js | 2 +- html/gui/js/modules/metric_explorer/MetricExplorer.js | 2 +- html/plotly_template.html | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/classes/DataWarehouse/Access/Usage.php b/classes/DataWarehouse/Access/Usage.php index 1c95a6c317..986e40466a 100644 --- a/classes/DataWarehouse/Access/Usage.php +++ b/classes/DataWarehouse/Access/Usage.php @@ -847,7 +847,8 @@ function ($drillTarget) { && $usageGroupBy !== 'none' ) { $rank = $meDataSeries['legendrank'] / 3; - $meDataSeries['name'] = "${rank}. " . $meDataSeries['name']; + $meDataSeries['name'] = "${rank}. " . $meDataSeries['name']; + $meDataSeries['oname'] = "${rank}. " . $meDataSeries['oname']; } } diff --git a/html/gui/js/PlotlyChartWrapper.js b/html/gui/js/PlotlyChartWrapper.js index 5473cdd448..f2027eebe1 100644 --- a/html/gui/js/PlotlyChartWrapper.js +++ b/html/gui/js/PlotlyChartWrapper.js @@ -133,8 +133,8 @@ XDMoD.utils.createChart = function (chartOptions, extraHandlers) { return; } - const update = relayoutChart(chartDiv, baseChartOptions.layout.height, true); - Plotly.relayout(baseChartOptions.renderTo, update); + const update = relayoutChart(chartDiv, baseChartOptions.layout.width, baseChartOptions.layout.height, true); + Plotly.update(baseChartOptions.renderTo, update.data, update.layout, update.traces); }); return chart; diff --git a/html/gui/js/modules/Usage.js b/html/gui/js/modules/Usage.js index 309632d505..d3b81fed15 100644 --- a/html/gui/js/modules/Usage.js +++ b/html/gui/js/modules/Usage.js @@ -2613,7 +2613,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { if (chartDiv) { Plotly.relayout(this.chartId, { width: adjWidth, height: adjHeight }); const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); - Plotly.relayout(this.chartId, update); + Plotly.update(this.chartId, update.data, update.layout, update.traces); } } } //onResize diff --git a/html/gui/js/modules/metric_explorer/MetricExplorer.js b/html/gui/js/modules/metric_explorer/MetricExplorer.js index 80cea609b3..9771978ab8 100644 --- a/html/gui/js/modules/metric_explorer/MetricExplorer.js +++ b/html/gui/js/modules/metric_explorer/MetricExplorer.js @@ -6336,7 +6336,7 @@ Ext.extend(XDMoD.Module.MetricExplorer, XDMoD.PortalModule, { if (chartDiv) { Plotly.relayout(`plotly-panel${this.id}`, { width: adjWidth, height: adjHeight }); const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); - Plotly.relayout(`plotly-panel${this.id}`, update); + Plotly.relayout(`plotly-panel${this.id}`, update.data, update.layout, update.traces); } } //onResize diff --git a/html/plotly_template.html b/html/plotly_template.html index 803d7e3416..9dc92444ce 100644 --- a/html/plotly_template.html +++ b/html/plotly_template.html @@ -113,8 +113,8 @@ if (chartOptions.layout.annotations.length === 0){ return; } - const update = relayoutChart(chartDiv, chartOptions.layout.height, true, true); - Plotly.relayout('container', update); + const update = relayoutChart(chartDiv, chartOptions.layout.width, chartOptions.layout.height, true, true); + Plotly.update('container', update.data, update.layout, update.traces); }); }); From 35a0ee829a034558f7f7b1c440df8c6aa9951c0d Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Fri, 5 Dec 2025 10:20:02 -0500 Subject: [PATCH 07/17] change plotly function call --- html/gui/js/libraries/PlotlyUtilities.js | 1 - html/gui/js/modules/metric_explorer/MetricExplorer.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index 0b137d607c..1a25ba4a4d 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -378,7 +378,6 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp traceIndices.push(index); } }); - Plotly.restyle(chartDiv, traceNameUpdates, traceIndices); const topCenter = isTopLegend(chartDiv._fullLayout); const marginRight = chartDiv._fullLayout._size.r; diff --git a/html/gui/js/modules/metric_explorer/MetricExplorer.js b/html/gui/js/modules/metric_explorer/MetricExplorer.js index 9771978ab8..0756964217 100644 --- a/html/gui/js/modules/metric_explorer/MetricExplorer.js +++ b/html/gui/js/modules/metric_explorer/MetricExplorer.js @@ -6336,7 +6336,7 @@ Ext.extend(XDMoD.Module.MetricExplorer, XDMoD.PortalModule, { if (chartDiv) { Plotly.relayout(`plotly-panel${this.id}`, { width: adjWidth, height: adjHeight }); const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); - Plotly.relayout(`plotly-panel${this.id}`, update.data, update.layout, update.traces); + Plotly.update(`plotly-panel${this.id}`, update.data, update.layout, update.traces); } } //onResize From 1f6a2a92a32f99b2a2473f3ebb69a95586d80920 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Fri, 5 Dec 2025 10:35:11 -0500 Subject: [PATCH 08/17] Add in missing code --- html/gui/js/libraries/PlotlyUtilities.js | 26 +++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index 1a25ba4a4d..60fcb97b87 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -378,6 +378,8 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp traceIndices.push(index); } }); + update.data = traceNameUpdates; + update.traces = traceIndices; const topCenter = isTopLegend(chartDiv._fullLayout); const marginRight = chartDiv._fullLayout._size.r; @@ -416,7 +418,7 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp const isPie = chartDiv._fullData.length > 0 && chartDiv._fullData[0].type === 'pie'; const subtitleUpdates = adjustSubtitle(chartDiv._fullLayout, subtitleIndex, topCenter, firstRender); - update = subtitleUpdates.chartUpdates; + update.layout = subtitleUpdates.chartUpdates; if (isPie && topCenter && subtitleUpdates.subtitleLineCount > 0) { extraShift -= 10; @@ -441,7 +443,7 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp // Observed inconsistency with margin when subtitle is one line long. Unsure of the cause. if (lineBreakCount > 0) { if (firstRender) { - update['margin.t'] = marginTop + (titleHeight * lineBreakCount); + update.layout['margin.t'] = marginTop + (titleHeight * lineBreakCount); } else if (subtitleUpdates.subtitleLineCount === 1) { marginTop = subtitleUpdates.chartUpdates['margin.t'] - (titleHeight * lineBreakCount); } else { @@ -450,8 +452,8 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp if (topCenter) { if (subtitleUpdates.subtitleLineCount > 0) { marginTop += subtitleHeight + 5; - update['legend.y'] -= 0.025; - update['margin.t'] += chartDiv._fullLayout.legend._height + subtitleHeight; + update.layout['legend.y'] -= 0.025; + update.layout['margin.t'] += chartDiv._fullLayout.legend._height + subtitleHeight; } else { marginTop -= chartDiv._fullLayout.legend._height / 2; } @@ -466,20 +468,20 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp if ((titleIndex === -1 || chartDiv._fullLayout.annotations[titleIndex].text.length === 0) && subtitleUpdates.subtitleLineCount === 0) { if (topCenter) { - update['legend.y'] = 1.0; + update.layout['legend.y'] = 1.0; } else { - update['margin.t'] = 10; + update.layout['margin.t'] = 10; } } const titleYShift = (marginTop + legendHeight) - titleHeight; if (titleIndex !== -1) { - update[`annotations[${titleIndex}].yshift`] = subtitleUpdates.subtitleLineCount >= 3 ? titleYShift + 5 : titleYShift; + update.layout[`annotations[${titleIndex}].yshift`] = subtitleUpdates.subtitleLineCount >= 3 ? titleYShift + 5 : titleYShift; } if (subtitleIndex !== -1) { - update[`annotations[${subtitleIndex}].yshift`] = titleYShift - (subtitleHeight * subtitleUpdates.subtitleLineCount); + update.layout[`annotations[${subtitleIndex}].yshift`] = titleYShift - (subtitleHeight * subtitleUpdates.subtitleLineCount); } const marginBottom = chartDiv._fullLayout._size.b; @@ -491,12 +493,12 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp const shiftYDown = marginBottom * -1; const exportShift = isExport ? 30 : 0; if (creditsIndex !== -1) { - update[`annotations[${creditsIndex}].yshift`] = shiftYDown; - update[`annotations[${creditsIndex}].xshift`] = marginRight - pieChartXShift - exportShift; + update.layout[`annotations[${creditsIndex}].yshift`] = shiftYDown; + update.layout[`annotations[${creditsIndex}].xshift`] = marginRight - pieChartXShift - exportShift; } if (restrictedIndex !== -1) { - update[`annotations[${restrictedIndex}].yshift`] = shiftYDown; - update[`annotations[${restrictedIndex}].xshift`] = (marginLeft - pieChartXShift - exportShift) * -1; + update.layout[`annotations[${restrictedIndex}].yshift`] = shiftYDown; + update.layout[`annotations[${restrictedIndex}].xshift`] = (marginLeft - pieChartXShift - exportShift) * -1; } } return update; From dab2daa68bc00532125422a8881811ba1c033a25 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Fri, 5 Dec 2025 10:38:00 -0500 Subject: [PATCH 09/17] Linter fix --- classes/DataWarehouse/Access/Usage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/DataWarehouse/Access/Usage.php b/classes/DataWarehouse/Access/Usage.php index 986e40466a..2751093d20 100644 --- a/classes/DataWarehouse/Access/Usage.php +++ b/classes/DataWarehouse/Access/Usage.php @@ -847,8 +847,8 @@ function ($drillTarget) { && $usageGroupBy !== 'none' ) { $rank = $meDataSeries['legendrank'] / 3; - $meDataSeries['name'] = "${rank}. " . $meDataSeries['name']; - $meDataSeries['oname'] = "${rank}. " . $meDataSeries['oname']; + $meDataSeries['name'] = "${rank}. " . $meDataSeries['name']; + $meDataSeries['oname'] = "${rank}. " . $meDataSeries['oname']; } } From 2feb60b5cd09986f9f2f8db8c27efe55c7324466 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Fri, 5 Dec 2025 17:32:12 -0500 Subject: [PATCH 10/17] Since we are not setting the name of the trace to anything we don't need 'oname'. --- classes/DataWarehouse/Access/Usage.php | 1 - classes/DataWarehouse/Visualization/AggregateChart.php | 2 -- classes/DataWarehouse/Visualization/TimeseriesChart.php | 2 -- html/gui/js/libraries/PlotlyUtilities.js | 6 ++---- 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/classes/DataWarehouse/Access/Usage.php b/classes/DataWarehouse/Access/Usage.php index 2751093d20..1c95a6c317 100644 --- a/classes/DataWarehouse/Access/Usage.php +++ b/classes/DataWarehouse/Access/Usage.php @@ -848,7 +848,6 @@ function ($drillTarget) { ) { $rank = $meDataSeries['legendrank'] / 3; $meDataSeries['name'] = "${rank}. " . $meDataSeries['name']; - $meDataSeries['oname'] = "${rank}. " . $meDataSeries['oname']; } } diff --git a/classes/DataWarehouse/Visualization/AggregateChart.php b/classes/DataWarehouse/Visualization/AggregateChart.php index 076bc6a160..15215ea334 100644 --- a/classes/DataWarehouse/Visualization/AggregateChart.php +++ b/classes/DataWarehouse/Visualization/AggregateChart.php @@ -1134,7 +1134,6 @@ public function configure( $trace = array_merge($trace, array( 'automargin'=> $data_description->display_type == 'pie' ? true : null, - 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => true @@ -1501,7 +1500,6 @@ protected function buildErrorDataSeries( // create the data series description: $error_trace = array_merge($trace, array( - 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => false diff --git a/classes/DataWarehouse/Visualization/TimeseriesChart.php b/classes/DataWarehouse/Visualization/TimeseriesChart.php index e4f032de67..67a7b982c0 100644 --- a/classes/DataWarehouse/Visualization/TimeseriesChart.php +++ b/classes/DataWarehouse/Visualization/TimeseriesChart.php @@ -579,7 +579,6 @@ public function configure( } $trace = array( - 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => true @@ -878,7 +877,6 @@ public function configure( $visible = $data_description->visibility->{$dsn}; } $trendline_trace = array( - 'oname' => $lookupDataSeriesName, 'name' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => false, diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index 60fcb97b87..3190e02637 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -373,10 +373,8 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp const wordWrapLimit = Number.parseInt(chartRatioChange * characterLimit, 10); const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); chartDiv.data.forEach((trace, index) => { - if (trace.oname) { - traceNameUpdates.name.push(trace.oname.replaceAll(regex, '$1
')); - traceIndices.push(index); - } + traceNameUpdates.name.push(trace.name.replaceAll(regex, '$1
')); + traceIndices.push(index); }); update.data = traceNameUpdates; update.traces = traceIndices; From 339d0924257742bc60fc0de4d72876c991208b64 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Fri, 5 Dec 2025 19:00:26 -0500 Subject: [PATCH 11/17] shouldn't have to specify traces because it add them all already --- html/gui/js/PlotlyChartWrapper.js | 2 +- html/gui/js/libraries/PlotlyUtilities.js | 5 +---- html/gui/js/modules/Usage.js | 2 +- html/gui/js/modules/metric_explorer/MetricExplorer.js | 2 +- html/plotly_template.html | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/html/gui/js/PlotlyChartWrapper.js b/html/gui/js/PlotlyChartWrapper.js index f2027eebe1..957f436779 100644 --- a/html/gui/js/PlotlyChartWrapper.js +++ b/html/gui/js/PlotlyChartWrapper.js @@ -134,7 +134,7 @@ XDMoD.utils.createChart = function (chartOptions, extraHandlers) { } const update = relayoutChart(chartDiv, baseChartOptions.layout.width, baseChartOptions.layout.height, true); - Plotly.update(baseChartOptions.renderTo, update.data, update.layout, update.traces); + Plotly.update(baseChartOptions.renderTo, update.data, update.layout); }); return chart; diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index 3190e02637..7fadc730c7 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -358,7 +358,6 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp if (chartDiv._fullLayout.annotations.length > 0) { // Wrap long titles based on width const traceNameUpdates = { name: [] }; - const traceIndices = []; const chartRatioChange = adjWidth / chartDiv.clientWidth; let characterLimit = 150; if (adjWidth < 400) { @@ -372,12 +371,10 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp } const wordWrapLimit = Number.parseInt(chartRatioChange * characterLimit, 10); const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); - chartDiv.data.forEach((trace, index) => { + chartDiv.data.forEach((trace) => { traceNameUpdates.name.push(trace.name.replaceAll(regex, '$1
')); - traceIndices.push(index); }); update.data = traceNameUpdates; - update.traces = traceIndices; const topCenter = isTopLegend(chartDiv._fullLayout); const marginRight = chartDiv._fullLayout._size.r; diff --git a/html/gui/js/modules/Usage.js b/html/gui/js/modules/Usage.js index d3b81fed15..4d47b47585 100644 --- a/html/gui/js/modules/Usage.js +++ b/html/gui/js/modules/Usage.js @@ -2613,7 +2613,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { if (chartDiv) { Plotly.relayout(this.chartId, { width: adjWidth, height: adjHeight }); const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); - Plotly.update(this.chartId, update.data, update.layout, update.traces); + Plotly.update(this.chartId, update.data, update.layout); } } } //onResize diff --git a/html/gui/js/modules/metric_explorer/MetricExplorer.js b/html/gui/js/modules/metric_explorer/MetricExplorer.js index 0756964217..99c5d8a13a 100644 --- a/html/gui/js/modules/metric_explorer/MetricExplorer.js +++ b/html/gui/js/modules/metric_explorer/MetricExplorer.js @@ -6336,7 +6336,7 @@ Ext.extend(XDMoD.Module.MetricExplorer, XDMoD.PortalModule, { if (chartDiv) { Plotly.relayout(`plotly-panel${this.id}`, { width: adjWidth, height: adjHeight }); const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); - Plotly.update(`plotly-panel${this.id}`, update.data, update.layout, update.traces); + Plotly.update(`plotly-panel${this.id}`, update.data, update.layout); } } //onResize diff --git a/html/plotly_template.html b/html/plotly_template.html index 9dc92444ce..7657b0c99c 100644 --- a/html/plotly_template.html +++ b/html/plotly_template.html @@ -114,7 +114,7 @@ return; } const update = relayoutChart(chartDiv, chartOptions.layout.width, chartOptions.layout.height, true, true); - Plotly.update('container', update.data, update.layout, update.traces); + Plotly.update('container', update.data, update.layout); }); }); From 22798e89773b10c8d9eb0c116ca92f260a439f62 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Tue, 9 Dec 2025 10:29:28 -0500 Subject: [PATCH 12/17] Sonar adjustment --- html/gui/js/libraries/PlotlyUtilities.js | 2 +- user_manual_builder/conf.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index 7fadc730c7..f7879923ff 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -463,7 +463,7 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp if ((titleIndex === -1 || chartDiv._fullLayout.annotations[titleIndex].text.length === 0) && subtitleUpdates.subtitleLineCount === 0) { if (topCenter) { - update.layout['legend.y'] = 1.0; + update.layout['legend.y'] = 1; } else { update.layout['margin.t'] = 10; } diff --git a/user_manual_builder/conf.py b/user_manual_builder/conf.py index 3f97182096..8f91c25fac 100644 --- a/user_manual_builder/conf.py +++ b/user_manual_builder/conf.py @@ -11,9 +11,9 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'XDMoD Manual' -copyright = ' University at Buffalo Center for Computational Research' +copyright = '2025 University at Buffalo Center for Computational Research' author = 'UB CCR' -release = '' +release = '11.5.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration From 26d783cc59254c3b833b24b88b072535b26817ff Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Tue, 9 Dec 2025 10:30:23 -0500 Subject: [PATCH 13/17] Remove extra code --- user_manual_builder/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_manual_builder/conf.py b/user_manual_builder/conf.py index 8f91c25fac..3f97182096 100644 --- a/user_manual_builder/conf.py +++ b/user_manual_builder/conf.py @@ -11,9 +11,9 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'XDMoD Manual' -copyright = '2025 University at Buffalo Center for Computational Research' +copyright = ' University at Buffalo Center for Computational Research' author = 'UB CCR' -release = '11.5.0' +release = '' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration From 61b74b0a25d384de32b8e9a05b1b09a860158513 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Mon, 12 Jan 2026 17:24:54 -0500 Subject: [PATCH 14/17] Add missing change from merge conflict --- html/gui/js/PlotlyChartWrapper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/gui/js/PlotlyChartWrapper.js b/html/gui/js/PlotlyChartWrapper.js index f2cc0a4dec..b6b4208940 100644 --- a/html/gui/js/PlotlyChartWrapper.js +++ b/html/gui/js/PlotlyChartWrapper.js @@ -136,7 +136,7 @@ XDMoD.utils.createChart = function (chartOptions, extraHandlers) { } const update = relayoutChart(chartDiv, baseChartOptions.layout.width, baseChartOptions.layout.height, true, baseChartOptions.isExport); - Plotly.relayout(baseChartOptions.renderTo, update); + Plotly.update(baseChartOptions.renderTo, update.data, update.layout); }); return chart; From 55c6079650b3a60f02ee51dccd1bfc90010ed5be Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Tue, 13 Jan 2026 15:18:05 -0500 Subject: [PATCH 15/17] Add back code with oname, merging this branch with main after Plotly Export Update caused some issues --- classes/DataWarehouse/Access/Usage.php | 1 + classes/DataWarehouse/Visualization/AggregateChart.php | 2 ++ classes/DataWarehouse/Visualization/TimeseriesChart.php | 2 ++ html/gui/js/PlotlyChartWrapper.js | 2 +- html/gui/js/libraries/PlotlyUtilities.js | 9 +++++++-- html/gui/js/modules/Usage.js | 2 +- html/gui/js/modules/metric_explorer/MetricExplorer.js | 2 +- 7 files changed, 15 insertions(+), 5 deletions(-) diff --git a/classes/DataWarehouse/Access/Usage.php b/classes/DataWarehouse/Access/Usage.php index 1c95a6c317..2751093d20 100644 --- a/classes/DataWarehouse/Access/Usage.php +++ b/classes/DataWarehouse/Access/Usage.php @@ -848,6 +848,7 @@ function ($drillTarget) { ) { $rank = $meDataSeries['legendrank'] / 3; $meDataSeries['name'] = "${rank}. " . $meDataSeries['name']; + $meDataSeries['oname'] = "${rank}. " . $meDataSeries['oname']; } } diff --git a/classes/DataWarehouse/Visualization/AggregateChart.php b/classes/DataWarehouse/Visualization/AggregateChart.php index 15215ea334..fcb216e319 100644 --- a/classes/DataWarehouse/Visualization/AggregateChart.php +++ b/classes/DataWarehouse/Visualization/AggregateChart.php @@ -1135,6 +1135,7 @@ public function configure( $trace = array_merge($trace, array( 'automargin'=> $data_description->display_type == 'pie' ? true : null, 'name' => $lookupDataSeriesName, + 'oname' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => true ), @@ -1501,6 +1502,7 @@ protected function buildErrorDataSeries( // create the data series description: $error_trace = array_merge($trace, array( 'name' => $lookupDataSeriesName, + 'oname' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => false ), diff --git a/classes/DataWarehouse/Visualization/TimeseriesChart.php b/classes/DataWarehouse/Visualization/TimeseriesChart.php index 67a7b982c0..1a710dce93 100644 --- a/classes/DataWarehouse/Visualization/TimeseriesChart.php +++ b/classes/DataWarehouse/Visualization/TimeseriesChart.php @@ -580,6 +580,7 @@ public function configure( $trace = array( 'name' => $lookupDataSeriesName, + 'oname' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => true ), @@ -878,6 +879,7 @@ public function configure( } $trendline_trace = array( 'name' => $lookupDataSeriesName, + 'oname' => $lookupDataSeriesName, 'meta' => array( 'primarySeries' => false, 'trendlineSeries' => true diff --git a/html/gui/js/PlotlyChartWrapper.js b/html/gui/js/PlotlyChartWrapper.js index b6b4208940..f728d5c58e 100644 --- a/html/gui/js/PlotlyChartWrapper.js +++ b/html/gui/js/PlotlyChartWrapper.js @@ -136,7 +136,7 @@ XDMoD.utils.createChart = function (chartOptions, extraHandlers) { } const update = relayoutChart(chartDiv, baseChartOptions.layout.width, baseChartOptions.layout.height, true, baseChartOptions.isExport); - Plotly.update(baseChartOptions.renderTo, update.data, update.layout); + Plotly.update(baseChartOptions.renderTo, update.data, update.layout, update.traces); }); return chart; diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index f7879923ff..d163403dc1 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -358,6 +358,7 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp if (chartDiv._fullLayout.annotations.length > 0) { // Wrap long titles based on width const traceNameUpdates = { name: [] }; + const traceIndices = []; const chartRatioChange = adjWidth / chartDiv.clientWidth; let characterLimit = 150; if (adjWidth < 400) { @@ -371,10 +372,14 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp } const wordWrapLimit = Number.parseInt(chartRatioChange * characterLimit, 10); const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); - chartDiv.data.forEach((trace) => { - traceNameUpdates.name.push(trace.name.replaceAll(regex, '$1
')); + chartDiv.data.forEach((trace, index) => { + if (trace.oname) { + traceNameUpdates.name.push(trace.oname.replaceAll(regex, '$1
')); + traceIndices.push(index); + } }); update.data = traceNameUpdates; + update.traces = traceIndices; const topCenter = isTopLegend(chartDiv._fullLayout); const marginRight = chartDiv._fullLayout._size.r; diff --git a/html/gui/js/modules/Usage.js b/html/gui/js/modules/Usage.js index 4d47b47585..d3b81fed15 100644 --- a/html/gui/js/modules/Usage.js +++ b/html/gui/js/modules/Usage.js @@ -2613,7 +2613,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { if (chartDiv) { Plotly.relayout(this.chartId, { width: adjWidth, height: adjHeight }); const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); - Plotly.update(this.chartId, update.data, update.layout); + Plotly.update(this.chartId, update.data, update.layout, update.traces); } } } //onResize diff --git a/html/gui/js/modules/metric_explorer/MetricExplorer.js b/html/gui/js/modules/metric_explorer/MetricExplorer.js index 99c5d8a13a..0756964217 100644 --- a/html/gui/js/modules/metric_explorer/MetricExplorer.js +++ b/html/gui/js/modules/metric_explorer/MetricExplorer.js @@ -6336,7 +6336,7 @@ Ext.extend(XDMoD.Module.MetricExplorer, XDMoD.PortalModule, { if (chartDiv) { Plotly.relayout(`plotly-panel${this.id}`, { width: adjWidth, height: adjHeight }); const update = relayoutChart(chartDiv, adjWidth, adjHeight, false); - Plotly.update(`plotly-panel${this.id}`, update.data, update.layout); + Plotly.update(`plotly-panel${this.id}`, update.data, update.layout, update.traces); } } //onResize From 6063b1a843a5527ad44519258b8c0114a00470bb Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Wed, 14 Jan 2026 16:20:16 -0500 Subject: [PATCH 16/17] Adjust regular expression to not break in the middle of words. However, if a single word is over the character limit then we must hyphenate that word --- html/gui/js/libraries/PlotlyUtilities.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index d163403dc1..fe0cd6a91b 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -371,10 +371,20 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp characterLimit = 100; } const wordWrapLimit = Number.parseInt(chartRatioChange * characterLimit, 10); - const regex = new RegExp(`(.{${wordWrapLimit}})`, 'g'); + const regex = new RegExp(`(?![^\\n]{1,${wordWrapLimit}}$)(?:([^\\n]{1,${wordWrapLimit}})\\s|([^\\n]{${wordWrapLimit}}))`,'g'); + chartDiv.data.forEach((trace, index) => { if (trace.oname) { - traceNameUpdates.name.push(trace.oname.replaceAll(regex, '$1
')); + traceNameUpdates.name.push( + // Hardwrap is when we have to break middle of a word (add hyphen) + // Otherwise we are soft word wrapping and will break on whitespace. + trace.oname.replaceAll(regex, (match, softWrap, hardWrap) => { + if (hardWrap) { + return hardWrap + '-
'; + } + return softWrap + '
'; + }) + ); traceIndices.push(index); } }); From 00d0f661967aa12ebf12651e91f85ec60a74fc33 Mon Sep 17 00:00:00 2001 From: Andrew Stoltman Date: Wed, 14 Jan 2026 16:25:03 -0500 Subject: [PATCH 17/17] linter fix --- html/gui/js/libraries/PlotlyUtilities.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/html/gui/js/libraries/PlotlyUtilities.js b/html/gui/js/libraries/PlotlyUtilities.js index fe0cd6a91b..80d74721e0 100644 --- a/html/gui/js/libraries/PlotlyUtilities.js +++ b/html/gui/js/libraries/PlotlyUtilities.js @@ -354,7 +354,7 @@ function adjustSubtitle(layout, subtitleIndex, legendTopCenter, firstRender) { */ /* exported relayoutChart */ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExport = false) { - let update = {}; + const update = {}; if (chartDiv._fullLayout.annotations.length > 0) { // Wrap long titles based on width const traceNameUpdates = { name: [] }; @@ -371,7 +371,7 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp characterLimit = 100; } const wordWrapLimit = Number.parseInt(chartRatioChange * characterLimit, 10); - const regex = new RegExp(`(?![^\\n]{1,${wordWrapLimit}}$)(?:([^\\n]{1,${wordWrapLimit}})\\s|([^\\n]{${wordWrapLimit}}))`,'g'); + const regex = new RegExp(`(?![^\\n]{1,${wordWrapLimit}}$)(?:([^\\n]{1,${wordWrapLimit}})\\s|([^\\n]{${wordWrapLimit}}))`, 'g'); chartDiv.data.forEach((trace, index) => { if (trace.oname) { @@ -380,9 +380,9 @@ function relayoutChart(chartDiv, adjWidth, adjHeight, firstRender = false, isExp // Otherwise we are soft word wrapping and will break on whitespace. trace.oname.replaceAll(regex, (match, softWrap, hardWrap) => { if (hardWrap) { - return hardWrap + '-
'; + return `${hardWrap}-
`; } - return softWrap + '
'; + return `${softWrap}
`; }) ); traceIndices.push(index);