PageSourceSearch

https://premier.pet/bitrix/cache/js/r1/premier_ru/page_1f56f8d72cb…4ee70a4689deb81e873e6_v1.js?178637769755730

js premier.pet collected 2026-10-06 04:40:42 UTC 55,730 bytes, 1,304 lines download raw bytes

1
2; /* Start:"a:4:{s:4:"full";s:53:"/local/templates/premier_ru/js/map.js?178637747355470";s:6:"source";s:37:"/local/templates/premier_ru/js/map.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
3// /**** MAP (Yandex) ****/
4
5function sendMapGoal(goal) {
6    if (typeof window.ym !== 'function') return;
7    window.ym(90492667, 'reachGoal', goal);
8}
9
10function sendTrackingPixel(url) {
11    if (!url) return;
12
13    var normalizedUrl = String(url);
14    var hasProtocol = /^https?:\/\//i.test(normalizedUrl);
15    var hasProtocolRelative = /^\/\//.test(normalizedUrl);
16
17    if (!hasProtocol && !hasProtocolRelative) {
18        normalizedUrl = 'https://' + normalizedUrl;
19    } else if (hasProtocolRelative) {
20        normalizedUrl = 'https:' + normalizedUrl;
21    }
22
23    if (/^https:\/\/bu--s\d+\.sync\.t2\.ru(?:\/|$|\?)/i.test(normalizedUrl)) {
24        var t2BaseUrl = normalizedUrl.split('?')[0].replace(/\/+$/, '');
25        normalizedUrl = t2BaseUrl + '/api/v1/postback?request=sync';
26    }
27
28    var img = new Image();
29
30    window.__trackingPixels = window.__trackingPixels || [];
31    window.__trackingPixels.push(img);
32
33    img.onload = img.onerror = function () {
34        window.setTimeout(function () {
35            var index = window.__trackingPixels.indexOf(img);
36            if (index !== -1) {
37                window.__trackingPixels.splice(index, 1);
38            }
39        }, 0);
40    };
41
42    img.src = normalizedUrl;
43}
44
45function sendT2(url) {
46    sendTrackingPixel(url);
47}
48
49function sendRostelecom(url) {
50    sendTrackingPixel(url);
51}
52
53function initMapGoalsTracking() {
54    document.addEventListener('click', (event) => {
55        const target = event.target;
56        if (!target || !target.closest) return;
57
58        const onlineTab = target.closest('#onlineShops');
59        if (onlineTab) {
60            sendMapGoal('visit_online_stores_section');
61            return;
62        }
63
64        const searchInput = target.closest('.search-input');
65        if (searchInput && searchInput.closest('.section-map')) {
66            sendMapGoal('interaction_search_where_buy');
67            //sendT2('bu--s340.sync.t2.ru');
68            //sendRostelecom('9a--205.stbid.ru');
69
70        }
71    });
72}
73
74initMapGoalsTracking();
75ymaps.ready(init);
76
77function init() {
78    /* =====================
79       1) Карта
80    ===================== */
81
82    const defaultCenter = [55.751574, 37.573856];
83    const geoCenter = getGeoCenterFromCookie();
84
85    const MAX_ZOOM = 21;
86    const map = new ymaps.Map('map', {
87        center: geoCenter ?? defaultCenter,
88        zoom: 10,
89        maxZoom: MAX_ZOOM,
90        controls: []
91    });
92    map.options.set('suppressMapOpenBlock', true);
93    map.options.set('yandexMapDisablePoiInteractivity', true);
94
95    window.addEventListener('geo:city-change', (event) => {
96        const lat = event?.detail?.lat;
97        const lon = event?.detail?.lon;
98        if (!Number.isFinite(lat) || !Number.isFinite(lon)) return;
99        map.setCenter([lat, lon], 13, {duration: 300});
100    });
101
102    map.behaviors.disable('scrollZoom');
103    map.behaviors.enable('multiTouch');
104    map.behaviors.enable('dblClickZoom');
105
106    const geolocation = ymaps.geolocation;
107    const isMobileNow = () => window.matchMedia('(max-width: 767px)').matches;
108    const enableCtrlScrollZoom = () => {
109        if (isMobileNow()) return;
110        map.behaviors.enable('scrollZoom');
111    };
112    const disableCtrlScrollZoom = () => {
113        map.behaviors.disable('scrollZoom');
114    };
115
116    document.addEventListener('keydown', (event) => {
117        if (event.key === 'Control') enableCtrlScrollZoom();
118    });
119    document.addEventListener('keyup', (event) => {
120        if (event.key === 'Control') disableCtrlScrollZoom();
121    });
122    window.addEventListener('blur', disableCtrlScrollZoom);
123
124    /* =====================
125       1.1) Иконка геолокации пользователя
126    ===================== */
127
128    function svgToDataUri(svg) {
129        const cleaned = String(svg)
130            .replace(/\s+/g, ' ')
131            .replace(/"/g, "'")
132            .trim();
133        return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(cleaned)}`;
134    }
135
136    const myGeoSvg = `
137<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26" fill="none">
138  <g clip-path="url(#clip0_9507_35558)">
139    <path d="M12.7555 24.033C18.9836 24.033 24.0325 18.9841 24.0325 12.756C24.0325 6.52789 18.9836 1.479 12.7555 1.479C6.5274 1.479 1.47852 6.52789 1.47852 12.756C1.47852 18.9841 6.5274 24.033 12.7555 24.033Z" fill="#E42C00"/>
140    <path d="M12.7555 25.032C5.98552 25.032 0.478516 19.525 0.478516 12.756C0.478516 5.986 5.98552 0.479004 12.7555 0.479004C19.5255 0.479004 25.0325 5.986 25.0325 12.756C25.0325 19.525 19.5255 25.032 12.7555 25.032ZM12.7555 2.479C7.08852 2.479 2.47852 7.089 2.47852 12.756C2.47852 18.423 7.08852 23.032 12.7555 23.032C18.4225 23.032 23.0325 18.422 23.0325 12.756C23.0325 7.09 18.4225 2.479 12.7555 2.479Z" fill="white"/>
141    <path d="M8.08984 17.4829L10.8588 12.5229H12.8148L10.0458 17.4829H8.08984ZM14.5328 17.4829V9.29494H12.1808C11.6218 9.29494 11.1878 9.42494 10.8758 9.68494C10.5648 9.94494 10.4088 10.2859 10.4088 10.7079C10.4088 11.1249 10.5538 11.4609 10.8408 11.7139C11.1288 11.9679 11.5318 12.0939 12.0528 12.0939H15.0888V13.6409H12.0528C11.3278 13.6409 10.7018 13.5209 10.1768 13.2799C9.65084 13.0389 9.24484 12.6969 8.96184 12.2539C8.67884 11.8109 8.53684 11.2869 8.53684 10.6809C8.53684 10.0629 8.68284 9.52994 8.97484 9.07994C9.26684 8.62994 9.68684 8.28
141194 10.2338 8.03494C10.7818 7.78794 11.4298 7.66394 12.1808 7.66394H16.3288V17.4819H14.5328V17.4829Z" fill="white"/>
142  </g>
143  <defs>
144    <clipPath id="clip0_9507_35558">
145      <rect width="25.512" height="25.512" fill="white"/>
146    </clipPath>
147  </defs>
148</svg>
149`;
150
151    const myGeoIconHref = svgToDataUri(myGeoSvg);
152    let myGeoPlacemark = null;
153
154    function getGeoCenterFromCookie() {
155        const prefix = (typeof window.__COOKIE_PREFIX__ === 'string' && window.__COOKIE_PREFIX__)
156            ? window.__COOKIE_PREFIX__
157            : '';
158        if (prefix) {
159            const raw = getCookieValue(`${prefix}_DEVDEV24_GEO_COORDS`);
160            if (raw) {
161                try {
162                    const data = JSON.parse(raw);
163                    const lat = data?.lat;
164                    const lon = data?.lon;
165                    if (Number.isFinite(lat) && Number.isFinite(lon)) {
166                        return [lat, lon];
167                    }
168                } catch (e) {
169                    return null;
170                }
171            }
172        }
173
174        const lat = window.__GEO_COORDS__?.lat;
175        const lon = window.__GEO_COORDS__?.lon;
176        if (Number.isFinite(lat) && Number.isFinite(lon)) {
177            return [lat, lon];
178        }
179
180        return null;
181    }
182
183    function getCookieValue(name) {
184        const target = `${name}=`;
185        const parts = document.cookie.split(';');
186        for (const part of parts) {
187            const item = part.trim();
188            if (item.startsWith(target)) {
189                return decodeURIComponent(item.slice(target.length));
190            }
191        }
192        return '';
193    }
194
195    /* =====================
196       2) Данные точек
197       Ожидаем window.mapPoints в одном из форматов:
198       - { list: [ ... ] }
199       - [ ... ]
200
201       ВАЖНО: показываем только type !== 'shop'
202    ===================== */
203
204    const defaultIcons = {
205        default: '/local/templates/premier_ru/img/marker-default.svg',
206        active: '/local/templates/premier_ru/img/marker-active.svg',
207        search: '/local/templates/premier_ru/img/Pin_shop.svg'
208    };
209
210    async function loadRawList() {
211
212        const url = (typeof window.mapPoints === 'string' && window.mapPoints)
213            ? window.mapPoints
214            : '';
215
216        if (!url) return [];
217
218        try {
219            const res = await fetch(url, {credentials: 'same-origin'});
220            if (!res.ok) return [];
221            const json = await res.json();
222            if (Array.isArray(json?.list)) return json.list;
223            if (Array.isArray(json)) return json;
224            return [];
225        } catch (e) {
226            // eslint-disable-next-line no-console
227            console.error('[map] failed to load points json:', e);
228            return [];
229        }
230    }
231
232    function escHtml(str) {
233        return String(str ?? '')
234            .replaceAll('&', '&amp;')
235            .replaceAll('<', '&lt;')
236            .replaceAll('>', '&gt;')
237            .replaceAll('"', '&quot;')
238            .replaceAll("'", '&#039;');
239    }
240
241    function telHref(phone) {
242        const digits = String(phone ?? '').replace(/\D+/g, '');
243        return digits ? `tel:${digits}` : 'javascript:void(0);';
244    }
245
246    function normalizePoint(p) {
247        // Твой формат:
248        // coord: [lat, lon]
249        // name, fullName, address, phone, time, url, urlTitle, logo, type
250        const coords = Array.isArray(p?.coord) ? p.coord : (Array.isArray(p?.coords) ? p.coords : null);
251        if (!coords || coords.length !== 2) return null;
252
253        const title = (p?.name ?? p?.fullName ?? p?.title ?? '').toString().trim();
254
255        return {
256            id: p?.id ?? null,
257            realId: p?.real_id ?? p?.realId ?? null,
258            type: p?.type ?? null,
259            coords: [Number(coords[0]), Number(coords[1])],
260            title: title || 'Точка на карте',
261            name: p?.name ?? '',
262            fullName: p?.fullName ?? '',
263            address: p?.address ?? '',
264            phone: p?.phone ?? '',
265            time: p?.time ?? '',
266            city: p?.city ?? '',
267            site: p?.url ? {href: p.url, label: (p?.urlTitle ?? p.url)} : null,
268            logo: p?.logo ?? '',
269            email: '',
270            icons: {
271                default: p?.icons?.default ?? p?.iconDefault ?? defaultIcons.default,
272                active: p?.icons?.active ?? p?.iconActive ?? defaultIcons.active
273            },
274            raw: p
275        };
276    }
277
278    function getYandexMapsWebLink(lat, lon, title = '') {
279        const text = title ? `&text=${encodeURIComponent(title)}` : '';
280        return `https://yandex.ru/maps/?ll=${lon},${lat}&pt=${lon},${lat}&z=17&l=map${text}`;
281    }
282
283    function getYandexMapsAppLink(lat, lon) {
284        return `yandexmaps://maps.yandex.ru/?ll=${lon},${lat}&pt=${lon},${lat}&z=17&l=map`;
285    }
286
287    function get2GisLink(lat, lon) {
288        return `https://2gis.ru/geo/${lon},${lat}?m=${lon},${lat}/17`;
289    }
290
291    function renderPointHtml(point) {
292        const address = point.address
293            ? `
294        <div class="map-info__contacts-item">
295        <svg class="map-info__contacts-item-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
296          <path d="M10.0044 10C10.4181 10 10.7708 9.85271 11.0625 9.55813C11.3542 9.26354 11.5 8.90937 11.5 8.49562C11.5 8.08187 11.3527 7.72917 11.0581 7.4375C10.7635 7.14583 10.4094 7 9.99563 7C9.58188 7 9.22917 7.14729 8.9375 7.44188C8.64583 7.73646 8.5 8.09063 8.5 8.50438C8.5 8.91813 8.64729 9.27083 8.94187 9.5625C9.23646 9.85417 9.59062 10 10.0044 10ZM10 16.0208C11.6528 14.5347 12.8993 13.1667 13.7396 11.9167C14.5799 10.6667 15 9.54861 15 8.5625C15 7.10417 14.5243 5.89583 13.5729 4.9375C12.6215 3.97917 11.4306 3.5 10 3.5C8.56944 3.5 7.37847 3.97917 6.42708 4.9375C5.47569 5.89583 5 7.10417 5 8.5625C5 9.54861 5.42014 10.6667 6.26042 11.9167C7.10069 13.1667 8.34722 14.5347 10 16.0208ZM10 18C7.81986 16.179 6.19146 14.4877 5.11479 
29612.926C4.03826 11.3642 3.5 9.90972 3.5 8.5625C3.5 6.70139 4.11806 5.14236 5.35417 3.88542C6.59028 2.62847 8.13542 2 9.98958 2C11.8438 2 13.3924 2.62847 14.6354 3.88542C15.8785 5.14236 16.5 6.70139 16.5 8.5625C16.5 9.90972 15.9653 11.3611 14.8958 12.9167C13.8264 14.4722 12.1944 16.1667 10 18Z" fill="black"></path>
297        </svg>
298          <div class="map-info__contacts-item-text">${escHtml(point.address)}</div>
299        </div>
300      `
301            : '';
302
303        const time = point.time
304            ? `
305        <div class="map-info__contacts-item">
306        <svg class="map-info__contacts-item-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
307                    <path d="M12.7917 13.5417L13.8542 12.4792L10.75 9.375V5H9.25V10L12.7917 13.5417ZM10 18C8.8975 18 7.86146 17.7917 6.89188 17.375C5.92229 16.9583 5.07292 16.3854 4.34375 15.6562C3.61458 14.9271 3.04167 14.0767 2.625 13.105C2.20833 12.1333 2 11.0951 2 9.99042C2 8.88569 2.20833 7.84722 2.625 6.875C3.04167 5.90278 3.61458 5.05556 4.34375 4.33333C5.07292 3.61111 5.92333 3.04167 6.895 2.625C7.86667 2.20833 8.90486 2 10.0096 2C11.1143 2 12.1528 2.21 13.1252 2.63C14.0974 3.05 14.9432 3.62 15.6625 4.34C16.3817 5.06 16.951 5.90667 17.3706 6.88C17.7902 7.85333 18 8.89333 18 10C18 11.1025 17.7917 12.1385 17.375 13.1081C16.9583 14.0777 16.3889 14.9271 15.6667 15.6562C14.9444 16.3854 14.0963 16.9583 13.1223 17.375C12.1483 17.7917 11.1075 18 10 18ZM10.01 16.5C11.8082 16.5 13.3394 15.8646 14.6038 14.5938C15.8679 13.3229 16.5 11.7883 16.5 9.99C16.5 8.19181 15.8679 6.66056 14.6038 5.39625C13.3394 4.13208 11.8082 3.5 10.01 3.5C8.21167 3.5 6.67708 4.13208 5.40625 5.39625C4.13542 6.66056 3.5 8.19181 3.5 9.99C3.5 11.7883 4.13542 13.3229 5.40625 14.5938C6.67708 15.8646 8.21167 16.5 10.01 16.5Z" fill="black"></path>
308                  </svg>
309          <div class="map-info__contacts-item-text">${escHtml(point.time)}</div>
310        </div>
311      `
312            : '';
313
314        const phone = point.phone
315            ? `
316        <div class="map-info__contacts-item">
317        <svg class="map-info__contacts-item-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
318                    <path d="M4 17H3V12.3333L6.54167 11.5833L9.04167 14.0208C9.61111 13.6736 10.1256 13.3227 10.5852 12.9681C11.0448 12.6135 11.4678 12.2422 11.8542 11.8541C12.2569 11.4513 12.6424 11.0069 13.0104 10.5208C13.3785 10.0347 13.7222 9.52079 14.0417 8.97913L11.6042 6.52079L12.3958 2.97913H17V3.97913C17 5.52079 16.6458 7.0694 15.9375 8.62496C15.2292 10.1805 14.2222 11.6111 12.9167 12.9166C11.6111 14.2222 10.1806 15.2291 8.625 15.9375C7.06944 16.6458 5.52778 17 4 17ZM14.7292 7.56246C14.9375 7.07635 15.1042 6.57288 15.2292 6.05204C15.3542 5.53121 15.4444 5.0069 15.5 4.47913H13.5933L13.2292 6.04163L14.7292 7.56246ZM7.54167 14.6458L6.0625 13.2083L4.5 13.5416V15.5C5.01389 15.4305 5.53819 15.3229 6.07292 15.177C6.60764 15.0312 7.09722 14.8541 7.54167 14.6458Z" fill="black"></path>
319                  </svg>
320          <div class="map-info__contacts-item-text">
321            <div class="d-block">${escHtml(point.phone)}</div>
322          </div>
323        </div>
324      `
325            : '';
326
327        const email = point.email
328            ? `
329        <div class="map-info__contacts-item">
330                  <svg class="map-info__contacts-item-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
331                    <path d="M2 16V4H18V16H2ZM10 11L3.5 7.27083V14.5H16.5V7.27083L10 11ZM10 9.25L16.4792 5.5H3.54167L10 9.25ZM3.5 7.27083V5.5V14.5V7.27083Z" fill="black"></path>
332                  </svg>
333                  <div class="map-info__contacts-item-text">${escHtml(point.email)}</div>
334                </div>
335      `
336            : '';
337
338        const site = point.site
339            ? `
340        <a class="button button_blue d-flex" href="${escHtml(point.site.href)}" target="_blank" rel="noopener">
341          ${escHtml(point.site.label)}
342        </a>
343      `
344            : '';
345
346        const [lat, lon] = point.coords;
347
348        const yandexWebLink = getYandexMapsWebLink(lat, lon, point.title);
349        const yandexAppLink = isMobileNow() ? getYandexMapsAppLink(lat, lon) : '';
350        const openOnMaps = `
351        <div class="map-info__maps">
352                <div class="map-info__maps-title">Открыть на картах:</div> 
353                <div class="map-info__maps-block">
354                <a class="map-info__maps-item js-open-yandex-map" href="${yandexWebLink}" data-app-href="${yandexAppLink}" target="_blank" rel="noopener">яндекс</a>
355                <a class="map-info__maps-item" href="${get2GisLink(lat, lon)}" target="_blank" rel="noopener">2gis</a></div>
356              </div>
357              <div class="map-info__note">
358                Фактическое наличие продукта и цену на него необходимо уточнять непосредственно в торговой точке.
359                ИП Эстрин К.В. не несет ответственности за отсутствие продукта в розничных магазинах.
360              </div>
361`
362
363        return `
364      <div class="map-info__contacts">
365        ${address}
366        ${time}
367        ${phone}
368        ${email}
369      </div>
370      ${site}
371      ${openOnMaps}
372    `;
373    }
374
375    document.addEventListener('click', (event) => {
376        const link = event.target.closest('.js-open-yandex-map');
377        if (!link || !isMobileNow()) return;
378
379        const appHref = link.getAttribute('data-app-href');
380        const webHref = link.getAttribute('href');
381        if (!appHref || !webHref) return;
382
383        event.preventDefault();
384
385        let appOpened = false;
386        const onVisibilityChange = () => {
387            if (document.hidden) appOpened = true;
388        };
389        document.addEventListener('visibilitychange', onVisibilityChange, {once: true});
390
391        window.location.href = appHref;
392        window.setTimeout(() => {
393            if (!appOpened) window.location.href = webHref;
394        }, 1200);
395    });
396
397    let activePlacemark = null;
398    let searchPlacemarkById = new Map();
399
400    function escapeRegex(str) {
401        return String(str).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
402    }
403
404    function highlightMatch(text, query) {
405        if (!text) return '';
406        if (!query) return escHtml(text);
407        const safeText = escHtml(text);
408        const safeQuery = escHtml(query);
409        const pattern = new RegExp(escapeRegex(safeQuery), 'gi');
410        return safeText.replace(pattern, (match) => `<span class="json-results__highlight">${match}</span>`);
411    }
412
413    function buildSearchText(values) {
414        return values
415            .filter(Boolean)
416            .join(' ')
417            .toString()
418            .replace(/\s+/g, ' ')
419            .trim()
420            .toLowerCase();
421    }
422
423    function buildLooseSearchText(value) {
424        return String(value ?? '')
425            .toLowerCase()
426            .replace(/ё/g, 'е')
427            .replace(/[^a-zа-я0-9]+/gi, ' ')
428            .replace(/\b(г|город|респ|республика|обл|область|край|ао|автономный|округ|р-н|район|мкр|микрорайон|кв-л|квартал|тер|территория|снт|днт|онст|ст|станция|пос|поселок|посёлок|пгт|с|село|дер|деревня|х|хутор|пр|проезд|пр-д|проток|пр-т|проспект|аллея|ал|б-р|бульвар|наб|набережная|ш|шоссе|туп|тупик|пер|переулок|пл|площадь|ул|улица|линия|линии|д|дом|вл|владение|двлд|домовладение|уч|участок|к|корп|корпус|стр|строение|соор|сооружение|лит|литера|блок|секция|подъезд|кв|квартира|оф|офис|пом|помещение)\b/gi, ' ')
429            .replace(/\s+/g, ' ')
430            .trim();
431    }
432
433    function initSearch(points, shops) {
434        const searchWraps = document.querySelectorAll('.search-wrap');
435        if (!searchWraps.length) return;
436
437        let userCoords = null;
438        let locationRequested = false;
439        let geoSearchPlacemark = null;
440        const geocodeState = {
441            query: '',
442            items: [],
443            requestId: 0
444        };
445
446        function clearGeoPlacemark() {
447            if (!geoSearchPlacemark) return;
448            map.geoObjects.remove(geoSearchPlacemark);
449            geoSearchPlacemark = null;
450        }
451
452        function toRadians(value) {
453            return (value * Math.PI) / 180;
454        }
455
456        function getDistanceMeters(from, to) {
457            if (!from || !to) return null;
458            const [lat1, lon1] = from;
459            const [lat2, lon2] = to;
460            const earthRadius = 6371000;
461            const dLat = toRadians(lat2 - lat1);
462            const dLon = toRadians(lon2 - lon1);
463            const lat1Rad = toRadians(lat1);
464            const lat2Rad = toRadians(lat2);
465            const a = Math.sin(dLat / 2) ** 2
466                + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(dLon / 2) ** 2;
467            const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
468            return earthRadius * c;
469        }
470
471        function formatDistance(meters) {
472            if (!Number.isFinite(meters)) return '';
473            if (meters < 1000) return `${Math.round(meters)} м`;
474            return `${(meters / 1000).toFixed(1).replace('.', ',')} км`;
475        }
476
477        function requestUserLocation(onDone) {
478            if (userCoords || locationRequested) return;
479            if (!('geolocation' in navigator)) return;
480            locationRequested = true;
481            navigator.geolocation.getCurrentPosition(
482                (pos) => {
483                    userCoords = [pos.coords.latitude, pos.coords.longitude];
484                    if (typeof onDone === 'function') onDone();
485                },
486                () => {
487                    locationRequested = true;
488                },
489                {enableHighAccuracy: true, timeout: 8000, maximumAge: 30000}
490            );
491        }
492
493        // Запрашиваем геолокацию сразу при заходе на страницу
494        requestUserLocation();
495
496        const pointsIndexed = points.map((point, index) => {
497            point._searchId = String(index);
498            const addressText = buildSearchText([point.city, point.address]);
499            const addressLooseText = buildLooseSearchText([point.city, point.address].filter(Boolean).join(' '));
500            const nameText = buildSearchText([point.name]);
501            return {
502                point,
503                searchId: String(index),
504                addressText,
505                addressLooseText,
506                nameText
507            };
508        });
509
510        searchWraps.forEach((wrap) => {
511            const block = wrap.closest('.tabs__block--js');
512            const isOnline = block?.getAttribute('data-thumb') === 'map-2';
513            const input = wrap.querySelector('.search-input');
514            const resultsWrap = wrap.querySelector('.map-where__json-results__wrap');
515            const resultsList = wrap.querySelector('.js-json-search-result');
516            const resultsHead = wrap.querySelector('.map-where__json-results__head');
517            const tabs = Array.from(wrap.querySelectorAll('.map-where__json-results__tabs-item'));
518            const optionsWrap = wrap.querySelector('.map-where__find-options');
519
520            if (!input || !resultsWrap || !resultsList || tabs.length < 2) return;
521
522            let mode = 'address';
523
524            const setMode = (nextMode) => {
525                mode = nextMode;
526                tabs.forEach((tab, index) => {
527                    const shouldBeActive = (nextMode === 'address' && index === 0)
528                        || (nextMode === 'name' && index === 1);
529                    tab.classList.toggle('active', shouldBeActive);
530                });
531                if (nextMode === 'address') {
532                    input.setAttribute('placeholder', 'Введите город или адрес');
533                } else {
534                    input.setAttribute('placeholder', 'Введите название');
535                }
536                updateResults();
537            };
538
539            const hideResults = () => {
540                resultsWrap.classList.add('d-none');
541                resultsList.innerHTML = '';
542                resultsList.style.display = 'none';
543            };
544
545            const showResults = () => {
546                resultsWrap.classList.remove('d-none');
547            };
548
549            const updateResults = () => {
550                const query = input.value.trim();
551
552                if (isOnline) {
553                    const queryLower = query.toLowerCase();
554                    const filtered = (shops || []).filter((shop) => {
555                        const nameText = buildSearchText([shop.name]);
556                        return queryLower ? nameText.includes(queryLower) : true;
557                    });
558                    renderOnlineShops(filtered, 0);
559                    hideResults();
560                    return;
561                }
562
563                if (!query) {
564                    resultsList.innerHTML = '';
565                    resultsList.style.display = 'none';
566                    showResults();
567                    return;
568                }
569
570                if (!userCoords) {
571                    requestUserLocation(updateResults);
572                }
573
574                const queryLower = query.toLowerCase();
575                const queryLoose = buildLooseSearchText(query);
576                const queryLooseTokens = queryLoose ? queryLoose.split(' ') : [];
577
578                const addressMatches = (entry) => {
579                    if (entry.addressText.includes(queryLower)) return true;
580                    if (!queryLooseTokens.length) return false;
581                    return queryLooseTokens.every((token) => entry.addressLooseText.includes(token));
582                };
583                const matches = pointsIndexed
584                    .filter((entry) => {
585                        if (mode === 'address') {
586                            return addressMatches(entry);
587                        }
588                        return entry.nameText.includes(queryLower);
589                    })
590                    .map((entry) => {
591                        const distanceMeters = userCoords ? getDistanceMeters(userCoords, entry.point.coords) : null;
592                        return {
593                            ...entry,
594                            distanceMeters: Number.isFinite(distanceMeters) ? distanceMeters : null
595                        };
596                    });
597
598                if (userCoords) {
599                    matches.sort((a, b) => {
600                        if (a.distanceMeters === null && b.distanceMeters === null) return 0;
601                        if (a.distanceMeters === null) return 1;
602                        if (b.distanceMeters === null) return -1;
603                        return a.distanceMeters - b.distanceMeters;
604                    });
605                }
606
607                matches.splice(50);
608
609                const geoItems = (mode === 'address' && geocodeState.query === query)
610                    ? geocodeState.items
611                    : [];
612
613                if (!matches.length) {
614                    // Не прячем, если есть результаты геокодера
615                    if (!geoItems.length) {
616                        resultsList.innerHTML = '';
617                        resultsList.style.display = 'none';
618                        showResults();
619                        return;
620                    }
621                }
622
623                const itemsHtml = matches.map((entry) => {
624                    const point = entry.point;
625                    const subtitle = [point.city, point.address].filter(
625Boolean).join(', ');
626                    const iconSrc = defaultIcons.search || point.icons?.default || defaultIcons.default;
627                    const distanceMeters = userCoords ? getDistanceMeters(userCoords, point.coords) : entry.distanceMeters;
628                    const distanceLabel = formatDistance(distanceMeters);
629
630                    return `
631                    <li class="json-results__item" data-point-id="${escHtml(entry.searchId)}">
632                        <div class="json-results__icon">
633                            <img src="${escHtml(iconSrc)}" alt="" loading="lazy">
634                        </div>
635                        <div class="json-results__inner">
636                            <div class="json-results__title">
637                                <div class="json-results__name">${highlightMatch(point.title, query)}</div>
638                                ${distanceLabel ? `<div class="json-results__distance">${escHtml(distanceLabel)}</div>` : ''}
639                            </div>
640                            <div class="json-results__subtitle">${mode === 'address' ? highlightMatch(subtitle, query) : escHtml(subtitle)}</div>
641                        </div>
642                    </li>
643                    `;
644                }).join('');
645
646                const geoItemsHtml = geoItems.map((geo, index) => {
647                    const subtitle = geo.subtitle || '';
648                    return `
649                    <li class="json-results__item" data-geo-index="${index}" data-geo="1">
650                        <div class="json-results__inner">
651                            <div class="json-results__title">
652                                <div class="json-results__name">${highlightMatch(geo.title, query)}</div>
653                            </div>
654                            ${subtitle ? `<div class="json-results__subtitle">${highlightMatch(subtitle, query)}</div>` : ''}
655                        </div>
656                    </li>
657                    `;
658                }).join('');
659
660                const settlementKinds = new Set(['locality']);
661                const settlementGeoItems = [];
662                const otherGeoItems = [];
663                geoItems.forEach((item, index) => {
664                    if (settlementKinds.has(item.kind)) {
665                        settlementGeoItems.push({...item, _geoIndex: index});
666                    } else {
667                        otherGeoItems.push({...item, _geoIndex: index});
668                    }
669                });
670
671                const settlementGeoItemsHtml = settlementGeoItems.map((geo) => {
672                    const subtitle = geo.subtitle || '';
673                    return `
674                    <li class="json-results__item" data-geo-index="${geo._geoIndex}" data-geo="1" data-kind="${escHtml(geo.kind || '')}">
675                        <div class="json-results__inner">
676                            <div class="json-results__title">
677                                <div class="json-results__name">${highlightMatch(geo.title, query)}</div>
678                            </div>
679                            ${subtitle ? `<div class="json-results__subtitle">${highlightMatch(subtitle, query)}</div>` : ''}
680                        </div>
681                    </li>
682                    `;
683                }).join('');
684
685                const otherGeoItemsHtml = otherGeoItems.map((geo) => {
686                    const subtitle = geo.subtitle || '';
687                    return `
688                    <li class="json-results__item" data-geo-index="${geo._geoIndex}" data-geo="1" data-kind="${escHtml(geo.kind || '')}">
689                        <div class="json-results__inner">
690                            <div class="json-results__title">
691                                <div class="json-results__name">${highlightMatch(geo.title, query)}</div>
692                            </div>
693                            ${subtitle ? `<div class="json-results__subtitle">${highlightMatch(subtitle, query)}</div>` : ''}
694                        </div>
695                    </li>
696                    `;
697                }).join('');
698
699                resultsList.innerHTML = settlementGeoItemsHtml + itemsHtml + otherGeoItemsHtml;
700                resultsList.style.display = 'block';
701                showResults();
702            };
703
704            const updateGeocode = (query) => {
705                if (mode !== 'address') return;
706                const trimmed = query.trim();
707                if (!trimmed) {
708                    geocodeState.query = '';
709                    geocodeState.items = [];
710                    clearGeoPlacemark();
711                    return;
712                }
713                if (geocodeState.query === trimmed) return;
714
715                const requestId = ++geocodeState.requestId;
716                geocodeState.query = trimmed;
717                geocodeState.items = [];
718
719                ymaps.geocode(trimmed, {
720                    results: 6,
721                    boundedBy: [[41.2, 19.6], [82.1, 180]],
722                    strictBounds: true
723                }).then((res) => {
724                    if (requestId !== geocodeState.requestId) return;
725                    const geoObjects = res.geoObjects.toArray();
726                    geocodeState.items = geoObjects.map((geoObject) => {
727                        const coords = geoObject.geometry.getCoordinates();
728                        const addressLine = geoObject.getAddressLine
729                            ? geoObject.getAddressLine()
730                            : geoObject.properties.get('text');
731                        const name = geoObject.properties.get('name') || addressLine || 'Объект';
732                        const kind = geoObject.getKind
733                            ? geoObject.getKind()
734                            : geoObject.properties.get('metaDataProperty.GeocoderMetaData.kind');
735                        return {
736                            title: name,
737                            subtitle: addressLine && addressLine !== name ? addressLine : '',
738                            coords,
739                            kind
740                        };
741                    });
742                    updateResults();
743                }).catch(() => {
744                    if (requestId !== geocodeState.requestId) return;
745                    geocodeState.items = [];
746                    updateResults();
747                });
748            };
749
750            const defaultPlaceholder = input.getAttribute('data-default-placeholder') || input.getAttribute('placeholder') || '';
751            if (!input.getAttribute('data-default-placeholder')) {
752                input.setAttribute('data-default-placeholder', defaultPlaceholder);
753            }
754
755            if (resultsHead) {
756                resultsHead.classList.add('d-none');
757            }
758
759            if (isOnline) {
760                mode = 'name';
761                resultsWrap.classList.add('d-none');
762                if (optionsWrap) optionsWrap.classList.add('d-none');
763                input.setAttribute('placeholder', 'Введите название');
764            } else {
765                tabs[0].addEventListener('click', () => setMode('address'));
766                tabs[1].addEventListener('click', () => setMode('name'));
767                setMode('address');
768            }
769
770            input.addEventListener('input', () => {
771                updateGeocode(input.value);
772                updateResults();
773            });
774            input.addEventListener('focus', () => {
775                if (resultsHead) resultsHead.classList.remove('d-none');
776                updateResults();
777            });
778            input.addEventListener('click', () => {
779                if (resultsHead) resultsHead.classList.remove('d-none');
780                updateResults();
781            });
782
783            resultsList.addEventListener('click', (event) => {
784                if (isOnline) return;
785                const item = event.target.closest('.json-results__item');
786                if (!item) return;
787                if (item.getAttribute('data-geo') === '1') {
788                    const geoIndex = Number(item.getAttribute('data-geo-index'));
789                    const geo = geocodeState.items[geoIndex];
790                    if (!geo) return;
791
792                    input.value = geo.title || input.value;
793                    hideResults();
794                    resetActivePlacemark();
795                    hideDesktopInfo();
796
797                    clearGeoPlacemark();
798                    geoSearchPlacemark = new ymaps.Placemark(
799                        geo.coords,
800                        {hintContent: geo.title || 'Объект'},
801                        {preset: 'islands#redIcon'}
802                    );
803                    map.geoObjects.add(geoSearchPlacemark);
804
805                    const targetZoom = Math.max(map.getZoom(), 14);
806                    map.setCenter(geo.coords, targetZoom, {duration: 300});
807                    return;
808                }
809
810                const pointId = item.getAttribute('data-point-id');
811                const entry = pointsIndexed.find((current) => current.searchId === pointId);
812                if (!entry) return;
813
814                const point = entry.point;
815                const placemark = searchPlacemarkById.get(pointId);
816
817                clearGeoPlacemark();
818
819                if (mode === 'address') {
820                    input.value = point.address || point.title;
821                } else {
822                    input.value = point.title;
823                }
824
825                hideResults();
826
827                if (placemark) {
828                    const targetZoom = Math.max(map.getZoom(), 14);
829                    map.setCenter(point.coords, targetZoom, {duration: 300});
830                    openPoint(point, placemark);
831                }
832            });
833
834            document.addEventListener('click', (event) => {
835                if (isOnline) return;
836                if (!wrap.contains(event.target)) {
837                    hideResults();
838                }
839            });
840        });
841    }
842
843    function renderOnlineShops(shops, page = 0) {
844        const block = document.querySelector('.tabs__block--js[data-thumb="map-2"]');
845        if (!block) return;
846        const list = block.querySelector('.shops');
847        if (!list) return;
848        const moreBtn = block.querySelector('.reviews-more-link');
849
850        const defaultImg = block.querySelector('.shops-item_img')?.getAttribute('src')
851            || '/local/templates/premier_ru/img/Shop_cart.jpg';
852        const noLogoImg = '/local/templates/premier_ru/img/no_shop_logo.svg';
853
854        const perPage = 24;
855        const sliceEnd = (page + 1) * perPage;
856        const items = (shops || []).slice(0, sliceEnd).map((shop) => {
857            const name = shop.name || shop.title || 'Интернет-магазин';
858            const logo = shop.logo || noLogoImg || defaultImg;
859            const href = shop?.site?.href ? String(shop.site.href) : '';
860            const wrapperStart = href
861                ? `<a class="shops-item" href="${escHtml(href)}" target="_blank" rel="noopener">`
862                : '<div class="shops-item">';
863            const wrapperEnd = href ? '</a>' : '</div>';
864            return `
865                ${wrapperStart}
866                    <div class="shops-item_img__wrap">
867                        <img class="shops-item_img" src="${escHtml(logo)}" alt="" title="" loading="lazy">
868                    </div>
869                    <div class="shops-item_name">${escHtml(name)}</div>
870                ${wrapperEnd}
871            `;
872        }).join('');
873
874        list.innerHTML = items;
875
876        if (moreBtn) {
877            moreBtn.style.display = (shops && shops.length > sliceEnd) ? '' : 'none';
878            moreBtn.dataset.page = String(page);
879        }
880    }
881
882    function initOnlineShops(shops) {
883        const onlineTab = document.getElementById('onlineShops');
884        if (!onlineTab) return;
885        const block = document.querySelector('.tabs__block--js[data-thumb="map-2"]');
886        const moreBtn = block ? block.querySelector('.reviews-more-link') : null;
887        let currentPage = 0;
888
889        const onClick = () => {
890            currentPage = 0;
891            renderOnlineShops(shops, currentPage);
892        };
893        onlineTab.addEventListener('click', onClick);
894
895        if (moreBtn) {
896            moreBtn.addEventListener('click', () => {
897                currentPage += 1;
898                renderOnlineShops(shops, currentPage);
899            });
900        }
901
902        if (onlineTab.classList.contains('active')) {
903            renderOnlineShops(shops, currentPage);
904        }
905    }
906
907    // Точки грузим асинхронно (если пришла ссылка на json)
908    loadRawList().then((rawList) => {
909        const allPoints = (rawList || [])
910            .map(normalizePoint)
911            .filter(Boolean);
912
913        const points = allPoints;
914        const shops = allPoints.filter(p => (p?.type ?? null) === 'shipping');
915
916        initSearch(points, shops);
917        renderPoints(points);
918        initOnlineShops(shops);
919    });
920
921    /* =====================
922       3) Desktop инфоблок
923    ===================== */
924
925    const infoBlock = document.getElementById('mapInfo');
926    const infoTitle = document.getElementById('mapInfoTitle');
927    const infoText = document.getElementById('mapInfoText');
928    const infoClose = document.getElementById('mapInfoClose');
929
930    function hideDesktopInfo() {
931        if (infoBlock) infoBlock.style.display = 'none';
932    }
933
934    function showDesktopInfo(payload) {
935        // payload: { title, html }
936        if (!infoBlock || !infoTitle || !infoText) return;
937        infoTitle.textContent = payload.title || '';
938        infoText.innerHTML = payload.html || '';
939        infoBlock.style.display = 'block';
940    }
941
942    if (infoClose) {
943        infoClose.addEventListener('click', () => {
944            hideDesktopInfo();
945            resetActivePlacemark();
946        });
947    }
948
949    /* =====================
950       4) Mobile modal (Bootstrap)
951    ===================== */
952
953    const modalEl = document.getElementById('mapModal');
954    const modalTitle = document.getElementById('mapModalTitle');
955    const modalBody = document.getElementById('mapModalBody');
956    const modal = (modalEl && window.bootstrap) ? new bootstrap.Modal(modalEl) : null;
957
958    function showMobileModal(payload) {
959        // payload: { title, html }
960        if (!modal || !modalTitle || !modalBody) {
961            showDesktopInfo(payload);
962            return;
963        }
964        modalTitle.textContent = payload.title || '';
965        modalBody.innerHTML = payload.html || '';
966        modal.show();
967    }
968
969    if (modalEl) {
970        modalEl.addEventListener('hidden.bs.modal', () => {
971            resetActivePlacemark();
972        });
973    }
974
975    function openPoint(point, placemark) {
976        if (!placemark) return;
977        resetActivePlacemark();
978
979        const iconActive = placemark.properties.get('iconActive');
980        if (iconActive) {
981            placemark.options.set(iconActive);
982        }
983        activePlacemark = placemark;
984
985        const payload = {
986            title: point.title,
987            html: renderPointHtml(point)
988        };
989
990        if (isMobileNow()) {
991            showMobileModal(payload);
992        } else {
993            showDesktopInfo(payload);
994        }
995    }
996
997    function renderPoints(points) {
998        /* =====================
999           5) Метки
1000        ===================== */
1001
1002        searchPlacemarkById = new Map();
1003
1004        const placemarks = points.map(point => {
1005            const iconDefault = {
1006                iconLayout: 'default#image',
1007                iconImageHref: point.icons.default,
1008                iconImageSize: [40, 40],
1009                iconImageOffset: [-20, -40]
1010            };
1011
1012            const iconActive = {
1013                iconLayout: 'default#image',
1014                iconImageHref: point.icons.active,
1015                iconImageSize: [50, 50],
1016                iconImageOffset: [-25, -50]
1017            };
1018
1019            const placemark = new ymaps.Placemark(point.coords, {}, {
1020                ...iconDefault,
1021                hasBalloon: false,
1022                openBalloonOnClick: false
1023            });
1024            placemark.properties.set('iconDefault', iconDefault);
1025            placemark.properties.set('iconActive', iconActive);
1026
1027            placemark.events.add('click', () => {
1028                openPoint(point, placemark);
1029            });
1030
1031            if (point?._searchId !== undefined && point?._searchId !== null) {
1032                searchPlacemarkById.set(String(point._searchId), placemark);
1033            }
1034
1035            return placemark;
1036        });
1037
1038        /* =====================
1039           6) Кластер
1040        ===================== */
1041
1042        const clusterer = new ymaps.Clusterer({
1043            gridSize: 120,
1044            clusterDisableClickZoom: true,
1045            clusterOpenBalloonOnClick: false,
1046            clusterIcons: [
1047                {
1048                    href: '/local/templates/premier_ru/img/cluster.svg',
1049                    size: [50, 50],
1050                    offset: [-25, -25]
1051                }
1052            ],
1053            clusterIconContentLayout: ymaps.templateLayoutFactory.createClass(
1054                '<div style="color:#fff;font-weight:bold;line-height:50px;text-align:center;">{{ properties.geoObjects.length }}</div>'
1055            )
1056        });
1057
1058        const spiderfyState = {
1059            items: []
1060        };
1061
1062        const clearSpiderfy = () => {
1063            if (!spiderfyState.items.length) return;
1064            spiderfyState.items.forEach(({placemark, coords}) => {
1065                placemark.geometry.setCoordinates(coords);
1066                map.geoObjects.remove(placemark);
1067                clusterer.add(placemark);
1068            });
1069            spiderfyState.items = [];
1070        };
1071
1072        map.events.add('click', clearSpiderfy);
1073        map.events.add('zoomchange', clearSpiderfy);
1074
1075        clusterer.events.add('click', (event) => {
1076            const cluster = event.get('target');
1077            if (!cluster || typeof cluster.getGeoObjects !== 'function') return;
1078
1079            const geoObjects = cluster.getGeoObjects();
1080            if (!geoObjects || geoObjects.length < 2) return;
1081
1082            const center = geoObjects[0].geometry.getCoordinates();
1083            const eps = 1e-6;
1084            const sameCoords = geoObjects.every((obj) => {
1085                const coords = obj.geometry.getCoordinates();
1086                return Math.abs(coords[0] - center[0]) < eps && Math.abs(coords[1] - center[1]) < eps;
1087            });
1088            if (!sameCoords) {
1089                if (typeof event.preventDefault === 'function') {
1090                    event.preventDefault();
1091                }
1092                const center = cluster.geometry?.getCoordinates?.();
1093                if (Array.isArray(center)) {
1094                    const currentZoom = map.getZoom() ?? 10;
1095                    const maxZoom = map.options.get('maxZoom') ?? MAX_ZOOM;
1096                    const nextZoom = Math.min(currentZoom + 1, maxZoom);
1097                    map.setCenter(center, nextZoom, {duration: 300});
1098                }
1099                return;
1100            }
1101
1102            if (typeof event.preventDefault === 'function') {
1103                event.preventDefault();
1104            }
1105
1106            clearSpiderfy();
1107
1108            const count = geoObjects.length;
1109            const step = (Math.PI * 2) / count;
1110            const zoom = map.getZoom() ?? 10;
1111            const baseRadius = 0.0006;
1112            const radius = baseRadius * Math.pow(2, (10 - zoom) / 2);
1113            const latRad = (center[0] * Math.PI) / 180;
1114            const lonScale = Math.cos(latRad) || 1;
1115
1116            geoObjects.forEach((placemark, index) => {
1117                const angle = index * step;
1118                const lat = center[0] + (radius * Math.sin(angle));
1119                const lon = center[1] + (radius * Math.cos(angle)) / lonScale;
1120                clusterer.remove(placemark);
1121                placemark.geometry.setCoordinates([lat, lon]);
1122                map.geoObjects.add(placemark);
1123                spiderfyState.items.push({placemark, coords: center});
1124            });
1125        });
1126
1127        clusterer.add(placemarks);
1128        map.geoObjects.add(clusterer);
1129    }
1130
1131    /* =====================
1132       7) Кастомные кнопки
1133    ===================== */
1134
1135    const btnZoomIn = document.getElementById('zoomIn');
1136    const btnZoomOut = document.getElementById('zoomOut');
1137    const btnGeo = document.getElementById('geo');
1138    const btnFullscreen = document.getElementById('fullscreen');
1139
1140    if (btnZoomIn) {
1141        btnZoomIn.onclick = () => {
1142            const nextZoom = Math.min(map.getZoom() + 1, MAX_ZOOM);
1143            map.setZoom(nextZoom, {duration: 200});
1144        };
1145    }
1146
1147    if (btnZoomOut) {
1148        btnZoomOut.onclick = () => map.setZoom(map.getZoom() - 1, {duration: 200});
1149    }
1150
1151    if (btnGeo) {
1152        btnGeo.onclick = () => {
1153            geolocation.get({provider: 'auto'}).then(res => {
1154                const coords = res.geoObjects.position; // [lat, lon]
1155
1156                // Не плодим метки: удаляем предыдущую
1157                if (myGeoPlacemark) {
1158                    map.geoObjects.remove(myGeoPlacemark);
1159                    myGeoPlacemark = null;
1160                }
1161
1162                myGeoPlacemark = new ymaps.Placemark(
1163                    coords,
1164                    {hintContent: 'Моё местоположение'},
1165                    {
1166                        iconLayout: 'default#image',
1167                        iconImageHref: myGeoIconHref,
1168                        iconImageSize: [26, 26],
1169                        iconImageOffset: [-13, -13]
1170                    }
1171                );
1172
1173                map.geoObjects.add(myGeoPlacemark);
1174                map.setCenter(coords, 14, {duration: 300});
1175            }).catch(() => {
1176                // eslint-disable-next-line no-alert
1177                alert('Не удалось определить местоположение. Разрешите доступ к геолокации.');
1178            });
1179        };
1180    }
1181
1182    if (btnFullscreen) {
1183        const getFullscreenElement = () => (
1184            document.fullscreenElement
1185            || document.webkitFullscreenElement
1186            || document.mozFullScreenElement
1187            || document.msFullscreenElement
1188        );
1189        const onFullscreenHover = () => {
1190            btnFullscreen.style.background = '#f0f0f0';
1191        };
1192        const onFullscreenLeave = () => {
1193            btnFullscreen.style.background = '#fff';
1194        };
1195        let fullscreenHoverBound = false;
1196        const fullscreenButtonHome = {
1197            parent: btnFullscreen.parentNode,
1198            nextSibling: btnFullscreen.nextSibling
1199        };
1200        const setFullscreenButtonState = (isFullscreen) => {
1201            if (isFullscreen) {
1202                if (fullscreenButtonHome.parent && btnFullscreen.parentNode !== document.body) {
1203                    document.body.appendChild(btnFullscreen);
1204                }
1205                btnFullscreen.dataset.prevPosition = btnFullscreen.style.position || '';
1206                btnFullscreen.dataset.prevTop = btnFullscreen.style.top || '';
1207                btnFullscreen.dataset.prevRight = btnFullscreen.style.right || '';
1208                btnFullscreen.dataset.prevBottom = btnFullscreen.style.bottom || '';
1209                btnFullscreen.dataset.prevLeft = btnFullscreen.style.left || '';
1210                btnFullscreen.dataset.prevZIndex = btnFullscreen.style.zIndex || '';
1211                btnFullscreen.dataset.prevDisplay = btnFullscreen.style.display || '';
1212                btnFullscreen.dataset.prevWidth = btnFullscreen.style.width || '';
1213                btnFullscreen.dataset.prevHeight = btnFullscreen.style.height || '';
1214                btnFullscreen.dataset.prevBackground = btnFullscreen.style.background || '';
1215                btnFullscreen.dataset.prevAlignItems = btnFullscreen.style.alignItems || '';
1216                btnFullscreen.dataset.prevJustifyContent = btnFullscreen.style.justifyContent || '';
1217                Object.assign(btnFullscreen.style, {
1218                    position: 'fixed',
1219                    top: '16px',
1220                    right: '16px',
1221                    bottom: '',
1222                    left: '',
1223                    zIndex: '10000',
1224                    display: 'flex',
1225                    width: '2.5rem',
1226                    height: '2.5rem',
1227                    background: '#fff',
1228                    cursor: 'pointer',
1229                    alignItems: 'center',
1230                    justifyContent: 'center'
1231                });
1232                if (!fullscreenHoverBound) {
1233                    btnFullscreen.addEventListener('mouseenter', onFullscreenHover);
1234                    btnFullscreen.addEventListener('mouseleave', onFullscreenLeave);
1235                    fullscreenHoverBound = true;
1236                }
1237            } else {
1238                if (fullscreenButtonHome.parent) {
1239                    if (fullscreenButtonHome.nextSibling && fullscreenButtonHome.nextSibling.parentNode === fullscreenButtonHome.parent) {
1240                        fullscreenButtonHome.parent.insertBefore(btnFullscreen, fullscreenButtonHome.nextSibling);
1241                    } else {
1242                        fullscreenButtonHome.parent.appendChild(btnFullscreen);
1243                    }
1244                }
1245                btnFullscreen.style.position = btnFullscreen.dataset.prevPosition || '';
1246                btnFullscreen.style.top = btnFullscreen.dataset.prevTop || '';
1247                btnFullscreen.style.right = btnFullscreen.dataset.prevRight || '';
1248                btnFullscreen.style.bottom = btnFullscreen.dataset.prevBottom || '';
1249                btnFullscreen.style.left = btnFullscreen.dataset.prevLeft || '';
1250                btnFullscreen.style.zIndex = btnFullscreen.dataset.prevZIndex || '';
1251                btnFullscreen.style.display = btnFullscreen.dataset.prevDisplay || '';
1252                btnFullscreen.style.width = btnFullscreen.dataset.prevWidth || '';
1253                btnFullscreen.style.height = btnFullscreen.dataset.prevHeight || '';
1254                btnFullscreen.style.background = btnFullscreen.dataset.prevBackground || '';
1255                btnFullscreen.style.alignItems = btnFullscreen.dataset.prevAlignItems || '';
1256                btnFullscreen.style.justifyContent = btnFullscreen.dataset.prevJustifyContent || '';
1257                if (fullscreenHoverBound) {
1258                    btnFullscreen.removeEventListener('mouseenter', onFullscreenHover);
1259                    btnFullscreen.removeEventListener('mouseleave', onFullscreenLeave);
1260                    fullscreenHoverBound = false;
1261                }
1262            }
1263        };
1264        const exitFullscreen = () => {
1265            if (document.exitFullscreen) return document.exitFullscreen();
1266            if (document.webkitExitFullscreen) return document.webkitExitFullscreen();
1267            if (document.mozCancelFullScreen) return document.mozCancelFullScreen();
1268            if (document.msExitFullscreen) return document.msExitFullscreen();
1269            return null;
1270        };
1271
1272        btnFullscreen.onclick = () => {
1273            if (map.container.isFullscreen() || getFullscreenElement()) {
1274                map.container.exitFullscreen();
1275                exitFullscreen();
1276                return;
1277            }
1278
1279            map.container.enterFullscreen();
1280        };
1281
1282        if (map.container?.events) {
1283            map.container.events.add('fullscreenenter', () => setFullscreenButtonState(true));
1284            map.container.events.add('fullscreenexit', () => setFullscreenButtonState(false));
1285        }
1286
1287        document.addEventListener('fullscreenchange', () => {
1288            setFullscreenButtonState(Boolean(getFullscreenElement()));
1289        });
1290    }
1291
1292    /* =====================
1293       8) Helpers
1294    ===================== */
1295
1296    function resetActivePlacemark() {
1297        if (!activePlacemark) return;
1298        activePlacemark.options.set(activePlacemark.properties.get('iconDefault'));
1299        activePlacemark = null;
1300    }
1301}
1302
1303/* End */
1304;; /* /local/templates/premier_ru/js/map.js?178637747355470*/

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.