(function() { 'use strict'; // Wait for DOM to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { // Get user info from parent page (set by Layout.js) const userInfo = window.accessiWidgetUserInfo || null; let isAuthenticated = false; let isAdmin = false; let userEmail = ''; if (userInfo && userInfo.email) { isAuthenticated = true; userEmail = userInfo.email; if (userInfo.role === 'admin') { isAdmin = true; } } // Check for stored admin token const storedToken = sessionStorage.getItem('accessiwidget_admin_token'); const storedExpiry = sessionStorage.getItem('accessiwidget_admin_expiry'); const storedEmail = sessionStorage.getItem('accessiwidget_admin_email'); if (storedToken && storedExpiry && storedEmail) { const now = new Date(); const expiry = new Date(storedExpiry); if (now < expiry) { isAuthenticated = true; isAdmin = true; userEmail = storedEmail; } else { sessionStorage.removeItem('accessiwidget_admin_token'); sessionStorage.removeItem('accessiwidget_admin_expiry'); sessionStorage.removeItem('accessiwidget_admin_email'); } } const config = { id: '69719389ee84e6fd1e4916df', position: 'bottom-right', brandColor: '#3b82f6', clientName: 'Based politics ', companyLogoUrl: '', features: { textResizer: true, contrastModes: true, dyslexiaFriendly: true, bigCursor: true, screenReader: true, languageSelector: true, profileSeizureSafe: true, profileVisionImpaired: true, profileAdhdFriendly: true, profileCognitive: true, profileKeyboardNav: true, profileScreenReader: true, contentScaling: true, readableFont: true, highlightTitles: true, highlightLinks: true, textMagnifier: true, fontSizing: true, lineHeight: true, textSpacing: true, alignCenter: true, alignLeft: true, darkContrast: true, lightContrast: true, highContrast: true, highSaturation: true, lowSaturation: true, monochrome: true, muteSounds: true, hideImages: true, readingGuide: true, stopAnimations: true, readingMask: true, highlightHover: true, highlightFocus: true, bigBlackCursor: true, bigWhiteCursor: true } }; // Prevent multiple initializations if (window.__accessiWidgetInitialized) return; window.__accessiWidgetInitialized = true; // Create widget styles const style = document.createElement('style'); style.textContent = ` .accessiwidget-btn { position: fixed; width: 56px; height: 56px; border-radius: 50%; background: linear-gradient(135deg, ${config.brandColor} 0%, ${config.brandColor}dd 100%); border: none; cursor: pointer; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 999999 !important; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; filter: none !important; pointer-events: auto !important; touch-action: manipulation; -webkit-tap-highlight-color: transparent; } @media (max-width: 768px) { .accessiwidget-btn { width: 64px; height: 64px; } } .accessiwidget-btn:hover { transform: scale(1.1); box-shadow: 0 6px 24px rgba(0,0,0,0.4); } .accessiwidget-btn svg { width: 28px; height: 28px; fill: white; } .accessiwidget-panel { position: fixed; width: 360px; max-height: 85vh; background: linear-gradient(to bottom, #ffffff 0%, #f8f9fa 100%); border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.25); z-index: 1000000; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; filter: none !important; overflow-y: auto; } .accessiwidget-panel.hidden { display: none; } .accessiwidget-header { display: flex; flex-direction: column; align-items: center; padding: 20px 20px 16px; background: ${config.brandColor}; position: relative; } .accessiwidget-logo { width: 48px; height: 48px; border-radius: 12px; background: white; display: flex; align-items: center; justify-content: center; margin-bottom: 12px; overflow: hidden; } .accessiwidget-logo img { width: 100%; height: 100%; object-fit: cover; } .accessiwidget-logo svg { width: 28px; height: 28px; fill: ${config.brandColor}; } .accessiwidget-company-name { font-size: 16px; font-weight: 600; color: white; text-align: center; margin-bottom: 4px; } .accessiwidget-close { position: absolute; top: 12px; right: 12px; background: rgba(255,255,255,0.2); border: none; cursor: pointer; padding: 6px; color: white; border-radius: 6px; transition: background 0.2s; } .accessiwidget-close:hover { background: rgba(255,255,255,0.3); } .accessiwidget-content { padding: 16px; } .accessiwidget-section { margin-bottom: 20px; } .accessiwidget-section-title { font-size: 13px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; padding-left: 4px; } .accessiwidget-feature { display: flex; justify-content: space-between; align-items: center; padding: 12px; background: white; border-radius: 10px; margin-bottom: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); transition: transform 0.2s, box-shadow 0.2s; } .accessiwidget-feature:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); } .accessiwidget-feature-label { font-size: 14px; font-weight: 500; color: #333; flex: 1; } .accessiwidget-feature-desc { font-size: 11px; color: #888; margin-top: 2px; } .accessiwidget-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 12px; } .accessiwidget-grid-item { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 12px 8px; background: white; border-radius: 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); cursor: pointer; transition: all 0.2s; text-align: center; } .accessiwidget-grid-item:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); } .accessiwidget-grid-item.active { background: ${config.brandColor}; color: white; } .accessiwidget-grid-icon { font-size: 20px; margin-bottom: 6px; } .accessiwidget-grid-label { font-size: 11px; font-weight: 500; } .accessiwidget-toggle { width: 44px; height: 24px; background: #ddd; border-radius: 12px; position: relative; cursor: pointer; transition: background 0.3s; } .accessiwidget-toggle.active { background: ${config.brandColor}; } .accessiwidget-toggle::after { content: ''; position: absolute; width: 20px; height: 20px; background: white; border-radius: 50%; top: 2px; left: 2px; transition: transform 0.3s; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } .accessiwidget-toggle.active::after { transform: translateX(20px); } .accessiwidget-control { display: flex; gap: 6px; } .accessiwidget-control button { padding: 6px 12px; background: white; border: 2px solid #e0e0e0; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s; } .accessiwidget-control button:hover { border-color: ${config.brandColor}; color: ${config.brandColor}; } .accessiwidget-control select { padding: 6px 12px; border: 2px solid #e0e0e0; border-radius: 6px; font-size: 13px; background: white; cursor: pointer; } .accessiwidget-footer { padding: 12px 20px; text-align: center; background: rgba(0,0,0,0.02); border-top: 1px solid rgba(0,0,0,0.05); } .accessiwidget-branding { font-size: 11px; color: #888; } .accessiwidget-branding a { color: ${config.brandColor}; text-decoration: none; font-weight: 600; } body.text-large > *:not(.accessiwidget-btn):not(.accessiwidget-panel) { font-size: 120% !important; line-height: 1.6 !important; } body.text-larger > *:not(.accessiwidget-btn):not(.accessiwidget-panel) { font-size: 140% !important; line-height: 1.7 !important; } body.aw-high-contrast > *:not(.accessiwidget-btn):not(.accessiwidget-panel) { filter: contrast(200%) !important; } html.aw-dark-contrast::before { content: ""; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.3); mix-blend-mode: darken; pointer-events: none; z-index: 999998; } html.aw-light-contrast::before { content: ""; position: fixed; inset: 0; background: rgba(255, 255, 255, 0.3); mix-blend-mode: lighten; pointer-events: none; z-index: 999998; } body.aw-high-saturation > *:not(.accessiwidget-btn):not(.accessiwidget-panel) { filter: saturate(200%); } body.aw-low-saturation > *:not(.accessiwidget-btn):not(.accessiwidget-panel) { filter: saturate(50%); } body.aw-monochrome > *:not(.accessiwidget-btn):not(.accessiwidget-panel) { filter: grayscale(100%); } body.aw-hide-images img:not(.accessiwidget-panel img), body.aw-hide-images svg:not(.accessiwidget-panel svg), body.aw-hide-images [role="img"]:not(.accessiwidget-panel [role="img"]) { display: none !important; } body.aw-stop-animations > *:not(.accessiwidget-btn):not(.accessiwidget-panel) * { animation: none !important; } body.aw-readable-font > *:not(.accessiwidget-btn):not(.accessiwidget-panel) * { font-family: Arial, sans-serif !important; } body.aw-highlight-titles h1:not(.accessiwidget-panel *), body.aw-highlight-titles h2:not(.accessiwidget-panel *), body.aw-highlight-titles h3:not(.accessiwidget-panel *), body.aw-highlight-titles h4:not(.accessiwidget-panel *), body.aw-highlight-titles h5:not(.accessiwidget-panel *), body.aw-highlight-titles h6:not(.accessiwidget-panel *) { background: yellow !important; padding: 2px 4px !important; } body.aw-highlight-links a:not(.accessiwidget-panel a) { background: yellow !important; padding: 2px 4px !important; } body.aw-align-center > *:not(.accessiwidget-btn):not(.accessiwidget-panel) * { text-align: center !important; } body.aw-align-left > *:not(.accessiwidget-btn):not(.accessiwidget-panel) * { text-align: left !important; } body.aw-highlight-hover > *:not(.accessiwidget-btn):not(.accessiwidget-panel) *:hover { outline: 3px solid ${config.brandColor} !important; } body.aw-highlight-focus > *:not(.accessiwidget-btn):not(.accessiwidget-panel) *:focus { outline: 3px solid ${config.brandColor} !important; } body.aw-big-black-cursor > *:not(.accessiwidget-btn):not(.accessiwidget-panel), body.aw-big-black-cursor > *:not(.accessiwidget-btn):not(.accessiwidget-panel) * { cursor: url('data:image/svg+xml;utf8,') 20 20, auto !important; } body.aw-big-white-cursor > *:not(.accessiwidget-btn):not(.accessiwidget-panel), body.aw-big-white-cursor > *:not(.accessiwidget-btn):not(.accessiwidget-panel) * { cursor: url('data:image/svg+xml;utf8,') 20 20, auto !important; } .accessiwidget-loading-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.7); display: flex; align-items: center; justify-content: center; z-index: 1000001; backdrop-filter: blur(4px); } .accessiwidget-loading-content { background: white; padding: 32px 48px; border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); text-align: center; } .accessiwidget-loading-spinner { width: 48px; height: 48px; border: 4px solid #f3f3f3; border-top: 4px solid ${config.brandColor}; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 16px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .accessiwidget-loading-text { font-size: 18px; font-weight: 600; color: #333; } `; document.head.appendChild(style); // Position classes const positions = { 'bottom-right': { bottom: '20px', right: '20px' }, 'bottom-left': { bottom: '20px', left: '20px' }, 'top-right': { top: '20px', right: '20px' }, 'top-left': { top: '20px', left: '20px' } }; const pos = positions[config.position] || positions['bottom-right']; // Create button const button = document.createElement('button'); button.className = 'accessiwidget-btn'; button.innerHTML = ''; Object.assign(button.style, pos); // Create panel const panel = document.createElement('div'); panel.className = 'accessiwidget-panel hidden'; // Calculate panel position relative to button const panelPos = {}; if (config.position.includes('bottom')) { panelPos.bottom = '80px'; } else { panelPos.top = '80px'; } if (config.position.includes('right')) { panelPos.right = '20px'; } else { panelPos.left = '20px'; } Object.assign(panel.style, panelPos); let sections = []; // Accessibility Profiles let profiles = []; if (config.features.profileSeizureSafe) { profiles.push(`
`); } if (config.features.profileVisionImpaired) { profiles.push(` `); } if (config.features.profileAdhdFriendly) { profiles.push(` `); } if (config.features.profileCognitive) { profiles.push(` `); } if (config.features.profileKeyboardNav) { profiles.push(` `); } if (config.features.profileScreenReader) { profiles.push(` `); } if (profiles.length > 0) { sections.push(` `); } // Content Adjustments let contentFeatures = []; if (config.features.textResizer || config.features.fontSizing) { contentFeatures.push(` `); } if (config.features.lineHeight) { contentFeatures.push(` `); } if (config.features.textSpacing) { contentFeatures.push(` `); } let contentGrid = []; if (config.features.readableFont) contentGrid.push(``); if (config.features.highlightTitles) contentGrid.push(``); if (config.features.highlightLinks) contentGrid.push(``); if (config.features.textMagnifier) contentGrid.push(``); if (config.features.alignCenter) contentGrid.push(``); if (config.features.alignLeft) contentGrid.push(``); if (contentFeatures.length > 0 || contentGrid.length > 0) { sections.push(` `); } // Color Adjustments let colorGrid = []; if (config.features.darkContrast) colorGrid.push(``); if (config.features.lightContrast) colorGrid.push(``); if (config.features.highContrast) colorGrid.push(``); if (config.features.highSaturation) colorGrid.push(``); if (config.features.lowSaturation) colorGrid.push(``); if (config.features.monochrome) colorGrid.push(``); if (colorGrid.length > 0) { sections.push(` `); } // Orientation & Navigation let orientationGrid = []; if (config.features.muteSounds) orientationGrid.push(``); if (config.features.hideImages) orientationGrid.push(``); if (config.features.readingGuide) orientationGrid.push(``); if (config.features.stopAnimations) orientationGrid.push(``); if (config.features.readingMask) orientationGrid.push(``); if (config.features.highlightHover) orientationGrid.push(``); if (config.features.highlightFocus) orientationGrid.push(``); if (config.features.bigBlackCursor) orientationGrid.push(``); if (config.features.bigWhiteCursor) orientationGrid.push(``); if (orientationGrid.length > 0) { sections.push(` `); } // Tab system for widget sections let currentTab = 'accessibility'; const tabs = [ { id: 'accessibility', label: 'Accessibility Tools', icon: 'βΏ' }, { id: 'security', label: 'Security', icon: 'π' }, { id: 'performance', label: 'Performance', icon: 'β‘' }, { id: 'modernization', label: 'Modernization', icon: 'π' } ]; const tabsHTML = tabs.map(tab => ` `).join(''); const tabsStyle = ` .accessiwidget-tabs { display: flex; gap: 8px; margin-bottom: 16px; border-bottom: 1px solid rgba(0,0,0,0.05); overflow-x: auto; padding-bottom: 0; } .accessiwidget-tab { display: flex; align-items: center; gap: 6px; padding: 10px 14px; background: none; border: none; cursor: pointer; font-size: 12px; font-weight: 500; color: #888; border-bottom: 2px solid transparent; transition: all 0.2s; white-space: nowrap; } .accessiwidget-tab:hover { color: #666; } .accessiwidget-tab.active { color: ${config.brandColor}; border-bottom-color: ${config.brandColor}; } .accessiwidget-tab-icon { font-size: 14px; } .accessiwidget-tab-label { font-size: 11px; } .accessiwidget-tab-content { display: none; } .accessiwidget-tab-content.active { display: block; } .accessiwidget-coming-soon { text-align: center; padding: 40px 20px; } .accessiwidget-coming-soon-icon { font-size: 48px; margin-bottom: 16px; } .accessiwidget-coming-soon-text { color: #888; font-size: 14px; margin-bottom: 8px; } .accessiwidget-coming-soon-desc { color: #aaa; font-size: 12px; } `; style.textContent += tabsStyle; panel.innerHTML = ` `; // Add to page document.body.appendChild(button); document.body.appendChild(panel); // Load saved state from localStorage const savedState = localStorage.getItem('accessiwidget_state'); let state = savedState ? JSON.parse(savedState) : { textSize: 0, lineHeight: 0, letterSpacing: 0, profiles: {}, features: {}, }; // Apply saved state on page load const applyState = () => { if (state.textSize === 1) document.body.classList.add('text-large'); if (state.textSize === 2) document.body.classList.add('text-larger'); if (state.lineHeight > 0) document.body.style.lineHeight = `${1.5 + state.lineHeight * 0.3}`; if (state.letterSpacing > 0) document.body.style.letterSpacing = `${state.letterSpacing}px`; Object.keys(state.features || {}).forEach(feature => { if (state.features[feature]) { const className = feature.replace(/([A-Z])/g, '-$1').toLowerCase(); document.body.classList.add(`aw-${className}`); // Re-create special features on page load if (feature === 'readingGuide') { createReadingGuide(); } if (feature === 'textMagnifier') { createMagnifier(); } } }); }; applyState(); // Save state helper const saveState = () => { localStorage.setItem('accessiwidget_state', JSON.stringify(state)); }; // Reading guide element let readingGuide = null; const createReadingGuide = () => { if (!readingGuide) { readingGuide = document.createElement('div'); readingGuide.style.cssText = ` position: fixed; left: 0; right: 0; height: 3px; background: ${config.brandColor}; pointer-events: none; z-index: 999998; box-shadow: 0 0 10px rgba(0,0,0,0.3); `; document.body.appendChild(readingGuide); document.addEventListener('mousemove', (e) => { if (readingGuide && state.features.readingGuide) { readingGuide.style.top = e.clientY + 'px'; } }); } readingGuide.style.display = 'block'; }; const removeReadingGuide = () => { if (readingGuide) { readingGuide.style.display = 'none'; } }; // Text magnifier element let magnifier = null; const createMagnifier = () => { if (!magnifier) { magnifier = document.createElement('div'); magnifier.style.cssText = ` position: fixed; width: 150px; height: 150px; border: 3px solid ${config.brandColor}; border-radius: 50%; pointer-events: none; z-index: 999998; display: none; background: white; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.3); `; document.body.appendChild(magnifier); document.addEventListener('mousemove', (e) => { if (magnifier && state.features.textMagnifier) { magnifier.style.display = 'block'; magnifier.style.left = (e.clientX - 75) + 'px'; magnifier.style.top = (e.clientY - 75) + 'px'; const text = document.elementFromPoint(e.clientX, e.clientY); if (text && text.textContent) { magnifier.textContent = text.textContent.slice(0, 100); magnifier.style.fontSize = '20px'; magnifier.style.padding = '20px'; magnifier.style.color = '#000'; } } }); } magnifier.style.display = 'block'; }; const removeMagnifier = () => { if (magnifier) { magnifier.style.display = 'none'; } }; // Helper to toggle features const toggleFeature = (featureName, el) => { const wasActive = state.features[featureName]; state.features[featureName] = !wasActive; const isActive = state.features[featureName]; const className = `aw-${featureName.replace(/([A-Z])/g, '-$1').toLowerCase()}`; const target = (featureName === 'darkContrast' || featureName === 'lightContrast') ? document.documentElement : document.body; if (isActive) { target.classList.add(className); if (el) el.classList.add('active'); } else { target.classList.remove(className); if (el) el.classList.remove('active'); } // Special handlers for certain features if (featureName === 'readingGuide') { if (state.features[featureName]) { createReadingGuide(); } else { removeReadingGuide(); } } if (featureName === 'textMagnifier') { if (state.features[featureName]) { createMagnifier(); } else { removeMagnifier(); } } // Profile handlers if (featureName === 'profileSeizure') { if (state.features[featureName]) { state.features.stopAnimations = true; state.features.lowSaturation = true; document.body.classList.add('aw-stop-animations', 'aw-low-saturation'); // Update UI for activated features const stopAnimEl = panel.querySelector('[data-action="stop-animations"]'); const lowSatEl = panel.querySelector('[data-action="low-saturation"]'); if (stopAnimEl) stopAnimEl.classList.add('active'); if (lowSatEl) lowSatEl.classList.add('active'); } else { state.features.stopAnimations = false; state.features.lowSaturation = false; document.body.classList.remove('aw-stop-animations', 'aw-low-saturation'); const stopAnimEl = panel.querySelector('[data-action="stop-animations"]'); const lowSatEl = panel.querySelector('[data-action="low-saturation"]'); if (stopAnimEl) stopAnimEl.classList.remove('active'); if (lowSatEl) lowSatEl.classList.remove('active'); } } if (featureName === 'profileVision') { if (state.features[featureName]) { state.textSize = 1; document.body.classList.add('text-large'); state.features.highContrast = true; document.body.classList.add('aw-high-contrast'); const highContEl = panel.querySelector('[data-action="high-contrast"]'); if (highContEl) highContEl.classList.add('active'); } else { state.textSize = 0; document.body.classList.remove('text-large', 'text-larger'); state.features.highContrast = false; document.body.classList.remove('aw-high-contrast'); const highContEl = panel.querySelector('[data-action="high-contrast"]'); if (highContEl) highContEl.classList.remove('active'); } } if (featureName === 'profileAdhd') { if (state.features[featureName]) { state.features.stopAnimations = true; state.features.readingGuide = true; document.body.classList.add('aw-stop-animations'); createReadingGuide(); const stopAnimEl = panel.querySelector('[data-action="stop-animations"]'); const readGuideEl = panel.querySelector('[data-action="reading-guide"]'); if (stopAnimEl) stopAnimEl.classList.add('active'); if (readGuideEl) readGuideEl.classList.add('active'); } else { state.features.stopAnimations = false; state.features.readingGuide = false; document.body.classList.remove('aw-stop-animations'); removeReadingGuide(); const stopAnimEl = panel.querySelector('[data-action="stop-animations"]'); const readGuideEl = panel.querySelector('[data-action="reading-guide"]'); if (stopAnimEl) stopAnimEl.classList.remove('active'); if (readGuideEl) readGuideEl.classList.remove('active'); } } if (featureName === 'profileCognitive') { if (state.features[featureName]) { state.features.readableFont = true; state.features.highlightLinks = true; document.body.classList.add('aw-readable-font', 'aw-highlight-links'); const readFontEl = panel.querySelector('[data-action="readable-font"]'); const highLinksEl = panel.querySelector('[data-action="highlight-links"]'); if (readFontEl) readFontEl.classList.add('active'); if (highLinksEl) highLinksEl.classList.add('active'); } else { state.features.readableFont = false; state.features.highlightLinks = false; document.body.classList.remove('aw-readable-font', 'aw-highlight-links'); const readFontEl = panel.querySelector('[data-action="readable-font"]'); const highLinksEl = panel.querySelector('[data-action="highlight-links"]'); if (readFontEl) readFontEl.classList.remove('active'); if (highLinksEl) highLinksEl.classList.remove('active'); } } if (featureName === 'profileKeyboard') { if (state.features[featureName]) { state.features.highlightFocus = true; document.body.classList.add('aw-highlight-focus'); const focusEl = panel.querySelector('[data-action="highlight-focus"]'); if (focusEl) focusEl.classList.add('active'); } else { state.features.highlightFocus = false; document.body.classList.remove('aw-highlight-focus'); const focusEl = panel.querySelector('[data-action="highlight-focus"]'); if (focusEl) focusEl.classList.remove('active'); } } if (featureName === 'stopAnimations') { const target = (featureName === 'darkContrast' || featureName === 'lightContrast') ? document.documentElement : document.body; if (state.features[featureName]) { target.classList.add(`aw-${featureName.replace(/([A-Z])/g, '-$1').toLowerCase()}`); if (el) el.classList.add('active'); // Pause all videos and audio document.querySelectorAll('video, audio').forEach(media => media.pause()); } else { target.classList.remove(`aw-${featureName.replace(/([A-Z])/g, '-$1').toLowerCase()}`); if (el) el.classList.remove('active'); } saveState(); return; } if (featureName === 'profileReader') { if (state.features[featureName]) { state.features.readableFont = true; document.body.classList.add('aw-readable-font'); const readFontEl = panel.querySelector('[data-action="readable-font"]'); if (readFontEl) readFontEl.classList.add('active'); // Start reading page content if (window.speechSynthesis) { const pageText = document.body.innerText; const utterance = new SpeechSynthesisUtterance(pageText); utterance.rate = 0.9; window.speechSynthesis.speak(utterance); } } else { state.features.readableFont = false; document.body.classList.remove('aw-readable-font'); const readFontEl = panel.querySelector('[data-action="readable-font"]'); if (readFontEl) readFontEl.classList.remove('active'); // Stop reading if (window.speechSynthesis) { window.speechSynthesis.cancel(); } } } saveState(); }; // Global functions window.AccessiWidget = { closePanel: () => panel.classList.add('hidden'), openPanel: () => panel.classList.remove('hidden'), // Text size increaseText: () => { state.textSize = Math.min(state.textSize + 1, 2); document.body.classList.remove('text-large', 'text-larger'); if (state.textSize === 1) document.body.classList.add('text-large'); if (state.textSize === 2) document.body.classList.add('text-larger'); saveState(); }, decreaseText: () => { state.textSize = Math.max(state.textSize - 1, 0); document.body.classList.remove('text-large', 'text-larger'); if (state.textSize === 1) document.body.classList.add('text-large'); saveState(); }, resetText: () => { state.textSize = 0; document.body.classList.remove('text-large', 'text-larger'); saveState(); }, // Line height increaseLineHeight: () => { state.lineHeight = Math.min(state.lineHeight + 1, 3); document.body.style.lineHeight = `${1.5 + state.lineHeight * 0.3}`; saveState(); }, decreaseLineHeight: () => { state.lineHeight = Math.max(state.lineHeight - 1, 0); document.body.style.lineHeight = state.lineHeight === 0 ? '' : `${1.5 + state.lineHeight * 0.3}`; saveState(); }, resetLineHeight: () => { state.lineHeight = 0; document.body.style.lineHeight = ''; saveState(); }, // Letter spacing increaseSpacing: () => { state.letterSpacing = Math.min(state.letterSpacing + 1, 5); document.body.style.letterSpacing = `${state.letterSpacing}px`; saveState(); }, decreaseSpacing: () => { state.letterSpacing = Math.max(state.letterSpacing - 1, 0); document.body.style.letterSpacing = state.letterSpacing === 0 ? '' : `${state.letterSpacing}px`; saveState(); }, resetSpacing: () => { state.letterSpacing = 0; document.body.style.letterSpacing = ''; saveState(); }, // Feature toggles toggleFeature: (name, el) => toggleFeature(name, el), }; // Set active states on elements based on saved state setTimeout(() => { Object.keys(state.features || {}).forEach(feature => { if (state.features[feature]) { const el = panel.querySelector(`[data-action="${feature.replace(/([A-Z])/g, '-$1').toLowerCase()}"]`); if (el) el.classList.add('active'); } }); }, 100); // Tab switching - show all tabs for admins, hide for non-authenticated users panel.querySelectorAll('.accessiwidget-tab').forEach(tabBtn => { if (!isAdmin && tabBtn.dataset.tab !== 'accessibility') { tabBtn.style.display = 'none'; } tabBtn.addEventListener('click', () => { const tabId = tabBtn.dataset.tab; // Update active tab button panel.querySelectorAll('.accessiwidget-tab').forEach(btn => { btn.classList.remove('active'); }); tabBtn.classList.add('active'); // Update active tab content panel.querySelectorAll('.accessiwidget-tab-content').forEach(content => { content.classList.remove('active'); }); const tabContent = panel.querySelector(`#${tabId}-tab`); if (tabContent) { tabContent.classList.add('active'); } }); }); // Set accessibility tab as active by default const accessibilityTab = panel.querySelector('[data-tab="accessibility"]'); if (accessibilityTab) { accessibilityTab.classList.add('active'); } // Security tab - load vulnerabilities (isolated) let cachedSecurityData = null; const loadSecurityAudit = async (forceRefresh = false) => { try { const securityTab = panel.querySelector('#security-tab'); if (!securityTab) return; // Check cache first if (cachedSecurityData && !forceRefresh) { renderSecurityVulnerabilities(cachedSecurityData, securityTab); return; } securityTab.innerHTML = ''; const response = await fetch('https://accessi-widget-2f90b83b.base44.app/api/functions/scanSiteVulnerabilities', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: window.location.href.split('?')[0], widget_id: config.id }) }); const data = await response.json(); cachedSecurityData = data; renderSecurityVulnerabilities(data, securityTab); } catch (error) { console.warn('Failed to load security audit:', error); try { const securityTab = panel.querySelector('#security-tab'); if (securityTab) { securityTab.innerHTML = `No vulnerabilities detected