PageSourceSearch

https://soyqirim.az/wp-content/themes/soyqirim-v2-working-phase2/a…?v=1.0.56-bws-source-alignment

js soyqirim.az collected 2026-10-02 22:40:03 UTC 44,920 bytes, 967 lines download raw bytes

1(function(){
2  'use strict';
3  const $ = (s, r=document) => r.querySelector(s);
4  const $$ = (s, r=document) => Array.from(r.querySelectorAll(s));
5
6  const menuToggle = $('.menu-toggle');
7  const mobileMenu = $('.mobile-menu');
8  function setMobileMenu(open){
9    document.documentElement.classList.toggle('menu-open', !!open);
10    document.body.classList.toggle('menu-open', !!open);
11    if(mobileMenu) mobileMenu.classList.toggle('is-open', !!open);
12    if(menuToggle){
13      menuToggle.classList.toggle('is-open', !!open);
14      menuToggle.setAttribute('aria-expanded', open ? 'true' : 'false');
15    }
16  }
17  function toggleMobileSubmenu(btn){
18    const row = btn.closest('.mobile-parent-row') || btn;
19    const sub = row.nextElementSibling || btn.nextElementSibling;
20    const open = !btn.classList.contains('open');
21    btn.classList.toggle('open', open);
22    row.classList.toggle('open', open);
23    btn.setAttribute('aria-expanded', open ? 'true' : 'false');
24    if(sub && sub.classList.contains('mobile-sub')){
25      sub.hidden = !open;
26      sub.classList.toggle('is-open', open);
27    }
28  }
29  if(menuToggle && mobileMenu){
30    menuToggle.addEventListener('click', function(e){
31      e.preventDefault();
32      setMobileMenu(!document.body.classList.contains('menu-open'));
33    });
34    $$('.mobile-menu a').forEach(function(link){ link.addEventListener('click', function(){ setMobileMenu(false); }); });
35    $$('.mobile-parent-toggle, .mobile-parent').forEach(function(btn){
36      btn.addEventListener('click', function(e){
37        e.preventDefault();
38        toggleMobileSubmenu(btn);
39      });
40    });
41    document.addEventListener('keydown', function(e){
42      if(e.key === 'Escape' && document.body.classList.contains('menu-open')) setMobileMenu(false);
43    });
44  }
45
46  // Persist the explicit language before navigation. Full-page cache can serve
47  // a prefixed URL without running PHP, so relying only on Set-Cookie response
48  // headers makes language switches intermittent.
49  function persistFrontendLanguage(lang){
50    if(!/^(az|en|ru|ar)$/.test(lang)) return;
51    const secure = location.protocol === 'https:' ? '; Secure' : '';
52    const common = '; path=/; max-age=31536000; SameSite=Lax' + secure;
53    const expired = '; path=/; max-age=0; SameSite=Lax' + secure;
54    const cookieDomain = location.hostname.indexOf('.') !== -1 && location.hostname !== 'localhost'
55      ? location.hostname.replace(/^www\./,'')
56      : '';
57    function clearCookieVariants(name){
58      document.cookie = name + '=; ' + expired.replace(/^; /, '');
59      if(cookieDomain){
60        document.cookie = name + '=; ' + expired.replace(/^; /, '') + '; domain=.' + cookieDomain;
61      }
62    }
63    ['sqv2_frontend_lang','mltlngg_language','mltlngg_current_language'].forEach(clearCookieVariants);
64    document.cookie = 'sqv2_frontend_lang=' + encodeURIComponent(lang) + common;
65    ['mltlngg_language','mltlngg_current_language'].forEach(function(name){
66      const domain = cookieDomain ? '; domain=.' + cookieDomain : '';
67      document.cookie = name + '=' + encodeURIComponent(lang) + common + domain;
68    });
69  }
70  $$('.sqv2-lang-link[data-sqv2-language]').forEach(function(link){
71    link.addEventListener('click', function(){
72      persistFrontendLanguage(link.getAttribute('data-sqv2-language') || '');
73    });
74  });
75  const explicitLanguage = location.pathname.split('/').filter(Boolean)[0] || '';
76  if(/^(en|ru|ar)$/.test(explicitLanguage)) persistFrontendLanguage(explicitLanguage);
77
78  // V2 search overlay. Uses event delegation so desktop and mobile search
79  // buttons work even if markup is re-rendered by cache/minify plugins.
80  const searchOverlay = $('[data-search-overlay]');
81  const searchClose = $('[data-search-close]');
82  const searchInput = searchOverlay ? $('input[type="search"]', searchOverlay) : null;
83  let sqv2SearchScrollY = 0;
84  function lockSearchScroll(){
85    sqv2SearchScrollY = window.scrollY || document.documentElement.scrollTop || 0;
86    document.documentElement.classList.add('sqv2-search-locked');
87    document.body.classList.add('sqv2-search-open');
88    document.body.style.position = 'fixed';
89    document.body.style.top = '-' + sqv2SearchScrollY + 'px';
90    document.body.style.left = '0';
91    document.body.style.right = '0';
92    document.body.style.width = '100%';
93  }
94  function unlockSearchScroll(){
95    document.documentElement.classList.remove('sqv2-search-locked');
96    document.body.classList.remove('sqv2-search-open');
97    document.body.style.position = '';
98    document.body.style.top = '';
99    document.body.style.left = '';
100    document.body.style.right = '';
101    document.body.style.width = '';
102    window.scrollTo(0, sqv2SearchScrollY || 0);
103  }
104  function openSearch(){
105    if(!searchOverlay) return;
106    searchOverlay.classList.add('is-open');
107    searchOverlay.setAttribute('aria-hidden','false');
108    lockSearchScroll();
109    window.setTimeout(function(){ searchInput && searchInput.focus(); }, 80);
110  }
111  function closeSearch(){
112    if(!searchOverlay) return;
113    searchOverlay.classList.remove('is-open');
114    searchOverlay.setAttribute('aria-hidden','true');
115    unlockSearchScroll();
116  }
117  document.addEventListener('click', function(e){
118    const openBtn = e.target.closest ? e.target.closest('[data-search-open]') : null;
119    if(openBtn){ e.preventDefault(); setMobileMenu(false); openSearch(); return; }
120    const closeBtn = e.target.closest ? e.target.closest('[data-search-close]') : null;
121    if(closeBtn){ e.preventDefault(); closeSearch(); }
122  });
123  searchClose && searchClose.addEventListener('click', closeSearch);
124  searchOverlay && searchOverlay.addEventListener('click', function(e){ if(e.target === searchOverlay) closeSearch(); });
125  document.addEventListener('keydown', function(e){ if(e.key === 'Escape') closeSearch(); });
126
127  const hero = $('[data-hero]');
128  if(hero){
129    const slides = Array.isArray(window.SQV2_HERO_SLIDES) ? window.SQV2_HERO_SLIDES : [];
130    const bgWrap = $('.hero-bgs', hero);
131    const timeline = $('.timeline-wrap', hero);
132    const line = $('.timeline-line', hero);
133    const progress = $('.timeline-progress', hero);
134    const center = $('.hero-center', hero);
135    const counterNow = $('.counter-now', hero);
136    const total = $('.hero-counter b', hero);
137    const label = $('.hero-label', hero);
138    const year = $('.hero-year', hero);
139    const desc = $('.hero-desc', hero);
140    const cta = $('[data-hero-cta]', hero);
141    const prev = $('.side-prev', hero);
142    const next = $('.side-next', hero);
143    let current = 0;
144    let timer = null;
145    let paused = false;
146
147    if(total) total.textContent = String(slides.length || 1).padStart(2,'0');
148    const bgNodes = $$('.hero-bg', hero);
149    function safeBgUrl(url){
150      return String(url || '').replace(/"/g, '%22');
151    }
152    function ensureHeroBg(i){
153      const s = slides[i];
154      const bg = bgNodes[i];
155      if(!s || !s.img || !bg || bg.dataset.loaded === 'true') return;
156      bg.style.backgroundImage = 'url("' + safeBgUrl(s.img) + '")';
157      bg.dataset.loaded = 'true';
158    }
159    function warmAdjacentHeroBg(){
160      if(!slides.length) return;
161      const nextIndex = (current + 1) % slides.length;
162      const run = function(){ ensureHeroBg(nextIndex); };
163      // Do not compete with first paint/LCP on cold cache. The requested slide
164      // still loads immediately when the user navigates; this only prewarms the
165      // next one after the first page experience is stable.
166      window.setTimeout(function(){
167        if('requestIdleCallback' in window) window.requestIdleCallback(run, { timeout: 1200 });
168        else run();
169      }, 5000);
170    }
171
172    slides.forEach(function(s, i){
173      let bg = bgNodes[i];
174      if(!bg){
175        bg = document.createElement('div');
176        bgNodes[i] = bg;
177        bgWrap.appendChild(bg);
178      }
179      bg.className = 'hero-bg' + (i === 0 ? ' active' : '');
180      if(i === 0) ensureHeroBg(i);
181
182      const node = document.createElement('button');
183      const thumb = s.thumb || s.img;
184      node.type = 'button';
185      node.className = 'timeline-node' + (i === 0 ? ' active' : '');
186      node.innerHTML = '<span class="timeline-thumb"><img loading="lazy" decoding="async" width="220" height="147" src="'+thumb+'" alt="'+escapeHtml(s.year)+'"></span><span class="timeline-dot"></span><span class="timeline-year">'+escapeHtml(s.year)+'</span>';
187      node.addEventListener('click', function(){ go(i); });
188      timeline.appendChild(node);
189    });
190
191    function escapeHtml(str){
192      return String(str || '').replace(/[&<>"']/g, function(m){ return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[m]); });
193    }
194    function render(){
195      if(!slides.length) return;
196      const s = slides[current];
197      bgNodes.forEach(function(bg, i){ bg && bg.classList.toggle('active', i === current); });
198      $$('.timeline-node', hero).forEach(function(node, i){ node.classList.toggle('active', i === current); });
199      if(counterNow) counterNow.textContent = String(current + 1).padStart(2,'0');
200      if(label) label.textContent = s.label || '';
201      if(year) year.textContent = s.title || s.year || '';
202      if(desc) desc.textContent = s.desc || '';
203      if(cta) cta.href = s.url || '#';
204      const prevYear = slides[(current - 1 + slides.length) % slides.length].year;
205      const nextYear = slides[(current + 1) % slides.length].year;
206      if(prev){
207        prev.textContent = prevYear;
208        prev.setAttribute('aria-label', prevYear + ' ' + (prev.getAttribute('data-a11y-label') || ''));
209      }
210      if(next){
211        next.textContent = nextYear;
212        next.setAttribute('aria-label', nextYear + ' ' + (next.getAttribute('data-a11y-label') || ''));
213      }
214      updateTimelineLine();
215    }
216    function updateTimelineLine(){
217      if(!timeline) return;
218      const nodes = $$('.timeline-node', hero);
219      if(!nodes.length) return;
220      const first = nodes[0];
221      const last = nodes[nodes.length - 1];
222      const active = nodes[current] || first;
223      const firstCenter = first.offsetLeft + (first.offsetWidth / 2);
224      const lastCenter = last.offsetLeft + (last.offsetWidth / 2);
225      const activeCenter = active.offsetLeft + (active.offsetWidth / 2);
226      if(line){
227        line.style.left = firstCenter + 'px';
228        line.style.right = Math.max(0, timeline.clientWidth - lastCenter) + 'px';
229      }
230      if(progress){
231        progress.style.left = firstCenter + 'px';
232        progress.style.width = Math.max(0, activeCenter - firstCenter) + 'px';
233      }
234    }
235    function go(i){
236      if(!slides.length || i === current) return;
237      ensureHeroBg((i + slides.length) % slides.length);
238      center && center.classList.add('is-changing');
239      window.setTimeout(function(){
240        current = (i + slides.length) % slides.length;
241        render();
242        warmAdjacentHeroBg();
243        center && center.classList.remove('is-changing');
244      }, 180);
245      restart();
246    }
247    function goNext(){ go(current + 1); }
248    function goPrev(){ go(current - 1); }
249    next && next.addEventListener('click', goNext);
250    prev && prev.addEventListener('click', goPrev);
251    hero.addEventListener('mouseenter', function(){ paused = true; });
252    hero.addEventListener('mouseleave', function(){ paused = false; });
253    document.addEventListener('keydown', function(e){ if(e.key === 'ArrowRight') goNext(); if(e.key === 'ArrowLeft') goPrev(); });
254    window.addEventListener('resize', updateTimelineLine, { passive: true });
255    let sx=0, sy=0, st=0;
256    hero.addEventListener('touchstart', function(e){ if(e.touches[0]){ sx=e.touches[0].clientX; sy=e.touches[0].clientY; st=Date.now(); } }, {passive:true});
257    hero.addEventListener('touchend', function(e){ if(!e.changedTouches[0]) return; const dx=e.changedTouches[0].clientX-sx; const dy=e.changedTouches[0].clientY-sy; if(Math.abs(dx)>55 && Math.abs(dx)>Math.abs(dy) && Date.now()-st<700){ dx<0 ? goNext() : goPrev(); } }, {passive:true});
258    function start(){ timer = window.setInterval(function(){ if(!paused) goNext(); }, 8000); }
259    function restart(){ window.clearInterval(timer); start(); }
260    render();
261    warmAdjacentHeroBg();
262    start();
263  }
264
265
266
267  // Strict lazy images for below-the-fold editorial cards. Native lazy loading
268  // can still fetch nearby images during Lighthouse; this keeps large remote
269  // district images out of the initial network waterfall until they approach view.
270  (function(){
271    const lazyImages = $$('img[data-sqv2-lazy-src]');
272    if(!lazyImages.length) return;
273    function load(img){
274      const src = img.getAttribute('data-sqv2-lazy-src');
275      if(!src) return;
276      img.src = src;
277      img.removeAttribute('data-sqv2-lazy-src');
278      img.classList.add('is-sqv2-lazy-loaded');
279    }
280    if('IntersectionObserver' in window){
281      const io = new IntersectionObserver(function(entries){
282        entries.forEach(function(entry){
283          if(entry.isIntersecting){ io.unobserve(entry.target); load(entry.target); }
284        });
285      }, { rootMargin: '180px 0px' });
286      lazyImages.forEach(function(img){ io.observe(img); });
287    } else {
288      lazyImages.forEach(load);
289    }
290  })();
291
292  function scrollStrip(selector, dir){
293    const strip = $(selector);
294    if(!strip) return;
295    const first = strip.children[0];
296    const step = first ? first.getBoundingClientRect().width + 24 : 360;
297    strip.scrollBy({ left: step * dir, behavior: 'smooth' });
298  }
299  $$('.news-next').forEach(btn => btn.addEventListener('click', () => scrollStrip('.news-airbnb-track', 1)));
300  $$('.news-prev').forEach(btn => btn.addEventListener('click', () => scrollStrip('.news-airbnb-track', -1)));
301  $$('.area-next').forEach(btn => btn.addEventListener('click', () => scrollStrip('.district-strip', 1)));
302  $$('.area-prev').forEach(btn => btn.addEventListener('click', () => scrollStrip('.district-strip', -1)));
303
304  $$('[data-article-slider]').forEach(function(slider){
305    const slides = $$('.article-slide', slider);
306    const dots = $$('.article-slider-dot', slider);
307    const prev = $('.article-slider-prev', slider);
308    const next = $('.article-slider-next', slider);
309    const full = $('.article-slider-fullscreen', slider);
310    let index = 0;
311    function render(){
312      slides.forEach((s,i)=>s.classList.toggle('is-active', i===index));
313      dots.forEach((d,i)=>d.classList.toggle('is-active', i===index));
314      if(dots[index] && dots[index].scrollIntoView){
315        try { dots[index].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } catch(e) {}
316      }
317    }
318    function go(i){ index = (i + slides.length) % slides.length; render(); }
319    prev && prev.addEventListener('click', () => go(index-1));
320    next && next.addEventListener('click', () => go(index+1));
321    dots.forEach((dot,i)=>dot.addEventListener('click', () => go(i)));
322    if(full){
323      full.addEventListener('click', function(){
324        const isOpen = slider.classList.contains('is-fullscreen');
325        slider.classList.toggle('is-fullscreen', !isOpen);
326        document.documentElement.classList.toggle('sqv2-slider-fullscreen-open', !isOpen);
327        document.body && document.body.classList.toggle('sqv2-slider-fullscreen-open', !isOpen);
328        full.setAttribute('aria-label', isOpen ? 'Fullscreen' : 'Close fullscreen');
329      });
330      document.addEventListener('keydown', function(e){
331        if(e.key === 'Escape' && slider.classList.contains('is-fullscreen')){
332          slider.classList.remove('is-fullscreen');
333          document.documentElement.classList.remove('sqv2-slider-fullscreen-open');
334          document.body && document.body.classList.remove('sqv2-slider-fullscreen-open');
335          full.setAttribute('aria-label', 'Fullscreen');
336        }
337      });
338    }
339    render();
340  });
341
342
343
344  $$('[data-sqv2-article-viewer]').forEach(function(viewer){
345    const slides = $$('.sqv2-article-media-viewer__slide', viewer);
346    const dots = $$('.sqv2-article-media-viewer__dot', viewer);
347    const prev = $('.sqv2-article-media-viewer__prev', viewer);
348    const next = $('.sqv2-article-media-viewer__next', viewer);
349    const full = $('.sqv2-article-media-viewer__fullscreen', viewer);
350    const close = $('.sqv2-article-media-viewer__modal-close', viewer);
351    const current = $('[data-sqv2-current]', viewer);
352    const progress = $('.sqv2-article-media-viewer__progress span', viewer);
353    let index = 0;
354    if (!slides.length) return;
355
356    function render(){
357      slides.forEach((slide, i) => slide.classList.toggle('is-active', i === index));
358      dots.forEach((dot, i) => dot.classList.toggle('is-active', i === index));
359      if (current) current.textContent = String(index + 1);
360      if (progress) {
361        const percent = slides.length > 1 ? (((index + 1) / slides.length) * 100) : 100;
362        progress.style.setProperty('width', percent + '%', 'important');
363      }
364    }
365
366    function go(nextIndex){
367      index = (nextIndex + slides.length) % slides.length;
368      render();
369    }
370
371    function setFullscreen(open){
372      viewer.classList.toggle('is-fullscreen', open);
373      document.documentElement.classList.toggle('sqv2-slider-fullscreen-open', open);
374      document.body && document.body.classList.toggle('sqv2-slider-fullscreen-open', open);
375      if (full) {
376        full.setAttribute('aria-label', open ? 'Close fullscreen' : 'Fullscreen');
377        full.setAttribute('title', open ? 'Close fullscreen' : 'Fullscreen');
378        full.setAttribute('aria-pressed', open ? 'true' : 'false');
379      }
380      if (close) {
381        close.hidden = !open;
382        close.setAttribute('aria-hidden', open ? 'false' : 'true');
383      }
384    }
385
386    prev && prev.addEventListener('click', () => go(index - 1));
387    next && next.addEventListener('click', () => go(index + 1));
388    dots.forEach((dot, i) => dot.addEventListener('click', () => go(i)));
389    full && full.addEventListener('click', () => setFullscreen(!viewer.classList.contains('is-fullscreen')));
390    close && close.addEventListener('click', () => setFullscreen(false));
391
392    viewer.addEventListener('click', function(e){
393      if (!viewer.classList.contains('is-fullscreen')) return;
394      if (e.target === viewer) setFullscreen(false);
395    });
396
397    document.addEventListener('keydown', function(e){
398      if (e.key === 'Escape' && viewer.classList.contains('is-fullscreen')) {
399        setFullscreen(false);
400      }
401      if (!viewer.classList.contains('is-fullscreen') || slides.length < 2) return;
402      if (e.key === 'ArrowLeft') go(index - 1);
403      if (e.key === 'ArrowRight') go(index + 1);
404    });
405
406    render();
407  });
408
409  function sqv2CurrentShareUrl(){
410    try {
411      const url = new URL(window.location.href);
412      url.hash = '';
413      return url.toString();
414    } catch(e) {
415      return window.location.href.split('#')[0];
416    }
417  }
418  function sqv2FacebookShareHref(url){
419    return 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(url);
420  }
421  const currentShareUrl = sqv2CurrentShareUrl();
422  $$('.share-rail').forEach(function(rail){
423    const facebook = $('a[href*="facebook.com/sharer"]', rail);
424    if(facebook) facebook.setAttribute('href', sqv2FacebookShareHref(currentShareUrl));
425    $$('.sqv2-copy-link', rail).forEach(function(btn){
426      btn.setAttribute('data-copy', currentShareUrl);
427    });
428  });
429  $$('.sqv2-copy-link').forEach(function(btn){
430    btn.addEventListener('click', function(){
431      const value = btn.getAttribute('data-copy') || sqv2CurrentShareUrl();
432      if(navigator.share){
433        try { navigator.share({title: document.title || '', url: value}).catch(function(){}); } catch(err) {}
434      }
435      if(navigator.clipboard){ navigator.clipboard.writeText(value).catch(function(){}); }
436      btn.classList.add('copied');
437      window.setTimeout(() => btn.classList.remove('copied'), 1200);
438    });
439  });
440})();
441
442(function(){
443  'use strict';
444  const cfg = window.SQV2_SEARCH || null;
445  if(!cfg) return;
446  const input = document.querySelector('[data-sqv2-search-input]');
447  const form = document.querySelector('[data-sqv2-search-form]');
448  const suggestions = document.querySelector('[data-sqv2-search-suggestions]');
449  if(form){
450    form.addEventListener('submit', function(e){
451      const q = input ? input.value.trim() : '';
452      if(!q) return;
453      e.preventDefault();
454      const base = (cfg.searchUrl || form.getAttribute('action') || window.location.pathname).replace(/[?#].*$/, '');
455      window.location.href = base + '?s=' + encodeURIComponent(q);
456    });
457  }
458  // Do not block the native GET form submit. This makes search work even
459  // when AJAX suggestions are disabled or blocked by cache/security plugins.
460  let timer = null;
461  function render(items){
462    if(!suggestions) return;
463    suggestions.innerHTML = '';
464    const max = Math.max(1, parseInt(cfg.maxSuggestions || 10, 10));
465    if(!items || !items.length){
466      if(input && input.value.trim().length > 1){
467        const empty = document.createElement('div');
468        empty.className = 'sqv2-search-suggestion sqv2-search-suggestion-empty';
469        empty.innerHTML = '<strong>'+escapeHtml(cfg.noResults || 'No results')+'</strong>';
470        suggestions.appendChild(empty);
471      }
472      return;
473    }
474    items.slice(0, max).forEach(function(item){
475      const a = document.createElement('a');
476      a.className = 'sqv2-search-suggestion';
477      a.href = item.url;
478      const meta = item.date || item.type || '';
479      a.innerHTML = '<strong>'+escapeHtml(item.title)+'</strong>' + (meta ? '<span>'+escapeHtml(meta)+'</span>' : '');
480      suggestions.appendChild(a);
481    });
482  }
483  function escapeHtml(str){ return String(str || '').replace(/[&<>"']/g, function(m){ return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[m]); }); }
484  function normalizeSearch(str){
485    return String(str || '')
486      .toLocaleLowerCase('az')
487      .normalize('NFKD')
488      .replace(/[\u0300-\u036f]/g, '')
489      .replace(/[ı]/g, 'i')
490      .replace(/[ə]/g, 'e')
491      .replace(/[ö]/g, 'o')
492      .replace(/[ü]/g, 'u')
493      .replace(/[ğ]/g, 'g')
494      .replace(/[ş]/g, 's')
495      .replace(/[ç]/g, 'c')
496      .replace(/\s+/g, ' ')
497      .trim();
498  }
499  function localIndexItems(q){
500    const index = Array.isArray(cfg.index) ? cfg.index : [];
501    if(!index.length) return [];
502    const max = Math.max(1, parseInt(cfg.maxSuggestions || 10, 10));
503    const terms = normalizeSearch(q).split(' ').filter(Boolean);
504    if(!terms.length) return [];
505    const scored = [];
506    index.forEach(function(item, originalIndex){
507      const title = normalizeSearch(item.title || '');
508      const haystack = normalizeSearch((item.search || '') + ' ' + (item.title || '') + ' ' + (item.date || '') + ' ' + (item.type || ''));
509      let score = 0;
510      for(let i = 0; i < terms.length; i++){
511        const term = terms[i];
512        if(!haystack.includes(term)) return;
513        if(title === term) score += 220;
514        else if(title.startsWith(term)) score += 160;
515        else if(title.includes(term)) score += 110;
516        else score += 40;
517      }
518      scored.push({item:item, score:score, originalIndex:originalIndex});
519    });
520    scored.sort(function(a,b){ return (b.score - a.score) || (a.originalIndex - b.originalIndex); });
521    return scored.slice(0, max).map(function(row){ return row.item; });
522  }
523  if(input){
524    input.addEventListener('input', function(){
525      const q = input.value.trim();
526      window.clearTimeout(timer);
527      if(q.length < 2){ render([]); return; }
528
529      const localItems = localIndexItems(q);
530      render(localItems);
531      if(localItems.length >= Math.max(1, parseInt(cfg.maxSuggestions || 10, 10))){ return; }
532
533      timer = window.setTimeout(function(){
534        if(!window.fetch){ return; }
535        const lang = encodeURIComponent(cfg.lang || 'az');
536        const ajaxUrl = cfg.ajax + '?action=sqv2_search_suggest&lang=' + lang + '&q=' + encodeURIComponent(q);
537        const suggestBase = (cfg.suggestUrl || cfg.searchUrl || window.location.pathname).replace(/[?#].*$/, '');
538        const fallbackJsonUrl = suggestBase + '?sqv2_suggest=1&lang=' + lang + '&s=' + encodeURIComponent(q);
539        function requestText(url, accept){
540          return fetch(url, {credentials:'same-origin', headers:{'Accept': accept || 'application/json'}}).then(function(r){
541            if(!r.ok) throw new Error('bad_status');
542            return r.text();
543          });
544        }
545        function requestJson(url){
546          return requestText(url, 'application/json').then(function(text){
547            var json;
548            try { json = JSON.parse(text); } catch(e) { throw new Error('bad_json'); }
549            if(json && json.success && json.data) return json.data.items || [];
550            if(json && json.data && Array.isArray(json.data.items)) return json.data.items;
551            if(json && Array.isArray(json.items)) return json.items;
552            return [];
553          });
554        }
555        requestJson(ajaxUrl)
556          .catch(function(){ return requestJson(fallbackJsonUrl); })
557          .then(function(remoteItems){
558            const merged = [];
559            const seen = Object.create(null);
560            (remoteItems || []).concat(localItems || []).forEach(function(item){
561              const key = String(item && item.url || '') || String(item && item.title || '');
562              if(!key || seen[key]) return;
563              seen[key] = true;
564              merged.push(item);
565            });
566            render(merged.length ? merged : localItems);
567          })
568          .catch(function(){ render(localItems); });
569      }, 180);
570    });
571  }
572})();
573
574(function(){
575  'use strict';
576  function qs(s, r){ return (r || document).querySelector(s); }
577  function isNewsAjaxLink(a){
578    return a && a.href && a.closest && a.closest('.sqv2-news-page') && (a.closest('.sqv2-news-filter-tabs') || a.closest('.sqv2-pagination'));
579  }
580  function swapNewsContent(url, push){
581    var page = qs('.sqv2-news-page');
582    var container = page && qs('.container', page);
583    if(!page || !container || !window.fetch || !window.DOMParser){ window.location.href = url; return; }
584    page.classList.add('is-filter-loading');
585    fetch(url, {credentials:'same-origin', headers:{'X-Requested-With':'XMLHttpRequest'}})
586      .then(function(r){ if(!r.ok) throw new Error('bad_status'); return r.text(); })
587      .then(function(html){
588        var doc = new DOMParser().parseFromString(html, 'text/html');
589        var nextPage = qs('.sqv2-news-page', doc);
590        var nextContainer = nextPage && qs('.container', nextPage);
591        if(!nextContainer) throw new Error('missing_news');
592        container.innerHTML = nextContainer.innerHTML;
593        if(push !== false) history.pushState({sqv2News:true}, '', url);
594        var hero = qs('.sqv2-news-hero') || qs('.page-hero');
595        if(hero) hero.scrollIntoView({block:'start', behavior:'smooth'});
596      })
597      .catch(function(){ window.location.href = url; })
598      .finally(function(){ page.classList.remove('is-filter-loading'); });
599  }
600  document.addEventListener('click', function(e){
601    var a = e.target && e.target.closest ? e.target.closest('a') : null;
602    if(!isNewsAjaxLink(a)) return;
603    if(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || a.target) return;
604    e.preventDefault();
605    if(a.classList.contains('chip')){
606      var wrap = a.closest('.sqv2-news-filter-tabs');
607      if(wrap){ Array.prototype.forEach.call(wrap.querySelectorAll('.chip'), function(x){ x.classList.remove('active'); }); a.classList.add('active'); }
608    }
609    swapNewsContent(a.href, true);
610  });
611  window.addEventListener('popstate', function(){ if(qs('.sqv2-news-page')) swapNewsContent(window.location.href, false); });
612  // Step 50: robust directions chooser and stable Azerbaijani date/time fields.
613  (function(){
614    function qs2(sel, root){ return (root || document).querySelector(sel); }
615    function qsa2(sel, root){ return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }
616    function isMobileLike(){
617      return (window.matchMedia && window.matchMedia('(max-width: 900px)').matches) || (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) || ('ontouchstart' in window);
618    }
619    function openMapSheet(trigger){
620      var sheet = qs2('[data-sqv2-map-choice]');
621      if(!sheet) return false;
622      sheet.hidden = false;
623      sheet.classList.add('is-open');
624      sheet.setAttribute('aria-hidden','false');
625      document.body.classList.add('sqv2-map-choice-open');
626      qsa2('a', sheet).forEach(function(a){
627        var key = a.getAttribute('data-map-app');
628        if(key === 'google' && trigger.getAttribute('data-google-url')) a.href = trigger.getAttribute('data-google-url');
629        if(key === 'waze' && trigger.getAttribute('data-waze-url')) a.href = trigger.getAttribute('data-waze-url');
630        if(key === 'apple' && trigger.getAttribute('data-apple-url')) a.href = trigger.getAttribute('data-apple-url');
631      });
632      return true;
633    }
634    function closeMapSheet(){
635      var sheet = qs2('[data-sqv2-map-choice]');
636      if(!sheet) return;
637      sheet.classList.remove('is-open');
638      sheet.setAttribute('aria-hidden','true');
639      document.body.classList.remove('sqv2-map-choice-open');
640      window.setTimeout(function(){ if(!sheet.classList.contains('is-open')) sheet.hidden = true; }, 120);
641    }
642    function handleDirectionsEvent(e){
643      var closeBtn = e.target && e.target.closest ? e.target.closest('[data-sqv2-directions-close]') : null;
644      if(closeBtn){ e.preventDefault(); closeMapSheet(); return; }
645      var sheet = qs2('[data-sqv2-map-choice]');
646      if(sheet && e.target === sheet){ e.preventDefault(); closeMapSheet(); return; }
647      var trigger = e.target && e.target.closest ? e.target.closest('[data-sqv2-directions-open]') : null;
648      if(!trigger) return;
649      e.preventDefault();
650      if(e.stopPropagation) e.stopPropagation();
651      if(isMobileLike() && openMapSheet(trigger)) return;
652      var url = trigger.getAttribute('data-google-url') || trigger.getAttribute('href');
653      if(url) window.open(url, '_blank', 'noopener');
654    }
655    document.addEventListener('click', handleDirectionsEvent, true);
656    document.addEventListener('keydown', function(e){
657      if(e.key === 'Escape') closeMapSheet();
658      var trigger = e.target && e.target.closest ? e.target.closest('[data-sqv2-directions-open]') : null;
659      if(!trigger || (e.key !== 'Enter' && e.key !== ' ')) return;
660      e.preventDefault();
661      if(isMobileLike() && openMapSheet(trigger)) return;
662      var url = trigger.getAttribute('data-google-url') || trigger.getAttribute('href');
663      if(url) window.open(url, '_blank', 'noopener');
664    }, true);
665
666    function digits(v){ return String(v || '').replace(/\D+/g, ''); }
667    function pad2(v){ v = String(v || ''); return v.length === 1 ? '0' + v : v; }
668    function fullYear(y){ y = String(y || ''); return y.length === 2 ? '20' + y : y.slice(0, 4); }
669    function dateTextFromIso(v){
670      var m = String(v || '').match(/^(\d{4})-(\d{2})-(\d{2})$/);
671      return m ? (m[3] + '.' + m[2] + '.' + m[1]) : '';
672    }
673    function normalizeDateLive(v){
674      var raw = String(v || '').trim().replace(/[\/\-]/g, '.').replace(/[^0-9.]/g, '');
675      if(!raw) return '';
676      if(raw.indexOf('.') !== -1){
677        var trailingDot = raw.slice(-1) === '.';
678        var parts = raw.split('.');
679        var day = (parts[0] || '').replace(/\D/g, '').slice(0, 2);
680        var month = (parts[1] || '').replace(/\D/g, '').slice(0, 2);
681        var year = (parts.slice(2).join('') || '').replace(/\D/g, '').slice(0, 4);
682        var out = day;
683        if(parts.length > 1) out += '.' + month;
684        if(parts.length > 2) out += '.' + year;
685        if(trailingDot && out.slice(-1) !== '.') out += '.';
686        return out.slice(0, 10);
687      }
688      var d = digits(raw).slice(0, 8);
689      if(!d) return '';
690      if(d.length <= 2) return d;
691      if(d.length <= 4) return d.slice(0, 2) + '.' + d.slice(2);
692      return d.slice(0, 2) + '.' + d.slice(2, 4) + '.' + d.slice(4);
693    }
694    function normalizeDateFinal(v){
695      var raw = String(v || '').trim();
696      var p = raw.match(/^(\d{1,2})[.\-/](\d{1,2})[.\-/](\d{2}|\d{4})$/);
697      if(p) return pad2(p[1]).slice(-2) + '.' + pad2(p[2]).slice(-2) + '.' + fullYear(p[3]);
698      var d = digits(raw).slice(0, 8);
699      if(!d) return '';
700      if(d.length <= 2) return d;
701      if(d.length <= 4) return d.slice(0, 2) + '.' + d.slice(2);
702      var day = d.slice(0, 2);
703      var month = d.slice(2, 4);
704      var year = d.slice(4);
705      if(year.length === 2) year = '20' + year;
706      return day + '.' + month + '.' + year;
707    }
708    function isoFromDateText(v){
709      var normalized = normalizeDateFinal(v);
710      var m = normalized.match(/^(\d{2})\.(\d{2})\.(\d{4})$/);
711      return m ? (m[3] + '-' + m[2] + '-' + m[1]) : '';
712    }
713    function normalizeTimeLive(v){
714      var raw = String(v || '').trim().replace(/[.\-]/g, ':').replace(/[^0-9:]/g, '');
715      if(!raw) return '';
716      if(raw.indexOf(':') !== -1){
717        var trailingColon = raw.slice(-1) === ':';
718        var parts = raw.split(':');
719        var h = (parts[0] || '').replace(/\D/g, '').slice(0, 2);
720        var m = (parts.slice(1).join('') || '').replace(/\D/g, '').slice(0, 2);
721        return h + ':' + m + (trailingColon && m === '' ? '' : '');
722      }
723      var d = digits(raw).slice(0, 4);
724      if(!d) return '';
725      if(d.length <= 2) return d;
726      return d.slice(0, 2) + ':' + d.slice(2);
727    }
728    function normalizeTimeFinal(v){
729      var raw = String(v || '').trim();
730      var h = '', m = '';
731      var p = raw.match(/^(\d{1,2})[:.\-](\d{0,2})$/);
732      var d = digits(raw).slice(0, 4);
733      if(p){ h = p[1]; m = p[2] || '00'; }
734      else if(d.length <= 2){ h = d; m = '00'; }
735      else { h = d.slice(0, 2); m = d.slice(2, 4); }
736      if(!h) return '';
737      var hh = Math.min(23, Math.max(0, parseInt(h || '0', 10)));
738      var mm = Math.min(59, Math.max(0, parseInt(m || '0', 10)));
739      return pad2(hh) + ':' + pad2(mm);
740    }
741    function syncDateText(input, final){
742      if(!input) return;
743      var before = input.value;
744      var after = final ? normalizeDateFinal(before) : normalizeDateLive(before);
745      if(before !== after){ input.value = after; try{ input.setSelectionRange(after.length, after.length); }catch(e){} }
746      var native = input.closest('.sqv2-picker-field') && input.closest('.sqv2-picker-field').querySelector('[data-sqv2-native-date]');
747      var iso = isoFromDateText(input.value);
748      if(native && iso) native.value = iso;
749    }
750    function syncTimeText(input, final){
751      if(!input) return;
752      var before = input.value;
753      var after = final ? normalizeTimeFinal(before) : normalizeTimeLive(before);
754      if(before !== after){ input.value = after; try{ input.setSelectionRange(after.length, after.length); }catch(e){} }
755    }
756    function openNativePicker(native){
757      if(!native) return;
758      try { if(native.showPicker){ native.showPicker(); return; } } catch(err) {}
759      try { native.focus(); native.click(); } catch(err2) {}
760    }
761    // Do not live-format while the user is typing. Live masking can swallow
762    // zeros such as 16:00 in some mobile/desktop browsers. Final formatting
763    // runs on blur/change/submit only.
764    document.addEventListener('blur', function(e){
765      if(e.target && e.target.matches('[data-sqv2-date-az]')) syncDateText(e.target, true);
766      if(e.target && e.target.matches('[data-sqv2-time-az], [data-sqv2-time-stable]')) syncTimeText(e.target, true);
767    }, true);
768    document.addEventListener('change', function(e){
769      if(e.target && e.target.matches('[data-sqv2-native-date]')){
770        var textDate = e.target.closest('.sqv2-picker-field') && e.target.closest('.sqv2-picker-field').querySelector('[data-sqv2-date-az]');
771        if(textDate) textDate.value = dateTextFromIso(e.target.value);
772      }
773      if(e.target && e.target.matches('[data-sqv2-date-az]')) syncDateText(e.target, true);
774      if(e.target && e.target.matches('[data-sqv2-time-az], [data-sqv2-time-stable]')) syncTimeText(e.target, true);
775    }, true);
776    document.addEventListener('paste', function(e){
777      if(e.target && e.target.matches('[data-sqv2-date-az], [data-sqv2-time-az], [data-sqv2-time-stable]')){
778        window.setTimeout(function(){
779          if(e.target.matches('[data-sqv2-date-az]')) syncDateText(e.target, true);
780          if(e.target.matches('[data-sqv2-time-az], [data-sqv2-time-stable]')) syncTimeText(e.target, true);
781        }, 0);
782      }
783    }, true);
784    document.addEventListener('click', function(e){
785      var dateBtn = e.target && e.target.closest ? e.target.closest('[data-sqv2-open-date]') : null;
786      var timeBtn = e.target && e.target.closest ? e.target.closest('[data-sqv2-open-time]') : null;
787      if(dateBtn){
788        e.preventDefault(); e.stopPropagation();
789        var wrap = dateBtn.closest('.sqv2-picker-field');
790        var text = wrap && wrap.querySelector('[data-sqv2-date-az]');
791        var native = wrap && wrap.querySelector('[data-sqv2-native-date]');
792        if(text) syncDateText(text, true);
793        if(native && text){ var iso = isoFromDateText(text.value); if(iso) native.value = iso; }
794        openNativePicker(native);
795      }
796      if(timeBtn){
797        e.preventDefault(); e.stopPropagation();
798        var wrap2 = timeBtn.closest('.sqv2-picker-field');
799        var text2 = wrap2 && wrap2.querySelector('[data-sqv2-time-az], [data-sqv2-time-stable]');
800        var menu = wrap2 && wrap2.querySelector('[data-sqv2-time-menu]');
801        if(menu){
802          var open = menu.hasAttribute('hidden');
803          qsa2('[data-sqv2-time-menu]').forEach(function(m){ m.hidden = true; m.classList.remove('is-open'); });
804          if(open){ menu.hidden = false; menu.classList.add('is-open'); }
805        }
806        if(text2){ text2.focus(); }
807      }
808    }, true);
809    document.addEventListener('click', function(e){
810      var opt = e.target && e.target.closest ? e.target.closest('[data-sqv2-time-option]') : null;
811      if(opt){
812        e.preventDefault(); e.stopPropagation();
813        var wrap = opt.closest('.sqv2-picker-field');
814        var input = wrap && wrap.querySelector('[data-sqv2-time-az], [data-sqv2-time-stable]');
815        if(input){ input.value = opt.getAttribute('value') || opt.textContent.trim(); input.focus(); }
816        var menu = wrap && wrap.querySelector('[data-sqv2-time-menu]');
817        if(menu){ menu.hidden = true; menu.classList.remove('is-open'); }
818        return;
819      }
820      if(!e.target.closest || !e.target.closest('.sqv2-picker-field')){
821        qsa2('[data-sqv2-time-menu]').forEach(function(m){ m.hidden = true; m.classList.remove('is-open'); });
822      }
823    }, true);
824    document.addEventListener('submit', function(e){
825      var form = e.target && e.target.closest ? e.target.closest('.sqv2-tour-form') : null;
826      if(!form) return;
827      qsa2('[data-sqv2-date-az]', form).forEach(function(input){ syncDateText(input, true); });
828      qsa2('[data-sqv2-time-az], [data-sqv2-time-stable]', form).forEach(function(input){ syncTimeText(input, true); });
829    }, true);
830  })();
831
832
833
834  /* Step 53: make district detail chips scroll reliably on touch devices. */
835  (function(){
836    function rows(){ return Array.prototype.slice.call(document.querySelectorAll('.district-side-nav-scroll')); }
837    function prepare(row){
838      if(!row || row.__sqv2ChipScrollReady) return;
839      row.__sqv2ChipScrollReady = true;
840      row.setAttribute('data-sqv2-chip-scroll','true');
841      var active = row.querySelector('a.active');
842      if(active && active.scrollIntoView){
843        window.setTimeout(function(){
844          try{ active.scrollIntoView({behavior:'smooth', block:'nearest', inline:'center'}); }catch(e){}
845        }, 150);
846      }
847      var dragging = false;
848      var moved = false;
849      var startX = 0;
850      var startLeft = 0;
851      function endDrag(e){
852        if(!dragging) return;
853        dragging = false;
854        row.classList.remove('is-dragging');
855        try{ if(e && e.pointerId != null) row.releasePointerCapture(e.pointerId); }catch(err){}
856        window.setTimeout(function(){ moved = false; }, 80);
857      }
858      row.addEventListener('pointerdown', function(e){
859        if(e.pointerType === 'mouse' && e.button !== 0) return;
860        if(row.scrollWidth <= row.clientWidth + 2) return;
861        dragging = true;
862        moved = false;
863        startX = e.clientX;
864        startLeft = row.scrollLeft;
865        row.classList.add('is-dragging');
866        try{ row.setPointerCapture(e.pointerId); }catch(err){}
867      }, {passive:true});
868      row.addEventListener('pointermove', function(e){
869        if(!dragging) return;
870        var dx = e.clientX - startX;
871        if(Math.abs(dx) > 3){
872          moved = true;
873          row.scrollLeft = startLeft - dx;
874          if(e.cancelable) e.preventDefault();
875        }
876      }, {passive:false});
877      row.addEventListener('pointerup', endDrag, {passive:true});
878      row.addEventListener('pointercancel', endDrag, {passive:true});
879      row.addEventListener('mouseleave', endDrag, {passive:true});
880      row.addEventListener('click', function(e){
881        if(moved){
882          e.preventDefault();
883          e.stopPropagation();
884          if(e.stopImmediatePropagation) e.stopImmediatePropagation();
885        }
886      }, true);
887    }
888    function init(){ rows().forEach(prepare); }
889    if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();
890    window.addEventListener('resize', function(){ rows().forEach(function(row){ row.style.overflowX = 'auto'; }); }, {passive:true});
891  })();
892
893
894  /* Step 54: robust native + drag horizontal scroll for district chips and hero timeline. */
895  (function(){
896    function toArray(list){ return Array.prototype.slice.call(list || []); }
897    function scrollers(){
898      return toArray(document.querySelectorAll('.district-side-nav, .district-side-nav-scroll, .hero-timeline, .hero-timeline .timeline-wrap'));
899    }
900    function isScrollable(el){ return el && el.scrollWidth > el.clientWidth + 4; }
901    function prepare(el){
902      if(!el || el.__sqv2HScroll54) return;
903      el.__sqv2HScroll54 = true;
904      var dragging = false;
905      var moved = false;
906      var startX = 0;
907      var startLeft = 0;
908      function start(e){
909        if(e.pointerType === 'mouse' && e.button !== 0) return;
910        if(!isScrollable(el)) return;
911        dragging = true;
912        moved = false;
913        startX = e.clientX;
914        startLeft = el.scrollLeft;
915        el.classList.add('is-dragging');
916        try{ el.setPointerCapture(e.pointerId); }catch(err){}
917        if(e.stopPropagation) e.stopPropagation();
918      }
919      function move(e){
920        if(!dragging) return;
921        var dx = e.clientX - startX;
922        if(Math.abs(dx) > 2){
923          moved = true;
924          el.scrollLeft = startLeft - dx;
925          if(e.cancelable) e.preventDefault();
926          if(e.stopPropagation) e.stopPropagation();
927        }
928      }
929      function end(e){
930        if(!dragging) return;
931        dragging = false;
932        el.classList.remove('is-dragging');
933        try{ if(e && e.pointerId != null) el.releasePointerCapture(e.pointerId); }catch(err){}
934        window.setTimeout(function(){ moved = false; }, 90);
935        if(e && e.stopPropagation) e.stopPropagation();
936      }
937      el.addEventListener('pointerdown', start, {passive:true});
938      el.addEventListener('pointermove', move, {passive:false});
939      el.addEventListener('pointerup', end, {passive:true});
940      el.addEventListener('pointercancel', end, {passive:true});
941      el.addEventListener('mouseleave', end, {passive:true});
942      el.addEventListener('click', function(e){
943        if(moved){
944          e.preventDefault();
945          e.stopPropagation();
946          if(e.stopImmediatePropagation) e.stopImmediatePropagation();
947        }
948      }, true);
949      ['touchstart','touchmove','touchend'].forEach(function(type){
950        el.addEventListener(type, function(e){ if(isScrollable(el) && e.stopPropagation) e.stopPropagation(); }, {passive:true});
951      });
952    }
953    function centerActive(){
954      toArray(document.querySelectorAll('.district-side-nav-scroll a.active, .hero-timeline .timeline-node.active')).forEac
954h(function(active){
955        var parent = active.closest('.district-side-nav') || active.closest('.district-side-nav-scroll') || active.closest('.hero-timeline') || active.parentElement;
956        if(parent && isScrollable(parent)){
957          var left = active.offsetLeft - (parent.clientWidth - active.offsetWidth) / 2;
958          try{ parent.scrollTo({left:Math.max(0,left), behavior:'smooth'}); }catch(e){ parent.scrollLeft = Math.max(0,left); }
959        }
960      });
961    }
962    function init(){ scrollers().forEach(prepare); window.setTimeout(centerActive, 180); }
963    if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();
964    window.addEventListener('resize', function(){ window.setTimeout(init, 60); }, {passive:true});
965  })();
966
967})();

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.