1/* ========================================================================== 2 ÐвеÑеÑÑ. Ðоведение ÑайÑа. 3 Ðез внеÑÐ½Ð¸Ñ Ð·Ð°Ð²Ð¸ÑимоÑÑей. СкÑолл ÑлÑÑаем ÑолÑко ÑеÑез IntersectionObserver. 4 ========================================================================== */ 5(function () { 6 'use strict'; 7 8 var reduced = window.matchMedia('(prefers-reduced-motion: reduce)'); 9 var desktop = window.matchMedia('(min-width: 1181px)'); 10 11 /* ---------------------------------------------- Шапка: ÑоÑÑоÑние «пÑилипла» */ 12 function stickyHeader() { 13 var hdr = document.querySelector('.hdr'); 14 var sentinel = document.querySelector('[data-hdr-sentinel]'); 15 if (!hdr || !sentinel || !('IntersectionObserver' in window)) return; 16 new IntersectionObserver(function (entries) { 17 hdr.classList.toggle('is-stuck', !entries[0].isIntersecting); 18 }, { threshold: 0 }).observe(sentinel); 19 } 20 21 /* ------------------------------------------------------ ÐÐµÐ½Ñ Ð¸ Ð¿Ð¾Ð´Ð¼ÐµÐ½Ñ */ 22 function menu() { 23 var nav = document.querySelector('[data-nav]'); 24 var burger = document.querySelector('[data-burger]'); 25 var items = Array.prototype.slice.call(document.querySelectorAll('.nav__item[data-sub]')); 26 if (!nav) return; 27 28 function closeAll(except) { 29 items.forEach(function (it) { 30 if (it === except) return; 31 it.setAttribute('data-open', 'false'); 32 var t = it.querySelector('[data-sub-toggle]'); 33 if (t) t.setAttribute('aria-expanded', 'false'); 34 it.querySelectorAll('[data-sub2]').forEach(function (g) { 35 g.setAttribute('data-open', 'false'); 36 var b = g.querySelector('.subnav__toggle'); 37 if (b) b.setAttribute('aria-expanded', 'false'); 38 }); 39 }); 40 } 41 function toggle(item, open) { 42 item.setAttribute('data-open', open ? 'true' : 'false'); 43 var t = item.querySelector('[data-sub-toggle]'); 44 if (t) t.setAttribute('aria-expanded', open ? 'true' : 'false'); 45 } 46 47 items.forEach(function (item) { 48 var link = item.querySelector('.nav__link'); 49 var more = item.querySelector('[data-sub-toggle]'); 50 var timer; 51 52 // ÐеÑкÑоп: наведение Ñ Ð½ÐµÐ±Ð¾Ð»ÑÑой задеÑжкой на ÑÑ Ð¾Ð´, ÑÑÐ¾Ð±Ñ Ð½Ðµ мигало 53 item.addEventListener('pointerenter', function (e) { 54 if (!desktop.matches || e.pointerType === 'touch') return; 55 clearTimeout(timer); 56 closeAll(item); 57 toggle(item, true); 58 }); 59 item.addEventListener('pointerleave', function (e) { 60 if (!desktop.matches || e.pointerType === 'touch') return; 61 timer = setTimeout(function () { toggle(item, false); }, 120); 62 }); 63 64 /* СÑÑлка ведÑÑ Ð½Ð° ÑÑÑаниÑÑ Ñаздела и на деÑкÑопе, и на ÑелеÑоне. 65 ÐÐ¾Ð´Ð¼ÐµÐ½Ñ ÑаÑкÑÑÐ²Ð°ÐµÑ ÑоÑеднÑÑ ÑÑÑелка - ÐµÑ Ð¸ жмÑÑ, когда нÑжен ÑпиÑок. */ 66 if (more) { 67 more.addEventListener('click', function () { 68 var open = item.getAttribute('data-open') === 'true'; 69 closeAll(item); 70 toggle(item, !open); 71 }); 72 } 73 link.addEventListener('focus', function () { 74 if (!desktop.matches) return; 75 closeAll(item); 76 toggle(item, true); 77 }); 78 item.addEventListener('keydown', function (e) { 79 if (e.key === 'Escape') { toggle(item, false); link.focus(); } 80 }); 81 item.addEventListener('focusout', function (e) { 82 if (!item.contains(e.relatedTarget)) toggle(item, false); 83 }); 84 }); 85 86 /* ÐÑоÑой ÑÑовенÑ: ниÑи. Ðа деÑкÑопе ÑаÑкÑÑваеÑÑÑ Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ð¸ÐµÐ¼, на Ñапе - кликом */ 87 var groups = Array.prototype.slice.call(document.querySelectorAll('[data-sub2]')); 88 function toggle2(group, open) { 89 group.setAttribute('data-open', open ? 'true' : 'false'); 90 var btn = group.querySelector('.subnav__toggle'); 91 if (btn) btn.setAttribute('aria-expanded', open ? 'true' : 'false'); 92 } 93 function closeGroups(except) { 94 groups.forEach(function (g) { if (g !== except) toggle2(g, false); }); 95 } 96 groups.forEach(function (group) { 97 var btn = group.querySelector('.subnav__toggle'); 98 var timer; 99 group.addEventListener('pointerenter', function (e) { 100 if (!desktop.matches || e.pointerType === 'touch') return; 101 clearTimeout(timer); closeGroups(group); toggle2(group, true); 102 }); 103 group.addEventListener('pointerleave', function (e) { 104 if (!desktop.matches || e.pointerType === 'touch') return; 105 timer = setTimeout(function () { toggle2(group, false); }, 120); 106 }); 107 if (btn) { 108 btn.addEventListener('click', function () { 109 var open = group.getAttribute('data-open') === 'true'; 110 closeGroups(group); toggle2(group, !open); 111 }); 112 } 113 group.addEventListener('keydown', function (e) { 114 if (e.key === 'Escape') { toggle2(group, false); if (btn) btn.focus(); } 115 }); 116 group.addEventListener('focusout', function (e) { 117 if (!group.contains(e.relatedTarget)) toggle2(group, false); 118 }); 119 }); 120 121 document.addEventListener('pointerdown', function (e) { 122 if (!e.target.closest('.nav__item')) closeAll(null); 123 }); 124 125 if (burger) { 126 burger.addEventListener('click', function () { 127 var open = burger.getAttribute('aria-expanded') === 'true'; 128 burger.setAttribute('aria-expanded', open ? 'false' : 'true'); 129 nav.classList.toggle('is-open', !open); 130 if (open) closeAll(null); 131 }); 132 } 133 desktop.addEventListener('change', function () { 134 nav.classList.remove('is-open'); 135 if (burger) burger.setAttribute('aria-expanded', 'false'); 136 closeAll(null); 137 }); 138 } 139 140 /* ------------------------------------------------------------- ÐккоÑдеон */ 141 function accordion() { 142 document.querySelectorAll('[data-acc] .acc__btn').forEach(function (btn) { 143 btn.addEventListener('click', function () { 144 var item = btn.closest('.acc__item'); 145 var open = item.getAttribute('data-open') === 'true'; 146 item.setAttribute('data-open', open ? 'false' : 'true'); 147 btn.setAttribute('aria-expanded', open ? 'false' : 'true'); 148 }); 149 }); 150 } 151 152 /* ------------------------------------------- ÐлиннÑй оÑзÑв: ÑиÑаÑÑ Ð¿Ð¾Ð»Ð½Ð¾ÑÑÑÑ */ 153 function reviews() { 154 document.querySelectorAll('[data-review-more]').forEach(function (btn) { 155 btn.addEventListener('click', function () { 156 var box = btn.closest('.review'); 157 box.setAttribute('data-open', 'true'); 158 var text = box.querySelector('.review__text'); 159 if (text) text.removeAttribute('aria-expanded'); 160 }); 161 }); 162 } 163 164 /* ------------------------------------------------- ФилÑÑÑ Ð¸ «показаÑÑ ÐµÑÑ» */ 165 function caseList() { 166 var root = document.querySelector('[data-cases]'); 167 if (!root) return; 168 var cards = Array.prototype.slice.call(root.querySelectorAll('.case, .post')); 169 var filters = Array.prototype.slice.call(document.querySelectorAll('[data-filter]')); 170 var moreBtn = document.querySelector('[data-cases-more]'); 171 var empty = document.querySelector('[data-cases-empty]'); 172 var STEP = parseInt(root.dataset.casesStep || '9', 10); 173 var shown = STEP; 174 var active = 'all'; 175 176 function matches(card) { 177 return active === 'all' || (card.dataset.tags || '').split('|').indexOf(active) > -1; 178 } 179 function render() { 180 var visible = 0; 181 cards.forEach(function (card) { 182 if (!matches(card)) { card.hidden = true; return; } 183 visible++; 184 card.hidden = visible > shown; 185 }); 186 if (moreBtn) moreBtn.hidden = visible <= shown; 187 if (empty) empty.hidden = visible !== 0; 188 }
189 filters.forEach(function (f) { 190 f.addEventListener('click', function () { 191 active = f.dataset.filter; 192 shown = STEP; 193 filters.forEach(function (x) { x.setAttribute('aria-pressed', String(x === f)); }); 194 render(); 195 }); 196 }); 197 if (moreBtn) moreBtn.addEventListener('click', function () { shown += STEP; render(); }); 198 render(); 199 } 200 201 /* ------------------------------------------------------------ ÐлокиÑовка пÑокÑÑÑки */ 202 /* СкÑÑваем пÑокÑÑÑÐºÑ Ð¿Ð¾Ð´ окном и компенÑиÑÑем ÑиÑÐ¸Ð½Ñ Ð¿Ð¾Ð»Ð¾ÑÑ, ÑÑÐ¾Ð±Ñ ÑÑÑаниÑа не пÑÑгала */ 203 var locks = 0; 204 function lockScroll() { 205 if (locks++ > 0) return; 206 var w = window.innerWidth - document.documentElement.clientWidth; 207 document.documentElement.style.setProperty('--sbw', w + 'px'); 208 document.body.classList.add('is-locked'); 209 } 210 function unlockScroll() { 211 if (--locks > 0) return; 212 locks = 0; 213 document.body.classList.remove('is-locked'); 214 document.documentElement.style.removeProperty('--sbw'); 215 } 216 217 /* ------------------------------------------------------------ Цели ÐеÑÑики */ 218 var METRIKA = 50166202; 219 function goal(name, params) { 220 if (typeof window.ym !== 'function') return; 221 try { 222 if (params) window.ym(METRIKA, 'reachGoal', name, params); 223 else window.ym(METRIKA, 'reachGoal', name); 224 } catch (e) {} 225 } 226 227 /* ÐÑмеÑеннÑе ÑÐ¸Ð¿Ñ Â«ÑÑо Ñ Ð¾ÑелоÑÑ Ð±Ñ ÑлÑÑÑиÑÑ»: ÑÑ Ð¾Ð´ÑÑ Ð² паÑамеÑÑ Ñели, 228 ÑÑÐ¾Ð±Ñ Ð² оÑÑÑÑе бÑло видно, Ñ ÐºÐ°ÐºÐ¾Ð¹ задаÑей пÑÐ¸Ñ Ð¾Ð´ÑÑ. */ 229 function pickedTags(form) { 230 var v = []; 231 form.querySelectorAll('input[name="tags[]"]:checked').forEach(function (i) { 232 v.push(i.value); 233 }); 234 return v.join(', '); 235 } 236 237 /* ------------------------------------------------------------ ÐаÑка ÑелеÑона */ 238 /* +7 (___) ___-__-__: ведÑÑие 8 и 7 ÑÑиÑаем кодом ÑÑÑÐ°Ð½Ñ */ 239 function phoneMask(input) { 240 function format(value) { 241 var d = value.replace(/\D/g, ''); 242 /* +7 Ñже в поле - ÑÑо код ÑÑÑанÑ; вÑÑавленнÑй Ð½Ð¾Ð¼ÐµÑ Ð¸Ð· 11 ÑиÑÑ Ð½Ð°ÑинаеÑÑÑ Ñ 7 или 8 */ 243 if (/^\s*\+7/.test(value)) { 244 d = d.slice(1); 245 /* пеÑÐ²Ð°Ñ Ð½Ð°Ð±ÑÐ°Ð½Ð½Ð°Ñ 8 или 7 - пÑивÑÑнÑй вÑÑ Ð¾Ð´ на межгоÑод, а не ÑаÑÑÑ Ð½Ð¾Ð¼ÐµÑа */ 246 if (d.length === 1 && (d === '8' || d === '7')) d = ''; 247 } else if (d.length === 11 && (d.charAt(0) === '8' || d.charAt(0) === '7')) d = d.slice(1); 248 d = d.slice(0, 10); 249 if (!d) return /^\s*\+7/.test(value) ? '+7 (' : ''; 250 var out = '+7 (' + d.slice(0, 3); 251 if (d.length >= 3) out += ')'; 252 if (d.length > 3) out += ' ' + d.slice(3, 6); 253 if (d.length > 6) out += '-' + d.slice(6, 8); 254 if (d.length > 8) out += '-' + d.slice(8, 10); 255 return out; 256 } 257 input.addEventListener('input', function () { 258 var atEnd = input.selectionStart === input.value.length; 259 input.value = format(input.value); 260 if (atEnd) input.setSelectionRange(input.value.length, input.value.length); 261 }); 262 input.addEventListener('focus', function () { if (!input.value) input.value = '+7 ('; }); 263 input.addEventListener('blur', function () { if (input.value.replace(/\D/g, '').length <= 1) input.value = ''; }); 264 } 265 266 /* ------------------------------------------------------------ ÐÑпÑавка ÑоÑм */ 267 /* Ðдна оÑпÑавка Ð´Ð»Ñ ÐºÐ¾ÑоÑкой ÑоÑÐ¼Ñ Ð² окне и болÑÑой внизÑ. ÐдÑÐµÑ Ð¾Ð±ÑабоÑÑика 268 задаÑÑÑÑ Ð² одном меÑÑе, _build/common.js -> FORM_ENDPOINT. ÐÑÑÑо - заглÑÑка. */ 269 var mdlRoot = document.querySelector('[data-mdl]'); 270 var ENDPOINT = mdlRoot ? mdlRoot.getAttribute('data-endpoint') || '' : ''; 271 var SERVICE = mdlRoot ? mdlRoot.getAttribute('data-service') || '' : ''; 272 273 function send(data) { 274 if (!ENDPOINT) { 275 /* заглÑÑка: даннÑе никÑда не ÑÑ Ð¾Ð´ÑÑ, оÑÐ²ÐµÑ ÐºÐ°Ðº Ñ Ð½Ð°ÑÑоÑÑего обÑабоÑÑика */ 276 return new Promise(function (resolve) { setTimeout(function () { resolve({ ok: true, stub: true }); }, 600); }); 277 } 278 return fetch(ENDPOINT, { method: 'POST', body: data, headers: { 'Accept': 'application/json' } }) 279 .then(function (r) { return r.json().catch(function () { return { ok: r.ok }; }); }); 280 } 281 282 function setupForm(form, hooks) { 283 var submit = form.querySelector('[data-submit]'); 284 var status = form.querySelector('[data-status]'); 285 var ts = form.querySelector('[data-ts]');
286 if (ts) ts.value = String(Date.now()); 287 form.querySelectorAll('[data-phone]').forEach(phoneMask); 288 289 /* Файл Ñ Ð¢Ð: Ð¸Ð¼Ñ Ð²ÑбÑанного Ñайла и Ð»Ð¸Ð¼Ð¸Ñ 10 ÐÐ */ 290 var fileInput = form.querySelector('[data-file]'); 291 var fileName = form.querySelector('[data-file-name]'); 292 if (fileInput && fileName) { 293 fileInput.addEventListener('change', function () { 294 var f = fileInput.files && fileInput.files[0]; 295 fileName.classList.remove('is-invalid'); 296 if (!f) { fileName.textContent = ''; return; } 297 if (f.size > 10 * 1024 * 1024) { 298 fileName.textContent = 'Файл болÑÑе 10 ÐÐ, вÑбеÑиÑе Ñайл поменÑÑе'; 299 fileName.classList.add('is-invalid'); 300 fileInput.value = ''; 301 return; 302 } 303 fileName.textContent = f.name; 304 }); 305 } 306 307 function setError(field, on) { field.classList.toggle('is-invalid', on); } 308 309 function validate() { 310 var ok = true; 311 form.querySelectorAll('.field[data-required]').forEach(function (field) { 312 if (field.hidden) return; 313 var input = field.querySelector('input'); 314 var value = (input.value || '').trim(); 315 var bad = !value; 316 if (!bad && input.type === 'email') bad = !/^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/.test(value); 317 if (!bad && input.type === 'tel') bad = value.replace(/\D/g, '').length < 11; 318 setError(field, bad); 319 if (bad && ok) input.focus(); 320 if (bad) ok = false; 321 }); 322 form.querySelectorAll('.field:not([data-required]) input[type="email"]').forEach(function (input) { 323 var v = (input.value || '').trim(); 324 var bad = !!v && !/^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/.test(v); 325 setError(input.closest('.field'), bad); 326 if (bad) ok = false; 327 }); 328 var agree = form.querySelector('[data-agree]'); 329 if (agree) { 330 var box = agree.querySelector('input'); 331 agree.classList.toggle('is-invalid', !box.checked); 332 if (!box.checked) ok = false; 333 } 334 return ok; 335 } 336 337 form.querySelectorAll('.field input').forEach(function (input) { 338 input.addEventListener('input', function () { 339 var field = input.closest('.field'); 340 if (field.classList.contains('is-invalid')) setError(field, false); 341 }); 342 }); 343 var agreeBox = form.querySelector('[data-agree] input'); 344 if (agreeBox) agreeBox.addEventListener('change', function () { 345 if (agreeBox.checked) agreeBox.closest('[data-agree]').classList.remove('is-invalid'); 346 }); 347 348 /* ÐеÑвÑй ввод в лÑбое поле: Ñаг воÑонки Ð¼ÐµÐ¶Ð´Ñ Ð¾ÑкÑÑÑием и оÑпÑавкой. 349 СлÑÑаÑÐµÐ»Ñ Ð¾Ð´Ð½Ð¾ÑазовÑй, инаÑе ÑÐµÐ»Ñ ÑйдÑÑ Ð½Ð° каждÑÑ Ð½Ð°Ð¶Ð°ÑÑÑ ÐºÐ»Ð°Ð²Ð¸ÑÑ. */ 350 var started = false; 351 form.addEventListener('input', function () { 352 if (started) return; 353 started = true; 354 goal('form_start', { 355 form: form.getAttribute('data-form-type') || 'full', 356 page: location.pathname 357 }); 358 }); 359 360 form.addEventListener('submit', function (e) { 361 e.preventDefault(); 362 if (status) { status.textContent = ''; status.classList.remove('is-error'); } 363 if (!validate()) { 364 if (status) status.textContent = 'ÐÑовеÑÑÑе оÑмеÑеннÑе полÑ.'; 365 return; 366 } 367 submit.setAttribute('data-loading', 'true'); 368 submit.disabled = true; 369 370 var type = form.getAttribute('data-form-type') || 'full'; 371 var data = new FormData(form); 372 data.set('form_type', type); 373 data.set('page_url', location.href); 374 data.set('page_title', document.title); 375 data.set('service', SERVICE); 376 data.set('button_text', form.getAttribute('data-button-text') || (type === 'full' ? 'ÐÑпÑавиÑÑ' : '')); 377 data.set('page', location.pathname); 378 379 send(data).then(function (res) { 380 if (!res || !res.ok) throw new Error((res && res.error) || 'send'); 381 goal('form_' + type, { 382 service: SERVICE || '', 383 page: location.pathname, 384 topic: pickedTags(form), 385 form: type 386 }); 387 if (hooks && hooks.done) hooks.done(); 388 form.reset(); 389 if (fileName) fileName.textContent = '';
390 if (ts) ts.value = String(Date.now()); 391 if (status && !(hooks && hooks.done)) { 392 status.textContent = 'СпаÑибо за довеÑие! ÐÐµÐ½ÐµÐ´Ð¶ÐµÑ ÑвÑжеÑÑÑ Ñ Ðами в ближайÑее вÑемÑ.'; 393 } 394 }).catch(function () { 395 if (hooks && hooks.fail) { hooks.fail(); return; } 396 if (status) { 397 status.classList.add('is-error'); 398 status.innerHTML = 'Ðе ÑдалоÑÑ Ð¾ÑпÑавиÑÑ, позвониÑе нам: <a href="tel:+79237375033">8 (923) 737-50-33</a>'; 399 } 400 }).then(function () { 401 submit.removeAttribute('data-loading'); 402 submit.disabled = false; 403 }); 404 }); 405 } 406 407 function forms() { 408 document.querySelectorAll('[data-form]').forEach(function (form) { 409 if (form.closest('[data-mdl]')) return; // ÑоÑÐ¼Ñ Ð² окне наÑÑÑÐ°Ð¸Ð²Ð°ÐµÑ modal() 410 setupForm(form); 411 }); 412 } 413 414 /* ------------------------------------------------------------ Ðкно Ñ ÐºÐ¾ÑоÑкой ÑоÑмой */ 415 function modal() { 416 var root = mdlRoot; 417 if (!root) return; 418 var card = root.querySelector('.mdl__card'); 419 var body = root.querySelector('[data-mdl-body]'); 420 var form = root.querySelector('[data-form]'); 421 var title = root.querySelector('[data-mdl-title]'); 422 var siteField = root.querySelector('[data-mdl-site]'); 423 var formHtml = body.innerHTML; // иÑÑ Ð¾Ð´Ð½Ð¾Ðµ ÑодеÑжимое Ð´Ð»Ñ Ð¿Ð¾Ð²ÑоÑного оÑкÑÑÑÐ¸Ñ 424 var trigger = null, closeTimer = null; 425 var reduced = window.matchMedia('(prefers-reduced-motion: reduce)'); 426 427 function focusables() { 428 return Array.prototype.slice.call(card.querySelectorAll( 429 'a[href], button:not([disabled]), input:not([type="hidden"]):not([tabindex="-1"]), textarea, select')) 430 .filter(function (el) { return el.offsetParent !== null; }); 431 } 432 433 function bindForm() { 434 form = root.querySelector('[data-form]'); 435 title = root.querySelector('[data-mdl-title]'); 436 siteField = root.querySelector('[data-mdl-site]'); 437 setupForm(form, { done: showDone, fail: showFail }); 438 } 439 440 function configure(kind, heading, buttonText) { 441 if (!form) bindForm(); 442 form.setAttribute('data-form-type', kind); 443 form.setAttribute('data-button-text', buttonText || ''); 444 title.textContent = kind === 'callback' ? 'ÐаказаÑÑ Ð·Ð²Ð¾Ð½Ð¾Ðº' : (heading || 'ÐÑÑавиÑÑ Ð·Ð°ÑвкÑ'); 445 siteField.hidden = kind === 'callback'; 446 var status = form.querySelector('[data-status]'); 447 if (status) { status.textContent = ''; status.classList.remove('is-error'); } 448 form.querySelectorAll('.is-invalid').forEach(function (el) { el.classList.remove('is-invalid'); }); 449 } 450 451 /* ÐÑог оÑпÑавки заменÑÐµÑ ÑодеÑжимое окна; пÑи ÑледÑÑÑем оÑкÑÑÑии ÑоÑма возвÑаÑаеÑÑÑ */ 452 function showDone() { 453 body.innerHTML = '<div class="mdl__done">' 454 + '<span class="mdl__done-ic" aria-hidden="true"><svg class="ic"><use href="#check"/></svg></span>' 455 + '<h2 class="mdl__title" id="mdl-title">СпаÑибо за довеÑие!</h2>' 456 + '<p>ÐÐµÐ½ÐµÐ´Ð¶ÐµÑ ÑвÑжеÑÑÑ Ñ Ðами в ближайÑее вÑемÑ</p>' 457 + '<button class="btn btn--lg" type="button" data-mdl-close>ÐакÑÑÑÑ</button></div>'; 458 form = null; 459 var btn = body.querySelector('[data-mdl-close]'); 460 if (btn) btn.focus(); 461 } 462 function showFail() { 463 var status = form && form.querySelector('[data-status]'); 464 if (!status) return; 465 status.classList.add('is-error'); 466 status.innerHTML = 'Ðе ÑдалоÑÑ Ð¾ÑпÑавиÑÑ, позвониÑе нам: <a href="tel:+79237375033">8 (923) 737-50-33</a>'; 467 } 468 469 function open(btn) { 470 trigger = btn; 471 clearTimeout(closeTimer); 472 if (!form) { body.innerHTML = formHtml; bindForm(); } 473 configure(btn.getAttribute('data-modal'), btn.getAttribute('data-title'), (btn.textContent || '').trim()); 474 root.hidden = false;
475 root.setAttribute('data-open', 'true'); 476 lockScroll(); 477 /* ÐºÐ°Ð´Ñ Ð½Ð° пÑименение иÑÑ Ð¾Ð´Ð½Ð¾Ð³Ð¾ ÑоÑÑоÑниÑ, инаÑе пеÑÐµÑ Ð¾Ð´ не пÑоигÑаеÑÑÑ */ 478 requestAnimationFrame(function () { 479 requestAnimationFrame(function () { root.setAttribute('data-in', 'true'); }); 480 }); 481 var first = form.querySelector('input:not([type="hidden"]):not([tabindex="-1"])'); 482 setTimeout(function () { if (first) first.focus({ preventScroll: true }); }, reduced.matches ? 0 : 60); 483 goal('form_open', { 484 button: (btn.textContent || '').trim(), 485 page: location.pathname 486 }); 487 } 488 489 function close() { 490 if (root.getAttribute('data-open') !== 'true') return; 491 root.setAttribute('data-in', 'false'); 492 closeTimer = setTimeout(function () { 493 root.setAttribute('data-open', 'false'); 494 root.hidden = true; 495 unlockScroll(); 496 if (trigger && document.contains(trigger)) trigger.focus({ preventScroll: true }); 497 }, reduced.matches ? 0 : 200); 498 } 499 500 bindForm(); 501 502 document.addEventListener('click', function (e) { 503 var btn = e.target.closest('[data-modal]'); 504 if (btn) { e.preventDefault(); open(btn); return; } 505 if (e.target.closest('[data-mdl-close]') && root.contains(e.target)) close(); 506 }); 507 508 document.addEventListener('keydown', function (e) { 509 if (root.getAttribute('data-open') !== 'true') return; 510 if (e.key === 'Escape') { e.preventDefault(); close(); return; } 511 if (e.key !== 'Tab') return; // ÑокÑÑ Ð½Ðµ ÑÑ Ð¾Ð´Ð¸Ñ Ð¸Ð· окна 512 var f = focusables(); 513 if (!f.length) return; 514 var first = f[0], last = f[f.length - 1]; 515 if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } 516 else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } 517 else if (!card.contains(document.activeElement)) { e.preventDefault(); first.focus(); } 518 }); 519 } 520 521 /* ------------------------------------------- ÐоÑвление ÑекÑий пÑи ÑкÑолле */ 522 function reveal() { 523 var nodes = document.querySelectorAll('.reveal'); 524 if (!nodes.length) return; 525 if (reduced.matches || !('IntersectionObserver' in window)) { 526 nodes.forEach(function (n) { n.classList.add('is-in'); }); 527 return; 528 } 529 var io = new IntersectionObserver(function (entries) { 530 entries.forEach(function (entry) { 531 if (!entry.isIntersecting) return; 532 entry.target.classList.add('is-in'); 533 io.unobserve(entry.target); 534 }); 535 }, { rootMargin: '0px 0px -10% 0px', threshold: 0 }); 536 /* threshold в долÑÑ Ð½Ðµ годиÑÑÑ: Ñ Ð´Ð»Ð¸Ð½Ð½ÑÑ Ð±Ð»Ð¾ÐºÐ¾Ð² (полиÑика, ÑÑаÑÑи) 8% вÑÑоÑÑ 537 болÑÑе ÑкÑана, и поÑвление не ÑÑабаÑÑвало никогда */ 538 nodes.forEach(function (n) { io.observe(n); }); 539 } 540 541 542 /* ------------------------------------------------- ÐайÑбокÑ: пиÑÑма и ÑоÑо */ 543 function lightbox() { 544 /* ÐÑÑпп на ÑÑÑаниÑе Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð½ÐµÑколÑко: пиÑÑма, дипломÑ, ÑакÑÑ. 545 Ðкно одно, ÑпиÑок подÑÑавлÑем ÑоÑ, по коÑоÑÐ¾Ð¼Ñ ÐºÐ»Ð¸ÐºÐ½Ñли. */ 546 var groups = Array.prototype.slice.call(document.querySelectorAll('[data-lightbox]')); 547 if (!groups.length) return; 548 var items = []; 549 550 var box = document.querySelector('[data-lb]'); 551 if (!box) return; // на ÑÑÑаниÑе Ð½ÐµÑ Ð¾ÐºÐ½Ð° пÑоÑмоÑÑа - вÑÑ Ð¾Ð´Ð¸Ð¼ ÑÐ¸Ñ Ð¾ 552 var frame = box.querySelector('.lb__frame'); 553 var img = box.querySelector('.lb__img'); 554 var count = box.querySelector('[data-lb-count]'); 555 var prev = box.querySelector('[data-lb-prev]'); 556 var next = box.querySelector('[data-lb-next]'); 557 var closeBtn = box.querySelector('[data-lb-close]'); 558 var bar = box.querySelector('.lb__bar'); 559 var i = 0, lastFocus = null; 560 561 function show(n, instant) { 562 if (!items.length) return; 563 // лиÑÑаем по кÑÑгÑ: за поÑледним пиÑÑмом идÑÑ Ð¿ÐµÑвое и наобоÑÐ¾Ñ 564 i = (n + items.length) % items.length; 565 var a = items[i]; 566 var apply = function () { 567 img.src = a.dataset.lbSrc; 568 img.alt = a.dataset.lbAlt || ''; 569 frame.classList.remove('is-swapping'); 570 }; 571 if (instant || reduced.matches) apply(); 572 else { frame.classList.add('is-swapping'); setTimeout(apply, 130); } 573 count.textContent = (i + 1) + ' из ' + items.length; 574 } 575 576 function open(list, n) { 577 items = list; 578 /* У гÑÑÐ¿Ð¿Ñ Ð¸Ð· одного изобÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð»Ð¸ÑÑаÑÑ Ð½ÐµÑего */ 579 if (bar) bar.hidden = items.length < 2; 580 lastFocus = document.activeElement; 581 show(n, true); 582 box.setAttribute('data-open', 'true'); 583 lockScroll(); 584 requestAnimationFrame(function () { box.setAttribute('data-in', 'true'); }); 585 closeBtn.focus(); 586 } 587 function close() { 588 box.setAttribute('data-in', 'false'); 589 var done = function () { 590 box.setAttribute('data-open', 'false'); 591 unlockScroll(); 592 if (lastFocus) lastFocus.focus(); 593 }; 594 if (reduced.matches) done(); else setTimeout(done, 200); 595 } 596 597 groups.forEach(function (root) { 598 var list = Array.prototype.slice.call(root.querySelectorAll('a[data-lb-src]')); 599 list.forEach(function (a, n) { 600 a.addEventListener('click', function (e) { e.preventDefault(); open(list, n); }); 601 }); 602 }); 603 prev.addEventListener('click', function () { show(i - 1); }); 604 next.addEventListener('click', function () { show(i + 1); }); 605 closeBtn.addEventListener('click', close); 606 box.querySelector('.lb__scrim').addEventListener('click', close); 607 608 document.addEventListener('keydown', function (e) { 609 if (box.getAttribute('data-open') !== 'true') return; 610 if (e.key === 'Escape') { e.preventDefault(); close(); } 611 if (e.key === 'ArrowLeft') show(i - 1); 612 if (e.key === 'ArrowRight') show(i + 1); 613 if (e.key === 'Tab') { // ÑокÑÑ Ð½Ðµ ÑÑ Ð¾Ð´Ð¸Ñ Ð¸Ð· окна 614 var f = box.querySelectorAll('button:not([disabled])'); 615 var first = f[0], last = f[f.length - 1]; 616 if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } 617 else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } 618 } 619 }); 620 621 /* Свайп: каÑÑинка идÑÑ Ð·Ð° палÑÑем один к одномÑ, ÑеÑение по ÑкоÑоÑÑи бÑоÑка */ 622 var x0 = 0, t0 = 0, dx = 0, dragging = false;
623 frame.addEventListener('pointerdown', function (e) { 624 if (e.pointerType === 'mouse') return; 625 dragging = true; x0 = e.clientX; t0 = e.timeStamp; dx = 0; 626 frame.setPointerCapture(e.pointerId); 627 frame.style.transition = 'none'; 628 }); 629 frame.addEventListener('pointermove', function (e) { 630 if (!dragging) return; 631 dx = e.clientX - x0; 632 frame.style.transform = 'translateX(' + dx.toFixed(1) + 'px)'; 633 }); 634 function release(e) { 635 if (!dragging) return; 636 dragging = false; 637 var v = Math.abs(dx) / Math.max(1, e.timeStamp - t0); 638 frame.style.transition = 'transform 260ms cubic-bezier(.23,1,.32,1)'; 639 frame.style.transform = ''; 640 if (Math.abs(dx) > 70 || v > .5) show(dx < 0 ? i + 1 : i - 1); 641 dx = 0; 642 } 643 frame.addEventListener('pointerup', release); 644 frame.addEventListener('pointercancel', release); 645 } 646 647 /* ------------------------------------------------- ÐоÑиÑÑвание маÑеÑиала */ 648 /* Ð¦ÐµÐ»Ñ read_75. СÑиÑаем по вÑÑоÑе Ñамой ÑÑаÑÑи, а не вÑей ÑÑÑаниÑÑ: 649 подвал, «ÑиÑайÑе Ñакже» и ÑоÑма Ð²Ð½Ð¸Ð·Ñ Ð² ÑÑÑÑ Ð¸Ð´Ñи не должнÑ. */ 650 function readDepth() { 651 if (!/^\/(blog|issledovaniya)\/[^/]+\/?$/.test(location.pathname)) return; 652 var art = document.querySelector('.art__body'); 653 if (!art) return; 654 var done = false; 655 656 function check() { 657 if (done) return; 658 var box = art.getBoundingClientRect(); 659 if (box.height <= 0) return; 660 /* ÑколÑко ÑÑаÑÑи Ñже ÑÑло вÑÑе нижней кÑомки ÑкÑана */ 661 var seen = window.innerHeight - box.top; 662 if (seen / box.height >= 0.75) { 663 done = true; 664 window.removeEventListener('scroll', check); 665 goal('read_75', { page: location.pathname }); 666 } 667 } 668 669 window.addEventListener('scroll', check, { passive: true }); 670 check(); 671 } 672 673 /* ----------------------------------------------------------- ÐаглÑÑки */ 674 function stubs() { 675 document.addEventListener('click', function (e) { 676 var a = e.target.closest('a[aria-disabled="true"]'); 677 if (a) e.preventDefault(); 678 }); 679 } 680 681 function init() { 682 stickyHeader(); menu(); accordion(); reviews(); caseList(); 683 forms(); modal(); lightbox(); reveal(); stubs(); readDepth(); 684 } 685 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); 686 else init(); 687})();
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.