PageSourceSearch

https://www.simflight.com/wp-content/plugins/dark-reader/assets/js/dark-reader-init.js?ver=1.1.13

js simflight.com collected 2026-10-02 03:01:36 UTC 39,588 bytes, 1,053 lines download raw bytes

1/**
2 * Dark Reader initialization script
3 * Uses the darkreader.js library to add dark mode functionality to WordPress sites
4 * Modern implementation with ES6+ features
5 */
6
7(() => {
8    'use strict';
9
10    /**
11     * Get current page ID for localStorage key
12     * @returns {string}
13     */
14    const getCurrentPageId = () => {
15        const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
16        return settings.currentPageId || 'default';
17    };
18
19    /**
20     * Get page visibility mode
21     * @returns {string}
22     */
23    const getPageVisibilityMode = () => {
24        const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
25        return settings.pageVisibilityMode || 'show_all';
26    };
27
28    /**
29     * Check if current mode uses global localStorage
30     * @returns {boolean}
31     */
32    const isGlobalMode = () => {
33        const mode = getPageVisibilityMode();
34        return mode === 'show_all' || mode === 'hide_all';
35    };
36
37    /**
38     * Clean up localStorage when switching modes
39     */
40    const cleanupLocalStorage = () => {
41        try {
42            const mode = getPageVisibilityMode();
43            const currentModeStored = localStorage.getItem('darkReaderVisibilityMode');
44
45            // If mode has changed, clean up old localStorage entries
46            if (currentModeStored && currentModeStored !== mode) {
47                if (mode === 'show_all' || mode === 'hide_all') {
48                    // Switching to global mode - remove all page-specific entries
49                    Object.keys(localStorage).forEach(key => {
50                        if (key.startsWith('darkReaderEnabled_')) {
51                            localStorage.removeItem(key);
52                        }
53                    });
54                } else {
55                    // Switching to page-specific mode - remove global entry
56                    localStorage.removeItem('darkReaderEnabled');
57                }
58            }
59
60            // Store current mode
61            localStorage.setItem('darkReaderVisibilityMode', mode);
62        } catch (e) {
63        }
64    };
65
66    /**
67     * Get dark mode preference from localStorage or default settings
68     * @returns {boolean} - Whether dark mode should be enabled
69     */
70    const getDarkModePreference = () => {
71        try {
72            cleanupLocalStorage();
73
74            // Check if user has manually toggled (takes precedence over everything)
75            const manualToggle = localStorage.getItem('darkReaderManualToggle');
76            if (manualToggle === 'true') {
77                if (isGlobalMode()) {
78                    const global = localStorage.getItem('darkReaderEnabled');
79                    if (global !== null) {
80                        return global === '1';
81                    }
82                } else {
83                    const pageId = getCurrentPageId();
84                    const pageSpecificKey = `darkReaderEnabled_${pageId}`;
85                    const pageSpecific = localStorage.getItem(pageSpecificKey);
86
87                    if (pageSpecific !== null) {
88                        return pageSpecific === '1';
89                    }
90                }
91            }
92
93            // Check if system color scheme is enabled
94            const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
95            if (settings.useSystemColorScheme) {
96                // Use system preference
97                if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
98                    return true;
99                }
100                if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
101                    return false;
102                }
103            }
104
105            // Check localStorage for saved preference (when not using system scheme or manual toggle)
106            if (isGlobalMode()) {
107                // Global modes: use single localStorage key
108                const global = localStorage.getItem('darkReaderEnabled');
109                if (global !== null) {
110                    return global === '1';
111                }
112            } else {
113                // Page-specific modes: use page-specific localStorage key
114                const pageId = getCurrentPageId();
115                const pageSpecificKey = `darkReaderEnabled_${pageId}`;
116                const pageSpecific = localStorage.getItem(pageSpecificKey);
117
118                if (pageSpecific !== null) {
119                    return pageSpecific === '1';
120                }
121            }
122        } catch (e) {
123        }
124
125        // Check for default setting from WordPress
126        return Boolean(typeof darkReaderSettings !== 'undefined' && darkReaderSettings && darkReaderSettings.defaultDarkMode);
127    };
128
129    /**
130     * Apply critical dark mode styles immediately to prevent FOUC
131     */
132    const applyCriticalDarkStyles = () => {
133        // Check if dark mode should be applied on this page (Pro feature)
134        const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
135        const shouldApplyOnThisPage = settings.shouldApplyDarkMode !== false;
136
137        if (!shouldApplyOnThisPage) {
138            return;
139        }
140
141        // Check user preference using the new localStorage logic
142        // This will now also check system color scheme if enabled
143        if (!getDarkModePreference()) {
144            return;
145        }
146
147        // Create critical CSS to prevent flash of light mode
148        const criticalCSS = `
149            html {
150                filter: invert(1) hue-rotate(180deg) !important;
151                transition: filter 0.1s ease !important;
152            }
153            img, video, iframe, svg, 
154            [style*="background-image"],
155            canvas, embed, object {
156                filter: invert(1) hue-rotate(180deg) !important;
157            }
158        `;
159
160        const style = document.createElement('style');
161        style.id = 'dark-reader-critical';
162        style.textContent = criticalCSS;
163
164        // Insert immediately, even before head is fully parsed
165        if (document.head) {
166            document.head.appendChild(style);
167        } else {
168            // If head doesn't exist yet, wait for it
169            const observer = new MutationObserver((mutations, obs) => {
170                if (document.head) {
171                    document.head.appendChild(style);
172                    obs.disconnect();
173                }
174            });
175            observer.observe(document.documentElement, {
176                childList: true,
177                subtree: true
178            });
179        }
180
181        // Set data attribute immediately
182        document.documentElement.setAttribute('data-darkreader-mode', 'critical');
183        if (document.body) {
184            document.body.classList.add('dark-mode');
185        }
186    };
187
188    /**
189     * Remove critical styles when DarkReader takes over
190     */
191    const removeCriticalStyles = () => {
192        const criticalStyle = document.getElementById('dark-reader-critical');
193        if (criticalStyle) {
194            criticalStyle.remove();
195        }
196
197        // Also remove fallback critical styles and classes
198        const fallbackStyle = document.getElementById('dark-reader-critical-fallback');
199        if (fallbackStyle) {
200            fallbackStyle.remove();
201        }
202
203        document.documentElement.classList.remove('dark-reader-critical-loading');
204    };
205
206    /**
207     * Create Dark Reader fixes object from exclusions
208     * @returns {Object} DarkReader fixes configuration
209     */
210    const createDarkReaderFixes = () => {
211        const fixes = {
212            ignoreInlineStyle: [],
213            ignoreImageAnalysis: [],
214            css: ''
215        };
216
217        // Always exclude toggle buttons from image analysis and transformations
218        const toggleButtonSelectors = [
219            '.dark-reader-toggle',
220            '.dark-reader-toggle *',
221            '.dark-reader-toggle-slider',
222            '.dark-reader-toggle-switch',
223            '[class*="dark-reader-toggle"]',
224            '[class*="toggle-slider"]',
225            '.dark-mode-toggle',
226            '[data-dark-toggle]'
227        ];
228
229        // Add toggle buttons to ignoreImageAnalysis
230        fixes.ignoreImageAnalysis.push(...toggleButtonSelectors);
231
232        // Check if darkReaderSettings is available
233        if (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) {
234            let customCSS = '';
235
236
237
238            // Check if darkReaderSettings has exclusions
239            if (darkReaderSettings.exclusions) {
240
241                let allExcludedSelectors = [];
242
243                // Collect custom CSS selectors
244                if (darkReaderSettings.exclusions.selectors) {
245                    let selectors = [];
246                    if (Array.isArray(darkReaderSettings.exclusions.selectors)) {
247                        selectors = darkReaderSettings.exclusions.selectors.filter(selector =>
248                            typeof selector === 'string' && selector.trim() !== ''
249                        );
250                    } else if (typeof darkReaderSettings.exclusions.selectors === 'string') {
251                        // Handle case where selectors is a single string
252                        selectors = darkReaderSettings.exclusions.selectors.split(',').map(s => s.trim()).filter(s => s !== '');
253                    }
254
255                    if (selectors.length > 0) {
256                        allExcludedSelectors.push(...selectors);
257                    }
258                }
259
260                // Add image exclusions
261                if (darkReaderSettings.exclusions.images && Array.isArray(darkReaderSettings.exclusions.images)) {
262                    const images = darkReaderSettings.exclusions.images.filter(selector =>
263                        typeof selector === 'string' && selector.trim() !== ''
264                    );
265
266                    // Add to both CSS exclusions and DarkReader's ignoreImageAnalysis
267                    allExcludedSelectors.push(...images);
268                    fixes.ignoreImageAnalysis.push(...images);
269                }
270
271                // Add video exclusions
272                if (darkReaderSettings.exclusions.videos && Array.isArray(darkReaderSettings.exclusions.videos)) {
273                    const videos = darkReaderSettings.exclusions.videos.filter(selector =>
274                        typeof selector === 'string' && selector.trim() !== ''
275                    );
276
277                    // Videos should be excluded via CSS, not ignoreImageAnalysis
278                    allExcludedSelectors.push(...videos);
279                }
280
281                // Create CSS to exclude elements from dark mode transformation
282                if (allExcludedSelectors.length > 0) {
283                    const selectorString = allExcludedSelectors.join(', ');
284                    const exclusionCSS = `
285                    /* Dark Reader Pro Element Exclusions */
286                    ${selectorString} {
287                        filter: none !important;
288                        backdrop-filter: none !important;
289                        color-scheme: initial !important;
290                        color: initial !important;
291                        --darkreader-inline-color: initial !important;
292                        -webkit-filter: none !important;
293                    }
294                    ${selectorString} * {
295                        filter: none !important;
296                        backdrop-filter: none !important;
297                        -webkit-filter: none !important;
298                    }
299                `;
300                    customCSS += exclusionCSS;
301                }
302
303                // Set the final CSS
304                if (customCSS) {
305                    fixes.css = customCSS;
306                }
307            }
308
309        } else {
310        }
311
312        // Always add toggle button exclusion CSS
313        const toggleExclusionCSS = `
314        /* Dark Reader Toggle Button Exclusions */
315        .dark-reader-toggle,
316        .dark-reader-toggle *,
317        .dark-reader-toggle-slider,
318        .dark-reader-toggle-slider:before,
319        .dark-reader-toggle-slider:after,
320        .dark-reader-toggle-switch,
321        .dark-reader-toggle-switch *,
322        [class*="dark-reader-toggle"],
323        [class*="toggle-slider"],
324        .dark-mode-toggle,
325        [data-dark-toggle] {
326            filter: none !important;
327            backdrop-filter: none !important;
328            -webkit-filter: none !important;
329            --darkreader-inline-bgcolor: initial !important;
330            --darkreader-inline-bgimage: initial !important;
331            --darkreader-inline-border: initial !important;
332            --darkreader-inline-color: initial !important;
333        }
334        `;
335        
336        fixes.css = (fixes.css || '') + toggleExclusionCSS;
337
338        return fixes;
339    };
340
341    /**
342     * Create Dark Reader configuration object
343     * Builds config from WordPress settings with sensible defaults
344     * @returns {Object} DarkReader configuration
345     */
346    const createDarkReaderConfig = () => {
347        // Use default values if darkReaderSettings is not available
348        const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
349
350        const config = {
351            brightness: parseInt(settings.brightness) || 100,
352            contrast: parseInt(settings.contrast) || 90,
353            sepia: parseInt(settings.sepia) || 10,
354            grayscale: parseInt(settings.grayscale) || 0,
355            textStroke: parseFloat(settings.textStroke) || 0,
356            mode: parseInt(settings.mode) === 0 ? 0 : 1,
357            enableForPDFs: Boolean(settings.enableForPDFs),
358            immediateModify: settings.immediateModify !== false
359        };
360
361        // Add dark scheme colors if provided
362        if (settings.darkSchemeBackgroundColor) {
363            config.darkSchemeBackgroundColor = settings.darkSchemeBackgroundColor;
364        }
365        if (settings.darkSchemeTextColor) {
366            config.darkSchemeTextColor = settings.darkSchemeTextColor;
367        }
368
369        return config;
370    };
371
372    // Note: darkReaderFixes is created on-demand in functions to avoid timing issues
373
374    /**
375     * Store dark mode preference in localStorage
376     * @param {boolean} isDark - Whether dark mode is enabled
377     */
378    const setDarkModePreference = (isDark) => {
379        try {
380
381            if (isGlobalMode()) {
382                // Global modes: set global preference
383                localStorage.setItem('darkReaderEnabled', isDark ? '1' : '0');
384            } else {
385                // Page-specific modes: set page-specific preference
386                const pageId = getCurrentPageId();
387                const pageSpecificKey = `darkReaderEnabled_${pageId}`;
388                localStorage.setItem(pageSpecificKey, isDark ? '1' : '0');
389            }
390
391            // Set manual toggle flag
392            localStorage.setItem('darkReaderManualToggle', 'true');
393
394            // Dispatch custom event for analytics tracking
395            document.dispatchEvent(new CustomEvent('darkReaderToggled', {
396                detail: { isDark: isDark }
397            }));
398        } catch (e) {
399        }
400    };
401
402    /**
403     * Initialize dark mode based on user preference and page settings
404     */
405    const initDarkMode = () => {
406        try {
407            const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
408            const shouldApplyOnThisPage = settings.shouldApplyDarkMode !== false;
409            const userPreference = getDarkModePreference();
410
411            if (shouldApplyOnThisPage && userPreference) {
412                // Enable dark mode
413                try {
414                    const config = createDarkReaderConfig();
415                    const fixes = createDarkReaderFixes();
416                    DarkReader.enable(config, fixes);
417                    removeCriticalStyles();
418                    document.documentElement.setAttribute('data-darkreader-mode', 'dynamic');
419                    if (document.body) {
420                        document.body.classList.add('dark-mode');
421                    }
422
423                    // Initialize PDF dark mode if enabled
424                    if (settings.enableForPDFs) {
425                        initPDFDarkMode();
426                    }
427                } catch (error) {
428                    // Fallback: enable without fixes
429                    const config = createDarkReaderConfig();
430                    DarkReader.enable(config);
431                    removeCriticalStyles();
432                    document.documentElement.setAttribute('data-darkreader-mode', 'dynamic');
433                    if (document.body) {
434                        document.body.classList.add('dark-mode');
435                    }
436
437                    // Initialize PDF dark mode if enabled
438                    if (settings.enableForPDFs) {
439                        initPDFDarkMode();
440                    }
441                }
442            } else {
443                // Disable dark mode
444                DarkReader.disable();
445                removeCriticalStyles();
446                document.documentElement.setAttribute('data-darkreader-mode', 'light');
447                if (document.body) {
448                    document.body.classList.remove('dark-mode');
449                }
450
451                // Disable PDF dark mode
452                disablePDFDarkMode();
453            }
454
455            updateToggleButton(userPreference);
456        } catch (e) {
457            // Anything thrown before the branches above would leave the critical
458            // class on <html> and the page stuck colour-inverted.
459            removeCriticalStyles();
460        }
461    };
462
463    /**
464     * Initialize PDF dark mode functionality
465     */
466    const initPDFDarkMode = () => {
467        try {
468            // Check if PDF dark mode is enabled
469            const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
470            if (!settings.enableForPDFs) {
471                return;
472            }
473
474            // Load PDF.js if not already loaded
475            loadPDFJS().then(() => {
476                // Set up PDF viewer detection and dark mode
477                setupPDFViewerDarkMode();
478            }).catch(error => {
479            });
480        } catch (e) {
481        }
482    };
483
484    /**
485     * Load PDF.js library dynamically
486     */
487    const loadPDFJS = async () => {
488        // Check if PDF.js is already loaded
489        if (typeof pdfjsLib !== 'undefined') {
490            return Promise.resolve();
491        }
492
493        // Load PDF.js from CDN
494        const pdfjsScript = document.createElement('script');
495        pdfjsScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js';
496        pdfjsScript.async = true;
497
498        return new Promise((resolve, reject) => {
499            pdfjsScript.onload = () => {
500                // Set worker path
501                if (typeof pdfjsLib !== 'undefined') {
502                    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
503                    resolve();
504                } else {
505                    reject(new Error('PDF.js failed to load'));
506                }
507            };
508            pdfjsScript.onerror = () => reject(new Error('Failed to load PDF.js'));
509            document.head.appendChild(pdfjsScript);
510        });
511    };
512
513    /**
514     * Set up PDF viewer dark mode
515     */
516    const setupPDFViewerDarkMode = () => {
517        try {
518            // Detect different types of PDF viewers
519            const pdfElements = document.querySelectorAll('embed[type="application/pdf"], object[type="application/pdf"], iframe[src*=".pdf"], iframe[src*="blob:"]');
520
521            pdfElements.forEach(element => {
522                applyDarkModeToPDFElement(element);
523            });
524
525            // Watch for dynamically added PDF elements
526            const observer = new MutationObserver((mutations) => {
527                mutations.forEach((mutation) => {
528                    mutation.addedNodes.forEach((node) => {
529                        if (node.nodeType === Node.ELEMENT_NODE) {
530                            // Check if the added node is a PDF element
531                            if (isPDFElement(node)) {
532                                applyDarkModeToPDFElement(node);
533                            }
534
535                            // Check children of added node
536                            const pdfChildren = node.querySelectorAll('embed[type="application/pdf"], object[type="application/pdf"], iframe[src*=".pdf"], iframe[src*="blob:"]');
537                            pdfChildren.forEach(element => {
538                                applyDarkModeToPDFElement(element);
539                            });
540                        }
541                    });
542                });
543            });
544
545            observer.observe(document.body, {
546                childList: true,
547                subtree: true
548            });
549
550            // Store observer for cleanup
551            window.darkReaderPDFObserver = observer;
552        } catch (e) {
553        }
554    };
555
556    /**
557     * Check if an element is a PDF element
558     */
559    const isPDFElement = (element) => {
560        if (!element || !element.tagName) return false;
561
562        const tagName = element.tagName.toLowerCase();
563        const type = element.getAttribute('type');
564        const src = element.getAttribute('src');
565
566        return (
567            (tagName === 'embed' && type === 'application/pdf') ||
568            (tagName === 'object' && type === 'application/pdf') ||
569            (tagName === 'iframe' && (src?.includes('.pdf') || src?.startsWith('blob:')))
570        );
571    };
572
573    /**
574     * Apply dark mode to a PDF element
575     */
576    const applyDarkModeToPDFElement = (element) => {
577        try {
578            if (!element) return;
579
580            // Add dark mode class to PDF container
581            element.classList.add('dark-reader-pdf-dark');
582
583            // Create dark mode overlay for PDF
584            createPDFDarkOverlay(element);
585
586            // Apply CSS filters for browser PDF viewers
587            applyPDFCSSFilters(element);
588
589            // Handle iframe PDF viewers (like browser built-in viewers)
590            if (element.tagName.toLowerCase() === 'iframe') {
591                handleIframePDFViewer(element);
592            }
593        } catch (e) {
594        }
595    };
596
597    /**
598     * Create dark mode overlay for PDF elements
599     */
600    const createPDFDarkOverlay = (element) => {
601        try {
602            // Remove existing overlay if any
603            const existingOverlay = element.parentNode?.querySelector('.dark-reader-pdf-overlay');
604            if (existingOverlay) {
605                existingOverlay.remove();
606            }
607
608            // Create overlay
609            const overlay = document.createElement('div');
610            overlay.className = 'dark-reader-pdf-overlay';
611            overlay.style.cssText = `
612                position: absolute;
613                top: 0;
614                left: 0;
615                width: 100%;
616                height: 100%;
617                background: rgba(0, 0, 0, 0.1);
618                pointer-events: none;
619                z-index: 1;
620                opacity: 0;
621                transition: opacity 0.3s ease;
622            `;
623
624            // Position the element relatively if not already
625            const computedStyle = window.getComputedStyle(element);
626            if (computedStyle.position === 'static') {
627                element.style.position = 'relative';
628            }
629
630            // Insert overlay
631            element.parentNode.insertBefore(overlay, element.nextSibling);
632
633            // Show overlay
634            setTimeout(() => {
635                overlay.style.opacity = '1';
636            }, 100);
637        } catch (e) {
638        }
639    };
640
641    /**
642     * Apply CSS filters for browser PDF viewers
643     */
644    const applyPDFCSSFilters = (element) => {
645        try {
646            const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
647
648            // Create filter string based on settings
649            const brightness = settings.brightness || 100;
650            const contrast = settings.contrast || 90;
651            const sepia = settings.sepia || 10;
652            const grayscale = settings.grayscale || 0;
653
654            const filterString = `
655                brightness(${brightness}%) 
656                contrast(${contrast}%) 
657                sepia(${sepia}%) 
658                grayscale(${grayscale}%)
659                invert(1) 
660                hue-rotate(180deg)
661            `;
662
663            element.style.filter = filterString;
664            element.style.transition = 'filter 0.3s ease';
665        } catch (e) {
666        }
667    };
668
669    /**
670     * Handle iframe PDF viewers (browser built-in viewers)
671     */
672    const handleIframePDFViewer = (iframe) => {
673        try {
674            // Wait for iframe to load
675            iframe.addEventListener('load', () => {
676                try {
677                    const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
678                    if (!iframeDoc) return;
679
680                    // Apply dark mode to iframe content
681                    applyDarkModeToIframeContent(iframeDoc);
682                } catch (e) {
683                    // Cross-origin iframe, can't access content
684                }
685            });
686        } catch (e) {
687        }
688    };
689
690    /**
691     * Apply dark mode to iframe content
692     */
693    const applyDarkModeToIframeContent = (iframeDoc) => {
694        try {
695            // Add dark mode styles to iframe
696            const darkStyles = iframeDoc.createElement('style');
697            darkStyles.textContent = `
698                body {
699                    background-color: #1a1a1a !important;
700                    color: #ffffff !important;
701                }
702                * {
703                    background-color: #1a1a1a !important;
704                    color: #ffffff !important;
705                    border-color: #333333 !important;
706                }
707                canvas {
708                    filter: invert(1) hue-rotate(180deg) !important;
709                }
710            `;
711            iframeDoc.head.appendChild(darkStyles);
712        } catch (e) {
713        }
714    };
715
716    /**
717     * Disable PDF dark mode
718     */
719    const disablePDFDarkMode = () => {
720        try {
721            // Remove PDF dark mode classes
722            document.querySelectorAll('.dark-reader-pdf-dark').forEach(element => {
723                element.classList.remove('dark-reader-pdf-dark');
724                element.style.filter = '';
725            });
726
727            // Remove overlays
728            document.querySelectorAll('.dark-reader-pdf-overlay').forEach(overlay => {
729                overlay.remove();
730            });
731
732            // Stop observer
733            if (window.darkReaderPDFObserver) {
734                window.darkReaderPDFObserver.disconnect();
735                window.darkReaderPDFObserver = null;
736            }
737        } catch (e) {
738        }
739    };
740
741    /**
742     * Toggle dark mode on/off
743     */
744    const toggleDarkMode = () => {
745        const isDarkModeOn = getDarkModePreference();
746
747        // Check if dark mode should be applied on this page (Pro feature)
748        const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
749        const shouldApplyOnThisPage = settings.shouldApplyDarkMode !== false;
750
751        try {
752            if (isDarkModeOn) {
753                // Switch to light mode
754                DarkReader.disable();
755                removeCriticalStyles();
756                document.documentElement.setAttribute('data-darkreader-mode', 'light');
757                if (document.body) {
758                    document.body.classList.remove('dark-mode');
759                }
760                setDarkModePreference(false);
761
762                // Disable PDF dark mode
763                disablePDFDarkMode();
764            } else {
765                // Check if dark mode is allowed on this page before enabling
766                if (!shouldApplyOnThisPage) {
767                    // Don't enable dark mode on pages where it's not allowed
768                    return;
769                }
770
771                // Switch to dark mode
772                try {
773                    const config = createDarkReaderConfig();
774                    const fixes = createDarkReaderFixes();
775                    DarkReader.enable(config, fixes);
776                    removeCriticalStyles();
777                    document.documentElement.setAttribute('data-darkreader-mode', 'dynamic');
778                    if (document.body) {
779                        document.body.classList.add('dark-mode');
780                    }
781                    setDarkModePreference(true);
782
783                    // Initialize PDF dark mode if enabled
784                    const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
785                    if (settings.enableForPDFs) {
786                        initPDFDarkMode();
787                    }
788                } catch (error) {
789                    // Fallback: enable without fixes
790                    const config = createDarkReaderConfig();
791                    DarkReader.enable(config);
792                    removeCriticalStyles();
793                    document.documentElement.setAttribute('data-darkreader-mode', 'dynamic');
794                    if (document.body) {
795                        document.body.classList.add('dark-mode');
796                    }
797                    setDarkModePreference(true);
798
799                    // Initialize PDF dark mode if enabled
800                    const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
801                    if (settings.enableForPDFs) {
802                        initPDFDarkMode();
803                    }
804                }
805            }
806
807            updateToggleButton(!isDarkModeOn);
808        } catch (e) {
809        }
810    };
811
812    // Expose for Pro accessibility (keyboard shortcut, URL params) so they use the same
813    // config, fixes, localStorage format, and manual-toggle flag as the UI toggle.
814    window.darkReaderToggle = toggleDarkMode;
815    window.darkReaderInitDarkMode = initDarkMode;
816    window.darkReaderSetPreferenceAndApply = (enabled) => {
817        setDarkModePreference(enabled);
818        initDarkMode();
819    };
820
821    /**
822     * Update toggle button appearance based on dark mode state
823     * @param {boolean} isDark - Whether dark mode is enabled
824     */
825    const updateToggleButton = (isDark) => {
826        const toggleButton = document.getElementById('dark-reader-toggle');
827
828        try {
829            // Update main toggle button if it exists
830            if (toggleButton) {
831                toggleButton.classList.toggle('dark', isDark);
832            }
833
834            // Update all other inline toggle buttons (from shortcodes)
835            document.querySelectorAll('.dark-reader-toggle-inline').forEach(container => {
836                container.classList.toggle('dark', isDark);
837            });
838        } catch (e) {
839        }
840    };
841
842    /**
843     * Set up event listeners
844     */
845    const setupEventListeners = () => {
846        try {
847            // Main toggle button
848            const toggleButton = document.getElementById('dark-reader-toggle');
849            if (toggleButton) {
850                toggleButton.addEventListener('click', toggleDarkMode);
851            }
852
853            document.querySelectorAll('.dark-reader-toggle-inline').forEach(container => {
854                container.addEventListener('click', toggleDarkMode);
855            });
856
857            // Set up system color scheme listener if enabled
858            setupSystemColorSchemeListener();
859        } catch (e) {
860        }
861    };
862
863    /**
864     * Set up system color scheme listener
865     * Listens for changes in OS dark mode preference and applies them automatically
866     */
867    const setupSystemColorSchemeListener = () => {
868        try {
869            const settings = (typeof darkReaderSettings !== 'undefined' && darkReaderSettings) ? darkReaderSettings : {};
870
871            // Only set up listener if system color scheme is enabled
872            if (!settings.useSystemColorScheme) {
873                return;
874            }
875
876            // Check if matchMedia is supported (should be on all modern browsers including iOS)
877            if (!window.matchMedia) {
878                return;
879            }
880
881            const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
882
883            // Handler for system color scheme changes
884            const handleSystemColorSchemeChange = (e) => {
885                // Don't auto-switch if user has manually toggled
886                const manualToggle = localStorage.getItem('darkReaderManualToggle');
887                if (manualToggle === 'true') {
888                    return;
889                }
890
891                const shouldApplyOnThisPage = settings.shouldApplyDarkMode !== false;
892                if (!shouldApplyOnThisPage) {
893                    return;
894                }
895
896                const prefersDark = e.matches;
897
898                if (prefersDark) {
899                    // System switched to dark mode - enable dark reader
900                    try {
901                        const config = createDarkReaderConfig();
902                        const fixes = createDarkReaderFixes();
903                        DarkReader.enable(config, fixes);
904                        removeCriticalStyles();
905                        document.documentElement.setAttribute('data-darkreader-mode', 'dynamic');
906                        if (document.body) {
907                            document.body.classList.add('dark-mode');
908                        }
909
910                        // Store preference without setting manual toggle flag
911                        try {
912                            if (isGlobalMode()) {
913                                localStorage.setItem('darkReaderEnabled', '1');
914                            } else {
915                                const pageId = getCurrentPageId();
916                                const pageSpecificKey = `darkReaderEnabled_${pageId}`;
917                                localStorage.setItem(pageSpecificKey, '1');
918                            }
919                        } catch (err) { }
920
921                        // Initialize PDF dark mode if enabled
922                        if (settings.enableForPDFs) {
923                            initPDFDarkMode();
924                        }
925                    } catch (error) {
926                        // Fallback: enable without fixes
927                        const config = createDarkReaderConfig();
928                        DarkReader.enable(config);
929                        removeCriticalStyles();
930                        document.documentElement.setAttribute('data-darkreader-mode', 'dynamic');
931                        if (document.body) {
932                            document.body.classList.add('dark-mode');
933                        }
934
935                        try {
936                            if (isGlobalMode()) {
937                                localStorage.setItem('darkReaderEnabled', '1');
938                            } else {
939                                const pageId = getCurrentPageId();
940                                const pageSpecificKey = `darkReaderEnabled_${pageId}`;
941                                localStorage.setItem(pageSpecificKey, '1');
942                            }
943                        } catch (err) { }
944
945                        if (settings.enableForPDFs) {
946                            initPDFDarkMode();
947                        }
948                    }
949                } else {
950                    // System switched to light mode - disable dark reader
951                    DarkReader.disable();
952                    removeCriticalStyles();
953                    document.documentElement.setAttribute('data-darkreader-mode', 'light');
954                    if (document.body) {
955                        document.body.classList.remove('dark-mode');
956                    }
957
958                    // Store preference without setting manual toggle flag
959                    try {
960                        if (isGlobalMode()) {
961                            localStorage.setItem('darkReaderEnabled', '0');
962                        } else {
963                            const pageId = getCurrentPageId();
964                            const pageSpecificKey = `darkReaderEnabled_${pageId}`;
965                            localStorage.setItem(pageSpecificKey, '0');
966                        }
967                    } catch (err) { }
968
969                    // Disable PDF dark mode
970                    disablePDFDarkMode();
971                }
972
973                updateToggleButton(prefersDark);
974            };
975
976            // Add listener using modern method (iOS 14+)
977            if (darkModeQuery.addEventListener) {
978                darkModeQuery.addEventListener('change', handleSystemColorSchemeChange);
979            }
980            // Fallback for older iOS versions (iOS 13 and below)
981            else if (darkModeQuery.addListener) {
982                darkModeQuery.addListener(handleSystemColorSchemeChange);
983            }
984
985        } catch (e) {
986        }
987    };
988
989    // Apply critical styles immediately to prevent FOUC
990    applyCriticalDarkStyles();
991
992    // Early initialization - check if we can initialize immediately
993    if (typeof DarkReader !== 'undefined') {
994        initDarkMode();
995        setupEventListeners();
996    }
997
998    // Initialize when DOM is fully loaded
999    document.addEventListener('DOMContentLoaded', () => {
1000
1001        // Check if DarkReader library is available
1002        if (typeof DarkReader === 'undefined') {
1003            return;
1004        }
1005
1006        // Initialize dark mode
1007        initDarkMode();
1008
1009        // Set up event listeners
1010        setupEventListeners();
1011
1012        // Make debug functions available globally for testing
1013        window.darkReaderDebug = {
1014            getSettings: () => typeof darkReaderSettings !== 'undefined' ? darkReaderSettings : 'undefined',
1015            getExclusions: () => {
1016                if (typeof darkReaderSettings !== 'undefined' && darkReaderSettings && darkReaderSettings.exclusions) {
1017                    return darkReaderSettings.exclusions;
1018                }
1019                return 'No exclusions found';
1020            },
1021            testFixes: () => {
1022                const fixes = createDarkReaderFixes();
1023                return fixes;
1024            },
1025            reloadDarkMode: () => {
1026                initDarkMode();
1027            },
1028            testExclusions: () => {
1029
1030                if (typeof darkReaderSettings !== 'undefined' && darkReaderSettings.exclusions) {
1031
1032                    const fixes = createDarkReaderFixes();
1033
1034                    // Test if excluded elements exist on page
1035                    if (darkReaderSettings.exclusions.images) {
1036                        darkReaderSettings.exclusions.images.forEach(selector => {
1037                            const elements = document.querySelectorAll(selector);
1038                        });
1039                    }
1040
1041                    if (darkReaderSettings.exclusions.videos) {
1042                        darkReaderSettings.exclusions.videos.forEach(selector => {
1043                            const elements = document.querySelectorAll(selector);
1044                        });
1045                    }
1046                } else {
1047                }
1048
1049                return 'Test completed';
1050            }
1051        };
1052    });
1053})();

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.