carget
Version:
optimization js code for carget website
1,587 lines (1,410 loc) • 96.1 kB
JavaScript
const isExelLoadingFinish = false
let Google_DB = {}
let carData = {}
const customConsoleLog = async (
description,
value,
descriptionStyle,
valueStyle
) => {
await console.log(`%c${description}`, descriptionStyle, value)
}
let TEST3, TEST4
let modelsArray = []
const data4 = [
'Работаем за свои средства',
'Белая и прозрачная структура договоров',
'Официальные оплаты по счетам',
'Быстрый возврат средств клиенту в случае нарушения условий договора',
'Исключены таможенные риски',
'Большой парк электромобилей в наличии в Москве',
'Русификация авто и техническая поддержка',
]
const data5 = [
{
img: 'https://carget.su/wp-content/uploads/2023/11/phone-icon.png',
contact: '+7 996 410-01-87',
},
{
img: 'https://carget.su/wp-content/uploads/2023/11/whatsapp-icon.png',
contact: '+7 996 410-01-87',
},
{
img: 'https://carget.su/wp-content/uploads/2023/11/telegram-icon.png',
contact: '+7 996 410-01-87',
},
{
img: 'https://carget.su/wp-content/uploads/2023/11/email-icon.png',
contact: 'carget@bk.ru',
},
]
let carState = {
model: [],
models: [],
options: {
color: [0, '', []],
wheels: [0, '', [], true],
interiorColor: [0, '', []],
runningBoards: [[], true],
},
delivery: {},
labFees: 0,
recyclingFee: 0,
carLocalization: 0,
marga: [],
}
let updatedCarState
let myPriceModels
const priceInRub = {}
const pricecustomInRub = {}
let savedSlides = {}
let originalColorOptions = {}
let originalWheelsOptions = {}
let originalInteriorColorOptions = {}
// Создаем динамически divPriceModel для каждой модели
const divPriceModels = {}
let previousModel,
previousColor = null
// ..... импорты ..... //
let saleAll = 0
let pdfDoc
let totalPrice = 0
let indexDisk = 0
let myArrowDiskImages
let myDiskImage, mySalonImage
let isUpdatingCarousel = false
let modelNames, modelElements
let arrayImagesForPDF = [
'https://carget.su/wp-content/uploads/2023/11/carget-logo.jpg',
'https://carget.su/wp-content/uploads/2023/11/carget-logo.jpg',
'https://carget.su/wp-content/uploads/2023/11/carget-logo.jpg',
]
const arrayTitlePrice = {
color: '#carget-acordion-color-price span',
wheels: '#carget-acordion-wheels-price span',
interiorColor: '#carget-acordion-interiorColor-price span',
}
const arrayWrappers = [
'.carget-color',
'.carget-wheels',
'.carget-interiorColor',
'.carget-runningBoards',
]
const cargetModels = document.querySelector(
'#carget-models .elementor-container'
)
// ЦЕНА В ШАПКЕ
const totalpriceElements = document.querySelectorAll('.header-totalprice')
// ЧЕКБОКСЫ ---------------------------------------- //
const checkbox = document.getElementById('myCheckbox')
const checkboxLabel = document.querySelector('.custom-checkbox-label')
const checkboxSpan = document.getElementById('custom-checkbox-span')
// ЧЕК --------------------------------------- //
const checkImages = document.querySelectorAll('.check-list-image')
const discountCheck = document.getElementById('check-list-items-discount')
const priceItemDiscount = document.getElementById('price-items-discount')
// Выбираем все элементы с классами для цвета, салона, дисков и дополнительных опций
const colorKuzovaElements = document.querySelectorAll('.check-color')
const colorSalonaElements = document.querySelectorAll('.check-color-salon')
const diskElements = document.querySelectorAll('.check-disk')
const dopOptionsElements = document.querySelector('.check-dop-option')
const totalPriceCarElements = document.querySelectorAll('.total-price-car')
const totalSaleCarElements = document.querySelectorAll('.total-discount')
// Вставляем стоимости
const priceColorElements = document.querySelectorAll('.check-color-price')
const priceColorSalonElements = document.querySelectorAll(
'.check-color-salon-price'
)
const priceDiskElements = document.querySelectorAll('.check-disk-price')
const priceDopOptionsElements = document.querySelector(
'.check-dop-option-price'
)
// Получаем элементы ВСЕХ каруселей
const colorCarousel = document
.getElementById('carget-acordion-color')
.querySelector('.swiper')
const colorImageCarousel = document
.getElementById('carget-acordion-color-image')
.querySelector('.swiper')
const diskDiametr = document.getElementById('carget-acordion-disk')
? document.getElementById('carget-acordion-disk').querySelector('.swiper')
: ''
const diskImage = document.getElementById('carget-acordion-disk-image')
? document
.getElementById('carget-acordion-disk-image')
.querySelector('.swiper')
: ''
const colorSalon = document
.getElementById('carget-acordion-salon')
.querySelector('.swiper')
const salonImage = document
.getElementById('carget-acordion-salon-color')
.querySelector('.swiper')
// Определим функцию для добавления нового элемента
function addNewElementInCheck(customs) {
try {
// Найдем элемент по id
var wrapperBlock = document.getElementById('check-list-wrapper-block')
if (!wrapperBlock)
throw new Error("Элемент с id 'check-list-wrapper-block' не найден.")
// Проверим и удалим существующий элемент, если он уже добавлен
var existingElement = document.querySelector(
'.check-list-items.new-element-custom'
)
if (existingElement) {
wrapperBlock.removeChild(existingElement)
}
// Создадим новый div элемент с классом 'check-list-items' и 'new-element'
var newCheckListItem = document.createElement('div')
newCheckListItem.className = 'check-list-items new-element-custom'
// Используем данные из объекта customs для создания внутренней структуры HTML
newCheckListItem.innerHTML = `
<div class="check-list-row">
<span class="check-list-item">${customs.title}</span>
<span class="check-list-item check-new-item"></span>
<span class="check-list-item check-new-item-price">${customs.price}</span>
</div>
`
// Найдем элемент "Суммарная скидка"
var discountElement = document.getElementById('check-list-items-discount')
if (!discountElement)
throw new Error("Элемент с id 'check-list-items-discount' не найден.")
// Вставим новый элемент перед элементом "Суммарная скидка"
wrapperBlock.insertBefore(newCheckListItem, discountElement)
document
.querySelectorAll('.new-element-custom .check-list-item')
.forEach((e) => {
e.style.borderBottom = '0'
})
} catch (error) {
console.error(
'Произошла ошибка при добавлении нового элемента:',
error.message
)
}
}
const loader = document.getElementById('header-loader')
const cargetLoader = document.getElementById('carget-loader')
function showLoader() {
loader.style.display = 'block'
cargetLoader.style.display = 'block'
}
function hideLoader() {
loader.style.display = 'none'
cargetLoader.style.display = 'none'
console.log('loader hide')
}
// Создаем элемент иконки автомобиля от Google Fonts
const loaderCar = document.createElement('div')
loaderCar.id = 'loader-car'
loaderCar.className = 'loaderCar-text'
loaderCar.textContent = 'CARGET'
// Создаем элемент текста загрузки
const loaderText = document.createElement('div')
loaderText.id = 'loader-text'
loaderText.className = 'loader-text'
loaderText.textContent = 'Подгружаем данные'
// Добавляем элемент текста в загрузчик
loader.appendChild(loaderCar)
loader.appendChild(loaderText)
// Тексты для смены
const texts = [
'Настраиваем конфигуратор',
'Почти готово',
'Загружаем настройки',
'Финальные штрихи',
]
let textIndex = 0
function changeLoaderText() {
loaderText.textContent = texts[textIndex]
textIndex = (textIndex + 1) % texts.length
}
// Меняем текст каждые 2 секунды
const textChangeInterval = setInterval(changeLoaderText, 2000)
let selectedColor
const processSlidesColor = (
carousel,
savedSlides,
addedSlides,
option,
attribute
) => {
// Проверяем, был ли слайд уже добавлен
const isSlideAlreadyAdded = addedSlides.some((slide) =>
slide.innerHTML.includes(`alt="${option.color}"`)
)
// Если слайд не был добавлен, фильтруем и добавляем уникальные слайды
if (!isSlideAlreadyAdded) {
const filteredSlides = savedSlides.filter((slide) =>
slide.innerHTML.includes(`alt="${option.color}"`)
)
// Для carousels, которые содержат изображения, удаляем дублирующие слайды по alt
if (attribute) {
const uniqueAlts = {}
filteredSlides.forEach((slide) => {
const alt = slide.querySelector(attribute).getAttribute('alt')
if (!(alt in uniqueAlts)) {
uniqueAlts[alt] = true
} else {
slide.remove()
}
})
}
filteredSlides.forEach((slide) => {
carousel.swiper.appendSlide(slide)
addedSlides.push(slide)
})
}
}
const processSlidesWheelsSalon = (
swiper,
savedSlides,
addedSlides,
option,
attribute,
selectedModel,
selectedColor
) => {
// Проверяем, соответствует ли опция выбранной модели и цвету
if (
(option.models.includes(selectedModel) || option.models.includes('All')) &&
(option.colors.includes(selectedColor) || option.colors.includes('All'))
) {
// Проверяем, был ли слайд уже добавлен
const isSlideAlreadyAdded = Array.from(addedSlides).some(
(slide) => slide.childNodes[0].childNodes[0].alt === option.color
)
// Если слайд не был добавлен, фильтруем и добавляем уникальные слайды
if (!isSlideAlreadyAdded) {
/* const filteredSlides = savedSlides.filter((slide) =>
slide.innerHTML.includes(`alt="${option.color}"`)
) */
const filteredSlides = savedSlides.filter(
(slide) => slide.childNodes[0].childNodes[0].alt === option.color
)
// Для carousels, которые содержат изображения, удаляем дублирующие слайды по alt
if (attribute) {
const uniqueAlts = {}
filteredSlides.forEach((slide) => {
const alt = slide.childNodes[0].childNodes[0].alt
if (!(alt in uniqueAlts)) {
uniqueAlts[alt] = true
} else {
slide.remove()
}
})
}
filteredSlides.forEach((slide) => {
swiper.appendSlide(slide)
addedSlides.add(slide)
})
}
} else {
}
}
const checkColor = (selectedModel) => {
if (carState.options.color[2][0].models) {
carState.options.color[2] = [...originalColorOptions]
carState.options.color[2] = carState.options.color[2].filter(
(option) =>
option.models.includes(selectedModel) || option.models.includes('All')
)
colorCarousel.swiper.removeAllSlides()
colorImageCarousel.swiper.removeAllSlides()
let addedColorSlides = []
let addedColorImageSlides = []
carState.options.color[2].forEach((option) => {
// Обрабатываем слайды для карусели цвета
processSlidesColor(
colorCarousel,
savedSlides.colorCarousel,
addedColorSlides,
option,
'.swiper-slide-image'
)
// Обрабатываем слайды для карусели изображений цвета
processSlidesColor(
colorImageCarousel,
savedSlides.colorImageCarousel,
addedColorImageSlides,
option,
'.swiper-slide-image'
)
})
areAllSlidesWithSameAlt(colorCarousel)
? removeDuplicateSlides(colorCarousel)
: ''
areAllSlidesWithSameAlt(colorImageCarousel)
? removeDuplicateSlides(colorImageCarousel)
: ''
colorCarousel.swiper.update()
colorImageCarousel.swiper.update()
}
if (colorCarousel.swiper.slides.length > 1) {
customConsoleLog(
'colorCarousel.swiper.slides.length > 1',
colorCarousel.swiper.slides.length,
'background: brown; color: white;',
'font-weight: bold;'
)
setTimeout(() => {
colorCarousel.swiper.slideTo(colorCarousel.swiper.params.slidesPerView)
}, 400)
} else if (colorCarousel.swiper.slides.length <= 1) {
customConsoleLog(
'colorCarousel.swiper.slides.length <= 1',
colorCarousel.swiper.slides.length,
'background: brown; color: white;',
'font-weight: bold;'
)
setTimeout(() => {
carState.options.color[1] = colorCarousel.querySelector(
'.swiper-slide-active img'
).alt
}, 1500)
} else {
console.log('сработал не известный сценарий checkColor')
}
return true
}
const checkWheels = (selectedModel) => {
if (diskDiametr.swiper) {
// Показать блок диски, если есть хотя бы один слайд и активирована опция колес
document.querySelectorAll(arrayWrappers[1]).forEach((e) => {
if (diskDiametr.swiper.slides.length >= 1 || carState.options.wheels[3]) {
e.style.display = 'block'
}
})
// Работа с базой - Копируем исходные опции колес
carState.options.wheels[2] = [...originalWheelsOptions]
// Фильтруем опции колес по выбранной модели и цвету
carState.options.wheels[2] = carState.options.wheels[2].filter(
(option) =>
(option.models.includes(selectedModel) ||
option.models.includes('All')) &&
(option.colors.includes(
previousColor ? previousColor : selectedColor
) ||
option.colors.includes('All'))
)
// Работа с каруселью - Удаляем все слайды из каруселей диаметра и изображений дисков
diskDiametr.swiper.removeAllSlides()
diskImage.swiper.removeAllSlides()
let addedDiskDiametrSlides = new Set()
// Обрабатываем слайды для диаметра и изображений дисков
carState.options.wheels[2].forEach((option) => {
processSlidesWheelsSalon(
diskDiametr.swiper,
savedSlides.diskDiametr,
addedDiskDiametrSlides,
option,
'.swiper-slide-image',
selectedModel,
selectedColor
)
})
// Проверяем и удаляем дубликаты слайдов
areAllSlidesWithSameAlt(diskDiametr)
? removeDuplicateSlides(diskDiametr)
: ''
// Обновляем карусели
diskDiametr.swiper.update()
diskImage.swiper.update()
// Сценарии для карусели
if (diskDiametr.swiper.slides.length > 1) {
customConsoleLog(
'diskDiametr > 1',
diskDiametr.swiper.slides.length,
'background: yellow; color: black;',
'font-weight: bold;'
)
setTimeout(() => {
// diskDiametr.swiper.params.centeredSlides = true
// diskDiametr.swiper.params.slideToClickedSlide = true
diskDiametr
.querySelectorAll('.elementor-swiper-button')
.forEach((e) => {
e.style.display = 'inline-flex'
})
}, 1500)
} else if (diskDiametr.swiper.slides.length <= 1) {
customConsoleLog(
'diskDiametr <= 1',
diskDiametr.swiper.slides.length,
'background: yellow; color: black;',
'font-weight: bold;'
)
// Если один или меньше слайдов, скрываем кнопки переключения
setTimeout(() => {
carState.options.wheels[1] = diskDiametr.querySelector(
'.swiper-slide-active img'
)
? diskDiametr.querySelector('.swiper-slide-active img').alt
: ''
diskDiametr
.querySelectorAll('.elementor-swiper-button')
.forEach((e) => {
e.style.display = 'none'
})
}, 1500)
} else {
console.log('нет такого СЦЕНАРИЯ для дисков')
customConsoleLog(
'нет такого СЦЕНАРИЯ для дисков',
diskDiametr.swiper.slides.length,
'background: yellow; color: black;',
'font-weight: bold;'
)
}
}
diskImage.swiper
? updateCarouselDisk(`${carState.options.color[1]}-0`, 'checkWheels')
: ''
return true
}
const checkSalon = (selectedModel) => {
carState.options.interiorColor[2] = [...originalInteriorColorOptions]
carState.options.interiorColor[2] = carState.options.interiorColor[2].filter(
(option) =>
(option.models.includes(selectedModel) ||
option.models.includes('All')) &&
(option.colors.includes(previousColor ? previousColor : selectedColor) ||
option.colors.includes('All'))
)
colorSalon.swiper.removeAllSlides()
salonImage.swiper.removeAllSlides()
let addedColorSalonSlides = []
let addedSalonImageSlides = []
carState.options.interiorColor[2].forEach((option) => {
processSlidesColor(
colorSalon,
savedSlides.colorSalon,
addedColorSalonSlides,
option,
'.swiper-slide-image'
)
processSlidesColor(
salonImage,
savedSlides.salonImage,
addedSalonImageSlides,
option,
'.swiper-slide-image'
)
})
areAllSlidesWithSameAlt(colorSalon) ? removeDuplicateSlides(colorSalon) : ''
colorSalon.swiper.update()
salonImage.swiper.update()
//обновляю цвет салона, заголовок и цену
if (colorSalon.swiper.slides.length > 1) {
setTimeout(() => {
colorSalon.swiper.slideTo(colorSalon.swiper.params.slidesPerView)
? colorSalon.swiper.slideTo(colorSalon.swiper.params.slidesPerView)
: colorSalon.swiper.slideNext()
colorSalon.swiper.params.centeredSlides = true
colorSalon.swiper.params.slideToClickedSlide = true
colorSalon.querySelectorAll('.elementor-swiper-button').forEach((e) => {
e.style.display = 'inline-flex'
})
}, 1500)
} else if (colorSalon.swiper.slides.length <= 1) {
setTimeout(() => {
carState.options.interiorColor[1] = colorSalon.querySelector(
'.swiper-slide-active img'
).alt
colorSalon.querySelectorAll('.elementor-swiper-button').forEach((e) => {
e.style.display = 'none'
})
}, 1500)
}
return true
}
async function updateOptions(selectedModel, where) {
customConsoleLog(
'updateOptions',
where,
'background: blue; color: white;',
'font-weight: bold;'
)
const { options } = carState
const { wheels, color } = options
const wheelsCheckNeeded = wheels[3] === true
const colorCheckNeeded = selectedModel && selectedModel !== previousModel
const salonCheckNeeded = colorSalon.swiper ? true : false
const runChecks = async ({
isCheckColor = false,
isCheckWheels = false,
isCheckSalon = false,
}) => {
try {
if (isCheckColor && colorCarousel.swiper) {
await checkColor(selectedModel)
}
if (isCheckWheels) {
await checkWheels(selectedModel)
}
if (isCheckSalon && colorSalon.swiper) {
await checkSalon(selectedModel)
}
await updateTitlePrice('updateOptions')
} catch (error) {
console.error('Ошибка при выполнении проверок:', error)
}
}
try {
// -------------------- МОДЕЛЬ ИЗМЕНИЛАСЬ
if (colorCheckNeeded) {
previousModel = selectedModel
customConsoleLog(
'МОДЕЛЬ ИЗМЕНИЛАСЬ',
selectedModel,
'background: seagreen; color: white;',
'font-weight: bold;'
)
await runChecks({
isCheckColor: colorCheckNeeded,
isCheckWheels: wheelsCheckNeeded,
isCheckSalon: salonCheckNeeded,
})
const customs = {
title: '*Стоимость таможни РФ',
description: '—',
price: `${numberWithSpaces(
roundNumberToNChars(
pricecustomInRub[selectedModel],
4,
'updateOptions'
)
)} руб.`,
}
addNewElementInCheck(customs)
}
// -------------------- ЦВЕТ КУЗОВА ИЗМЕНИЛСЯ
else if (color[1] && color[1] !== previousColor) {
previousColor = color[1]
customConsoleLog(
'ЦВЕТ КУЗОВА ИЗМЕНИЛСЯ',
color[1],
'background: seagreen; color: white;',
'font-weight: bold;'
)
await runChecks({
isCheckColor: colorCheckNeeded,
isCheckWheels: wheelsCheckNeeded,
isCheckSalon: salonCheckNeeded,
})
}
} catch (error) {
console.error('Ошибка при обновлении опций:', error)
}
}
//удаляем дубликаты, если один элемент
function removeDuplicateSlides(carouselElement) {
try {
// Создаем Set для хранения уникальных значений alt
const uniqueAlts = new Set()
// Получаем все слайды карусели
const slides = carouselElement.querySelectorAll('.swiper-slide')
// Проходимся по каждому слайду
slides.forEach((slide, index) => {
const imgElement = slide.querySelector('.swiper-slide-image')
if (imgElement) {
// Получаем значение атрибута alt изображения
const alt = imgElement.getAttribute('alt')
// Если такого alt еще нет в Set, добавляем его
if (!uniqueAlts.has(alt)) {
uniqueAlts.add(alt)
} else {
// Если такой alt уже есть, удаляем текущий слайд из DOM
slide.remove()
}
} else {
console.warn(
`Слайд ${
index + 1
} не содержит изображение с классом '.swiper-slide-image'`
)
}
})
} catch (error) {
console.error('Ошибка в функции removeDuplicateSlides:', error)
}
}
//проверяем на одинаковый alt у всех в карусели
function areAllSlidesWithSameAlt(carousel) {
const slides = carousel.querySelectorAll('.swiper-slide')
if (slides.length === 0) {
// Если карусель пустая, считаем, что все слайды с одинаковым alt
return true
}
// Получаем значение атрибута alt первого слайда
const firstSlideAlt = slides[0]
.querySelector('.swiper-slide-image')
.getAttribute('alt')
// Проверяем, совпадает ли значение атрибута alt у всех слайдов
for (let i = 1; i < slides.length; i++) {
const slideAlt = slides[i]
.querySelector('.swiper-slide-image')
.getAttribute('alt')
if (slideAlt !== firstSlideAlt) {
// Если хотя бы одно значение отличается, возвращаем false
return false
}
}
// Если все значения атрибута alt одинаковы, возвращаем true
return true
}
// плюс процент с округлением
function calculateWithPercentage(sum, percentage) {
return roundNumberToNChars(
Math.ceil(sum * (1 + percentage / 100)),
4,
'calculateWithPercentage'
)
}
// пробелы
function numberWithSpaces(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ')
}
// округление
function roundNumberToNChars(number, n, where) {
number = Math.ceil(number)
if (typeof number !== 'number' || !Number.isInteger(number) || number < 0) {
throw new Error('number должно быть положительным целым числом', where)
}
if (typeof n !== 'number' || !Number.isInteger(n) || n < 1) {
throw new Error(
'n должно быть положительным целым числом больше или равным 1',
where
)
}
const multiplier = Math.pow(10, n - 1)
const roundedNumber = Math.ceil(number / multiplier) * multiplier
return roundedNumber
}
// API курсы валют
async function fetchCurrencyRates() {
try {
const storedData = localStorage.getItem('currencyRates')
if (!storedData) {
const responseCNY = await fetch('https://open.er-api.com/v6/latest/CNY')
const dataCNY = await responseCNY.json()
const responseUSD = await fetch('https://open.er-api.com/v6/latest/USD')
const dataUSD = await responseUSD.json()
const rates = { CNY: dataCNY.rates.RUB, USD: dataUSD.rates.RUB }
await saveCurrencyRatesToLocalStorage(rates)
} else {
const currencyRates = JSON.parse(storedData)
const lastUpdated = new Date(currencyRates.lastUpdated)
const currentDate = new Date()
const currentDateString = `${currentDate.getDate()}-${
currentDate.getMonth() + 1
}-${currentDate.getFullYear()}`
const lastUpdatedString = `${lastUpdated.getDate()}-${
lastUpdated.getMonth() + 1
}-${lastUpdated.getFullYear()}`
if (currentDateString !== lastUpdatedString) {
const responseCNY = await fetch('https://open.er-api.com/v6/latest/CNY')
const dataCNY = await responseCNY.json()
const responseUSD = await fetch('https://open.er-api.com/v6/latest/USD')
const dataUSD = await responseUSD.json()
const rates = { CNY: dataCNY.rates.RUB, USD: dataUSD.rates.RUB }
await saveCurrencyRatesToLocalStorage(rates)
}
}
} catch (error) {
console.error('Ошибка при загрузке курсов валют:', error)
throw error // Обработка ошибки
}
}
async function saveCurrencyRatesToLocalStorage(data) {
const currentDate = new Date().toISOString() // Получить текущую дату и время
const currencyRates = { rates: data, lastUpdated: currentDate }
await localStorage.setItem('currencyRates', JSON.stringify(currencyRates))
return data
}
/*
// КАРУСЕЛИ НА НУЖНЫЙ СЛАЙД ОБНОВЛЕНИЯ
async function updateCarouselDisk(dc, where) {
// Включаем карусель diskImage и очищаем все слайды
diskImage.swiper.removeAllSlides()
const consoleValue = { dc, where }
customConsoleLog(
'updateCarouselDisk',
consoleValue,
'background: blue; color: white;',
'font-weight: bold;'
)
const color = dc.split('-')[0]
const matchingIndexes = []
carState.options.wheels[2].forEach((option) => {
// Проверяем, что у опции есть нужный цвет или 'All'
if (option.colors.includes(color) || option.colors.includes('All')) {
matchingIndexes.push(option.index) // Добавляем индекс в массив
}
})
console.log('Matching indexes:', matchingIndexes)
const exactMatchSlides = myArrowDiskImages.filter((slide) => {
// Проверяем условия фильтрации для каждого слайда
return matchingIndexes.some(
(index) => slide.querySelector('img').alt === `${color}-${index}`
)
})
console.log('Filtered slides:', exactMatchSlides)
exactMatchSlides.forEach((slide) => {
diskImage.swiper.appendSlide(slide)
})
diskImage.swiper.update()
// Обновляем состояние carState
carState.options.wheels[0] = indexOption('wheels', 0, 'index').price
carState.options.wheels[1] = indexOption('wheels', 0, 'index').color
if (diskDiametr.swiper) {
diskDiametr.swiper.slideTo(diskDiametr.swiper.params.slidesPerView, 400)
diskDiametr.swiper.update()
}
diskDiametr.querySelectorAll('.elementor-swiper-button').forEach((button) => {
button.style.display =
diskDiametr.swiper.slides.length <= 1 ? 'none' : 'inline-flex'
})
updateTitlePrice('updateCarouselDisk')
}*/
// КАРУСЕЛИ НА НУЖНЫЙ СЛАЙД ОБНОВЛЕНИЯ
async function updateCarouselDisk(dc, where) {
// Включаем карусель diskImage и очищаем все слайды
diskImage.swiper.removeAllSlides()
const consoleValue = { dc, where }
customConsoleLog(
'updateCarouselDisk',
consoleValue,
'background: blue; color: white;',
'font-weight: bold;'
)
// const color = dc.split('-')[0]
const color = dc.substring(0, dc.lastIndexOf('-'))
const matchingIndexes = []
if (carState.options.wheels[2] && diskDiametr.swiper) {
carState.options.wheels[2].forEach((option) => {
// Проверяем, что у опции есть нужный цвет или 'All'
if (option.colors.includes(color) || option.colors.includes('All')) {
matchingIndexes.push(option.index) // Добавляем индекс в массив
}
})
} else if (carState.options.wheels[2] && !diskDiametr.swiper) {
matchingIndexes.push(carState.options.wheels[2][0].index)
} else {
console.error('matchingIndexes underfind')
return
}
let exactMatchSlides = []
if (Array.isArray(myArrowDiskImages)) {
exactMatchSlides = myArrowDiskImages.filter((slide) => {
// Проверяем условия фильтрации для каждого слайда
return matchingIndexes.some(
(index) => slide.querySelector('img').alt === `${color}-${index}`
)
})
} else if (myArrowDiskImages) {
exactMatchSlides = myArrowDiskImages
} else {
console.error('myArrowDiskImages is undefined')
return
}
exactMatchSlides.forEach((slide) => {
diskImage.swiper.appendSlide(slide)
})
diskImage.swiper.update()
// Обновляем состояние carState
const wheelIndexOption = indexOption('wheels', 0, 'index')
if (wheelIndexOption) {
carState.options.wheels[0] = wheelIndexOption.price
carState.options.wheels[1] = wheelIndexOption.color
} else {
console.error('indexOption("wheels", 0, "index") returned undefined')
return
}
if (diskDiametr && diskDiametr.swiper) {
diskDiametr.swiper.slideTo(diskDiametr.swiper.params.slidesPerView, 400)
diskDiametr.swiper.update()
diskDiametr
.querySelectorAll('.elementor-swiper-button')
.forEach((button) => {
button.style.display =
diskDiametr.swiper.slides.length <= 1 ? 'none' : 'inline-flex'
})
} else if (diskDiametr) {
// Если нет swiper, обработка для случая без swiper
// Например, можно просто показать первый элемент и скрыть кнопки навигации
const slides = Array.from(diskDiametr.children)
slides.forEach((slide, index) => {
slide.style.display = index === 0 ? 'block' : 'none'
})
diskDiametr
.querySelectorAll('.elementor-swiper-button')
.forEach((button) => {
button.style.display = slides.length <= 1 ? 'none' : 'inline-flex'
})
} else {
console.error('diskDiametr is undefined')
return
}
updateTitlePrice('updateCarouselDisk')
}
function findSlideIndexByAlt(slider, altAttribute) {
const slides = slider.querySelectorAll('.swiper-slide')
for (let i = 0; i < slides.length; i++) {
const slide = slides[i]
const img = slide.querySelector('img')
if (img && img.alt === altAttribute) {
const dataIndex = slide.getAttribute('data-swiper-slide-index')
return parseInt(dataIndex)
}
}
return -1 // Возвращаем -1, если слайд с указанным alt не найден
}
// ПРОВЕРКА НА ДИСКИ ДЛЯ ВЫГРУЗКИ ФОТО АВТО, ЕСЛИ FALSE ТО ВЫГРУЖАЮ ИЗ ПЕРВОЙ КАРУСЕЛИ
// Функция для установки содержимого изображения в чек и обновления массива изображений для PDF
async function updateImages(imageElement) {
if (!imageElement) return
const imgElement = imageElement.querySelector('img')
if (!imgElement) return
const src = imgElement.getAttribute('src')
if (!src) return
arrayImagesForPDF[1] = src
checkImages[0].innerHTML = myDiskImage.outerHTML
}
async function loadImageAsDataURLWithLogging(imagePath) {
try {
const imageDataURL = await loadImageAsDataURL(imagePath)
return imageDataURL
} catch (error) {
console.error('Error loading image:', error)
throw error
}
}
// Функция для преобразования изображения в Data URL
async function loadImageAsDataURL(imagePath) {
// Проверяем, является ли переданный путь уже Data URL
if (!imagePath || imagePath.startsWith('data:image')) {
return imagePath // Если это пустое значение или Data URL, возвращаем его без изменений
}
try {
const response = await fetch(imagePath)
if (!response.ok) {
throw new Error('Failed to fetch image')
}
let blob = await response.blob() // Изменили const на let
const reader = new FileReader()
// Проверяем, является ли тип изображения WebP или PNG
const isWebP = blob.type === 'image/webp'
if (isWebP) {
// Если изображение в формате WebP или PNG, конвертируем его в другой формат
const image = new Image()
image.src = URL.createObjectURL(blob)
await image.decode() // Дожидаемся загрузки изображения
const canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
const context = canvas.getContext('2d')
context.drawImage(image, 0, 0)
const convertedBlob = await new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (!blob) {
reject(new Error('Failed to convert image'))
}
resolve(blob)
}) // Если изображение в формате PNG, конвертируем его в JPEG
})
blob = convertedBlob // Изменили присвоение значения константе
}
return new Promise((resolve, reject) => {
reader.onload = () => resolve(reader.result)
reader.onerror = reject
reader.readAsDataURL(blob)
})
} catch (error) {
throw new Error('Error loading image as Data URL: ' + error.message)
}
}
// СКИДКА заголовки параметров
const updateTitlePrice = async (where) => {
customConsoleLog(
'updateTitlePrice',
where,
'background: blue; color: white;',
'font-weight: bold;'
)
if (typeof arrayTitlePrice !== 'object' || arrayTitlePrice === null) {
console.error('arrayTitlePrice не является объектом')
return
}
Object.entries(arrayTitlePrice).forEach(([option, selector]) => {
try {
const optionData = carState.options[option]
if (!optionData) return
const [price, selectedColor, colorOptions] = optionData
let selectedOption = colorOptions.find(
({ color }) => color === selectedColor
)
if (!selectedOption && colorOptions.length > 0) {
selectedOption = colorOptions[0]
}
const element = document.querySelector(selector)
if (element && selectedOption) {
const saleHtml =
selectedOption.sale !== undefined
? `<span style="font-weight:700; display: flex; justify-content: center; gap: 8px;">
${price} руб. <del style="text-decoration-color:#D10000; font-weight:300;">${selectedOption.sale} руб.</del>
</span>`
: `<span>${price} руб.</span>`
element.innerHTML = `
<div style="display:flex;flex-direction:column;">
<span style="font-weight:700;">${
selectedColor === selectedOption.color
? selectedColor
: selectedOption.color
}</span>
${saleHtml}
</div>`
}
} catch (error) {
console.error(`Error updating option ${option}:`, error)
}
})
// Проверка состояния колес и наличия diskImage
if (carState.options.wheels[3] === true && diskImage) {
// ФОТО АВТО В ЧЕК
myDiskImage = diskImage.querySelector('.swiper-slide-active')
? diskImage.querySelector('.swiper-slide-active')
: diskImage.querySelector('.swiper-slide')
if (myDiskImage) {
// Копируем src изображения перед обновлением массива
await updateImages(myDiskImage)
}
} else if (carState.options.wheels[3] === false && colorImageCarousel) {
myDiskImage = colorImageCarousel.querySelector('.swiper-slide-active')
if (myDiskImage) {
// Копируем src изображения перед обновлением массива
await updateImages(myDiskImage)
}
customConsoleLog(
'wheels[3] === false',
'',
'background: brown; color: white;',
'font-weight: bold;'
)
} else {
myDiskImage = colorImageCarousel.querySelector('.swiper-slide')
// Копируем src изображения перед обновлением массива
await updateImages(myDiskImage)
customConsoleLog(
'wheels[3] ни один сценарий не сработал',
'',
'background: brown; color: white;',
'font-weight: bold;'
)
}
if (salonImage && salonImage.querySelector('.swiper-slide-active img')) {
arrayImagesForPDF[2] = salonImage.querySelector(
'.swiper-slide-active img'
)?.src
} else if (salonImage && salonImage.querySelector('.swiper-slide img')) {
arrayImagesForPDF[2] = salonImage.querySelector('.swiper-slide img')?.src
}
updateCheck()
}
// Показать или скрыть ДОП ОПЦИИ
const checkDopOtionShow = () => {
const displayValue = carState.options.runningBoards[1] ? 'flex' : 'none'
document.querySelectorAll(arrayWrappers[3]).forEach((e) => {
e.style.display = displayValue
})
}
// Обнуление после выбора другой модели
const removeShowDopOptions = () => {
const dopOptionsWrapper = document.querySelector(
'.dop-options-wrapper .elementor-widget-wrap'
)
const selectedModel = carState.model[1] // Получаем текущую модель
const children = dopOptionsWrapper.children
const childrenArray = Array.from(children)
carState.options.runningBoards[0].forEach((option, index) => {
const child = children[index]
const checkbox = document.getElementById(child.id)
const checkboxInput = checkbox.querySelector(`#${child.id}`)
// Установка состояния чекбокса в зависимости от условий
if (option.check) {
option.show = false
checkboxInput.checked = false
}
const modelsToShow = option.models // Список моделей, для которых опция должна быть показана
const showOption =
modelsToShow.includes(selectedModel) || modelsToShow.includes('All')
const displayValue = showOption ? 'flex' : 'none'
childrenArray.forEach((el) => {
if (el.id === child.id) {
el.style.display = displayValue
}
})
})
}
const updateDopOptions = () => {
const selectedModel = carState.model[1] // Получаем текущую модель
//ДОП ОПЦИИ
const dopOptionsWrapper = document.querySelector(
'.dop-options-wrapper .elementor-widget-wrap'
)
const children = dopOptionsWrapper.children
const childrenArray = Array.from(children)
carState.options.runningBoards[0].forEach((option, index) => {
const child = children[index]
const modelsToShow = option.models // Список моделей, для которых опция должна быть показана
const showOption =
modelsToShow.includes(selectedModel) || modelsToShow.includes('All')
const displayValue = showOption ? 'flex' : 'none'
childrenArray.forEach((el) => {
if (el.id === child.id) {
el.style.display = displayValue
}
})
const checkbox = document.getElementById(child.id)
const checkboxInput = checkbox.querySelector(`#${child.id}`)
const checkboxLabel2 = checkbox.querySelector('.custom-checkbox-label')
const checkboxLabel = checkbox.querySelector(`.dop-option-title`)
const checkboxDescription = checkbox.querySelector(
'.dop-option-description'
)
const checkboxSpan = checkbox.querySelector(`#span-${child.id}`)
// Устанавливаем заголовок и описание из option.title и option.description
// СКИДКА доп опции
checkboxLabel.textContent = option.title
checkboxDescription.textContent = option.description
checkboxSpan.innerHTML = `<div style="display:flex; gap:8px;">
<span style="font-weight:700;">${numberWithSpaces(
option.price
)} руб.</span>
${
option.sale !== 0 && option.sale
? `<del style="text-decoration-color:#D10000; font-weight:300; color: #000">${option.sale} руб.</del> `
: ''
}
</div>`
// Добавление слушателей событий для чекбоксов
const handleClick = () => {
if (option.check) {
checkboxInput.checked = !checkboxInput.checked
option.show = checkboxInput.checked
updateCheck()
}
}
checkboxLabel2.addEventListener('click', handleClick)
checkboxSpan.addEventListener('click', handleClick)
})
}
const checkDopOptions = () => {
// Получаем данные о дополнительных опциях, которые нужно отобразить
const dopOptionsToShow = carState.options.runningBoards[0].filter(
(option) => option.show
)
// Очищаем текстовое содержимое элементов dopOptionsElements и priceDopOptionsElements
dopOptionsElements.textContent = ''
priceDopOptionsElements.textContent = ''
// Если есть отображаемые опции, выводим информацию о них
if (dopOptionsToShow.length > 0 && carState.options.runningBoards[1]) {
dopOptionsToShow.forEach((option) => {
const title = option.title
const price = option.price
const titleElement = document.createElement('div')
titleElement.textContent = title
const priceElement = document.createElement('div')
priceElement.textContent = `${numberWithSpaces(price)} руб.`
dopOptionsElements.appendChild(titleElement)
priceDopOptionsElements.appendChild(priceElement)
})
} else {
// Если опций нет или показывать их не нужно, выводим информацию "Нет"
const noOptionsElement = document.createElement('div')
noOptionsElement.textContent = 'Нет'
dopOptionsElements.appendChild(noOptionsElement)
priceDopOptionsElements.appendChild(noOptionsElement)
}
}
// РАБОТА С МОДЕЛЯМИ
// ---- НОВЫЙ МЕТОД --------
// Определение функции select
function select(selector) {
return document.querySelectorAll(selector)
}
/**
* Получение ID элемента на основе имени модели.
* @param {string} name - Имя модели.
* @param {string} prefix - Префикс ID (по умолчанию 'accordion-car-model-').
* @returns {string} - ID элемента.
*/
function getElementId(name, prefix = 'accordion-car-model-') {
const id = `${prefix}${name.toLowerCase()}`
return id
}
/**
* Создание объекта с элементами для модели.
* @param {string} modelName - Название модели.
* @param {object} modelInfo - Информация о модели.
* @returns {object} - Объект с элементами для модели.
*/
function createModelElement(modelName, modelInfo) {
const lowercaseName = modelName.toLowerCase()
const elements = {
model: select(`.${getElementId(lowercaseName)}`),
acc: select(`.${getElementId(lowercaseName)} h4`),
mobileButton: select(`.car-${lowercaseName}-mobile`),
accordionItem: select(
`.${getElementId(lowercaseName)} .elementor-tab-title`
),
}
return elements
}
/**
* Определение элементов для каждой модели.
* @param {array} models - Массив объектов с информацией о моделях.
* @returns {object} - Объект с элементами для каждой модели.
*/
function defineModelElements(models) {
const elements = {}
models.forEach((model) => {
const modelName = Object.keys(model)[0]
elements[modelName] = createModelElement(modelName, model[modelName])
})
return elements
}
// СОЗДАНИЕ ЭЛЕМЕНТОВ - ПРАЙС В МОДЕЛИ
const createDivPriceModel = (modelName) => {
const divPriceModel = document.createElement('div')
divPriceModel.className = `car${modelName}Model` // Используем динамический класс
divPriceModel.id = `carmodel-price-${modelName.toLowerCase()}`
return divPriceModel
}
// ЗАКРЫТОЕ ПОЛОЖЕНИЕ АККОРДИОНОВ
async function closeAccordions() {
modelNames.forEach((e) => {
modelElements[e].accordionItem[0].click()
})
}
// СОЗДАЕМ ЦЕНУ АККОРДИОНАМ
const innerPriceTitleModels = () => {
// ЗАПИСЬ СТОИМОСТИ В ЗАГОЛОВКИ АККАРДИОНА
// СКИДКА модели комплектации
modelNames.forEach((modelName, index) => {
divPriceModels[modelName].innerHTML = `
<div style="display:flex;flex-direction:column; gap: 8px;">
${
carState.models[index][modelName].sale !== 0 &&
carState.models[index][modelName].sale
? `<span style="font-size:16; font-weight:300"><del style="color: white;">${carState.models[index][modelName].sale} руб.</del></span>`
: ''
}
<span style="font-weight:700;">${numberWithSpaces(
myPriceModels[index]
)} руб.</span>
</div>
`
})
modelNames.forEach((modelName, index) => {
const price = numberWithSpaces(myPriceModels[index])
modelElements[modelName].acc.forEach((element) => {
element.innerHTML = `
<div style="display:flex;flex-direction:column; gap: 8px;">
${
carState.models[index][modelName].sale !== 0 &&
carState.models[index][modelName].sale
? `<span style="font-size:20px; font-weight:300"><del style="color: white;">${carState.models[index][modelName].sale} руб.</del></span>`
: ''
}
<span style="font-weight:700;">${price} руб.</span>
</div>
`
})
modelElements[modelName].accordionItem[0].appendChild(
divPriceModels[modelName]
)
})
}
// Обновление кнопок
function createAndAttachButtonClickHandler(modelName) {
const buttons = document.querySelectorAll(
`.car-${modelName.toLowerCase()}-mobile`
)
buttons.forEach((button) => {
button.addEventListener('click', () => {
carState.model[1] = `${modelName}`
updateButtonState(carState)
removeShowDopOptions()
updateCheck()
})
})
}
// Функция обновления состояния кнопок моделей
function updateButtonState(carState) {
modelNames.forEach((modelName) => {
const isSelected = carState.model[1] === `${modelName}`
const buttons = document.querySelectorAll(
`.car-${modelName.toLowerCase()}-mobile`
)
buttons.forEach((button) => {
const buttonTextElement = button.querySelector('.elementor-button-text')
const elementorButton = button.querySelector('.elementor-button')
button.style.cursor = 'pointer'
button.style.borderRadius = '3px'
if (isSelected) {
// Если выбрано, измените фон и цвет текста
button.style.backgroundColor = '#fff'
elementorButton ? (elementorButton.style.backgroundColor = '#fff') : ''
buttonTextElement.style.color = '#000'
updateOptions(modelName, 'updateButtonState')
} else {
// Если не выбрано, восстановите стандартные стили
button.style.backgroundColor = '#DB2424'
elementorButton
? (elementorButton.style.backgroundColor = '#DB2424')
: ''
buttonTextElement.style.color = '#fff'
}
buttonTextElement.textContent = isSelected ? 'ВЫБРАНО' : 'ВЫБРАТЬ'
})
})
}
function convertCarState(carState) {
const storedData = localStorage.getItem('currencyRates')
const currencyRates = JSON.parse(storedData)
const usdToRubExchangeRate = currencyRates.rates.USD
const cnyToRubExchangeRate = currencyRates.rates.CNY
// Конвертация МОДЕЛЕЙ в рубли
modelNames.forEach((modelName) => {
const modelPrice = carState.models.find((model) => model[modelName])
const customPrice = modelPrice ? modelPrice[modelName].custom : 0 // Получаем таможенную стоимость модели
const modelPriceCNY = modelPrice ? modelPrice[modelName].price : 0
pricecustomInRub[modelName] = customPrice * cnyToRubExchangeRate
priceInRub[modelName] = modelPriceCNY * cnyToRubExchangeRate
})
// Обновление объекта состояния с новыми значениями в рублях
const updatedCarState = {
...carState,
models: carState.models.map((model) => {
const modelName = Object.keys(model)[0]
const priceInRub = model[modelName].price * cnyToRubExchangeRate
const customInRub = model[modelName].custom * cnyToRubExchangeRate
return { [modelName]: { price: priceInRub, custom: customInRub } }
}),
delivery: {
...carState.delivery,
fromTurgartToBishkek:
carState.delivery.fromTurgartToBishkek * usdToRubExchangeRate,
customs: carState.delivery.customs * usdToRubExchangeRate,
otherExpenses: carState.delivery.otherExpenses * usdToRubExchangeRate,
fromBishkekToRussia:
carState.delivery.fromBishkekToRussia * usdToRubExchangeRate,
},
labFees: carState.labFees,
recyclingFee: carState.recyclingFee,
carLocalization: carState.carLocalization,
}
return updatedCarState
}
function sumCarModelsPrices(carState) {
// Получение всех цен из объекта состояния БЕЗ ОПЦИЙ
const {
models,
delivery: {
fromTurgartToBishkek,
customs,
otherExpenses,
fromBishkekToRussia,
},
labFees,
recyclingFee,
carLocalization,
marga,
} = carState
// Суммирование всех цен
const totalPrices = modelNames.map((modelName) => {
const modelPriceCNY =
models.find((model) => model[modelName])?.[modelName].price || 0
const customCNY =
models.find((model) => model[modelName])?.[modelName].custom || 0
const total =
modelPriceCNY +
customCNY +
fromTurgartToBishkek +
customs +
otherExpenses +
fromBishkekToRussia +
labFees +
recyclingFee +
carLocalization
const totalWithMarga = calculateWithPercentage(total, carState.marga)
// return roundNumberToNChars(totalWithMarga, 4, 'sumCarModelsPrices')
return roundNumberToNChars(totalWithMarga, 4, 'sumCarModelsPrices')
})
return totalPrices
}
function sumCarPrices(carState, myPriceModels) {
// Получение всех цен из объекта состояния ОПЦИЙ
const {
options: { color, wheels, interiorColor, runningBoards },
} = carState
const selectedModel = carState.model[1]
const modelIndex = modelNames.indexOf(selectedModel)
// Добавим консольные логи для отслеживания
// Проверим, что modelIndex не равен -1
if (modelIndex === -1) {
console.error('Выбранная модель не найдена в modelNames.')
return // Возвращаем undefined в случае ошибки
}
const modelPrice = myPriceModels[modelIndex]
let total =
modelPrice +
color[0] +
wheels[0] +
interiorColor[0] /* - saleAll если нужно делать вычет */
if (runningBoards[1]) {
const options = runningBoards[0]
options.forEach((option) => {
if (option.show) {
total += option.price
}
})
}
totalPrice = total
return numberWithSpaces(total)
}
const innerPriceHeader = () => {
// ЗАПИСЬ В ШАПКУ ТОТАЛ ПРАЙС
totalpriceElements.forEach((e) => {
e.innerHTML = numberWithSpaces(totalPrice)
})
}
const totalSaleCheck = () => {
// Сумма скидок для выбранной модели
const model = carState.models.find((model) => model[carState.model[1]])?.[
carState.model[1]
]
const modelDiscount = model && model.hasOwnProperty('sale') ? model.sale : 0
// Сумма скидок для выбранного цвета
const colorDiscount =
carState.options.color[2].find(
(option) =>
option.color === carState.options.color[1] &&
option.hasOwnProperty('sale')
)?.sale || 0
// Сумма скидок для выбранных колес
const wheelDiscount =
carState.options.wheels[2].find(
(wheel) => wheel.color === carState.options.wheels[1]
)?.sale || 0
// Сумма скидок для выбранного цвета салона
const interiorColorDiscount =
carState.options.interiorColor[2].find(
(option) => option.color === carState.options.interiorColor[1]
)?.sale || 0
const runningBoardsDiscount = carState.options.runningBoards[0]
.filter((option) => option.show && option.hasOwnProperty('sale'))
.reduce((totalDiscount, option) => totalDiscount + (option.sale || 0), 0)
// Общая сумма всех скидок
const to