1document.addEventListener('DOMContentLoaded', function () { 2 3 var panel = document.getElementById('a11yPanel'); 4 var toggleBtn = document.getElementById('a11yToggleBtn'); 5 if (!panel || !toggleBtn) return; 6 7 var body = document.body; 8 var root = document.documentElement; 9 10 var closeBtn = document.getElementById('a11yClose'); 11 var resetBtn = document.getElementById('a11yReset'); 12 13 var viewModeClasses = ['spcNormal', 'spcGrayscale', 'spcNoImage']; 14 var modeBoxes = document.querySelectorAll('.a11y-mode-box'); 15 16 var fontSlider = document.getElementById('f-size'); 17 var fontVal = document.getElementById('f-size-val'); 18 var lineHeightSlider = document.getElementById('l-height'); 19 var lineHeightVal = document.getElementById('l-height-val'); 20 21 var alignGroup = document.getElementById('a11yAlign'); 22 var fontFamilySelect = document.getElementById('a11yFontFamily'); 23 24 var readableFontToggle = document.getElementById('a11yReadableFont'); 25 var readingGuideToggle = document.getElementById('a11yReadingGuide'); 26 var bigCursorToggle = document.getElementById('a11yBigCursor'); 27 28 var selectionBtn = document.getElementById('a11ySelectionBtn'); 29 30 var PANEL_OPEN_KEY = 'a11y_panel_open'; 31 var READABLE_STACK = 'Verdana, Tahoma, "Segoe UI", Arial, sans-serif'; 32 33 var STORAGE_KEYS = [ 34 'a11y_mode', 35 'a11y_font', 36 'a11y_line_height', 37 'a11y_align', 38 'a11y_font_family', 39 'a11y_readable_font', 40 'a11y_reading_guide', 41 'a11y_big_cursor' 42 ]; 43 44 function store(key, value) { 45 try { 46 if (value === null) { 47 localStorage.removeItem(key); 48 } else { 49 localStorage.setItem(key, value); 50 } 51 } catch (e) {} 52 } 53 54 function read(key) { 55 try { 56 return localStorage.getItem(key); 57 } catch (e) { 58 return null; 59 } 60 } 61 62 /* ============================ 63 PANEL POZITSIYASI 64 ============================ */ 65 function getHeaderHeight() { 66 var header = document.querySelector('.header') 67 || document.querySelector('.main-header') 68 || document.querySelector('.site-header') 69 || document.querySelector('header'); 70 return header ? header.offsetHeight : 90; 71 } 72 73 function updatePanelPosition() { 74 panel.style.top = (getHeaderHeight() + 10) + 'px'; 75 } 76 77 window.addEventListener('resize', updatePanelPosition); 78 updatePanelPosition(); 79 80 /* ============================ 81 PANEL OCHISH / YOPISH 82 ============================ */ 83 function openPanel() { 84 updatePanelPosition(); 85 panel.classList.add('a11y-open'); 86 toggleBtn.classList.add('a11y-active'); 87 toggleBtn.setAttribute('aria-expanded', 'true'); 88 try { sessionStorage.setItem(PANEL_OPEN_KEY, '1'); } catch (e) {} 89 } 90 91 function closePanel() { 92 panel.classList.remove('a11y-open'); 93 toggleBtn.classList.remove('a11y-active'); 94 toggleBtn.setAttribute('aria-expanded', 'false'); 95 try { sessionStorage.removeItem(PANEL_OPEN_KEY); } catch (e) {} 96 } 97 98 toggleBtn.addEventListener('click', function (e) { 99 e.preventDefault(); 100 e.stopPropagation(); 101 if (panel.classList.contains('a11y-open')) { 102 closePanel(); 103 } else { 104 openPanel(); 105 } 106 }); 107 108 if (closeBtn) { 109 closeBtn.addEventListener('click', function (e) { 110 e.preventDefault(); 111 e.stopPropagation(); 112 closePanel(); 113 }); 114 } 115 116 document.addEventListener('keydown', function (e) { 117 if (e.key === 'Escape' && panel.classList.contains('a11y-open')) { 118 closePanel(); 119 } 120 }); 121 122 try { 123 if (sessionStorage.getItem(PANEL_OPEN_KEY) === '1') openPanel(); 124 } catch (e) {} 125 126 /* ============================ 127 KO'RINISH REJIMI 128 ============================ */ 129 function applyViewMode(mode) { 130 if (viewModeClasses.indexOf(mode) === -1) mode = 'spcNormal';
131 viewModeClasses.forEach(function (c) { body.classList.remove(c); }); 132 body.classList.add(mode); 133 root.classList.toggle('a11y-grayscale', mode === 'spcGrayscale'); 134 modeBoxes.forEach(function (b) { b.classList.toggle('active', b.classList.contains(mode)); }); 135 store('a11y_mode', mode); 136 } 137 138 if (modeBoxes.length) { 139 modeBoxes.forEach(function (box) { 140 box.addEventListener('click', function () { 141 var mode = viewModeClasses.find(function (c) { return box.classList.contains(c); }); 142 if (mode) applyViewMode(mode); 143 }); 144 }); 145 } 146 147 /* ============================ 148 MATN O'LCHAMI VA QATOR BALANDLIGI 149 ============================ */ 150 var BASE_FONT_SIZE = 18; 151 var baseSizes = new WeakMap(); 152 153 function isOwnUi(el) { 154 return el.closest('#a11yPanel, #a11ySelectionBtn, #a11yReadingLine') !== null; 155 } 156 157 function isIcon(el) { 158 var cls = el.getAttribute('class') || ''; 159 return el.tagName === 'I' || /(^|\s)fa[bsrl]?(\s|-)/.test(cls); 160 } 161 162 function scalableElements() { 163 var skip = { SCRIPT: 1, STYLE: 1, NOSCRIPT: 1, SVG: 1, PATH: 1, IMG: 1, BR: 1, HR: 1, IFRAME: 1, VIDEO: 1, CANVAS: 1 }; 164 return Array.prototype.filter.call(body.querySelectorAll('*'), function (el) { 165 return !skip[el.tagName] && !isIcon(el) && !isOwnUi(el); 166 }); 167 } 168 169 function applyFontSize(v) { 170 if (fontVal) fontVal.textContent = v; 171 172 var factor = parseFloat(v) / BASE_FONT_SIZE; 173 body.style.fontSize = v + 'px'; 174 175 scalableElements().forEach(function (el) { 176 var base = baseSizes.get(el); 177 178 if (base === undefined) { 179 base = parseFloat(window.getComputedStyle(el).fontSize); 180 if (!base) return; 181 baseSizes.set(el, base); 182 } 183 184 if (factor === 1) { 185 el.style.removeProperty('font-size'); 186 } else { 187 el.style.setProperty('font-size', (base * factor).toFixed(2) + 'px', 'important'); 188 } 189 }); 190 191 store('a11y_font', v); 192 } 193 194 function applyLineHeight(v) { 195 if (lineHeightVal) lineHeightVal.textContent = v; 196 body.style.setProperty('--a11y-line-height', v); 197 body.classList.add('a11y-line-height'); 198 store('a11y_line_height', v); 199 } 200 201 if (fontSlider) { 202 fontSlider.addEventListener('input', function () { 203 if (fontVal) fontVal.textContent = this.value; 204 }); 205 fontSlider.addEventListener('change', function () { applyFontSize(this.value); }); 206 } 207 208 if (lineHeightSlider) lineHeightSlider.addEventListener('input', function () { applyLineHeight(this.value); }); 209 210 /* ============================ 211 MATN TEKISLASH 212 ============================ */ 213 function applyAlign(value) { 214 body.classList.remove('a11y-align-left', 'a11y-align-right'); 215 if (value === 'left' || value === 'right') { 216 body.classList.add('a11y-align-' + value); 217 } else { 218 value = ''; 219 } 220 221 if (alignGroup) { 222 alignGroup.querySelectorAll('.a11y-seg').forEach(function (btn) { 223 btn.classList.toggle('active', btn.dataset.align === value); 224 btn.setAttribute('aria-pressed', btn.dataset.align === value ? 'true' : 'false'); 225 }); 226 } 227 228 store('a11y_align', value); 229 } 230 231 if (alignGroup) { 232 alignGroup.addEventListener('click', function (e) { 233 var btn = e.target.closest('.a11y-seg'); 234 if (btn) applyAlign(btn.dataset.align || ''); 235 }); 236 } 237 238 /* ============================ 239 SHRIFT (o'qish uchun qulay + tanlangan oila) 240 ============================ */ 241 function applyFontFamily() { 242 var family = fontFamilySelect ? fontFamilySelect.value : ''; 243 var readable = readableFontToggle ? readableFontToggle.classList.contains('on') : false; 244 245 if (!family && readable) { 246 family = READABLE_STACK; 247 } 248 249 if (family) { 250 body.style.setProperty('--a11y-font', family); 251 body.classList.add('a11y-font-custom'); 252 } else { 253 body.style.removeProperty('--a11y-font'); 254 body.classList.remove('a11y-font-custom'); 255 } 256 257 body.classList.toggle('a11y-readable-text', readable); 258 } 259 260 if (fontFamilySelect) { 261 fontFamilySelect.addEventListener('change', function () { 262 store('a11y_font_family', fontFamilySelect.value); 263 applyFontFamily(); 264 }); 265 } 266 267 /* ============================ 268 TOGGLE FUNKSIYALAR 269 ============================ */ 270 function toggle(el, bodyClass, key, on) { 271 if (!el) return; 272 el.classList.toggle('on', on); 273 el.setAttribute('aria-checked', on ? 'true' : 'false'); 274 if (bodyClass) body.classList.toggle(bodyClass, on); 275 store(key, on ? '1' : '0'); 276 } 277 278 function bindToggle(el, bodyClass, key, after) { 279 if (!el) return; 280 el.addEventListener('click', function () { 281 toggle(el, bodyClass, key, !el.classList.contains('on')); 282 if (after) after(); 283 }); 284 } 285 286 bindToggle(readableFontToggle, null, 'a11y_readable_font', applyFontFamily); 287 bindToggle(readingGuideToggle, 'a11y-reading-guide-active', 'a11y_reading_guide'); 288 bindToggle(bigCursorToggle, 'a11y-big-cursor', 'a11y_big_cursor'); 289 290 /* ============================ 291 O'QISH CHIZIG'I 292 ============================ */ 293 var readingLine = document.createElement('div'); 294 readingLine.id = 'a11yReadingLine'; 295 body.appendChild(readingLine); 296 297 function moveReadingLine(y) { 298 if (body.classList.contains('a11y-reading-guide-active')) { 299 readingLine.style.top = (y - 18) + 'px'; 300 } 301 } 302 303 document.addEventListener('mousemove', function (e) { 304 moveReadingLine(e.clientY); 305 }); 306 307 document.addEventListener('touchmove', function (e) { 308 if (e.touches && e.touches.length) moveReadingLine(e.touches[0].clientY); 309 }, { passive: true }); 310 311 /* ============================ 312 BELGILANGAN MATNNI O'QISH 313 ============================ */ 314 var synth = window.speechSynthesis;
315 var availableVoices = []; 316 var selectionCheckTimer = null; 317 var isSelectionBtnClicked = false; 318 319 function loadVoices() { 320 availableVoices = synth.getVoices(); 321 } 322 323 if (synth) { 324 loadVoices(); 325 if (synth.onvoiceschanged !== undefined) { 326 synth.onvoiceschanged = loadVoices; 327 } 328 } 329 330 function pickBestVoice() { 331 if (!availableVoices.length) return null; 332 var uzVoice = availableVoices.find(function (v) { return v.lang.toLowerCase().indexOf('uz') === 0; }); 333 if (uzVoice) return uzVoice; 334 var trVoice = availableVoices.find(function (v) { return v.lang.toLowerCase().indexOf('tr') === 0; }); 335 if (trVoice) return trVoice; 336 var ruVoice = availableVoices.find(function (v) { return v.lang.toLowerCase().indexOf('ru') === 0; }); 337 if (ruVoice) return ruVoice; 338 return availableVoices[0]; 339 } 340 341 function speak(text, onEnd) { 342 if (!synth) { 343 alert("Brauzeringiz ovozli o'qishni qo'llab-quvvatlamaydi."); 344 return; 345 } 346 synth.cancel(); 347 if (!text || !text.trim()) return; 348 349 var cleanText = text.replace(/\s+/g, ' ').trim(); 350 var utterance = new SpeechSynthesisUtterance(cleanText); 351 var voice = pickBestVoice(); 352 353 if (voice) { 354 utterance.voice = voice; 355 utterance.lang = voice.lang; 356 } else { 357 utterance.lang = 'tr-TR'; 358 } 359 360 utterance.rate = 0.9; 361 utterance.pitch = 1; 362 utterance.volume = 1; 363 utterance.onend = onEnd || function () {}; 364 365 synth.speak(utterance); 366 } 367 368 function stopSelectionReading() { 369 if (synth) synth.cancel(); 370 if (selectionBtn) selectionBtn.classList.remove('reading'); 371 } 372 373 function hideSelectionBtn() { 374 if (selectionBtn) selectionBtn.style.display = 'none'; 375 } 376 377 function showSelectionBtn(range, text) { 378 if (!selectionBtn) return; 379 380 var rects = range.getClientRects(); 381 var lastRect = (rects && rects.length) ? rects[rects.length - 1] : range.getBoundingClientRect(); 382 383 var btnWidth = 34; 384 var top = window.scrollY + lastRect.bottom + 10; 385 var left = window.scrollX + lastRect.left + (lastRect.width / 2) - (btnWidth / 2); 386 387 var maxLeft = window.scrollX + window.innerWidth - btnWidth - 10; 388 var minLeft = window.scrollX + 10; 389 left = Math.max(minLeft, Math.min(left, maxLeft)); 390 391 selectionBtn.style.top = top + 'px'; 392 selectionBtn.style.left = left + 'px'; 393 selectionBtn.style.display = 'flex'; 394 selectionBtn.dataset.text = text; 395 } 396 397 document.addEventListener('selectionchange', function () { 398 if (isSelectionBtnClicked) return; 399 400 clearTimeout(selectionCheckTimer); 401 selectionCheckTimer = setTimeout(function () { 402 var selection = window.getSelection(); 403 var text = selection ? selection.toString().trim() : ''; 404 405 if (text.length > 1 && selection.rangeCount > 0) { 406 var range = selection.getRangeAt(0); 407 showSelectionBtn(range, text); 408 } else { 409 hideSelectionBtn(); 410 } 411 }, 150); 412 }); 413 414 if (selectionBtn) { 415 selectionBtn.addEventListener('mousedown', function (e) { 416 isSelectionBtnClicked = true; 417 e.preventDefault(); 418 }); 419 420 selectionBtn.addEventListener('click', function (e) { 421 e.stopPropagation(); 422 var text = selectionBtn.dataset.text || ''; 423 424 if (selectionBtn.classList.contains('reading')) { 425 stopSelectionReading(); 426 } else { 427 selectionBtn.classList.add('reading'); 428 speak(text, function () { 429 selectionBtn.classList.remove('reading'); 430 }); 431 } 432 433 setTimeout(function () { isSelectionBtnClicked = false; }, 200); 434 }); 435 } 436 437 document.addEventListener('mousedown', function (e) { 438 if (selectionBtn && !selectionBtn.contains(e.target)) { 439 setTimeout(function () { 440 var selection = window.getSelection(); 441 var text = selection ? selection.toString().trim() : ''; 442 if (!text || text.length <= 1) { 443 hideSelectionBtn(); 444 } 445 }, 10); 446 } 447 }); 448 449 /* ============================ 450 TIKLASH 451 ============================ */ 452 if (resetBtn) { 453 resetBtn.addEventListener('click', function () {
454 STORAGE_KEYS.forEach(function (k) { store(k, null); }); 455 456 applyViewMode('spcNormal'); 457 458 if (fontSlider) { fontSlider.value = BASE_FONT_SIZE; applyFontSize(BASE_FONT_SIZE); } 459 460 if (lineHeightSlider) lineHeightSlider.value = 1.6; 461 if (lineHeightVal) lineHeightVal.textContent = '1.6'; 462 body.classList.remove('a11y-line-height'); 463 body.style.removeProperty('--a11y-line-height'); 464 465 applyAlign(''); 466 467 if (fontFamilySelect) fontFamilySelect.value = ''; 468 toggle(readableFontToggle, null, 'a11y_readable_font', false); 469 applyFontFamily(); 470 471 toggle(readingGuideToggle, 'a11y-reading-guide-active', 'a11y_reading_guide', false); 472 toggle(bigCursorToggle, 'a11y-big-cursor', 'a11y_big_cursor', false); 473 474 stopSelectionReading(); 475 476 STORAGE_KEYS.forEach(function (k) { store(k, null); }); 477 }); 478 } 479 480 /* ============================ 481 SAQLANGAN SOZLAMALARNI YUKLASH 482 ============================ */ 483 applyViewMode(read('a11y_mode') || 'spcNormal'); 484 485 var savedFont = read('a11y_font'); 486 if (savedFont && fontSlider) { fontSlider.value = savedFont; applyFontSize(savedFont); } 487 488 var savedLineHeight = read('a11y_line_height'); 489 if (savedLineHeight && lineHeightSlider) { lineHeightSlider.value = savedLineHeight; applyLineHeight(savedLineHeight); } 490 491 applyAlign(read('a11y_align') || ''); 492 493 var savedFamily = read('a11y_font_family'); 494 if (savedFamily && fontFamilySelect) fontFamilySelect.value = savedFamily; 495 toggle(readableFontToggle, null, 'a11y_readable_font', read('a11y_readable_font') === '1'); 496 applyFontFamily(); 497 498 toggle(readingGuideToggle, 'a11y-reading-guide-active', 'a11y_reading_guide', read('a11y_reading_guide') === '1'); 499 toggle(bigCursorToggle, 'a11y-big-cursor', 'a11y_big_cursor', read('a11y_big_cursor') === '1'); 500 501 window.addEventListener('beforeunload', function () { 502 if (synth) synth.cancel(); 503 }); 504 505});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.