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

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

255 lines 12.2 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 var _typoKeys = {
5 family:'font-family', weight:'font-weight', style:'font-style',
6 decoration:'text-decoration', transform:'text-transform',
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 };
12 function typoCssVarsForEl(el, obj, prefix) {
13 if (!obj || typeof obj !== 'object') return;
14 Object.keys(_typoKeys).forEach(function (k) {
15 var v = obj[k];
16 if (v === undefined || v === '' || v === null) return;
17 if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px');
18 else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || '');
19 else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px');
20 else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px');
21 el.style.setProperty(prefix + _typoKeys[k], String(v));
22 });
23 }
24
25 function calcROI(investment, returned, years) {
26 var netProfit = returned - investment;
27 var roi = investment > 0 ? (netProfit / investment) * 100 : 0;
28 var annualized = investment > 0 && years > 0 && returned > 0
29 ? (Math.pow(returned / investment, 1 / years) - 1) * 100
30 : 0;
31 var payback = netProfit > 0 && years > 0
32 ? investment / (netProfit / years)
33 : null;
34 return { netProfit: netProfit, roi: roi, annualized: annualized, payback: payback };
35 }
36
37 function fmt(n, cur) {
38 var abs = Math.abs(n);
39 return (n < 0 ? '-' : '') + cur + abs.toLocaleString('en-US', {minimumFractionDigits:0, maximumFractionDigits:0});
40 }
41 function fmtPct(n) { return (n >= 0 ? '+' : '') + n.toFixed(2) + '%'; }
42
43 function initROI(app) {
44 var opts;
45 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch(e) { opts = {}; }
46
47 var accent = opts.accentColor || '#6c3fb5';
48 var cardBg = opts.cardBg || '#ffffff';
49 var resultBg = opts.resultBg || accent;
50 var resultColor = opts.resultColor || '#ffffff';
51 var positiveColor = opts.positiveColor || '#10b981';
52 var negativeColor = opts.negativeColor || '#ef4444';
53 var statBg = opts.statBg || '#f3f4f6';
54 var statBorder = opts.statBorder || '#e5e7eb';
55 var statValueColor= opts.statValueColor || '#111827';
56 var statLabelColor= opts.statLabelColor || '#6b7280';
57 var inputBorder = opts.inputBorder || '#e5e7eb';
58 var labelColor = opts.labelColor || '#374151';
59 var titleColor = opts.titleColor || '';
60 var subtitleColor = opts.subtitleColor || '';
61 var sectionBg = opts.sectionBg || '';
62 var cur = opts.currency || '$';
63
64 var titleSize = (opts.titleSize || 28) + 'px';
65 var resultSize = (opts.resultSize || 48) + 'px';
66 var cardRadius = (opts.cardRadius || 16) + 'px';
67 var inputRadius = (opts.inputRadius || 8) + 'px';
68 var maxWidth = (opts.maxWidth || 560) + 'px';
69 var paddingTop = (opts.paddingTop != null ? opts.paddingTop : 60) + 'px';
70 var paddingBottom = (opts.paddingBottom != null ? opts.paddingBottom : 60) + 'px';
71
72 var showPayback = opts.showPayback !== false;
73 var showAnnualized = opts.showAnnualized !== false;
74 var showBreakdown = opts.showBreakdown !== false;
75
76 app.style.setProperty('--bkbg-roi-accent', accent);
77 typoCssVarsForEl(app, opts.titleTypo, '--bkroi-tt-');
78 typoCssVarsForEl(app, opts.subtitleTypo, '--bkroi-st-');
79 app.style.paddingTop = paddingTop;
80 app.style.paddingBottom = paddingBottom;
81 if (sectionBg) app.style.background = sectionBg;
82
83 var wrap = document.createElement('div');
84 wrap.className = 'bkbg-roi-wrap';
85 wrap.style.cssText = 'background:' + cardBg + ';border-radius:' + cardRadius + ';max-width:' + maxWidth;
86 app.appendChild(wrap);
87
88 // Header
89 if ((opts.showTitle && opts.title) || (opts.showSubtitle && opts.subtitle)) {
90 var hdr = document.createElement('div');
91 hdr.className = 'bkbg-roi-header';
92 if (opts.showTitle && opts.title) {
93 var t = document.createElement('div');
94 t.className = 'bkbg-roi-title';
95 if (titleColor) t.style.color = titleColor;
96 t.textContent = opts.title;
97 hdr.appendChild(t);
98 }
99 if (opts.showSubtitle && opts.subtitle) {
100 var s = document.createElement('div');
101 s.className = 'bkbg-roi-subtitle';
102 if (subtitleColor) s.style.color = subtitleColor;
103 s.textContent = opts.subtitle;
104 hdr.appendChild(s);
105 }
106 wrap.appendChild(hdr);
107 }
108
109 function makeInput(labelText, defaultVal, hasCur, step) {
110 var field = document.createElement('div');
111 field.className = 'bkbg-roi-field';
112 var lbl = document.createElement('label');
113 lbl.className = 'bkbg-roi-label';
114 lbl.style.color = labelColor;
115 lbl.textContent = labelText;
116 var wrap2 = document.createElement('div');
117 wrap2.className = 'bkbg-roi-input-wrap';
118 if (hasCur) {
119 var pfx = document.createElement('span');
120 pfx.className = 'bkbg-roi-prefix';
121 pfx.textContent = cur;
122 wrap2.appendChild(pfx);
123 }
124 var inp = document.createElement('input');
125 inp.type = 'number'; inp.value = defaultVal; inp.min = '0';
126 if (step) inp.step = step;
127 inp.className = 'bkbg-roi-input' + (hasCur ? ' has-prefix' : '');
128 inp.style.cssText = 'border:1.5px solid ' + inputBorder + ';border-radius:' + inputRadius;
129 wrap2.appendChild(inp);
130 field.appendChild(lbl);
131 field.appendChild(wrap2);
132 return { field: field, inp: inp };
133 }
134
135 // Row 1: investment + return
136 var grid = document.createElement('div');
137 grid.className = 'bkbg-roi-grid';
138 var invF = makeInput('Initial Investment', opts.defaultInvestment || 10000, true);
139 var retF = makeInput('Total Return', opts.defaultReturn || 15000, true);
140 grid.appendChild(invF.field);
141 grid.appendChild(retF.field);
142 wrap.appendChild(grid);
143
144 // Row 2: years (full width)
145 var yrsWrap = document.createElement('div');
146 yrsWrap.className = 'bkbg-roi-fullrow';
147 var yrsLbl = document.createElement('label');
148 yrsLbl.className = 'bkbg-roi-label';
149 yrsLbl.style.color = labelColor;
150 yrsLbl.textContent = 'Investment Period (Years)';
151 var yrsInp = document.createElement('input');
152 yrsInp.type = 'number'; yrsInp.value = opts.defaultYears || 3;
153 yrsInp.min = '0.1'; yrsInp.step = '0.5';
154 yrsInp.className = 'bkbg-roi-input';
155 yrsInp.style.cssText = 'border:1.5px solid ' + inputBorder + ';border-radius:' + inputRadius;
156 yrsWrap.appendChild(yrsLbl);
157 yrsWrap.appendChild(yrsInp);
158 wrap.appendChild(yrsWrap);
159
160 // Result card
161 var resultCard = document.createElement('div');
162 resultCard.className = 'bkbg-roi-result-card';
163 resultCard.style.cssText = 'background:' + resultBg + ';color:' + resultColor;
164 var rLbl = document.createElement('div');
165 rLbl.className = 'bkbg-roi-result-label';
166 rLbl.textContent = 'Return on Investment';
167 var rVal = document.createElement('div');
168 rVal.className = 'bkbg-roi-result-value';
169 rVal.style.fontSize = resultSize;
170 var rSub = document.createElement('div');
171 rSub.className = 'bkbg-roi-result-sub';
172 resultCard.appendChild(rLbl);
173 resultCard.appendChild(rVal);
174 resultCard.appendChild(rSub);
175 wrap.appendChild(resultCard);
176
177 // Breakdown stats
178 var statsRow, statInv, statNet, statTotal;
179 if (showBreakdown) {
180 statsRow = document.createElement('div');
181 statsRow.className = 'bkbg-roi-stats';
182 function makeStat(lbl) {
183 var card = document.createElement('div');
184 card.className = 'bkbg-roi-stat';
185 card.style.cssText = 'background:' + statBg + ';border:1px solid ' + statBorder;
186 var v = document.createElement('div'); v.className = 'bkbg-roi-stat-val'; v.style.color = statValueColor;
187 var l = document.createElement('div'); l.className = 'bkbg-roi-stat-lbl'; l.style.color = statLabelColor;
188 l.textContent = lbl;
189 card.appendChild(v); card.appendChild(l);
190 return { card: card, val: v };
191 }
192 statInv = makeStat('Invested');
193 statNet = makeStat('Net Profit');
194 statTotal = makeStat('Total Return');
195 statsRow.appendChild(statInv.card);
196 statsRow.appendChild(statNet.card);
197 statsRow.appendChild(statTotal.card);
198 wrap.appendChild(statsRow);
199 }
200
201 // Extras
202 var extrasRow, statAnn, statPay;
203 if (showAnnualized || showPayback) {
204 extrasRow = document.createElement('div');
205 extrasRow.className = 'bkbg-roi-extras';
206 function makeExtra(lbl) {
207 var card = document.createElement('div');
208 card.className = 'bkbg-roi-stat';
209 card.style.cssText = 'flex:1;background:' + statBg + ';border:1px solid ' + statBorder;
210 var v = document.createElement('div'); v.className = 'bkbg-roi-stat-val'; v.style.color = statValueColor;
211 var l = document.createElement('div'); l.className = 'bkbg-roi-stat-lbl'; l.style.color = statLabelColor;
212 l.textContent = lbl;
213 card.appendChild(v); card.appendChild(l);
214 return { card: card, val: v };
215 }
216 if (showAnnualized) { statAnn = makeExtra('Annualized ROI'); extrasRow.appendChild(statAnn.card); }
217 if (showPayback) { statPay = makeExtra('Payback Period'); extrasRow.appendChild(statPay.card); }
218 wrap.appendChild(extrasRow);
219 }
220
221 function refresh() {
222 var inv = parseFloat(invF.inp.value) || 0;
223 var ret = parseFloat(retF.inp.value) || 0;
224 var yrs = parseFloat(yrsInp.value) || 1;
225 var r = calcROI(inv, ret, yrs);
226 var pos = r.netProfit >= 0;
227
228 rVal.textContent = fmtPct(r.roi);
229 rSub.textContent = 'Net Profit: ' + fmt(r.netProfit, cur);
230 resultCard.style.background = pos ? resultBg : negativeColor;
231
232 if (showBreakdown) {
233 statInv.val.textContent = fmt(inv, cur);
234 statNet.val.textContent = fmt(r.netProfit, cur);
235 statNet.val.style.color = pos ? positiveColor : negativeColor;
236 statTotal.val.textContent = fmt(ret, cur);
237 }
238 if (showAnnualized && statAnn) {
239 statAnn.val.textContent = fmtPct(r.annualized);
240 statAnn.val.style.color = pos ? positiveColor : negativeColor;
241 }
242 if (showPayback && statPay) {
243 statPay.val.textContent = r.payback != null ? r.payback.toFixed(1) + ' yr' : '';
244 }
245 }
246
247 [invF.inp, retF.inp, yrsInp].forEach(function(inp) {
248 inp.addEventListener('input', refresh);
249 });
250 refresh();
251 }
252
253 document.querySelectorAll('.bkbg-roi-app').forEach(initROI);
254 })();
255