1window.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); 2window.bInitFormCounter = true; 3 4let appConstructor = function () { 5 this.control = { 6 field: '[data-form-field]', 7 input: 'data-form-input', 8 label: '[data-form-label]', 9 className: { 10 filled: 'form__field_filled', 11 error: 'form__field_error', 12 }, 13 }; 14 15 this.dropdown = { 16 container: '[data-custom-dropdown]', 17 value: '[data-custom-dropdown-value]', 18 option: '[data-custom-dropdown-option]', 19 className: { 20 container: { 21 opened: 'form__dropdown_opened', 22 }, 23 select: 'form__dropdown-select', 24 value: 'form__dropdown-value', 25 list: 'form__dropdown-options', 26 item: { 27 el: 'form__dropdown-options-item', 28 selected: 'form__dropdown-options-item_selected', 29 }, 30 }, 31 }; 32 33 this.modal = { 34 container: '[data-modal]', 35 opened: 'is-modal-opened', 36 visible: 'modal_visible', 37 }; 38}; 39 40appConstructor.prototype.getScrollbarWidth = function () { 41 return window.innerWidth - document.documentElement.clientWidth; 42} 43 44appConstructor.prototype.getScrollOffset = function ( 45 el, 46 offset = 0, 47 left = false, 48 wrapper = null, 49) { 50 let 51 scroll = left ? 52 window.scrollX : 53 window.scrollY, 54 scrollWrapper = wrapper ? 55 (left ? 56 document.querySelector(wrapper).scrollLeft : 57 document.querySelector(wrapper).scrollTop) : 58 0, 59 position = left ? 60 el.getBoundingClientRect().left : 61 el.getBoundingClientRect().top 62 ; 63 64 return position + scroll + scrollWrapper - offset; 65} 66 67appConstructor.prototype.getWindowParams = function () { 68 return { 69 top: window.scrollY, 70 left: window.scrollX, 71 width: window.innerWidth, 72 height: window.innerHeight, 73 xScroll: document.body.scrollWidth > document.body.clientWidth, 74 yScroll: document.body.scrollHeight > document.body.clientHeight, 75 }; 76} 77 78appConstructor.prototype.getContentOffset = function (screenWidth = 768) { 79 let self = this; 80 81 return self.getWindowParams().width < screenWidth ? 82 self.int.header.height : 83 self.int.header.heightMd; 84} 85 86appConstructor.prototype.scrollTo = function ( 87 to, 88 duration = 300, 89 left = false, 90 wrapper = null, 91) { 92 let 93 element = wrapper ? 94 document.querySelector(wrapper) : 95 (document.scrollingElement ?? document.documentElement), 96 direction = left ? 97 element.scrollLeft : 98 element.scrollTop, 99 start = direction, 100 change = to - start, 101 startDate = +new Date(), 102 easeInOutQuad = function (t, b, c, d) { 103 t /= d / 2; 104 if (t < 1) 105 return c / 2 * t * t + b; 106 t--; 107 return -c / 2 * (t * (t - 2) - 1) + b; 108 }, 109 animateScroll = function () { 110 let 111 currentDate = +new Date(), 112 currentTime = currentDate - startDate 113 ; 114 115 if (left) { 116 element.scrollLeft = parseInt(easeInOutQuad(currentTime, start, change, duration)); 117 118 currentTime < duration ? 119 requestAnimationFrame(animateScroll) : 120 element.scrollLeft = to; 121 } else { 122 element.scrollTop = parseInt(easeInOutQuad(currentTime, start, change, duration)); 123 124 currentTime < duration ? 125 requestAnimationFrame(animateScroll) : 126 element.scrollTop = to; 127 } 128 } 129 ; 130 animateScroll(); 131} 132 133appConstructor.prototype.handleScrollTo = function (target, offset = null) { 134 let self = this; 135 136 let 137 anchor = typeof target === 'string' ? document.querySelector(target) : target, 138 _offset = offset ?? 100 139 ; 140 141 if (anchor) { 142 self.scrollTo(self.getScrollOffset(anchor, _offset)); 143 } 144} 145 146appConstructor.prototype.modalShow = function (modal) { 147 let self = this; 148 149 let _modal = document.querySelector(modal); 150 151 document.querySelectorAll(self.modal.container).forEach((el) => { 152 el.classList.remove(self.modal.visible); 153 }); 154 155 if (_modal) { 156 let offset = self.getScrollbarWidth(); 157 _modal.classList.add(self.modal.visible); 158 document.documentElement.classList.add(self.modal.opened); 159 document.body.style.paddingRight = offset + 'px'; 160 } 161} 162 163appConstructor.prototype.modalHide = function () { 164 let self = this; 165 166 document.querySelectorAll(self.modal.container).forEach((el) => { 167 el.classList.remove(self.modal.visible); 168 }); 169 170 document.documentElement.classList.remove(self.modal.opened); 171 document.body.removeAttribute('style'); 172} 173 174appConstructor.prototype.viewTestimonial = function (id) { 175 let self = this; 176 177 let 178 el = document.getElementById(id), 179 modalContent = document.getElementById('modalTextContent') 180 ; 181 182 if (el && modalContent) { 183 modalContent.innerHTML = el.innerHTML; 184 self.modalShow('#modalText'); 185 } 186} 187 188appConstructor.prototype.handleDropdown = function () { 189 let self = this; 190 191 let dropdowns = document.querySelectorAll(self.dropdown.container); 192 193 dropdowns.forEach(function (dropdown) { 194 let 195 select, 196 options, 197 value, 198 list, 199 param 200 ; 201 202 if (dropdown.dataset.customDropdown !== 'inited') { 203 select = dropdown.querySelector('select'); 204 options = select.querySelectorAll('option'); 205 206 value = document.createElement('DIV'); 207 value.classList.add(self.dropdown.className.value); 208 value.classList.add('form__field-control'); 209 value.setAttribute('data-custom-dropdown-value', ''); 210 value.innerHTML = select.options[select.selectedIndex].innerHTML; 211 212 list = document.createElement('DIV'); 213 list.classList.add(self.dropdown.className.list); 214 215 options.forEach(function (option, i) { 216 let text = String(option.innerHTML).trim(); 217 param = document.createElement('DIV'); 218 param.innerHTML = text; 219 param.setAttribute('data-custom-dropdown-option', ''); 220 param.classList.add(self.dropdown.className.item.el); 221 param.addEventListener('click', function (e) { 222 dropdown.querySelectorAll(self.dropdown.option).forEac
222h(function (el) { 223 el.classList.remove(self.dropdown.className.item.selected); 224 }); 225 226 if (option.innerHTML === this.innerHTML) { 227 value.innerHTML = this.innerHTML; 228 this.classList.add(self.dropdown.className.item.selected); 229 select.selectedIndex = i; 230 select.dispatchEvent(new Event('change')); 231 } 232 233 value.click(); 234 }); 235 236 if (i === select.selectedIndex) { 237 param.classList.add(self.dropdown.className.item.selected); 238 if (text === '') { 239 value.innerHTML = dropdown.dataset.placeholder ?? ''; 240 } 241 } 242 243 if (text !== '') { 244 list.appendChild(param); 245 } 246 }); 247 248 dropdown.appendChild(value); 249 dropdown.appendChild(list); 250 select.classList.add(self.dropdown.className.select); 251 dropdown.dataset.customDropdown = 'inited'; 252 253 value.addEventListener('click', function (e) { 254 e.stopPropagation(); 255 handleClose(this); 256 dropdown.classList.toggle(self.dropdown.className.container.opened); 257 }); 258 } 259 }); 260 261 function handleClose(el) { 262 dropdowns.forEach(function (dropdown) { 263 let dropdownValue = dropdown.querySelector(self.dropdown.value); 264 265 if (el !== dropdownValue) 266 dropdown.classList.remove(self.dropdown.className.container.opened); 267 }); 268 } 269 270 document.addEventListener('click', handleClose); 271} 272 273appConstructor.prototype.handleEllipsis = function () { 274 let self = this; 275 276 document.querySelectorAll('[data-ellipsis]').forEach((text) => { 277 let 278 className = 'testimonials__slide-text_collapsed', 279 parent = text.parentNode 280 ; 281 282 if (text.scrollHeight > text.clientHeight) { 283 parent.classList.add(className); 284 } else { 285 parent.classList.remove(className); 286 } 287 }); 288} 289 290appConstructor.prototype.handleField = function () { 291 let self = this; 292 293 document.addEventListener('focusout', function (e) { 294 if (e.target && e.target.hasAttribute(self.control.input)) { 295 proceed(e.target); 296 } 297 }); 298 299 document.addEventListener('input', function (e) { 300 if (e.target && e.target.hasAttribute(self.control.input)) { 301 proceed(e.target); 302 let field = e.target.closest(self.control.field); 303 if (field) { 304 field.classList.remove(self.control.className.error); 305 } 306 } 307 }); 308 309 function proceed(input) { 310 let field = input.closest(self.control.field); 311 312 field && input.value.length && input.value !== '' ? 313 field.classList.add(self.control.className.filled) : 314 field.classList.remove(self.control.className.filled); 315 } 316} 317 318appConstructor.prototype.handleForm = function () { 319 let self = this; 320 321 let proceed = true; 322 323 if (proceed) { 324 Array.from(document.forms).forEach((form) => { 325 form.onsubmit = (e) => e.preventDefault(); 326 form.querySelectorAll('[type="submit"]').forEach((btn) => { 327 btn.setAttribute('disabled', ''); 328 btn.disabled = true; 329 }); 330 }); 331 window.addEventListener('touchstart', enable); 332 window.addEventListener('mousemove', enable); 333 window.addEventListener('click', enable); 334 window.addEventListener('keydown', enable); 335 } 336 337 function enable() { 338 if (!proceed) { 339 return false; 340 } 341 document.querySelectorAll('form').forEach((form) => { 342 form.onsubmit = (e) => true; 343 form.querySelectorAll('[type="submit"]').forEach((btn) => { 344 btn.removeAttribute('disabled'); 345 btn.disabled = false; 346 }); 347 }); 348 proceed = false; 349 } 350} 351 352appConstructor.prototype.handleFormAgreement = function (control) { 353 let self = this; 354 355 let 356 el = control.closest('.form__agreement'), 357 form = control.closest('form'), 358 formDiv = control.closest('.form'), 359 btn = form ? form.querySelector('[type="submit"]') : null, 360 modalBody = control.closest('.modal__body') 361 ; 362 363 if (el) { 364 control.checked ? el.classList.remove('form__agreement_error') : el.classList.add('form__agreement_error'); 365 } 366 367 if (btn) {
368 control.checked ? btn.removeAttribute('disabled') : btn.setAttribute('disabled', ''); 369 } 370 371 if (modalBody) { 372 control.checked ? modalBody.classList.remove('modal__body_error') : modalBody.classList.add('modal__body_error'); 373 } else if (formDiv) { 374 control.checked ? formDiv.classList.remove('form_error') : formDiv.classList.add('form_error'); 375 } 376} 377 378appConstructor.prototype.handleFormAgreementSubscribe = function (control) { 379 let self = this; 380 381 let 382 form = control.closest('form'), 383 input = form.querySelector('[data-form-input="DO_SUBSCRIBE"]') 384 ; 385 386 if (input) { 387 input.value = control.checked ? 'Ðа' : 'ÐеÑ'; 388 } 389} 390 391appConstructor.prototype.handleFormRequired = function (form) { 392 let self = this; 393 394 let 395 btn = form.querySelector('[type="submit"]'), 396 formDiv = form.querySelector('.form'), 397 modalBody = form.querySelector('.modal__body'), 398 required = form.querySelectorAll('[required]'), 399 valid = true, 400 focus = true 401 ; 402 403 required.forEach(control => { 404 if (control.type === 'checkbox') { 405 valid = control.checked; 406 let agreement = control.closest('.form__agreement'); 407 if (agreement) { 408 agreement.classList[valid ? 'remove' : 'add']('form__agreement_error'); 409 } 410 } else { 411 valid = control.value.length; 412 let field = control.closest('[data-form-field]'); 413 if (field) { 414 field.classList[valid ? 'remove' : 'add']('form__field_error'); 415 } 416 } 417 if (focus) { 418 focus = false; 419 if (!modalBody) { 420 self.handleScrollTo(control, 140); 421 } 422 } 423 }); 424 425 /*if (btn) { 426 valid ? btn.removeAttribute('disabled') : btn.setAttribute('disabled', ''); 427 }*/ 428 429 if (modalBody) { 430 valid ? modalBody.classList.remove('modal__body_error') : modalBody.classList.add('modal__body_error'); 431 } else if (formDiv) { 432 valid ? formDiv.classList.remove('form_error') : formDiv.classList.add('form_error'); 433 } 434} 435 436appConstructor.prototype.handleMask = function () { 437 let self = this; 438 439 if (typeof Inputmask === 'function') { 440 Inputmask({ 441 mask: '9{15}', 442 showMaskOnHover: false, 443 placeholder: '', 444 greedy: false, 445 }).mask('[type="tel"]'); 446 } 447} 448 449appConstructor.prototype.handleParallax = function (items, endX = 0, endY = 0, x = 0, y = 0) { 450 let self = this; 451 452 let pxSpeed = .75; 453 454 x += Math.floor((endX - x) / pxSpeed); 455 y += Math.floor((endY - y) / pxSpeed); 456 457 items.forEach(function (item) { 458 let val = parseInt(item.dataset.px); 459 item.style.transform = `translate(${x / val}px, ${y / val}px)`; 460 }); 461} 462 463appConstructor.prototype.handleSwiper = function (id, options = {}) { 464 let self = this; 465 document.addEventListener('DOMContentLoaded', () => { 466 new Swiper(id, options); 467 }); 468} 469 470appConstructor.prototype.handleTags = function () { 471 let self = this; 472 473 document.querySelectorAll('[data-tags]').forEach((el) => { 474 let 475 cnt = el.querySelector('[data-tags-cnt]'), 476 invisible = 0, 477 items = el.querySelectorAll('[data-tags-item]') 478 ; 479 480 if (cnt) { 481 el.removeChild(cnt); 482 } 483 484 countInvisible(); 485 486 if (el.scrollHeight - 8 > 24) { 487 cnt = document.createElement('div'); 488 cnt.classList.add('tags__item'); 489 cnt.classList.add('tags__item_cnt'); 490 cnt.setAttribute('data-tags-cnt', String(invisible)); 491 cnt.dataset.tagsCnt = String(invisible); 492 cnt.innerHTML = '+' + String(invisible); 493 el.insertAdjacentElement('beforeend', cnt); 494 invisible = 0; 495 countInvisible(); 496 cnt.innerHTML = '+' + String(invisible); 497 } else { 498 cnt = el.querySelector('[data-tags-cnt]'); 499 if (cnt) { 500 el.removeChild(cnt); 501 } 502 } 503 504 function countInvisible() { 505 items.forEach((item) => { 506 if (item.offsetTop + item.offsetHeight > el.offsetTop + el.offsetHeight) { 507 invisible++; 508 } 509 }); 510 } 511 }); 512} 513 514appConstructor.prototype.handleTippy = function () { 515 let self = this; 516 517 tippy('[data-tippy-content]', { 518 interactive: true, 519 }); 520} 521 522appConstructor.prototype.handleViewport = function () { 523 let self = this; 524 525 let meta = document.querySelector('meta[name="viewport"]'); 526 if (meta) {
527 let content = document.documentElement.clientWidth < 375 ? 'width=360px' : 'width=device-width'; 528 meta.setAttribute('content', content); 529 } 530} 531 532appConstructor.prototype.formReinit = function () { 533 let self = this; 534 535 self.handleField(); 536 self.handleMask(); 537 self.handleDropdown(); 538} 539 540appConstructor.prototype.formSubmit = function ( 541 form, 542 isModal = false, 543 modalSuccess = 'modalSuccess', 544 url = '/local/ajax/form.php', 545) { 546 let self = this; 547 548 let focused = 0; 549 550 form.querySelector('[type="submit"]').addEventListener('click', () => { 551 self.handleFormRequired(form); 552 }); 553 554 form.querySelectorAll('[required]').forEach(control => { 555 control.addEventListener('input', e => { 556 if (control.type !== 'checkbox') { 557 let field = control.closest('[data-form-field]'); 558 if (field) { 559 field.classList[control.value.length ? 'remove' : 'add']('form__field_error'); 560 } 561 } 562 }); 563 control.addEventListener('focus', e => { 564 focused++; 565 }); 566 }); 567 568 if (typeof BX != 'undefined') { 569 BX.bind(form, 'submit', BX.proxy(function (e) { 570 BX.PreventDefault(e); 571 572 handlePreloader(); 573 574 let formData = new FormData(form); 575 formData.append('focus' + (new Date()).getUTCHours(), encodeURIComponent(focused)); 576 formData.append('ajaxCall', window.formToken); 577 578 fetch(url, { 579 body: formData, 580 method: 'POST', 581 }).then(r => r.json()).then(r => { 582 if (r.success) { 583 form.reset(); 584 App.modalShow('#' + modalSuccess); 585 handlePreloader(false); 586 if (isModal && typeof ym === 'function') { 587 ym(22994086, 'reachGoal', 'pop_up'); 588 } 589 } else { 590 handlePreloader(false); 591 592 if (r.errors && typeof r.errors === 'object') { 593 form.querySelectorAll('[data-form-input]').forEach(input => { 594 if (Object.keys(r.errors)?.length && Object.keys(r.errors).includes(input.dataset.formInput)) { 595 input.closest('[data-form-field]').classList.add('form__field_error'); 596 } 597 }); 598 } 599 } 600 }); 601 }, form, url)); 602 } 603} 604 605appConstructor.prototype.zoom = function (el, e, zoom = 2) { 606 const 607 img = el.querySelector('img'), 608 { pageX, pageY } = e, 609 { width, height, left, top } = el.getBoundingClientRect(), 610 x = (pageX - left - width / 2) / width * 100, 611 y = (pageY - (top + window.scrollY) - height / 2) / height * 100, 612 tx = -x * (zoom - 1) / 2, 613 ty = -y * (zoom - 1) / 2 614 ; 615 616 img.style.transform = `scale(${zoom}) translate(${tx}%, ${ty}%)`; 617} 618 619function handlePreloader(show = true) { 620 let el = document.getElementById('preloader'); 621 if (el) { 622 el.classList[show ? 'add' : 'remove']('preloader_visible'); 623 } 624} 625 626appConstructor.prototype.init = function () { 627 let self = this; 628 629 document.addEventListener('DOMContentLoaded', () => { 630 self.handleDropdown(); 631 self.handleField(); 632 self.handleForm(); 633 self.handleMask(); 634 self.handleEllipsis(); 635 self.handleTags(); 636 self.handleTippy(); 637 self.handleViewport(); 638 639 window.addEventListener('resize', e => { 640 self.handleEllipsis(); 641 self.handleTags(); 642 self.handleViewport(); 643 }); 644 645 if (window.innerWidth >= 1280) { 646 window.addEventListener('mousemove', e => { 647 self.handleParallax( 648 document.querySelectorAll('[data-px]'), 649 window.innerWidth / 2 - e.x, 650 window.innerHeight / 2 - e.y, 651 ); 652 }); 653 } 654 }); 655} 656 657App = new appConstructor(); 658 659App.init();
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.