PageSourceSearch

https://www.skoda-hamburg.de/media/mod_accessibility_sa11y/js/accessibility.js?2.1.0.1791304871

js skoda-hamburg.de collected 2026-10-06 16:41:20 UTC 114,791 bytes, 3,302 lines download raw bytes

1const MODULE_CONFIG = {
2  version: "2.0.0",
3  storageKey: "mod_accessibility_sa11y_settings"
4};
5const FUNCTION_TYPES = {
6  BOOLEAN: "boolean",
7  LEVEL: "level"
8};
9const ACCESSIBILITY_FUNCTIONS = {
10  // Font Adjustments
11  "font-size": { type: FUNCTION_TYPES.LEVEL, maxLevel: 3 },
12  "line-height": { type: FUNCTION_TYPES.LEVEL, maxLevel: 3 },
13  "letter-spacing": { type: FUNCTION_TYPES.LEVEL, maxLevel: 3 },
14  // Visual Effects
15  "contrast": { type: FUNCTION_TYPES.LEVEL, maxLevel: 3 },
16  "brightness": { type: FUNCTION_TYPES.LEVEL, maxLevel: 3 },
17  "saturation": { type: FUNCTION_TYPES.LEVEL, maxLevel: 3 },
18  // Text Enhancements
19  "readable-font": { type: FUNCTION_TYPES.BOOLEAN },
20  "dyslexic-font": { type: FUNCTION_TYPES.BOOLEAN },
21  "text-magnifier": { type: FUNCTION_TYPES.BOOLEAN },
22  "text-to-speech": { type: FUNCTION_TYPES.BOOLEAN },
23  // Color Adjustments
24  "invert-colors": { type: FUNCTION_TYPES.BOOLEAN },
25  "grayscale": { type: FUNCTION_TYPES.LEVEL, maxLevel: 3 },
26  // Interactions
27  "cursor": { type: FUNCTION_TYPES.BOOLEAN },
28  "underline-links": { type: FUNCTION_TYPES.BOOLEAN },
29  "reading-guide": { type: FUNCTION_TYPES.BOOLEAN },
30  "pause-animations": { type: FUNCTION_TYPES.BOOLEAN }
31};
32const PROFILE_DEFINITIONS = {
33  "vision-impaired": {
34    name: "Sehbehinderungsmodus",
35    functions: {
36      "font-size": 1,
37      "line-height": 2,
38      "saturation": 1,
39      "readable-font": true,
40      "text-magnifier": true
41    }
42  },
43  "adhd-friendly": {
44    name: "ADHD Modus",
45    functions: {
46      "line-height": 1,
47      "saturation": 1,
48      "text-magnifier": true,
49      "pause-animations": true
50    }
51  },
52  "seizure-free": {
53    name: "Anfallsfreier Modus",
54    functions: {
55      "line-height": 1,
56      "grayscale": 2,
57      "pause-animations": true
58    }
59  }
60};
61const DEFAULT_SETTINGS = {
62  version: MODULE_CONFIG.version,
63  functions: Object.keys(ACCESSIBILITY_FUNCTIONS).reduce((acc, key) => {
64    const func = ACCESSIBILITY_FUNCTIONS[key];
65    acc[key] = func.type === FUNCTION_TYPES.LEVEL ? 0 : false;
66    return acc;
67  }, {}),
68  profiles: Object.keys(PROFILE_DEFINITIONS).reduce((acc, key) => {
69    acc[key] = false;
70    return acc;
71  }, {})
72};
73const EVENTS = {
74  FUNCTION_CHANGED: "accessibility:function:changed",
75  PROFILE_CHANGED: "accessibility:profile:changed",
76  SETTINGS_RESET: "accessibility:settings:reset"
77};
78class StorageManager {
79  constructor() {
80    this.storageKey = MODULE_CONFIG.storageKey;
81    this.storageVersion = MODULE_CONFIG.storageVersion;
82  }
83  /**
84   * Load settings from localStorage with defaults
85   * @returns {Object} Settings object
86   */
87  loadSettings() {
88    try {
89      const stored = localStorage.getItem(this.storageKey);
90      if (!stored) {
91        return DEFAULT_SETTINGS;
92      }
93      const settings = JSON.parse(stored);
94      if (settings.version !== this.storageVersion) {
95        return this.mergeWithDefaults(settings);
96      }
97      return settings;
98    } catch (error) {
99      console.error("Error loading settings:", error);
100      return DEFAULT_SETTINGS;
101    }
102  }
103  /**
104   * Save settings to localStorage
105   * @param {Object} settings - Settings to save
106   * @returns {boolean} Success status
107   */
108  saveSettings(settings) {
109    try {
110      const settingsToSave = {
111        ...settings,
112        version: this.storageVersion
113      };
114      localStorage.setItem(this.storageKey, JSON.stringify(settingsToSave));
115      return true;
116    } catch (error) {
117      console.error("Error saving settings:", error);
118      return false;
119    }
120  }
121  /**
122   * Merge stored settings with defaults
123   * @param {Object} stored - Stored settings
124   * @returns {Object} Merged settings
125   */
126  mergeWithDefaults(stored) {
127    const merged = { ...DEFAULT_SETTINGS };
128    if (stored.functions) {
129      Object.entries(stored.functions).forEach(([key, value]) => {
130        if (merged.functions.hasOwnProperty(key)) {
131          merged.functions[key] = value;
132        }
133      });
134    }
135    if (stored.profiles) {
136      Object.entries(stored.profiles).forEach(([key, value]) => {
137        if (merged.profiles.hasOwnProperty(key)) {
138          merged.profiles[key] = value;
139        }
140      });
141    }
142    if (stored.ui) {
143      merged.ui = { ...merged.ui, ...stored.ui };
144    }
145    return merged;
146  }
147  /**
148   * Reset all settings to defaults
149   * @returns {Object} Default settings
150   */
151  resetSettings() {
152    try {
153      const defaults = { ...DEFAULT_SETTINGS };
154      this.saveSettings(defaults);
155      return defaults;
156    } catch (error) {
157      console.error("Error resetting settings:", error);
158      return DEFAULT_SETTINGS;
159    }
160  }
161  /**
162   * Update specific setting
163   * @param {string} path - Dot notation path (e.g., 'functions.fontSize')
164   * @param {*} value - Value to set
165   * @returns {boolean} Success status
166   */
167  updateSetting(path, value) {
168    const settings = this.loadSettings();
169    const keys = path.split(".");
170    let current = settings;
171    for (let i = 0; i < keys.length - 1; i++) {
172      if (!current[keys[i]]) {
173        current[keys[i]] = {};
174      }
175      current = current[keys[i]];
176    }
177    current[keys[keys.length - 1]] = value;
178    return this.saveSettings(settings);
179  }
180}
181const storage = new StorageManager();
182class StateManager extends EventTarget {
183  constructor() {
184    super();
185    this.state = {
186      functions: {},
187      profiles: {},
188      version: "2.0.0"
189    };
190  }
191  /**
192   * Get current state (safe copy)
193   * @returns {Object} Current state
194   */
195  getState() {
196    return {
197      functions: { ...this.state.functions },
198      profiles: { ...this.state.profiles },
199      version: this.state.version
200    };
201  }
202  /**
203   * Get function state
204   * @param {string} functionName - Function name
205   * @returns {*} Function value
206   */
207  getFunctionState(functionName) {
208    return this.state.functions[functionName];
209  }
210  /**
211   * Set function state
212   * @param {string} functionName - Function name
213   * @param {*} value - New value
214   * @param {boolean} skipSave - Skip saving to storage
215   */
216  setFunctionState(functionName, value, skipSave = false) {
217    const oldValue = this.getFunctionState(functionName);
218    if (oldValue === value) {
219      return;
220    }
221    this.state.functions = this.state.functions || {};
222    this.state.functions[functionName] = value;
223    if (!skipSave) {
224      storage.saveSettings(this.state);
225    }
226    this.dispatchEvent(new CustomEvent(EVENTS.FUNCTION_CHANGED, {
227      detail: {
228        functionName,
229        value,
230        oldValue
231      }
232    }));
233  }
234  /**
235   * Get profile state
236   * @param {string} profileKey - Profile key
237   * @returns {boolean} Profile active state
238   */
239  getProfileState(profileKey) {
240    return this.state.profiles[profileKey] || false;
241  }
242  /**
243   * Set profile state
244   * @param {string} profileKey - Profile key
245   * @param {boolean} active - Active state
246   */
247  setProfileState(profileKey, active) {
248    const oldState = this.getProfileState(profileKey);
249    if (oldState === active) {
250      return;
251    }
252    this.state.profiles = this.state.profiles || {};
253    this.state.profiles[profileKey] = active;
254    storage.saveSettings(this.state);
255    this.dispatchEvent(new CustomEvent(EVENTS.PROFILE_CHANGED, {
256      detail: {
257        profileKey,
258        active,
259        oldState
260      }
261    }));
262  }
263  /**
264   * Activate profile with mutual exclusivity
265   * @param {string} profileKey - Profile to activate
266   */
267  activateProfile(profileKey) {
268    if (!PROFILE_DEFINITIONS[profileKey]) {
269      return;
270    }
271    const profile = PROFILE_DEFINITIONS[profileKey];
272    Object.keys(PROFILE_DEFINITIONS).forEach((key) => {
273      this.state.profiles[key] = key === profileKey;
274    });
275    const functionUpdates = [];
276    Object.keys(ACCESSIBILITY_FUNCTIONS).forEach((funcName) => {
277      const func = ACCESSIBILITY_FUNCTIONS[funcName];
278      const defaultValue = func.type === FUNCTION_TYPES.LEVEL ? 0 : false;
279      functionUpdates.push({ name: funcName, value: defaultValue });
280    });
281    Object.entries(profile.functions).forEach(([funcName, value]) => {
282      const existingUpdate = functionUpdates.find((u) => u.name === funcName);
283      if (existingUpdate) {
284        existingUpdate.value = value;
285      } else {
286        functionUpdates.push({ name: funcName, value });
287      }
288    });
289    const changedFunctions = [];
290    for (const update of functionUpdates) {
291      const oldValue = this.getFunctionState(update.name);
292      if (oldValue !== update.value) {
293        this.state.functions[update.name] = update.value;
294        changedFunctions.push({ name: update.name, value: update.value, oldValue });
295      }
296    }
297    storage.saveSettings(this.state);
298    this.dispatchEvent(new CustomEvent(EVENTS.PROFILE_CHANGED, {
299      detail: {
300        profileKey,
301        active: true,
302        functions: this.state.functions
303      }
304    }));
305    for (const change of changedFunctions) {
306      this.dispatchEvent(new CustomEvent(EVENTS.FUNCTION_CHANGED, {
307        detail: {
308          functionName: change.name,
309          value: change.value,
310          oldValue: change.oldValue,
311          fromProfile: profileKey
312        }
313      }));
314    }
315  }
316  /**
317   * Deactivate profile
318   * @param {string} profileKey - Profile to deactivate
319   */
320  deactivateProfile(profileKey) {
321    if (!this.getProfileState(profileKey)) {
322      return;
323    }
324    this.state.profiles[profileKey] = false;
325    Object.keys(ACCESSIBILITY_FUNCTIONS).forEach((funcName) => {
326      const func = ACCESSIBILITY_FUNCTIONS[funcName];
327      const defaultValue = func.type === FUNCTION_TYPES.LEVEL ? 0 : false;
328      const oldValue = this.state.functions[funcName];
329      this.state.functions[funcName] = defaultValue;
330      if (oldValue !== defaultValue) {
331        this.dispatchEvent(new CustomEvent(EVENTS.FUNCTION_CHANGED, {
332          detail: {
333            functionName: funcName,
334            value: defaultValue,
335            oldValue,
336            fromProfile: "deactivation"
337          }
338        }));
339      }
340    });
341    storage.saveSettings(this.state);
342    this.dispatchEvent(new CustomEvent(EVENTS.PROFILE_CHANGED, {
343      detail: {
344        profileKey,
345        active: false
346      }
347    }));
348  }
349  /**
350   * Reset all settings
351   */
352  resetAll() {
353    this.state = storage.resetSettings();
354    const wrapper = document.getElementById("site-content-wrapper");
355    if (wrapper) {
356      const classes = wrapper.className.split(" ");
357      const cleanedClasses = classes.filter((c) => !c.includes("accessibility"));
358      wrapper.className = cleanedClasses.join(" ");
359    }
360    if (wrapper && wrapper.style) {
361      wrapper.style.removeProperty("--accessibility-font-scale");
362      wrapper.style.removeProperty("--accessibility-line-height");
363      wrapper.style.removeProperty("--accessibility-line-height-factor");
364      wrapper.style.removeProperty("--accessibility-letter-spacing");
365      wrapper.style.removeProperty("--accessibility-contrast");
366      wrapper.style.removeProperty("--accessibility-brightness");
367      wrapper.style.removeProperty("--accessibility-saturation");
368    }
369    const styleElements = document.querySelectorAll('[id*="accessibility"][id*="styles"]');
370    styleElements.forEach((el) => el.remove());
371    this.dispatchEvent(new CustomEvent(EVENTS.SETTINGS_RESET, {
372      detail: { reset: true }
373    }));
374  }
375  /**
376   * Get active profile
377   * @returns {string|null} Active profile key or null
378   */
379  getActiveProfile() {
380    for (const [key, active] of Object.entries(this.state.profiles || {})) {
381      if (active) return key;
382    }
383    return null;
384  }
385}
386const stateManager = new StateManager();
387/**
388 * Simplified Module System
389 * Centralized implementation of all accessibility functions
390 *
391 * @module modules-simple
392 * @package     Villaester.Module
393 * @subpackage  AccessibilitySa11y
394 * @version     3.1.0
395 * @author      Kiki Schuelling
396 * @copyright   2025 Villaester Moderne Medien
397 * @license     GNU GPL v2 or later
398 * 
399 * IMPORTANT: Saturation fix - v3.0.1
400 * Saturation is correctly applied ONLY to #site-content-wrapper
401 */
402class BaseModule {
403  /**
404   * Creates a new BaseModule instance
405   *
406   * @param {string} name - Module identifier (e.g., 'font', 'visual', 'color')
407   */
408  constructor(name) {
409    this.name = name;
410    this.styleId = `accessibility-${name}-styles`;
411  }
412  /**
413   * Injects CSS styles into the document head
414   * Creates or updates a style element with the module's styles
415   *
416   * @param {string} css - CSS rules to inject
417   * @returns {void}
418   */
419  injectStyles(css) {
420    let styleEl = document.getElementById(this.styleId);
421    if (!styleEl) {
422      styleEl = document.createElement("style");
423      styleEl.id = this.styleId;
424      document.head.appendChild(styleEl);
425    }
426    styleEl.textContent = css;
427  }
428  /**
429   * Removes the module's injected styles from the document
430   *
431   * @returns {void}
432   */
433  removeStyles() {
434    const styleEl = document.getElementById(this.styleId);
435    if (styleEl) styleEl.remove();
436  }
437  /**
438   * Adds a CSS class to the content wrapper element
439   * Uses cached DOM reference for performance
440   *
441   * @param {string} className - CSS class name to add
442   * @returns {void}
443   */
444  addClass(className) {
445    const wrapper = document.getElementById("site-content-wrapper");
446    if (wrapper) {
447      wrapper.classList.add(className);
448    } else {
449      console.warn("site-content-wrapper not found, skipping addClass for:", className);
450    }
451  }
452  /**
453   * Removes a CSS class from the content wrapper element
454   * Uses cached DOM reference for performance
455   *
456   * @param {string} className - CSS class name to remove
457   * @returns {void}
458   */
459  removeClass(className) {
460    const wrapper = document.getElementById("site-content-wrapper");
461    if (wrapper) {
462      wrapper.classList.remove(className);
463    } else {
464      console.warn("site-content-wrapper not found, skipping removeClass for:", className);
465    }
466  }
467  /**
468   * Sets a CSS custom property (variable) on the site-content-wrapper
469   *
470   * @param {string} name - CSS variable name (including '--' prefix)
471   * @param {string|number} value - Value to set
472   * @returns {void}
473   */
474  setCSSVar(name, value) {
475    const wrapper = document.getElementById("site-content-wrapper");
476    if (wrapper) {
477      wrapper.style.setProperty(name, value);
478    }
479  }
480  /**
481   * Removes a CSS custom property from the site-content-wrapper
482   *
483   * @param {string} name - CSS variable name to remove
484   * @returns {void}
485   */
486  removeCSSVar(name) {
487    const wrapper = document.getElementById("site-content-wrapper");
488    if (wrapper) {
489      wrapper.style.removeProperty(name);
490    }
491  }
492}
493class FontModule extends BaseModule {
494  /**
495   * Creates a new FontModule instance
496   * Initializes font scale values for different levels
497   */
498  constructor() {
499    super("font");
500    this.scales = [1, 1.125, 1.25, 1.375];
501    this.lineHeightIncreases = [0, 30, 50, 70];
502    this.letterSpacings = [0, 0.02, 0.05, 0.08];
503  }
504  /**
505   * Sets the font size level
506   * Applies appropriate CSS classes and variables based on level
507   *
508   * @param {number} level - Font size level (0-3, where 0 is default)
509   * @returns {void}
510   */
511  setFontSize(level) {
512    this.removeClass("accessibility-font-size");
513    this.removeClass("accessibility-font-size-1");
514    this.removeClass("accessibility-font-size-2");
515    this.removeClass("accessibility-font-size-3");
516    this.removeCSSVar("--accessibility-font-scale");
517    if (level === 0) {
518      const existingStyles = document.getElementById(this.styleId);
519      if (existingStyles) {
520        let content = existingStyles.textContent;
521        content = content.replace(/\/\* Font-Size Level.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
522        existingStyles.textContent = content;
523      }
524    } else {
525      const scale = this.scales[Math.min(level, 3)];
526      this.setCSSVar("--accessibility-font-scale", scale);
527      this.addClass(`accessibility-font-size-${level}`);
528      let existingStyles = document.getElementById(this.styleId);
529      if (!existingStyles) {
530        existingStyles = document.createElement("style");
531        existingStyles.id = this.styleId;
532        document.head.appendChild(existingStyles);
533      }
534      let content = existingStyles.textContent;
535      content = content.replace(/\/\* Font-Size Level.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
536      content += `
537        /* Font-Size Level ${level} active - scale: ${scale} */
538      `;
539      existingStyles.textContent = content;
540    }
541  }
542  /**
543   * Sets the line height level
544   * Increases spacing between lines for better readability
545   *
546   * @param {number} level - Line height level (0-3, where 0 is default)
547   * @returns {void}
548   */
549  setLineHeight(level) {
550    this.removeClass("accessibility-line-height");
551    this.removeClass("accessibility-line-height-1");
552    this.removeClass("accessibility-line-height-2");
553    this.removeClass("accessibility-line-height-3");
554    this.removeCSSVar("--accessibility-line-height-factor");
555    if (level === 0) {
556      const existingStyles = document.getElementById(this.styleId);
557      if (existingStyles) {
558        let content = existingStyles.textContent;
559        content = content.replace(/\/\* Line-Height Level.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
560        existingStyles.textContent = content;
561      }
562    } else {
563      const increase = this.lineHeightIncreases[Math.min(level, 3)];
564      const factor = 1 + increase / 100;
565      this.setCSSVar("--accessibility-line-height-factor", factor);
566      this.addClass(`accessibility-line-height`);
567      this.addClass(`accessibility-line-height-${level}`);
568      let existingStyles = document.getElementById(this.styleId);
569      if (!existingStyles) {
570        existingStyles = document.createElement("style");
571        existingStyles.id = this.styleId;
572        document.head.appendChild(existingStyles);
573      }
574      let content = existingStyles.textContent;
575      content = content.replace(/\/\* Line-Height Level.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
576      content += `
577        /* Line-Height Level ${level} - ${increase}% increase */
578        .accessibility-line-height * { 
579          line-height: calc(1em * var(--accessibility-line-height-factor, 1.3)) !important; 
580        }
581        .accessibility-line-height p,
582        .accessibility-line-height li,
583        .accessibility-line-height div,
584        .accessibility-line-height span,
585        .accessibility-line-height h1,
586        .accessibility-line-height h2,
587        .accessibility-line-height h3,
588        .accessibility-line-height h4,
589        .accessibility-line-height h5,
590        .accessibility-line-height h6 {
591          line-height: calc(1.5em * var(--accessibility-line-height-factor, 1.3)) !important;
592        }
593      `;
594      existingStyles.textContent = content;
595    }
596  }
597  /**
598   * Sets the letter spacing level
599   * Increases spacing between characters for better readability
600   *
601   * @param {number} level - Letter spacing level (0-3, where 0 is default)
602   * @returns {void}
603   */
604  setLetterSpacing(level) {
605    this.removeClass("accessibility-letter-spacing");
606    this.removeClass("accessibility-letter-spacing-1");
607    this.removeClass("accessibility-letter-spacing-2");
608    this.removeClass("accessibility-letter-spacing-3");
609    this.removeCSSVar("--accessibility-letter-spacing");
610    if (level === 0) {
611      const existingStyles = document.getElementById(this.styleId);
612      if (existingStyles) {
613        let content = existingStyles.textContent;
614        content = content.replace(/\/\* Letter-Spacing Level.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
615        existingStyles.textContent = content;
616      }
617    } else {
618      const spacing = this.letterSpacings[Math.min(level, 3)];
619      this.setCSSVar("--accessibility-letter-spacing", spacing + "em");
620      this.addClass("accessibility-letter-spacing");
621      this.addClass(`accessibility-letter-spacing-${level}`);
622      let existingStyles = document.getElementById(this.styleId);
623      if (!existingStyles) {
624        existingStyles = document.createElement("style");
625        existingStyles.id = this.styleId;
626        document.head.appendChild(existingStyles);
627      }
628      let content = existingStyles.textContent;
629      content = content.replace(/\/\* Letter-Spacing Level.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
630      content += `
631        /* Letter-Spacing Level ${level} - ${spacing}em */
632        .accessibility-letter-spacing * { 
633          letter-spacing: var(--accessibility-letter-spacing, normal) !important; 
634        }
635      `;
636      existingStyles.textContent = content;
637    }
638  }
639  /**
640   * Resets all font settings to default values
641   *
642   * @returns {void}
643   */
644  reset() {
645    this.setFontSize(0);
646    this.setLineHeight(0);
647    this.setLetterSpacing(0);
648  }
649}
650class VisualModule extends BaseModule {
651  /**
652   * Creates a new VisualModule instance
653   */
654  constructor() {
655    super("visual");
656  }
657  /**
658   * Sets the contrast level
659   * Increases color contrast for better visibility
660   *
661   * @param {number} level - Contrast level (0-3, where 0 is default)
662   * @returns {void}
663   */
664  setContrast(level) {
665    if (level === 0) {
666      this.removeClass("accessibility-contrast");
667      this.removeCSSVar("--accessibility-contrast");
668    } else {
669      if (window.ModAccessibilityFunctions && document.getElementById("site-content-wrapper")?.classList.contains("accessibility-invert-colors")) {
670        window.ModAccessibilityFunctions.setFunctionState("invert-colors", false);
671      }
672      const values = [1, 1.25, 1.5, 2];
673      const contrast = values[Math.min(level - 1, 3)];
674      this.setCSSVar("--accessibility-contrast", contrast);
675      this.addClass("accessibility-contrast");
676      this.injectStyles(`
677        .accessibility-contrast { 
678          filter: contrast(var(--accessibility-contrast, 1)) !important; 
679        }
680      `);
681    }
682  }
683  setBrightness(level) {
684    if (level === 0) {
685      this.removeClass("accessibility-brightness");
686      this.removeCSSVar("--accessibility-brightness");
687    } else {
688      if (window.ModAccessibilityFunctions && document.getElementById("site-content-wrapper")?.classList.contains("accessibility-invert-colors")) {
689        window.ModAccessibilityFunctions.setFunctionState("invert-colors", false);
690      }
691      const values = [1, 0.9, 0.8, 0.7];
692      const brightness = values[Math.min(level - 1, 3)];
693      this.setCSSVar("--accessibility-brightness", brightness);
694      this.addClass("accessibility-brightness");
695      this.injectStyles(`
696        .accessibility-brightness { 
697          filter: brightness(var(--accessibility-brightness, 1)) !important; 
698        }
699      `);
700    }
701  }
702  setSaturation(level) {
703    if (level === 0) {
704      this.removeClass("accessibility-saturation");
705      this.removeCSSVar("--accessibility-saturation");
706    } else {
707      const values = [1, 1.5, 2, 2.5];
708      const saturation = values[Math.min(level - 1, 3)];
709      this.setCSSVar("--accessibility-saturation", saturation);
710      this.addClass("accessibility-saturation");
711      this.injectStyles(`
712        .accessibility-saturation { 
713          filter: saturate(var(--accessibility-saturation, 1)) !important; 
714        }
715      `);
716    }
717  }
718  reset() {
719    this.setContrast(0);
720    this.setBrightness(0);
721    this.setSaturation(0);
722  }
723}
724class ColorModule extends BaseModule {
725  /**
726   * Creates a new ColorModule instance
727   */
728  constructor() {
729    super("color");
730  }
731  /**
732   * Toggles color inversion
733   * Inverts all colors on the page for users with specific visual needs
734   *
735   * @param {boolean} enabled - Whether to enable color inversion
736   * @returns {void}
737   */
738  setInvertColors(enabled) {
739    if (enabled) {
740      if (window.ModAccessibilityFunctions) {
741        window.ModAccessibilityFunctions.setFunctionState("brightness", 0);
742        window.ModAccessibilityFunctions.setFunctionState("contrast", 0);
743      }
744      this.addClass("accessibility-invert-colors");
745      this.injectStyles(`
746        .accessibility-invert-colors { 
747          filter: invert(1) !important; 
748        }
749      `);
750    } else {
751      this.removeClass("accessibility-invert-colors");
752    }
753  }
754  setGrayscale(level) {
755    if (level === 0) {
756      this.removeClass("accessibility-grayscale");
757    } else {
758      const values = [0, 0.5, 1];
759      const gray = values[Math.min(level, 2)];
760      this.addClass("accessibility-grayscale");
761      this.injectStyles(`
762        .accessibility-grayscale { 
763          filter: grayscale(${gray}) !important; 
764        }
765      `);
766    }
767  }
768  reset() {
769    this.setInvertColors(false);
770    this.setGrayscale(0);
771  }
772}
773class TextModule extends BaseModule {
774  /**
775   * Creates a new TextModule instance
776   * Initializes text-to-speech components if available
777   */
778  constructor() {
779    super("text");
780    this.speechSynthesis = window.speechSynthesis;
781    this.currentUtterance = null;
782    this.clickListenerId = null;
783    this.hoverListenerId = null;
784    this.hoverTimeoutId = null;
785  }
786  /**
787   * Darken a color by a percentage
788   * @param {string} hex - Hex color code
789   * @param {number} percent - Percentage to darken
790   * @returns {string} Darkened hex color
791   */
792  darkenColor(hex, percent) {
793    hex = hex.replace("#", "");
794    const r = parseInt(hex.substring(0, 2), 16);
795    const g = parseInt(hex.substring(2, 4), 16);
796    const b = parseInt(hex.substring(4, 6), 16);
797    const factor = 1 - percent / 100;
798    const newR = Math.round(r * factor);
799    const newG = Math.round(g * factor);
800    const newB = Math.round(b * factor);
801    return "#" + [newR, newG, newB].map((x) => x.toString(16).padStart(2, "0")).join("");
802  }
803  /**
804   * Get contrasting text color (black or white) based on background
805   * @param {string} hex - Background hex color
806   * @returns {string} '#ffffff' or '#000000'
807   */
808  getContrastColor(hex) {
809    hex = hex.replace("#", "");
810    const r = parseInt(hex.substring(0, 2), 16);
811    const g = parseInt(hex.substring(2, 4), 16);
812    const b = parseInt(hex.substring(4, 6), 16);
813    const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
814    return luminance > 0.5 ? "#000000" : "#ffffff";
815  }
816  /**
817   * Toggles readable font (Roboto)
818   * Applies a clean, highly readable font for better text comprehension
819   *
820   * @param {boolean} enabled - Whether to enable readable font
821   * @param {boolean} fromOtherFont - Internal flag to prevent recursion
822   * @returns {void}
823   */
824  setReadableFont(enabled, fromOtherFont = false) {
825    this.removeClass("accessibility-readable-font");
826    this.removeClass("accessibility-readable-font-roboto");
827    if (enabled) {
828      if (!fromOtherFont && document.getElementById("site-content-wrapper")?.classList.contains("accessibility-dyslexic-font")) {
829        this.setDyslexicFont(false, true);
830      }
831      this.addClass("accessibility-readable-font");
832      this.addClass("accessibility-readable-font-roboto");
833      let existingStyles = document.getElementById(this.styleId);
834      if (!existingStyles) {
835        existingStyles = document.createElement("style");
836        existingStyles.id = this.styleId;
837        document.head.appendChild(existingStyles);
838      }
839      let content = existingStyles.textContent;
840      content = content.replace(/\/\* Readable Font Active.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
841      content += `
842        /* Readable Font Active */
843        .accessibility-readable-font * {
844          font-family: 'Roboto', "Segoe UI", Arial, sans-serif !important;
845          text-rendering: optimizeLegibility !important;
846          -webkit-font-smoothing: antialiased !important;
847          -moz-osx-font-smoothing: grayscale !important;
848        }
849        .accessibility-readable-font p,
850        .accessibility-readable-font li,
851        .accessibility-readable-font div,
852        .accessibility-readable-font span,
853        .accessibility-readable-font a,
854        .accessibility-readable-font td,
855        .accessibility-readable-font th {
856          font-family: 'Roboto', "Segoe UI", Arial, sans-serif !important;
857          font-weight: 400 !important;
858        }
859        .accessibility-readable-font h1,
860        .accessibility-readable-font h2,
861        .accessibility-readable-font h3,
862        .accessibility-readable-font h4,
863        .accessibility-readable-font h5,
864        .accessibility-readable-font h6 {
865          font-family: 'Roboto', "Segoe UI", Arial, sans-serif !important;
866          font-weight: 600 !important;
867        }
868        .accessibility-readable-font strong,
869        .accessibility-readable-font b {
870          font-weight: 700 !important;
871        }
872      `;
873      existingStyles.textContent = content;
874    } else {
875      const existingStyles = document.getElementById(this.styleId);
876      if (existingStyles) {
877        let content = existingStyles.textContent;
878        content = content.replace(/\/\* Readable Font Active.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
879        existingStyles.textContent = content;
880      }
881    }
882  }
883  setDyslexicFont(enabled, fromOtherFont = false) {
884    this.removeClass("accessibility-dyslexic-font");
885    if (enabled) {
886      if (!fromOtherFont && document.getElementById("site-content-wrapper")?.classList.contains("accessibility-readable-font")) {
887        this.setReadableFont(false, true);
888      }
889      this.addClass("accessibility-dyslexic-font");
890      let existingStyles = document.getElementById(this.styleId);
891      if (!existingStyles) {
892        existingStyles = document.createElement("style");
893        existingStyles.id = this.styleId;
894        document.head.appendChild(existingStyles);
895      }
896      let content = existingStyles.textContent;
897      content = content.replace(/\/\* Dyslexic Font Active.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
898      content += `
899        /* Dyslexic Font Active */
900        @font-face {
901          font-family: 'OpenDyslexic';
902          src: url('/media/mod_accessibility_sa11y/fonts/opendyslexic/OpenDyslexic-Regular.woff2') format('woff2');
903          font-weight: normal;
904          font-style: normal;
905          font-display: swap;
906        }
907        @font-face {
908          font-family: 'OpenDyslexic';
909          src: url('/media/mod_accessibility_sa11y/fonts/opendyslexic/OpenDyslexic-Bold.woff2') format('woff2');
910          font-weight: bold;
911          font-style: normal;
912          font-display: swap;
913        }
914        .accessibility-dyslexic-font * {
915          font-family: 'OpenDyslexic', Arial, sans-serif !important;
916          letter-spacing: 0.05em !important;
917          word-spacing: 0.1em !important;
918          line-height: 1.5em !important;
919        }
920        .accessibility-dyslexic-font p,
921        .accessibility-dyslexic-font li,
922        .accessibility-dyslexic-font div,
923        .accessibility-dyslexic-font span,
924        .accessibility-dyslexic-font a {
925          font-family: 'OpenDyslexic', Arial, sans-serif !important;
926        }
927      `;
928      existingStyles.textContent = content;
929    } else {
930      const existingStyles = document.getElementById(this.styleId);
931      if (existingStyles) {
932        let content = existingStyles.textContent;
933        content = content.replace(/\/\* Dyslexic Font Active.*?\*\/[\s\S]*?(?=\/\*|$)/g, "");
934        existingStyles.textContent = content;
935      }
936    }
937  }
938  setTextMagnifier(enabled) {
939    if (enabled) {
940      this.addClass("accessibility-text-magnifier");
941      if (window.ModAccessibilityFunctions?.advancedMagnifier) {
942        this.useAdvancedMagnifier(enabled);
943        return;
944      }
945      const getPrimaryColor = () => {
946        const wrapper = document.getElementById("site-content-wrapper");
947        let color = wrapper ? getComputedStyle(wrapper).getPropertyValue("--a11y-primary-color").trim() : "";
948        if (!color) {
949          const a11yBox = document.getElementById("a11y-box");
950          if (a11yBox) {
951            color = a11yBox.dataset.primaryColor;
952          }
953        }
954        if (!color && window.a11yPrimaryColor) {
955          color = window.a11yPrimaryColor;
956        }
957        return color || "#0056b3";
958      };
959      if (this.magnifierContainer) {
960        const primaryColor = getPrimaryColor();
961        const darkerShade = this.darkenColor(primaryColor, 10);
962        const textColor = this.getContrastColor(primaryColor);
963        this.magnifierContainer.style.borderColor = primaryColor;
964        const header = this.magnifierContainer.querySelector("div:first-child");
965        if (header && header !== this.magnifierContent) {
966          header.style.background = `linear-gradient(90deg, ${primaryColor} 0%, ${darkerShade} 100%)`;
967          header.style.borderBottomColor = darkerShade;
968          const headerTexts = header.querySelectorAll("span");
969          headerTexts.forEach((span) => {
970            span.style.color = textColor;
971          });
972          const zoomIndicator = header.querySelector("#magnifier-zoom");
973          if (zoomIndicator) {
974            zoomIndicator.style.background = textColor === "#ffffff" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.1)";
975          }
976        }
977      }
978      if (!this.magnifierContainer) {
979        const primaryColorForBorder = getPrimaryColor();
980        this.magnifierContainer = document.createElement("div");
981        this.magnifierContainer.id = "a11y-text-magnifier";
982        this.magnifierContainer.style.cssText = `
983          position: fixed;
984          width: 600px;
985          min-height: 180px;
986          max-height: 300px;
987          background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
988          border: 2px solid ${primaryColorForBorder};
989          border-radius: 12px;
990          box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 0 0 1px rgba(255,255,255,0.8) inset;
991          pointer-events: none;
992          z-index: 999998;
993          display: none;
994          overflow: hidden;
995          transform-origin: center;
996          transition: opacity 0.2s ease;
997        `;
998        const primaryColor = getPrimaryColor();
999        const darkerShade = this.darkenColor(primaryColor, 10);
1000        const textColor = this.getContrastColor(primaryColor);
1001        const header = document.createElement("div");
1002        header.style.cssText = `
1003          background: linear-gradient(90deg, ${primaryColor} 0%, ${darkerShade} 100%);
1004          padding: 8px 15px;
1005          display: flex;
1006          justify-content: space-between;
1007          align-items: center;
1008          border-bottom: 1px solid ${darkerShade};
1009        `;
1010        const title = document.createElement("span");
1011        title.style.cssText = `
1012          color: ${textColor};
1013          font-size: 12px;
1014          font-weight: 600;
1015          letter-spacing: 0.5px;
1016          text-transform: uppercase;
1017        `;
1018        title.textContent = "Textlupe";
1019        const zoomIndicator = document.createElement("span");
1020        zoomIndicator.id = "magnifier-zoom";
1021        zoomIndicator.style.cssText = `
1022          color: ${textColor};
1023          font-size: 11px;
1024          font-weight: 500;
1025          background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.1)"};
1026          padding: 2px 8px;
1027          border-radius: 4px;
1028        `;
1029        zoomIndicator.textContent = "2.0x";
1030        header.appendChild(title);
1031        header.appendChild(zoomIndicator);
1032        this.magnifierContent = document.createElement("div");
1033        this.magnifierContent.style.cssText = `
1034          padding: 20px;
1035          font-size: 2em;
1036          line-height: 1.6;
1037          color: #2c3e50;
1038          font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
1039          font-weight: 400;
1040          overflow-y: auto;
1041          max-height: 250px;
1042          word-wrap: break-word;
1043          text-align: left;
1044          background: white;
1045          position: relative;
1046        `;
1047        this.wordHighlight = document.createElement("div");
1048        this.wordHighlight.style.cssText = `
1049          position: absolute;
1050          background: rgba(255, 235, 59, 0.3);
1051          border: 1px solid rgba(255, 193, 7, 0.5);
1052          border-radius: 3px;
1053          pointer-events: none;
1054          display: none;
1055          padding: 2px 4px;
1056          margin: -2px -4px;
1057        `;
1058        this.magnifierContainer.appendChild(header);
1059        this.magnifierContainer.appendChild(this.magnifierContent);
1060        this.magnifierContent.appendChild(this.wordHighlight);
1061        document.body.appendChild(this.magnifierContainer);
1062        this.magnifierZoom = 2;
1063      }
1064      const handleMagnifier = (e) => {
1065        const target = e.target;
1066        if (!target || !target.textContent || target.textContent.trim().length === 0) {
1067          this.magnifierContainer.style.display = "none";
1068          return;
1069        }
1070        const primaryColor = getPrimaryColor();
1071        const darkerShade = this.darkenColor(primaryColor, 10);
1072        const textColor = this.getContrastColor(primaryColor);
1073        this.magnifierContainer.style.borderColor = primaryColor;
1074        const header = this.magnifierContainer.querySelector("div:first-child");
1075        if (header && header !== this.magnifierContent) {
1076          header.style.background = `linear-gradient(90deg, ${primaryColor} 0%, ${darkerShade} 100%)`;
1077          header.style.borderBottomColor = darkerShade;
1078          const headerElements = header.querySelectorAll("*");
1079          headerElements.forEach((el) => {
1080            if (el.style) {
1081              el.style.color = textColor;
1082            }
1083          });
1084          const zoomEl = document.getElementById("magnifier-zoom");
1085          if (zoomEl) {
1086            zoomEl.style.background = textColor === "#ffffff" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.1)";
1087            zoomEl.style.color = textColor;
1088          }
1089        }
1090        let contextElement = target;
1091        let text = "";
1092        let isSpecialElement = false;
1093        if (target.tagName === "IMG") {
1094          text = target.alt || target.title || "Bild ohne Beschreibung";
1095          isSpecialElement = true;
1096        } else if (target.tagName === "BUTTON" || target.tagName === "A") {
1097          text = target.textContent.trim() || target.title || target.getAttribute("aria-label") || "";
1098          if (target.tagName === "A" && !text) {
1099            text = target.href;
1100          }
1101          isSpecialElement = true;
1102        } else if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
1103          text = target.value || target.placeholder || "";
1104          isSpecialElement = true;
1105        }
1106        if (!isSpecialElement) {
1107          const parentBlock = target.closest("p, li, h1, h2, h3, h4, h5, h6, td, blockquote, article, section");
1108          if (parentBlock) {
1109            contextElement = parentBlock;
1110          }
1111          text = contextElement.textContent.trim();
1112          if (text.length > 300) {
1113            const range = document.caretRangeFromPoint(e.clientX, e.clientY);
1114            if (range && range.startContainer.nodeType === Node.TEXT_NODE) {
1115              const fullText = range.startContainer.textContent;
1116              const offset = range.startOffset;
1117              let start = offset;
1118              let end = offset;
1119              while (start > 0 && start > offset - 100) {
1120                if (fullText[start] === "." || fullText[start] === "!" || fullText[start] === "?") {
1121                  start++;
1122                  break;
1123                }
1124                start--;
1125              }
1126              while (end < fullText.length && end < offset + 200) {
1127                if (fullText[end] === "." || fullText[end] === "!" || fullText[end] === "?") {
1128                  end++;
1129                  break;
1130                }
1131                end++;
1132              }
1133              text = fullText.substring(start, end).trim();
1134              if (start > 0) text = "..." + text;
1135              if (end < fullText.length) text = text + "...";
1136              const wordStart = offset;
1137              let wordEnd = offset;
1138              while (wordEnd < fullText.length && fullText[wordEnd] !== " " && fullText[wordEnd] !== "," && fullText[wordEnd] !== ".") {
1139                wordEnd++;
1140              }
1141              const currentWord = fullText.substring(wordStart, wordEnd);
1142              if (currentWord && currentWord.trim()) {
1143                const parts = text.split(currentWord);
1144                if (parts.length > 1) {
1145                  this.magnifierContent.innerHTML = "";
1146                  parts.forEach((part, index) => {
1147                    const textNode = document.createTextNode(part);
1148                    this.magnifierContent.appendChild(textNode);
1149                    if (index < parts.length - 1) {
1150                      const highlight = document.createElement("span");
1151                      highlight.style.cssText = `
1152                        background: linear-gradient(180deg, transparent 40%, rgba(255, 235, 59, 0.6) 40%, rgba(255, 235, 59, 0.6) 95%, transparent 95%);
1153                        padding: 0 2px;
1154                        font-weight: 500;
1155                      `;
1156                      highlight.textContent = currentWord;
1157                      this.magnifierContent.appendChild(highlight);
1158                    }
1159                  });
1160                } else {
1161                  this.magnifierContent.textContent = text;
1162                }
1163              } else {
1164                this.magnifierContent.textContent = text;
1165              }
1166            } else {
1167              text = text.substring(0, 300);
1168              if (text.length === 300) text += "...";
1169              this.magnifierContent.textContent = text;
1170            }
1171          } else {
1172            this.magnifierContent.textContent = text;
1173          }
1174        } else {
1175          this.magnifierContent.textContent = text;
1176        }
1177        if (text) {
1178          const zoomEl = document.getElementById("magnifier-zoom");
1179          if (zoomEl) {
1180            zoomEl.textContent = this.magnifierZoom.toFixed(1) + "x";
1181          }
1182          this.magnifierContent.style.fontSize = this.magnifierZoom + "em";
1183          this.magnifierContainer.style.display = "block";
1184          this.magnifierContainer.style.opacity = "1";
1185          const rect = this.magnifierContainer.getBoundingClientRect();
1186          const magnifierWidth = 600;
1187          const magnifierHeight = Math.min(300, rect.height);
1188          let x = e.clientX - magnifierWidth / 2;
1189          let y = e.clientY - magnifierHeight - 40;
1190          x = Math.max(10, Math.min(x, window.innerWidth - magnifierWidth - 10));
1191          if (y < 10) {
1192            y = e.clientY + 40;
1193          }
1194          if (y + magnifierHeight > window.innerHeight - 10) {
1195            y = Math.max(10, Math.min(e.clientY - magnifierHeight / 2, window.innerHeight - magnifierHeight - 10));
1196            if (e.clientX > window.innerWidth / 2) {
1197              x = e.clientX - magnifierWidth - 40;
1198            } else {
1199              x = e.clientX + 40;
1200            }
1201          }
1202          this.magnifierContainer.style.left = x + "px";
1203          this.magnifierContainer.style.top = y + "px";
1204        }
1205      };
1206      const hideMagnifier = () => {
1207        if (this.magnifierContainer) {
1208          this.magnifierContainer.style.opacity = "0";
1209          setTimeout(() => {
1210            if (this.magnifierContainer) {
1211              this.magnifierContainer.style.display = "none";
1212            }
1213          }, 200);
1214        }
1215      };
1216      const handleKeyboard = (e) => {
1217        if (!this.magnifierContainer || this.magnifierContainer.style.display === "none") return;
1218        if ((e.ctrlKey || e.metaKey) && (e.key === "+" || e.key === "=")) {
1219          e.preventDefault();
1220          this.magnifierZoom = Math.min(4, this.magnifierZoom + 0.5);
1221          const zoomEl = document.getElementById("magnifier-zoom");
1222          if (zoomEl) zoomEl.textContent = this.magnifierZoom.toFixed(1) + "x";
1223          this.magnifierContent.style.fontSize = this.magnifierZoom + "em";
1224        }
1225        if ((e.ctrlKey || e.metaKey) && e.key === "-") {
1226          e.preventDefault();
1227          this.magnifierZoom = Math.max(1, this.magnifierZoom - 0.5);
1228          const zoomEl = document.getElementById("magnifier-zoom");
1229          if (zoomEl) zoomEl.textContent = this.magnifierZoom.toFixed(1) + "x";
1230          this.magnifierContent.style.fontSize = this.magnifierZoom + "em";
1231        }
1232        if ((e.ctrlKey || e.metaKey) && e.key === "0") {
1233          e.preventDefault();
1234          this.magnifierZoom = 2;
1235          const zoomEl = document.getElementById("magnifier-zoom");
1236          if (zoomEl) zoomEl.textContent = this.magnifierZoom.toFixed(1) + "x";
1237          this.magnifierContent.style.fontSize = this.magnifierZoom + "em";
1238        }
1239      };
1240      this.magnifierHandler = handleMagnifier;
1241      this.magnifierHideHandler = hideMagnifier;
1242      this.magnifierKeyboardHandler = handleKeyboard;
1243      document.addEventListener("mousemove", this.magnifierHandler, { passive: true });
1244      document.addEventListener("mouseleave", this.magnifierHideHandler);
1245      document.addEventListener("keydown", this.magnifierKeyboardHandler);
1246    } else {
1247      this.removeClass("accessibility-text-magnifier");
1248      if (window.ModAccessibilityFunctions?.advancedMagnifier) {
1249        this.useAdvancedMagnifier(false);
1250      }
1251      if (this.magnifierHandler) {
1252        document.removeEventListener("mousemove", this.magnifierHandler);
1253        this.magnifierHandler = null;
1254      }
1255      if (this.magnifierHideHandler) {
1256        document.removeEventListener("mouseleave", this.magnifierHideHandler);
1257        this.magnifierHideHandler = null;
1258      }
1259      if (this.magnifierKeyboardHandler) {
1260        document.removeEventListener("keydown", this.magnifierKeyboardHandler);
1261        this.magnifierKeyboardHandler = null;
1262      }
1263      if (this.magnifierContainer) {
1264        this.magnifierContainer.remove();
1265        this.magnifierContainer = null;
1266        this.magnifierContent = null;
1267        this.wordHighlight = null;
1268      }
1269      const existingMagnifier = document.getElementById("a11y-text-magnifier");
1270      if (existingMagnifier) {
1271        existingMagnifier.remove();
1272      }
1273    }
1274  }
1275  /**
1276   * Use advanced magnifier if available
1277   */
1278  useAdvancedMagnifier(enabled) {
1279    const advMagnifier = window.ModAccessibilityFunctions?.advancedMagnifier;
1280    if (!advMagnifier) return;
1281    if (enabled) {
1282      advMagnifier.create();
1283      let throttleTimer = null;
1284      const handleAdvancedMagnifier = (e) => {
1285        if (throttleTimer) return;
1286        throttleTimer = setTimeout(() => {
1287          throttleTimer = null;
1288        }, 50);
1289        const target = e.target;
1290        if (target.closest("#a11y-text-magnifier-advanced") || target.closest("#accessibility-menu") || target.closest("#a11y-toggle")) {
1291          return;
1292        }
1293        if (!target || !target.textContent || target.textContent.trim().length === 0) {
1294          advMagnifier.hide();
1295          return;
1296        }
1297        let text = "";
1298        const parentBlock = target.closest("p, li, h1, h2, h3, h4, h5, h6, td, blockquote, article, span, a");
1299        if (parentBlock) {
1300          text = parentBlock.textContent.trim();
1301        } else {
1302          text = target.textContent.trim();
1303        }
1304        if (text && text.length > 2) {
1305          advMagnifier.setText(text);
1306          advMagnifier.show(e.clientX, e.clientY);
1307        }
1308      };
1309      const handleMouseLeave = () => {
1310        if (!advMagnifier.isPinned) {
1311          advMagnifier.hide();
1312        }
1313      };
1314      const handleKeyboard = (e) => {
1315        const container = document.getElementById("a11y-text-magnifier-advanced");
1316        if (!container) return;
1317        const isVisible = container.style.display === "block";
1318        if (e.key === "Escape" && isVisible && !advMagnifier.isPinned) {
1319          advMagnifier.hide();
1320          return;
1321        }
1322        if (e.key === "p" && isVisible) {
1323          e.preventDefault();
1324          advMagnifier.togglePin();
1325          return;
1326        }
1327        if (!isVisible) return;
1328        if (e.key === "+" || e.key === "=") {
1329          e.preventDefault();
1330          advMagnifier.setZoom(advMagnifier.zoom + 0.5);
1331        } else if (e.key === "-" || e.key === "_") {
1332          e.preventDefault();
1333          advMagnifier.setZoom(advMagnifier.zoom - 0.5);
1334        } else if (e.key === "0") {
1335          e.preventDefault();
1336          advMagnifier.setZoom(2);
1337        }
1338        if (e.key === "ArrowLeft") {
1339          e.preventDefault();
1340          advMagnifier.navigateWord(-1);
1341        } else if (e.key === "ArrowRight") {
1342          e.preventDefault();
1343          advMagnifier.navigateWord(1);
1344        }
1345        if (e.key === "t" && isVisible) {
1346          e.preventDefault();
1347          advMagnifier.toggleSpeech();
1348        }
1349      };
1350      this.advancedMagnifierHandler = handleAdvancedMagnifier;
1351      this.advancedMagnifierLeaveHandler = handleMouseLeave;
1352      this.advancedMagnifierKeyHandler = handleKeyboard;
1353      document.addEventListener("mousemove", this.advancedMagnifierHandler, { passive: true });
1354      document.addEventListener("mouseleave", this.advancedMagnifierLeaveHandler);
1355      document.addEventListener("keydown", this.advancedMagnifierKeyHandler);
1356    } else {
1357      if (this.advancedMagnifierHandler) {
1358        document.removeEventListener("mousemove", this.advancedMagnifierHandler);
1359        this.advancedMagnifierHandler = null;
1360      }
1361      if (this.advancedMagnifierLeaveHandler) {
1362        document.removeEventListener("mouseleave", this.advancedMagnifierLeaveHandler);
1363        this.advancedMagnifierLeaveHandler = null;
1364      }
1365      if (this.advancedMagnifierKeyHandler) {
1366        document.removeEventListener("keydown", this.advancedMagnifierKeyHandler);
1367        this.advancedMagnifierKeyHandler = null;
1368      }
1369      if (advMagnifier) {
1370        advMagnifier.destroy();
1371      }
1372    }
1373  }
1374  /**
1375   * Toggles text-to-speech functionality
1376   * Enables reading of text content on hover or click
1377   *
1378   * @param {boolean} enabled - Whether to enable text-to-speech
1379   * @returns {void}
1380   */
1381  setTextToSpeech(enabled) {
1382    if (enabled) {
1383      this.addClass("accessibility-text-to-speech");
1384      this.startTextToSpeech();
1385    } else {
1386      this.removeClass("accessibility-text-to-speech");
1387      this.stopTextToSpeech();
1388    }
1389  }
1390  /**
1391   * Starts text-to-speech functionality
1392   * Sets up event listeners for text selection and reading
1393   *
1394   * @private
1395   * @returns {void}
1396   */
1397  startTextToSpeech() {
1398    if (!this.speechSynthesis) {
1399      console.warn("Text-to-speech not supported in this browser");
1400      return;
1401    }
1402    this.stopTextToSpeech();
1403    const clickHandler = (e) => {
1404      const target = e.target;
1405      const text = target.textContent || target.innerText;
1406      if (text && text.trim()) {
1407        this.speak(text.trim());
1408      }
1409    };
1410    const hoverHandler = (e) => {
1411      const target = e.target;
1412      if (target.childElementCount === 0) {
1413        const text = target.textContent || target.innerText;
1414        if (text && text.trim() && text.trim().length > 3) {
1415          if (this.hoverTimeoutId) {
1416            clearTimeout(this.hoverTimeoutId);
1417          }
1418          this.hoverTimeoutId = setTimeout(() => {
1419            this.speak(text.trim());
1420          }, 500);
1421        }
1422      }
1423    };
1424    this.clickHandler = clickHandler;
1425    this.hoverHandler = hoverHandler;
1426    document.addEventListener("click", this.clickHandler);
1427    document.addEventListener("mouseover", this.hoverHandler);
1428  }
1429  stopTextToSpeech() {
1430    if (this.speechSynthesis) {
1431      this.speechSynthesis.cancel();
1432    }
1433    if (this.clickHandler) {
1434      document.removeEventListener("click", this.clickHandler);
1435      this.clickHandler = null;
1436    }
1437    if (this.hoverHandler) {
1438      document.removeEventListener("mouseover", this.hoverHandler);
1439      this.hoverHandler = null;
1440    }
1441    if (this.hoverTimeoutId) {
1442      clearTimeout(this.hoverTimeoutId);
1443      this.hoverTimeoutId = null;
1444    }
1445  }
1446  /**
1447   * Speaks the provided text using the Speech Synthesis API
1448   * Prioritizes German voice if available
1449   *
1450   * @param {string} text - Text to be spoken
1451   * @returns {void}
1452   */
1453  speak(text) {
1454    if (!this.speechSynthesis) return;
1455    this.speechSynthesis.cancel();
1456    this.currentUtterance = new SpeechSynthesisUtterance(text);
1457    this.currentUtterance.rate = 0.9;
1458    this.currentUtterance.pitch = 1;
1459    this.currentUtterance.volume = 1;
1460    const voices = this.speechSynthesis.getVoices();
1461    const germanVoice = voices.find((voice) => voice.lang.startsWith("de"));
1462    if (germanVoice) {
1463      this.currentUtterance.voice = germanVoice;
1464    }
1465    this.speechSynthesis.speak(this.currentUtterance);
1466  }
1467  reset() {
1468    this.setReadableFont(false);
1469    this.setDyslexicFont(false);
1470    this.setTextMagnifier(false);
1471    this.setTextToSpeech(false);
1472    if (this.magnifierContainer) {
1473      this.magnifierContainer.remove();
1474      this.magnifierContainer = null;
1475      this.magnifierContent = null;
1476      this.wordHighlight = null;
1477    }
1478  }
1479}
1480class InteractionModule extends BaseModule {
1481  /**
1482   * Creates a new InteractionModule instance
1483   */
1484  constructor() {
1485    super("interaction");
1486    this.readingGuideListenerId = null;
1487    this.readingGuideElement = null;
1488    this.currentCursorLevel = 0;
1489  }
1490  /**
1491   * Sets the big cursor with multiple size levels
1492   * Uses simple larger cursor with visual indicator
1493   * 
1494   * @param {number} level - Cursor size level (0-3, where 0 is default)
1495   * @returns {void}
1496   */
1497  setBigCursor(level) {
1498    this.removeClass("accessibility-cursor");
1499    this.removeClass("accessibility-cursor-1");
1500    this.removeClass("accessibility-cursor-2");
1501    this.removeClass("accessibility-cursor-3");
1502    const existingCursorStyle = document.getElementById("a11y-cursor-style");
1503    if (existingCursorStyle) {
1504      existingCursorStyle.remove();
1505    }
1506    const existingIndicator = document.getElementById("a11y-cursor-indicator");
1507    if (existingIndicator) {
1508      existingIndicator.remove();
1509    }
1510    if (level === 0 || !level) {
1511      this.currentCursorLevel = 0;
1512      return;
1513    }
1514    level = Math.min(Math.max(level, 1), 3);
1515    this.currentCursorLevel = level;
1516    const sizes = {
1517      1: 24,
1518      // Small indicator
1519      2: 32,
1520      // Medium indicator
1521      3: 40
1522      // Large indicator
1523    };
1524    const size = sizes[level];
1525    this.addClass(`accessibility-cursor-${level}`);
1526    const indicator = document.createElement("div");
1527    indicator.id = "a11y-cursor-indicator";
1528    indicator.style.cssText = `
1529      position: fixed;
1530      width: ${size}px;
1531      height: ${size}px;
1532      border: 3px solid #000;
1533      border-radius: 50%;
1534      background: rgba(255, 255, 0, 0.3);
1535      pointer-events: none;
1536      z-index: 9998;
1537      transform: translate(-50%, -50%);
1538      transition: none;
1539      display: none;
1540    `;
1541    document.body.appendChild(indicator);
1542    const moveHandler = (e) => {
1543      indicator.style.left = e.clientX + "px";
1544      indicator.style.top = e.clientY + "px";
1545      indicator.style.display = "block";
1546    };
1547    const leaveHandler = () => {
1548      indicator.style.display = "none";
1549    };
1550    document.addEventListener("mousemove", moveHandler);
1551    document.addEventListener("mouseleave", leaveHandler);
1552    indicator.dataset.moveHandler = "cursor-move";
1553    indicator.dataset.leaveHandler = "cursor-leave";
1554    const styleEl = document.createElement("style");
1555    styleEl.id = "a11y-cursor-style";
1556    styleEl.textContent = `
1557      /* Big Cursor Level ${level} - Indicator Size: ${size}px */
1558      /* Version: 4.0.${Date.now()} */
1559      
1560      /* Keep normal cursor but make it more visible */
1561      .accessibility-cursor-${level} {
1562        cursor: auto !important;
1563      }
1564      
1565      .accessibility-cursor-${level} a,
1566      .accessibility-cursor-${level} button,
1567      .accessibility-cursor-${level} [role="button"],
1568      .accessibility-cursor-${level} [onclick] {
1569        cursor: pointer !important;
1570      }
1571      
1572      .accessibility-cursor-${level} input[type="text"],
1573      .accessibility-cursor-${level} textarea,
1574      .accessibility-cursor-${level} [contenteditable="true"] {
1575        cursor: text !important;
1576      }
1577      
1578      /* Visual indicator styling */
1579      #a11y-cursor-indicator {
1580        mix-blend-mode: difference;
1581      }
1582      
1583      /* Respect reduced motion preference */
1584      @media (prefers-reduced-motion: reduce) {
1585        #a11y-cursor-indicator {
1586          display: none !important;
1587        }
1588      }
1589    `;
1590    document.head.appendChild(styleEl);
1591  }
1592  // Keep old setCursor for backwards compatibility
1593  setCursor(enabled) {
1594    this.setBigCursor(enabled ? 1 : 0);
1595  }
1596  setUnderlineLinks(enabled) {
1597    const styleId = "a11y-underline-links-style";
1598    const existingStyle = document.getElementById(styleId);
1599    if (enabled) {
1600      this.addClass("accessibility-underline-links");
1601      if (!existingStyle) {
1602        const styleEl = document.createElement("style");
1603        styleEl.id = styleId;
1604        styleEl.textContent = `
1605          /* Underline Links Styles */
1606          #site-content-wrapper.accessibility-underline-links a { 
1607            text-decoration: underline !important; 
1608            text-decoration-thickness: 2px !important; 
1609            text-underline-offset: 2px !important;
1610          }
1611          #site-content-wrapper.accessibility-underline-links a:hover { 
1612            text-decoration: underline !important; 
1613            text-decoration-thickness: 3px !important; 
1614          }
1615        `;
1616        document.head.appendChild(styleEl);
1617      }
1618    } else {
1619      this.removeClass("accessibility-underline-links");
1620      if (existingStyle) {
1621        existingStyle.remove();
1622      }
1623    }
1624  }
1625  setPauseAnimations(enabled) {
1626    const styleId = "a11y-pause-animations-style";
1627    const existingStyle = document.getElementById(styleId);
1628    if (enabled) {
1629      this.addClass("accessibility-pause-animations");
1630      if (!existingStyle) {
1631        const styleEl = document.createElement("style");
1632        styleEl.id = styleId;
1633        styleEl.textContent = `
1634          /* Pause Animations Styles */
1635          #site-content-wrapper.accessibility-pause-animations *, 
1636          #site-content-wrapper.accessibility-pause-animations *:before, 
1637          #site-content-wrapper.accessibility-pause-animations *:after { 
1638            animation-play-state: paused !important; 
1639            animation-duration: 0.01s !important;
1640            transition: none !important; 
1641            transition-duration: 0s !important;
1642          }
1643          #site-content-wrapper.accessibility-pause-animations img,
1644          #site-content-wrapper.accessibility-pause-animations video,
1645          #site-content-wrapper.accessibility-pause-animations iframe {
1646            animation: none !important;
1647            transition: none !important;
1648          }
1649        `;
1650        document.head.appendChild(styleEl);
1651      }
1652    } else {
1653      this.removeClass("accessibility-pause-animations");
1654      if (existingStyle) {
1655        existingStyle.remove();
1656      }
1657    }
1658  }
1659  /**
1660   * Toggles the reading guide
1661   * Shows a horizontal line that follows the mouse cursor for easier reading
1662   *
1663   * @param {boolean} enabled - Whether to enable the reading guide
1664   * @returns {void}
1665   */
1666  /**
1667   * Sets page zoom level for better visibility
1668   * Zooms the entire page content
1669   * 
1670   * @param {number} level - Zoom level (0-3, where 0 is default)
1671   * @returns {void}
1672   */
1673  setPageZoom(level) {
1674    this.removeClass("accessibility-zoom-1");
1675    this.removeClass("accessibility-zoom-2");
1676    this.removeClass("accessibility-zoom-3");
1677    if (level === 0 || !level) {
1678      return;
1679    }
1680    level = Math.min(Math.max(level, 1), 3);
1681    const zoomFactors = {
1682      1: 1.1,
1683      // 110% zoom
1684      2: 1.25,
1685      // 125% zoom
1686      3: 1.5
1687      // 150% zoom
1688    };
1689    const zoomFactor = zoomFactors[level];
1690    this.addClass(`accessibility-zoom-${level}`);
1691    const wrapper = document.getElementById("site-content-wrapper");
1692    if (wrapper) {
1693      wrapper.style.zoom = zoomFactor;
1694      wrapper.style.transformOrigin = "top center";
1695    }
1696  }
1697  setReadingGuide(enabled) {
1698    if (enabled) {
1699      this.addClass("accessibility-reading-guide");
1700      const updateGuide = (e) => {
1701        if (!this.readingGuideElement) {
1702          this.readingGuideElement = document.createElement("div");
1703          this.readingGuideElement.id = "a11y-reading-guide";
1704          this.readingGuideElement.style.cssText = `
1705            position: fixed;
1706            left: 0;
1707            right: 0;
1708            height: 20px;
1709            background: rgba(255, 255, 0, 0.3);
1710            border-top: 2px solid #333;
1711            border-bottom: 2px solid #333;
1712            pointer-events: none;
1713            z-index: 9998;
1714            transition: top 0.1s ease;
1715          `;
1716          document.body.appendChild(this.readingGuideElement);
1717        }
1718        this.readingGuideElement.style.top = e.clientY - 10 + "px";
1719      };
1720      this.readingGuideHandler = updateGuide;
1721      document.addEventListener("mousemove", this.readingGuideHandler, { passive: true });
1722    } else {
1723      this.removeClass("accessibility-reading-guide");
1724      if (this.readingGuideHandler) {
1725        document.removeEventListener("mousemove", this.readingGuideHandler);
1726        this.readingGuideHandler = null;
1727      }
1728      if (this.readingGuideElement) {
1729        this.readingGuideElement.remove();
1730        this.readingGuideElement = null;
1731      }
1732    }
1733  }
1734  reset() {
1735    this.setBigCursor(0);
1736    this.setUnderlineLinks(false);
1737    this.setPauseAnimations(false);
1738    this.setReadingGuide(false);
1739    this.setPageZoom(0);
1740    const wrapper = document.getElementById("site-content-wrapper");
1741    if (wrapper) {
1742      wrapper.style.zoom = "";
1743      wrapper.style.transformOrigin = "";
1744    }
1745  }
1746}
1747class SimpleModules {
1748  /**
1749   * Creates a new SimpleModules instance
1750   * Initializes all sub-modules and defines available functions
1751   */
1752  constructor() {
1753    this.font = new FontModule();
1754    this.visual = new VisualModule();
1755    this.color = new ColorModule();
1756    this.text = new TextModule();
1757    this.interaction = new InteractionModule();
1758    this.functions = [
1759      "font-size",
1760      "line-height",
1761      "letter-spacing",
1762      "contrast",
1763      "brightness",
1764      "saturation",
1765      "invert-colors",
1766      "grayscale",
1767      "readable-font",
1768      "dyslexic-font",
1769      "text-magnifier",
1770      "text-to-speech",
1771      "cursor",
1772      "underline-links",
1773      "pause-animations",
1774      "reading-guide"
1775    ];
1776  }
1777  /**
1778   * Applies an accessibility function with the specified value
1779   * Routes the request to the appropriate module
1780   *
1781   * @param {string} functionName - Name of the accessibility function
1782   * @param {number|boolean} value - Value to apply (level for multi-state, boolean for toggles)
1783   * @returns {void}
1784   * @example
1785   * modules.apply('font-size', 2); // Set font size to level 2
1786   * modules.apply('readable-font', true); // Enable readable font
1787   */
1788  apply(functionName, value) {
1789    switch (functionName) {
1790      case "font-size":
1791        this.font.setFontSize(value);
1792        break;
1793      case "line-height":
1794        this.font.setLineHeight(value);
1795        break;
1796      case "letter-spacing":
1797        this.font.setLetterSpacing(value);
1798        break;
1799      case "contrast":
1800        this.visual.setContrast(value);
1801        break;
1802      case "brightness":
1803        this.visual.setBrightness(value);
1804        break;
1805      case "saturation":
1806        this.visual.setSaturation(value);
1807        break;
1808      case "invert-colors":
1809        this.color.setInvertColors(value);
1810        break;
1811      case "grayscale":
1812        this.color.setGrayscale(value);
1813        break;
1814      case "readable-font":
1815        this.text.setReadableFont(value);
1816        break;
1817      case "dyslexic-font":
1818        this.text.setDyslexicFont(value);
1819        break;
1820      case "text-magnifier":
1821        this.text.setTextMagnifier(value);
1822        break;
1823      case "text-to-speech":
1824        this.text.setTextToSpeech(value);
1825        break;
1826      case "cursor":
1827        this.interaction.setBigCursor(value ? value : 0);
1828        break;
1829      case "underline-links":
1830        this.interaction.setUnderlineLinks(value);
1831        break;
1832      case "pause-animations":
1833        this.interaction.setPauseAnimations(value);
1834        break;
1835      case "reading-guide":
1836        this.interaction.setReadingGuide(value);
1837        break;
1838      case "page-zoom":
1839        this.interaction.setPageZoom(value);
1840        break;
1841    }
1842  }
1843  /**
1844   * Resets all accessibility features to default state
1845   * Removes all classes, CSS variables, and injected styles
1846   *
1847   * @returns {void}
1848   */
1849  reset() {
1850    this.font.reset();
1851    this.visual.reset();
1852    this.color.reset();
1853    this.text.reset();
1854    this.interaction.reset();
1855    const wrapper = document.getElementById("site-content-wrapper");
1856    if (wrapper) {
1857      const classes = Array.from(wrapper.classList);
1858      classes.forEach((c) => {
1859        if (c.startsWith("accessibility-")) {
1860          wrapper.classList.remove(c);
1861        }
1862      });
1863      const styles = wrapper.style;
1864      for (let i = styles.length - 1; i >= 0; i--) {
1865        const prop = styles[i];
1866        if (prop.startsWith("--accessibility-")) {
1867          styles.removeProperty(prop);
1868        }
1869      }
1870    } else {
1871      console.warn("site-content-wrapper not found during reset");
1872    }
1873    document.querySelectorAll('style[id*="accessibility"]').forEach((el) => el.remove());
1874  }
1875}
1876const simpleModules = new SimpleModules();
1877class ProfilesManager {
1878  constructor() {
1879    this.profiles = PROFILE_DEFINITIONS;
1880    this.activeProfiles = /* @__PURE__ */ new Set();
1881    this.stateManager = null;
1882  }
1883  /**
1884   * Toggle a profile - SIMPLIFIED VERSION
1885   * @param {string} profileKey - Profile to toggle
1886   * @returns {boolean} Success status
1887   */
1888  toggleProfile(profileKey) {
1889    const profile = this.profiles[profileKey];
1890    if (!profile) {
1891      console.error(`Profile not found: ${profileKey}`);
1892      return false;
1893    }
1894    const isActive = this.activeProfiles.has(profileKey);
1895    if (isActive) {
1896      this.deactivateProfile(profileKey);
1897    } else {
1898      this.activateProfile(profileKey);
1899    }
1900    return true;
1901  }
1902  /**
1903   * Activate a profile - SIMPLIFIED VERSION
1904   * Only one profile can be active at a time
1905   * @param {string} profileKey - Profile to activate
1906   * @returns {boolean} Success status
1907   */
1908  activateProfile(profileKey) {
1909    const profile = this.profiles[profileKey];
1910    if (!profile) {
1911      console.error(`Profile not found: ${profileKey}`);
1912      return false;
1913    }
1914    if (this.stateManager) {
1915      this.stateManager.activateProfile(profileKey);
1916      this.activeProfiles.clear();
1917      this.activeProfiles.add(profileKey);
1918    }
1919    document.dispatchEvent(new CustomEvent("accessibility:profile:activated", {
1920      detail: { profileKey, profile }
1921    }));
1922    return true;
1923  }
1924  /**
1925   * Deactivate a profile - SIMPLIFIED VERSION
1926   * @param {string} profileKey - Profile to deactivate
1927   */
1928  deactivateProfile(profileKey) {
1929    const profile = this.profiles[profileKey];
1930    if (!profile || !this.activeProfiles.has(profileKey)) {
1931      return false;
1932    }
1933    this.activeProfiles.delete(profileKey);
1934    if (this.stateManager) {
1935      this.stateManager.setProfileState(profileKey, false);
1936      if (profile.functions) {
1937        Object.entries(profile.functions).forEach(([functionName, value]) => {
1938          const defaultValue = typeof value === "number" ? 0 : false;
1939          this.stateManager.setFunctionState(functionName, defaultValue, true);
1940        });
1941      }
1942    }
1943    document.dispatchEvent(new CustomEvent("accessibility:profile:deactivated", {
1944      detail: { profileKey, profile }
1945    }));
1946    return true;
1947  }
1948  /**
1949   * Get profile functions
1950   * @param {string} profileKey - Profile key
1951   * @returns {Object|null} Profile functions or null
1952   */
1953  getProfileFunctions(profileKey) {
1954    const profile = this.profiles[profileKey];
1955    return profile ? profile.functions : null;
1956  }
1957}
1958const profilesManager = new ProfilesManager();
1959class AdvancedMagnifier {
1960  constructor() {
1961    this.container = null;
1962    this.contentArea = null;
1963    this.isPinned = false;
1964    this.isResizing = false;
1965    this.currentMode = "light";
1966    this.zoom = 2;
1967    this.width = 600;
1968    this.height = 200;
1969    this.isSpeaking = false;
1970    this.currentText = "";
1971    this.wordIndex = 0;
1972    this.words = [];
1973    this.colorModes = {
1974      light: {
1975        bg: "#ffffff",
1976        text: "#2c3e50",
1977        highlight: "rgba(255, 235, 59, 0.4)",
1978        border: "#2c3e50"
1979      },
1980      dark: {
1981        bg: "#1a1a1a",
1982        text: "#e0e0e0",
1983        highlight: "rgba(100, 200, 255, 0.4)",
1984        border: "#3498db"
1985      },
1986      highContrast: {
1987        bg: "#000000",
1988        text: "#ffff00",
1989        highlight: "rgba(255, 255, 255, 0.3)",
1990        border: "#ffffff"
1991      },
1992      sepia: {
1993        bg: "#f4ecd8",
1994        text: "#5c4033",
1995        highlight: "rgba(139, 69, 19, 0.2)",
1996        border: "#8b4513"
1997      }
1998    };
1999  }
2000  /**
2001   * Get primary color from various sources
2002   */
2003  getPrimaryColor() {
2004    let color = getComputedStyle(document.documentElement).getPropertyValue("--a11y-primary-color").trim();
2005    if (!color) {
2006      const a11yBox = document.getElementById("a11y-box");
2007      if (a11yBox) {
2008        color = a11yBox.dataset.primaryColor;
2009      }
2010    }
2011    if (!color && window.a11yPrimaryColor) {
2012      color = window.a11yPrimaryColor;
2013    }
2014    return color || "#0056b3";
2015  }
2016  /**
2017   * Darken a color by a percentage
2018   */
2019  darkenColor(hex, percent) {
2020    hex = hex.replace("#", "");
2021    const r = parseInt(hex.substring(0, 2), 16);
2022    const g = parseInt(hex.substring(2, 4), 16);
2023    const b = parseInt(hex.substring(4, 6), 16);
2024    const factor = 1 - percent / 100;
2025    const newR = Math.round(r * factor);
2026    const newG = Math.round(g * factor);
2027    const newB = Math.round(b * factor);
2028    return "#" + [newR, newG, newB].map((x) => x.toString(16).padStart(2, "0")).join("");
2029  }
2030  /**
2031   * Get contrasting text color (black or white) based on background
2032   */
2033  getContrastColor(hex) {
2034    hex = hex.replace("#", "");
2035    const r = parseInt(hex.substring(0, 2), 16);
2036    const g = parseInt(hex.substring(2, 4), 16);
2037    const b = parseInt(hex.substring(4, 6), 16);
2038    const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
2039    return luminance > 0.5 ? "#000000" : "#ffffff";
2040  }
2041  create() {
2042    if (this.container) return;
2043    this.container = document.createElement("div");
2044    this.container.id = "a11y-text-magnifier-advanced";
2045    this.container.className = "magnifier-advanced";
2046    this.updateContainerStyle();
2047    const header = this.createHeader();
2048    this.contentArea = document.createElement("div");
2049    this.contentArea.className = "magnifier-content";
2050    this.updateContentStyle();
2051    const footer = this.createFooter();
2052    const resizeHandle = document.createElement("div");
2053    resizeHandle.className = "magnifier-resize";
2054    resizeHandle.style.cssText = `
2055      position: absolute;
2056      bottom: 0;
2057      right: 0;
2058      width: 20px;
2059      height: 20px;
2060      cursor: nwse-resize;
2061      background: linear-gradient(135deg, transparent 50%, rgba(52, 152, 219, 0.5) 50%);
2062      border-radius: 0 0 10px 0;
2063    `;
2064    this.container.appendChild(header);
2065    this.container.appendChild(this.contentArea);
2066    this.container.appendChild(footer);
2067    this.container.appendChild(resizeHandle);
2068    document.body.appendChild(this.container);
2069    this.initResize(resizeHandle);
2070    this.synth = window.speechSynthesis;
2071    this.injectStyles();
2072  }
2073  createHeader() {
2074    const primaryColor = this.getPrimaryColor();
2075    const darkerShade = this.darkenColor(primaryColor, 10);
2076    const textColor = this.getContrastColor(primaryColor);
2077    const header = document.createElement("div");
2078    header.className = "magnifier-header";
2079    header.style.cssText = `
2080      background: linear-gradient(90deg, ${primaryColor} 0%, ${darkerShade} 100%);
2081      padding: 10px 15px;
2082      display: flex;
2083      justify-content: space-between;
2084      align-items: center;
2085      border-bottom: 1px solid ${darkerShade};
2086      user-select: none;
2087      cursor: move;
2088    `;
2089    const leftSide = document.createElement("div");
2090    leftSide.style.cssText = "display: flex; align-items: center; gap: 15px;";
2091    const title = document.createElement("span");
2092    title.style.cssText = `
2093      color: ${textColor};
2094      font-size: 12px;
2095      font-weight: 600;
2096      letter-spacing: 0.5px;
2097      text-transform: uppercase;
2098    `;
2099    title.textContent = "TEXTLUPE PRO";
2100    const modeSelector = document.createElement("select");
2101    modeSelector.className = "mode-selector";
2102    modeSelector.style.cssText = `
2103      background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)"};
2104      color: ${textColor};
2105      border: 1px solid ${textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"};
2106      padding: 3px 8px;
2107      border-radius: 4px;
2108      font-size: 11px;
2109      cursor: pointer;
2110    `;
2111    Object.keys(this.colorModes).forEach((mode) => {
2112      const option = document.createElement("option");
2113      option.value = mode;
2114      option.textContent = mode.charAt(0).toUpperCase() + mode.slice(1);
2115      option.style.background = "#34495e";
2116      modeSelector.appendChild(option);
2117    });
2118    modeSelector.addEventListener("change", (e) => {
2119      this.setColorMode(e.target.value);
2120    });
2121    leftSide.appendChild(title);
2122    leftSide.appendChild(modeSelector);
2123    const rightSide = document.createElement("div");
2124    rightSide.style.cssText = "display: flex; align-items: center; gap: 10px;";
2125    const zoomIndicator = document.createElement("span");
2126    zoomIndicator.id = "magnifier-zoom";
2127    zoomIndicator.style.cssText = `
2128      color: ${textColor};
2129      font-size: 11px;
2130      font-weight: 500;
2131      background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.1)"};
2132      padding: 3px 8px;
2133      border-radius: 4px;
2134      min-width: 40px;
2135      text-align: center;
2136    `;
2137    zoomIndicator.textContent = "2.0x";
2138    const ttsButton = document.createElement("button");
2139    ttsButton.className = "tts-button";
2140    ttsButton.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
2141      <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
2142    </svg>`;
2143    ttsButton.title = "Text vorlesen (T)";
2144    ttsButton.style.cssText = `
2145      background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)"};
2146      color: ${textColor};
2147      border: 1px solid ${textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"};
2148      padding: 5px 8px;
2149      border-radius: 4px;
2150      cursor: pointer;
2151      display: flex;
2152      align-items: center;
2153      justify-content: center;
2154    `;
2155    ttsButton.addEventListener("click", () => this.toggleSpeech());
2156    const pinButton = document.createElement("button");
2157    pinButton.className = "pin-button";
2158    pinButton.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
2159      <path d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z"/>
2160    </svg>`;
2161    pinButton.title = "Lupe fixieren (P)";
2162    pinButton.style.cssText = `
2163      background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)"};
2164      color: ${textColor};
2165      border: 1px solid ${textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"};
2166      padding: 5px 8px;
2167      border-radius: 4px;
2168      cursor: pointer;
2169      display: flex;
2170      align-items: center;
2171      justify-content: center;
2172    `;
2173    pinButton.addEventListener("click", () => this.togglePin());
2174    rightSide.appendChild(zoomIndicator);
2175    rightSide.appendChild(ttsButton);
2176    rightSide.appendChild(pinButton);
2177    header.appendChild(leftSide);
2178    header.appendChild(rightSide);
2179    this.makeDraggable(header);
2180    return header;
2181  }
2182  createFooter() {
2183    const primaryColor = this.getPrimaryColor();
2184    const darkerShade = this.darkenColor(primaryColor, 10);
2185    const textColor = this.getContrastColor(primaryColor);
2186    const footer = document.createElement("div");
2187    footer.className = "magnifier-footer";
2188    footer.style.cssText = `
2189      background: linear-gradient(90deg, ${primaryColor} 0%, ${darkerShade} 100%);
2190      padding: 10px 15px;
2191      border-top: 2px solid ${darkerShade};
2192      display: flex;
2193      justify-content: space-between;
2194      align-items: center;
2195      gap: 15px;
2196      font-size: 11px;
2197      color: ${textColor};
2198      flex-wrap: wrap;
2199    `;
2200    const wordNav = document.createElement("div");
2201    wordNav.style.cssText = "display: flex; align-items: center; gap: 10px;";
2202    const prevWord = document.createElement("button");
2203    prevWord.innerHTML = "◀";
2204    prevWord.style.cssText = `
2205      background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.1)"};
2206      border: 1px solid ${textColor === "#ffffff" ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.2)"};
2207      color: ${textColor};
2208      padding: 2px 8px;
2209      border-radius: 3px;
2210      cursor: pointer;
2211      transition: all 0.2s;
2212    `;
2213    prevWord.addEventListener("click", () => this.navigateWord(-1));
2214    prevWord.addEventListener("mouseover", () => {
2215      prevWord.style.background = textColor === "#ffffff" ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.2)";
2216    });
2217    prevWord.addEventListener("mouseout", () => {
2218      prevWord.style.background = textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.1)";
2219    });
2220    const wordIndicator = document.createElement("span");
2221    wordIndicator.id = "word-indicator";
2222    wordIndicator.textContent = "Wort 1/1";
2223    wordIndicator.style.color = textColor;
2224    const nextWord = document.createElement("button");
2225    nextWord.innerHTML = "▶";
2226    nextWord.style.cssText = `
2227      background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.1)"};
2228      border: 1px solid ${textColor === "#ffffff" ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.2)"};
2229      color: ${textColor};
2230      padding: 2px 8px;
2231      border-radius: 3px;
2232      cursor: pointer;
2233      transition: all 0.2s;
2234    `;
2235    nextWord.addEventListener("click", () => this.navigateWord(1));
2236    nextWord.addEventListener("mouseover", () => {
2237      nextWord.style.background = textColor === "#ffffff" ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.2)";
2238    });
2239    nextWord.addEventListener("mouseout", () => {
2240      nextWord.style.background = textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.1)";
2241    });
2242    wordNav.appendChild(prevWord);
2243    wordNav.appendChild(wordIndicator);
2244    wordNav.appendChild(nextWord);
2245    const shortcuts = document.createElement("div");
2246    shortcuts.style.cssText = `
2247      display: flex;
2248      align-items: center;
2249      gap: 8px;
2250      color: ${textColor};
2251      font-size: 11px;
2252      font-weight: 500;
2253      background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)"};
2254      padding: 4px 10px;
2255      border-radius: 4px;
2256      border: 1px solid ${textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.15)"};
2257    `;
2258    const createShortcutBadge = (key, description) => {
2259      const badge = document.createElement("span");
2260      badge.style.cssText = `
2261        display: inline-flex;
2262        align-items: center;
2263        gap: 4px;
2264        padding: 2px 6px;
2265        background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"};
2266        border-radius: 3px;
2267        white-space: nowrap;
2268      `;
2269      const keySpan = document.createElement("strong");
2270      keySpan.style.cssText = `
2271        font-weight: 700;
2272        color: ${textColor};
2273        background: ${textColor === "#ffffff" ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"};
2274        padding: 0 4px;
2275        border-radius: 2px;
2276        font-size: 10px;
2277      `;
2278      keySpan.textContent = key;
2279      const descSpan = document.createElement("span");
2280      descSpan.style.cssText = `
2281        font-size: 10px;
2282        opacity: 0.9;
2283      `;
2284      descSpan.textContent = description;
2285      badge.appendChild(keySpan);
2286      badge.appendChild(descSpan);
2287      return badge;
2288    };
2289    shortcuts.appendChild(createShortcutBadge("+/-", "Zoom"));
2290    shortcuts.appendChild(createShortcutBadge("P", "Pin"));
2291    shortcuts.appendChild(createShortcutBadge("T", "Vorlesen"));
2292    shortcuts.appendChild(createShortcutBadge("←→", "Navigation"));
2293    shortcuts.appendChild(createShortcutBadge("Esc", "Schließen"));
2294    footer.appendChild(wordNav);
2295    footer.appendChild(shortcuts);
2296    return footer;
2297  }
2298  updateContainerStyle() {
2299    if (!this.container) return;
2300    this.container.style.cssText = `
2301      position: fixed;
2302      width: ${this.width}px;
2303      min-height: ${this.height}px;
2304      max-height: 500px;
2305      background: white;
2306      border: 2px solid ${this.colorModes[this.currentMode].border};
2307      border-radius: 12px;
2308      box-shadow: 0 10px 40px rgba(0,0,0,0.3), 0 0 0 1px rgba(255,255,255,0.8) inset;
2309      z-index: 999998;
2310      display: none;
2311      overflow: hidden;
2312      resize: both;
2313      transition: opacity 0.3s ease, transform 0.3s ease;
2314      ${this.isPinned ? "pointer-events: auto;" : "pointer-events: none;"}
2315    `;
2316  }
2317  updateContentStyle() {
2318    if (!this.contentArea) return;
2319    const mode = this.colorModes[this.currentMode];
2320    this.contentArea.style.cssText = `
2321      padding: 20px;
2322      font-size: ${this.zoom}em;
2323      line-height: 1.8;
2324      color: ${mode.text};
2325      background: ${mode.bg};
2326      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
2327      overflow-y: auto;
2328      max-height: 350px;
2329      word-wrap: break-word;
2330      text-align: left;
2331      position: relative;
2332    `;
2333  }
2334  setColorMode(mode) {
2335    if (!this.colorModes[mode]) return;
2336    this.currentMode = mode;
2337    this.updateContentStyle();
2338    this.updateContainerStyle();
2339  }
2340  setZoom(newZoom) {
2341    this.zoom = Math.max(1, Math.min(5, newZoom));
2342    this.contentArea.style.fontSize = this.zoom + "em";
2343    const zoomEl = document.getElementById("magnifier-zoom");
2344    if (zoomEl) zoomEl.textContent = this.zoom.toFixed(1) + "x";
2345  }
2346  togglePin() {
2347    this.isPinned = !this.isPinned;
2348    this.updateContainerStyle();
2349    const pinButton = this.container.querySelector(".pin-button");
2350    if (pinButton) {
2351      pinButton.style.background = this.isPinned ? "rgba(52, 152, 219, 0.3)" : "rgba(255,255,255,0.1)";
2352    }
2353  }
2354  toggleSpeech() {
2355    if (this.isSpeaking) {
2356      this.stopSpeech();
2357    } else {
2358      this.startSpeech();
2359    }
2360  }
2361  startSpeech() {
2362    if (!this.synth || !this.currentText) return;
2363    this.stopSpeech();
2364    const utterance = new SpeechSynthesisUtterance(this.currentText);
2365    utterance.rate = 0.9;
2366    utterance.pitch = 1;
2367    utterance.volume = 1;
2368    const voices = this.synth.getVoices();
2369    const germanVoice = voices.find((v) => v.lang.startsWith("de"));
2370    if (germanVoice) utterance.voice = germanVoice;
2371    utterance.onend = () => {
2372      this.isSpeaking = false;
2373      this.updateTTSButton();
2374    };
2375    this.synth.speak(utterance);
2376    this.isSpeaking = true;
2377    this.updateTTSButton();
2378  }
2379  stopSpeech() {
2380    if (this.synth) {
2381      this.synth.cancel();
2382      this.isSpeaking = false;
2383      this.updateTTSButton();
2384    }
2385  }
2386  updateTTSButton() {
2387    const button = this.container?.querySelector(".tts-button");
2388    if (button) {
2389      if (this.isSpeaking) {
2390        button.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
2391          <path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>
2392        </svg>`;
2393        button.style.background = "rgba(231, 76, 60, 0.3)";
2394      } else {
2395        button.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
2396          <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
2397        </svg>`;
2398        button.style.background = "rgba(255,255,255,0.1)";
2399      }
2400    }
2401  }
2402  setText(text) {
2403    if (!text) return;
2404    this.currentText = text;
2405    this.words = text.split(/\s+/).filter((w) => w.length > 0);
2406    this.wordIndex = 0;
2407    this.updateContent();
2408    const indicator = document.getElementById("word-indicator");
2409    if (indicator) {
2410      indicator.textContent = `Wort ${this.wordIndex + 1}/${this.words.length}`;
2411    }
2412  }
2413  updateContent() {
2414    if (!this.contentArea || !this.currentText) return;
2415    if (this.words.length > 0) {
2416      const parts = [];
2417      let currentPos = 0;
2418      this.words.forEach((word, index) => {
2419        const wordPos = this.currentText.indexOf(word, currentPos);
2420        if (wordPos > currentPos) {
2421          parts.push({
2422            text: this.currentText.substring(currentPos, wordPos),
2423            highlighted: false
2424          });
2425        }
2426        parts.push({
2427          text: word,
2428          highlighted: index === this.wordIndex
2429        });
2430        currentPos = wordPos + word.length;
2431      });
2432      if (currentPos < this.currentText.length) {
2433        parts.push({
2434          text: this.currentText.substring(currentPos),
2435          highlighted: false
2436        });
2437      }
2438      this.contentArea.innerHTML = parts.map((part) => {
2439        if (part.highlighted) {
2440          const mode = this.colorModes[this.currentMode];
2441          return `<span style="
2442            background: ${mode.highlight};
2443            padding: 2px 4px;
2444            border-radius: 3px;
2445            font-weight: 600;
2446            animation: pulse 1s ease-in-out;
2447          ">${part.text}</span>`;
2448        }
2449        return part.text;
2450      }).join("");
2451    } else {
2452      this.contentArea.textContent = this.currentText;
2453    }
2454  }
2455  navigateWord(direction) {
2456    if (this.words.length === 0) return;
2457    this.wordIndex = Math.max(0, Math.min(this.words.length - 1, this.wordIndex + direction));
2458    this.updateContent();
2459    const indicator = document.getElementById("word-indicator");
2460    if (indicator) {
2461      indicator.textContent = `Wort ${this.wordIndex + 1}/${this.words.length}`;
2462    }
2463    if (this.isSpeaking && this.words[this.wordIndex]) {
2464      this.stopSpeech();
2465      const utterance = new SpeechSynthesisUtterance(this.words[this.wordIndex]);
2466      utterance.rate = 0.8;
2467      this.synth.speak(utterance);
2468    }
2469  }
2470  show(x, y) {
2471    if (!this.container) return;
2472    this.container.style.display = "block";
2473    if (!this.isPinned) {
2474      const rect = this.container.getBoundingClientRect();
2475      let posX = x - rect.width / 2;
2476      let posY = y - rect.height - 30;
2477      posX = Math.max(10, Math.min(posX, window.innerWidth - rect.width - 10));
2478      if (posY < 10) {
2479        posY = y + 30;
2480      }
2481      this.container.style.left = posX + "px";
2482      this.container.style.top = posY + "px";
2483    }
2484    this.container.style.opacity = "1";
2485    this.container.style.transform = "scale(1)";
2486  }
2487  hide() {
2488    if (!this.container || this.isPinned) return;
2489    this.container.style.opacity = "0";
2490    this.container.style.transform = "scale(0.95)";
2491    setTimeout(() => {
2492      if (this.container && !this.isPinned) {
2493        this.container.style.display = "none";
2494      }
2495    }, 300);
2496    this.stopSpeech();
2497  }
2498  makeDraggable(handle) {
2499    let isDragging = false;
2500    let startX, startY, initialX, initialY;
2501    handle.addEventListener("mousedown", (e) => {
2502      if (e.target.tagName === "BUTTON" || e.target.tagName === "SELECT") return;
2503      isDragging = true;
2504      startX = e.clientX;
2505      startY = e.clientY;
2506      const rect = this.container.getBoundingClientRect();
2507      initialX = rect.left;
2508      initialY = rect.top;
2509      e.preventDefault();
2510    });
2511    document.addEventListener("mousemove", (e) => {
2512      if (!isDragging) return;
2513      const dx = e.clientX - startX;
2514      const dy = e.clientY - startY;
2515      this.container.style.left = initialX + dx + "px";
2516      this.container.style.top = initialY + dy + "px";
2517    });
2518    document.addEventListener("mouseup", () => {
2519      isDragging = false;
2520    });
2521  }
2522  initResize(handle) {
2523    let isResizing = false;
2524    let startX, startY, startWidth, startHeight;
2525    handle.addEventListener("mousedown", (e) => {
2526      isResizing = true;
2527      startX = e.clientX;
2528      startY = e.clientY;
2529      startWidth = this.width;
2530      startHeight = this.height;
2531      this.container.style.transition = "none";
2532      e.preventDefault();
2533      e.stopPropagation();
2534    });
2535    document.addEventListener("mousemove", (e) => {
2536      if (!isResizing) return;
2537      this.width = Math.max(400, Math.min(1e3, startWidth + e.clientX - startX));
2538      this.height = Math.max(150, Math.min(500, startHeight + e.clientY - startY));
2539      this.container.style.width = this.width + "px";
2540      this.container.style.minHeight = this.height + "px";
2541    });
2542    document.addEventListener("mouseup", () => {
2543      if (isResizing) {
2544        isResizing = false;
2545        this.container.style.transition = "opacity 0.3s ease, transform 0.3s ease";
2546      }
2547    });
2548  }
2549  injectStyles() {
2550    const style = document.createElement("style");
2551    style.textContent = `
2552      @keyframes pulse {
2553        0% { transform: scale(1); }
2554        50% { transform: scale(1.05); }
2555        100% { transform: scale(1); }
2556      }
2557      
2558      .magnifier-advanced select,
2559      .magnifier-advanced button {
2560        outline: none;
2561      }
2562      
2563      .magnifier-advanced button:hover {
2564        opacity: 0.8;
2565      }
2566      
2567      .magnifier-advanced button:active {
2568        transform: scale(0.95);
2569      }
2570      
2571      .magnifier-content::-webkit-scrollbar {
2572        width: 8px;
2573      }
2574      
2575      .magnifier-content::-webkit-scrollbar-track {
2576        background: rgba(0,0,0,0.1);
2577        border-radius: 4px;
2578      }
2579      
2580      .magnifier-content::-webkit-scrollbar-thumb {
2581        background: rgba(0,0,0,0.3);
2582        border-radius: 4px;
2583      }
2584      
2585      .magnifier-content::-webkit-scrollbar-thumb:hover {
2586        background: rgba(0,0,0,0.5);
2587      }
2588    `;
2589    document.head.appendChild(style);
2590  }
2591  destroy() {
2592    this.stopSpeech();
2593    if (this.container) {
2594      this.container.remove();
2595      this.container = null;
2596      this.contentArea = null;
2597    }
2598  }
2599}
2600class SimpleUI {
2601  constructor() {
2602    this.overlay = null;
2603    this.toggleButton = null;
2604    this.menu = null;
2605    this.isOpen = false;
2606    this.stateManager = null;
2607  }
2608  /**
2609   * Initialize the UI
2610   */
2611  init() {
2612    this.overlay = document.getElementById("a11y-box");
2613    this.toggleButton = document.getElementById("a11y-toggle");
2614    this.menu = document.getElementById("accessibility-menu");
2615    if (!this.overlay || !this.toggleButton) {
2616      console.error("Accessibility UI elements not found");
2617      return;
2618    }
2619    this.setupEvents();
2620    this.isOpen = this.overlay.classList.contains("is-open");
2621    if (document.readyState === "complete" || document.readyState === "interactive") {
2622      setTimeout(() => this.updateUIFromState(), 100);
2623    } else {
2624      document.addEventListener("DOMContentLoaded", () => {
2625        setTimeout(() => this.updateUIFromState(), 100);
2626      });
2627    }
2628  }
2629  /**
2630   * Setup event handlers
2631   */
2632  setupEvents() {
2633    this.toggleButton.addEventListener("click", (e) => {
2634      e.preventDefault();
2635      this.toggle();
2636    });
2637    document.addEventListener("accessibility:profile:activated", (e) => {
2638      const { profileKey } = e.detail;
2639      this.updateProfileUI(profileKey, true);
2640      setTimeout(() => {
2641        this.updateFunctionsFromProfile(profileKey);
2642        this.syncUIWithActualState();
2643      }, 200);
2644    });
2645    document.addEventListener("accessibility:profile:deactivated", (e) => {
2646      const { profileKey } = e.detail;
2647      this.updateProfileUI(profileKey, false);
2648      this.resetFunctionsUI();
2649    });
2650    document.querySelectorAll("[data-profile]").forEach((profileBtn) => {
2651      profileBtn.addEventListener("click", (e) => {
2652        e.preventDefault();
2653        e.stopPropagation();
2654        const profileKey = profileBtn.getAttribute("data-profile");
2655        if (window.ModAccessibilityFunctions?.toggleProfile) {
2656          window.ModAccessibilityFunctions.toggleProfile(profileKey);
2657        }
2658      });
2659    });
2660    document.querySelectorAll("[data-function]").forEach((funcBtn) => {
2661      funcBtn.addEventListener("click", (e) => {
2662        const isLevel = e.target.matches("[data-level]") || e.target.closest("[data-level]");
2663        if (isLevel) return;
2664        const hasLevels = funcBtn.querySelector(".level-controls-horizontal") || funcBtn.querySelector("[data-level]");
2665        if (hasLevels) {
2666          e.preventDefault();
2667          e.stopPropagation();
2668          return;
2669        }
2670        e.preventDefault();
2671        e.stopPropagation();
2672        const funcName = funcBtn.getAttribute("data-function");
2673        this.toggleFunction(funcName, funcBtn);
2674      });
2675    });
2676    document.querySelectorAll("[data-level]").forEach((levelBtn) => {
2677      levelBtn.addEventListener("click", (e) => {
2678        e.preventDefault();
2679        e.stopPropagation();
2680        const funcName = levelBtn.closest("[data-function]")?.getAttribute("data-function");
2681        const level = parseInt(levelBtn.getAttribute("data-level"));
2682        if (funcName && !isNaN(level)) {
2683          this.setFunctionLevel(funcName, level, levelBtn);
2684        }
2685      });
2686    });
2687    const resetBtn = document.querySelector('[data-action="reset"]');
2688    if (resetBtn) {
2689      resetBtn.addEventListener("click", (e) => {
2690        e.preventDefault();
2691        this.resetAll();
2692      });
2693    }
2694    this.setupKeyboardShortcuts();
2695    document.addEventListener("click", (e) => {
2696      if (this.isOpen && !this.overlay.contains(e.target)) {
2697        this.close();
2698      }
2699    });
2700    document.addEventListener("keydown", (e) => {
2701      if (e.key === "Escape" && this.isOpen) {
2702        this.close();
2703      }
2704    });
2705  }
2706  /**
2707   * Toggle overlay visibility
2708   */
2709  toggle() {
2710    if (this.isOpen) {
2711      this.close();
2712    } else {
2713      this.open();
2714    }
2715  }
2716  /**
2717   * Open overlay
2718   */
2719  open() {
2720    if (!this.overlay) return;
2721    this.overlay.classList.remove("is-closed");
2722    this.overlay.classList.add("is-open");
2723    this.isOpen = true;
2724    if (this.menu) {
2725      this.menu.style.display = "block";
2726    }
2727  }
2728  /**
2729   * Close overlay
2730   */
2731  close() {
2732    if (!this.overlay) return;
2733    this.overlay.classList.remove("is-open");
2734    this.overlay.classList.add("is-closed");
2735    this.isOpen = false;
2736    if (this.menu) {
2737      this.menu.style.display = "none";
2738    }
2739  }
2740  /**
2741   * Setup keyboard shortcuts
2742   */
2743  setupKeyboardShortcuts() {
2744    document.addEventListener("keydown", (e) => {
2745      if (e.altKey && e.key === "a") {
2746        e.preventDefault();
2747        this.toggle();
2748      }
2749      if (e.altKey && e.key === "r") {
2750        e.preventDefault();
2751        this.resetAll();
2752      }
2753    });
2754  }
2755  /**
2756   * Toggle a profile (activate/deactivate)
2757   */
2758  toggleProfile(profileKey) {
2759    if (window.ModAccessibilityFunctions?.toggleProfile) {
2760      window.ModAccessibilityFunctions.toggleProfile(profileKey);
2761    }
2762  }
2763  /**
2764   * Update profile UI element
2765   */
2766  updateProfileUI(profileKey, isActive) {
2767    const profileElement = document.querySelector(`[data-profile="${profileKey}"]`);
2768    if (!profileElement) return;
2769    if (isActive) {
2770      profileElement.classList.add("is-active");
2771      profileElement.setAttribute("data-active", "true");
2772    } else {
2773      profileElement.classList.remove("is-active");
2774      profileElement.setAttribute("data-active", "false");
2775    }
2776    const checkbox = profileElement.querySelector('input[type="checkbox"]');
2777    if (checkbox) {
2778      checkbox.checked = isActive;
2779    }
2780    const controlToggle = profileElement.querySelector(".control-toggle");
2781    if (controlToggle) {
2782      controlToggle.setAttribute("data-active", isActive);
2783      if (isActive) {
2784        controlToggle.classList.add("is-active");
2785      } else {
2786        controlToggle.classList.remove("is-active");
2787      }
2788    }
2789    const toggleSwitch = profileElement.querySelector(".toggle-switch");
2790    if (toggleSwitch) {
2791      if (isActive) {
2792        toggleSwitch.classList.add("is-active");
2793      } else {
2794        toggleSwitch.classList.remove("is-active");
2795      }
2796    }
2797    const statusLabel = profileElement.querySelector(".toggle-status-label");
2798    if (statusLabel) {
2799      if (isActive) {
2800        statusLabel.classList.add("is-active");
2801      } else {
2802        statusLabel.classList.remove("is-active");
2803      }
2804    }
2805    if (isActive) {
2806      document.querySelectorAll("[data-profile]").forEach((otherProfile) => {
2807        if (otherProfile !== profileElement) {
2808          const otherKey = otherProfile.getAttribute("data-profile");
2809          if (otherKey !== profileKey) {
2810            otherProfile.classList.remove("is-active");
2811            otherProfile.setAttribute("data-active", "false");
2812            const otherCheckbox = otherProfile.querySelector('input[type="checkbox"]');
2813            if (otherCheckbox) otherCheckbox.checked = false;
2814            const otherStatusLabel = otherProfile.querySelector(".toggle-status-label");
2815            if (otherStatusLabel) otherStatusLabel.classList.remove("is-active");
2816          }
2817        }
2818      });
2819    }
2820  }
2821  /**
2822   * Toggle a function
2823   */
2824  toggleFunction(funcName, button) {
2825    const isActive = button.getAttribute("data-active") === "true";
2826    const newState = !isActive;
2827    if (newState && (funcName === "readable-font" || funcName === "dyslexic-font")) {
2828      const otherFontName = funcName === "readable-font" ? "dyslexic-font" : "readable-font";
2829      const otherButton = document.querySelector(`[data-function="${otherFontName}"]`);
2830      if (otherButton && otherButton.getAttribute("data-active") === "true") {
2831        this.toggleFunction(otherFontName, otherButton);
2832      }
2833    }
2834    if (newState && funcName === "invert-colors") {
2835      const brightnessButtons = document.querySelectorAll('[data-function="brightness"] [data-level]');
2836      brightnessButtons.forEach((btn) => {
2837        btn.classList.remove("active");
2838        btn.setAttribute("aria-pressed", "false");
2839      });
2840      const brightnessContainer = document.querySelector('[data-function="brightness"]');
2841      if (brightnessContainer) {
2842        brightnessContainer.setAttribute("data-active", "false");
2843        brightnessContainer.classList.remove("is-active");
2844      }
2845      const contrastButtons = document.querySelectorAll('[data-function="contrast"] [data-level]');
2846      contrastButtons.forEach((btn) => {
2847        btn.classList.remove("active");
2848        btn.setAttribute("aria-pressed", "false");
2849      });
2850      const contrastContainer = document.querySelector('[data-function="contrast"]');
2851      if (contrastContainer) {
2852        contrastContainer.setAttribute("data-active", "false");
2853        contrastContainer.classList.remove("is-active");
2854      }
2855    }
2856    button.setAttribute("data-active", newState);
2857    button.classList.toggle("is-active", newState);
2858    const checkbox = button.querySelector('input[type="checkbox"]');
2859    if (checkbox) {
2860      checkbox.checked = newState;
2861    }
2862    const controlToggle = button.querySelector(".control-toggle");
2863    if (controlToggle) {
2864      controlToggle.setAttribute("data-active", newState);
2865      if (newState) {
2866        controlToggle.classList.add("is-active");
2867      } else {
2868        controlToggle.classList.remove("is-active");
2869      }
2870    }
2871    const toggleSwitch = button.querySelector(".toggle-switch");
2872    if (toggleSwitch) {
2873      if (newState) {
2874        toggleSwitch.classList.add("is-active");
2875      } else {
2876        toggleSwitch.classList.remove("is-active");
2877      }
2878    }
2879    const statusLabel = button.querySelector(".toggle-status-label");
2880    if (statusLabel) {
2881      if (newState) {
2882        statusLabel.classList.add("is-active");
2883      } else {
2884        statusLabel.classList.remove("is-active");
2885      }
2886    }
2887    if (window.ModAccessibilityFunctions?.setFunctionState) {
2888      window.ModAccessibilityFunctions.setFunctionState(funcName, newState);
2889    }
2890  }
2891  /**
2892   * Set function level
2893   */
2894  setFunctionLevel(funcName, level, button) {
2895    if ((funcName === "brightness" || funcName === "contrast") && level > 0) {
2896      const invertColorsButton = document.querySelector('[data-function="invert-colors"]');
2897      if (invertColorsButton && invertColorsButton.getAttribute("data-active") === "true") {
2898        this.toggleFunction("invert-colors", invertColorsButton);
2899      }
2900    }
2901    const container = button.closest("[data-function]");
2902    if (container) {
2903      const allLevelButtons = container.querySelectorAll("[data-level]");
2904      const currentActive = container.querySelector("[data-level].active");
2905      const currentLevel = currentActive ? parseInt(currentActive.getAttribute("data-level")) : 0;
2906      allLevelButtons.forEach((btn) => {
2907        btn.classList.remove("active");
2908        btn.setAttribute("aria-pressed", "false");
2909      });
2910      if (currentLevel === level) {
2911        level = 0;
2912      } else {
2913        button.classList.add("active");
2914        button.setAttribute("aria-pressed", "true");
2915      }
2916    }
2917    if (window.ModAccessibilityFunctions?.setFunctionState) {
2918      window.ModAccessibilityFunctions.setFunctionState(funcName, level);
2919    }
2920  }
2921  /**
2922   * Reset all settings
2923   */
2924  resetAll() {
2925    document.querySelectorAll("[data-profile], [data-function]").forEach((btn) => {
2926      btn.classList.remove("is-active");
2927      btn.setAttribute("data-active", "false");
2928      const statusLabel = btn.querySelector(".toggle-status-label");
2929      if (statusLabel) {
2930        statusLabel.classList.remove("is-active");
2931      }
2932    });
2933    document.querySelectorAll('input[type="checkbox"]').forEach((checkbox) => {
2934      checkbox.checked = false;
2935    });
2936    document.querySelectorAll("[data-level]").forEach((btn) => {
2937      btn.classList.remove("active");
2938    });
2939    if (window.ModAccessibilityFunctions?.resetAll) {
2940      window.ModAccessibilityFunctions.resetAll();
2941    }
2942  }
2943  /**
2944   * Sync UI with actual state from state manager
2945   */
2946  syncUIWithActualState() {
2947    if (this.stateManager) {
2948      const state = this.stateManager.getState();
2949      if (state && state.functions) {
2950        Object.entries(state.functions).forEach(([funcName, value]) => {
2951          this.updateFunctionUI(funcName, value);
2952        });
2953      }
2954    }
2955  }
2956  /**
2957   * Update a single function's UI
2958   */
2959  updateFunctionUI(funcName, value) {
2960    const funcElement = document.querySelector(`[data-function="${funcName}"]`);
2961    if (funcElement) {
2962      const isActive = value === true || typeof value === "number" && value > 0;
2963      funcElement.setAttribute("data-active", isActive ? "true" : "false");
2964      if (isActive) {
2965        funcElement.classList.add("is-active");
2966      } else {
2967        funcElement.classList.remove("is-active");
2968      }
2969      const checkbox = funcElement.querySelector('input[type="checkbox"]');
2970      if (checkbox) {
2971        checkbox.checked = isActive;
2972      }
2973      const controlToggle = funcElement.querySelector(".control-toggle");
2974      if (controlToggle) {
2975        controlToggle.setAttribute("data-active", isActive ? "true" : "false");
2976        if (isActive) {
2977          controlToggle.classList.add("is-active");
2978        } else {
2979          controlToggle.classList.remove("is-active");
2980        }
2981      }
2982      const toggleSwitch = funcElement.querySelector(".toggle-switch");
2983      if (toggleSwitch) {
2984        if (isActive) {
2985          toggleSwitch.classList.add("is-active");
2986        } else {
2987          toggleSwitch.classList.remove("is-active");
2988        }
2989      }
2990      const statusLabel = funcElement.querySelector(".toggle-status-label");
2991      if (statusLabel) {
2992        if (isActive) {
2993          statusLabel.classList.add("is-active");
2994        } else {
2995          statusLabel.classList.remove("is-active");
2996        }
2997      }
2998      if (typeof value === "number") {
2999        funcElement.querySelectorAll("[data-level]").forEach((levelBtn) => {
3000          const level = parseInt(levelBtn.getAttribute("data-level"));
3001          if (value > 0 && level === value) {
3002            levelBtn.classList.add("active");
3003            levelBtn.setAttribute("aria-pressed", "true");
3004          } else {
3005            levelBtn.classList.remove("active");
3006            levelBtn.setAttribute("aria-pressed", "false");
3007          }
3008        });
3009      }
3010    }
3011  }
3012  /**
3013   * Update function UI elements based on profile settings
3014   */
3015  updateFunctionsFromProfile(profileKey) {
3016    if (this.stateManager) {
3017      const state = this.stateManager.getState();
3018      if (state.functions) {
3019        Object.entries(state.functions).forEach(([funcName, value]) => {
3020          this.updateFunctionUI(funcName, value);
3021        });
3022      }
3023    }
3024  }
3025  /**
3026   * Reset all function UI elements
3027   */
3028  resetFunctionsUI() {
3029    document.querySelectorAll("[data-function]").forEach((funcElement) => {
3030      funcElement.classList.remove("is-active");
3031      funcElement.setAttribute("data-active", "false");
3032      const toggle = funcElement.querySelector(".control-toggle");
3033      if (toggle) {
3034        toggle.classList.remove("is-active");
3035        toggle.setAttribute("data-active", "false");
3036      }
3037      const checkbox = funcElement.querySelector('input[type="checkbox"]');
3038      if (checkbox) {
3039        checkbox.checked = false;
3040      }
3041      const statusLabel = funcElement.querySelector(".toggle-status-label");
3042      if (statusLabel) {
3043        statusLabel.classList.remove("is-active");
3044      }
3045      const levelIndicator = funcElement.querySelector(".level-indicator");
3046      if (levelIndicator) {
3047        levelIndicator.textContent = "0";
3048      }
3049      funcElement.querySelectorAll("[data-level]").forEach((levelBtn) => {
3050        levelBtn.classList.remove("active");
3051        levelBtn.setAttribute("aria-pressed", "false");
3052      });
3053    });
3054  }
3055  /**
3056   * Update UI from saved state on initialization
3057   */
3058  updateUIFromState() {
3059    if (this.stateManager) {
3060      const state = this.stateManager.getState();
3061      if (state.profiles) {
3062        const activeProfiles = Object.entries(state.profiles).filter(([key, active]) => active);
3063        if (activeProfiles.length > 1) {
3064          console.warn("Multiple profiles were active, keeping only the first one:", activeProfiles[0][0]);
3065          const firstActiveKey = activeProfiles[0][0];
3066          Object.entries(state.profiles).forEach(([profileKey, wasActive]) => {
3067            const profileBtn = document.querySelector(`[data-profile="${profileKey}"]`);
3068            if (profileBtn) {
3069              const shouldBeActive = profileKey === firstActiveKey;
3070              if (shouldBeActive) {
3071                profileBtn.classList.add("is-active");
3072                profileBtn.setAttribute("data-active", "true");
3073              } else {
3074                profileBtn.classList.remove("is-active");
3075                profileBtn.setAttribute("data-active", "false");
3076              }
3077              const checkbox = profileBtn.querySelector('input[type="checkbox"]');
3078              if (checkbox) {
3079                checkbox.checked = shouldBeActive;
3080              }
3081              const statusLabel = profileBtn.querySelector(".toggle-status-label");
3082              if (statusLabel) {
3083                if (shouldBeActive) {
3084                  statusLabel.classList.add("is-active");
3085                } else {
3086                  statusLabel.classList.remove("is-active");
3087                }
3088              }
3089            }
3090          });
3091          Object.keys(state.profiles).forEach((key) => {
3092            const shouldBeActive = key === firstActiveKey;
3093            if (state.profiles[key] !== shouldBeActive) {
3094              this.stateManager.setProfileState(key, shouldBeActive);
3095            }
3096          });
3097        } else {
3098          Object.entries(state.profiles).forEach(([profileKey, active]) => {
3099            const profileBtn = document.querySelector(`[data-profile="${profileKey}"]`);
3100            if (profileBtn) {
3101              if (active) {
3102                profileBtn.classList.add("is-active");
3103                profileBtn.setAttribute("data-active", "true");
3104              } else {
3105                profileBtn.classList.remove("is-active");
3106                profileBtn.setAttribute("data-active", "false");
3107              }
3108              const checkbox = profileBtn.querySelector('input[type="checkbox"]');
3109              if (checkbox) {
3110                checkbox.checked = active;
3111              }
3112              const statusLabel = profileBtn.querySelector(".toggle-status-label");
3113              if (statusLabel) {
3114                if (active) {
3115                  statusLabel.classList.add("is-active");
3116                } else {
3117                  statusLabel.classList.remove("is-active");
3118                }
3119              }
3120            }
3121          });
3122        }
3123      }
3124      if (state.functions) {
3125        Object.entries(state.functions).forEach(([funcName, value]) => {
3126          this.updateFunctionUI(funcName, value);
3127        });
3128      }
3129    }
3130  }
3131}
3132const simpleUI = new SimpleUI();
3133class AccessibilityModule {
3134  constructor() {
3135    this.version = MODULE_CONFIG.version;
3136    this.modules = /* @__PURE__ */ new Map();
3137    this.ensureWrapper();
3138    this.stateManager = stateManager;
3139    this.storage = storage;
3140    this.ui = simpleUI;
3141    this.modules.set("simple", simpleModules);
3142    this.modules.set("profiles", profilesManager);
3143    this.advancedMagnifier = new AdvancedMagnifier();
3144    this.modules.set("advancedMagnifier", this.advancedMagnifier);
3145    profilesManager.stateManager = stateManager;
3146    stateManager.state = storage.loadSettings();
3147    this.setupEventListeners();
3148    simpleUI.stateManager = stateManager;
3149    simpleUI.init();
3150    this.modules.set("ui", simpleUI);
3151    this.createGlobalInterface();
3152    this.loadSettings();
3153    setTimeout(() => {
3154      if (simpleUI && simpleUI.updateUIFromState) {
3155        simpleUI.stateManager = stateManager;
3156        simpleUI.updateUIFromState();
3157      }
3158    }, 100);
3159  }
3160  /**
3161   * Ensure wrapper element exists
3162   * Creates a wrapper around body content if it doesn't exist
3163   */
3164  ensureWrapper() {
3165    let wrapper = document.getElementById("site-content-wrapper");
3166    if (!wrapper) {
3167      wrapper = document.createElement("div");
3168      wrapper.id = "site-content-wrapper";
3169      const bodyChildren = Array.from(document.body.children);
3170      bodyChildren.forEach((child) => {
3171        if (child.tagName !== "SCRIPT" && child.tagName !== "STYLE" && child.id !== "a11y-box" && !child.classList.contains("a11y-box")) {
3172          wrapper.appendChild(child);
3173        }
3174      });
3175      document.body.insertBefore(wrapper, document.body.firstChild);
3176    }
3177  }
3178  /**
3179   * Set up event listeners
3180   */
3181  setupEventListeners() {
3182    stateManager.addEventListener(EVENTS.FUNCTION_CHANGED, (event) => {
3183      this.handleFunctionChange(event.detail);
3184    });
3185    stateManager.addEventListener(EVENTS.PROFILE_CHANGED, (event) => {
3186      this.handleProfileChange(event.detail);
3187    });
3188    stateManager.addEventListener(EVENTS.SETTINGS_RESET, () => {
3189      this.handleSettingsReset();
3190    });
3191  }
3192  /**
3193   * Handle function state changes
3194   * @param {Object} detail - Change details
3195   */
3196  handleFunctionChange(detail) {
3197    const { functionName, value } = detail;
3198    this.applyFunctionCSS(functionName, value);
3199  }
3200  /**
3201   * Handle profile state changes
3202   * @param {Object} detail - Change details
3203   */
3204  handleProfileChange(detail) {
3205    const { profileKey, active, functions } = detail;
3206    if (active && functions) {
3207      Object.entries(functions).forEach(([functionName, value]) => {
3208        this.applyFunctionCSS(functionName, value);
3209      });
3210    }
3211  }
3212  /**
3213   * Handle settings reset
3214   */
3215  handleSettingsReset() {
3216    const styleElement = document.getElementById("a11y-dynamic-styles");
3217    if (styleElement) {
3218      styleElement.textContent = "";
3219    }
3220    try {
3221      simpleModules.reset();
3222    } catch (e) {
3223      console.error(`Error resetting modules: ${e.message}`);
3224    }
3225  }
3226  /**
3227   * Apply CSS for a specific function
3228   * @param {string} functionName - Function name
3229   * @param {*} value - Function value
3230   */
3231  applyFunctionCSS(functionName, value) {
3232    simpleModules.apply(functionName, value);
3233  }
3234  /**
3235   * Create global interface for backward compatibility
3236   */
3237  createGlobalInterface() {
3238    if (typeof window === "undefined") return;
3239    window.ModAccessibilityFunctions = {
3240      // State management
3241      getState: () => stateManager.getState(),
3242      getFunctionState: (name) => stateManager.getFunctionState(name),
3243      setFunctionState: (name, value) => stateManager.setFunctionState(name, value),
3244      // Profile management
3245      activateProfile: (profileKey) => profilesManager.activateProfile(profileKey),
3246      deactivateProfile: (profileKey) => profilesManager.deactivateProfile(profileKey),
3247      toggleProfile: (profileKey) => profilesManager.toggleProfile(profileKey),
3248      getActiveProfile: () => stateManager.getActiveProfile(),
3249      // Settings
3250      resetAll: () => stateManager.resetAll(),
3251      loadSettings: () => storage.loadSettings(),
3252      saveSettings: (settings) => storage.saveSettings(settings),
3253      // Module reference
3254      module: this,
3255      version: this.version,
3256      // Advanced magnifier reference
3257      advancedMagnifier: this.advancedMagnifier
3258    };
3259  }
3260  /**
3261   * Load saved settings
3262   */
3263  loadSettings() {
3264    const settings = stateManager.state;
3265    const activeProfile = stateManager.getActiveProfile();
3266    if (settings.functions) {
3267      Object.entries(settings.functions).forEach(([name, value]) => {
3268        if (value !== void 0 && value !== null && value !== 0 && value !== false) {
3269          if (!activeProfile && settings.profiles) {
3270            const anyProfileActive = Object.values(settings.profiles).some((p) => p === true);
3271            if (!anyProfileActive) {
3272              return;
3273            }
3274          }
3275          this.applyFunctionCSS(name, value);
3276        }
3277      });
3278    }
3279    return settings;
3280  }
3281  /**
3282   * Save current settings
3283   */
3284  saveSettings() {
3285    const state = stateManager.getState();
3286    storage.saveSettings(state);
3287  }
3288}
3289const accessibilityModule = new AccessibilityModule();
3290if (typeof window !== "undefined") {
3291  window.ModAccessibilityFunctions = window.ModAccessibilityFunctions || accessibilityModule;
3292  window.Accessibility = accessibilityModule;
3293}
3294export {
3295  accessibilityModule as default,
3296  profilesManager,
3297  simpleModules,
3298  stateManager,
3299  storage,
3300  simpleUI as ui
3301};
3302//# sourceMappingURL=accessibility.js.map

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.