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

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

285 lines 12.6 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 ['sizeUnit','font-size-unit'],
9 ['lineHeightDesktop','line-height-d'],['lineHeightTablet','line-height-t'],['lineHeightMobile','line-height-m'],
10 ['letterSpacingDesktop','letter-spacing-d'],['letterSpacingTablet','letter-spacing-t'],['letterSpacingMobile','letter-spacing-m'],
11 ['wordSpacingDesktop','word-spacing-d'],['wordSpacingTablet','word-spacing-t'],['wordSpacingMobile','word-spacing-m']
12 ];
13 function typoCssVarsForEl(el, typo, prefix) {
14 if (!typo || typeof typo !== 'object') return;
15 _typoKeys.forEach(function(pair) {
16 var val = typo[pair[0]];
17 if (val !== undefined && val !== '' && val !== null) {
18 var v = (typeof val === 'number' && pair[1].indexOf('font-size') !== -1) ? val + 'px' : String(val);
19 el.style.setProperty(prefix + pair[1], v);
20 }
21 });
22 }
23
24 var CYCLE_MIN = 90;
25
26 function pad2(n) { return String(n).padStart(2, '0'); }
27
28 function addMinutes(h, m, mins) {
29 var total = h * 60 + m + mins;
30 total = ((total % 1440) + 1440) % 1440;
31 return { h: Math.floor(total / 60), m: total % 60 };
32 }
33 function subMinutes(h, m, mins) {
34 return addMinutes(h, m, -mins);
35 }
36 function fmt12(h, m) {
37 var ampm = h < 12 ? 'AM' : 'PM';
38 var hh = h % 12 || 12;
39 return hh + ':' + pad2(m) + ' ' + ampm;
40 }
41
42 function calcTimes(mode, h, m, onsetDelay, cMin, cMax) {
43 var results = [];
44 for (var c = cMax; c >= cMin; c--) {
45 var totalMins = (onsetDelay || 14) + c * CYCLE_MIN;
46 var t = mode === 'wake' ? addMinutes(h, m, totalMins) : subMinutes(h, m, totalMins);
47 results.push({ cycles: c, h: t.h, m: t.m, totalSleep: c * CYCLE_MIN });
48 }
49 return results;
50 }
51
52 function buildApp(app) {
53 var opts = {};
54 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch (e) {}
55
56 var o = opts;
57 var mode = o.mode || 'wake';
58 var onsetDly = parseInt(o.onsetDelay) || 14;
59 var cMin = parseInt(o.cyclesMin) || 4;
60 var cMax = parseInt(o.cyclesMax) || 6;
61 var timeSz = o.timeSize || 36;
62 var radius = (o.cardRadius || 16) + 'px';
63 var accentC = o.accentColor || '#6c3fb5';
64 var bestC = o.bestColor || '#6c3fb5';
65 var goodC = o.goodColor || '#3b82f6';
66 var okC = o.okColor || '#f59e0b';
67 var tagBg = o.cycleTagBg || '#ede9fe';
68 var tagC = o.cycleTagColor || '#6c3fb5';
69 var labelC = o.labelColor || '#374151';
70 var resultBg = o.resultBg || '#f5f3ff';
71 var resultBdr = o.resultBorder || '#ede9fe';
72 var tipBg = o.tipBg || '#fef9c3';
73 var tipC = o.tipColor || '#713f12';
74
75 var now = new Date();
76 var selH = now.getHours();
77 var selM = Math.floor(now.getMinutes() / 5) * 5;
78
79 app.innerHTML = '';
80 app.style.fontFamily = 'inherit';
81 app.style.boxSizing = 'border-box';
82
83 typoCssVarsForEl(app, o.titleTypo, '--bkslp-tt-');
84 typoCssVarsForEl(app, o.subtitleTypo, '--bkslp-st-');
85
86 // Section
87 var section = document.createElement('div');
88 section.className = 'bkbg-sleep-section';
89 section.style.paddingTop = (o.paddingTop || 60) + 'px';
90 section.style.paddingBottom = (o.paddingBottom || 60) + 'px';
91 section.style.background = o.sectionBg || '';
92 app.appendChild(section);
93
94 var card = document.createElement('div');
95 card.style.background = o.cardBg || '#ffffff';
96 card.style.borderRadius = radius;
97 card.style.padding = '36px 32px';
98 card.style.maxWidth = (o.maxWidth || 580) + 'px';
99 card.style.margin = '0 auto';
100 card.style.boxShadow = '0 4px 24px rgba(0,0,0,0.09)';
101 card.style.boxSizing = 'border-box';
102 section.appendChild(card);
103
104 // Title / subtitle
105 if (o.showTitle || o.showSubtitle) {
106 var hdr = document.createElement('div');
107 hdr.style.marginBottom = '20px';
108 if (o.showTitle) {
109 var ttl = document.createElement('div');
110 ttl.className = 'bkbg-sleep-title';
111 ttl.textContent = o.title || 'Sleep Calculator';
112 ttl.style.color = o.titleColor || '#1e1b4b';
113 ttl.style.marginBottom = '6px';
114 hdr.appendChild(ttl);
115 }
116 if (o.showSubtitle && o.subtitle) {
117 var sub = document.createElement('div');
118 sub.className = 'bkbg-sleep-subtitle';
119 sub.textContent = o.subtitle;
120 sub.style.color = o.subtitleColor || '#6b7280';
121 hdr.appendChild(sub);
122 }
123 card.appendChild(hdr);
124 }
125
126 // Mode tab row
127 var tabRow = document.createElement('div');
128 tabRow.style.cssText = 'display:flex;gap:10px;margin-bottom:24px;';
129 var modeLabelWake = '🌙 I want to wake up at\u2026';
130 var modeLabelSleep = '⏰ I need to wake up at\u2026';
131 var modes = [['wake', modeLabelWake], ['sleep', modeLabelSleep]];
132 var modeBtns = {};
133
134 modes.forEach(function (pair) {
135 var m = pair[0]; var lbl = pair[1];
136 var btn = document.createElement('button');
137 btn.type = 'button';
138 btn.textContent = lbl;
139 btn.className = 'bkbg-sleep-mode-btn';
140 btn.style.cssText = 'flex:1;padding:10px;border-radius:8px;font-family:inherit;font-size:14px;font-weight:700;cursor:pointer;transition:all .15s;';
141 btn.addEventListener('click', function () {
142 mode = m;
143 refreshTabs();
144 refreshLabel();
145 renderResults();
146 });
147 modeBtns[m] = btn;
148 tabRow.appendChild(btn);
149 });
150 card.appendChild(tabRow);
151
152 function refreshTabs() {
153 ['wake', 'sleep'].forEach(function (m2) {
154 var btn = modeBtns[m2];
155 var a2 = m2 === mode;
156 btn.style.background = a2 ? accentC : 'transparent';
157 btn.style.color = a2 ? '#fff' : accentC;
158 btn.style.border = '2px solid ' + (a2 ? accentC : '#e5e7eb');
159 });
160 }
161 refreshTabs();
162
163 // Label above time row
164 var timeLabel = document.createElement('div');
165 timeLabel.style.cssText = 'font-size:13px;font-weight:600;margin-bottom:8px;color:' + labelC + ';';
166 card.appendChild(timeLabel);
167
168 function refreshLabel() {
169 timeLabel.textContent = mode === 'wake' ? 'I plan to go to bed at:' : 'I need to wake up at:';
170 }
171 refreshLabel();
172
173 // Time selector
174 var timeRow = document.createElement('div');
175 timeRow.className = 'bkbg-sleep-time-row';
176 timeRow.style.cssText = 'display:flex;align-items:center;gap:10px;margin-bottom:24px;';
177
178 function makeSelect(opts2, initVal, onChange) {
179 var sel = document.createElement('select');
180 sel.style.cssText = 'padding:10px 14px;font-size:20px;font-weight:700;border:2px solid #e5e7eb;border-radius:' + (o.inputRadius || 8) + 'px;background:#fff;cursor:pointer;';
181 opts2.forEach(function (opt) {
182 var op = document.createElement('option');
183 op.value = opt.value;
184 op.textContent = opt.label;
185 if (String(opt.value) === String(initVal)) op.selected = true;
186 sel.appendChild(op);
187 });
188 sel.addEventListener('change', function () { onChange(sel.value); renderResults(); });
189 return sel;
190 }
191
192 var hourOpts = [];
193 for (var h2 = 0; h2 < 24; h2++) {
194 var ampm2 = h2 < 12 ? 'AM' : 'PM';
195 var hh2 = h2 % 12 || 12;
196 hourOpts.push({ label: hh2 + ' ' + ampm2, value: String(h2) });
197 }
198 var minOpts = [];
199 for (var mm = 0; mm < 60; mm += 5) { minOpts.push({ label: pad2(mm), value: String(mm) }); }
200
201 var hSel = makeSelect(hourOpts, selH, function (v) { selH = parseInt(v, 10); });
202 var sep = document.createElement('span');
203 sep.textContent = ':';
204 sep.style.cssText = 'font-size:22px;font-weight:700;color:' + labelC + ';';
205 var mSel = makeSelect(minOpts, selM, function (v) { selM = parseInt(v, 10); });
206
207 timeRow.appendChild(hSel);
208 timeRow.appendChild(sep);
209 timeRow.appendChild(mSel);
210 card.appendChild(timeRow);
211
212 // Results label
213 var resLabel = document.createElement('div');
214 resLabel.style.cssText = 'font-size:13px;font-weight:600;margin-bottom:12px;color:' + labelC + ';';
215 card.appendChild(resLabel);
216
217 // Results container
218 var resContainer = document.createElement('div');
219 resContainer.className = 'bkbg-sleep-results';
220 resContainer.style.marginBottom = o.showTips ? '20px' : '0';
221 card.appendChild(resContainer);
222
223 // Tips
224 if (o.showTips) {
225 var tipBox = document.createElement('div');
226 tipBox.className = 'bkbg-sleep-tip';
227 tipBox.style.cssText = 'background:' + tipBg + ';color:' + tipC + ';border-radius:' + radius + ';padding:14px 18px;font-size:13px;line-height:1.6;';
228 tipBox.innerHTML = '<strong>💡 Tip:</strong> Sleep comes in 90-minute cycles. Waking at the end of a cycle helps you feel more refreshed. Allow ' + onsetDly + ' minutes to fall asleep.';
229 card.appendChild(tipBox);
230 }
231
232 function qualColor(c) {
233 if (c >= 6) return bestC;
234 if (c >= 5) return goodC;
235 return okC;
236 }
237
238 function renderResults() {
239 var results = calcTimes(mode, selH, selM, onsetDly, cMin, cMax);
240 resLabel.textContent = mode === 'wake' ? 'Set your alarm for one of these times:' : 'Try to fall asleep at one of these times:';
241 resContainer.innerHTML = '';
242 results.forEach(function (r, i) {
243 var qC = qualColor(r.cycles);
244 var totalH = Math.floor(r.totalSleep / 60);
245 var totalM = r.totalSleep % 60;
246 var row = document.createElement('div');
247 row.className = 'bkbg-sleep-result-row';
248 row.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:14px 18px;margin-bottom:8px;background:' + resultBg + ';border:1.5px solid ' + resultBdr + ';border-left:4px solid ' + (i === 0 ? qC : 'transparent') + ';border-radius:' + radius + ';transition:box-shadow .18s,transform .18s;box-sizing:border-box;';
249
250 var left = document.createElement('div');
251 var timeEl = document.createElement('div');
252 timeEl.textContent = fmt12(r.h, r.m);
253 timeEl.style.cssText = 'font-size:' + timeSz + 'px;font-weight:800;color:' + qC + ';line-height:1.1;';
254 var durEl = document.createElement('div');
255 durEl.textContent = totalH + 'h' + (totalM ? ' ' + totalM + 'm' : '') + ' of sleep';
256 durEl.style.cssText = 'font-size:12px;color:' + labelC + ';margin-top:2px;';
257 left.appendChild(timeEl);
258 left.appendChild(durEl);
259
260 var right = document.createElement('div');
261 right.style.textAlign = 'right';
262 var tag = document.createElement('div');
263 tag.textContent = r.cycles + ' cycles';
264 tag.style.cssText = 'display:inline-block;font-weight:700;font-size:12px;background:' + tagBg + ';color:' + tagC + ';padding:3px 10px;border-radius:100px;margin-bottom:3px;';
265 right.appendChild(tag);
266 if (i === 0) {
267 var best = document.createElement('div');
268 best.textContent = '�
269 Best';
270 best.style.cssText = 'font-size:11px;color:' + qC + ';font-weight:600;margin-top:2px;';
271 right.appendChild(best);
272 }
273
274 row.appendChild(left);
275 row.appendChild(right);
276 resContainer.appendChild(row);
277 });
278 }
279
280 renderResults();
281 }
282
283 document.querySelectorAll('.bkbg-sleep-app').forEach(buildApp);
284 })();
285