PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / statistics-calculator / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/statistics-calculator/frontend.js

303 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 /* ---- Typography CSS-var helper ---- */
5 var _typoKeys = {family:'font-family',weight:'font-weight',style:'font-style',decoration:'text-decoration',transform:'text-transform'};
6 var _respKeys = {sizeDesktop:'font-size-d',sizeTablet:'font-size-t',sizeMobile:'font-size-m',lineHeightDesktop:'line-height-d',lineHeightTablet:'line-height-t',lineHeightMobile:'line-height-m',letterSpacingDesktop:'letter-spacing-d',letterSpacingTablet:'letter-spacing-t',letterSpacingMobile:'letter-spacing-m',wordSpacingDesktop:'word-spacing-d',wordSpacingTablet:'word-spacing-t',wordSpacingMobile:'word-spacing-m'};
7 function typoCssVarsForEl(el, obj, prefix) {
8 if (!obj || typeof obj !== 'object') return;
9 var k;
10 for (k in _typoKeys) { if (obj[k]) el.style.setProperty(prefix + _typoKeys[k], obj[k]); }
11 for (k in _respKeys) { if (obj[k] !== undefined && obj[k] !== '') el.style.setProperty(prefix + _respKeys[k], obj[k]); }
12 }
13
14 /* ---- Math helpers ---- */
15 function parseNums(raw) {
16 var parts = raw.trim().split(/[\s,;]+/);
17 var nums = [];
18 for (var i = 0; i < parts.length; i++) {
19 var p = parts[i].trim();
20 if (p === '') continue;
21 var n = parseFloat(p);
22 if (isNaN(n)) throw new Error('Invalid number: "' + p + '"');
23 nums.push(n);
24 }
25 if (nums.length < 2) throw new Error('Enter at least 2 numbers.');
26 return nums;
27 }
28
29 function mean(arr) { return arr.reduce(function (a, b) { return a + b; }, 0) / arr.length; }
30
31 function median(sorted) {
32 var n = sorted.length;
33 return n % 2 === 0 ? (sorted[n / 2 - 1] + sorted[n / 2]) / 2 : sorted[Math.floor(n / 2)];
34 }
35
36 function mode(arr) {
37 var freq = {};
38 arr.forEach(function (n) { freq[n] = (freq[n] || 0) + 1; });
39 var maxF = Math.max.apply(null, Object.values(freq));
40 if (maxF === 1) return { values: [], label: 'No mode' };
41 var modes = Object.keys(freq).filter(function (k) { return freq[k] === maxF; })
42 .map(Number).sort(function (a, b) { return a - b; });
43 return { values: modes, label: modes.join(', '), freq: maxF };
44 }
45
46 function stdDev(arr, mn) {
47 var variance = arr.reduce(function (s, v) { return s + Math.pow(v - mn, 2); }, 0) / arr.length;
48 return { variance: variance, stdDev: Math.sqrt(variance) };
49 }
50
51 function quartiles(sorted) {
52 var n = sorted.length;
53 var lo = sorted.slice(0, Math.floor(n / 2));
54 var hi = n % 2 === 0 ? sorted.slice(n / 2) : sorted.slice(Math.floor(n / 2) + 1);
55 return { q1: median(lo), q3: median(hi) };
56 }
57
58 function skewness(arr, mn, sd) {
59 if (sd === 0) return 0;
60 var n = arr.length;
61 var s = arr.reduce(function (a, v) { return a + Math.pow((v - mn) / sd, 3); }, 0);
62 return (n / ((n - 1) * (n - 2))) * s;
63 }
64
65 function histogram(arr, bins) {
66 var mn = Math.min.apply(null, arr);
67 var mx = Math.max.apply(null, arr);
68 var rng = mx - mn;
69 if (rng === 0) { return [{ label: mn.toString(), count: arr.length, lo: mn, hi: mn }]; }
70 bins = bins || Math.max(4, Math.min(12, Math.ceil(Math.sqrt(arr.length))));
71 var w = rng / bins;
72 var buckets = [];
73 for (var i = 0; i < bins; i++) {
74 var lo = mn + i * w;
75 var hi = lo + w;
76 var count = arr.filter(function (v) { return v >= lo && (i === bins - 1 ? v <= hi : v < hi); }).length;
77 buckets.push({ lo: lo, hi: hi, count: count });
78 }
79 return buckets;
80 }
81
82 /* ---- DOM build ---- */
83 function initApp(app) {
84 var opts;
85 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch (e) { return; }
86
87 var accent = opts.accentColor || '#6c3fb5';
88 var meanC = opts.meanColor || '#6c3fb5';
89 var medianC = opts.medianColor || '#0ea5e9';
90 var modeC = opts.modeColor || '#10b981';
91 var stdC = opts.stdDevColor || '#f59e0b';
92 var barC = opts.barColor || accent;
93 var cardR = (opts.cardRadius || 16) + 'px';
94 var statR = (opts.statRadius || 12) + 'px';
95 var inpR = (opts.inputRadius || 8) + 'px';
96 var maxW = (opts.maxWidth || 680) + 'px';
97 var dp = opts.decimalPlaces !== undefined ? opts.decimalPlaces : 4;
98 var lblClr = opts.labelColor || '#374151';
99
100 app.style.paddingTop = (opts.paddingTop || 60) + 'px';
101 app.style.paddingBottom = (opts.paddingBottom || 60) + 'px';
102 if (opts.sectionBg) app.style.background = opts.sectionBg;
103
104 /* Typography CSS vars */
105 if (opts.titleSize) app.style.setProperty('--bkstc-tt-sz', opts.titleSize + 'px');
106 if (opts.titleFontWeight) app.style.setProperty('--bkstc-tt-wt', opts.titleFontWeight);
107 if (opts.titleLineHeight !== undefined) app.style.setProperty('--bkstc-tt-lh', opts.titleLineHeight);
108 if (opts.subtitleFontSize) app.style.setProperty('--bkstc-st-sz', opts.subtitleFontSize + 'px');
109 if (opts.subtitleLineHeight !== undefined) app.style.setProperty('--bkstc-st-lh', opts.subtitleLineHeight);
110 typoCssVarsForEl(app, opts.titleTypo, '--bkstc-tt-');
111 typoCssVarsForEl(app, opts.subtitleTypo, '--bkstc-st-');
112
113 /* Card */
114 var card = document.createElement('div');
115 card.className = 'bkbg-stc-card';
116 Object.assign(card.style, { background: opts.cardBg || '#fff', borderRadius: cardR, maxWidth: maxW });
117 app.appendChild(card);
118
119 if (opts.showTitle && opts.title) {
120 var ttl = document.createElement('div'); ttl.className = 'bkbg-stc-title';
121 ttl.textContent = opts.title;
122 if (opts.titleColor) ttl.style.color = opts.titleColor;
123 card.appendChild(ttl);
124 }
125 if (opts.showSubtitle && opts.subtitle) {
126 var sub = document.createElement('div'); sub.className = 'bkbg-stc-subtitle';
127 sub.textContent = opts.subtitle;
128 if (opts.subtitleColor) sub.style.color = opts.subtitleColor;
129 card.appendChild(sub);
130 }
131
132 /* Input */
133 var lbl = document.createElement('label'); lbl.className = 'bkbg-stc-lbl'; lbl.textContent = 'Dataset';
134 if (lblClr) lbl.style.color = lblClr; card.appendChild(lbl);
135 var ta = document.createElement('textarea'); ta.className = 'bkbg-stc-textarea';
136 ta.value = opts.defaultData || '4, 8, 15, 16, 23, 42';
137 ta.placeholder = 'e.g. 4, 8, 15, 16, 23, 42';
138 ta.style.borderRadius = inpR; ta.style.background = opts.inputBg || '#f9fafb';
139 ta.style.borderColor = opts.inputBorder || '#d1d5db';
140 card.appendChild(ta);
141
142 /* Buttons row */
143 var row = document.createElement('div'); row.className = 'bkbg-stc-row'; card.appendChild(row);
144 var btn = document.createElement('button'); btn.className = 'bkbg-stc-calc-btn';
145 btn.textContent = '⚡ Calculate'; btn.style.background = accent; row.appendChild(btn);
146 var clr = document.createElement('button'); clr.className = 'bkbg-stc-clear-btn';
147 clr.textContent = 'Clear'; row.appendChild(clr);
148 var errEl = document.createElement('div'); errEl.className = 'bkbg-stc-error'; card.appendChild(errEl);
149
150 /* Results area */
151 var resultsArea = document.createElement('div'); card.appendChild(resultsArea);
152
153 function fmt(n) { return parseFloat(n.toFixed(dp)).toString(); }
154
155 function STAT_CARDS(mn, med, mod, sd, v, rng, knt, min, max) {
156 return [
157 { label: 'Mean', val: fmt(mn), color: meanC },
158 { label: 'Median', val: fmt(med), color: medianC },
159 { label: 'Mode', val: mod.label,color: modeC },
160 { label: 'Std Dev', val: fmt(sd), color: stdC },
161 { label: 'Variance', val: fmt(v), color: stdC },
162 { label: 'Range', val: fmt(rng), color: accent },
163 { label: 'Count', val: knt, color: accent },
164 { label: 'Min', val: fmt(min), color: '#6b7280' },
165 { label: 'Max', val: fmt(max), color: '#6b7280' }
166 ];
167 }
168
169 function render(nums) {
170 resultsArea.innerHTML = '';
171 var sorted = nums.slice().sort(function (a, b) { return a - b; });
172 var mn = mean(nums);
173 var med = median(sorted);
174 var mod = mode(nums);
175 var sv = stdDev(nums, mn);
176 var sd = sv.stdDev; var variance = sv.variance;
177 var rng = sorted[sorted.length - 1] - sorted[0];
178 var qs = quartiles(sorted);
179 var iqr = qs.q3 - qs.q1;
180 var skew = skewness(nums, mn, sd);
181 var min = sorted[0]; var max = sorted[sorted.length - 1];
182
183 /* ---- Stat grid ---- */
184 var grid = document.createElement('div'); grid.className = 'bkbg-stc-grid';
185 STAT_CARDS(mn, med, mod, sd, variance, rng, nums.length, min, max).forEach(function (s) {
186 var sc = document.createElement('div'); sc.className = 'bkbg-stc-stat-card';
187 sc.style.background = opts.statCardBg || '#fafafa';
188 sc.style.borderTopColor = s.color;
189 sc.style.borderRadius = statR;
190 var sl = document.createElement('div'); sl.className = 'bkbg-stc-stat-lbl'; sl.textContent = s.label;
191 var sv2 = document.createElement('div'); sv2.className = 'bkbg-stc-stat-val'; sv2.textContent = s.val; sv2.style.color = s.color;
192 sc.appendChild(sl); sc.appendChild(sv2); grid.appendChild(sc);
193 });
194 /* Extra single-row stats */
195 [
196 { label: 'Q1', val: fmt(qs.q1) },
197 { label: 'Q3', val: fmt(qs.q3) },
198 { label: 'IQR', val: fmt(iqr) },
199 { label: 'Skewness', val: fmt(skew) }
200 ].forEach(function (s) {
201 var sc = document.createElement('div'); sc.className = 'bkbg-stc-stat-card';
202 sc.style.background = opts.statCardBg || '#fafafa'; sc.style.borderTopColor = '#9ca3af';
203 sc.style.borderRadius = statR;
204 var sl = document.createElement('div'); sl.className = 'bkbg-stc-stat-lbl'; sl.textContent = s.label;
205 var sv2 = document.createElement('div'); sv2.className = 'bkbg-stc-stat-val'; sv2.textContent = s.val; sv2.style.color = '#6b7280';
206 sc.appendChild(sl); sc.appendChild(sv2); grid.appendChild(sc);
207 });
208 resultsArea.appendChild(grid);
209
210 /* ---- Histogram ---- */
211 if (opts.showHistogram !== false) {
212 var sep1 = document.createElement('div'); sep1.className = 'bkbg-stc-sep'; resultsArea.appendChild(sep1);
213 var ht = document.createElement('div'); ht.className = 'bkbg-stc-section-title'; ht.textContent = 'Frequency Histogram'; resultsArea.appendChild(ht);
214 var buckets = histogram(nums);
215 var maxCt = Math.max.apply(null, buckets.map(function (b) { return b.count; }));
216 var hWrap = document.createElement('div'); hWrap.className = 'bkbg-stc-histogram'; resultsArea.appendChild(hWrap);
217 buckets.forEach(function (b) {
218 var col = document.createElement('div'); col.className = 'bkbg-stc-hist-col';
219 var bar = document.createElement('div'); bar.className = 'bkbg-stc-hist-bar';
220 var h = maxCt > 0 ? Math.max(4, (b.count / maxCt) * 90) : 4;
221 bar.style.height = h + 'px'; bar.style.background = barC; bar.title = 'Count: ' + b.count;
222 var lbl2 = document.createElement('div'); lbl2.className = 'bkbg-stc-hist-lbl';
223 lbl2.textContent = parseFloat(b.lo.toFixed(2));
224 col.appendChild(bar); col.appendChild(lbl2); hWrap.appendChild(col);
225 });
226 }
227
228 /* ---- Box plot ---- */
229 if (opts.showBoxPlot !== false && max > min) {
230 var sep2 = document.createElement('div'); sep2.className = 'bkbg-stc-sep'; resultsArea.appendChild(sep2);
231 var bt2 = document.createElement('div'); bt2.className = 'bkbg-stc-section-title'; bt2.textContent = 'Box Plot'; resultsArea.appendChild(bt2);
232 var bpw = document.createElement('div'); bpw.className = 'bkbg-stc-boxplot-wrap';
233 var bpt = document.createElement('div'); bpt.className = 'bkbg-stc-boxplot-track';
234 var span = max - min;
235 var q1pct = ((qs.q1 - min) / span) * 100;
236 var q3pct = ((qs.q3 - min) / span) * 100;
237 var medpct = ((med - min) / span) * 100;
238 var iqrEl = document.createElement('div'); iqrEl.className = 'bkbg-stc-boxplot-iqr';
239 iqrEl.style.left = q1pct + '%'; iqrEl.style.width = (q3pct - q1pct) + '%';
240 iqrEl.style.background = barC + '66';
241 var medEl = document.createElement('div'); medEl.className = 'bkbg-stc-boxplot-median';
242 medEl.style.left = medpct + '%'; medEl.style.background = medianC;
243 bpt.appendChild(iqrEl); bpt.appendChild(medEl);
244 var bpl = document.createElement('div'); bpl.className = 'bkbg-stc-boxplot-labels';
245 [
246 { label: 'Min ' + fmt(min), left: '0%' },
247 { label: 'Q1 ' + fmt(qs.q1), left: q1pct + '%' },
248 { label: 'Med ' + fmt(med), left: medpct + '%' },
249 { label: 'Q3 ' + fmt(qs.q3), left: q3pct + '%' },
250 { label: 'Max ' + fmt(max), left: '100%' }
251 ].forEach(function (p) {
252 var s = document.createElement('span'); s.textContent = p.label; bpl.appendChild(s);
253 });
254 bpw.appendChild(bpt); bpw.appendChild(bpl); resultsArea.appendChild(bpw);
255 }
256
257 /* ---- Sorted list ---- */
258 if (opts.showSortedList !== false) {
259 var sep3 = document.createElement('div'); sep3.className = 'bkbg-stc-sep'; resultsArea.appendChild(sep3);
260 var st = document.createElement('div'); st.className = 'bkbg-stc-section-title'; st.textContent = 'Sorted Values'; resultsArea.appendChild(st);
261 var sw = document.createElement('div'); sw.className = 'bkbg-stc-sorted';
262 var lowerFence = qs.q1 - 1.5 * iqr;
263 var upperFence = qs.q3 + 1.5 * iqr;
264 sorted.forEach(function (n) {
265 var pill = document.createElement('span'); pill.className = 'bkbg-stc-pill';
266 var isMode = mod.values.indexOf(n) !== -1;
267 var isOut = n < lowerFence || n > upperFence;
268 if (isMode) { pill.classList.add('bkbg-stc-pill-mode'); pill.style.background = modeC; }
269 else if (isOut) { pill.classList.add('bkbg-stc-pill-outlier'); }
270 pill.textContent = fmt(n); if (isMode) pill.title = 'Mode'; if (isOut) pill.title = 'Outlier';
271 sw.appendChild(pill);
272 });
273 resultsArea.appendChild(sw);
274 var legend = document.createElement('div'); legend.style.cssText = 'font-size:11px;color:#9ca3af;margin-top:8px;display:flex;gap:16px;flex-wrap:wrap;';
275 [{ bg: modeC, label: '● Mode' }, { bg: '#fee2e2', label: '● Outlier (IQR×1.5)' }].forEach(function (l) {
276 var s = document.createElement('span'); s.style.color = l.bg; s.textContent = l.label; legend.appendChild(s);
277 });
278 resultsArea.appendChild(legend);
279 }
280 }
281
282 /* Trigger initial calculation */
283 function calculate() {
284 errEl.textContent = '';
285 try {
286 var nums = parseNums(ta.value);
287 render(nums);
288 } catch (e) {
289 resultsArea.innerHTML = '';
290 errEl.textContent = '' + e.message;
291 }
292 }
293
294 btn.addEventListener('click', calculate);
295 clr.addEventListener('click', function () { ta.value = ''; resultsArea.innerHTML = ''; errEl.textContent = ''; });
296 ta.addEventListener('keydown', function (e) { if (e.key === 'Enter' && e.ctrlKey) { e.preventDefault(); calculate(); } });
297
298 calculate(); /* auto-run on load */
299 }
300
301 document.querySelectorAll('.bkbg-stc-app').forEach(initApp);
302 })();
303