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('&', '&') 235 .replaceAll('<', '<') 236 .replaceAll('>', '>') 237 .replaceAll('"', '"') 238 .replaceAll("'", '''); 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.