PageSourceSearch

https://torgplit.com/design/main.js

js torgplit.com collected 2026-10-06 18:54:11 UTC 171,331 bytes, 4,363 lines download raw bytes

1console.time('loading time')
2///////////////////////////////////////
3/// Общие функции ///
4///////////////////////////////////////
5
6// Установка cookie файлов на основном домене магазина, в случае если покупатель зашёл на другой домен магазина, например по старой ссылке
7try {
8  $(document).ajaxSuccess(function (i, l, h) {
9    try {
10      if (-1 < h.data.indexOf('ajax_q=1')) {
11        var d = $.parseJSON(l.responseText)
12        if (null != d && typeof d.setcookie == 'object') {
13          for (var g in d.setcookie) {
14            var c = document.createElement('script')
15            c.type = 'text/javascript'
16            c.async = typeof d.status == 'undefined' || d.status == 'reload' ? true : false
17            c.src = d.setcookie[g]
18            var f = document.getElementsByTagName('script')[0]
19            f.parentNode.insertBefore(c, f)
20          }
21        }
22      }
23    } catch (j) {}
24  })
25} catch (e) {}
26// Установка cookie файлов на основном домене магазина, в случае если покупатель зашёл на другой домен магазина, например по старой ссылке
27try {
28  $(document).ajaxSuccess(function (i, l, h) {
29    try {
30      if (typeof h.data != 'undefined' && -1 < h.data.indexOf('ajax_q=1')) {
31        var d = $.parseJSON(l.responseText)
32        if (null != d && typeof d.setcookie == 'object') {
33          for (var g in d.setcookie) {
34            var c = document.createElement('script')
35            c.type = 'text/javascript'
36            c.async = typeof d.status == 'undefined' || d.status == 'reload' ? true : false
37            c.src = d.setcookie[g]
38            var f = document.getElementsByTagName('script')[0]
39            f.parentNode.insertBefore(c, f)
40          }
41        }
42      }
43    } catch (j) {}
44  })
45} catch (e) {}
46
47///////////////////////////////////////
48// Отправляет ошибку на сервер, для того чтобы служба тех поддержки могла разобраться в проблеме как можно быстрее.
49///////////////////////////////////////
50function sendError(desc, page, line) {
51  let img = document.createElement('img')
52  img.src = 'https://storeland.ru/error/js?desc=' + encodeURIComponent(desc) + '&page=' + encodeURIComponent(window.location) + '&line=0'
53  img.style.position = 'absolute'
54  img.style.top = '-9999px'
55  try {
56    document.getElementsByTagName('head').appendChild(img)
57  } catch (e) {}
58  return false
59}
60
61///////////////////////////////////////
62// Функция определения ширины экрана пользователя. /JS/
63///////////////////////////////////////
64function getClientWidth() {
65  return document.compatMode == 'CSS1Compat' && !window.opera ? document.documentElement.clientWidth : document.body.clientWidth
66}
67
68///////////////////////////////////////
69// Функция определения браузера. /JS/
70///////////////////////////////////////
71function userAgent() {
72  const agent = detect.parse(navigator.userAgent).browser.family.replace(/\s+/g, '-')
73  document.body.classList.add(agent)
74}
75
76///////////////////////////////////////
77// Добавляет пробел 1000 -> 1 000  /  10000 -> 10 000. /JS/JEST/
78///////////////////////////////////////
79function addSpaces(str) {
80  return String(str).replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, '$1 ')
81}
82
83///////////////////////////////////////
84// Считает сумму  33 599,65 + 2000 - 1910-41,6
85///////////////////////////////////////
86function GetSum(val, precision) {
87  if (typeof precision == 'undefined' || precision < 0) {
88    precision = 0
89  }
90  // Возводим в степень точности 10 для округления
91  var p = Math.pow(10, precision)
92  try {
93    return Math.round(parseFloat(eval(val.toString().replace(/\s/gi, '').replace(/,/gi, '.'))) * p) / p
94  } catch (e) {
95    return 0
96  }
97}
98
99///////////////////////////////////////
100// Возвращает правильное окончание для слова
101///////////////////////////////////////
102function genWordEnd(num, e = '', m = 'а', mm = 'oв') {
103  // Если передали пустую строку, вместо цифры
104  if (0 == num.length) {
105    num = 0
106  }
107  // Превращаем цифру в правильный INT
108  num = GetSum(num).toString()
109  // Получаем последний символ цифры
110  ch1 = num.substring(num.length - 1)
111  // Получаем последний символ цифры
112  ch2 = num.length == 1 ? 0 : num.substring(num.length - 2, num.length - 1)
113  // Если последняя цифра - 1, вернем единственное число
114  if (ch2 != 1 && ch1 == 1) {
115    return e
116  }
117  // Если последняя цифра - от 2 до 4х , вернем множественное чило из массива с индексом 2
118  else if (ch2 != 1 && ch1 > 1 && ch1 <= 4) {
119    return m
120  }
121  // Если последняя цифра - от 5 до 0 , вернем множественное чило из массива с индексом 3
122  else if (ch2 == 1 || ch1 > 4 || ch1 == 0) {
123    return mm
124  }
125}
126
127///////////////////////////////////////
128// Ленивая загрузка. /JS/
129///////////////////////////////////////
130function lazyload() {
131  lozad().observe()
132}
133
134///////////////////////////////////////
135// Уведомления
136///////////////////////////////////////
137function notyStart(content, type) {
138  new Noty({
139    text: content,
140    layout: 'topRight',
141    type: type,
142    theme: '',
143    textAlign: 'center',
144    animation: {
145      open: 'animated fadeInDown',
146      close: 'animated fadeOutUp',
147      easing: 'swing',
148      speed: 400,
149    },
150    timeout: '2000',
151    progressBar: true,
152    closable: true,
153    closeOnSelfClick: true,
154    modal: false,
155    dismissQueue: false,
156    onClose: true,
157    killer: false,
158  }).show()
159}
160
161///////////////////////////////////////
162// Функция Наверх. /JS/
163///////////////////////////////////////
164function toTop() {
165  const goto = document.querySelector('.toTop')
166  // Показать при скроле
167  window.addEventListener('scroll', function () {
168    window.pageYOffset > 99 ? goto.classList.remove('is-hidden') : goto.classList.add('is-hidden')
169  })
170
171  // Действие наверх
172  goto.addEventListener('click', () => scrollTop(0))
173}
174
175///////////////////////////////////////
176// Изменение текста в объкте. /JS/JEST/
177///////////////////////////////////////
178function changeTxt(object) {
179  const slot = object.querySelector('[slot]')
180  if (!slot) return false
181  const hideText = slot.getAttribute('slot')
182  const showText = slot.textContent.trim()
183  // Обновляем данные
184  slot.textContent = hideText
185  slot.setAttribute('slot', showText)
186}
187
188///////////////////////////////////////
189// Функция Показать все/Скрыть.
190// TODO добавить функцию проверки элементов на экране при адаптивности
191///////////////////////////////////////
192class VisibleItems {
193  constructor(selector) {
194    this.selector = document.querySelector(selector)
195    // Проверяем наличие объекта
196    if (!this.selector) return false
197    this.button = this.selector.querySelector('.visible__button')
198    this.items = this.selector.querySelectorAll('.visible__item')
199    this.itemsVisible = '.visible__item:visible'
200    // Проверяем видимость кнопки
201    this.checkVisible()
202    // Запуск функций при нажатии
203    this.onClickVisible()
204  }
205
206  // Получаем кол-во элементов
207  getItemsLength() {
208    return this.items.length
209  }
210
211  // Получаем кол-во видимых элементов
212  getItemsVisibleLength() {
213    return $(this.selector).find(this.itemsVisible).length
214  }
215
216  // Скрыть/показать кнопку
217  checkVisible() {
218    this.button.parentElement.style.display = this.getItemsLength() > this.getItemsVisibleLength() ? 'block' : 'none'
219  }
220
221  // Функции при клике
222  onClickVisible() {
223    const content = this.selector
224    this.button.addEventListener('click', function (event) {
225      event.preventDefault()
226      changeTxt(this)
227      isActived(this)
228      isActived(content)
229      setTimeout(() => {
230        scrollTop(this.matches('.is-actived') ? true : content.offsetTop)
231      }, 100)
232    })
233  }
234}
235
236// Переход к контенту сверху
237function scrollTop(offsetTop) {
238  // console.log('offsetTop', offsetTop);
239  if (offsetTop == true) return false
240  window.scrollTo({
241    top: offsetTop,
242    left: 0,
243    behavior: 'smooth',
244  })
245}
246
247// Сообщение пользователю
248function notyMessage(message) {
249  return `
250		<div class="noty__addto flex">
251			<div class="noty__title">Внимание!</div>
252			<div class="noty__content">${message}</div>
253		</div>
254	`
255}
256
257// Функция проверки активного класса у объекта
258function isActived(object, active = 'is-actived') {
259  object.matches('.' + active) ? object.classList.remove(active) : object.classList.add(active)
260}
261
262// Функция обновления количества
263function updateCount(selector, count) {
264  document.querySelectorAll(selector).forEach((element) => {
265    element.setAttribute('data-count', count)
266    element.textContent = count
267  })
268}
269
270///////////////////////////////////////
271// Конструктор функции пароля. /JS/
272///////////////////////////////////////
273class Password {
274  constructor(){
275    this.passForm = $('.form__client')
276    // Если нет формы с паролем
277    if (!this.passForm) return false
278
279    // Показать пароль
280    this.showPassword(this.passForm)
281    // Предупреждение Капслока
282    this.showCapsWarning(this.passForm)
283    // Регистрация
284    this.onRegistration(this.passForm)
285  }
286
287  // Показать пароль 
288  showPassword(passForm) {
289    const button = passForm.find('.password__button')
290    
291    // Действия при клике
292    button.off('click').on('click', function(event){
293      const input = event.target.previousElementSibling
294      // Если не ввели пароль
295      if (input.value.length < 1) return false
296  
297      // Добавляем активный класс
298      event.target.classList.toggle('is-actived')
299  
300      // Изменяем тип input поля с password на text
301      input.type == 'text' ? (input.type = 'password') : (input.type = 'text')
302    })
303  }
304
305  // Определения capslock в поле пароля. /JS/
306  showCapsWarning(passForm) {
307    const input = passForm.find('.password__input')
308    // Действие при вводе в поле
309    input.on('keyup', function() {
310      password.checkCapsWarning()
311    })
312  }
313
314  // Показать ошибку. /JS/
315  checkCapsWarning() {
316    const status = event.getModifierState && event.getModifierState('CapsLock')
317    const caps = $(event.target.closest('form')).find('.capslock')
318    status ? caps.show() : caps.hide()
319  }
320
321  // Скрыть ошибку. /JS/
322  removeCapsWarning(caps) {
323    caps.hide()
324  }
325
326  // Регистрация. /JS/
327  onRegistration(passForm) {
328    const registration = passForm.find('.form__registration input')
329    if (!registration) return false
330
331    registration.on('click', (event) => {
332      const form = event.target.closest('form')
333      const input = form.querySelector('.password__input')
334      const email = form.querySelector('.form__email')
335      if (event.target.checked) {
336        event.target.checked = true
337        email.setAttribute('required', true)
338        email.classList.add('required')
339        email.previousElementSibling.classList.add('required')
340        input.parentElement.style.display = ''
341      } else {
342        event.target.checked = false
343        email.setAttribute('required', false)
344        email.classList.remove('required')
345        email.previousElementSibling.classList.remove('required')
346        input.parentElement.style.display = 'none'
347      }
348    })
349  }
350
351}
352
353
354///////////////////////////////////////
355// Конструктор функции Сравнения товаров
356///////////////////////////////////////
357class Compare {
358  constructor() {
359    // console.log('Compare start')
360  }
361
362  // Функции при клике
363  onClick() {
364    // console.log('Compare onClick')
365    const element = document.querySelector('.compare__table')
366    // Если нет контента
367    if (!element) {
368      return false
369    }
370
371    // Действия
372    element.addEventListener('click', function (event) {
373      // Объявление переменных
374      const btnSwitch = event.target.closest('.compare__switch')
375      const btnSelected = event.target.closest('.compare__selected')
376      const btnInput = event.target.closest('.compare__input')
377      const btnShow = event.target.closest('.compare__showAll')
378
379      // Сравнение товаров. Отображение всех и различающихся характеристик товара
380      if (btnSwitch) {
381        compare.onSwitch(btnSwitch)
382      }
383      // Скрытие характеристик товара, которые выделил пользователь
384      else if (btnSelected) {
385        compare.onSelected()
386      }
387      // Отображение скрытых характеристик товара
388      else if (btnShow) {
389        compare.onShow(btnShow)
390      }
391      // Отобразить скрытые при клике
392      else if (btnInput) {
393        document.querySelector('.compare__selected').classList.remove('is-hide')
394      }
395      // Выход из функции
396      else {
397        return false
398      }
399    })
400  }
401
402  // Скрытие характеристик товара, которые выделил пользователь
403  onSelected() {
404    // console.log('Compare onSelected')
405    document.querySelector('.compare__showAll').classList.remove('is-hide')
406    document.querySelectorAll('.compare__line').forEach((el) => {
407      const сhecked = el.querySelector('.compare__input:checked')
408      if (сhecked) {
409        el.style.display = 'none'
410      }
411    })
412  }
413
414  // Показать отличия
415  onSwitch(obj) {
416    // console.log('Compare onSwitch')
417    // Обновляем текст
418    changeTxt(obj)
419    if (obj.matches('.switch-on')) {
420      obj.classList.remove('switch-on')
421      document.querySelector('.compare__showAll').classList.add('is-hide')
422      document.querySelectorAll('.compare__line').forEach((el) => (el.style.display = ''))
423    } else {
424      obj.classList.add('switch-on')
425      document.querySelector('.compare__showAll').classList.remove('is-hide')
426      document.querySelectorAll('.compare__line').forEach((element) => (element.style.display = element.matches('.is-same') ? 'none' : ''))
427    }
428  }
429
430  // Отображение скрытых характеристик товара
431  onShow(obj) {
432    obj.classList.add('is-hide')
433    document.querySelector('.compare__switch').classList.remove('switch-on')
434    document.querySelectorAll('.compare__line').forEach((el) => (el.style.display = ''))
435  }
436
437  // Слайдер сравнения
438  onSlider() {
439    // console.log('Compare onSlider')
440    const swiper = new Swiper('.compare__line', {
441      loop: false,
442      allowTouchMove: false,
443      autoplay: false,
444      autoHeight: false,
445      draggable: false,
446      freeMode: false,
447      grabCursor: false,
448      simulateTouch: false,
449      slidesPerView: 4,
450      spaceBetween: 16,
451      watchSlidesProgress: true,
452      lazy: {
453        enabled: false,
454        loadPrevNext: true,
455        loadOnTransitionStart: true,
456      },
457      navigation: {
458        nextEl: '.swiper-button-next',
459        prevEl: '.swiper-button-prev',
460      },
461      breakpoints: {
462        0: {
463          slidesPerView: '1',
464        },
465        320: {
466          slidesPerView: '2',
467        },
468        480: {
469          slidesPerView: '2',
470        },
471        640: {
472          slidesPerView: '3',
473        },
474        768: {
475          slidesPerView: '3',
476        },
477        1024: {
478          slidesPerView: '4',
479        },
480        1200: {
481          slidesPerView: '4',
482        },
483      },
484    })
485  }
486}
487
488// Проверка вводимых значений в количестве товара
489function keyPress(oToCheckField, oKeyEvent) {
490  return oKeyEvent.charCode === 0 || /\d/.test(String.fromCharCode(oKeyEvent.charCode))
491}
492
493///////////////////////////////////////
494/*** Кол-во ***/
495///////////////////////////////////////
496class Quantity {
497  init(doc = document) {
498    // console.log('doc', doc);
499    if (!doc) {
500      doc = document
501    }
502
503    const qtys = doc.querySelectorAll('.qty')
504
505    qtys.forEach((e) => {
506      const minus = e.querySelector('.qty__select-minus')
507      const plus = e.querySelector('.qty__select-plus')
508      const input = e.querySelector('.qty__input')
509
510      // Если карточка товара
511      const prodView = e.closest('.productViewBlock')
512
513      // Если корзина
514      const prodCart = e.closest('.page-cart__inner')
515
516      // Если выпадающая корзина
517      const prodAddto = e.closest('.addto__cart')
518
519      // Если выпадающая корзина
520      const prodItem = e.closest('.product__item')
521
522      // Минус
523      minus.addEventListener('click', () => quantity.getMinus(input))
524
525      // Плюс
526      plus.addEventListener('click', () => quantity.getPlus(input))
527
528      // Изменение
529      input.addEventListener('input', function () {
530        quantity.getCheck(input)
531
532        // Если карточка товара
533        if (prodView) {
534          // console.log('prodView', prodView);
535          quantity.updView(prodView)
536        }
537
538        // Если корзина
539        if (prodCart) {
540          console.log('prodCart', prodCart);
541          quantity.updCart(input)
542          cart.minSum()
543        }
544
545        // Если выпадающая корзина
546        if (prodAddto) {
547          // console.log('prodAddto', prodAddto);
548          quantity.updAddto(input)
549        }
550
551        // Если товар
552        if (prodItem) {
553          // console.log('prodItem', prodItem);
554          quantity.updAddtoValue(input)
555        }
556      })
557    })
558
559    this.updAddtoSale()
560  }
561
562  // Функция Плюс + для товара. //JS
563  getPlus(obj) {
564    console.log('plus obj', obj)
565    obj.value = parseInt(obj.value) + 1
566    obj.setAttribute('value', obj.value)
567    obj.dispatchEvent(new Event('input'))
568    return false
569  }
570
571  // Функция Минус - для товара. //JS
572  getMinus(obj) {
573    console.log('minus obj', obj)
574    obj.value = parseInt(obj.value) - 1
575    obj.setAttribute('value', obj.value)
576    obj.dispatchEvent(new Event('input'))
577    return false
578  }
579
580  // Проверка кол-ва. //JS
581  getCheck(obj) {
582    // console.log('check obj', obj);
583    // Количество
584    let val = parseInt(obj.value)
585    const minus = obj.parentElement.querySelector('.qty__select-minus')
586    const plus = obj.parentElement.querySelector('.qty__select-plus')
587    // Проверка максимальныго остатка
588    const max = parseInt(obj.getAttribute('max'))
589
590    // Если вводят 0 то заменяем на 1
591    if (!val || val < 1) {
592      obj.value = val = 1
593      minus.classList.add('qty__disable')
594      return false
595    } else {
596      minus.classList.remove('qty__disable')
597    }
598
599    // Если можно добавить больше чем в наличии
600    if (!max) return false
601
602    // Если товара нет в наличии
603    if (max == 0) {
604      obj.value = 1
605      minus.classList.add('qty__disable')
606      plus.classList.add('qty__disable')
607      // Сообщение пользователю
608      const message = notyMessage('Вы пытаетесь положить в корзин
608у товар которого нет в наличии')
609      notyStart(message, 'warning')
610      return false
611    }
612
613    // Если добавляют больше
614    if (val > max) {
615      obj.value = val = max
616      plus.classList.add('qty__disable')
617      // Сообщение пользователю
618      const message = notyMessage('Вы пытаетесь положить в корзину товара больше, чем есть в наличии')
619      notyStart(message, 'warning')
620      return false
621    } else {
622      plus.classList.remove('qty__disable')
623    }
624  }
625
626  // Обновление цены в карточке товара
627  updView(obj) {
628    // Обновление цены
629    const priceNow = obj.querySelector('.price__now')
630    const priceData = priceNow.getAttribute('data-price')
631    const val = obj.querySelector('.qty__input').value
632    const multiNow = parseInt(val * priceData)
633    priceNow.querySelector('.num').innerHTML = addSpaces(multiNow)
634
635    // Обновление старой цены
636    const priceOld = obj.querySelector('.price__old')
637if (priceOld && parseFloat(priceOld.getAttribute('data-price')) > 0) {
638  const priceData = parseFloat(priceOld.getAttribute('data-price'))
639  const multiOld = parseInt(val * priceData, 10)
640  priceOld.querySelector('.num').innerHTML = addSpaces(multiOld)
641  console.log('goods priceOld', priceData)
642  console.log('goods multiOld', multiOld)
643}
644
645  }
646
647  // Корзина
648  updCart(obj) {
649    console.log('updCart', obj);
650    const item = obj.closest('.cartTable__item')
651    const mod = item.getAttribute('data-mod-id')
652    const priceNow = item.querySelector('.cartTable__price')
653    const form = item.closest('.cartTable__form')
654    const data = new FormData(form)
655    data.append('only_body', '1')
656
657    // Обновить корзину
658    setTimeout(() => {
659      quantity.getCart(data, mod, priceNow)
660    }, 10)
661  }
662
663  // Обновление в корзине
664  async getCart(data, mod, priceNow) {
665    await fetch('/cart', {
666      method: 'POST',
667      body: data,
668    })
669      // Получаем ответ
670      .then((response) => response.text())
671      // Преобразуем в html
672      .then((html) => {
673        // Convert the HTML string into a document object
674        const parser = new DOMParser()
675        const doc = parser.parseFromString(html, 'text/html')
676        quantity.updCartContent(doc, mod, priceNow)
677        // console.log('getCart doc', doc);
678      })
679      .catch((error) => console.error(error))
680  }
681
682  // Обновление в корзине
683  updCartContent(doc, mod, priceNow) {
684    console.log('doc2', doc);
685    // console.log('mod2', mod);
686    // console.log('priceNow2', priceNow);
687    // Обновить цену
688    const price = doc.querySelector('.cartTable__item[data-mod-id="' + mod + '"] .cartTable__price')
689    priceNow.innerHTML = price.innerHTML
690    // Обновить кол-во
691    // const newCount = doc.querySelector('.cart-count').innerHTML
692    // console.log('newCount', newCount);
693    // quantityAddto.updAddtoCount(newCount);
694    // Обновить итого
695    const newTotal = doc.querySelector('.cartTotal').innerHTML
696    const allTotal = document.querySelectorAll('.cartTotal')
697    allTotal.forEach(total => total.innerHTML = newTotal)
698    // console.log('newTotal', newTotal);
699    // console.log('allTotal', allTotal);
700    // Обновить минимальную сумму заказа
701    // $('.order-delivery__title.is-actived input').click()
702    $('.order-delivery__radio:checked').trigger('click')
703    cart.minSum()
704    cart.orderStart()
705    console.log($('.order-delivery__title.is-actived input'));
706    // console.log('price', price);
707    // console.log('newCount', newCount);
708    // console.log('newTotal', newTotal);
709    // console.log('newLabel', newLabel);
710  }
711
712  // Выпадающая корзина
713  updAddto(obj) {
714    // console.log('updAddto obj', obj);
715    const item = obj.closest('.addto__item')
716    const mod = item.getAttribute('data-mod-id')
717    const price = item.querySelector('.addto__price')
718    const form = item.closest('.addto__form')
719    const data = new FormData(form)
720    data.append('only_body', '1')
721
722    // Обновить корзину
723    setTimeout(() => {
724      quantity.getAddtoCart(data, mod, price)
725    }, 100)
726  }
727
728  // Обновление выпадающей корзины
729  async getAddtoCart(data, mod, price) {
730    // console.log('getAddtoCart0',data, mod, price);
731    await fetch('/cart', {
732      method: 'POST',
733      body: data,
734    })
735      // Получаем ответ
736      .then((response) => response.text())
737      // Преобразуем в html
738      .then((html) => {
739        // Convert the HTML string into a document object
740        const parser = new DOMParser()
741        const doc = parser.parseFromString(html, 'text/html')
742        quantity.updAddtoContent(doc, mod, price)
743      })
744      .catch((error) => console.error(error))
745  }
746
747  // Обновление выпадающей корзины
748  updAddtoContent(doc, mod, price) {
749    // Обновить цену
750    const cartPrice = doc.querySelector('.cartTable__item[data-mod-id="' + mod + '"] .cartTable__price')
751    price.innerHTML = cartPrice.innerHTML
752    // Обновить кол-во
753    const newCount = doc.querySelector('.cart-count').innerHTML
754    updateCount('.cart-count', newCount)
755    // Обновить сумму
756    const newSum = doc.querySelector('.cartSumTotal').innerHTML
757    quantity.updCartSum(newSum)
758    // console.log('updAddto newSum', newSum);
759    // Обновить скидку
760    const newDiscount = doc.querySelector('.cartTotal__item-discount')
761    const container = document.querySelector('.sideblock__item.cart')
762    const totalDiscount = container.querySelector('.addto__total-discount')
763    const addtoCart = container.querySelector('.addto__cart')
764    const createDiscount = document.createElement('div')
765    createDiscount.className = 'addto__total-block addto__total-discount flex'
766    if (newDiscount) {
767      console.log('updAddto newDiscount1', newDiscount);
768      addtoCart.classList.add('has-discount')
769      if (totalDiscount){
770        quantity.updAddtoDiscount(newDiscount.innerHTML)
771        totalDiscount.classList.remove('is-hide')
772        quantity.updAddtoSale()
773      } else {
774        createDiscount.innerHTML = newDiscount.innerHTML
775        container.querySelector('.addto__total').prepend(createDiscount)
776        console.log('updAddto createDiscount', createDiscount);
777      }
778    } else {
779      addtoCart.classList.remove('has-discount')
780      totalDiscount?.classList.add('is-hide')
781      console.log('updAddto newDiscount2', newDiscount);
782      console.log('updAddto totalDiscount2', totalDiscount);
783    }
784  }
785
786  // Обновление итоговой цены
787  updCartSum(sum) {
788    const elements = document.querySelectorAll('.cartSumDiscount')
789    elements.forEach((element) => (element.innerHTML = sum))
790  }
791
792  // Обновление итоговой скидки
793  updAddtoDiscount(discount) {
794    const element = document.querySelector('.addto__total-discount')
795    element.innerHTML = discount
796    // console.log('discount', discount);
797    // console.log('element', element);
798  }
799
800  // Отображаем скидку
801  updAddtoSale() {
802    const cart = document.querySelector('.addto__cart')
803    const total = cart?.querySelector('.addto__total-discount')
804    // console.info('info', total);
805    if (total === null) {
806      cart?.classList.remove('has-discount')
807    } else {
808      cart?.classList.add('has-discount')
809    }
810  }
811
812  // Обновить кол-во выпадающей корзины
813  updAddtoValue(obj) {
814    const val = obj.value
815    const id = obj.closest('[data-id]').getAttribute('data-id')
816    const mod = obj.getAttribute('name')
817    const item = document.querySelector('.addto__item[data-id="' + id + '"] .qty__input[name="' + mod + '"]')
818
819    if (!item) return false;
820    item.value = val
821    item.dispatchEvent(new Event('input'))
822  }
823}
824
825///////////////////////////////////////
826// Конструктор функции Товара
827///////////////////////////////////////
828class Product {
829  constructor() {
830    this.init = function () {
831      $('.product__item').each(function () {
832        // product.hoverImage($(this))
833        product.priceDiff(this, 'money')
834        product.attrVisibility(this)
835      })
836    }
837
838    // Запуск функции активного класса товара в других категориях. /JS/
839    this.inCartAll = function (id, mod) {
840      const items = document.querySelectorAll('.product__item[data-id="' + id + '"]')
841      items.forEach((e) => {
842        const qty = e.querySelector('.qty__input')
843        const count = e.querySelector('.inCart__count')
844        const addtoItem = document.querySelector('.addto__item[data-id="' + id + '"] .qty__input[name="' + mod + '"]')
845        // console.log('qty1', qty);
846        // console.log('count1', count);
847        // console.log('addtoItem1', addtoItem);
848        // console.log('addtoItem2', addtoItem.value);
849        // Обновление данных
850        e.classList.add('product__inCart')
851        qty.value = addtoItem.value
852        count.textContent = addtoItem.value
853      })
854    }
855
856    // Разница цены в процентах. /JS/
857    this.priceDiff = function (obj, type = 'percent') {
858      // console.log('obj', obj);
859      // если есть старая цена
860      const sales = obj.querySelector('.sticker__sales')
861      const now = obj.querySelector('.price__now')
862      const old = obj.querySelector('.price__old')
863      const sale = obj.querySelector('.product__discount-link')
864      if (old) {
865        const priceOld = parseInt(old.getAttribute('data-price'))
866        const priceNow = parseInt(now.getAttribute('data-price'))
867        let diff = 0
868        if (type == 'percent') {
869          const diffPercent = (((priceOld - priceNow) / priceOld) * 100).toFixed()
870          if (sales) {
871            sales.innerHTML = `-${diffPercent}%`
872          }
873        } else {
874          diff = (priceOld - priceNow).toFixed()
875          if (sales) {
876            sales.innerHTML = 'цена снижена'
877          }
878          if (sale) {
879            sale.parentElement.style.display = ''
880            sale.innerHTML = `<span class="RUB"><span class="num">цена снижена на ${addSpaces(diff)}</span></span>`
881          }
882        }
883      } else {
884        if (sales) sales.style.display = 'none'
885        if (sale) sale.parentElement.style.display = 'none'
886        return false
887      }
888    }
889
890    // Смена изображений при наведении
891    this.hoverImage = function (obj) {
892      const imagesLen = obj.find('.product__imgID').length
893      // если больше 2 изображений товара
894      if (imagesLen > 2) {
895        // Создаем элементы при наведении на которые будут меняться изображения
896        obj.find('.product__imgID').each(function () {
897          const image = $(this).attr('data-image')
898          const id = $(this).attr('data-id')
899          // Создаем элементы
900          obj.find('.product__image-hover').append('<div class="product__hoverImage" data-image="' + image + '" data-id="' + id + '"></div>')
901          // Добавляем активный класс на элемент навигации
902          if (id == obj.find('.product__img').data('id')) {
903            obj.find('.product__hoverImage').removeClass('is-actived')
904            obj.find('.product__hoverImage[data-id="' + id + '"]').addClass('is-actived')
905          }
906        })
907
908        // Ховер эффект изменения изображения
909        obj.find('.product__hoverImage').hover(
910          $.debounce(50, function () {
911            const image = $(this).attr('data-image')
912            const id = $(this).attr('data-id')
913            obj.find('.product__img').attr({
914              image: image,
915              'data-id': id,
916            })
917
918            obj.find('.product__img img').attr('src', image)
919            obj.find('.product__hoverImage').removeClass('is-actived')
920            $(this).addClass('is-actived')
921          }),
922        )
923      } else {
924        return false
925      }
926    }
927
928    // Добавление в Сравнение и Избранное
929    this.addTo = function () {
930      // Определяем заголовок уведомления
931      function checkMessage(str) {
932        return str.slice(str.indexOf('успешно'), str.length)
933      }
934
935      // Создать сообщение действия
936      function createNoty(title, message) {
937        return `
938					<div class="noty__addto flex">
939            <div class="noty__title">${title}</div>
940            <div class="noty__message">${message}</div>
941					</div>
942				`
943      }
944
945      // Создать товара в списке
946      function createItem(pDataid, pUrl, pName, pImg, pDataChar, pDataPrice, pDataPriceOld, delUrl) {
947        function priceOld() {
948          return `<del class="price__old" data-price="${pDataPriceOld}">
949						<span title="${pDataPriceOld} российских рублей">
950							<span class="num">${addSpaces(pDataPriceOld)}</span>
951							<span>р.</span>
952						</span>
953					</del>`
954        }
955
956        return `
957					<div class="addto__item flex" data-id="${pDataid}">
958						<a class="addto__image flex-center" href="${pUrl}" title="${pName}"><img src="${pImg}" alt="${pName}" /></a>
959						<div class="addto__content flex">
960							<a class="addto__name" href="${pUrl}" title="${pName}"><span>${pName}</span></a>
961							<div class="addto__price flex ${pDataChar}">
962                ${pDataPriceOld === 0 ? '' : priceOld()}
963								<span class="price__now" data-price="${pDataPrice}"><span title="${pDataPrice} российских рублей"><span class="num">${addSpaces(pDataPrice)}</span><span>р.</span></span></span>
964							</div>
965							<a class="addto__remove" href="${delUrl}?id=${pDataid}" data-id="${pDataid}" title="Убрать товар из списка"><i class="icon-delete"></i></a>
966						</div>
967					</div>
968				`
969      }
970
971      // Обновление ссылки
972      function updateLink(a, from, to, newIsAddStatus, newTitle) {
973        // Обновляем ссылку, на которую будет уходить запрос и информацию о ней
974        a.attr('href', a.attr('href').replace(new RegExp(from), to))
975          .attr('title', newTitle)
976          .attr('data-action-is-add', newIsAddStatus)
977      }
978
979      // Добавление/удаление товара Сравнение
980      const blockCompare = '.compare'
981      const addCompare = '.add-compare'
982      const addtoComapre = '.addto__compare'
983
984      $(addCompare)
985        .off('click')
986        .on('click', function () {
987          // Объект ссылки, по которой кликнули
988          const a = $(this),
989            isAdd = a.attr('data-action-is-add'),
990            addUrl = a.attr('data-action-add-url'),
991            delUrl = a.attr('data-action-delete-url'),
992            addTitle = a.attr('data-action-add-title'),
993            delTitle = a.attr('data-action-delete-title'),
994            aText = a.find('span'),
995            pName = a.attr('data-prodname'),
996            pUrl = a.attr('data-produrl'),
997            pImg = a.attr('data-prodimg'),
998            pDataid = a.attr('data-id'),
999            pDataPrice = a.attr('data-mod-price'),
1000            pDataPriceOld = a.attr('data-mod-price-old'),
1001            pDataChar = a.attr('data-char-code'),
1002            // addTooltip = a.attr('data-add-tooltip'),
1003            // delTooltip = a.attr('data-del-tooltip'),
1004            // pDataMod = a.attr('data-mod-id'),
1005            // actionUrl = a.attr('data-action-url'),
1006            requestUrl = a.attr('href')
1007
1008          let flag = 0
1009
1010          // Проверка элемента в объекте
1011          $(addtoComapre)
1012            .find('.addto__item')
1013            .each(function () {
1014              if ($(this).attr('data-id') == pDataid) {
1015                flag = 1
1016              }
1017
1018              if (flag == 1) {
1019                $(this).remove()
1020                return false
1021              }
1022
1023              return flag
1024            })
1025
1026          // Если есть информация о том какие URL адреса будут изменены, то можено не перегружать страницу и сделать запрос через ajax
1027          if (addUrl && delUrl) {
1028            $.ajax({
1029              url: requestUrl,
1030              type: 'POST',
1031              dataType: 'json',
1032              cache: false,
1033              data: {
1034                ajax_q: 1,
1035              },
1036              success: function (data) {
1037                if ('ok' == data.status) {
1038                  // Рендер элементов в список
1039                  if (flag == 0) {
1040                    if ($(addtoComapre).length) {
1041                      $(addtoComapre).find('.addto__items').prepend(createItem(pDataid, pUrl, pName, pImg, pDataChar, pDataPrice, pDataPriceOld, delUrl))
1042                    }
1043                  }
1044
1045                  // Если указано, что изменилось число товаров на сравнении
1046                  const countCompare = data.compare_goods_count
1047                  if (typeof countCompare != 'undefined') {
1048                    // Блок информации о том, что есть товары на сравнении
1049                    const sidecount = $(blockCompare).find('[data-count]')
1050                    // Указываем информацию о новом количестве товаров на сравнении
1051                    sidecount.text(countCompare).attr('data-count', countCompare)
1052                    if (countCompare > 0) {
1053                      $(blockCompare).addClass('has-items')
1054                    } else {
1055                      $(blockCompare).removeClass('has-items')
1056                      sidecount.attr('data-count', '0').text('0')
1057                      $(addCompare).removeAttr('title').removeClass('is-added')
1058                    }
1059                  }
1060
1061                  // Проверка статуса добавления товара
1062                  if (isAdd == 1) {
1063                    // Обновляем ссылку, на которую будет уходить запрос и информацию о ней
1064                    updateLink(a, addUrl, delUrl, 0, delTitle ? delTitle : '')
1065                    a.addClass('is-added').parent().addClass('is-added')
1066
1067                    // Обновляем ссылку у всех товаров на странице
1068                    $(addCompare).each(function () {
1069                      if ($(this).attr('data-id') == pDataid) {
1070                        $(this).addClass('is-added')
1071                        updateLink($(this), addUrl, delUrl, 0, delTitle ? delTitle : '')
1072                      }
1073                    })
1074                  } else {
1075                    // Обновляем ссылку, на которую будет уходить запрос и информацию о ней
1076                    updateLink(a, delUrl, addUrl, 1, addTitle ? addTitle : '')
1077                    a.removeClass('is-added').parent().removeClass('is-added')
1078
1079                    // Обновляем ссылку у всех товаров на странице
1080                    $(addCompare).each(function () {
1081                      if ($(this).attr('data-id') == pDataid) {
1082                        $(this).removeClass('is-added')
1083                        updateLink($(this), delUrl, addUrl, 1, addTitle ? addTitle : '')
1084                      }
1085                    })
1086                  }
1087
1088                  // Обновление текстовой надписи с описанием действия
1089                  if (aText.length) {
1090                    aText.text(a.attr(isAdd == 1 ? 'data-del-tooltip' : 'data-add-tooltip'))
1091                  }
1092
1093                  // Сообщение уведомления
1094                  const content = createNoty(pName, checkMessage(data.message))
1095
1096                  // Если есть функция, которая отображает сообщения пользователю
1097                  if (typeof Noty == 'function') {
1098                    notyStart(content, 'success')
1099                  }
1100                } else if ('error' == data.status) {
1101                  // Сообщение уведомления
1102                  const content = createNoty(pName, 'Не удалось добавить в сравнение!')
1103
1104                  // Если есть функция, которая отображает сообщения пользователю
1105                  if (typeof Noty == 'function') {
1106                    notyStart(content, 'warning')
1107                  }
1108                }
1109              },
1110              error: function () {
1111                console.log('Error Ajax add-compare')
1112                notyStart('Error Ajax add-compare', 'warning')
1113              },
1114            })
1115
1116            return false
1117          }
1118        })
1119
1120      // Добавление/удаление товара Избранное
1121      const blockFavorites = '.favorites'
1122      const addFavorites = '.add-favorites'
1123      const addtoFavorites = '.addto__favorites'
1124
1125      $(addFavorites)
1126        .off('click')
1127        .on('click', function () {
1128          // Объект ссылки, по которой кликнули
1129          const a = $(this),
1130            isAdd = a.attr('data-action-is-add'),
1131            addUrl = a.attr('data-action-add-url'),
1132            delUrl = a.attr('data-action-delete-url'),
1133            addTitle = a.attr('data-action-add-title'),
1134            delTitle = a.attr('data-action-delete-title'),
1135            aText = a.find('span'),
1136            pName = a.attr('data-prodname'),
1137            pUrl = a.attr('data-produrl'),
1138            pImg = a.attr('data-prodimg'),
1139            pDataid = a.attr('data-id'),
1140            pDataPrice = a.attr('data-mod-price'),
1141            pDataPriceOld = a.attr('data-mod-price-old'),
1142            pDataChar = a.attr('data-char-code'),
1143            // addTooltip = a.attr('data-add-tooltip'),
1144            // delTooltip = a.attr('data-del-tooltip'),
1145            // pDataMod = a.attr('data-mod-id'),
1146            // actionUrl = a.attr('data-action-url'),
1147            requestUrl = a.attr('href')
1148
1149          let flag = 0
1150
1151          // Проверка элемента в объекте
1152          $(addtoFavorites)
1153            .find('.addto__item')
1154            .each(function () {
1155              if ($(this).attr('data-id') == pDataid) {
1156                flag = 1
1157              }
1158
1159              if (flag == 1) {
1160                $(this).remove()
1161                return false
1162              }
1163
1164              return flag
1165            })
1166
1167          // Если есть информация о том какие URL адреса будут изменены, то можено не перегружать страницу и сделать запрос через ajax
1168          if (addUrl && delUrl) {
1169            $.ajax({
1170              url: requestUrl,
1171              type: 'POST',
1172              dataType: 'json',
1173              cache: false,
1174              data: {
1175                ajax_q: 1,
1176              },
1177              success: function (data) {
1178                if ('ok' == data.status) {
1179                  // Рендер элементов в список
1180                  if (flag == 0) {
1181                    if ($(addtoFavorites).length) {
1182                      $(addtoFavorites).find('.addto__items').prepend(createItem(pDataid, pUrl, pName, pImg, pDataChar, pDataPrice, pDataPriceOld, delUrl))
1183                    }
1184                  }
1185
1186                  // Если указано, что изменилось число товаров в избранном
1187                  const countFavorites = data.favorites_goods_count
1188                  if (typeof countFavorites != 'undefined') {
1189                    // Блок информации о том, что есть товары в избранном
1190                    const sidecount = $(blockFavorites).find('[data-count]')
1191                    // Указываем информацию о новом количестве товаров в избранном
1192                    sidecount.text(countFavorites).attr('data-count', countFavorites)
1193                    if (countFavorites > 0) {
1194                      $(blockFavorites).addClass('has-items')
1195                    } else {
1196                      $(blockFavorites).removeClass('has-items')
1197                      sidecount.attr('data-count', '0').text('0')
1198                      $(addFavorites).removeAttr('title').removeClass('is-added')
1199                    }
1200                  }
1201
1202                  // Проверка статуса добавления товара
1203                  if (isAdd == 1) {
1204                    // Обновляем ссылку, на которую будет уходить запрос и информацию о ней
1205                    updateLink(a, addUrl, delUrl, 0, delTitle ? delTitle : '')
1206                    a.addClass('is-added').parent().addClass('is-added')
1207
1208                    // Обновляем ссылку у всех товаров на странице
1209                    $(addFavorites).each(function () {
1210                      if ($(this).attr('data-id') == pDataid) {
1211                        $(this).addClass('is-added')
1212                        updateLink($(this), addUrl, delUrl, 0, delTitle ? delTitle : '')
1213                      }
1214                    })
1215                  } else {
1216                    // Обновляем ссылку, на которую будет уходить запрос и информацию о ней
1217                    updateLink(a, delUrl, addUrl, 1, addTitle ? addTitle : '')
1218                    a.removeClass('is-added').parent().removeClass('is-added')
1219
1220                    // Обновляем ссылку у всех товаров на странице
1221                    $(addFavorites).each(function () {
1222                      if ($(this).attr('data-id') == pDataid) {
1223                        $(this).removeClass('is-added')
1224                        updateLink($(this), delUrl, addUrl, 1, addTitle ? addTitle : '')
1225                      }
1226                    })
1227                  }
1228
1229                  // Обновление текстовой надписи с описанием действия
1230                  if (aText.length) {
1231                    aText.text(a.attr(isAdd == 1 ? 'data-del-tooltip' : 'data-add-tooltip'))
1232                  }
1233
1234                  // Сообщение уведомления
1235                  const content = createNoty(pName, checkMessage(data.message))
1236
1237                  // Если есть функция, которая отображает сообщения пользователю
1238                  if (typeof Noty == 'function') {
1239                    notyStart(content, 'success')
1240                  }
1241                } else if ('error' == data.status) {
1242                  const message = `Для добавления товара в избранное, Вам необходимо <a class="noty__link" href="/user/login">Войти в личный кабинет</a> или <a class="noty__link" href="/user/register">Зарегистрироваться</a>`
1243                  // Сообщение уведомления
1244                  const content = createNoty(message, 'Не удалось добавить в избранное!')
1245
1246                  // Если есть функция, которая отображает сообщения пользователю
1247                  if (typeof Noty == 'function') {
1248                    notyStart(content, 'warning')
1249                  }
1250                }
1251              },
1252              error: function () {
1253                console.log('Error Ajax add-favorites')
1254                notyStart('Error Ajax add-favorites', 'warning')
1255              },
1256            })
1257
1258            return false
1259          }
1260        })
1261    }
1262
1263    // Добавление товара в корзину
1264    this.addCart = function () {
1265      $('.product__form').off('submit').on('submit', function () {
1266        // Добавляем активные классы и обновлем счетчик товаров
1267        $('.cart').addClass('has-items')
1268
1269        // Быстрый заказ
1270        if ($(this).attr('rel') === 'quick') {
1271          product.quickOrder(this)
1272          return false
1273        }
1274
1275        // Находим форму, которую отправляем на сервер, для добавления товара в корзину
1276        const formBlock = $($(this).get(0))
1277        const formData = formBlock.serializeArray()
1278        const goodsUrl = this.querySelector('[name="form[goods_url]"]').value
1279        // console.info('this', goodsUrl)
1280
1281        // Проверка на существование формы отправки запроса на добавление товара в корзину
1282        if (1 > formBlock.length || formBlock.get(0).tagName != 'FORM') {
1283          alert('Не удалось найти форму добавления товара в корзину')
1284          return false
1285        }
1286
1287        // Получаем данные формы, которые будем отправлять на сервер
1288        // Сообщаем серверу, что мы пришли через ajax запрос
1289        formData.push({ name: 'ajax_q', value: 1 })
1290        // Так же сообщим ему, что нужно сразу отобразить форму быстрого заказа
1291        //formData.push({name: 'fast_order', value: 1});
1292        // Аяксом добавляем товар в корзину и вызываем форму быстрого заказа товара
1293        $.ajax({
1294          type: 'POST',
1295          cache: false,
1296          url: formBlock.attr('action'),
1297          data: formData,
1298          success: function (data) {
1299            // Анализ системного сообщения в коризне
1300            const content = $(data).html()
1301            $.fancybox.open(content)
1302            
1303            // Обновление скидки
1304            // console.log('add data', data);
1305            // quantity.updAddtoSale()
1306
1307            // Если есть сопутствующие
1308            getRelatedGoods(goodsUrl)
1309
1310            // Закрыть окно через 5 секунд
1311            setTimeout(() => {
1312              $.fancybox.close()
1313            }, 5000);
1314
1315            // Скрытое обновление корзины
1316            $('.hiddenUpdate').html(data)
1317          },
1318        })
1319
1320        return false
1321      })
1322    }
1323
1324    // Добавление товара в корзину
1325    this.onCart = function () {
1326      $('.add-cart').on('click', function () {
1327        const form = $(this).closest('form')
1328        if ($(this).hasClass('quick')) {
1329          form.attr('rel', 'quick')
1330        } else {
1331          const rel = form.attr('rel')
1332          if (rel) {
1333            form.attr('rel', rel.replace('quick', ''))
1334          }
1335        }
1336        form.trigger('submit')
1337        return false
1338      })
1339    }
1340
1341    // Уведомить при отсутствии товара
1342    this.onNotify = function () {
1343      $('.add-notify').on('click', function (event) {
1344        console.log('add-notify', event);
1345        const formBlock = $(this).closest('.product__form')
1346        const goodsMod = formBlock.find('[name="form[goods_mod_id]"]').val()
1347        const goodsName = formBlock.find('[name="form[goods_name]"]').val()
1348        const goodsLink = formBlock.find('[name="form[goods_url]"]').val()
1349        const goodsImage = formBlock.find('[name="form[goods_image]"]').val()
1350        $('.form__goods-image img').attr('src', goodsImage).attr('alt', goodsName)
1351        $('.form__goods-name').attr('href', goodsLink).text(goodsName)
1352        $('.notifyGoodsModId').val(goodsMod)
1353      })
1354    }
1355
1356    // Быстрый заказ
1357    this.quickOrder = function (formSelector) {
1358      // Находим форму, которую отправляем на сервер, для добавления товара в корзину
1359      const formBlock = $($(formSelector).get(0))
1360      // Проверка на существование формы отправки запроса на добавление товара в корзину
1361      if (1 > formBlock.length || formBlock.get(0).tagName != 'FORM') {
1362        alert('Не удалось найти форму добавления товара в корзину')
1363        return false
1364      }
1365      // Получаем данные формы, которые будем отправлять на сервер
1366      const formData = formBlock.serializeArray()
1367      // Сообщаем серверу, что мы пришли через ajax запрос
1368      formData.push({ name: 'ajax_q', value: 1 })
1369      // Так же сообщим ему, что нужно сразу отобразить форму быстрого заказа
1370      formData.push({ name: 'fast_order', value: 1 })
1371      // Аяксом добавляем товар в корзину и вызываем форму быстрого заказа товара
1372      $.ajax({
1373        type: 'POST',
1374        cache: false,
1375        url: formBlock.attr('action'),
1376        data: formData,
1377        success: function (data) {
1378          $.fancybox.open(data, {
1379            keyboard: false,
1380            baseClass: 'fastOrder',
1381            afterShow: function () {
1382              // password.onClick()
1383              // password.capsWarning()
1384              new Password()
1385              order.onInit()
1386              order.onSelect()
1387              order.coupons()
1388              order.onValidate()
1389              cart.minSum()
1390              supplier()
1391              // Стили для новых селектов
1392              $('.form__phone').mask('+7 (999) 999-9999')
1393              // Скрытое обновление корзины
1394              $('.hiddenUpdate').html(data)
1395            },
1396          })
1397        },
1398      })
1399
1400      return false
1401    }
1402
1403    // Быстрый просмотр товара
1404    this.quickView = function () {
1405      // Все товары
1406      const items = document.querySelectorAll('.product__item')
1407      // Массив предзагруженных товаров
1408      let quickViewPreload = []
1409
1410      // Обрабатываем товары
1411      items.forEach((item) => {
1412        // Метка загрузки
1413        let loaded = false
1414        let once = false
1415
1416        // ID товара
1417        const ID = item.getAttribute('data-id')
1418
1419        // Ссылка на товар
1420        const link = item.querySelector('.product__name')
1421        if (!link) { return false }
1422        let url = link.getAttribute('href')
1423        url += (false !== url.indexOf('?') ? '&' : '?') + 'only_body=1'
1424        // console.log('url', url)
1425        // Запуск Функции при наведении
1426        item.addEventListener('mouseover', quickViewHover)
1427
1428        // Копка Быстрый просмотр
1429        const quick = item.querySelector('.product__quickview')
1430        // Запуск Функции при клике
1431        if (quick) {
1432          quick.addEventListener('click', () => quickViewClick('productView'))
1433        }
1434
1435        // Запуск Функции выбора модификации при клике
1436        const quickMod = item.querySelector('.add-mod')
1437        if (quickMod) {
1438          quickMod.addEventListener('click', () => quickViewClick('productViewMod'))
1439        }
1440
1441        // Функции при наведении. Предзагрузка
1442        function quickViewHover() {
1443          // Если товар загружен, выход из функции
1444          if (loaded) {
1445            return false
1446          }
1447
1448          // Обрабатываем полученные данные
1449          quickViewFetch(url)
1450
1451          // Ставим метку загрузки
1452          loaded = true
1453        }
1454
1455        // Обрабатываем полученные данные
1456        async function quickViewFetch(url) {
1457          // console.log('url2', url)
1458          return await getFetch(url).then((html) => {
1459            const block = html.querySelector('.productViewBlock')
1460            const content = product.quickViewContent(block)
1461            const id = block.getAttribute('data-id')
1462            // console.log('content', content)
1463            // Если данные уже добавлены
1464            if (quickViewPreload.find((element) => element.id == ID)) {
1465              return false
1466            }
1467            // Отправляем данные в общий массив для быстрой подгрузки
1468            quickViewPreload.push({ id, url, content })
1469            return content
1470          }).catch(error => console.error(error.message))
1471        }
1472
1473        // Функции при клике
1474        function quickViewClick(mod) {
1475          event.preventDefault()
1476          // alert('quickViewClick 4: ' + mod)
1477          // alert('quickViewPreload: ' + quickViewPreload)
1478          // Если есть совпадение в массиве предзагрузки
1479          if (quickViewPreload.find((element) =>
1479 element.id == ID)) {
1480            quickViewPreloadFilter(quickViewPreload, mod)
1481          } else {
1482            quickViewFetch(url)
1483            // Если нет, то запуск функции через 1000мс
1484            setTimeout(() => {
1485              quickViewPreloadFilter(quickViewPreload, mod)
1486            }, 1000)
1487          }
1488        }
1489
1490        // Фильтр контента из массива
1491        function quickViewPreloadFilter(objects, mod) {
1492          return objects.filter((element) => {
1493            if (ID == element.id) {
1494              // Открыть в модальном окне
1495              $.fancybox.open(element.content)
1496              // Выбор модификации
1497              mod == 'productViewMod' ? element.content.classList.add('productViewMod') : element.content.classList.remove('productViewMod')
1498
1499              // Обновление кол-ва
1500              // e.content.querySelector('.qty__input').value = item.querySelector('.qty__input').value;
1501              // e.content.querySelector('.qty__input').dispatchEvent(new Event('input'));
1502
1503              // Обновление цены
1504              const price = element.content.querySelector('.productView__price .price__now')
1505              element.content.querySelector('.productView__add-price').innerHTML = price.innerHTML
1506
1507              // Запуск функций карточки товара
1508              if (once) return false
1509              product.addCart()
1510              product.addTo()
1511              product.onNotify()
1512              // goods.onClick()
1513              // goods.swiperThumbs(mod)
1514              new Goods().modifications(element.content)
1515              quantity.init(element.content)
1516              // Отметка загруженности
1517              once = true
1518              return element.content
1519            }
1520          })
1521        }
1522      })
1523    }
1524
1525    // Обработка контента быстрого просмотра товара
1526    this.quickViewContent = function (object) {
1527      // Удаляем табы
1528      if (object.querySelector('.productView__tabs')) {
1529        object.querySelector('.productView__tabs').remove()
1530      }
1531      // Удаляем аналоги
1532      if (object.querySelector('#related-views')) {
1533        object.querySelector('#related-views').remove()
1534      }
1535      return object
1536    }
1537
1538    // Показать/Скрыть характеристики
1539    this.attrVisibility = function(object) {
1540      const attrs = object.querySelector('.product__attr')
1541      if (!attrs) return false
1542      const button = attrs.querySelector('.product__attr-more')
1543      if (!button) return false
1544      button.addEventListener('click', handleClick)
1545
1546      function handleClick(){
1547        this.classList.toggle('is-actived')
1548        this.parentElement.classList.toggle('is-actived')
1549        changeTxt(this)
1550      }
1551    }
1552  }
1553}
1554
1555// Получить Сопутствующие товары по ссылке
1556function getRelatedGoods(url){
1557  let newUrl = `${url}${url.indexOf('?') ? '&' : '?'}only_body=1`
1558
1559  Promise.resolve(getFetch(newUrl)).then(html => {
1560    const related = html.querySelector('#related-goods .swiper-products')
1561    // console.info('html', html);
1562    // console.info('related', related);
1563
1564    const modal = document.querySelector('.modal.fancybox-content');
1565    const modalGoods = modal?.querySelectorAll('.form__goods')
1566    const useful = modal?.querySelector('.modal__useful');
1567    const usefulContent = useful?.querySelector('.modal__useful-content')
1568    // console.info('related', related);
1569    // console.info('modal', modal);
1570    // console.info('modalGoods', modalGoods);
1571    // console.info('useful', useful);
1572    // console.info('usefulContent', usefulContent);
1573
1574    if (related) {
1575      usefulContent.prepend(related)
1576      useful.classList.remove('is-hide')
1577      modalGoods?.forEach(el => el.classList.remove('no-border'))
1578      swiperUseful()
1579      const images = modal.querySelectorAll('.product__item img')
1580      images.forEach(image => {
1581        const link = image.getAttribute('data-src')
1582        if (link) image.setAttribute('src', link)
1583        // console.info('image', image);
1584        // console.info('link', link);
1585      })
1586      // console.info('images', images);
1587
1588      const usefulSwiper = usefulContent.querySelector('.swiper-wrapper')
1589      // console.info('usefulSwiper3', usefulSwiper);
1590      usefulSwiper?.classList.remove('products__grid')
1591      usefulSwiper?.classList.add('products__list')
1592    } else {
1593      useful?.classList.add('is-hide')
1594      modalGoods?.forEach(el => el.classList.add('no-border'))
1595    }
1596    // console.info('html', html);
1597
1598    return html
1599  }).catch(error => console.error('error', error))
1600  // console.info('url', url);
1601  // console.info('newUrl', newUrl);
1602  // console.info('content', content);
1603}
1604
1605// Функция получения HTML контента по ссылке
1606async function getFetch(url) {
1607  // console.log('getFetch url', url);
1608  return await fetch(url)
1609    // Получаем ответ
1610    .then((response) => response.text())
1611    // Преобразуем в html
1612    .then((text) => {
1613      const parser = new DOMParser()
1614      const html = parser.parseFromString(text, 'text/html')
1615      // console.log('getFetch html', html);
1616      return html
1617    })
1618    // Если получили ошибку
1619    .catch((error) => console.trace(error))
1620}
1621
1622///////////////////////////////////////
1623/*** Действия удаления из ... ***/
1624///////////////////////////////////////
1625class Remove {
1626  constructor() {
1627    // console.info('Remove start');
1628
1629    // Действия при клике
1630    this.onClick = function (selector = '.sideblock') {
1631      const content = document.querySelector(selector)
1632      // Если нет контента
1633      if (!content) {
1634        return false
1635      }
1636      // Действия
1637      content.parentElement.addEventListener('click', function (event) {
1638        // Объявление переменных
1639        const removeCart = event.target.closest('.cart .addto__remove')
1640        const removeCartAll = event.target.closest('.cart .addto__removeAll')
1641        const removeCompare = event.target.closest('.compare .addto__remove')
1642        const removeCompareAll = event.target.closest('.compare .addto__removeAll')
1643        const removeFavorites = event.target.closest('.favorites .addto__remove')
1644        const removeFavoritesAll = event.target.closest('.favorites .addto__removeAll')
1645
1646        // Удалить из корзины
1647        if (removeCart) {
1648          console.log('removeCart', event)
1649          event.preventDefault()
1650          remove.fromCart(removeCart)
1651          // Удалить из сравнения
1652        } else if (removeCartAll) {
1653          // console.log('removeCartAll', event)
1654          event.preventDefault()
1655          remove.fromCartAll(removeCartAll)
1656          // Удалить все из сравнения
1657        } else if (removeCompare) {
1658          // console.log('removeCompare', event)
1659          event.preventDefault()
1660          remove.fromCompare(removeCompare)
1661          // Удалить все из сравнения
1662        } else if (removeCompareAll) {
1663          // console.log('removeCompareAll', event)
1664          event.preventDefault()
1665          remove.fromCompareAll(removeCompareAll)
1666          // Удалить из избранного
1667        } else if (removeFavorites) {
1668          event.preventDefault()
1669          remove.fromFavorites(removeFavorites)
1670          // Удалить все из избранного
1671        } else if (removeFavoritesAll) {
1672          // console.log('fromFavoritesAll', event)
1673          event.preventDefault()
1674          remove.fromFavoritesAll(removeFavoritesAll)
1675        } else {
1676          return false
1677        }
1678      })
1679    }
1680
1681    // Обновление данных после удаления
1682    this.fromElements = function (objects) {
1683      objects.forEach((element) => {
1684        // console.log('e', e);
1685        element.parentElement.classList.remove('is-added')
1686        element.classList.remove('is-added')
1687        element.querySelector('span').innerText = element.getAttribute('data-add-tooltip')
1688        element.setAttribute('data-action-is-add', '1')
1689        element.setAttribute('title', element.getAttribute('data-add-tooltip'))
1690        element.setAttribute('href', element.getAttribute('data-action-add-url') + '?id=' + element.getAttribute('data-mod-id'))
1691      })
1692    }
1693
1694    // Удаление товара из корзины без обновлении страницы
1695    this.fromCart = function (obj) {
1696      // console.log('Remove fromCart', obj);
1697      if (confirm('Вы точно хотите удалить товар из корзины?')) {
1698        const href = obj.getAttribute('href')
1699        const qty = obj.getAttribute('data-qty')
1700        const id = obj.getAttribute('data-id')
1701        const oldCount = document.querySelector('.cart-count').getAttribute('data-count')
1702
1703        $.ajax({
1704          cache: false,
1705          url: href,
1706          success: function (data) {
1707            // console.log('Remove data', data);
1708            let flag = 0
1709            // Обновляем кол-во
1710            const newCount = oldCount - qty
1711            updateCount('.cart-count', newCount)
1712            // Окончание кол-ва товаров
1713            document.querySelectorAll('.cart-wordend').forEach((element) => {
1714              element.innerHTML = $(data).find('.cart-wordend').html()
1715            })
1716            // Итоговая сумма со скидкой
1717            document.querySelectorAll('.cartSumDiscount').forEach((element) => {
1718              const newPrice = Math.ceil($(data).find('.cartSumDiscount').attr('data-price'))
1719              element.setAttribute('data-price', newPrice)
1720              element.querySelector('.num').textContent = addSpaces(newPrice)
1721            })
1722            // 
1723            quantity.updAddtoSale()
1724            // Окончание кол-ва товаров
1725            document.querySelectorAll('.addto__total-discount').forEach((element) => {
1726              console.log('cartTotal__item', $(data).find('.cartTotal__item-discount').length);
1727              if ($(data).find('.cartTotal__item-discount').length > 0){
1728                element.classList.remove('is-hide')
1729                element.innerHTML = $(data).find('.cartTotal__item-discount').html()
1730                console.log('discount', element)
1731              }else{
1732                element.classList.add('is-hide')
1733                document.querySelector('.addto__cart').classList.remove('has-discount')
1734                element.innerHTML = ''
1735                console.log('discount2', element)
1736              } 
1737            })
1738
1739            // Скрываем и удаляем товар из корзины
1740            obj
1741              .closest('.cart')
1742              .querySelector('.addto__item[data-id="' + id + '"]')
1743              .remove()
1744
1745            if (newCount != 0) {
1746              document.querySelectorAll('.cart .addto__item').forEach((element) => {
1747                if (flag == 0) {
1748                  if (element.style.display == 'none') {
1749                    element.style.display = 'flex'
1750                    flag++
1751                  }
1752                }
1753              })
1754            } else {
1755              // Обновляем кол-во
1756              updateCount('.cart-count', '0')
1757              document.querySelectorAll('.cart').forEach((element) => element.classList.remove('has-items'))
1758            }
1759
1760            // Удаляем класс добавленного товара в корзину
1761            document.querySelectorAll('.product__inCart[data-id="' + id + '"]').forEach((element) => {
1762              element.classList.remove('product__inCart')
1763              const qty = element.querySelector('.qty__input')
1764              if (qty) qty.value = 1
1765              
1766            })
1767
1768          },
1769        })
1770      }
1771    }
1772
1773    // Удаление ВСЕХ товаров из Корзины без обновлении страницы
1774    this.fromCartAll = function (obj) {
1775      // console.log('Remove fromCartAll');
1776      if (confirm('Вы точно хотите очистить корзину?')) {
1777        $.ajax({
1778          cache: false,
1779          url: obj.getAttribute('href'),
1780          success: function (data) {
1781            // Обновляем кол-во
1782            updateCount('.cart-count', '0')
1783            document.querySelectorAll('.cart').forEach((e) => e.classList.remove('has-items'))
1784            document.querySelectorAll('.cart .addto__item').forEach((e) => e.remove())
1785            document.querySelectorAll('.product__inCart').forEach((e) => {
1786              e.classList.remove('product__inCart')
1787              e.querySelector('.qty__input') ? e.querySelector('.qty__input').value = 1 : ''
1788            })
1789          },
1790        })
1791      }
1792    }
1793
1794    // Удаление товара из Сравнения без обновлении страницы
1795    this.fromCompare = function (obj) {
1796      // console.log('fromCompare', obj)
1797      if (confirm('Вы точно хотите удалить товар из сравнения?')) {
1798        const href = obj.getAttribute('href')
1799        const id = obj.getAttribute('data-id')
1800        const oldCount = document.querySelector('.compare-count').getAttribute('data-count')
1801
1802        $.ajax({
1803          cache: false,
1804          url: href,
1805          success: function (data) {
1806            let flag = 0
1807            // Обновляем кол-во
1808            const newCount = oldCount - 1
1809            updateCount('.compare-count', newCount)
1810
1811            // Скрываем и удаляем товар из корзины
1812            obj
1813              .closest('.compare')
1814              .querySelector('.addto__item[data-id="' + id + '"]')
1815              .remove()
1816
1817            if (newCount != 0) {
1818              document.querySelectorAll('.compare .addto__item').forEach((element) => {
1819                if (flag == 0) {
1820                  if (element.style.display == 'none') {
1821                    element.style.display = 'flex'
1822                    flag++
1823                  }
1824                }
1825              })
1826            } else {
1827              // Обновляем кол-во
1828              updateCount('.compare-count', '0')
1829              document.querySelectorAll('.compare').forEach((element) => element.classList.remove('has-items'))
1830            }
1831
1832            // Удаляем класс добавленного товара
1833            const objects = document.querySelectorAll('.add-compare[data-id="' + id + '"]')
1834            objects.length ? remove.fromElements(objects) : false
1835          },
1836        })
1837      }
1838    }
1839
1840    // Удаление ВСЕХ товаров из Сравнения без обновлении страницы
1841    this.fromCompareAll = function (obj) {
1842      // console.log('fromCompareAll', obj)
1843      if (confirm('Вы точно хотите очистить сравнение?')) {
1844        $.ajax({
1845          cache: false,
1846          url: obj.getAttribute('href'),
1847          success: function (data) {
1848            // Обновляем кол-во
1849            updateCount('.compare-count', '0')
1850            document.querySelectorAll('.compare').forEach((e) => e.classList.remove('has-items'))
1851            document.querySelectorAll('.compare .addto__item').forEach((e) => e.remove())
1852            remove.fromElements(document.querySelectorAll('.add-compare.is-added'))
1853          },
1854        })
1855      }
1856    }
1857
1858    // Удаление товара из Избранного без обновлении страницы
1859    this.fromFavorites = function (obj) {
1860      // console.log('fromFavorites', obj)
1861      if (confirm('Вы точно хотите удалить товар из Избранного?')) {
1862        const href = obj.getAttribute('href')
1863        const id = obj.getAttribute('data-id')
1864        const oldCount = document.querySelector('.favorites-count').getAttribute('data-count')
1865
1866        $.ajax({
1867          cache: false,
1868          url: href,
1869          success: function () {
1870            let flag = 0
1871            // Обновляем кол-во
1872            const newCount = oldCount - 1
1873            updateCount('.favorites-count', newCount)
1874
1875            // Скрываем и удаляем товар из корзины
1876            obj
1877              .closest('.addto__favorites')
1878              .querySelector('.addto__item[data-id="' + id + '"]')
1879              .remove()
1880
1881            if (newCount != 0) {
1882              document.querySelectorAll('.favorites .addto__item').forEach((e) => {
1883                if (flag == 0) {
1884                  if (e.style.display == 'none') {
1885                    e.style.display = 'flex'
1886                    flag++
1887                  }
1888                }
1889              })
1890            } else {
1891              // Обновляем кол-во
1892              updateCount('.favorites-count', '0')
1893              document.querySelectorAll('.favorites').forEach((e) => e.classList.remove('has-items'))
1894            }
1895
1896            const elements = document.querySelectorAll('.add-favorites[data-id="' + id + '"]')
1897            if (elements.length) {
1898              remove.fromElements(elements)
1899            }
1900          },
1901        })
1902      }
1903    }
1904
1905    // Удаление ВСЕХ товаров из Избранного без обновлении страницы
1906    this.fromFavoritesAll = function (obj) {
1907      // console.log('fromFavoritesAll', obj)
1908      if (confirm('Вы точно хотите очистить Избранное?')) {
1909        $.ajax({
1910          cache: false,
1911          url: obj.getAttribute('href'),
1912          success: function (data) {
1913            // Обновляем кол-во
1914            updateCount('.favorites-count', '0')
1915            document.querySelectorAll('.favorites').forEach((e) => e.classList.remove('has-items'))
1916            document.querySelectorAll('.favorites .addto__item').forEach((e) => e.remove())
1917            remove.fromElements(document.querySelectorAll('.add-favorites.is-added'))
1918          },
1919        })
1920      }
1921    }
1922  }
1923}
1924
1925///////////////////////////////////////
1926/*** Скрипты для Товары, Категории ***/
1927///////////////////////////////////////
1928class CatalogFilter {
1929  constructor() {
1930    const objects = document.querySelectorAll('.filter__search')
1931    objects.forEach((object) => {
1932      // Поиск по фильтрам
1933      this.filterSearchHide(object)
1934      object.addEventListener('input', () => this.filterSearch(object))
1935
1936      // Выбора фильтра
1937      const filterInputs = object.parentElement.querySelectorAll('.filter__item input')
1938      filterInputs.forEach((element) => {
1939        element.addEventListener('click', function (event) {
1940          event.target.form.submit()
1941        })
1942      })
1943
1944      // Свернуть/Развернуть фильтр
1945      const filterTitle = object.closest('.filter__collapsible').querySelector('.filter__title')
1946      filterTitle.addEventListener('click', () => this.filterSlideDownUp(filterTitle))
1947
1948      // Кол-во выбранных фильтров
1949      const filterCount = object.closest('.filter__collapsible').querySelectorAll('.filter__item.is-checked').length
1950      filterTitle.setAttribute('data-count', filterCount)
1951
1952      // Кол-во всех выбранных фильтров 
1953      const filterAllCount = object.closest('.form__filters').querySelectorAll('.filter__item.is-checked').length
1954      document.querySelector('.filters__icon').setAttribute('data-count', filterAllCount)
1955    })
1956
1957    // Фильтр цены
1958    this.filterPrice()
1959    
1960    // Инфо оптовых цен
1961    this.infoSupplier()
1962  }
1963
1964  // Поиск по фильтрам
1965  filterSearch(object) {
1966    // Поисковый запрос
1967    const query = object.value.toLowerCase()
1968
1969    // Объекты элементов
1970    const items = object.parentElement.querySelectorAll('.filter__item')
1971    const searchEmpty = object.parentElement.querySelector('.filter__search-empty')
1972
1973    // Массив названий фильтров
1974    const itemsArray = []
1975    items.forEach((element) => itemsArray.push(element.getAttribute('data-value').toLowerCase()))
1976
1977    // Совпадение в массиве
1978    const resultArray = itemsArray.map((element, index) => (element.indexOf(query) >= 0 ? index : -1)).filter((element) => element >= 0)
1979
1980    // Фильтры не найдены
1981    items.forEach((element) => {
1982      if (resultArray.length === 0) {
1983        element.classList.add('is-hide')
1984        searchEmpty.classList.remove('is-hide')
1985      } else {
1986        element.classList.add('is-hide')
1987        searchEmpty.classList.add('is-hide')
1988        resultArray.forEach((element) => items[element].classList.remove('is-hide'))
1989      }
1990    })
1991  }
1992
1993  // Показать поиск
1994  filterSearchHide(object) {
1995    const items = object.parentElement.querySelectorAll('.filter__item')
1996    items.length < 6 ? object.classList.add('is-hide') : object.classList.remove('is-hide')
1997  }
1998
1999  // Фильтр по ценам
2000  filterPrice() {
2001    const priceFilterMinAvailable = parseInt($('[name="form[filter][available_price][min]"]').val()), // Минимальное значение цены для фильтра
2002      priceFilterMaxAvailable = parseInt($('[name="form[filter][available_price][max]"]').val()), // Максимальное значение цены для фильтра
2003      priceSliderBlock = $('#goods-filter-price-slider'), // Максимальное значение цены для фильтра
2004      priceInputMin = $('#goods-filter-min-price'), // Поле ввода текущего значения цены "От"
2005      priceInputMax = $('#goods-filter-max-price'), // Поле ввода текущего значения цены 'До'
2006      priceSubmitButtonBlock = $('.filters-price__buttons') // Блок с кнопкой, которую есть смысл нажимать только тогда, когда изменялся диапазон цен.
2007
2008    // Слайдер, который используется для удобства выбора цены
2009    priceSliderBlock.slider({
2010      range: true,
2011      min: priceFilterMinAvailable,
2012      max: priceFilterMaxAvailable,
2013      values: [parseInt($('#goods-filter-min-price').val()), parseInt($('#goods-filter-max-price').val())],
2014      slide: function (event, ui) {
2015        priceInputMin.val(ui.values[0])
2016        priceInputMax.val(ui.values[1])
2017        priceSubmitButtonBlock.css('display', 'flex')
2018      },
2019    })
2020
2021    // При изменении минимального значения цены
2022    priceInputMin.keyup(function () {
2023      let newVal = parseInt($(this).val())
2024      if (newVal < priceFilterMinAvailable) {
2025        newVal = priceFilterMinAvailable
2026      }
2027      priceSliderBlock.slider('values', 0, newVal)
2028      priceSubmitButtonBlock.css('display', 'flex')
2029    })
2030
2031    // При изменении максимального значения цены
2032    priceInputMax.keyup(function () {
2033      let newVal = parseInt($(this).val())
2034      if (newVal > priceFilterMaxAvailable) {
2035        newVal = priceFilterMaxAvailable
2036      }
2037      priceSliderBlock.slider('values', 1, newVal)
2038      priceSubmitButtonBlock.css('display', 'flex')
2039    })
2040
2041    // Активный фильтр цены
2042    if (priceInputMin.val() > priceFilterMinAvailable || priceInputMax.val() < priceFilterMaxAvailable) {
2043      $('.filters-price').addClass('has-filters')
2044      $('.toolbar').addClass('has-filters')
2045      $('#filters').addClass('has-filters')
2046    } else {
2047      $('.filters-price').removeClass('has-filters')
2048      $('.toolbar').removeClass('has-filters')
2049      $('#filters').removeClass('has-filters')
2050    }
2051  }
2052
2053  // Показать/Скрыть фильтр
2054  filterSlideDownUp(object) {
2055    console.log('object', object);
2056    if (object.matches('.is-actived')) {
2057      object.classList.remove('is-actived')
2058      $(object).next().slideUp(600)
2059    } else {
2060      object.classList.add('is-actived')
2061      $(object).next().slideDown(600)
2062    }
2063  }
2064
2065  // Показать/Скрыть Инфо по оптовым ценам
2066  infoSupplier() {
2067    const promos = document.querySelectorAll('.products__promo')
2068    if (promos === null) {return false}
2069    
2070    promos.forEach(promo => {
2071      const close = promo.querySelector('.products__promo-close')
2072      close.addEventListener('click', handleClick)
2073
2074      handlePromo(promo)
2075    })
2076
2077    function handleClick(){
2078      this.parentElement.classList.add('is-hide')
2079    }
2080
2081    function handlePromo(promo){
2082      const cartSum = document.querySelector('.cartSumDiscount')
2083      const cartDiscounts = promo.querySelectorAll('.products__promo-text')
2084
2085      cartDiscounts.forEach((element, index) => {
2086        const priceDiscount = parseInt(element.getAttribute('data-price'))
2087        const priceCart = cartSum ? parseInt(cartSum.getAttribute('data-price')) : 0
2088        if (priceDiscount < priceCart) {
2089          element.parentElement.classList.add('is-hide')
2090        } else {
2091          element.parentElement.classList.remove('is-hide')
2092        }
2093        
2094      })
2095    }
2096  }
2097}
2098
2099///////////////////////////////////////
2100// Товар. Карточка товара
2101///////////////////////////////////////
2102class Goods {
2103  constructor () {
2104    this.productViewBlock = document.querySelector('.productViewBlock')
2105    this.productViewTabs = document.querySelector('.productView__tabs')
2106    this.productModsActive = this.productViewBlock.querySelector('.goodsModificationsSlug')
2107
2108    // Запуск слайдера изображений
2109    this.swiperThumbs('product')
2110
2111    // Запуск модификаций
2112    this.modifications(this.productViewBlock)
2113
2114    // Запуск Скрыть/Показать описание
2115    this.shortDescriptions(this.productViewBlock)
2116
2117    // Запуск Скрыть/Показать характеристики
2118    this.shortFeatures(this.productViewBlock)
2119
2120    // Если нет данных по табам
2121    if (!this.productViewTabs) return false
2122
2123    // Запуск Рейтинга
2124    this.opinionRating(this.productViewTabs)
2125    this.opinionGradeDialog(this.productViewTabs)
2126
2127    // Запуск функций капчи
2128    this.captchaRefresh(this.productViewTabs)
2129
2130    // Запуск функций показать все отзывы
2131    this.opinionItems(this.productViewTabs)
2132
2133    // Запуск функций навигации отзывов
2134    this.opinionNavs(this.productViewTabs)
2135
2136    // Запуск функций навигации отзывов
2137    this.opinionAnswer(this.productViewTabs)
2138    
2139  }
2140
2141  // Слайдер доп. изображений
2142  swiperThumbs = (type) => {
2143    // console.log('mod swiperImages', mod);
2144    if (type === 'productView' || type === 'productViewMod') {
2145      return false
2146    }
2147    // Слайдер товаров
2148    const swiperThumb = new Swiper('.thumblist .swiper', {
2149      loop: false,
2150      slidesPerView: '3',
2151      spaceBetween: 16,
2152      freeMode: true,
2153      watchSlidesProgress: true,
2154      navigation: {
2155        nextEl: '.swiper-button-next',
2156        prevEl: '.swiper-button-prev',
2157      },
2158      pagination: {
2159        enabled: false,
2160        el: '.swiper-pagination',
2161        clickable: true,
2162        type: 'bullets',
2163        dynamicBullets: true,
2164      },
2165      on: {
2166        init: function(){
2167          // Скрыть слайдер если 1 изображение
2168          if (this.slides.length === 1) {
2169            this.el.parentElement.style.display = 'none'
2170          }
2171          // Скрыть навигацию
2172          if (this.isLocked) {
2173            this.navigation.nextEl.style.display = 'none'
2174            this.navigation.prevEl.style.display = 'none'
2175          }
2176        }
2177      }
2178    })
2179  }
2180
2181  // Модификации
2182  modifications = (productViewBlock) => {
2183    if (productViewBlock === null) return false
2184    
2185    // Создаем объект модификации
2186    const modView = new Object()
2187    // Блок карточки товара
2188    modView.block = productViewBlock
2189    // Все возможные модификации
2190    modView.slugs = modView.block.querySelectorAll('.goodsModificationsSlug')
2191    // Кнопки модификации
2192    modView.buttons = modView.block.querySelectorAll('.modifications-button')
2193    // Селекты модификации
2194    modView.selects = modView.block.querySelectorAll('.modifications-props__select')
2195
2196    // Обработка объекта модификации
2197    function handleModView(modView){
2198      modView.activedButtons = getActivedButtons(modView.buttons)
2199      modView.activedButtonsId = getActivedButtonsId(modView.activedButtons)
2200      modView.activedSlug = modView.block.querySelector('.goodsModificationsSlug[slugs="'+ modView.activedButtonsId.join('_') +'"]')
2201      modView.availableSlugs = getFlatArray(getAvailableSlugs(modView.activedButtonsId, modView.slugs))
2202      modView.availableButtonsId = getFlatArray(getAvailableButtons(modView.availableSlugs))
2203      modView.disableButtonsId = getDisableButtonsId(getButtonsId(modView.buttons), getFlatArray(modView.availableButtonsId))
2204
2205      handleModActivedSlugs(modView.activedSlug, modView.slugs)
2206      handleModDisableButtons(modView.disableButtonsId, modView.buttons)
2207    }
2208    // Запуск функций обработки объекта модификации
2209    handleModView(modView)
2210
2211    // Запуск функций при клике на кнопку модификации
2212    modView.buttons.forEach(button => button.addEventListener('click', handleModClick))
2213      
2214    // Фукнции при клике на кнопку модификации
2215    function handleModClick(event) {
2216      event.preventDefault()
2217      // console.log('event', event);
2218      // Кнопка модификации
2219      const button = event.target.closest('.modifications-button')
2220      const parents = button.parentElement.querySelectorAll('.modifications-button')
2221      // Удаляем все активные классы у кнопок
2222      parents.forEach(button => button.classList.remove('is-actived'))
2223      // Удаляем все заблокированные классы у кнопок
2224      modView.buttons.forEach(button => button.classList.remove('is-disabled'))
2225      // Добавляем активные классы у кнопок
2226      button.classList.add('is-actived')
2227
2228      // Запуск функций обработки модификации
2229      handleModView(modView)
2230      handleModDisableButtons(modView.disableButtonsId, modView.buttons)
2231
2232      // console.log('modView', modView);
2233    }
2234
2235    // Получаем активные модификации
2236    function getActivedButtons(buttons){
2237      return Array.from(buttons).filter(button => button.classList.contains('is-actived'))
2238    }
2239
2240    // Получаем ID активных модификаций
2241    function getActivedButtonsId(activedButtons){
2242      return activedButtons.map(button => button.getAttribute('data-id')).sort((a, b) => a - b)
2243    }
2244
2245    // Получаем доступные модификации
2246    function getAvailableSlugs(activedButtonsId, slugs){
2247      return activedButtonsId.map(id => {
2248        return Array.from(slugs).filter(slug => {
2249          // Если одномерные модификации
2250          if (getSlug(slug).length === 1){
2251            return getSlug(slug)
2252          } else if (getSlug(slug).includes(id)) {
2253            return getSlug(slug)
2254          }
2255        })
2256      })
2257    }
2258
2259    // Получаем доступные кнопки модификации
2260    function getAvailableButtons(availableSlugs){
2261      return getFlatArray(availableSlugs).map(slug => getSlug(slug))
2262    }
2263
2264    // Получить ИД всех кнопок модификаций
2265    function getButtonsId(buttons){
2266      return Array.from(buttons).map(button => button.getAttribute('data-id'))
2267    }
2268
2269    // Получить ИД недоступных кнопок модификаций
2270    function getDisableButtonsId(buttons, availableButtons){
2271      return buttons.filter(button => !getFlatArray(availableButtons).includes(button))
2272    }
2273
2274    // Получить массив модификаций
2275    function getSlug(slug){
2276      return slug.getAttribute('slugs').split('_')
2277    }
2278
2279    // Убираем вложенность уникального массива модификаций
2280    function getFlatArray(array) {
2281      return Array.from(new Set(array.flat(Infinity)))
2282    }
2283
2284    // Определяем активные скрытые данные модификации
2285    function handleModActivedSlugs(activedSlug, slugs){
2286      // Удаляем все классы
2287      slugs.forEach(slug => slug.classList.remove('is-active'))
2288      // Если найдена модификация
2289      if (activedSlug) {
2290        activedSlug.classList.add('is-active')
2291        // Обновляем данные модификации
2292        handleModUpdate(activedSlug)
2293      } else {        
2294        console.error('Модификация не выбрана. Проверьте настройки товара.');
2295        alert('Модификация не выбрана. Проверьте настройки товара.')
2296      }
2297    }
2298
2299    // Определяем заблокированные кнопки
2300    function handleModDisableButtons(disableButtonsId, buttons){
2301      disableButtonsId.forEach(id => {
2302        buttons.forEach(button => {
2303          if (button.getAttribute('data-id') === id) {
2304            button.classList.add('is-disabled')
2305          }
2306        })
2307      })
2308      
2309    }
2310
2311    // Обновление модификации
2312    function handleModUpdate(modificationBlock, goodsModView = modView.block.querySelector('.productView')) {
2313      console.info(modificationBlock)
2314      // Переменные выбранной модификации
2315      const modificationId = modificationBlock.querySelector('[name="id"]').value
2316      const modificationPriceNow = parseInt(modificationBlock.querySelector('[name="price_now"]').value)
2317      const modificationPriceNowFormated = modificationBlock.querySelector('.price_now_formated').innerHTML
2318      const modificationPriceOld = parseInt(modificationBlock.querySelector('[name="price_old"]').value)
2319      const modificationPriceOldFormated = modificationBlock.querySelector('.price_old_formated').innerHTML
2320      const modificationRestValue = parseInt(modificationBlock.querySelector('[name="rest_value"]').value)
2321      const modificationMeasure = modificationBlock.querySelector('[name="measure_name"]').value
2322      const modificationArtNumber = modificationBlock.querySelector('[name="art_number"]').value
2323      const modificationDescription = modificationBlock.querySelector('.description').innerHTML
2324      const modificationModImageId = parseInt(modificationBlock.querySelector('[name="goods_mod_image_id"]').value)
2325      // Переменные отображаемой модификации
2326      const goodsModId = goodsModView.querySelector('[name="form[goods_mod_id]"]')
2327      const goodsPriceBlock = goodsModView.querySelector('.productView__price')
2328      const goodsPriceNow = goodsPriceBlock.querySelector('.price__now')
2329      const goodsPriceOld = goodsPriceBlock.querySelector('.price__old')
2330      const goodsModRestBlock = goodsModView.querySelector('.productView__available')
2331      const goodsModRestValue = goodsModRestBlock.querySelector('.productRestValue')
2332      const goodsQty = goodsModView.querySelector('.productView__qty')
2333      const goodsQtyInput = goodsQty.querySelector('.qty__input')
2334      const goodsArtNumberBlock = goodsModView.querySelector('.productView__articles')
2335      const goodsArtNumber = goodsArtNumberBlock.querySelector('.productView__article')
2336      const goodsModDescription = goodsModView.querySelector('.modifications__description')
2337      const goodsModAddPrice = goodsModView.querySelector('.productView__add-price')
2338
2339      // Цена товара
2340      goodsPriceNow.setAttribute('data-price', modificationPriceNow)
2341      goodsPriceNow.innerHTML = modificationPriceNowFormated
2342      goodsModAddPrice.innerHTML = modificationPriceNowFormated
2343
2344      const goodsPriceNowData = parseInt(goodsPriceNow.getAttribute('data-price'))
2345
2346      
2347      if (modificationPriceOld > modificationPriceNow) {
2348        console.log('good');
2349        goodsPriceOld.setAttribute('data-price', modificationPriceOld)
2350        goodsPriceOld.innerHTML = modificationPriceOldFormated
2351        goodsPriceOld.style.display = ''
2352        document.querySelector('.productView__supplier').classList.remove('is-hide')
2353      } else {
2354        console.log('bad');
2355        goodsPriceOld.setAttribute('data-price', '')
2356        goodsPriceOld.innerHTML = ''
2357        goodsPriceOld.style.display = 'none'
2358        document.querySelector('.productView__supplier').classList.add('is-hide')
2359      }
2360      
2361      console.log('modificationPriceNow', modificationPriceNow);
2362      console.log('modificationPriceOld', modificationPriceOld);
2363      console.log('modificationPriceOldFormated', modificationPriceOldFormated);
2364      
2365
2366
2367      // Старая цена товара
2368      console.log('goodsPriceOld', goodsPriceOld);
2369      console.log('goodsPriceNowData', goodsPriceNowData);
2370      // if (goodsPriceOldData > goodsPriceNowData) {
2371      //   if (goodsPriceOld === null) {
2372      //     const priceOld = document.createElement('del')
2373      //     priceOld.className = 'price__old'
2374      //     priceOld.setAttribute('data-price', modificationPriceOld)
2375      //     priceOld.innerHTML = modificationPriceOldFormated
2376      //     priceOld.style.display = ''
2377      //     goodsPriceBlock.append(priceOld)
2378      //   } else {
2379      //     console.log('goodsPriceOld', goodsPriceOld);
2380      //   }
2381      // } else {
2382      //   // if (goodsPriceOld) {
2383      //   //   // goodsPriceOld.style.display = 'none'
2384      //   //   goodsModView.querySelector('.productView__supplier').style.display = 'none'
2385      //   // }
2386        
2387      // }
2388      
2389      product.priceDiff(goodsModView, 'money')
2390
2391      // Наличие
2392      if (modificationRestValue > 0 && modificationRestValue < 10) {
2393        // Мало в наличии
2394        modView.block.classList.remove('productView__empty')
2395        goodsModRestBlock.classList.remove('rest-zero', 'rest-alot')
2396        goodsModRestBlock.classList.add('rest-few')
2397        goodsModRestValue.setAttribute('data-value', modificationRestValue)
2398        goodsModRestValue.querySelector('strong').innerHTML = modificationRestValue + ' ' + modificationMeasure
2399      } else if (modificationRestValue > 9) {
2400        // Много в наличии
2401        modView.block.classList.remove('productView__empty')
2402        goodsModRestBlock.classList.remove('rest-zero', 'rest-few')
2403        goodsModRestBlock.classList.add('rest-alot')
2404        goodsModRestValue.setAttribute('data-value', modificationRestValue)
2405        goodsModRestValue.querySelector('strong').innerHTML = modificationRestValue + ' ' + modificationMeasure
2406      } else {
2407        // Нет в наличии
2408        modView.block.classList.add('productView__empty')
2409        goodsModRestBlock.classList.remove('rest-few', 'rest-alot')
2410        goodsModRestBlock.classList.add('rest-zero')
2411        goodsModRestValue.setAttribute('data-value', modificationRestValue)
2412        goodsModRestValue.querySelector('strong').innerHTML = 'Нет'
2413        goodsQtyInput.value = 1
2414        goodsQtyInput.setAttribute('max', 1)
2415      }
2416
2417      // Если включено в настройках 'Отключить возможность класть в корзин
2417у больше товара, чем есть в наличии'
2418      if (goodsQty.classList.contains('has-max')) {
2419        goodsQtyInput.setAttribute('max', modificationRestValue)
2420      } else {
2421        goodsQtyInput.setAttribute('max', 9999)
2422      }
2423
2424      // Покажем артикул модификации товара, если он указан
2425      if (modificationArtNumber) {
2426        goodsArtNumberBlock.classList.remove('is-hide')
2427        goodsArtNumber.innerHTML = modificationArtNumber
2428      } else {
2429        goodsArtNumberBlock.classList.add('is-hide')
2430        goodsArtNumber.innerHTML = ''
2431      }
2432
2433      // Описание модификации товара. Покажем если оно есть, спрячем если его у модификации нет
2434      if (modificationDescription) {
2435        goodsModDescription.classList.remove('is-hide')
2436        goodsModDescription.innerHTML = modificationDescription
2437      } else {
2438        goodsModDescription.classList.add('is-hide')
2439        goodsModDescription.innerHTML = ''
2440      }
2441
2442      // Идентификатор товарной модификации
2443      goodsModId.value = modificationId
2444      goodsQtyInput.setAttribute('name', 'form[goods_mod_id][' + modificationId + ']')
2445
2446      // Обновляем изображние модификации товара, если оно указано
2447      handleModImage(modificationModImageId, goodsModView);
2448
2449    }
2450
2451    // Меняет главное изображение товара на изображение с идентификатором
2452    function handleModImage(goodsModImageId, block) {
2453      // Если не указан идентификатор модификации товара, значит ничего менять не нужно.
2454      if(!goodsModImageId) {return false;}
2455      // Блок с изображением выбранной модификации товара
2456      const goodsModImageBlock = block.querySelector('.thumblist [data-id="' + goodsModImageId + '"')
2457      // Изображение модификации товара, на которое нужно будет изменить главное изображение товара.
2458      const mediumImageUrl = goodsModImageBlock.querySelector('a').getAttribute('href')
2459      // Блок, в котором находится главное изображение товара
2460      const mainImageBlock = block.querySelector('.productView__image')
2461      // Главное изображение, в которое будем вставлять новое изображение
2462      const mainImage = mainImageBlock.querySelector('img')
2463      // Если изображение модификации товара найдено - изменяем главное изображение
2464      mainImage.setAttribute('src', mediumImageUrl)
2465      mainImage.parentElement.setAttribute('href', mediumImageUrl)
2466      // Изменяем идентификатор главного изображения
2467      mainImageBlock.setAttribute('data-id', goodsModImageId)
2468      return true
2469    }
2470
2471  }
2472
2473  // Скрыть/Показать короткое описания
2474  shortDescriptions = (productViewBlock) => {
2475    const text = productViewBlock.querySelector('.productView__desc-text')
2476    const more = productViewBlock.querySelector('.productView__desc-more')
2477
2478    if (text && more) {
2479      // Скрыть/Показать
2480      setTimeout(() => {
2481        text.querySelector('span').offsetHeight > 105 ? more.classList.remove('is-hide') : more.classList.add('is-hide')
2482      }, 1000);
2483
2484      // Действие при клике
2485      more.addEventListener('click', handleDescMoreClick)
2486
2487      // Функции при клике
2488      function handleDescMoreClick(event){
2489        event.preventDefault()
2490        const button = event.target.closest('.productView__desc-more')
2491        const content = button.previousElementSibling
2492        isActived(button)
2493        isActived(content)
2494        changeTxt(button)
2495      }
2496    }
2497
2498  }
2499
2500  // Скрыть/Показать характеристики
2501  shortFeatures = (productViewBlock) => {
2502    const block = productViewBlock.querySelector('.productView__features-short')
2503    if (!block) return false
2504    const items = block.querySelectorAll('.features__item')
2505    const more = block.querySelector('.features__more')
2506    // Если больше 6 характеристик, показываем кнопку
2507    if (items.length > 6) more.classList.remove('is-hide')
2508
2509    // Действие при клике
2510    more.addEventListener('click', handleFeaturesMoreClick)
2511
2512    // Функции при клике
2513    function handleFeaturesMoreClick(event){
2514      event.preventDefault()
2515      const button = event.target.closest('.features__more')
2516      const content = event.target.closest('.productView__features-short')
2517      isActived(button)
2518      isActived(content)
2519      changeTxt(button)
2520    }
2521  }
2522
2523  // Рейтинг. Добавление отзыва о товаре
2524  opinionRating = (productViewTabs) => {
2525    if (!productViewTabs) return false
2526    const rating = productViewTabs.querySelector('.goodsOpinionRating')
2527    if (rating){
2528      $(rating).rating()
2529    } else {
2530      return false
2531    }
2532
2533    // Рейтинг. 
2534    const reviewCount = $('.opinion__item').length;
2535    if(reviewCount) {
2536      for(let i = 0;i < 6;i++) {
2537        const currentRatingStage = $('.opinion__item[data-rating='+ i +']').length;
2538        $('.grade__item[data-rating='+ i +'] .grade__count').text(currentRatingStage);
2539        const percent = parseInt(100 / (reviewCount / currentRatingStage));
2540        $('.grade__item[data-rating='+ i +'] .grade__status-active').css('width', percent + '%');
2541      }
2542    }
2543  }
2544
2545  // Оптовая цена
2546  opinionGradeDialog = (productViewTabs) => {
2547    const block = productViewTabs.querySelector('.opinion__score-inner')
2548    if (!block) return false
2549    const button = block.querySelector('i')
2550    const content = block.querySelector('dialog')
2551
2552    // Действие при клике
2553    button.addEventListener('click', handleDialogClick)
2554
2555    // Функции при клике
2556    function handleDialogClick(event){
2557      event.preventDefault()
2558      isActived(button, 'is-opened')
2559      isActived(content, 'is-opened')
2560      content.toggleAttribute('open')
2561      $('#overlay').addClass('is-opened transparent')
2562    }
2563  }
2564
2565  // Капча
2566  captchaRefresh = (productViewTabs) => {
2567    const content = productViewTabs.querySelector('.opinion__captcha')
2568    if (!content) return false
2569    const button = content.querySelector('.captcha__refresh')
2570    const image = content.querySelector('.captcha__image')
2571
2572    // Действие при клике
2573    button.addEventListener('click', handleCaptchaClick)
2574
2575    // Функции при клике
2576    function handleCaptchaClick(event){
2577      captchaRefreshImage(event.target, 1, 1)
2578      image.setAttribute('src', image.getAttribute('src') + '&rand' + Math.random(0, 10000))
2579    }
2580
2581    // Крутит изображение при обновлении картинки защиты от роботов
2582    function captchaRefreshImage(img, num, cnt){
2583      if (cnt > 13) {return false}
2584      img.setAttribute('src', img.getAttribute('rel') + 'icon/refresh/' + num + '.gif')
2585      num = num == 6 ? 0 : num
2586    }
2587  }
2588
2589  // Показать/Скрыть все отзывы
2590  opinionItems = (productViewTabs) => {
2591    const block = productViewTabs.querySelector('.productView__opinion')
2592    if (!block) return false
2593    const items = block.querySelector('.opinion__items')
2594    const buttons = block.querySelector('.opinion__buttons')
2595
2596    // Показать/Скрыть кнопки
2597    if (!items) return false
2598    items.childElementCount > 4 ? buttons.classList.remove('is-hide') : buttons.classList.add('is-hide')
2599
2600    // Действие при клике
2601    buttons.addEventListener('click', handleOpinionItems)
2602
2603    // Функции при клике
2604    function handleOpinionItems(event){
2605      event.preventDefault()
2606      isActived(items)
2607      isActived(buttons)
2608      changeTxt(buttons)
2609    }
2610  }
2611
2612  // Навигация отзывов
2613  opinionNavs = (productViewTabs) => {
2614    const block = productViewTabs.querySelector('.productView__opinion')
2615    const navs = block.querySelectorAll('.opinion__nav')
2616    const items = block.querySelectorAll('.opinion__item')
2617    const buttons = block.querySelector('.opinion__buttons')
2618
2619    navs.forEach(nav => nav.addEventListener('click', handleNavClick))
2620
2621    function handleNavClick(event){
2622      const buttonNav = event.target.parentElement
2623      const buttonId = buttonNav.getAttribute('data-nav')
2624      const buttonCount = buttonNav.getAttribute('data-count')
2625
2626      // Если нет отзывов по фильтру
2627      if (buttonCount == 0) {
2628        return false
2629      }
2630      
2631      navs.forEach(btn => btn.classList.remove('is-actived'))
2632      buttonNav.classList.add('is-actived')
2633
2634      // Все
2635      if (buttonId == 'all') {
2636        items.forEach((item) => (item.style.display = ''))
2637        buttons.style.display = ''
2638      }
2639
2640      // Положительные
2641      if (buttonId == 'good') {
2642        items.forEach((item) => (item.style.display = item.matches('[data-nav-content="good"]') ? 'block' : 'none'))
2643        buttons.style.display = 'none'
2644      }
2645
2646      // Отрицательные
2647      if (buttonId == 'bad') {
2648        items.forEach((item) => (item.style.display = item.matches('[data-nav-content="bad"]') ? 'block' : 'none'))
2649        buttons.style.display = 'none'
2650      }
2651
2652    }
2653  }
2654
2655  // Показать/Скрыть ответ Админа
2656  opinionAnswer = (productViewTabs) => {
2657    const button = productViewTabs.querySelector('.opinion__answer-button')
2658    // const content = productViewTabs.querySelector('.opinion__answer-content')
2659
2660    if (!button) return false
2661
2662    button.addEventListener('click', handleAnswerClick)
2663
2664    function handleAnswerClick(event){
2665      event.preventDefault()
2666      isActived(event.target.parentElement)
2667      isActived(event.target.parentElement.previousElementSibling, 'is-hide')
2668      changeTxt(event.target.parentElement)
2669    }
2670  }
2671
2672  // // Форма
2673  // opinionForm = (productViewTabs) => {
2674  //   const form = productViewTabs.querySelector('.opinion__form')
2675  //   const button = productViewTabs.querySelector('.opinion__block-button')
2676
2677  //   button.addEventListener('click', handleFormClick)
2678
2679  //   function handleFormClick(event){
2680  //     event.preventDefault()
2681  //     console.log('event', event);
2682  //     $(form).validate()
2683  //     $(form).submit()
2684  //   }
2685  // }
2686
2687}
2688
2689///////////////////////////////////////
2690// Корзина
2691///////////////////////////////////////
2692class Cart {
2693  constructor() {
2694    // Функции при клике
2695    this.onClick = function () {
2696      const content = document.querySelector('.page-cart')
2697      // Если нет контента
2698      if (!content) {
2699        return false
2700      }
2701
2702      content.addEventListener('click', function (event) {
2703        // console.log('event1', event)
2704        // console.log('event1.target', event.target)
2705        // Объявление переменных
2706        const remove = event.target.closest('.cartTable__remove')
2707        const start = event.target.closest('.startOrder')
2708        const close = event.target.closest('.closeOrder')
2709        const clear = event.target.closest('.cartTable__clear')
2710        const make = event.target.closest('.confirmOrder')
2711
2712        if (remove) {
2713          event.preventDefault()
2714          cart.removeItem(remove)
2715        } else if (start) {
2716          cart.orderStart()
2717        } else if (close) {
2718          cart.orderClose()
2719        } else if (make) {
2720          // console.log('make', make);
2721          event.preventDefault()
2722          const form = $('.order-fast__form')
2723          form.validate()
2724          form.submit()
2725        } else if (clear) {
2726          const yep = confirm('Вы действительно хотите удалить все товары из корзины?')
2727          if (yep == true) {
2728            return true
2729          } else {
2730            event.preventDefault()
2731            return false
2732          }
2733        }
2734      })
2735    }
2736
2737    // Удаление товара из корзины
2738    this.removeItem = function (obj) {
2739      const yep = confirm('Вы точно хотите удалить товар из корзины?')
2740      if (yep == true) {
2741        obj.closest('.cartTable__item').style.display = 'none'
2742        const url = obj.getAttribute('href')
2743        $.ajax({
2744          url: url,
2745          cache: false,
2746          success: function (data) {
2747            $('.page-cart').html($(data).find('.page-cart').html())
2748            // Вызов функции минимального заказа в корзине
2749            cart.minSum()
2750            cart.orderStart()
2751            quantity.init()
2752          },
2753        })
2754      } else {
2755        return false
2756      }
2757    }
2758
2759    // Функция вычисления остатка до минимальной суммы заказа
2760    this.minSum = function () {
2761      if ($('.cartTotal__min').length) {
2762        const minPrice = parseInt($('.cartTotal__min-price').data('price'))
2763        const totalSum = parseInt($('.cartSumDiscount').data('price'))
2764        // console.info('minPrice', minPrice)
2765        // console.info('totalSum', totalSum)
2766        if (minPrice > totalSum) {
2767          const diff = minPrice - totalSum
2768          console.info('diff', diff)
2769          $('.cartTotal__min-price').find('.num').text(addSpaces(diff))
2770          $('.cartTotal__min-price').addClass('show')
2771          $('.total__button').attr('disabled', true).addClass('is-disabled')
2772          // $('.cartTotal__min').removeClass('is-hide')
2773        } else {
2774          console.info('no-diff')
2775          $('.total__buttons button').attr('disabled', false).removeClass('is-disabled')
2776          $('.cartTotal__min').addClass('is-hide')
2777        }
2778      }
2779    }
2780
2781    // Функция быстрого оформления заказа в корзине
2782    this.orderStart = function () {
2783      const globalOrder = $('.cartTable__order')
2784      const pageCart = $('.page-cart')
2785      //объект блока куда будет выводиться форма быстрого заказа
2786      const OrderAjaxBlock = $('.cartTable__ajax')
2787      const urlQuickForm = '/cart/add' // адрес страницы с формой
2788
2789      // данные которые отарвятся на сервер чтобы получить только форму быстрого заказа без нижней части и верхней части сайта
2790      const quickFormData = [
2791        { name: 'ajax_q', value: 1 },
2792        { name: 'fast_order', value: 1 },
2793      ]
2794      pageCart.addClass('is-started')
2795      // $('.qty__input').attr('readonly', 'readonly')
2796      globalOrder.show('slow')
2797      $.ajax({
2798        type: 'POST',
2799        cache: false,
2800        url: urlQuickForm,
2801        data: quickFormData,
2802        success: function (data) {
2803          OrderAjaxBlock.html($(data).find('.order-fast__content').wrap('<div></div>').html()).show('slow')
2804          // $('html, body').delay(400).animate({ scrollTop: globalOrder.offset().top }, 800)
2805          // password.onClick()
2806          // password.onRegistration(document.querySelectorAll('.cartTable__ajax form'))
2807          new Password()
2808          order.onInit()
2809          order.onSelect()
2810          order.coupons()
2811          order.onValidate()
2812          cart.boundInputs()
2813          setTimeout(() => {
2814            supplier()
2815          }, 1000);
2816
2817          // Стили для новых селектов
2818          $('.form__phone').mask('+7 (999) 999-9999')
2819
2820          // Выключение кнопки оформления заказа если не все поля заполнены
2821        },
2822      })
2823      return false
2824    }
2825
2826    // Отменить заказ
2827    this.orderClose = function () {
2828      $('.page-cart').removeClass('is-started')
2829      // $('.qty__input').removeAttr('readonly')
2830      $('.startOrder').removeClass('is-disabled')
2831      $('.cartTable__order').hide('slow')
2832      $('html, body')
2833        .delay(400)
2834        .animate({ scrollTop: $('#main').offset().top }, 800)
2835    }
2836
2837    // Связывание инпут полей между Физ и Юр лицом
2838    this.boundInputs = function () {
2839      $('.form__person').on('input change', function(event){
2840        $('.form__person').val(event.target.value)
2841      })
2842      $('.form__phone').on('input change', function(event){
2843        $('.form__phone').val(event.target.value)
2844      })
2845
2846    }
2847
2848  }
2849}
2850
2851///////////////////////////////////////
2852/* Скрипты для оформления заказов */
2853///////////////////////////////////////
2854class Order {
2855  constructor() {
2856    // Валидация формы в оформлении заказа
2857    this.onValidate = function () {
2858      // console.log('onValidate');
2859      if ($('.order-fast__form').length === 0) {return false}
2860      // Валидация формы
2861      $('.order-fast__form').validate({
2862        validClass: 'valid',
2863        errorPlacement: function (error, element) {
2864          // console.log('error', error);
2865          // console.log('element', element);
2866        },
2867      })
2868
2869      // Выключение кнопки оформления заказа если не все поля заполнены
2870      $('.order-fast__form [required]').on('input change', function () {
2871        // console.log('change', this);
2872        if ($('.order-fast__form').valid()) {
2873          $('.total__buttons button').attr('title', 'Оформить заказ').removeClass('is-disabled')
2874        } else {
2875          $('.total__buttons button').attr('title', 'Заполните все поля').addClass('is-disabled')
2876        }
2877      })
2878
2879      // Проверка обязательных полей
2880      if ($('.order-fast__form').valid()) {
2881        $('.total__buttons button').attr('title', 'Оформить заказ').removeClass('is-disabled')
2882      } else {
2883        $('.total__buttons button').attr('title', 'Заполните все поля').addClass('is-disabled')
2884        $('.order-fast__form input, .order-fast__form textarea, .order-fast__form select').removeClass('error')
2885      }
2886    }
2887
2888    // Регистрация и выбор доставки
2889    this.onInit = function () {
2890      // Выбор даты доставки. Документация к плагину //t1m0n.name/air-datepicker/docs/index-ru.html
2891      $('#deliveryConvenientDate').datepicker({
2892        // Если true, то при активации даты, календарь закроется.
2893        autoClose: true,
2894        // Можно выбрать только даты, идущие за сегодняшним днем, включая сегодня
2895        minDate: new Date(),
2896      })
2897
2898      // Действия при выборе варианта доставки на этапе оформления заказа
2899      $('.order-delivery__radio').on('click', function (d) {
2900        // Отображение вариантов оплаты при выборе доставки
2901        const ID = $('input[name="form[delivery][id]"]:checked').val()
2902        $('.order-payment').hide()
2903        $('.order-payment[rel="' + ID + '"]').show()
2904        $('.order-payment[rel="' + ID + '"]')
2905          .find('input:first')
2906          .click()
2907
2908        $('.order-delivery__radio').each(function () {
2909          $(this).prop('checked', false)
2910          $(this).parent().removeClass('is-actived')
2911        })
2912
2913        $('.order-delivery-zone__radio').each(function () {
2914          $(this).prop('checked', false)
2915          $(this).parent().removeClass('is-actived')
2916        })
2917
2918        const val = $(this).val()
2919        const fz = $($('.order-delivery-zone__radio[data-id=' + val + ']')[0])
2920        $(this).prop('checked', true)
2921        fz.prop('checked', true)
2922        $(this).parent().addClass('is-actived')
2923
2924        const price = $(this).attr('price')
2925        const priceBlock = $('.order-delivery__item[data-id=' + val + ']')
2926          .find('.order-delivery__price')
2927          .find('.num')
2928        // Обновление цены при наличии зоны
2929        const cartSumTotal = $('.cartSumDelivery .num')
2930        const zonePrice = $('.order-delivery-zone__radio:checked').attr('price')
2931        if (zonePrice > 0) {
2932          priceBlock.text(addSpaces(zonePrice))
2933          cartSumTotal.text(addSpaces(zonePrice))
2934        } else {
2935          priceBlock.text(price)
2936          cartSumTotal.text(addSpaces(price))
2937        }
2938
2939        // Обновление цены с учетом доставки
2940        const cartSumDiscount = $('.cartSumDiscount:eq(0)').attr('data-price')
2941        const newSum = parseFloat(cartSumDiscount) + parseInt(priceBlock.eq(0).text())
2942        $('.cartSumTotal .num').text(addSpaces(Math.round(newSum)))
2943        console.log('cartSumDiscount', cartSumDiscount);
2944        console.log('newSum', newSum);
2945
2946        // Скрытие необязательных полей при выборе самовывоза
2947        // if ($(this).data('name') == 'Самовывоз') {
2948        //   $('.order-fast__form').addClass('pickup')
2949        //   $('.address input, .address textarea').val('Самовывоз')
2950        //   $('#deliveryConvenientDate').val('01.01.2220')
2951        //   $('.total__buttons button').attr('title', 'Оформить заказ').removeClass('is-disabled')
2952        // } else {
2953        //   $('.order-fast__form').removeClass('pickup')
2954        //   $('.address input, .address textarea').val('')
2955        //   $('#deliveryConvenientDate').val('')
2956        // }
2957      })
2958
2959      // Действия при выборе зоны внутри варианта доставки на этапе оформления заказа
2960      $('.order-delivery-zone__radio').on('click', function () {
2961        const val = $(this).attr('data-id')
2962        const price = $(this).attr('price')
2963        const priceBlock = $('.order-delivery__item[data-id=' + val + ']')
2964          .find('.order-delivery__price')
2965          .find('.num')
2966        // Обновление цены
2967        priceBlock.text(addSpaces(price))
2968
2969        $('.order-delivery__radio').each(function () {
2970          $(this).prop('checked', false)
2971          if ($(this).val() == val) {
2972            $(this).prop('checked', true)
2973          } else {
2974            $(this).prop('checked', false)
2975          }
2976        })
2977
2978        // Выбор варианта оплаты при выборе зоны доставки
2979        const ID = $('input[name="form[delivery][id]"]:checked').val()
2980        $('.order-payment').hide()
2981        $('.order-payment[rel="' + ID + '"]').show()
2982        $('.order-payment[rel="' + ID + '"]')
2983          .find('input:first')
2984          .click()
2985
2986        // Обновление цены с учетом доставки
2987        const cartSumDiscount = $('.cartSumDiscount:eq(0) .num').text().toString().replace(/\s/g, '')
2988        const newSum = parseInt(cartSumDiscount) + parseInt(priceBlock.text())
2989        $('.cartSumTotal .num').text(addSpaces(newSum))
2990        $('.cartSumDelivery .num').text(addSpaces(price))
2991        console.log('newSum2', newSum)
2992      })
2993
2994      // Выбор оплаты по клику
2995      $('.order-payment__radio').on('click', function () {
2996        const paymentDescription = $('.order-payment__radio:checked').parent().find('.order-payment__desc').html()
2997        const payDesc = $('.order-payment__description')
2998        payDesc.html(paymentDescription)
2999        if (paymentDescription === undefined) {
3000          payDesc.addClass('is-hide').removeClass('is-show')
3001        } else {
3002          payDesc.addClass('is-show').removeClass('is-hide')
3003        }
3004      })
3005
3006      // Выбор оплаты по умолчанию
3007      $('.order-payment__radio').each(function () {
3008        // console.log('each', $(this).parent());
3009        const paymentDescription = $('.order-payment__radio:checked').parent().find('.order-payment__desc').html()
3010        const payDesc = $('.order-payment__description')
3011        payDesc.html(paymentDescription)
3012        if (paymentDescription == undefined) {
3013          payDesc.addClass('is-hide').removeClass('is-show')
3014        } else {
3015          payDesc.addClass('is-show').removeClass('is-hide')
3016        }
3017      })
3018
3019      setTimeout(function () {
3020        $('.order-delivery__select').trigger('change')
3021      }, 100)
3022    }
3023
3024    // Выбор доставки и оплаты
3025    this.onSelect = function () {
3026      // Выбор доставки
3027      $('.order-delivery__select').on('change', function () {
3028        const selectedDelId = $('.order-delivery__select').find('option:selected').attr('data-id')
3029        $('.order-delivery-zone__selectBox').addClass('is-hide').removeClass('is-show')
3030        $('.order-delivery-zone__selectBox[data-id="' + selectedDelId + '"]')
3031          .addClass('is-show')
3032          .removeClass('is-hide')
3033        $('.order-delivery-zone__selectBox option').attr('selected', false)
3034        $('.order-delivery-zone__selectBox[data-id="' + selectedDelId + '"] option:first-of-type').attr('selected', true)
3035        $('.order-delivery__radio[value="' + selectedDelId + '"]').click()
3036        const WithoutZone = $('.order-delivery__item[data-id=' + selectedDelId + '] .order-delivery__radio:checked').attr('pricewithoutzones')
3037        const WithZone = $('.order-delivery__item[data-id=' + selectedDelId + '] .order-delivery-zone__radio:checked').attr('price')
3038
3039        let startprice = WithoutZone
3040        if (WithZone >= 0) {
3041          startprice = WithZone
3042        }
3043
3044        $('.order-delivery__changeprice').text(addSpaces(startprice))
3045        $('.cartSumDelivery .num').text(addSpaces(startprice))
3046        $('.order-payment').addClass('is-hide').removeClass('is-show')
3047        $('.order-payment[rel="' + selectedDelId + '"]')
3048          .addClass('is-show')
3049          .removeClass('is-hide')
3050        const startInputId = $('.order-delivery__radio:checked').attr('value')
3051        $('.order-payments__items .order-payment input').attr('checked', false)
3052        $('.order-payments__items .order-payment[rel="' + startInputId + '"] input').each(function () {
3053          $(this).click()
3054          return false
3055        })
3056
3057        $('.order-payment__select option:first-child').prop('selected', true)
3058
3059        // Вывод описания доставки
3060        const delDesc = $('.order-delivery__desc')
3061        const deliveryDescription = $('.order-delivery__radio:checked').parent().find('.order-delivery__desc').html()
3062        delDesc.html(deliveryDescription)
3063        if (deliveryDescription == undefined) {
3064          delDesc.addClass('is-hide').removeClass('is-show')
3065        } else {
3066          delDesc.addClass('is-show').removeClass('is-hide')
3067        }
3068
3069        // Вывод описания оплаты
3070        const paymentDescription = $('.order-payment__radio:checked').parent().find('.order-delivery__desc').html()
3071        const payDesc = $('.order-payment__description')
3072        payDesc.html(paymentDescription)
3073        if (paymentDescription == undefined) {
3074          payDesc.addClass('is-hide').removeClass('is-show')
3075        } else {
3076          payDesc.addClass('is-show').removeClass('is-hide')
3077        }
3078      })
3079
3080      // Выбор зоны доставки
3081      $('.order-delivery-zone__select').on('change', function () {
3082        const optValue = $(this).find('option:selected').attr('value')
3083        $('.order-delivery-zone__radio[value="' + optValue + '"]').click()
3084        const WithZone = $('.order-delivery-zone__radio:checked').attr('price')
3085        $('.order-delivery__changeprice').text(addSpaces(WithZone))
3086        $('.cartSumDelivery .num').text(addSpaces(WithZone))
3087      })
3088
3089      // Выбор оплаты
3090      $('.order-payment__select').on('change', function () {
3091        const selectedDelId = $(this).find('option:selected').attr('value')
3092        $('.order-payment__radio[value="' + selectedDelId + '"]').click()
3093        const paymentDescription = $('.order-payment__radio:checked').parent().find('.order-payment__desc').html()
3094        const payDesc = $('.order-payment__description')
3095        payDesc.html(paymentDescription)
3096        if (paymentDescription == undefined) {
3097          payDesc.addClass('is-hide').removeClass('is-show')
3098        } else {
3099          payDesc.addClass('is-show').removeClass('is-hide')
3100        }
3101      })
3102    }
3103
3104    // Отправка купона при оформлении заказа
3105    this.coupons = function () {
3106      const submitBtn = $('.coupon__button')
3107      const couponInput = $('.coupon__code')
3108      const couponParent = couponInput.parent()
3109      const resetBtn = $('.coupon__reset')
3110      const totalCouponBlock = $('.cartTotal__coupons')
3111      const totalDiscountBlock = $('.cartTotal__item-discount')
3112      const couponFake = $('.total__coupon .coupon__code')
3113
3114      // Отправка формы
3115      submitBtn.off('click').on('click', function () {
3116        const url = '/order/stage/confirm'
3117        const val = couponInput.val()
3118        const oldVal = couponInput.attr('data-value')
3119        couponInput.attr('data-value', val)
3120
3121        // Если ничего не ввели
3122        if (val === '') {
3123          couponInput.addClass('error')
3124          return false
3125        }
3126
3127        // Если купон не изменился
3128        // if (val === oldVal) {
3129        //   couponInput.removeClass('focus')
3130        //   return false
3131        // }
3132        
3133        console.log('val', val);
3134
3135        // Получаем данные формы, которые будем отправлять на сервер
3136        const formData = $('#order_form').serializeArray()
3137        formData.push({ name: 'ajax_q', value: 1 })
3138        formData.push({ name: 'only_body', value: 1 })
3139        formData.push({ name: 'form[coupon_code]', value: val })
3140
3141        $.ajax({
3142          type: 'POST',
3143          cache: false,
3144          url: url,
3145          data: formData,
3146          success: function (data) {
3147            // Получаем блок скидки
3148            const discountBlock = $(data).closest('#order_form').find('.order-discount')
3149            const discountName = discountBlock.find('.order-discount__name').text()
3150            const discountPrice = discountBlock.find('.order-discount__value').html()
3151            const discountPercent = discountBlock.find('.order-discount__percent').html()
3152
3153            // Определяем наличие скидки в % или валюте
3154            let discountSum = 0
3155            if (discountPrice) {
3156              discountSum = discountPrice
3157            } else if (discountPercent) {
3158              discountSum = discountPercent
3159            } else {
3160              totalCouponBlock.hide()
3161            }
3162
3163            // Записываем название и размер скидки по купону
3164            totalCouponBlock.find('.cartTotal__label span').html(discountName)
3165            totalCouponBlock.find('.cartSumCoupons').html(discountSum)
3166            totalCouponBlock.show()
3167            totalDiscountBlock.hide()
3168
3169            // Получаем новую итоговую стоимость заказа
3170            const totalBlock = $(data).closest('#order_form').find('.order-total')
3171            const totalSum = totalBlock.find('.cartSumDiscount').data('price')
3172            const deliveryPrice = parseInt($('.cartSumDelivery:eq(0) .num').text())
3173            const newTotalSum = totalSum + deliveryPrice
3174            const cartSumTotal = $('.cartSumTotal:eq(0) .num').text().toString().replace(/\s/g, '')
3175
3176            // Проверяем купон
3177            if (newTotalSum > cartSumTotal) {
3178              couponInput.val('').attr('placeholder', 'Купон неверен').removeClass('focus')
3179              couponParent.removeClass('success').addClass('error')
3180              totalCouponBlock.hide()
3181              totalDiscountBlock.show()
3182              $('.cartSumTotal .num').text(addSpaces(newTotalSum))
3183              console.log('newSum4', newTotalSum)
3184            } else if (newTotalSum == cartSumTotal) {
3185              if (discountName) {
3186                couponInput.addClass('focus')
3187                couponParent.addClass('success')
3188                totalCouponBlock.show()
3189              } else {
3190                couponInput.val('').addClass('error')
3191                couponParent.addClass('error')
3192                totalCouponBlock.hide()
3193              }
3194            } else {
3195              couponInput.addClass('focus')
3196              couponParent.removeClass('error').addClass('success')
3197              totalCouponBlock.show()
3198              // Обновляем значение итоговой стоимости
3199              $('.cartSumTotal').attr('data-price', newTotalSum)
3200              $('.cartSumTotal .num').text(addSpaces(newTotalSum))
3201              $('.cartSumCoupons').attr('data-price', newTotalSum)
3202              $('.cartSumDiscount').attr('data-price', totalSum)
3203              $('.cartSumDiscount .num').text(addSpaces(totalSum))
3204            }
3205
3206            // Тестирование. Проверка переменных
3207            console.log('---', )
3208            console.log('data', data)
3209            console.log('data2', $(data))
3210            console.log('discountName', discountName)
3211            console.log('discountPrice', discountPrice)
3212            console.log('discountPercent', discountPercent)
3213            console.log('totalBlock', totalBlock)
3214            console.log('totalSum', totalSum)
3215            console.log('deliveryPrice', deliveryPrice)
3216            console.log('newTotalSum', newTotalSum)
3217            console.log('---', )
3218          },
3219          error: function () {
3220            console.log('Возникла ошибка: Невозможно отправить форму купона.')
3221          },
3222        })
3223      })
3224
3225      // Сброс
3226      resetBtn.on('click', function () {
3227        couponInput.val('').trigger('input')
3228        setTimeout(function () {
3229          totalCouponBlock.hide()
3230          totalDiscountBlock.show()
3231          const cartSum = $('.cartSumDiscount').data('price')
3232          const deliveryPrice = parseInt($('.cartSumDelivery:eq(0) .num').text())
3233          const newTotalSum = cartSum + deliveryPrice
3234          // Возвращаем значение по умолчанию итоговой стоимости
3235          $('.cartSumTotal .num').text(addSpaces(newTotalSum))
3236          $('.cartSumTotal').attr('data-price', newTotalSum)
3237          $('.cartSumCoupons').attr('data-price', newTotalSum)
3238          $('.cartSumDiscount').attr('data-price', newTotalSum)
3239          $('.cartSumDiscount .num').text(addSpaces(newTotalSum))
3240          couponInput.val('').attr('placeholder', 'Введите купон').removeClass('focus').removeClass('error')
3241          couponParent.removeClass('error').removeClass('success')
3242        }, 500)
3243      })
3244
3245      // Отображение кнопки Сброс
3246      couponInput.on('input', function () {
3247        if ($(this).val()) {
3248          resetBtn.addClass('focus')
3249        } else {
3250          resetBtn.removeClass('focus')
3251        }
3252      })
3253
3254      // Дублированное поле купона
3255      couponFake.on('input', function () {
3256        $('.coupon__code').val($(this).val())
3257      })
3258    }
3259
3260    // Оформление заказа в выпадающей корзине
3261    this.orderCart = function () {
3262      const urlQuickForm = '/cart/add' // адрес страницы с формой
3263
3264      // данные которые отправятся на сервер чтобы получить только форму быстрого заказа
3265      const quickFormData = [
3266        { name: 'ajax_q', value: 1 },
3267        { name: 'fast_order', value: 1 },
3268      ]
3269      // Отправляем запрос
3270      $.ajax({
3271        type: 'POST',
3272        cache: false,
3273        url: urlQuickForm,
3274        data: quickFormData,
3275        success: function (data) {
3276          $.fancybox.open(data, {
3277            keyboard: false,
3278            baseClass: 'fastOrder',
3279            afterShow: function () {
3280              // password.onClick()
3281              // password.capsWarning()
3282              order.onInit()
3283              order.onSelect()
3284              order.coupons()
3285              order.onValidate()
3286              // Стили для новых селектов
3287              $('.form__phone').mask('+7 (999) 999-9999')
3288            },
3289          })
3290        },
3291      })
3292    }
3293  }
3294}
3295
3296// Объявляем конструктор функции пароля
3297const password = new Password()
3298// Объявляем конструктор Сравнения
3299const compare = new Compare()
3300// Объявляем конструктор Товара
3301const product = new Product()
3302// Объявляем конструктор Удаления
3303const remove = new Remove()
3304// Объявляем конструктор Товары
3305// const catalogFilter = new CatalogFilter();
3306// Объявляем конструктор Товар
3307// const goods = new Goods()
3308// Объявляем конструктор Корзины
3309const cart = new Cart()
3310// Объявляем конструктор Заказа
3311const order = new Order()
3312// Объявляем конструктор Заказа
3313const quantity = new Quantity()
3314
3315
3316///////////////////////////////////////
3317// Дополнительные пункты меню в шапке Перенос пунктов меню
3318// TODO Переписать функцию
3319///////////////////////////////////////
3320function mainnav(id, rows, media) {
3321  if (getClientWidth() > media) {
3322    const mainNav = $(id)
3323    const overMenuExist = mainNav.find('.mainnav__overflow li').length
3324    const mainNavList = mainNav.find('.mainnav__list')
3325    const mainNavOverflow = mainNav.find('.mainnav__overflow')
3326    const mainNavMore = $('.mainnav__more')
3327
3328    // Восстановление классов для больших экранов
3329    mainNavOverflow.addClass('dropdown__content')
3330    mainNavMore.addClass('mainnav__more-hidden')
3331
3332    if (overMenuExist) {
3333      mainNavOverflow.find('li').removeClass('mainnav__replaced')
3334      mainNavMore.remove()
3335      mainNavOverflow.find('li').each(function () {
3336        mainNavList.append($(this))
3337      })
3338    }
3339
3340    const menuHeight = rows
3341    const menuWidth = mainNav.width() * menuHeight
3342    const menuCount = mainNavList.find('li').length + 1
3343    let nextCheck = 0
3344
3345    for (let i = 1; i < menuCount; i++) {
3346      const currentWidth = parseInt(Math.ceil(mainNavList.find('li:nth-child(' + i + ')').width())) + 18
3347      nextCheck += currentWidth
3348
3349      if (nextCheck > menuWidth) {
3350        let a = i
3351        for (a; a < menuCount; a++) {
3352          mainNavList.find('li:nth-child(' + a + ')').addClass('mainnav__replaced')
3353        }
3354
3355        mainNav.find('.mainnav__replaced').each(function () {
3356          mainNavOverflow.append($(this))
3357        })
3358
3359        mainNavList.append('<li class="mainnav__more"><a class="mainnav__link flex"><span>Ещё</span><i class="icon-chevron_down"></i></a></li>')
3360
3361        mainNav.find('.mainnav__more').on('click', function () {
3362          if ($(this).hasClass('is-opened')) {
3363            $(this).removeClass('is-opened')
3364            mainNav.removeClass('is-opened')
3365            mainNavOverflow.removeClass('is-opened')
3366            $('#overlay').removeClass('is-opened transparent')
3367          } else {
3368            $(this).addClass('is-opened')
3369            mainNav.addClass('is-opened')
3370            mainNavOverflow.addClass('is-opened')
3371            $('#overlay').addClass('is-opened transparent')
3372          }
3373          // Определение положения кнопки еще
3374          // positionMore()
3375        })
3376
3377        // Определение положения кнопки еще
3378        function positionMore() {
3379          const morePos = mainNav.find('.mainnav__more').position().left
3380          const contentPos = parseInt(morePos) - mainNavOverflow.width() / 4
3381          mainNavOverflow.css({ left: contentPos })
3382        }
3383
3384        return false
3385      }
3386    }
3387  } else {
3388    // Удаление классов для маленьких экранов
3389    $('.mainnav__overflow').removeClass('dropdown__content')
3390    $('.mainnav__more').removeClass('mainnav__more-hidden')
3391  }
3392}
3393
3394///////////////////////////////////////
3395// Отсчет даты до окончания акции
3396///////////////////////////////////////
3397function counterDate() {
3398  const id = $('.counter')
3399  // Если не найдет счетчик прекращаем работу функции
3400  if (!id.length) {
3401    return false
3402  }
3403  // Перебираем каждый счетчик
3404  id.each(function () {
3405    const t = $(this)
3406    // Устанавливаем дату обратного отсчета ММ-ДД-ГГ
3407    const expired = t.attr('data-expired')
3408    const countDownDate = new Date(expired).getTime()
3409    // Обновление счетчика каждую секунду
3410    const x = setInterval(function () {
3411      const now = new Date().getTime()
3412      const distance = countDownDate - now
3413      const days = Math.floor(distance / (1000 * 60 * 60 * 24))
3414      const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
3415      const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))
3416      const seconds = Math.floor((distance % (1000 * 60)) / 1000)
3417      // Вывод
3418      t.find('.days span').text(days)
3419      t.find('.hours span').text(hours)
3420      t.find('.minutes span').text(minutes)
3421      t.find('.seconds span').text(seconds)
3422      // Счетчик завершен
3423      if (distance < 0) {
3424        clearInterval(x)
3425        // t.hide();
3426        t.closest('promotion__item').addClass('.promotion__empty')
3427      } else {
3428        // t.css({'display':'inline-flex'});
3429        // t.prev().hide();
3430        t.closest('promotion__item').removeClass('.promotion__empty')
3431      }
3432    }, 1000)
3433  })
3434}
3435
3436///////////////////////////////////////
3437// Открытие Контактов, Меню, Сравнения, Избранного
3438///////////////////////////////////////
3439function openMenu() {
3440  // Открытие каталога с сохранением вложенности
3441  $('.catalog__open').on('click', function (event) {
3442    event.preventDefault()
3443    const parent = $(this).closest('.parent')
3444    const sub = $(this).parent().next('.catalog__sub')
3445    const open = $(this).closest('.catalog__open')
3446    if (parent.hasClass('is-actived')) {
3447      sub.slideUp(600)
3448      parent.removeClass('is-actived')
3449      open.removeClass('is-actived')
3450    } else {
3451      sub.slideDown(600)
3452      parent.addClass('is-actived')
3453      open.addClass('is-actived')
3454    }
3455  })
3456
3457  // Закрытие всего при нажатии на темную часть
3458  $('#overlay').on('click', function (event) {
3459    event.preventDefault()
3460    if ($(this).hasClass('is-opened')) {
3461      closeAll()
3462    }
3463  })
3464
3465  // Открытие элементов
3466  $('[data-open]').on('click', function (event) {
3467    event.preventDefault()
3468    const value = $(this).data('open')
3469    $('[data-open]').removeClass('is-opened')
3470    $('[data-opened]').removeClass('is-opened')
3471    if ($('[data-opened="' + value + '"]').hasClass('is-opened')) {
3472      $(this).removeClass('is-opened').parent().removeClass('is-opened')
3473      $('#overlay').removeClass('is-opened')
3474      $('[data-opened="' + value + '"]').removeClass('is-opened')
3475    } else {
3476      $(this).addClass('is-opened').parent().addClass('is-opened')
3477      $('#overlay').addClass('is-opened')
3478      $('[data-opened="' + value + '"]').addClass('is-opened')
3479    }
3480  })
3481
3482  // Открыть поиск
3483  $('.header-search__icon').on('click', function (event) {
3484    event.preventDefault()
3485    $('.search').addClass('is-opened')
3486    $('#overlay').addClass('is-opened')
3487    document.querySelector('.search__input').focus()
3488  })
3489}
3490
3491// Функция удаления классов всех активных элементов
3492function closeAll() {
3493  $('div, a, form, span, nav, ul, li, i, dialog').removeClass('is-opened')
3494  $('.overflowMenu').removeClass('is-actived')
3495  $('dialog').removeAttr('open')
3496  // $('.search__reset').click();
3497  $('#overlay').click()
3498  setTimeout(function () {
3499    $('#overlay').removeClass('transparent')
3500    // $('.search__reset').click()
3501    // $('.search').removeClass('is-opened')
3502    // $('.search__input').blur();
3503    $('#overlay').click()
3504  }, 100)
3505}
3506
3507///////////////////////////////////////
3508/* Аякс Отправка формы без обновления страницы */
3509///////////////////////////////////////
3510function ajaxForms(id, flag, successMessage, errorMessage) {
3511  flag = false
3512  // console.log('ajaxForms loaded ', id)
3513  if (!id) {
3514    return false
3515  }
3516  const form = $(id).find('.form__callback')
3517  form.on('submit', function (event) {
3518    event.preventDefault()
3519    if (!flag) {
3520      t = $(this)
3521      const url = t.prop('action')
3522      const formData = t.serializeArray()
3523      formData.push({ name: 'ajax_q', value: 1 })
3524      formData.push({ name: 'only_body', value: 1 })
3525      $.ajax({
3526        method: 'POST',
3527        cache: false,
3528        url: url,
3529        data: formData,
3530        success: function (d) {
3531          const serverCall = JSON.parse(d).status
3532          if (serverCall == 'ok') {
3533            setTimeout(function () {
3534              $.fancybox.close()
3535            }, 2000)
3536            // Функция, которая отображает сообщения пользователю
3537            const content = `
3538							<div class="noty__addto flex">
3539								<div class="noty__title">Успешно!</div>
3540								<div class="noty__content">${successMessage}</div>
3541							</div>
3542						`
3543            notyStart(content, 'success')
3544            flag = true
3545          }
3546        },
3547        error: function (d) {
3548          callbackError()
3549        },
3550      })
3551    } else {
3552      callbackError()
3553    }
3554  })
3555
3556  function callbackError() {
3557    $(id).addClass('error')
3558    // Функция, которая отображает сообщения пользователю
3559    const content = `
3560			<div class="noty__addto flex">
3561				<div class="noty__title">Ошибка!</div>
3562				<div class="noty__content">${errorMessage}</div>
3563			</div>
3564		`
3565    notyStart(content, 'warning')
3566  }
3567}
3568
3569// 'Аналог' в модальном окне.
3570ajaxForms('#fancybox__choose', 'chooseFlag', 'Вы будете уведомлены о поступлении аналога', 'Вы уже отправляли запрос. Пожалуйста ожидайте.')
3571// 'Обратный звонок' в модальном окне.
3572ajaxForms('#fancybox__callback', 'fancyCallbackFlag', 'Запрос обратного звонка успешно отправлен администрации магазина', 'Вы уже отправляли запрос. Пожалуйста ожидайте звонка.')
3573// 'Уведомить' в модальном окне.
3574ajaxForms('#fancybox__notify', 'notifyFlag', 'Вы будете уведомлены о поступлении товара', 'Вы уже отправляли запрос. Пожалуйста ожидайте.')
3575// Страница 'Обратный звонок'.
3576ajaxForms('.page-сallback', 'pageCallbackFlag', 'Спасибо за обращение! Мы перезвоним вам в ближайшее время', 'Вы уже отправляли запрос. Пожалуйста ожидайте звонка.')
3577// 'Обратный звонок в подвале'.
3578ajaxForms('#callback', 'callbackFlag', 'Спасибо за обращение! Мы перезвоним вам в ближайшее время', 'Вы уже отправляли запрос. Пожалуйста ожидайте звонка.')
3579// 'Подписаться'.
3580ajaxForms('#subscribe','subscribeFlag','Спасибо за обращение! Вы подписались на наши уведомления','Вы уже отправляли запрос. Пожалуйста ожидайте.')
3581
3582///////////////////////////////////////
3583// Слайдер на главной
3584function swiperShow() {
3585  const id = '#slideshow'
3586  const swiper = new Swiper(id + ' .swiper', {
3587    loop: false,
3588    preloadImages: false,
3589    watchSlidesProgress: true,
3590    watchOverflow: true,
3591    hashNavigation: false,
3592    slidesPerView: '1',
3593    spaceBetween: 16,
3594    speed: 400,
3595    lazy: {
3596      enabled: true,
3597      loadPrevNext: true,
3598      loadOnTransitionStart: true,
3599    },
3600    navigation: {
3601      enabled: true,
3602      nextEl: id + ' .swiper-button-next',
3603      prevEl: id + ' .swiper-button-prev',
3604    },
3605    pagination: {
3606      enabled: true,
3607      el: id + ' .swiper-pagination',
3608      clickable: true,
3609      type: 'bullets',
3610      dynamicBullets: true,
3611    },
3612  })
3613}
3614
3615// Функции стандартного слайдера
3616function swiperSales() {
3617  const id = '#pdt__sales'
3618  // Слайдер товаров
3619  const swiper = new Swiper(id + ' .swiper', {
3620    loop: false,
3621    autoplay: false,
3622    watchSlidesProgress: true,
3623    simulateTouch: true,
3624    grabCursor: true,
3625    slidesPerView: '3',
3626    spaceBetween: 16,
3627    nested: true,
3628    preloadImages: false,
3629    lazy: {
3630      enabled: false,
3631      loadPrevNext: true,
3632      loadOnTransitionStart: true,
3633    },
3634    navigation: {
3635      nextEl: id + ' .swiper-button-next',
3636      prevEl: id + ' .swiper-button-prev',
3637    },
3638    pagination: {
3639      enabled: true,
3640      el: id + ' .swiper-pagination',
3641      clickable: true,
3642      type: 'bullets',
3643      dynamicBullets: true,
3644    },
3645    breakpoints: {
3646      0: {
3647        slidesPerView: '1',
3648      },
3649      320: {
3650        slidesPerView: '1',
3651      },
3652      480: {
3653        slidesPerView: '1',
3654      },
3655      640: {
3656        slidesPerView: '1',
3657      },
3658      768: {
3659        slidesPerView: '2',
3660      },
3661      1024: {
3662        slidesPerView: '2',
3663      },
3664      1200: {
3665        slidesPerView: '2',
3666      },
3667      1440: {
3668        slidesPerView: '2',
3669      },
3670      1600: {
3671        slidesPerView: '3',
3672      },
3673    },
3674  })
3675}
3676
3677// Слайдер Новостей
3678function swiperNews(selector) {
3679  if (document.querySelector(selector) === null) {
3680    return false
3681  }
3682
3683  // Свайпер слайдер новостей
3684  const swiper = new Swiper(selector + ' .swiper', {
3685    loop: false,
3686    autoplay: false,
3687    watchSlidesProgress: true,
3688    simulateTouch: true,
3689    grabCursor: true,
3690    slidesPerView: '3',
3691    spaceBetween: 32,
3692    nested: true,
3693    preloadImages: false,
3694    autoHeight: false,
3695    lazy: {
3696      enabled: false,
3697      loadPrevNext: true,
3698      loadOnTransitionStart: true,
3699    },
3700    navigation: {
3701      nextEl: '#news .swiper-button-next',
3702      prevEl: '#news .swiper-button-prev',
3703    },
3704    pagination: {
3705      enabled: true,
3706      el: selector + ' .swiper-pagination',
3707      clickable: true,
3708      type: 'bullets',
3709      dynamicBullets: true,
3710    },
3711    breakpoints: {
3712      0: {
3713        slidesPerView: '1',
3714      },
3715      320: {
3716        slidesPerView: '1',
3717        spaceBetween: 16,
3718      },
3719      480: {
3720        slidesPerView: '2',
3721        spaceBetween: 16,
3722      },
3723      640: {
3724        slidesPerView: '2',
3725        spaceBetween: 16,
3726      },
3727      768: {
3728        slidesPerView: '2',
3729        spaceBetween: 16,
3730      },
3731      1024: {
3732        slidesPerView: '2',
3733        spaceBetween: 32,
3734      },
3735      1200: {
3736        slidesPerView: '3',
3737        spaceBetween: 32,
3738      },
3739    }
3740  })
3741}
3742
3743// Функции стандартного слайдера
3744function swiperSlider(selector) {
3745  // Слайдер товаров
3746  const swiper = new Swiper(selector + ' .swiper', {
3747    loop: false,
3748    autoplay: false,
3749    watchSlidesProgress: true,
3750    simulateTouch: true,
3751    grabCursor: true,
3752    slidesPerView: '4',
3753    spaceBetween: 32,
3754    nested: true,
3755    preloadImages: false,
3756    lazy: {
3757      enabled: false,
3758      loadPrevNext: true,
3759      loadOnTransitionStart: true,
3760    },
3761    navigation: {
3762    	nextEl: selector + ' .swiper-button-next',
3763    	prevEl: selector + ' .swiper-button-prev',
3764    },
3765    pagination: {
3766    	enabled: true,
3767    	el: selector + ' .swiper-pagination',
3768    	clickable: true,
3769    	type: 'bullets',
3770      dynamicBullets: true,
3771    },
3772    breakpoints: {
3773      0: {
3774        slidesPerView: '1',
3775      },
3776      320: {
3777        slidesPerView: '2',
3778        spaceBetween: 12,
3779      },
3780      480: {
3781        slidesPerView: '3',
3782        spaceBetween: 16,
3783      },
3784      640: {
3785        slidesPerView: '3',
3786        spaceBetween: 24,
3787      },
3788      768: {
3789        slidesPerView: '3',
3790      },
3791      1024: {
3792        slidesPerView: '3',
3793      },
3794      1600: {
3795        slidesPerView: '4',
3796      },
3797      1920: {
3798        slidesPerView: '4',
3799      },
3800    },
3801  })
3802}
3803
3804// Функции стандартного слайдера
3805function swiperSmall(id) {
3806  // Слайдер товаров
3807  const swiper = new Swiper(id + ' .swiper', {
3808    loop: false,
3809    autoplay: false,
3810    watchSlidesProgress: true,
3811    simulateTouch: true,
3812    grabCursor: true,
3813    slidesPerView: '5',
3814    spaceBetween: 24,
3815    nested: true,
3816    preloadImages: false,
3817    lazy: {
3818      enabled: false,
3819      loadPrevNext: true,
3820      loadOnTransitionStart: true,
3821    },
3822    navigation: {
3823      nextEl: id + ' .swiper-button-next',
3824      prevEl: id + ' .swiper-button-prev',
3825    },
3826    pagination: {
3827      enabled: true,
3828      el: id + ' .swiper-pagination',
3829      clickable: true,
3830      type: 'bullets',
3831      dynamicBullets: true,
3832    },
3833    breakpoints: {
3834      0: {
3835        slidesPerView: '1',
3836      },
3837      320: {
3838        slidesPerView: '2',
3839        spaceBetween: 12,
3840      },
3841      480: {
3842        slidesPerView: '3',
3843        spaceBetween: 16,
3844      },
3845      640: {
3846        slidesPerView: '3',
3847        spaceBetween: 24,
3848      },
3849      768: {
3850        slidesPerView: '3',
3851        spaceBetween: 24,
3852      },
3853      1024: {
3854        slidesPerView: '4',
3855        spaceBetween: 32,
3856      },
3857      1200: {
3858        slidesPerView: '4',
3859        spaceBetween: 32,
3860      },
3861      1440: {
3862        slidesPerView: '4',
3863        spaceBetween: 32,
3864      },
3865      1920: {
3866        slidesPerView: '5',
3867        spaceBetween: 32,
3868      },
3869    },
3870  })
3871}
3872
3873// Слайдер Наши акции
3874function swiperPromotions() {
3875  const id = '#promotions'
3876  // Свайпер слайдер новостей
3877  const swiper = new Swiper(id + ' .swiper', {
3878    loop: false,
3879    autoplay: false,
3880    watchSlidesProgress: true,
3881    simulateTouch: true,
3882    grabCursor: true,
3883    slidesPerView: '4',
3884    spaceBetween: 32,
3885    nested: true,
3886    preloadImages: false,
3887    autoHeight: false,
3888    lazy: {
3889      enabled: false,
3890      loadPrevNext: true,
3891      loadOnTransitionStart: true,
3892    },
3893    navigation: {
3894      enabled: true,
3895      nextEl: id + ' .swiper-button-next',
3896      prevEl: id + ' .swiper-button-prev',
3897    },
3898    pagination: {
3899      enabled: true,
3900      el: id + ' .swiper-pagination',
3901      clickable: true,
3902      type: 'bullets',
3903      dynamicBullets: true,
3904    },
3905    breakpoints: {
3906      0: {
3907        slidesPerView: '1',
3908      },
3909      320: {
3910        slidesPerView: '2',
3911        spaceBetween: 12,
3912      },
3913      480: {
3914        slidesPerView: '2',
3915        spaceBetween: 16,
3916      },
3917      640: {
3918        slidesPerView: '3',
3919        spaceBetween: 16,
3920      },
3921      768: {
3922        slidesPerView: '3',
3923        spaceBetween: 16,
3924      },
3925      1024: {
3926        slidesPerView: '3',
3927        spaceBetween: 32,
3928      },
3929      1200: {
3930        slidesPerView: '3',
3931        spaceBetween: 32,
3932      },
3933      1600: {
3934        slidesPerView: '4',
3935        spaceBetween: 32,
3936      },
3937    },
3938  })
3939}
3940
3941// Слайдер Наши акции
3942function swiperManufacturers() {
3943  const id = '#manufacturers'
3944  // Свайпер слайдер новостей
3945  const swiper = new Swiper(id + ' .swiper', {
3946    loop: false,
3947    autoplay: false,
3948    watchSlidesProgress: true,
3949    simulateTouch: true,
3950    grabCursor: true,
3951    slidesPerView: '6',
3952    spaceBetween: 32,
3953    nested: true,
3954    preloadImages: false,
3955    autoHeight: false,
3956    lazy: {
3957      enabled: false,
3958      loadPrevNext: true,
3959      loadOnTransitionStart: true,
3960    },
3961    navigation: {
3962      enabled: true,
3963      nextEl: id + ' .swiper-button-next',
3964      prevEl: id + ' .swiper-button-prev',
3965    },
3966    pagination: {
3967      enabled: true,
3968      el: id + ' .swiper-pagination',
3969      clickable: true,
3970      type: 'bullets',
3971      dynamicBullets: true,
3972    },
3973    breakpoints: {
3974      0: {
3975        slidesPerView: '1',
3976      },
3977      320: {
3978        slidesPerView: '2',
3979        spaceBetween: 10,
3980      },
3981      480: {
3982        slidesPerView: '3',
3983        spaceBetween: 16,
3984      },
3985      640: {
3986        slidesPerView: '3',
3987        spaceBetween: 16,
3988      },
3989      768: {
3990        slidesPerView: '4',
3991        spaceBetween: 16,
3992      },
3993      1024: {
3994        slidesPerView: '4',
3995        spaceBetween: 20,
3996      },
3997      1200: {
3998        slidesPerView: '4',
3999        spaceBetween: 24,
4000      },
4001      1440: {
4002        slidesPerView: '4',
4003        spaceBetween: 24,
4004      },
4005      1600: {
4006        slidesPerView: '6',
4007        spaceBetween: 32,
4008      },
4009    },
4010  })
4011}
4012
4013// Слайдер Может пригодиться с этим товаром:
4014function swiperUseful() {
4015  const selector = '.modal__useful'
4016  // Слайдер товаров
4017  const swiper = new Swiper(selector + ' .swiper', {
4018    loop: false,
4019    autoplay: false,
4020    watchSlidesProgress: true,
4021    simulateTouch: true,
4022    grabCursor: true,
4023    slidesPerView: '2',
4024    spaceBetween: 16,
4025    nested: true,
4026    preloadImages: false,
4027    lazy: {
4028      enabled: false,
4029      loadPrevNext: true,
4030      loadOnTransitionStart: true,
4031    },
4032    navigation: {
4033      nextEl: selector + ' .swiper-button-next',
4034      prevEl: selector + ' .swiper-button-prev',
4035    },
4036    pagination: {
4037      enabled: true,
4038      el: selector + ' .swiper-pagination',
4039      clickable: true,
4040      type: 'bullets',
4041      dynamicBullets: true,
4042    },
4043    breakpoints: {
4044      0: {
4045        slidesPerView: '1',
4046      },
4047      320: {
4048        slidesPerView: '1',
4049      },
4050      480: {
4051        slidesPerView: '1',
4052      },
4053      640: {
4054        slidesPerView: '2',
4055      },
4056      768: {
4057        slidesPerView: '2',
4058      },
4059      1024: {
4060        slidesPerView: '2',
4061      },
4062      1200: {
4063        slidesPerView: '2',
4064      },
4065      1440: {
4066        slidesPerView: '2',
4067      },
4068      1920: {
4069        slidesPerView: '2',
4070      },
4071    },
4072  })
4073}
4074
4075///////////////////////////////////////
4076// Табы
4077///////////////////////////////////////
4078class Tabs {
4079  constructor(selector, tabs = '.tabs__nav', contents = '.tabs__content', link = '.tabs__link') {
4080    this.selector = document.querySelector(selector)
4081    if (this.selector === null) return false
4082    this.tabs = this.selector.querySelectorAll(tabs)
4083    this.contents = this.selector.querySelectorAll(contents)
4084    this.link = this.selector.querySelector(link)
4085
4086    // Функции при клике
4087    this.onClickHandler()
4088  }
4089
4090  // Убрать все активные классы
4091  removeActived(objects, name = 'is-actived') {
4092    objects.forEach((element) => element.classList.remove(name))
4093  }
4094
4095  // Установить активный класс
4096  addActived(object, name = 'is-actived') {
4097    object.classList.add(name)
4098  }
4099
4100  // Обработчик нажатия на таб
4101  onClickHandler() {
4102    const t = this
4103    const tabs = this.tabs
4104    const contents = this.contents
4105    const selector = this.selector
4106    const link = this.link
4107    tabs.forEach((element) => {
4108      element.addEventListener('click', function (event) {
4109        event.preventDefault()
4110        t.removeActived(tabs)
4111        t.removeActived(contents)
4112        t.addActived(element)
4113        t.tabsLinkUpdate(element, link)
4114
4115        const tabId = element.getAttribute('data-tab')
4116        contents.forEach((content) => (content.getAttribute('data-tab-content') === tabId ? t.addActived(content) : false))
4117
4118        if (selector.matches('#news')) {
4119          t.navigateVisibility('#' + tabId, selector)
4120        }
4121
4122        // console.log('selector', selector)
4123        // console.log('itemsLength', itemsLength);
4124      })
4125    })
4126  }
4127
4128  tabsLinkUpdate(element, link) {
4129    if (link === null) { return false; }
4130    link.querySelector('a').setAttribute('href', element.getAttribute('href'))
4131    link.querySelector('a span').textContent = 'Все ' + element.textContent
4132    const start = link.previousElementSibling.querySelector('a').getBoundingClientRect().left
4133    const coords = element.getBoundingClientRect().left - start + 'px'
4134    if (getClientWidth() > 768) {
4135      link.style.transform = `translateX(${coords})`
4136    }
4137  }
4138
4139  navigateVisibility(tabId, selector) {
4140    const itemsLength = document.querySelectorAll(tabId + ' .swiper-slide').length
4141
4142    if (itemsLength > 3) {
4143      selector.querySelector('.swiper-button-next').style.display = ''
4144      selector.querySelector('.swiper-button-prev').style.display = ''
4145    } else {
4146      selector.querySelector('.swiper-button-next').style.display = 'none'
4147      selector.querySelector('.swiper-button-prev').style.display = 'none'
4148    }
4149  }
4150}
4151
4152// Переключение типа цен (Оптовые)
4153function supplier1() {
4154  const elements = document.querySelectorAll('.navigate__supplier')
4155  const storeSupplier = 'store_supplier'
4156  const storeDelivery = 'store_delivery'
4157
4158  elements.forEach(element => {
4159    if (element.classList.contains('navigate__supplier-delivery')) {
4160      getStorageActived(localStorage.getItem(storeDelivery), element)
4161      getStorageActived(localStorage.getItem(storeDelivery), element.closest('body'), 'is-pickup')
4162      handlePickup(localStorage.getItem(storeDelivery))
4163      element.addEventListener('click', () => handleClick(element, storeDelivery))
4164    } else {
4165      getStorageActived(localStorage.getItem(storeSupplier), element)
4166      getStorageActived(localStorage.getItem(storeSupplier), element.closest('body'))
4167      element.addEventListener('click', () => handleClick(element, storeSupplier))
4168    }
4169  })
4170
4171  function handleClick(element, store) {
4172    setStorage(store)
4173    getStorageActived(localStorage.getItem(store), element)
4174
4175    if (store === storeSupplier) {
4176      // getStorageActived(localStorage.getItem(store), element)
4177      getStorageActived(localStorage.getItem(store), element.closest('body'))
4178    }
4179
4180    if (store === storeDelivery) {
4181      // getStorageActived(localStorage.getItem(store), element)
4182      getStorageActived(localStorage.getItem(store), element.closest('body'), 'is-pickup')
4183      handlePickup(localStorage.getItem(store))
4184    }
4185  }
4186
4187  function handlePickup(storage) {
4188    if (storage === 'true') {
4189      $('.order-delivery__radio[data-name="Самовывоз"]').trigger('click')
4190      $('.order-fast__block.address .form__input').val('Самовывоз')
4191      $('.order-fast__block.address').hide()
4192      $('#deliveryConvenientDate').val('01.01.2222')
4193    } else {
4194      $('.order-delivery__radio').eq(0).trigger('click')
4195      $('.order-fast__block.address .form__input').val('')
4196      $('.order-fast__block.address').show()
4197      $('#deliveryConvenientDate').val('')
4198    }
4199  }
4200
4201  function getStorageActived(storage, element, klass = 'is-supplier'){
4202    return storage === 'true' ? element.classList.add(klass) : element.classList.remove(klass)
4203  }
4204  
4205  function setStorage(store){
4206    return localStorage.getItem(store) === 'true' ? localStorage.setItem(store, 'false') : localStorage.setItem(store, 'true')
4207  }
4208
4209}
4210
4211function supplier() {
4212  const switchers = document.querySelectorAll('.navigate__supplier')
4213
4214  switchers.forEach(element => {
4215    const first = element.querySelector('[data-switch="first"]')
4216    const second = element.querySelector('[data-switch="second"]')
4217    const switcher = element.querySelector('[data-switcher]')
4218    console.log('switcher', switcher);
4219
4220    first.addEventListener('click', () => {
4221      first.classList.add('is-actived')
4222      second.classList.remove('is-actived')
4223      second.parentElement.classList.remove('is-supplier')
4224      second.closest('.order-fast__block').classList.remove('is-actived')
4225
4226      if (element.classList.contains('navigate__supplier-delivery')) {
4227        handlePickup(false)
4228      }
4229    })
4230
4231    second.addEventListener('click', () => {
4232      first.classList.remove('is-actived')
4233      second.classList.add('is-actived')
4234      second.parentElement.classList.add('is-supplier')
4235      second.closest('.order-fast__block').classList.add('is-actived')
4236
4237      if (element.classList.contains('navigate__supplier-delivery')) {
4238        handlePickup(true)
4239      }
4240    })
4241
4242    switcher.addEventListener('click', () => {
4243      console.log('click-switcher');
4244      console.log('first-switcher', first);
4245      console.log('contains-switcher', first.classList.contains('is-actived'));
4246      console.log('second-switcher', second);
4247      if (first.classList.contains('is-actived')){
4248        second.click()
4249      } else {
4250        first.click()
4251      }
4252    })
4253  })
4254
4255  function handlePickup(storage) {
4256    console.log('storage', storage);
4257    if (storage === true) {
4258      $('.order-delivery__radio').attr('checked', false)
4259      $('.order-delivery__radio[data-name="Самовывоз"]').attr('checked', true)
4260      $('.order-delivery__radio[data-name="Самовывоз"]').trigger('click')
4261      $('.order-fast__block.address .form__input').val('Самовывоз')
4262      $('.order-fast__block.address').hide()
4263      $('#deliveryConvenientDate').val('01.01.2222')
4264      document.body.classList.add('is-pickup')
4265      console.log('true', storage);
4266    } else {
4267      $('.order-delivery__radio[data-name="Самовывоз"]').attr('checked', false)
4268      $('.order-delivery__radio').eq(0).trigger('click')
4269      $('.order-fast__block.address .form__input').val('')
4270      $('.order-fast__block.address').show()
4271      $('#deliveryConvenientDate').val('')
4272      document.body.classList.remove('is-pickup')
4273      console.log('false', storage);
4274    }
4275  }
4276  
4277
4278}
4279
4280///////////////////////////////////////
4281// Загрузка основных функций шаблона
4282///////////////////////////////////////
4283document.addEventListener('DOMContentLoaded', function () {
4284  userAgent()
4285  openMenu()
4286  toTop()
4287  mainnav('header .mainnav', '1', 991)
4288  lazyload()
4289  catalogHover()
4290  remove.onClick()
4291  // quantity.updAddtoSale()
4292  quantity.init()
4293
4294  // Удаление классов загрузки для элементов страницы
4295  $('.loading').addClass('loaded')
4296  $('div, section, ul, body').removeClass('loading')
4297
4298  // Отправка данных формы по нажатию на Enter в случае если курсор находится в input полях (В некоторых браузерах при нажатии по enter срабатывает клик по первому submit полю, которое является кнопкой назад. Для этого написан этот фикс)
4299  $('form')
4300    .bind('keypress', function (e) {
4301      if (e.ctrlKey && (e.which == 10 || e.which == 13)) {
4302        $(this).submit()
4303      }
4304    })
4305    .find('input')
4306    .bind('keypress', function (e) {
4307      if (e.which == 10 || e.which == 13) {
4308        try {
4309          $(this.form).submit()
4310        } catch (e) {}
4311        return false
4312      }
4313    })
4314
4315  // Маска ввода телефона
4316  $('.form__phone').mask('+7 (999) 999-9999')
4317
4318  // Возврашаем пользователя на страницу с которой был сделан обратный звонок
4319  $('.callbackredirect').val(document.location.href)
4320})
4321
4322// Запуск функций при изменении экрана
4323window.addEventListener('resize', function () {
4324  if (getClientWidth() > 481 && window.outerHeight < 630) {
4325    $('body').addClass('landscape')
4326  } else {
4327    $('body').removeClass('landscape')
4328  }
4329  mainnav('header .mainnav', '1', 991)
4330  // console.log('getClientWidth()', getClientWidth());
4331})
4332
4333///////////////////////////////////////
4334// Каталог при наведении
4335///////////////////////////////////////
4336function catalogHover() {
4337  const content = document.querySelector('.sidebar-catalog')
4338  const items = content.querySelectorAll('.catalog__item.parent')
4339
4340  items.forEach(item => {
4341    item.addEventListener('mouseenter', handleCatalogItem)
4342  })
4343
4344  function handleCatalogItem(event){
4345    const item = event.target.closest('.catalog__item.parent')
4346    const sub = item.querySelector('.catalog__sub')
4347    const link = item.querySelector('.catalog__link')
4348    const label = sub.querySelector('.catalog__label')
4349    if (!item) return false
4350
4351    if (label){
4352      label.innerHTML = link.outerHTML
4353    } else {
4354      const createLabel = document.createElement('li')
4355      createLabel.className = 'catalog__label'
4356      createLabel.innerHTML = link.outerHTML
4357      sub.prepend(createLabel)
4358    }
4359
4360  }
4361}
4362
4363console.timeEnd('loading time')

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.