PageSourceSearch

https://nutriben.es/cdn/shop/t/11/assets/custom-recommendations.js?v=153808704974993533371751288551

js nutriben.es collected 2026-10-03 21:19:24 UTC 13,764 bytes, 176 lines download raw bytes

1class CustomRecommendations{constructor(container){this.container=container,this.relatedIds=JSON.parse(this.container.getAttribute("data-related-ids")),this.sectionId=this.container.getAttribute("data-section-id"),this.relatedProductsContainer=this.container.querySelector("#related-products"),this.storefrontAccessToken=this.container.getAttribute("data-storefront-access-token"),this.graphqlEndpoint="/api/2023-10/graphql.json",this.loadingText=this.container.dataset.loadingText||"Loading related products...",this.noRecommendationsText=this.container.dataset.noRecommendationsText||"No related products found",this.init()}init(){if(!this.relatedIds||!this.relatedIds.length)return;const sliderElement=document.createElement("slider-element");sliderElement.id=`Slider-${this.sectionId}`,sliderElement.className=this.container.getAttribute("data-slider-class")||"grid slider",sliderElement.setAttribute("selector",".card-grid>.card");const motionList=document.createElement("motion-list");motionList.className=this.container.getAttribute("data-grid-class")||"card-grid grid",sliderElement.appendChild(motionList),this.relatedProductsContainer.innerHTML=`<div class="text-center py-8">${this.loadingText}</div>`,this.motionList=motionList,this.sliderElement=sliderElement,this.fetchProductsByIds(this.relatedIds).then(products=>this.renderProducts(products)).catch(error=>{console.error("Error processing products:",error),this.relatedProductsContainer.innerHTML=`<div class="text-center py-8">${this.noRecommendationsText}</div>`})}async fetchProductsByIds(productIds){const skuQuery=productIds.join(" OR "),query=`
2      query getProductsBySKU {
3        products(first: ${productIds.length}, query: "${skuQuery}") {
4          nodes {
5            id
6            title
7            handle
8            vendor
9            availableForSale
10            metafield(namespace: "custom", key: "pack_skus") {
11              value
12            }
13            featuredImage {
14              url(transform: { maxWidth: 800 })
15            }
16            images(first: 2) {
17              edges {
18                node {
19                  url(transform: { maxWidth: 800 })
20                }
21              }
22            }
23            variants(first: 1) {
24              edges {
25                node {
26                  id
27                  price {
28                    amount
29                    currencyCode
30                  }
31                  compareAtPrice {
32                    amount
33                    currencyCode
34                  }
35                  availableForSale
36                }
37              }
38            }
39          }
40        }
41      }
42    `;try{const result=await(await fetch(this.graphqlEndpoint,{method:"POST",headers:{"Content-Type":"application/json","X-Shopify-Storefront-Access-Token":this.storefrontAccessToken},body:JSON.stringify({query})})).json();if(result.errors)throw console.error("GraphQL errors:",result.errors),new Error("Error fetching products via GraphQL");return result.data.products.nodes.filter(node=>node!==null)}catch(error){throw console.error("Error fetching products with GraphQL:",error),error}}renderProducts(products){if(!products||products.length===0){this.relatedProductsContainer.innerHTML=`<div class="text-center py-8">${this.noRecommendationsText}</div>`;return}this.relatedProductsContainer.innerHTML="",this.relatedProductsContainer.appendChild(this.sliderElement),products.forEach(product=>this.renderProductCard(product)),typeof window.initializeSlider=="function"&&window.initializeSlider(this.sliderElement)}renderProductCard(product){const card=document.createElement("div");card.className="card product-card product-card--standard product-card--thickness flex flex-col leading-none relative overflow-hidden";const productId=product.id.split("/").pop(),productUrl=`/products/${product.handle}`,productTitle=product.title,productVendor=product.vendor,imageUrl=product.featuredImage?product.featuredImage.url:"",isAvailable=product.availableForSale,variantId=product.variants.edges[0]?.node.id.split("/").pop()||"",packSelector=this.renderPackSelector(product,productId),quickAddAvailableHidden=isAvailable?"":"hidden",quickAddSoldoutHidden=isAvailable?"hidden":"";let cardHtml=`
43      <div class="product-card__media relative h-auto overflow-hidden">
44        <button type="button" class="quick-view__button button button--secondary z-2 absolute top-0 right-0 opacity-0" is="hover-button" aria-controls="Quickview-${this.sectionId}-${productId}" aria-expanded="false">
45          <span class="btn-fill" data-fill></span>
46          <span class="btn-text">
47            <svg class="icon icon-eye icon-sm" viewBox="0 0 20 20" stroke="currentColor" fill="none" xmlns="http://www.w3.org/2000/svg">
48              <path stroke-linecap="round" d="M18.3334 10C18.3334 12.0833 15.8334 16.6667 10 16.6667C4.16669 16.6667 1.66669 12.0833 1.66669 10C1.66669 7.91668 4.16669 3.33334 10 3.33334C15.8334 3.33334 18.3334 7.91668 18.3334 10Z"></path>
49              <path stroke-linecap="round" d="M12.5 10C12.5 11.3807 11.3807 12.5 10 12.5C8.61931 12.5 7.50002 11.3807 7.50002 10C7.50002 8.6193 8.61931 7.50001 10 7.50001C11.3807 7.50001 12.5 8.6193 12.5 10Z"></path>
50            </svg>
51            <span class="sr-only">Ver</span>
52          </span>
53          <span class="btn-loader">
54            <span></span>
55            <span></span>
56            <span></span>
57          </span>
58        </button>
59        
60        <quick-view id="Quickview-${this.sectionId}-${productId}" class="quick-view x-modal drawer z-40 fixed bottom-0 left-0 h-full w-full pointer-events-none" data-product-url="${productUrl}" role="dialog" aria-label="Elegir opciones para ${productTitle}" aria-modal="true">
61          <overlay-element class="overlay fixed-modal invisible opacity-0 fixed bottom-0 left-0 w-full h-screen pointer-events-none" aria-controls="Quickview-${this.sectionId}-${productId}" aria-expanded="false"></overlay-element>
62          <div class="drawer__inner z-10 absolute top-0 flex flex-col w-full h-full overflow-hidden">
63            <gesture-element class="drawer__header flex justify-between opacity-0 invisible relative">
64              <button class="button button--secondary button--close drawer__close z-1 absolute top-0 right-0 flex items-center justify-center opacity-0" type="button" is="hover-button" aria-controls="Quickview-${this.sectionId}-${productId}" aria-expanded="false" aria-label="Cerrar">
65                <span class="btn-fill" data-fill></span>
66                <span class="btn-text">
67                  <svg class="icon icon-close icon-sm" viewBox="0 0 20 20" stroke="currentColor" fill="none" xmlns="http://www.w3.org/2000/svg">
68                    <path stroke-linecap="round" stroke-linejoin="round" d="M5 15L15 5M5 5L15 15"></path>
69                  </svg>
70                </span>
71                <span class="btn-loader">
72                  <span></span>
73                  <span></span>
74                  <span></span>
75                </span>
76              </button>
77            </gesture-element>
78            <div class="quick-view__content drawer__content opacity-0 invisible flex flex-col h-full grow shrink"></div>
79          </div>
80        </quick-view>
81        
82        <div class="badges z-2 absolute grid gap-3 pointer-events-none"></div>
83        
84        <a class="block relative media media--${this.container.getAttribute("data-image-ratio")||"portrait"} overflow-hidden" href="${productUrl}" aria-label="${productTitle}" tabindex="-1">
85          ${imageUrl?`<img src="${imageUrl}" alt="${productTitle}" loading="lazy" is="lazy-image">`:""}
86        </a>
87        
88        <div class="quick-add flex justify-end md:justify-center absolute w-full z-1 pointer-events-none quick-add-available ${quickAddAvailableHidden}">
89            <form method="post" action="/cart/add" id="QuickAddProductForm-${this.sectionId}-${productId}" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data" is="product-form">
90              <input type="hidden" name="form_type" value="product">
91              <input type="hidden" name="utf8" value="\u2713">
92              <input type="hidden" name="id" value="${variantId}">
93              <button type="submit" class="button button--primary pointer-events-auto md:opacity-0" is="hover-button" data-variant-id="${variantId}" aria-label="Agregar al carrito">
94                <span class="btn-fill" data-fill></span>
95                <span class="btn-text">
96                  <svg class="icon icon-cart icon-sm md:hidden" viewBox="0 0 24 24" stroke="currentColor" fill="none" xmlns="http://www.w3.org/2000/svg">
97                    <path stroke-miterlimit="10" d="M7.1802 7.58C7.1802 4.36 9.2402 1.75 11.7902 1.75C14.3402 1.75 16.4002 4.36 16.4002 7.58M11.7902 7.58C2.0402 7.58 0.690197 6.97 2.3502 16.18C3.3002 21.48 3.9802 22.22 11.7902 22.22C19.6002 22.22 20.2802 21.47 21.2302 16.18C22.8902 6.97 21.5502 7.58 11.7902 7.58Z"></path>
98                  </svg>
99                  <span class="hidden md:block" data-sold-out="Agotado">Agregar al carrito</span>
100                </span>
101                <span class="btn-loader">
102                  <span></span>
103                  <span></span>
104                  <span></span>
105                </span>
106              </button>
107              <input type="hidden" name="product-id" value="${productId}">
108              <input type="hidden" name="section-id" value="${this.sectionId}">
109            </form>
110        </div>
111        <div class="quick-add flex justify-end md:justify-center absolute w-full z-1 pointer-events-none quick-add-soldout ${quickAddSoldoutHidden}">
112            <button type="button" class="button button--secondary button--blur" disabled="disabled">
113              Agotado
114            </button>
115        </div>
116      </div>
117      
118      <div class="product-card__content grow flex flex-col justify-start text-left">
119        <div class="product-card__top w-full">
120          <span class="sr-only">Proveedor:</span>
121          <a class="caption reversed-link uppercase leading-none tracking-widest" href="/collections/vendors?q=${encodeURIComponent(productVendor)}" title="${productVendor}">${productVendor}</a>
122        </div>
123        
124        <div class="product-card__details flex flex-col lg:flex-row items-baseline gap-2 w-full">
125          <p class="grow">
126            <a class="product-card__title reversed-link text-base-xl font-medium leading-tight" href="${productUrl}">${productTitle}</a>
127          </p>
128          
129          <div class="price flex flex-wrap lg:flex-col lg:items-end gap-2 md:gap-1d5">
130            ${this.getPriceHtml(product)}
131          </div>
132        </div>
133        
134        ${packSelector}
135      </div>
136    `;card.innerHTML=cardHtml,this.motionList.appendChild(card)}renderPackSelector(product,productId){if(!product.metafield||!product.metafield.value)return"";const elementId=`${this.sectionId}-${productId}`,formId=`QuickAddProductForm-${this.sectionId}-${productId}`,price=product.variants.edges[0]?.node.price?.amount||"0",compareAtPrice=product.variants.edges[0]?.node.compareAtPrice?.amount||"";return`
137      <div class="product-pack-variant-selector" data-pack-variant-selector data-context="card" style="display:none">
138        <div class="form__label flex items-center justify-between gap-2 w-full">
139          <label class="flex gap-2">Packs:</label>
140        </div>
141        <pack-variant-selects 
142          id="PackVariantSelector-${elementId}" 
143          class="pack-variant-selects"
144          data-section-id="${this.sectionId}" 
145          data-product-id="${productId}"
146          data-product-handle="${product.handle}"
147          data-product-form-id="${formId}"
148          data-storefront-access-token="${this.storefrontAccessToken}"
149          data-context="card">
150          <div class="pack-radio-group">
151            <div class="pack-radio-item">
152              <input type="radio" 
153                    id="pack-default-${elementId}" 
154                    name="pack-option-${elementId}" 
155                    value="${product.handle}" 
156                    class="sr-only pack-radio"
157                    data-is-default="true" 
158                    data-available="${product.availableForSale}"
159                    data-variant-id="${product.variants.edges[0]?.node.id.split("/").pop()||""}"
160                    data-product-title="${product.title}"
161                    data-product-image="${product.featuredImage?.url||""}"
162                    data-product-price="${price}"
163                    data-product-compare-at-price="${compareAtPrice}"
164                    checked>
165              <label for="pack-default-${elementId}" class="active">1 unidad</label>
166            </div>
167            
168            <template data-pack-data>${product.metafield.value}</template>
169          </div>
170        </pack-variant-selects>
171      </div>
172    `}getPriceHtml(product){if(!product.variants||!product.variants.edges||
172!product.variants.edges.length)return"";const variant=product.variants.edges[0].node,price=window.theme.formatMoney(parseFloat(variant.price.amount)*100,window.theme.moneyFormat);return variant.compareAtPrice&&parseFloat(variant.compareAtPrice.amount)>parseFloat(variant.price.amount)?`
173        <span class="price__sale whitespace-nowrap">${window.theme.formatMoney(parseFloat(variant.compareAtPrice.amount)*100,window.theme.moneyFormat)}</span>
174        <span class="price__regular whitespace-nowrap">${price}</span>
175      `:`<span class="price__regular whitespace-nowrap">${price}</span>`}}document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("[data-custom-recommendations]").forEach(container=>{new CustomRecommendations(container)})});
176//# sourceMappingURL=/cdn/shop/t/11/assets/custom-recommendations.js.map?v=153808704974993533371751288551

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.