AccesSight Pro

outils d'accessibilité pour les malvoyants

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==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();
    }
})();