(function () { "use strict"; function clamp(value, min, max) { if (value < min) { return min; } if (value > max) { return max; } return value; } function toNumber(value) { var num = Number(value); return Number.isFinite(num) ? num : null; } function escapeHTML(value) { return String(value) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function formatValue(value, format) { if (value === null || value === undefined || Number.isNaN(value)) { return "-"; } switch (format) { case "int": return String(Math.round(value)); case "float1": return Number(value).toFixed(1); case "float2": return Number(value).toFixed(2); default: return String(value); } } function pickTickIndices(total, desired) { if (total <= 0) { return []; } if (total === 1) { return [0]; } var target = Math.max(2, Math.min(total, desired || 6)); if (target >= total) { var all = []; for (var i = 0; i < total; i++) { all.push(i); } return all; } var indices = [0]; var step = (total - 1) / (target - 1); for (var j = 1; j < target - 1; j++) { indices.push(Math.round(j * step)); } indices.push(total - 1); var seen = {}; var deduped = []; for (var k = 0; k < indices.length; k++) { var idx = indices[k]; if (!seen[idx]) { seen[idx] = true; deduped.push(idx); } } deduped.sort(function (a, b) { return a - b; }); return deduped; } function getPlotBounds(width, height) { return { left: 52, top: 16, right: width - 20, bottom: height - 78, }; } function buildScales(config, plot) { var maxY = 0; for (var i = 0; i < config.series.length; i++) { var values = config.series[i].values || []; for (var j = 0; j < values.length; j++) { var value = toNumber(values[j]); if (value !== null && value > maxY) { maxY = value; } } } if (maxY <= 0) { maxY = 1; } var count = config.labels.length; var xSpan = plot.right - plot.left; var ySpan = plot.bottom - plot.top; return { maxY: maxY, xForIndex: function (index) { if (count <= 1) { return plot.left; } return plot.left + (index / (count - 1)) * xSpan; }, yForValue: function (value) { var numeric = toNumber(value); if (numeric === null) { return null; } return plot.bottom - (numeric / maxY) * ySpan; }, }; } function drawGrid(ctx, plot, config, scales) { ctx.save(); ctx.strokeStyle = "#e2e8f0"; ctx.lineWidth = 1; ctx.setLineDash([2, 4]); var yTickCount = Math.max(2, config.yTicks || 5); for (var i = 0; i < yTickCount; i++) { var yRatio = i / (yTickCount - 1); var y = plot.top + yRatio * (plot.bottom - plot.top); ctx.beginPath(); ctx.moveTo(plot.left, y); ctx.lineTo(plot.right, y); ctx.stroke(); } var xIndices = pickTickIndices(config.labels.length, config.xTicks || 6); for (var j = 0; j < xIndices.length; j++) { var x = scales.xForIndex(xIndices[j]); ctx.beginPath(); ctx.moveTo(x, plot.top); ctx.lineTo(x, plot.bottom); ctx.stroke(); } ctx.restore(); } function drawAxes(ctx, plot) { ctx.save(); ctx.strokeStyle = "#94a3b8"; ctx.lineWidth = 1.5; ctx.setLineDash([]); ctx.beginPath(); ctx.moveTo(plot.left, plot.bottom); ctx.lineTo(plot.right, plot.bottom); ctx.stroke(); ctx.beginPath(); ctx.moveTo(plot.left, plot.top); ctx.lineTo(plot.left, plot.bottom); ctx.stroke(); ctx.restore(); } function drawLabels(ctx, plot, config, scales) { ctx.save(); ctx.fillStyle = "#475569"; ctx.font = "10px sans-serif"; var yTickCount = Math.max(2, config.yTicks || 5); for (var i = 0; i < yTickCount; i++) { var ratio = i / (yTickCount - 1); var y = plot.top + ratio * (plot.bottom - plot.top); var value = scales.maxY * (1 - ratio); ctx.textAlign = "right"; ctx.textBaseline = "middle"; ctx.fillText(formatValue(value, "int"), plot.left - 8, y); } var xIndices = pickTickIndices(config.labels.length, config.xTicks || 6); for (var j = 0; j < xIndices.length; j++) { var idx = xIndices[j]; var tick = (config.tickLabels && config.tickLabels[idx]) || config.labels[idx] || ""; ctx.textAlign = "center"; ctx.textBaseline = "top"; ctx.fillText(tick, scales.xForIndex(idx), plot.bottom + 12); } if (config.yLabel) { ctx.save(); ctx.translate(16, plot.top + (plot.bottom-plot.top)/2); ctx.rotate(-Math.PI / 2); ctx.textAlign = "center"; ctx.textBaseline = "top"; ctx.font = "12px sans-serif"; ctx.fillText(config.yLabel, 0, 0); ctx.restore(); } if (config.xLabel) { ctx.textAlign = "center"; ctx.textBaseline = "top"; ctx.font = "12px sans-serif"; ctx.fillText(config.xLabel, plot.left + (plot.right-plot.left)/2, plot.bottom + 48); } ctx.restore(); } function drawSeries(ctx, plot, config, scales) { for (var i = 0; i < config.series.length; i++) { var series = config.series[i]; var values = series.values || []; if (!values.length) { continue; } ctx.save(); ctx.strokeStyle = series.color || "#2563eb"; ctx.lineWidth = series.lineWidth || 2.5; ctx.setLineDash(Array.isArray(series.dash) ? series.dash : []); ctx.beginPath(); var moved = false; for (var j = 0; j < values.length; j++) { var y = scales.yForValue(values[j]); if (y === null) { continue; } var x = scales.xForIndex(j); if (!moved) { ctx.moveTo(x, y); moved = true; } else { ctx.lineTo(x, y); } } ctx.stroke(); ctx.restore(); } } function drawLegend(ctx, config, width, height) { var x = 52; var y = height - 32; ctx.save(); ctx.font = "12px sans-serif"; ctx.textBaseline = "middle"; for (var i = 0; i < config.series.length; i++) { var series = config.series[i]; var label = series.name || "Series"; ctx.strokeStyle = series.color || "#2563eb"; ctx.fillStyle = "#475569"; ctx.lineWidth = series.lineWidth || 2.5; ctx.setLineDash(Array.isArray(series.dash) ? series.dash : []); ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + 16, y); ctx.stroke(); ctx.setLineDash([]); ctx.fillText(label, x + 22, y); x += 22 + ctx.measureText(label).width + 18; if (x > width - 160) { x = 52; y += 18; } } ctx.restore(); } function updateTooltip(state, config) { if (!state.tooltip) { return; } if (state.hoverIndex === null) { state.tooltip.classList.remove("visible"); state.tooltip.setAttribute("aria-hidden", "true"); return; } var idx = state.hoverIndex; var rows = []; rows.push('