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

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

328 lines 17.8 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 function typoCssVarsForEl(typo, prefix, el) {
5 if (!typo) return;
6 if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif");
7 if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight);
8 if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform);
9 if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style);
10 if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration);
11 var su = typo.sizeUnit || 'px';
12 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su);
13 if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su);
14 if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su);
15 var lhu = typo.lineHeightUnit || '';
16 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu);
17 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu);
18 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu);
19 var lsu = typo.letterSpacingUnit || 'px';
20 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu);
21 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu);
22 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu);
23 var wsu = typo.wordSpacingUnit || 'px';
24 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu);
25 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu);
26 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu);
27 }
28
29 var ACTIVITY_OPTIONS = [
30 { value: 'sedentary', label: 'Sedentary (little/no exercise)', mult: 1.200 },
31 { value: 'light', label: 'Lightly active (1–3 days/week)', mult: 1.375 },
32 { value: 'moderate', label: 'Moderately active (3–5 days/week)', mult: 1.550 },
33 { value: 'very', label: 'Very active (6–7 days/week)', mult: 1.725 },
34 { value: 'extra', label: 'Extra active (physical job + hard ex)', mult: 1.900 },
35 ];
36
37 var GOAL_OPTIONS = [
38 { value: 'lose2', label: 'Lose weight fast (−1000 kcal)', adj: -1000 },
39 { value: 'lose1', label: 'Lose weight (−500 kcal)', adj: -500 },
40 { value: 'maintain', label: 'Maintain weight', adj: 0 },
41 { value: 'gain1', label: 'Gain weight (+300 kcal)', adj: +300 },
42 { value: 'gain2', label: 'Build muscle (+500 kcal)', adj: +500 },
43 ];
44
45 function calcBMR(gender, weightKg, heightCm, age) {
46 var base = 10 * weightKg + 6.25 * heightCm - 5 * age;
47 return gender === 'female' ? base - 161 : base + 5;
48 }
49
50 function actMult(activity) {
51 var opt = ACTIVITY_OPTIONS.filter(function (o) { return o.value === activity; })[0];
52 return opt ? opt.mult : 1.55;
53 }
54
55 function buildApp(app) {
56 var opts = {};
57 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch (e) {}
58
59 var accent = opts.accentColor || '#6c3fb5';
60 var cardBg = opts.cardBg || '#ffffff';
61 var resultBg = opts.resultBg || '#f5f3ff';
62 var resultBdr= opts.resultBorder || '#ede9fe';
63 var cRadius = (opts.cardRadius || 16) + 'px';
64 var iRadius = (opts.inputRadius || 8) + 'px';
65 var maxW = (opts.maxWidth || 620) + 'px';
66 var ptop = (opts.paddingTop || 60) + 'px';
67 var pbot = (opts.paddingBottom|| 60) + 'px';
68 var tdeeSize = (opts.tdeeSize || 52) + 'px';
69 var titleSz = (opts.titleSize || 26) + 'px';
70 var lclr = opts.labelColor || '#374151';
71 var proPct = opts.proteinPct || 25;
72 var crbPct = opts.carbPct || 45;
73 var fatPct = opts.fatPct || 30;
74
75 /* State */
76 var state = {
77 units: opts.defaultUnits || 'metric',
78 gender: opts.defaultGender || 'male',
79 age: opts.defaultAge || 30,
80 weight: opts.defaultWeight || 70,
81 height: opts.defaultHeight || 170,
82 activity: opts.defaultActivity || 'moderate',
83 goal: 'maintain',
84 };
85
86 /* Card */
87 var card = document.createElement('div');
88 card.className = 'bkbg-cal-card';
89 card.style.cssText = 'background:' + cardBg + ';border-radius:' + cRadius + ';max-width:' + maxW + ';margin:0 auto;padding:' + ptop + ' 32px ' + pbot;
90 app.innerHTML = '';
91 app.appendChild(card);
92 typoCssVarsForEl(opts.typoTitle, '--bkbg-cal-tt-', card);
93 typoCssVarsForEl(opts.typoSub, '--bkbg-cal-ts-', card);
94
95 /* Title */
96 if (opts.showTitle !== false && opts.title) {
97 var h2 = document.createElement('h2');
98 h2.className = 'bkbg-cal-title';
99 h2.style.cssText = 'color:' + (opts.titleColor||'#1e1b4b') + ';margin:0 0 8px';
100 h2.textContent = opts.title;
101 card.appendChild(h2);
102 }
103 if (opts.showSubtitle !== false && opts.subtitle) {
104 var sub = document.createElement('p');
105 sub.className = 'bkbg-cal-subtitle';
106 sub.style.cssText = 'color:' + (opts.subtitleColor||'#6b7280') + ';text-align:center;margin:0 0 24px';
107 sub.textContent = opts.subtitle;
108 card.appendChild(sub);
109 }
110
111 /* Helpers */
112 function mkLabel(txt) {
113 var l = document.createElement('label');
114 l.className = 'bkbg-cal-label';
115 l.style.cssText = 'font-size:13px;font-weight:600;color:' + lclr + ';display:block;margin-bottom:4px';
116 l.textContent = txt;
117 return l;
118 }
119 function mkField(label, input) {
120 var d = document.createElement('div');
121 d.className = 'bkbg-cal-field';
122 d.style.marginBottom = '14px';
123 if (label) d.appendChild(mkLabel(label));
124 d.appendChild(input);
125 return d;
126 }
127
128 var inpCSS = 'width:100%;padding:10px 12px;border:1.5px solid #e5e7eb;border-radius:' + iRadius + ';font-size:15px;box-sizing:border-box;outline:none;background:#fff;color:' + lclr;
129 var selCSS = inpCSS + ';appearance:auto';
130
131 /* Units toggle */
132 var unitsRow = document.createElement('div');
133 unitsRow.className = 'bkbg-cal-units-row';
134 unitsRow.style.cssText = 'display:flex;gap:10px;margin-bottom:14px';
135 var uBtns = {};
136 ['metric','imperial'].forEach(function (u) {
137 var b = document.createElement('button');
138 b.className = 'bkbg-cal-toggle-btn' + (state.units===u?' active':'');
139 b.style.cssText = 'flex:1;padding:10px;border:2px solid '+(state.units===u?accent:'#e5e7eb')+';border-radius:'+iRadius+';background:'+(state.units===u?accent:'#fff')+';color:'+(state.units===u?'#fff':lclr)+';font-weight:600;cursor:pointer;font-size:13px';
140 b.textContent = u === 'metric' ? 'Metric (kg/cm)' : 'Imperial (lbs/in)';
141 b.addEventListener('click', function () { state.units = u; refreshUnitBtns(); updateLabels(); update(); });
142 uBtns[u] = b;
143 unitsRow.appendChild(b);
144 });
145 card.appendChild(unitsRow);
146
147 function refreshUnitBtns() {
148 ['metric','imperial'].forEach(function (u) {
149 var active = state.units === u;
150 uBtns[u].style.background = active ? accent : '#fff';
151 uBtns[u].style.borderColor = active ? accent : '#e5e7eb';
152 uBtns[u].style.color = active ? '#fff' : lclr;
153 });
154 }
155
156 /* Gender toggle */
157 card.appendChild(mkLabel('Gender'));
158 var genderRow = document.createElement('div');
159 genderRow.style.cssText = 'display:flex;gap:10px;margin-bottom:14px';
160 var gBtns = {};
161 ['male','female'].forEach(function (g) {
162 var b = document.createElement('button');
163 b.style.cssText = 'flex:1;padding:10px;border:2px solid '+(state.gender===g?accent:'#e5e7eb')+';border-radius:'+iRadius+';background:'+(state.gender===g?accent:'#fff')+';color:'+(state.gender===g?'#fff':lclr)+';font-weight:600;cursor:pointer;font-size:14px';
164 b.textContent = g === 'male' ? '♂ Male' : '♀ Female';
165 b.addEventListener('click', function () {
166 state.gender = g;
167 ['male','female'].forEach(function (x) {
168 var ac = state.gender === x;
169 gBtns[x].style.background = ac ? accent : '#fff';
170 gBtns[x].style.borderColor = ac ? accent : '#e5e7eb';
171 gBtns[x].style.color = ac ? '#fff' : lclr;
172 });
173 update();
174 });
175 gBtns[g] = b;
176 genderRow.appendChild(b);
177 });
178 card.appendChild(genderRow);
179
180 /* Number inputs */
181 var ageInp = document.createElement('input');
182 ageInp.type = 'number'; ageInp.min = 10; ageInp.max = 100; ageInp.value = state.age; ageInp.className = 'bkbg-cal-input'; ageInp.style.cssText = inpCSS;
183 ageInp.addEventListener('input', function () { state.age = parseInt(ageInp.value)||1; update(); });
184
185 var heightInp = document.createElement('input');
186 heightInp.type = 'number'; heightInp.value = state.height; heightInp.className = 'bkbg-cal-input'; heightInp.style.cssText = inpCSS;
187 heightInp.addEventListener('input', function () { state.height = parseFloat(heightInp.value)||170; update(); });
188
189 var weightInp = document.createElement('input');
190 weightInp.type = 'number'; weightInp.value = state.weight; weightInp.className = 'bkbg-cal-input'; weightInp.style.cssText = inpCSS;
191 weightInp.addEventListener('input', function () { state.weight = parseFloat(weightInp.value)||70; update(); });
192
193 var heightFld = mkField('Height (cm)', heightInp);
194 var weightFld = mkField('Weight (kg)', weightInp);
195
196 function updateLabels() {
197 var wU = state.units === 'imperial' ? 'lbs' : 'kg';
198 var hU = state.units === 'imperial' ? 'in' : 'cm';
199 heightFld.querySelector('label').textContent = 'Height (' + hU + ')';
200 weightFld.querySelector('label').textContent = 'Weight (' + wU + ')';
201 }
202
203 card.appendChild(mkField('Age (years)', ageInp));
204 card.appendChild(heightFld);
205 card.appendChild(weightFld);
206
207 /* Activity */
208 var actSel = document.createElement('select');
209 actSel.className = 'bkbg-cal-select'; actSel.style.cssText = selCSS;
210 ACTIVITY_OPTIONS.forEach(function (o) {
211 var opt = document.createElement('option');
212 opt.value = o.value; opt.textContent = o.label;
213 if (o.value === state.activity) opt.selected = true;
214 actSel.appendChild(opt);
215 });
216 actSel.addEventListener('change', function () { state.activity = actSel.value; update(); });
217 card.appendChild(mkField('Activity level', actSel));
218
219 /* Goal */
220 if (opts.showGoalCalc !== false) {
221 var goalSel = document.createElement('select');
222 goalSel.className = 'bkbg-cal-select'; goalSel.style.cssText = selCSS;
223 GOAL_OPTIONS.forEach(function (o) {
224 var opt = document.createElement('option');
225 opt.value = o.value; opt.textContent = o.label;
226 if (o.value === 'maintain') opt.selected = true;
227 goalSel.appendChild(opt);
228 });
229 goalSel.addEventListener('change', function () { state.goal = goalSel.value; update(); });
230 card.appendChild(mkField('Goal', goalSel));
231 }
232
233 /* Result box */
234 var resultBox = document.createElement('div');
235 resultBox.className = 'bkbg-cal-result';
236 resultBox.style.cssText = 'background:' + resultBg + ';border:1.5px solid ' + resultBdr + ';border-radius:' + cRadius + ';padding:24px;text-align:center;margin-bottom:20px';
237
238 var resultLbl = document.createElement('div');
239 resultLbl.className = 'bkbg-cal-result-label';
240 resultLbl.style.cssText = 'font-size:13px;color:#9ca3af;text-transform:uppercase;letter-spacing:0.06em;margin-bottom:4px';
241 resultLbl.textContent = 'Daily Calories (TDEE)';
242 resultBox.appendChild(resultLbl);
243
244 var tdeeNum = document.createElement('div');
245 tdeeNum.className = 'bkbg-cal-tdee';
246 tdeeNum.style.cssText = 'font-size:' + tdeeSize + ';font-weight:800;color:' + (opts.tdeeColor||accent) + ';line-height:1;margin-bottom:4px';
247 resultBox.appendChild(tdeeNum);
248
249 var kcalLbl = document.createElement('div');
250 kcalLbl.className = 'bkbg-cal-kcal';
251 kcalLbl.style.cssText = 'font-size:13px;color:#9ca3af';
252 kcalLbl.textContent = 'kcal / day';
253 resultBox.appendChild(kcalLbl);
254
255 var bmrLbl = document.createElement('div');
256 bmrLbl.className = 'bkbg-cal-bmr';
257 bmrLbl.style.cssText = 'margin-top:12px;font-size:13px;color:' + (opts.bmrColor||'#8b5cf6');
258 if (opts.showBMR !== false) resultBox.appendChild(bmrLbl);
259
260 card.appendChild(resultBox);
261
262 /* Macros */
263 var macroSection;
264 var macroBar, proNum, crbNum, fatNum;
265 if (opts.showMacros !== false) {
266 macroSection = document.createElement('div');
267
268 macroBar = document.createElement('div');
269 macroBar.className = 'bkbg-cal-macro-bar';
270 macroBar.style.cssText = 'display:flex;border-radius:8px;overflow:hidden;height:12px;margin-bottom:16px';
271 var barPro = document.createElement('div');
272 barPro.style.cssText = 'flex:' + proPct + ';background:' + (opts.proColor||'#ef4444');
273 var barCrb = document.createElement('div');
274 barCrb.style.cssText = 'flex:' + crbPct + ';background:' + (opts.carbColor||'#f59e0b');
275 var barFat = document.createElement('div');
276 barFat.style.cssText = 'flex:' + fatPct + ';background:' + (opts.fatColor||'#3b82f6');
277 macroBar.appendChild(barPro); macroBar.appendChild(barCrb); macroBar.appendChild(barFat);
278 macroSection.appendChild(macroBar);
279
280 var macroGrid = document.createElement('div');
281 macroGrid.className = 'bkbg-cal-macros';
282 macroGrid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px';
283
284 function mkMacroCard(name, color, pct) {
285 var mc = document.createElement('div');
286 mc.className = 'bkbg-cal-macro-card';
287 mc.style.cssText = 'text-align:center;background:#fff;border-radius:10px;padding:12px 8px;border:1.5px solid #e5e7eb';
288 mc.innerHTML = '<div class="bkbg-cal-macro-name" style="font-size:11px;text-transform:uppercase;letter-spacing:0.05em;color:#9ca3af;margin-bottom:4px">' + name + '</div>' +
289 '<div class="bkbg-cal-macro-num" style="font-size:24px;font-weight:800;line-height:1;color:' + color + '">—g</div>' +
290 '<div class="bkbg-cal-macro-pct" style="font-size:12px;color:#9ca3af;margin-top:2px">' + pct + '%</div>';
291 return mc;
292 }
293
294 var proCrd = mkMacroCard('Protein', opts.proColor||'#ef4444', proPct);
295 var crbCrd = mkMacroCard('Carbs', opts.carbColor||'#f59e0b', crbPct);
296 var fatCrd = mkMacroCard('Fat', opts.fatColor||'#3b82f6', fatPct);
297 proNum = proCrd.querySelector('.bkbg-cal-macro-num');
298 crbNum = crbCrd.querySelector('.bkbg-cal-macro-num');
299 fatNum = fatCrd.querySelector('.bkbg-cal-macro-num');
300 macroGrid.appendChild(proCrd); macroGrid.appendChild(crbCrd); macroGrid.appendChild(fatCrd);
301 macroSection.appendChild(macroGrid);
302 card.appendChild(macroSection);
303 }
304
305 /* Update function */
306 function update() {
307 var wkg = state.units === 'imperial' ? state.weight * 0.453592 : state.weight;
308 var hcm = state.units === 'imperial' ? state.height * 2.54 : state.height;
309 var bmr = Math.round(calcBMR(state.gender, wkg, hcm, state.age));
310 var tdee = Math.round(bmr * actMult(state.activity));
311 var gOpt = GOAL_OPTIONS.filter(function (o) { return o.value === (state.goal||'maintain'); })[0];
312 var adj = gOpt ? gOpt.adj : 0;
313 var target = tdee + adj;
314 tdeeNum.textContent = target.toLocaleString();
315 if (opts.showBMR !== false) bmrLbl.textContent = 'Basal Metabolic Rate (BMR): ' + bmr.toLocaleString() + ' kcal/day';
316 if (opts.showMacros !== false) {
317 proNum.textContent = Math.round(target * proPct / 100 / 4) + 'g';
318 crbNum.textContent = Math.round(target * crbPct / 100 / 4) + 'g';
319 fatNum.textContent = Math.round(target * fatPct / 100 / 9) + 'g';
320 }
321 }
322
323 update();
324 }
325
326 document.querySelectorAll('.bkbg-cal-app').forEach(buildApp);
327 })();
328