| @@ -8,8 +8,14 @@ | ||
| 8 | 8 | "use strict"; |
| 9 | 9 | |
| 10 | 10 | const FLAG = "kingAddonsAgeGateInitialized"; |
| 11 | 11 | |
| 12 | + // Module-scoped references used by helpers like setStatus()/closeOverlay(). | |
| 13 | + // These are assigned during init() and remain available for button handlers. | |
| 14 | + let payload = null; | |
| 15 | + let root = null; | |
| 16 | + let state = null; | |
| 17 | + | |
| 12 | 18 | /** |
| 13 | 19 | * Initialize Age Gate once. |
| 14 | 20 | * |
| 15 | 21 | * @returns {void} |
| @@ -14,9 +20,9 @@ | ||
| 14 | 20 | * |
| 15 | 21 | * @returns {void} |
| 16 | 22 | */ |
| 17 | 23 | const init = () => { |
| 18 | - const payload = window.kingAddonsAgeGate; | |
| 24 | + payload = window.kingAddonsAgeGate; | |
| 19 | 25 | if (!payload || !payload.enabled) { |
| 20 | 26 | return; |
| 21 | 27 | } |
| 22 | 28 | |
| @@ -31,12 +37,12 @@ | ||
| 31 | 37 | } |
| 32 | 38 | |
| 33 | 39 | window[FLAG] = true; |
| 34 | 40 | |
| 35 | - const root = ensureRoot(); | |
| 41 | + root = ensureRoot(); | |
| 36 | 42 | const card = root.querySelector('.king-addons-age-gate__card'); |
| 37 | 43 | const content = root.querySelector('.king-addons-age-gate__content'); |
| 38 | - const state = { | |
| 44 | + state = { | |
| 39 | 45 | status: payload.status || '', |
| 40 | 46 | cookieDays: resolveCookieDays(payload), |
| 41 | 47 | }; |
| 42 | 48 | |
| @@ -113,10 +119,36 @@ | ||
| 113 | 119 | * Applies design variables to the overlay. |
| 114 | 120 | */ |
| 115 | 121 | function applyDesign(rootEl, cardEl, data) { |
| 116 | 122 | const design = data.design || {}; |
| 117 | - rootEl.style.setProperty('--ka-age-overlay', design.overlayColor || '#0d0d0d'); | |
| 118 | - rootEl.style.setProperty('--ka-age-overlay-opacity', design.overlayOpacity ?? 0.7); | |
| 123 | + | |
| 124 | + // Reset template + animation modifiers (important for Elementor preview re-init). | |
| 125 | + const templateClasses = [ | |
| 126 | + 'king-addons-age-gate--template-center-card', | |
| 127 | + 'king-addons-age-gate--template-bottom-card', | |
| 128 | + 'king-addons-age-gate--template-top-card', | |
| 129 | + 'king-addons-age-gate--template-side-left', | |
| 130 | + 'king-addons-age-gate--template-side-right', | |
| 131 | + 'king-addons-age-gate--template-fullscreen', | |
| 132 | + ]; | |
| 133 | + templateClasses.forEach((cls) => rootEl.classList.remove(cls)); | |
| 134 | + Array.from(rootEl.classList) | |
| 135 | + .filter((cls) => cls.indexOf('king-addons-age-gate--animate-') === 0) | |
| 136 | + .forEach((cls) => rootEl.classList.remove(cls)); | |
| 137 | + | |
| 138 | + cardEl.classList.remove('king-addons-age-gate__card--bottom'); | |
| 139 | + cardEl.classList.remove('king-addons-age-gate__card--top'); | |
| 140 | + | |
| 141 | + const existingBg = cardEl.querySelector('.king-addons-age-gate__background'); | |
| 142 | + if (existingBg) { | |
| 143 | + existingBg.remove(); | |
| 144 | + } | |
| 145 | + | |
| 146 | + const overlayColor = (design.overlayColor || '#0d0d0d').toString(); | |
| 147 | + const overlayOpacity = clamp01(design.overlayOpacity ?? 0.7); | |
| 148 | + rootEl.style.setProperty('--ka-age-overlay', overlayColor); | |
| 149 | + rootEl.style.setProperty('--ka-age-overlay-opacity', overlayOpacity); | |
| 150 | + rootEl.style.setProperty('--ka-age-overlay-rgba', toRgba(overlayColor, overlayOpacity) || ('rgba(13, 13, 13, ' + overlayOpacity + ')')); | |
| 119 | 151 | rootEl.style.setProperty('--ka-age-card-bg', design.cardBackground || '#ffffff'); |
| 120 | 152 | rootEl.style.setProperty('--ka-age-card-width', (design.cardWidth || 520) + 'px'); |
| 121 | 153 | rootEl.style.setProperty('--ka-age-text', design.textColor || '#111827'); |
| 122 | 154 | rootEl.style.setProperty('--ka-age-title-size', (design.titleSize || 24) + 'px'); |
| @@ -131,12 +163,20 @@ | ||
| 131 | 163 | rootEl.style.setProperty('--ka-age-btn-no-hover-bg', design.buttonNoHoverBg || design.buttonNoBg || '#d92d20'); |
| 132 | 164 | rootEl.style.setProperty('--ka-age-btn-yes-hover-color', design.buttonYesHoverColor || design.buttonYesColor || '#ffffff'); |
| 133 | 165 | rootEl.style.setProperty('--ka-age-btn-no-hover-color', design.buttonNoHoverColor || design.buttonNoColor || '#ffffff'); |
| 134 | 166 | |
| 135 | - if (design.cardAlign === 'bottom') { | |
| 167 | + const template = (design.template || 'center-card').toString(); | |
| 168 | + rootEl.classList.add('king-addons-age-gate--template-' + template); | |
| 169 | + | |
| 170 | + // Back-compat: old settings may still use cardAlign. | |
| 171 | + if (design.cardAlign === 'bottom' || template === 'bottom-card') { | |
| 136 | 172 | cardEl.classList.add('king-addons-age-gate__card--bottom'); |
| 137 | 173 | } |
| 138 | 174 | |
| 175 | + if (template === 'top-card' || design.cardAlign === 'top') { | |
| 176 | + cardEl.classList.add('king-addons-age-gate__card--top'); | |
| 177 | + } | |
| 178 | + | |
| 139 | 179 | if (design.backgroundImage) { |
| 140 | 180 | const bg = document.createElement('div'); |
| 141 | 181 | bg.className = 'king-addons-age-gate__background'; |
| 142 | 182 | bg.style.backgroundImage = 'url(' + design.backgroundImage + ')'; |
| @@ -147,8 +187,55 @@ | ||
| 147 | 187 | rootEl.classList.add('king-addons-age-gate--animate-' + design.animation); |
| 148 | 188 | } |
| 149 | 189 | } |
| 150 | 190 | |
| 191 | + function clamp01(value) { | |
| 192 | + const numberValue = typeof value === 'number' ? value : parseFloat(value); | |
| 193 | + if (!Number.isFinite(numberValue)) { | |
| 194 | + return 0.7; | |
| 195 | + } | |
| 196 | + return Math.min(1, Math.max(0, numberValue)); | |
| 197 | + } | |
| 198 | + | |
| 199 | + function toRgba(color, alpha) { | |
| 200 | + const c = (color || '').toString().trim(); | |
| 201 | + if (!c) { | |
| 202 | + return null; | |
| 203 | + } | |
| 204 | + | |
| 205 | + // #RGB / #RRGGBB | |
| 206 | + if (c[0] === '#') { | |
| 207 | + let hex = c.slice(1); | |
| 208 | + if (hex.length === 3) { | |
| 209 | + hex = hex.split('').map((ch) => ch + ch).join(''); | |
| 210 | + } | |
| 211 | + if (hex.length !== 6) { | |
| 212 | + return null; | |
| 213 | + } | |
| 214 | + const r = parseInt(hex.slice(0, 2), 16); | |
| 215 | + const g = parseInt(hex.slice(2, 4), 16); | |
| 216 | + const b = parseInt(hex.slice(4, 6), 16); | |
| 217 | + if ([r, g, b].some((n) => Number.isNaN(n))) { | |
| 218 | + return null; | |
| 219 | + } | |
| 220 | + return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')'; | |
| 221 | + } | |
| 222 | + | |
| 223 | + // rgb(r, g, b) | |
| 224 | + const rgbMatch = c.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i); | |
| 225 | + if (rgbMatch) { | |
| 226 | + return 'rgba(' + rgbMatch[1] + ', ' + rgbMatch[2] + ', ' + rgbMatch[3] + ', ' + alpha + ')'; | |
| 227 | + } | |
| 228 | + | |
| 229 | + // rgba(r, g, b, a) -> replace alpha | |
| 230 | + const rgbaMatch = c.match(/^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([0-9.]+)\s*\)$/i); | |
| 231 | + if (rgbaMatch) { | |
| 232 | + return 'rgba(' + rgbaMatch[1] + ', ' + rgbaMatch[2] + ', ' + rgbaMatch[3] + ', ' + alpha + ')'; | |
| 233 | + } | |
| 234 | + | |
| 235 | + return null; | |
| 236 | + } | |
| 237 | + | |
| 151 | 238 | /** |
| 152 | 239 | * Shows the overlay. |
| 153 | 240 | */ |
| 154 | 241 | function showOverlay(rootEl, data) { |
| @@ -442,21 +529,26 @@ | ||
| 442 | 529 | /** |
| 443 | 530 | * Sets cookie and updates state. |
| 444 | 531 | */ |
| 445 | 532 | function setStatus(status, days) { |
| 446 | - const revision = payload.cookie && payload.cookie.revision ? payload.cookie.revision : ''; | |
| 447 | - const value = encodeURIComponent(status + '|' + revision); | |
| 448 | - let cookie = payload.cookie.name + '=' + value + '; path=/; SameSite=Lax'; | |
| 449 | - if (payload.cookie.domain) { | |
| 450 | - cookie += '; domain=' + payload.cookie.domain; | |
| 533 | + if (payload && payload.cookie && payload.cookie.name) { | |
| 534 | + const revision = payload.cookie.revision ? payload.cookie.revision : ''; | |
| 535 | + const value = encodeURIComponent(status + '|' + revision); | |
| 536 | + let cookie = payload.cookie.name + '=' + value + '; path=/; SameSite=Lax'; | |
| 537 | + if (payload.cookie.domain) { | |
| 538 | + cookie += '; domain=' + payload.cookie.domain; | |
| 539 | + } | |
| 540 | + if (days > 0) { | |
| 541 | + const date = new Date(); | |
| 542 | + date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); | |
| 543 | + cookie += '; expires=' + date.toUTCString(); | |
| 544 | + } | |
| 545 | + document.cookie = cookie; | |
| 451 | 546 | } |
| 452 | - if (days > 0) { | |
| 453 | - const date = new Date(); | |
| 454 | - date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); | |
| 455 | - cookie += '; expires=' + date.toUTCString(); | |
| 547 | + | |
| 548 | + if (state) { | |
| 549 | + state.status = status; | |
| 456 | 550 | } |
| 457 | - document.cookie = cookie; | |
| 458 | - state.status = status; | |
| 459 | 551 | } |
| 460 | 552 | |
| 461 | 553 | /** |
| 462 | 554 | * Locks document scroll. |
| @@ -471,9 +563,11 @@ | ||
| 471 | 563 | */ |
| 472 | 564 | function closeOverlay() { |
| 473 | 565 | const previous = document.documentElement.dataset.kaAgeGateScroll || ''; |
| 474 | 566 | document.documentElement.style.overflow = previous; |
| 475 | - root.classList.remove('king-addons-age-gate--visible'); | |
| 567 | + if (root) { | |
| 568 | + root.classList.remove('king-addons-age-gate--visible'); | |
| 569 | + } | |
| 476 | 570 | } |
| 477 | 571 | |
| 478 | 572 | /** |
| 479 | 573 | * Cookie days resolver based on behaviour. |