AccesSight Pro

outils d'accessibilité pour les malvoyants

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         AccesSight Pro
// @namespace    http://tampermonkey.net/
// @version      5.0
// @description  outils d'accessibilité pour les malvoyants
// @author       Yglsan
// @include      *
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @license      GPL-3.0
// ==/UserScript==

(function() {
    'use strict';

    // Configuration initiale
    const defaultConfig = {
        textSize: 16,
        contrastMode: 'normal',
        speechRate: 1,
        highlightLinks: true,
        darkMode: false,
        savedPosition: { x: 20, y: 20 }
    };

    // État courant
    let config = {
        ...defaultConfig,
        ...GM_getValue('accessightConfig', {})
    };

    // Création de l'interface utilisateur
    function createAccessibilityPanel() {
        const panel = document.createElement('div');
        panel.id = 'accessight-panel';
        panel.style.cssText = `
            position: fixed;
            top: ${config.savedPosition.y}px;
            left: ${config.savedPosition.x}px;
            background: ${config.darkMode ? '#333' : '#FFF'};
            color: ${config.darkMode ? '#FFF' : '#000'};
            padding: 1rem;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
            z-index: 10000;
            font-family: Arial, sans-serif;
            min-width: 250px;
            cursor: move;
        `;

        // Header avec titre et bouton de fermeture
        const header = document.createElement('div');
        header.innerHTML = `
            <h2 style="margin: 0 0 1rem 0; font-size: 1.2rem;">AccesSight</h2>
            <button aria-label="Fermer" style="position: absolute; top: 5px; right: 5px; background: none; border: none; cursor: pointer;">×</button>
        `;
        panel.appendChild(header);

        // Contrôles principaux
        const controls = [
            { type: 'range', label: 'Taille du texte', min: 12, max: 24, value: config.textSize, key: 'textSize' },
            { type: 'select', label: 'Contraste', options: ['Normal', 'Élevé', 'Inversé'], value: config.contrastMode, key: 'contrastMode' },
            { type: 'button', label: 'Lecture vocale', action: 'toggleSpeech' },
            { type: 'toggle', label: 'Mode sombre', value: config.darkMode, key: 'darkMode' },
            { type: 'toggle', label: 'Surbrillance liens', value: config.highlightLinks, key: 'highlightLinks' }
        ];

        controls.forEach(control => {
            const controlGroup = document.createElement('div');
            controlGroup.style.marginBottom = '0.5rem';

            switch (control.type) {
                case 'range':
                    controlGroup.innerHTML = `
                        <label style="display: block; margin-bottom: 0.3rem;">
                            ${control.label}: <output>${control.value}px</output>
                        </label>
                        <input type="range" 
                               min="${control.min}" 
                               max="${control.max}" 
                               value="${control.value}" 
                               style="width: 100%;"
                               data-key="${control.key}">
                    `;
                    break;

                case 'select':
                    controlGroup.innerHTML = `
                        <label style="display: block; margin-bottom: 0.3rem;">${control.label}</label>
                        <select style="width: 100%;" data-key="${control.key}">
                            ${control.options.map(opt => 
                                `<option value="${opt.toLowerCase()}" ${opt.toLowerCase() === control.value ? 'selected' : ''}>${opt}</option>`
                            ).join('')}
                        </select>
                    `;
                    break;

                case 'toggle':
                    controlGroup.innerHTML = `
                        <label style="display: flex; align-items: center; gap: 0.5rem;">
                            <input type="checkbox" ${control.value ? 'checked' : ''} data-key="${control.key}">
                            ${control.label}
                        </label>
                    `;
                    break;

                case 'button':
                    controlGroup.innerHTML = `
                        <button style="width: 100%; padding: 0.5rem;" data-action="${control.action}">
                            ${control.label}
                        </button>
                    `;
                    break;
            }

            panel.appendChild(controlGroup);
        });

        // Gestion des événements
        panel.querySelectorAll('input, select').forEach(element => {
            element.addEventListener('change', handleSettingChange);
        });

        panel.querySelector('[data-action="toggleSpeech"]').addEventListener('click', toggleTextToSpeech);

        // Déplacement du panel
        let isDragging = false;
        let initialX, initialY, currentX, currentY;

        panel.addEventListener('mousedown', startDragging);
        document.addEventListener('mousemove', dragPanel);
        document.addEventListener('mouseup', stopDragging);

        function startDragging(e) {
            isDragging = true;
            initialX = e.clientX - panel.offsetLeft;
            initialY = e.clientY - panel.offsetTop;
        }

        function dragPanel(e) {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                panel.style.left = `${currentX}px`;
                panel.style.top = `${currentY}px`;
            }
        }

        function stopDragging() {
            isDragging = false;
            GM_setValue('accessightConfig', {
                ...config,
                savedPosition: { x: currentX, y: currentY }
            });
        }

        document.body.appendChild(panel);
        applyAccessibilitySettings();
    }

    // Appliquer les paramètres d'accessibilité
    function applyAccessibilitySettings() {
        // Taille du texte
        document.documentElement.style.fontSize = `${config.textSize}px`;

        // Contraste
        switch (config.contrastMode) {
            case 'élevé':
                GM_addStyle(`
                    body {
                        filter: contrast(150%);
                    }
                `);
                break;
            case 'inversé':
                GM_addStyle(`
                    body {
                        filter: invert(1) hue-rotate(180deg);
                    }
                `);
                break;
        }

        // Mode sombre
        if (config.darkMode) {
            GM_addStyle(`
                body {
                    background: #111 !important;
                    color: #FFF !important;
                }
            `);
        }

        // Surbrillance des liens
        if (config.highlightLinks) {
            GM_addStyle(`
                a {
                    outline: 2px solid #FF0000 !important;
                    padding: 2px !important;
                }
            `);
        }
    }

    // Gestion des changements de paramètres
    function handleSettingChange(e) {
        const key = e.target.dataset.key;
        let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;

        if (key === 'textSize') value = parseInt(value);
        
        config[key] = value;
        GM_setValue('accessightConfig', config);
        applyAccessibilitySettings();

        if (key === 'textSize') {
            e.target.parentNode.querySelector('output').textContent = `${value}px`;
        }
    }

    // Synthèse vocale
    let speech = null;
    function toggleTextToSpeech() {
        if (!speech) {
            speech = new window.SpeechSynthesisUtterance();
            speech.text = document.body.textContent;
            speech.rate = config.speechRate;
            window.speechSynthesis.speak(speech);
        } else {
            window.speechSynthesis.cancel();
            speech = null;
        }
    }

    // Initialisation
    function init() {
        if (!document.getElementById('accessight-panel')) {
            createAccessibilityPanel();
        }
    }

    // Démarrage
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();