| 1 |
(function () { |
| 2 |
var CDN = 'https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js'; |
| 3 |
|
| 4 |
function loadChart(cb) { |
| 5 |
if (window.Chart) { cb(window.Chart); return; } |
| 6 |
var s = document.createElement('script'); |
| 7 |
s.src = CDN; |
| 8 |
s.onload = function () { cb(window.Chart); }; |
| 9 |
document.head.appendChild(s); |
| 10 |
} |
| 11 |
|
| 12 |
function hexToRgba(hex, alpha) { |
| 13 |
hex = (hex || '#000000').replace('#', ''); |
| 14 |
if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; |
| 15 |
var r = parseInt(hex.substr(0,2),16), g = parseInt(hex.substr(2,2),16), b = parseInt(hex.substr(4,2),16); |
| 16 |
return 'rgba('+r+','+g+','+b+','+(alpha/100)+')'; |
| 17 |
} |
| 18 |
|
| 19 |
function parseLines(str) { |
| 20 |
return (str || '').split('\n').map(function(s){ return parseFloat(s.trim()); }).filter(function(v){ return !isNaN(v); }); |
| 21 |
} |
| 22 |
|
| 23 |
function parseLabels(str) { |
| 24 |
return (str || '').split('\n').map(function(s){ return s.trim(); }).filter(Boolean); |
| 25 |
} |
| 26 |
|
| 27 |
function initChart(card) { |
| 28 |
var raw = card.getAttribute('data-chart'); |
| 29 |
if (!raw) return; |
| 30 |
var opts; |
| 31 |
try { opts = JSON.parse(raw); } catch(e) { return; } |
| 32 |
|
| 33 |
var canvas = card.querySelector('.bkbg-mc-canvas'); |
| 34 |
if (!canvas) return; |
| 35 |
|
| 36 |
var labels = parseLabels(opts.labels); |
| 37 |
var datasetsRaw = []; |
| 38 |
try { datasetsRaw = JSON.parse(opts.datasetsJson || '[]'); } catch(e) {} |
| 39 |
|
| 40 |
var datasets = datasetsRaw.map(function(ds) { |
| 41 |
var data = parseLines(ds.data); |
| 42 |
var base = { |
| 43 |
label: ds.label || '', |
| 44 |
type: ds.type || 'bar', |
| 45 |
data: data, |
| 46 |
yAxisID: ds.yAxis || 'y', |
| 47 |
backgroundColor: ds.type === 'line' ? 'transparent' : hexToRgba(ds.color || '#6c3fb5', opts.fillAlpha || 85), |
| 48 |
borderColor: ds.color || '#6c3fb5', |
| 49 |
borderWidth: ds.type === 'line' ? (opts.lineWidth || 2) : 0, |
| 50 |
borderRadius: ds.type === 'bar' ? (opts.barRadius || 4) : 0, |
| 51 |
pointRadius: ds.type === 'line' ? (opts.dotSize || 4) : 0, |
| 52 |
pointBackgroundColor: ds.color || '#6c3fb5', |
| 53 |
tension: 0.4, |
| 54 |
fill: false, |
| 55 |
}; |
| 56 |
return base; |
| 57 |
}); |
| 58 |
|
| 59 |
var scalesOpts = { |
| 60 |
x: { grid: { display: opts.showGrid !== false } }, |
| 61 |
y: { |
| 62 |
position: 'left', |
| 63 |
title: { display: !!opts.yLabel, text: opts.yLabel || '' }, |
| 64 |
grid: { display: opts.showGrid !== false }, |
| 65 |
} |
| 66 |
}; |
| 67 |
if (opts.dualAxis) { |
| 68 |
scalesOpts.y1 = { |
| 69 |
position: 'right', |
| 70 |
title: { display: !!opts.y1Label, text: opts.y1Label || '' }, |
| 71 |
grid: { drawOnChartArea: false }, |
| 72 |
}; |
| 73 |
} |
| 74 |
|
| 75 |
if (canvas._chartInstance) { canvas._chartInstance.destroy(); } |
| 76 |
|
| 77 |
canvas.style.height = (opts.chartHeight || 400) + 'px'; |
| 78 |
|
| 79 |
canvas._chartInstance = new window.Chart(canvas, { |
| 80 |
type: 'bar', |
| 81 |
data: { labels: labels, datasets: datasets }, |
| 82 |
options: { |
| 83 |
responsive: true, |
| 84 |
maintainAspectRatio: false, |
| 85 |
animation: opts.animate !== false ? { duration: 900, easing: 'easeOutQuart' } : false, |
| 86 |
plugins: { |
| 87 |
legend: { display: opts.showLegend !== false, position: opts.legendPos || 'top' }, |
| 88 |
tooltip: { mode: 'index', intersect: false }, |
| 89 |
}, |
| 90 |
scales: scalesOpts, |
| 91 |
} |
| 92 |
}); |
| 93 |
} |
| 94 |
|
| 95 |
function init() { |
| 96 |
loadChart(function () { |
| 97 |
var cards = document.querySelectorAll('.bkbg-mc-card[data-chart]'); |
| 98 |
cards.forEach(function (card) { |
| 99 |
var io = new IntersectionObserver(function (entries, obs) { |
| 100 |
if (entries[0].isIntersecting) { |
| 101 |
obs.disconnect(); |
| 102 |
initChart(card); |
| 103 |
} |
| 104 |
}, { threshold: 0.15 }); |
| 105 |
io.observe(card); |
| 106 |
}); |
| 107 |
}); |
| 108 |
} |
| 109 |
|
| 110 |
if (document.readyState !== 'loading') { init(); } |
| 111 |
else { document.addEventListener('DOMContentLoaded', init); } |
| 112 |
})(); |
| 113 |
|