1(function ($) { 2 'use strict'; 3 4 let hasUserInteracted = false; 5 6 function triggerCartButtonEvents() { 7 const element = document.querySelector('.wps-btn-cart'); 8 if (element) { 9 // Dispatch mouse events for desktop 10 element.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); 11 element.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); 12 element.dispatchEvent(new MouseEvent('click', { bubbles: true })); 13 14 // Dispatch touch events for mobile devices 15 if ('ontouchstart' in window) { 16 element.dispatchEvent(new TouchEvent('touchstart', { bubbles: true, cancelable: true })); 17 element.dispatchEvent(new TouchEvent('touchend', { bubbles: true, cancelable: true })); 18 } 19 } 20 } 21 22 function handleFirstUserInteraction() { 23 if (!hasUserInteracted) { 24 hasUserInteracted = true; 25 setTimeout(() => { 26 triggerCartButtonEvents(); 27 }, 300); 28 29 // Remove event listeners after first interaction 30 document.removeEventListener('click', handleFirstUserInteraction, true); 31 document.removeEventListener('touchstart', handleFirstUserInteraction, true); 32 document.removeEventListener('keydown', handleFirstUserInteraction, true); 33 document.removeEventListener('scroll', handleFirstUserInteraction, true); 34 } 35 } 36 37 const ProductDisplay = { 38 instances: {}, 39 40 init: function () { 41 const self = this; 42 43 wp.hooks.addAction('on.itemsLoad', 'shopwp', function (payload, settings) { 44 handleFirstUserInteraction(); 45 }); 46 47 wp.hooks.addAction('on.afterPayloadUpdate', 'shopwp', function (itemsState) { 48 self.handleProductData(itemsState); 49 }); 50 }, 51 52 handleProductData: function (itemsState) { 53 const self = this; 54 55 if (!itemsState || !itemsState.edges || itemsState.edges.length === 0) { 56 return; 57 } 58 59 const product = itemsState.edges[0].node; 60 61 $('.bhs-product-display').each(function () { 62 const $container = $(this); 63 const uniqueId = $container.data('unique-id'); 64 const config = window.productDisplayConfigs[uniqueId]; 65 66 if (!config) return; 67 68 self.renderProduct($container, uniqueId, product, config); 69 }); 70 }, 71 72 renderProduct: function ($container, uniqueId, product, config) { 73 const self = this; 74 75 if (!self.instances[uniqueId]) { 76 self.instances[uniqueId] = { 77 allVariants: [], 78 selectedOption: null, 79 basePrice: 0 80 }; 81 } 82 83 const instance = self.instances[uniqueId]; 84 85 if (product.totalInventory <= 2 && product.totalInventory > 0) { 86 $container.find('.product__inventory').html( 87 '<span class="product__inventory--only">Hurry, only ' + product.totalInventory + ' left</span>' 88 ); 89 } 90 91 if (config.showDescription && !config.customDescription) { 92 self.renderDescription($container, uniqueId, product.descriptionHtml); 93 } 94 95 if (config.showAvailability && !config.hideInStock) { 96 self.renderAvailability($container, uniqueId, product.availableForSale); 97 } 98 99 if (config.showTitle && !config.customTitle) { 100 if (product.vendor) { 101 $container.find('.product-single__title').html(product.vendor); 102 } 103 if (product.title) { 104 $container.find('.product-title-vendor-style').html(product.title); 105 } 106 } 107 108 if (config.showPrice && product.priceRange) { 109 self.renderPrice($container, product.priceRange); 110 instance.basePrice = product.priceRange.maxVariantPrice.amount; 111 } 112 113 if (!product.availableForSale) { 114 return; 115 } 116 117 const selectors = {}; 118 let salePrice = 0; 119
120 product.variants.edges.forEach(function (variant) { 121 if (variant.node.availableForSale) { 122 instance.allVariants.push(variant.node); 123 124 if (variant.node.compareAtPriceV2 && variant.node.compareAtPriceV2.amount) { 125 if (salePrice > variant.node.compareAtPriceV2.amount || salePrice == 0) { 126 salePrice = variant.node.compareAtPriceV2.amount; 127 } 128 } 129 130 variant.node.selectedOptions.forEach(function (option) { 131 if (selectors[option.name]) { 132 if (!selectors[option.name].values.includes(option.value)) { 133 selectors[option.name].values.push(option.value); 134 } 135 } else { 136 selectors[option.name] = { 137 values: [option.value] 138 }; 139 } 140 }); 141 } 142 }); 143 144 if (salePrice > 0) { 145 const formattedSalePrice = self.formatMoney(salePrice); 146 $container.find('.product__price').append( 147 '<span class="price-item sale-price-item">$' + formattedSalePrice + '</span>' 148 ); 149 $container.find('.product__price').append( 150 '<span class="price__badge price__badge--sale"><span>Sale</span></span>' 151 ); 152 } 153 154 if (config.showBuyButton) { 155 self.renderBuyForm($container, uniqueId, selectors, config.buttonText, config); 156 } 157 }, 158 159 renderDescription: function ($container, uniqueId, descriptionHtml) { 160 if (!descriptionHtml) return; 161 162 const arrayDescription = descriptionHtml.split(/<h4[^>]*>|<\/h4>/).filter(String); 163 164 if (arrayDescription.length > 1) { 165 let tabs = $('<div class="station-tabs"></div>'); 166 let count = 0; 167 168 arrayDescription.forEach(function (desc, i) { 169 if (i % 2 == 0) { 170 const actual = count; 171 const link = $('<a href="#tab-' + count + '" class="station-tabs-link"><span>' + desc + '</span></a>'); 172 link.click(function (e) { 173 e.preventDefault(); 174 ProductDisplay.setActiveTab($container, actual); 175 }); 176 tabs.append(link); 177 } else { 178 tabs.append( 179 '<div class="station-tabs-content" id="st-tab-' + count + '">' + 180 '<div class="station-tabs-content-inner">' + desc + '</div></div>' 181 ); 182 count++; 183 } 184 }); 185 186 $container.find('#' + uniqueId + '_description').html(tabs); 187 this.setActiveTab($container, 0); 188 } else { 189 $container.find('#' + uniqueId + '_description').html(descriptionHtml); 190 } 191 }, 192 193 setActiveTab: function ($container, index) { 194 $container.find('.station-tabs-link').removeClass('active'); 195 $container.find('.station-tabs-link').eq(index).addClass('active'); 196 $container.find('.station-tabs-content').hide(); 197 $container.find('.station-tabs-content').eq(index).show(); 198 }, 199 200 renderAvailability: function ($container, uniqueId, availableForSale) { 201 const html = availableForSale 202 ? '<span class="product-availability in-stock">In Stock</span>' 203 : '<span class="product-availability out-stock">Sold out</span>'; 204 $container.find('#' + uniqueId + '_availability').html(html); 205 }, 206 207 renderPrice: function ($container, priceRange) { 208 if (priceRange && priceRange.maxVariantPrice) { 209 const price = this.formatMoney(priceRange.maxVariantPrice.amount); 210 $container.find('.product__price').html( 211 '<span class="price-item">$' + price + '</span>' 212 ); 213 } 214 }, 215 216 renderBuyForm: function ($container, uniqueId, selectors, buttonText, config) { 217 const self = this; 218 const $buyContainer = $container.find('#' + uniqueId + '_buy'); 219 const $form = $('<form method="post" class="form-product"></form>'); 220 221 $container.find('.product__variant').html(''); 222 223 // Ocultar variaciones si está configurado 224 const hideVariations = config.hideVariations || false; 225 226 if (selectors && Object.keys(selectors).length > 0 && !hideVariations) { 227 const entries = Object.entries(selectors).reverse(); 228 const $controlsGroup = $('<div class="controls-group"></div>'); 229 230 entries.forEach(function (sel) { 231 const options = '<option>' + sel[1].values.join('</option><option>') + '</option>'; 232 const $controlOption = $('<div class="shop-option"></div>'); 233 $controlOption.append('<label>' + sel[0] + '</label>'); 234 235 const $selector = $('<select data-name="' + sel[0] + '" class="light-style" name="shop-' + sel[0] + '">' + options + '</select>'); 236 $selector.change(function () { 237 self.onChangeOption($container, uniqueId); 238 }); 239 240 $controlOption.append($selector); 241 $controlsGroup.append($controlOption); 242 }); 243 244 $form.append($controlsGroup); 245 } 246 247 $form.append( 248 '<div class="control-holder">' + 249 '<div class="custom-form"><input type="number" name="quantity" value="1" min="1" class="quantity-input"></div>' + 250 '<button type="submit" class="btn btn-secondary" id="shop-add-to-cart-' + uniqueId + '">' + 251 '<span>' + buttonText + '</span>' + 252 '</button>' + 253 '</div>' 254 ); 255 256 $buyContainer.html($form); 257 258 if (typeof jcf !== 'undefined') { 259 jcf.replace($form.find('input')); 260 jcf.replace($form.find('select')); 261 } 262 263 // Preseleccionar variante si está configurado 264 if (config.hideVariations && config.preselectedVariant) { 265 self.preselectVariant($container, uniqueId, config.preselectedVariant); 266 } else { 267 self.onChangeOption($container, uniqueId); 268 } 269 270 $form.submit(function (e) { 271 e.preventDefault(); 272 self.addToCart($container, uniqueId); 273 }); 274 }, 275 276 onChangeOption: function ($container, uniqueId) { 277 const instance = this.instances[uniqueId]; 278 instance.selectedOption = null; 279 280 const $button = $container.find('#shop-add-to-cart-' + uniqueId); 281 $button.prop('disabled', true); 282 $button.find('span').html('Sold out'); 283 284 $container.find('.product__price').html( 285 '<span class="price-item">$' + this.formatMoney(instance.basePrice) + '</span>' 286 ); 287 288 const selected = {}; 289 $container.find('.controls-group select').each(function () { 290 const name = $(this).data('name'); 291 const value = $(this).val(); 292 selected[name] = value; 293 }); 294 295 if (instance.allVariants.length == 1) { 296 instance.selectedOption = instance.allVariants[0]; 297 } else {
298 instance.allVariants.forEach(function (variant) { 299 let equals = 0; 300 variant.selectedOptions.forEach(function (option) { 301 if (selected[option.name] && selected[option.name] == option.value) { 302 equals++; 303 } 304 }); 305 if (equals == variant.selectedOptions.length) { 306 instance.selectedOption = variant; 307 return; 308 } 309 }); 310 } 311 312 if (instance.selectedOption) { 313 $button.prop('disabled', false); 314 $button.find('span').html(window.productDisplayConfigs[uniqueId].buttonText); 315 316 if (instance.selectedOption.priceV2) { 317 const price = this.formatMoney(instance.selectedOption.priceV2.amount); 318 $container.find('.product__price').html( 319 '<span class="price-item">$' + price + '</span>' 320 ); 321 } 322 323 if (instance.selectedOption.sku) { 324 $container.find('.product__variant-sku').html('SKU: ' + instance.selectedOption.sku); 325 } 326 } 327 }, 328 329 addToCart: function ($container, uniqueId) { 330 const instance = this.instances[uniqueId]; 331 332 if (!instance.selectedOption) { 333 return; 334 } 335 336 const quantity = parseInt($container.find('input[name="quantity"]').val()) || 1; 337 338 if (quantity > 0) { 339 const $button = $container.find('#shop-add-to-cart-' + uniqueId); 340 $button.prop('disabled', true); 341 $button.find('span').html('Adding...'); 342 343 const params = { 344 lines: [ 345 { 346 quantity: quantity, 347 merchandiseId: instance.selectedOption.id, 348 }, 349 ], 350 extras: { 351 openCartAfterAdding: true, 352 }, 353 }; 354 355 window.dataLayer = window.dataLayer || []; 356 window.dataLayer.push({ 357 event: "add_to_cart", 358 element_id: "#shop-add-to-cart-" + uniqueId, 359 element_type: "add_to_cart", 360 params: params, 361 }); 362 363 if (typeof wp !== 'undefined' && wp.hooks) { 364 wp.hooks.doAction('do.addToCart', params); 365 } 366 367 triggerCartButtonEvents(); 368 369 setTimeout(function () { 370 $button.prop('disabled', false); 371 $button.find('span').html(window.productDisplayConfigs[uniqueId].buttonText); 372 }, 1000); 373 } 374 }, 375 376 preselectVariant: function ($container, uniqueId, variantIndex) { 377 const instance = this.instances[uniqueId]; 378 const index = parseInt(variantIndex) - 1; 379 380 if (instance.allVariants && instance.allVariants[index]) { 381 instance.selectedOption = instance.allVariants[index]; 382 383 const $button = $container.find('#shop-add-to-cart-' + uniqueId); 384 $button.prop('disabled', false); 385 $button.find('span').html(window.productDisplayConfigs[uniqueId].buttonText); 386 387 if (instance.selectedOption.priceV2) { 388 const price = this.formatMoney(instance.selectedOption.priceV2.amount); 389 $container.find('.product__price').html( 390 '<span class="price-item">$' + price + '</span>' 391 ); 392 } 393 394 if (instance.selectedOption.sku) { 395 $container.find('.product__variant-sku').html('SKU: ' + instance.selectedOption.sku); 396 } 397 } else { 398 if (instance.allVariants && instance.allVariants.length > 0) { 399 instance.selectedOption = instance.allVariants[0]; 400 this.onChangeOption($container, uniqueId); 401 } 402 } 403 }, 404 405 formatMoney: function (amount) { 406 return parseFloat(amount).toFixed(2); 407 } 408 }; 409 410 $(document).ready(function () { 411 if (typeof wp !== 'undefined' && wp.hooks) { 412 ProductDisplay.init(); 413 } 414 }); 415 416})(jQuery);
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.