PageSourceSearch

https://machupicchubest.com/assets/theme-toggle-search-fix.js?v=20260712-dark-default-override-fix-v11

js machupicchubest.com collected 2026-10-03 01:38:55 UTC 6,288 bytes, 187 lines download raw bytes

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.