PageSourceSearch

https://www.sammu.uz/new/assets/js/accessibility.js?v=1.0.64

js sammu.uz collected 2026-09-24 09:02:59 UTC 17,077 bytes, 505 lines download raw bytes

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.