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 / bmi-calculator / frontend.js

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

307 lines 14.7 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 /* ── Calculation helpers ──────────────────────────────────────────────── */
5 function calcBMI(system, weightKg, heightCm, weightLbs, heightFt, heightIn) {
6 var bmi;
7 if (system === 'metric') {
8 var hM = heightCm / 100;
9 bmi = hM > 0 ? weightKg / (hM * hM) : 0;
10 } else {
11 var totalIn = (heightFt * 12) + heightIn;
12 bmi = totalIn > 0 ? (703 * weightLbs) / (totalIn * totalIn) : 0;
13 }
14 return Math.round(bmi * 10) / 10;
15 }
16
17 function bmiCategory(bmi) {
18 if (bmi < 18.5) return 'underweight';
19 if (bmi < 25) return 'normal';
20 if (bmi < 30) return 'overweight';
21 return 'obese';
22 }
23
24 function bmiPct(bmi) {
25 var clamped = Math.min(Math.max(bmi, 10), 40);
26 return ((clamped - 10) / 30) * 100;
27 }
28
29 function idealRange(system, heightCm, heightFt, heightIn) {
30 var minBMI = 18.5, maxBMI = 24.9;
31 if (system === 'metric') {
32 var hM = heightCm / 100;
33 return { min: (minBMI * hM * hM).toFixed(1), max: (maxBMI * hM * hM).toFixed(1), unit: 'kg' };
34 } else {
35 var totalIn = heightFt * 12 + heightIn;
36 return {
37 min: ((minBMI * totalIn * totalIn) / 703).toFixed(1),
38 max: ((maxBMI * totalIn * totalIn) / 703).toFixed(1),
39 unit: 'lbs'
40 };
41 }
42 }
43
44 var CAT_INTERP = {
45 underweight: 'Consider speaking with a healthcare professional about healthy weight gain.',
46 normal: 'Great job! You are in a healthy weight range.',
47 overweight: 'Small lifestyle changes can bring you into a healthy range.',
48 obese: 'Consult a healthcare professional for personalized guidance.',
49 };
50
51 function typoCssVarsForEl(typo, prefix, el) {
52 if (!typo) return;
53 if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif");
54 if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight);
55 if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform);
56 if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style);
57 if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration);
58 var su = typo.sizeUnit || 'px';
59 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su);
60 if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su);
61 if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su);
62 var lhu = typo.lineHeightUnit || '';
63 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu);
64 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu);
65 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu);
66 var lsu = typo.letterSpacingUnit || 'px';
67 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu);
68 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu);
69 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu);
70 var wsu = typo.wordSpacingUnit || 'px';
71 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu);
72 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu);
73 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu);
74 }
75
76 /* ── Build the full UI into the app container ─────────────────────────── */
77 function buildApp(app) {
78 var opts = {};
79 try { opts = JSON.parse(app.dataset.opts || '{}'); } catch (e) {}
80
81 var system = opts.unitSystem || 'metric';
82 var showToggle = opts.showUnitToggle !== false;
83
84 /* current values */
85 var weightKg = opts.weightDefaultKg || 70;
86 var heightCm = opts.heightDefaultCm || 170;
87 var weightLbs = opts.weightDefaultLbs || 154;
88 var heightFt = opts.heightDefaultFt || 5;
89 var heightIn = parseInt(opts.heightDefaultIn) || 7;
90
91 var catColors = {
92 underweight: opts.underweightColor || '#3b82f6',
93 normal: opts.normalColor || '#10b981',
94 overweight: opts.overweightColor || '#f59e0b',
95 obese: opts.obeseColor || '#ef4444',
96 };
97 var catLabels = { underweight: 'Underweight', normal: 'Normal', overweight: 'Overweight', obese: 'Obese' };
98 var accent = opts.accentColor || '#6c3fb5';
99 var cardBg = opts.cardBg || '#ffffff';
100 var resultBg = opts.resultBg || '#f5f3ff';
101 var resultBdr = opts.resultBorder || '#ede9fe';
102 var labelColor= opts.labelColor || '#374151';
103 var radius = (opts.cardRadius || 16) + 'px';
104 var btnR = (opts.btnRadius || 8) + 'px';
105 var maxW = (opts.maxWidth || 680) + 'px';
106 var ptop = (opts.paddingTop || 60) + 'px';
107 var pbot = (opts.paddingBottom || 60) + 'px';
108
109 /* --- Outer container --- */
110 app.innerHTML = '';
111 app.style.cssText = 'box-sizing:border-box;';
112
113 var card = document.createElement('div');
114 card.className = 'bkbg-bmi-card';
115 card.style.cssText = 'background:' + cardBg + ';border-radius:' + radius + ';padding:32px;box-shadow:0 4px 24px rgba(0,0,0,0.08);max-width:' + maxW + ';margin:0 auto;padding-top:' + ptop + ';padding-bottom:' + pbot + ';box-sizing:border-box;';
116 app.appendChild(card);
117
118 /* --- Unit toggle --- */
119 if (showToggle) {
120 var toggleRow = document.createElement('div');
121 toggleRow.className = 'bkbg-bmi-unit-toggle';
122 ['metric', 'imperial'].forEach(function(s) {
123 var btn = document.createElement('button');
124 btn.className = 'bkbg-bmi-unit-btn' + (system === s ? ' active' : '');
125 btn.textContent = s === 'metric' ? 'Metric (kg / cm)' : 'Imperial (lbs / ft)';
126 btn.style.cssText = 'border-radius:' + btnR + ';background:' + (system === s ? accent : '#f3f4f6') + ';color:' + (system === s ? '#fff' : '#374151') + ';';
127 btn.dataset.sys = s;
128 toggleRow.appendChild(btn);
129 });
130 card.appendChild(toggleRow);
131 }
132
133 /* --- Input fields --- */
134 var inputWrap = document.createElement('div');
135 inputWrap.className = 'bkbg-bmi-inputs ' + system;
136 card.appendChild(inputWrap);
137
138 function makeField(label, id, value, min, max) {
139 var wrap = document.createElement('div');
140 var lbl = document.createElement('label');
141 lbl.htmlFor = id;
142 lbl.textContent = label;
143 lbl.className = 'bkbg-bmi-label';
144 lbl.style.color = labelColor;
145 var inp = document.createElement('input');
146 inp.id = id;
147 inp.type = 'number';
148 inp.className = 'bkbg-bmi-input';
149 inp.value = value;
150 inp.min = min;
151 inp.max = max;
152 inp.style.accentColor = accent;
153 wrap.appendChild(lbl);
154 wrap.appendChild(inp);
155 inputWrap.appendChild(wrap);
156 return inp;
157 }
158
159 var inpWKg, inpHCm, inpWLbs, inpHFt, inpHIn;
160 function buildInputs() {
161 inputWrap.innerHTML = '';
162 inputWrap.className = 'bkbg-bmi-inputs ' + system;
163 if (system === 'metric') {
164 inpWKg = makeField('Weight (kg)', 'bmi-wkg', weightKg, opts.weightMinKg || 20, opts.weightMaxKg || 200);
165 inpHCm = makeField('Height (cm)', 'bmi-hcm', heightCm, opts.heightMinCm || 100, opts.heightMaxCm || 220);
166 } else {
167 inpWLbs = makeField('Weight (lbs)', 'bmi-wlbs', weightLbs, opts.weightMinLbs || 44, opts.weightMaxLbs || 440);
168 inpHFt = makeField('Height (ft)', 'bmi-hft', heightFt, 3, 8);
169 inpHIn = makeField('Height (in)', 'bmi-hin', heightIn, 0, 11);
170 }
171 }
172 buildInputs();
173
174 /* --- Result area --- */
175 var resultBox = document.createElement('div');
176 resultBox.className = 'bkbg-bmi-result';
177 resultBox.style.cssText = 'background:' + resultBg + ';border:1.5px solid ' + resultBdr + ';border-radius:' + radius + ';';
178 card.appendChild(resultBox);
179
180 var bmiNumEl = document.createElement('div'); bmiNumEl.className = 'bkbg-bmi-number';
181 var bmiCatEl = document.createElement('div'); bmiCatEl.className = 'bkbg-bmi-cat-label';
182 var gaugeWrap, gaugeTrack, gaugeNeedle, gaugeLabels;
183 var legendEl;
184 var idealEl;
185 var interpEl;
186
187 resultBox.appendChild(bmiNumEl);
188 resultBox.appendChild(bmiCatEl);
189
190 if (opts.showGauge !== false) {
191 gaugeWrap = document.createElement('div'); gaugeWrap.className = 'bkbg-bmi-gauge-wrap';
192 gaugeTrack = document.createElement('div'); gaugeTrack.className = 'bkbg-bmi-gauge-track';
193 gaugeTrack.style.background = 'linear-gradient(to right,' + catColors.underweight + ' 0%,' + catColors.normal + ' 30%,' + catColors.overweight + ' 60%,' + catColors.obese + ' 100%)';
194 gaugeNeedle = document.createElement('div'); gaugeNeedle.className = 'bkbg-bmi-gauge-needle';
195 gaugeLabels = document.createElement('div'); gaugeLabels.className = 'bkbg-bmi-gauge-labels';
196 gaugeLabels.innerHTML = '<span>10</span><span>18.5</span><span>25</span><span>30</span><span>40+</span>';
197 gaugeTrack.appendChild(gaugeNeedle);
198 gaugeWrap.appendChild(gaugeTrack);
199 gaugeWrap.appendChild(gaugeLabels);
200 resultBox.appendChild(gaugeWrap);
201 }
202
203 if (opts.showCategories !== false) {
204 legendEl = document.createElement('div');
205 legendEl.className = 'bkbg-bmi-legend';
206 Object.keys(catColors).forEach(function(k) {
207 var item = document.createElement('div'); item.className = 'bkbg-bmi-legend-item';
208 var dot = document.createElement('span'); dot.className = 'bkbg-bmi-legend-dot'; dot.style.background = catColors[k];
209 var txt = document.createElement('span'); txt.textContent = catLabels[k];
210 item.appendChild(dot); item.appendChild(txt);
211 legendEl.appendChild(item);
212 });
213 resultBox.appendChild(legendEl);
214 }
215
216 if (opts.showIdealWeight !== false) {
217 idealEl = document.createElement('p');
218 idealEl.className = 'bkbg-bmi-ideal';
219 resultBox.appendChild(idealEl);
220 }
221
222 if (opts.showInterpretation !== false) {
223 interpEl = document.createElement('p');
224 interpEl.className = 'bkbg-bmi-interp';
225 resultBox.appendChild(interpEl);
226 }
227
228 if (opts.showDisclaimer !== false && opts.disclaimer) {
229 var disc = document.createElement('p');
230 disc.className = 'bkbg-bmi-disclaimer';
231 disc.textContent = opts.disclaimer;
232 card.appendChild(disc);
233 }
234
235 /* --- Update display --- */
236 function update() {
237 if (system === 'metric') {
238 weightKg = parseFloat(inpWKg.value) || 70;
239 heightCm = parseFloat(inpHCm.value) || 170;
240 } else {
241 weightLbs = parseFloat(inpWLbs.value) || 154;
242 heightFt = parseInt(inpHFt.value) || 5;
243 heightIn = parseInt(inpHIn.value) || 0;
244 }
245
246 var bmi = calcBMI(system, weightKg, heightCm, weightLbs, heightFt, heightIn);
247 var cat = bmiCategory(bmi);
248 var col = catColors[cat];
249 var pct = bmiPct(bmi);
250
251 bmiNumEl.textContent = bmi.toFixed(1);
252 bmiNumEl.style.color = col;
253 bmiCatEl.textContent = catLabels[cat];
254 bmiCatEl.style.color = col;
255
256 if (gaugeNeedle) {
257 gaugeNeedle.style.left = pct + '%';
258 gaugeNeedle.style.color = col;
259 gaugeNeedle.style.borderColor = col;
260 }
261
262 if (idealEl) {
263 var ir = idealRange(system, heightCm, heightFt, heightIn);
264 idealEl.innerHTML = 'Ideal weight range: <strong>' + ir.min + '' + ir.max + ' ' + ir.unit + '</strong>';
265 }
266
267 if (interpEl) {
268 interpEl.textContent = CAT_INTERP[cat] || '';
269 interpEl.style.color = col;
270 }
271 }
272
273 /* --- Event bindings --- */
274 function bindInputs() {
275 var allInputs = inputWrap.querySelectorAll('input');
276 allInputs.forEach(function(inp) {
277 inp.addEventListener('input', update);
278 });
279 }
280 bindInputs();
281 update();
282
283 /* Unit toggle events */
284 if (showToggle) {
285 card.querySelector('.bkbg-bmi-unit-toggle').addEventListener('click', function(e) {
286 var btn = e.target.closest('.bkbg-bmi-unit-btn');
287 if (!btn) return;
288 system = btn.dataset.sys;
289 card.querySelectorAll('.bkbg-bmi-unit-btn').forEach(function(b) {
290 var active = b.dataset.sys === system;
291 b.classList.toggle('active', active);
292 b.style.background = active ? accent : '#f3f4f6';
293 b.style.color = active ? '#fff' : '#374151';
294 });
295 buildInputs();
296 bindInputs();
297 update();
298 });
299 }
300
301 typoCssVarsForEl(opts.typoTitle, '--bkbg-bmi-title-', app);
302 typoCssVarsForEl(opts.typoBmi, '--bkbg-bmi-number-', app);
303 }
304
305 document.querySelectorAll('.bkbg-bmi-app').forEach(buildApp);
306 })();
307