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

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

254 lines 14.9 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 var BRACKETS = {
26 single: [
27 { lo: 0, hi: 11925, rate: 0.10 },
28 { lo: 11925, hi: 48475, rate: 0.12 },
29 { lo: 48475, hi: 103350, rate: 0.22 },
30 { lo: 103350, hi: 197300, rate: 0.24 },
31 { lo: 197300, hi: 250525, rate: 0.32 },
32 { lo: 250525, hi: 626350, rate: 0.35 },
33 { lo: 626350, hi: Infinity, rate: 0.37 }
34 ],
35 married: [
36 { lo: 0, hi: 23850, rate: 0.10 },
37 { lo: 23850, hi: 96950, rate: 0.12 },
38 { lo: 96950, hi: 206700, rate: 0.22 },
39 { lo: 206700, hi: 394600, rate: 0.24 },
40 { lo: 394600, hi: 501050, rate: 0.32 },
41 { lo: 501050, hi: 751600, rate: 0.35 },
42 { lo: 751600, hi: Infinity, rate: 0.37 }
43 ],
44 hoh: [
45 { lo: 0, hi: 17000, rate: 0.10 },
46 { lo: 17000, hi: 64850, rate: 0.12 },
47 { lo: 64850, hi: 103350, rate: 0.22 },
48 { lo: 103350, hi: 197300, rate: 0.24 },
49 { lo: 197300, hi: 250500, rate: 0.32 },
50 { lo: 250500, hi: 626350, rate: 0.35 },
51 { lo: 626350, hi: Infinity, rate: 0.37 }
52 ]
53 };
54 var STD_DED = { single: 15000, married: 30000, hoh: 22500 };
55 var SS_CAP = 176100;
56
57 function calcFedTax(taxable, filing) {
58 var brackets = BRACKETS[filing] || BRACKETS.single;
59 var tax = 0;
60 for (var i = 0; i < brackets.length; i++) {
61 var b = brackets[i];
62 if (taxable <= b.lo) break;
63 tax += (Math.min(taxable, b.hi) - b.lo) * b.rate;
64 }
65 return Math.max(0, tax);
66 }
67
68 function toAnnual(val, period) {
69 if (period === 'monthly') return val * 12;
70 if (period === 'hourly') return val * 2080;
71 return val;
72 }
73
74 function calcAll(income, period, filing, stateTaxPct, k401Pct, otherDed) {
75 var gross = toAnnual(parseFloat(income) || 0, period);
76 var ded401k = gross * ((parseFloat(k401Pct) || 0) / 100);
77 var other = parseFloat(otherDed) || 0;
78 var preTax = ded401k + other;
79 var taxable = Math.max(0, gross - preTax - (STD_DED[filing] || 15000));
80 var fedTax = calcFedTax(taxable, filing);
81 var stateTax = (gross - preTax) * ((parseFloat(stateTaxPct) || 0) / 100);
82 var ss = Math.min(gross, SS_CAP) * 0.062;
83 var medicare = gross * 0.0145;
84 var totalTax = fedTax + stateTax + ss + medicare;
85 var netAnnual = Math.max(0, gross - totalTax - preTax);
86 return {
87 gross: gross, preTax: preTax, ded401k: ded401k, otherDed: other,
88 fedTax: fedTax, stateTax: stateTax, ss: ss, medicare: medicare,
89 totalTax: totalTax, netAnnual: netAnnual,
90 effectiveRate: gross > 0 ? (totalTax / gross) * 100 : 0
91 };
92 }
93
94 function fmt(n, cur) {
95 return (cur || '$') + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
96 }
97
98 function pct(n, base) {
99 return base > 0 ? Math.min(100, (n / base) * 100) : 0;
100 }
101
102 document.querySelectorAll('.bkbg-slc-app').forEach(function (root) {
103 var opts = {};
104 try { opts = JSON.parse(root.getAttribute('data-opts') || '{}'); } catch(e) {}
105
106 var cur = opts.currency || '$';
107 var showBreakdown = opts.showBreakdown !== false;
108 var showAllPeriods = opts.showAllPeriods !== false;
109 var showEffective = opts.showEffectiveRate !== false;
110 var accentColor = opts.accentColor || '#0ea5e9';
111 var taxColor = opts.taxColor || '#ef4444';
112 var dedColor = opts.dedColor || '#f59e0b';
113 var sectionBg = opts.sectionBg || '#f0f9ff';
114 var cardBg = opts.cardBg || '#ffffff';
115 var inputBg = opts.inputBg || '#f9fafb';
116 var resultBg = opts.resultBg || '#e0f2fe';
117 var titleColor = opts.titleColor || '#0c4a6e';
118 var subtitleColor = opts.subtitleColor || '#6b7280';
119 var labelColor = opts.labelColor || '#374151';
120
121 var state = {
122 income: opts.defaultIncome || 75000,
123 period: opts.defaultPeriod || 'annual',
124 filing: opts.defaultFiling || 'single',
125 stateTax: opts.defaultStateTax != null ? opts.defaultStateTax : 5,
126 k401: opts.default401k != null ? opts.default401k : 6,
127 otherDed: opts.defaultOtherDed || 0
128 };
129
130 var FILING_LABELS = { single: 'Single', married: 'Married (Joint)', hoh: 'Head of Household' };
131 var PERIOD_LABELS = { annual: 'Annual', monthly: 'Monthly', hourly: 'Hourly' };
132
133 function brow(label, val, barPct, color) {
134 return '<div class="bkbg-slc-brow">' +
135 '<span class="bkbg-slc-brow-label" style="color:' + labelColor + ';">' + label + '</span>' +
136 '<div class="bkbg-slc-brow-bar-wrap"><div class="bkbg-slc-brow-bar" style="width:' + barPct.toFixed(1) + '%;background:' + color + ';"></div></div>' +
137 '<span class="bkbg-slc-brow-val" style="color:' + color + ';">' + val + '</span>' +
138 '</div>';
139 }
140
141 function render() {
142 var r = calcAll(state.income, state.period, state.filing, state.stateTax, state.k401, state.otherDed);
143
144 root.innerHTML =
145 '<div class="bkbg-slc-card" style="background:' + cardBg + ';max-width:' + (opts.contentMaxWidth || 720) + 'px;">' +
146 '<h2 class="bkbg-slc-title" style="color:' + titleColor + ';">' + (opts.title || 'Salary &amp; Take-Home Pay Calculator') + '</h2>' +
147 '<p class="bkbg-slc-subtitle" style="color:' + subtitleColor + ';">' + (opts.subtitle || 'Estimate your net pay after taxes and deductions') + '</p>' +
148
149 '<div class="bkbg-slc-inputs" style="background:' + sectionBg + ';">' +
150 '<div class="bkbg-slc-row">' +
151 '<div class="bkbg-slc-field">' +
152 '<label class="bkbg-slc-label" style="color:' + labelColor + ';">Income</label>' +
153 '<div class="bkbg-slc-input-wrap">' +
154 '<span class="bkbg-slc-prefix">' + cur + '</span>' +
155 '<input type="number" class="bkbg-slc-input" id="slc-income" value="' + state.income + '" style="background:' + inputBg + ';padding-left:28px;" min="0" step="1000">' +
156 '</div>' +
157 '</div>' +
158 '<div class="bkbg-slc-field">' +
159 '<label class="bkbg-slc-label" style="color:' + labelColor + ';">Pay Period</label>' +
160 '<select class="bkbg-slc-select" id="slc-period" style="background:' + inputBg + ';">' +
161 '<option value="annual"' + (state.period==='annual' ? ' selected':'' ) + '>Annual</option>' +
162 '<option value="monthly"' + (state.period==='monthly' ? ' selected':'' ) + '>Monthly</option>' +
163 '<option value="hourly"' + (state.period==='hourly' ? ' selected':'' ) + '>Hourly</option>' +
164 '</select>' +
165 '</div>' +
166 '<div class="bkbg-slc-field">' +
167 '<label class="bkbg-slc-label" style="color:' + labelColor + ';">Filing Status</label>' +
168 '<select class="bkbg-slc-select" id="slc-filing" style="background:' + inputBg + ';">' +
169 '<option value="single"' + (state.filing==='single' ? ' selected':'') + '>Single</option>' +
170 '<option value="married"' + (state.filing==='married' ? ' selected':'') + '>Married (Joint)</option>' +
171 '<option value="hoh"' + (state.filing==='hoh' ? ' selected':'') + '>Head of Household</option>' +
172 '</select>' +
173 '</div>' +
174 '</div>' +
175 '<div class="bkbg-slc-row">' +
176 '<div class="bkbg-slc-field">' +
177 '<label class="bkbg-slc-label" style="color:' + labelColor + ';">State Tax Rate (%)</label>' +
178 '<input type="number" class="bkbg-slc-input" id="slc-statetax" value="' + state.stateTax + '" style="background:' + inputBg + ';" min="0" max="20" step="0.1">' +
179 '</div>' +
180 '<div class="bkbg-slc-field">' +
181 '<label class="bkbg-slc-label" style="color:' + labelColor + ';">401(k) Contribution (%)</label>' +
182 '<input type="number" class="bkbg-slc-input" id="slc-401k" value="' + state.k401 + '" style="background:' + inputBg + ';" min="0" max="30" step="0.5">' +
183 '</div>' +
184 '<div class="bkbg-slc-field">' +
185 '<label class="bkbg-slc-label" style="color:' + labelColor + ';">Other Deductions (' + cur + ')</label>' +
186 '<input type="number" class="bkbg-slc-input" id="slc-other" value="' + state.otherDed + '" style="background:' + inputBg + ';" min="0" step="100">' +
187 '</div>' +
188 '</div>' +
189 '</div>' +
190
191 '<div class="bkbg-slc-net-hero" style="background:' + resultBg + ';">' +
192 '<div class="bkbg-slc-net-label" style="color:' + labelColor + ';">💵 Annual Take-Home Pay</div>' +
193 '<div class="bkbg-slc-net-val" style="color:' + accentColor + ';">' + fmt(r.netAnnual, cur) + '</div>' +
194 (showEffective ? '<div class="bkbg-slc-net-eff" style="color:' + labelColor + ';">Effective tax rate: ' + r.effectiveRate.toFixed(1) + '%</div>' : '') +
195 '</div>' +
196
197 (showAllPeriods ?
198 '<div class="bkbg-slc-periods">' +
199 ['Monthly','Bi-weekly','Weekly','Daily','Hourly'].map(function(lbl, i) {
200 var divisors = [12, 26, 52, 260, 2080];
201 return '<div class="bkbg-slc-period-card" style="border-color:' + accentColor + '55;background:' + accentColor + '12;">' +
202 '<div class="bkbg-slc-period-label" style="color:' + labelColor + ';">' + lbl + '</div>' +
203 '<div class="bkbg-slc-period-val" style="color:' + accentColor + ';">' + fmt(r.netAnnual / divisors[i], cur) + '</div>' +
204 '</div>';
205 }).join('') +
206 '</div>' : '') +
207
208 (showBreakdown ?
209 '<div class="bkbg-slc-breakdown">' +
210 '<h3 class="bkbg-slc-breakdown-title" style="color:' + labelColor + ';">📊 Annual Breakdown</h3>' +
211 brow('Gross Income', fmt(r.gross, cur), 100, accentColor) +
212 (r.ded401k > 0 ? brow('401(k) Contribution', ''+fmt(r.ded401k, cur), pct(r.ded401k, r.gross), dedColor) : '') +
213 (r.otherDed > 0 ? brow('Other Deductions', ''+fmt(r.otherDed, cur), pct(r.otherDed,r.gross), dedColor) : '') +
214 brow('Federal Income Tax', ''+fmt(r.fedTax, cur), pct(r.fedTax, r.gross), taxColor) +
215 brow('State Income Tax', ''+fmt(r.stateTax, cur), pct(r.stateTax, r.gross), taxColor) +
216 brow('Social Security', ''+fmt(r.ss, cur), pct(r.ss, r.gross), taxColor) +
217 brow('Medicare', ''+fmt(r.medicare, cur), pct(r.medicare, r.gross), taxColor) +
218 '<div class="bkbg-slc-divider"></div>' +
219 brow('✓ Net Take-Home Pay', fmt(r.netAnnual, cur), pct(r.netAnnual,r.gross), accentColor) +
220 '</div>' : '') +
221 '</div>';
222
223 bindEvents();
224 }
225
226 function bindEvents() {
227 var inputs = {
228 'slc-income': function(v){ state.income = parseFloat(v) || 0; },
229 'slc-statetax': function(v){ state.stateTax = parseFloat(v) || 0; },
230 'slc-401k': function(v){ state.k401 = parseFloat(v) || 0; },
231 'slc-other': function(v){ state.otherDed = parseFloat(v) || 0; }
232 };
233 var selects = {
234 'slc-period': function(v){ state.period = v; },
235 'slc-filing': function(v){ state.filing = v; }
236 };
237
238 Object.keys(inputs).forEach(function(id) {
239 var el = root.querySelector('#' + id);
240 if (el) el.addEventListener('input', function() { inputs[id](this.value); render(); });
241 });
242 Object.keys(selects).forEach(function(id) {
243 var el = root.querySelector('#' + id);
244 if (el) el.addEventListener('change', function() { selects[id](this.value); render(); });
245 });
246 }
247
248 render();
249 typoCssVarsForEl(root, opts.titleTypo, '--bkslc-tt-');
250 typoCssVarsForEl(root, opts.subtitleTypo, '--bkslc-st-');
251 });
252
253 })();
254