PageSourceSearch

https://gadbiblian.gob.ec/wp-content/themes/biblian-institucional/assets/js/accessibility.js?ver=0.7.4

js gadbiblian.gob.ec collected 2026-10-03 02:44:53 UTC 8,962 bytes, 294 lines download raw bytes

1(() => {
2  'use strict';
3
4  if (window.BIBLIAN_ACCESSIBILITY_LOADED) {
5    return;
6  }
7  window.BIBLIAN_ACCESSIBILITY_LOADED = true;
8
9  const STORAGE_KEY = 'biblian_a11y_preferences_v1';
10  const STORAGE_VERSION = 2;
11  const SCALE_VALUES = [1, 1.125, 1.25, 1.375, 1.5];
12  const TOGGLE_NAMES = [
13    'high-contrast',
14    'grayscale',
15    'underline-links',
16    'readable-text',
17    'focus-boost',
18    'stop-motion',
19  ];
20  const root = document.documentElement;
21  let initialized = false;
22
23  const normalizePreferences = (candidate) => {
24    const toggles = {};
25    const candidateToggles = candidate && typeof candidate.toggles === 'object'
26      ? candidate.toggles
27      : {};
28
29    TOGGLE_NAMES.forEach((name) => {
30      toggles[name] = candidateToggles[name] === true;
31    });
32
33    const scaleIndex = Number.isInteger(candidate?.scaleIndex)
34      ? Math.min(SCALE_VALUES.length - 1, Math.max(0, candidate.scaleIndex))
35      : 0;
36
37    return { version: STORAGE_VERSION, toggles, scaleIndex };
38  };
39
40  let state = loadPreferences();
41
42  function loadPreferences() {
43    try {
44      const raw = window.localStorage.getItem(STORAGE_KEY);
45      const storedPreferences = raw ? JSON.parse(raw) : null;
46      const preferences = normalizePreferences(storedPreferences);
47
48      // Version 1 persisted the pause used during QA and could leave the public
49      // hero frozen indefinitely. Migrate that legacy value once; choices made
50      // with the current widget remain persistent like the other preferences.
51      if (storedPreferences?.version !== STORAGE_VERSION) {
52        preferences.toggles['stop-motion'] = false;
53      }
54      return preferences;
55    } catch (error) {
56      return normalizePreferences(null);
57    }
58  }
59
60  const savePreferences = () => {
61    try {
62      window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
63    } catch (error) {
64      // The controls remain functional when private browsing blocks storage.
65    }
66  };
67
68  const announce = (message) => {
69    const status = document.getElementById('biblian-a11y-status');
70    if (!status) {
71      return;
72    }
73    status.textContent = '';
74    window.requestAnimationFrame(() => {
75      status.textContent = message;
76    });
77  };
78
79  const dispatchMotionPreference = () => {
80    document.dispatchEvent(new CustomEvent('biblian:a11y-motion', {
81      detail: { paused: state.toggles['stop-motion'] === true },
82    }));
83  };
84
85  const refreshPanel = () => {
86    const panel = document.getElementById('biblian-a11y-panel');
87    if (!panel) {
88      return;
89    }
90
91    panel.querySelectorAll('[data-a11y-toggle]').forEach((button) => {
92      const name = button.getAttribute('data-a11y-toggle');
93      const active = name ? state.toggles[name] === true : false;
94      button.classList.toggle('is-active', active);
95      button.setAttribute('aria-pressed', String(active));
96    });
97
98    const value = panel.querySelector('[data-a11y-value="text-size"]');
99    if (value) {
100      value.textContent = `${Math.round(SCALE_VALUES[state.scaleIndex] * 100)}%`;
101    }
102
103    const increase = panel.querySelector('[data-a11y-action="text-up"]');
104    const decrease = panel.querySelector('[data-a11y-action="text-down"]');
105    if (increase) {
106      increase.disabled = state.scaleIndex === SCALE_VALUES.length - 1;
107    }
108    if (decrease) {
109      decrease.disabled = state.scaleIndex === 0;
110    }
111  };
112
113  const applyRootPreferences = () => {
114    TOGGLE_NAMES.forEach((name) => {
115      root.classList.toggle(`biblian-a11y-${name}`, state.toggles[name] === true);
116    });
117
118    const scale = SCALE_VALUES[state.scaleIndex];
119    root.classList.toggle('biblian-a11y-text-scale', state.scaleIndex !== 0);
120    if (state.scaleIndex === 0) {
121      root.style.removeProperty('--biblian-a11y-text-scale');
122    } else {
123      root.style.setProperty('--biblian-a11y-text-scale', String(scale));
124    }
125  };
126
127  const applyPreferences = () => {
128    applyRootPreferences();
129    refreshPanel();
130    dispatchMotionPreference();
131  };
132
133  const openPanel = () => {
134    const panel = document.getElementById('biblian-a11y-panel');
135    const trigger = document.getElementById('biblian-a11y-fab');
136    if (!panel || !trigger) {
137      return;
138    }
139
140    panel.hidden = false;
141    trigger.setAttribute('aria-expanded', 'true');
142    trigger.setAttribute('aria-label', 'Cerrar herramientas de accesibilidad');
143    refreshPanel();
144    const firstAvailableControl = panel.querySelector('[data-a11y-action="text-up"]:not(:disabled)')
145      || panel.querySelector('[data-a11y-action="text-down"]:not(:disabled)')
146      || panel.querySelector('[data-a11y-toggle]')
147      || panel.querySelector('[data-a11y-close]');
148    firstAvailableControl?.focus();
149  };
150
151  const closePanel = (restoreFocus = true) => {
152    const panel = document.getElementById('biblian-a11y-panel');
153    const trigger = document.getElementById('biblian-a11y-fab');
154    if (!panel || !trigger || panel.hidden) {
155      return;
156    }
157
158    panel.hidden = true;
159    trigger.setAttribute('aria-expanded', 'false');
160    trigger.setAttribute('aria-label', 'Abrir herramientas de accesibilidad');
161    if (restoreFocus) {
162      trigger.focus();
163    }
164  };
165
166  const togglePanel = () => {
167    const panel = document.getElementById('biblian-a11y-panel');
168    if (!panel) {
169      return;
170    }
171    if (panel.hidden) {
172      openPanel();
173    } else {
174      closePanel();
175    }
176  };
177
178  const togglePreference = (name, button) => {
179    if (!TOGGLE_NAMES.includes(name)) {
180      return;
181    }
182    state.toggles[name] = !state.toggles[name];
183    savePreferences();
184    applyPreferences();
185    const label = button.querySelector('.biblian-a11y-control__label')?.textContent?.trim() || 'Preferencia';
186    announce(`${label}: ${state.toggles[name] ? 'activado' : 'desactivado'}.`);
187  };
188
189  const runAction = (name) => {
190    if (name === 'text-up') {
191      state.scaleIndex = Math.min(SCALE_VALUES.length - 1, state.scaleIndex + 1);
192      announce(`Tamaño de texto: ${Math.round(SCALE_VALUES[state.scaleIndex] * 100)}%.`);
193    } else if (name === 'text-down') {
194      state.scaleIndex = Math.max(0, state.scaleIndex - 1);
195      announce(`Tamaño de texto: ${Math.round(SCALE_VALUES[state.scaleIndex] * 100)}%.`);
196    } else if (name === 'reset') {
197      state = normalizePreferences(null);
198      announce('Preferencias de accesibilidad restablecidas.');
199    } else {
200      return;
201    }
202
203    savePreferences();
204    applyPreferences();
205  };
206
207  const initialize = () => {
208    if (initialized) {
209      return;
210    }
211    initialized = true;
212
213    const trigger = document.getElementById('biblian-a11y-fab');
214    const panel = document.getElementById('biblian-a11y-panel');
215    if (!trigger || !panel) {
216      return;
217    }
218
219    applyPreferences();
220
221    trigger.addEventListener('click', togglePanel);
222    panel.addEventListener('click', (event) => {
223      if (!(event.target instanceof Element)) {
224        return;
225      }
226
227      if (event.target.closest('[data-a11y-close]')) {
228        closePanel();
229        return;
230      }
231
232      const toggle = event.target.closest('[data-a11y-toggle]');
233      if (toggle instanceof HTMLButtonElement) {
234        togglePreference(toggle.dataset.a11yToggle || '', toggle);
235        return;
236      }
237
238      const action = event.target.closest('[data-a11y-action]');
239      if (action instanceof HTMLButtonElement && !action.disabled) {
240        runAction(action.dataset.a11yAction || '');
241      }
242    });
243
244    document.addEventListener('keydown', (event) => {
245      if (event.altKey && event.key.toLowerCase() === 'a') {
246        event.preventDefault();
247        togglePanel();
248      } else if (event.key === 'Escape' && !panel.hidden) {
249        event.preventDefault();
250        closePanel();
251      }
252    });
253
254    document.addEventListener('click', (event) => {
255      if (panel.hidden || !(event.target instanceof Node)) {
256        return;
257      }
258      if (!panel.contains(event.target) && !trigger.contains(event.target)) {
259        closePanel(false);
260      }
261    });
262  };
263
264  // The script is intentionally loaded in <head>. Apply persisted root classes
265  // before the body paints so contrast, text size and reduced motion do not
266  // flash in their default state during navigation.
267  applyRootPreferences();
268  document.addEventListener('DOMContentLoaded', initialize, { once: true });
269  if (document.readyState !== 'loading') {
270    initialize();
271  }
272
273  window.BiblianAccessibility = {
274    open: openPanel,
275    close: closePanel,
276    reset: () => runAction('reset'),
277    setMotionPaused: (paused) => {
278      const nextPaused = paused === true;
279      if (state.toggles['stop-motion'] === nextPaused) {
280        dispatchMotionPreference();
281        return;
282      }
283
284      state.toggles['stop-motion'] = nextPaused;
285      savePreferences();
286      applyPreferences();
287      announce(`Pausar movimiento: ${nextPaused ? 'activado' : 'de
287sactivado'}.`);
288    },
289    getState: () => ({
290      scaleIndex: state.scaleIndex,
291      toggles: { ...state.toggles },
292    }),
293  };
294})();

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.