PageSourceSearch

https://www.lojadodesejo.com.br/stores/594/static/js/combinedPurchase.js?v=1

js lojadodesejo.com.br collected 2026-10-05 19:29:05 UTC 25,846 bytes, 806 lines download raw bytes

1var combinedPurchaseJS = [];
2
3/**
4 * Configurações de atributos necessárias para a execução do combined purchase na loja
5 *
6 * @var Object
7 */
8const combinedPurchaseSettings = {
9  combinedPurchaseContainer: "[combinedPurchaseContainer]",
10  combinedPurchaseContainerCss: "combined-purchase",
11  productAttribute: "product-attribute",
12  mainProductContainer: "[combinedPurchaseMainProduct]",
13  carouselContainer: ".combined-purchase__carousel",
14  pricesContainer: "[combinedPurchasePrices]",
15  finalPrice: "[combinedPurchaseFinalPrice]",
16  installments: "[buyTogheterInstallments]",
17  productsJson: "#skuProducts",
18  mainProduct: "mainProduct",
19  productCarousel: "productCarousel",
20  imageItemCss: "combined-purchase__image",
21  optionItemCss: "combined-purchase__option",
22  sizeContainerItemCss: "combined-purchase__size-container",
23  sizeItemCss: "combined-purchase__size",
24  buyButton: "[combinedPurchaseBuyButton]",
25  buyButtonDisabled: "[combinedPurchaseBuyDisabledButton]",
26  buyButtonCss: "combined-purchase__buy-button",
27  sizeType: "div",
28  showUnitsOutOfStock: true,
29  installmentsNumber: 6,
30};
31
32/**
33 * Inicializa o combined purchase, pegando o Json inserido no código a partir do atributo "productsJson"
34 *
35 * @returns void
36 */
37combinedPurchaseJS.initilizeCombinedPurchase = function () {
38  let productsJson = document.querySelector(combinedPurchaseSettings.productsJson);
39
40  if (!productsJson) {
41    return;
42  }
43
44  productsJson = productsJson.innerHTML;
45
46  if (!productsJson) {
47    hideProductTogether();
48    return;
49  }
50
51  productsJson = JSON.parse(productsJson);
52  createCombinedPurchase(productsJson);
53};
54
55/**
56 * Oculta o compre junto
57 */
58const hideProductTogether = () => {
59  let combinedPurchaseContainer = document.querySelector(combinedPurchaseSettings.combinedPurchaseContainer);
60  combinedPurchaseContainer.classList.add(`${combinedPurchaseSettings.combinedPurchaseContainerCss}--hidden`);
61};
62
63/**
64 * Cria o combined purchase, utilizando o array recebido na inicialização
65 *
66 * @param {Array} productsJson
67 */
68const createCombinedPurchase = (productsJson) => {
69  let products = productsJson.produtos;
70  let productsCombinedPurchase = products.compreJunto;
71
72  if (!productsCombinedPurchase || productsCombinedPurchase.length < 1) {
73    hideProductTogether();
74    return;
75  }
76
77  generateMainProduct(products);
78  let productsCreated = generateCombinedPurchaseProducts(productsCombinedPurchase);
79
80  if (!productsCreated) {
81    hideProductTogether();
82    return;
83  }
84
85  initializeCombinedPurchaseCarousel();
86  getProdutsToAddEvents();
87  let optionsCombinedPurchase = document.querySelectorAll('.combined-purchase__options-data .combined-purchase__info');
88  optionsCombinedPurchase.forEach(container => {
89    enableDraggableCombinedPurchase(container);
90  });
91
92  let sizesCombinedPurchase = document.querySelectorAll('.combined-purchase__sizes-data .combined-purchase__size-container');
93  sizesCombinedPurchase.forEach(container => {
94    enableDraggableCombinedPurchase(container);
95  });
96};
97
98/**
99 * Gera todo o conteúdo do produto principal
100 *
101 * @param {Array} products
102 */
103const generateMainProduct = (products) => {
104  let productId = Object.keys(products)[0];
105  let product = products[productId];
106  let attribute = combinedPurchaseSettings.mainProduct;
107
108  let productHtml = getProductHtml(product, productId, attribute);
109  let productContainer = document.querySelector(combinedPurchaseSettings.mainProductContainer);
110
111  productContainer.innerHTML = productHtml;
112};
113
114/**
115 * Gera todo o conteudo dos produtos listados em compre junto
116 *
117 * @param {Array} productsCombinedPurchase
118 * @returns {Boolean} retorna se a criação dos produtos foi um sucesso ou não
119 */
120const generateCombinedPurchaseProducts = (productsCombinedPurchase) => {
121  let attribute = combinedPurchaseSettings.productCarousel;
122  let productContainer = document.querySelector(combinedPurchaseSettings.carouselContainer);
123
124  productsCombinedPurchase.forEach((products) => {
125    let productId = Object.keys(products)[0];
126    let product = products[productId];
127
128    let productHtml = getProductHtml(product, productId, attribute);
129
130    productContainer.innerHTML += productHtml;
131  });
132
133  if (!productContainer) {
134    return false;
135  }
136
137  return true;
138};
139
140/**
141 * Inicializa o carrossel do compre junto
142 */
143const initializeCombinedPurchaseCarousel = () => {
144  const productsCarousel = document.querySelector(combinedPurchaseSettings.carouselContainer);
145
146  new Glider(productsCarousel, {
147    slidesToShow: 1,
148    dots: '#dots',
149    draggable: true,
150    arrows: {
151      prev: '.combined-purchase__arrows--left',
152      next: '.combined-purchase__arrows--right'
153    }
154  });
155
156  productsCarousel.addEventListener(('glider-animated','glider-slide-visible'), function (event) {
157    let products = document.querySelectorAll(`[${combinedPurchaseSettings.productCarousel}-item].active.visible`);
158
159    if (products.length > 0) {
160      clickOnFirstOption(products[0], combinedPurchaseSettings.productCarousel);
161      updateTotalPrice();
162    }
163  });
164};
165
166/**
167 * Atualiza o preço total com base no produto principal e no produto combinado
168 */
169const updateTotalPrice = () => {
170  let mainProductPrice = document.querySelector(`[${combinedPurchaseSettings.mainProduct}-priceinput]`).value;
171  let combinedProductPrice = document.querySelector(`[${combinedPurchaseSettings.productCarousel}-priceinput]`).value;
172
173  let totalPrice = parseFloat(mainProductPrice) + parseFloat(combinedProductPrice);
174  let finalPriceContainer = document.querySelector(combinedPurchaseSettings.finalPrice);
175  finalPriceContainer.innerHTML = `R$ ${totalPrice.toFixed(2).replace('.', ',')}`;
176};
177
178/**
179 * Inicializa todos os carrosséis de opções do compre junto
180 */
181const initializeCombinedPurchaseOptionsCarousel = () => {
182  let carousel = document.querySelector(".combined-purchase__options-data .combined-purchase__info");
183
184  new Glider(carousel, {
185    slidesToShow: 1,
186    draggable: true,
187    arrows: false,
188  });
189};
190
191/**
192 * Inicializa todos os carrosséis de tamanhos do compre junto
193 */
194const initializeCombinedPurchaseSizeCarousel = () => {
195  let carousel = document.querySelector(".combined-purchase__sizes-data .combined-purchase__size-container");
196
197  new Glider(carousel, {
198    slidesToShow: 1,
199    draggable: true,
200    arrows: false,
201  });
202};
203
204const enableDraggableCombinedPurchase = (element, speed = 2) => {
205  if (!element) {
206    return
207  }
208  let isDragging = false;
209  let startX;
210  let scrollLeft;
211
212  element.addEventListener('mousedown', (e) => {
213      isDragging = true;
214      startX = e.pageX - element.offsetLeft;
215      scrollLeft = element.scrollLeft;
216      element.style.cursor = 'grabbing';
217  });
218
219  element.addEventListener('mouseleave', () => {
220      isDragging = false;
221      element.style.cursor = 'grab';
222  });
223
224  element.addEventListener('mouseup', () => {
225      isDragging = false;
226      element.style.cursor = 'grab';
227  });
228
229  element.addEventListener('mousemove', (e) => {
230      if (!isDragging) return;
231      e.preventDefault();
232      const x = e.pageX - element.offsetLeft;
233      const walk = (x - startX) * speed;
234      element.scrollLeft = scrollLeft - walk;
235  });
236}
237
238/**
239 * Clica na primeira opção do produto
240 *
241 * @param {HTMLElement} product
242 * @param {String} attribute
243 */
244const clickOnFirstOption = (product, attribute) => {
245  if (product) {
246    const options = product.querySelectorAll(`[${attribute}-option]`);
247    const availableOptions = Array.from(options).filter(option => {
248      const stock = option.getAttribute(`${attribute}-stock`);
249      return stock && parseInt(stock) > 0;
250    });
251
252    if (availableOptions.length > 0) {
253      availableOptions[0].click();
254    } else if (options.length > 0) {
255      options[0].click();
256    }
257  }
258};
259
260/**
261 * Clica no primeiro tamanho que possui estoque
262 *
263 * @param {Array} sizes
264 * @param {String} attribute
265 * @returns
266 */
267const clickOnFirstSizeInStock = (sizes, attribute) => {
268  let sizeSelected = null;
269
270  sizes.forEach((size) => {
271    let stock = size.getAttribute(`${attribute}-stock`);
272
273    if (stock > 0 && !sizeSelected) {
274      sizeSelected = size;
275    }
276  });
277
278  if (combinedPurchaseSettings.sizeType == "select") {
279    if (!sizeSelected) {
280      sizeSelected = sizes[0];
281    }
282
283    let optionSelected = sizeSelected.getAttribute(`${attribute}-option-selected`);
284    let sizeContainer = document.querySelector(`[${attribute}-size-container="${optionSelected}"]`);
285
286    sizeContainer.value = sizeSelected.value;
287
288    sizeContainer.dispatchEvent(new Event("change"));
289  } else {
290    if (!sizeSelected) {
291      sizes[0].click();
292      return;
293    }
294
295    sizeSelected.click();
296  }
297};
298
299/**
300 * Adiciona a primeira imagem no seu respectivo produto
301 *
302 * @param {String} productId
303 * @param {String} attribute
304 */
305const addFirstImageToProduct = (productId, attribute) => {
306  let item = document.querySelector(`[${attribute}-item="${productId}"]`);
307  let imageContainer = document.querySelector(`[${attribute}-image-container="${productId}"]`);
308
309  let image = item.querySelector(`[${attribute}-image]`);
310
311  if (!image) {
312    return;
313  }
314
315  imageContainer.innerHTML = image.innerHTML;
316};
317
318/**
319 * Busca os produtos para adicionar os event listeners
320 */
321const getProdutsToAddEvents = () => {
322  let products = document.querySelectorAll(`[${combinedPurchaseSettings.productAttribute}]`);
323
324  products.forEach((product) => {
325    let attribute = product.getAttribute(combinedPurchaseSettings.productAttribute);
326    let productId = product.getAttribute(`${attribute}-item`);
327
328    addFirstImageToProduct(productId, attribute);
329    addEventsToProduct(productId, attribute);
330  });
331
332  let mainProduct = document.querySelector(`[${combinedPurchaseSettings.mainProduct}-item]`);
333  clickOnFirstOption(mainProduct, combinedPurchaseSettings.mainProduct);
334
335  let productCarousel = document.querySelector(`[${combinedPurchaseSettings.productCarousel}-item]`);
336  clickOnFirstOption(productCarousel, combinedPurchaseSettings.productCarousel);
337};
338
339/**
340 * Adiciona todos os eventos de clique ao respectivo produto
341 *
342 * @param {String} productId
343 * @param {String} attribute
344 */
345const addEventsToProduct = (productId, attribute) => {
346  let item = document.querySelector(`[${attribute}-item="${productId}"]`);
347  let imageContainer = document.querySelector(`[${attribute}-image-container="${productId}"]`);
348  let options = item.querySelectorAll(`[${attribute}-option]`);
349  let sizes = item.querySelectorAll(`[${attribute}-size]`);
350
351  let allOptions = document.querySelectorAll(`[${attribute}-option]`);
352  let allSizes = document.querySelectorAll(`[${attribute}-size]`);
353  let allSizesContainer = document.querySelectorAll(`[${attribute}-size-container]`);
354
355  options.forEach((option) => {
356    option.addEventListener("click", (optionClicked) => {
357      let optionAttributeValue = optionClicked.target.getAttribute(`${attribute}-option`);
358
359      allOptions.forEach((optionNotClicked) => {
360        optionNotClicked.classList.remove(`${combinedPurchaseSettings.optionItemCss}--active`);
361      });
362
363      allSizesContainer.forEach((sizeData) => {
364        sizeData.classList.remove(`${combinedPurchaseSettings.sizeContainerItemCss}--active`);
365      });
366
367      optionClicked.target.classList.add(`${combinedPurchaseSettings.optionItemCss}--active`);
368
369      let image = item.querySelector(`[${attribute}-image="${optionAttributeValue}"]`);
370
371      if (image) {
372        imageContainer.innerHTML = image.innerHTML;
373      }
374
375      let sizeContainer = item.querySelector(`[${attribute}-size-container="${optionAttributeValue}"]`);
376      sizeContainer.classList.add(`${combinedPurchaseSettings.sizeContainerItemCss}--active`);
377
378      let sizes = sizeContainer.querySelectorAll(`[${attribute}-size]`);
379      clickOnFirstSizeInStock(sizes, attribute);
380
381      updateTotalPrice();
382
383      optionClicked.preventDefault();
384    });
385  });
386
387  if (combinedPurchaseSettings.sizeType == "select") {
388    allSizesContainer.forEach((sizeContainer) => {
389      sizeContainer.addEventListener("change", (sizeContainerChanged) => {
390        let sizeValue = sizeContainerChanged.target.value;
391        let sizeClicked = document.querySelector(`[${attribute}-size="${sizeValue}"]`);
392        let price = sizeClicked.getAttribute(`${attribute}-price`);
393        addProductToForm(sizeClicked, attribute);
394        getPrice(price, attribute);
395        checkBuyButtonEnabled();
396
397        sizeContainerChanged.preventDefault();
398      });
399    });
400  } else {
401    sizes.forEach((size) => {
402      size.addEventListener("click", (sizeClicked) => {
403        allSizes.forEach((sizeData) => {
404          sizeData.classList.remove(`${combinedPurchaseSettings.sizeItemCss}--active`);
405        });
406
407        sizeClicked.target.classList.add(`${combinedPurchaseSettings.sizeItemCss}--active`);
408
409        let price = sizeClicked.target.getAttribute(`${attribute}-price`);
410        addProductToForm(sizeClicked.target, attribute);
411        getPrice(price, attribute);
412        checkBuyButtonEnabled();
413
414        sizeClicked.preventDefault();
415      });
416    });
417  }
418};
419
420/**
421 * Adiciona dados do produto no form do compre junto
422 *
423 * @param {HTMLElement} sizeHtml
424 * @param {String} attribute
425 */
426const addProductToForm = (sizeHtml, attribute) => {
427  let productIds = sizeHtml.getAttribute(`${attribute}-size`);
428  let inputFormContainer = document.querySelector(`[${attribute}-form]`);
429  let inputName = inputFormContainer.getAttribute(`${attribute}-form`);
430  let stock = sizeHtml.getAttribute(`${attribute}-stock`);
431  let [product, option, size] = productIds.split("-");
432
433  let html = `
434        <input type="hidden" name="produto${inputName}" value="${product}">
435        <input type="hidden" name="opcao${inputName}" value="${option}">
436        <input type="hidden" name="tamanho${inputName}" value="${size}">
437        <span combinedpurchase-stock-data="${stock}"></span>
438    `;
439
440  inputFormContainer.innerHTML = html;
441};
442
443/**
444 * Verifica se o botão de compra vai estar ativo ou não a partir do estoque dos produtos
445 */
446const checkBuyButtonEnabled = () => {
447  let stocks = document.querySelectorAll(`[combinedpurchase-stock-data]`);
448  let buyButton = document.querySelector(combinedPurchaseSettings.buyButton);
449  let buyButtonDisabled = document.querySelector(combinedPurchaseSettings.buyButtonDisabled);
450  let buttonEnabled = true;
451
452  if (!stocks) {
453    return;
454  }
455
456  stocks.forEach((stock) => {
457    let quantity = stock.getAttribute(`combinedpurchase-stock-data`);
458
459    if (quantity < 1) {
460      buttonEnabled = false;
461    }
462  });
463
464  if (!buttonEnabled) {
465    buyButton.classList.add(`${combinedPurchaseSettings.buyButtonCss}--hidden`);
466    buyButtonDisabled.classList.remove(`${combinedPurchaseSettings.buyButtonCss}--hidden`);
467  } else {
468    buyButton.classList.remove(`${combinedPurchaseSettings.buyButtonCss}--hidden`);
469    buyButtonDisabled.classList.add(`${combinedPurchaseSettings.buyButtonCss}--hidden`);
470  }
471};
472
473/**
474 * Recebe o valor do produto e adiciona no input correspondente
475 *
476 * @param {String} priceString
477 * @param {String} attribute
478 */
479const getPrice = (priceString, attribute) => {
480  let price = priceString.replace("R$ ", "").replace(".", "").replace(",", ".");
481  let priceInput = document.querySelector(`[${attribute}-priceinput]`);
482  priceInput.setAttribute("value", price);
483
484  calculateFinalPrice();
485};
486
487/**
488 * Calcula o preço final do compre junto e apresenta ao usuário
489 */
490const calculateFinalPrice = () => {
491  let mainProductPrice = document.querySelector(`[${combinedPurchaseSettings.mainProduct}-priceinput]`);
492  let combinedPurchasePrice = document.querySelector(`[${combinedPurchaseSettings.productCarousel}-priceinput]`);
493
494  let finalPriceContainer = document.querySelector(combinedPurchaseSettings.finalPrice);
495  let installmentsContainer = document.querySelector(combinedPurchaseSettings.installments);
496
497  mainProductPrice = parseFloat(mainProductPrice.value);
498  combinedPurchasePrice = parseFloat(combinedPurchasePrice.value);
499
500  let finalPrice = mainProductPrice + combinedPurchasePrice;
501  finalPrice = finalPrice.toFixed(2);
502
503  let installments = finalPrice / combinedPurchaseSettings.installmentsNumber;
504  installments = installments.toFixed(2);
505
506  let finalPriceString = "R$ " + finalPrice.replace(".", ",");
507  let installmentsString = "R$ " + installments.replace(".", ",");
508
509  finalPriceContainer.innerHTML = finalPriceString;
510  installmentsContainer.innerHTML = installmentsString;
511};
512
513/**
514 * Calcula o estoque total disponível para uma opção específica de produto.
515 *
516 * @param {Object} units - Um objeto contendo as unidades do produto, onde cada unidade possui informações como estoque e opção.
517 * @param {String} optionId - O ID da opção para a qual o estoque total deve ser calculado.
518 * @returns {Number} O estoque total disponível para a opção especificada.
519 */
520const getTotalStockForOption = (units, optionId) => {
521  return Object.values(units).reduce((total, unit) => {
522    if (unit.opcao.id === optionId) {
523      return total + parseInt(unit.estoque, 10);
524    }
525    return total;
526  }, 0);
527};
528
529/**
530 * Gera todo o código HTML do respectivo produto
531 *
532 * @param {Object} product
533 * @param {String} productId
534 * @param {String} attribute
535 * @returns
536 */
537const getProductHtml = (product, productId, attribute, atributeDiscount) => {
538  const url = document.querySelector("#urlsite").value;
539
540  let units = product.unidades;
541
542  let unitsIds = Object.keys(units);
543  let unitsOptions = [];
544  let unitsSizes = [];
545
546  unitsIds.forEach(function (unitId) {
547    if (
548      !combinedPurchaseSettings.showUnitsOutOfStock &&
549      units[unitId].estoque < 1
550    ) {
551      return;
552    }
553
554    let splittedUnitId = unitId.split("/");
555    let option = splittedUnitId[0];
556    let size = splittedUnitId[1];
557
558    if (!unitsOptions.includes(option)) {
559      unitsOptions.push(option);
560    }
561
562    if (!unitsSizes.includes(size)) {
563      unitsSizes.push(size);
564    }
565  });
566
567  let optionContainerDisplayClass = unitsOptions.length < 2 ? " hidden" : "";
568  let sizeContainerDisplayClass = unitsSizes.length < 2 ? " hidden" : "";
569  let descriptionContainerText = product.descricaoCurta ? product.descricaoCurta : '';
570  
571  let verifyContainersDisplay = 
572  optionContainerDisplayClass === " hidden" &&
573  sizeContainerDisplayClass === " hidden" &&
574  descriptionContainerText === ""
575    ? " combined-purchase__name--only"
576    : "";
577
578  let options = [];
579  let optionsHtml = "";
580  let imagesHtml = "";
581  let sizesHtml = [];
582
583  imagesHtml += getProductImagesHtml(product, productId, attribute);
584
585  for (let unitId in units) {
586    let unit = units[unitId];
587    let optionId = unit.opcao.id;
588    let sizeHtml = getSizeHtml(unit, productId, attribute, product);
589
590    if (!sizeHtml && !combinedPurchaseSettings.showUnitsOutOfStock) {
591      continue;
592    }
593
594    const totalStockForOption = getTotalStockForOption(units, optionId);
595
596    if (options.indexOf(optionId) < 0) {
597      options.push(optionId);
598
599      sizesHtml[optionId] = sizeHtml;
600
601      imagesHtml += getUnitImagesHtml(unit.opcao, productId, attribute);
602      optionsHtml += getOptionHtml(unit.opcao, productId, attribute, totalStockForOption );
603    } else {
604      sizesHtml[optionId] += getSizeHtml(unit, productId, attribute, product);
605    }
606  }
607
608  if (sizesHtml.length < 1) {
609    return "";
610  }
611
612  let sizesContentHtml = getSizesContentHtml(sizesHtml, productId, attribute);
613  let urlProduct = `${url}/produto/${productId}`;
614
615  return `
616        <div class="combined-purchase__item" product-attribute-discount="${atributeDiscount}" product-attribute="${attribute}" ${attribute}-item="${productId}">
617            <a href="${urlProduct}" class="combined-purchase__image-container" ${attribute}-image-container="${productId}">
618            </a>
619            <div class="combined-purchase__content">
620                <div class="combined-purchase__name${verifyContainersDisplay}">${product.nome}</div>
621                <div class="combined-purchase__description">
622                        ${descriptionContainerText}
623                </div>
624                <div class="combined-purchase__options-data${optionContainerDisplayClass}">
625                        <span>Cor</span>
626                        <div class="combined-purchase__info">
627                            ${optionsHtml}
628                        </div>
629                </div>
630                <div class="combined-purchase__sizes-data${sizeContainerDisplayClass}">
631                        <span>Tamanho</span>
632                        <div class="combined-purchase__info">
633                            ${sizesContentHtml}
634                        </div>
635                </div>
636            </div>
637            <div class="combined-purchase__image-data">
638                ${imagesHtml}
639            </div>
640        </div>
641    `;
642};
643
644/**
645 * Gera o código Html das imagens do produto
646 *
647 * @param {Object} product
648 * @param {String} productId
649 * @param {String} attribute
650 * @returns {String}
651 */
652const getProductImagesHtml = (product, productId, attribute) => {
653  let images = product.imagens;
654  let html = "";
655
656  if (!images) {
657    return html;
658  }
659
660  images.forEach((image) => {
661    html += `
662            <div ${attribute}-image="${productId}">
663                <img class="combined-purchase__image" alt="image" src=${image} width="300" height="300" style="height: auto" loading="lazy"/>
664            </div>
665        `;
666  });
667
668  return html;
669};
670
671/**
672 * Gera o código Html das imagens da unidade
673 *
674 * @param {Object} option
675 * @param {String} productId
676 * @param {String} attribute
677 * @returns {String}
678 */
679const getUnitImagesHtml = (option, productId, attribute) => {
680  if (!option.imagem) {
681    return "";
682  }
683
684  return `
685        <div ${attribute}-image="${productId}-${option.id}">
686            <img class="combined-purchase__image" alt="image" src=${option.imagem} width="300" height="300" style="height: auto" loading="lazy"/>
687        </div>
688    `;
689};
690
691/**
692 * Gera o código Html das opcções
693 *
694 * @param {Object} option
695 * @param {String} productId
696 * @param {String} attribute
697 * @returns {String}
698 */
699const getOptionHtml = (option, productId, attribute, totalStock) => {
700  let html = "";
701
702  var imagemCor = option.imagemCor ? option.imagemCor : "";
703  var imagem = option.imagem ? option.imagem : "";
704
705  if (imagemCor || imagem) {
706    html = `
707        <img 
708            class="combined-purchase__option"
709            ${attribute}-option="${productId}-${option.id}"
710            ${attribute}-stock="${totalStock}"
711            src="${imagemCor || imagem}"
712            alt="${option.descricao}" 
713            title="${option.descricao}" 
714            loading="lazy" width="60" height="60" 
715        />
716    `;
717  } else if (option.corHexa != null && option.corHexa != "") {
718    html = `
719            <a
720                class="combined-purchase__option"
721                style="background-color: #${option.corHexa}"
722                alt="${option.descricao}"
723                ${attribute}-option="${productId}-${option.id}"
724                ${attribute}-stock="${totalStock}"
725            ></a>
726        `;
727  } else {
728    let optionImage = $("#urlsite").val() + "/static/img/semimagem.jpg";
729
730    html = `
731        <img 
732            class="combined-purchase__option"
733            ${attribute}-option="${productId}-${option.id}" 
734            ${attribute}-stock="${totalStock}"
735            src="${optionImage}" 
736            alt="${option.descricao}" 
737            title="${option.descricao}"
738            loading="lazy" width="60" height="60" 
739        />
740    `;
741  }
742
743  return html;
744};
745
746/**
747 * Gera o código Html dos tamanhos
748 *
749 * @param {Object} size
750 * @param {String} productId
751 * @param {String} attribute
752 * @returns {String}
753 */
754const getSizeHtml = (unit, productId, attribute) => {
755  let size = unit.tamanho;
756  let option = unit.opcao;
757  let unitNotAllowed = "";
758
759  if (!combinedPurchaseSettings.showUnitsOutOfStock && unit.estoque < 1) {
760    return "";
761  }
762
763  if (unit.estoque < 1) {
764    unitNotAllowed = "combined-purchase__size--disabled";
765  }
766
767  return `
768        <a
769            value="${productId}-${option.id}-${size.id}"
770            class="combined-purchase__size ${unitNotAllowed}"
771            ${attribute}-price="${unit.por}"
772            ${attribute}-option-selected="${productId}-${option.id}"
773            ${attribute}-size="${productId}-${option.id}-${size.id}"
774            ${attribute}-stock="${unit.estoque}"
775        >
776            ${size.descricao}
777        </a>
778    `;
779};
780
781/**
782 * Gera o código Html do container dos tamanhos e adiciona o HTML dos tamanhos dentro dele
783 *
784 * @param {String} sizesHtml
785 * @param {String} productId
786 * @param {String} attribute
787 * @returns {String}
788 */
789const getSizesContentHtml = (sizesHtml, productId, attribute) => {
790  let html = "";
791
792  sizesHtml.map((sizes, optionId) => {
793    html += `
794            <div class="combined-purchase__size-container" ${attribute}-size-container="${productId}-${optionId}">
795                ${sizes}
796            </div>
797        `;
798  });
799
800  return html;
801};
802
803/**
804 * Executa a aplicação após o DOM estar pronto
805 */
806lazyLoadService.addFunctionToQueueAfterInteraction(combinedPurchaseJS.initilizeCombinedPurchase);

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.