const cookieMaxAge = 604800; function getCookie(name) { let matches = document.cookie.match( new RegExp( "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, "\\$1") + "=([^;]*)" ) ); return matches ? decodeURIComponent(matches[1]) : undefined; } function setCookie(name, value, props) { props = props || {}; var exp = props.expires; if (typeof exp == "number" && exp) { var d = new Date(); d.setTime(d.getTime() + exp * 1000); exp = props.expires = d; } if (exp && exp.toUTCString) { props.expires = exp.toUTCString(); } value = encodeURIComponent(value); var updatedCookie = name + "=" + value; for (var propName in props) { updatedCookie += "; " + propName; var propValue = props[propName]; if (propValue !== true) { updatedCookie += "=" + propValue; } } document.cookie = updatedCookie; } function deleteCookie(name) { setCookie(name, null, { expires: -1 }); } //import { flsModules } from "./modules.js"; const flsModules = {}; // -------------- //import { isMobile, bodyLockStatus, bodyLock, bodyUnlock, bodyLockToggle } from "../files/functions.js"; //import { flsModules } from "../files/modules.js"; // ----------- functions // Подключение списка активных модулей /* Проверка поддержки webp, добавление класса webp или no-webp для HTML */ const isWebp = () => { // Проверка поддержки webp const testWebP = callback => { let webP = new Image() webP.onload = webP.onerror = () => callback(webP.height == 2) webP.src = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA" } // Добавление класса webp или no-webp для HTML testWebP(support => { let className = support ? 'webp' : 'no-webp' document.documentElement.classList.add(className) }) } /* Проверка мобильного браузера */ const isMobile = { Android: () => navigator.userAgent.match(/Android/i), BlackBerry: () => navigator.userAgent.match(/BlackBerry/i), iOS: () => navigator.userAgent.match(/iPhone|iPad|iPod/i), Opera: () => navigator.userAgent.match(/Opera Mini/i), Windows: () => navigator.userAgent.match(/IEMobile/i), any: () => (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()) } /* Добавление класса touch для HTML если браузер мобильный */ const addTouchClass = () => isMobile.any() ? document.documentElement.classList.add('touch') : null // Добавление loaded для HTML после полной загрузки страницы const addLoadedClass = () => { window.addEventListener("load", () => { setTimeout(() => document.documentElement.classList.add('loaded'), 0) }) } // Получение хеша в адресе сайта const getHash = () => location.hash ? location.hash.replace('#', '') : null // Указание хеша в адресе сайта const setHash = hash => { hash = hash ? `#${hash}` : window.location.href.split('#')[0] history.pushState('', '', hash) } // Учет плавающей панели на мобильных устройствах при 100vh const fullVHfix = () => { const fullScreen = document.querySelector('[data-fullscreen]') if (fullScreen && isMobile.any()) { const fixHeight = () => { let vh = window.innerHeight * 0.01 document.documentElement.style.setProperty('--vh', `${vh}px`) } window.addEventListener('resize', fixHeight) fixHeight() } } // Плавная прокрутка к якорям const anchors = () => { const anchors = document.querySelectorAll('a[href*="#"]') if (anchors.length) { anchors.forEach(anchor => { anchor.addEventListener('click', e => { e.preventDefault() const blockID = anchor.getAttribute('href') const headerBottomHeight = document.querySelector('.bottom-header').clientHeight let topPos = document.querySelector(blockID).getBoundingClientRect().top + window.pageYOffset - headerBottomHeight - 20 if (window.innerWidth < 992) { topPos = document.querySelector(blockID).getBoundingClientRect().top + window.pageYOffset - 60 } // menuClose() window.scrollTo({ top: topPos, // scroll so that the element is at the top of the view behavior: 'smooth' // smooth scroll }) // document.querySelector(blockID).scrollIntoView({ behavior: 'smooth' }) }) }) } } // Плавное раскрытие блока let _slideUp = (target, duration = 500, showmore = 0) => { if (!target.classList.contains('_slide')) { target.classList.add('_slide') target.style.transitionProperty = 'height, margin, padding' target.style.transitionDuration = duration + 'ms' target.style.height = `${target.offsetHeight}px` target.offsetHeight target.style.overflow = 'hidden' target.style.height = showmore ? `${showmore}px` : `0px` target.style.paddingTop = 0 target.style.paddingBottom = 0 target.style.marginTop = 0 target.style.marginBottom = 0 window.setTimeout(() => { target.hidden = !showmore ? true : false !showmore ? target.style.removeProperty('height') : null target.style.removeProperty('padding-top') target.style.removeProperty('padding-bottom') target.style.removeProperty('margin-top') target.style.removeProperty('margin-bottom') !showmore ? target.style.removeProperty('overflow') : null target.style.removeProperty('transition-duration') target.style.removeProperty('transition-property') target.classList.remove('_slide') // Создаем событие document.dispatchEvent(new CustomEvent("slideUpDone", { detail: { target: target } })) }, duration) } } // Плавное закрытие блока let _slideDown = (target, duration = 500, showmore = 0) => { if (!target.classList.contains('_slide')) { target.classList.add('_slide') target.hidden = target.hidden ? false : null showmore ? target.style.removeProperty('height') : null let height = target.offsetHeight target.style.overflow = 'hidden' target.style.height = showmore ? `${showmore}px` : `0px` target.style.paddingTop = 0 target.style.paddingBottom = 0 target.style.marginTop = 0 target.style.marginBottom = 0 target.offsetHeight target.style.transitionProperty = "height, margin, padding" target.style.transitionDuration = duration + 'ms' target.style.height = height + 'px' target.style.removeProperty('padding-top') target.style.removeProperty('padding-bottom') target.style.removeProperty('margin-top') target.style.removeProperty('margin-bottom') window.setTimeout(() => { target.style.removeProperty('height') target.style.removeProperty('overflow') target.style.removeProperty('transition-duration') target.style.removeProperty('transition-property') target.classList.remove('_slide') // Создаем событие document.dispatchEvent(new CustomEvent("slideDownDone", { detail: { target: target } })) }, duration) } } let _slideToggle = (target, duration = 500) => target.hidden ? _slideDown(target, duration) : _slideUp(target, duration) let bodyLockStatus = true // Блокировать прокрутку let bodyLock = (delay = 500) => { const body = document.body if (bodyLockStatus) { let locksPadding = document.querySelectorAll("[data-lp]") locksPadding.forEach(lockPadding => lockPadding.style.paddingRight = window.innerWidth - document.querySelector('.wrapper').offsetWidth + 'px' ) body.style.paddingRight = window.innerWidth - document.querySelector('.wrapper').offsetWidth + 'px' document.documentElement.classList.add("lock") bodyLockStatus = false setTimeout(() => bodyLockStatus = true, delay) } } // Разблокировать прокрутку let bodyUnlock = (delay = 500) => { const body = document.body if (bodyLockStatus) { let locksPadding = document.querySelectorAll("[data-lp]") setTimeout(() => { locksPadding.forEach(lockPadding => lockPadding.style.paddingRight = '0') body.style.paddingRight = '0' document.documentElement.classList.remove("lock") }, delay) bodyLockStatus = false setTimeout(() => bodyLockStatus = true, delay) } } let bodyLockToggle = (delay = 500) => document.documentElement.classList.contains('lock') ? bodyUnlock(delay) : bodyLock(delay) /* Спойлеры [data-spollers] для родителя [data-spoller] для заголовков Спойлеры с медиа-запросами: [data-spollers="992,max"], [data-spollers="768,min"] [data-spollers-speed] скорость открытия Если нужен аккордион [data-one-spoller] для родителя */ const spollers = () => { const spollersArray = document.querySelectorAll('[data-spollers]') if (spollersArray.length) { // Получение обычных слойлеров const spollersRegular = Array.from(spollersArray).filter((item, index, self) => !item.dataset.spollers.split(",")[0]) // Инициализация function initSpollers(spollersArray, matchMedia = false) { spollersArray.forEach(spollersBlock => { spollersBlock = matchMedia ? spollersBlock.item : spollersBlock if (matchMedia.matches || !matchMedia) { spollersBlock.classList.add('_spoller-init') initSpollerBody(spollersBlock) spollersBlock.addEventListener("click", setSpollerAction) } else { spollersBlock.classList.remove('_spoller-init') initSpollerBody(spollersBlock, false) spollersBlock.removeEventListener("click", setSpollerAction) } }) } // Инициализация обычных слойлеров spollersRegular.length ? initSpollers(spollersRegular) : null // Получение слойлеров с медиа запросами let mdQueriesArray = dataMediaQueries(spollersArray, "spollers") if (mdQueriesArray?.length) { mdQueriesArray.forEach(mdQueriesItem => { // Событие mdQueriesItem.matchMedia.addEventListener("change", function () { initSpollers(mdQueriesItem.itemsArray, mdQueriesItem.matchMedia) }) initSpollers(mdQueriesItem.itemsArray, mdQueriesItem.matchMedia) }) } // Работа с контентом function initSpollerBody(spollersBlock, hideSpollerBody = true) { let spollerTitles = spollersBlock.querySelectorAll('[data-spoller]') if (spollerTitles.length) { spollerTitles = Array.from(spollerTitles).filter(item => item.closest('[data-spollers]') === spollersBlock) spollerTitles.forEach(spollerTitle => { if (hideSpollerBody) { spollerTitle.removeAttribute('tabindex') if (!spollerTitle.classList.contains('_spoller-active')) { spollerTitle.nextElementSibling.hidden = true } } else { spollerTitle.setAttribute('tabindex', '-1') spollerTitle.nextElementSibling.hidden = false } }) } } function setSpollerAction(e) { const el = e.target if (el.closest('[data-spoller]')) { const spollerTitle = el.closest('[data-spoller]') const spollersBlock = spollerTitle.closest('[data-spollers]') const oneSpoller = spollersBlock.hasAttribute('data-one-spoller') const spollerSpeed = spollersBlock.dataset.spollersSpeed ? parseInt(spollersBlock.dataset.spollersSpeed) : 500 if (!spollersBlock.querySelectorAll('._slide').length) { if (oneSpoller && !spollerTitle.classList.contains('_spoller-active')) hideSpollersBody(spollersBlock) spollerTitle.classList.toggle('_spoller-active') _slideToggle(spollerTitle.nextElementSibling, spollerSpeed) } e.preventDefault() } } function hideSpollersBody(spollersBlock) { const spollerActiveTitle = spollersBlock.querySelector('[data-spoller]._spoller-active') const spollerSpeed = spollersBlock.dataset.spollersSpeed ? parseInt(spollersBlock.dataset.spollersSpeed) : 500 if (spollerActiveTitle && !spollersBlock.querySelectorAll('._slide').length) { spollerActiveTitle.classList.remove('_spoller-active') _slideUp(spollerActiveTitle.nextElementSibling, spollerSpeed) } } const spollersClose = document.querySelectorAll('[data-spoller-close]') if (spollersClose.length) { document.addEventListener("click", function (e) { const el = e.target; if (!el.closest('[data-spollers]')) { spollersClose.forEach(spollerClose => { const spollersBlock = spollerClose.closest('[data-spollers]') const spollerSpeed = spollersBlock.dataset.spollersSpeed ? parseInt(spollersBlock.dataset.spollersSpeed) : 500 if (!spollersBlock.querySelectorAll('._slide').length) { spollerClose.classList.remove('_spoller-active') _slideUp(spollerClose.nextElementSibling, spollerSpeed) } }); } }); } } } /* Табы [data-tabs] для родителя [data-tabs-titles] для родителя заголовков [data-tabs-body] для родителя блоков Если нужно добавление хеша, [data-tabs-hash] для родителя Открытие табов с анимаеций, [data-tabs-animate], скорость [data-tabs-animate="1000"] Превращение табов в спойлеры [data-tabs="992"] */ const tabs = () => { const tabs = document.querySelectorAll('[data-tabs]') let tabsActiveHash = [] if (tabs.length) { const hash = getHash() if (hash && hash.startsWith('tab-')) tabsActiveHash = hash.replace('tab-', '').split('-') tabs.forEach((tabsBlock, index) => { tabsBlock.classList.add('_tab-init') tabsBlock.setAttribute('data-tabs-index', index) tabsBlock.addEventListener("click", setTabsAction) initTabs(tabsBlock) }) // Получение слойлеров с медиа запросами let mdQueriesArray = dataMediaQueries(tabs, "tabs") if (mdQueriesArray?.length) { mdQueriesArray.forEach(mdQueriesItem => { // Событие mdQueriesItem.matchMedia.addEventListener("change", () => setTitlePosition(mdQueriesItem.itemsArray, mdQueriesItem.matchMedia)) setTitlePosition(mdQueriesItem.itemsArray, mdQueriesItem.matchMedia) }) } } // Установка позиций заголовков function setTitlePosition(tabsMediaArray, matchMedia) { tabsMediaArray.forEach(tabsMediaItem => { tabsMediaItem = tabsMediaItem.item let tabsTitles = tabsMediaItem.querySelector('[data-tabs-titles]') let tabsTitleItems = tabsMediaItem.querySelectorAll('[data-tabs-title]') let tabsContent = tabsMediaItem.querySelector('[data-tabs-body]') let tabsContentItems = tabsMediaItem.querySelectorAll('[data-tabs-item]') tabsTitleItems = Array.from(tabsTitleItems).filter(item => item.closest('[data-tabs]') === tabsMediaItem) tabsContentItems = Array.from(tabsContentItems).filter(item => item.closest('[data-tabs]') === tabsMediaItem) tabsContentItems.forEach((tabsContentItem, index) => { if (matchMedia.matches) { tabsContent.append(tabsTitleItems[index]) tabsContent.append(tabsContentItem) tabsMediaItem.classList.add('_tab-spoller') } else { tabsTitles.append(tabsTitleItems[index]) tabsMediaItem.classList.remove('_tab-spoller') } }) }) } // Работа с контентом function initTabs(tabsBlock) { let tabsTitles = tabsBlock.querySelectorAll('[data-tabs-titles]>*') let tabsContent = tabsBlock.querySelectorAll('[data-tabs-body]>*') const tabsBlockIndex = tabsBlock.dataset.tabsIndex const tabsActiveHashBlock = tabsActiveHash[0] == tabsBlockIndex if (tabsActiveHashBlock) { const tabsActiveTitle = tabsBlock.querySelector('[data-tabs-titles]>._tab-active') tabsActiveTitle ? tabsActiveTitle.classList.remove('_tab-active') : null } if (tabsContent.length) { tabsContent = Array.from(tabsContent).filter(item => item.closest('[data-tabs]') === tabsBlock) tabsTitles = Array.from(tabsTitles).filter(item => item.closest('[data-tabs]') === tabsBlock) tabsContent.forEach((tabsContentItem, index) => { tabsTitles[index].setAttribute('data-tabs-title', '') tabsContentItem.setAttribute('data-tabs-item', '') if (tabsActiveHashBlock && index == tabsActiveHash[1]) tabsTitles[index].classList.add('_tab-active') tabsContentItem.hidden = !tabsTitles[index].classList.contains('_tab-active') }) } } function setTabsStatus(tabsBlock) { let tabsTitles = tabsBlock.querySelectorAll('[data-tabs-title]') let tabsContent = tabsBlock.querySelectorAll('[data-tabs-item]') const tabsBlockIndex = tabsBlock.dataset.tabsIndex function isTabsAnamate(tabsBlock) { if (tabsBlock.hasAttribute('data-tabs-animate')) tabsBlock.dataset.tabsAnimate > 0 ? Number(tabsBlock.dataset.tabsAnimate) : 500 } const tabsBlockAnimate = isTabsAnamate(tabsBlock) if (tabsContent.length > 0) { const isHash = tabsBlock.hasAttribute('data-tabs-hash') tabsContent = Array.from(tabsContent).filter(item => item.closest('[data-tabs]') === tabsBlock) tabsTitles = Array.from(tabsTitles).filter(item => item.closest('[data-tabs]') === tabsBlock) tabsContent.forEach((tabsContentItem, index) => { if (tabsTitles[index].classList.contains('_tab-active')) { if (tabsBlockAnimate) _slideDown(tabsContentItem, tabsBlockAnimate) else tabsContentItem.hidden = false if (isHash && !tabsContentItem.closest('.popup')) setHash(`tab-${tabsBlockIndex}-${index}`) } else { if (tabsBlockAnimate) _slideUp(tabsContentItem, tabsBlockAnimate) else tabsContentItem.hidden = true } }) } } function setTabsAction(e) { const el = e.target if (el.closest('[data-tabs-title]')) { const tabTitle = el.closest('[data-tabs-title]') const tabsBlock = tabTitle.closest('[data-tabs]') if (!tabTitle.classList.contains('_tab-active') && !tabsBlock.querySelector('._slide')) { let tabActiveTitle = tabsBlock.querySelectorAll('[data-tabs-title]._tab-active') tabActiveTitle.length ? tabActiveTitle = Array.from(tabActiveTitle).filter(item => item.closest('[data-tabs]') === tabsBlock) : null tabActiveTitle.length ? tabActiveTitle[0].classList.remove('_tab-active') : null tabTitle.classList.add('_tab-active') setTabsStatus(tabsBlock) } e.preventDefault() } } } // Меню бургер const menuInit = () => { if (document.querySelector(".icon-menu")) { document.addEventListener("click", e => { if (bodyLockStatus && e.target.closest('.icon-menu')) { bodyLockToggle() document.documentElement.classList.toggle("menu-open") } }) } } function menuOpen() { bodyLock() document.documentElement.classList.add("menu-open") } function menuClose() { bodyUnlock() document.documentElement.classList.remove("menu-open") } function closeMenuWithSwipe() { if (window.innerWidth < 768 && isMobile.any()) { const menu = document.querySelector('.menu__body') menu.addEventListener('touchstart', handleTouchStart, false) menu.addEventListener('touchmove', handleTouchMove, false) let x1 = null function handleTouchStart(e) { const firstTouch = e.touches[0] x1 = firstTouch.clientX } function handleTouchMove(e) { if (!x1) return false let x2 = e.touches[0].clientX let xDiff = x2 - x1 if (xDiff < -40) menuClose() } } } // Модуль "показать еще" ======================================================================================================================================================================================================================= /* Документация по работе в шаблоне: data-showmore-media = "768,min" data-showmore="size/items" data-showmore-content="размер/кол-во" data-showmore-button="скорость" Сниппет (HTML): showmore */ function showMore() { window.addEventListener("load", function (e) { const showMoreBlocks = document.querySelectorAll('[data-showmore]'); let showMoreBlocksRegular; let mdQueriesArray; if (showMoreBlocks.length) { // Получение обычных объектов showMoreBlocksRegular = Array.from(showMoreBlocks).filter(function (item, index, self) { return !item.dataset.showmoreMedia; }); // Инициализация обычных объектов showMoreBlocksRegular.length ? initItems(showMoreBlocksRegular) : null; document.addEventListener("click", showMoreActions); window.addEventListener("resize", showMoreActions); // Получение объектов с медиа запросами mdQueriesArray = dataMediaQueries(showMoreBlocks, "showmoreMedia"); if (mdQueriesArray && mdQueriesArray.length) { mdQueriesArray.forEach(mdQueriesItem => { // Событие mdQueriesItem.matchMedia.addEventListener("change", function () { initItems(mdQueriesItem.itemsArray, mdQueriesItem.matchMedia); }); }); initItemsMedia(mdQueriesArray); } } function initItemsMedia(mdQueriesArray) { mdQueriesArray.forEach(mdQueriesItem => { initItems(mdQueriesItem.itemsArray, mdQueriesItem.matchMedia); }); } function initItems(showMoreBlocks, matchMedia) { showMoreBlocks.forEach(showMoreBlock => { initItem(showMoreBlock, matchMedia); }); } function initItem(showMoreBlock, matchMedia = false) { showMoreBlock = matchMedia ? showMoreBlock.item : showMoreBlock; let showMoreContent = showMoreBlock.querySelectorAll('[data-showmore-content]'); let showMoreButton = showMoreBlock.querySelectorAll('[data-showmore-button]'); showMoreContent = Array.from(showMoreContent).filter(item => item.closest('[data-showmore]') === showMoreBlock)[0]; showMoreButton = Array.from(showMoreButton).filter(item => item.closest('[data-showmore]') === showMoreBlock)[0]; const hiddenHeight = getHeight(showMoreBlock, showMoreContent); if (matchMedia.matches || !matchMedia) { if (hiddenHeight < getOriginalHeight(showMoreContent)) { _slideUp(showMoreContent, 0, hiddenHeight); showMoreButton.hidden = false; } else { _slideDown(showMoreContent, 0, hiddenHeight); showMoreButton.hidden = true; } } else { _slideDown(showMoreContent, 0, hiddenHeight); showMoreButton.hidden = true; } } function getHeight(showMoreBlock, showMoreContent) { let hiddenHeight = 0; const showMoreType = showMoreBlock.dataset.showmore ? showMoreBlock.dataset.showmore : 'size'; if (showMoreType === 'items') { const showMoreTypeValue = showMoreContent.dataset.showmoreContent ? showMoreContent.dataset.showmoreContent : 3; const showMoreItems = showMoreContent.children; for (let index = 1; index < showMoreItems.length; index++) { const showMoreItem = showMoreItems[index - 1]; hiddenHeight += showMoreItem.offsetHeight; if (index == showMoreTypeValue) break } } else { const showMoreTypeValue = showMoreContent.dataset.showmoreContent ? showMoreContent.dataset.showmoreContent : 150; hiddenHeight = showMoreTypeValue; } return hiddenHeight; } function getOriginalHeight(showMoreContent) { let parentHidden; let hiddenHeight = showMoreContent.offsetHeight; showMoreContent.style.removeProperty('height'); if (showMoreContent.closest(`[hidden]`)) { parentHidden = showMoreContent.closest(`[hidden]`); parentHidden.hidden = false; } let originalHeight = showMoreContent.offsetHeight; parentHidden ? parentHidden.hidden = true : null; showMoreContent.style.height = `${hiddenHeight}px`; return originalHeight; } function showMoreActions(e) { const targetEvent = e.target; const targetType = e.type; if (targetType === 'click') { if (targetEvent.closest('[data-showmore-button]')) { const showMoreButton = targetEvent.closest('[data-showmore-button]'); const showMoreBlock = showMoreButton.closest('[data-showmore]'); const showMoreContent = showMoreBlock.querySelector('[data-showmore-content]'); const showMoreSpeed = showMoreBlock.dataset.showmoreButton ? showMoreBlock.dataset.showmoreButton : '500'; const hiddenHeight = getHeight(showMoreBlock, showMoreContent); if (!showMoreContent.classList.contains('_slide')) { showMoreBlock.classList.contains('_showmore-active') ? _slideUp(showMoreContent, showMoreSpeed, hiddenHeight) : _slideDown(showMoreContent, showMoreSpeed, hiddenHeight); showMoreBlock.classList.toggle('_showmore-active'); } } } else if (targetType === 'resize') { showMoreBlocksRegular && showMoreBlocksRegular.length ? initItems(showMoreBlocksRegular) : null; mdQueriesArray && mdQueriesArray.length ? initItemsMedia(mdQueriesArray) : null; } } }); } // Получить цифры из строки const getDigFromString = item => parseInt(item.replace(/[^\d]/g, '')) // Форматирование цифр типа 100 000 000 const getDigFormat = item => item.toString().replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, "$1 ") // Убрать класс из всех элементов массива const removeClasses = (array, className) => { for (var i = 0; i < array.length; i++) array[i].classList.remove(className) } // Уникализация массива const uniqArray = array => { return array.filter((item, index, self) => { return self.indexOf(item) === index }) } // Функция получения индекса внутри родителя const indexInParent = (parent, element) => { const array = Array.prototype.slice.call(parent.children) return Array.prototype.indexOf.call(array, element) } // Обработа медиа запросов из атрибутов function dataMediaQueries(array, dataSetValue) { // Получение объектов с медиа запросами const media = Array.from(array).filter(function (item, index, self) { if (item.dataset[dataSetValue]) { return item.dataset[dataSetValue].split(",")[0]; } }); // Инициализация объектов с медиа запросами if (media.length) { const breakpointsArray = []; media.forEach(item => { const params = item.dataset[dataSetValue]; const breakpoint = {}; const paramsArray = params.split(","); breakpoint.value = paramsArray[0]; breakpoint.type = paramsArray[1] ? paramsArray[1].trim() : "max"; breakpoint.item = item; breakpointsArray.push(breakpoint); }); // Получаем уникальные брейкпоинты let mdQueries = breakpointsArray.map(function (item) { return '(' + item.type + "-width: " + item.value + "px)," + item.value + ',' + item.type; }); mdQueries = uniqArray(mdQueries); const mdQueriesArray = []; if (mdQueries.length) { // Работаем с каждым брейкпоинтом mdQueries.forEach(breakpoint => { const paramsArray = breakpoint.split(","); const mediaBreakpoint = paramsArray[1]; const mediaType = paramsArray[2]; const matchMedia = window.matchMedia(paramsArray[0]); // Объекты с нужными условиями const itemsArray = breakpointsArray.filter(function (item) { if (item.value === mediaBreakpoint && item.type === mediaType) { return true; } }); mdQueriesArray.push({ itemsArray, matchMedia }) }); return mdQueriesArray; } } } // ---------- class Popup { constructor(options) { let config = { init: true, // Для кнопок attributeOpenButton: 'data-popup', // Атрибут для кнопки, которая вызывает попап attributeCloseButton: 'data-close', // Атрибут для кнопки, которая закрывает попап // Для сторонних объектов fixElementSelector: '[data-lp]', // Атрибут для элементов с левым паддингом (которые fixed) // Для объекта попапа youtubeAttribute: 'data-popup-youtube', // Атрибут для кода youtube youtubePlaceAttribute: 'data-popup-youtube-place', // Атрибут для вставки ролика youtube setAutoplayYoutube: true, // Изменение классов classes: { popup: 'popup', // popupWrapper: 'popup__wrapper', popupContent: 'popup__content', popupActive: 'popup_show', // Добавляется для попапа, когда он открывается bodyActive: 'popup-show', // Добавляется для боди, когда попап открыт }, focusCatch: true, // Фокус внутри попапа зациклен closeEsc: true, // Закрытие по ESC bodyLock: true, // Блокировка скролла hashSettings: { location: true, // Хэш в адресной строке goHash: true, // Переход по наличию в адресной строке }, on: { // События beforeOpen: function () { }, afterOpen: function () { }, beforeClose: function () { }, afterClose: function () { }, }, } this.youTubeCode; this.isOpen = false; // Текущее окно this.targetOpen = { selector: false, element: false, } // Предыдущее открытое this.previousOpen = { selector: false, element: false, } // Последнее закрытое this.lastClosed = { selector: false, element: false, } this._dataValue = false; this.hash = false; this._reopen = false; this._selectorOpen = false; this.lastFocusEl = false; this._focusEl = [ 'a[href]', 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', 'button:not([disabled]):not([aria-hidden])', 'select:not([disabled]):not([aria-hidden])', 'textarea:not([disabled]):not([aria-hidden])', 'area[href]', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not([tabindex^="-"])' ]; //this.options = Object.assign(config, options); this.options = { ...config, ...options, classes: { ...config.classes, ...options?.classes, }, hashSettings: { ...config.hashSettings, ...options?.hashSettings, }, on: { ...config.on, ...options?.on, } } this.bodyLock = false; this.options.init ? this.initPopups() : null } initPopups() { this.eventsPopup(); } eventsPopup() { // Клик на всем документе document.addEventListener("click", function (e) { // Клик по кнопке "открыть" const buttonOpen = e.target.closest(`[${this.options.attributeOpenButton}]`); if (buttonOpen) { e.preventDefault(); this._dataValue = buttonOpen.getAttribute(this.options.attributeOpenButton) ? buttonOpen.getAttribute(this.options.attributeOpenButton) : 'error'; this.youTubeCode = buttonOpen.getAttribute(this.options.youtubeAttribute) ? buttonOpen.getAttribute(this.options.youtubeAttribute) : null; if (this._dataValue !== 'error') { if (!this.isOpen) this.lastFocusEl = buttonOpen; this.targetOpen.selector = `${this._dataValue}`; this._selectorOpen = true; this.open(); return; } else console.log('error'); return; } // Закрытие на пустом месте (popup__wrapper) и кнопки закрытия (popup__close) для закрытия const buttonClose = e.target.closest(`[${this.options.attributeCloseButton}]`); if (buttonClose || !e.target.closest(`.${this.options.classes.popupContent}`) && this.isOpen) { e.preventDefault(); this.close(); return; } }.bind(this)); // Закрытие по ESC document.addEventListener("keydown", function (e) { if (this.options.closeEsc && e.which == 27 && e.code === 'Escape' && this.isOpen) { e.preventDefault(); this.close(); return; } if (this.options.focusCatch && e.which == 9 && this.isOpen) { this._focusCatch(e); return; } }.bind(this)) // Открытие по хешу if (this.options.hashSettings.goHash) { // Проверка изменения адресной строки window.addEventListener('hashchange', function () { if (window.location.hash) { this._openToHash(); } else { this.close(this.targetOpen.selector); } }.bind(this)) window.addEventListener('load', function () { if (window.location.hash) { this._openToHash(); } }.bind(this)) } } open(selectorValue) { if (bodyLockStatus) { // Если перед открытием попапа был режим lock this.bodyLock = document.documentElement.classList.contains('lock') ? true : false; // Если ввести значение селектора (селектор настраивается в options) if (selectorValue && typeof (selectorValue) === "string" && selectorValue.trim() !== "") { this.targetOpen.selector = selectorValue; this._selectorOpen = true; } if (this.isOpen) { this._reopen = true; this.close(); } if (!this._selectorOpen) this.targetOpen.selector = this.lastClosed.selector; if (!this._reopen) this.previousActiveElement = document.activeElement; this.targetOpen.element = document.querySelector(this.targetOpen.selector); if (this.targetOpen.element) { // YouTube if (this.youTubeCode) { const codeVideo = this.youTubeCode; const urlVideo = `https://www.youtube.com/embed/${codeVideo}?rel=0&showinfo=0&autoplay=1` const iframe = document.createElement('iframe'); iframe.setAttribute('allowfullscreen', ''); const autoplay = this.options.setAutoplayYoutube ? 'autoplay;' : ''; iframe.setAttribute('allow', `${autoplay}; encrypted-media`); iframe.setAttribute('src', urlVideo); if (!this.targetOpen.element.querySelector(`[${this.options.youtubePlaceAttribute}]`)) { const youtubePlace = this.targetOpen.element.querySelector('.popup__text').setAttribute(`${this.options.youtubePlaceAttribute}`, ''); } this.targetOpen.element.querySelector(`[${this.options.youtubePlaceAttribute}]`).appendChild(iframe); } if (this.options.hashSettings.location) { // Получение хэша и его выставление this._getHash(); this._setHash(); } // До открытия this.options.on.beforeOpen(this); // Создаем свое событие после открытия попапа document.dispatchEvent(new CustomEvent("beforePopupOpen", { detail: { popup: this } })); this.targetOpen.element.classList.add(this.options.classes.popupActive); document.documentElement.classList.add(this.options.classes.bodyActive); if (!this._reopen) { !this.bodyLock ? bodyLock() : null; } else this._reopen = false; this.targetOpen.element.setAttribute('aria-hidden', 'false'); // Запоминаю это открытое окно. Оно будет последним открытым this.previousOpen.selector = this.targetOpen.selector; this.previousOpen.element = this.targetOpen.element; this._selectorOpen = false; this.isOpen = true; setTimeout(() => { this._focusTrap(); }, 50); // После открытия this.options.on.afterOpen(this); // Создаем свое событие после открытия попапа document.dispatchEvent(new CustomEvent("afterPopupOpen", { detail: { popup: this } })); } else console.log('error'); } } close(selectorValue) { if (selectorValue && typeof (selectorValue) === "string" && selectorValue.trim() !== "") { this.previousOpen.selector = selectorValue; } if (!this.isOpen || !bodyLockStatus) { return; } // До закрытия this.options.on.beforeClose(this); // Создаем свое событие перед закрытием попапа document.dispatchEvent(new CustomEvent("beforePopupClose", { detail: { popup: this } })); // YouTube if (this.youTubeCode) { if (this.targetOpen.element.querySelector(`[${this.options.youtubePlaceAttribute}]`)) this.targetOpen.element.querySelector(`[${this.options.youtubePlaceAttribute}]`).innerHTML = ''; } this.previousOpen.element.classList.remove(this.options.classes.popupActive); // aria-hidden this.previousOpen.element.setAttribute('aria-hidden', 'true'); if (!this._reopen) { document.documentElement.classList.remove(this.options.classes.bodyActive); !this.bodyLock ? bodyUnlock() : null; this.isOpen = false; } // Очищение адресной строки this._removeHash(); if (this._selectorOpen) { this.lastClosed.selector = this.previousOpen.selector; this.lastClosed.element = this.previousOpen.element; } // После закрытия this.options.on.afterClose(this); // Создаем свое событие после закрытия попапа document.dispatchEvent(new CustomEvent("afterPopupClose", { detail: { popup: this } })); setTimeout(() => { this._focusTrap(); }, 50); } // Получение хэша _getHash() { if (this.options.hashSettings.location) { this.hash = this.targetOpen.selector.includes('#') ? this.targetOpen.selector : this.targetOpen.selector.replace('.', '#') } } _openToHash() { let classInHash = document.querySelector(`.${window.location.hash.replace('#', '')}`) ? `.${window.location.hash.replace('#', '')}` : document.querySelector(`${window.location.hash}`) ? `${window.location.hash}` : null; const buttons = document.querySelector(`[${this.options.attributeOpenButton} = "${classInHash}"]`) ? document.querySelector(`[${this.options.attributeOpenButton} = "${classInHash}"]`) : document.querySelector(`[${this.options.attributeOpenButton} = "${classInHash.replace('.', "#")}"]`); if (buttons && classInHash) this.open(classInHash); } // Утсановка хэша _setHash() { history.pushState('', '', this.hash); } _removeHash() { history.pushState('', '', window.location.href.split('#')[0]) } _focusCatch(e) { const focusable = this.targetOpen.element.querySelectorAll(this._focusEl); const focusArray = Array.prototype.slice.call(focusable); const focusedIndex = focusArray.indexOf(document.activeElement); if (e.shiftKey && focusedIndex === 0) { focusArray[focusArray.length - 1].focus(); e.preventDefault(); } if (!e.shiftKey && focusedIndex === focusArray.length - 1) { focusArray[0].focus(); e.preventDefault(); } } _focusTrap() { const focusable = this.previousOpen.element.querySelectorAll(this._focusEl); if (!this.isOpen && this.lastFocusEl) { this.lastFocusEl.focus(); } else { focusable[0].focus(); } } } // Запускаем и добавляем в объект модулей flsModules.popup = new Popup({}); // -------------- const cookieBanner = document.querySelector(".cookie-banner"); const cookieBannerAcceptBtn = document.querySelector("#banner-accept"); const cookieBannerRejectBtn = document.querySelector("#banner-reject"); const cookieBannerSettingBtn = document.querySelector("#banner-setting"); const cookieWidget = document.querySelector(".cookie-widget-container"); const cookieWidgetBtn = document.querySelector("#cookie-widget"); const cookiePopupAcceptBtn = document.querySelector("#popup-accept"); const cookiePopupRejectBtn = document.querySelector("#popup-reject"); const cookiePopupUserChoicesBtn = document.querySelector("#popup-user-choices"); const cookiePopupCloseBtn = document.querySelector("#popup-cookie-close-btn"); const cookiePopupPerformanceBtn = document.querySelector( "#popup-performance-cookie-toggle" ); const cookiePopupTargetingBtn = document.querySelector( "#popup-targeting-cookie-toggle" ); const cookiePopupFunctionalBtn = document.querySelector( "#popup-functional-cookie-toggle" ); const getVisitedCookie = () => { return getCookie("visited"); }; const setVisitedCookie = () => { setCookie("visited", "true", { secure: true, "max-age": cookieMaxAge }); }; const acceptAllCookies = () => { setCookie("performanceCookie", "true", { secure: true, "max-age": cookieMaxAge }); setCookie("targetingCookie", "true", { secure: true, "max-age": cookieMaxAge }); setCookie("functionalCookie", "true", { secure: true, "max-age": cookieMaxAge }); cookiePopupPerformanceBtn.checked = true; cookiePopupTargetingBtn.checked = true; cookiePopupFunctionalBtn.checked = true; }; const rejectAllCookies = () => { deleteCookie("performanceCookie"); deleteCookie("targetingCookie"); deleteCookie("functionalCookie"); cookiePopupPerformanceBtn.checked = false; cookiePopupTargetingBtn.checked = false; cookiePopupFunctionalBtn.checked = false; }; const hideBannerAndShowWidget = () => { cookieBanner.classList.add("cookie-hidden"); cookieWidget.classList.remove("cookie-hidden"); }; const setPopupToggleBtnsStateFromCookie = () => { const performanceCookie = getCookie("performanceCookie"); const targetingCookie = getCookie("targetingCookie"); const functionalCookie = getCookie("functionalCookie"); if (performanceCookie) { cookiePopupPerformanceBtn.checked = true; } else { cookiePopupPerformanceBtn.checked = false; } if (targetingCookie) { cookiePopupTargetingBtn.checked = true; } else { cookiePopupTargetingBtn.checked = false; } if (functionalCookie) { cookiePopupFunctionalBtn.checked = true; } else { cookiePopupFunctionalBtn.checked = false; } }; // banner const onClickBannerAcceptBtn = (e) => { e.preventDefault(); setVisitedCookie(); acceptAllCookies(); hideBannerAndShowWidget(); }; const onClickBannerRejectBtn = (e) => { e.preventDefault(); setVisitedCookie(); rejectAllCookies(); hideBannerAndShowWidget(); }; const onClickBannerSettingBtn = (e) => { e.preventDefault(); flsModules.popup.open("#cookie"); }; // widget const onClickWidgetBtn = (e) => { e.preventDefault(); flsModules.popup.open("#cookie"); }; // popup const onClickPopupAcceptBtn = (e) => { e.preventDefault(); acceptAllCookies(); flsModules.popup.close("#cookie"); if (!cookieBanner.classList.contains("cookie-hidden")) { hideBannerAndShowWidget(); setVisitedCookie(); } }; const onClickPopupRejectBtn = (e) => { e.preventDefault(); rejectAllCookies(); flsModules.popup.close("#cookie"); if (!cookieBanner.classList.contains("cookie-hidden")) { hideBannerAndShowWidget(); setVisitedCookie(); } }; const onClickPopupConfirmUserBtn = (e) => { e.preventDefault(); flsModules.popup.close("#cookie"); if (cookiePopupPerformanceBtn.checked === true) { setCookie("performanceCookie", "true", { secure: true, "max-age": cookieMaxAge }); } else { deleteCookie("performanceCookie"); } if (cookiePopupTargetingBtn.checked === true) { setCookie("targetingCookie", "true", { secure: true, "max-age": cookieMaxAge }); } else { deleteCookie("targetingCookie"); } if (cookiePopupFunctionalBtn.checked === true) { setCookie("functionalCookie", "true", { secure: true, "max-age": cookieMaxAge }); } else { deleteCookie("functionalCookie"); } if (!cookieBanner.classList.contains("cookie-hidden")) { hideBannerAndShowWidget(); setVisitedCookie(); } }; const init = () => { cookieBannerAcceptBtn.addEventListener("click", onClickBannerAcceptBtn); cookieBannerRejectBtn.addEventListener("click", onClickBannerRejectBtn); cookieBannerSettingBtn.addEventListener("click", onClickBannerSettingBtn); cookieWidgetBtn.addEventListener("click", onClickWidgetBtn); cookiePopupAcceptBtn.addEventListener("click", onClickPopupAcceptBtn); cookiePopupRejectBtn.addEventListener("click", onClickPopupRejectBtn); cookiePopupUserChoicesBtn.addEventListener( "click", onClickPopupConfirmUserBtn ); if (getVisitedCookie()) { cookieWidget.classList.remove("cookie-hidden"); setPopupToggleBtnsStateFromCookie(); } else { cookiePopupCloseBtn.addEventListener("click", onClickPopupRejectBtn); cookieBanner.classList.remove("cookie-hidden"); setTimeout(() => { cookieBanner.classList.add("cookie-banner-show"); }, 50); } }; init();