(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(`
Seizure Safe
Reduces flashes & motion
`); } if (config.features.profileVisionImpaired) { profiles.push(`
Vision Impaired
Enhanced visuals
`); } if (config.features.profileAdhdFriendly) { profiles.push(`
ADHD Friendly
More focus, less distractions
`); } if (config.features.profileCognitive) { profiles.push(`
Cognitive Disability
Assists reading & focusing
`); } if (config.features.profileKeyboardNav) { profiles.push(`
Keyboard Navigation
Use keyboard to navigate
`); } if (config.features.profileScreenReader) { profiles.push(`
Screen Reader
Optimized for screen readers
`); } if (profiles.length > 0) { sections.push(`
Accessibility Profiles
${profiles.join('')}
`); } // Content Adjustments let contentFeatures = []; if (config.features.textResizer || config.features.fontSizing) { contentFeatures.push(`
Text Size
`); } if (config.features.lineHeight) { contentFeatures.push(`
Line Height
`); } if (config.features.textSpacing) { contentFeatures.push(`
Letter Spacing
`); } let contentGrid = []; if (config.features.readableFont) contentGrid.push(`
πŸ“–
Readable Font
`); if (config.features.highlightTitles) contentGrid.push(`
πŸ“
Highlight Titles
`); if (config.features.highlightLinks) contentGrid.push(`
πŸ”—
Highlight Links
`); if (config.features.textMagnifier) contentGrid.push(`
πŸ”
Text Magnifier
`); if (config.features.alignCenter) contentGrid.push(`
↔️
Align Center
`); if (config.features.alignLeft) contentGrid.push(`
←
Align Left
`); if (contentFeatures.length > 0 || contentGrid.length > 0) { sections.push(`
Content Adjustments
${contentFeatures.join('')} ${contentGrid.length > 0 ? `
${contentGrid.join('')}
` : ''}
`); } // Color Adjustments let colorGrid = []; if (config.features.darkContrast) colorGrid.push(`
πŸŒ™
Dark Contrast
`); if (config.features.lightContrast) colorGrid.push(`
β˜€οΈ
Light Contrast
`); if (config.features.highContrast) colorGrid.push(`
βšͺ
High Contrast
`); if (config.features.highSaturation) colorGrid.push(`
🎨
High Saturation
`); if (config.features.lowSaturation) colorGrid.push(`
πŸ’§
Low Saturation
`); if (config.features.monochrome) colorGrid.push(`
⚫
Monochrome
`); if (colorGrid.length > 0) { sections.push(`
Color Adjustments
${colorGrid.join('')}
`); } // Orientation & Navigation let orientationGrid = []; if (config.features.muteSounds) orientationGrid.push(`
πŸ”‡
Mute Sounds
`); if (config.features.hideImages) orientationGrid.push(`
πŸ–ΌοΈ
Hide Images
`); if (config.features.readingGuide) orientationGrid.push(`
πŸ“
Reading Guide
`); if (config.features.stopAnimations) orientationGrid.push(`
⏸️
Stop Animations
`); if (config.features.readingMask) orientationGrid.push(`
πŸ“„
Reading Mask
`); if (config.features.highlightHover) orientationGrid.push(`
✨
Highlight Hover
`); if (config.features.highlightFocus) orientationGrid.push(`
🎯
Highlight Focus
`); if (config.features.bigBlackCursor) orientationGrid.push(`
⚫
Big Black Cursor
`); if (config.features.bigWhiteCursor) orientationGrid.push(`
βšͺ
Big White Cursor
`); if (orientationGrid.length > 0) { sections.push(`
Navigation & Orientation
${orientationGrid.join('')}
`); } // 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 = `
${config.clientName || 'Accessibility Tools'}
${tabsHTML}
${sections.join('')}

Unlock premium features

Signed in as

${userEmail}

Scanning for vulnerabilities...

Analyzing performance...

Analyzing site modernization...

`; // 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 = '

Scanning for vulnerabilities...

'; 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 = `
Failed to load security audit
`; const retryBtn = securityTab.querySelector('.accessiwidget-retry-btn'); if (retryBtn) { retryBtn.addEventListener('click', () => loadSecurityAudit(true)); } } } catch (renderError) { console.warn('Error rendering security error state:', renderError); } } }; // Render vulnerabilities to the security tab (isolated) const renderSecurityVulnerabilities = (data, securityTab) => { try { if (!securityTab) return; if (data.vulnerabilities) { let html = ''; if (data.vulnerabilities.length === 0) { html = `
βœ“

No vulnerabilities detected

`; } else { html = '
'; data.vulnerabilities.forEach((vuln, idx) => { if (!vuln.is_resolved) { const severityColors = { critical: '#ef4444', high: '#f97316', medium: '#eab308', low: '#3b82f6' }; const vulnId = `vuln-${idx}`; html += `
${vuln.name}
${vuln.description}
`; } }); html += '
'; } securityTab.innerHTML = html; // Add toggle functionality securityTab.querySelectorAll('.vuln-toggle').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const vulnId = btn.dataset.vuln; const content = securityTab.querySelector(`#${vulnId}`); if (content) { const isOpen = content.style.display !== 'none'; content.style.display = isOpen ? 'none' : 'block'; btn.style.transform = isOpen ? 'rotate(0deg)' : 'rotate(180deg)'; } }); }); } } catch (tabError) { console.warn('Error rendering security tab:', tabError); if (securityTab) { securityTab.innerHTML = '
Unable to load security audit
'; } } }; // Performance metrics (isolated) let cachedPerformanceData = null; const loadPerformanceMetrics = async (forceRefresh = false) => { try { const perfTab = panel.querySelector('#performance-tab'); if (!perfTab) return; // Check cache first if (cachedPerformanceData && !forceRefresh) { renderPerformanceMetrics(cachedPerformanceData, perfTab); return; } perfTab.innerHTML = '

Analyzing performance...

'; const response = await fetch('https://accessi-widget-2f90b83b.base44.app/api/functions/scanSitePerformance', { 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(); cachedPerformanceData = data; renderPerformanceMetrics(data, perfTab); } catch (error) { console.warn('Failed to load performance metrics:', error); try { const perfTab = panel.querySelector('#performance-tab'); if (perfTab) { perfTab.innerHTML = `
Failed to load performance metrics
`; const retryBtn = perfTab.querySelector('.accessiwidget-retry-btn'); if (retryBtn) { retryBtn.addEventListener('click', () => loadPerformanceMetrics(true)); } } } catch (renderError) { console.warn('Error rendering performance error state:', renderError); } } }; // Render performance metrics (isolated) const renderPerformanceMetrics = (data, perfTab) => { try { if (!perfTab) return; if (data.metrics) { let html = '
'; // Metrics cards html += `
Load Time
${data.metrics.loadTime}
Page Size
${data.metrics.pageSize}
`; // Resource counts html += `
Resources
${data.metrics.scriptCount} Scripts
${data.metrics.cssCount} CSS Files
${data.metrics.imageCount} Images
${data.metrics.hasLazyLoading ? '
βœ“ Lazy loading detected
' : '
βœ— No lazy loading detected
'}
`; // Suggestions if (data.suggestions && data.suggestions.length > 0) { html += '
Suggestions
'; data.suggestions.forEach(suggestion => { html += `
πŸ’‘ ${suggestion}
`; }); html += '
'; } html += '
'; perfTab.innerHTML = html; } } catch (tabError) { console.warn('Error rendering performance tab:', tabError); if (perfTab) { perfTab.innerHTML = '
Unable to load performance metrics
'; } } }; // Modernization analysis (isolated) let cachedModernizationData = null; const loadModernizationAnalysis = async (forceRefresh = false) => { try { const modernTab = panel.querySelector('#modernization-tab'); if (!modernTab) return; // Check cache first if (cachedModernizationData && !forceRefresh) { renderModernizationAnalysis(cachedModernizationData, modernTab); return; } modernTab.innerHTML = '

Analyzing site modernization...

'; const response = await fetch('https://accessi-widget-2f90b83b.base44.app/api/functions/scanSiteModernization', { 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(); cachedModernizationData = data; renderModernizationAnalysis(data, modernTab); } catch (error) { console.warn('Failed to load modernization analysis:', error); try { const modernTab = panel.querySelector('#modernization-tab'); if (modernTab) { modernTab.innerHTML = `
Failed to load modernization analysis
`; const retryBtn = modernTab.querySelector('.accessiwidget-retry-btn'); if (retryBtn) { retryBtn.addEventListener('click', () => loadModernizationAnalysis(true)); } } } catch (renderError) { console.warn('Error rendering modernization error state:', renderError); } } }; const renderModernizationAnalysis = (data, modernTab) => { try { if (!modernTab) return; if (data.primaryCategory) { let html = '
'; // Category detection html += '
' + '
Primary Category
' + '
' + data.primaryCategory + '
' + (data.primaryCategoryPercentage ? '
' + data.primaryCategoryPercentage + '% match
' : '') + '
'; // Secondary categories if (data.secondaryCategories && data.secondaryCategories.length > 0) { html += '
Secondary Categories
'; data.secondaryCategories.forEach(cat => { const pct = data.categoryPercentages[cat] || 0; html += `
${cat}${pct}%
`; }); html += '
'; } // Typography consistency html += '
' + '
Typography
' + '
' + data.textConsistency.fontFamilies + ' font families
' + '
' + data.textConsistency.fontSizes + ' font sizes
' + '
' + data.textConsistency.colorUsage + ' colors used
'; data.textConsistency.inconsistencies.forEach(inc => { const isGood = inc.includes('Good'); html += '
' + (isGood ? 'βœ“' : '⚠️') + ' ' + inc + '
'; }); html += '
'; // UX Recommendations if (data.uxRecommendations && data.uxRecommendations.length > 0) { html += '
User Experience
'; data.uxRecommendations.forEach(rec => { html += '
β†’ ' + rec + '
'; }); html += '
'; } // Mobile readiness html += '
' + '
' + (data.mobileReady ? 'βœ“ Mobile Responsive' : 'βœ— Mobile Optimization Needed') + '
' + '
'; // General suggestions if (data.suggestions && data.suggestions.length > 0) { html += '
'; data.suggestions.forEach(sug => { html += '
πŸ’‘ ' + sug + '
'; }); html += '
'; } html += '
'; modernTab.innerHTML = html; } } catch (tabError) { console.warn('Error rendering modernization tab:', tabError); if (modernTab) { modernTab.innerHTML = '
Unable to load modernization analysis
'; } } }; // Load tabs when clicked panel.querySelectorAll('.accessiwidget-tab').forEach(tab => { if (tab.dataset.tab === 'security') { tab.addEventListener('click', loadSecurityAudit); } else if (tab.dataset.tab === 'performance') { tab.addEventListener('click', loadPerformanceMetrics); } else if (tab.dataset.tab === 'modernization') { tab.addEventListener('click', loadModernizationAnalysis); } }); // Listen for admin login postMessage window.addEventListener('message', (event) => { // Verify origin for security if (event.data && event.data.type === 'WIDGET_ADMIN_TOKEN') { const { token, email, expiresAt } = event.data; // Verify token with backend fetch('https://accessi-widget-2f90b83b.base44.app/api/functions/verifyWidgetAdminToken', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, widgetId: config.id }) }) .then(r => r.json()) .then(data => { if (data.valid) { // Store token sessionStorage.setItem('accessiwidget_admin_token', token); sessionStorage.setItem('accessiwidget_admin_expiry', expiresAt); sessionStorage.setItem('accessiwidget_admin_email', email); // Reload to apply admin access location.reload(); } }) .catch(err => console.error('Token verification failed:', err)); } }); // Attach event listeners using data-action attributes panel.querySelectorAll('[data-action]').forEach(el => { const action = el.dataset.action; if (action === 'close') { el.addEventListener('click', () => window.AccessiWidget.closePanel()); } else if (action === 'admin-login') { el.addEventListener('click', () => { const returnOrigin = window.location.origin; const popupUrl = `https://accessi-widget-2f90b83b.base44.app/WidgetAdminLogin?widgetId=${config.id}&returnOrigin=${encodeURIComponent(returnOrigin)}`; window.open(popupUrl, 'WidgetAdminLogin', 'width=500,height=600,scrollbars=yes'); }); } else if (action === 'logout') { el.addEventListener('click', () => { sessionStorage.removeItem('accessiwidget_admin_token'); sessionStorage.removeItem('accessiwidget_admin_expiry'); sessionStorage.removeItem('accessiwidget_admin_email'); location.reload(); }); } else if (action === 'decText') { el.addEventListener('click', () => window.AccessiWidget.decreaseText()); } else if (action === 'resetText') { el.addEventListener('click', () => window.AccessiWidget.resetText()); } else if (action === 'incText') { el.addEventListener('click', () => window.AccessiWidget.increaseText()); } else if (action === 'decLineHeight') { el.addEventListener('click', () => window.AccessiWidget.decreaseLineHeight()); } else if (action === 'resetLineHeight') { el.addEventListener('click', () => window.AccessiWidget.resetLineHeight()); } else if (action === 'incLineHeight') { el.addEventListener('click', () => window.AccessiWidget.increaseLineHeight()); } else if (action === 'decSpacing') { el.addEventListener('click', () => window.AccessiWidget.decreaseSpacing()); } else if (action === 'resetSpacing') { el.addEventListener('click', () => window.AccessiWidget.resetSpacing()); } else if (action === 'incSpacing') { el.addEventListener('click', () => window.AccessiWidget.increaseSpacing()); } else { // Handle all other feature toggles const featureName = action.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); el.addEventListener('click', () => window.AccessiWidget.toggleFeature(featureName, el)); } }); // Toggle panel on button click let touchHandled = false; button.addEventListener('touchstart', (e) => { e.preventDefault(); e.stopPropagation(); touchHandled = true; panel.classList.toggle('hidden'); }, { passive: false }); button.addEventListener('click', (e) => { if (touchHandled) { touchHandled = false; return; } e.preventDefault(); e.stopPropagation(); panel.classList.toggle('hidden'); }); // Close panel when clicking outside document.addEventListener('click', (e) => { if (!button.contains(e.target) && !panel.contains(e.target)) { panel.classList.add('hidden'); } }); } })();