PageSourceSearch

https://www.orchardortho.com/application/files/cache/js/b1a76215428135fe5335f8df26839c96c41fbecc.js

js orchardortho.com collected 2026-10-04 13:25:21 UTC 23,029 bytes, 644 lines download raw bytes

1/**
2 * Popup Alert JavaScript
3 * Reads settings from window.popupAlertSettings and displays popup
4 */
5
6(function() {
7    'use strict';
8
9    // Check if settings exist
10    if (typeof window.popupAlertSettings === 'undefined' || !window.popupAlertSettings.popup) {
11        return;
12    }
13
14    var config = window.popupAlertSettings.popup;
15
16    // Configuration
17    var CONFIG = {
18        autoShowDelay: 500, // Half a second after page load
19        animationDuration: 500
20    };
21
22    // Additional CTA Classes - Edit these to add custom classes to CTAs
23    var ADDITIONAL_POPUP_CTA_CLASSES = ''; // Additional classes for popup CTA button (e.g., 'btn-primary btn-large')
24    var ADDITIONAL_HERO_CTA_CLASSES = ''; // Additional classes for hero CTA button (e.g., 'btn-primary btn-large')
25
26    // Hero CTA Text Override - Set this to use different text on the hero button
27    // Leave as empty string '' to use the popup's CTA text instead
28    var HERO_CTA_TEXT_OVERRIDE = '';
29
30    // Check if current page is the home page
31    function isHomePage() {
32        // Method 1: Check pathname is root (works for most sites)
33        var pathname = window.location.pathname;
34        var cleanPath = pathname.replace(/\/$/, '') || '/';
35        if (cleanPath === '/') {
36            return true;
37        }
38
39        // Method 2: Check body or html for home-related classes (fallback)
40        // Covers: WordPress (page-template-home, home, front-page),
41        //         Concrete CMS (ccm-page-id-1, page_1),
42        //         generic (page-home)
43        var htmlClasses = document.documentElement.className || '';
44        var bodyClasses = document.body ? (document.body.className || '') : '';
45        var combined = ' ' + htmlClasses + ' ' + bodyClasses + ' ';
46
47        var homeIndicators = [
48            /\bpage-template-home\b/i,
49            /\bfront-page\b/i,
50            /\bpage-home\b/i,
51            /\bccm-page-id-1\b/i,   // Concrete CMS home page
52            /\bpage_1\b/i,           // Concrete CMS home page alt
53            /\s+home\s+/i            // generic "home" as standalone class
54        ];
55
56        for (var i = 0; i < homeIndicators.length; i++) {
57            if (homeIndicators[i].test(combined)) {
58                return true;
59            }
60        }
61
62        return false;
63    }
64
65    // Check if popup should be shown
66    function shouldShowPopup() {
67        console.log('Popup Alert: Checking if should show popup...');
68
69        // Check if enabled
70        if (!config.enabled) {
71            console.log('Popup Alert: Popup is disabled');
72            return false;
73        }
74        console.log('Popup Alert: Popup is enabled');
75
76        // Only show on home page
77        if (!isHomePage()) {
78            console.log('Popup Alert: Not on home page, pathname:', window.location.pathname);
79            return false;
80        }
81        console.log('Popup Alert: On home page, will check other conditions');
82
83        // Check date range
84        var now = new Date();
85        console.log('Popup Alert: Current date:', now);
86
87        // Check start date - if set, must be today or in the past
88        if (config.start_date) {
89            var startDate = new Date(config.start_date);
90            startDate.setHours(0, 0, 0, 0); // Start of day
91            var today = new Date();
92            today.setHours(0, 0, 0, 0);
93            console.log('Popup Alert: Start date:', startDate);
94            if (today < startDate) {
95                console.log('Popup Alert: Not started yet');
96                return false; // Not started yet
97            }
98        }
99
100        // Check end date - if set, must be today or in the future
101        if (config.end_date) {
102            var endDate = new Date(config.end_date);
103            endDate.setHours(23, 59, 59, 999); // End of day
104            var nowTime = new Date().getTime();
105            console.log('Popup Alert: End date:', endDate);
106            console.log('Popup Alert: Current time:', nowTime);
107            console.log('Popup Alert: End time:', endDate.getTime());
108            if (nowTime > endDate.getTime()) {
109                console.log('Popup Alert: Already ended - will not show');
110                return false; // Already ended
111            }
112        }
113
114        console.log('Popup Alert: All checks passed, popup should show');
115        return true;
116    }
117
118    // Create and inject popup HTML
119    function createPopup() {
120        console.log('Popup Alert: Creating popup HTML...');
121
122        // Create overlay
123        var overlay = document.createElement('div');
124        overlay.className = 'popup-overlay';
125        overlay.id = 'popup-overlay';
126
127        // Create popup container
128        var container = document.createElement('div');
129        container.className = 'popup-container';
130        container.id = 'popup-container';
131
132        // Create close button
133        var closeBtn = document.createElement('button');
134        closeBtn.className = 'popup-close';
135        closeBtn.id = 'popup-close';
136        closeBtn.setAttribute('aria-label', 'Close popup');
137        closeBtn.innerHTML = '<span>&times;</span>';
138
139        // Create content wrapper
140        var content = document.createElement('div');
141        content.className = 'popup-content';
142
143        // Add image if exists
144        if (config.img_url) {
145            var imageDiv = document.createElement('div');
146            imageDiv.className = 'popup-image';
147            var img = document.createElement('img');
148            img.src = config.img_url;
149            img.alt = config.img_alt || '';
150            if (config.img_width) img.width = config.img_width;
151            if (config.img_height) img.height = config.img_height;
152            imageDiv.appendChild(img);
153            content.appendChild(imageDiv);
154        }
155
156        // Add heading and description
157        if (config.heading || config.description) {
158            var textDiv = document.createElement('div');
159
160            if (config.heading) {
161                var h2 = document.createElement('h2');
162                h2.textContent = config.heading;
163                textDiv.appendChild(h2);
164            }
165
166            if (config.description) {
167                // Security: Use DOMParser to safely parse HTML and prevent XSS
168                var parser = new DOMParser();
169                var doc = parser.parseFromString(config.description, 'text/html');
170                var bodyContent = doc.body;
171                while (bodyContent.firstChild) {
172                    textDiv.appendChild(bodyContent.firstChild);
173                }
174            }
175
176            content.appendChild(textDiv);
177        }
178
179        // Add CTA if exists
180        if (config.cta_link) {
181            var ctaDiv = document.createElement('div');
182            ctaDiv.className = 'popup-cta';
183            var ctaLink = document.createElement('a');
184            ctaLink.href = config.cta_link;
185            ctaLink.className = 'btn' + (ADDITIONAL_POPUP_CTA_CLASSES ? ' ' + ADDITIONAL_POPUP_CTA_CLASSES : '');
186            var ctaSpan = document.createElement('span');
187            ctaSpan.textContent = config.cta_text || config.cta_name || 'Learn More';
188            ctaLink.appendChild(ctaSpan);
189            ctaDiv.appendChild(ctaLink);
190            content.appendChild(ctaDiv);
191        }
192
193        // Assemble popup
194        container.appendChild(closeBtn);
195        container.appendChild(content);
196        overlay.appendChild(container);
197
198        // Inject before closing </main> tag, or fallback to body
199        var mainElement = document.querySelector('main');
200        if (mainElement) {
201            mainElement.appendChild(overlay);
202        } else {
203            document.body.appendChild(overlay);
204        }
205
206        return {
207            overlay: overlay,
208            container: container,
209            closeBtn: closeBtn
210        };
211    }
212
213    // Helper: check if an element's class list contains a token matching "xs"
214    // Only flags actual word-boundary matches like "xs_something", "something-xs", "xs"
215    // Does NOT flag substrings like "exposed", "boxshadow", "fixsubmenu"
216    function classHasXsToken(className) {
217        if (!className) return false;
218        // Split by whitespace and check each class name
219        var classes = className.split(/\s+/);
220        for (var i = 0; i < classes.length; i++) {
221            var cls = classes[i];
222            // Match class names that start with "xs", end with "xs", contain "_xs_" or "-xs-",
223            // or are exactly "xs"
224            if (/^xs[_-]|[_-]xs$|[_-]xs[_-]|^xs$/i.test(cls)) {
225                return true;
226            }
227        }
228        return false;
229    }
230
231    // Inject popup CTA into hero section (if on home page)
232    function injectHeroCTA() {
233        // Check if already injected (prevent duplicate injections)
234        if (document.querySelector('.popup-hero-cta-injected')) {
235            console.log('Popup Alert: Hero CTA already injected, skipping');
236            return;
237        }
238
239        // Check if on home page (using same logic as popup display)
240        if (!isHomePage()) {
241            console.log('Popup Alert: Not home page, skipping hero CTA injection');
242            return;
243        }
244
245        // Check if popup has CTA configured
246        if (!config.cta_link) {
247            console.log('Popup Alert: No CTA link configured, skipping hero injection');
248            return;
249        }
250
251        // Find hero element - look for any element with "hero" in class
252        var heroElements = document.querySelectorAll('[class*="hero"]');
253        console.log('Popup Alert: Searching for elements with "hero" in class, found:', heroElements.length);
254
255        if (heroElements.length === 0) {
256            console.log('Popup Alert: No hero element found');
257            return;
258        }
259
260        var heroElement = null;
261        var headerElement = document.querySelector('.header, header');
262
263        // Filter hero elements - skip ones with "xs" token or inside header
264        for (var i = 0; i < heroElements.length; i++) {
265            var elem = heroElements[i];
266            var className = elem.className || '';
267
268            // Skip if class has an "xs" token (sticky mobile buttons, etc.)
269            if (classHasXsToken(className)) {
270                console.log('Popup Alert: Skipping element (has "xs" token):', elem, 'Classes:', className);
271                continue;
272            }
273
274            // Skip if element is inside .header or <header>
275            if (headerElement && headerElement.contains(elem)) {
276                console.log('Popup Alert: Skipping element (inside header):', elem);
277                continue;
278            }
279
280            // Found valid hero element
281            heroElement = elem;
282            console.log('Popup Alert: Selected hero element:', heroElement);
283            console.log('Popup Alert: Hero element classes:', heroElement.className);
284            break;
285        }
286
287        if (!heroElement) {
288            console.log('Popup Alert: No valid hero element found');
289            return;
290        }
291
292        // Find all <a> tags with class "btn" within the hero
293        // Use [class*="btn"] selector to also catch "btn btn-lg" etc.,
294        // then filter to actual btn class
295        var allLinks = heroElement.querySelectorAll('a');
296        var existingBtns = [];
297        for (var j = 0; j < allLinks.length; j++) {
298            var link = allLinks[j];
299            // Check if it has "btn" as a class token (not substring)
300            if (link.classList && link.classList.contains('btn')) {
301                existingBtns.push(link);
302            }
303        }
304
305        if (existingBtns.length === 0) {
306            console.log('Popup Alert: No existing buttons (a.btn) found in hero');
307            return;
308        }
309
310        console.log('Popup Alert: Found', existingBtns.length, 'existing button(s) in hero');
311
312        // Inject popup CTA after each existing button
313        existingBtns.forEach(function(existingBtn, index) {
314            var existingClasses = existingBtn.className;
315            console.log('Popup Alert: Existing button', index + 1, 'classes:', existingClasses);
316
317            // Create wrapper div
318            var wrapperDiv = document.createElement('div');
319            wrapperDiv.className = 'popup-hero-cta-wrapper';
320            wrapperDiv.style.marginTop = '15px';
321
322            // Create new CTA link with same classes as existing button
323            var newCtaLink = document.createElement('a');
324            newCtaLink.href = config.cta_link;
325            newCtaLink.className = existingClasses + ' popup-hero-cta-injected' + (ADDITIONAL_HERO_CTA_CLASSES ? ' ' + ADDITIONAL_HERO_CTA_CLASSES : '');
326            var heroCtaSpan = document.createElement('span');
327            // Use hero-specific text if provided, otherwise fall back to popup CTA text
328            var heroCtaText = HERO_CTA_TEXT_OVERRIDE || config.hero_cta_text || config.cta_text || config.cta_name || 'Learn More';
329            heroCtaSpan.textContent = heroCtaText;
330            newCtaLink.setAttribute('title', heroCtaText);
331            newCtaLink.appendChild(heroCtaSpan);
332
333            wrapperDiv.appendChild(newCtaLink);
334
335            if (existingBtn.parentNode) {
336                existingBtn.parentNode.insertBefore(wrapperDiv, existingBtn.nextSibling);
337                console.log('Popup Alert: Successfully injected CTA after button', index + 1);
338            } else {
339                console.error('Popup Alert: Could not find parent node for button', index + 1);
340            }
341        });
342    }
343
344    // Show Popup
345    function showPopup() {
346        console.log('Popup Alert: showPopup() called');
347        var popup = createPopup();
348        if (!popup || !popup.overlay) {
349            console.error('Popup Alert: Failed to create popup element');
350            return;
351        }
352        console.log('Popup Alert: Popup created, showing...');
353        var overlay = popup.overlay;
354        var container = popup.container;
355        var closeBtn = popup.closeBtn;
356
357        container.classList.remove('slide-in');
358        overlay.style.display = 'block';
359
360        void overlay.offsetWidth;
361        void container.offsetWidth;
362
363        requestAnimationFrame(function() {
364            overlay.classList.add('active');
365            setTimeout(function() {
366                container.classList.add('slide-in');
367            }, 100);
368        });
369
370        document.body.style.overflow = 'hidden';
371
372        closeBtn.addEventListener('click', function(e) {
373            e.preventDefault();
374            hidePopup(overlay, container);
375        });
376
377        overlay.addEventListener('click', function(e) {
378            if (e.target === overlay) {
379                hidePopup(overlay, container);
380            }
381        });
382
383        var escHandler = function(e) {
384            if (e.key === 'Escape' && overlay.classList.contains('active')) {
385                hidePopup(overlay, container);
386                document.removeEventListener('keydown', escHandler);
387            }
388        };
389        document.addEventListener('keydown', escHandler);
390    }
391
392    // Hide Popup
393    function hidePopup(overlay, container) {
394        container.classList.remove('slide-in');
395
396        setTimeout(function() {
397            overlay.classList.remove('active');
398            setTimeout(function() {
399                overlay.style.display = 'none';
400                overlay.remove();
401            }, 300);
402            document.body.style.overflow = '';
403        }, CONFIG.animationDuration);
404    }
405
406    // Auto-show popup on page load
407    function initAutoShow() {
408        console.log('Popup Alert: Initializing auto-show...');
409
410        // Always try to inject hero CTA on home page (independent of popup display timing)
411        // Use a small delay to ensure DOM is fully ready
412        if (isHomePage() && config.enabled && config.cta_link) {
413            setTimeout(function() {
414                injectHeroCTA();
415            }, 100);
416        }
417
418        if (shouldShowPopup()) {
419            console.log('Popup Alert: Will show popup in', CONFIG.autoShowDelay, 'ms');
420            setTimeout(function() {
421                console.log('Popup Alert: Showing popup now');
422                showPopup();
423            }, CONFIG.autoShowDelay);
424        } else {
425            console.log('Popup Alert: Popup will not be shown');
426        }
427    }
428
429    // Initialize on DOM ready
430    if (document.readyState === 'loading') {
431        document.addEventListener('DOMContentLoaded', initAutoShow);
432    } else {
433        initAutoShow();
434    }
435
436    // Debug logging
437    console.log('Popup Alert: Script loaded');
438    console.log('Popup Alert: Settings available?', typeof window.popupAlertSettings !== 'undefined');
439    if (typeof window.popupAlertSettings !== 'undefined') {
440        console.log('Popup Alert: Popup enabled?', config.enabled);
441        console.log('Popup Alert: Full settings:', window.popupAlertSettings);
442    }
443
444})();;
445
446/**
447 * Alert Bar JavaScript
448 * Reads settings from window.popupAlertSettings and displays alert bar above header
449 */
450
451(function() {
452    'use strict';
453
454    // Check if settings exist
455    if (typeof window.popupAlertSettings === 'undefined' || !window.popupAlertSettings.alert_bar) {
456        return;
457    }
458
459    var config = window.popupAlertSettings.alert_bar;
460
461    // Configuration
462    var CONFIG = {
463        animationDuration: 300
464    };
465
466    // Additional CTA Classes - Edit these to add custom classes to CTAs
467    var ADDITIONAL_ALERT_BAR_CTA_CLASSES = ''; // Additional classes for alert bar CTA button (e.g., 'btn-primary btn-large')
468
469    // Check if alert bar should be shown
470    function shouldShowAlertBar() {
471        // Check if enabled
472        if (!config.enabled) {
473            return false;
474        }
475
476        // Check date range
477        var now = new Date();
478        
479        // Check start date - if set, must be today or in the past
480        if (config.start_date) {
481            var startDate = new Date(config.start_date);
482            startDate.setHours(0, 0, 0, 0); // Start of day
483            var today = new Date();
484            today.setHours(0, 0, 0, 0);
485            if (today < startDate) {
486                return false; // Not started yet
487            }
488        }
489
490        // Check end date - if set, must be today or in the future
491        if (config.end_date) {
492            var endDate = new Date(config.end_date);
493            endDate.setHours(23, 59, 59, 999); // End of day
494            var nowTime = now.getTime();
495            if (nowTime >
495 endDate.getTime()) {
496                return false; // Already ended - IMPORTANT: This prevents display after end date
497            }
498        }
499
500        return true;
501    }
502
503    // Smart positioning - inject as first child inside header element
504    function positionAlertBar(alertBar) {
505        var header = document.querySelector('.header');
506        var body = document.body;
507
508        if (header) {
509            // Insert as first child inside the header element
510            if (header.firstChild) {
511                header.insertBefore(alertBar, header.firstChild);
512            } else {
513                header.appendChild(alertBar);
514            }
515        } else {
516            // Fallback: top of body
517            body.insertBefore(alertBar, body.firstChild);
518        }
519
520        // Add class to body to indicate alert bar is present (CSS will handle spacing)
521        if (header) {
522            document.body.classList.add('alert-bar-active');
523        }
524    }
525
526    // Create and inject alert bar HTML
527    function createAlertBar() {
528        // Create alert bar element
529        var alertBar = document.createElement('div');
530        alertBar.className = 'alert-bar';
531        alertBar.id = 'alert-bar';
532        alertBar.style.backgroundColor = config.bg_color || '#4A90E2';
533        alertBar.style.color = config.text_color || '#FFFFFF';
534
535        // Create content wrapper
536        var content = document.createElement('div');
537        content.className = 'alert-bar-content';
538
539        // Create text container with inline CTA
540        var textContainer = document.createElement('div');
541        textContainer.className = 'alert-bar-text';
542        
543        // Add text
544        if (config.text) {
545            textContainer.textContent = config.text;
546        }
547        
548        // Add CTA inline if exists
549        if (config.cta_link) {
550            var ctaLink = document.createElement('a');
551            ctaLink.href = config.cta_link;
552            // Add base btn class and additional classes if defined
553            ctaLink.className = 'btn' + (ADDITIONAL_ALERT_BAR_CTA_CLASSES ? ' ' + ADDITIONAL_ALERT_BAR_CTA_CLASSES : '');
554            // Wrap CTA text in span element
555            var ctaSpan = document.createElement('span');
556            ctaSpan.textContent = config.cta_text || config.cta_name || 'Learn More';
557            ctaLink.appendChild(ctaSpan);
558            textContainer.appendChild(ctaLink);
559        }
560        
561        content.appendChild(textContainer);
562
563        // Create close button
564        var closeBtn = document.createElement('button');
565        closeBtn.className = 'alert-bar-close';
566        closeBtn.setAttribute('aria-label', 'Close alert bar');
567        closeBtn.innerHTML = '&times;';
568        content.appendChild(closeBtn);
569
570        // Assemble alert bar
571        alertBar.appendChild(content);
572
573        return alertBar;
574    }
575
576    // Show Alert Bar
577    function showAlertBar() {
578        var alertBar = createAlertBar();
579
580        // Position it
581        positionAlertBar(alertBar);
582
583        // Start hidden (above viewport)
584        alertBar.style.transform = 'translateY(-100%)';
585        alertBar.style.display = 'block';
586
587        // Force reflow
588        void alertBar.offsetWidth;
589
590        // Slide down animation
591        requestAnimationFrame(function() {
592            alertBar.classList.add('slide-down');
593            alertBar.style.transform = 'translateY(0)';
594        });
595
596        // Close button handler
597        var closeBtn = alertBar.querySelector('.alert-bar-close');
598        if (closeBtn) {
599            closeBtn.addEventListener('click', function(e) {
600                e.preventDefault();
601                // No cookie - alert bar will show again on next visit
602                hideAlertBar(alertBar);
603            });
604        }
605
606        return alertBar;
607    }
608
609    // Hide Alert Bar
610    function hideAlertBar(alertBar) {
611        // Slide up animation
612        alertBar.style.transform = 'translateY(-100%)';
613        alertBar.classList.remove('slide-down');
614
615        setTimeout(function() {
616            alertBar.style.display = 'none';
617            
618            // Remove alert bar active class (CSS will restore header spacing)
619            document.body.classList.remove('alert-bar-active');
620
621            // Remove from DOM
622            alertBar.remove();
623        }, CONFIG.animationDuration);
624    }
625
626    // Initialize on DOM ready
627    function init() {
628        if (shouldShowAlertBar()) {
629            // Wait a bit for page to load
630            setTimeout(function() {
631                showAlertBar();
632            }, 100);
633        }
634    }
635
636    if (document.readyState === 'loading') {
637        document.addEventListener('DOMContentLoaded', init);
638    } else {
639        init();
640    }
641
642})();
643;
644

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.