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

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

306 lines 14.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 || typeof typo !== 'object') return;
6 var m = { family:'font-family', weight:'font-weight', transform:'text-transform', style:'font-style', decoration:'text-decoration',
7 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
8 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
9 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
10 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' };
11 Object.keys(m).forEach(function (k) {
12 if (typo[k] !== undefined && typo[k] !== '') {
13 var v = typo[k], u = typo[k + 'Unit'] || '';
14 if (/Desktop|Tablet|Mobile/.test(k) && typeof v === 'number') v = v + (u || 'px');
15 el.style.setProperty(prefix + m[k], '' + v);
16 }
17 });
18 }
19
20 function calcSalePrice(orig, pct) {
21 var savings = orig * (pct / 100);
22 var sale = orig - savings;
23 return { orig: orig, sale: sale, savings: savings, pct: pct };
24 }
25 function calcDiscountPct(orig, sale) {
26 if (orig <= 0) return { orig: orig, sale: sale, savings: 0, pct: 0 };
27 var savings = orig - sale;
28 var pct = (savings / orig) * 100;
29 return { orig: orig, sale: sale, savings: savings, pct: pct };
30 }
31 function calcOrigPrice(sale, pct) {
32 if (pct >= 100) return { orig: sale, sale: sale, savings: 0, pct: pct };
33 var orig = sale / (1 - pct / 100);
34 var savings = orig - sale;
35 return { orig: orig, sale: sale, savings: savings, pct: pct };
36 }
37
38 function fmtM(val, cur, pos) {
39 var s = parseFloat(val).toFixed(2);
40 return pos === 'after' ? s + cur : cur + s;
41 }
42
43 function makeEl(tag, attrs, children) {
44 var el = document.createElement(tag);
45 if (attrs) {
46 Object.keys(attrs).forEach(function (k) {
47 if (k === 'className') { el.className = attrs[k]; }
48 else if (k === 'style') { Object.assign(el.style, attrs[k]); }
49 else if (k.startsWith('data-')) { el.setAttribute(k, attrs[k]); }
50 else { el[k] = attrs[k]; }
51 });
52 }
53 if (children) {
54 (Array.isArray(children) ? children : [children]).forEach(function (c) {
55 if (c == null || c === false) return;
56 el.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
57 });
58 }
59 return el;
60 }
61
62 var MODE_LABELS = {
63 sale_price: 'Find Sale Price',
64 discount_pct: 'Find Discount %',
65 orig_price: 'Find Original Price'
66 };
67
68 function buildApp(app) {
69 var opts = {};
70 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch (e) {}
71
72 var o = opts;
73 var mode = o.mode || 'sale_price';
74 var currency = o.currency || '$';
75 var curPos = o.currencyPos || 'before';
76 var origVal = parseFloat(o.defaultOriginal) || 200;
77 var pctVal = parseFloat(o.defaultDiscount) || 25;
78 var saleVal = parseFloat(o.defaultSale) || 150;
79
80 app.innerHTML = '';
81 app.style.fontFamily = 'inherit';
82 app.style.boxSizing = 'border-box';
83
84 /* Typography CSS vars */
85 app.style.setProperty('--bkbg-disc-ttl-fs', (o.titleSize || 28) + 'px');
86 if (o.typoTitle) typoCssVarsForEl(o.typoTitle, '--bkbg-disc-ttl-', app);
87 if (o.typoSubtitle) typoCssVarsForEl(o.typoSubtitle, '--bkbg-disc-sub-', app);
88
89 // Section wrapper
90 var section = makeEl('div', { style: {
91 paddingTop: (o.paddingTop || 60) + 'px',
92 paddingBottom: (o.paddingBottom || 60) + 'px',
93 background: o.sectionBg || ''
94 }});
95
96 var card = makeEl('div', { className: 'bkbg-disc-card', style: {
97 background: o.cardBg || '#ffffff',
98 borderRadius: (o.cardRadius || 16) + 'px',
99 padding: '36px 32px',
100 maxWidth: (o.maxWidth || 520) + 'px',
101 margin: '0 auto',
102 boxShadow: '0 4px 24px rgba(0,0,0,0.09)'
103 }});
104 section.appendChild(card);
105 app.appendChild(section);
106
107 // Title / subtitle
108 if (o.showTitle || o.showSubtitle) {
109 var hdr = makeEl('div', { style: { marginBottom: '20px' } });
110 if (o.showTitle) {
111 hdr.appendChild(makeEl('div', { className: 'bkbg-disc-title', style: {
112 color: o.titleColor || '#1e1b4b'
113 }}, [o.title || 'Discount Calculator']));
114 }
115 if (o.showSubtitle) {
116 hdr.appendChild(makeEl('div', { className: 'bkbg-disc-subtitle', style: {
117 color: o.subtitleColor || '#6b7280'
118 }}, [o.subtitle || '']));
119 }
120 card.appendChild(hdr);
121 }
122
123 // Mode tabs
124 var tabRow = makeEl('div', { style: { display: 'flex', gap: '8px', marginBottom: '24px', flexWrap: 'wrap' } });
125 var modeBtns = {};
126 ['sale_price', 'discount_pct', 'orig_price'].forEach(function (m) {
127 var btn = makeEl('button', { type: 'button', className: 'bkbg-disc-mode-btn', style: {
128 flex: '1 1 auto',
129 padding: '9px 14px',
130 borderRadius: '8px',
131 border: '2px solid ' + (mode === m ? (o.accentColor || '#6c3fb5') : '#e5e7eb'),
132 background: mode === m ? (o.accentColor || '#6c3fb5') : 'transparent',
133 color: mode === m ? '#fff' : (o.accentColor || '#6c3fb5'),
134 fontWeight: 600, fontSize: '13px', cursor: 'pointer', transition: 'all .15s'
135 }}, [MODE_LABELS[m]]);
136 modeBtns[m] = btn;
137 btn.addEventListener('click', function () {
138 mode = m;
139 refreshTabs();
140 buildInputs();
141 update();
142 });
143 tabRow.appendChild(btn);
144 });
145 card.appendChild(tabRow);
146
147 // Inputs container
148 var inputsContainer = makeEl('div', { style: { marginBottom: '24px' } });
149 card.appendChild(inputsContainer);
150
151 // Result card
152 var resultCard = makeEl('div', { className: 'bkbg-disc-result', style: {
153 background: o.resultBg || '#f5f3ff',
154 border: '2px solid ' + (o.resultBorder || '#ede9fe'),
155 borderRadius: (o.cardRadius || 16) + 'px',
156 padding: '24px 28px',
157 textAlign: 'center'
158 }});
159 card.appendChild(resultCard);
160
161 // Shared input references
162 var origInput, pctInput, saleInput;
163
164 function makeMoneyInput(labelText, initVal, onCh) {
165 var row = makeEl('div', { className: 'bkbg-disc-input-row', style: { marginBottom: '14px' } });
166 var lbl = makeEl('label', { style: { display: 'block', fontSize: '13px', fontWeight: 600, color: o.labelColor || '#374151', marginBottom: '5px' } }, [labelText]);
167 row.appendChild(lbl);
168 var flex = makeEl('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } });
169 if (curPos === 'before') {
170 flex.appendChild(makeEl('span', { style: { color: o.labelColor || '#374151', fontWeight: 700, fontSize: '16px' } }, [currency]));
171 }
172 var inp = makeEl('input', { type: 'number', value: initVal, min: '0', step: '0.01', style: {
173 flex: '1', padding: '10px 14px',
174 borderRadius: (o.inputRadius || 8) + 'px',
175 border: '1.5px solid #e5e7eb', fontSize: '16px', outline: 'none'
176 }});
177 inp.addEventListener('focus', function () { inp.style.borderColor = o.accentColor || '#6c3fb5'; inp.style.boxShadow = '0 0 0 3px rgba(108,63,181,.15)'; });
178 inp.addEventListener('blur', function () { inp.style.borderColor = '#e5e7eb'; inp.style.boxShadow = ''; });
179 inp.addEventListener('input', function () { onCh(parseFloat(inp.value) || 0); update(); });
180 flex.appendChild(inp);
181 if (curPos === 'after') {
182 flex.appendChild(makeEl('span', { style: { color: o.labelColor || '#374151', fontWeight: 700, fontSize: '16px' } }, [currency]));
183 }
184 row.appendChild(flex);
185 return { row: row, inp: inp };
186 }
187
188 function makePctInput(labelText, initVal, onCh) {
189 var row = makeEl('div', { className: 'bkbg-disc-input-row', style: { marginBottom: '14px' } });
190 var lbl = makeEl('label', { style: { display: 'block', fontSize: '13px', fontWeight: 600, color: o.labelColor || '#374151', marginBottom: '5px' } }, [labelText]);
191 row.appendChild(lbl);
192 var flex = makeEl('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } });
193 var inp = makeEl('input', { type: 'number', value: initVal, min: '0', max: '100', step: '0.1', style: {
194 flex: '1', padding: '10px 14px',
195 borderRadius: (o.inputRadius || 8) + 'px',
196 border: '1.5px solid #e5e7eb', fontSize: '16px', outline: 'none'
197 }});
198 inp.addEventListener('focus', function () { inp.style.borderColor = o.accentColor || '#6c3fb5'; inp.style.boxShadow = '0 0 0 3px rgba(108,63,181,.15)'; });
199 inp.addEventListener('blur', function () { inp.style.borderColor = '#e5e7eb'; inp.style.boxShadow = ''; });
200 inp.addEventListener('input', function () { onCh(parseFloat(inp.value) || 0); update(); });
201 flex.appendChild(inp);
202 flex.appendChild(makeEl('span', { style: { color: o.labelColor || '#374151', fontWeight: 700, fontSize: '16px' } }, ['%']));
203 row.appendChild(flex);
204 return { row: row, inp: inp };
205 }
206
207 function buildInputs() {
208 inputsContainer.innerHTML = '';
209 origInput = pctInput = saleInput = null;
210
211 if (mode === 'sale_price' || mode === 'discount_pct') {
212 var origPair = makeMoneyInput('Original Price', origVal, function (v) { origVal = v; });
213 inputsContainer.appendChild(origPair.row);
214 origInput = origPair.inp;
215 }
216 if (mode === 'sale_price' || mode === 'orig_price') {
217 var pctPair = makePctInput('Discount Percentage', pctVal, function (v) { pctVal = v; });
218 inputsContainer.appendChild(pctPair.row);
219 pctInput = pctPair.inp;
220 }
221 if (mode === 'discount_pct' || mode === 'orig_price') {
222 var salePair = makeMoneyInput('Sale Price', saleVal, function (v) { saleVal = v; });
223 inputsContainer.appendChild(salePair.row);
224 saleInput = salePair.inp;
225 }
226 }
227
228 function getResult() {
229 if (mode === 'sale_price') return calcSalePrice(origVal, pctVal);
230 if (mode === 'discount_pct') return calcDiscountPct(origVal, saleVal);
231 return calcOrigPrice(saleVal, pctVal);
232 }
233
234 function update() {
235 var r = getResult();
236 var badgePct = Math.round(r.pct * 10) / 10;
237 var savingsFrac = r.orig > 0 ? Math.min(1, r.savings / r.orig) : 0;
238 var accentC = o.accentColor || '#6c3fb5';
239 var saleSz = o.salePriceSize || 56;
240 var salePriceC = o.salePriceColor || '#6c3fb5';
241 var origPriceC = o.origPriceColor || '#9ca3af';
242 var savingsC = o.savingsColor || '#10b981';
243 var badgeBg = o.badgeBg || '#ef4444';
244 var badgeClr = o.badgeColor || '#ffffff';
245 var barFillC = o.barFill || '#10b981';
246 var barBgC = o.barBg || '#e5e7eb';
247 var labelC = o.labelColor || '#374151';
248
249 resultCard.innerHTML = '';
250
251 if (o.showBadge && r.pct > 0) {
252 resultCard.appendChild(makeEl('div', { className: 'bkbg-disc-badge', style: {
253 background: badgeBg, color: badgeClr,
254 fontWeight: 800, fontSize: '14px',
255 padding: '4px 14px', borderRadius: '100px',
256 marginBottom: '14px', display: 'inline-block', letterSpacing: '.03em'
257 }}, [badgePct + '% OFF']));
258 }
259
260 resultCard.appendChild(makeEl('div', { className: 'bkbg-disc-sale-price', style: {
261 fontSize: saleSz + 'px', fontWeight: 800,
262 color: salePriceC, lineHeight: '1.1', marginBottom: '6px'
263 }}, [fmtM(r.sale, currency, curPos)]));
264
265 if (r.orig !== r.sale) {
266 resultCard.appendChild(makeEl('div', { className: 'bkbg-disc-orig-price', style: {
267 fontSize: Math.round(saleSz * 0.4) + 'px',
268 color: origPriceC, textDecoration: 'line-through', marginBottom: '10px'
269 }}, [fmtM(r.orig, currency, curPos)]));
270 }
271
272 if (r.savings > 0) {
273 resultCard.appendChild(makeEl('div', { style: {
274 fontSize: '16px', fontWeight: 700, color: savingsC, marginBottom: '16px'
275 }}, ['You Save: ' + fmtM(r.savings, currency, curPos)]));
276 }
277
278 if (o.showSavingsBar && r.orig > 0) {
279 var barLabels = makeEl('div', { style: { fontSize: '12px', color: labelC, marginBottom: '6px', display: 'flex', justifyContent: 'space-between' } },
280 [makeEl('span', null, ['Original']), makeEl('span', null, ['Savings'])]);
281 resultCard.appendChild(barLabels);
282 var track = makeEl('div', { className: 'bkbg-disc-bar-track', style: { height: '10px', borderRadius: '100px', background: barBgC, overflow: 'hidden' } });
283 var fill = makeEl('div', { className: 'bkbg-disc-bar-fill', style: { height: '100%', width: '0%', background: barFillC, borderRadius: '100px', transition: 'width .35s' } });
284 track.appendChild(fill);
285 resultCard.appendChild(track);
286 setTimeout(function () { fill.style.width = (savingsFrac * 100).toFixed(1) + '%'; }, 30);
287 }
288 }
289
290 function refreshTabs() {
291 ['sale_price', 'discount_pct', 'orig_price'].forEach(function (m) {
292 var btn = modeBtns[m];
293 var active = m === mode;
294 btn.style.background = active ? (o.accentColor || '#6c3fb5') : 'transparent';
295 btn.style.color = active ? '#fff' : (o.accentColor || '#6c3fb5');
296 btn.style.borderColor = active ? (o.accentColor || '#6c3fb5') : '#e5e7eb';
297 });
298 }
299
300 buildInputs();
301 update();
302 }
303
304 document.querySelectorAll('.bkbg-disc-app').forEach(buildApp);
305 })();
306