1/** 2 * ECS Dark Mode Switcher 3 * 4 * Handles three display modes (toggle, dual, dropdown) for the 5 * ECS Dark Mode Switcher widget. Persists the user's choice via 6 * a cookie so PHP can add data-ecs-scheme="alt" to <html> before 7 * first paint â cache-safe. 8 * 9 * No dependencies â vanilla JS, loaded in the footer. 10 */ 11(function () { 12 'use strict'; 13 14 // Guard against double-init when loaded more than once. 15 if ( window.__ecsDmsReady ) { return; } 16 window.__ecsDmsReady = true; 17 18 var COOKIE_NAME = 'ecs_color_scheme'; 19 var ALT_VALUE = 'alt'; 20 21 /** 22 * Get the current active scheme ('alt' or null). 23 */ 24 function getCurrentScheme() { 25 return document.documentElement.getAttribute('data-ecs-scheme'); 26 } 27 28 /** 29 * Apply a scheme by setting / removing the attribute on <html>. 30 */ 31 function applyScheme(scheme) { 32 if (scheme === ALT_VALUE) { 33 document.documentElement.setAttribute('data-ecs-scheme', ALT_VALUE); 34 } else { 35 document.documentElement.removeAttribute('data-ecs-scheme'); 36 } 37 swapLogos(scheme === ALT_VALUE); 38 } 39 40 /** 41 * Swap site logo src when dark mode is active. 42 * Targets the Elementor Site Logo widget and the standard WP custom logo. 43 * Stores the original src in data-ecs-logo-default so it can be restored. 44 */ 45 function swapLogos(isAlt) { 46 var cfg = window.ecsSchemeConfig || {}; 47 var darkUrl = cfg.darkLogoUrl; 48 if (!darkUrl) { return; } 49 50 document.querySelectorAll( 51 '.elementor-widget-theme-site-logo img, .elementor-widget-site-logo img, a.custom-logo-link img' 52 ).forEach(function(img) { 53 if (isAlt) { 54 if (!img.dataset.ecsLogoDefault) { 55 img.dataset.ecsLogoDefault = img.src; 56 } 57 if (img.src !== darkUrl) { 58 img.src = darkUrl; 59 img.srcset = ''; 60 } 61 } else { 62 if (img.dataset.ecsLogoDefault) { 63 img.src = img.dataset.ecsLogoDefault; 64 img.dataset.ecsLogoDefault = ''; 65 } 66 } 67 }); 68 } 69 70 /** 71 * Persist the choice as a cookie so both PHP (server-side, for cache-aware 72 * pages) and the inline anti-FOUC script can read it before first paint. 73 */ 74 function persistScheme(scheme) { 75 if (scheme === ALT_VALUE) { 76 document.cookie = COOKIE_NAME + '=alt; path=/; max-age=31536000; SameSite=Lax'; 77 } else { 78 document.cookie = COOKIE_NAME + '=; path=/; max-age=0; SameSite=Lax'; 79 } 80 } 81 82 /** 83 * Return true if the user has a manually stored scheme preference. 84 */ 85 function hasCookie() { 86 return /(?:^|;\s*)ecs_color_scheme=/.test( document.cookie ); 87 } 88 89 /** 90 * Sync every widget on the page to the current scheme. 91 * Handles toggle, dual, and dropdown display types. 92 */ 93 function syncAllWidgets() { 94 var isAlt = getCurrentScheme() === ALT_VALUE; 95 96 document.querySelectorAll('.ecs-dms-wrap').forEach(function (wrap) { 97 // Wrapper gets .is-alt for CSS toggle state switching 98 wrap.classList.toggle('is-alt', isAlt); 99 100 var display = wrap.dataset.display; 101 102 if (display === 'toggle') { 103 var btn = wrap.querySelector('.ecs-dms-btn'); 104 if (btn) { 105 btn.classList.toggle('is-active', isAlt); 106 } 107 108 } else if (display === 'dual') { 109 wrap.querySelectorAll('.ecs-dms-btn').forEach(function (btn) { 110 var isActive = btn.dataset.scheme === ALT_VALUE ? isAlt : !isAlt; 111 btn.classList.toggle('is-active', isActive); 112 btn.setAttribute('aria-pressed', isActive ? 'true' : 'false'); 113 }); 114 115 } else if (display === 'dropdown') { 116 var select = wrap.querySelector('.ecs-dms-select'); 117 if (select) { 118 select.value = isAlt ? ALT_VALUE : 'default'; 119 } 120 } 121 }); 122 } 123 124 /** 125 * Click handler â handles toggle and dual buttons. 126 */ 127 function handleClick(event) { 128 var btn = event.target.closest('.ecs-dms-btn'); 129 if (!btn) { return; } 130 131 var wrap = btn.closest('.ecs-dms-wrap'); 132 if (!wrap) { return; } 133 134 var newScheme; 135 136 if (wrap.dataset.display === 'dual') { 137 // Each dual button carries its target scheme explicitly 138 newScheme = btn.dataset.scheme; 139 } else { 140 // Toggle: flip the current scheme 141 newScheme = getCurrentScheme() === ALT_VALUE ? 'default' : ALT_VALUE; 142 } 143 144 applyScheme(newScheme); 145 persistScheme(newScheme); 146 syncAllWidgets(); 147 } 148 149 /** 150 * Change handler â handles dropdown. 151 */ 152 function handleDropdownChange(event) { 153 var select = event.target; 154 if (!select.classList.contains('ecs-dms-select')) { return; } 155 156 applyScheme(select.value); 157 persistScheme(select.value); 158 syncAllWidgets(); 159 } 160 161 // ââ Init âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 162
163 function init() { 164 // Sync widgets and logo to whatever the anti-FOUC script already set on <html> 165 syncAllWidgets(); 166 swapLogos(getCurrentScheme() === ALT_VALUE); 167 168 // System Auto: if enabled and no manual preference stored, 169 // follow live OS theme changes. 170 var cfg = window.ecsSchemeConfig || {}; 171 if ( cfg.systemAuto ) { 172 var mq = window.matchMedia && window.matchMedia( '(prefers-color-scheme: dark)' ); 173 if ( mq ) { 174 mq.addEventListener( 'change', function ( e ) { 175 if ( ! hasCookie() ) { 176 applyScheme( e.matches ? ALT_VALUE : 'default' ); 177 syncAllWidgets(); 178 } 179 } ); 180 } 181 } 182 183 // Delegate on document so Elementor re-renders don't require re-binding 184 document.addEventListener('click', handleClick); 185 document.addEventListener('change', handleDropdownChange); 186 } 187 188 if (document.readyState === 'loading') { 189 document.addEventListener('DOMContentLoaded', init); 190 } else { 191 init(); 192 } 193})();
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.