PluginProbe
Blockenberg — 618 Advanced Gutenberg Blocks, 683 Templates and AI Agent for WordPress Block Editor / 2.0.14
Blockenberg — 618 Advanced Gutenberg Blocks, 683 Templates and AI Agent for WordPress Block Editor v2.0.14
2.0.15 2.0.14 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 / newsletter-popup / frontend.js

frontend.js in Blockenberg — 618 Advanced Gutenberg Blocks, 683 Templates and AI Agent for WordPress Block Editor 2.0.14, at blocks/newsletter-popup/frontend.js

391 lines 14.4 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 STORE_PREFIX = 'bkbg-nlpop-';
26
27 /* ── helpers ── */
28 function hexAlpha(hex, pct) {
29 var r = parseInt(hex.slice(1, 3), 16);
30 var g = parseInt(hex.slice(3, 5), 16);
31 var b = parseInt(hex.slice(5, 7), 16);
32 return 'rgba(' + r + ',' + g + ',' + b + ',' + (pct / 100).toFixed(2) + ')';
33 }
34
35 function validEmail(v) {
36 return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.trim());
37 }
38
39 function storageKey(cookieName) {
40 return STORE_PREFIX + (cookieName || 'default');
41 }
42
43 function isSuppressed(key, days) {
44 if (!days) return false;
45 try {
46 var raw = localStorage.getItem(key);
47 if (!raw) return false;
48 var data = JSON.parse(raw);
49 var elapsed = (Date.now() - data.ts) / 86400000; // days
50 return elapsed < days;
51 } catch (e) {
52 return false;
53 }
54 }
55
56 function saveLocal(key, action) {
57 try {
58 localStorage.setItem(key, JSON.stringify({ ts: Date.now(), action: action }));
59 } catch (e) {}
60 }
61
62 /* ── build overlay DOM ── */
63 function buildOverlay(o) {
64 var overlay = document.createElement('div');
65 overlay.className = 'bkbg-nlpop-overlay';
66 overlay.style.backgroundColor = hexAlpha(o.overlayBg, o.overlayOpacity);
67
68 var box = document.createElement('div');
69 box.className = 'bkbg-nlpop-box';
70 box.style.maxWidth = o.maxWidth + 'px';
71 box.style.borderRadius = o.borderRadius + 'px';
72 box.style.background = o.popupBg;
73 box.style.boxShadow = '0 24px 64px rgba(0,0,0,0.18), 0 4px 16px rgba(0,0,0,0.10)';
74
75 var hasImage = o.imageUrl && o.imagePosition !== 'none';
76 var isSide = hasImage && (o.imagePosition === 'left' || o.imagePosition === 'right');
77 var isTop = hasImage && o.imagePosition === 'top';
78
79 /* inner wrapper */
80 var inner = document.createElement('div');
81 inner.className = 'bkbg-nlpop-inner' + (isSide ? ' layout-' + o.imagePosition : '');
82
83 /* close button */
84 if (o.showClose) {
85 var closeBtn = document.createElement('button');
86 closeBtn.className = 'bkbg-nlpop-close';
87 closeBtn.setAttribute('aria-label', 'Close');
88 closeBtn.innerHTML = '&times;';
89 closeBtn.style.background = o.closeBg;
90 closeBtn.style.color = o.closeColor;
91 box.appendChild(closeBtn);
92 }
93
94 /* top image */
95 if (isTop) {
96 var topImg = document.createElement('div');
97 topImg.className = 'bkbg-nlpop-top-img';
98 topImg.style.backgroundImage = 'url(' + o.imageUrl + ')';
99 inner.appendChild(topImg);
100 }
101
102 /* left side image */
103 if (isSide && o.imagePosition === 'left') {
104 var sideL = document.createElement('div');
105 sideL.className = 'bkbg-nlpop-side-img';
106 sideL.style.backgroundImage = 'url(' + o.imageUrl + ')';
107 inner.appendChild(sideL);
108 }
109
110 /* content block */
111 var contentWrap = document.createElement('div');
112 contentWrap.className = 'bkbg-nlpop-content';
113 contentWrap.style.padding = o.padding + 'px';
114 /* compress top padding if top image exists */
115 if (isTop) {
116 contentWrap.style.paddingTop = Math.round(o.padding * 0.7) + 'px';
117 }
118
119 var heading = document.createElement('h2');
120 heading.className = 'bkbg-nlpop-heading';
121 heading.textContent = o.heading;
122 heading.style.color = o.headingColor;
123
124 var subtext = document.createElement('p');
125 subtext.className = 'bkbg-nlpop-subtext';
126 subtext.textContent = o.subtext;
127 subtext.style.color = o.textColor;
128
129 typoCssVarsForEl(box, o.headingTypo, '--bkbg-nlpop-ht-');
130 typoCssVarsForEl(box, o.subtextTypo, '--bkbg-nlpop-st-');
131
132 /* form */
133 var form = document.createElement('form');
134 form.className = 'bkbg-nlpop-form';
135 form.setAttribute('novalidate', '');
136
137 var emailRow = document.createElement('div');
138 emailRow.className = 'bkbg-nlpop-email-row';
139
140 var input = document.createElement('input');
141 input.type = 'email';
142 input.className = 'bkbg-nlpop-input';
143 input.placeholder = o.emailPlaceholder;
144 input.style.background = o.inputBg;
145 input.style.borderColor = o.inputBorder;
146 input.style.color = o.inputColor;
147
148 var submit = document.createElement('button');
149 submit.type = 'submit';
150 submit.className = 'bkbg-nlpop-submit';
151 submit.textContent = o.buttonLabel;
152 submit.style.background = o.buttonBg;
153 submit.style.color = o.buttonColor;
154 submit.style.fontSize = o.ctaSize + 'px';
155
156 var errorMsg = document.createElement('div');
157 errorMsg.className = 'bkbg-nlpop-error';
158
159 emailRow.appendChild(input);
160 emailRow.appendChild(submit);
161 form.appendChild(emailRow);
162 form.appendChild(errorMsg);
163
164 /* success state */
165 var successDiv = document.createElement('div');
166 successDiv.className = 'bkbg-nlpop-success';
167 successDiv.style.color = o.headingColor;
168
169 var successIcon = document.createElement('div');
170 successIcon.className = 'bkbg-nlpop-success-icon';
171 successIcon.textContent = '🎉';
172
173 var successMsg = document.createElement('p');
174 successMsg.className = 'bkbg-nlpop-success-msg';
175 successMsg.textContent = o.successMessage;
176
177 var successSub = document.createElement('p');
178 successSub.className = 'bkbg-nlpop-success-sub';
179 successSub.textContent = o.successSubtext;
180 successSub.style.color = o.textColor;
181
182 successDiv.appendChild(successIcon);
183 successDiv.appendChild(successMsg);
184 successDiv.appendChild(successSub);
185
186 contentWrap.appendChild(heading);
187 contentWrap.appendChild(subtext);
188 contentWrap.appendChild(form);
189 contentWrap.appendChild(successDiv);
190
191 inner.appendChild(contentWrap);
192
193 /* right side image */
194 if (isSide && o.imagePosition === 'right') {
195 var sideR = document.createElement('div');
196 sideR.className = 'bkbg-nlpop-side-img';
197 sideR.style.backgroundImage = 'url(' + o.imageUrl + ')';
198 inner.appendChild(sideR);
199 }
200
201 box.appendChild(inner);
202 overlay.appendChild(box);
203
204 return { overlay: overlay, box: box, closeBtn: box.querySelector('.bkbg-nlpop-close'), form: form, input: input, errorMsg: errorMsg, successDiv: successDiv };
205 }
206
207 /* ── open / close ── */
208 function openPopup(els, key, days) {
209 document.body.appendChild(els.overlay);
210 requestAnimationFrame(function () {
211 requestAnimationFrame(function () {
212 els.overlay.classList.add('is-visible');
213 });
214 });
215 }
216
217 function closePopup(els, key, action) {
218 saveLocal(key, action || 'dismissed');
219 els.overlay.classList.remove('is-visible');
220 setTimeout(function () {
221 if (els.overlay.parentNode) {
222 els.overlay.parentNode.removeChild(els.overlay);
223 }
224 }, 380);
225 }
226
227 /* ── wires events ── */
228 function wireEvents(els, o, key) {
229 /* close button */
230 if (els.closeBtn) {
231 els.closeBtn.addEventListener('click', function () {
232 closePopup(els, key, 'dismissed');
233 });
234 }
235
236 /* overlay click */
237 if (o.overlayClickClose) {
238 els.overlay.addEventListener('click', function (e) {
239 if (e.target === els.overlay) {
240 closePopup(els, key, 'dismissed');
241 }
242 });
243 }
244
245 /* escape key */
246 var onKey = function (e) {
247 if (e.key === 'Escape') {
248 closePopup(els, key, 'dismissed');
249 document.removeEventListener('keydown', onKey);
250 }
251 };
252 document.addEventListener('keydown', onKey);
253
254 /* form submit */
255 els.form.addEventListener('submit', function (e) {
256 e.preventDefault();
257 var val = els.input.value.trim();
258 if (!validEmail(val)) {
259 els.errorMsg.textContent = 'Please enter a valid email address.';
260 els.input.focus();
261 return;
262 }
263 els.errorMsg.textContent = '';
264 /* show success */
265 els.form.style.display = 'none';
266 els.successDiv.classList.add('is-visible');
267 saveLocal(key, 'subscribed');
268 /* auto-close after 3s */
269 setTimeout(function () {
270 closePopup(els, key, 'subscribed');
271 }, 3000);
272 });
273
274 /* clear error on input */
275 els.input.addEventListener('input', function () {
276 els.errorMsg.textContent = '';
277 });
278 }
279
280 /* ── setup triggers ── */
281 function setupTriggers(o, key, cb) {
282 var fired = false;
283 function fire() {
284 if (fired) return;
285 fired = true;
286 cb();
287 }
288
289 var type = o.triggerType;
290
291 /* delay trigger */
292 if (type === 'delay' || type === 'both') {
293 setTimeout(fire, (o.delaySeconds || 5) * 1000);
294 }
295
296 /* scroll trigger */
297 if (type === 'scroll' || type === 'both') {
298 function onScroll() {
299 var scrolled = (window.scrollY || document.documentElement.scrollTop);
300 var total = document.documentElement.scrollHeight - window.innerHeight;
301 if (total > 0 && (scrolled / total) * 100 >= (o.scrollPercent || 40)) {
302 window.removeEventListener('scroll', onScroll);
303 fire();
304 }
305 }
306 window.addEventListener('scroll', onScroll, { passive: true });
307 }
308
309 /* exit intent (desktop: cursor leaves window top; mobile: timeout fallback) */
310 if (o.exitIntent) {
311 var isMobile = /Mobi|Android/i.test(navigator.userAgent);
312 if (isMobile) {
313 setTimeout(fire, 15000);
314 } else {
315 var onMouse = function (e) {
316 if (e.clientY < 10) {
317 document.removeEventListener('mouseleave', onMouse);
318 fire();
319 }
320 };
321 document.addEventListener('mouseleave', onMouse);
322 }
323 }
324 }
325
326 /* ── main init ── */
327 function init() {
328 document.querySelectorAll('.bkbg-nlpop-app').forEach(function (appEl) {
329 if (appEl.dataset.rendered) return;
330 appEl.dataset.rendered = '1';
331
332 var opts;
333 try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; }
334
335 var o = Object.assign({
336 heading: "Don't miss out! 🎁",
337 subtext: 'Join 10,000+ readers. Get the best content delivered to your inbox every week.',
338 emailPlaceholder: 'Enter your email address',
339 buttonLabel: 'Subscribe Now',
340 successMessage: "🎉 You're in! Check your inbox for a welcome email.",
341 successSubtext: 'You can unsubscribe at any time.',
342 imageUrl: '',
343 imageId: 0,
344 imagePosition: 'none',
345 triggerType: 'delay',
346 delaySeconds: 5,
347 scrollPercent: 40,
348 exitIntent: false,
349 cookieName: 'bkbg-nlpop',
350 cookieDays: 7,
351 showClose: true,
352 overlayClickClose: true,
353 maxWidth: 520,
354 borderRadius: 16,
355 padding: 40,
356 headingSize: 26,
357 textSize: 15,
358 ctaSize: 15,
359 overlayOpacity: 60,
360 overlayBg: '#000000',
361 popupBg: '#ffffff',
362 headingColor: '#111827',
363 textColor: '#6b7280',
364 inputBg: '#f9fafb',
365 inputBorder: '#d1d5db',
366 inputColor: '#374151',
367 buttonBg: '#6366f1',
368 buttonColor: '#ffffff',
369 closeBg: '#f3f4f6',
370 closeColor: '#6b7280',
371 successMessage: "🎉 You're in! Check your inbox for a welcome email.",
372 successSubtext: 'You can unsubscribe at any time.'
373 }, opts);
374
375 var key = storageKey(o.cookieName);
376
377 /* check suppression */
378 if (isSuppressed(key, o.cookieDays)) return;
379
380 setupTriggers(o, key, function () {
381 var els = buildOverlay(o);
382 wireEvents(els, o, key);
383 openPopup(els, key, o.cookieDays);
384 });
385 });
386 }
387
388 if (document.readyState !== 'loading') { init(); }
389 else { document.addEventListener('DOMContentLoaded', init); }
390 })();
391