PageSourceSearch

https://flagishtoki.ru/js/mobile-nav.js

js flagishtoki.ru collected 2026-10-08 11:18:27 UTC 15,930 bytes, 430 lines download raw bytes

1(function() {
2  var NOVOFON_SCRIPT_SRC = 'https://widget.novofon.ru/novofon.js?k=05_xeUX36XdX3PbrlMNWhXSFAkADRo7G';
3  var FDPOPUP_SCRIPT_SRC = 'https://feedback.flagishtoki.ru/js/widgets/popup.js?v=0.0.3';
4  var THIRD_PARTY_TIMEOUT = 5000;
5  var scriptPromises = {};
6
7  function hasNovofon() {
8    return Boolean(window.Novofon && typeof window.Novofon.openSiteRequestPanel === 'function');
9  }
10
11  function hasFDPopup() {
12    return Boolean(window.FDPopup && typeof window.FDPopup.open === 'function');
13  }
14
15  function waitForCondition(check, timeout) {
16    return new Promise(function(resolve, reject) {
17      var startedAt = Date.now();
18
19      function step() {
20        if (check()) {
21          resolve();
22          return;
23        }
24
25        if (Date.now() - startedAt >= timeout) {
26          reject(new Error('Third-party script timed out.'));
27          return;
28        }
29
30        window.setTimeout(step, 50);
31      }
32
33      step();
34    });
35  }
36
37  function getExistingScript(src) {
38    return document.querySelector('script[src="' + src + '"]');
39  }
40
41  function ensureScript(src, readyCheck) {
42    if (readyCheck()) {
43      return Promise.resolve();
44    }
45
46    if (scriptPromises[src]) {
47      return scriptPromises[src];
48    }
49
50    scriptPromises[src] = new Promise(function(resolve, reject) {
51      var script = getExistingScript(src);
52
53      function waitForReady() {
54        waitForCondition(readyCheck, THIRD_PARTY_TIMEOUT).then(resolve).catch(reject);
55      }
56
57      if (script) {
58        script.addEventListener('load', waitForReady, { once: true });
59        script.addEventListener('error', reject, { once: true });
60        waitForReady();
61        return;
62      }
63
64      script = document.createElement('script');
65      script.async = true;
66      script.src = src;
67      script.dataset.flagishtokiLazy = 'true';
68      script.addEventListener('load', waitForReady, { once: true });
69      script.addEventListener('error', reject, { once: true });
70      (document.head || document.documentElement).appendChild(script);
71    }).catch(function(error) {
72      delete scriptPromises[src];
73      throw error;
74    });
75
76    return scriptPromises[src];
77  }
78
79  function ensureNovofonLoaded() {
80    return ensureScript(NOVOFON_SCRIPT_SRC, hasNovofon);
81  }
82
83  function ensureFDPopupLoaded() {
84    return ensureScript(FDPOPUP_SCRIPT_SRC, hasFDPopup);
85  }
86
87  function warmNovofonLoader() {
88    ensureNovofonLoaded().catch(function() {
89      return null;
90    });
91  }
92
93  function warmFDPopupLoader() {
94    ensureFDPopupLoaded().catch(function() {
95      return null;
96    });
97  }
98
99  function openCallbackWithFallback(fallbackUrl) {
100    var safeFallback = fallbackUrl || 'kontakty.html';
101
102    ensureNovofonLoaded().then(function() {
103      try {
104        if (hasNovofon()) {
105          window.Novofon.openSiteRequestPanel();
106          return;
107        }
108      } catch (error) {
109        // Fall back to the contacts page when the third-party callback widget fails.
110      }
111
112      window.location.href = safeFallback;
113    }).catch(function() {
114      window.location.href = safeFallback;
115    });
116
117    return false;
118  }
119
120  function openFeedbackPopupWithFallback(element, formId) {
121    var popupId = Number(formId || (element && element.getAttribute('data-feedback-popup-id')) || 0);
122    var fallbackHref = element && element.getAttribute ? element.getAttribute('href') : '';
123    var overlayOpacity = Number(element && element.getAttribute ? element.getAttribute('data-feedback-popup-overlay-opacity') : '0.1');
124    var options = {
125      hideCloseBtn: false,
126      host: element && element.getAttribute ? (element.getAttribute('data-feedback-popup-host') || 'feedback.flagishtoki.ru') : 'feedback.flagishtoki.ru',
127      overlay: {
128        background: element && element.getAttribute ? (element.getAttribute('data-feedback-popup-overlay-background') || '#000000') : '#000000',
129        opacity: Number.isFinite(overlayOpacity) ? overlayOpacity : 0.1
130      }
131    };
132
133    if (!popupId) {
134      return true;
135    }
136
137    ensureFDPopupLoaded().then(function() {
138      if (hasFDPopup()) {
139        window.FDPopup.open(popupId, options);
140        return;
141      }
142
143      if (fallbackHref) {
144        window.location.href = fallbackHref;
145      }
146    }).catch(function() {
147      if (fallbackHref) {
148        window.location.href = fallbackHref;
149      }
150    });
151
152    return false;
153  }
154
155  function bindWarmupTriggers(selector, handler) {
156    var nodes = document.querySelectorAll(selector);
157
158    Array.prototype.forEach.call(nodes, function(node) {
159      if (node.dataset.flagishtokiWarmBound === 'true') {
160        return;
161      }
162
163      node.dataset.flagishtokiWarmBound = 'true';
164      node.addEventListener('pointerenter', handler, { once: true });
165      node.addEventListener('focusin', handler, { once: true });
166      node.addEventListener('touchstart', handler, { once: true, passive: true });
167    });
168  }
169
170  function loadDeferredIframe(iframe) {
171    if (!iframe || iframe.dataset.loaded === 'true') {
172      return;
173    }
174
175    var src = iframe.getAttribute('data-src');
176    if (!src) {
177      return;
178    }
179
180    iframe.src = src;
181    iframe.dataset.loaded = 'true';
182  }
183
184  function initYandexRatingBadges() {
185    var frames = Array.prototype.slice.call(document.querySelectorAll('.site-footer__ratingFrame[data-src]'));
186
187    if (!frames.length) {
188      return;
189    }
190
191    if ('IntersectionObserver' in window) {
192      var observer = new IntersectionObserver(function(entries) {
193        entries.forEach(function(entry) {
194          if (entry.isIntersecting) {
195            loadDeferredIframe(entry.target);
196            observer.unobserve(entry.target);
197          }
198        });
199      }, {
200        rootMargin: '220px 0px'
201      });
202
203      frames.forEach(function(frame) {
204        observer.observe(frame);
205      });
206      return;
207    }
208
209    window.addEventListener('load', function() {
210      window.setTimeout(function() {
211        frames.forEach(loadDeferredIframe);
212      }, 1200);
213    }, { once: true });
214  }
215  function ready(callback) {
216    if (document.readyState === 'loading') {
217      document.addEventListener('DOMContentLoaded', callback, { once: true });
218      return;
219    }
220
221    callback();
222  }
223
224  ready(function() {
225    window.flagishtokiOpenCallback = openCallbackWithFallback;
226    window.flagishtokiOpenFeedbackPopup = openFeedbackPopupWithFallback;
227
228    bindWarmupTriggers('[data-callback-trigger]', warmNovofonLoader);
229    bindWarmupTriggers('[data-feedback-popup-id]', warmFDPopupLoader);
230
231    var body = document.body;
232    var docEl = document.documentElement;
233    var shell = document.querySelector('.shell');
234    var toggle;
235    var drawer;
236    var backdrop;
237    var isBound = false;
238
239    if (!body || !shell) {
240      return;
241    }
242
243    function buildMobileLayout() {
244      var existingHeader = document.querySelector('.mobile-header');
245      var existingBackdrop = document.querySelector('.mobile-drawer-backdrop');
246      var existingDrawer = document.getElementById('mobileDrawer');
247      var aside;
248      var header;
249
250      if (!existingBackdrop) {
251        existingBackdrop = document.createElement('div');
252        existingBackdrop.className = 'mobile-drawer-backdrop';
253        existingBackdrop.setAttribute('data-mobile-menu-close', 'true');
254        body.insertBefore(existingBackdrop, body.firstChild);
255      }
256
257      if (!existingDrawer) {
258        aside = document.createElement('aside');
259        aside.className = 'mobile-drawer';
260        aside.id = 'mobileDrawer';
261        aside.setAttribute('aria-hidden', 'true');
262        aside.innerHTML = '' +
263          '<div class="mobile-drawer__head">' +
264            '<div>' +
265              '<div class="mobile-drawer__title">Меню</div>' +
266              '<div class="mobile-drawer__subtitle">Быстрый доступ к разделам и каталогу продукции.</div>' +
267            '</div>' +
268            '<button type="button" class="mobile-drawer__close" data-mobile-menu-close="true" aria-label="Закрыть меню">×</button>' +
269          '</div>' +
270          '<a class="mobile-drawer__call" href="tel:+74956210144">+7 495 621-01-44<span>Связаться с менеджером</span></a>' +
271          '<div class="mobile-drawer__section">' +
272            '<strong>Быстрые ссылки</strong>' +
273            '<ul class="mobile-drawer__links">' +
274              '<li><a href="produkciya.html">Продукция</a></li>' +
275              '<li><a href="galereya.html">Галерея</a></li>' +
276              '<li><a href="akcii-na-flagshtoki.html">Акции</a></li>' +
277              '<li><a target="_blank" rel="noopener noreferrer" data-feedback-popup-id="45182" data-feedback-popup-host="feedback.flagishtoki.ru" data-feedback-popup-overlay-background="#000000" data-feedback-popup-overlay-opacity="0.1" onclick="return window.flagishtokiOpenFeedbackPopup ? window.flagishtokiOpenFeedbackPopup(this, 45182) : true;" href="https://feedback.flagishtoki.ru/form/view/45182">Оставить заявку</a></li>' +
278            '</ul>' +
279          '</div>' +
280          '<div class="mobile-drawer__section">' +
281            '<strong>Каталог</strong>' +
282            '<ul class="mobile-drawer__catalog">' +
283              '<li><a href="flagshtoki.html">Флагштоки</a></li>' +
284              '<li><a href="mobilnye-flagshtoki.html">Мобильные флагштоки</a></li>' +
285              '<li>
285<a href="kronshteyny-dlya-flagov.html">Кронштейны для флагов</a></li>' +
286              '<li><a href="pechat-flagov.html">Печать флагов</a></li>' +
287              '<li><a href="znamyona-i-gerby.html">Знамена, гербы</a></li>' +
288              '<li><a href="aksessuary-dlya-flagshtokov.html">Аксессуары</a></li>' +
289              '<li><a href="ceny-na-flagshtoki-i-flagi.html">Цены</a></li>' +
290              '<li><a href="dostavka-flagshtokov.html">Доставка</a></li>' +
291              '<li><a href="ustanovka-flagshtokov.html">Установка</a></li>' +
292              '<li><a href="garantiya-na-flagshtoki.html">Гарантия</a></li>' +
293              '<li><a href="obsluzhivanie-i-remont-flagshtokov.html">Обслуживание, ремонт</a></li>' +
294              '<li><a href="tehnicheskaya-dokumentaciya-na-flagshtoki.html">Документация</a></li>' +
295              '<li><a href="arenda-flagshtokov.html">Аренда</a></li>' +
296              '<li><a href="predlozhenie-o-sotrudnichestve.html">Сотрудничество</a></li>' +
297              '<li><a href="o-kompanii.html">О компании</a></li>' +
298            '</ul>' +
299          '</div>';
300        body.insertBefore(aside, shell);
301      }
302
303      if (!existingHeader) {
304        header = document.createElement('div');
305        header.className = 'mobile-header';
306        header.innerHTML = '' +
307          '<div class="mobile-header__bar">' +
308            '<div class="mobile-header__left">' +
309              '<button type="button" class="mobile-menu-toggle" id="mobileMenuToggle" data-mobile-menu-toggle="true" aria-controls="mobileDrawer" aria-expanded="false" aria-label="Открыть меню">' +
310                '<span></span>' +
311                '<span></span>' +
312                '<span></span>' +
313              '</button>' +
314              '<a class="mobile-brand" href="/">' +
315                '<strong>Флаги и Штоки</strong>' +
316                '<span>Флагштоки и флаги с доставкой по России</span>' +
317              '</a>' +
318            '</div>' +
319            '<div class="mobile-header__right">' +
320              '<a class="mobile-phone-cta" href="tel:+74956210144">+7 495 621-01-44</a>' +
321            '</div>' +
322          '</div>' +
323          '<div class="mobile-header__utility">' +
324            '<div class="mobile-header__search">' +
325              '<div class="home-search home-search--mobile" data-home-search>' +
326                '<form class="home-search__form" action="search.html" method="get" role="search" novalidate>' +
327                  '<label class="page-title-hidden" for="mobile-search-query">Поиск по каталогу</label>' +
328                  '<input class="home-search__field" id="mobile-search-query" type="search" name="q" value="" placeholder="Например: флагшток, флаг, кронштейн" autocomplete="off">' +
329                  '<button class="home-search__button" type="submit" aria-label="Найти"></button>' +
330                '</form>' +
331                '<ul class="home-search__results" data-home-search-results></ul>' +
332              '</div>' +
333            '</div>' +
334            '<button type="button" class="mobile-callback-cta" data-callback-trigger="true" data-callback-fallback="kontakty.html">Заказать звонок</button>' +
335          '</div>';
336        shell.insertBefore(header, shell.firstChild);
337      }
338    }
339
340    function syncElements() {
341      toggle = document.getElementById('mobileMenuToggle');
342      drawer = document.getElementById('mobileDrawer');
343      backdrop = document.querySelector('.mobile-drawer-backdrop');
344      Array.prototype.forEach.call(document.querySelectorAll('.mobile-callback-cta'), function(button) {
345        var fallbackUrl = button.getAttribute('data-callback-fallback') || 'kontakty.html';
346
347        button.setAttribute('data-callback-fallback', fallbackUrl);
348        button.setAttribute('data-callback-trigger', 'true');
349        button.removeAttribute('onclick');
350
351        if (button.dataset.callbackBound === 'true') {
352          return;
353        }
354
355        button.addEventListener('click', function(event) {
356          event.preventDefault();
357          openCallbackWithFallback(fallbackUrl);
358        });
359        button.dataset.callbackBound = 'true';
360      });
361
362      if (toggle && !toggle.hasAttribute('data-mobile-menu-toggle')) {
363        toggle.setAttribute('data-mobile-menu-toggle', 'true');
364      }
365    }
366
367    function setMenuState(isOpen) {
368      if (!toggle || !drawer) {
369        return;
370      }
371
372      body.classList.toggle('mobile-menu-open', isOpen);
373      docEl.classList.toggle('mobile-menu-open', isOpen);
374      toggle.classList.toggle('is-active', isOpen);
375      toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
376      drawer.setAttribute('aria-hidden', isOpen ? 'false' : 'true');
377
378      if (backdrop) {
379        backdrop.setAttribute('aria-hidden', isOpen ? 'false' : 'true');
380      }
381    }
382
383    function bindInteractions() {
384      if (isBound) {
385        return;
386      }
387
388      document.addEventListener('click', function(event) {
389        var toggleTarget = event.target.closest('[data-mobile-menu-toggle]');
390        var closeTarget = event.target.closest('[data-mobile-menu-close]');
391        var linkTarget = drawer && event.target.closest('#mobileDrawer a');
392
393        if (toggleTarget) {
394          event.preventDefault();
395          setMenuState(!body.classList.contains('mobile-menu-open'));
396          return;
397        }
398
399        if (closeTarget || linkTarget) {
400          setMenuState(false);
401        }
402      });
403
404      document.addEventListener('keydown', function(event) {
405        if (event.key === 'Escape') {
406          setMenuState(false);
407        }
408      });
409
410      window.addEventListener('resize', function() {
411        if (window.innerWidth > 900) {
412          setMenuState(false);
413        }
414      });
415
416      isBound = true;
417    }
418
419    buildMobileLayout();
420    syncElements();
421    initYandexRatingBadges();
422
423    if (!toggle || !drawer) {
424      return;
425    }
426
427    bindInteractions();
428    setMenuState(false);
429  });
430})();

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.