AccesSight Pro

outils d'accessibilité pour les malvoyants

  1. // ==UserScript==
  2. // @name AccesSight Pro
  3. // @namespace http://tampermonkey.net/
  4. // @version 5.0
  5. // @description outils d'accessibilité pour les malvoyants
  6. // @author Yglsan
  7. // @include *
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_addStyle
  11. // @license GPL-3.0
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. // Configuration initiale
  18. const defaultConfig = {
  19. textSize: 16,
  20. contrastMode: 'normal',
  21. speechRate: 1,
  22. highlightLinks: true,
  23. darkMode: false,
  24. savedPosition: { x: 20, y: 20 }
  25. };
  26.  
  27. // État courant
  28. let config = {
  29. ...defaultConfig,
  30. ...GM_getValue('accessightConfig', {})
  31. };
  32.  
  33. // Création de l'interface utilisateur
  34. function createAccessibilityPanel() {
  35. const panel = document.createElement('div');
  36. panel.id = 'accessight-panel';
  37. panel.style.cssText = `
  38. position: fixed;
  39. top: ${config.savedPosition.y}px;
  40. left: ${config.savedPosition.x}px;
  41. background: ${config.darkMode ? '#333' : '#FFF'};
  42. color: ${config.darkMode ? '#FFF' : '#000'};
  43. padding: 1rem;
  44. border-radius: 8px;
  45. box-shadow: 0 4px 12px rgba(0,0,0,0.2);
  46. z-index: 10000;
  47. font-family: Arial, sans-serif;
  48. min-width: 250px;
  49. cursor: move;
  50. `;
  51.  
  52. // Header avec titre et bouton de fermeture
  53. const header = document.createElement('div');
  54. header.innerHTML = `
  55. <h2 style="margin: 0 0 1rem 0; font-size: 1.2rem;">AccesSight</h2>
  56. <button aria-label="Fermer" style="position: absolute; top: 5px; right: 5px; background: none; border: none; cursor: pointer;">×</button>
  57. `;
  58. panel.appendChild(header);
  59.  
  60. // Contrôles principaux
  61. const controls = [
  62. { type: 'range', label: 'Taille du texte', min: 12, max: 24, value: config.textSize, key: 'textSize' },
  63. { type: 'select', label: 'Contraste', options: ['Normal', 'Élevé', 'Inversé'], value: config.contrastMode, key: 'contrastMode' },
  64. { type: 'button', label: 'Lecture vocale', action: 'toggleSpeech' },
  65. { type: 'toggle', label: 'Mode sombre', value: config.darkMode, key: 'darkMode' },
  66. { type: 'toggle', label: 'Surbrillance liens', value: config.highlightLinks, key: 'highlightLinks' }
  67. ];
  68.  
  69. controls.forEach(control => {
  70. const controlGroup = document.createElement('div');
  71. controlGroup.style.marginBottom = '0.5rem';
  72.  
  73. switch (control.type) {
  74. case 'range':
  75. controlGroup.innerHTML = `
  76. <label style="display: block; margin-bottom: 0.3rem;">
  77. ${control.label}: <output>${control.value}px</output>
  78. </label>
  79. <input type="range"
  80. min="${control.min}"
  81. max="${control.max}"
  82. value="${control.value}"
  83. style="width: 100%;"
  84. data-key="${control.key}">
  85. `;
  86. break;
  87.  
  88. case 'select':
  89. controlGroup.innerHTML = `
  90. <label style="display: block; margin-bottom: 0.3rem;">${control.label}</label>
  91. <select style="width: 100%;" data-key="${control.key}">
  92. ${control.options.map(opt =>
  93. `<option value="${opt.toLowerCase()}" ${opt.toLowerCase() === control.value ? 'selected' : ''}>${opt}</option>`
  94. ).join('')}
  95. </select>
  96. `;
  97. break;
  98.  
  99. case 'toggle':
  100. controlGroup.innerHTML = `
  101. <label style="display: flex; align-items: center; gap: 0.5rem;">
  102. <input type="checkbox" ${control.value ? 'checked' : ''} data-key="${control.key}">
  103. ${control.label}
  104. </label>
  105. `;
  106. break;
  107.  
  108. case 'button':
  109. controlGroup.innerHTML = `
  110. <button style="width: 100%; padding: 0.5rem;" data-action="${control.action}">
  111. ${control.label}
  112. </button>
  113. `;
  114. break;
  115. }
  116.  
  117. panel.appendChild(controlGroup);
  118. });
  119.  
  120. // Gestion des événements
  121. panel.querySelectorAll('input, select').forEach(element => {
  122. element.addEventListener('change', handleSettingChange);
  123. });
  124.  
  125. panel.querySelector('[data-action="toggleSpeech"]').addEventListener('click', toggleTextToSpeech);
  126.  
  127. // Déplacement du panel
  128. let isDragging = false;
  129. let initialX, initialY, currentX, currentY;
  130.  
  131. panel.addEventListener('mousedown', startDragging);
  132. document.addEventListener('mousemove', dragPanel);
  133. document.addEventListener('mouseup', stopDragging);
  134.  
  135. function startDragging(e) {
  136. isDragging = true;
  137. initialX = e.clientX - panel.offsetLeft;
  138. initialY = e.clientY - panel.offsetTop;
  139. }
  140.  
  141. function dragPanel(e) {
  142. if (isDragging) {
  143. e.preventDefault();
  144. currentX = e.clientX - initialX;
  145. currentY = e.clientY - initialY;
  146. panel.style.left = `${currentX}px`;
  147. panel.style.top = `${currentY}px`;
  148. }
  149. }
  150.  
  151. function stopDragging() {
  152. isDragging = false;
  153. GM_setValue('accessightConfig', {
  154. ...config,
  155. savedPosition: { x: currentX, y: currentY }
  156. });
  157. }
  158.  
  159. document.body.appendChild(panel);
  160. applyAccessibilitySettings();
  161. }
  162.  
  163. // Appliquer les paramètres d'accessibilité
  164. function applyAccessibilitySettings() {
  165. // Taille du texte
  166. document.documentElement.style.fontSize = `${config.textSize}px`;
  167.  
  168. // Contraste
  169. switch (config.contrastMode) {
  170. case 'élevé':
  171. GM_addStyle(`
  172. body {
  173. filter: contrast(150%);
  174. }
  175. `);
  176. break;
  177. case 'inversé':
  178. GM_addStyle(`
  179. body {
  180. filter: invert(1) hue-rotate(180deg);
  181. }
  182. `);
  183. break;
  184. }
  185.  
  186. // Mode sombre
  187. if (config.darkMode) {
  188. GM_addStyle(`
  189. body {
  190. background: #111 !important;
  191. color: #FFF !important;
  192. }
  193. `);
  194. }
  195.  
  196. // Surbrillance des liens
  197. if (config.highlightLinks) {
  198. GM_addStyle(`
  199. a {
  200. outline: 2px solid #FF0000 !important;
  201. padding: 2px !important;
  202. }
  203. `);
  204. }
  205. }
  206.  
  207. // Gestion des changements de paramètres
  208. function handleSettingChange(e) {
  209. const key = e.target.dataset.key;
  210. let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
  211.  
  212. if (key === 'textSize') value = parseInt(value);
  213. config[key] = value;
  214. GM_setValue('accessightConfig', config);
  215. applyAccessibilitySettings();
  216.  
  217. if (key === 'textSize') {
  218. e.target.parentNode.querySelector('output').textContent = `${value}px`;
  219. }
  220. }
  221.  
  222. // Synthèse vocale
  223. let speech = null;
  224. function toggleTextToSpeech() {
  225. if (!speech) {
  226. speech = new window.SpeechSynthesisUtterance();
  227. speech.text = document.body.textContent;
  228. speech.rate = config.speechRate;
  229. window.speechSynthesis.speak(speech);
  230. } else {
  231. window.speechSynthesis.cancel();
  232. speech = null;
  233. }
  234. }
  235.  
  236. // Initialisation
  237. function init() {
  238. if (!document.getElementById('accessight-panel')) {
  239. createAccessibilityPanel();
  240. }
  241. }
  242.  
  243. // Démarrage
  244. if (document.readyState === 'loading') {
  245. document.addEventListener('DOMContentLoaded', init);
  246. } else {
  247. init();
  248. }
  249. })();