1/* ============================================================= 2 MACHUPICCHU BEST â Tema oscuro predeterminado y selección manual 3 Versión 20260712-dark-default-override-fix-v11 4 5 Prioridad del tema: 6 1. Preferencia manual guardada en localStorage: mb_theme. 7 2. Si no existe una preferencia válida, modo oscuro. 8 9 No consulta la preferencia visual del dispositivo ni cambia el tema 10 cuando cambia la configuración del sistema. El inicializador crÃtico de 11 index.html aplica el mismo criterio antes del primer renderizado. 12============================================================= */ 13(function(){ 14 'use strict'; 15 16 var KEY = 'mb_theme'; 17 var USER_KEY = 'mb_theme_user'; 18 var updateScheduled = false; 19 var observer = null; 20 21 function safeLocalGet(key){ 22 try{return localStorage.getItem(key);}catch(_){return null;} 23 } 24 25 function safeLocalSet(key,value){ 26 try{localStorage.setItem(key,value);return true;}catch(_){return false;} 27 } 28 29 function language(){ 30 try{ 31 if(window.state && /^(es|en|pt|fr)$/.test(window.state.lang || '')) return window.state.lang; 32 var saved = safeLocalGet('mb_lang'); 33 if(/^(es|en|pt|fr)$/.test(saved || '')) return saved; 34 var path = String(location.pathname || '').toLowerCase(); 35 var match = path.match(/^\/(en|pt|fr)(?:\/|$)/); 36 if(match) return match[1]; 37 return document.documentElement.lang || 'es'; 38 }catch(_){return 'es';} 39 } 40 41 function buttonLabel(theme){ 42 var lang = language(); 43 var showDark = theme === 'light'; 44 var labels = { 45 es:showDark ? 'ð Modo oscuro' : 'âï¸ Modo claro', 46 en:showDark ? 'ð Dark mode' : 'âï¸ Light mode', 47 pt:showDark ? 'ð Modo escuro' : 'âï¸ Modo claro', 48 fr:showDark ? 'ð Mode sombre' : 'âï¸ Mode clair' 49 }; 50 return labels[lang] || labels.es; 51 } 52 53 function readStoredTheme(){ 54 var hasManualChoice = safeLocalGet(USER_KEY) === '1'; 55 var saved = safeLocalGet(KEY); 56 if(!hasManualChoice){ 57 /* Versiones anteriores guardaban light automáticamente. Se descarta 58 ese valor heredado y se mantiene oscuro hasta una elección manual. */ 59 safeLocalSet(KEY,'dark'); 60 return 'dark'; 61 } 62 return saved === 'light' ? 'light' : 'dark'; 63 } 64 65 function currentTheme(){ 66 var attr = document.documentElement.getAttribute('data-theme'); 67 if(attr === 'light' || attr === 'dark') return attr; 68 return readStoredTheme(); 69 } 70 71 function updateMeta(theme){ 72 var meta = document.querySelector('meta[name="theme-color"]'); 73 if(meta) meta.setAttribute('content',theme === 'dark' ? '#071923' : '#F5F9FC'); 74 var apple = document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]'); 75 if(apple) apple.setAttribute('content',theme === 'dark' ? 'black-translucent' : 'default'); 76 } 77 78 function updateButtons(theme){ 79 theme = theme === 'light' ? 'light' : 'dark'; 80 var label = buttonLabel(theme); 81 var buttons = Array.prototype.slice.call( 82 document.querySelectorAll('#mobileThemeToggle,[data-theme-toggle],.mobile-menu-btn.theme') 83 ); 84 85 buttons.forEach(function(btn){ 86 if(btn.classList.contains('mb-desktop-theme-toggle')){ 87 /* La capa UX conserva su icono SVG; aquà se sincroniza el estado accesible. */ 88 if(btn.getAttribute('aria-label') !== label) btn.setAttribute('aria-label',label); 89 if(btn.getAttribute('title') !== label) btn.setAttribute('title',label); 90 }else if(btn.textContent !== label){ 91 btn.textContent = label; 92 } 93 btn.dataset.themeState = theme; 94 btn.dataset.themeFixDelegated = '1'; 95 btn.setAttribute('aria-pressed',theme === 'dark' ? 'true' : 'false'); 96 }); 97 } 98 99 function applyTheme(theme,persist){ 100 theme = theme === 'light' ? 'light' : 'dark'; 101 var html = document.documentElement; 102 var body = document.body; 103 104 html.classList.toggle('theme-light',theme === 'light'); 105 html.classList.toggle('theme-dark',theme === 'dark'); 106 html.setAttribute('data-theme',theme); 107 html.style.colorScheme = theme; 108 109 if(body){ 110 body.classList.toggle('theme-light',theme === 'light'); 111 body.classList.toggle('theme-dark',theme === 'dark'); 112 body.setAttribute('data-theme',theme); 113 } 114 115 updateMeta(theme); 116 updateButtons(theme); 117 118 if(persist){ 119 safeLocalSet(KEY,theme); 120 safeLocalSet(USER_KEY,'1'); 121 } 122 123 try{ 124 window.dispatchEvent(new CustomEvent('mb:themechange',{ 125 detail:{theme:theme,persisted:!!persist} 126 })); 127 }catch(_){ } 128 return theme; 129 } 130 131 window.setThemeMode = function(theme){ 132 return applyTheme(theme === 'light' ? 'light' : 'dark',true); 133 }; 134 135 window.toggleThemeMode = function(){
136 return applyTheme(currentTheme() === 'light' ? 'dark' : 'light',true); 137 }; 138 139 document.addEventListener('click',function(event){ 140 var target = event.target && event.target.closest 141 ? event.target.closest('#mobileThemeToggle,[data-theme-toggle],.mobile-menu-btn.theme') 142 : null; 143 if(!target) return; 144 event.preventDefault(); 145 event.stopPropagation(); 146 window.toggleThemeMode(); 147 },true); 148 149 function scheduleButtonUpdate(){ 150 if(updateScheduled) return; 151 updateScheduled = true; 152 setTimeout(function(){ 153 updateScheduled = false; 154 updateButtons(currentTheme()); 155 },60); 156 } 157 158 function watchButtons(){ 159 if(observer || !document.body || !window.MutationObserver) return; 160 observer = new MutationObserver(function(mutations){ 161 var relevant = mutations.some(function(mutation){ 162 return mutation.type === 'childList' && (mutation.addedNodes.length || mutation.removedNodes.length); 163 }); 164 if(relevant) scheduleButtonUpdate(); 165 }); 166 observer.observe(document.body,{childList:true,subtree:true}); 167 } 168 169 function boot(){ 170 applyTheme(readStoredTheme(),false); 171 watchButtons(); 172 scheduleButtonUpdate(); 173 } 174 175 if(document.readyState === 'loading'){ 176 document.addEventListener('DOMContentLoaded',boot,{once:true}); 177 }else{ 178 boot(); 179 } 180 181 window.addEventListener('load',function(){ 182 applyTheme(readStoredTheme(),false); 183 },{once:true}); 184 185 setTimeout(function(){updateButtons(currentTheme());},300); 186 setTimeout(function(){updateButtons(currentTheme());},1200); 187})();
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.