Plugin Directory

Changeset 3334981


Ignore:
Timestamp:
07/27/2025 07:33:25 PM (8 months ago)
Author:
emberlydigital
Message:

Updating trunk with new version

Location:
emberly-popups/trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • emberly-popups/trunk/emberly-popups.php

    r3290697 r3334981  
    44   Author URI: https://emberlydigital.com/
    55   description: Lightweight, accessible popups called via functions. By developers, for developers!
    6    Version: 1.2
     6   Version: 1.3
    77   Author: Emberly Digital
    88   License: GPL2
  • emberly-popups/trunk/lib/functions.php

    r3290697 r3334981  
    7474
    7575    // Close the popup HTML structure
    76     $popup_end .= '</div></div></div></div>';
     76    $popup_end = '</div></div></div></div>';
    7777
    7878    // Return or echo
  • emberly-popups/trunk/lib/pop.js

    r3290697 r3334981  
    11document.addEventListener('DOMContentLoaded', () => {
    2     const focusableElements = 'input, button, [href], select, textarea, [tabindex]:not([tabindex="-1"])';
    3     let lastFocusedElement; // To store the last focused element before popup opens
    4 
    5     const allAutoOpenPopups = document.querySelectorAll('.em-auto-open');
    6 
    7     function isDebugMode(popupOverlay) {
    8         return popupOverlay?.getAttribute('data-em-popup-debug') === 'true';
    9     }
    10 
    11     function forceLabelClicksInsidePopup(popupOverlay) {
    12         if (!popupOverlay) return;
    13    
    14         popupOverlay.addEventListener('click', function(e) {
    15             const label = e.target.closest('label[for]');
    16             if (label) {
    17                 const inputId = label.getAttribute('for');
    18                 const input = document.getElementById(inputId);
    19                 if (input) {
    20                     input.focus();   // Focus first
    21                     input.click();   // Then trigger click manually
    22 
    23                     // If debug mode is enabled, log the event.
    24                     if (isDebugMode(popupOverlay)) {
    25                         console.log(`[Emberly Popups] Label clicked: ${label.textContent}`);
    26                         console.log(`[Emberly Popups] Input focused: ${inputId}`);
    27                     }
    28                 }
    29             }
    30         });
    31     }   
    32 
    33     // Function to set a cookie with a default expiration of 24 hours
    34     function setCookie(name, value, hours = 24) {
    35         const date = new Date();
    36         date.setTime(date.getTime() + (hours * 60 * 60 * 1000)); // Convert hours to milliseconds
    37         const expires = "expires=" + date.toUTCString();
    38         document.cookie = `${name}=${value}; ${expires}; path=/`;
    39        
    40         // Log information about the current UTC, and when the cookie will expire.
    41         console.log(`[Emberly Popups] UTC: ${date.toUTCString()}`);
    42         console.log(`[Emberly Popups] Emberly Popup Cookie expires in ${hours} hours`);
    43 
    44         // If debug mode is enabled, log the event.
    45         const popupOverlay = document.querySelector('.em-popup-overlay.em-active');
    46         if (isDebugMode(popupOverlay)) {
    47             console.log(`[Emberly Popups] Cookie set: ${name}=${value}`);
    48         }
    49     }
    50 
    51     // Function to get a cookie
    52     function getCookie(name) {
    53         const cookies = document.cookie.split(';');
    54         for (let i = 0; i < cookies.length; i++) {
    55             const cookie = cookies[i].trim();
    56             if (cookie.startsWith(name + '=')) {
    57                 return cookie.substring(name.length + 1);
    58             }
    59         }
    60         return null;
    61     }
    62 
    63     // Function to keep focus inside the popup
    64     function trapFocus(event) {
    65         const dialog = document.querySelector('.em-popup-overlay.em-active > .em-popup');
    66         const isTabPressed = event.key === 'Tab';
    67 
    68         if (!isTabPressed) return;
    69 
    70         const focusableContent = dialog.querySelectorAll(focusableElements);
    71         const firstFocusableElement = focusableContent[0];
    72         const lastFocusableElement = focusableContent[focusableContent.length - 1];
    73 
    74         if (event.shiftKey) {
    75             // If Shift + Tab
    76             if (document.activeElement === firstFocusableElement) {
    77                 event.preventDefault();
    78                 lastFocusableElement.focus();
    79             }
    80         } else {
    81             // If Tab
    82             if (document.activeElement === lastFocusableElement) {
    83                 event.preventDefault();
    84                 firstFocusableElement.focus();
    85             }
    86         }
    87     }
    88 
    89     function unfocus(event) {
    90         const dialog = document.querySelector('.em-popup-overlay.em-active > .em-popup');
    91         // Get the list of focusable elements
    92         const focusableContent = Array.from(dialog.querySelectorAll(focusableElements));
    93 
    94         if(!focusableContent.includes(event.target)) {
    95             event.preventDefault();
    96             // Focus the first focusable element inside the popup
    97             const firstFocusableElement = dialog.querySelector(focusableElements);
    98             if(firstFocusableElement) {
    99                 firstFocusableElement.focus();
    100             }
    101         }
    102     }
    103 
    104     function lockScroll(popupOverlay) {
    105         const scrollY = window.scrollY || document.documentElement.scrollTop;
    106         document.body.style.position = 'fixed';
    107         document.body.style.top = `-${scrollY}px`;
    108         document.body.style.left = '0';
    109         document.body.style.right = '0';
    110         document.body.style.width = '100%';
    111         document.body.dataset.scrollY = scrollY;
    112    
    113         if (isDebugMode(popupOverlay)) {
    114             console.log(`[Emberly Popups] Scroll locked at: ${scrollY}px`);
    115         }
    116     }
    117    
     2    const focusableElements = 'input, button, [href], select, textarea, [tabindex]:not([tabindex="-1"])';
     3    let lastFocusedElement; // To store the last focused element before popup opens
     4
     5    const allAutoOpenPopups = document.querySelectorAll('.em-auto-open');
     6
     7    function isDebugMode(popupOverlay) {
     8       return popupOverlay?.getAttribute('data-em-popup-debug') === 'true';
     9    }
     10
     11    function forceLabelClicksInsidePopup(popupOverlay) {
     12       if (!popupOverlay) return;
     13
     14       popupOverlay.addEventListener('click', function(e) {
     15          const label = e.target.closest('label[for]');
     16          if (label) {
     17             const inputId = label.getAttribute('for');
     18             const input = document.getElementById(inputId);
     19             if (input) {
     20                input.focus();
     21                input.click();
     22
     23                if (isDebugMode(popupOverlay)) {
     24                   console.log(`[Emberly Popups] Label clicked: ${label.textContent}`);
     25                   console.log(`[Emberly Popups] Input focused: ${inputId}`);
     26                }
     27             }
     28          }
     29       });
     30    }
     31
     32    function setCookie(name, value, hours = 24) {
     33       const date = new Date();
     34       date.setTime(date.getTime() + (hours * 60 * 60 * 1000));
     35       document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`;
     36
     37       console.log(`[Emberly Popups] UTC: ${date.toUTCString()}`);
     38       console.log(`[Emberly Popups] Emberly Popup Cookie expires in ${hours} hours`);
     39
     40       const popupOverlay = document.querySelector('.em-popup-overlay.em-active');
     41       if (isDebugMode(popupOverlay)) {
     42          console.log(`[Emberly Popups] Cookie set: ${name}=${value}`);
     43       }
     44    }
     45
     46    function getCookie(name) {
     47       return document.cookie
     48          .split(';')
     49          .map(c => c.trim())
     50          .find(c => c.startsWith(name + '='))
     51          ?.substring(name.length + 1) || null;
     52    }
     53
     54    function trapFocus(event) {
     55       const dialog = document.querySelector('.em-popup-overlay.em-active > .em-popup');
     56       if (event.key !== 'Tab' || !dialog) return;
     57
     58       const focusable = dialog.querySelectorAll(focusableElements);
     59       const first = focusable[0], last = focusable[focusable.length - 1];
     60       if (event.shiftKey && document.activeElement === first) {
     61          event.preventDefault(), last.focus();
     62       } else if (!event.shiftKey && document.activeElement === last) {
     63          event.preventDefault(), first.focus();
     64       }
     65    }
     66
     67    function unfocus(event) {
     68       const dialog = document.querySelector('.em-popup-overlay.em-active > .em-popup');
     69       if (!dialog) return;
     70       const focusable = Array.from(dialog.querySelectorAll(focusableElements));
     71       if (!focusable.includes(event.target)) {
     72          event.preventDefault();
     73          dialog.querySelector(focusableElements)?.focus();
     74       }
     75    }
     76
     77    function lockScroll(popupOverlay) {
     78       const scrollY = window.scrollY;
     79       Object.assign(document.body.style, {
     80          position: 'fixed',
     81          top: `-${scrollY}px`,
     82          left: '0',
     83          right: '0',
     84          width: '100%'
     85       });
     86       document.body.dataset.scrollY = scrollY;
     87
     88       if (isDebugMode(popupOverlay)) {
     89          console.log(`[Emberly Popups] Scroll locked at: ${scrollY}px`);
     90       }
     91    }
     92
    11893    function unlockScroll(popupOverlay) {
    11994        const scrollY = parseInt(document.body.dataset.scrollY || '0', 10);
    120         document.body.style.position = '';
    121         document.body.style.top = '';
    122         document.body.style.left = '';
    123         document.body.style.right = '';
    124         document.body.style.width = '';
     95
     96        // Temporarily disable smooth scroll
     97        const html = document.documentElement;
     98        const prevScrollBehavior = html.style.scrollBehavior;
     99        html.style.scrollBehavior = 'auto';
     100
     101        // Restore scroll position instantly
     102        Object.assign(document.body.style, {
     103            position: '',
     104            top: '',
     105            left: '',
     106            right: '',
     107            width: ''
     108        });
    125109        delete document.body.dataset.scrollY;
     110
    126111        window.scrollTo(0, scrollY);
    127    
     112
     113        // Re-enable smooth scroll after short delay
     114        setTimeout(() => {
     115            html.style.scrollBehavior = prevScrollBehavior || '';
     116        }, 50);
     117
    128118        if (isDebugMode(popupOverlay)) {
    129119            console.log(`[Emberly Popups] Scroll unlocked, returned to: ${scrollY}px`);
    130120        }
    131     }   
    132 
    133     // Function to handle the popup open
    134     function openPopup(trigger) {
    135         lastFocusedElement = document.activeElement; // Save the last focused element
    136         const popupId = trigger.getAttribute('em-popup-trigger-id');
    137 
    138         const popupOverlay = document.querySelector('.em-popup-overlay[data-em-popup-id="' + popupId + '"]');
    139         handleOpenPopup(popupOverlay); 
    140        
    141         // If debug mode is enabled, log the event.
    142         if (isDebugMode(popupOverlay)) {
    143             console.log(`[Emberly Popups] Popup opened: ${popupId}`);
    144         }
    145121    }
    146122
    147     function openPopupByID(popupId) {
    148         const popupOverlay = document.querySelector('.em-popup-overlay[data-em-popup-id="' + popupId + '"]');
    149         handleOpenPopup(popupOverlay);
    150 
    151         // If debug mode is enabled, log the event.
    152         if (isDebugMode(popupOverlay)) {
    153             console.log(`[Emberly Popups] Popup opened: ${popupId}`);
    154         }
    155     }
    156 
    157     // Function to handle the popup open
    158     function handleOpenPopup(popupOverlay) {
    159         if (!popupOverlay) return;
    160    
    161         // Check for a delay attribute
    162         let delayTime = popupOverlay.getAttribute('data-em-delay');
    163         delayTime = delayTime ? parseInt(delayTime) : 0;
    164    
    165         const delay = () => {
    166             popupOverlay.style.display = 'flex';
    167             setTimeout(() => {
    168                 popupOverlay.classList.add('em-active');
    169             }, 10); // give browser a tick to apply display change
    170 
    171             // Manual label-to-input binding fix
    172             forceLabelClicksInsidePopup(popupOverlay);
    173    
    174             // Turn on scroll lock.
    175             lockScroll(popupOverlay);
    176    
    177             // Make divs surrounding cross-origin iframes focusable
    178             const iframeContainers = popupOverlay.querySelectorAll('div:has(> iframe)');
    179             iframeContainers.forEach((iframe) => {
    180                 iframe.setAttribute('tabindex', "0");
    181             });
    182    
    183             // Focus the first focusable element inside the popup
    184             const firstFocusableElement = popupOverlay.querySelector(focusableElements);
    185             if (firstFocusableElement) {
    186                 firstFocusableElement.focus();
    187                 // Prevent being able to focus on the whole overlay
    188                 popupOverlay.addEventListener('click', unfocus);
    189             }
    190    
    191             // Add event listener to handle trap focus
    192             document.addEventListener('keydown', trapFocus);
    193         };
    194    
    195         // If there is a delay, wait before opening
    196         if (delayTime > 0) {
    197             setTimeout(delay, delayTime);
    198         } else {
    199             delay();
    200         }
    201 
    202         // If debug mode is enabled, log the event.
    203         if (isDebugMode(popupOverlay)) {
    204             console.log(`[Emberly Popups] Popup opened with delay: ${delayTime}ms`);
    205         }
    206     }
    207 
    208     // Function to handle the popup close and stop the video
    209     function closePopup(popupId) {
    210         console.log(popupId);
    211         const popupOverlay = document.querySelector(`.em-popup-overlay[data-em-popup-id="${popupId}"]`);
    212         if (popupOverlay) {
    213             popupOverlay.classList.remove('em-active');
    214             setTimeout(() => {
    215                 popupOverlay.style.display = 'none';
    216             }, 400); // 400ms = match the CSS opacity transition
    217            
    218             popupOverlay.removeEventListener('click', unfocus);
    219             if (lastFocusedElement) {
    220                 lastFocusedElement.focus(); // Return focus to the last focused element
    221             }
    222         }
    223         // Remove scroll lock from the body and html.
    224         unlockScroll(popupOverlay);
    225 
    226         // Remove event listener to handle trap focus
    227         document.removeEventListener('keydown', trapFocus);
    228 
    229         // If debug mode is enabled, log the event.
    230         if (isDebugMode(popupOverlay)) {
    231             console.log(`[Emberly Popups] Popup closed: ${popupId}`);
    232         }
    233     }
    234 
    235     // Open the popup when the trigger is clicked.
    236     const triggers = document.querySelectorAll('[em-popup-trigger-id]');
    237     triggers.forEach(trigger => {
    238         trigger.addEventListener('click', (event) => {
    239             event.preventDefault();
    240    
    241             const popupId = trigger.getAttribute('em-popup-trigger-id');
    242             const popupOverlay = document.querySelector(`.em-popup-overlay[data-em-popup-id="${popupId}"]`);
    243    
    244             // Always log something for verification
    245             console.log(`[Emberly Popup] Trigger clicked for popup ID: "${popupId}"`);
    246            
    247             if (!popupOverlay) {
    248                 console.warn(`[Emberly Popup] No popup overlay found for ID: "${popupId}"`);
    249             } else {
    250                 console.log(`[Emberly Popup] Found popup overlay for ID: "${popupId}"`);
    251             }
    252    
    253             if (popupOverlay?.getAttribute('data-em-popup-debug') === 'true') {
    254                 console.log('%c[Emberly Popup Debug]', 'color: orange; font-weight: bold;');
    255                 console.log('Trigger element:', trigger);
    256                 console.log('Popup overlay element:', popupOverlay);
    257             }
    258    
    259             openPopup(trigger);
    260         });
    261     });
    262 
    263     // Add event listeners to the close buttons
    264     const closeButtons = document.querySelectorAll('.em-popup-close');
    265     closeButtons.forEach(closeButton => {
    266         closeButton.addEventListener('click', () => {
    267             const popupId = closeButton.closest('.em-popup').getAttribute('data-em-popup-id');
    268             closePopup(popupId);
    269         });
    270     });
    271 
    272     // Close popup when clicking outside the popup content
    273     const popups = document.querySelectorAll('.em-popup-overlay');
    274     popups.forEach(popup => {
    275         popup.addEventListener('click', (e) => {
    276             const popupContent = popup.querySelector('.em-popup');
    277             if (!popupContent.contains(e.target)) {
    278                 const popupId = popup.getAttribute('data-em-popup-id');
    279                 closePopup(popupId);
    280             }
    281         });
    282     });
    283 
    284     // look for all elements with the class em-show-once
    285     allAutoOpenPopups.forEach(element => {
    286         const id = element.getAttribute('data-em-popup-id');
    287         const shouldOpenOnce = element.classList.contains('em-show-once');
    288        
    289         let displayOnceMethod = 'cookie'; // default
    290         if (element.classList.contains('em-persistence-method-session')) {
    291             displayOnceMethod = 'session';
    292         }
    293 
    294         const displayEvery = element.getAttribute('data-em-show-interval-ms');
    295         const displayEveryMilliseconds = displayEvery ? parseInt(displayEvery) : 0;
    296 
    297         let alreadyDisplayed = false;
    298         const now = Date.now();
    299 
    300         if (shouldOpenOnce) {
    301             if (displayOnceMethod === 'cookie') {
    302                 alreadyDisplayed = getCookie(id);
    303             } else if (displayOnceMethod === 'session') {
    304                 alreadyDisplayed = sessionStorage.getItem(id);
    305             }
    306         } else if (displayEveryMilliseconds > 0) {
    307             let lastShown = null;
    308             if (displayOnceMethod === 'cookie') {
    309                 lastShown = getCookie(id + '_timestamp');
    310             } else if (displayOnceMethod === 'session') {
    311                 lastShown = sessionStorage.getItem(id + '_timestamp');
    312             }
    313             if (lastShown) {
    314                 const lastShownTime = parseInt(lastShown, 10);
    315                 if (now - lastShownTime < displayEveryMilliseconds) {
    316                     alreadyDisplayed = true; // too soon, don't show
    317                 }
    318             }
    319         }
    320 
    321         if (alreadyDisplayed) return;
    322 
    323         // Open popup
    324         openPopupByID(id);
    325 
    326         // After opening, store either:
    327         if (shouldOpenOnce) {
    328             if (displayOnceMethod === 'cookie') {
    329                 setCookie(id, 'true', 365 * 24); // expires in 1 year (effectively forever)
    330             } else {
    331                 sessionStorage.setItem(id, 'true');
    332             }
    333         } else if (displayEveryMilliseconds > 0) {
    334             if (displayOnceMethod === 'cookie') {
    335                 // Convert milliseconds to hours for cookie expiration
    336                 const hours = displayEveryMilliseconds / (1000 * 60 * 60);
    337                 setCookie(id + '_timestamp', now.toString(), hours);
    338             } else {
    339                 sessionStorage.setItem(id + '_timestamp', now.toString());
    340             }
    341         }
    342     });
    343 
    344     // Log debug info when popups are initialized (on load)
    345     document.querySelectorAll('.em-popup-overlay').forEach(popup => {
    346         if (isDebugMode(popup)) {
    347             const id = popup.getAttribute('data-em-popup-id');
    348             const delay = popup.getAttribute('data-em-delay') || '0';
    349             const interval = popup.getAttribute('data-em-show-interval-ms') || '0';
    350             const showOnce = popup.classList.contains('em-show-once');
    351             const persistence = popup.classList.contains('em-persistence-method-session') ? 'session' : 'cookie';
    352 
    353             console.groupCollapsed(`[Emberly Popups] Popup Loaded: "${id}"`);
    354             console.table({
    355                 'Popup ID': id,
    356                 'Delay (ms)': delay,
    357                 'Show Once': showOnce,
    358                 'Persistence': persistence,
    359                 'Interval (ms)': interval
    360             });
    361             console.log('Popup Element:', popup);
    362             console.groupEnd();
    363         }
    364     });
    365 
    366     // Add escape key support to close the popup
    367     document.addEventListener('keydown', (event) => {
    368         if (event.key === 'Escape') {
    369             const activePopup = document.querySelector('.em-popup-overlay.em-active');
    370             if (activePopup) {
    371                 const popupId = activePopup.getAttribute('id');
    372                 closePopup(popupId);
    373             }
    374         }
    375     });
     123    function handleOpenPopup(popupOverlay) {
     124       if (!popupOverlay) return;
     125       let delayTime = parseInt(popupOverlay.getAttribute('data-em-delay') || '0', 10);
     126
     127       const doOpen = () => {
     128          popupOverlay.style.display = 'flex';
     129          setTimeout(() => popupOverlay.classList.add('em-active'), 10);
     130
     131          forceLabelClicksInsidePopup(popupOverlay);
     132          lockScroll(popupOverlay);
     133
     134          popupOverlay.querySelectorAll('div:has(> iframe)').forEach(iframe => {
     135             iframe.setAttribute('tabindex', '0');
     136          });
     137
     138          const first = popupOverlay.querySelector(focusableElements);
     139          if (first) {
     140             first.focus();
     141             popupOverlay.addEventListener('click', unfocus);
     142          }
     143          document.addEventListener('keydown', trapFocus);
     144
     145          if (isDebugMode(popupOverlay)) {
     146             console.log(`[Emberly Popups] Popup opened with delay: ${delayTime}ms`);
     147          }
     148       };
     149
     150       delayTime > 0 ? setTimeout(doOpen, delayTime) : doOpen();
     151
     152       if (isDebugMode(popupOverlay)) {
     153          console.log(`[Emberly Popups] Popup opened: ${popupOverlay.dataset.emPopupId}`);
     154       }
     155    }
     156
     157    function openPopup(trigger) {
     158       lastFocusedElement = document.activeElement;
     159       const popupId = trigger.getAttribute('em-popup-trigger-id');
     160       const popupOverlay = document.querySelector(`.em-popup-overlay[data-em-popup-id="${popupId}"]`);
     161       handleOpenPopup(popupOverlay);
     162    }
     163
     164    function openPopupByID(popupId) {
     165       const popupOverlay = document.querySelector(`.em-popup-overlay[data-em-popup-id="${popupId}"]`);
     166       handleOpenPopup(popupOverlay);
     167    }
     168
     169    function closePopup(popupId) {
     170       const popupOverlay = document.querySelector(`.em-popup-overlay[data-em-popup-id="${popupId}"]`);
     171       if (!popupOverlay) return;
     172
     173       popupOverlay.classList.remove('em-active');
     174       setTimeout(() => popupOverlay.style.display = 'none', 400);
     175       popupOverlay.removeEventListener('click', unfocus);
     176       unlockScroll(popupOverlay);
     177       document.removeEventListener('keydown', trapFocus);
     178
     179       lastFocusedElement?.focus();
     180
     181       if (isDebugMode(popupOverlay)) {
     182          console.log(`[Emberly Popups] Popup closed: ${popupId}`);
     183       }
     184    }
     185
     186    // ——— Delegated click handler —————————————————————————
     187    document.addEventListener('click', event => {
     188       // 1) Popup trigger?
     189       const trigger = event.target.closest('[em-popup-trigger-id]');
     190       if (trigger) {
     191          event.preventDefault();
     192          const id = trigger.getAttribute('em-popup-trigger-id');
     193          console.log(`[Emberly Popup] Trigger clicked for popup ID: "${id}"`);
     194          openPopup(trigger);
     195          return;
     196       }
     197
     198       // 2) Close button inside popup?
     199       const closeBtn = event.target.closest('.em-popup-close');
     200       if (closeBtn) {
     201          event.preventDefault();
     202          const overlay = closeBtn.closest('.em-popup-overlay');
     203          if (overlay) closePopup(overlay.dataset.emPopupId);
     204          return;
     205       }
     206
     207       // 3) Click outside popup content?
     208       const overlay = event.target.closest('.em-popup-overlay');
     209       if (overlay && !event.target.closest('.em-popup')) {
     210          event.preventDefault();
     211          closePopup(overlay.dataset.emPopupId);
     212       }
     213    });
     214
     215    // ——— Auto-open logic —————————————————————
     216    allAutoOpenPopups.forEach(el => {
     217       const id = el.dataset.emPopupId;
     218       const once = el.classList.contains('em-show-once');
     219       const method = el.classList.contains('em-persistence-method-session') ? 'session' : 'cookie';
     220       const intervalMs = parseInt(el.getAttribute('data-em-show-interval-ms') || '0', 10);
     221       const now = Date.now();
     222       let shown = false;
     223
     224       if (once) {
     225          shown = method === 'cookie' ? getCookie(id) : sessionStorage.getItem(id);
     226       } else if (intervalMs > 0) {
     227          const last = method === 'cookie'
     228             ? getCookie(id + '_timestamp')
     229             : sessionStorage.getItem(id + '_timestamp');
     230          if (last && now - parseInt(last, 10) < intervalMs) shown = true;
     231       }
     232
     233       if (shown) return;
     234       openPopupByID(id);
     235
     236       if (once) {
     237          if (method === 'cookie') setCookie(id, 'true', 365 * 24);
     238          else sessionStorage.setItem(id, 'true');
     239       } else if (intervalMs > 0) {
     240          if (method === 'cookie') {
     241             setCookie(id + '_timestamp', now.toString(), intervalMs / (1000*60*60));
     242          } else {
     243             sessionStorage.setItem(id + '_timestamp', now.toString());
     244          }
     245       }
     246    });
     247
     248    // ——— Debug info on init ——————————————————————————
     249    document.querySelectorAll('.em-popup-overlay').forEach(popup => {
     250       if (isDebugMode(popup)) {
     251          const id = popup.dataset.emPopupId;
     252          console.groupCollapsed(`[Emberly Popups] Popup Loaded: "${id}"`);
     253          console.table({
     254             'Popup ID': id,
     255             'Delay (ms)': popup.getAttribute('data-em-delay') || '0',
     256             'Show Once': popup.classList.contains('em-show-once'),
     257             'Persistence': popup.classList.contains('em-persistence-method-session') ? 'session' : 'cookie',
     258             'Interval (ms)': popup.getAttribute('data-em-show-interval-ms') || '0',
     259          });
     260          console.log('Popup Element:', popup);
     261          console.groupEnd();
     262       }
     263    });
     264
     265    // ——— Escape-key support ——————————————————————————
     266    document.addEventListener('keydown', event => {
     267       if (event.key === 'Escape') {
     268          const active = document.querySelector('.em-popup-overlay.em-active');
     269          if (active) closePopup(active.dataset.emPopupId);
     270       }
     271    });
    376272});
  • emberly-popups/trunk/lib/styles.css

    r3290697 r3334981  
    4949
    5050.em-popup .em-popup-close {
    51     width: 1rem;
    52     height: 1rem;
     51    width: 1.5rem;
     52    height: 1.5rem;
    5353    display: flex;
    5454    position: absolute;
     
    5858    transition: all 0.4s ease;
    5959    opacity: 0.5;
    60     padding: 0;
     60    padding: 0.25rem;
     61    border: 0;
     62}
     63
     64.em-popup .em-popup-close, .em-popup .em-popup-close:focus {
    6165    background: transparent;
    62     border: 0;
     66    color: #222;
    6367}
    6468
  • emberly-popups/trunk/readme.txt

    r3290697 r3334981  
    11=== Emberly Popups ===
    22Contributors: emberlydigital
    3 Tags: popup, modal, overlay, wordpress popup
     3Tags: popup, modal, overlay, accessible, developer
    44Requires at least: 5.0
    55Tested up to: 6.8
    6 Stable tag: 1.2
     6Stable tag: 1.3
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
    9 Lightweight, accessible popups called via functions. By developers, for developers!
     9
     10Lightweight, accessible popups for WordPress—made by developers, for developers.
    1011
    1112== Description ==
    1213
    13 Emberly Popups is a lightweight WordPress function that creates accessible, customizable popup modals.
     14**Emberly Popups** is a simple PHP function that generates ARIA‑compliant modal popups. Great for newsletters, surveys, announcements, or any call‑to‑action:
    1415
    15 Features include:
    16 - Open automatically after a delay
    17 - Show only once per user with cookie- or session-based control
    18 - Control how often the popup appears using an interval
    19 - Support for shortcodes inside popup content
    20 - Adjustable popup width and padding
    21 - Accessible HTML structure with ARIA roles and attributes
     16* Auto‑open after a configurable delay 
     17* Cookie‑ or session‑based “show once” control 
     18* Repeat interval (milliseconds) between popups 
     19* Shortcode support inside popup content 
     20* Adjustable width and padding 
     21* Accessible markup with proper ARIA roles
    2222
    2323== Installation ==
    2424
    25 1. Include the `emberly_popup()` function in your theme or plugin.
    26 2. Call `emberly_popup()` where you want the popup to appear.
    27 3. Make sure `assets/icons/close.svg` exists for the close button.
     251. Copy the **emberly_popup()** function into your theme’s `functions.php` or a custom plugin. 
     262. Enqueue or include the popup’s CSS and JS assets. 
     273. Ensure `assets/icons/close.svg` exists (used for the close button). 
     284. Call `emberly_popup()` anywhere in your PHP templates.
     29
     30== Usage ==
     31
     32=== PHP 8+ (named arguments) ===
     33```php
     34emberly_popup(
     35    title            : 'Welcome!',
     36    content          : '<p>Thanks for visiting our site. Don’t miss our latest offers!</p>',
     37    id               : 'welcome-popup',
     38    width            : '50rem',
     39    padding          : '3rem',
     40    echo             : true,
     41    output_shortcodes: true,
     42    auto_open        : true,
     43    delay            : 2000,
     44    show_once        : false,
     45    persistence_method: 'session',
     46    show_interval_ms : 1800000  // 30 minutes
     47);
     48```
     49
     50=== PHP 7 & earlier (ordered arguments) ===
     51```php
     52emberly_popup(
     53    '',                               // $title
     54    '<p>Thanks for visiting our site. Don't miss our latest offers!</p>',
     55    'category-survey-popup',          // $id
     56    '50rem',                          // $width
     57    '3rem',                           // $padding
     58    true,                             // $echo
     59    true,                             // $output_shortcodes
     60    true,                             // $auto_open
     61    2000,                             // $delay
     62    false,                            // $show_once
     63    'session',                        // $persistence_method
     64    1800000                           // $show_interval_ms
     65);
     66```
     67
     68=== Manual triggers === 
     69Anywhere in your markup, add:
     70```html
     71<a href="#" em-popup-trigger-id="welcome-popup">Open Welcome Popup</a>
     72```
     73The `em-popup-trigger-id` value must match the `$id` you set in `emberly_popup()`.
     74
     75== Parameters ==
     76
     77| Parameter             | Type    | Default   | Description                                                                 |
     78|-----------------------|---------|-----------|-----------------------------------------------------------------------------|
     79| title                 | string  | ''        | Popup heading text.                                                         |
     80| content               | string  | ''        | HTML for the popup’s body.                                                  |
     81| id                    | string  | ''        | Unique popup identifier (required for cookies/sessions & triggers).         |
     82| width                 | string  | '60rem'   | CSS max-width for the popup container.                                       |
     83| padding               | string  | '3rem'    | Inner padding inside the popup.                                              |
     84| echo                  | bool    | true      | Echo the markup immediately (false to return as string).                     |
     85| output_shortcodes     | bool    | false     | Process WordPress shortcodes in `content`.                                   |
     86| auto_open             | bool    | false     | Automatically open after `delay` ms.                                         |
     87| delay                 | int     | 0         | Milliseconds to wait before auto-open.                                       |
     88| show_once             | bool    | false     | If true, shows only once per session/cookie period.                          |
     89| persistence_method    | string  | 'cookie'  | Where to store “shown” flag: 'cookie' or 'session'.                           |
     90| show_interval_ms      | int     | 0         | Minimum interval (ms) before showing again. 0 disables repeats.              |
     91| debug                 | bool    | false     | Log internal events to browser console (load, open, cookies, scroll lock).   |
    2892
    2993== Debugging ==
    3094
    31 Want to know exactly when a popup loads, opens, sets cookies, or locks the scroll?
    32 
    33 Use the `debug` parameter to enable verbose logging for a specific popup. This outputs helpful information to the browser console for troubleshooting purposes.
    34 
    35 To enable:
     95Add `debug: true` (PHP 8+) or append `true` as the 13th argument (PHP 7) to enable console logging:
    3696
    3797```php
     98// PHP 8+ example
    3899emberly_popup(
    39     title: 'Example',
     100    title : 'Debug Popup',
    40101    content: '<p>Debugging is on!</p>',
    41     id: 'debug-popup',
    42     debug: true // 👈 enable debug mode
    43 );
    44 
    45 == Usage ==
    46 
    47 Calling a popup:
    48 
    49 Popups can only be called via PHP functions. The most ideal scenario is to call them using PHP 8+, which allows you to be specific about which attributes you're utilizing.
    50 
    51 Example using **PHP 8+ named arguments**:
    52 
    53 ```php
    54 emberly_popup(
    55     title: '',
    56     content: '<p>Thanks for visiting our site. Don\'t miss our latest offers!</p>',
    57     id: 'welcome-popup',
    58     width: '50rem',
    59     padding: '3rem',
    60     echo: true,
    61     output_shortcodes: true,
    62     auto_open: true,
    63     delay: 2000,
    64     show_once: false,
    65     persistence_method: 'session',
    66     show_interval_ms: 1800000 // 30 minutes in milliseconds
     102    id    : 'debug-popup',
     103    debug : true
    67104);
    68105```
    69106
    70 Example for **PHP 7 and earlier** (ordered arguments):
    71 
    72 ```php
    73 emberly_popup(
    74     '', // $title
    75     '<p>Thanks for visiting our site. Don\'t miss our latest offers!</p>',
    76     'category-survey-popup', // $id
    77     '50rem', // $width
    78     '3rem', // $padding
    79     true, // $echo
    80     true, // $output_shortcodes
    81     true, // $auto_open
    82     2000, // $delay
    83     false, // $show_once
    84     'session', // $persistence_method
    85     1800000 // $show_interval_ms
    86 );
    87 ```
    88 
    89 == Triggering Popups ==
    90 
    91 You can open popups automatically by using the auto_open parameter.
    92 
    93 Want to open with a click? Popups can be opened manually by adding a trigger element anywhere on your page with the `em-popup-trigger-id` attribute. The value should match the ID you assigned to the popup via the `$id` parameter in your `emberly_popup()` call.
    94 
    95 ### Example
    96 
    97 ```html
    98 <a href="#" em-popup-trigger-id="welcome-popup">Open Welcome Popup</a>
    99 
    100 Parameters:
    101 
    102 | Parameter | Type | Default | Description |
    103 |:--|:--|:--|:--|
    104 | $title | string | '' | Title text of the popup. |
    105 | $content | string | '' | Content HTML inside the popup. |
    106 | $id | string | '' | Unique ID for the popup. |
    107 | $width | string | '60rem' | Maximum width of the popup. |
    108 | $padding | string | '3rem' | Padding inside the popup. |
    109 | $echo | bool | true | Echo the popup or return it as a string. |
    110 | $output_shortcodes | bool | false | Run shortcodes inside the content. |
    111 | $auto_open | bool | false | Open automatically after a delay. |
    112 | $delay | int | 0 | Auto-open delay in milliseconds. |
    113 | $show_once | bool | false | Only show once per session or cookie, depending on persistence. |
    114 | $persistence_method | string | 'cookie' | Where to store whether the popup was shown. Options: 'cookie' or 'session'. |
    115 | $show_interval_ms | int | 0 | How often (in milliseconds) the popup should show again. 0 disables repeat showing. |
    116 
    117107== Frequently Asked Questions ==
    118108
    119 = What happens if no ID is set? =
     109= What if I don’t set an ID? =
     110An ID is required for cookies/sessions and manual triggers; omit at your own risk.
    120111
    121 Each popup should have a unique ID for proper functioning, especially when using cookies, sessions, or auto-open behavior.
    122 
    123 = Can I use shortcodes inside the popup content? =
    124 
    125 Yes, just set `$output_shortcodes` to `true`.
     112= Can I use shortcodes in my popup? =
     113Yes—set `output_shortcodes` to `true`.
    126114
    127115== Changelog ==
    128116
     117= 1.3 =
     118* Fixed minor CSS selector issue in nested themes.
     119* Added `debug` parameter to PHP 7 ordered args example.
     120* Improved README formatting for WP.org.
     121* Fixed trigger logging order. Now logs triggers before content by moving the trigger handler up to the body.
     122* Added compatibility to override smooth scrolling when closing a popup.
     123
     124= 1.2 =
     125* Added `debug` logging support.
     126* Support for PHP 8 named arguments.
     127
    129128= 1.0 =
    130 * Initial release.
     129* Initial public release.
    131130
    132131== Upgrade Notice ==
    133132
    134 = 1.0 =
    135 First public release.
     133= 1.3 =
     134Make sure to rebuild assets if you override CSS/JS; no breaking changes.
    136135
    137136== Screenshots ==
    138 
    139 No screenshots available.
     137No screenshots yet. Feel free to submit one via the support forum!
Note: See TracChangeset for help on using the changeset viewer.