1(function () { 2 'use strict'; 3 4 var root = document.documentElement; 5 6 // ---------------------------------------------------------------- ÑзÑк 7 var meta = { 8 ru: { 9 title: 'СиÑиÑÐºÐ°Ð½Ð½ÐµÑ â паноÑÐ°Ð¼Ñ ÑÐ»Ð¸Ñ Ð´Ð»Ñ Ð¯Ð½Ð´ÐµÐºÑ ÐаÑÑ', 10 description: 'ÐÐР«СиÑиÑканнеÑ» Ñ 2009 года ÑÐ½Ð¸Ð¼Ð°ÐµÑ Ð¿Ð°Ð½Ð¾ÑÐ°Ð¼Ñ ÑÐ»Ð¸Ñ Ð´Ð»Ñ Ð¯Ð½Ð´ÐµÐºÑ ÐаÑÑ: более 300 гоÑодов РоÑÑии и ближнего заÑÑбежÑÑ, более 2 миллионов паноÑам.' 11 }, 12 en: { 13 title: 'CityScanner â street-level panoramas for Yandex Maps', 14 description: 'CityScanner LLC has been capturing street-level panoramas for Yandex Maps since 2009: 300+ cities in Russia and neighbouring countries, over 2 million panoramas.' 15 } 16 }; 17 18 function applyMeta(lang) { 19 document.title = meta[lang].title; 20 var d = document.querySelector('meta[name="description"]'); 21 if (d) d.setAttribute('content', meta[lang].description); 22 } 23 24 function setLang(lang) { 25 root.lang = lang; 26 applyMeta(lang); 27 try { localStorage.setItem('lang', lang); } catch (e) {} 28 var url = new URL(location.href); 29 if (lang === 'ru') url.searchParams.delete('lang'); else url.searchParams.set('lang', lang); 30 history.replaceState(null, '', url); 31 } 32 33 applyMeta(root.lang); 34 document.querySelectorAll('[data-set-lang]').forEach(function (b) { 35 b.addEventListener('click', function () { setLang(b.getAttribute('data-set-lang')); }); 36 }); 37 38 // ---------------------------------------------------------------- Ð¼ÐµÐ½Ñ 39 var burger = document.querySelector('.cs-header__menu'); 40 var nav = document.getElementById('nav'); 41 var header = document.querySelector('.cs-header'); 42 function closeMenu() { nav.classList.remove('is-open'); burger.setAttribute('aria-expanded', 'false'); } 43 burger.addEventListener('click', function () { 44 var open = nav.classList.toggle('is-open'); 45 burger.setAttribute('aria-expanded', String(open)); 46 }); 47 nav.addEventListener('click', function (e) { if (e.target.closest('a')) closeMenu(); }); 48 document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeMenu(); }); 49 50 // ÑÐµÐ½Ñ Ñ Ð¿ÑилипÑей Ñапки (shadow-sm) â ÑолÑко когда ÑÑÑаниÑа пÑокÑÑÑена 51 function onScroll() { header.classList.toggle('is-stuck', window.scrollY > 4); } 52 window.addEventListener('scroll', onScroll, { passive: true }); 53 onScroll(); 54 55 // ÑекÑÑий Ñаздел в Ð¼ÐµÐ½Ñ â ÑÐ²ÐµÑ accent и «ÑоÑка ÑÑÑмки» под пÑнкÑом, как в SiteHeader 56 if ('IntersectionObserver' in window) { 57 var links = {}; 58 nav.querySelectorAll('a[href^="#"]').forEach(function (a) { links[a.getAttribute('href').slice(1)] = a; }); 59 var visible = {}; 60 var spy = new IntersectionObserver(function (entries) { 61 entries.forEach(function (e) { visible[e.target.id] = e.isIntersecting; }); 62 var current = null;
63 Object.keys(links).forEach(function (id) { if (visible[id] && !current) current = id; }); 64 Object.keys(links).forEach(function (id) { 65 if (id === current) links[id].setAttribute('aria-current', 'true'); 66 else links[id].removeAttribute('aria-current'); 67 }); 68 }, { rootMargin: '-45% 0px -50% 0px' }); 69 Object.keys(links).forEach(function (id) { var el = document.getElementById(id); if (el) spy.observe(el); }); 70 } 71 72 document.querySelectorAll('.year').forEach(function (el) { el.textContent = new Date().getFullYear(); }); 73 74 // ---------------------------------------------------------------- галеÑÐµÑ 75 var box = document.getElementById('lightbox'); 76 if (box && typeof box.showModal === 'function') { 77 var boxImg = box.querySelector('img'); 78 document.querySelectorAll('.gallery__item').forEach(function (btn) { 79 btn.addEventListener('click', function () { 80 var img = btn.querySelector('img'); 81 boxImg.src = btn.getAttribute('data-full'); 82 boxImg.alt = img.alt; 83 box.showModal(); 84 }); 85 }); 86 box.addEventListener('click', function (e) { if (e.target === box || e.target === boxImg) box.close(); }); 87 } else { 88 document.querySelectorAll('.gallery__item').forEach(function (btn) { 89 btn.addEventListener('click', function () { window.open(btn.getAttribute('data-full'), '_blank'); }); 90 }); 91 } 92 93 // ---------------------------------------------------------------- паноÑама 94 // Ð¢Ð°Ð¹Ð»Ñ Ð³Ð¾ÑÐ¾Ð²Ð¸Ñ tools/build_assets.py (multires, кÑб 4608 px, Ñайл 512 px, 5 ÑÑовней) 95 function scene(name, yaw) { 96 return { 97 type: 'multires', 98 multiRes: { 99 basePath: 'assets/pano/' + name, 100 path: '/%l/%s%y_%x', 101 extension: 'jpg', 102 tileResolution: 512, 103 maxLevel: 5, 104 cubeResolution: 4608 105 }, 106 yaw: yaw, 107 pitch: -4 108 }; 109 } 110 111 var viewerEl = document.querySelector('.viewer'); 112 var viewer = null; 113 var startHfov = 105; 114 115 function initViewer() { 116 if (viewer || !window.pannellum) return; 117 var en = root.lang === 'en'; 118 // hfov в Pannellum â гоÑизонÑалÑнÑй Ñгол; на веÑÑикалÑном ÑкÑане беÑÑм Ñже, инаÑе ÑилÑнÑе иÑÐºÐ°Ð¶ÐµÐ½Ð¸Ñ 119 var portrait = viewerEl.clientWidth < viewerEl.clientHeight; 120 startHfov = portrait ? 70 : 105; 121 viewer = pannellum.viewer('pano', { 122 default: { 123 firstScene: 'p1', 124 sceneFadeDuration: 900, 125 autoLoad: true, 126 hfov: startHfov, 127 minHfov: 35, 128 maxHfov: 115, 129 mouseZoom: 'fullscreenonly', // колеÑо мÑÑи кÑÑÑÐ¸Ñ ÑÑÑаниÑÑ, а не зÑм 130 keyboardZoom: false, 131 compass: false, 132 showFullscreenCtrl: true, 133 strings: en ? {} : { 134 loadButtonLabel: 'ÐажмиÑе, ÑÑобÑ<br>загÑÑзиÑÑ Ð¿Ð°Ð½Ð¾ÑамÑ', 135 loadingLabel: 'ÐагÑÑзкаâ¦', 136 bylineLabel: '', 137 noPanoramaError: 'ÐаноÑама не Ñказана.',
138 fileAccessError: 'Ðе ÑдалоÑÑ Ð·Ð°Ð³ÑÑзиÑÑ Ñайл %s.', 139 malformedURLError: 'ÐекоÑÑекÑнÑй адÑÐµÑ Ð¿Ð°Ð½Ð¾ÑамÑ.', 140 iOS8WebGLError: 'ÐÐ°Ñ Ð±ÑаÑÐ·ÐµÑ Ð½Ðµ поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ WebGL.', 141 genericWebGLError: 'ÐÐ°Ñ Ð±ÑаÑÐ·ÐµÑ Ð½Ðµ поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ WebGL.', 142 textureSizeError: 'ÐаноÑама ÑлиÑком велика Ð´Ð»Ñ Ð²Ð°Ñего ÑÑÑÑойÑÑва.', 143 unknownError: 'ÐеизвеÑÑÐ½Ð°Ñ Ð¾Ñибка.' 144 } 145 }, 146 scenes: { 147 p1: scene('p1', 0), 148 p2: scene('p2', 0), 149 p3: scene('p3', 0), 150 p4: scene('p4', 0) 151 } 152 }); 153 154 ['mousedown', 'touchstart', 'pointerdown', 'keydown'].forEach(function (ev) { 155 viewerEl.addEventListener(ev, function () { viewerEl.classList.add('is-touched'); }, { once: true, passive: true }); 156 }); 157 158 initAutoRotate(); 159 160 viewer.on('scenechange', function (id) { 161 document.querySelectorAll('.scene').forEach(function (b) { 162 b.classList.toggle('is-active', b.getAttribute('data-scene') === id); 163 }); 164 }); 165 } 166 167 // ÐвÑовÑаÑение ведÑм Ñами, а не ÑеÑез autoRotate Pannellum: еÑли паноÑама кÑÑÑиÑÑÑ, 168 // пока ÐµÑ Ð½Ðµ видно, пÑи возвÑаÑе на ÑкÑан бÑаÑÐ·ÐµÑ Ð¿Ð¾Ð´Ð²Ð¸ÑÐ°ÐµÑ Ð½Ð° 0,3â0,5 Ñ. 169 // ÐоÑÑÐ¾Ð¼Ñ ÐºÑÑÑим, ÑолÑко когда паноÑама на ÑкÑане и ÑÑÑаниÑа не пÑокÑÑÑиваеÑÑÑ. 170 function initAutoRotate() { 171 if (window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return; 172 var visible = false, timer = null; 173 174 function schedule(delay) { 175 clearTimeout(timer); 176 viewer.stopAutoRotate(); 177 if (visible) timer = setTimeout(function () { viewer.startAutoRotate(-2.5, viewer.getPitch()); }, delay); 178 } 179 180 new IntersectionObserver(function (entries) { 181 visible = entries[entries.length - 1].isIntersecting; 182 schedule(1500); 183 }).observe(viewerEl); 184 window.addEventListener('scroll', function () { if (visible) schedule(1500); }, { passive: true }); 185 // поÑле Ñого как поÑеÑиÑÐµÐ»Ñ Ñам покÑÑÑил паноÑамÑ, даÑм ÐµÐ¼Ñ Ñпокойно поÑмоÑÑеÑÑ 186 ['pointerdown', 'touchstart', 'keydown'].forEach(function (ev) { 187 viewerEl.addEventListener(ev, function () { schedule(6000); }, { passive: true }); 188 }); 189 viewer.on('load', function () { schedule(1500); }); 190 } 191 192 document.querySelectorAll('.scene').forEach(function (b) { 193 b.addEventListener('click', function (e) { 194 e.stopPropagation(); 195 initViewer(); 196 var id = b.getAttribute('data-scene'); 197 if (viewer && viewer.getScene() !== id) viewer.loadScene(id, 0, -4, startHfov); 198 }); 199 }); 200 201 // паноÑÐ°Ð¼Ñ Ð³ÑÑзим, ÑолÑко когда до Ð½ÐµÑ Ð´Ð¾ÐºÑÑÑили 202 function lazy() { 203 if (!('IntersectionObserver' in window)) return initViewer(); 204 var io = new IntersectionObserver(function (entries) { 205 if (entries.some(function (e) { return e.isIntersecting; })) { io.disconnect(); initViewer(); } 206 }, { rootMargin: '300px 0px' }); 207 io.observe(viewerEl); 208 } 209 if (window.pannellum) lazy(); else window.addEventListener('load', lazy); 210})();
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.