1// Create an empty object to hold accessibility-related methods and data. 2const Accessibility = {}; 3 4// Select the root element of the document. 5const content = document.documentElement; 6 7// Define profiles with settings for specific accessibility needs. 8const profiles = { 9 "p-motor-impaired": { 10 pauseAnimations: 1, 11 linkNotices: 1, 12 }, 13 "p-blind": { 14 screenReader: 1, 15 }, 16 "p-color-blind": { 17 saturation: 2, 18 }, 19 "p-dyslexia": { 20 dyslexiaFont: 1, 21 pauseAnimations: 1, 22 }, 23 "p-low-vision": { 24 biggerText: 1, 25 dyslexiaFont: 2, 26 pauseAnimations: 1, 27 saturation: 2, 28 cursor: 1, 29 linkNotices: 1, 30 }, 31 "p-cognitive-learning": { 32 biggerText: 1, 33 pauseAnimations: 1, 34 linkNotices: 1, 35 cursor: 3, 36 }, 37 "p-seizure-epileptic": { 38 pauseAnimations: 1, 39 saturation: 1, 40 }, 41 "p-adhd": { 42 pauseAnimations: 1, 43 saturation: 1, 44 cursor: 2, 45 }, 46}; 47 48// Key used to store state in localStorage. 49const stateKey = "accessibilityState"; 50 51// Load saved state or initialize default state. 52let state = JSON.parse(localStorage.getItem(stateKey)) || { 53 activeProfile: null, 54 contrast: 0, 55 biggerText: 0, 56 cursor: 0, 57 pauseAnimations: 0, 58 screenReader: 0, 59 highlightHeadings: 0, 60 highlightLinks: 0, 61 saturation: 0, 62 dyslexiaFont: 0, 63 textSpacing: 0, 64 readMode: 0, 65 textAlign: 0, 66 lineHeight: 0, 67 muteSounds: 0, 68 hideImages: 0, 69 linkNotices: 0, 70 dictionary: 0, 71}; 72 73// Map feature names to their initialization functions for dynamic invocation. 74const featureFunctions = { 75 contrast: initializeContrast, 76 biggerText: initializeBiggerText, 77 cursor: initializeCursor, 78 pauseAnimations: initializePauseAnimations, 79 screenReader: initializeScreenReader, 80 highlightHeadings: initializeHighlightHeadings, 81 highlightLinks: initializeHighlightLinks, 82 saturation: initializeSaturation, 83 dyslexiaFont: initializeDyslexiaFont, 84 textSpacing: initializeTextSpacing, 85 readMode: initializeReadMode, 86 textAlign: initializeTextAlign, 87 lineHeight: initializeLineHeight, 88 muteSounds: initializeMuteSounds, 89 hideImages: initializeHideImages, 90 linkNotices: initializeLinkNotices, 91 dictionary: initializeDictionary, 92}; 93 94// Save the current state to localStorage. 95function saveStateToLocalStorage() { 96 localStorage.setItem("accessibilityState", JSON.stringify(state)); 97} 98 99// Apply a specific function based on a key and value. 100function applyFunctionByKey(key, value) { 101 if (featureFunctions.hasOwnProperty(key)) { 102 try { 103 featureFunctions[key](value); 104 } catch (error) { 105 console.error(`Error occurred while applying function for key: '${key}'.`, error); 106 } 107 } else { 108 console.warn(`Invalid key: '${key}'. This key is not supported by applyFunctionByKey. 109 Possible Causes: 110 1. Typo in the key name. 111 2. New functionality added but not included in the featureFunctions mapping. 112 Action Steps: 113 - Verify the key name in the call. 114 - If this is a new feature, update the featureFunctions mapping accordingly.`); 115 } 116} 117 118// Activate a specific profile by applying its settings. 119function activateProfile(profileId) { 120 const settings = profiles[profileId]; 121 122 // Apply each setting in the profile. 123 Object.entries(settings).forEach(([key, value]) => { 124 state[key] = value; 125 126 applyFunctionByKey(key, value); 127 }); 128 129 // Set the active profile. 130 state.activeProfile = profileId; 131 saveStateToLocalStorage(); 132} 133 134// Deactivate a specific profile by resetting its settings. 135function deactivateProfile(profileId) { 136 const settings = profiles[profileId]; 137 138 Object.keys(settings).forEach((key) => { 139 if (state[key] > 0) { 140 switch (key) { 141 case 'contrast': 142 Accessibility.resetContrast(); 143 break; 144 case 'saturation': 145 Accessibility.resetSaturation(); 146 break; 147 case 'screenReader': 148 Accessibility.resetScreenReader(); 149 break; 150 case 'highlightLinks': 151 Accessibility.resetHighlightToLinks(); 152 break; 153 case 'highlightHeadings': 154 Accessibility.resetHighlightToHeadings(); 155 break; 156 case 'biggerText': 157 Accessibility.resetBiggerText(); 158 break; 159 case 'textSpacing': 160 Accessibility.resetTextSpacing(); 161 break; 162 case 'hideImages': 163 Accessibility.resetHideImages(); 164 break; 165 case 'dyslexiaFont': 166 Accessibility.resetDyslexiaFont(); 167 break;
168 case 'cursor': 169 Accessibility.resetCursorState(); 170 break; 171 case 'lineHeight': 172 Accessibility.resetLineHeight(); 173 break; 174 case 'textAlign': 175 Accessibility.resetTextAlign(); 176 break; 177 case 'pauseAnimations': 178 Accessibility.resetPauseAnimations(); 179 break; 180 case 'readMode': 181 Accessibility.resetReadMode(); 182 break; 183 case 'muteSounds': 184 Accessibility.resetMuteSounds(); 185 break; 186 case 'linkNotices': 187 Accessibility.resetLinkNotices(); 188 break; 189 case 'dictionary': 190 Accessibility.resetDictionary(); 191 break; 192 default: 193 console.warn(`No reset function defined for ${key}`); 194 } 195 196 state[key] = 0; 197 } 198 }); 199 200 // Clear the active profile. 201 state.activeProfile = null; 202 203 saveStateToLocalStorage(); 204 205 // Update UI button state. 206 const button = document.getElementById(profileId); 207 if (button) { 208 button.setAttribute("aria-pressed", false); 209 button.classList.remove("aw-button--active"); 210 } 211} 212 213// Toggle a profile on or off. 214function toggleProfile(profileId) { 215 if (state.activeProfile === profileId) { 216 deactivateProfile(profileId); 217 } else { 218 if (state.activeProfile) deactivateProfile(state.activeProfile); 219 activateProfile(profileId); 220 } 221} 222 223// Initialize profile buttons and reset functionality. 224function initializeProfiles() { 225 document.querySelectorAll(".aw-button--profile").forEach((button) => { 226 button.addEventListener("click", () => { 227 const profileId = button.id; 228 toggleProfile(profileId); 229 230 // Update the button states based on the active profile. 231 document.querySelectorAll(".aw-button--profile").forEach((btn) => { 232 const isActive = state.activeProfile === btn.id; 233 btn.setAttribute("aria-pressed", isActive); 234 btn.classList.toggle("aw-button--active", isActive); 235 }); 236 }); 237 }); 238 239 // Add event listener to the reset button. 240 const resetButton = document.getElementById("aw_reset"); 241 resetButton.addEventListener("click", () => { 242 if (state.activeProfile) deactivateProfile(state.activeProfile); 243 244 // Reset all profile button states. 245 document.querySelectorAll(".aw-button--profile").forEach((btn) => { 246 btn.setAttribute("aria-pressed", false); 247 btn.classList.remove("aw-button--active"); 248 }); 249 }); 250} 251 252// Initialize profiles and apply saved settings on page load. 253document.addEventListener("DOMContentLoaded", () => { 254 initializeProfiles(); 255 256 if (state.activeProfile) { 257 activateProfile(state.activeProfile); 258 259 document.querySelectorAll(".aw-button--profile").forEach((btn) => { 260 const isActive = state.activeProfile === btn.id; 261 btn.setAttribute("aria-pressed", isActive); 262 btn.classList.toggle("aw-button--active", isActive); 263 }); 264 } 265}); 266 267function saveStateToLocalStorage() { 268 localStorage.setItem(stateKey, JSON.stringify(state)); 269} 270 271// Modal Window 272function initializeModal({ 273 modalId, 274 toggleButtonId, 275 closeButtonId 276}) { 277 const modal = document.getElementById(modalId); 278 const toggleButton = document.getElementById(toggleButtonId); 279 const closeButton = document.getElementById(closeButtonId); 280 281 document.addEventListener('keydown', (e) => { 282 if (e.ctrlKey && e.key === 'u') { 283 e.preventDefault(); 284 toggleModal(); 285 } 286 }); 287 288 toggleButton.addEventListener('click', () => { 289 toggleModal(); 290 }); 291 292 closeButton.addEventListener('click', () => { 293 toggleModal(); 294 }); 295 296 document.addEventListener('click', (e) => { 297 const isInsideModal = modal.contains(e.target); 298 const isToggleButton = toggleButton.contains(e.target); 299 300 if (!isInsideModal && !isToggleButton && !modal.classList.contains('aw-modal-hidden')) { 301 toggleModal(); 302 } 303 }); 304 305 function toggleModal() { 306 const isHidden = modal.classList.contains('aw-modal-hidden'); 307 const focusableElements = modal.querySelectorAll( 308 'button, [href], input, select, textarea, [tabindex]:not
308([tabindex="-1"]):not(svg):not(svg *)' 309 ); 310 const firstFocusableElement = focusableElements[0]; 311 const lastFocusableElement = focusableElements[focusableElements.length - 1]; 312 const closeButton = document.getElementById('aw-close-modal'); // Use the correct close button ID. 313 314 if (isHidden) { 315 modal.classList.remove('aw-modal-hidden'); 316 modal.setAttribute('aria-hidden', 'false'); 317 toggleButton.setAttribute('aria-expanded', 'true'); 318 modal.setAttribute('aria-label', 'Accessibility Menu opened'); 319 if (closeButton) { 320 closeButton.focus(); // Focus the close button when the modal is opened. 321 } 322 } else { 323 modal.classList.add('aw-modal-hidden'); 324 modal.setAttribute('aria-hidden', 'true'); 325 toggleButton.setAttribute('aria-expanded', 'false'); 326 modal.setAttribute('aria-label', 'Accessibility Menu closed'); 327 toggleButton.focus(); // Return focus to the toggle button when the modal is closed. 328 } 329 330 modal.addEventListener('keydown', (e) => { 331 if (e.key === 'Tab') { 332 if (e.shiftKey) { // Shift + Tab 333 if (document.activeElement === firstFocusableElement) { 334 e.preventDefault(); 335 lastFocusableElement.focus(); 336 } 337 } else { // Tab 338 if (document.activeElement === lastFocusableElement) { 339 e.preventDefault(); 340 firstFocusableElement.focus(); 341 } 342 } 343 } 344 345 if (e.key === 'Escape') { // Close the modal on Escape key press. 346 modal.classList.add('aw-modal-hidden'); 347 modal.setAttribute('aria-hidden', 'true'); 348 toggleButton.setAttribute('aria-expanded', 'false'); 349 modal.setAttribute('aria-label', 'Accessibility Menu closed'); 350 toggleButton.focus(); 351 } 352 }); 353 } 354} 355 356// Toggle Active Class and Press Arial 357function toggleActiveClass(buttonId, isActive) { 358 const button = document.getElementById(buttonId); 359 if (isActive) { 360 button.classList.add('aw-button--active'); 361 button.setAttribute('aria-pressed', 'true'); 362 } else { 363 button.classList.remove('aw-button--active'); 364 button.setAttribute('aria-pressed', 'false'); 365 } 366} 367 368// Toggle Aria Label 369function updateAriaLabel(buttonId, ariaLabels, state) { 370 const button = document.getElementById(buttonId); 371 button.setAttribute('aria-label', ariaLabels[state]); 372} 373 374// Toggle States 375function toggleState(stateKey, maxStates) { 376 if (maxStates <= 1) { 377 console.warn(`Invalid maxStates value for ${stateKey}.`); 378 return state[stateKey]; 379 } 380 state[stateKey] = (state[stateKey] + 1) % maxStates; 381 saveStateToLocalStorage(); 382 return state[stateKey]; 383} 384 385// Update Button State 386function updateButtonState(buttonId, isActive, ariaLabels, currentState, stateLabelId = null, stateLabels = null) { 387 const button = document.getElementById(buttonId); 388 if (button) { 389 // Update active class and aria-pressed attribute 390 button.classList.toggle('aw-button--active', isActive); 391 button.setAttribute('aria-pressed', isActive); 392 button.setAttribute('aria-label', ariaLabels[currentState]); 393 } 394 395 // Optionally update the state label text if provided 396 if (stateLabelId && stateLabels) { 397 const stateLabel = document.getElementById(stateLabelId); 398 if (stateLabel) { 399 stateLabel.textContent = stateLabels[currentState]; 400 } 401 } 402} 403 404// Initialize the screen reader functionality. 405function initializeScreenReader(savedState = 0) { 406 // Check if the browser supports speech synthesis. 407 if (!window.speechSynthesis) { 408 console.warn('Speech synthesis is not supported in this browser.'); 409 return; 410 } 411 412 // Attach methods to the Accessibility object for external access. 413 Accessibility.enableScreenReader = enableScreenReader; 414 Accessibility.disableScreenReader = disableScreenReader; 415 Accessibility.resetScreenReader = resetScreenReader; 416 417 // Define constants for the screen reader functionality. 418 const screenReaderButton = document.getElementById('screen-reader'); 419 const screenReaderSpeeds = [0, 1, 1.2, 0.75]; // Speed options for the screen reader. 420 const labels = [ 421 krakenAccessibilityTranslations.screenReader, 422 krakenAccessibilityTranslations.normalReading, 423 krakenAccessibilityTranslations.fastReading, 424 krakenAccessibilityTranslations.slowReading 425 ]; 426 const ariaLabels = [ 427 krakenAccessibilityTranslations.screenReaderDescription, 428 krakenAccessibilityTranslations.normalReadingActivated, 429 krakenAccessibilityTranslations.fastReadingActivated, 430 krakenAccessibilityTranslations.slowReadingActivated 431 ]; 432 const focusableSelectors = ` 433 [aria-label], [aria-labelledby], [aria-describedby], 434 h1, h2, h3, h4, h5, h6, 435 p, strong, ul, li, blockquote, address, 436 header, nav, main, footer, article, section, aside, span, 437 a, button, input, textarea, select, img 438 `; // Elements the screen reader can focus on. 439 const landmarks = ['header', 'nav', 'main', 'footer', 'article', 'section', 'aside']; // Landmark elements. 440 const speechSynthesis = window.speechSynthesis; 441 const utterance = new SpeechSynthesisUtterance(); 442 443 // Initialize the screen reader state if saved. 444 if (savedState > 0) { 445 updateButtonState( 446 'screen-reader', // Button ID 447 true, // Is Active 448 ariaLabels, // ARIA Labels 449 savedState, // Current State 450 'screen-reader-state', // State Label ID 451 labels // State Labels 452 ); 453 454 enableScreenReader(screenReaderSpeeds[savedState]); // Enable screen reader with the saved speed. 455 } 456 457 458 // Add event listener to the screen reader button. 459 screenReaderButton.addEventListener('click', () => { 460 const currentState = toggleState('screenReader', labels.length); 461 updateButtonState( 462 'screen-reader', // Button ID 463 currentState > 0, // Is Active 464 ariaLabels, // ARIA Labels 465 currentState, // Current State 466 'screen-reader-state', // Optional State Label ID 467 labels // Optional State Labels 468 ); 469 470 if (currentState === 0) { 471 disableScreenReader(); 472 } else { 473 enableScreenReader(screenReaderSpeeds[currentState]); 474 } 475 476 saveStateToLocalStorage(); // Save the current state. 477 }); 478 479 // Enable the screen reader and set the reading speed. 480 function enableScreenReader(speed) { 481 disableScreenReader(); // Cancel any ongoing speech synthesis. 482 utterance.rate = speed; 483 484 // Add event listeners for focus and click events. 485 document.addEventListener('focusin', handleFocus); 486 document.addEventListener('click', handleClick); 487 document.addEventListener('touchstart', handleClick); 488 } 489 490 // Disable the screen reader and remove event listeners. 491 function disableScreenReader() { 492 speechSynthesis.cancel(); // Stop any ongoing speech.
493 resetHighlighting(); // Remove any element highlighting. 494 document.removeEventListener('focusin', handleFocus); 495 document.removeEventListener('click', handleClick); 496 document.removeEventListener('touchstart', handleClick); 497 } 498 499 // Reset the screen reader to its initial state. 500 function resetScreenReader() { 501 disableScreenReader(); 502 state.screenReader = 0; 503 document.getElementById('screen-reader-state').textContent = labels[0]; 504 toggleActiveClass('screen-reader', false); 505 saveStateToLocalStorage(); 506 } 507 508 // Handle focus events and announce focused elements. 509 function handleFocus(event) { 510 const element = event.target.closest(focusableSelectors); 511 if (element) { 512 announceElement(element); 513 } 514 } 515 516 // Handle click and touch events to announce elements. 517 function handleClick(event) { 518 const element = event.target.closest(focusableSelectors); 519 const landmark = event.target.closest(landmarks.join(',')); 520 521 if (landmark && landmark === element) { 522 announceElement(landmark); 523 } else if (element) { 524 announceElement(element); 525 } 526 } 527 528 // Announce a given element using speech synthesis. 529 function announceElement(element) { 530 speechSynthesis.cancel(); // Stop ongoing announcements. 531 resetHighlighting(); // Clear previous highlights. 532 highlightElement(element); // Highlight the current element. 533 534 const tagName = element.tagName.toLowerCase(); 535 const ariaLabel = element.getAttribute('aria-label'); 536 const ariaLabelledBy = element.getAttribute('aria-labelledby'); 537 const ariaDescribedBy = element.getAttribute('aria-describedby'); 538 539 const labelText = ariaLabel || (ariaLabelledBy && document.getElementById(ariaLabelledBy)?.textContent.trim()) || ''; 540 const descriptionText = ariaDescribedBy && document.getElementById(ariaDescribedBy)?.textContent.trim(); 541 542 let announcement = ''; 543 544 // Determine the announcement text based on the element type. 545 if (landmarks.includes(tagName)) { 546 announcement = `${labelText ? labelText + ', ' : ''}${tagName} landmark`; 547 } else if (/^h[1-6]$/.test(tagName)) { 548 announcement = `${element.textContent.trim()}, header`; 549 } else if (tagName === 'a') { 550 announcement = `${labelText ? labelText + ', ' : ''}${element.textContent.trim()}, link`; 551 } else if (tagName === 'button') { 552 announcement = `${labelText ? labelText + ', ' : ''}${element.textContent.trim()}, button`; 553 } else if (['input', 'textarea', 'select'].includes(tagName)) { 554 announcement = `${labelText || 'Field'}, ${getFieldType(element)}${descriptionText ? ', ' + descriptionText : ''}`; 555 } else if (tagName === 'img') { 556 const altText = element.getAttribute('alt'); 557 announcement = altText ? altText.trim() : 'Image without description'; 558 } else { 559 announcement = element.textContent.trim(); 560 } 561 562 // Speak the announcement text if available. 563 if (announcement) { 564 utterance.text = announcement; 565 speechSynthesis.speak(utterance); 566 } 567 } 568 569 // Determine the type of input field for announcements. 570 function getFieldType(element) { 571 const type = element.getAttribute('type'); 572 if (element.tagName.toLowerCase() === 'textarea') return 'textarea field'; 573 if (element.tagName.toLowerCase() === 'select') return 'select dropdown'; 574 if (type) { 575 switch (type) { 576 case 'text': 577 return 'text input field'; 578 case 'email': 579 return 'email input field'; 580 case 'password': 581 return 'password input field'; 582 case 'checkbox': 583 return 'checkbox field'; 584 case 'radio': 585 return 'radio button'; 586 } 587 } 588 return 'input field'; 589 } 590 591 // Highlight an element visually. 592 function highlightElement(element) { 593 element.classList.add('aw-highlight'); 594 } 595 596 // Remove highlighting from all elements.
597 function resetHighlighting() { 598 document.querySelectorAll('.aw-highlight').forEach(el => el.classList.remove('aw-highlight')); 599 } 600} 601 602// Initialize the contrast functionality. 603function initializeContrast(savedState = 0) { 604 // Attach methods to the Accessibility object for external access. 605 Accessibility.enableContrast = enableContrast; 606 Accessibility.disableContrast = disableContrast; 607 Accessibility.resetContrast = resetContrast; 608 609 // Define constants for the contrast functionality. 610 const contrastButton = document.getElementById('contrast'); 611 const contrastStates = [ 612 krakenAccessibilityTranslations.contrast, 613 krakenAccessibilityTranslations.invertedColors, 614 krakenAccessibilityTranslations.darkContrast, 615 krakenAccessibilityTranslations.lightContrast 616 ]; 617 const ariaLabels = [ 618 krakenAccessibilityTranslations.contrastDescription, 619 krakenAccessibilityTranslations.invertColorsActivated, 620 krakenAccessibilityTranslations.darkContrastActivated, 621 krakenAccessibilityTranslations.lightContrastActivated 622 ]; 623 624 const textSelectors = '*:not(#aw-widget):not(#aw-widget *)'; // Selectors for text elements. 625 const linkSelectors = 'a:not(#aw-widget a)'; // Selectors for link elements. 626 const originalStyles = new Map(); // Store original styles for reset purposes. 627 628 // Initialize the contrast state if saved. 629 if (savedState > 0) { 630 toggleActiveClass('contrast', true); 631 updateAriaLabel('contrast', ariaLabels, savedState); 632 enableContrast(savedState); 633 document.getElementById('contrast-state').textContent = contrastStates[savedState]; 634 } 635 636 // Add event listener to the contrast button. 637 contrastButton.addEventListener('click', () => { 638 const currentState = toggleState('contrast', contrastStates.length); 639 updateButtonState( 640 'contrast', // Button ID 641 currentState > 0, // Is Active 642 ariaLabels, // ARIA Labels 643 currentState, // Current State 644 'contrast-state', // State Label ID 645 contrastStates // State Labels 646 ); 647 648 saveStateToLocalStorage(); // Save the current state. 649 enableContrast(currentState); 650 }); 651 652 // Enable a specific contrast mode based on the state. 653 function enableContrast(state) { 654 const htmlTag = document.documentElement; // Reference to the HTML element. 655 656 if (state === 0) { 657 disableContrast(); // Reset contrast if state is 0. 658 return; 659 } 660 661 if (state === 1) { // Inverted Colors mode. 662 htmlTag.classList.add('aw-modifier__invert'); 663 htmlTag.style.backgroundColor = ''; 664 } else { 665 htmlTag.classList.remove('aw-modifier__invert'); 666 667 // Apply styles to text elements based on contrast mode. 668 document.querySelectorAll(textSelectors).forEach(el => { 669 if (!originalStyles.has(el)) { 670 originalStyles.set(el, { 671 backgroundColor: el.style.backgroundColor, 672 borderColor: el.style.borderColor, 673 color: el.style.color, 674 }); 675 } 676 677 if (state === 2) { // Dark Contrast mode. 678 el.style.backgroundColor = 'rgb(0, 0, 0)'; 679 el.style.borderColor = 'rgb(255, 255, 255)'; 680 el.style.color = 'rgb(80, 208, 160)'; 681 } else if (state === 3) { // Light Contrast mode. 682 el.style.backgroundColor = 'rgb(255, 255, 255)'; 683 el.style.borderColor = 'rgb(0, 0, 0)'; 684 el.style.color = 'rgb(0, 0, 0)'; 685 } 686 687 el.setAttribute('data-contrast-modified', 'true'); 688 }); 689 690 // Apply styles to link elements based on contrast mode. 691 document.querySelectorAll(linkSelectors).forEach(link => { 692 if (!originalStyles.has(link)) { 693 originalStyles.set(link, { 694 backgroundColor: link.style.backgroundColor, 695 color: link.style.color, 696 }); 697 } 698 699 if (state === 2) { // Dark Contrast mode. 700 link.style.backgroundColor = 'rgb(0, 0, 0)'; 701 link.style.borderColor = 'rgb(255, 255, 255)'; 702 link.style.color = 'rgb(252, 255, 60)'; 703 } else if (state === 3) { // Light Contrast mode. 704 link.style.backgroundColor = 'rgb(255, 255, 255)'; 705 link.style.borderColor = 'rgb(0, 0, 0)'; 706 link.style.color = 'rgb(0, 0, 211)'; 707 } 708 709 link.setAttribute('data-contrast-modified', 'true'); 710 }); 711 } 712 } 713 714 // Reset contrast settings to default. 715 function resetContrast() { 716 disableContrast(); 717 state.contrast = 0; 718 document.getElementById('contrast-state').textContent = contrastStates[0]; 719 toggleActiveClass('contrast', false); 720 saveStateToLocalStorage(); 721 } 722 723 // Disable all contrast modifications and restore original styles. 724 function disableContrast() { 725 const htmlTag = document.documentElement; 726 htmlTag.classList.remove('aw-modifier__invert'); 727 htmlTag.style.backgroundColor = ''; 728 729 // Restore original styles for text elements.
730 document.querySelectorAll(textSelectors).forEach(el => { 731 if (el.getAttribute('data-contrast-modified') === 'true') { 732 const original = originalStyles.get(el); 733 if (original) { 734 Object.keys(original).forEach(prop => { 735 el.style[prop] = original[prop]; 736 }); 737 } 738 el.removeAttribute('data-contrast-modified'); 739 } 740 }); 741 742 // Restore original styles for link elements. 743 document.querySelectorAll(linkSelectors).forEach(link => { 744 if (link.getAttribute('data-contrast-modified') === 'true') { 745 const original = originalStyles.get(link); 746 if (original) { 747 Object.keys(original).forEach(prop => { 748 link.style[prop] = original[prop]; 749 }); 750 } 751 link.removeAttribute('data-contrast-modified'); 752 } 753 }); 754 755 // Reapply highlight settings if active. 756 if (state.highlightLinks) { 757 applyHighlightToLinks(true); 758 } 759 if (state.highlightHeadings) { 760 enableHighlightToHeadings(true); 761 } 762 } 763 764 // Highlight or unhighlight link elements. 765 function applyHighlightToLinks(isHighlighted) { 766 document.querySelectorAll(linkSelectors).forEach(link => { 767 link.style.backgroundColor = isHighlighted ? '#000000' : ''; 768 link.style.color = isHighlighted ? '#fff300' : ''; 769 }); 770 } 771 772 // Highlight or unhighlight heading elements. 773 function enableHighlightToHeadings(isHighlighted) { 774 document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => { 775 const hasAnchor = heading.querySelector('a'); 776 777 if (hasAnchor) { 778 hasAnchor.style.backgroundColor = isHighlighted ? '#000000' : ''; 779 hasAnchor.style.color = isHighlighted ? '#ff9900' : ''; 780 } else { 781 heading.style.backgroundColor = isHighlighted ? '#000000' : ''; 782 heading.style.color = isHighlighted ? '#ff9900' : ''; 783 } 784 }); 785 } 786} 787 788// Initialize the highlight links functionality. 789function initializeHighlightLinks(savedState = 0) { 790 // Attach methods to the Accessibility object for external access. 791 Accessibility.enableHighlightToLinks = enableHighlightToLinks; 792 Accessibility.resetHighlightToLinks = resetHighlightToLinks; 793 794 // Define constants for the highlight links functionality. 795 const highlightLinksButton = document.getElementById('highlight-links'); // Button to toggle link highlighting. 796 const highlightStates = [ 797 krakenAccessibilityTranslations.highlightLinks, 798 krakenAccessibilityTranslations.linksHighlighted 799 ]; 800 const linkAriaLabels = [ 801 krakenAccessibilityTranslations.highlightLinksDescription, 802 krakenAccessibilityTranslations.linksHighlightedState 803 ]; 804 805 // Initialize the highlight links state if saved. 806 if (savedState > 0) { 807 updateButtonState( 808 'highlight-links', // Button ID 809 true, // Is Active 810 linkAriaLabels, // ARIA Labels 811 savedState, // Current State 812 'highlight-links-state', // State Label ID 813 highlightStates // State Labels 814 ); 815 816 enableHighlightToLinks(true); // Enable highlighting if a saved state exists. 817 } 818 819 // Add event listener to the highlight links button. 820 highlightLinksButton.addEventListener('click', () => { 821 const currentState = toggleState('highlightLinks', highlightStates.length); 822 823 updateButtonState( 824 'highlight-links', // Button ID 825 currentState > 0, // Is Active 826 linkAriaLabels, // ARIA Labels 827 currentState, // Current State 828 'highlight-links-state', // State Label ID 829 highlightStates // State Labels 830 ); 831 832 enableHighlightToLinks(currentState > 0); // Enable or disable highlighting based on state. 833 834 saveStateToLocalStorage(); // Save the current state. 835 }); 836 837 // Enable or disable link highlighting based on the provided flag. 838 function enableHighlightToLinks(isHighlighted) { 839 document.querySelectorAll('a').forEach(link => { 840 link.style.backgroundColor = isHighlighted ? '#000000' : ''; // Highlight background color. 841 link.style.color = isHighlighted ? '#fff300' : ''; // Highlight text color. 842 }); 843 } 844 845 // Reset link highlighting to default state. 846 function resetHighlightToLinks(isHighlighted) { 847 enableHighlightToLinks(false); // Disable link highlighting. 848 state.highlightLinks = 0; // Reset state. 849 toggleActiveClass('highlight-links', false); 850 updateAriaLabel('highlight-links', linkAriaLabels, 0); 851 document.getElementById('highlight-links-state').textContent = highlightStates[0]; 852 853 saveStateToLocalStorage(); // Save the reset state. 854 } 855} 856 857// Initialize the highlight headings functionality. 858function initializeHighlightHeadings(savedState = 0) { 859 // Attach methods to the Accessibility object for external access. 860 Accessibility.enableHighlightToHeadings = enableHighlightToHeadings; 861 Accessibility.resetHighlightToHeadings = resetHighlightToHeadings; 862 863 // Define constants for the highlight headings functionality. 864 const highlightHeadingsButton = document.getElementById('highlight-headings'); // Button to toggle heading highlighting. 865 const highlightStates = [ 866 krakenAccessibilityTranslations.highlightHeadings, 867 krakenAccessibilityTranslations.headingsHighlighted 868 ]; 869 const headingAriaLabels = [ 870 krakenAccessibilityTranslations.highlightHeadingsDescription, 871 krakenAccessibilityTranslations.headingsHighlightedState 872 ]; 873 874 // Initialize the highlight headings state if saved. 875 if (savedState > 0) { 876 updateButtonState( 877 'highlight-headings', // Button ID 878 true, // Is Active 879 headingAriaLabels, // ARIA Labels 880 savedState, // Current State 881 'highlight-headings-state', // State Label ID 882 highlightStates // State Labels 883 ); 884 885 enableHighlightToHeadings(true); // Enable highlighting if a saved state exists. 886 } 887 888 // Add event listener to the highlight headings button. 889 highlightHeadingsButton.addEventListener('click', () => { 890 const currentState = toggleState('highlightHeadings', highlightStates.length); 891 updateButtonState( 892 'highlight-headings', // Button ID 893 currentState > 0, // Is Active 894 headingAriaLabels, // ARIA Labels 895 currentState, // Current State 896 'highlight-headings-state', // State Label ID 897 highlightStates // State Labels 898 ); 899 900 enableHighlightToHeadings(currentState > 0); // Enable or disable highlighting based on state. 901 902 saveStateToLocalStorage(); // Save the current state. 903 }); 904 905 // Enable or disable heading highlighting based on the provided flag. 906 function enableHighlightToHeadings(isHighlighted) { 907 document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => { 908 const hasAnchor = heading.querySelector('a'); // Check if the heading contains an anchor. 909 910 if (hasAnchor) { 911 hasAnchor.style.backgroundColor = isHighlighted ? '#000000' : ''; // Highlight background color for anchor. 912 hasAnchor.style.color = isHighlighted ? '#ff9900' : ''; // Highlight text color for anchor. 913 } else { 914 heading.style.backgroundColor = isHighlighted ? '#000000' : ''; // Highlight background color for heading. 915 heading.style.color = isHighlighted ? '#ff9900' : ''; // Highlight text color for heading. 916 } 917 }); 918 } 919 920 // Reset heading highlighting to default state. 921 function resetHighlightToHeadings(){ 922 enableHighlightToHeadings(false); // Disable heading highlighting. 923 state.highlightHeadings = 0; // Reset state. 924 toggleActiveClass('highlight-headings', false); 925 updateAriaLabel('highlight-headings', headingAriaLabels, 0); 926 document.getElementById('highlight-headings-state').textContent = highlightStates[0]; 927 928 saveStateToLocalStorage(); // Save the reset state. 929 } 930} 931 932// Initialize the bigger text functionality. 933function initializeBiggerText(savedState = 0) { 934 // Attach methods to the Accessibility object for external access. 935 Accessibility.enableBiggerTexts = enableBiggerText; 936 Accessibility.resetBiggerText = resetBiggerText; 937 938 // Define constants for the bigger text functionality. 939 const biggerTextButton = document.getElementById('bigger-text'); // Button to toggle bigger text. 940 const biggerTextStates = [ 941 krakenAccessibilityTranslations.biggerText, 942 krakenAccessibilityTranslations.bigTextLevel1, 943 krakenAccessibilityTranslations.bigTextLevel2, 944 krakenAccessibilityTranslations.bigTextLevel3, 945 krakenAccessibilityTranslations.bigTextLevel4 946 ]; 947 const biggerTextAriaLabels = [ 948 krakenAccessibilityTranslations.biggerTextDescription, 949 krakenAccessibilityTranslations.bigTextLevel1Activated, 950 krakenAccessibilityTranslations.bigTextLevel2Activated, 951 krakenAccessibilityTranslations.bigTextLevel3Activated, 952 krakenAccessibilityTranslations.bigTextLevel4Activated 953 ]; 954 const zoomClasses = [ 955 '', // Default 956 'aw-modifier__zoom-1', // 1.2x 957 'aw-modifier__zoom-2', // 1.3x 958 'aw-modifier__zoom-3', // 1.5x 959 'aw-modifier__zoom-4' // 1.6x 960 ]; 961 962 // Initialize the bigger text state if saved. 963 if (savedState > 0) { 964 updateButtonState( 965 'bigger-text', // Button ID 966 true, // Is Active 967 biggerTextAriaLabels, // ARIA Labels 968 savedState, // Current State 969 'bigger-text-state', // State Label ID 970 biggerTextStates // State Labels 971 ); 972 enableBiggerText(savedState); // Enable text resizing based on saved state. 973 } 974 975 // Add event listener to the bigger text button. 976 biggerTextButton.addEventListener('click', () => { 977 const currentState = toggleState('biggerText', biggerTextStates.length); 978 updateButtonState( 979 'bigger-text', // Button ID 980 currentState > 0, // Is Active 981 biggerTextAriaLabels, // ARIA Labels 982 currentState, // Current State 983 'bigger-text-state', // State Label ID 984 biggerTextStates // State Labels 985 ); 986 987 enableBiggerText(currentState); // Enable or disable text resizing. 988 989 saveStateToLocalStorage(); // Save the current state. 990 }); 991 992 // Enable or disable text resizing by adding/removing a class. 993 function enableBiggerText(level) { 994 // Remove all zoom classes, but filter out empty strings to prevent errors 995 document.body.classList.remove(...zoomClasses.filter(cls => cls)); 996 997 // Apply the new zoom class if level is greater than 0 998 if (level > 0) { 999 document.body.classList.add(zoomClasses[level]); 1000 } 1001 } 1002 1003 // Reset text resizing to default state. 1004 function resetBiggerText() { 1005 enableBiggerText(0); // Reset text size to default. 1006 state.biggerText = 0; // Reset state. 1007 toggleActiveClass('bigger-text', false); 1008 updateAriaLabel('bigger-text', biggerTextAriaLabels, 0); 1009 document.getElementById('bigger-text-state').textContent = biggerTextStates[0]; 1010 1011 saveStateToLocalStorage(); // Save the reset state. 1012 } 1013} 1014 1015// Initialize the text spacing functionality. 1016function initializeTextSpacing(savedState = 0) { 1017 // Attach methods to the Accessibility object for external access. 1018 Accessibility.resetTextSpacing = resetTextSpacing; 1019 1020 // Define constants for the text spacing functionality. 1021 const textSpacingButton = document.getElementById('text-spacing'); // Button to toggle text spacing. 1022 const spacingClasses = ['aw-modifier__lp-1', 'aw-modifier__lp-2', 'aw-modifier__lp-3']; // CSS classes for spacing levels. 1023 const spacingLabels = [ 1024 krakenAccessibilityTranslations.changeTextSpacing, 1025 krakenAccessibilityTranslations.slightSpacing, 1026 krakenAccessibilityTranslations.moderateSpacing, 1027 krakenAccessibilityTranslations.heavySpacing 1028 ]; 1029 const ariaLabels = [ 1030 krakenAccessibilityTranslations.changeTextSpacingDescription, 1031 krakenAccessibilityTranslations.slightSpacingActivated, 1032 krakenAccessibilityTranslations.moderateSpacingActivated, 1033 krakenAccessibilityTranslations.heavySpacingActivated 1034 ]; 1035 1036 // Initialize the text spacing state if saved. 1037 if (savedState > 0) { 1038 updateButtonState( 1039 'text-spacing', // Button ID 1040 true, // Is Active 1041 ariaLabels, // ARIA Labels 1042 savedState, // Current State 1043 'text-spacing-state', // State Label ID 1044 spacingLabels // State Labels 1045 ); 1046 document.documentElement.classList.add(spacingClasses[savedState - 1]); // Apply saved spacing class. 1047 } 1048 1049 // Add event listener to the text spacing button. 1050 textSpacingButton.addEventListener('click', () => { 1051 const currentState = toggleState('textSpacing', spacingLabels.length); 1052 const isActive = currentState > 0; // Check if text spacing should be enabled. 1053 updateButtonState( 1054 'text-spacing', // Button ID 1055 currentState > 0, // Is Active 1056 ariaLabels, // ARIA Labels 1057 currentState, // Current State 1058 null, // No State Label ID (if none exists) 1059 null // No State Labels (if none exist) 1060 ); 1061 1062 document.documentElement.classList.remove(...spacingClasses); // Remove all spacing classes. 1063 1064 if (isActive) { 1065 document.documentElement.classList.add(spacingClasses[currentState - 1]); // Add the current spacing class. 1066 } 1067 1068 document.getElementById('text-spacing-state').textContent = spacingLabels[currentState]; 1069 1070 saveStateToLocalStorage(); // Save the current state. 1071 }); 1072 1073 // Reset text spacing to default state. 1074 function resetTextSpacing() { 1075 document.documentElement.classList.remove(...spacingClasses); // Remove all spacing classes. 1076 state.textSpacing = 0; // Reset state. 1077 toggleActiveClass('text-spacing', false); 1078 updateAriaLabel('text-spacing', ariaLabels, 0); 1079 document.getElementById('text-spacing-state').textContent = spacingLabels[0]; 1080 1081 saveStateToLocalStorage(); // Save the reset state. 1082 } 1083} 1084 1085// Initialize the hide images functionality. 1086function initializeHideImages(savedState = 0) { 1087 // Attach methods to the Accessibility object for external access. 1088 Accessibility.enableHideImages = enableHideImages; 1089 Accessibility.resetHideImages = resetHideImages; 1090 1091 // Define constants for the hide images functionality. 1092 const hideImagesButton = document.getElementById('hide-images'); // Button to toggle image visibility. 1093 const hideImagesStates = [ 1094 krakenAccessibilityTranslations.hideImages, 1095 krakenAccessibilityTranslations.imagesHidden 1096 ]; 1097 const hideImagesAriaLabels = [ 1098 krakenAccessibilityTranslations.hideImagesDescription, 1099 krakenAccessibilityTranslations.imagesHiddenState 1100 ]; 1101
1102 const originalBackgroundImages = new Map(); // Store original background images for reset purposes. 1103 1104 // Initialize the hide images state if saved. 1105 if (savedState === 1) { 1106 updateButtonState( 1107 'hide-images', // Button ID 1108 true, // Is Active 1109 hideImagesAriaLabels, // ARIA Labels 1110 savedState, // Current State 1111 'hide-images-state', // State Label ID 1112 hideImagesStates // State Labels 1113 ); 1114 enableHideImages(true); // Enable hiding images based on saved state. 1115 } 1116 1117 // Add event listener to the hide images button. 1118 hideImagesButton.addEventListener('click', () => { 1119 const currentState = toggleState('hideImages', hideImagesStates.length); 1120 updateButtonState( 1121 'hide-images', // Button ID 1122 currentState > 0, // Is Active 1123 hideImagesAriaLabels, // ARIA Labels 1124 currentState, // Current State 1125 'hide-images-state', // State Label ID 1126 hideImagesStates // State Labels 1127 ); 1128 1129 enableHideImages(currentState === 1); // Enable or disable hiding images based on state. 1130 1131 saveStateToLocalStorage(); // Save the current state. 1132 }); 1133 1134 // Enable or disable hiding images. 1135 function enableHideImages(hide) { 1136 const elements = document.querySelectorAll('*'); // Select all elements on the page. 1137 1138 elements.forEach(element => { 1139 // Skip elements inside .aw-widget 1140 if (element.closest('.aw-widget')) { 1141 return; 1142 } 1143 1144 if (element.tagName.toLowerCase() === 'img' || element.tagName.toLowerCase() === 'svg') { 1145 element.style.visibility = hide ? 'hidden' : 'visible'; // Hide or show image elements while keeping their space. 1146 } 1147 1148 const computedStyle = getComputedStyle(element); 1149 const backgroundImage = computedStyle.backgroundImage; // Get background image style. 1150 1151 if (hide) { 1152 if (!originalBackgroundImages.has(element) && backgroundImage !== 'none') { 1153 originalBackgroundImages.set(element, backgroundImage); // Store the original background image. 1154 } 1155 element.style.backgroundImage = 'none'; // Hide background images. 1156 } else { 1157 if (originalBackgroundImages.has(element)) { 1158 element.style.backgroundImage = originalBackgroundImages.get(element); // Restore background images. 1159 } 1160 } 1161 }); 1162 1163 if (!hide) { 1164 originalBackgroundImages.clear(); // Clear stored background images when not hiding. 1165 } 1166 } 1167 1168 // Reset image visibility to default state. 1169 function resetHideImages () { 1170 enableHideImages(false); // Disable hiding images. 1171 state.hideImages = 0; // Reset state. 1172 toggleActiveClass('hide-images', false); 1173 updateAriaLabel('hide-images', hideImagesAriaLabels, 0); 1174 document.getElementById('hide-images-state').textContent = hideImagesStates[0]; 1175 1176 saveStateToLocalStorage(); // Save the reset state. 1177 } 1178} 1179 1180// Initialize the Dyslexia Friendly Font functionality. 1181function initializeDyslexiaFont(savedState = 0) { 1182 // Attach methods to the Accessibility object for external access. 1183 Accessibility.enableDyslexiaFont = enableDyslexiaFont; 1184 Accessibility.resetDyslexiaFont = resetDyslexiaFont; 1185 1186 // Define constants for the Dyslexia Font functionality. 1187 const dyslexiaFontButton = document.getElementById('dyslexia-font'); // Button to toggle dyslexia-friendly fonts. 1188 const dyslexiaFontStates = [ 1189 krakenAccessibilityTranslations.dyslexiaFriendly, 1190 krakenAccessibilityTranslations.dyslexiaFriendlyFont, 1191 krakenAccessibilityTranslations.legibleFont 1192 ]; 1193 const dyslexiaFontAriaLabels = [ 1194 krakenAccessibilityTranslations.dyslexiaFriendlyDescription, 1195 krakenAccessibilityTranslations.dyslexiaFriendlyActivated, 1196 krakenAccessibilityTranslations.legibleFontActivated 1197 ]; 1198 1199 // Initialize the dyslexia font state if saved. 1200 if (savedState > 0) { 1201 updateButtonState( 1202 'dyslexia-font', // Button ID 1203 true, // Is Active 1204 dyslexiaFontAriaLabels, // ARIA Labels 1205 savedState, // Current State 1206 'dyslexia-font-state', // State Label ID 1207 dyslexiaFontStates // State Labels 1208 ); 1209 1210 enableDyslexiaFont(savedState); // Enable dyslexia font based on saved state. 1211 } 1212 1213 // Add event listener to the Dyslexia Font button. 1214 dyslexiaFontButton.addEventListener('click', () => { 1215 const currentState = toggleState('dyslexiaFont', dyslexiaFontStates.length); 1216 updateButtonState( 1217 'dyslexia-font', // Button ID 1218 currentState > 0, // Is Active 1219 dyslexiaFontAriaLabels, // ARIA Labels 1220 currentState, // Current State 1221 'dyslexia-font-state', // State Label ID 1222 dyslexiaFontStates // State Labels 1223 ); 1224 1225 enableDyslexiaFont(currentState); // Enable or disable dyslexia font based on state. 1226 1227 saveStateToLocalStorage(); // Save the current state. 1228 }); 1229 1230 // Enable or disable dyslexia-friendly font styles. 1231 function enableDyslexiaFont(state) { 1232 const textElements = document.querySelectorAll( 1233 'h1, h2, h3, h4, h5, h6, p, strong, a, ul, ol, li, span, blockquote, button, label' 1234 ); // Elements to apply font styles. 1235 1236 const fontLinkId = 'dyslexic-font-style'; // ID for the font stylesheet. 1237 1238 const existingFontLink = document.getElementById(fontLinkId); 1239 if (existingFontLink) existingFontLink.remove(); // Remove existing font link if present. 1240
1241 textElements.forEach(el => { 1242 el.style.fontFamily = ''; // Reset font styles. 1243 }); 1244 1245 if (state === 1) { // Apply Dyslexia Friendly Font. 1246 if (!document.getElementById(fontLinkId)) { 1247 const fontLink = document.createElement('link'); 1248 fontLink.id = fontLinkId; 1249 fontLink.rel = 'stylesheet'; 1250 fontLink.href = 'https://cdn.jsdelivr.net/npm/[email protected]/open-dyslexic-regular.min.css'; 1251 document.head.appendChild(fontLink); // Add the dyslexia-friendly font stylesheet. 1252 } 1253 textElements.forEach(el => { 1254 el.style.fontFamily = '"OpenDyslexicRegular", Arial, sans-serif'; // Apply the font to elements. 1255 }); 1256 } else if (state === 2) { // Apply Legible Font. 1257 textElements.forEach(el => { 1258 el.style.fontFamily = 'Arial, serif'; // Apply a legible font to elements. 1259 }); 1260 } 1261 } 1262 1263 // Reset font styles to default. 1264 function resetDyslexiaFont() { 1265 enableDyslexiaFont(0); // Reset font to default. 1266 state.dyslexiaFont = 0; // Reset state. 1267 toggleActiveClass('dyslexia-font', false); 1268 updateAriaLabel('dyslexia-font', dyslexiaFontAriaLabels, 0); 1269 document.getElementById('dyslexia-font-state').textContent = dyslexiaFontStates[0]; 1270 1271 saveStateToLocalStorage(); // Save the reset state. 1272 } 1273} 1274 1275// Initialize the cursor functionality. 1276function initializeCursor(savedState = 0) { 1277 // Attach methods to the Accessibility object for external access. 1278 Accessibility.enableCursorState = enableCursorState; 1279 Accessibility.resetCursorState = resetCursorState; 1280 1281 // Define constants for the cursor functionality. 1282 const cursorButton = document.getElementById('cursor'); // Button to toggle cursor states. 1283 const cursorStates = [ 1284 krakenAccessibilityTranslations.defaultCursor, 1285 krakenAccessibilityTranslations.bigCursor, 1286 krakenAccessibilityTranslations.readingMask, 1287 krakenAccessibilityTranslations.readingGuide 1288 ]; 1289 const cursorAriaLabels = [ 1290 krakenAccessibilityTranslations.cursorDescription, 1291 krakenAccessibilityTranslations.bigCursorActivated, 1292 krakenAccessibilityTranslations.cursorReadingMaskActivated, 1293 krakenAccessibilityTranslations.cursorReadingGuideActivated 1294 ]; 1295 1296 // Initialize the cursor state if saved. 1297 if (savedState > 0) { 1298 updateButtonState( 1299 'cursor', // Button ID 1300 true, // Is Active 1301 cursorAriaLabels, // ARIA Labels 1302 savedState, // Current State 1303 'cursor-state', // State Label ID 1304 cursorStates // State Labels 1305 ); 1306 enableCursorState(savedState); // Enable cursor state based on saved state. 1307 } 1308 1309 // Add event listener to the cursor button. 1310 cursorButton.addEventListener('click', () => { 1311 const currentState = toggleState('cursor', cursorStates.length); 1312 updateButtonState( 1313 'cursor', // Button ID 1314 currentState > 0, // Is Active 1315 cursorAriaLabels, // ARIA Labels 1316 currentState, // Current State 1317 'cursor-state', // State Label ID 1318 cursorStates // State Labels 1319 ); 1320 1321 enableCursorState(currentState); // Enable or disable cursor modifications based on state. 1322 1323 saveStateToLocalStorage(); // Save the current state. 1324 }); 1325 1326 // Enable or disable cursor modifications based on state. 1327 function enableCursorState(state) { 1328 const cursorOverlayTopId = 'aw-overlay-top'; // ID for the top overlay element. 1329 const cursorOverlayBottomId = 'aw-overlay-bottom'; // ID for the bottom overlay element. 1330 const cursorLineId = 'aw-cursor-line'; // ID for the cursor line element. 1331 1332 // Remove previous cursor modifications. 1333 document.documentElement.classList.remove('aw-modifier__cursor'); 1334 removeElementById(cursorOverlayTopId); 1335 removeElementById(cursorOverlayBottomId); 1336 removeElementById(cursorLineId); 1337 document.removeEventListener('mousemove', handleCursorWindow); 1338 document.removeEventListener('mousemove', handleCursorLine); 1339 1340 // Apply new cursor modifications based on state. 1341 if (state === 1) { 1342 document.documentElement.classList.add('aw-modifier__cursor'); // Add big cursor class. 1343 } else if (state === 2) { 1344 createCursorOverlay(cursorOverlayTopId, cursorOverlayBottomId); // Create overlay elements for Reading Mask. 1345 document.addEventListener('mousemove', handleCursorWindow); // Adjust overlay position dynamically. 1346 } else if (state === 3) { 1347 createCursorLine(cursorLineId); // Create Reading Guide line. 1348 document.addEventListener('mousemove', handleCursorLine); // Adjust line position dynamically. 1349 } 1350 } 1351 1352 // Reset cursor modifications to default state. 1353 function resetCursorState() { 1354 enableCursorState(0); // Reset to default cursor. 1355 state.cursor = 0; // Reset state. 1356 toggleActiveClass('cursor', false); 1357 updateAriaLabel('cursor', cursorAriaLabels, 0); 1358 document.getElementById('cursor-state').textContent = curs
1358orStates[0]; 1359 1360 saveStateToLocalStorage(); // Save the reset state. 1361 } 1362 1363 // Create overlay elements for the Reading Mask. 1364 function createCursorOverlay(topId, bottomId) { 1365 const overlayTop = document.createElement('div'); 1366 overlayTop.id = topId; 1367 Object.assign(overlayTop.style, { 1368 position: 'fixed', 1369 top: '0', 1370 left: '0', 1371 width: '100%', 1372 height: '50%', 1373 backgroundColor: 'rgba(0, 0, 0, 0.5)', 1374 pointerEvents: 'none', 1375 zIndex: '9999' 1376 }); 1377 1378 const overlayBottom = document.createElement('div'); 1379 overlayBottom.id = bottomId; 1380 Object.assign(overlayBottom.style, { 1381 position: 'fixed', 1382 bottom: '0', 1383 left: '0', 1384 width: '100%', 1385 height: '50%', 1386 backgroundColor: 'rgba(0, 0, 0, 0.5)', 1387 pointerEvents: 'none', 1388 zIndex: '9999' 1389 }); 1390 1391 document.body.appendChild(overlayTop); // Add top overlay to the DOM. 1392 document.body.appendChild(overlayBottom); // Add bottom overlay to the DOM. 1393 } 1394 1395 // Adjust overlay positions based on mouse movement. 1396 function handleCursorWindow(e) { 1397 const overlayTop = document.getElementById('aw-overlay-top'); 1398 const overlayBottom = document.getElementById('aw-overlay-bottom'); 1399 if (overlayTop && overlayBottom) { 1400 const windowHeight = 100; // Height of the Reading Mask window. 1401 overlayTop.style.height = `${e.clientY - windowHeight / 2}px`; 1402 overlayBottom.style.height = `calc(100% - ${e.clientY + windowHeight / 2}px)`; 1403 } 1404 } 1405 1406 // Create the Reading Guide line element. 1407 function createCursorLine(lineId) { 1408 const cursorLine = document.createElement('div'); 1409 cursorLine.id = lineId; 1410 cursorLine.classList.add('aw-reading-guide'); 1411 1412 const arrow = document.createElement('div'); 1413 arrow.classList.add('aw-reading-guide__arrow'); // Add arrow indicator. 1414 1415 cursorLine.appendChild(arrow); // Append arrow to the line. 1416 1417 document.body.appendChild(cursorLine); // Add the line to the DOM. 1418 } 1419 1420 // Adjust the Reading Guide line position based on mouse movement. 1421 function handleCursorLine(e) { 1422 const cursorLine = document.getElementById('aw-cursor-line'); 1423 if (cursorLine) { 1424 cursorLine.style.left = `${e.clientX}px`; 1425 cursorLine.style.top = `${e.clientY}px`; 1426 } 1427 } 1428 1429 // Remove an element by its ID. 1430 function removeElementById(elementId) { 1431 const element = document.getElementById(elementId); 1432 if (element) element.remove(); // Remove the element if it exists. 1433 } 1434} 1435 1436// Initialize the line height functionality. 1437function initializeLineHeight(savedState = 0) { 1438 // Attach methods to the Accessibility object for external access. 1439 Accessibility.enableLineHeight = enableLineHeight; 1440 Accessibility.resetLineHeight = resetLineHeight; 1441 1442 // Define constants for the line height functionality. 1443 const lineHeightButton = document.getElementById('line-height'); // Button to toggle line height. 1444 const lineHeightStates = [ 1445 krakenAccessibilityTranslations.lineHeight, 1446 krakenAccessibilityTranslations.lineHeight15x, 1447 krakenAccessibilityTranslations.lineHeight175x, 1448 krakenAccessibilityTranslations.lineHeight2x 1449 ]; 1450 const lineHeightAriaLabels = [ 1451 krakenAccessibilityTranslations.defaultLineHeight, 1452 krakenAccessibilityTranslations.lineHeight15xActivated, 1453 krakenAccessibilityTranslations.lineHeight175xActivated, 1454 krakenAccessibilityTranslations.lineHeight2xActivated 1455 ]; 1456 1457 // Initialize the line height state if saved. 1458 if (savedState > 0) { 1459 updateButtonState( 1460 'line-height', // Button ID 1461 true, // Is Active 1462 lineHeightAriaLabels, // ARIA Labels 1463 savedState, // Current State 1464 'line-height-state', // State Label ID 1465 lineHeightStates // State Labels 1466 ); 1467 enableLineHeight(savedState); // Enable line height based on saved state. 1468 } 1469 1470 // Add event listener to the line height button. 1471 lineHeightButton.addEventListener('click', () => { 1472 const currentState = toggleState('lineHeight', lineHeightStates.length); 1473 updateButtonState( 1474 'line-height', // Button ID 1475 currentState > 0, // Is Active 1476 lineHeightAriaLabels, // ARIA Labels 1477 currentState, // Current State 1478 'line-height-state', // State Label ID 1479 lineHeightStates // State Labels 1480 ); 1481 1482 enableLineHeight(currentState); // Enable or disable line height adjustments. 1483 1484 saveStateToLocalStorage(); // Save the current state. 1485 }); 1486 1487 // Enable or disable line height adjustments. 1488 function enableLineHeight(state) { 1489 // Define line height values for different states. 1490 const lineHeightValues = ['', '1.5', '1.75', '2']; 1491 const h1Values = ['', '120px', '140px', '160px']; // Specific line height values for H1 elements. 1492 const h2Values = ['', '102px', '119px', '136px']; // Specific line height values for H2 elements. 1493 1494 // Select text elements and headings for line height adjustments. 1495 const textElements = document.querySelectorAll('p, ul, ol, li, span, h3, h4, h5, h6, blockquote'); 1496 const h1Elements = document.querySelectorAll('h1'); 1497 const h2Elements = document.querySelectorAll('h2'); 1498 1499 // Reset line height for all elements.
1500 textElements.forEach(el => (el.style.lineHeight = '')); 1501 h1Elements.forEach(el => (el.style.lineHeight = '')); 1502 h2Elements.forEach(el => (el.style.lineHeight = '')); 1503 1504 // Apply new line height values based on the current state. 1505 if (state > 0) { 1506 const lineHeight = lineHeightValues[state]; // Get the line height value for the current state. 1507 textElements.forEach(el => (el.style.lineHeight = lineHeight)); // Apply line height to text elements. 1508 h1Elements.forEach(el => (el.style.lineHeight = h1Values[state])); // Apply line height to H1 elements. 1509 h2Elements.forEach(el => (el.style.lineHeight = h2Values[state])); // Apply line height to H2 elements. 1510 } 1511 } 1512 1513 // Reset line height adjustments to default state. 1514 function resetLineHeight() { 1515 enableLineHeight(0); // Reset line height to default. 1516 state.lineHeight = 0; // Reset state. 1517 toggleActiveClass('line-height', false); // Remove active class from the button. 1518 updateAriaLabel('line-height', lineHeightAriaLabels, 0); // Reset ARIA label. 1519 document.getElementById('line-height-state').textContent = lineHeightStates[0]; // Reset displayed state. 1520 1521 saveStateToLocalStorage(); // Save the reset state. 1522 } 1523} 1524 1525// Initialize the text alignment functionality. 1526function initializeTextAlign(savedState = 0) { 1527 // Attach methods to the Accessibility object for external access. 1528 Accessibility.enableTextAlign = enableTextAlign; 1529 Accessibility.resetTextAlign = resetTextAlign; 1530 1531 // Define constants for the text alignment functionality. 1532 const textAlignButton = document.getElementById('text-align'); // Button to toggle text alignment. 1533 const textAlignStates = [ 1534 krakenAccessibilityTranslations.changeTextAlign, 1535 krakenAccessibilityTranslations.alignLeft, 1536 krakenAccessibilityTranslations.alignRight, 1537 krakenAccessibilityTranslations.alignCenter, 1538 krakenAccessibilityTranslations.alignJustified 1539 ]; 1540 const textAlignAriaLabels = [ 1541 krakenAccessibilityTranslations.changeTextAlignDescription, 1542 krakenAccessibilityTranslations.alignLeftActivated, 1543 krakenAccessibilityTranslations.alignRightActivated, 1544 krakenAccessibilityTranslations.alignCenterActivated, 1545 krakenAccessibilityTranslations.alignJustifiedActivated 1546 ]; 1547 const textAlignValues = ['', 'left', 'right', 'center', 'justify']; // CSS text alignment values. 1548 1549 // Initialize the text alignment state if saved. 1550 if (savedState > 0) { 1551 updateButtonState( 1552 'text-align', // Button ID 1553 true, // Is Active 1554 textAlignAriaLabels, // ARIA Labels 1555 savedState, // Current State 1556 'text-align-state', // State Label ID 1557 textAlignStates // State Labels 1558 ); 1559 enableTextAlign(savedState); // Enable text alignment based on saved state. 1560 } 1561 1562 // Add event listener to the text alignment button. 1563 textAlignButton.addEventListener('click', () => { 1564 const currentState = toggleState('textAlign', textAlignStates.length); 1565 updateButtonState( 1566 'text-align', // Button ID 1567 currentState > 0, // Is Active 1568 textAlignAriaLabels, // ARIA Labels 1569 currentState, // Current State 1570 'text-align-state', // State Label ID 1571 textAlignStates // State Labels 1572 ); 1573 1574 enableTextAlign(currentState); // Enable or disable text alignment. 1575 1576 saveStateToLocalStorage(); // Save the current state. 1577 }); 1578 1579 // Enable or disable text alignment based on state. 1580 function enableTextAlign(state) { 1581 const textAlign = textAlignValues[state]; // Get the text alignment value for the current state. 1582 1583 // Select all target elements but exclude those inside <nav> 1584 document.querySelectorAll('h1, h2, h3, h4, h5, h6, p, ul, li, pre') 1585 .forEach(el => { 1586 if (!el.closest('nav')) { // Ensure elements inside <nav> are not affected 1587 el.style.setProperty('text-align', textAlign, 'important'); 1588 } 1589 }); 1590 } 1591 1592 // Reset text alignment to the default state. 1593 function resetTextAlign() { 1594 enableTextAlign(0); // Reset text alignment to default. 1595 state.textAlign = 0; // Reset state. 1596 toggleActiveClass('text-align', false); // Remove active class from the button. 1597 updateAriaLabel('text-align', textAlignAriaLabels, 0); // Reset ARIA label. 1598 document.getElementById('text-align-state').textContent = textAlignStates[0]; // Reset displayed state. 1599 1600 saveStateToLocalStorage(); // Save the reset state. 1601 } 1602} 1603 1604// Initialize the saturation functionality. 1605function initializeSaturation(savedState = 0) { 1606 // Attach the resetSaturation method to the Accessibility object for external access. 1607 Accessibility.resetSaturation = resetSaturation; 1608 1609 // Define constants for the saturation functionality. 1610 const saturationButton = document.getElementById('saturation'); // Button to toggle saturation levels. 1611 const saturationClasses = ['aw-modifier__sat-low', 'aw-modifier__sat-high', 'aw-modifier__sat-desaturate']; // CSS classes for saturation levels. 1612 const saturationStates = [ 1613 krakenAccessibilityTranslations.changeSaturation, 1614 krakenAccessibilityTranslations.lowSaturation, 1615 krakenAccessibilityTranslations.highSaturation, 1616 krakenAccessibilityTranslations.desaturate 1617 ]; 1618 const ariaLabels = [ 1619 krakenAccessibilityTranslations.changeSaturationDescription, 1620 krakenAccessibilityTranslations.lowSaturationActivated, 1621 krakenAccessibilityTranslations.highSaturationActivated, 1622 krakenAccessibilityTranslations.desaturateActivated 1623 ]; 1624 1625 // Initialize the saturation state if saved. 1626 if (savedState > 0) { 1627 updateButtonState(
1628 'saturation', // Button ID 1629 true, // Is Active 1630 ariaLabels, // ARIA Labels 1631 savedState, // Current State 1632 'saturation-state', // State Label ID 1633 saturationStates // State Labels 1634 ); 1635 document.documentElement.classList.add(saturationClasses[savedState - 1]); // Apply the saved saturation class. 1636 } 1637 1638 // Add event listener to the saturation button. 1639 saturationButton.addEventListener('click', () => { 1640 const currentState = toggleState('saturation', saturationStates.length); 1641 updateButtonState( 1642 'saturation', // Button ID 1643 currentState > 0, // Is Active 1644 ariaLabels, // ARIA Labels 1645 currentState, // Current State 1646 'saturation-state', // State Label ID 1647 saturationStates // State Labels 1648 ); 1649 1650 const isActive = currentState > 0; 1651 1652 document.documentElement.classList.remove(...saturationClasses); // Remove all existing saturation classes. 1653 1654 if (isActive) { 1655 document.documentElement.classList.add(saturationClasses[currentState - 1]); // Apply the selected saturation class. 1656 } 1657 1658 saveStateToLocalStorage(); // Save the current state. 1659 }); 1660 1661 // Reset saturation adjustments to default state. 1662 function resetSaturation() { 1663 document.documentElement.classList.remove(...saturationClasses); // Remove all existing saturation classes. 1664 toggleActiveClass('saturation', false); // Remove active class from the button. 1665 updateAriaLabel('saturation', ariaLabels, 0); // Reset ARIA label. 1666 document.getElementById('saturation-state').textContent = saturationStates[0]; // Reset displayed state. 1667 state.saturation = 0; // Reset state. 1668 1669 saveStateToLocalStorage(); // Save the reset state. 1670 } 1671} 1672 1673// Initialize the pause animations functionality. 1674function initializePauseAnimations(savedState = 0) { 1675 Accessibility.enablePauseAnimations = enablePauseAnimations; 1676 Accessibility.resetPauseAnimations = resetPauseAnimations; 1677 1678 const pauseAnimationsButton = document.getElementById('pause-animations'); 1679 const animationStates = [ 1680 krakenAccessibilityTranslations.pauseAnimations, 1681 krakenAccessibilityTranslations.animationsPaused 1682 ]; 1683 const animationAriaLabels = [ 1684 krakenAccessibilityTranslations.pauseAnimationsDescription, 1685 krakenAccessibilityTranslations.animationsPausedState 1686 ]; 1687 1688 if (savedState > 0) { 1689 updateButtonState( 1690 'pause-animations', 1691 true, 1692 animationAriaLabels, 1693 savedState, 1694 'pause-animations-state', 1695 animationStates 1696 ); 1697 enablePauseAnimations(savedState); 1698 } 1699 1700 pauseAnimationsButton.addEventListener('click', () => { 1701 const currentState = toggleState('pauseAnimations', animationStates.length); 1702 updateButtonState( 1703 'pause-animations', 1704 currentState > 0, 1705 animationAriaLabels, 1706 currentState, 1707 'pause-animations-state', 1708 animationStates 1709 ); 1710 1711 enablePauseAnimations(currentState); 1712 saveStateToLocalStorage(); 1713 }); 1714 1715 function enablePauseAnimations(state) { 1716 const animationStyleTagId = 'aw-animation-style'; 1717 let animationStyleTag = document.getElementById(animationStyleTagId); 1718 1719 if (state === 1) { 1720 if (!animationStyleTag) { 1721 animationStyleTag = document.createElement('style'); 1722 animationStyleTag.id = animationStyleTagId; 1723 animationStyleTag.textContent = ` 1724 * { 1725 animation: none !important; 1726 transition: none !important; 1727 } 1728 `; 1729 document.head.appendChild(animationStyleTag); 1730 } 1731 1732 disableGSAPAnimations(); 1733 disableOtherAnimations(); 1734 } else { 1735 if (animationStyleTag) { 1736 animationStyleTag.remove(); 1737 } 1738 1739 enableGSAPAnimations(); 1740 enableOtherAnimations(); 1741 } 1742 } 1743 1744 function resetPauseAnimations() {
1745 toggleActiveClass('pause-animations', false); 1746 updateAriaLabel('pause-animations', animationAriaLabels, 0); 1747 document.getElementById('pause-animations-state').textContent = animationStates[0]; 1748 enablePauseAnimations(0); 1749 state.pauseAnimations = 0; 1750 saveStateToLocalStorage(); 1751 } 1752} 1753 1754// Pause GSAP animations globally. 1755function disableGSAPAnimations() { 1756 if (window.gsap && gsap.globalTimeline) { 1757 try { 1758 gsap.globalTimeline.pause(); 1759 //console.log('GSAP animations paused.'); 1760 } catch (error) { 1761 //console.error('Error pausing GSAP animations:', error); 1762 } 1763 } else { 1764 //console.warn('GSAP is not loaded or detected.'); 1765 } 1766} 1767 1768// Resume GSAP animations globally. 1769function enableGSAPAnimations() { 1770 if (window.gsap && gsap.globalTimeline) { 1771 try { 1772 gsap.globalTimeline.resume(); 1773 //console.log('GSAP animations resumed.'); 1774 } catch (error) { 1775 //console.error('Error resuming GSAP animations:', error); 1776 } 1777 } else { 1778 //console.warn('GSAP is not loaded or detected.'); 1779 } 1780} 1781 1782// Disable animations from common libraries. 1783function disableOtherAnimations() { 1784 //console.log('Disabling third-party animations...'); 1785 1786 // Animate.css - Remove animation classes 1787 document.querySelectorAll('.animate__animated').forEach(el => { 1788 el.classList.remove('animate__animated'); 1789 }); 1790 1791 // AOS (Animate on Scroll) 1792 if (window.AOS) { 1793 try { 1794 AOS.refreshHard(); // Remove all instances 1795 AOS.init({ disable: true }); // Disable AOS 1796 //console.log('AOS animations disabled.'); 1797 } catch (error) { 1798 //console.error('Error disabling AOS:', error); 1799 } 1800 } 1801 1802 // WOW.js - Remove animation classes 1803 document.querySelectorAll('.wow').forEach(el => { 1804 el.classList.remove('wow'); 1805 }); 1806 1807 // Remove inline styles that contain animations 1808 document.querySelectorAll('[style]').forEach(el => { 1809 if (el.style.animation || el.style.transition) {
1810 el.style.animation = 'none'; 1811 el.style.transition = 'none'; 1812 } 1813 }); 1814 1815 // Remove CSS keyframes animations 1816 const keyframeStyles = document.querySelectorAll('style'); 1817 keyframeStyles.forEach(styleTag => { 1818 if (styleTag.innerHTML.includes('@keyframes') || styleTag.innerHTML.includes('animation')) { 1819 styleTag.remove(); 1820 } 1821 }); 1822 1823 //console.log('All animations disabled.'); 1824} 1825 1826// Enable animations from common libraries. 1827function enableOtherAnimations() { 1828 //console.log('Re-enabling third-party animations...'); 1829 1830 // AOS (Animate on Scroll) 1831 if (window.AOS) { 1832 try { 1833 AOS.refresh(); // Re-enable AOS 1834 //console.log('AOS animations enabled.'); 1835 } catch (error) { 1836 //console.error('Error enabling AOS:', error); 1837 } 1838 } 1839 1840 // WOW.js - Reapply animation classes 1841 document.querySelectorAll('[data-wow]').forEach(el => { 1842 el.classList.add('wow'); 1843 }); 1844 1845 //console.log('All animations re-enabled.'); 1846} 1847 1848 1849// Initialize the Read Mode functionality. 1850function initializeReadMode(savedState = 0) { 1851 // Attach methods to the Accessibility object for external access. 1852 Accessibility.enableReadMode = enableReadMode; 1853 Accessibility.resetReadMode = resetReadMode; 1854 1855 // Define constants for the Read Mode functionality. 1856 const readModeButton = document.getElementById('read-mode'); // Button to toggle Read Mode. 1857 const readModeStates = [ 1858 krakenAccessibilityTranslations.enableReadMode, 1859 krakenAccessibilityTranslations.readModeActivated, 1860 ]; 1861 const readModeAriaLabels = [ 1862 krakenAccessibilityTranslations.readModeDescription, 1863 krakenAccessibilityTranslations.readModeEnabled 1864 ]; 1865 1866 // Initialize the Read Mode state if saved. 1867 if (savedState > 0) { 1868 updateButtonState( 1869 'read-mode', // Button ID 1870 true, // Is Active 1871 readModeAriaLabels, // ARIA Labels 1872 savedState, // Current State 1873 'read-mode-state', // State Label ID 1874 readModeStates // State Labels 1875 ); 1876 enableReadMode(savedState); // Enable Read Mode based on saved state. 1877 } 1878 1879 // Add event listener to the Read Mode button. 1880 readModeButton.addEventListener('click', () => { 1881 const currentState = toggleState('readMode', readModeStates.length); 1882 updateButtonState( 1883 'read-mode', // Button ID 1884 currentState > 0, // Is Active 1885 readModeAriaLabels, // ARIA Labels 1886 currentState, // Current State 1887 'read-mode-state', // State Label ID 1888 readModeStates // State Labels 1889 ); 1890 1891 enableReadMode(currentState); // Enable or disable Read Mode. 1892 1893 saveStateToLocalStorage(); // Save the current state. 1894 }); 1895 1896 // Enable or disable Read Mode based on the state. 1897 function enableReadMode(state) { 1898 const body = document.querySelector('body'); // Select the body element. 1899 const firstDiv = document.querySelector('div'); // Select the first div element. 1900 1901 if (state === 1) { 1902 body.classList.add('aw-modifier__read-mode-body'); // Add Read Mode class to the body. 1903 1904 if (firstDiv) { 1905 const wrapperDiv = document.createElement('div'); // Create a wrapper div. 1906 wrapperDiv.classList.add('aw-modifier__read-mode'); // Add Read Mode class to the wrapper. 1907 firstDiv.parentNode.insertBefore(wrapperDiv, firstDiv); // Insert wrapper before the first div. 1908 wrapperDiv.appendChild(firstDiv); // Move the first div into the wrapper. 1909 } 1910 1911 const header = document.querySelector('header'); // Select the header element. 1912 if (header) { 1913 simplifyHeader(header); // Simplify the header content. 1914 } 1915 1916 removeUnnecessaryElements(); // Remove unnecessary elements for a cleaner view. 1917 } else { 1918 location.reload(); // Reload the page to reset the view. 1919 } 1920 } 1921 1922 // Reset Read Mode to default state. 1923 function resetReadMode() { 1924 if (state.readMode > 0) { 1925 toggleActiveClass('read-mode', false); // Remove active class from the button. 1926 updateAriaLabel('read-mode', readModeAriaLabels, 0); // Reset ARIA label. 1927 document.getElementById('read-mode-state').textContent = readModeStates[0]; // Reset displayed state. 1928 state.readMode = 0; // Reset state. 1929 location.reload(); // Reload the page to reset the view. 1930 saveStateToLocalStorage(); // Save the reset state. 1931 } 1932 } 1933 1934 // Simplify the header content for Read Mode. 1935 function simplifyHeader(header) { 1936 const navElements = Array.from(header.querySelectorAll('nav')); // Get all navigation elements in the header. 1937 const navWithMostLinks = navElements.reduce((mostLinks, currentNav) => { 1938 const currentLinksCount = currentNav.querySelectorAll('a').length; // Count links in the current navigation. 1939 return currentLinksCount > mostLinks.linksCount 1940 ? { element: currentNav, linksCount: currentLinksCount } // Keep the navigation with the most links. 1941 : mostLinks; 1942 }, { element: null, linksCount: 0 }); 1943 1944 const storedNav = navWithMostLinks.element ? navWithMostLinks.element.cloneNode(true) : null; // Clone the selected navigation. 1945 1946 const images = Array.from(header.querySelectorAll('img')); // Get all images in the header. 1947 const largestImage = images.reduce((largest, img) => {
1948 if (!largest || img.naturalWidth > largest.naturalWidth) { 1949 return img; // Select the largest image. 1950 } 1951 return largest; 1952 }, null); 1953 1954 header.innerHTML = ''; // Clear the header content. 1955 1956 if (largestImage) { 1957 const parentLink = largestImage.closest('a'); // Check if the largest image is inside a link. 1958 if (parentLink) { 1959 const linkClone = document.createElement('a'); // Create a new link. 1960 linkClone.href = parentLink.href; 1961 linkClone.target = parentLink.target || '_self'; 1962 linkClone.appendChild(largestImage.cloneNode(false)); // Append the image to the link. 1963 header.appendChild(linkClone); // Add the link to the header. 1964 } else { 1965 header.appendChild(largestImage.cloneNode(false)); // Add the image directly to the header. 1966 } 1967 } 1968 1969 if (storedNav) { 1970 header.appendChild(storedNav); // Add the cloned navigation to the header. 1971 } 1972 } 1973 1974 // Remove unnecessary elements from the page for Read Mode. 1975 function removeUnnecessaryElements() { 1976 document.querySelectorAll('form').forEach((form) => form.remove()); // Remove all form elements. 1977 1978 document.querySelectorAll('img, video, iframe, svg').forEach((element) => { 1979 // Ignore SVGs inside #aw-widget 1980 if (!element.closest('header') && !(element.closest('#aw-widget') && element.tagName.toLowerCase() === 'svg')) { 1981 element.remove(); // Remove images, videos, iframes, and SVGs not in header or #aw-widget 1982 } 1983 }); 1984 1985 const mainFooter = document.querySelector('footer:not(main footer)'); // Select footers not inside the main footer. 1986 if (mainFooter) { 1987 mainFooter.remove(); // Remove the additional footers. 1988 } 1989 1990 const readModeElements = document.querySelectorAll('.aw-modifier__read-mode *'); // Get all elements in Read Mode wrapper. 1991 readModeElements.forEach((element) => { 1992 element.removeAttribute('class'); // Remove class attributes for a cleaner view. 1993 element.removeAttribute('style'); // Remove inline styles for consistency. 1994 }); 1995 1996 // Remove all <style> and <script> tags inside <body> 1997 document.body.querySelectorAll('style, script').forEach((tag) => tag.remove()); 1998 } 1999} 2000 2001// Initialize the Mute Sounds functionality. 2002function initializeMuteSounds(savedState = 0) { 2003 // Attach methods to the Accessibility object for external access. 2004 Accessibility.enableMuteSounds = enableMuteSounds; 2005 Accessibility.disableMuteSounds = disableMuteSounds; 2006 Accessibility.resetMuteSounds = resetMuteSounds; 2007 2008 // Define constants for the Mute Sounds functionality. 2009 const muteSoundsButton = document.getElementById('mute-sounds'); // Button to toggle sound muting. 2010 const muteStates = [ 2011 krakenAccessibilityTranslations.muteSounds, 2012 krakenAccessibilityTranslations.soundsMuted 2013 ]; 2014 const muteAriaLabels = [ 2015 krakenAccessibilityTranslations.muteSoundsDescription, 2016 krakenAccessibilityTranslations.soundsMutedState 2017 ]; 2018 2019 // Initialize the Mute Sounds state if saved. 2020 if (savedState > 0) { 2021 updateButtonState( 2022 'mute-sounds', // Button ID 2023 true, // Is Active 2024 muteAriaLabels, // ARIA Labels 2025 savedState, // Current State 2026 'mute-sounds-state', // State Label ID 2027 muteStates // State Labels 2028 ); 2029 2030 if (savedState === 1) { 2031 enableMuteSounds(); // Enable muting sounds based on saved state. 2032 } 2033 } 2034 2035 // Add event listener to the Mute Sounds button. 2036 muteSoundsButton.addEventListener('click', () => { 2037 state.muteSounds = (state.muteSounds + 1) % muteStates.length; // Toggle between mute states. 2038 const isMuted = state.muteSounds === 1; // Check if sounds should be muted. 2039 2040 updateButtonState( 2041 'mute-sounds', // Button ID 2042 state.muteSounds > 0, // Is Active 2043 muteAriaLabels, // ARIA Labels 2044 state.muteSounds, // Current State 2045 'mute-sounds-state', // State Label ID 2046 muteStates // State Labels 2047 ); 2048 2049 if (isMuted) { 2050 enableMuteSounds(); // Mute all sounds. 2051 } else { 2052 disableMuteSounds(); // Unmute all sounds. 2053 } 2054 2055 saveStateToLocalStorage(); // Save the current state. 2056 }); 2057 2058 // Enable muting of all sounds. 2059 function enableMuteSounds() { 2060 document.querySelectorAll('audio, video').forEach((media) => { 2061 media.muted = true; // Mute the media element. 2062 media.pause(); // Pause the media playback. 2063 }); 2064 2065 document.querySelectorAll('iframe').forEach((iframe) => {
2066 const src = iframe.src; // Get the source of the iframe. 2067 if (src && (src.includes('youtube.com') || src.includes('vimeo.com'))) { 2068 const muteParam = src.includes('?') ? '&mute=1' : '?mute=1'; // Add mute parameter to the URL. 2069 iframe.src = src + muteParam; // Update the iframe source. 2070 } 2071 }); 2072 } 2073 2074 // Disable muting and restore sound playback. 2075 function disableMuteSounds() { 2076 const mediaElements = document.querySelectorAll("audio, video"); 2077 const failedMedia = []; 2078 2079 mediaElements.forEach((media) => { 2080 try { 2081 media.muted = false; // Unmute the media element. 2082 media.play().catch((err) => { 2083 console.warn(`Playback failed for media element:`, media, err); 2084 failedMedia.push({ element: media, error: err }); 2085 }); 2086 } catch (err) { 2087 console.error(`Error unmuting or playing media element:`, media, err); 2088 failedMedia.push({ element: media, error: err }); 2089 } 2090 }); 2091 2092 if (failedMedia.length > 0) { 2093 console.warn(`${failedMedia.length} media elements failed to play. Retrying...`); 2094 retryFailedMedia(failedMedia); 2095 } 2096 2097 document.querySelectorAll("iframe").forEach((iframe) => { 2098 try { 2099 const src = iframe.src; 2100 if (src) { 2101 iframe.src = src.replace("&mute=1", "").replace("?mute=1", ""); // Remove mute parameter. 2102 } 2103 } catch (err) { 2104 console.error(`Error resetting iframe source:`, iframe, err); 2105 } 2106 }); 2107 } 2108 2109 function retryFailedMedia(failedMedia) { 2110 failedMedia.forEach(({ element, error }) => { 2111 try { 2112 element.play().catch((err) => { 2113 console.error(`Retry failed for media element:`, element, err); 2114 }); 2115 } catch (err) { 2116 console.error(`Retry attempt failed:`, element, err); 2117 } 2118 }); 2119 } 2120 2121 // Reset muting state to default (unmuted). 2122 function resetMuteSounds() { 2123 toggleActiveClass('mute-sounds', false); // Remove active class from the button. 2124 updateAriaLabel('mute-sounds', muteAriaLabels, 0); // Reset ARIA label. 2125 document.getElementById('mute-sounds-state').textContent = muteStates[0]; // Reset displayed state. 2126 state.muteSounds = 0; // Reset state. 2127 disableMuteSounds(); // Unmute all sounds. 2128 saveStateToLocalStorage(); // Save the reset state. 2129 } 2130} 2131 2132// Initialize the Link Notices functionality. 2133function initializeLinkNotices(savedState = 0) { 2134 // Attach methods to the Accessibility object for external access. 2135 Accessibility.enableLinkNotices = enableLinkNotices; 2136 Accessibility.disableLinkNotices = disableLinkNotices; 2137 Accessibility.resetLinkNotices = resetLinkNotices; 2138 2139 // Define constants for the Link Notices functionality. 2140 const linkNoticesButton = document.getElementById('link-notices'); // Button to toggle link notices. 2141 const linkNoticesStates = [ 2142 krakenAccessibilityTranslations.enableLinkNotices, 2143 krakenAccessibilityTranslations.linkNoticesOn 2144 ]; 2145 const linkNoticesAriaLabels = [ 2146 krakenAccessibilityTranslations.linkNoticesDescription, 2147 krakenAccessibilityTranslations.linkNoticesEnabled 2148 ]; 2149 2150 // Initialize the link notices state if saved. 2151 if (savedState > 0) { 2152 updateButtonState( 2153 'link-notices', // Button ID 2154 true, // Is Active 2155 linkNoticesAriaLabels, // ARIA Labels 2156 savedState, // Current State 2157 'link-notices-state', // State Label ID 2158 linkNoticesStates // State Labels 2159 ); 2160 2161 enableLinkNotices(); // Enable link notices based on saved state. 2162 } 2163 2164 // Add event listener to the Link Notices button. 2165 linkNoticesButton.addEventListener('click', () => { 2166 const currentState = toggleState('linkNotices', linkNoticesStates.length); 2167 const isActive = currentState > 0; 2168 updateButtonState( 2169 'link-notices', // Button ID 2170 currentState > 0, // Is Active 2171 linkNoticesAriaLabels, // ARIA Labels 2172 currentState, // Current State 2173 'link-notices-state', // State Label ID 2174 linkNoticesStates // State Labels 2175 ); 2176 2177 if (isActive) { 2178 enableLinkNotices(); // Enable link notices. 2179 } else { 2180 disableLinkNotices(); // Disable link notices. 2181 } 2182 2183 saveStateToLocalStorage(); // Save the current state. 2184 }); 2185 2186 // Enable link notices by adding tooltips and ARIA labels to links. 2187 function enableLinkNotices() { 2188 const links = document.querySelectorAll('a'); // Select all link elements. 2189 2190 links.forEach(link => { 2191 const linkName = link.textContent.trim() || link.href; // Use the link's text or URL as its name. 2192 const isNewTab = link.target === '_blank'; // Check if the link opens in a new tab. 2193 2194 if (isNewTab) { 2195 const additionalText = ', opens in a new window';
2195 // Text to describe the link's behavior. 2196 const ariaLabel = `${linkName}${additionalText}`; // Create a descriptive ARIA label. 2197 link.setAttribute('aria-label', ariaLabel); // Add ARIA label to the link. 2198 2199 if (!link.classList.contains('aw-tooltip')) { 2200 const tooltip = document.createElement('span'); // Create a tooltip element. 2201 tooltip.className = 'aw-tooltip-text'; // Add a class for styling the tooltip. 2202 tooltip.textContent = `${linkName}${additionalText}`; // Set the tooltip text. 2203 2204 link.classList.add('aw-tooltip'); // Add a class to indicate the link has a tooltip. 2205 link.appendChild(tooltip); // Append the tooltip to the link. 2206 2207 // Add event listeners to show or hide the tooltip on focus, blur, mouseenter, or mouseleave. 2208 link.addEventListener('focus', showTooltip); 2209 link.addEventListener('blur', hideTooltip); 2210 link.addEventListener('mouseenter', showTooltip); 2211 link.addEventListener('mouseleave', hideTooltip); 2212 } 2213 } 2214 }); 2215 } 2216 2217 // Disable link notices by removing tooltips and ARIA labels. 2218 function disableLinkNotices() { 2219 const links = document.querySelectorAll('a'); // Select all link elements. 2220 2221 links.forEach(link => { 2222 link.removeAttribute('aria-label'); // Remove the ARIA label. 2223 2224 if (link.classList.contains('aw-tooltip')) { 2225 const tooltip = link.querySelector('.aw-tooltip-text'); // Select the tooltip element. 2226 if (tooltip) tooltip.remove(); // Remove the tooltip element. 2227 2228 link.classList.remove('aw-tooltip'); // Remove the tooltip class from the link. 2229 link.removeEventListener('focus', showTooltip); // Remove event listeners for the tooltip. 2230 link.removeEventListener('blur', hideTooltip); 2231 link.removeEventListener('mouseenter', showTooltip); 2232 link.removeEventListener('mouseleave', hideTooltip); 2233 } 2234 }); 2235 } 2236 2237 // Reset link notices to the default state. 2238 function resetLinkNotices() { 2239 disableLinkNotices(); // Disable link notices. 2240 state.linkNotices = 0; // Reset state. 2241 toggleActiveClass('link-notices', false); // Remove active class from the button. 2242 updateAriaLabel('link-notices', linkNoticesAriaLabels, 0); // Reset ARIA label. 2243 document.getElementById('link-notices-state').textContent = linkNoticesStates[0]; // Reset displayed state. 2244 2245 saveStateToLocalStorage(); // Save the reset state. 2246 } 2247 2248 // Show the tooltip when the link gains focus or the mouse hovers over it. 2249 function showTooltip(event) { 2250 const tooltip = event.target.querySelector('.aw-tooltip-text'); // Get the tooltip element. 2251 if (tooltip) tooltip.style.visibility = 'visible'; // Make the tooltip visible. 2252 } 2253 2254 // Hide the tooltip when the link loses focus or the mouse leaves it. 2255 function hideTooltip(event) { 2256 const tooltip = event.target.querySelector('.aw-tooltip-text'); // Get the tooltip element. 2257 if (tooltip) tooltip.style.visibility = 'hidden'; // Hide the tooltip. 2258 } 2259} 2260 2261// Initialize the Dictionary functionality. 2262function initializeDictionary(savedState = 0) { 2263 Accessibility.enableDictionary = enableDictionary; 2264 Accessibility.disableDictionary = disableDictionary; 2265 Accessibility.resetDictionary = resetDictionary; 2266 2267 const dictionaryButton = document.getElementById('dictionary'); 2268 const dictionarySearchInput = document.getElementById('dictionary-search'); 2269 const dictionarySearchButton = document.getElementById('dictionary-search-button'); 2270 const dictionaryModal = document.getElementById("dictionary-modal"); 2271 2272 const dictionaryStates = [ 2273 krakenAccessibilityTranslations.enableDictionary, 2274 krakenAccessibilityTranslations.dictionaryOn, 2275 ]; 2276 const dictionaryAriaLabels = [ 2277 krakenAccessibilityTranslations.dictionaryDescription, 2278 krakenAccessibilityTranslations.dictionaryEnabled, 2279 ]; 2280 2281 if (savedState > 0) { 2282 updateButtonState( 2283 'dictionary', 2284 true, 2285 dictionaryAriaLabels, 2286 savedState, 2287 'dictionary-state', 2288 dictionaryStates 2289 ); 2290 enableDictionary(); 2291 } 2292 2293 dictionaryButton.addEventListener("click", (e) => { 2294 if (e.target.classList.contains("aw-dictionary-close")) return; 2295 2296 const currentState = toggleState('dictionary', dictionaryStates.length); 2297 const isActive = currentState > 0; 2298 updateButtonState( 2299 'dictionary', 2300 isActive, 2301 dictionaryAriaLabels, 2302 currentState, 2303 'dictionary-state', 2304 dictionaryStates 2305 ); 2306 2307 if (isActive) { 2308 enableDictionary(); 2309 } else { 2310 disableDictionary(); 2311 } 2312 2313 saveStateToLocalStorage(); 2314 }); 2315 2316 function enableDictionary() { 2317 document.body.addEventListener("mouseup", handleTextSelection); 2318 document.body.addEventListener("touchend", handleTouchSelection); 2319 document.body.addEventListener("dblclick", handleDoubleClickSelection); 2320 } 2321 2322 function disableDictionary() { 2323 document.body.removeEventListener("mouseup", handleTextSelection); 2324 document.body.removeEventListener("touchend", handleTouchSelection); 2325 document.body.removeEventListener("dblclick", handleDoubleClickSelection); 2326 } 2327 2328 function resetDictionary() { 2329 const currentState = toggleState('dictionary', dictionaryStates.length); 2330 updateButtonState( 2331 'dictionary', 2332 currentState > 0, 2333 dictionaryAriaLabels, 2334 currentState, 2335 'dictionary-state', 2336 dictionaryStates 2337 ); 2338 disableDictionary(); 2339 saveStateToLocalStorage(); 2340 } 2341 2342 function handleTextSelection(event) { 2343 if (event.target.closest("#dictionary-modal")) return; 2344 2345 const selectedText = window.getSelection().toString().trim(); 2346 if (selectedText.length > 0) { 2347 dictionarySearchInput.value = selectedText; 2348 fetchDefinition(selectedText); 2349 } 2350 } 2351 2352 function handleTouchSelection(event) { 2353 if (event.target.closest("#dictionary-modal")) return; 2354 2355 const selectedText = window.getSelection().toString().trim(); 2356 if (selectedText.length > 0) { 2357 dictionarySearchInput.value = selectedText; 2358 fetchDefinition(selectedText); 2359 } 2360 } 2361 2362 function handleDoubleClickSelection(event) { 2363 if (event.target.closest("#dictionary-modal")) return; 2364 2365 const selectedText = window.getSelection().toString().trim(); 2366 if (selectedText.length > 0) { 2367 dictionarySearchInput.value = selectedText; 2368 fetchDefinition(selectedText); 2369 } 2370 } 2371 2372 function fetchDefinition(selectedText) { 2373 if (selectedText.length > 0) { 2374 fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(selectedText)}`) 2375 .then(response => { 2376 if (!response.ok) { 2377 throw new Error("Word not found."); 2378 } 2379 return response.json(); 2380 }) 2381 .then(data => { 2382 if (!data || !data[0] || !data[0].meanings || data[0].meanings.length === 0) { 2383 throw new Error("Invalid response format."); 2384 } 2385 2386 const word = data[0].word; 2387 const meanings = data[0].meanings.flatMap(meaning => 2388 meaning.definitions.map(def => ({ 2389 partOfSpeech: meaning.partOfSpeech, 2390 definition: def.definition 2391 })) 2392 ); 2393 2394 if (meanings.length === 0) { 2395 throw new Error("No definitions available."); 2396 } 2397 2398 showDictionaryModal(word, meanings); 2399 window.getSelection().removeAllRanges(); 2400 }) 2401 .catch(error => { 2402 console.error("Dictionary Error:", error.message); 2403 }); 2404 } 2405 } 2406 2407 function showDictionaryModal(word, definitions) { 2408 const definitionElement = document.getElementById("dictionary-definition"); 2409 const counter = document.getElementById("definition-counter"); 2410 const prevButton = document.getElementById("prev-definition"); 2411 const nextButton = document.getElementById("next-definition"); 2412 const readButton = document.getElementById("read-definition"); 2413 const spellButton = document.getElementById("spell-word"); 2414 2415 let currentDefinitionIndex = 0; 2416 2417 function updateDefinition() { 2418 const currentDefinition = definitions[currentDefinitionIndex]; 2419 definitionElement.innerHTML = `<strong>${word} (${currentDefinition.partOfSpeech}):</strong> ${currentDefinition.definition}`; 2420 counter.textContent = `${currentDefinitionIndex + 1} of ${definitions.length}`; 2421 } 2422 2423 prevButton.onclick = () => { 2424 currentDefinitionIndex = (currentDefinitionIndex - 1 + definitions.length) % definitions.length; 2425 updateDefinition(); 2426 }; 2427 2428 nextButton.onclick = () => { 2429 currentDefinitionIndex = (currentDefinitionIndex + 1) % definitions.length; 2430 updateDefinition(); 2431 }; 2432 2433 readButton.onclick = () => { 2434 const currentDefinition = definitions[currentDefinitionIndex]; 2435 speakDefinition(word, currentDefinition.partOfSpeech, currentDefinition.definition); 2436 }; 2437 2438 spellButton.onclick = () => { 2439 spellWord(word); 2440 }; 2441 2442 if (definitions.length <= 1) { 2443 prevButton.style.display = "none"; 2444 nextButton.style.display = "none"; 2445 counter.style.display = "none"; 2446 } else { 2447 prevButton.style.display = "flex"; 2448 nextButton.style.display = "flex"; 2449 counter.style.display = "flex"; 2450 } 2451 2452 updateDefinition(); 2453 dictionaryModal.removeAttribute("inert"); 2454 dictionaryModal.classList.remove("aw-dm-hidden"); 2455 2456 dictionarySearchInput.focus(); 2457 dictionarySearchInput.setSelectionRange(dictionarySearchInput.value.length, dictionarySearchInput.value.length); 2458 } 2459 2460 // Function to read the definition aloud 2461 function speakDefinition(word, partOfSpeech, definition) { 2462 if ("speechSynthesis" in window) { 2463 speechSynthesis.cancel(); 2464 2465 const text = `${word}, ${partOfSpeech}. ${definition}`; 2466 const utterance = new SpeechSynthesisUtterance(text); 2467 utterance.lang = "en-US"; 2468 utterance.rate = 1; 2469 utterance.pitch = 1; 2470 2471 speechSynthesis.speak(utterance); 2472 } else { 2473 console.warn("Speech synthesis not supported in this browser."); 2474 } 2475 } 2476 2477 // Function to spell the word aloud 2478 function spellWord(word) { 2479 if ("speechSynthesis" in window) { 2480 speechSynthesis.cancel(); 2481 2482 const spelledOut = word.split("").join(". "); 2483 2484 const utterance = new SpeechSynthesisUtterance(`Spelling: ${spelledOut}`); 2485 utterance.lang = "en-US"; 2486 utterance.rate = 0.8; 2487 utterance.pitch = 1; 2488 2489 speechSynthesis.speak(utterance); 2490 } else { 2491 console.warn("Speech synthesis not supported in this browser."); 2492 } 2493 } 2494 2495 // Close dictionary modal on click 2496 document.querySelector("#aw-dictionary").addEventListener("click", () => { 2497 dictionaryModal.setAttribute("inert", "true"); 2498 dictionaryModal.classList.add("aw-dm-hidden"); 2499 }); 2500 2501 // Close dictionary modal when clicking outside of it 2502 dictionaryModal.addEventListener("click", (event) => { 2503 if (event.target === dictionaryModal) { 2504 dictionaryModal.setAttribute("inert", "true"); 2505 dictionaryModal.classList.add("aw-dm-hidden"); 2506 } 2507 }); 2508 2509 // Close modal when the Escape key is pressed 2510 document.addEventListener("keydown", (event) => { 2511 if (event.key === "Escape" && !dictionaryModal.classList.contains("aw-dm-hidden")) { 2512 dictionaryModal.setAttribute("inert", "true"); 2513 dictionaryModal.classList.add("aw-dm-hidden"); 2514 } 2515 }); 2516 2517 // Execute search on Enter key press 2518 dictionarySearchButton.addEventListener("click", () => { 2519 fetchDefinition(dictionarySearchInput.value.trim()); 2520 }); 2521 2522 dictionarySearchInput.addEventListener("keypress", (event) => { 2523 if (event.key === "Enter") { 2524 event.preventDefault(); 2525 fetchDefinition(dictionarySearchInput.value.trim()); 2526 } 2527 }); 2528} 2529 2530// Reset all settings to their default state. 2531function resetAllSettings() { 2532 // Iterate through each key in the state object and reset it.
2533 Object.keys(state).forEach((key) => { 2534 if (state[key] > 0) { 2535 switch (key) { 2536 case 'contrast': 2537 Accessibility.resetContrast(); 2538 break; 2539 case 'saturation': 2540 Accessibility.resetSaturation(); 2541 break; 2542 case 'screenReader': 2543 Accessibility.resetScreenReader(); 2544 break; 2545 case 'highlightLinks': 2546 Accessibility.resetHighlightToLinks(); 2547 break; 2548 case 'highlightHeadings': 2549 Accessibility.resetHighlightToHeadings(); 2550 break; 2551 case 'biggerText': 2552 Accessibility.resetBiggerText(); 2553 break; 2554 case 'textSpacing': 2555 Accessibility.resetTextSpacing(); 2556 break; 2557 case 'hideImages': 2558 Accessibility.resetHideImages(); 2559 break; 2560 case 'dyslexiaFont': 2561 Accessibility.resetDyslexiaFont(); 2562 break; 2563 case 'cursor': 2564 Accessibility.resetCursorState(); 2565 break; 2566 case 'lineHeight': 2567 Accessibility.resetLineHeight(); 2568 break; 2569 case 'textAlign': 2570 Accessibility.resetTextAlign(); 2571 break; 2572 case 'pauseAnimations': 2573 Accessibility.resetPauseAnimations(); 2574 break; 2575 case 'readMode': 2576 Accessibility.resetReadMode(); 2577 break; 2578 case 'muteSounds': 2579 Accessibility.resetMuteSounds(); 2580 break; 2581 case 'linkNotices': 2582 Accessibility.resetLinkNotices(); 2583 break; 2584 case 'dictionary': 2585 Accessibility.resetDictionary(); 2586 break; 2587 default: 2588 console.warn(`No reset function defined for ${key}`); 2589 } 2590 2591 // Reset the state for the key. 2592 state[key] = 0; 2593 } 2594 }); 2595 2596 // Clear the active profile. 2597 state.activeProfile = null; 2598 2599 // Save the updated state to localStorage. 2600 saveStateToLocalStorage(); 2601 2602 // Update UI elements (e.g., buttons). 2603 document.querySelectorAll(".aw-button").forEach((button) => { 2604 button.setAttribute("aria-pressed", false); 2605 button.classList.remove("aw-button--active"); 2606 }); 2607} 2608 2609// Reset all settings when a "reset all" button is clicked. 2610const resetAllButton = document.getElementById('aw_reset'); 2611if (resetAllButton) { 2612 resetAllButton.addEventListener('click', () => { 2613 resetAllSettings(); 2614 2615 // Add feedback logic 2616 const feedbackMessage = document.getElementById('aw-feedback'); 2617 if (feedbackMessage) { 2618 feedbackMessage.classList.remove('aw-feedback-hidden'); 2619 2620 // Automatically hide the feedback message after 3 seconds 2621 setTimeout(() => { 2622 feedbackMessage.classList.add('aw-feedback-hidden'); 2623 }, 2000); 2624 } 2625 }); 2626} 2627 2628// Main Initialization 2629function initializeAccessibility() { 2630 initializeModal({ 2631 modalId: 'aw-modal', 2632 toggleButtonId: 'aw-toggle-modal', 2633 closeButtonId: 'aw-close-modal', 2634 }); 2635 initializeScreenReader(state.screenReader); 2636 initializeContrast(state.contrast); 2637 initializeHighlightLinks(state.highlightLinks); 2638 initializeHighlightHeadings(state.highlightHeadings); 2639 initializeBiggerText(state.biggerText); 2640 initializeTextSpacing(state.textSpacing); 2641 initializeHideImages(state.hideImages); 2642 initializeDyslexiaFont(state.dyslexiaFont); 2643 initializeCursor(state.cursor); 2644 initializeLineHeight(state.lineHeight); 2645 initializeTextAlign(state.textAlign); 2646 initializeSaturation(state.saturation); 2647 initializePauseAnimations(state.pauseAnimations);
2648 initializeReadMode(state.readMode); 2649 initializeMuteSounds(state.muteSounds); 2650 initializeLinkNotices(state.linkNotices); 2651 initializeDictionary(state.dictionary); 2652 updateToggleButtonState(); 2653} 2654 2655// Function to update the toggle button state based on active features 2656function updateToggleButtonState() { 2657 const toggleButton = document.getElementById("aw-toggle-modal"); 2658 if (!toggleButton) return; 2659 2660 const hasActiveFeature = Object.values(state).some(value => value > 0); 2661 2662 if (hasActiveFeature) { 2663 toggleButton.classList.add("aw-widget__trigger--active"); 2664 toggleButton.setAttribute("aria-label", "Accessibility Widget Options are Active"); 2665 } else { 2666 toggleButton.classList.remove("aw-widget__trigger--active"); 2667 toggleButton.setAttribute("aria-label", "Accessibility Widget"); 2668 } 2669} 2670 2671// Ensure the toggle button state updates when any setting is changed 2672function saveStateToLocalStorage() { 2673 localStorage.setItem(stateKey, JSON.stringify(state)); 2674 updateToggleButtonState(); // Update the button state after saving changes 2675} 2676 2677// Initialization logic on page load 2678document.addEventListener('DOMContentLoaded', () => { 2679 initializeAccessibility(); 2680 updateToggleButtonState(); 2681}); 2682 2683// Add accessibly menu skip link. 2684document.addEventListener("DOMContentLoaded", function () { 2685 let skipNav = document.querySelector("nav.kraken-skip-links ul.kraken-skip-links__list"); 2686 if (skipNav) { 2687 let newListItem = document.createElement("li"); 2688 newListItem.classList.add("kraken-skip-links__item"); 2689 2690 let newLink = document.createElement("a"); 2691 newLink.classList.add("kraken-skip-links__link"); 2692 newLink.href = "#aw-toggle-modal"; 2693 newLink.textContent = "Skip to Accessibility Menu"; 2694 2695 newListItem.appendChild(newLink); 2696 skipNav.appendChild(newListItem); 2697 } 2698}); 2699
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.