1(function () { 2 "use strict"; 3 4 var STORAGE_KEY = "ventoryone-theme"; 5 var DARK_THEME = "dark"; 6 var LIGHT_THEME = "light"; 7 var THEME_CHANGE_EVENT = "ventoryone:themechange"; 8 9 function getStoredTheme() { 10 try { 11 var storedTheme = window.localStorage.getItem(STORAGE_KEY); 12 return storedTheme === DARK_THEME || storedTheme === LIGHT_THEME ? storedTheme : null; 13 } catch (error) { 14 return null; 15 } 16 } 17 18 function storeTheme(theme) { 19 try { 20 window.localStorage.setItem(STORAGE_KEY, theme); 21 } catch (error) { 22 // The applied theme remains usable for the current page when storage is unavailable. 23 } 24 } 25 26 function getCurrentTheme() { 27 return document.documentElement.dataset.theme === DARK_THEME ? DARK_THEME : LIGHT_THEME; 28 } 29 30 function getThemeColors() { 31 var styles = window.getComputedStyle(document.documentElement); 32 33 return { 34 text: styles.getPropertyValue("--vo-chart-text").trim(), 35 mutedText: styles.getPropertyValue("--vo-chart-muted-text").trim(), 36 grid: styles.getPropertyValue("--vo-chart-grid").trim(), 37 tooltipBackground: styles.getPropertyValue("--vo-chart-tooltip-background").trim(), 38 tooltipBorder: styles.getPropertyValue("--vo-chart-tooltip-border").trim(), 39 }; 40 } 41 42 function applyLegacyScaleTheme(scale, colors) { 43 if (!scale) { 44 return; 45 } 46 47 scale.gridLines = scale.gridLines || {}; 48 scale.gridLines.color = colors.grid; 49 scale.gridLines.zeroLineColor = colors.grid; 50 scale.ticks = scale.ticks || {}; 51 scale.ticks.fontColor = colors.mutedText; 52 53 if (scale.scaleLabel) { 54 scale.scaleLabel.fontColor = colors.text; 55 } 56 } 57 58 // Modern (Chart.js v3+) options are a resolver proxy: replacing a node 59 // (scale.grid = scale.grid || {}) corrupts it and later throws 60 // "t.startsWith is not a function". Only ever assign leaf values. 61 function assignColor(target, key, value) { 62 if (target && typeof value === "string" && value !== "") { 63 target[key] = value; 64 } 65 } 66 67 function applyModernScaleTheme(scale, colors) { 68 if (!scale) { 69 return; 70 } 71 72 assignColor(scale.grid, "color", colors.grid); 73 assignColor(scale.border, "color", colors.grid); 74 assignColor(scale.ticks, "color", colors.mutedText); 75 assignColor(scale.title, "color", colors.text); 76 } 77 78 function applyThemeToChartOptions(chart, colors) { 79 var options = chart.options || {}; 80 var scales = options.scales || {}; 81 82 ["xAxes", "yAxes"].forEach(function (axisName) { 83 (scales[axisName] || []).forEach(function (scale) { 84 applyLegacyScaleTheme(scale, colors); 85 }); 86 }); 87 88 Object.keys(scales).forEach(function (axisName) { 89 if (axisName !== "xAxes" && axisName !== "yAxes") { 90 applyModernScaleTheme(scales[axisName], colors); 91 } 92 }); 93 94 // Legacy (v2) nodes. 95 if (options.legend && options.legend.labels) { 96 assignColor(options.legend.labels, "fontColor", colors.text); 97 } 98 if (options.tooltips) { 99 assignColor(options.tooltips, "backgroundColor", colors.tooltipBackground); 100 assignColor(options.tooltips, "titleFontColor", colors.text); 101 assignColor(options.tooltips, "bodyFontColor", colors.text); 102 assignColor(options.tooltips, "borderColor", colors.tooltipBorder); 103 options.tooltips.borderWidth = 1; 104 } 105 106 // Modern (v3+) nodes already exist via Chart.defaults after 107 // construction, so we assign leaves rather than recreating them. 108 var plugins = options.plugins; 109 if (plugins && plugins.legend && plugins.legend.labels) { 110 assignColor(plugins.legend.labels, "color", colors.text); 111 } 112 if (plugins && plugins.tooltip) { 113 assignColor(plugins.tooltip, "backgroundColor", colors.tooltipBackgroun
113d); 114 assignColor(plugins.tooltip, "titleColor", colors.text); 115 assignColor(plugins.tooltip, "bodyColor", colors.text); 116 assignColor(plugins.tooltip, "borderColor", colors.tooltipBorder); 117 plugins.tooltip.borderWidth = 1; 118 } 119 } 120 121 function applyChartDefaults(colors) { 122 if (!window.Chart || !window.Chart.defaults) { 123 return; 124 } 125 126 var defaults = window.Chart.defaults; 127 if (defaults.global) { 128 assignColor(defaults.global, "defaultFontColor", colors.text); 129 assignColor(defaults.global, "defaultColor", colors.grid); 130 return; 131 } 132 133 // Modern charts are themed entirely through defaults (a plain object, 134 // safe to mutate), so charts created later inherit the current theme 135 // without touching any live options proxy. 136 assignColor(defaults, "color", colors.text); 137 assignColor(defaults, "borderColor", colors.grid); 138 if (defaults.scale) { 139 assignColor(defaults.scale.grid, "color", colors.grid); 140 assignColor(defaults.scale.ticks, "color", colors.mutedText); 141 assignColor(defaults.scale.title, "color", colors.text); 142 } 143 if (defaults.plugins) { 144 if (defaults.plugins.legend && defaults.plugins.legend.labels) { 145 assignColor(defaults.plugins.legend.labels, "color", colors.text); 146 } 147 if (defaults.plugins.tooltip) { 148 assignColor(defaults.plugins.tooltip, "backgroundColor", colors.tooltipBackground); 149 assignColor(defaults.plugins.tooltip, "titleColor", colors.text); 150 assignColor(defaults.plugins.tooltip, "bodyColor", colors.text); 151 assignColor(defaults.plugins.tooltip, "borderColor", colors.tooltipBorder); 152 } 153 } 154 } 155 156 function getChartInstances() { 157 if (!window.Chart || !window.Chart.instances) { 158 return []; 159 } 160 161 if (typeof window.Chart.instances.values === "function") { 162 return Array.from(window.Chart.instances.values()); 163 } 164 165 return Object.keys(window.Chart.instances).map(function (chartId) { 166 return window.Chart.instances[chartId]; 167 }); 168 } 169 170 function updateChartWithoutAnimation(chart) { 171 if (!chart || typeof chart.update !== "function") { 172 return; 173 } 174 175 var chartMajorVersion = window.Chart && window.Chart.version 176 ? Number.parseInt(window.Chart.version.split(".")[0], 10) 177 : 2; 178 179 chart.update(chartMajorVersion >= 3 ? "none" : 0); 180 } 181 182 function refreshCharts() { 183 if (!window.Chart) { 184 return; 185 } 186 187 var colors = getThemeColors(); 188 applyChartDefaults(colors); 189 190 getChartInstances().forEach(function (chart) { 191 applyThemeToChartOptions(chart, colors); 192 updateChartWithoutAnimation(chart); 193 }); 194 } 195 196 function updateThemeControls(theme) { 197 document.querySelectorAll("[data-theme-toggle]").forEach(function (control) { 198 var isDarkTheme = theme === DARK_THEME; 199 var nextThemeLabel = isDarkTheme ? control.dataset.lightLabel : control.dataset.darkLabel; 200 201 control.setAttribute("aria-checked", String(isDarkTheme)); 202 if (nextThemeLabel) { 203 control.setAttribute("aria-label", nextThemeLabel); 204 control.setAttribute("title", nextThemeLabel); 205 } 206 }); 207 } 208 209 function applyTheme(theme, persistPreference) { 210 document.documentElement.dataset.theme = theme; 211 document.documentElement.style.colorScheme = theme; 212 213 if (persistPreference) { 214 storeTheme(theme); 215 } 216 217 updateThemeControls(theme); 218 try { 219 refreshCharts(); 220 } catch (error) { 221 // A third-party chart failure must not prevent the app theme from changing. 222 window.console.warn("VentoryOne chart theming could not be refreshed.", error); 223 } 224 window.dispatchEvent(new CustomEvent(THEME_CHANGE_EVENT, {detail: {theme: theme}})); 225 } 226 227 function bindThemeControls() { 228 document.querySelectorAll("[data-theme-toggle]").forEach(function (control) { 229 if (control.dataset.themeToggleBound === "true") { 230 return; 231 } 232 233 control.dataset.themeToggleBound = "true"; 234 control.addEventListener("click", function () {
235 var nextTheme = getCurrentTheme() === DARK_THEME ? LIGHT_THEME : DARK_THEME; 236 applyTheme(nextTheme, true); 237 }); 238 }); 239 } 240 241 function initializeTheme() { 242 var initialTheme = getStoredTheme() || LIGHT_THEME; 243 bindThemeControls(); 244 applyTheme(initialTheme, false); 245 } 246 247 window.VentoryTheme = { 248 apply: function (theme) { 249 if (theme === DARK_THEME || theme === LIGHT_THEME) { 250 applyTheme(theme, true); 251 } 252 }, 253 current: getCurrentTheme, 254 refreshCharts: refreshCharts, 255 }; 256 257 if (document.readyState === "loading") { 258 document.addEventListener("DOMContentLoaded", initializeTheme, {once: true}); 259 } else { 260 initializeTheme(); 261 } 262}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.