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

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.6, at blocks/countdown/frontend.js

205 lines 6.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 function isSafeRedirectUrl(url) {
5 if (!url) return false;
6 var u = String(url).trim();
7 if (!u) return false;
8 if (/^\s*(javascript|data|vbscript):/i.test(u)) return false;
9 // allow absolute http(s) or relative URLs/fragments
10 return /^(https?:\/\/|\/|\.|\?|#)/i.test(u);
11 }
12
13 function clearEl(el) {
14 while (el && el.firstChild) {
15 el.removeChild(el.firstChild);
16 }
17 }
18
19 function pad(num) {
20 return num < 10 ? '0' + num : String(num);
21 }
22
23 function getTimeRemaining(targetDate) {
24 var now = new Date();
25 var target = new Date(targetDate);
26 var total = target - now;
27
28 if (total <= 0) {
29 return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0, expired: true };
30 }
31
32 return {
33 days: Math.floor(total / (1000 * 60 * 60 * 24)),
34 hours: Math.floor((total / (1000 * 60 * 60)) % 24),
35 minutes: Math.floor((total / (1000 * 60)) % 60),
36 seconds: Math.floor((total / 1000) % 60),
37 total: total,
38 expired: false
39 };
40 }
41
42 // Get evergreen target date from localStorage or create new one
43 function getEvergreenTarget(wrap, duration) {
44 var storageKey = 'bkbg_cd_evergreen_' + (wrap.id || wrap.getAttribute('data-evergreen-id') || 'default');
45 var stored = localStorage.getItem(storageKey);
46
47 if (stored) {
48 var storedDate = new Date(stored);
49 // If stored date is still in future, use it
50 if (storedDate > new Date()) {
51 return stored;
52 }
53 }
54
55 // Create new target date based on duration
56 var now = new Date();
57 var target = new Date(now.getTime() + duration);
58 var isoString = target.toISOString();
59 localStorage.setItem(storageKey, isoString);
60 return isoString;
61 }
62
63 function initCountdown(wrap) {
64 var target = wrap.getAttribute('data-target');
65 var unitsConfig = wrap.getAttribute('data-units') || '';
66 var showLabels = wrap.getAttribute('data-show-labels') === '1';
67 var showSeparators = wrap.getAttribute('data-show-separators') === '1';
68 var expiredAction = wrap.getAttribute('data-expired-action') || 'message';
69 var expiredMessage = wrap.getAttribute('data-expired-message') || "Time's up!";
70 var expiredRedirect = wrap.getAttribute('data-expired-redirect') || '';
71 // Evergreen mode
72 var evergreenMode = wrap.getAttribute('data-evergreen') === '1';
73 var evergreenDuration = parseInt(wrap.getAttribute('data-evergreen-duration') || '86400000', 10); // default 24h in ms
74
75 if (evergreenMode) {
76 target = getEvergreenTarget(wrap, evergreenDuration);
77 }
78
79 if (!target) return;
80
81 // Parse units config
82 var units = [];
83 unitsConfig.split('|').forEach(function (item) {
84 if (!item) return;
85 var parts = item.split(':');
86 if (parts.length >= 2) {
87 units.push({ key: parts[0], label: parts.slice(1).join(':') });
88 }
89 });
90
91 var countdownEl = wrap.querySelector('.bkbg-cd-countdown');
92 if (!countdownEl) return;
93
94 var hasExpired = false;
95
96 function render() {
97 var time = getTimeRemaining(target);
98
99 if (time.expired && !hasExpired) {
100 hasExpired = true;
101
102 // For evergreen mode, restart the countdown
103 if (evergreenMode) {
104 var storageKey = 'bkbg_cd_evergreen_' + (wrap.id || wrap.getAttribute('data-evergreen-id') || 'default');
105 localStorage.removeItem(storageKey);
106 target = getEvergreenTarget(wrap, evergreenDuration);
107 hasExpired = false;
108 render();
109 return;
110 }
111
112 handleExpiration();
113 return;
114 }
115
116 if (hasExpired) return;
117
118 // Build DOM safely (avoid innerHTML)
119 clearEl(countdownEl);
120 units.forEach(function (unit, index) {
121 var value = time[unit.key] !== undefined ? time[unit.key] : 0;
122 var isLast = index === units.length - 1;
123 var valueStr = pad(value);
124
125 var unitEl = document.createElement('div');
126 unitEl.className = 'bkbg-cd-unit';
127
128 var digitEl = document.createElement('div');
129 digitEl.className = 'bkbg-cd-digit';
130
131 var numberEl = document.createElement('span');
132 numberEl.className = 'bkbg-cd-number';
133 numberEl.textContent = valueStr;
134 digitEl.appendChild(numberEl);
135 unitEl.appendChild(digitEl);
136
137 if (showLabels) {
138 var labelEl = document.createElement('span');
139 labelEl.className = 'bkbg-cd-label';
140 labelEl.textContent = unit.label || '';
141 unitEl.appendChild(labelEl);
142 }
143
144 countdownEl.appendChild(unitEl);
145
146 if (showSeparators && !isLast) {
147 var sep = document.createElement('span');
148 sep.className = 'bkbg-cd-separator';
149 sep.textContent = ':';
150 countdownEl.appendChild(sep);
151 }
152 });
153 }
154
155 function handleExpiration() {
156 switch (expiredAction) {
157 case 'message':
158 clearEl(wrap);
159 var msg = document.createElement('div');
160 msg.className = 'bkbg-cd-expired';
161 msg.textContent = expiredMessage || "Time's up!";
162 wrap.appendChild(msg);
163 break;
164 case 'hide':
165 wrap.style.display = 'none';
166 break;
167 case 'redirect':
168 if (isSafeRedirectUrl(expiredRedirect)) {
169 window.location.href = expiredRedirect;
170 }
171 break;
172 case 'keep':
173 // Do nothing, keep showing 00:00:00
174 break;
175 }
176 }
177
178 // Initial render
179 render();
180
181 // Update every second
182 var interval = setInterval(function () {
183 if (hasExpired && expiredAction !== 'keep') {
184 clearInterval(interval);
185 return;
186 }
187 render();
188 }, 1000);
189 }
190
191 function init() {
192 var countdowns = document.querySelectorAll('.bkbg-cd-wrap[data-target], .bkbg-cd-wrap[data-evergreen="1"]');
193 countdowns.forEach(initCountdown);
194 }
195
196 // Initialize
197 if (document.readyState === 'loading') {
198 document.addEventListener('DOMContentLoaded', init);
199 } else {
200 init();
201 }
202 })();
203
204
205