PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
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 / cron-builder / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.11, at blocks/cron-builder/frontend.js

445 lines 17.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 function typoCssVarsForEl(typo, prefix, el) {
2 if (!typo || typeof typo !== 'object') return;
3 var map = {
4 family: 'font-family', weight: 'font-weight', style: 'font-style',
5 transform: 'text-transform', decoration: 'text-decoration',
6 sizeDesktop: 'font-size-d', sizeTablet: 'font-size-t', sizeMobile: 'font-size-m',
7 sizeUnit: null,
8 lineHeightDesktop: 'line-height-d', lineHeightTablet: 'line-height-t', lineHeightMobile: 'line-height-m',
9 lineHeightUnit: null,
10 letterSpacingDesktop: 'letter-spacing-d', letterSpacingTablet: 'letter-spacing-t', letterSpacingMobile: 'letter-spacing-m',
11 letterSpacingUnit: null,
12 wordSpacingDesktop: 'word-spacing-d', wordSpacingTablet: 'word-spacing-t', wordSpacingMobile: 'word-spacing-m',
13 wordSpacingUnit: null
14 };
15 var sizeU = typo.sizeUnit || 'px';
16 var lhU = typo.lineHeightUnit || '';
17 var lsU = typo.letterSpacingUnit || 'px';
18 var wsU = typo.wordSpacingUnit || 'px';
19 Object.keys(map).forEach(function (k) {
20 var css = map[k]; if (!css) return;
21 var v = typo[k]; if (v === undefined || v === '' || v === null) return;
22 if (/size|spacing/i.test(k)) {
23 var u = /letterSpacing/.test(k) ? lsU : /wordSpacing/.test(k) ? wsU : /lineHeight/.test(k) ? lhU : sizeU;
24 v = v + u;
25 }
26 el.style.setProperty(prefix + css, v);
27 });
28 }
29
30 (function () {
31 'use strict';
32
33 var PRESETS = [
34 { label: 'Every minute', expr: '* * * * *' },
35 { label: 'Every hour', expr: '0 * * * *' },
36 { label: 'Daily 9 AM', expr: '0 9 * * *' },
37 { label: 'Weekdays 9 AM', expr: '0 9 * * 1-5' },
38 { label: 'Weekly Monday', expr: '0 9 * * 1' },
39 { label: 'Monthly 1st', expr: '0 9 1 * *' },
40 { label: 'Yearly Jan 1st', expr: '0 0 1 1 *' }
41 ];
42
43 var MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
44 var WEEKDAYS = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
45
46 /* ---- Describe a cron expression in English ---- */
47 function describe(expr) {
48 var parts = (expr || '').trim().split(/\s+/);
49 if (parts.length !== 5) return 'Invalid expression';
50
51 var min = parts[0], hr = parts[1], dom = parts[2], mon = parts[3], dow = parts[4];
52
53 function fmtTime(h, m) {
54 var H = parseInt(h, 10), M = parseInt(m, 10);
55 if (isNaN(H) || isNaN(M)) return 'at ' + m + ':' + h;
56 var ampm = H >= 12 ? 'PM' : 'AM';
57 var h12 = H % 12 || 12;
58 return 'at ' + h12 + ':' + String(M).padStart(2, '0') + ' ' + ampm;
59 }
60
61 var timeStr = '';
62 if (min === '*' && hr === '*') {
63 timeStr = 'every minute';
64 } else if (min === '*') {
65 timeStr = 'every minute of hour ' + hr;
66 } else if (hr === '*') {
67 timeStr = 'at minute ' + min + ' of every hour';
68 } else {
69 timeStr = fmtTime(hr, min);
70 }
71
72 var domStr = '';
73 if (dom !== '*') {
74 if (/^\d+$/.test(dom)) domStr = 'on day ' + dom + ' of the month';
75 else domStr = 'on day ' + dom + ' of the month';
76 }
77
78 var monStr = '';
79 if (mon !== '*') {
80 if (/^\d+$/.test(mon) && parseInt(mon) >= 1 && parseInt(mon) <= 12) monStr = 'in ' + MONTHS[parseInt(mon) - 1];
81 else monStr = 'in month ' + mon;
82 }
83
84 var dowStr = '';
85 if (dow !== '*') {
86 var days = dow.split(',').map(function (d) {
87 if (/^\d+$/.test(d)) return WEEKDAYS[parseInt(d) % 7] || d;
88 if (/^\d+-\d+$/.test(d)) {
89 var range = d.split('-');
90 return (WEEKDAYS[parseInt(range[0])] || range[0]) + '' + (WEEKDAYS[parseInt(range[1])] || range[1]);
91 }
92 return d;
93 });
94 dowStr = 'on ' + days.join(', ');
95 }
96
97 var parts2 = [timeStr];
98 if (domStr) parts2.push(domStr);
99 if (monStr) parts2.push(monStr);
100 if (dowStr) parts2.push(dowStr);
101 if (dom === '*' && mon === '*' && dow === '*' && timeStr !== 'every minute') parts2.push('every day');
102
103 return parts2.join(', ');
104 }
105
106 /* ---- Validate a cron field ---- */
107 function validateField(val, min, max) {
108 if (val === '*') return true;
109 if (/^\*\/\d+$/.test(val)) return true;
110 if (/^\d+$/.test(val)) { var n = parseInt(val); return n >= min && n <= max; }
111 if (/^\d+-\d+$/.test(val)) {
112 var parts = val.split('-');
113 return parseInt(parts[0]) >= min && parseInt(parts[1]) <= max && parseInt(parts[0]) <= parseInt(parts[1]);
114 }
115 if (/^\d+(,\d+)+$/.test(val)) {
116 return val.split(',').every(function (n) { var x = parseInt(n); return x >= min && x <= max; });
117 }
118 return false;
119 }
120
121 /* ---- Compute next N run times ---- */
122 function nextRuns(expr, count) {
123 var parts = (expr || '').trim().split(/\s+/);
124 if (parts.length !== 5) return [];
125
126 function matchField(val, n, min, max) {
127 if (val === '*') return true;
128 if (/^\*\/(\d+)$/.test(val)) { var step = parseInt(val.slice(2)); return n % step === 0; }
129 if (/^\d+$/.test(val)) return parseInt(val) === n;
130 if (/^\d+-\d+$/.test(val)) { var r = val.split('-'); return n >= parseInt(r[0]) && n <= parseInt(r[1]); }
131 if (/^\d+(,\d+)+$/.test(val)) return val.split(',').map(Number).indexOf(n) !== -1;
132 return false;
133 }
134
135 var results = [];
136 var d = new Date();
137 d.setSeconds(0, 0);
138 d.setMinutes(d.getMinutes() + 1);
139
140 var limit = 0;
141 while (results.length < count && limit < 525600) {
142 limit++;
143 if (
144 matchField(parts[3], d.getMonth() + 1, 1, 12) &&
145 matchField(parts[2], d.getDate(), 1, 31) &&
146 matchField(parts[4], d.getDay(), 0, 7) &&
147 matchField(parts[1], d.getHours(), 0, 23) &&
148 matchField(parts[0], d.getMinutes(), 0, 59)
149 ) {
150 results.push(new Date(d));
151 }
152 d.setMinutes(d.getMinutes() + 1);
153 }
154 return results;
155 }
156
157 function relativeTime(date) {
158 var diff = date - Date.now();
159 var mins = Math.round(diff / 60000);
160 if (mins < 60) return 'in ' + mins + ' min';
161 var hrs = Math.round(mins / 60);
162 if (hrs < 24) return 'in ' + hrs + ' hr';
163 return 'in ' + Math.round(hrs / 24) + ' day' + (Math.round(hrs/24) > 1 ? 's' : '');
164 }
165
166 function initBlock(root) {
167 var opts;
168 try { opts = JSON.parse(root.getAttribute('data-opts')); } catch (e) { return; }
169
170 var a = opts;
171 var expr = a.defaultExpression || '0 9 * * *';
172 var exprParts = expr.split(/\s+/);
173 while (exprParts.length < 5) exprParts.push('*');
174
175 root.innerHTML = '';
176
177 var wrap = document.createElement('div');
178 wrap.className = 'bkbg-cb-wrap';
179 wrap.style.cssText = 'background:' + a.sectionBg + ';max-width:' + a.contentMaxWidth + 'px;margin:0 auto;';
180 root.appendChild(wrap);
181
182 typoCssVarsForEl(a.typoTitle, '--bkbg-cb-ttl-', wrap);
183 typoCssVarsForEl(a.typoSubtitle, '--bkbg-cb-sub-', wrap);
184
185 if (a.showTitle) {
186 var h = document.createElement('div');
187 h.className = 'bkbg-cb-title';
188 h.style.color = a.titleColor;
189 h.textContent = a.title;
190 wrap.appendChild(h);
191 }
192
193 if (a.showSubtitle) {
194 var s = document.createElement('div');
195 s.className = 'bkbg-cb-subtitle';
196 s.style.color = a.subtitleColor;
197 s.textContent = a.subtitle;
198 wrap.appendChild(s);
199 }
200
201 // Presets
202 if (a.showPresets) {
203 var presetsEl = document.createElement('div');
204 presetsEl.className = 'bkbg-cb-presets';
205 presetsEl.style.background = a.cardBg;
206 PRESETS.forEach(function (p) {
207 var btn = document.createElement('button');
208 btn.className = 'bkbg-cb-preset-btn';
209 btn.textContent = p.label;
210 btn.title = p.expr;
211 updatePresetStyle(btn, p.expr === expr);
212 btn.addEventListener('click', function () {
213 expr = p.expr;
214 exprParts = expr.split(/\s+/);
215 updateInputs();
216 renderAll();
217 presetsEl.querySelectorAll('.bkbg-cb-preset-btn').forEach(function (b) {
218 updatePresetStyle(b, b.title === expr);
219 });
220 });
221 presetsEl.appendChild(btn);
222 });
223 wrap.appendChild(presetsEl);
224 }
225
226 function updatePresetStyle(btn, active) {
227 btn.style.cssText = 'background:' + (active ? a.accentColor : '#e5e7eb') + ';color:' + (active ? '#fff' : '#374151') + ';';
228 }
229
230 // Fields card
231 var fieldsCard = document.createElement('div');
232 fieldsCard.className = 'bkbg-cb-card';
233 fieldsCard.style.background = a.cardBg;
234 wrap.appendChild(fieldsCard);
235
236 var FIELD_DEFS = [
237 { label: 'Minute', min: 0, max: 59 },
238 { label: 'Hour', min: 0, max: 23 },
239 { label: 'Day', min: 1, max: 31 },
240 { label: 'Month', min: 1, max: 12 },
241 { label: 'Weekday', min: 0, max: 7 }
242 ];
243
244 var fieldsRow = document.createElement('div');
245 fieldsRow.className = 'bkbg-cb-fields';
246 fieldsCard.appendChild(fieldsRow);
247
248 var inputs = [];
249
250 FIELD_DEFS.forEach(function (def, idx) {
251 var col = document.createElement('div');
252 col.className = 'bkbg-cb-field-col';
253
254 var inp = document.createElement('input');
255 inp.type = 'text';
256 inp.className = 'bkbg-cb-field-input';
257 inp.value = exprParts[idx] || '*';
258 inp.style.cssText = 'background:' + a.fieldBg + ';color:' + a.accentColor + ';';
259 inp.setAttribute('aria-label', def.label);
260 inp.title = def.label + ' (' + def.min + '-' + def.max + ')';
261
262 inp.addEventListener('input', function () {
263 exprParts[idx] = inp.value || '*';
264 expr = exprParts.join(' ');
265 renderAll();
266 });
267
268 inputs.push(inp);
269
270 var lbl = document.createElement('div');
271 lbl.className = 'bkbg-cb-field-label';
272 lbl.style.color = a.subtitleColor;
273 lbl.textContent = def.label;
274
275 col.appendChild(inp);
276 col.appendChild(lbl);
277 fieldsRow.appendChild(col);
278 });
279
280 function updateInputs() {
281 inputs.forEach(function (inp, idx) {
282 inp.value = exprParts[idx] || '*';
283 });
284 }
285
286 // Expression display
287 var exprDisplay = document.createElement('div');
288 exprDisplay.className = 'bkbg-cb-expr-display';
289 exprDisplay.style.cssText = 'background:' + a.fieldBg + ';color:' + a.accentColor + ';';
290 fieldsCard.appendChild(exprDisplay);
291
292 // Error display
293 var errorEl = document.createElement('div');
294 errorEl.className = 'bkbg-cb-error';
295 errorEl.style.display = 'none';
296 fieldsCard.appendChild(errorEl);
297
298 // Actions
299 var actionsRow = document.createElement('div');
300 actionsRow.className = 'bkbg-cb-actions';
301 var copyBtn = document.createElement('button');
302 copyBtn.className = 'bkbg-cb-btn';
303 copyBtn.style.background = a.accentColor;
304 copyBtn.textContent = 'Copy Expression';
305 var copyMsg = document.createElement('div');
306 copyMsg.className = 'bkbg-cb-copy-msg';
307 copyMsg.style.color = a.accentColor;
308 copyMsg.textContent = '✓ Copied to clipboard!';
309
310 copyBtn.addEventListener('click', function () {
311 navigator.clipboard && navigator.clipboard.writeText(expr).catch(function () {
312 var t = document.createElement('textarea');
313 t.value = expr; document.body.appendChild(t); t.select();
314 document.execCommand('copy'); document.body.removeChild(t);
315 });
316 copyMsg.classList.add('bkbg-cb-show');
317 setTimeout(function () { copyMsg.classList.remove('bkbg-cb-show'); }, 2000);
318 });
319
320 actionsRow.appendChild(copyBtn);
321 fieldsCard.appendChild(actionsRow);
322 fieldsCard.appendChild(copyMsg);
323
324 // Description card
325 var descCard = null, descText = null;
326 if (a.showDescription) {
327 descCard = document.createElement('div');
328 descCard.className = 'bkbg-cb-desc-card';
329 descCard.style.cssText = 'background:' + a.cardBg + ';border-left-color:' + a.accentColor + ';';
330
331 var descLabel = document.createElement('div');
332 descLabel.className = 'bkbg-cb-desc-label';
333 descLabel.style.color = a.subtitleColor;
334 descLabel.textContent = 'DESCRIPTION';
335 descCard.appendChild(descLabel);
336
337 descText = document.createElement('div');
338 descText.className = 'bkbg-cb-desc-text';
339 descText.style.color = a.labelColor;
340 descCard.appendChild(descText);
341 wrap.appendChild(descCard);
342 }
343
344 // Next runs card
345 var nextCard = null, nextList = null;
346 if (a.showNextRuns) {
347 nextCard = document.createElement('div');
348 nextCard.className = 'bkbg-cb-card';
349 nextCard.style.background = a.cardBg;
350
351 var nextTitle = document.createElement('div');
352 nextTitle.className = 'bkbg-cb-next-title';
353 nextTitle.style.color = a.labelColor;
354 nextTitle.textContent = 'Next Run Times';
355 nextCard.appendChild(nextTitle);
356
357 nextList = document.createElement('div');
358 nextCard.appendChild(nextList);
359 wrap.appendChild(nextCard);
360 }
361
362 function renderAll() {
363 // Update expression display
364 exprDisplay.textContent = exprParts.join(' ');
365
366 // Validate
367 var valid = [
368 validateField(exprParts[0], 0, 59),
369 validateField(exprParts[1], 0, 23),
370 validateField(exprParts[2], 1, 31),
371 validateField(exprParts[3], 1, 12),
372 validateField(exprParts[4], 0, 7)
373 ];
374
375 var allValid = valid.every(Boolean);
376 inputs.forEach(function (inp, idx) {
377 inp.style.borderColor = valid[idx] ? 'transparent' : '#ef4444';
378 });
379
380 if (!allValid) {
381 errorEl.style.display = 'block';
382 errorEl.textContent = 'Invalid expression — check highlighted fields';
383 } else {
384 errorEl.style.display = 'none';
385 }
386
387 // Description
388 if (descText) {
389 descText.textContent = describe(exprParts.join(' '));
390 }
391
392 // Next runs
393 if (nextList) {
394 nextList.innerHTML = '';
395 if (allValid) {
396 var runs = nextRuns(exprParts.join(' '), parseInt(a.nextRunsCount) || 5);
397 runs.forEach(function (d) {
398 var item = document.createElement('div');
399 item.className = 'bkbg-cb-next-item';
400 item.style.color = a.labelColor;
401
402 var arrow = document.createElement('span');
403 arrow.className = 'bkbg-cb-next-arrow';
404 arrow.style.color = a.accentColor;
405 arrow.textContent = '';
406
407 var dateText = document.createElement('span');
408 dateText.textContent = d.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' });
409
410 var rel = document.createElement('span');
411 rel.className = 'bkbg-cb-next-relative';
412 rel.textContent = relativeTime(d);
413
414 [arrow, dateText, rel].forEach(function (el) { item.appendChild(el); });
415 nextList.appendChild(item);
416 });
417 if (runs.length === 0) {
418 var noRun = document.createElement('div');
419 noRun.style.cssText = 'color:' + a.subtitleColor + ';font-size:13px;padding:8px 0;';
420 noRun.textContent = 'No upcoming runs found in the next year.';
421 nextList.appendChild(noRun);
422 }
423 } else {
424 var errNote = document.createElement('div');
425 errNote.style.cssText = 'color:#ef4444;font-size:13px;padding:8px 0;';
426 errNote.textContent = 'Fix the expression to see next run times.';
427 nextList.appendChild(errNote);
428 }
429 }
430 }
431
432 renderAll();
433 }
434
435 function init() {
436 document.querySelectorAll('.bkbg-cb-app').forEach(initBlock);
437 }
438
439 if (document.readyState === 'loading') {
440 document.addEventListener('DOMContentLoaded', init);
441 } else {
442 init();
443 }
444 })();
445