1<!doctype html> 2<html lang="en-AU"> 3<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# product: http://ogp.me/ns/product#"> 4 <meta charset="utf-8"/> 5<meta name="title" content="Huskee | Reusable Coffee Cups"/> 6<meta name="description" content="Shop Huskee's reusable coffee cups made from recycled & repurposed materials â HuskeeOriginal, HuskeeRenew & HuskeeSteel. Built to replace single-use."/> 7<meta name="robots" content="INDEX,FOLLOW"/> 8<meta name="viewport" content="width=device-width, initial-scale=1"/> 9<title>Huskee | Reusable Coffee Cups</title>
10<script type="text/javascript"> window.dataLayer = window.dataLayer || []; 11 function gtag() { dataLayer.push(arguments) } 12 13 window.amConsentManager = window.amConsentManager || { 14 consentStates: { 15 denied: 'denied', 16 granted: 'granted' 17 }, 18 storageName: 'amConsentMode', 19 gdprCookie: { 20 cookieGroupName: 'amcookie_allowed', 21 allCookiesAllowed: '0' 22 }, 23 defaultConsentTypesData: {"ad_storage":{"default":"0","group":"4"},"analytics_storage":{"default":"0","group":"3"},"ad_user_data":{"default":"1","group":"2"},"ad_personalization":{"default":"0","group":"4"}}, 24 currentStoreId: 27, 25 init: function () { 26 this.setDefaultConsent(); 27 28 if (this.shouldResetConsentState()) { 29 this.resetAllowedCookiesGroups(); 30 this.removeCurrentConsentState(); 31 } else { 32 this.sendUpdateConsent(); 33 } 34 }, 35 setDefaultConsent: function () { 36 gtag('consent', 'default', this.getFormattedDefaultConsentState()); 37 }, 38 getFormattedDefaultConsentState: function () { 39 let consentState = {}; 40 for (const [type, data] of Object.entries(this.defaultConsentTypesData)) { 41 consentState[type] = data.default === '1' ? this.consentStates.granted : this.consentStates.denied; 42 } 43 44 return consentState; 45 }, 46 updateConsent: function (consent) { 47 const storageConsent = this.getPersistedConsentTypeState(); 48 const consentState = {...storageConsent, ...consent}; 49 50 this.persistConsentTypeState(consentState); 51 this.sendUpdateConsent(); 52 }, 53 sendUpdateConsent: function () { 54 gtag('consent', 'update', this.getPersistedConsentTypeStateForGtag()); 55 }, 56 removeCurrentConsentState: function () { 57 this.persistConsentTypeState({}); 58 }, 59 getConsentTypeStateByGroupIds: function (groups) { 60 let consentTypeState = {}; 61 62 for (const [type, data] of Object.entries(this.defaultConsentTypesData)) { 63 consentTypeState[type] = { 64 group: data.group, 65 status: (groups.includes(data.group) || groups.includes(this.gdprCookie.allCookiesAllowed)) 66 ? this.consentStates.granted 67 : this.consentStates.denied 68 } 69 } 70 71 return consentTypeState; 72 }, 73 persistConsentTypeState: function (consentTypeState) { 74 let persistedState = this.getPersistedConsentTypeState(false); 75 persistedState[this.currentStoreId] = consentTypeState; 76 localStorage.setItem(this.storageName, JSON.stringify(persistedState)); 77 }, 78 getPersistedConsentTypeState: function (forCurrentStore = true) { 79 let persistedStates = JSON.parse(localStorage.getItem(this.storageName)) ?? {}; 80 81 if (forCurrentStore) { 82 return persistedStates?.[this.currentStoreId] ?? {}; 83 } 84 85 return persistedStates; 86 }, 87 getPersistedConsentTypeStateForGtag: function () { 88 let consentTypeState = {}; 89 let persistedState = this.getPersistedConsentTypeState(); 90 91 for (const [type, data] of Object.entries(persistedState)) { 92 consentTypeState[type] = data.status; 93 } 94 95 return consentTypeState; 96 }, 97 shouldResetConsentState: function () { 98 const persistedConsentTypes = this.getPersistedConsentTypeState(); 99 100 if (Object.keys(persistedConsentTypes).length === 0 101 || Object.keys(persistedConsentTypes).length !== Object.keys(this.defaultConsentTypesData).length 102 || !document.cookie.includes(this.gdprCookie.cookieGroupName) 103 ) { 104 return true; 105 } 106 107 for (const [type, data] of Object.entries(persistedConsentTypes)) { 108 if (data.group !== this.defaultConsentTypesData[type].group) { 109 return true; 110 } 111 } 112 113 return false;
114 }, 115 resetAllowedCookiesGroups: function () { 116 document.cookie = this.gdprCookie.cookieGroupName + '=0;max-age=-1;domain=' + location.hostname; 117 }, 118 resetConsentState: function () { 119 this.resetAllowedCookiesGroups(); 120 this.removeCurrentConsentState(); 121 gtag('consent', 'update', this.getFormattedDefaultConsentState()); 122 } 123 }; 124 125 window.amConsentManager.init();</script>
125 <link rel="stylesheet" type="text/css" media="all" href="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/css/styles.min.css" /> 126<link rel="stylesheet" type="text/css" media="all" href="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Amasty_Customform/css/form-builder.min.css" /> 127<link rel="stylesheet" type="text/css" media="all" href="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Amasty_Customform/css/form-render.min.css" /> 128<link rel="stylesheet" type="text/css" media="all" href="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/StripeIntegration_Payments/css/wallets.min.css" />
129<script type="text/javascript" src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Hyva_CheckoutStripe/js/load-stripe.js"></script>
vendor: 1 bytes, line 129
129
130<script type="text/javascript" defer="defer" src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Hyva_MagentoDataServices/js/storefront-event-collector.js"></script>
vendor: 1 bytes, line 130
130
131<script type="text/javascript" defer="defer" src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Hyva_MagentoDataServices/js/storefront-events-sdk.js"></script>
vendor: 1 bytes, line 131
131
132<script type="text/javascript" defer="defer" src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Hyva_MagentoProductRecommendations/js/recs-sdk.js"></script>
132 133<link rel="canonical" href="https://www.huskee.co/" /> 134<link rel="icon" type="image/x-icon" href="https://www.huskee.co/media/favicon/websites/24/huskee-icon.png" /> 135<link rel="shortcut icon" type="image/x-icon" href="https://www.huskee.co/media/favicon/websites/24/huskee-icon.png" /> 136 137 138 139 140<meta property="og:type" content="website" /> 141<meta property="og:title" content="Huskee | Reusable Coffee Cups" /> 142<meta property="og:description" content="Shop Huskee's reusable coffee cups made from recycled & repurposed materials â HuskeeOriginal, HuskeeRenew & HuskeeSteel. Built to replace single-use." /> 143<meta property="og:url" content="https://www.huskee.co/" /> 144<meta property="og:locale" content="en_AU" />
145<script type="text/javascript" data-routing="commerce=rum" defer="defer" src="https://rum.hlx.page/.rum/@adobe/helix-rum-js@^2/dist/rum-standalone.js"></script>
145 146<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&family=Raleway:wght@400;700&display=swap" rel="stylesheet">
147<script type="text/javascript">window.dataLayer = window.dataLayer || []; 148window.getAmGa4Cookie = function () { 149 let cookies = {}; 150 document.cookie.split(';').forEach(function (el) { 151 let [key, value] = el.split('='); 152 cookies[key.trim()] = value; 153 }); 154 155 return cookies['amga4_customer'] 156 ? decodeURIComponent(cookies['amga4_customer']) 157 : '{}'; 158}; 159 160(function () { 161 /** 162 * @var {[Object]} 163 */ 164 const userProperties = JSON.parse(window.getAmGa4Cookie()); 165 166 if (userProperties.customerGroup) { 167 window.dataLayer.push({'customerGroup': userProperties.customerGroup}); 168 } 169 if (userProperties.customerId) { 170 window.dataLayer.push({'customerId': userProperties.customerId}); 171 } 172 /** 173 * @var {[Object]} 174 */ 175 const dataToPush = JSON.parse('\u005B\u005D'); 176 177 dataToPush.forEach((dataObject) => { 178 window.dataLayer.push(dataObject); 179 }); 180})(); 181</script>
181 182<!-- Google Tag Manager -->
183<script> 184 function callAmGA4GTM () { 185 (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 186 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 187 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 188 'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]'); 189 n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f); 190 })(window,document,'script','dataLayer', 'GTM-W87W4T7'); 191 } 192</script>
192 193<!-- End Google Tag Manager --> 194
194<script> 195 (function () { 196 callAmGA4GTM(); 197 })(); 198 </script>
198 199 200<!--Custom Style--> 201 <!-- Google Tag Manager -->
202<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=true;j.src="https://stape-au.huskee.co/amfqcninfm.js?"+i;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','b1ug=BAJTLjouXDFVUTldOlRXEFBUQFhIBx9bBRMdAgsGXwUC');</script>
202 203<!-- End Google Tag Manager --> 204 205 206 <style nonce="ZHlqb3BjaWc5NGhlZ2RpNXVmcmt4YzJhM3hiZTg4N2k=">.cart-container .btn btn-primary w-full{display:none!}</style>
207<script> 208 var BASE_URL = 'https://www.huskee.co/'; 209 var THEME_PATH = 'https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU'; 210 var COOKIE_CONFIG = { 211 "expires": null, 212 "path": "\u002F", 213 "domain": ".huskee.co", 214 "secure": true, 215 "lifetime": "3600", 216 "cookie_restriction_enabled": false }; 217 var CURRENT_STORE_CODE = 'huskee_au_store_view'; 218 var CURRENT_WEBSITE_ID = '24'; 219 220 window.hyva = window.hyva || {} 221 222 window.cookie_consent_groups = window.cookie_consent_groups || {} 223 window.cookie_consent_groups['necessary'] = true; 224 225 window.cookie_consent_config = window.cookie_consent_config || {}; 226 window.cookie_consent_config['necessary'] = [].concat( 227 window.cookie_consent_config['necessary'] || [], 228 [ 229 'user_allowed_save_cookie', 230 'form_key', 231 'mage-messages', 232 'private_content_version', 233 'mage-cache-sessid', 234 'last_visited_store', 235 'section_data_ids' 236 ] 237 ); 238 //# sourceURL=hyva-variables.js 239</script>
vendor: 1 bytes, line 239
239
240<script> 241 (function( hyva, undefined ) { 242 243 function lifetimeToExpires(options, defaults) { 244 245 const lifetime = options.lifetime || defaults.lifetime; 246 247 if (lifetime) { 248 const date = new Date; 249 date.setTime(date.getTime() + lifetime * 1000); 250 return date; 251 } 252 253 return null; 254 } 255 256 function generateRandomString() { 257 258 const allowedCharacters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 259 length = 16; 260 261 let formKey = '', 262 charactersLength = allowedCharacters.length; 263 264 for (let i = 0; i < length; i++) { 265 formKey += allowedCharacters[Math.round(Math.random() * (charactersLength - 1))] 266 } 267 268 return formKey; 269 } 270 271 const sessionCookieMarker = {noLifetime: true} 272 273 const cookieTempStorage = {}; 274 275 const internalCookie = { 276 get(name) { 277 const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); 278 return v ? v[2] : null; 279 }, 280 set(name, value, days, skipSetDomain) { 281 let expires, 282 path, 283 domain, 284 secure, 285 samesite; 286 287 const defaultCookieConfig = { 288 expires: null, 289 path: '/', 290 domain: null, 291 secure: false, 292 lifetime: null, 293 samesite: 'lax' 294 }; 295 296 const cookieConfig = window.COOKIE_CONFIG || {}; 297 298 expires = days && days !== sessionCookieMarker 299 ? lifetimeToExpires({lifetime: 24 * 60 * 60 * days, expires: null}, defaultCookieConfig) 300 : lifetimeToExpires(window.COOKIE_CONFIG, defaultCookieConfig) || defaultCookieConfig.expires; 301 302 path = cookieConfig.path || defaultCookieConfig.path; 303 domain = !skipSetDomain && (cookieConfig.domain || defaultCookieConfig.domain); 304 secure = cookieConfig.secure || defaultCookieConfig.secure; 305 samesite = cookieConfig.samesite || defaultCookieConfig.samesite; 306 307 document.cookie = name + "=" + encodeURIComponent(value) + 308 (expires && days !== sessionCookieMarker ? '; expires=' + expires.toGMTString() : '') + 309 (path ? '; path=' + path : '') + 310 (domain ? '; domain=' + domain : '') + 311 (secure ? '; secure' : '') + 312 (samesite ? '; samesite=' + samesite : 'lax'); 313 }, 314 isWebsiteAllowedToSaveCookie() { 315 const allowedCookies = this.get('user_allowed_save_cookie'); 316 if (allowedCookies) { 317 const allowedWebsites = JSON.parse(unescape(allowedCookies)); 318 319 return allowedWebsites[CURRENT_WEBSITE_ID] === 1; 320 } 321 return false; 322 }, 323 getGroupByCookieName(name) { 324 const cookieConsentConfig = window.cookie_consent_config || {}; 325 let group = null; 326 for (let prop in cookieConsentConfig) { 327 if (!cookieConsentConfig.hasOwnProperty(prop)) continue; 328 if (cookieConsentConfig[prop].includes(name)) { 329 group = prop; 330 break; 331 } 332 } 333 return group; 334 }, 335 isCookieAllowed(name) { 336 const cookieGroup = this.getGroupByCookieName(name); 337 return cookieGroup 338 ? window.cookie_consent_groups[cookieGroup] 339 : this.isWebsiteAllowedToSaveCookie(); 340 }, 341 saveTempStorageCookies() { 342 for (const [name, data] of Object.entries(cookieTempStorage)) { 343 if (this.isCookieAllowed(name)) { 344 this.set(name, data['value'], data['days'], data['skipSetDomain']); 345 delete cookieTempStorage[name]; 346 } 347 } 348 } 349 }; 350 351 hyva.getCookie = (name) => { 352 const cookieConfig = window.COOKIE_CONFIG || {}; 353 354 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 355 return cookieTempStorage[name] ? cookieTempStorage[name]['value'] : null; 356 } 357 358 return internalCookie.get(name); 359 } 360 361 hyva.setCookie = (name, value, days, skipSetDomain) => { 362 const cookieConfig = window.COOKIE_CONFIG || {}; 363 364 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 365 cookieTempStorage[name] = {value, days, skipSetDomain}; 366 return; 367 } 368 return internalCookie.set(name, value, days, skipSetDomain); 369 } 370 371 372 hyva.setSessionCookie = (name, value, skipSetDomain) => { 373 return hyva.setCookie(name, value, sessionCookieMarker, skipSetDomain) 374 } 375 376 hyva.getBrowserStorage = () => { 377 const browserStorage = window.localStorage || window.sessionStorage; 378 if (!browserStorage) { 379 console.warn('Browser Storage is unavailable'); 380 return false;
381 } 382 try { 383 browserStorage.setItem('storage_test', '1'); 384 browserStorage.removeItem('storage_test'); 385 } catch (error) { 386 console.warn('Browser Storage is not accessible', error); 387 return false; 388 } 389 return browserStorage; 390 } 391 392 hyva.postForm = (postParams) => { 393 const form = document.createElement("form"); 394 395 let data = postParams.data; 396 397 if (! postParams.skipUenc && ! data.uenc) { 398 data.uenc = btoa(window.location.href); 399 } 400 form.method = "POST"; 401 form.action = postParams.action; 402 403 Object.keys(postParams.data).map(key => { 404 const field = document.createElement("input"); 405 field.type = 'hidden' 406 field.value = postParams.data[key]; 407 field.name = key; 408 form.appendChild(field); 409 }); 410 411 const form_key = document.createElement("input"); 412 form_key.type = 'hidden'; 413 form_key.value = hyva.getFormKey(); 414 form_key.name="form_key"; 415 form.appendChild(form_key); 416 417 document.body.appendChild(form); 418 419 form.submit(); 420 } 421 422 hyva.getFormKey = function () { 423 let formKey = hyva.getCookie('form_key'); 424 425 if (!formKey) { 426 formKey = generateRandomString(); 427 hyva.setCookie('form_key', formKey); 428 } 429 430 return formKey; 431 } 432 433 hyva.formatPrice = (value, showSign, options = {}) => { 434 const groupSeparator = options.groupSeparator; 435 const decimalSeparator = options.decimalSeparator 436 delete options.groupSeparator; 437 delete options.decimalSeparator; 438 const formatter = new Intl.NumberFormat( 439 'en\u002DAU', 440 Object.assign({ 441 style: 'currency', 442 currency: 'AUD', 443 signDisplay: showSign ? 'always' : 'auto' 444 }, options) 445 ); 446 return (typeof Intl.NumberFormat.prototype.formatToParts === 'function') ? 447 formatter.formatToParts(value).map(({type, value}) => { 448 switch (type) { 449 case 'currency': 450 return '\u0024' || value; 451 case 'minusSign': 452 return '- '; 453 case 'plusSign': 454 return '+ '; 455 case 'group': 456 return groupSeparator !== undefined ? groupSeparator : value; 457 case 'decimal': 458 return decimalSeparator !== undefined ? decimalSeparator : value; 459 default : 460 return value; 461 } 462 }).reduce((string, part) => string + part) : 463 formatter.format(value); 464 } 465 466 const formatStr = function (str, nStart) { 467 const args = Array.from(arguments).slice(2); 468 469 return str.replace(/(%+)([0-9]+)/g, (m, p, n) => { 470 const idx = parseInt(n) - nStart; 471 472 if (args[idx] === null || args[idx] === void 0) { 473 return m; 474 } 475 return p.length % 2 476 ? p.slice(0, -1).replace('%%', '%') + args[idx] 477 : p.replace('%%', '%') + n; 478 }) 479 } 480 481 hyva.str = function (string) { 482 const args = Array.from(arguments); 483 args.splice(1, 0, 1); 484 485 return formatStr.apply(undefined, args); 486 } 487 488 hyva.strf = function () { 489 const args = Array.from(arguments); 490 args.splice(1, 0, 0); 491 492 return formatStr.apply(undefined, args); 493 } 494 495 /** 496 * Take a html string as `content` parameter and 497 * extract an element from the DOM to replace in 498 * the current page under the same selector, 499 * defined by `targetSelector` 500 */ 501 hyva.replaceDomElement = (targetSelector, content) => { 502 // Parse the content and extract the DOM node using the `targetSelector` 503 const parser = new DOMParser(); 504 const doc = parser.parseFromString(content, 'text/html');
505 const contentNode = doc.querySelector(targetSelector); 506 507 // Bail if content or target can't be found 508 if (!contentNode || !document.querySelector(targetSelector)) { 509 return; 510 } 511 512 hyva.activateScripts(contentNode); 513 514 // Replace the old DOM node with the new content 515 document.querySelector(targetSelector).replaceWith(contentNode); 516 517 // Reload customerSectionData and display cookie-messages if present 518 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 519 hyva.initMessages(); 520 } 521 522 hyva.removeScripts = (contentNode) => { 523 const scripts = contentNode.getElementsByTagName('script'); 524 for (let i = 0; i < scripts.length; i++) { 525 scripts[i].parentNode.removeChild(scripts[i]); 526 } 527 const templates = contentNode.getElementsByTagName('template'); 528 for (let i = 0; i < templates.length; i++) { 529 const container = document.createElement('div'); 530 container.innerHTML = templates[i].innerHTML; 531 hyva.removeScripts(container); 532 templates[i].innerHTML = container.innerHTML; 533 } 534 } 535 536 hyva.activateScripts = (contentNode) => { 537 // Create new array from HTMLCollection to avoid mutation of collection while manipulating the DOM. 538 const scripts = Array.from(contentNode.getElementsByTagName('script')); 539 540 // Iterate over all script tags to duplicate+inject each into the head 541 for (const original of scripts) { 542 const script = document.createElement('script'); 543 original.type && (script.type = original.type); 544 script.innerHTML = original.innerHTML; 545 546 // Remove the original (non-executing) script from the contentNode 547 original.parentNode.removeChild(original) 548 549 // Add script to head 550 document.head.appendChild(script); 551 } 552 553 return contentNode; 554 } 555 556 const replace = {['+']: '-', ['/']: '_', ['=']: ','}; 557 hyva.getUenc = () => btoa(window.location.href).replace(/[+/=]/g, match => replace[match]); 558 559 let currentTrap; 560 561 const focusableElements = (rootElement) => { 562 const selector = 'button, [href], input, select, textarea, details, [tabindex]:not([tabindex="-1"]'; 563 return Array.from(rootElement.querySelectorAll(selector)) 564 .filter(el => { 565 return el.style.display !== 'none' 566 && !el.disabled 567 && el.tabIndex !== -1 568 && (el.offsetWidth || el.offsetHeight || el.getClientRects().length) 569 }) 570 } 571 572 const focusTrap = (e) => { 573 const isTabPressed = e.key === 'Tab' || e.keyCode === 9; 574 if (!isTabPressed) return; 575 576 const focusable = focusableElements(currentTrap) 577 const firstFocusableElement = focusable[0] 578 const lastFocusableElement = focusable[focusable.length - 1] 579 580 e.shiftKey 581 ? document.activeElement === firstFocusableElement && (lastFocusableElement.focus(), e.preventDefault()) 582 : document.activeElement === lastFocusableElement && (firstFocusableElement.focus(), e.preventDefault()) 583 }; 584 585 hyva.releaseFocus = (rootElement) => { 586 if (currentTrap && (!rootElement || rootElement === currentTrap)) { 587 currentTrap.removeEventListener('keydown', focusTrap) 588 currentTrap = null 589 } 590 } 591 hyva.trapFocus = (rootElement) => { 592 if (!rootElement) return; 593 hyva.releaseFocus() 594 currentTrap = rootElement 595 rootElement.addEventListener('keydown', focusTrap) 596 const firstElement = focusableElements(rootElement)[0] 597 firstElement && firstElement.focus() 598 } 599 600 hyva.safeParseNumber = (rawValue) => { 601 const number = rawValue ? parseFloat(rawValue) : null; 602 603 return Array.isArray(number) || isNaN(number) ? rawValue : number; 604 } 605 606 const toCamelCase = s => s.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); 607 hyva.createBooleanObject = (name, value = false, additionalMethods = {}) => { 608 609 const camelCase = toCamelCase(name);
610 const key = '__hyva_bool_' + name 611 612 return new Proxy(Object.assign( 613 additionalMethods, 614 { 615 [key]: !!value, 616 [name]() {return !!this[key]}, 617 ['!' + name]() {return !this[key]}, // @deprecated This does not work with non-CSP Alpine 618 ['not' + camelCase]() {return !this[key]}, 619 ['toggle' + camelCase]() {this[key] = !this[key]}, 620 [`set${camelCase}True`]() {this[key] = true}, 621 [`set${camelCase}False`]() {this[key] = false}, 622 } 623 ), { 624 set(target, prop, value) { 625 return prop === name 626 ? (target[key] = !!value) 627 : Reflect.set(...arguments); 628 } 629 }) 630 } 631 632 hyva.alpineInitialized = (fn) => window.addEventListener('alpine:initialized', fn, {once: true}) 633 window.addEventListener('alpine:init', () => Alpine.data('{}', () => ({})), {once: true}); 634 window.addEventListener('user-allowed-save-cookie', () => internalCookie.saveTempStorageCookies()) 635 636 }( window.hyva = window.hyva || {} )); 637 //# sourceURL=hyva.js 638</script>
vendor: 1 bytes, line 638
638
639<script type="speculationrules"> 640{ 641 "tag": "Hyva Theme", 642 "prefetch": [{ 643 "source": "document", 644 "where": { 645 "and": [ 646 { "href_matches": "/*" }, 647 { "not": { 648 "href_matches": ["/customer/*","/search/*","/sales/*","/wishlist/*","/checkout/*","/paypal/*","/newsletter/*","/stores/store/redirect/*"] }}, 649 { "not": { 650 "selector_matches": [".no-preload", ".do-not-prerender", "[download]", "[rel~=nofollow]", "[target]"] 651 }} 652 ] 653 }, 654 "eagerness": "moderate" 655 }] 656} 657</script>
657 658
659<script> 660 'use strict'; 661 662 (function () { 663 window.initAmAppendLabels = { 664 selectors: { 665 labelWrapper: '.amlabel-position-wrapper' 666 }, 667 /** 668 * We use approach from hyva.replaceDomElement method to replace labels and init components 669 * 670 * @param {HTMLElement} labelContainer 671 * @param {string} labelHtml 672 * @return {void} 673 */ 674 appendLabels: function (labelContainer, labelHtml) { 675 const parser = new DOMParser(); 676 const doc = parser.parseFromString(labelHtml, 'text/html'); 677 const nodes = doc.querySelectorAll(this.selectors.labelWrapper); 678 679 if (!nodes) { 680 return; 681 } 682 683 labelContainer.append(...nodes); 684 } 685 }; 686 })(); 687</script>
687 688
689<script> 690 window.addEventListener('load', function () { 691 window.magentoStorefrontEvents = magentoStorefrontEvents; 692 magentoStorefrontEvents.context.setStorefrontInstance( 693 {"storeUrl":"https:\/\/www.huskee.co\/","websiteId":24,"websiteCode":"huskee_au_website","storeId":27,"storeCode":"huskee_au_store","storeViewId":27,"storeViewCode":"huskee_au_store_view","websiteName":"Huskee AU","storeName":"Huskee AU Store","storeViewName":"Huskee AU Store View","baseCurrencyCode":"AUD","storeViewCurrencyCode":"AUD","catalogExtensionVersion":"103.4.32","environmentId":"93f6e2b7-1600-41f3-b215-94f262bda9ad","environment":"Production","storefrontTemplate":"Hyva"} ); 694 magentoStorefrontEvents.context.setMagentoExtension({ 695 magentoExtensionVersion: "8.0.1", 696 }); 697 magentoStorefrontEvents.context.setDataServicesExtension({ 698 version: "8.0.1", 699 }); 700 magentoStorefrontEvents.context.setPage({ 701 pageType: "Default" 702 }); 703 magentoStorefrontEvents.context.setContext("pageExtended", { 704 action: "page-view" 705 }); 706 707 let shopperId = "guest"; 708 magentoStorefrontEvents.context.setShopper({ shopperId: shopperId }); 709 710 711 magentoStorefrontEvents.publish.pageView(); 712 }); 713 714 function getCustomerIdFromDataServices() { 715 let customerId = hyva.getCookie("dataservices_customer_id"); 716 return customerId ? decodeURI(customerId).replace(/[^\w\s]/gi, "") : null; 717 } 718 719 function getCustomerGroupFromDataServices() { 720 try { 721 const decodedString = decodeURIComponent(hyva.getCookie("dataservices_customer_group")); 722 return decodedString ? JSON.parse(decodedString) : null; 723 } catch (error) { 724 console.error("Failed to parse customer group data:", error); 725 return null; 726 } 727 } 728</script>
728 729 730<style> 731 #confirmBox, 732 .am-related-title > .am-title { 733 background-color: #FFFFFF; 734 } 735 736 #confirmBox a, 737 #confirmBox a:hover, 738 #confirmBox a:active, 739 #confirmBox .amrelated-confirm-header { 740 color: #77b54e; 741 } 742 743 .amcart-confirm-buttons .button { 744 color: #FFFFFF; 745 } 746 747 .amcart-confirm-buttons .am-btn-left { 748 background-color: #BBBBBB; 749 } 750 751 .amcart-confirm-buttons .am-btn-right { 752 background-color: #0f8345; 753 } 754 755 .amcart-confirm-buttons, 756 .amcart-confirm-buttons .checkout, 757 .amcart-confirm-buttons .checkout:hover, 758 .amcart-confirm-buttons .checkout:active, 759 .amcart-message-box, 760 .am-related-box, 761 #am-a-count, 762 #am-a-count:visited, 763 .am-related-title > .am-title { 764 color: #000000; 765 } 766 </style> 767<!-- Google tag (gtag.js) -->
vendor: 65 bytes, lines 767-768
767 768<script async src="https://www.googletagmanager.com/gtag/js?id=
768AW-786048147
vendor: 13 bytes, line 768
768"></script>
769<script> 770
vendor: 138 bytes, lines 770-774
770window.dataLayer = window.dataLayer || []; 771 function gtag(){dataLayer.push(arguments);} 772 gtag('js', new Date()); 773 774 gtag('config', '
774AW-786048147
vendor: 5 bytes, line 774
774');
775</script>
775 776<style> 777#category-view-container{display:none} 778</style> 779</head> 780<body id="html-body" class="cms-home page-layout-1column cms-index-index page-layout-cms-full-width"> 781<div id="yelhex-geo-switch" 782 class="hidden w-full text-center py-11 px-5 box-border md:py-11 sm:py-6 geo-switch" 783 x-data="geoSwitcher" 784 x-init="init"> 785 <div class="font-bold"> 786 It looks you are in <span x-text="currentCountryName"></span>. Would you like to shop on the <span x-text="switchCountryName"></span> site? 787 </div> 788 <div class="w-auto mx-auto mt-5 text-center md:mt-5 sm:mt-4"> 789 <a href="#" 790 class="action primary confirm mr-8 md:mr-8 sm:mr-5 btn btn-primary inline" 791 x-bind:href="redirectUrl" 792 x-text="confirmText">Visit Huskee Australia</a> 793 <a href="#" 794 class="action dismiss" 795 @click.prevent="dismiss">Dismiss</a> 796 </div> 797</div> 798
799<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 800 function geoSwitcher() { 801 return { 802 headerName: 'visitor\u002Dgeo\u002Dcountry', 803 cookieName: 'geo-switcher-dismissed', 804 cookieExpiration: 86400, 805 currentWebsiteId: 24, 806 currentStoreCountry: 'AU', 807 countryConfiguration: {"AF":{"name":"Afghanistan"},"AL":{"name":"Albania"},"DZ":{"name":"Algeria"},"AS":{"name":"American Samoa"},"AD":{"name":"Andorra"},"AO":{"name":"Angola"},"AI":{"name":"Anguilla"},"AQ":{"name":"Antarctica"},"AG":{"name":"Antigua & Barbuda"},"AR":{"name":"Argentina"},"AM":{"name":"Armenia"},"AW":{"name":"Aruba"},"AU":{"name":"Australia","website":24},"AT":{"name":"Austria"},"AZ":{"name":"Azerbaijan"},"BS":{"name":"Bahamas"},"BH":{"name":"Bahrain"},"BD":{"name":"Bangladesh"},"BB":{"name":"Barbados"},"BY":{"name":"Belarus"},"BE":{"name":"Belgium"},"BZ":{"name":"Belize"},"BJ":{"name":"Benin"},"BM":{"name":"Bermuda"},"BT":{"name":"Bhutan"},"BO":{"name":"Bolivia"},"BA":{"name":"Bosnia & Herzegovina"},"BW":{"name":"Botswana"},"BV":{"name":"Bouvet Island"},"BR":{"name":"Brazil"},"IO":{"name":"British Indian Ocean Territory"},"VG":{"name":"British Virgin Islands"},"BN":{"name":"Brunei"},"BG":{"name":"Bulgaria"},"BF":{"name":"Burkina Faso"},"BI":{"name":"Burundi"},"KH":{"name":"Cambodia"},"CM":{"name":"Cameroon"},"CA":{"name":"Canada"},"CV":{"name":"Cape Verde"},"BQ":{"name":"Caribbean Netherlands"},"KY":{"name":"Cayman Islands"},"CF":{"name":"Central African Republic"},"TD":{"name":"Chad"},"CL":{"name":"Chile"},"CN":{"name":"China"},"CX":{"name":"Christmas Island"},"CC":{"name":"Cocos (Keeling) Islands"},"CO":{"name":"Colombia"},"KM":{"name":"Comoros"},"CG":{"name":"Congo - Brazzaville"},"CD":{"name":"Congo - Kinshasa"},"CK":{"name":"Cook Islands"},"CR":{"name":"Costa Rica"},"HR":{"name":"Croatia"},"CU":{"name":"Cuba"},"CW":{"name":"Cura\u00e7ao"},"CY":{"name":"Cyprus"},"CZ":{"name":"Czechia"},"CI":{"name":"C\u00f4te d\u2019Ivoire"},"DK":{"name":"Denmark"},"DJ":{"name":"Djibouti"},"DM":{"name":"Dominica"},"DO":{"name":"Dominican Republic"},"EC":{"name":"Ecuador"},"EG":{"name":"Egypt"},"SV":{"name":"El Salvador"},"GQ":{"name":"Equatorial Guinea"},"ER":{"name":"Eritrea"},"EE":{"name":"Estonia"},"SZ":{"name":"Eswatini"},"ET":{"name":"Ethiopia"},"FK":{"name":"Falkland Islands"},"FO":{"name":"Faroe Islands"},"FJ":{"name":"Fiji"},"FI":{"name":"Finland"},"FR":{"name":"France"},"GF":{"name":"French Guiana"},"PF":{"name":"French Polynesia"},"TF":{"name":"French Southern Territories"},"GA":{"name":"Gabon"},"GM":{"name":"Gambia"},"GE":{"name":"Georgia"},"DE":{"name":"Germany"},"GH":{"name":"Ghana"},"GI":{"name":"Gibraltar"},"GR":{"name":"Greece"},"GL":{"name":"Greenland"},"GD":{"name":"Grenada"},"GP":{"name":"Guadeloupe"},"GU":{"name":"Guam"},"GT":{"name":"Guatemala"},"GG":{"name":"Guernsey"},"GN":{"name":"Guinea"},"GW":{"name":"Guinea-Bissau"},"GY":{"name":"Guyana"},"HT":{"name":"Haiti"},"HM":{"name":"Heard & McDonald Islands"},"HN":{"name":"Honduras"},"HK":{"name":"Hong Kong SAR China"},"HU":{"name":"Hungary"},"IS":{"name":"Iceland"},"IN":{"name":"India"},"ID":{"name":"Indonesia"},"IR":{"name":"Iran"},"IQ":{"name":"Iraq"},"IE":{"name":"Ireland"},"IM":{"name":"Isle of Man"},"IL":{"name":"Israel"},"IT":{"name":"Italy"},"JM":{"name":"Jamaica"},"JP":{"name":"Japan"},"JE":{"name":"Jersey"},"JO":{"name":"Jordan"},"KZ":{"name":"Kazakhstan"},"KE":{"name":"Kenya"},"KI":{"name":"Kiribati"},"XK":{"name":"Kosovo"},"KW":{"name":"Kuwait"},"KG":{"name":"Kyrgyzstan"},"LA":{"name":"Laos"},"LV":{"name":"Latvia"},"LB":{"name":"Lebanon"},"LS":{"name":"Lesotho"},"LR":{"name":"Liberia"},"LY":{"name":"Libya"},"LI":{"name":"Liechtenstein"},"LT":{"name":"Lithuania"},"LU":{"name":"Luxembourg"},"MO":{"name":"Macao SAR China"},"MG":{"name":"Madagascar"},"MW":{"name":"Malawi"},"MY":{"name":"Malaysia"},"MV":{"name":"Maldives"},"ML":{"name":"Mali"},"MT":{"name":"Malta"},"MH":{"name":"Marshall Islands"},"MQ":{"name":"Martinique"},"MR":{"name":"Mauritania"},"MU":{"name":"Mauritius"},"YT":{"name":"Mayotte"},"MX":{"name":"Mexico"},"FM":{"name":"Micronesia"},"MD":{"name":"Moldova"},"MC":{"name":"Monaco"},"MN":{"name":"Mongolia"},"ME":{"name":"Montenegro"},"MS":{"name":"Montserrat"},"MA":{"name":"Morocco"},"MZ":{"name":"Mozambique"},"MM":{"name":"Myanmar (Burma)"},"NA":{"name":"Namibia"},"NR":{"name":"Nauru"},"NP":{"name":"Nepal"},"NL":{"name":"Netherlands"},"NC":{"name":"New Caledonia"},"NZ":{"name":"New Zealand"},"NI":{"name":"Nicaragua"},"NE":{"name":"Niger"},"NG":{"name":"Nigeria"},"NU":{"name":"Niue"},"NF":{"name":"Norfolk Island"},"KP":{"name":"North Korea"},"MK":{"name":"North Macedonia"},"MP":{"name":"Northern Mariana Islands"},"NO":{"name":"Norway"},"OM":{"name":"Oman"},"PK":{"name":"Pakistan"},"PW":{"name":"Palau"},"PS":{"name":"Palestinian Territories"},"PA":{"name":"Panama"},"PG":{"name":"Papua New Guinea"},"PY":{"name":"Paraguay"},"PE":{"name":"Peru"},"PH":{"name":"Philippines"},"PN":{"name":"Pitcairn Islands"},"PL":{"name":"Poland"},"PT":{"name":"Portugal"},"QA":{"name":"Qatar"},"RO":{"name":"Romania"},"RU":{"name":"Russia"},"RW":{"name":"Rwanda"},"RE":{"name":"R\u00e9union"},"WS":{"name":"Samoa"},"SM":{"name":"San Marino"},"SA":{"name":"Saudi Arabia"},"SN":{"name":"Senegal"},"RS":{"name":"Serbia"},"SC":{"name":"Seychelles"},"SL":{"name":"Sierra Leone"},"SG":{"name":"Singapore"},"SX":{"name":"Sint Maarten"},"SK":{"name":"Slovakia"},"SI":{"name":"Slovenia"},"SB":{"name":"Solomon Islands"},"SO":{"name":"Somalia"},"ZA":{"name":"South Africa"},"GS":{"name":"South Georgia & South Sandwich Islands"},"KR":{"name":"South Korea"},"ES":{"name":"Spain"},"LK":{"name":"Sri Lanka"},"BL":{"name":"St. Barth\u00e9lemy"},"SH":{"name":"St. Helena"},"KN":{"name":"St. Kitts & Nevis"},"LC":{"name":"St. Lucia"},"MF":{"name":"St. Martin"},"PM":{"name":"St. Pierre & Miquelon"},"VC":{"name":"St. Vincent & Grenadines"},"SD":{"name":"Sudan"},"SR":{"name":"Suriname"},"SJ":{"name":"Svalbard & Jan Mayen"},"SE":{"name":"Sweden"},"CH":{"name":"Switzerland"},"SY":{"name":"Syria"},"ST":{"name":"S\u00e3o Tom\u00e9 & Pr\u00edncipe"},"TW":{"name":"Taiwan"},"TJ":{"name":"Tajikistan"},"TZ":{"name":"Tanzania"},"TH":{"name":"Thailand"},"TL":{"name":"Timor-Leste"},"TG":{"name":"Togo"},"TK":{"name":"Tokelau"},"TO":{"name":"Tonga"},"TT":{"name":"Trinidad & Tobago"},"TN":{"name":"Tunisia"},"TM":{"name":"Turkmenistan"},"TC":{"name":"Turks & Caicos Islands"},"TV":{"name":"Tuvalu"},"TR":{"name":"T\u00fcrkiye"},"UM":{"name":"U.S. Outlying Islands"},"VI":{"name":"U.S. Virgin Islands"},"UG":{"name":"Uganda"}
807,"UA":{"name":"Ukraine"},"AE":{"name":"United Arab Emirates"},"GB":{"name":"United Kingdom","website":27},"US":{"name":"United States","website":30},"UY":{"name":"Uruguay"},"UZ":{"name":"Uzbekistan"},"VU":{"name":"Vanuatu"},"VA":{"name":"Vatican City"},"VE":{"name":"Venezuela"},"VN":{"name":"Vietnam"},"WF":{"name":"Wallis & Futuna"},"EH":{"name":"Western Sahara"},"YE":{"name":"Yemen"},"ZM":{"name":"Zambia"},"ZW":{"name":"Zimbabwe"},"AX":{"name":"\u00c5land Islands"}}, 808 websitesConfiguration: {"24":{"associated_country":"AU","allowed_countries":["AU"],"url":"https:\/\/www.huskee.co\/","name":"Huskee AU"},"27":{"associated_country":"GB","allowed_countries":["GB"],"url":"https:\/\/www.huskee.co\/uk\/","name":"Huskee UK"},"30":{"associated_country":"US","allowed_countries":["US"],"url":"https:\/\/www.huskee.co\/us\/","name":"Huskee US"}}, 809 currentCountryName: '', 810 switchCountryName: '', 811 redirectUrl: '#', 812 confirmText: 'Visit Store', 813 814 init() { 815 this.checkVisitorMatch(); 816 }, 817 818 getCookie(name) { 819 const value = `; ${document.cookie}`; 820 const parts = value.split(`; ${name}=`); 821 if (parts.length === 2) return parts.pop().split(';').shift(); 822 return null; 823 }, 824 825 setCookie(name, value, expirationSeconds) { 826 const date = new Date(); 827 date.setTime(date.getTime() + expirationSeconds * 1000); 828 document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/; SameSite=Lax`; 829 }, 830 831 checkVisitorMatch() { 832 if (this.getCookie(this.cookieName)) { 833 return; 834 } 835 836 let visitorCountry = this.getCookie(this.headerName); 837 838 839 // Country detection relies on server-side setting the cookie 840 // via CloudFlare, Fastly, or custom middleware 841 842 if (visitorCountry && visitorCountry in this.countryConfiguration) { 843 const countryConf = this.countryConfiguration[visitorCountry]; 844 if ('website' in countryConf) { 845 846 if (countryConf['website'] !== this.currentWebsiteId) { 847 848 const website = this.websitesConfiguration[countryConf['website']]; 849 const curUrl = document.URL; 850 const curWebsite = this.websitesConfiguration[this.currentWebsiteId]; 851 this.redirectUrl = curUrl.includes(curWebsite.url) ? 852 curUrl.replace(curWebsite.url, website.url) : website.url; 853 854 this.currentCountryName = this.countryConfiguration[visitorCountry].name; 855 this.switchCountryName = this.countryConfiguration[website.associated_country].name; 856 this.confirmText = `Visit ${website.name}`; 857 858 document.getElementById('yelhex-geo-switch').classList.remove('hidden'); 859 document.getElementById('yelhex-geo-switch').style.display = 'block'; 860 } 861 } 862 } 863 }, 864 865 dismiss() { 866 document.getElementById('yelhex-geo-switch').remove(); 867 window.scrollBy(0, 0); 868 this.setCookie(this.cookieName, '1', this.cookieExpiration); 869 } 870 } 871 } 872 873 function registerGeoSwitcherComponent() { 874 Alpine.data('geoSwitcher', geoSwitcher); 875 } 876 877 if (window.Alpine) { 878 registerGeoSwitcherComponent(); 879 } else { 880 window.addEventListener('alpine:init', registerGeoSwitcherComponent, { once: true }); 881 } 882</script>
882 883 884<!-- Google Tag Manager (noscript) -->
vendor: 73 bytes, lines 884-886
884 885<noscript> 886 <iframe src="https://www.googletagmanager.com/ns.html?id=
886GTM-W87W4T7
vendor: 96 bytes, lines 886-888
886" 887 height="0" width="0" style="display:none;visibility:hidden"></iframe> 888</noscript>
889<!-- End Google Tag Manager (noscript) --> 890 891<input name="form_key" type="hidden" value="IkWwwwNCC8u31LvS" /> 892 <noscript> 893 <section class="message global noscript border-b-2 border-blue-500 bg-blue-50 shadow-none m-0 px-0 rounded-none font-normal"> 894 <div class="container text-center"> 895 <p> 896 <strong>JavaScript seems to be disabled in your browser.</strong> 897 <span> 898 For the best experience on our site, be sure to turn on Javascript in your browser. </span> 899 </p> 900 </div> 901 </section> 902 </noscript> 903 904
905<script> 906 document.body.addEventListener('touchstart', () => {}, {passive: true}) 907</script>
907 908<div class="widget block block-static-block"> 909 <style>#html-body [data-pb-style=KS1PB5X]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#d9d1bf;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;text-align:center}</style><div class="announcement-bar" data-content-type="row" data-appearance="full-bleed" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="KS1PB5X"><div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><p>Free shipping on Australian orders over $90*</p> 910<style> 911.announcement-bar div { 912 color: #2B2A28; 913 background: #E8E1D3; 914} 915</style> 916</div></div></div>
917<script> 918 'use strict'; 919 920 (() => { 921 window.addEventListener( 922 'ga-initialized', 923 () => { 924 window.addEventListener('cart-item-added', (event) => { 925 let products = []; 926 927 event.detail.forEach((item) => { 928 products.push({ 929 id: item['product_sku'], 930 name: item['product_name'], 931 price: item['product_price_value'], 932 quantity: item['qty'], 933 }); 934 }); 935 936 dataLayer.push({ 937 event: 'addToCart', 938 ecommerce: { 939 currencyCode: 'AUD', 940 add: { 941 products: products, 942 }, 943 }, 944 }); 945 }); 946 }, 947 { once: true } 948 ); 949 })(); 950</script>
vendor: 1 bytes, line 950
950
951<script> 952 'use strict'; 953 954 (() => { 955 window.addEventListener( 956 'ga-initialized', 957 () => { 958 window.addEventListener('cart-item-removed', (event) => { 959 let products = []; 960 961 event.detail.forEach((item) => { 962 products.push({ 963 id: item['product_sku'], 964 name: item['product_name'], 965 price: item['product_price_value'], 966 quantity: item['qty'], 967 }); 968 }); 969 970 dataLayer.push({ 971 event: 'removeFromCart', 972 ecommerce: { 973 currencyCode: 'AUD', 974 remove: { 975 products: products, 976 }, 977 }, 978 }); 979 }); 980 }, 981 { once: true } 982 ); 983 })(); 984</script>
vendor: 1 bytes, line 984
984
985<script> 986 'use strict'; 987 988 (() => { 989 window.addEventListener( 990 'ga-initialized', 991 () => { 992 const banners = document.querySelectorAll('\u005Bdata\u002Dbanner\u002Did\u005D'); 993 let promotions = []; 994 995 banners.forEach((banner) => { 996 banner.addEventListener('click', () => { 997 window.dataLayer.push({ 998 event: 'promotionClick', 999 ecommerce: { 1000 promoClick: { 1001 promotions: [{ 1002 id: banner.dataset.bannerId, 1003 name: banner.dataset.bannerName, 1004 creative: '', 1005 position: 'cms_index_index', 1006 }], 1007 }, 1008 }, 1009 }); 1010 }); 1011 1012 promotions.push({ 1013 id: banner.dataset.bannerId, 1014 name: banner.dataset.bannerName, 1015 creative: '', 1016 position: 'cms_index_index', 1017 }); 1018 }); 1019 1020 if (promotions.length) { 1021 window.dataLayer.push({ 1022 event: 'promotionView', 1023 ecommerce: { 1024 promoView: { 1025 promotions: promotions, 1026 }, 1027 }, 1028 }); 1029 } 1030 }, 1031 { once: true } 1032 ); 1033 })(); 1034</script>
1034 1035
1036<script> 1037 'use strict'; 1038 1039 (function () { 1040 window.amGdprCookieBar = { 1041 isEnabled: 1, 1042 1043 /** 1044 * @public 1045 * @returns {int} 1046 */ 1047 getIsEnabled() { 1048 return this.isEnabled; 1049 } 1050 } 1051 })(); 1052</script>
1052 1053
1054<script data-cfasync="false"> 1055 window.amastyCookieManager = window.amastyCookieManager || {}; 1056</script>
1056 1057 1058<div x-data="initAmastyCookieBar" 1059 x-bind="eventListeners" 1060 x-cloak 1061 id="am-cookie-bar" 1062 > 1063 <dialog 1064 class="m-0 w-full p-4 md:p-2 bg-ambar-background max-w-none inset-y-auto bottom-0 z-50 inset-y-0" 1065 data-dialog-ref-name="am-cookie-bar" 1066 closeby="any" 1067 aria-label="Cookie Bar" 1068 x-bind="dialogConfig" 1069 x-htmldialog.modeless="hide" 1070 x-ref="am-cookie-bar" 1071 ><span tabindex="0" class="focus:outline-hidden"></span> 1072<div class="flex w-full flex-col gap-4 container md:flex-row md:items-center md:justify-between"> 1073 <div class="md:pr-8"> 1074 <p id="amcookie-bar-content" class="max-h-[30vh] text-sm"> 1075 We use cookies to help improve our services, make personal offers, and enhance your experience. If you do not accept optional cookies below, your experience may be affected. If you want to know more, please read the <a href="privacy-policy-cookie-restriction-mode" title="Cookie Policy">Cookie Policy</a> </p> 1076 </div> 1077 <div class="flex w-full flex-col gap-2 md:w-56 md:shrink-0"> 1078 1079 1080<button 1081 type="button" 1082 x-data="initAmCookieActionDispatcherBtn" 1083 data-dispatch-action="cookiebar-action-allow" 1084 @click.prevent="actiondispatchHandler" 1085 class="ambar-btn-accept ambar-action-btn w-full justify-center" 1086> 1087 Accept Cookies</button> 1088 1089<button 1090 type="button" 1091 x-data="initAmCookieActionDispatcherBtn" 1092 data-dispatch-action="cookiebar-action-settings" 1093 @click.prevent="actiondispatchHandler" 1094 class="ambar-btn-settings ambar-action-btn w-full justify-center" 1095> 1096 Custom Settings</button> 1097 </div> 1098</div> 1099 <button @click="closeCookieBarHandler" 1100 aria-label="Close Cookie Bar" 1101 class="absolute right-2 top-2 text-gray-300 transition 1102 hover:text-black focus:drop-shadow-md hover:drop-shadow-md" 1103 > 1104 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" 1105 stroke="currentColor" stroke-width="2"> 1106 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1107 </svg> 1108 </button> 1109</dialog> 1110</div> 1111
1112<script data-cfasync="false"> 1113 'use strict'; 1114 1115 function initAmastyCookieBar() { 1116 return { 1117 ...hyva.modal({ 1118 duration: 150, 1119 transitionEnter: 'transform duration-150', 1120 transitionLeave: 'transform duration-150' 1121 }), 1122 eventListeners: { 1123 ['@close-cookie-bar']() { 1124 this.opened['am-cookie-bar'] = false; 1125 }, 1126 ['@open-cookie-bar']() { 1127 this.show('am-cookie-bar'); 1128 this.$nextTick(() => hyva.trapFocus(this.modalElement)); 1129 }, 1130 ['@amasty-cookie-group-updated.window']() { 1131 const lastUpdate = Alpine.store('AmastyCookieGroups').lastUpdate; 1132 if (this.isShowNotificationBar(this.firstShowProcess, lastUpdate)) { 1133 !this.opened['am-cookie-bar'] && 1134 this.show('am-cookie-bar'); 1135 this.$nextTick(() => hyva.trapFocus(this.modalElement)); 1136 } else { 1137 this.opened['am-cookie-bar'] = false; 1138 } 1139 } 1140 }, 1141 1142 firstShowProcess: '0', 1143 1144 /** 1145 * @returns {void} 1146 */ 1147 init: function () { 1148 window.isGdprCookieEnabled = true; 1149 Alpine.store('AmastyCookieGroups').updateGroupData(); 1150 this.modalElement = this.$refs['am-cookie-bar']; 1151 this.modalElement.setAttribute('aria-describedby', 'amcookie-bar-content'); 1152 }, 1153 1154 /** 1155 * @param {string|number} firstShowProcess 1156 * @param {number} lastUpdate 1157 * @returns {boolean} 1158 */ 1159 isShowNotificationBar: function (firstShowProcess, lastUpdate) { 1160 return (this.isNeedFirstShow(firstShowProcess, lastUpdate) && !hyva.getCookie('amcookie_allowed') 1161 || this.isNeedShowOnUpdate(lastUpdate)); 1162 }, 1163 1164 /** 1165 * @param {string|number} firstShowProcess 1166 * @param {number} lastUpdate 1167 * @returns {boolean} 1168 */ 1169 isNeedFirstShow: function (firstShowProcess, lastUpdate) { 1170 hyva.getBrowserStorage().setItem('amCookieBarFirstShowTime', lastUpdate); 1171 1172 if (firstShowProcess === '0') { 1173 return true; 1174 } 1175 1176 if (hyva.getBrowserStorage().getItem('amCookieBarFirstShow') !== '1') { 1177 hyva.getBrowserStorage().setItem('amCookieBarFirstShow', '1'); 1178 1179 return true; 1180 } 1181 1182 return false; 1183 }, 1184 1185 /** 1186 * @param {number} lastUpdate 1187 * @returns {boolean} 1188 */ 1189 isNeedShowOnUpdate: function (lastUpdate) { 1190 if (!lastUpdate) { 1191 return false; 1192 } 1193 1194 return this.isNeedShowAfterLastVisit(lastUpdate) || this.isNeedShowAfterLastAccept(lastUpdate); 1195 }, 1196 1197 /** 1198 * @param {number} lastUpdate 1199 * @returns {boolean} 1200 */ 1201 isNeedShowAfterLastVisit: function (lastUpdate) { 1202 let needToShowAfterLastVisit = 1203 lastUpdate > hyva.getBrowserStorage().getItem('amCookieBarFirstShowTime'); 1204 1205 if (needToShowAfterLastVisit) { 1206 hyva.getBrowserStorage().setItem('amCookieBarFirstShow', null); 1207 hyva.setCookie('amcookie_allowed', '', -1, true); 1208 } 1209 1210 return needToShowAfterLastVisit; 1211 }, 1212 1213 /** 1214 * @param {number} lastUpdate 1215 * @returns {boolean} 1216 */ 1217 isNeedShowAfterLastAccept: function (lastUpdate) { 1218 let needToShowAfterLastAccept = false; 1219 1220 if (hyva.getBrowserStorage().getItem('am-last-cookie-acceptance')) { 1221 needToShowAfterLastAccept = 1222 lastUpdate > hyva.getBrowserStorage().getItem('am-last-cookie-acceptance'); 1223 } 1224 1225 return needToShowAfterLastAccept; 1226 }, 1227 1228 /** 1229 * @returns {void} 1230 */ 1231 closeCookieBarHandler: function () { 1232 this.$dispatch('close-cookie-bar'); 1233 } 1234 }; 1235 } 1236 1237 window.addEventListener('alpine:init', () => Alpine.data('initAmastyCookieBar', initAmastyCookieBar), {once: true}); 1238</script>
1238 1239<div class="page-wrapper"><header class="page-header"><a class="action skip sr-only focus:not-sr-only focus:absolute focus:z-40 focus:bg-white 1240 contentarea" 1241 href="#contentarea"> 1242 <span> 1243 Skip to Content </span> 1244</a> 1245
1245<script type="application/javascript" async 1246 src=https://static.klaviyo.com/onsite/js/TSsF5h/klaviyo.js ></script>
1246 1247 1248
1248<script type="text/x-magento-init"> 1249 { 1250 "*": { 1251 "KlaviyoCustomerData": {} 1252 } 1253 } 1254 </script>
vendor: 1 bytes, line 1254
1254
1255<script> 1256 function initHeader() { 1257 return { 1258 searchOpen: false, 1259 cart: {}, 1260 isCartOpen: false, 1261 getData(data) { 1262 if (data.cart) { this.cart = data.cart } 1263 }, 1264 isCartEmpty() { 1265 return !this.cart.summary_count 1266 }, 1267 toggleCart(event) { 1268 if (event.detail && event.detail.isOpen !== undefined) { 1269 this.isCartOpen = event.detail.isOpen 1270 if (!this.isCartOpen && this.$refs && this.$refs.cartButton) { 1271 this.$refs.cartButton.focus() 1272 } 1273 } else { 1274 this.isCartOpen = true 1275 } 1276 } 1277 } 1278 } 1279 1280 function initCompareHeader() { 1281 return { 1282 compareProducts: null, 1283 itemCount: 0, 1284 receiveCompareData(data) { 1285 if (data['compare-products']) { 1286 this.compareProducts = data['compare-products']; 1287 this.itemCount = this.compareProducts.count; 1288 } 1289 } 1290 } 1291 } 1292</script>
1292 1293<div id="header" class="main-header" x-data="initHeader()" @private-content-loaded.window="getData(event.detail.data)"> 1294 <div class="account-cart lg:justify-center xs:flex justify-between md:justify-center"> 1295 <div class="logo-mobile block lg:hidden"> <nav x-data="initMenuMobile_6ab50539b4114()" @load.window="setActiveMenu($root)" 1296 @keydown.window.escape="closeMenu()" class="z-20 order-2 sm:order-1 lg:order-2 navigation lg:hidden w-12 h-12" 1297 aria-label="Site navigation" role="navigation"> 1298 <!-- mobile --> 1299 <button x-ref="mobileMenuTrigger" @click="openMenu()" 1300 :class="{'overflow-x-hidden overflow-y-auto fixed top-0 left-0 w-full z-20' : open}" type="button" 1301 aria-label="Open menu" aria-haspopup="menu" :aria-expanded="open" 1302 :hidden="open"> 1303 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="p-3" width="48" height="48" :class="{ 'hidden' : open, 'block': !open }" aria-hidden="true"> 1304 <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/> 1305</svg> 1306 </button> 1307 <div x-ref="mobileMenuNavLinks" class=" 1308 fixed top-0 right-0 w-full h-full hidden 1309 flex-col border-t border-container bg-container-lighter 1310 overflow-y-auto overflow-x-hidden z-10 1311 " :class="{ 'flex z-10': open, 'hidden': !open }" :aria-hidden="open ? 'false' : 'true'" role="dialog" 1312 aria-modal="true"> 1313 1314 <ul class="border-t flex flex-col mt-16" 1315 aria-label="Site navigation links"> 1316 <li data-child-id="category-node-1083-main" class="level-0"> 1317 <div class="flex items-center" :class="{ 1318 '' : mobilePanelActiveId, 1319 '' : !mobilePanelActiveId 1320 }"> 1321 <a class="flex items-center w-full px-2 py-4 border-b cursor-pointer 1322 bg-container-lighter border-container level-0 1323 " href="https://www.huskee.co/shop" 1324 title="Shop"> 1325 Shop </a> 1326 <button @click="openSubcategory('category-node-1083')" class="absolute right-0 flex items-center justify-center w-11 h-11 cursor-pointer 1327 border-container" 1328 aria-label="Open Shop subcategories" 1329 aria-haspopup="true" 1330 :aria-expanded="mobilePanelActiveId === 'category-node-1083'"> 1331 <div class="w-6 h-8"> 1332 <i class="icon navPages-action-moreIcon"> 1333 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" 1334 class="icon-chevron" width="16" height="16" aria-hidden="true"> 1335 <path fill-rule="evenodd" 1336 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 1337 clip-rule="evenodd"></path> 1338 </svg> 1339 </i> 1340 </div> 1341 </button> 1342 </div> 1343 <div data-child-id="category-node-1083" 1344 class="flex flex-col gap-1 w-full h-full bg-container-lighter" :class="{ 1345 'hidden': mobilePanelActiveId !== 'category-node-1083' 1346 }"> 1347 <ul class="menu-slide" :class="{ 1348 '' : mobilePanelActiveId !== 'category-node-1083', 1349 '' : mobilePanelActiveId === 'category-node-1083', 1350 }" aria-label="Subcategories"> 1351 <li> 1352 <a href="https://www.huskee.co/shop" 1353 title="Shop" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 1354 bg-container-lighter border-container 1355 ">
1356 <span class=""> 1357 View All </span> 1358 </a> 1359 </li> 1360 <li> 1361 <a href="https://www.huskee.co/shop/shop-by-product" 1362 title="Shop By Product" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 1363 bg-container-lighter border-container 1364 "> 1365 <span class="text-base text-black"> 1366 Shop By Product </span> 1367 </a> 1368 1369 <ul class="border-l border-gray-200"> 1370 <li> 1371 <a href="https://www.huskee.co/shop/shop-by-product/reusable-cups" 1372 title="Reusable Cups" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1373 bg-container-lighter border-container 1374 "> 1375 <span class="text-sm text-gray-600"> 1376 Reusable Cups </span> 1377 </a> 1378 </li> 1379 <li> 1380 <a href="https://www.huskee.co/shop/shop-by-product/cups-no-lids" 1381 title="Cups (no lids)" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1382 bg-container-lighter border-container 1383 "> 1384 <span class="text-sm text-gray-600"> 1385 Cups (no lids) </span> 1386 </a> 1387 </li> 1388 <li> 1389 <a href="https://www.huskee.co/shop/shop-by-product/lids" 1390 title="Lids" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1391 bg-container-lighter border-container 1392 "> 1393 <span class="text-sm text-gray-600"> 1394 Lids </span> 1395 </a> 1396 </li> 1397 <li> 1398 <a href="https://www.huskee.co/shop/shop-by-product/saucers" 1399 title="Saucers" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1400 bg-container-lighter border-container 1401 "> 1402 <span class="text-sm text-gray-600"> 1403 Saucers </span> 1404 </a> 1405 </li> 1406 <li> 1407 <a href="https://www.huskee.co/shop/shop-by-product/espresso-sets" 1408 title="Espresso Sets" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1409 bg-container-lighter border-container 1410 ">
1411 <span class="text-sm text-gray-600"> 1412 Espresso Sets </span> 1413 </a> 1414 </li> 1415 <li> 1416 <a href="https://www.huskee.co/shop/shop-by-product/dog-bowls" 1417 title="Dog Bowls" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1418 bg-container-lighter border-container 1419 "> 1420 <span class="text-sm text-gray-600"> 1421 Dog Bowls </span> 1422 </a> 1423 </li> 1424 <li> 1425 <a href="https://www.huskee.co/shop/shop-by-product/replacement-parts" 1426 title="Replacement Parts" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1427 bg-container-lighter border-container 1428 "> 1429 <span class="text-sm text-gray-600"> 1430 Replacement Parts </span> 1431 </a> 1432 </li> 1433 </ul> 1434 </li> 1435 <li> 1436 <a href="https://www.huskee.co/shop/shop-by-collection" 1437 title="Shop By Collection" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 1438 bg-container-lighter border-container 1439 "> 1440 <span class="text-base text-black"> 1441 Shop By Collection </span> 1442 </a> 1443 1444 <ul class="border-l border-gray-200"> 1445 <li> 1446 <a href="https://www.huskee.co/shop/shop-by-collection/huskeeoriginal-category" 1447 title="HuskeeOriginal" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1448 bg-container-lighter border-container 1449 "> 1450 <span class="text-sm text-gray-600"> 1451 HuskeeOriginal </span> 1452 </a> 1453 </li> 1454 <li> 1455 <a href="https://www.huskee.co/shop/shop-by-collection/huskeerenew-category" 1456 title="HuskeeRenew" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1457 bg-container-lighter border-container 1458 ">
1459 <span class="text-sm text-gray-600"> 1460 HuskeeRenew </span> 1461 </a> 1462 </li> 1463 <li> 1464 <a href="https://www.huskee.co/shop/shop-by-collection/huskeesteel-category" 1465 title="HuskeeSteel" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1466 bg-container-lighter border-container 1467 "> 1468 <span class="text-sm text-gray-600"> 1469 HuskeeSteel </span> 1470 </a> 1471 </li> 1472 <li> 1473 <a href="https://www.huskee.co/shop/shop-by-collection/huskeeloop-category" 1474 title="HuskeeLoop" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1475 bg-container-lighter border-container 1476 "> 1477 <span class="text-sm text-gray-600"> 1478 HuskeeLoop </span> 1479 </a> 1480 </li> 1481 </ul> 1482 </li> 1483 <li> 1484 <a href="https://www.huskee.co/shop/new-arrivals" 1485 title="New Arrivals" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 1486 bg-container-lighter border-container 1487 "> 1488 <span class="text-base text-black"> 1489 New Arrivals </span> 1490 </a> 1491 1492 <ul class="border-l border-gray-200"> 1493 <li> 1494 <a href="https://www.huskee.co/shop/new-arrivals/blush-lemon-huskeesteel" 1495 title="Blush and Lemon Zest HuskeeSteel" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 1496 bg-container-lighter border-container 1497 "> 1498 <span class="text-sm text-gray-600"> 1499 Blush and Lemon Zest HuskeeSteel </span> 1500 </a> 1501 </li> 1502 </ul> 1503 </li> 1504 1505 </ul> 1506 </div> 1507 </li> 1508 <li> 1509 <div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><ul class="navPages-list"> 1510 <li 1511 class="navPages-item" 1512 x-data="{ open: false }" 1513 @mouseenter="if(window.innerWidth >= 768) open = true" 1514 @mouseleave="if(window.innerWidth >= 768) open = false" 1515 > 1516 <a 1517 class="navPages-action has-subMenu" 1518 href="#" 1519 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 1520 > 1521 Borrow by Huskee 1522 <i class="icon navPages-action-moreIcon"> 1523 <svg 1524 xmlns="http://www.w3.org/2000/svg"
1525 viewBox="0 0 20 20" 1526 fill="currentColor" 1527 class="icon-chevron" 1528 width="16" 1529 height="16" 1530 aria-hidden="true" 1531 > 1532 <path 1533 fill-rule="evenodd" 1534 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 1535 clip-rule="evenodd" 1536 ></path> 1537 </svg> 1538 </i> 1539 </a> 1540 <div 1541 class="navPage-subMenu" 1542 id="navPages-borrow_by_huskee" 1543 x-show="open" 1544 x-cloak 1545 x-transition 1546 :class="{ 'is-open': open }" 1547 > 1548 <ul class="navPage-subMenu-list"> 1549 <li class="navPage-subMenu-item"> 1550 <a 1551 class="navPage-subMenu-action navPages-action" 1552 href="https://borrowbyhuskee.co/" 1553 target="_blank" 1554 >What is Borrow by Huskee?</a 1555 > 1556 </li> 1557 <li class="navPage-subMenu-item"> 1558 <a 1559 class="navPage-subMenu-action navPages-action" 1560 href="https://swapcafe.huskee.co/login" 1561 target="_blank" 1562 >Venue Portal Login</a 1563 > 1564 </li> 1565 </ul> 1566 </div> 1567 </li> 1568 <li 1569 class="navPages-item" 1570 x-data="{ open: false }" 1571 @mouseenter="if(window.innerWidth >= 768) open = true" 1572 @mouseleave="if(window.innerWidth >= 768) open = false" 1573 > 1574 <a 1575 class="navPages-action has-subMenu" 1576 href="https://www.huskee.co//huskeeswap/" 1577 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 1578 > 1579 HuskeeSwap 1580 <i class="icon navPages-action-moreIcon"> 1581 <svg 1582 xmlns="http://www.w3.org/2000/svg"
1583 viewBox="0 0 20 20" 1584 fill="currentColor" 1585 class="icon-chevron" 1586 width="16" 1587 height="16" 1588 aria-hidden="true" 1589 > 1590 <path 1591 fill-rule="evenodd" 1592 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 1593 clip-rule="evenodd" 1594 ></path> 1595 </svg> 1596 </i> 1597 </a> 1598 <div 1599 class="navPage-subMenu" 1600 x-show="open" 1601 x-cloak 1602 x-transition 1603 :class="{ 'is-open': open }" 1604 > 1605 <ul class="navPage-subMenu-list"> 1606 1607 <li class="navPage-subMenu-item"> 1608 <a class="navPage-subMenu-action navPages-action" href="/swap/" 1609 >What is HuskeeSwap?</a 1610 > 1611 </li> 1612 <li class="navPage-subMenu-item"> 1613 <a 1614 class="navPage-subMenu-action navPages-action" 1615 href="https://swapcafe.huskee.co/login" 1616 target="_blank" 1617 >Swap Portal Login</a 1618 > 1619 </li> 1620 <li class="navPage-subMenu-item"> 1621 <a 1622 class="navPage-subMenu-action navPages-action" 1623 href="https://swapcafe.huskee.co/register" 1624 target="_blank" 1625 >Register Venue</a 1626 > 1627 </li> 1628 <li class="navPage-subMenu-item"> 1629 <a class="navPage-subMenu-action navPages-action" href="/locations/" 1630 >Find a HuskeeSwap Site</a 1631 > 1632 </li> 1633 </ul> 1634 </div> 1635 </li> 1636 <li class="navPages-item"> 1637 <a class="navPages-action" href="/huskeeloop/">HuskeeLoop</a> 1638 </li> 1639 <li 1640 class="navPages-item" 1641 x-data="{ open: false }" 1642 @mouseenter="if(window.innerWidth >= 768) open = true" 1643 @mouseleave="if(window.innerWidth >= 768) open = false" 1644 > 1645 <a 1646 class="navPages-action has-subMenu" 1647 href="#" 1648 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 1649 > 1650 Wholesale 1651 <i class="icon navPages-action-moreIcon"> 1652 <svg 1653 xmlns="http://www.w3.org/2000/svg"
1654 viewBox="0 0 20 20" 1655 fill="currentColor" 1656 class="icon-chevron" 1657 width="16" 1658 height="16" 1659 aria-hidden="true" 1660 > 1661 <path 1662 fill-rule="evenodd" 1663 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 1664 clip-rule="evenodd" 1665 ></path> 1666 </svg> 1667 </i> 1668 </a> 1669 <div 1670 class="navPage-subMenu" 1671 x-show="open" 1672 x-cloak 1673 x-transition 1674 :class="{ 'is-open': open }" 1675 > 1676 <ul class="navPage-subMenu-list"> 1677 1678 <li class="navPage-subMenu-item"> 1679 <a 1680 class="navPage-subMenu-action navPages-action" 1681 href="https://www.huskee.co//customer/account/login/" 1682 >Login/Register</a 1683 > 1684 </li> 1685 1686 <li class="navPage-subMenu-item"> 1687 <a 1688 class="navPage-subMenu-action navPages-action" 1689 href="/international-enquiries/" 1690 >International Enquiries</a 1691 > 1692 </li> 1693 </ul> 1694 </div> 1695 </li> 1696 <li class="navPages-item"> 1697 <a class="navPages-action" href="/co-branding/">Co-Branding</a> 1698 </li> 1699 <li 1700 class="navPages-item" 1701 x-data="{ open: false }" 1702 @mouseenter="if(window.innerWidth >= 768) open = true" 1703 @mouseleave="if(window.innerWidth >= 768) open = false" 1704 > 1705 <a 1706 class="navPages-action has-subMenu" 1707 href="#" 1708 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 1709 > 1710 Resources 1711 <i class="icon navPages-action-moreIcon"> 1712 <svg 1713 xmlns="http://www.w3.org/2000/svg"
1714 viewBox="0 0 20 20" 1715 fill="currentColor" 1716 class="icon-chevron" 1717 width="16" 1718 height="16" 1719 aria-hidden="true" 1720 > 1721 <path 1722 fill-rule="evenodd" 1723 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 1724 clip-rule="evenodd" 1725 ></path> 1726 </svg> 1727 </i> 1728 </a> 1729 <div 1730 class="navPage-subMenu" 1731 x-cloak 1732 id="navPages-resources" 1733 x-show="open" 1734 x-transition 1735 :class="{ 'is-open': open }" 1736 > 1737 <ul class="navPage-subMenu-list"> 1738 <li class="navPage-subMenu-item"> 1739 <a 1740 class="navPage-subMenu-action navPages-action has-subMenu" 1741 href="/our-story/" 1742 > 1743 About Huskee 1744 </a> 1745 <ul class="navPage-childList"> 1746 <li class="navPage-childList-item"> 1747 <a 1748 class="navPage-childList-action navPages-action" 1749 href="/our-story/" 1750 >Our Story</a 1751 > 1752 </li> 1753 <li class="navPage-childList-item"> 1754 <a 1755 class="navPage-childList-action navPages-action" 1756 href="/partnerships/" 1757 >Our Partnerships</a 1758 > 1759 </li> 1760 <li class="navPage-childList-item"> 1761 <a 1762 class="navPage-childList-action navPages-action" 1763 href="/customer-case-studies/" 1764 >Customer Case Studies</a 1765 > 1766 </li> 1767 </ul> 1768 </li> 1769 <li class="navPage-subMenu-item"> 1770 <a 1771 class="navPage-subMenu-action navPages-action has-subMenu" 1772 href="/support/" 1773 > 1774 Support 1775 </a> 1776 <ul class="navPage-childList" id="navPages-"> 1777 <li class="navPage-childList-item"> 1778 <a 1779 class="navPage-childList-action navPages-action" 1780 href="/faq/" 1781 >FAQ</a 1782 > 1783 </li> 1784 <li class="navPage-childList-item"> 1785 <a 1786 class="navPage-childList-action navPages-action" 1787 href="/contact/" 1788 >Contact Us</a 1789 > 1790 </li> 1791 </ul> 1792 </li> 1793 <li class="navPage-subMenu-item"> 1794 <a 1795 class="navPage-subMenu-action navPages-action has-subMenu" 1796 href="/discover/" 1797 > 1798 Discover 1799 </a> 1800 <ul class="navPage-childList" id="navPages-"> 1801 <li class="navPage-childList-item"> 1802 <a class="navPage-childList-action navPages-action" href="/blog/" 1803 >Blog</a 1804 > 1805 </li> 1806 <li class="navPage-childList-item"> 1807 <a 1808 class="navPage-childList-action navPages-action" 1809 href="/impact-calculator/" 1810 >Impact Calculator</a 1811 > 1812 </li> 1813 </ul> 1814 </li> 1815 </ul> 1816 </div> 1817 </li> 1818</ul> 1819</div> </li> 1820 </ul> 1821 <button @click="closeMenu()" class="absolute flex justify-end w-16 self-end mb-1" 1822 aria-label="Close menu" type="button"> 1823 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 1824 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1825</svg> 1826 </button> 1827 </div> 1828</nav>
1829<script> 1830 'use strict'; 1831 1832 const initMenuMobile_6ab50539b4114 = () => { 1833 return { 1834 mobilePanelActiveId: null, 1835 open: false, 1836 setActiveMenu(menuNode) { 1837 Array.from(menuNode.querySelectorAll('a')).filter(link => { 1838 return link.href === window.location.href.split('?')[0]; 1839 }).map(item => { 1840 item.classList.add('underline'); 1841 item.closest('li.level-0') && 1842 item.closest('li.level-0').querySelector('a.level-0').classList.add('underline'); 1843 }); 1844 }, 1845 openMenu() { 1846 this.open = true 1847 this.$nextTick(() => hyva.trapFocus(this.$refs['mobileMenuNavLinks'])); 1848 // Prevent from body scrolling while mobile menu opened 1849 document.body.style.overflow = 'hidden'; 1850 }, 1851 closeMenu() { 1852 document.body.style.overflow = ''; 1853 1854 if (this.open) { 1855 this.$nextTick(() => this.$refs['mobileMenuTrigger'].focus() || hyva.releaseFocus()); 1856 } 1857 1858 this.open = false 1859 this.mobilePanelActiveId = null 1860 }, 1861 openSubcategory(index) { 1862 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 1863 this.mobilePanelActiveId = this.mobilePanelActiveId === index ? 0 : index 1864 this.$nextTick(() => hyva.trapFocus(menuNodeRef)) 1865 }, 1866 backToMainCategories(index) { 1867 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 1868 this.mobilePanelActiveId = 0 1869 this.$nextTick(() => { 1870 hyva.trapFocus(this.$refs['mobileMenuNavLinks']) 1871 menuNodeRef.querySelector('a').focus() 1872 }) 1873 } 1874 } 1875 } 1876</script>
1876 1877<div class="navigation hidden lg:flex z-20 order-2 sm:order-1 lg:order-2" x-data="{ }"> 1878 <!-- root scope (no shared state; each node manages itself) --> 1879 <nav class="relative w-full" aria-label="Main menu"> 1880 <ul class="menu-root flex flex-wrap justify-center"> 1881 1882 <li id="nav-0" class="level0 parent level-top" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1883 <span class="node-line flex items-center"> 1884 <a class="py-1" href="https://www.huskee.co/shop" 1885 title="Shop"> 1886 Shop </a> 1887 1888 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 1889 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083"> 1890 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 1891 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 1892</svg> 1893 <span class="sr-only">Toggle submenu</span> 1894 </button> 1895 </span> 1896 1897 <ul id="submenu-category-node-1083" x-cloak x-show="open" x-transition 1898 class="submenu submenu-top"> 1899 <li id="nav-0" class="level1 parent" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1900 <span class="node-line flex items-center"> 1901 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product" 1902 title="Shop By Product"> 1903 Shop By Product </a> 1904 1905 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 1906 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083_category-node-1140"> 1907 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 1908 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 1909</svg> 1910 <span class="sr-only">Toggle submenu</span> 1911 </button> 1912 </span> 1913 1914 <ul id="submenu-category-node-1083_category-node-1140" 1915 class="submenu submenu-inner"> 1916 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1917 <span class="node-line flex items-center"> 1918 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/reusable-cups" 1919 title="Reusable Cups"> 1920 Reusable Cups </a> 1921 1922 </span> 1923 1924 </li> 1925 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1926 <span class="node-line flex items-center"> 1927 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/cups-no-lids" 1928 title="Cups (no lids)"> 1929 Cups (no lids) </a> 1930 1931 </span> 1932 1933 </li> 1934 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1935 <span class="node-line flex items-center"> 1936 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/lids" 1937 title="Lids"> 1938 Lids </a> 1939 1940 </span> 1941 1942 </li> 1943 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
1944 <span class="node-line flex items-center"> 1945 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/saucers" 1946 title="Saucers"> 1947 Saucers </a> 1948 1949 </span> 1950 1951 </li> 1952 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1953 <span class="node-line flex items-center"> 1954 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/espresso-sets" 1955 title="Espresso Sets"> 1956 Espresso Sets </a> 1957 1958 </span> 1959 1960 </li> 1961 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1962 <span class="node-line flex items-center"> 1963 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/dog-bowls" 1964 title="Dog Bowls"> 1965 Dog Bowls </a> 1966 1967 </span> 1968 1969 </li> 1970 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1971 <span class="node-line flex items-center"> 1972 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/replacement-parts" 1973 title="Replacement Parts"> 1974 Replacement Parts </a> 1975 1976 </span> 1977 1978 </li> 1979 </ul> 1980 </li> 1981 <li id="nav-0" class="level1 parent" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1982 <span class="node-line flex items-center"> 1983 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection" 1984 title="Shop By Collection"> 1985 Shop By Collection </a> 1986 1987 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 1988 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083_category-node-1170"> 1989 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 1990 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 1991</svg> 1992 <span class="sr-only">Toggle submenu</span> 1993 </button> 1994 </span> 1995 1996 <ul id="submenu-category-node-1083_category-node-1170" 1997 class="submenu submenu-inner"> 1998 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 1999 <span class="node-line flex items-center"> 2000 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeeoriginal-category" 2001 title="HuskeeOriginal"> 2002 HuskeeOriginal </a> 2003 2004 </span> 2005 2006 </li> 2007 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 2008 <span class="node-line flex items-center"> 2009 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeerenew-category" 2010 title="HuskeeRenew"> 2011 HuskeeRenew </a> 2012 2013 </span> 2014 2015 </li> 2016 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 2017 <span class="node-line flex items-center"> 2018 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeesteel-category" 2019 title="HuskeeSteel"> 2020 HuskeeSteel </a> 2021 2022 </span> 2023 2024 </li> 2025 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
2026 <span class="node-line flex items-center"> 2027 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeeloop-category" 2028 title="HuskeeLoop"> 2029 HuskeeLoop </a> 2030 2031 </span> 2032 2033 </li> 2034 </ul> 2035 </li> 2036 <li id="nav-0" class="level1 parent" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 2037 <span class="node-line flex items-center"> 2038 <a class="py-1" href="https://www.huskee.co/shop/new-arrivals" 2039 title="New Arrivals"> 2040 New Arrivals </a> 2041 2042 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 2043 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083_category-node-1125"> 2044 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 2045 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 2046</svg> 2047 <span class="sr-only">Toggle submenu</span> 2048 </button> 2049 </span> 2050 2051 <ul id="submenu-category-node-1083_category-node-1125" 2052 class="submenu submenu-inner"> 2053 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 2054 <span class="node-line flex items-center"> 2055 <a class="py-1" href="https://www.huskee.co/shop/new-arrivals/blush-lemon-huskeesteel" 2056 title="Blush and Lemon Zest HuskeeSteel"> 2057 Blush and Lemon Zest HuskeeSteel </a> 2058 2059 </span> 2060 2061 </li> 2062 </ul> 2063 </li> 2064 </ul> 2065 </li> 2066 <li> 2067 2068<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><ul class="navPages-list"> 2069 <li 2070 class="navPages-item" 2071 x-data="{ open: false }" 2072 @mouseenter="if(window.innerWidth >= 768) open = true" 2073 @mouseleave="if(window.innerWidth >= 768) open = false" 2074 > 2075 <a 2076 class="navPages-action has-subMenu" 2077 href="#" 2078 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 2079 > 2080 Borrow by Huskee 2081 <i class="icon navPages-action-moreIcon"> 2082 <svg 2083 xmlns="http://www.w3.org/2000/svg"
2084 viewBox="0 0 20 20" 2085 fill="currentColor" 2086 class="icon-chevron" 2087 width="16" 2088 height="16" 2089 aria-hidden="true" 2090 > 2091 <path 2092 fill-rule="evenodd" 2093 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 2094 clip-rule="evenodd" 2095 ></path> 2096 </svg> 2097 </i> 2098 </a> 2099 <div 2100 class="navPage-subMenu" 2101 id="navPages-borrow_by_huskee" 2102 x-show="open" 2103 x-cloak 2104 x-transition 2105 :class="{ 'is-open': open }" 2106 > 2107 <ul class="navPage-subMenu-list"> 2108 <li class="navPage-subMenu-item"> 2109 <a 2110 class="navPage-subMenu-action navPages-action" 2111 href="https://borrowbyhuskee.co/" 2112 target="_blank" 2113 >What is Borrow by Huskee?</a 2114 > 2115 </li> 2116 <li class="navPage-subMenu-item"> 2117 <a 2118 class="navPage-subMenu-action navPages-action" 2119 href="https://swapcafe.huskee.co/login" 2120 target="_blank" 2121 >Venue Portal Login</a 2122 > 2123 </li> 2124 </ul> 2125 </div> 2126 </li> 2127 <li 2128 class="navPages-item" 2129 x-data="{ open: false }" 2130 @mouseenter="if(window.innerWidth >= 768) open = true" 2131 @mouseleave="if(window.innerWidth >= 768) open = false" 2132 > 2133 <a 2134 class="navPages-action has-subMenu" 2135 href="https://www.huskee.co//huskeeswap/" 2136 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 2137 > 2138 HuskeeSwap 2139 <i class="icon navPages-action-moreIcon"> 2140 <svg 2141 xmlns="http://www.w3.org/2000/svg"
2142 viewBox="0 0 20 20" 2143 fill="currentColor" 2144 class="icon-chevron" 2145 width="16" 2146 height="16" 2147 aria-hidden="true" 2148 > 2149 <path 2150 fill-rule="evenodd" 2151 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 2152 clip-rule="evenodd" 2153 ></path> 2154 </svg> 2155 </i> 2156 </a> 2157 <div 2158 class="navPage-subMenu" 2159 x-show="open" 2160 x-cloak 2161 x-transition 2162 :class="{ 'is-open': open }" 2163 > 2164 <ul class="navPage-subMenu-list"> 2165 2166 <li class="navPage-subMenu-item"> 2167 <a class="navPage-subMenu-action navPages-action" href="/swap/" 2168 >What is HuskeeSwap?</a 2169 > 2170 </li> 2171 <li class="navPage-subMenu-item"> 2172 <a 2173 class="navPage-subMenu-action navPages-action" 2174 href="https://swapcafe.huskee.co/login" 2175 target="_blank" 2176 >Swap Portal Login</a 2177 > 2178 </li> 2179 <li class="navPage-subMenu-item"> 2180 <a 2181 class="navPage-subMenu-action navPages-action" 2182 href="https://swapcafe.huskee.co/register" 2183 target="_blank" 2184 >Register Venue</a 2185 > 2186 </li> 2187 <li class="navPage-subMenu-item"> 2188 <a class="navPage-subMenu-action navPages-action" href="/locations/" 2189 >Find a HuskeeSwap Site</a 2190 > 2191 </li> 2192 </ul> 2193 </div> 2194 </li> 2195 <li class="navPages-item"> 2196 <a class="navPages-action" href="/huskeeloop/">HuskeeLoop</a> 2197 </li> 2198 <li 2199 class="navPages-item" 2200 x-data="{ open: false }" 2201 @mouseenter="if(window.innerWidth >= 768) open = true" 2202 @mouseleave="if(window.innerWidth >= 768) open = false" 2203 > 2204 <a 2205 class="navPages-action has-subMenu" 2206 href="#" 2207 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 2208 > 2209 Wholesale 2210 <i class="icon navPages-action-moreIcon"> 2211 <svg 2212 xmlns="http://www.w3.org/2000/svg"
2213 viewBox="0 0 20 20" 2214 fill="currentColor" 2215 class="icon-chevron" 2216 width="16" 2217 height="16" 2218 aria-hidden="true" 2219 > 2220 <path 2221 fill-rule="evenodd" 2222 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 2223 clip-rule="evenodd" 2224 ></path> 2225 </svg> 2226 </i> 2227 </a> 2228 <div 2229 class="navPage-subMenu" 2230 x-show="open" 2231 x-cloak 2232 x-transition 2233 :class="{ 'is-open': open }" 2234 > 2235 <ul class="navPage-subMenu-list"> 2236 2237 <li class="navPage-subMenu-item"> 2238 <a 2239 class="navPage-subMenu-action navPages-action" 2240 href="https://www.huskee.co//customer/account/login/" 2241 >Login/Register</a 2242 > 2243 </li> 2244 2245 <li class="navPage-subMenu-item"> 2246 <a 2247 class="navPage-subMenu-action navPages-action" 2248 href="/international-enquiries/" 2249 >International Enquiries</a 2250 > 2251 </li> 2252 </ul> 2253 </div> 2254 </li> 2255 <li class="navPages-item"> 2256 <a class="navPages-action" href="/co-branding/">Co-Branding</a> 2257 </li> 2258 <li 2259 class="navPages-item" 2260 x-data="{ open: false }" 2261 @mouseenter="if(window.innerWidth >= 768) open = true" 2262 @mouseleave="if(window.innerWidth >= 768) open = false" 2263 > 2264 <a 2265 class="navPages-action has-subMenu" 2266 href="#" 2267 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 2268 > 2269 Resources 2270 <i class="icon navPages-action-moreIcon"> 2271 <svg 2272 xmlns="http://www.w3.org/2000/svg"
2273 viewBox="0 0 20 20" 2274 fill="currentColor" 2275 class="icon-chevron" 2276 width="16" 2277 height="16" 2278 aria-hidden="true" 2279 > 2280 <path 2281 fill-rule="evenodd" 2282 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 2283 clip-rule="evenodd" 2284 ></path> 2285 </svg> 2286 </i> 2287 </a> 2288 <div 2289 class="navPage-subMenu" 2290 x-cloak 2291 id="navPages-resources" 2292 x-show="open" 2293 x-transition 2294 :class="{ 'is-open': open }" 2295 > 2296 <ul class="navPage-subMenu-list"> 2297 <li class="navPage-subMenu-item"> 2298 <a 2299 class="navPage-subMenu-action navPages-action has-subMenu" 2300 href="/our-story/" 2301 > 2302 About Huskee 2303 </a> 2304 <ul class="navPage-childList"> 2305 <li class="navPage-childList-item"> 2306 <a 2307 class="navPage-childList-action navPages-action" 2308 href="/our-story/" 2309 >Our Story</a 2310 > 2311 </li> 2312 <li class="navPage-childList-item"> 2313 <a 2314 class="navPage-childList-action navPages-action" 2315 href="/partnerships/" 2316 >Our Partnerships</a 2317 > 2318 </li> 2319 <li class="navPage-childList-item"> 2320 <a 2321 class="navPage-childList-action navPages-action" 2322 href="/customer-case-studies/" 2323 >Customer Case Studies</a 2324 > 2325 </li> 2326 </ul> 2327 </li> 2328 <li class="navPage-subMenu-item"> 2329 <a 2330 class="navPage-subMenu-action navPages-action has-subMenu" 2331 href="/support/" 2332 > 2333 Support 2334 </a> 2335 <ul class="navPage-childList" id="navPages-"> 2336 <li class="navPage-childList-item"> 2337 <a 2338 class="navPage-childList-action navPages-action" 2339 href="/faq/" 2340 >FAQ</a 2341 > 2342 </li> 2343 <li class="navPage-childList-item"> 2344 <a 2345 class="navPage-childList-action navPages-action" 2346 href="/contact/" 2347 >Contact Us</a 2348 > 2349 </li> 2350 </ul> 2351 </li> 2352 <li class="navPage-subMenu-item"> 2353 <a 2354 class="navPage-subMenu-action navPages-action has-subMenu" 2355 href="/discover/" 2356 > 2357 Discover 2358 </a> 2359 <ul class="navPage-childList" id="navPages-"> 2360 <li class="navPage-childList-item"> 2361 <a class="navPage-childList-action navPages-action" href="/blog/" 2362 >Blog</a 2363 > 2364 </li> 2365 <li class="navPage-childList-item"> 2366 <a 2367 class="navPage-childList-action navPages-action" 2368 href="/impact-calculator/" 2369 >Impact Calculator</a 2370 > 2371 </li> 2372 </ul> 2373 </li> 2374 </ul> 2375 </div> 2376 </li> 2377</ul> 2378</div> 2379</li> 2380 </ul> 2381 </nav> 2382</div> 2383 2384</div> 2385 <div class="logo-mobile block lg:hidden"> 2386 <div class="order-1 sm:order-2 lg:order-1 w-full pb-2 sm:w-auto sm:pb-0"> 2387 <a 2388 class="flex items-center justify-center text-xl font-medium tracking-wide text-gray-800 2389 no-underline hover:no-underline font-title" 2390 href="https://www.huskee.co/" 2391 aria-label="Go to Home page" 2392 > 2393 <img 2394 src="https://www.huskee.co/media/logo/websites/24/huskee_logo.png" 2395 alt="Huskee" 2396 width="250" height="49" loading="eager" 2397 /> 2398 </a> 2399</div> 2400 </div> 2401 <div id="header" class="w-full block lg:hidden" x-data="initHeader()" 2402 @private-content-loaded.window="getData(event.detail.data)"> 2403 <div class="container flex flex-wrap lg:flex-nowrap items-center justify-end w-full px-2 py-3 mx-auto mt-0"> 2404 2405 <div class="flex items-center gap-1 md:gap-1 order-3 md:-mr-1"> 2406 2407 <!--Search Icon--> 2408 <button id="menu-search-icon" class="rounded p-1 outline-offset-2" @click.prevent=" 2409 searchOpen = !searchOpen;
2410 $dispatch('search-open'); 2411 " aria-label="Toggle search form" aria-haspopup="true" 2412 :aria-expanded="searchOpen" x-ref="searchButton"> 2413 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="md:h-6 md:w-6" width="24" height="24" aria-hidden="true"> 2414 <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/> 2415</svg> 2416 </button> 2417 2418 <!-- Additional Header Elements --> 2419 2420 <!--Customer Icon & Dropdown--> 2421 2422 <!--Customer Icon & Dropdown--> 2423 2424<div class="relative inline-block" x-data="{ open: false }" @keyup.escape="open = false" @click.outside="open = false"> 2425 <button type="button" id="customer-menu" class="block rounded p-1" 2426 @click="open = !open" :aria-expanded="open ? 'true' : 'false'" 2427 aria-label="My Account" aria-haspopup="true"> 2428 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="md:h-6 md:w-6" width="24" height="24" aria-hidden="true"> 2429 <path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/> 2430</svg> 2431 </button> 2432 <nav class="absolute right-0 z-20 w-40 py-2 mt-2 -mr-4 px-4 overflow-auto origin-top-right rounded-sm 2433 shadow-lg lg:mt-3 bg-container-lighter " x-cloak x-show="open" aria-labelledby="customer-menu"> 2434 <ul class="header-links-account grid gap-y-1"> 2435 <li class="link text-sm"> <a href="/customer/account/login">Login</a></li> 2436 <li class="link text-sm"> <a href="/customer/account/create">Signup</a></li> 2437 </ul> 2438 2439 </nav> 2440</div> 2441 2442 <!--Cart Icon--> 2443 <button id="menu-cart-icon" 2444 class="relative inline-block rounded p-1 pt-2 outline-offset-2" x-ref="cartButton" 2445 :aria-disabled="isCartEmpty()" :aria-label="` 2446 Toggle minicart, 2447 ${isCartEmpty() 2448 ? 'Cart is empty' 2449 : cart.summary_count > 1 2450 ? hyva.str('%1 items', cart.summary_count) 2451 : hyva.str('%1 item', cart.summary_count) 2452 }`" @click.prevent="() => { 2453 $dispatch('toggle-cart', { isOpen: true }) 2454 }" @toggle-cart.window="toggleCart($event)" :aria-expanded="isCartOpen" aria-haspopup="dialog" 2455 > 2456 <svg xmlns="http://www.w3.org/2000/svg" fill="#000000" width="26px" height="26px"
2457 viewBox="-1.5 0 19 19" class="cf-icon-svg"> 2458 <path 2459 d="M14.252 4.59a.924.924 0 0 1 .921.92v3.602a1.048 1.048 0 0 1-.916 1.017l-8.511.883a.573.573 0 0 1-.145.019.577.577 0 1 0 0 1.154h8.488a.563.563 0 1 1 0 1.126h-.91a1.03 1.03 0 1 1-1.104 0H6.849a1.03 1.03 0 1 1-1.104 0H5.6a1.703 1.703 0 1 1 0-3.406.585.585 0 0 1 .128.014L3.111 3.911H1.39a.563.563 0 1 1 0-1.125h2.09a.562.562 0 0 1 .515.337l.64 1.466h9.617z"> 2460 </path> 2461 </svg> 2462 <span x-text="cart.summary_count" x-show="!isCartEmpty()" x-cloak class="absolute -top-1.5 -right-1.5 h-5 px-2 py-1 rounded-full bg-primary text-white 2463 text-xs font-semibold leading-none text-center uppercase tabular-nums" 2464 aria-hidden="true"></span> 2465 </button> 2466 </div> 2467 </div> 2468 <!--Search--> 2469 <div class="absolute right-0 z-20 w-full border-t shadow-sm bg-container-lighter border-container-lighter" 2470 id="search-content" x-cloak x-show="searchOpen" @click.outside="searchOpen = false" @keydown.escape=" 2471 searchOpen = false; 2472 $refs.searchButton.focus(); 2473 "> 2474 2475 2476<div class="search-content-wrapper" 2477 x-data="initMirasvitSearch" 2478 @click.away="closeSearch" 2479 x-init="initShowPrevSearch" 2480> 2481 <div class="mx-auto text-black lg:p-0 xs:p-2" 2482 @click.away="closeSearch"> 2483 <div class="relative"> 2484 <form class="form minisearch relative" 2485 id="search_mini_form" 2486 action="https://www.huskee.co/catalogsearch/result/" 2487 method="get" 2488 autocomplete="off"> 2489 <label class="hidden" for="search" data-role="minisearch-label"> 2490 <span>Search</span> 2491 </label> 2492 <template x-if="isLayoutFullOpen"> 2493 <span class="text-gray-400 left-0 p-3"> 2494 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="text-gray-400" width="40" height="40" role="img"> 2495 <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/> 2496<title>search</title></svg> 2497 </span> 2498 </template> 2499 2500 <input id="search" x-ref="searchInput" type="search" 2501 name="q" 2502 value="" 2503 placeholder="Search Huskee" 2504 maxlength="128" 2505 class="w-full p-2 text-lg leading-normal transition appearance-none text-gray-800 focus:outline-none focus:border-transparent lg:text-xl" 2506 x-on:click="preOpen" 2507 x-on:search-open.window="preOpen" 2508 x-on:search-open.window.debounce.20="focusAfterOpen" 2509 x-on:search-open.window.debounce.100="startSearch" 2510 x-on:search-open.window.debounce.150="openSearch" 2511 x-on:search-open.window.debounce.300="doSearchSuggest" 2512 x-on:keydown.debounce.100="startSearch" 2513 x-on:click.debounce.150="openSearch" 2514 x-on:click.debounce.300="doSearchSuggest" 2515 x-on:keyup.debounce.300="doSearchSuggest"/> 2516 2517 <template x-if="isLayoutFullOpen"> 2518 <div class="right-0 top-0 p-3 flex"> 2519 <div class="mstInPage__indexList items-center hidden lg:flex text-base lg:text-lg"> 2520 <div> 2521 <a class="mstInPage__viewAll whitespace-nowrap mr-10 text-blue-500 no-underline" 2522 x-show="productIndex" 2523 :href="numberResultsUrl"> 2524 View all results â </a> 2525 </div> 2526 2527 <div class="flex"> 2528 <template x-for="(searchResult, identifier) in searchResults"> 2529 <template x-if="resultTotalItems"> 2530 <div 2531 class="mstInPage__index flex h-10 mr-1.5 px-2 pt-2 lg:pt-1 text-center align-middle inline-block rounded border border-transparent cursor-pointer whitespace-nowrap" 2532 @click="doActivateSection" 2533 :class="activeIndexClass">
2534 <span x-text="searchResult.title"></span> 2535 <small class="ml-1" 2536 :class="activeTotalsClass"> 2537 (<span x-html="searchResult.totalItems"></span>) 2538 </small> 2539 </div> 2540 </template> 2541 </template> 2542 </div> 2543 </div> 2544 <span @click="closeSearch" class="cursor-pointer"> 2545 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="text-gray-400" width="40" height="40" role="img"> 2546 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 2547<title>x</title></svg> 2548 </span> 2549 </div> 2550 </template> 2551 2552 </form> 2553 2554 2555 <div x-cloak 2556 id="mirasvitSearchResults" 2557 class="border-solid w-full mt-1 bg-white text-lg leading-normal transition appearance-none text-gray-800 lg:text-xl z-10 my-1 absolute right-0 lg:min-w-[400px]" 2558 x-show="isOpen"> 2559 <template x-if="hasSearchResults"> 2560 <div class="flex flex-col"> 2561 <!-- Search Results --> 2562 <div class="overflow-y-auto overflow-x-hidden gap-1 max-h-[65vh] sm:grid-cols-8" 2563 2564 2565 <!-- Products --> 2566 <div class="mstInPage__itemList magento_catalog_product" 2567 x-show="isNotLayoutFullOrProductActive"> 2568 <p class="text-lg leading-normal transition appearance-none text-gray-800 lg:text-base border-b p-2 flex font-poppins items-center"> 2569 <span x-text="productIndexTitle"></span> 2570 <template x-if="productIndex"> 2571 <small class="color-black ml-1"> 2572 (<span 2573 x-html="productTotalItems"></span>) 2574 </small> 2575 </template> 2576 </p> 2577 2578 <template x-if="productIndex"> 2579 <template x-if="isProductMisspell"> 2580 <div class="bg-gray-100 p-2"> 2581 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 2582 x-text="productTextMisspell"></p> 2583 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 2584 x-text="productTextCorrected"></p> 2585 </div> 2586 </template> 2587 </template> 2588 2589 <div class="flex flex-row"> 2590 2591 </div> 2592 2593 <div class="mx-auto grid grid-cols-1 p-2 gap-1"> 2594 <template x-if="productIndex"> 2595 <template x-for="item in productItems"> 2596 <div 2597 class="mst-searchautocomplete__item flex flex-row text-base card gap-4 p-1"> 2598 <div class="search-product-image flex-shrink-0"> 2599 <a :href="itemUrl" 2600 @click="processItemClick"> 2601 <img :src="itemImageUrl" 2602 :alt="itemName" class="search-product-image object-contain"/> 2603 </a> 2604 </div> 2605 <div class="mst__product-meta w-full"> 2606 <a :href="itemUrl" 2607 @click="processItemClick" 2608 class="flex flex-row flex-wrap w-full"> 2609 <div class="flex flex-col w-full"> 2610 <h4 class="text-gray-900 font-bold font-poppins text-sm " 2611 x-html="item.name"> 2612 </h4> 2613 <p class="text-gray-600 font-poppins text-xs mt-1" 2614 x-html="item.sku"> 2615 </p> 2616 </div> 2617 </a> 2618 <div class="mt-auto font-poppins w-full pb-2 text-sm"> 2619 <div class="flex justify-between"> 2620 <div class="flex flex-row text-sm mt-2"> 2621 <sp
2621an class="text-gray-700 font-semibold justify-end lg:flex-col xl:flex-row" 2622 x-html="itemPrice"> 2623 </span> 2624 </div> 2625 </div> 2626 </div> 2627 </div> 2628 </div> 2629 </template> 2630 </template> 2631 <template x-if="isNotLoadingAndNoResults"> 2632 <p class="text-lg">No results found.</p> 2633 </template> 2634 </div> 2635 2636 </div> 2637 2638 <!-- Categories, Pages, Posts, Articles (Full page layout) --> 2639 2640 2641 <!-- Categories, Pages, Posts, Articles (1|2-column layout) --> 2642 <!-- <template x-for="(searchResult, identifier) in searchResults"> 2643 <template 2644 x-if="resultsForOtherIndexType"> 2645 <div class="mb-6"> 2646 <p class="text-lg leading-normal transition appearance-none text-gray-800 lg:text-base border-b p-2 flex font-poppins items-center"> 2647 <span x-text="searchResult.title"></span> 2648 <small class="text-gray-500 ml-1"> 2649 (<span x-html="searchResult.totalItems"></span>) 2650 </small> 2651 </p> 2652 <template x-if="searchResult.isMisspell"> 2653 <div class="bg-gray-100 p-2"> 2654 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 2655 x-text="searchResult.textMisspell"></p> 2656 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 2657 x-text="searchResult.textCorrected"></p> 2658 </div> 2659 </template> 2660 <div class="px-2"> 2661 <template x-for="item in searchResult.items"> 2662 <a :href="itemUrl"> 2663 <div class="my-2 flex flex-wrap"> 2664 <span 2665 class="m-1 font-bold text-sm leading-loose cursor-pointer" 2666 x-html="item.name"> 2667 </span> 2668 </div> 2669 </a> 2670 </template> 2671 </div> 2672 </div> 2673 </template> 2674 </template> --> 2675 2676 2677 </div> 2678 2679 <!-- Bottom --> 2680 <div class="p-3 w-full h-full order-2 col-span-8 bg-gray-100"> 2681 <div class="flex items-center justify-center"> 2682 <a :href="numberResultsUrl"> 2683 <p class="text-gray-900 transition duration-300 hover:text-gray-500 font-bold text-sm" 2684 x-html="numberResults"></p> 2685 </a> 2686 </div> 2687 </div> 2688 </div> 2689 </template> 2690 2691 <template x-if="noResults"> 2692 <div class="bg-gray-100 p-2"> 2693 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 2694 x-text="noResultsText"></p> 2695 </div> 2696 </template> 2697 </div> 2698 </div> 2699 </div> 2700</div> 2701
2702<script> 2703 function initMirasvitSearch() { 2704 return { 2705 //initialize Variables 2706 showHotSearch: false, 2707 hasSearchResults: false, 2708 noResults: false, 2709 isLoading: false, 2710 isOpen: false, 2711 isContainerOpen: false, 2712 blockConfig: {"query":"","priceFormat":{"pattern":"$%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"minSearchLength":3,"url":"https:\/\/www.huskee.co\/searchautocomplete\/ajax\/suggest\/","storeId":"27","delay":100,"isAjaxCartButton":false,"isShowCartButton":false,"isShowImage":true,"isShowPrice":true,"isShowSku":true,"isShowRating":false,"isShowDescription":false,"isShowStockStatus":false,"isShowAdditionalAttributes":false,"isKeepLastSearchRequest":false,"layout":"1column","popularTitle":"Popular Suggestions","popularSearches":[],"isTypeaheadEnabled":true,"typeaheadUrl":"https:\/\/www.huskee.co\/searchautocomplete\/ajax\/typeahead\/","logUrl":"https:\/\/www.huskee.co\/searchautocomplete\/ajax\/log\/","minSuggestLength":2,"currency":"AUD","limit":null,"customerGroupId":0,"filterPosition":"","availableOrders":{"_score":"Relevance","price_0_24":"Price","created_at.sort_created_at":"Latest","arrangement.sort_arrangement":"Custom arrangement","latest":"Latest"},"defaultOrder":"_score","localStorageFields":{"prevRequestField":"mstLatestRequest","isRestorePrevRequestFlag":"mstIsRestorePrevRequest","isRefererAutocompleteFlag":"mstIsRefererAutocomplete"},"isHighlightingEnabled":true}, 2713 layoutFull: 0, 2714 layoutTwoColumns: 0, 2715 page: 1, 2716 xhttp: false, 2717 noResultsText: '', 2718 numberResults: '', 2719 numberResultsUrl: '', 2720 order: '_score', 2721 direction: 'desc', 2722 isActiveDirection: false, 2723 filterList: new Map(), 2724 buckets: [], 2725 collapses: [], 2726 priceFrom: -1, 2727 priceTo: -1, 2728 priceMin: -1, 2729 priceMax: -1, 2730 minThumb: 0, 2731 maxThumb: 0, 2732 step: 1, 2733 generalUrl: '', 2734 searchResults: {}, 2735 activeIndex: 'magento_catalog_product', 2736 highlightedParams: ['name', 'sku', 'description'], 2737 currentRequestUrl: '', 2738 availableOrders: '{"_score":"Relevance","price_0_24":"Price","created_at.sort_created_at":"Latest","arrangement.sort_arrangement":"Custom arrangement","latest":"Latest"}', 2739 2740 initShowPrevSearch() { 2741 this.$nextTick(showPrevSearch(this.$el)); 2742 }, 2743 2744 isLayoutFullOpen() { 2745 return this.layoutFull && this.isContainerOpen; 2746 }, 2747 2748 isProductIndexActive() { 2749 return this.activeIndex === 'magento_catalog_product'; 2750 }, 2751 2752 isNotLayoutFullOrProductActive() { 2753 return !this.layoutFull || this.activeIndex === 'magento_catalog_product'; 2754 }, 2755 2756 productIndexTitle() { 2757 if (this.searchResults['magento_catalog_product'] && this.searchResults['magento_catalog_product'].title) { 2758 return this.searchResults['magento_catalog_product'].title; 2759 } 2760 }, 2761 2762 productIndex() { 2763 return this.searchResults['magento_catalog_product']; 2764 }, 2765 2766 isNotLoadingAndNoResults() { 2767 return !this.isLoading && this.noResults; 2768 }, 2769 2770 productTotalItems() { 2771 return this.searchResults['magento_catalog_product'].totalItems; 2772 }, 2773 2774 productItems() { 2775 return this.searchResults['magento_catalog_product'].items; 2776 }, 2777 2778 isProductMisspell() { 2779 return this.searchResults['magento_catalog_product'].isMisspell; 2780 }, 2781 2782 productTextMisspell() { 2783 return this.searchResults['magento_catalog_product'].textMisspell; 2784 }, 2785 2786 productTextCorrected() { 2787 return this.searchResults['magento_catalog_product'].textCorrected; 2788 }, 2789 2790 resultsForOtherIndexType() { 2791 return !!this.searchResult.totalItems && ('magento_catalog_product' !== this.identifier); 2792 }, 2793 2794 itemUrl() { 2795 return `${this.item.url}`; 2796 }, 2797 2798 itemImageUrl() { 2799 // Check multiple possible property names for the image URL 2800 if (this.item.image_url) return this.item.image_url; 2801 if (this.item.imageUrl) return this.item.imageUrl; 2802 if (this.item.image) return this.item.image; 2803 if (this.item.thumbnail) return this.item.thumbnail; 2804 if (this.item.small_image) return this.item.small_image; 2805 2806 // Debug: log the item to see what properties are available 2807 console.log('Search item properties:', Object.keys(this.item)); 2808 2809 return ''; 2810 }, 2811 2812 itemName() { 2813 return `${this.item.name}`; 2814 }, 2815 2816 additionalAttributes() { 2817 return this.item.additionalAttributes; 2818 }, 2819 2820 itemPrice() { 2821 return this.item.price; 2822 }, 2823 2824 fiveStars() { 2825 return 5; 2826 }, 2827 2828 ratingStar() { 2829 return { 2830 brightColor: '#f6e05e', 2831 shadowColor: '#cbd5e0', 2832 star: `M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 2833 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 2834 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 2835 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z` 2836 }; 2837 }, 2838 2839 notEqualToRateRound() { 2840 return this.i !== this.item.rateRound; 2841 }, 2842 2843 equalToRateRound() { 2844 return this.i === this.item.rateRound; 2845 }, 2846 2847 partialFill() { 2848 return 'partialFill' + (undefined === this.item.sku_orig ? this.item.sku : this.item.sku_orig); 2849 }, 2850 2851 partialFillUrl() { 2852 return 'url(#partialFill' + (undefined === this.item.sku_orig ? this.item.sku : this.item.sku_orig) + ')'; 2853 }, 2854 2855 styleRateRound() { 2856 return this.i < this.item.rateRound ? 'color: ' + this.brightColor : 'color: ' + this.shadowColor; 2857 }, 2858 2859 stockStatusLabel() { 2860 return this.item.stockStatus === 2 ? 'In stock' : 'Out of stock'; 2861 }, 2862 2863 stockStatusClass() { 2864 return this.item.stockStatus === 2 ? 'text-green' : 'text-red'; 2865 }, 2866 2867 addToCartNotAllowed() { 2868 return {'opacity-50 cursor-not-allowed pointer-events-none' : this.item.stockStatus !== 2}; 2869 }, 2870 2871 mainColumnClass() { 2872 return { 2873 'sm:col-span-6' : (this.activeIndex === 'magento_catalog_product') 2874 }; 2875 }, 2876 2877 leftColumnClass() { 2878 return { 2879 'sm:col-span-2' : (this.activeIndex === 'magento_catalog_product') 2880 } 2881 }, 2882 2883 fullSizeClass() { 2884 return {'z-50 fixed top-0 left-0 w-full h-full lg:py-8 bg-gray-600/50' : this.isContainerOpen}; 2885 }, 2886 2887 cardClass() { 2888 return {'card' : this.isContainerOpen}; 2889 }, 2890 2891 fullSizeFormClass() { 2892 return {'flex justify-between' : this.isContainerOpen}; 2893 }, 2894 2895 fullSizeInputClass() { 2896 return {'border-0 outline-0 shadow-none ring-0 ring-offset-0 focus:shadow-none': this.isContainerOpen}; 2897 }, 2898 2899 fullSizeResultsClass() { 2900 return {'gap-4 lg:grid-cols-8' : this.isContainerOpen}; 2901 }, 2902 2903 activeIndexClass() { 2904 return { 'active bg-blue-600 text-white' : this.activeIndex === this.identifier }; 2905 }, 2906 2907 activeTotalsClass() { 2908 return this.activeIndex === this.identifier ? 'text-gray-50' : 'text-gray-500'; 2909 }, 2910 2911 identifierClass() { 2912 return `${this.identifier}`; 2913 }, 2914 2915 isActiveIndex() { 2916 return !!this.activeIndex; 2917 }, 2918 2919 isIdentifierActive() { 2920 return this.activeIndex === this.identifier; 2921 }, 2922 2923 resultTotalItems() { 2924 return !!this.searchResult.totalItems; 2925 }, 2926 2927 productIndexPages() { 2928 return this.searchResults['magento_catalog_product'].pages; 2929 }, 2930 2931 itemQueryText() { 2932 return ('magento_search_query' !== this.identifier) ? this.item.name.replaceAll(`<i>`, `<i class='mx-2'>`) : this.item.query_text; 2933 }, 2934 2935 pageActiveClass() { 2936 return {'active bg-blue-600 text-white': this.page.isActive }; 2937 }, 2938 2939 expandedBucketClass() { 2940 return { '_expanded': this.bucket.isExpanded }; 2941 }, 2942 2943 rotateExpandedClass() { 2944 return { 'rotate-180': this.bucket.isExpanded }; 2945 }, 2946 2947 isPriceBucket() { 2948 return this.bucket.code === 'price'; 2949 }, 2950 2951 isNotPriceBucket() { 2952 return this.bucket.code !== 'price'; 2953 }, 2954 2955 activeBucketItemClass() { 2956 return { '_active': this.item.isActive }; 2957 }, 2958 2959 selectBucketItem() { 2960 this.selectItem(this.bucket.code, this.item.key); 2961 }, 2962 2963 getAvailableOrders() { 2964 return JSON.parse(this.availableOrders); 2965 }, 2966 2967 isOrderSelected() { 2968 return this.order === this.key; 2969 }, 2970 2971 ascClass() { 2972 return {'hidden' : this.direction === 'asc'}; 2973 }, 2974 2975 descClass() { 2976 return {'hidden' : this.direction === 'desc'}; 2977 }, 2978 2979 doActivateSection() { 2980 let identifier = this.identifier; 2981 this.activateSection(identifier); 2982 }, 2983 2984 focusAfterOpen() { 2985 this.$el.focus() 2986 this.$el.select() 2987 }, 2988 2989 doSearchSuggest() { 2990 this.doSearch(this.$refs.searchInput.value) 2991 }, 2992 2993 hotSearchClickEvent: function (event) { 2994 const hotSearchClick = event.currentTarget.innerText; 2995 let searchElement = document.querySelector('#search'); 2996 searchElement.value = hotSearchClick; 2997 searchElement.click(); 2998 this.doSearch(hotSearchClick); 2999 searchElement.focus(); 3000 }, 3001 3002 preOpen: function () { 3003 if (this.layoutFull) { 3004 document.querySelector('body').classList.add('mstInPage'); 3005 } 3006 3007 this.isContainerOpen = true; 3008 }, 3009 3010 openSearch: function () { 3011 if (document.getElementById("search").value.length >= this.blockConfig.minSearchLength) { 3012 this.isOpen = true; 3013 this.showHotSearch = false;
3014 } else { 3015 this.showHotSearch = true; 3016 this.hasSearchResults = false; 3017 } 3018 }, 3019 3020 startSearch: function () { 3021 this.isOpen = !!this.$refs.searchInput; 3022 }, 3023 3024 closeSearch: function () { 3025 this.isOpen = false; 3026 3027 3028 if (this.layoutFull) { 3029 document.querySelector('body').classList.remove('mstInPage'); 3030 } 3031 3032 this.isContainerOpen = false; 3033 this.searchOpen = false; 3034 3035 //const event = new Event("search-open"); 3036 //window.dispatchEvent(event); 3037 }, 3038 3039 setNoResultsText(text) { 3040 if (!text) { 3041 return; 3042 } 3043 3044 this.noResultsText = text; 3045 }, 3046 3047 pageLabel() { 3048 return this.page.label; 3049 }, 3050 3051 goToPage: function () { 3052 this.page = this.page.label; 3053 this.doSearch(this.query); 3054 }, 3055 3056 // The doSearch() method must be called separately for compatibility with other modules 3057 setOrder() { 3058 this.order = this.$el.value; 3059 this.doSearch(this.query); 3060 }, 3061 3062 asc() { 3063 this.isActiveDirection = true; 3064 this.direction = 'asc'; 3065 this.doSearch(this.query); 3066 }, 3067 3068 desc() { 3069 this.isActiveDirection = true; 3070 this.direction = 'desc'; 3071 this.doSearch(this.query); 3072 }, 3073 3074 doSearch: function (query) { 3075 3076 // Min search length 3077 if (query.length < this.blockConfig.minSearchLength) { 3078 this.showHotSearch = true; 3079 this.isLoading = false; 3080 this.hasSearchResults = false; 3081 this.noResults = false; 3082 3083 return; 3084 } else { 3085 this.showHotSearch = false; 3086 this.hasSearchResults = true; 3087 this.noResults = false; 3088 } 3089 3090 if (this.query !== query) { 3091 this.query = query; 3092 this.filterList = new Map(); 3093 } 3094 3095 let self = this; 3096 3097 let url = this.blockConfig.url.replace(/\/$/, '') 3098 + '/?q=' + encodeURIComponent(this.query) 3099 + '&store_id=' + this.blockConfig.storeId 3100 + '&cat=false' 3101 + '&p=' + this.page 3102 + '&order=' + this.order 3103 + '&direction=' + this.direction 3104 + '&is_active_dir=' + this.isActiveDirection 3105 + '&buckets[0]=category_ids' 3106 + '¤cy=' + this.blockConfig.currency 3107 ; 3108 3109 3110 const generalUrl = url; 3111 3112 if (generalUrl === self.generalUrl) { 3113 this.isLoading = false; 3114 3115 return; 3116 } else { 3117 this.isLoading = true; 3118 url += '&index=' + this.activeIndex; 3119 } 3120 3121 // AJAX for searchautocomplete/ajax/suggest/ 3122 if (this.xhttp) { 3123 try { 3124 this.xhttp.abort(); 3125 } catch { 3126 3127 } 3128 } 3129 3130 this.currentRequestUrl = url; 3131 3132 this.xhttp = new XMLHttpRequest(); 3133 let xhttp = this.xhttp; 3134 3135 xhttp.open("GET", url, true); 3136 xhttp.setRequestHeader("Content-Type", "application/json"); 3137 3138 xhttp.onreadystatechange = function () { 3139 if (this.readyState === 4 && this.status === 200) { 3140 // Response from ajax call in JSON. 3141 const response = JSON.parse(this.responseText); 3142 3143 self.searchResults = {}; 3144 3145 for (const index in response.indexes) { 3146 if (response.indexes[index].items.length > 0) { 3147 const res = response.indexes[index]; 3148 3149 if ('magento_catalog_product' === res.identifier || 'magento_catalog_c
3149ategory' === res.identifier) { 3150 res.items.forEach(function (item, index) { 3151 self.highlightedParams.forEach(param => { 3152 res.items[index][param + '_orig'] = item[param]; 3153 res.items[index][param] = highlightMatch(item[param], query); 3154 }); 3155 }); 3156 } 3157 3158 self.searchResults[res.identifier] = res; 3159 } 3160 } 3161 3162 if ( 3163 !self.searchResults['magento_catalog_product'] 3164 && (Object.values(self.searchResults).length > 0) 3165 ) { 3166 self.activateSection(Object.keys(self.searchResults)[0]); 3167 } 3168 3169 if (self.searchResults['magento_catalog_product'] 3170 ) { 3171 self.searchResults['magento_catalog_product'].items.forEach((item, key) => { 3172 if (item.rating) { 3173 let rate = item.rating/20; 3174 let fragment = (rate - Math.floor(rate))*100; 3175 3176 if (fragment <= 0) { 3177 fragment = 100 3178 } 3179 3180 self.searchResults['magento_catalog_product'].items[key].rateRound 3181 = (Math.ceil(rate)); 3182 3183 self.searchResults['magento_catalog_product'].items[key].fragment 3184 = fragment + '%'; 3185 } 3186 }); 3187 3188 const sorter = self.searchResults['magento_catalog_product'].sorter; 3189 if (!!sorter && sorter.direction !== self.direction) { 3190 self.direction = sorter.direction; 3191 } 3192 } 3193 3194 self.setBuckets(self.searchResults, self.activeIndex); 3195 3196 self.isLoading = false; 3197 self.noResults = false; 3198 self.numberResults = response.textAll; 3199 self.numberResultsUrl = response.urlAll; 3200 self.generalUrl = generalUrl; 3201 3202 if (response.noResults) { 3203 self.setNoResultsText(response.textEmpty); 3204 self.hasSearchResults = false; 3205 self.noResults = true; 3206 } 3207 3208 self.saveRequest(); 3209 3210 self.isActiveDirection = false; 3211 } 3212 }; 3213 xhttp.send(); 3214 }, 3215 3216 saveRequest() { 3217 let searchRequestUrl = this.currentRequestUrl; 3218 let config = this.blockConfig; 3219 3220 let selectedFilters = {}; 3221 3222 for (const bucketCode in this.buckets) { 3223 let currentBucketCode = this.buckets[bucketCode].code;
3224 let currentBucketFilterList = this.filterList.get(currentBucketCode); 3225 3226 if (!currentBucketFilterList) { 3227 continue; 3228 } 3229 3230 let bucketSelectedFilters = []; 3231 currentBucketFilterList.forEach((categoryId) => { 3232 bucketSelectedFilters.push(categoryId); 3233 }) 3234 3235 selectedFilters[currentBucketCode] = bucketSelectedFilters; 3236 } 3237 3238 let requestData = { 3239 requestUrl: searchRequestUrl, 3240 filtersData: selectedFilters, 3241 activeIndex: this.activeIndex 3242 }; 3243 3244 let requestDataJson = JSON.stringify(requestData) 3245 3246 if (this.blockConfig.isKeepLastSearchRequest) { 3247 localStorage.setItem(config.localStorageFields.prevRequestField, requestDataJson); 3248 } 3249 }, 3250 3251 setBuckets(indexes, indexIdentifier) { 3252 this.buckets = []; 3253 3254 for (const key in indexes) { 3255 if (key !== indexIdentifier) { 3256 continue; 3257 } 3258 3259 const idx = indexes[key]; 3260 3261 for (const bucketKey in idx.buckets) { 3262 const bucket = idx.buckets[bucketKey] 3263 let bucketItems = []; 3264 3265 if (bucket.code === 'price') { 3266 this.priceMin = this.priceMin === -1 || this.priceMin > bucket.min ? bucket.min : this.priceMin; 3267 this.priceMax = this.priceMax === -1 || this.priceMax < bucket.max ? bucket.max : this.priceMax; 3268 this.priceFrom = this.priceFrom === -1 ? this.priceMin : this.priceFrom; 3269 this.priceTo = this.priceTo === -1 ? this.priceMax : this.priceTo; 3270 this.minPriceTrigger(); 3271 this.maxPriceTrigger(); 3272 } 3273 3274 for (const itemKey in bucket.items) { 3275 const item = bucket.items[itemKey]; 3276 3277 let isActive = this.filterList.get(bucket.code)?.includes(item.key) || false; 3278 3279 bucketItems.push({...item, isActive, select: () => this.selectItem(bucket.code, item.key)}); 3280 } 3281 3282 if (bucketItems.length > 0 || bucket.code === 'price') { 3283 this.buckets.push({ 3284 ...bucket, 3285 items: bucketItems, 3286 isExpanded: !this.collapses.includes(bucket.code), 3287 expand: () => this.toggleCollapse(bucket.code) 3288 }); 3289 } 3290 } 3291 } 3292 }, 3293 3294 selectItem(bucketCode, key, triggerSearch = true) { 3295 let filterList = this.filterList; 3296 let filterItems = filterList.get(bucketCode) || []; 3297 3298 if (bucketCode === 'price') { 3299 filterList.set(bucketCode, [key]); 3300 } else { 3301 let index = filterItems.indexOf(key); 3302 3303 if (index >= 0) { 3304 filterItems.splice(index, 1); 3305 filterItems.length > 0 ? filterList.set(bucketCode, filterItems) : filterList.delete(bucketCode); 3306 } else { 3307 filterItems.push(key); 3308 filterList.set(bucketCode, filterItems); 3309 } 3310 } 3311 3312 this.filterList = new Map(filterList); 3313 3314 if (triggerSearch) { 3315 this.doSearch(this.query); 3316 } 3317 }, 3318 3319 processItemClick() { 3320 localStorage.setItem(this.blockConfig.localStorageFields.isRestorePrevRequestFlag, "1") 3321 localStorage.setItem(this.blockConfig.localStorageFields.isRefererAutocompleteFlag, "1") 3322 window.location.href = this.item.url; 3323 }, 3324 3325 toggleCollapse(code) { 3326 3327 if (this.collapses.includes(code)) { 3328 this.collapses = this.collapses.filter(c => c !== code); 3329 } else { 3330 this.collapses.push(code); 3331 } 3332 3333 this.setBuckets(this.searchResults, this.activeIndex); 3334 }, 3335 3336 minPriceTrigger() { 3337 if (!this.isNumeric(this.priceFrom) || (this.priceFrom < this.priceMin)) { 3338 this.priceFrom = this.priceMin; 3339 } 3340 this.priceFrom = Math.min(this.priceFrom, this.priceTo - this.step); 3341 this.minThumb = ((this.priceFrom - this.priceMin) / (this.priceMax - this.priceMin)) * 100; 3342 }, 3343 3344 maxPriceTrigger() { 3345 if (!this.isNumeric(this.priceTo) || (this.priceTo > this.priceMax)) { 3346 this.priceTo = this.priceMax; 3347 } 3348 this.priceTo = Math.max(this.priceTo, this.priceFrom + this.step); 3349 this.maxThumb = 100 - (((this.priceTo - this.priceMin) / (this.priceMax - this.priceMin)) * 100); 3350 }, 3351 3352 isNumeric(obj) { 3353 return !isNaN(parseFloat(obj)) && isFinite(obj); 3354 }, 3355 3356 activateSection(identifier) { 3357 if (identifier !== this.activeIndex) { 3358 this.activeIndex = identifier; 3359 this.goToPage(1); 3360 } 3361 this.saveRequest() 3362 }, 3363 3364 processAddToCart() { 3365 let addToCartElement = this.$el; 3366 let linkToCart = this.item.addToCartUrl; 3367 3368 if (this.blockConfig.isAjaxCartButton) { 3369 fetch(linkToCart + '?isAjax=1' + '&form_key='
3369+ hyva.getFormKey(), { 3370 method: 'POST', 3371 headers: { 3372 'Accept': 'application/json', 3373 'Content-Type': 'application/json' 3374 } 3375 }) 3376 .then(response => response.json()) 3377 .then(data => { 3378 let messageElement = document.createElement('div'); 3379 messageElement.className = `to_cart_message message ${data.success ? 'success' : 'error'} text-sm p-2`; 3380 messageElement.innerHTML = data.message; 3381 3382 addToCartElement.closest('.mst__product-meta').prepend(messageElement); 3383 3384 /** 3385 * Reload Section data 3386 */ 3387 hyva.setCookie('mage-cache-sessid', '', -1, true); 3388 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 3389 3390 setTimeout(() => { 3391 let messageElement = addToCartElement.closest('.mst__product-meta').querySelector('.to_cart_message'); 3392 if (messageElement) { 3393 messageElement.remove(); 3394 } 3395 }, 5000); 3396 }) 3397 .catch(error => { 3398 console.error('Error:', error); 3399 }); 3400 } else { 3401 addToCartElement.setAttribute('href', linkToCart); 3402 addToCartElement.click(); 3403 } 3404 3405 return false; 3406 } 3407 } 3408 } 3409 3410 window.addEventListener('alpine:init', () => Alpine.data('initMirasvitSearch', initMirasvitSearch), {once: true}); 3411 3412 function showPrevSearch() { 3413 let mirasvitSearch = Alpine.$data(document.getElementById('search')); 3414 let config = mirasvitSearch.blockConfig; 3415 let localStorageFields = mirasvitSearch.blockConfig.localStorageFields; 3416 let prevRequestFlag = !!(localStorage.getItem(localStorageFields.isRestorePrevRequestFlag)); 3417 let prevRequest = localStorage.getItem(localStorageFields.prevRequestField); 3418 3419 if (!prevRequest) { 3420 return; 3421 } 3422 3423 prevRequest = JSON.parse(prevRequest); 3424 3425 function delay(ms) { 3426 return new Promise(resolve => setTimeout(resolve, ms)); 3427 } 3428 3429 async function triggerEvents() { 3430 await delay(20); 3431 window.dispatchEvent(new Event('search-open')); 3432 3433 await delay(100); 3434 window.dispatchEvent(new Event('search-open')); 3435 3436 await delay(150); 3437 window.dispatchEvent(new Event('search-open')); 3438 3439 await delay(300); 3440 window.dispatchEvent(new Event('search-open')); 3441 } 3442 3443 if (config.isKeepLastSearchRequest 3444 && window.performance 3445 && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD 3446 && prevRequestFlag) { 3447
3448 let prevRequestParams = new URL(prevRequest.requestUrl).searchParams; 3449 let prevRequestFiltersData = prevRequest.filtersData; 3450 3451 /** 3452 * Search init, pass the query into the input, select active index 3453 * 3454 * @type {string} 3455 */ 3456 mirasvitSearch.$refs.searchInput.value = prevRequestParams.get('q'); 3457 3458 /** 3459 * Passing filters 3460 */ 3461 Object.entries(prevRequestFiltersData).forEach((filter) => { 3462 filter[1].forEach((key) => { 3463 mirasvitSearch.selectItem(filter[0], key, false); 3464 }) 3465 }); 3466 3467 3468 /** 3469 * Passing the rest of data 3470 */ 3471 mirasvitSearch.query = prevRequestParams.get('q'); 3472 mirasvitSearch.page = prevRequestParams.get('p'); 3473 mirasvitSearch.order = prevRequestParams.get('order'); 3474 mirasvitSearch.direction = prevRequestParams.get('direction'); 3475 3476 /** 3477 * Make the search to be open 3478 */ 3479 mirasvitSearch.searchOpen = true; 3480 3481 /** 3482 * Trigger required for search events 3483 */ 3484 triggerEvents(); 3485 3486 if (!prevRequest.activeIndex) { 3487 return; 3488 } 3489 mirasvitSearch.activateSection(prevRequest.activeIndex); 3490 } 3491 } 3492 3493 function restrictPrevSearch () { 3494 if (window.performance 3495 && (!(window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD)) 3496 ) { 3497 let localStorageFields = {"prevRequestField":"mstLatestRequest","isRestorePrevRequestFlag":"mstIsRestorePrevRequest","isRefererAutocompleteFlag":"mstIsRefererAutocomplete"}; 3498 3499 if (localStorage.getItem(localStorageFields.isRefererAutocompleteFlag)) { 3500 localStorage.setItem(localStorageFields.isRefererAutocompleteFlag, ''); 3501 return; 3502 } 3503 3504 if (!(localStorage.getItem(localStorageFields.isRefererAutocompleteFlag))) { 3505 localStorage.setItem(localStorageFields.prevRequestField, ''); 3506 localStorage.setItem(localStorageFields.isRestorePrevRequestFlag, '') 3507 } 3508 } 3509 } 3510 3511 restrictPrevSearch(); 3512</script>
3512 3513
3514<script> 3515 function highlightMatch(html, query) { 3516 if (!html) { 3517 return html; 3518 } 3519 3520 const cssClass = '' 3521 let arQuery = query.split(' '); 3522 let arSpecialChars = [ 3523 {'key': 'a', 'value': '(à |â|Ä |a)'}, 3524 {'key': 'c', 'value': '(ç|Ä|c)'}, 3525 {'key': 'e', 'value': '(è|é|Ä|ê|ë|Ä|e)'}, 3526 {'key': 'i', 'value': '(î|ï|į|i)'}, 3527 {'key': 'o', 'value': '(ô|o)'}, 3528 {'key': 's', 'value': '(Å¡|s)'}, 3529 {'key': 'u', 'value': '(ù|ü|û|Å«|ų|u)'} 3530 ]; 3531 3532 arQuery.forEach(function (word) { 3533 word = word.trim(); 3534 3535 if (word) { 3536 word = word.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); 3537 arSpecialChars.forEach(function (match) { 3538 word = word.replace(new RegExp(match.key, 'g'), match.value); 3539 }); 3540 3541 if ("span".indexOf(word.toLowerCase()) === -1) { 3542 html = html.replace(new RegExp('(' + word + '(?![^<>]*>))', 'ig'), function ($1, match) { 3543 return '<span class="' + cssClass + '">' + match + '</span>'; 3544 }); 3545 } 3546 } 3547 }); 3548 3549 return html; 3550 } 3551</script>
3551 3552 3553<style> 3554 input[type=range]::-webkit-slider-thumb { 3555 pointer-events: all; 3556 width: 24px; 3557 height: 24px; 3558 -webkit-appearance: none; 3559 /* @apply w-6 h-6 appearance-none pointer-events-auto; */ 3560 } 3561 3562 body.mstInPage { 3563 position: absolute; 3564 overflow-y: hidden; 3565 width: 100%; 3566 height: 100%; 3567 box-sizing: border-box; 3568 } 3569 3570 body.mstInPage #search { 3571 --tw-ring-shadow: none; 3572 } 3573 3574 body.mstInPage #search::-webkit-search-cancel-button, 3575 body.mstInPage #search::-webkit-search-decoration { 3576 -webkit-appearance: none; 3577 appearance: none; 3578 } 3579 3580 body.mstInPage #search-content { 3581 z-index: 1000; 3582 } 3583</style> 3584 </div> 3585 3586 <!--Cart Drawer--> 3587
3587<script> 3588 function initCartDrawer() { 3589 return { 3590 open: false, 3591 isLoading: false, 3592 cart: {}, 3593 maxItemsToDisplay: 10, 3594 itemsCount: 0, 3595 getData(data) { 3596 if (data.cart) { 3597 this.cart = data.cart; 3598 this.itemsCount = data.cart.items && data.cart.items.length || 0; 3599 this.setCartItems(); 3600 } 3601 this.isLoading = false; 3602 }, 3603 cartItems: [], 3604 getItemCountTitle() { 3605 return hyva.strf('\u00250\u0020of\u0020\u00251\u0020products\u0020in\u0020cart\u0020displayed', this.maxItemsToDisplay, this.itemsCount) 3606 }, 3607 setCartItems() { 3608 this.cartItems = this.cart.items && this.cart.items.sort((a, b) => b.item_id - a.item_id) || []; 3609 3610 if (this.maxItemsToDisplay > 0) { 3611 this.cartItems = this.cartItems.slice(0, parseInt(this.maxItemsToDisplay, 10)); 3612 } 3613 }, 3614 deleteItemFromCart(itemId) { 3615 this.isLoading = true; 3616 3617 const itemData = this.cart.items.filter((item) => item['item_id'] === itemId); 3618 const formKey = hyva.getFormKey(); 3619 const postUrl = BASE_URL + 'checkout/sidebar/removeItem/'; 3620 3621 fetch(postUrl, { 3622 "headers": { 3623 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 3624 }, 3625 "body": "form_key=" + formKey + "&item_id=" + itemId, 3626 "method": "POST", 3627 "mode": "cors", 3628 "credentials": "include" 3629 }).then(response => { 3630 if (response.redirected) { 3631 window.location.href = response.url; 3632 } else if (response.ok) { 3633 return response.json(); 3634 } else { 3635 window.dispatchMessages && window.dispatchMessages([{ 3636 type: 'warning', 3637 text: 'Could\u0020not\u0020remove\u0020item\u0020from\u0020quote.' 3638 }]); 3639 this.isLoading = false; 3640 } 3641 }).then(result => { 3642 window.dispatchMessages && window.dispatchMessages([{ 3643 type: result.success ? 'success' : 'error', 3644 text: result.success 3645 ? 'You\u0020removed\u0020the\u0020item.' 3646 : result.error_message 3647 }], result.success ? 5000 : 0); 3648 3649 if (result.success && itemData) { 3650 window.dispatchEvent(new CustomEvent('cart-item-removed', { detail: itemData })); 3651 } 3652 3653 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 3654 }); 3655 }, 3656 scrollLock(use = true) { 3657 document.body.style.overflow = use ? "hidden" : ""; 3658 }, 3659 toggleCartDrawer(event) { 3660 if (event.detail && event.detail.isOpen !== undefined) { 3661 if (event.detail.isOpen) { 3662 this.openCartDrawer(); 3663 } else { 3664 this.open = false; 3665 this.scrollLock(false); 3666 this.$refs && this.$refs.cartDialogContent && hyva.releaseFocus(this.$refs.cartDialogContent); 3667 } 3668 } else { 3669 this.openCartDrawer() 3670 } 3671 }, 3672 openCartDrawer() { 3673 this.open = true; 3674 this.scrollLock(true); 3675 this.$nextTick(() => { 3676 this.$refs && this.$refs.cartDialogContent && hyva.trapFocus(this.$refs.cartDialogContent) 3677 }) 3678 }, 3679 closeCartDrawer() { 3680 this.$dispatch('toggle-cart', { isOpen: false }) 3681 }, 3682 getSectionDataExtraActions() { 3683 if (!this.cart.extra_actions) { 3684 return ''; 3685 } 3686 3687 const contentNode = document.createElement('div'); 3688 contentNode.innerHTML = this.cart.extra_actions; 3689 3690 hyva.activateScripts(contentNode); 3691 3692 return contentNode.innerHTML; 3693 } 3694 } 3695 } 3696</script>
3696 3697<section x-cloak 3698 x-show="cart" 3699 id="cart-drawer" 3700 x-data="initCartDrawer()" 3701 @private-content-loaded.window="getData($event.detail.data)" 3702 @toggle-cart.window="toggleCartDrawer($event)" 3703 @keydown.escape="closeCartDrawer" 3704> 3705 <div role="dialog" 3706 aria-labelledby="cart-drawer-title" 3707 aria-modal="true" 3708 :aria-hidden="!open" 3709 class="fixed inset-y-0 right-0 z-30 flex max-w-full"> 3710 <div class="backdrop" 3711 x-show="open" 3712 x-transition:enter="ease-in-out duration-500" 3713 x-transition:enter-start="opacity-0" 3714 x-transition:enter-end="opacity-100" 3715 x-transition:leave="ease-in-out duration-500" 3716 x-transition:leave-start="opacity-100" 3717 x-transition:leave-end="opacity-0" 3718 role="button" 3719 @click="closeCartDrawer" 3720 aria-label="Close minicart"></div> 3721 <div class="relative w-screen max-w-md shadow-2xl" 3722 x-show="open" 3723 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" 3724 x-transition:enter-start="translate-x-full" 3725 x-transition:enter-end="translate-x-0" 3726 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" 3727 x-transition:leave-start="translate-x-0" 3728 x-transition:leave-end="translate-x-full" 3729 x-ref="cartDialogContent" 3730 role="region" 3731 :tabindex="open ? 0 : -1" 3732 aria-label="My Cart" 3733 3734 > 3735 <div class="flex flex-col h-full max-h-screen bg-white text-black shadow-xl"> 3736 3737 <header class="relative p-4"> 3738 <p id="cart-drawer-title" class="text-sm font-medium leading-7 text-gray-900"> 3739 <strong>My Cart</strong> 3740 <span class="items-total text-xs" 3741 x-show="maxItemsToDisplay && maxItemsToDisplay < itemsCount" 3742 x-text="getItemCountTitle()"> 3743 </span> 3744 </p> 3745 </header> 3746 3747 3748 <template x-if="!itemsCount"> 3749 <div class="relative px-4 py-4 bg-white border-bs sm:px-4 border-container"> 3750 Cart is empty </div> 3751 </template> 3752 3753 <template x-if="itemsCount"> 3754 <div class="relative grid gap-0 sm:gap-0 px-1 py-0 pb-8 sm:px-3 bg-white border-b border-container overflow-y-auto overscroll-y-contain"> 3755 <template x-for="item in cartItems"> 3756 <div class="flex items-start py-3 px-1 space-x-4 transition duration-150 ease-in-out"> 3757 <template x-if="item.product_has_url && item.is_visible_in_site_visibility"> 3758 <a :href="item.product_url" 3759 class="w-1/4 !ml-0" 3760 :aria-label="hyva.strf('Product\u0020\u0022\u00250\u0022', item.product_name)" 3761 > 3762 <img 3763 :src="item.product_image.src" 3764 :width="item.product_image.width" 3765 :height="item.product_image.height" 3766 loading="lazy" 3767 :alt="item.product_name" 3768 /> 3769 </a> 3770 </template> 3771 <template x-if="!item.product_has_url || !item.is_visible_in_site_visibility"> 3772 <div class="w-1/4 !ml-0"> 3773 <img 3774 :src="item.product_image.src" 3775 :width="item.product_image.width" 3776 :height="item.product_image.height" 3777 loading="lazy" 3778 :alt="item.product_name" 3779 /> 3780 </div> 3781 </template> 3782 <div class="w-3/4 space-y-2"> 3783 <div> 3784 <p class="text-sm font-poppins">
3785 <span x-html="item.qty"></span> x <span x-html="item.product_name"></span> 3786 </p> 3787 <!-- <p class="text-sm"><span x-html="item.product_sku"></span></p> --> 3788 </div> 3789 <template x-for="option in item.options"> 3790 <div class="pt-2"> 3791 <p class="font-semibold" x-text="option.label + ':'"></p> 3792 <p class="text-secondary" x-html="option.value"></p> 3793 </div> 3794 </template> 3795 <p><span x-html="item.product_price" class="font-poppins font-regular"></span></p> 3796 <div class="pt-2"> 3797 <a :href="item.configure_url" 3798 x-show="item.product_type !== 'grouped' && item.is_visible_in_site_visibility" 3799 class="inline-flex mr-2" 3800 :aria-label="hyva.strf('Edit\u0020product\u0020\u0022\u00250\u0022', item.product_name)" 3801 > 3802 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" aria-hidden="true"> 3803 <path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/> 3804</svg> 3805 </a> 3806 <button type="button" 3807 class="inline-flex" 3808 @click="deleteItemFromCart(item.item_id)" 3809 :aria-label="hyva.strf('Remove\u0020product\u0020\u0022\u00250\u0022\u0020from\u0020cart', item.product_name)" 3810 > 3811 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" aria-hidden="true"> 3812 <path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/> 3813</svg> 3814 </button> 3815 </div> 3816 3817 </div> 3818 </div> 3819 </template> 3820 </div> 3821 </template> 3822 3823 <div x-show="itemsCount" x-cloak> 3824 3825 <div class="fixed bottom-0 w-full grid gap-2 sm:gap-2 py-3 px-1 sm:px-3 bg-white"> 3826 <div class="w-full px-3 space-x-4 transition duration-150 ease-in-out"> 3827 <p class="font-poppins">Subtotal : <span x-html="cart.subtotal"></span></p> 3828 </div> 3829 <div class="w-full px-3 space-x-4 transition duration-150 ease-in-out flex items-center"> 3830 <a @click="closeCartDrawer; $dispatch('toggle-authentication', 3831 {url: 'https://www.huskee.co/checkout/'});" 3832 href="https://www.huskee.co/checkout/" 3833 class="inline-flex btn btn-primary" 3834 > 3835 Checkout </a> 3836 <span>or</span> 3837 <a href="https://www.huskee.co/checkout/cart/" 3838 class="btn btn-secondary" 3839 > 3840 View Cart </a> 3841 </div>
3842 <div x-html="getSectionDataExtraActions()"></div> 3843 <div class="stripe-minicart-button-container min-h-a11y"> 3844 3845 <div x-data="initStripePaymentRequestButtonMinicart" 3846 id="stripe-minicart-button-wrapper" 3847 @toggle-cart.window="initMinicartButton" 3848 class="relative grid gap-6 px-4 bg-white sm:gap-8 sm:px-6 min-h-a11y !mb-0 !mr-0" 3849 > 3850 <div id="stripe-minicart-button"></div> 3851 <div 3852 class="z-50 fixed inset-0 flex justify-center items-center bg-white/70 select-none" 3853 x-show="isLoading" 3854 x-cloak 3855 x-transition.opacity.duration.200ms 3856> 3857 <div class="flex items-center"> 3858 <svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" class="animate-spin" aria-hidden="true"> 3859 <circle style="opacity: .3;" cx="12" cy="12" r="10"/> 3860 <path fill="none" stroke-width="2" d="M12,2 a10,10 0 0,1 10,10"/> 3861</svg> 3862 <div class="ml-10 text-xl"> 3863 Loading... </div> 3864 </div> 3865</div> 3866 </div> 3867</div> 3868 </div> 3869 </div> 3870 3871 </div> 3872 3873 <button 3874 type="button" 3875 @click="closeCartDrawer" 3876 aria-label="Close minicart" 3877 class="absolute top-0 right-2 p-2 mt-0 text-gray-500 transition-colors hover:text-black" 3878 > 3879 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 3880 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 3881</svg> 3882 </button> 3883 </div> 3884 <div 3885 class="z-50 fixed inset-0 flex justify-center items-center bg-white/70 select-none" 3886 x-show="isLoading" 3887 x-cloak 3888 x-transition.opacity.duration.200ms 3889> 3890 <div class="flex items-center"> 3891 <svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" class="animate-spin" aria-hidden="true"> 3892 <circle style="opacity: .3;" cx="12" cy="12" r="10"/> 3893 <path fill="none" stroke-width="2" d="M12,2 a10,10 0 0,1 10,10"/> 3894</svg> 3895 <div class="ml-10 text-xl"> 3896 Loading... </div> 3897 </div> 3898</div> 3899 </div> 3900 3901</section> 3902 3903 <!--Authentication Pop-Up--> 3904 </div> 3905 <!--Customer Icon & Dropdown--> 3906<!-- <a href="/giftcertificates.php" class="text-sm hover:underline header-anchor md:hidden lg:grid">--> 3907<!-- --><!-- </a>--> 3908 <a href="https://www.huskee.co/customer/account/login/" 3909 class="text-sm hover:underline header-anchor md:hidden lg:grid"> 3910 Sign In </a> 3911 3912 <!--Cart Icon--> 3913 <button class="flex gap-1 px-3 cart-icon lg:flex hidden" id="menu-cart-icon" 3914 class="relative flex" x-ref="cartButton" :aria-disabled="isCartEmpty()" :aria-label="` 3915 Toggle minicart, 3916 ${isCartEmpty() 3917 ? 'Cart is empty' 3918 : cart.summary_count > 1 3919 ? hyva.str('%1 items', cart.summary_count) 3920 : hyva.str('%1 item', cart.summary_count) 3921 }`" @click.prevent="() => { 3922 $dispatch('toggle-cart', { isOpen: true }) 3923 }" @toggle-cart.window="toggleCart($event)" :aria-expanded="isCartOpen" aria-haspopup="dialog" 3924 > 3925 <svg xmlns="http://www.w3.org/2000/svg" fill="#000000" width="20px" height="20px" viewBox="-1.5 0 19 19" 3926 class="cf-icon-svg"> 3927 <path 3928 d="M14.252 4.59a.924.924 0 0 1 .921.92v3.602a1.048 1.048 0 0 1-.916 1.017l-8.511.883a.573.573 0 0 1-.145.019.577.577 0 1 0 0 1.154h8.488a.563.563 0 1 1 0 1.126h-.91a1.03 1.03 0 1 1-1.104 0H6.849a1.03 1.03 0 1 1-1.104 0H5.6a1.703 1.703 0 1 1 0-3.406.585.585 0 0 1 .128.014L3.111 3.911H1.39a.563.563 0 1 1 0-1.125h2.09a.562.562 0 0 1 .515.337l.64 1.466h9.617z" /> 3929 </svg>
3930 <span class="hidden md:block">Cart</span> 3931 <span x-text="isCartEmpty() ? 0 : cart.summary_count" class=" -top-0 -right-8 h-5 w-5 px-1 py-1 rounded-full bg-black text-white 3932 text-xs font-semibold leading-none text-center uppercase tabular-nums">0</span> 3933 </button> 3934 3935 3936 <div class="switcher website switcher-website" 3937 id="switcher-website" 3938 x-data="websiteSwitcher" 3939 @click.away="open = false"> 3940 <strong class="switcher-label"></strong> 3941 <div class="actions dropdown options switcher-options"> 3942 <div class="action toggle switcher-trigger cursor-pointer" 3943 role="button" 3944 tabindex="0" 3945 @click="open = !open" 3946 @keydown.enter="open = !open" 3947 @keydown.space.prevent="open = !open" 3948 id="switcher-store-trigger"> 3949 <strong class="flex items-center"> 3950 <span class="website-item flex items-center"> 3951 <span class="website-item-flag"> 3952 <img src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/YelHex_WebsiteSwitcherGroups/images/flag_au.png" alt="AU" /> 3953 </span> 3954 <span class="website-item-name">AU</span> 3955 </span> 3956 <svg class="ml-2 w-4 h-4 transition-transform duration-200" 3957 :class="{ 'rotate-180': open }" 3958 xmlns="http://www.w3.org/2000/svg" 3959 fill="none"
3960 viewBox="0 0 24 24" 3961 stroke="currentColor"> 3962 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> 3963 </svg> 3964 </strong> 3965 </div> 3966 <ul class="dropdown switcher-dropdown" 3967 x-show="open" 3968 x-cloak 3969 x-transition 3970 @click="open = false"> 3971 <li class="switcher-option"> 3972 <a class="website-item" href="https://www.huskee.co/"> 3973 <span class="website-item-flag"> 3974 <img src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/YelHex_WebsiteSwitcherGroups/images/flag_au.png" alt="AU" /> 3975 </span> 3976 <span class="website-item-name">AU</span> 3977 </a> 3978 </li> 3979 <li class="switcher-option"> 3980 <a class="website-item" href="https://www.huskee.co/uk/"> 3981 <span class="website-item-flag"> 3982 <img src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/YelHex_WebsiteSwitcherGroups/images/flag_uk.png" alt="UK" /> 3983 </span> 3984 <span class="website-item-name">UK</span> 3985 </a> 3986 </li> 3987 <li class="switcher-option"> 3988 <a class="website-item" href="https://www.huskee.co/us/"> 3989 <span class="website-item-flag"> 3990 <img src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/YelHex_WebsiteSwitcherGroups/images/flag_us.png" alt="US" /> 3991 </span> 3992 <span class="website-item-name">US</span> 3993 </a> 3994 </li> 3995 </ul> 3996 </div> 3997 </div> 3998 3999
3999<script> 4000 function websiteSwitcher() { 4001 return { 4002 open: false 4003 } 4004 } 4005 window.addEventListener('alpine:init', () => Alpine.data('websiteSwitcher', websiteSwitcher), {once: true}) 4006 </script>
4006 4007 </div> 4008</div> 4009 4010 4011<div x-data="{ shrink: false }"> 4012 4013 <div class="logo-search hidden md:hidden lg:grid"> 4014 <div class="logo hidden md:block"> 4015 <div class="order-1 sm:order-2 lg:order-1 w-full pb-2 sm:w-auto sm:pb-0"> 4016 <a 4017 class="flex items-center justify-center text-xl font-medium tracking-wide text-gray-800 4018 no-underline hover:no-underline font-title" 4019 href="https://www.huskee.co/" 4020 aria-label="Go to Home page" 4021 > 4022 <img 4023 src="https://www.huskee.co/media/logo/websites/24/huskee_logo.png" 4024 alt="Huskee" 4025 width="250" height="49" loading="eager" 4026 /> 4027 </a> 4028</div> 4029 </div> 4030 <div class="header-search"> 4031 4032 4033<div class="search-content-wrapper" 4034 x-data="initMirasvitSearch" 4035 @click.away="closeSearch" 4036 x-init="initShowPrevSearch" 4037> 4038 <div class="mx-auto text-black lg:p-0 xs:p-2" 4039 @click.away="closeSearch"> 4040 <div class="relative"> 4041 <form class="form minisearch relative" 4042 id="search_mini_form" 4043 action="https://www.huskee.co/catalogsearch/result/" 4044 method="get" 4045 autocomplete="off"> 4046 <label class="hidden" for="search" data-role="minisearch-label"> 4047 <span>Search</span> 4048 </label> 4049 <template x-if="isLayoutFullOpen"> 4050 <span class="text-gray-400 left-0 p-3"> 4051 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="text-gray-400" width="40" height="40" role="img"> 4052 <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/> 4053<title>search</title></svg> 4054 </span> 4055 </template> 4056 4057 <input id="search" x-ref="searchInput" type="search" 4058 name="q" 4059 value="" 4060 placeholder="Search Huskee" 4061 maxlength="128" 4062 class="w-full p-2 text-lg leading-normal transition appearance-none text-gray-800 focus:outline-none focus:border-transparent lg:text-xl" 4063 x-on:click="preOpen" 4064 x-on:search-open.window="preOpen" 4065 x-on:search-open.window.debounce.20="focusAfterOpen" 4066 x-on:search-open.window.debounce.100="startSearch" 4067 x-on:search-open.window.debounce.150="openSearch" 4068 x-on:search-open.window.debounce.300="doSearchSuggest" 4069 x-on:keydown.debounce.100="startSearch" 4070 x-on:click.debounce.150="openSearch" 4071 x-on:click.debounce.300="doSearchSuggest" 4072 x-on:keyup.debounce.300="doSearchSuggest"/> 4073 4074 <template x-if="isLayoutFullOpen"> 4075 <div class="right-0 top-0 p-3 flex"> 4076 <div class="mstInPage__indexList items-center hidden lg:flex text-base lg:text-lg"> 4077 <div> 4078 <a class="mstInPage__viewAll whitespace-nowrap mr-10 text-blue-500 no-underline" 4079 x-show="productIndex" 4080 :href="numberResultsUrl"> 4081 View all results â </a> 4082 </div> 4083 4084 <div class="flex"> 4085 <template x-for="(searchResult, identifier) in searchResults"> 4086 <template x-if="resultTotalItems"> 4087 <div 4088 class="mstInPage__index flex h-10 mr-1.5 px-2 pt-2 lg:pt-1 text-center align-middle inline-block rounded border border-transparent cursor-pointer whitespace-nowrap" 4089 @click="doActivateSection" 4090 :class="activeIndexClass">
4091 <span x-text="searchResult.title"></span> 4092 <small class="ml-1" 4093 :class="activeTotalsClass"> 4094 (<span x-html="searchResult.totalItems"></span>) 4095 </small> 4096 </div> 4097 </template> 4098 </template> 4099 </div> 4100 </div> 4101 <span @click="closeSearch" class="cursor-pointer"> 4102 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="text-gray-400" width="40" height="40" role="img"> 4103 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 4104<title>x</title></svg> 4105 </span> 4106 </div> 4107 </template> 4108 4109 </form> 4110 4111 4112 <div x-cloak 4113 id="mirasvitSearchResults" 4114 class="border-solid w-full mt-1 bg-white text-lg leading-normal transition appearance-none text-gray-800 lg:text-xl z-10 my-1 absolute right-0 lg:min-w-[400px]" 4115 x-show="isOpen"> 4116 <template x-if="hasSearchResults"> 4117 <div class="flex flex-col"> 4118 <!-- Search Results --> 4119 <div class="overflow-y-auto overflow-x-hidden gap-1 max-h-[65vh] sm:grid-cols-8" 4120 4121 4122 <!-- Products --> 4123 <div class="mstInPage__itemList magento_catalog_product" 4124 x-show="isNotLayoutFullOrProductActive"> 4125 <p class="text-lg leading-normal transition appearance-none text-gray-800 lg:text-base border-b p-2 flex font-poppins items-center"> 4126 <span x-text="productIndexTitle"></span> 4127 <template x-if="productIndex"> 4128 <small class="color-black ml-1"> 4129 (<span 4130 x-html="productTotalItems"></span>) 4131 </small> 4132 </template> 4133 </p> 4134 4135 <template x-if="productIndex"> 4136 <template x-if="isProductMisspell"> 4137 <div class="bg-gray-100 p-2"> 4138 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 4139 x-text="productTextMisspell"></p> 4140 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 4141 x-text="productTextCorrected"></p> 4142 </div> 4143 </template> 4144 </template> 4145 4146 <div class="flex flex-row"> 4147 4148 </div> 4149 4150 <div class="mx-auto grid grid-cols-1 p-2 gap-1"> 4151 <template x-if="productIndex"> 4152 <template x-for="item in productItems"> 4153 <div 4154 class="mst-searchautocomplete__item flex flex-row text-base card gap-4 p-1"> 4155 <div class="search-product-image flex-shrink-0"> 4156 <a :href="itemUrl" 4157 @click="processItemClick"> 4158 <img :src="itemImageUrl" 4159 :alt="itemName" class="search-product-image object-contain"/> 4160 </a> 4161 </div> 4162 <div class="mst__product-meta w-full"> 4163 <a :href="itemUrl" 4164 @click="processItemClick" 4165 class="flex flex-row flex-wrap w-full"> 4166 <div class="flex flex-col w-full"> 4167 <h4 class="text-gray-900 font-bold font-poppins text-sm " 4168 x-html="item.name"> 4169 </h4> 4170 <p class="text-gray-600 font-poppins text-xs mt-1" 4171 x-html="item.sku"> 4172 </p> 4173 </div> 4174 </a> 4175 <div class="mt-auto font-poppins w-full pb-2 text-sm"> 4176 <div class="flex justify-between"> 4177 <div class="flex flex-row text-sm mt-2"> 4178 <sp
4178an class="text-gray-700 font-semibold justify-end lg:flex-col xl:flex-row" 4179 x-html="itemPrice"> 4180 </span> 4181 </div> 4182 </div> 4183 </div> 4184 </div> 4185 </div> 4186 </template> 4187 </template> 4188 <template x-if="isNotLoadingAndNoResults"> 4189 <p class="text-lg">No results found.</p> 4190 </template> 4191 </div> 4192 4193 </div> 4194 4195 <!-- Categories, Pages, Posts, Articles (Full page layout) --> 4196 4197 4198 <!-- Categories, Pages, Posts, Articles (1|2-column layout) --> 4199 <!-- <template x-for="(searchResult, identifier) in searchResults"> 4200 <template 4201 x-if="resultsForOtherIndexType"> 4202 <div class="mb-6"> 4203 <p class="text-lg leading-normal transition appearance-none text-gray-800 lg:text-base border-b p-2 flex font-poppins items-center"> 4204 <span x-text="searchResult.title"></span> 4205 <small class="text-gray-500 ml-1"> 4206 (<span x-html="searchResult.totalItems"></span>) 4207 </small> 4208 </p> 4209 <template x-if="searchResult.isMisspell"> 4210 <div class="bg-gray-100 p-2"> 4211 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 4212 x-text="searchResult.textMisspell"></p> 4213 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 4214 x-text="searchResult.textCorrected"></p> 4215 </div> 4216 </template> 4217 <div class="px-2"> 4218 <template x-for="item in searchResult.items"> 4219 <a :href="itemUrl"> 4220 <div class="my-2 flex flex-wrap"> 4221 <span 4222 class="m-1 font-bold text-sm leading-loose cursor-pointer" 4223 x-html="item.name"> 4224 </span> 4225 </div> 4226 </a> 4227 </template> 4228 </div> 4229 </div> 4230 </template> 4231 </template> --> 4232 4233 4234 </div> 4235 4236 <!-- Bottom --> 4237 <div class="p-3 w-full h-full order-2 col-span-8 bg-gray-100"> 4238 <div class="flex items-center justify-center"> 4239 <a :href="numberResultsUrl"> 4240 <p class="text-gray-900 transition duration-300 hover:text-gray-500 font-bold text-sm" 4241 x-html="numberResults"></p> 4242 </a> 4243 </div> 4244 </div> 4245 </div> 4246 </template> 4247 4248 <template x-if="noResults"> 4249 <div class="bg-gray-100 p-2"> 4250 <p class="text-base leading-normal transition appearance-none text-gray-800 lg:text-lg" 4251 x-text="noResultsText"></p> 4252 </div> 4253 </template> 4254 </div> 4255 </div> 4256 </div> 4257</div> 4258
4259<script> 4260 function initMirasvitSearch() { 4261 return { 4262 //initialize Variables 4263 showHotSearch: false, 4264 hasSearchResults: false, 4265 noResults: false, 4266 isLoading: false, 4267 isOpen: false, 4268 isContainerOpen: false, 4269 blockConfig: {"query":"","priceFormat":{"pattern":"$%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"minSearchLength":3,"url":"https:\/\/www.huskee.co\/searchautocomplete\/ajax\/suggest\/","storeId":"27","delay":100,"isAjaxCartButton":false,"isShowCartButton":false,"isShowImage":true,"isShowPrice":true,"isShowSku":true,"isShowRating":false,"isShowDescription":false,"isShowStockStatus":false,"isShowAdditionalAttributes":false,"isKeepLastSearchRequest":false,"layout":"1column","popularTitle":"Popular Suggestions","popularSearches":[],"isTypeaheadEnabled":true,"typeaheadUrl":"https:\/\/www.huskee.co\/searchautocomplete\/ajax\/typeahead\/","logUrl":"https:\/\/www.huskee.co\/searchautocomplete\/ajax\/log\/","minSuggestLength":2,"currency":"AUD","limit":null,"customerGroupId":0,"filterPosition":"","availableOrders":{"_score":"Relevance","price_0_24":"Price","created_at.sort_created_at":"Latest","arrangement.sort_arrangement":"Custom arrangement","latest":"Latest"},"defaultOrder":"_score","localStorageFields":{"prevRequestField":"mstLatestRequest","isRestorePrevRequestFlag":"mstIsRestorePrevRequest","isRefererAutocompleteFlag":"mstIsRefererAutocomplete"},"isHighlightingEnabled":true}, 4270 layoutFull: 0, 4271 layoutTwoColumns: 0, 4272 page: 1, 4273 xhttp: false, 4274 noResultsText: '', 4275 numberResults: '', 4276 numberResultsUrl: '', 4277 order: '_score', 4278 direction: 'desc', 4279 isActiveDirection: false, 4280 filterList: new Map(), 4281 buckets: [], 4282 collapses: [], 4283 priceFrom: -1, 4284 priceTo: -1, 4285 priceMin: -1, 4286 priceMax: -1, 4287 minThumb: 0, 4288 maxThumb: 0, 4289 step: 1, 4290 generalUrl: '', 4291 searchResults: {}, 4292 activeIndex: 'magento_catalog_product', 4293 highlightedParams: ['name', 'sku', 'description'], 4294 currentRequestUrl: '', 4295 availableOrders: '{"_score":"Relevance","price_0_24":"Price","created_at.sort_created_at":"Latest","arrangement.sort_arrangement":"Custom arrangement","latest":"Latest"}', 4296 4297 initShowPrevSearch() { 4298 this.$nextTick(showPrevSearch(this.$el)); 4299 }, 4300 4301 isLayoutFullOpen() { 4302 return this.layoutFull && this.isContainerOpen; 4303 }, 4304 4305 isProductIndexActive() { 4306 return this.activeIndex === 'magento_catalog_product'; 4307 }, 4308 4309 isNotLayoutFullOrProductActive() { 4310 return !this.layoutFull || this.activeIndex === 'magento_catalog_product'; 4311 }, 4312 4313 productIndexTitle() { 4314 if (this.searchResults['magento_catalog_product'] && this.searchResults['magento_catalog_product'].title) { 4315 return this.searchResults['magento_catalog_product'].title; 4316 } 4317 }, 4318 4319 productIndex() { 4320 return this.searchResults['magento_catalog_product']; 4321 }, 4322 4323 isNotLoadingAndNoResults() { 4324 return !this.isLoading && this.noResults; 4325 }, 4326 4327 productTotalItems() { 4328 return this.searchResults['magento_catalog_product'].totalItems; 4329 }, 4330 4331 productItems() { 4332 return this.searchResults['magento_catalog_product'].items; 4333 }, 4334 4335 isProductMisspell() { 4336 return this.searchResults['magento_catalog_product'].isMisspell; 4337 }, 4338 4339 productTextMisspell() { 4340 return this.searchResults['magento_catalog_product'].textMisspell; 4341 }, 4342 4343 productTextCorrected() { 4344 return this.searchResults['magento_catalog_product'].textCorrected; 4345 }, 4346 4347 resultsForOtherIndexType() { 4348 return !!this.searchResult.totalItems && ('magento_catalog_product' !== this.identifier); 4349 }, 4350 4351 itemUrl() { 4352 return `${this.item.url}`; 4353 }, 4354 4355 itemImageUrl() { 4356 // Check multiple possible property names for the image URL 4357 if (this.item.image_url) return this.item.image_url; 4358 if (this.item.imageUrl) return this.item.imageUrl; 4359 if (this.item.image) return this.item.image; 4360 if (this.item.thumbnail) return this.item.thumbnail; 4361 if (this.item.small_image) return this.item.small_image; 4362 4363 // Debug: log the item to see what properties are available 4364 console.log('Search item properties:', Object.keys(this.item)); 4365 4366 return ''; 4367 }, 4368 4369 itemName() { 4370 return `${this.item.name}`; 4371 }, 4372 4373 additionalAttributes() { 4374 return this.item.additionalAttributes; 4375 }, 4376 4377 itemPrice() { 4378 return this.item.price; 4379 }, 4380 4381 fiveStars() { 4382 return 5; 4383 }, 4384 4385 ratingStar() { 4386 return { 4387 brightColor: '#f6e05e', 4388 shadowColor: '#cbd5e0', 4389 star: `M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 4390 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 4391 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 4392 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z` 4393 }; 4394 }, 4395 4396 notEqualToRateRound() { 4397 return this.i !== this.item.rateRound; 4398 }, 4399 4400 equalToRateRound() { 4401 return this.i === this.item.rateRound; 4402 }, 4403 4404 partialFill() { 4405 return 'partialFill' + (undefined === this.item.sku_orig ? this.item.sku : this.item.sku_orig); 4406 }, 4407 4408 partialFillUrl() { 4409 return 'url(#partialFill' + (undefined === this.item.sku_orig ? this.item.sku : this.item.sku_orig) + ')'; 4410 }, 4411 4412 styleRateRound() { 4413 return this.i < this.item.rateRound ? 'color: ' + this.brightColor : 'color: ' + this.shadowColor; 4414 }, 4415 4416 stockStatusLabel() { 4417 return this.item.stockStatus === 2 ? 'In stock' : 'Out of stock'; 4418 }, 4419 4420 stockStatusClass() { 4421 return this.item.stockStatus === 2 ? 'text-green' : 'text-red'; 4422 }, 4423 4424 addToCartNotAllowed() { 4425 return {'opacity-50 cursor-not-allowed pointer-events-none' : this.item.stockStatus !== 2}; 4426 }, 4427 4428 mainColumnClass() { 4429 return { 4430 'sm:col-span-6' : (this.activeIndex === 'magento_catalog_product') 4431 }; 4432 }, 4433 4434 leftColumnClass() { 4435 return { 4436 'sm:col-span-2' : (this.activeIndex === 'magento_catalog_product') 4437 } 4438 }, 4439 4440 fullSizeClass() { 4441 return {'z-50 fixed top-0 left-0 w-full h-full lg:py-8 bg-gray-600/50' : this.isContainerOpen}; 4442 }, 4443 4444 cardClass() { 4445 return {'card' : this.isContainerOpen}; 4446 }, 4447 4448 fullSizeFormClass() { 4449 return {'flex justify-between' : this.isContainerOpen}; 4450 }, 4451 4452 fullSizeInputClass() { 4453 return {'border-0 outline-0 shadow-none ring-0 ring-offset-0 focus:shadow-none': this.isContainerOpen}; 4454 }, 4455 4456 fullSizeResultsClass() { 4457 return {'gap-4 lg:grid-cols-8' : this.isContainerOpen}; 4458 }, 4459 4460 activeIndexClass() { 4461 return { 'active bg-blue-600 text-white' : this.activeIndex === this.identifier }; 4462 }, 4463 4464 activeTotalsClass() { 4465 return this.activeIndex === this.identifier ? 'text-gray-50' : 'text-gray-500'; 4466 }, 4467 4468 identifierClass() { 4469 return `${this.identifier}`; 4470 }, 4471 4472 isActiveIndex() { 4473 return !!this.activeIndex; 4474 }, 4475 4476 isIdentifierActive() { 4477 return this.activeIndex === this.identifier; 4478 }, 4479 4480 resultTotalItems() { 4481 return !!this.searchResult.totalItems; 4482 }, 4483 4484 productIndexPages() { 4485 return this.searchResults['magento_catalog_product'].pages; 4486 }, 4487 4488 itemQueryText() { 4489 return ('magento_search_query' !== this.identifier) ? this.item.name.replaceAll(`<i>`, `<i class='mx-2'>`) : this.item.query_text; 4490 }, 4491 4492 pageActiveClass() { 4493 return {'active bg-blue-600 text-white': this.page.isActive }; 4494 }, 4495 4496 expandedBucketClass() { 4497 return { '_expanded': this.bucket.isExpanded }; 4498 }, 4499 4500 rotateExpandedClass() { 4501 return { 'rotate-180': this.bucket.isExpanded }; 4502 }, 4503 4504 isPriceBucket() { 4505 return this.bucket.code === 'price'; 4506 }, 4507 4508 isNotPriceBucket() { 4509 return this.bucket.code !== 'price'; 4510 }, 4511 4512 activeBucketItemClass() { 4513 return { '_active': this.item.isActive }; 4514 }, 4515 4516 selectBucketItem() { 4517 this.selectItem(this.bucket.code, this.item.key); 4518 }, 4519 4520 getAvailableOrders() { 4521 return JSON.parse(this.availableOrders); 4522 }, 4523 4524 isOrderSelected() { 4525 return this.order === this.key; 4526 }, 4527 4528 ascClass() { 4529 return {'hidden' : this.direction === 'asc'}; 4530 }, 4531 4532 descClass() { 4533 return {'hidden' : this.direction === 'desc'}; 4534 }, 4535 4536 doActivateSection() { 4537 let identifier = this.identifier; 4538 this.activateSection(identifier); 4539 }, 4540 4541 focusAfterOpen() { 4542 this.$el.focus() 4543 this.$el.select() 4544 }, 4545 4546 doSearchSuggest() { 4547 this.doSearch(this.$refs.searchInput.value) 4548 }, 4549 4550 hotSearchClickEvent: function (event) { 4551 const hotSearchClick = event.currentTarget.innerText; 4552 let searchElement = document.querySelector('#search'); 4553 searchElement.value = hotSearchClick; 4554 searchElement.click(); 4555 this.doSearch(hotSearchClick); 4556 searchElement.focus(); 4557 }, 4558 4559 preOpen: function () { 4560 if (this.layoutFull) { 4561 document.querySelector('body').classList.add('mstInPage'); 4562 } 4563 4564 this.isContainerOpen = true; 4565 }, 4566 4567 openSearch: function () { 4568 if (document.getElementById("search").value.length >= this.blockConfig.minSearchLength) { 4569 this.isOpen = true; 4570 this.showHotSearch = false;
4571 } else { 4572 this.showHotSearch = true; 4573 this.hasSearchResults = false; 4574 } 4575 }, 4576 4577 startSearch: function () { 4578 this.isOpen = !!this.$refs.searchInput; 4579 }, 4580 4581 closeSearch: function () { 4582 this.isOpen = false; 4583 4584 4585 if (this.layoutFull) { 4586 document.querySelector('body').classList.remove('mstInPage'); 4587 } 4588 4589 this.isContainerOpen = false; 4590 this.searchOpen = false; 4591 4592 //const event = new Event("search-open"); 4593 //window.dispatchEvent(event); 4594 }, 4595 4596 setNoResultsText(text) { 4597 if (!text) { 4598 return; 4599 } 4600 4601 this.noResultsText = text; 4602 }, 4603 4604 pageLabel() { 4605 return this.page.label; 4606 }, 4607 4608 goToPage: function () { 4609 this.page = this.page.label; 4610 this.doSearch(this.query); 4611 }, 4612 4613 // The doSearch() method must be called separately for compatibility with other modules 4614 setOrder() { 4615 this.order = this.$el.value; 4616 this.doSearch(this.query); 4617 }, 4618 4619 asc() { 4620 this.isActiveDirection = true; 4621 this.direction = 'asc'; 4622 this.doSearch(this.query); 4623 }, 4624 4625 desc() { 4626 this.isActiveDirection = true; 4627 this.direction = 'desc'; 4628 this.doSearch(this.query); 4629 }, 4630 4631 doSearch: function (query) { 4632 4633 // Min search length 4634 if (query.length < this.blockConfig.minSearchLength) { 4635 this.showHotSearch = true; 4636 this.isLoading = false; 4637 this.hasSearchResults = false; 4638 this.noResults = false; 4639 4640 return; 4641 } else { 4642 this.showHotSearch = false; 4643 this.hasSearchResults = true; 4644 this.noResults = false; 4645 } 4646 4647 if (this.query !== query) { 4648 this.query = query; 4649 this.filterList = new Map(); 4650 } 4651 4652 let self = this; 4653 4654 let url = this.blockConfig.url.replace(/\/$/, '') 4655 + '/?q=' + encodeURIComponent(this.query) 4656 + '&store_id=' + this.blockConfig.storeId 4657 + '&cat=false' 4658 + '&p=' + this.page 4659 + '&order=' + this.order 4660 + '&direction=' + this.direction 4661 + '&is_active_dir=' + this.isActiveDirection 4662 + '&buckets[0]=category_ids' 4663 + '¤cy=' + this.blockConfig.currency 4664 ; 4665 4666 4667 const generalUrl = url; 4668 4669 if (generalUrl === self.generalUrl) { 4670 this.isLoading = false; 4671 4672 return; 4673 } else { 4674 this.isLoading = true; 4675 url += '&index=' + this.activeIndex; 4676 } 4677 4678 // AJAX for searchautocomplete/ajax/suggest/ 4679 if (this.xhttp) { 4680 try { 4681 this.xhttp.abort(); 4682 } catch { 4683 4684 } 4685 } 4686 4687 this.currentRequestUrl = url; 4688 4689 this.xhttp = new XMLHttpRequest(); 4690 let xhttp = this.xhttp; 4691 4692 xhttp.open("GET", url, true); 4693 xhttp.setRequestHeader("Content-Type", "application/json"); 4694 4695 xhttp.onreadystatechange = function () { 4696 if (this.readyState === 4 && this.status === 200) { 4697 // Response from ajax call in JSON. 4698 const response = JSON.parse(this.responseText); 4699 4700 self.searchResults = {}; 4701 4702 for (const index in response.indexes) { 4703 if (response.indexes[index].items.length > 0) { 4704 const res = response.indexes[index]; 4705 4706 if ('magento_catalog_product' === res.identifier || 'magento_catalog_c
4706ategory' === res.identifier) { 4707 res.items.forEach(function (item, index) { 4708 self.highlightedParams.forEach(param => { 4709 res.items[index][param + '_orig'] = item[param]; 4710 res.items[index][param] = highlightMatch(item[param], query); 4711 }); 4712 }); 4713 } 4714 4715 self.searchResults[res.identifier] = res; 4716 } 4717 } 4718 4719 if ( 4720 !self.searchResults['magento_catalog_product'] 4721 && (Object.values(self.searchResults).length > 0) 4722 ) { 4723 self.activateSection(Object.keys(self.searchResults)[0]); 4724 } 4725 4726 if (self.searchResults['magento_catalog_product'] 4727 ) { 4728 self.searchResults['magento_catalog_product'].items.forEach((item, key) => { 4729 if (item.rating) { 4730 let rate = item.rating/20; 4731 let fragment = (rate - Math.floor(rate))*100; 4732 4733 if (fragment <= 0) { 4734 fragment = 100 4735 } 4736 4737 self.searchResults['magento_catalog_product'].items[key].rateRound 4738 = (Math.ceil(rate)); 4739 4740 self.searchResults['magento_catalog_product'].items[key].fragment 4741 = fragment + '%'; 4742 } 4743 }); 4744 4745 const sorter = self.searchResults['magento_catalog_product'].sorter; 4746 if (!!sorter && sorter.direction !== self.direction) { 4747 self.direction = sorter.direction; 4748 } 4749 } 4750 4751 self.setBuckets(self.searchResults, self.activeIndex); 4752 4753 self.isLoading = false; 4754 self.noResults = false; 4755 self.numberResults = response.textAll; 4756 self.numberResultsUrl = response.urlAll; 4757 self.generalUrl = generalUrl; 4758 4759 if (response.noResults) { 4760 self.setNoResultsText(response.textEmpty); 4761 self.hasSearchResults = false; 4762 self.noResults = true; 4763 } 4764 4765 self.saveRequest(); 4766 4767 self.isActiveDirection = false; 4768 } 4769 }; 4770 xhttp.send(); 4771 }, 4772 4773 saveRequest() { 4774 let searchRequestUrl = this.currentRequestUrl; 4775 let config = this.blockConfig; 4776 4777 let selectedFilters = {}; 4778 4779 for (const bucketCode in this.buckets) { 4780 let currentBucketCode = this.buckets[bucketCode].code;
4781 let currentBucketFilterList = this.filterList.get(currentBucketCode); 4782 4783 if (!currentBucketFilterList) { 4784 continue; 4785 } 4786 4787 let bucketSelectedFilters = []; 4788 currentBucketFilterList.forEach((categoryId) => { 4789 bucketSelectedFilters.push(categoryId); 4790 }) 4791 4792 selectedFilters[currentBucketCode] = bucketSelectedFilters; 4793 } 4794 4795 let requestData = { 4796 requestUrl: searchRequestUrl, 4797 filtersData: selectedFilters, 4798 activeIndex: this.activeIndex 4799 }; 4800 4801 let requestDataJson = JSON.stringify(requestData) 4802 4803 if (this.blockConfig.isKeepLastSearchRequest) { 4804 localStorage.setItem(config.localStorageFields.prevRequestField, requestDataJson); 4805 } 4806 }, 4807 4808 setBuckets(indexes, indexIdentifier) { 4809 this.buckets = []; 4810 4811 for (const key in indexes) { 4812 if (key !== indexIdentifier) { 4813 continue; 4814 } 4815 4816 const idx = indexes[key]; 4817 4818 for (const bucketKey in idx.buckets) { 4819 const bucket = idx.buckets[bucketKey] 4820 let bucketItems = []; 4821 4822 if (bucket.code === 'price') { 4823 this.priceMin = this.priceMin === -1 || this.priceMin > bucket.min ? bucket.min : this.priceMin; 4824 this.priceMax = this.priceMax === -1 || this.priceMax < bucket.max ? bucket.max : this.priceMax; 4825 this.priceFrom = this.priceFrom === -1 ? this.priceMin : this.priceFrom; 4826 this.priceTo = this.priceTo === -1 ? this.priceMax : this.priceTo; 4827 this.minPriceTrigger(); 4828 this.maxPriceTrigger(); 4829 } 4830 4831 for (const itemKey in bucket.items) { 4832 const item = bucket.items[itemKey]; 4833 4834 let isActive = this.filterList.get(bucket.code)?.includes(item.key) || false; 4835 4836 bucketItems.push({...item, isActive, select: () => this.selectItem(bucket.code, item.key)}); 4837 } 4838 4839 if (bucketItems.length > 0 || bucket.code === 'price') { 4840 this.buckets.push({ 4841 ...bucket, 4842 items: bucketItems, 4843 isExpanded: !this.collapses.includes(bucket.code), 4844 expand: () => this.toggleCollapse(bucket.code) 4845 }); 4846 } 4847 } 4848 } 4849 }, 4850 4851 selectItem(bucketCode, key, triggerSearch = true) { 4852 let filterList = this.filterList; 4853 let filterItems = filterList.get(bucketCode) || []; 4854 4855 if (bucketCode === 'price') { 4856 filterList.set(bucketCode, [key]); 4857 } else { 4858 let index = filterItems.indexOf(key); 4859 4860 if (index >= 0) { 4861 filterItems.splice(index, 1); 4862 filterItems.length > 0 ? filterList.set(bucketCode, filterItems) : filterList.delete(bucketCode); 4863 } else { 4864 filterItems.push(key); 4865 filterList.set(bucketCode, filterItems); 4866 } 4867 } 4868 4869 this.filterList = new Map(filterList); 4870 4871 if (triggerSearch) { 4872 this.doSearch(this.query); 4873 } 4874 }, 4875 4876 processItemClick() { 4877 localStorage.setItem(this.blockConfig.localStorageFields.isRestorePrevRequestFlag, "1") 4878 localStorage.setItem(this.blockConfig.localStorageFields.isRefererAutocompleteFlag, "1") 4879 window.location.href = this.item.url; 4880 }, 4881 4882 toggleCollapse(code) { 4883 4884 if (this.collapses.includes(code)) { 4885 this.collapses = this.collapses.filter(c => c !== code); 4886 } else { 4887 this.collapses.push(code); 4888 } 4889 4890 this.setBuckets(this.searchResults, this.activeIndex); 4891 }, 4892 4893 minPriceTrigger() { 4894 if (!this.isNumeric(this.priceFrom) || (this.priceFrom < this.priceMin)) { 4895 this.priceFrom = this.priceMin; 4896 } 4897 this.priceFrom = Math.min(this.priceFrom, this.priceTo - this.step); 4898 this.minThumb = ((this.priceFrom - this.priceMin) / (this.priceMax - this.priceMin)) * 100; 4899 }, 4900 4901 maxPriceTrigger() { 4902 if (!this.isNumeric(this.priceTo) || (this.priceTo > this.priceMax)) { 4903 this.priceTo = this.priceMax; 4904 } 4905 this.priceTo = Math.max(this.priceTo, this.priceFrom + this.step); 4906 this.maxThumb = 100 - (((this.priceTo - this.priceMin) / (this.priceMax - this.priceMin)) * 100); 4907 }, 4908 4909 isNumeric(obj) { 4910 return !isNaN(parseFloat(obj)) && isFinite(obj); 4911 }, 4912 4913 activateSection(identifier) { 4914 if (identifier !== this.activeIndex) { 4915 this.activeIndex = identifier; 4916 this.goToPage(1); 4917 } 4918 this.saveRequest() 4919 }, 4920 4921 processAddToCart() { 4922 let addToCartElement = this.$el; 4923 let linkToCart = this.item.addToCartUrl; 4924 4925 if (this.blockConfig.isAjaxCartButton) { 4926 fetch(linkToCart + '?isAjax=1' + '&form_key='
4926+ hyva.getFormKey(), { 4927 method: 'POST', 4928 headers: { 4929 'Accept': 'application/json', 4930 'Content-Type': 'application/json' 4931 } 4932 }) 4933 .then(response => response.json()) 4934 .then(data => { 4935 let messageElement = document.createElement('div'); 4936 messageElement.className = `to_cart_message message ${data.success ? 'success' : 'error'} text-sm p-2`; 4937 messageElement.innerHTML = data.message; 4938 4939 addToCartElement.closest('.mst__product-meta').prepend(messageElement); 4940 4941 /** 4942 * Reload Section data 4943 */ 4944 hyva.setCookie('mage-cache-sessid', '', -1, true); 4945 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 4946 4947 setTimeout(() => { 4948 let messageElement = addToCartElement.closest('.mst__product-meta').querySelector('.to_cart_message'); 4949 if (messageElement) { 4950 messageElement.remove(); 4951 } 4952 }, 5000); 4953 }) 4954 .catch(error => { 4955 console.error('Error:', error); 4956 }); 4957 } else { 4958 addToCartElement.setAttribute('href', linkToCart); 4959 addToCartElement.click(); 4960 } 4961 4962 return false; 4963 } 4964 } 4965 } 4966 4967 window.addEventListener('alpine:init', () => Alpine.data('initMirasvitSearch', initMirasvitSearch), {once: true}); 4968 4969 function showPrevSearch() { 4970 let mirasvitSearch = Alpine.$data(document.getElementById('search')); 4971 let config = mirasvitSearch.blockConfig; 4972 let localStorageFields = mirasvitSearch.blockConfig.localStorageFields; 4973 let prevRequestFlag = !!(localStorage.getItem(localStorageFields.isRestorePrevRequestFlag)); 4974 let prevRequest = localStorage.getItem(localStorageFields.prevRequestField); 4975 4976 if (!prevRequest) { 4977 return; 4978 } 4979 4980 prevRequest = JSON.parse(prevRequest); 4981 4982 function delay(ms) { 4983 return new Promise(resolve => setTimeout(resolve, ms)); 4984 } 4985 4986 async function triggerEvents() { 4987 await delay(20); 4988 window.dispatchEvent(new Event('search-open')); 4989 4990 await delay(100); 4991 window.dispatchEvent(new Event('search-open')); 4992 4993 await delay(150); 4994 window.dispatchEvent(new Event('search-open')); 4995 4996 await delay(300); 4997 window.dispatchEvent(new Event('search-open')); 4998 } 4999 5000 if (config.isKeepLastSearchRequest 5001 && window.performance 5002 && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD 5003 && prevRequestFlag) { 5004
5005 let prevRequestParams = new URL(prevRequest.requestUrl).searchParams; 5006 let prevRequestFiltersData = prevRequest.filtersData; 5007 5008 /** 5009 * Search init, pass the query into the input, select active index 5010 * 5011 * @type {string} 5012 */ 5013 mirasvitSearch.$refs.searchInput.value = prevRequestParams.get('q'); 5014 5015 /** 5016 * Passing filters 5017 */ 5018 Object.entries(prevRequestFiltersData).forEach((filter) => { 5019 filter[1].forEach((key) => { 5020 mirasvitSearch.selectItem(filter[0], key, false); 5021 }) 5022 }); 5023 5024 5025 /** 5026 * Passing the rest of data 5027 */ 5028 mirasvitSearch.query = prevRequestParams.get('q'); 5029 mirasvitSearch.page = prevRequestParams.get('p'); 5030 mirasvitSearch.order = prevRequestParams.get('order'); 5031 mirasvitSearch.direction = prevRequestParams.get('direction'); 5032 5033 /** 5034 * Make the search to be open 5035 */ 5036 mirasvitSearch.searchOpen = true; 5037 5038 /** 5039 * Trigger required for search events 5040 */ 5041 triggerEvents(); 5042 5043 if (!prevRequest.activeIndex) { 5044 return; 5045 } 5046 mirasvitSearch.activateSection(prevRequest.activeIndex); 5047 } 5048 } 5049 5050 function restrictPrevSearch () { 5051 if (window.performance 5052 && (!(window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD)) 5053 ) { 5054 let localStorageFields = {"prevRequestField":"mstLatestRequest","isRestorePrevRequestFlag":"mstIsRestorePrevRequest","isRefererAutocompleteFlag":"mstIsRefererAutocomplete"}; 5055 5056 if (localStorage.getItem(localStorageFields.isRefererAutocompleteFlag)) { 5057 localStorage.setItem(localStorageFields.isRefererAutocompleteFlag, ''); 5058 return; 5059 } 5060 5061 if (!(localStorage.getItem(localStorageFields.isRefererAutocompleteFlag))) { 5062 localStorage.setItem(localStorageFields.prevRequestField, ''); 5063 localStorage.setItem(localStorageFields.isRestorePrevRequestFlag, '') 5064 } 5065 } 5066 } 5067 5068 restrictPrevSearch(); 5069</script>
5069 5070
5071<script> 5072 function highlightMatch(html, query) { 5073 if (!html) { 5074 return html; 5075 } 5076 5077 const cssClass = '' 5078 let arQuery = query.split(' '); 5079 let arSpecialChars = [ 5080 {'key': 'a', 'value': '(à |â|Ä |a)'}, 5081 {'key': 'c', 'value': '(ç|Ä|c)'}, 5082 {'key': 'e', 'value': '(è|é|Ä|ê|ë|Ä|e)'}, 5083 {'key': 'i', 'value': '(î|ï|į|i)'}, 5084 {'key': 'o', 'value': '(ô|o)'}, 5085 {'key': 's', 'value': '(Å¡|s)'}, 5086 {'key': 'u', 'value': '(ù|ü|û|Å«|ų|u)'} 5087 ]; 5088 5089 arQuery.forEach(function (word) { 5090 word = word.trim(); 5091 5092 if (word) { 5093 word = word.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); 5094 arSpecialChars.forEach(function (match) { 5095 word = word.replace(new RegExp(match.key, 'g'), match.value); 5096 }); 5097 5098 if ("span".indexOf(word.toLowerCase()) === -1) { 5099 html = html.replace(new RegExp('(' + word + '(?![^<>]*>))', 'ig'), function ($1, match) { 5100 return '<span class="' + cssClass + '">' + match + '</span>'; 5101 }); 5102 } 5103 } 5104 }); 5105 5106 return html; 5107 } 5108</script>
5108 5109 5110<style> 5111 input[type=range]::-webkit-slider-thumb { 5112 pointer-events: all; 5113 width: 24px; 5114 height: 24px; 5115 -webkit-appearance: none; 5116 /* @apply w-6 h-6 appearance-none pointer-events-auto; */ 5117 } 5118 5119 body.mstInPage { 5120 position: absolute; 5121 overflow-y: hidden; 5122 width: 100%; 5123 height: 100%; 5124 box-sizing: border-box; 5125 } 5126 5127 body.mstInPage #search { 5128 --tw-ring-shadow: none; 5129 } 5130 5131 body.mstInPage #search::-webkit-search-cancel-button, 5132 body.mstInPage #search::-webkit-search-decoration { 5133 -webkit-appearance: none; 5134 appearance: none; 5135 } 5136 5137 body.mstInPage #search-content { 5138 z-index: 1000; 5139 } 5140</style> 5141 </div> 5142 5143 </div> 5144 5145 <div class="header-menu lg:block hidden" x-data="{ shrink: false }" @scroll.window="shrink = (window.scrollY > 150)" 5146 class="header-menu sticky" :class="{ 'fixed': shrink}"> 5147 <div class="logo sticky-menu"> 5148 <div class="order-1 sm:order-2 lg:order-1 w-full pb-2 sm:w-auto sm:pb-0"> 5149 <a 5150 class="flex items-center justify-center text-xl font-medium tracking-wide text-gray-800 5151 no-underline hover:no-underline font-title" 5152 href="https://www.huskee.co/" 5153 aria-label="Go to Home page" 5154 > 5155 <img 5156 src="https://www.huskee.co/media/logo/websites/24/huskee_logo.png" 5157 alt="Huskee" 5158 width="250" height="49" loading="eager" 5159 /> 5160 </a> 5161</div> 5162 </div> 5163 <div class="logo-desktop hidden md:block"> <nav x-data="initMenuMobile_6ab50539b4114()" @load.window="setActiveMenu($root)" 5164 @keydown.window.escape="closeMenu()" class="z-20 order-2 sm:order-1 lg:order-2 navigation lg:hidden w-12 h-12" 5165 aria-label="Site navigation" role="navigation"> 5166 <!-- mobile --> 5167 <button x-ref="mobileMenuTrigger" @click="openMenu()" 5168 :class="{'overflow-x-hidden overflow-y-auto fixed top-0 left-0 w-full z-20' : open}" type="button" 5169 aria-label="Open menu" aria-haspopup="menu" :aria-expanded="open" 5170 :hidden="open"> 5171 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="p-3" width="48" height="48" :class="{ 'hidden' : open, 'block': !open }" aria-hidden="true"> 5172 <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/> 5173</svg> 5174 </button> 5175 <div x-ref="mobileMenuNavLinks" class=" 5176 fixed top-0 right-0 w-full h-full hidden 5177 flex-col border-t border-container bg-container-lighter 5178 overflow-y-auto overflow-x-hidden z-10 5179 " :class="{ 'flex z-10': open, 'hidden': !open }" :aria-hidden="open ? 'false' : 'true'" role="dialog" 5180 aria-modal="true"> 5181 5182 <ul class="border-t flex flex-col mt-16" 5183 aria-label="Site navigation links"> 5184 <li data-child-id="category-node-1083-main" class="level-0"> 5185 <div class="flex items-center" :class="{ 5186 '' : mobilePanelActiveId, 5187 '' : !mobilePanelActiveId 5188 }"> 5189 <a class="flex items-center w-full px-2 py-4 border-b cursor-pointer 5190 bg-container-lighter border-container level-0 5191 " href="https://www.huskee.co/shop" 5192 title="Shop"> 5193 Shop </a> 5194 <button @click="openSubcategory('category-node-1083')" class="absolute right-0 flex items-center justify-center w-11 h-11 cursor-pointer 5195 border-container" 5196 aria-label="Open Shop subcategories" 5197 aria-haspopup="true" 5198 :aria-expanded="mobilePanelActiveId === 'category-node-1083'"> 5199 <div class="w-6 h-8"> 5200 <i class="icon navPages-action-moreIcon"> 5201 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" 5202 class="icon-chevron" width="16" height="16" aria-hidden="true"> 5203 <path fill-rule="evenodd" 5204 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 5205 clip-rule="evenodd"></path> 5206 </svg> 5207 </i> 5208 </div> 5209 </button> 5210 </div> 5211 <div data-child-id="category-node-1083" 5212 class="flex flex-col gap-1 w-full h-full bg-container-lighter" :class="{ 5213 'hidden': mobilePanelActiveId !== 'category-node-1083' 5214 }"> 5215 <ul class="menu-slide" :class="{ 5216 '' : mobilePanelActiveId !== 'category-node-1083', 5217 '' : mobilePanelActiveId === 'category-node-1083', 5218 }" aria-label="Subcategories"> 5219 <li> 5220 <a href="https://www.huskee.co/shop" 5221 title="Shop" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 5222 bg-container-lighter border-container 5223 ">
5224 <span class=""> 5225 View All </span> 5226 </a> 5227 </li> 5228 <li> 5229 <a href="https://www.huskee.co/shop/shop-by-product" 5230 title="Shop By Product" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 5231 bg-container-lighter border-container 5232 "> 5233 <span class="text-base text-black"> 5234 Shop By Product </span> 5235 </a> 5236 5237 <ul class="border-l border-gray-200"> 5238 <li> 5239 <a href="https://www.huskee.co/shop/shop-by-product/reusable-cups" 5240 title="Reusable Cups" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5241 bg-container-lighter border-container 5242 "> 5243 <span class="text-sm text-gray-600"> 5244 Reusable Cups </span> 5245 </a> 5246 </li> 5247 <li> 5248 <a href="https://www.huskee.co/shop/shop-by-product/cups-no-lids" 5249 title="Cups (no lids)" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5250 bg-container-lighter border-container 5251 "> 5252 <span class="text-sm text-gray-600"> 5253 Cups (no lids) </span> 5254 </a> 5255 </li> 5256 <li> 5257 <a href="https://www.huskee.co/shop/shop-by-product/lids" 5258 title="Lids" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5259 bg-container-lighter border-container 5260 "> 5261 <span class="text-sm text-gray-600"> 5262 Lids </span> 5263 </a> 5264 </li> 5265 <li> 5266 <a href="https://www.huskee.co/shop/shop-by-product/saucers" 5267 title="Saucers" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5268 bg-container-lighter border-container 5269 "> 5270 <span class="text-sm text-gray-600"> 5271 Saucers </span> 5272 </a> 5273 </li> 5274 <li> 5275 <a href="https://www.huskee.co/shop/shop-by-product/espresso-sets" 5276 title="Espresso Sets" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5277 bg-container-lighter border-container 5278 ">
5279 <span class="text-sm text-gray-600"> 5280 Espresso Sets </span> 5281 </a> 5282 </li> 5283 <li> 5284 <a href="https://www.huskee.co/shop/shop-by-product/dog-bowls" 5285 title="Dog Bowls" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5286 bg-container-lighter border-container 5287 "> 5288 <span class="text-sm text-gray-600"> 5289 Dog Bowls </span> 5290 </a> 5291 </li> 5292 <li> 5293 <a href="https://www.huskee.co/shop/shop-by-product/replacement-parts" 5294 title="Replacement Parts" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5295 bg-container-lighter border-container 5296 "> 5297 <span class="text-sm text-gray-600"> 5298 Replacement Parts </span> 5299 </a> 5300 </li> 5301 </ul> 5302 </li> 5303 <li> 5304 <a href="https://www.huskee.co/shop/shop-by-collection" 5305 title="Shop By Collection" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 5306 bg-container-lighter border-container 5307 "> 5308 <span class="text-base text-black"> 5309 Shop By Collection </span> 5310 </a> 5311 5312 <ul class="border-l border-gray-200"> 5313 <li> 5314 <a href="https://www.huskee.co/shop/shop-by-collection/huskeeoriginal-category" 5315 title="HuskeeOriginal" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5316 bg-container-lighter border-container 5317 "> 5318 <span class="text-sm text-gray-600"> 5319 HuskeeOriginal </span> 5320 </a> 5321 </li> 5322 <li> 5323 <a href="https://www.huskee.co/shop/shop-by-collection/huskeerenew-category" 5324 title="HuskeeRenew" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5325 bg-container-lighter border-container 5326 ">
5327 <span class="text-sm text-gray-600"> 5328 HuskeeRenew </span> 5329 </a> 5330 </li> 5331 <li> 5332 <a href="https://www.huskee.co/shop/shop-by-collection/huskeesteel-category" 5333 title="HuskeeSteel" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5334 bg-container-lighter border-container 5335 "> 5336 <span class="text-sm text-gray-600"> 5337 HuskeeSteel </span> 5338 </a> 5339 </li> 5340 <li> 5341 <a href="https://www.huskee.co/shop/shop-by-collection/huskeeloop-category" 5342 title="HuskeeLoop" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5343 bg-container-lighter border-container 5344 "> 5345 <span class="text-sm text-gray-600"> 5346 HuskeeLoop </span> 5347 </a> 5348 </li> 5349 </ul> 5350 </li> 5351 <li> 5352 <a href="https://www.huskee.co/shop/new-arrivals" 5353 title="New Arrivals" class="flex items-center w-full px-2 py-4 border-b cursor-pointer 5354 bg-container-lighter border-container 5355 "> 5356 <span class="text-base text-black"> 5357 New Arrivals </span> 5358 </a> 5359 5360 <ul class="border-l border-gray-200"> 5361 <li> 5362 <a href="https://www.huskee.co/shop/new-arrivals/blush-lemon-huskeesteel" 5363 title="Blush and Lemon Zest HuskeeSteel" class="flex items-center w-full px-2 py-3 border-b cursor-pointer 5364 bg-container-lighter border-container 5365 "> 5366 <span class="text-sm text-gray-600"> 5367 Blush and Lemon Zest HuskeeSteel </span> 5368 </a> 5369 </li> 5370 </ul> 5371 </li> 5372 5373 </ul> 5374 </div> 5375 </li> 5376 <li> 5377 <div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><ul class="navPages-list"> 5378 <li 5379 class="navPages-item" 5380 x-data="{ open: false }" 5381 @mouseenter="if(window.innerWidth >= 768) open = true" 5382 @mouseleave="if(window.innerWidth >= 768) open = false" 5383 > 5384 <a 5385 class="navPages-action has-subMenu" 5386 href="#" 5387 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 5388 > 5389 Borrow by Huskee 5390 <i class="icon navPages-action-moreIcon"> 5391 <svg 5392 xmlns="http://www.w3.org/2000/svg"
5393 viewBox="0 0 20 20" 5394 fill="currentColor" 5395 class="icon-chevron" 5396 width="16" 5397 height="16" 5398 aria-hidden="true" 5399 > 5400 <path 5401 fill-rule="evenodd" 5402 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 5403 clip-rule="evenodd" 5404 ></path> 5405 </svg> 5406 </i> 5407 </a> 5408 <div 5409 class="navPage-subMenu" 5410 id="navPages-borrow_by_huskee" 5411 x-show="open" 5412 x-cloak 5413 x-transition 5414 :class="{ 'is-open': open }" 5415 > 5416 <ul class="navPage-subMenu-list"> 5417 <li class="navPage-subMenu-item"> 5418 <a 5419 class="navPage-subMenu-action navPages-action" 5420 href="https://borrowbyhuskee.co/" 5421 target="_blank" 5422 >What is Borrow by Huskee?</a 5423 > 5424 </li> 5425 <li class="navPage-subMenu-item"> 5426 <a 5427 class="navPage-subMenu-action navPages-action" 5428 href="https://swapcafe.huskee.co/login" 5429 target="_blank" 5430 >Venue Portal Login</a 5431 > 5432 </li> 5433 </ul> 5434 </div> 5435 </li> 5436 <li 5437 class="navPages-item" 5438 x-data="{ open: false }" 5439 @mouseenter="if(window.innerWidth >= 768) open = true" 5440 @mouseleave="if(window.innerWidth >= 768) open = false" 5441 > 5442 <a 5443 class="navPages-action has-subMenu" 5444 href="https://www.huskee.co//huskeeswap/" 5445 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 5446 > 5447 HuskeeSwap 5448 <i class="icon navPages-action-moreIcon"> 5449 <svg 5450 xmlns="http://www.w3.org/2000/svg"
5451 viewBox="0 0 20 20" 5452 fill="currentColor" 5453 class="icon-chevron" 5454 width="16" 5455 height="16" 5456 aria-hidden="true" 5457 > 5458 <path 5459 fill-rule="evenodd" 5460 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 5461 clip-rule="evenodd" 5462 ></path> 5463 </svg> 5464 </i> 5465 </a> 5466 <div 5467 class="navPage-subMenu" 5468 x-show="open" 5469 x-cloak 5470 x-transition 5471 :class="{ 'is-open': open }" 5472 > 5473 <ul class="navPage-subMenu-list"> 5474 5475 <li class="navPage-subMenu-item"> 5476 <a class="navPage-subMenu-action navPages-action" href="/swap/" 5477 >What is HuskeeSwap?</a 5478 > 5479 </li> 5480 <li class="navPage-subMenu-item"> 5481 <a 5482 class="navPage-subMenu-action navPages-action" 5483 href="https://swapcafe.huskee.co/login" 5484 target="_blank" 5485 >Swap Portal Login</a 5486 > 5487 </li> 5488 <li class="navPage-subMenu-item"> 5489 <a 5490 class="navPage-subMenu-action navPages-action" 5491 href="https://swapcafe.huskee.co/register" 5492 target="_blank" 5493 >Register Venue</a 5494 > 5495 </li> 5496 <li class="navPage-subMenu-item"> 5497 <a class="navPage-subMenu-action navPages-action" href="/locations/" 5498 >Find a HuskeeSwap Site</a 5499 > 5500 </li> 5501 </ul> 5502 </div> 5503 </li> 5504 <li class="navPages-item"> 5505 <a class="navPages-action" href="/huskeeloop/">HuskeeLoop</a> 5506 </li> 5507 <li 5508 class="navPages-item" 5509 x-data="{ open: false }" 5510 @mouseenter="if(window.innerWidth >= 768) open = true" 5511 @mouseleave="if(window.innerWidth >= 768) open = false" 5512 > 5513 <a 5514 class="navPages-action has-subMenu" 5515 href="#" 5516 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 5517 > 5518 Wholesale 5519 <i class="icon navPages-action-moreIcon"> 5520 <svg 5521 xmlns="http://www.w3.org/2000/svg"
5522 viewBox="0 0 20 20" 5523 fill="currentColor" 5524 class="icon-chevron" 5525 width="16" 5526 height="16" 5527 aria-hidden="true" 5528 > 5529 <path 5530 fill-rule="evenodd" 5531 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 5532 clip-rule="evenodd" 5533 ></path> 5534 </svg> 5535 </i> 5536 </a> 5537 <div 5538 class="navPage-subMenu" 5539 x-show="open" 5540 x-cloak 5541 x-transition 5542 :class="{ 'is-open': open }" 5543 > 5544 <ul class="navPage-subMenu-list"> 5545 5546 <li class="navPage-subMenu-item"> 5547 <a 5548 class="navPage-subMenu-action navPages-action" 5549 href="https://www.huskee.co//customer/account/login/" 5550 >Login/Register</a 5551 > 5552 </li> 5553 5554 <li class="navPage-subMenu-item"> 5555 <a 5556 class="navPage-subMenu-action navPages-action" 5557 href="/international-enquiries/" 5558 >International Enquiries</a 5559 > 5560 </li> 5561 </ul> 5562 </div> 5563 </li> 5564 <li class="navPages-item"> 5565 <a class="navPages-action" href="/co-branding/">Co-Branding</a> 5566 </li> 5567 <li 5568 class="navPages-item" 5569 x-data="{ open: false }" 5570 @mouseenter="if(window.innerWidth >= 768) open = true" 5571 @mouseleave="if(window.innerWidth >= 768) open = false" 5572 > 5573 <a 5574 class="navPages-action has-subMenu" 5575 href="#" 5576 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 5577 > 5578 Resources 5579 <i class="icon navPages-action-moreIcon"> 5580 <svg 5581 xmlns="http://www.w3.org/2000/svg"
5582 viewBox="0 0 20 20" 5583 fill="currentColor" 5584 class="icon-chevron" 5585 width="16" 5586 height="16" 5587 aria-hidden="true" 5588 > 5589 <path 5590 fill-rule="evenodd" 5591 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 5592 clip-rule="evenodd" 5593 ></path> 5594 </svg> 5595 </i> 5596 </a> 5597 <div 5598 class="navPage-subMenu" 5599 x-cloak 5600 id="navPages-resources" 5601 x-show="open" 5602 x-transition 5603 :class="{ 'is-open': open }" 5604 > 5605 <ul class="navPage-subMenu-list"> 5606 <li class="navPage-subMenu-item"> 5607 <a 5608 class="navPage-subMenu-action navPages-action has-subMenu" 5609 href="/our-story/" 5610 > 5611 About Huskee 5612 </a> 5613 <ul class="navPage-childList"> 5614 <li class="navPage-childList-item"> 5615 <a 5616 class="navPage-childList-action navPages-action" 5617 href="/our-story/" 5618 >Our Story</a 5619 > 5620 </li> 5621 <li class="navPage-childList-item"> 5622 <a 5623 class="navPage-childList-action navPages-action" 5624 href="/partnerships/" 5625 >Our Partnerships</a 5626 > 5627 </li> 5628 <li class="navPage-childList-item"> 5629 <a 5630 class="navPage-childList-action navPages-action" 5631 href="/customer-case-studies/" 5632 >Customer Case Studies</a 5633 > 5634 </li> 5635 </ul> 5636 </li> 5637 <li class="navPage-subMenu-item"> 5638 <a 5639 class="navPage-subMenu-action navPages-action has-subMenu" 5640 href="/support/" 5641 > 5642 Support 5643 </a> 5644 <ul class="navPage-childList" id="navPages-"> 5645 <li class="navPage-childList-item"> 5646 <a 5647 class="navPage-childList-action navPages-action" 5648 href="/faq/" 5649 >FAQ</a 5650 > 5651 </li> 5652 <li class="navPage-childList-item"> 5653 <a 5654 class="navPage-childList-action navPages-action" 5655 href="/contact/" 5656 >Contact Us</a 5657 > 5658 </li> 5659 </ul> 5660 </li> 5661 <li class="navPage-subMenu-item"> 5662 <a 5663 class="navPage-subMenu-action navPages-action has-subMenu" 5664 href="/discover/" 5665 > 5666 Discover 5667 </a> 5668 <ul class="navPage-childList" id="navPages-"> 5669 <li class="navPage-childList-item"> 5670 <a class="navPage-childList-action navPages-action" href="/blog/" 5671 >Blog</a 5672 > 5673 </li> 5674 <li class="navPage-childList-item"> 5675 <a 5676 class="navPage-childList-action navPages-action" 5677 href="/impact-calculator/" 5678 >Impact Calculator</a 5679 > 5680 </li> 5681 </ul> 5682 </li> 5683 </ul> 5684 </div> 5685 </li> 5686</ul> 5687</div> </li> 5688 </ul> 5689 <button @click="closeMenu()" class="absolute flex justify-end w-16 self-end mb-1" 5690 aria-label="Close menu" type="button"> 5691 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 5692 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 5693</svg> 5694 </button> 5695 </div> 5696</nav>
5697<script> 5698 'use strict'; 5699 5700 const initMenuMobile_6ab50539b4114 = () => { 5701 return { 5702 mobilePanelActiveId: null, 5703 open: false, 5704 setActiveMenu(menuNode) { 5705 Array.from(menuNode.querySelectorAll('a')).filter(link => { 5706 return link.href === window.location.href.split('?')[0]; 5707 }).map(item => { 5708 item.classList.add('underline'); 5709 item.closest('li.level-0') && 5710 item.closest('li.level-0').querySelector('a.level-0').classList.add('underline'); 5711 }); 5712 }, 5713 openMenu() { 5714 this.open = true 5715 this.$nextTick(() => hyva.trapFocus(this.$refs['mobileMenuNavLinks'])); 5716 // Prevent from body scrolling while mobile menu opened 5717 document.body.style.overflow = 'hidden'; 5718 }, 5719 closeMenu() { 5720 document.body.style.overflow = ''; 5721 5722 if (this.open) { 5723 this.$nextTick(() => this.$refs['mobileMenuTrigger'].focus() || hyva.releaseFocus()); 5724 } 5725 5726 this.open = false 5727 this.mobilePanelActiveId = null 5728 }, 5729 openSubcategory(index) { 5730 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 5731 this.mobilePanelActiveId = this.mobilePanelActiveId === index ? 0 : index 5732 this.$nextTick(() => hyva.trapFocus(menuNodeRef)) 5733 }, 5734 backToMainCategories(index) { 5735 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 5736 this.mobilePanelActiveId = 0 5737 this.$nextTick(() => { 5738 hyva.trapFocus(this.$refs['mobileMenuNavLinks']) 5739 menuNodeRef.querySelector('a').focus() 5740 }) 5741 } 5742 } 5743 } 5744</script>
5744 5745<div class="navigation hidden lg:flex z-20 order-2 sm:order-1 lg:order-2" x-data="{ }"> 5746 <!-- root scope (no shared state; each node manages itself) --> 5747 <nav class="relative w-full" aria-label="Main menu"> 5748 <ul class="menu-root flex flex-wrap justify-center"> 5749 5750 <li id="nav-0" class="level0 parent level-top" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5751 <span class="node-line flex items-center"> 5752 <a class="py-1" href="https://www.huskee.co/shop" 5753 title="Shop"> 5754 Shop </a> 5755 5756 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 5757 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083"> 5758 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 5759 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 5760</svg> 5761 <span class="sr-only">Toggle submenu</span> 5762 </button> 5763 </span> 5764 5765 <ul id="submenu-category-node-1083" x-cloak x-show="open" x-transition 5766 class="submenu submenu-top"> 5767 <li id="nav-0" class="level1 parent" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5768 <span class="node-line flex items-center"> 5769 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product" 5770 title="Shop By Product"> 5771 Shop By Product </a> 5772 5773 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 5774 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083_category-node-1140"> 5775 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 5776 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 5777</svg> 5778 <span class="sr-only">Toggle submenu</span> 5779 </button> 5780 </span> 5781 5782 <ul id="submenu-category-node-1083_category-node-1140" 5783 class="submenu submenu-inner"> 5784 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5785 <span class="node-line flex items-center"> 5786 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/reusable-cups" 5787 title="Reusable Cups"> 5788 Reusable Cups </a> 5789 5790 </span> 5791 5792 </li> 5793 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5794 <span class="node-line flex items-center"> 5795 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/cups-no-lids" 5796 title="Cups (no lids)"> 5797 Cups (no lids) </a> 5798 5799 </span> 5800 5801 </li> 5802 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5803 <span class="node-line flex items-center"> 5804 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/lids" 5805 title="Lids"> 5806 Lids </a> 5807 5808 </span> 5809 5810 </li> 5811 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
5812 <span class="node-line flex items-center"> 5813 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/saucers" 5814 title="Saucers"> 5815 Saucers </a> 5816 5817 </span> 5818 5819 </li> 5820 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5821 <span class="node-line flex items-center"> 5822 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/espresso-sets" 5823 title="Espresso Sets"> 5824 Espresso Sets </a> 5825 5826 </span> 5827 5828 </li> 5829 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5830 <span class="node-line flex items-center"> 5831 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/dog-bowls" 5832 title="Dog Bowls"> 5833 Dog Bowls </a> 5834 5835 </span> 5836 5837 </li> 5838 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5839 <span class="node-line flex items-center"> 5840 <a class="py-1" href="https://www.huskee.co/shop/shop-by-product/replacement-parts" 5841 title="Replacement Parts"> 5842 Replacement Parts </a> 5843 5844 </span> 5845 5846 </li> 5847 </ul> 5848 </li> 5849 <li id="nav-0" class="level1 parent" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5850 <span class="node-line flex items-center"> 5851 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection" 5852 title="Shop By Collection"> 5853 Shop By Collection </a> 5854 5855 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 5856 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083_category-node-1170"> 5857 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 5858 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 5859</svg> 5860 <span class="sr-only">Toggle submenu</span> 5861 </button> 5862 </span> 5863 5864 <ul id="submenu-category-node-1083_category-node-1170" 5865 class="submenu submenu-inner"> 5866 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5867 <span class="node-line flex items-center"> 5868 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeeoriginal-category" 5869 title="HuskeeOriginal"> 5870 HuskeeOriginal </a> 5871 5872 </span> 5873 5874 </li> 5875 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5876 <span class="node-line flex items-center"> 5877 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeerenew-category" 5878 title="HuskeeRenew"> 5879 HuskeeRenew </a> 5880 5881 </span> 5882 5883 </li> 5884 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5885 <span class="node-line flex items-center"> 5886 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeesteel-category" 5887 title="HuskeeSteel"> 5888 HuskeeSteel </a> 5889 5890 </span> 5891 5892 </li> 5893 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
5894 <span class="node-line flex items-center"> 5895 <a class="py-1" href="https://www.huskee.co/shop/shop-by-collection/huskeeloop-category" 5896 title="HuskeeLoop"> 5897 HuskeeLoop </a> 5898 5899 </span> 5900 5901 </li> 5902 </ul> 5903 </li> 5904 <li id="nav-0" class="level1 parent" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5905 <span class="node-line flex items-center"> 5906 <a class="py-1" href="https://www.huskee.co/shop/new-arrivals" 5907 title="New Arrivals"> 5908 New Arrivals </a> 5909 5910 <button type="button" class="ml-1 rounded p-1 text-black submenu-toggle" :class="open ? 'is-open' : ''" 5911 :aria-expanded="open ? 'true' : 'false'" aria-controls="submenu-category-node-1083_category-node-1125"> 5912 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="icon-chevron" width="20" height="20" aria-hidden="true"> 5913 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 5914</svg> 5915 <span class="sr-only">Toggle submenu</span> 5916 </button> 5917 </span> 5918 5919 <ul id="submenu-category-node-1083_category-node-1125" 5920 class="submenu submenu-inner"> 5921 <li id="nav-0" class="level2" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> 5922 <span class="node-line flex items-center"> 5923 <a class="py-1" href="https://www.huskee.co/shop/new-arrivals/blush-lemon-huskeesteel" 5924 title="Blush and Lemon Zest HuskeeSteel"> 5925 Blush and Lemon Zest HuskeeSteel </a> 5926 5927 </span> 5928 5929 </li> 5930 </ul> 5931 </li> 5932 </ul> 5933 </li> 5934 <li> 5935 5936<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><ul class="navPages-list"> 5937 <li 5938 class="navPages-item" 5939 x-data="{ open: false }" 5940 @mouseenter="if(window.innerWidth >= 768) open = true" 5941 @mouseleave="if(window.innerWidth >= 768) open = false" 5942 > 5943 <a 5944 class="navPages-action has-subMenu" 5945 href="#" 5946 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 5947 > 5948 Borrow by Huskee 5949 <i class="icon navPages-action-moreIcon"> 5950 <svg 5951 xmlns="http://www.w3.org/2000/svg"
5952 viewBox="0 0 20 20" 5953 fill="currentColor" 5954 class="icon-chevron" 5955 width="16" 5956 height="16" 5957 aria-hidden="true" 5958 > 5959 <path 5960 fill-rule="evenodd" 5961 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 5962 clip-rule="evenodd" 5963 ></path> 5964 </svg> 5965 </i> 5966 </a> 5967 <div 5968 class="navPage-subMenu" 5969 id="navPages-borrow_by_huskee" 5970 x-show="open" 5971 x-cloak 5972 x-transition 5973 :class="{ 'is-open': open }" 5974 > 5975 <ul class="navPage-subMenu-list"> 5976 <li class="navPage-subMenu-item"> 5977 <a 5978 class="navPage-subMenu-action navPages-action" 5979 href="https://borrowbyhuskee.co/" 5980 target="_blank" 5981 >What is Borrow by Huskee?</a 5982 > 5983 </li> 5984 <li class="navPage-subMenu-item"> 5985 <a 5986 class="navPage-subMenu-action navPages-action" 5987 href="https://swapcafe.huskee.co/login" 5988 target="_blank" 5989 >Venue Portal Login</a 5990 > 5991 </li> 5992 </ul> 5993 </div> 5994 </li> 5995 <li 5996 class="navPages-item" 5997 x-data="{ open: false }" 5998 @mouseenter="if(window.innerWidth >= 768) open = true" 5999 @mouseleave="if(window.innerWidth >= 768) open = false" 6000 > 6001 <a 6002 class="navPages-action has-subMenu" 6003 href="https://www.huskee.co//huskeeswap/" 6004 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 6005 > 6006 HuskeeSwap 6007 <i class="icon navPages-action-moreIcon"> 6008 <svg 6009 xmlns="http://www.w3.org/2000/svg"
6010 viewBox="0 0 20 20" 6011 fill="currentColor" 6012 class="icon-chevron" 6013 width="16" 6014 height="16" 6015 aria-hidden="true" 6016 > 6017 <path 6018 fill-rule="evenodd" 6019 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 6020 clip-rule="evenodd" 6021 ></path> 6022 </svg> 6023 </i> 6024 </a> 6025 <div 6026 class="navPage-subMenu" 6027 x-show="open" 6028 x-cloak 6029 x-transition 6030 :class="{ 'is-open': open }" 6031 > 6032 <ul class="navPage-subMenu-list"> 6033 6034 <li class="navPage-subMenu-item"> 6035 <a class="navPage-subMenu-action navPages-action" href="/swap/" 6036 >What is HuskeeSwap?</a 6037 > 6038 </li> 6039 <li class="navPage-subMenu-item"> 6040 <a 6041 class="navPage-subMenu-action navPages-action" 6042 href="https://swapcafe.huskee.co/login" 6043 target="_blank" 6044 >Swap Portal Login</a 6045 > 6046 </li> 6047 <li class="navPage-subMenu-item"> 6048 <a 6049 class="navPage-subMenu-action navPages-action" 6050 href="https://swapcafe.huskee.co/register" 6051 target="_blank" 6052 >Register Venue</a 6053 > 6054 </li> 6055 <li class="navPage-subMenu-item"> 6056 <a class="navPage-subMenu-action navPages-action" href="/locations/" 6057 >Find a HuskeeSwap Site</a 6058 > 6059 </li> 6060 </ul> 6061 </div> 6062 </li> 6063 <li class="navPages-item"> 6064 <a class="navPages-action" href="/huskeeloop/">HuskeeLoop</a> 6065 </li> 6066 <li 6067 class="navPages-item" 6068 x-data="{ open: false }" 6069 @mouseenter="if(window.innerWidth >= 768) open = true" 6070 @mouseleave="if(window.innerWidth >= 768) open = false" 6071 > 6072 <a 6073 class="navPages-action has-subMenu" 6074 href="#" 6075 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 6076 > 6077 Wholesale 6078 <i class="icon navPages-action-moreIcon"> 6079 <svg 6080 xmlns="http://www.w3.org/2000/svg"
6081 viewBox="0 0 20 20" 6082 fill="currentColor" 6083 class="icon-chevron" 6084 width="16" 6085 height="16" 6086 aria-hidden="true" 6087 > 6088 <path 6089 fill-rule="evenodd" 6090 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 6091 clip-rule="evenodd" 6092 ></path> 6093 </svg> 6094 </i> 6095 </a> 6096 <div 6097 class="navPage-subMenu" 6098 x-show="open" 6099 x-cloak 6100 x-transition 6101 :class="{ 'is-open': open }" 6102 > 6103 <ul class="navPage-subMenu-list"> 6104 6105 <li class="navPage-subMenu-item"> 6106 <a 6107 class="navPage-subMenu-action navPages-action" 6108 href="https://www.huskee.co//customer/account/login/" 6109 >Login/Register</a 6110 > 6111 </li> 6112 6113 <li class="navPage-subMenu-item"> 6114 <a 6115 class="navPage-subMenu-action navPages-action" 6116 href="/international-enquiries/" 6117 >International Enquiries</a 6118 > 6119 </li> 6120 </ul> 6121 </div> 6122 </li> 6123 <li class="navPages-item"> 6124 <a class="navPages-action" href="/co-branding/">Co-Branding</a> 6125 </li> 6126 <li 6127 class="navPages-item" 6128 x-data="{ open: false }" 6129 @mouseenter="if(window.innerWidth >= 768) open = true" 6130 @mouseleave="if(window.innerWidth >= 768) open = false" 6131 > 6132 <a 6133 class="navPages-action has-subMenu" 6134 href="#" 6135 @click="if(window.innerWidth < 768) { $event.preventDefault(); open = !open }" 6136 > 6137 Resources 6138 <i class="icon navPages-action-moreIcon"> 6139 <svg 6140 xmlns="http://www.w3.org/2000/svg"
6141 viewBox="0 0 20 20" 6142 fill="currentColor" 6143 class="icon-chevron" 6144 width="16" 6145 height="16" 6146 aria-hidden="true" 6147 > 6148 <path 6149 fill-rule="evenodd" 6150 d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" 6151 clip-rule="evenodd" 6152 ></path> 6153 </svg> 6154 </i> 6155 </a> 6156 <div 6157 class="navPage-subMenu" 6158 x-cloak 6159 id="navPages-resources" 6160 x-show="open" 6161 x-transition 6162 :class="{ 'is-open': open }" 6163 > 6164 <ul class="navPage-subMenu-list"> 6165 <li class="navPage-subMenu-item"> 6166 <a 6167 class="navPage-subMenu-action navPages-action has-subMenu" 6168 href="/our-story/" 6169 > 6170 About Huskee 6171 </a> 6172 <ul class="navPage-childList"> 6173 <li class="navPage-childList-item"> 6174 <a 6175 class="navPage-childList-action navPages-action" 6176 href="/our-story/" 6177 >Our Story</a 6178 > 6179 </li> 6180 <li class="navPage-childList-item"> 6181 <a 6182 class="navPage-childList-action navPages-action" 6183 href="/partnerships/" 6184 >Our Partnerships</a 6185 > 6186 </li> 6187 <li class="navPage-childList-item"> 6188 <a 6189 class="navPage-childList-action navPages-action" 6190 href="/customer-case-studies/" 6191 >Customer Case Studies</a 6192 > 6193 </li> 6194 </ul> 6195 </li> 6196 <li class="navPage-subMenu-item"> 6197 <a 6198 class="navPage-subMenu-action navPages-action has-subMenu" 6199 href="/support/" 6200 > 6201 Support 6202 </a> 6203 <ul class="navPage-childList" id="navPages-"> 6204 <li class="navPage-childList-item"> 6205 <a 6206 class="navPage-childList-action navPages-action" 6207 href="/faq/" 6208 >FAQ</a 6209 > 6210 </li> 6211 <li class="navPage-childList-item"> 6212 <a 6213 class="navPage-childList-action navPages-action" 6214 href="/contact/" 6215 >Contact Us</a 6216 > 6217 </li> 6218 </ul> 6219 </li> 6220 <li class="navPage-subMenu-item"> 6221 <a 6222 class="navPage-subMenu-action navPages-action has-subMenu" 6223 href="/discover/" 6224 > 6225 Discover 6226 </a> 6227 <ul class="navPage-childList" id="navPages-"> 6228 <li class="navPage-childList-item"> 6229 <a class="navPage-childList-action navPages-action" href="/blog/" 6230 >Blog</a 6231 > 6232 </li> 6233 <li class="navPage-childList-item"> 6234 <a 6235 class="navPage-childList-action navPages-action" 6236 href="/impact-calculator/" 6237 >Impact Calculator</a 6238 > 6239 </li> 6240 </ul> 6241 </li> 6242 </ul> 6243 </div> 6244 </li> 6245</ul> 6246</div> 6247</li> 6248 </ul> 6249 </nav> 6250</div> 6251 6252</div> 6253 </div> 6254</div> 6255</div> 6256
6257<!--Cart Drawer-->
6258<script> 6259 function initCartDrawer() { 6260 return { 6261 open: false, 6262 isLoading: false, 6263 cart: {}, 6264 maxItemsToDisplay: 10, 6265 itemsCount: 0, 6266 getData(data) { 6267 if (data.cart) { 6268 this.cart = data.cart; 6269 this.itemsCount = data.cart.items && data.cart.items.length || 0; 6270 this.setCartItems(); 6271 } 6272 this.isLoading = false; 6273 }, 6274 cartItems: [], 6275 getItemCountTitle() { 6276 return hyva.strf('\u00250\u0020of\u0020\u00251\u0020products\u0020in\u0020cart\u0020displayed', this.maxItemsToDisplay, this.itemsCount) 6277 }, 6278 setCartItems() { 6279 this.cartItems = this.cart.items && this.cart.items.sort((a, b) => b.item_id - a.item_id) || []; 6280 6281 if (this.maxItemsToDisplay > 0) { 6282 this.cartItems = this.cartItems.slice(0, parseInt(this.maxItemsToDisplay, 10)); 6283 } 6284 }, 6285 deleteItemFromCart(itemId) { 6286 this.isLoading = true; 6287 6288 const itemData = this.cart.items.filter((item) => item['item_id'] === itemId); 6289 const formKey = hyva.getFormKey(); 6290 const postUrl = BASE_URL + 'checkout/sidebar/removeItem/'; 6291 6292 fetch(postUrl, { 6293 "headers": { 6294 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 6295 }, 6296 "body": "form_key=" + formKey + "&item_id=" + itemId, 6297 "method": "POST", 6298 "mode": "cors", 6299 "credentials": "include" 6300 }).then(response => { 6301 if (response.redirected) { 6302 window.location.href = response.url; 6303 } else if (response.ok) { 6304 return response.json(); 6305 } else { 6306 window.dispatchMessages && window.dispatchMessages([{ 6307 type: 'warning', 6308 text: 'Could\u0020not\u0020remove\u0020item\u0020from\u0020quote.' 6309 }]); 6310 this.isLoading = false; 6311 } 6312 }).then(result => { 6313 window.dispatchMessages && window.dispatchMessages([{ 6314 type: result.success ? 'success' : 'error', 6315 text: result.success 6316 ? 'You\u0020removed\u0020the\u0020item.' 6317 : result.error_message 6318 }], result.success ? 5000 : 0); 6319 6320 if (result.success && itemData) { 6321 window.dispatchEvent(new CustomEvent('cart-item-removed', { detail: itemData })); 6322 } 6323 6324 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 6325 }); 6326 }, 6327 scrollLock(use = true) { 6328 document.body.style.overflow = use ? "hidden" : ""; 6329 }, 6330 toggleCartDrawer(event) { 6331 if (event.detail && event.detail.isOpen !== undefined) { 6332 if (event.detail.isOpen) { 6333 this.openCartDrawer(); 6334 } else { 6335 this.open = false; 6336 this.scrollLock(false); 6337 this.$refs && this.$refs.cartDialogContent && hyva.releaseFocus(this.$refs.cartDialogContent); 6338 } 6339 } else { 6340 this.openCartDrawer() 6341 } 6342 }, 6343 openCartDrawer() { 6344 this.open = true; 6345 this.scrollLock(true); 6346 this.$nextTick(() => { 6347 this.$refs && this.$refs.cartDialogContent && hyva.trapFocus(this.$refs.cartDialogContent) 6348 }) 6349 }, 6350 closeCartDrawer() { 6351 this.$dispatch('toggle-cart', { isOpen: false }) 6352 }, 6353 getSectionDataExtraActions() { 6354 if (!this.cart.extra_actions) { 6355 return ''; 6356 } 6357 6358 const contentNode = document.createElement('div'); 6359 contentNode.innerHTML = this.cart.extra_actions; 6360 6361 hyva.activateScripts(contentNode); 6362 6363 return contentNode.innerHTML; 6364 } 6365 } 6366 } 6367</script>
6367 6368<section x-cloak 6369 x-show="cart" 6370 id="cart-drawer" 6371 x-data="initCartDrawer()" 6372 @private-content-loaded.window="getData($event.detail.data)" 6373 @toggle-cart.window="toggleCartDrawer($event)" 6374 @keydown.escape="closeCartDrawer" 6375> 6376 <div role="dialog" 6377 aria-labelledby="cart-drawer-title" 6378 aria-modal="true" 6379 :aria-hidden="!open" 6380 class="fixed inset-y-0 right-0 z-30 flex max-w-full"> 6381 <div class="backdrop" 6382 x-show="open" 6383 x-transition:enter="ease-in-out duration-500" 6384 x-transition:enter-start="opacity-0" 6385 x-transition:enter-end="opacity-100" 6386 x-transition:leave="ease-in-out duration-500" 6387 x-transition:leave-start="opacity-100" 6388 x-transition:leave-end="opacity-0" 6389 role="button" 6390 @click="closeCartDrawer" 6391 aria-label="Close minicart"></div> 6392 <div class="relative w-screen max-w-md shadow-2xl" 6393 x-show="open" 6394 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" 6395 x-transition:enter-start="translate-x-full" 6396 x-transition:enter-end="translate-x-0" 6397 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" 6398 x-transition:leave-start="translate-x-0" 6399 x-transition:leave-end="translate-x-full" 6400 x-ref="cartDialogContent" 6401 role="region" 6402 :tabindex="open ? 0 : -1" 6403 aria-label="My Cart" 6404 6405 > 6406 <div class="flex flex-col h-full max-h-screen bg-white text-black shadow-xl"> 6407 6408 <header class="relative p-4"> 6409 <p id="cart-drawer-title" class="text-sm font-medium leading-7 text-gray-900"> 6410 <strong>My Cart</strong> 6411 <span class="items-total text-xs" 6412 x-show="maxItemsToDisplay && maxItemsToDisplay < itemsCount" 6413 x-text="getItemCountTitle()"> 6414 </span> 6415 </p> 6416 </header> 6417 6418 6419 <template x-if="!itemsCount"> 6420 <div class="relative px-4 py-4 bg-white border-bs sm:px-4 border-container"> 6421 Cart is empty </div> 6422 </template> 6423 6424 <template x-if="itemsCount"> 6425 <div class="relative grid gap-0 sm:gap-0 px-1 py-0 pb-8 sm:px-3 bg-white border-b border-container overflow-y-auto overscroll-y-contain"> 6426 <template x-for="item in cartItems"> 6427 <div class="flex items-start py-3 px-1 space-x-4 transition duration-150 ease-in-out"> 6428 <template x-if="item.product_has_url && item.is_visible_in_site_visibility"> 6429 <a :href="item.product_url" 6430 class="w-1/4 !ml-0" 6431 :aria-label="hyva.strf('Product\u0020\u0022\u00250\u0022', item.product_name)" 6432 > 6433 <img 6434 :src="item.product_image.src" 6435 :width="item.product_image.width" 6436 :height="item.product_image.height" 6437 loading="lazy" 6438 :alt="item.product_name" 6439 /> 6440 </a> 6441 </template> 6442 <template x-if="!item.product_has_url || !item.is_visible_in_site_visibility"> 6443 <div class="w-1/4 !ml-0"> 6444 <img 6445 :src="item.product_image.src" 6446 :width="item.product_image.width" 6447 :height="item.product_image.height" 6448 loading="lazy" 6449 :alt="item.product_name" 6450 /> 6451 </div> 6452 </template> 6453 <div class="w-3/4 space-y-2"> 6454 <div> 6455 <p class="text-sm font-poppins">
6456 <span x-html="item.qty"></span> x <span x-html="item.product_name"></span> 6457 </p> 6458 <!-- <p class="text-sm"><span x-html="item.product_sku"></span></p> --> 6459 </div> 6460 <template x-for="option in item.options"> 6461 <div class="pt-2"> 6462 <p class="font-semibold" x-text="option.label + ':'"></p> 6463 <p class="text-secondary" x-html="option.value"></p> 6464 </div> 6465 </template> 6466 <p><span x-html="item.product_price" class="font-poppins font-regular"></span></p> 6467 <div class="pt-2"> 6468 <a :href="item.configure_url" 6469 x-show="item.product_type !== 'grouped' && item.is_visible_in_site_visibility" 6470 class="inline-flex mr-2" 6471 :aria-label="hyva.strf('Edit\u0020product\u0020\u0022\u00250\u0022', item.product_name)" 6472 > 6473 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" aria-hidden="true"> 6474 <path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/> 6475</svg> 6476 </a> 6477 <button type="button" 6478 class="inline-flex" 6479 @click="deleteItemFromCart(item.item_id)" 6480 :aria-label="hyva.strf('Remove\u0020product\u0020\u0022\u00250\u0022\u0020from\u0020cart', item.product_name)" 6481 > 6482 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" aria-hidden="true"> 6483 <path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/> 6484</svg> 6485 </button> 6486 </div> 6487 6488 </div> 6489 </div> 6490 </template> 6491 </div> 6492 </template> 6493 6494 <div x-show="itemsCount" x-cloak> 6495 6496 <div class="fixed bottom-0 w-full grid gap-2 sm:gap-2 py-3 px-1 sm:px-3 bg-white"> 6497 <div class="w-full px-3 space-x-4 transition duration-150 ease-in-out"> 6498 <p class="font-poppins">Subtotal : <span x-html="cart.subtotal"></span></p> 6499 </div> 6500 <div class="w-full px-3 space-x-4 transition duration-150 ease-in-out flex items-center"> 6501 <a @click="closeCartDrawer; $dispatch('toggle-authentication', 6502 {url: 'https://www.huskee.co/checkout/'});" 6503 href="https://www.huskee.co/checkout/" 6504 class="inline-flex btn btn-primary" 6505 > 6506 Checkout </a> 6507 <span>or</span> 6508 <a href="https://www.huskee.co/checkout/cart/" 6509 class="btn btn-secondary" 6510 > 6511 View Cart </a> 6512 </div>
6513 <div x-html="getSectionDataExtraActions()"></div> 6514 <div class="stripe-minicart-button-container min-h-a11y"> 6515 6516 <div x-data="initStripePaymentRequestButtonMinicart" 6517 id="stripe-minicart-button-wrapper" 6518 @toggle-cart.window="initMinicartButton" 6519 class="relative grid gap-6 px-4 bg-white sm:gap-8 sm:px-6 min-h-a11y !mb-0 !mr-0" 6520 > 6521 <div id="stripe-minicart-button"></div> 6522 <div 6523 class="z-50 fixed inset-0 flex justify-center items-center bg-white/70 select-none" 6524 x-show="isLoading" 6525 x-cloak 6526 x-transition.opacity.duration.200ms 6527> 6528 <div class="flex items-center"> 6529 <svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" class="animate-spin" aria-hidden="true"> 6530 <circle style="opacity: .3;" cx="12" cy="12" r="10"/> 6531 <path fill="none" stroke-width="2" d="M12,2 a10,10 0 0,1 10,10"/> 6532</svg> 6533 <div class="ml-10 text-xl"> 6534 Loading... </div> 6535 </div> 6536</div> 6537 </div> 6538</div> 6539 </div> 6540 </div> 6541 6542 </div> 6543 6544 <button 6545 type="button" 6546 @click="closeCartDrawer" 6547 aria-label="Close minicart" 6548 class="absolute top-0 right-2 p-2 mt-0 text-gray-500 transition-colors hover:text-black" 6549 > 6550 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 6551 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 6552</svg> 6553 </button> 6554 </div> 6555 <div 6556 class="z-50 fixed inset-0 flex justify-center items-center bg-white/70 select-none" 6557 x-show="isLoading" 6558 x-cloak 6559 x-transition.opacity.duration.200ms 6560> 6561 <div class="flex items-center"> 6562 <svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" class="animate-spin" aria-hidden="true"> 6563 <circle style="opacity: .3;" cx="12" cy="12" r="10"/> 6564 <path fill="none" stroke-width="2" d="M12,2 a10,10 0 0,1 10,10"/> 6565</svg> 6566 <div class="ml-10 text-xl"> 6567 Loading... </div> 6568 </div> 6569</div> 6570 </div> 6571 6572</section> 6573 6574<!--Authentication Pop-Up--> 6575</div> 6576</header><main id="maincontent" class="page-main-full-width"><div id="contentarea" tabindex="-1"></div> 6577<div class="page messages">
6577<script> 6578 function initMessages() { 6579 "use strict"; 6580 return { 6581 messages: window.mageMessages || [], 6582 isEmpty() { 6583 return this.messages.reduce( 6584 function (isEmpty, message) { 6585 return isEmpty && message === undefined 6586 }, true 6587 ) 6588 }, 6589 hasMessages() { 6590 return !this.isEmpty(); 6591 }, 6592 hasMessage() { 6593 return !!this.message; 6594 }, 6595 removeMessage() { 6596 this.messages[this.index] = undefined; 6597 }, 6598 addMessages(messages, hideAfter) { 6599 messages.map((message) => { 6600 this.messages = this.messages.concat(message); 6601 if (hideAfter === undefined && message.type === 'success' && window.defaultSuccessMessageTimeout) { 6602 hideAfter = window.defaultSuccessMessageTimeout; 6603 } 6604 if (hideAfter) { 6605 this.setHideTimeOut(this.messages.length -1, hideAfter); 6606 } 6607 }); 6608 }, 6609 setHideTimeOut(messageIndex, hideAfter) { 6610 setTimeout((messageIndex) => { 6611 this.messages[messageIndex] = undefined; 6612 }, hideAfter, messageIndex); 6613 }, 6614 eventListeners: { 6615 ['@messages-loaded.window'](event) { 6616 this.addMessages(event.detail.messages, event.detail.hideAfter) 6617 }, 6618 ['@private-content-loaded.window'](event) { 6619 const data = event.detail.data; 6620 if ( 6621 data.messages && 6622 data.messages.messages && 6623 data.messages.messages.length 6624 ) { 6625 this.addMessages(data.messages.messages); 6626 } 6627 }, 6628 ['@clear-messages.window']() { 6629 this.messages = []; 6630 } 6631 }, 6632 getMessageUiId() { 6633 return 'message-' + this.message.type; 6634 } 6635 } 6636 } 6637 6638 window.addEventListener('alpine:init', () => Alpine.data('initMessages', initMessages), {once: true}) 6639</script>
6639 6640<section id="messages" 6641 x-data="initMessages" 6642 x-bind="eventListeners" 6643 role="alert" 6644> 6645 <template x-if="hasMessages"> 6646 <div class="w-full"> 6647 <div class="messages container mx-auto py-3"> 6648 <template x-for="(message, index) in messages" :key="index"> 6649 <div> 6650 <template x-if="hasMessage"> 6651 <div class="message" 6652 :class="message.type" 6653 :ui-id="getMessageUiId" 6654 > 6655 <span x-html="message.text"></span> 6656 <button 6657 type="button" 6658 class="text-gray-600 hover:text-black" 6659 aria-label="Close message" 6660 @click.prevent="removeMessage" 6661 > 6662 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="stroke-current" width="18" height="18" aria-hidden="true"> 6663 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 6664</svg> 6665 </button> 6666 </div> 6667 </template> 6668 </div> 6669 </template> 6670 </div> 6671 </div> 6672 </template> 6673</section> 6674</div><div class="columns"><div class="column main"><div x-data="recommendedProducts_above_main_content" 6675 x-init="initProductRecBlock" 6676 id="recs_block_above_main_content" 6677 class="container" x-cloak> 6678 <template x-if="productBanners"> 6679 <div> 6680 <template x-for="banner in productBanners"> 6681 <div class="product-slider" x-intersect.once="scrollViewEvent"> 6682 <section class="my-12 text-gray-700 body-font"> 6683 <div class="relative"> 6684 <div class="container flex flex-col items-center pt-6 pb-3 mx-auto mb-6 border-b-2 border-gray-300 md:flex-row"> 6685 <h3 x-text='banner.storefrontLabel' 6686 class="text-2xl font-medium text-gray-900 title-font"></h3> 6687 </div> 6688 <div class="relative flex flex-wrap gap-2 w-full md:px-1 xl:px-2"> 6689 <template x-for="product in banner.products"> 6690 <div class="mr-1 p-1 w-64"> 6691 <form method="post" x-bind:action="createAddToCartUrl" 6692 @submit.prevent="addToCartRecsEvent" 6693 class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full"> 6694 <input name="form_key" type="hidden" :value="getFormKey"/> 6695 <input type="hidden" name="product" :value="product.productId"> 6696 <a @click="recsClickEvent" 6697 class="product photo product-item-photo block mx-auto mb-3 cursor-pointer" 6698 tabindex="-1"> 6699 <img class="hover:shadow-sm object-contain" loading="lazy" 6700 :alt="getImageAltTag" 6701 :title="getImageTitle" 6702 :src="getImageSrc" 6703 width="360" 6704 height="360" 6705 > 6706 </a> 6707 <div class="product-info flex flex-col flex-grow"> 6708 <div 6709 class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 6710 <a class="product-item-link cursor-pointer" 6711 @click="recsClickEvent" 6712 x-text="product.name"></a> 6713 </div> 6714 <template x-if="product.prices"> 6715 <div class="pt-1 text-gray-900 price-box price-final_price" x-bind:data-product-id="product.productId">
6716 <span class="normal-price"> 6717 <span class="price-container price-final_price" x-show="showProductPrice"> 6718 <span class="price-label"> 6719 As low as </span> 6720 <span id="product-price" class="price-wrapper "> 6721 <span class="price" x-text="renderPrice"></span> 6722 </span> 6723 </span> 6724 </span> 6725 </div> 6726 </template> 6727 <template x-if="product.addToCartAllowed"> 6728 <div 6729 class="mt-auto pt-3 flex flex-wrap justify-center items-center"> 6730 <button 6731 class="w-auto btn btn-primary justify-center text-sm mr-auto" 6732 aria-label="Add to Cart"> 6733 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="h-6 w-6 border-current inline" width="25" height="25" role="img"> 6734 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"/> 6735<title>shopping-cart</title></svg> 6736 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 6737 Add to Cart </span> 6738 </button> 6739 </div> 6740 </template> 6741 </div> 6742 </form> 6743 </div> 6744 </template> 6745 </div> 6746 </div> 6747 </section> 6748 </div> 6749 </template> 6750 </div> 6751 </template> 6752</div> 6753
6754<script> 6755 'use strict'; 6756 window.addEventListener( 6757 'alpine:init', 6758 () => Alpine.data('recommendedProducts_above_main_content', () => ({ 6759 productBanners: [], 6760 renderedUnitIds: [], 6761 pagePreconfiguredDeferred: {}, 6762 bannerPlacement: 'above-main-content', 6763 initProductRecBlock: function () { 6764 let self = this; 6765 window.magentoStorefrontEvents.subscribe.recsResponseReceived(function () { 6766 let recsData = window.magentoStorefrontEvents.context.getRecommendations(); 6767 self.renderProductRecs(recsData); 6768 }); 6769 }, 6770 showProductPrice(){ 6771 return this.product.prices 6772 && this.product.prices.minimum 6773 && this.product.prices.minimum.final; 6774 }, 6775 renderPrice(){ 6776 return hyva.formatPrice(this.product.prices.minimum.final); 6777 }, 6778 renderProductRecs: function (recsData) { 6779 let self = this; 6780 6781 recsData.units.sort((a, b) => a.displayOrder - b.displayOrder); 6782 6783 const rendered = []; 6784 6785 recsData.units.forEach(function (unit) { 6786 if (unit.pagePlacement === self.bannerPlacement 6787 && !self.renderedUnitIds.includes(unit.unitId)) { 6788 self.renderedUnitIds.push(unit.unitId); 6789 self.productBanners.push(unit); 6790 rendered.push(unit.unitId); 6791 } 6792 }); 6793 6794 if (rendered.length) { 6795 setTimeout(function () { 6796 rendered.forEach(function (unitId) { 6797 window.magentoStorefrontEvents.publish.recsUnitRender(unitId); 6798 }); 6799 }, 0); 6800 } 6801 }, 6802 addToCartRecsEvent: function (event) { 6803 // requires recommendationsContext to be set 6804 Promise.resolve( 6805 magentoStorefrontEvents.publish.recsItemAddToCartClick( 6806 this.banner.unitId, this.product.productId, magentoStorefrontEvents.context 6807 ) 6808 ).then(() => event.target.submit()); 6809 }, 6810 scrollViewEvent: function () { 6811 magentoStorefrontEvents.publish.recsUnitView( 6812 this.banner.unitId, magentoStorefrontEvents.context 6813 ); 6814 }, 6815 recsClickEvent: function () { 6816 Promise.resolve( 6817 magentoStorefrontEvents.publish.recsItemClick( 6818 this.banner.unitId, this.product.productId, magentoStorefrontEvents.context 6819 ) 6820 ).then(() => { window.location.href = this.product.url; }); 6821 }, 6822 createAddToCartUrl: function () { 6823 const currentLocationUENC = encodeURIComponent( 6824 this.encodeUenc(window.location.href), 6825 ); 6826 return BASE_URL + 6827 "checkout/cart/add/uenc/" + 6828 currentLocationUENC + 6829 "/product/" + 6830 this.product.productId 6831 }, 6832 encodeUenc: function (value) { 6833 const regex = /=/gi; 6834 return btoa(value).replace(regex, ","); 6835 }, 6836 getImageAltTag() { 6837 return this.product?.image?.label || ''; 6838 }, 6839 getImageTitle() { 6840 return this.product?.image?.label || ''; 6841 }, 6842 getImageSrc(){ 6843 return this.product?.image?.url || 'https\u003A\u002F\u002Fwww.huskee.co\u002Fmedia\u002Fcatalog\u002Fproduct\u002Fplaceholder\u002Fdefault\u002F1204566951673160.fqSZYPyGbBHB3oKqfkg5_height640_1.png'; 6844 }, 6845 getFormKey() { 6846 return hyva.getFormKey(); 6847 } 6848 })), 6849 {once: true} 6850 ) 6851</script>
6851 6852
6853<script type="text/x-magento-init"> 6854 { 6855 "#contact-form": { 6856 "validation": {} 6857 } 6858 } 6859</script>
vendor: 1 bytes, line 6859
6859
6860<script type="text/x-magento-init"> 6861 { 6862 "#newsletter-validate-detail": { 6863 "validation": { 6864 "errorClass": "mage-error" 6865 } 6866 } 6867 } 6868</script>
vendor: 1 bytes, line 6868
6868
6869<script nonce="Z2dsMXNkNmdxb2tvNjEyemtkbGN1N29pYzNvM3FqejU=" type="application/ld+json"> 6870{ 6871 "@context": "http://schema.org", 6872 "@type": "WebSite", 6873 "url": "https://www.huskee.co/", 6874 "potentialAction": { 6875 "@type": "SearchAction", 6876 "target": "https://www.huskee.co/catalogsearch/result/index/?q={search_term_string}", 6877 "query-input": "required name=search_term_string" 6878 } 6879} 6880</script>
6880 6881<style>#html-body [data-pb-style=U7FNYWP]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;margin:0;padding:0}#html-body [data-pb-style=HCY4VHF]{min-height:400px}#html-body [data-pb-style=UNFOA33]{background-position:right top;background-size:cover;background-repeat:no-repeat;min-height:400px}#html-body [data-pb-style=O8CO9EH]{min-height:400px;background-color:transparent}#html-body [data-pb-style=ANJ87GF]{opacity:1;visibility:visible}#html-body [data-pb-style=A2SFMJA]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;text-align:center;margin-top:20px}#html-body [data-pb-style=RULH3FF]{text-align:center}#html-body [data-pb-style=XRYJDDR]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;text-align:center}#html-body [data-pb-style=IJ7M67A],#html-body [data-pb-style=WMMCPIU]{text-align:center}#html-body [data-pb-style=SMSBL75]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#f8f8f9;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;padding:30px 10px}#html-body [data-pb-style=AEMAE3Q]{padding-bottom:10px}#html-body [data-pb-style=M66Y6U7]{background-position:left top;background-size:contain;background-repeat:no-repeat;background-attachment:scroll;text-align:center;padding-left:0;padding-right:0;align-self:stretch}#html-body [data-pb-style=J8DIQWY]{display:flex;width:100%}#html-body [data-pb-style=PM7PX72]{justify-content:center;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:33.3333%;padding:10px;align-self:center}#html-body [data-pb-style=NNEXLSB]{border-style:none}#html-body [data-pb-style=K53BFSX],#html-body [data-pb-style=UQ84C43]{max-width:100%;height:auto}#html-body [data-pb-style=HTMP3RK]{justify-content:center;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:33.3333%;padding:10px;align-self:center}#html-body [data-pb-style=IKQAFL9]{border-style:none}#html-body [data-pb-style=DW08X88],#html-body [data-pb-style=O9U76GU]{max-width:100%;height:auto}#html-body [data-pb-style=PK8KYBD]{justify-content:center;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:33.3333%;padding:10px;align-self:center}#html-body [data-pb-style=KDGHINW]{border-style:none}#html-body [data-pb-style=TNG5VAJ],#html-body [data-pb-style=Y5YEU5X]{max-width:100%;height:auto}#html-body [data-pb-style=AKXBE72],#html-body [data-pb-style=MAGNNRH]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll}#html-body [data-pb-style=MAGNNRH]{justify-content:flex-start;display:flex;flex-direction:column}#html-body [data-pb-style=AKXBE72]{margin-left:30px;margin-right:30px;align-self:stretch}#html-body [data-pb-style=ECH84IM]{display:flex;width:100%}#html-body [data-pb-style=PMUBSBU]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:50%;align-self:stretch}#html-body [data-pb-style=XYXGSXN]{border-style:none}#html-body [data-pb-style=DVDRM82],#html-body [data-pb-style=NQE9MAT]{max-width:100%;height:auto}#html-body [data-pb-style=LY4EG4E],#html-body [data-pb-style=OEOWFA5]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll}#html-body [data-pb-style=OEOWFA5]{width:50%;align-self:stretch}#html-body [data-pb-style=LY4EG4E]{text-align:center}#html-body [data-pb-style=Y3B563C]{border-style:none}#html-body [data-pb-style=E68SA46],#html-body [data-pb-style=KQ6G3PW]{max-width:100%;height:auto}#html-body [data-pb-style=O80KWAS]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;align-self:stretch}#html-body [data-pb-style=SKK11AV]{display:flex;width:100%}#html-body [data-pb-style=O0BRCJ8]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:100%;align-self:stretch}#html-body [data-pb-style=A538RNU]{text-align:center}#html-body [data-pb-style=LMXFIGC]{display:inline-block}#html-body [data-pb-style=I0I9565]{text-align:center}#html-body [data-pb-style=OWWJ190]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#f4f4f4;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;text-align:center}#html-body [data-pb-style=OQPTY95]{text-align:center}#html-body [data-pb-style=K9JDSCG]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;align-self:stretch}#html-body [data-pb-style=G9MX4EJ]{display:flex;width:100%}#html-body [data-pb-style=QLX9COF]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#fff;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:25%;align-self:stretch}#html-body [data-pb-style=F01NG3C]{border-style:none}#html-body [data-pb-style=A40A13C],#html-body [data-pb-style=X3LAMVW]{max-width:100%;height:auto}#html-body [data-pb-style=JH5LLG6]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#fff;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:25%;align-self:stretch}#html-body [data-pb-style=IT0XQ3C]{border-style:none}#html-body [data-pb-style=EA54UYV],#html-body [data-pb-style=EM8K6KS]{max-width:100%;height:auto}#html-body [data-pb-style=A65YT3R]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#fff;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:25%;align-self:stretch}#html-body [data-pb-style=QR0K1TU]{border-style:none}#html-body [data-pb-style=J2I147K],#html-body [data-pb-style=XV16VK6]{max-width:100%;height:auto}#html-body [data-pb-style=WTRSMYQ]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#fff;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:25%;align-self:stretch}#html-body [data-pb-style=EMWRB6W]{border-style:none}#html-body [data-pb-style=FBW2CXL],#html-body [data-pb-style=WD4B7MT]{max-width:100%;height:auto}#html-body [data-pb-style=J4TLBX2],#html-body [data-pb-style=M61BPTM]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll}#html-body [data-pb-style=J4TLBX2]{justify-content:flex-start;display:flex;flex-direction:column;text-align:center}#html-body [data-pb-style=M61BPTM]{align-self:stretch}#html-body [data-pb-style=AJ75D35]{display:flex;width:100%}#html-body [data-pb-style=YCARII9]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#fff;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:50%;align-self:stretch}#html-body [data-pb-style=NQHK0UA]{border-style:none}#html-body [data-pb-style=AKQ3BAD],#html-body [data-pb-style=Y2F7W20]{max-width:100%;height:auto}#html-body [data-pb-style=EB2GFP9]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:50%;align-self:stretch}#html-body [data-pb-style=DYG7FMV]{border-style:none}#html-body [data-pb-style=OVSCXM6],#html-body [data-pb-style=TRC8F2D]{max-width:100%;height:auto}#html-body [data-pb-style=PDID3XJ]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;align-self:stretch}#html-body [data-pb-style=BGYDJ8S]{display:flex;width:100%}#html-body [data-pb-style=PEPG4BF]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:50%;align-self:stretch}#html-body [data-pb-style=R9C947Q]{border-style:none}#html-body [data-pb-style=JNUBA25],#html-body [data-pb-style=V7XAHIT]{max-width:100%;height:auto}#html-body [data-pb-style=UGNDHOU]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:50%;align-self:stretch}#html-body [data-pb-style=JX14H1R]{border-style:none}#html-body [data-pb-style=G9B4PV3],#html-body [data-pb-style=PCQKEPJ]{max-width:100%;height:auto}#html-body [data-pb-style=FU676B0],#html-body [data-pb-style=H93YP00]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll}#html-body [data-pb-style=FU676B0]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#eeedea;text-align:center;padding-top:40px}#html-body [data-pb-style=H93YP00]{align-self:stretch}#html-body [data-pb-style=EFKHDO2]{display:flex;width:100%}#html-body [data-pb-style=A52KBCW],#html-body [data-pb-style=KQPL5OS]{justify-content:flex-start;display:flex;flex-direction:column;width:50%;align-self:stretch}#html-body [data-pb-style=A52KBCW],#html-body [data-pb-style=IBUSNW6],#html-body [data-pb-style=KQPL5OS],#html-body [data-pb-style=ORX5H2N]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll}#html-body [data-pb-style=IBUSNW6]{justify-content:flex-start;display:flex;flex-direction:column;background-color:#4a4a4c;text-align:center}#html-body [data-pb-style=ORX5H2N]{align-self:stretch}#html-body [data-pb-style=GB1I34K]{display:flex;width:100%}
6881#html-body [data-pb-style=DOOK9LC],#html-body [data-pb-style=J2HCGFJ]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:75%;align-self:stretch}#html-body [data-pb-style=DOOK9LC]{width:25%}#html-body [data-pb-style=O9FT92K]{border-style:none}#html-body [data-pb-style=EM8VOKM],#html-body [data-pb-style=HVN8KXF]{max-width:100%;height:auto}#html-body [data-pb-style=BKXBRDP]{border-style:none}#html-body [data-pb-style=QJ1NHM1],#html-body [data-pb-style=UCIWFJV]{max-width:100%;height:auto}@media only screen and (max-width: 768px) { #html-body [data-pb-style=BKXBRDP],#html-body [data-pb-style=DYG7FMV],#html-body [data-pb-style=EMWRB6W],#html-body [data-pb-style=F01NG3C],#html-body [data-pb-style=IKQAFL9],#html-body [data-pb-style=IT0XQ3C],#html-body [data-pb-style=JX14H1R],#html-body [data-pb-style=KDGHINW],#html-body [data-pb-style=NNEXLSB],#html-body [data-pb-style=NQHK0UA],#html-body [data-pb-style=O9FT92K],#html-body [data-pb-style=QR0K1TU],#html-body [data-pb-style=R9C947Q],#html-body [data-pb-style=XYXGSXN],#html-body [data-pb-style=Y3B563C]{border-style:none} }</style><div class="hero_banner" data-content-type="row" data-appearance="full-bleed" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="U7FNYWP"><div class="pagebuilder-slider" data-content-type="slider" data-appearance="default" data-autoplay="false" data-autoplay-speed="4000" data-fade="false" data-infinite-loop="false" data-show-arrows="false" data-show-dots="false" data-element="main" data-pb-style="HCY4VHF"><div data-content-type="slide" data-slide-name="" data-appearance="poster" data-show-button="always" data-show-overlay="never" data-element="main"><a href="https://www.huskee.co/shop/new-arrivals/test" target="" data-link-type="category" title="" data-element="link"><div class="pagebuilder-slide-wrapper background-image-6ab5084375791" data-background-images='{\"desktop_image\":\"https://www.huskee.co/media/wysiwyg/Desktop_Banner_3_.jpg\",\"mobile_image\":\"https://www.huskee.co/media/wysiwyg/Mobile_Banner_2x.jpg\"}' data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="wrapper" data-pb-style="UNFOA33"><div class="pagebuilder-overlay pagebuilder-poster-overlay" data-overlay-color="" aria-label="" title="" data-element="overlay" data-pb-style="O8CO9EH"><div class="pagebuilder-poster-content"><div data-element="content"><div id="znid-158279786589" class="zn-element-wrapper zn-off-sm" data-type="banner" data-full-width="true" data-effect="none"><div class="zn-element zn-lg"><div class="zn-banner" tabindex="0" role="link"><div class="zn-children-container"><div class="zn-children"><div class="zn-child"><div class="zn-template"><div id="znid-541018233378" class="zn-element-wrapper" data-type="row" data-full-width="false" data-layout="12 12 12" data-vertical-align="top"><div class="zn-element zn-lg"><div class="zn-row" data-reverse-lg="false" data-reverse-md="false" data-reverse-sm="false"><div class="zn-column zn-column-lg-12 zn-column-md-12 zn-column-sm-12"><div class="zn-children"><div class="zn-child"><div class="zn-template"><div id="znid-196159729602" class="zn-element-wrapper" data-type="heading"><div class="zn-element zn-lg"><div id="znid-158279786589" class="zn-element-wrapper zn-off-sm" data-type="banner" data-full-width="true" data-effect="none"><div class="zn-element zn-lg"><div class="zn-banner" tabindex="0" role="link"><div class="zn-children-container"><div class="zn-children"><div class="zn-child"><div class="zn-template"><div id="znid-541018233378" class="zn-element-wrapper" data-type="row" data-full-width="false" data-layout="12 12 12" data-vertical-align="top"><div class="zn-element zn-lg"><div class="zn-row" data-reverse-lg="false" data-reverse-md="false" data-reverse-sm="false"><div class="zn-column zn-column-lg-12 zn-column-md-12 zn-column-sm-12"><div class="zn-children"><div class="zn-child"><div class="zn-template"><div id="znid-196159729602" class="zn-element-wrapper" data-type="heading"><div class="zn-element zn-lg"><h1 class="zn-heading" style="text-align: left;">Coffee dates just got a colour upgrade</h1></div></div></div></div><div class="zn-child" style="text-align: left;"><div class="zn-template"><div id="znid-248373048478" class="zn-element-wrapper" data-type="heading"><div class="zn-element zn-lg"><h2 class="zn-heading">6oz & 8oz HuskeeSteel Blush & Lemon Zest</h2></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div><div id="znid-020448823483" class="zn-element-wrapper" data-type="heading"><div class="zn-element zn-lg" style="text-align: left;"></div></div></div><button type="button" class="pagebuilder-slide-button pagebuilder-button-primary" data-element="button" data-pb-style="ANJ87GF">Shop Now</button></div></div></div><style type="text/css">.background-image-6ab5084375791 {background-image: url(https://www.huskee.co/media/wysiwyg/Desktop_Banner_3_.jpg);}@media only screen and (max-width: 300px) and (min-width: 640px) {.background-image-6ab5084375791 {background-image: url(https://www.huskee.co/media/wysiwyg/Mobile_Banner_2x.jpg);}}@media only screen and (max-width: 640px) {.background-image-6ab5084375791 {background-image: url(https://www.huskee.co/media/wysiwyg/Mobile_Banner_2x.jpg);}}</style></a></div></div><div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><style> 6882.hero_banner .pagebuilder-slide-button{background: #FCACB9;} 6883.hero_banner .pagebuilder-slide-button:hover, 6884.hero_banner .pagebuilder-slide-button:focus, 6885.hero_banner .pagebuilder-slide-button:active{background: #FCACB9;} 6886[data-content-type=row][data-appearance=full-width]>.row-full-width-inner {margin: auto;} 6887 6888.hero_banner .carousel-nav{display: none !important;} 6889 6890 6891</style></div></div><div data-content-type="row" data-appearance="full-width" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="A2SFMJA"><div class="row-full-width-inner" data-element="inner"><h1 data-content-type="heading" data-appearance="default" data-element="main" data-pb-style="RULH3FF">Reusable Coffee Cups Made From Recycled Materials</h1></div></div><div class="home_product_slider" data-content-type="row" data-appearance="full-width" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="XRYJDDR"><div class="row-full-width-inner" data-element="inner"><h2 data-content-type="heading" data-appearance="default" data-element="main" data-pb-style="IJ7M67A">SHOP OUR BESTSELLERS</h2><div data-content-type="products" data-appearance="grid" data-element="main" data-pb-style="WMMCPIU"><div class="block widget block-products-list grid"> 6892 <div class="block-content"> 6893 <div class="product-grids mx-auto grid gap-8 sm:grid-cols-2 lg:grid-cols-4"> 6894 6895<form method="post" action="https://www.huskee.co/checkout/cart/add/uenc/%25uenc%25/product/14369/" class="item product product-item product_addtocart_form grow flex flex-col w-full " data-sku="HS06PII01-S2C-2"> 6896 <input name="form_key" type="hidden" value="fSEaVeHJYxkfPQj0"> <input type="hidden" name="product" value="14369"> 6897 <a href="https://www.huskee.co/6oz-huskeesteel-cup-lid-blush" title="6oz HuskeeSteel Blush" class="product photo product-item-photo block mx-auto " tabindex="-1"> 6898 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-14369.window="$root.src = $event.detail" src="https://www.huskee.co/media/catalog/product/h/u/huskeesteel-blush-6oz_1.jpg?optimize=medium&bg-color=255,255,255&fit=bounds&height=300&width=240&canvas=240:300" loading="lazy" width="240" height="300" alt="6oz HuskeeSteel Blush Pink reusable coffee cup with lid" title="6oz HuskeeSteel Blush Pink reusable coffee cup with lid"> 6899 </a> 6900 <div class="add-to-cart"> 6901 <div class="mt-auto pt-3 justify-center flex flex-wrap items-center gap-2 6902 "> 6903 <button class="w-auto btn btn-primary justify-center text-sm cart-button" aria-label="Add to Cart 6oz HuskeeSteel Blush" data-addto="cart">
6904 <span class="inline lg:inline"> 6905 Add to Cart </span> 6906 </button> 6907 6908 <div class="flex flex-wrap gap-2 compare"> 6909
6909<script> 6910 'use strict'; 6911 6912 (() => { 6913 window.addEventListener( 6914 'ga-initialized', 6915 () => { 6916 const productForm = document.querySelector( 6917 'form\u005Bdata\u002Dsku\u003D\u0022HS06PII01\u002DS2C\u002D2\u0022\u005D' 6918 ); 6919 6920 if (!productForm) { 6921 return; 6922 } 6923 6924 let sku = 'HS06PII01\u002DS2C\u002D2'; 6925 6926 window.addEventListener( 6927 'listing\u002Dconfigurable-selection-changed', 6928 (event) => { 6929 if (event.detail.skuCandidates.length === 1 6930 && event.detail.productId === '14369' 6931 ) { 6932 sku = event.detail.skuCandidates[0]; 6933 } 6934 } 6935 ); 6936 6937 function bindEventListener(element, event) 6938 { 6939 element.addEventListener(event, () => { 6940 if (productForm.checkValidity()) { 6941 const priceSelector = false ? '#product-price-14369 .price' 6942 : '[data-price-box="product-id-14369"] .price'; 6943 6944 window.dispatchEvent(new CustomEvent('cart-item-added', { 6945 detail: [{ 6946 product_sku: sku, 6947 product_name: '6oz\u0020HuskeeSteel\u0020Blush', 6948 product_price_value: document.querySelector(priceSelector).textContent.slice(1), 6949 6950 qty: 1, 6951 }], 6952 })); 6953 } 6954 }); 6955 } 6956 6957 const button = productForm.querySelector( 6958 'button\u005Bdata\u002Daddto\u003D\u0022cart\u0022\u005D' 6959 ); 6960 6961 if (!button) { 6962 return; 6963 } 6964 6965 bindEventListener(button, 'click'); 6966 }, 6967 { once: true } 6968 ); 6969 })(); 6970</script>
6970 6971 </div> 6972 </div> 6973 </div> 6974 <div class="product-info flex flex-col grow "> 6975 <div class="product-sku font-poppins text-xs text-zinc-500 mb-1 text-center "> 6976 HS06PII01-S2C-2 </div> 6977 <div class="mb-1 items-center justify-center font-poppins font-regular text-base text-center "> 6978 <a class="product-item-link" href="https://www.huskee.co/6oz-huskeesteel-cup-lid-blush"> 6979 6oz HuskeeSteel Blush </a> 6980 </div> 6981 6982 <div class="mx-auto "> 6983 </div> 6984 6985 6986 6987 6988 <div class="text-center" x-data="initPriceBox()" x-defer="intersect" @update-prices-14369.window="updatePrice($event.detail);"> 6989 <div class="price-box price-final_price" data-role="priceBox" data-product-id="14369" data-price-box="product-id-14369"> 6990 <span x-data x-id="['product\u002Dprice\u002D14369']" class="price-container price-final_price tax weee"> 6991 <span :id="$id('product\u002Dprice\u002D14369')" data-price-amount="31.999001" data-price-type="finalPrice" class="price-wrapper "><span class="price">$32.00</span></span> 6992 </span> 6993 6994</div> </div> 6995 </div> 6996</form> 6997 6998 <div class="amlabel-position-wrapper absolute max-w-full h-fit-content w-fit-content flex z-20 6999 amlabel-position-top-right-14369-prod flex-col" :class="labelClasses" x-data="initAmlabel" x-init="initLabel" x-amintersect="render" data-amlabel-config='{"position":"top-right","size":"30","path":".product-image-container, .product-item-photo, .cdz-product-top","mode":"prod","move":0,"product":"14369","label":13,"margin":10,"alignment":0,"order":1,"redirect_url":"","tooltip":{"backgroundColor":"","color":"","status":1,"content":""}}'> 7000 <div x-ref="amLabel" x-cloak x-show="initialized" x-transition:enter="transition ease-out duration-250" x-transition:enter-start="opacity-0 transform scale-90" x-transition:enter-end="opacity-100 transform scale-100" @resize.window.debounce="resize" @click="labelClick" class="group relative amasty-label-container amasty-label-container-13-14369-prod amasty-label-for-14369" data-amlabel-order="1"> 7001 <div style=""> 7002 <div class="amlabel-text whitespace-nowrap absolute left-1/2 top-1/2 -translate-y-1/2 -translate-x-1/2"> 7003 </div> 7004 <img id="amasty-label-image-13-14369-prod" class="amasty-label-image opacity-100 align-top w-full h-full" src="https://www.huskee.co/media/amasty/
7004amlabel/NEW-Sept2024.png" title="6oz HuskeeSteel Blush" alt="6oz HuskeeSteel Blush"> 7005 </div> 7006 </div> 7007 </div> 7008 7009<form method="post" action="https://www.huskee.co/checkout/cart/add/uenc/%25uenc%25/product/14378/" class="item product product-item product_addtocart_form grow flex flex-col w-full " data-sku="HS08PZZ01-S2C-2"> 7010 <input name="form_key" type="hidden" value="fSEaVeHJYxkfPQj0"> <input type="hidden" name="product" value="14378"> 7011 <a href="https://www.huskee.co/8oz-huskeesteel-cup-lid-lemon-zest" title="8oz HuskeeSteel Lemon Zest" class="product photo product-item-photo block mx-auto " tabindex="-1"> 7012 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-14378.window="$root.src = $event.detail" src="https://www.huskee.co/media/catalog/product/h/u/huskeesteel-lemon-zest-8oz_1.jpg?optimize=medium&bg-color=255,255,255&fit=bounds&height=300&width=240&canvas=240:300" loading="lazy" width="240" height="300" alt="8oz HuskeeSteel Lemon Zest reusable coffee cup with lid" title="8oz HuskeeSteel Lemon Zest reusable coffee cup with lid"> 7013 </a> 7014 <div class="add-to-cart"> 7015 <div class="mt-auto pt-3 justify-center flex flex-wrap items-center gap-2 7016 "> 7017 <button class="w-auto btn btn-primary justify-center text-sm cart-button" aria-label="Add to Cart 8oz HuskeeSteel Lemon Zest" data-addto="cart"> 7018 <span class="inline lg:inline"> 7019 Add to Cart </span> 7020 </button> 7021 7022 <div class="flex flex-wrap gap-2 compare"> 7023
7023<script> 7024 'use strict'; 7025 7026 (() => { 7027 window.addEventListener( 7028 'ga-initialized', 7029 () => { 7030 const productForm = document.querySelector( 7031 'form\u005Bdata\u002Dsku\u003D\u0022HS08PZZ01\u002DS2C\u002D2\u0022\u005D' 7032 ); 7033 7034 if (!productForm) { 7035 return; 7036 } 7037 7038 let sku = 'HS08PZZ01\u002DS2C\u002D2'; 7039 7040 window.addEventListener( 7041 'listing\u002Dconfigurable-selection-changed', 7042 (event) => { 7043 if (event.detail.skuCandidates.length === 1 7044 && event.detail.productId === '14378' 7045 ) { 7046 sku = event.detail.skuCandidates[0]; 7047 } 7048 } 7049 ); 7050 7051 function bindEventListener(element, event) 7052 { 7053 element.addEventListener(event, () => { 7054 if (productForm.checkValidity()) { 7055 const priceSelector = false ? '#product-price-14378 .price' 7056 : '[data-price-box="product-id-14378"] .price'; 7057 7058 window.dispatchEvent(new CustomEvent('cart-item-added', { 7059 detail: [{ 7060 product_sku: sku, 7061 product_name: '8oz\u0020HuskeeSteel\u0020Lemon\u0020Zest', 7062 product_price_value: document.querySelector(priceSelector).textContent.slice(1), 7063 7064 qty: 1, 7065 }], 7066 })); 7067 } 7068 }); 7069 } 7070 7071 const button = productForm.querySelector( 7072 'button\u005Bdata\u002Daddto\u003D\u0022cart\u0022\u005D' 7073 ); 7074 7075 if (!button) { 7076 return; 7077 } 7078 7079 bindEventListener(button, 'click'); 7080 }, 7081 { once: true } 7082 ); 7083 })(); 7084</script>
7084 7085 </div> 7086 </div> 7087 </div> 7088 <div class="product-info flex flex-col grow "> 7089 <div class="product-sku font-poppins text-xs text-zinc-500 mb-1 text-center "> 7090 HS08PZZ01-S2C-2 </div> 7091 <div class="mb-1 items-center justify-center font-poppins font-regular text-base text-center "> 7092 <a class="product-item-link" href="https://www.huskee.co/8oz-huskeesteel-cup-lid-lemon-zest"> 7093 8oz HuskeeSteel Lemon Zest </a> 7094 </div> 7095 7096 <div class="mx-auto "> 7097 </div> 7098 7099 7100 7101 7102 <div class="text-center" x-data="initPriceBox()" x-defer="intersect" @update-prices-14378.window="updatePrice($event.detail);"> 7103 <div class="price-box price-final_price" data-role="priceBox" data-product-id="14378" data-price-box="product-id-14378"> 7104 <span x-data x-id="['product\u002Dprice\u002D14378']" class="price-container price-final_price tax weee"> 7105 <span :id="$id('product\u002Dprice\u002D14378')" data-price-amount="34.001001" data-price-type="finalPrice" class="price-wrapper "><span class="price">$34.00</span></span> 7106 </span> 7107 7108</div> </div> 7109 </div> 7110</form> 7111 7112 <div class="amlabel-position-wrapper absolute max-w-full h-fit-content w-fit-content flex z-20 7113 amlabel-position-top-right-14378-prod flex-col" :class="labelClasses" x-data="initAmlabel" x-init="initLabel" x-amintersect="render" data-amlabel-config='{"position":"top-right","size":"30","path":".product-image-container, .product-item-photo, .cdz-product-top","mode":"prod","move":0,"product":"14378","label":13,"margin":10,"alignment":0,"order":1,"redirect_url":"","tooltip":{"backgroundColor":"","color":"","status":1,"content":""}}'> 7114 <div x-ref="amLabel" x-cloak x-show="initialized" x-transition:enter="transition ease-out duration-250" x-transition:enter-start="opacity-0 transform scale-90" x-transition:enter-end="opacity-100 transform scale-100" @resize.window.debounce="resize" @click="labelClick" class="group relative amasty-label-container amasty-label-container-13-14378-prod amasty-label-for-14378" data-amlabel-order="1"> 7115 <div style=""> 7116 <div class="amlabel-text whitespace-nowrap absolute left-1/2 top-1/2 -translate-y-1/2 -translate-x-1/2"> 7117 </div> 7118 <img id="amasty-label-image-13-14378-prod" class="amasty-label-image opacity-100 align-top w-full h-full" src="https://www.huskee.co/media/amasty/
7118amlabel/NEW-Sept2024.png" title="8oz HuskeeSteel Lemon Zest" alt="8oz HuskeeSteel Lemon Zest"> 7119 </div> 7120 </div> 7121 </div> 7122 7123<form method="post" action="https://www.huskee.co/checkout/cart/add/uenc/%25uenc%25/product/13371/" class="item product product-item product_addtocart_form grow flex flex-col w-full " data-sku="HC08LC01-E"> 7124 <input name="form_key" type="hidden" value="fSEaVeHJYxkfPQj0"> <input type="hidden" name="product" value="13371"> 7125 <a href="https://www.huskee.co/8oz-huskeeoriginal-charcoal" title="8oz HuskeeOriginal Charcoal" class="product photo product-item-photo block mx-auto " tabindex="-1"> 7126 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-13371.window="$root.src = $event.detail" src="https://www.huskee.co/media/catalog/product/8/o/8oz-cup-lid-charcoal_1.jpg?optimize=medium&bg-color=255,255,255&fit=bounds&height=300&width=240&canvas=240:300" loading="lazy" width="240" height="300" alt="8oz HuskeeOriginal Charcoal" title="8oz HuskeeOriginal Charcoal"> 7127 </a> 7128 <div class="add-to-cart"> 7129 <div class="mt-auto pt-3 justify-center flex flex-wrap items-center gap-2 7130 "> 7131 <button class="w-auto btn btn-primary justify-center text-sm cart-button" aria-label="Add to Cart 8oz HuskeeOriginal Charcoal" data-addto="cart"> 7132 <span class="inline lg:inline"> 7133 Add to Cart </span> 7134 </button> 7135 7136 <div class="flex flex-wrap gap-2 compare"> 7137
7137<script> 7138 'use strict'; 7139 7140 (() => { 7141 window.addEventListener( 7142 'ga-initialized', 7143 () => { 7144 const productForm = document.querySelector( 7145 'form\u005Bdata\u002Dsku\u003D\u0022HC08LC01\u002DE\u0022\u005D' 7146 ); 7147 7148 if (!productForm) { 7149 return; 7150 } 7151 7152 let sku = 'HC08LC01\u002DE'; 7153 7154 window.addEventListener( 7155 'listing\u002Dconfigurable-selection-changed', 7156 (event) => { 7157 if (event.detail.skuCandidates.length === 1 7158 && event.detail.productId === '13371' 7159 ) { 7160 sku = event.detail.skuCandidates[0]; 7161 } 7162 } 7163 ); 7164 7165 function bindEventListener(element, event) 7166 { 7167 element.addEventListener(event, () => { 7168 if (productForm.checkValidity()) { 7169 const priceSelector = false ? '#product-price-13371 .price' 7170 : '[data-price-box="product-id-13371"] .price'; 7171 7172 window.dispatchEvent(new CustomEvent('cart-item-added', { 7173 detail: [{ 7174 product_sku: sku, 7175 product_name: '8oz\u0020HuskeeOriginal\u0020Charcoal', 7176 product_price_value: document.querySelector(priceSelector).textContent.slice(1), 7177 7178 qty: 1, 7179 }], 7180 })); 7181 } 7182 }); 7183 } 7184 7185 const button = productForm.querySelector( 7186 'button\u005Bdata\u002Daddto\u003D\u0022cart\u0022\u005D' 7187 ); 7188 7189 if (!button) { 7190 return; 7191 } 7192 7193 bindEventListener(button, 'click'); 7194 }, 7195 { once: true } 7196 ); 7197 })(); 7198</script>
7198 7199 </div> 7200 </div> 7201 </div> 7202 <div class="product-info flex flex-col grow "> 7203 <div class="product-sku font-poppins text-xs text-zinc-500 mb-1 text-center "> 7204 HC08LC01-E </div> 7205 <div class="mb-1 items-center justify-center font-poppins font-regular text-base text-center "> 7206 <a class="product-item-link" href="https://www.huskee.co/8oz-huskeeoriginal-charcoal"> 7207 8oz HuskeeOriginal Charcoal </a> 7208 </div> 7209 7210 <div class="mx-auto "> 7211 </div> 7212 7213 7214 7215 7216 <div class="text-center" x-data="initPriceBox()" x-defer="intersect" @update-prices-13371.window="updatePrice($event.detail);"> 7217 <div class="price-box price-final_price" data-role="priceBox" data-product-id="13371" data-price-box="product-id-13371"> 7218 <span x-data x-id="['product\u002Dprice\u002D13371']" class="price-container price-final_price tax weee"> 7219 <span :id="$id('product\u002Dprice\u002D13371')" data-price-amount="19.998001" data-price-type="finalPrice" class="price-wrapper "><span class="price">$20.00</span></span> 7220 </span> 7221 7222</div> </div> 7223 </div> 7224</form> 7225 7226<form method="post" action="https://www.huskee.co/checkout/cart/add/uenc/%25uenc%25/product/13335/" class="item product product-item product_addtocart_form grow flex flex-col w-full " data-sku="HT12PA01-R"> 7227 <input name="form_key" type="hidden" value="fSEaVeHJYxkfPQj0"> <input type="hidden" name="product" value="13335"> 7228 <a href="https://www.huskee.co/12oz-huskeerenew-amber" title="12oz HuskeeRenew Amber" class="product photo product-item-photo block mx-auto " tabindex="-1"> 7229 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-13335.window="$root.src = $event.detail" src="https://www.huskee.co/media/catalog/product/1/2/12oz-cup-lid-amber_2.jpg?optimize=medium&bg-color=255,255,255&fit=bounds&height=300&width=240&canvas=240:300" loading="lazy" width="240" height="300" alt="12oz HuskeeRenew Amber" title="12oz HuskeeRenew Amber"> 7230 </a> 7231 <div class="add-to-cart"> 7232 <div class="mt-auto pt-3 justify-center flex flex-wrap items-center gap-2 7233 "> 7234 <button class="w-auto btn btn-primary justify-center text-sm cart-button" aria-label="Add to Cart 12oz HuskeeRenew Amber" data-addto="cart"> 7235 <span class="inline lg:inline"> 7236 Add to Cart </span> 7237 </button> 7238 7239 <div class="flex flex-wrap gap-2 compare"> 7240
7240<script> 7241 'use strict'; 7242 7243 (() => { 7244 window.addEventListener( 7245 'ga-initialized', 7246 () => { 7247 const productForm = document.querySelector( 7248 'form\u005Bdata\u002Dsku\u003D\u0022HT12PA01\u002DR\u0022\u005D' 7249 ); 7250 7251 if (!productForm) { 7252 return; 7253 } 7254 7255 let sku = 'HT12PA01\u002DR'; 7256 7257 window.addEventListener( 7258 'listing\u002Dconfigurable-selection-changed', 7259 (event) => { 7260 if (event.detail.skuCandidates.length === 1 7261 && event.detail.productId === '13335' 7262 ) { 7263 sku = event.detail.skuCandidates[0]; 7264 } 7265 } 7266 ); 7267 7268 function bindEventListener(element, event) 7269 { 7270 element.addEventListener(event, () => { 7271 if (productForm.checkValidity()) { 7272 const priceSelector = false ? '#product-price-13335 .price' 7273 : '[data-price-box="product-id-13335"] .price'; 7274 7275 window.dispatchEvent(new CustomEvent('cart-item-added', { 7276 detail: [{ 7277 product_sku: sku, 7278 product_name: '12oz\u0020HuskeeRenew\u0020Amber', 7279 product_price_value: document.querySelector(priceSelector).textContent.slice(1), 7280 7281 qty: 1, 7282 }], 7283 })); 7284 } 7285 }); 7286 } 7287 7288 const button = productForm.querySelector( 7289 'button\u005Bdata\u002Daddto\u003D\u0022cart\u0022\u005D' 7290 ); 7291 7292 if (!button) { 7293 return; 7294 } 7295 7296 bindEventListener(button, 'click'); 7297 }, 7298 { once: true } 7299 ); 7300 })(); 7301</script>
7301 7302 </div> 7303 </div> 7304 </div> 7305 <div class="product-info flex flex-col grow "> 7306 <div class="product-sku font-poppins text-xs text-zinc-500 mb-1 text-center "> 7307 HT12PA01-R </div> 7308 <div class="mb-1 items-center justify-center font-poppins font-regular text-base text-center "> 7309 <a class="product-item-link" href="https://www.huskee.co/12oz-huskeerenew-amber"> 7310 12oz HuskeeRenew Amber </a> 7311 </div> 7312 7313 <div class="mx-auto "> 7314 </div> 7315 7316 7317 7318 7319 <div class="text-center" x-data="initPriceBox()" x-defer="intersect" @update-prices-13335.window="updatePrice($event.detail);"> 7320 <div class="price-box price-final_price" data-role="priceBox" data-product-id="13335" data-price-box="product-id-13335"> 7321 <span x-data x-id="['product\u002Dprice\u002D13335']" class="price-container price-final_price tax weee"> 7322 <span :id="$id('product\u002Dprice\u002D13335')" data-price-amount="31.999001" data-price-type="finalPrice" class="price-wrapper "><span class="price">$32.00</span></span> 7323 </span> 7324 7325</div> </div> 7326 </div> 7327</form> 7328 </div> 7329 </div> 7330 </div>
7330<script type="text/x-magento-init"> 7331 {"#product_addtocart_form:not([amcart-observed]), form[data-role=\"tocart-form\"]:not([amcart-observed]), .action.tocart:not([amcart-observed])":{"Amasty_Cart\/js\/amcart":{"send_url":"https:\/\/www.huskee.co\/amasty_cart\/cart\/add\/","src_image_progress":"https:\/\/www.huskee.co\/static\/version1789639671\/frontend\/Huskee\/default\/en_AU\/Amasty_Cart\/images\/loading.gif","type_loading":"0","align":"0","open_minicart":false,"autosubmit":0,"page_id":null}}} </script>
7331 7332 7333 </div></div></div><div data-content-type="row" data-appearance="full-width" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="SMSBL75"><div class="row-full-width-inner" data-element="inner"><h2 data-content-type="heading" data-appearance="default" data-element="main" data-pb-style="AEMAE3Q">Shop by Range</h2><div class="pagebuilder-column-group" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="false" data-element="main" data-pb-style="M66Y6U7"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="J8DIQWY"><div class="pagebuilder-column" data-content-type="column" data-appearance="align-center" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="PM7PX72"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="NNEXLSB"><a href="https://www.huskee.co/shop/shop-by-collection/huskeeoriginal-category" target="" data-link-type="category" title="" data-element="link"><img class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/huskeeoriginal.jpg" alt="8oz HuskeeOriginal Charcoal cup and lid" title="" loading="lazy" data-use-native-image-dimensions="false" width="400" height="" data-element="desktop_image" data-pb-style="K53BFSX"><img class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/huskeeoriginal_1.jpg" alt="8oz HuskeeOriginal Charcoal cup and lid" title="" loading="lazy" data-use-native-image-dimensions="false" width="" height="" data-element="mobile_image" data-pb-style="UQ84C43"></a></figure></div><div class="pagebuilder-column" data-content-type="column" data-appearance="align-center" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="HTMP3RK"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="IKQAFL9"><a href="https://www.huskee.co/shop/shop-by-collection/huskeerenew-category" target="" data-link-type="category" title="" data-element="link"><img class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/huskeerenew.jpg" alt="8oz HuskeeRenew Amber cup and lid" title="" loading="lazy" data-use-native-image-dimensions="false" width="400" height="" data-element="desktop_image" data-pb-style="O9U76GU"><img class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/huskeerenew_1.jpg" alt="8oz HuskeeRenew Amber cup and lid" title="" loading="lazy" data-use-native-image-dimensions="false" width="" height="" data-element="mobile_image" data-pb-style="DW08X88"></a></figure></div><div class="pagebuilder-column" data-content-type="column" data-appearance="align-center" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="PK8KYBD"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="KDGHINW"><a href="https://www.huskee.co/shop/shop-by-collection/huskeesteel-category" target="" data-link-type="category" title="" data-element="link"><img class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/huskeesteel.jpg" alt="8oz HuskeeSteel Blush cup and lid" title="" loading="lazy" data-use-native-image-dimensions="false" width="400" height="" data-element="desktop_image" data-pb-style="Y5YEU5X"><img class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/huskeesteel_1.jpg" alt="8oz HuskeeSteel Blush cup and lid" title="" loading="lazy" data-use-native-image-dimensions="false" width="" height="" data-element="mobile_image" data-pb-style="TNG5VAJ"></a></figure></div></div></div></div></div><div data-content-type="row" data-appearance="contained" data-element="main"><div data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="" data-element="inner" data-pb-style="MAGNNRH"><div class="pagebuilder-column-group home_content_section" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="false" data-element="main" data-pb-style="AKXBE72"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="ECH84IM"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="PMUBSBU"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="XYXGSXN"><img width="600" height="462" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/huskee-original-range_1.jpg" alt="5 cups from the HuskeeOriginal range with lids and a saucer" title="" loading="lazy" data-element="desktop_image" data-pb-style="NQE9MAT"><img width="600" height="462" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/huskee-original-range_1.jpg" alt="5 cups from the HuskeeOriginal range with lids and a saucer" title="" loading="lazy" data-element="mobile_image" data-pb-style="DVDRM82"></figure></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="OEOWFA5"><h1 data-content-type="heading" data-appearance="default" data-element="main">DESIGNED FOR A WASTE FREE WORLD</h1><div data-content-type="text" data-appearance="default" data-element="main"><p>At Huskee, we design functional and beautiful products that use waste and recycled material to help the coffee industry move away from single-use.<br><br>Our journey began with the iconic HuskeeOriginal — the Huskee coffee mug that started it all. A modular, reusable cup for home, café or takeaway, moulded from waste coffee husk and available from 3oz espresso cup sizing right up to 16oz.<br><br>Since then we've added HuskeeRenew, made from recycled plastic, and HuskeeSteel, our double-walled stainless steel range — plus brand coffee cups for cafés, hotels and offices that want their own logo on the cup.</p></div></div></div></div></div></div><div class="full_banner" data-content-type="row" data-appearance="full-bleed" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="LY4EG4E"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="Y3B563C"><img width="2374" height="1000" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/.renditions/wysiwyg/Huskee-PR-Shoot-Output-HiRes-18.jpg" alt="6oz HuskeeOriginal charcoal cup and saucer in a cafe" title="" loading="lazy" data-element="desktop_image" data-pb-style="E68SA46"><img width="2374" height="1000" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/.renditions/wysiwyg/Huskee-PR-Shoot-Output-HiRes-18.jpg" alt="6oz HuskeeOriginal charcoal cup and saucer in a cafe" title="" loading="lazy" data-element="mobile_image" data-pb-style="KQ6G3PW"></figure><div class="pagebuilder-column-group home_content_section" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="false" data-element="main" data-pb-style="O80KWAS"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="SKK11AV"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="O0BRCJ8"><div data-content-type="text" data-appearance="default" data-element="main" data-pb-style="A538RNU"><p>Learn more about our end of life program and discover how your purchase will c
7333ontribute to a waste-free world.</p></div><div data-content-type="buttons" data-appearance="inline" data-same-width="false" data-element="main"><div data-content-type="button-item" data-appearance="default" data-element="main" data-pb-style="LMXFIGC"><a class="pagebuilder-button-primary" href="https://www.huskee.co/huskeeloop" target="" data-link-type="page" data-element="link" data-pb-style="I0I9565"><span data-element="link_text">Read about HuskeeLoop</span></a></div></div></div></div></div></div><div class="the_stats" data-content-type="row" data-appearance="full-width" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="OWWJ190"><div class="row-full-width-inner" data-element="inner"><h3 data-content-type="heading" data-appearance="default" data-element="main" data-pb-style="OQPTY95">THE STATS</h3><div data-content-type="text" data-appearance="default" data-element="main"><p>We want to reduce waste in the coffee industry. Here are some compelling statistics.</p></div><div class="pagebuilder-column-group" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="" data-element="main" data-pb-style="K9JDSCG"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="G9MX4EJ"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="QLX9COF"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="F01NG3C"><img width="285" height="139" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/.renditions/wysiwyg/84281156.jpg" alt="" title="" loading="lazy" data-element="desktop_image" data-pb-style="X3LAMVW"><img width="285" height="139" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/.renditions/wysiwyg/84281156.jpg" alt="" title="" loading="lazy" data-element="mobile_image" data-pb-style="A40A13C"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>Australians consume more than 50,000 cups of coffee every half hour.</p></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="JH5LLG6"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="IT0XQ3C"><img width="285" height="139" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/.renditions/wysiwyg/30625730.jpg" alt="" title="" loading="lazy" data-element="desktop_image" data-pb-style="EA54UYV"><img width="285" height="139" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/.renditions/wysiwyg/30625730.jpg" alt="" title="" loading="lazy" data-element="mobile_image" data-pb-style="EM8K6KS"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>600 Billion disposable coffee cups are produced worldwide each year.</p></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="A65YT3R"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="QR0K1TU"><img width="285" height="139" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/.renditions/wysiwyg/44345501.jpg" alt="" title="" loading="lazy" data-element="desktop_image" data-pb-style="J2I147K"><img width="285" height="139" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/.renditions/wysiwyg/44345501.jpg" alt="" title="" loading="lazy" data-element="mobile_image" data-pb-style="XV16VK6"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>Up to 90% of disposable coffee cups are not recycled.</p></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="WTRSMYQ"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="EMWRB6W"><img width="285" height="139" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/.renditions/wysiwyg/66140534.jpg" alt="" title="" loading="lazy" data-element="desktop_image" data-pb-style="FBW2CXL"><img width="285" height="139" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/.renditions/wysiwyg/66140534.jpg" alt="" title="" loading="lazy" data-element="mobile_image" data-pb-style="WD4B7MT"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>60,000 kgs of waste from cups goes to landfill each year in Aus alone.</p></div></div></div></div></div></div><div class="two_columns" data-content-type="row" data-appearance="full-width" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="J4TLBX2"><div class="row-full-width-inner" data-element="inner"><div class="pagebuilder-column-group" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="" data-element="main" data-pb-style="M61BPTM"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="AJ75D35"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="YCARII9"><h3 data-content-type="heading" data-appearance="default" data-element="main">HuskeeTech</h3><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="NQHK0UA"><img width="520" height="520" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/29296998-resave-46669442.png" alt="Waste Coffee Husk" title="" loading="lazy" data-element="desktop_image" data-pb-style="Y2F7W20"><img width="520" height="520" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/29296998-resave-46669442.png" alt="Waste Coffee Husk" title="" loading="lazy" data-element="mobile_image" data-pb-style="AKQ3BAD"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>
7333Our unique eco-composite polymer made from coffee husk that forms the basis of our HuskeeOriginal cups, saucers and lids.</p></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="EB2GFP9"><h3 data-content-type="heading" data-appearance="default" data-element="main">HuskeeOriginal</h3><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="DYG7FMV"><img width="520" height="520" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/24053621-resave-62537500.png" alt="stacked 6oz HuskeeOriginal Natural and 8oz HuskeeOriginal Natural" title="" loading="lazy" data-element="desktop_image" data-pb-style="TRC8F2D"><img width="520" height="520" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/24053621-resave-62537500.png" alt="stacked 6oz HuskeeOriginal Natural and 8oz HuskeeOriginal Natural" title="" loading="lazy" data-element="mobile_image" data-pb-style="OVSCXM6"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>The ultimate reusable cup. Durable, beautiful and ready for anything & anywhere.</p></div></div></div></div><div class="pagebuilder-column-group" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="" data-element="main" data-pb-style="PDID3XJ"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="BGYDJ8S"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="PEPG4BF"><h3 data-content-type="heading" data-appearance="default" data-element="main">HuskeeSwap</h3><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="R9C947Q"><img width="520" height="520" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/75296297.png" alt="HuskeeSwap icon" title="" loading="lazy" data-element="desktop_image" data-pb-style="JNUBA25"><img width="520" height="520" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/75296297.png" alt="HuskeeSwap icon" title="" loading="lazy" data-element="mobile_image" data-pb-style="V7XAHIT"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>Our unique cup exchange system for cafes, designed for baristas and coffee lovers.</p></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="UGNDHOU"><h3 data-content-type="heading" data-appearance="default" data-element="main">HuskeeLoop</h3><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="JX14H1R"><img width="480" height="480" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/wysiwyg/test-webp-96636680.png" alt="HuskeeLoop Icon" title="" loading="lazy" data-element="desktop_image" data-pb-style="G9B4PV3"><img width="480" height="480" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/wysiwyg/test-webp-96636680.png" alt="HuskeeLoop Icon" title="" loading="lazy" data-element="mobile_image" data-pb-style="PCQKEPJ"></figure><div data-content-type="text" data-appearance="default" data-element="main"><p>Our <span style="color: rgb(33, 150, 243);"><a style="color: rgb(33, 150, 243);" href="/huskeeloop/">closed loop system</a></span> repurposes end of life cups to create new products, so no cups end up in landfill.</p></div></div></div></div></div></div><div data-content-type="row" data-appearance="full-width" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="FU676B0"><div class="row-full-width-inner" data-element="inner"><div class="pagebuilder-column-group" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="" data-element="main" data-pb-style="H93YP00"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="EFKHDO2"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="KQPL5OS"><div data-content-type="text" data-appearance="default" data-element="main"><h4 id="PJQSIPQ">Sign up to our Emails</h4> 7334<p>Be the first to hear about new products and sales.</p></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="A52KBCW"><div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><div class="klaviyo-form-UbZ7K7"></div></div></div></div></div></div></div><div class="awards_section" data-content-type="row" data-appearance="full-width" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="IBUSNW6"><div class="row-full-width-inner" data-element="inner"><div class="pagebuilder-column-group" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="12" data-background-lazy-load="" data-element="main" data-pb-style="ORX5H2N"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="GB1I34K"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="J2HCGFJ"><div data-content-type="text" data-appearance="default" data-element="main"><p>We acknowledge the Traditional Custodians of the land where we founded Huskee, the Garigal and Cammeraygal People. We pay our respects to all First Nations Elders - past and present.</p></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="DOOK9LC"><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="O9FT92K"><img width="90" height="34" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/.renditions/wysiwyg/Good-Design-Award-Best-in-Class.png" alt="" title="" loading="lazy" data-element="desktop_image" data-pb-style="EM8VOKM"><img width="90" height="34" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/.renditions/wysiwyg/Good-Design-Award-Best-in-Class.png" alt="" title="" loading="lazy" data-element="mobile_image" data-pb-style="HVN8KXF"></figure><figure data-content-type="image" data-appearance="full-width" data-element="main" data-pb-style="BKXBRDP"><img width="60" height="88" class="pagebuilder-mobile-hidden" src="https://www.huskee.co/media/.renditions/wysiwyg/2018-B-Corp-Logo-White-M-600211
73347.png" alt="" title="" loading="lazy" data-element="desktop_image" data-pb-style="UCIWFJV"><img width="60" height="88" class="pagebuilder-mobile-only" src="https://www.huskee.co/media/.renditions/wysiwyg/2018-B-Corp-Logo-White-M-6002117.png" alt="" title="" loading="lazy" data-element="mobile_image" data-pb-style="QJ1NHM1"></figure></div></div></div></div></div> 7335 <form class="amhideprice-form" id="amgroupcat-form" action="" method="post" style="display: none;"> 7336 <input name="form_key" type="hidden" value="" /> 7337 <input name="product_id" type="hidden" value="" /> 7338 <fieldset class="fieldset"> 7339 <legend class="legend amhideprice-form-ledend"> 7340 <span>Get a Quote for</span> 7341 <span style="font-weight: bolder;" class="product-name"></span> 7342 </legend> 7343 <br> 7344 <div class="amhideprice-fields-container"> 7345 <div class="field required"> 7346 <label for="amgroupcat-name" class="label"> 7347 <span>Customer Name</span> 7348 </label> 7349 <div class="control"> 7350 <input type="text" name="name" class="input-text" 7351 id="amgroupcat-name" 7352 data-validate="{required:true}"> 7353 </div> 7354 </div> 7355 <div class="field required"> 7356 <label for="amgroupcat-email" class="label"> 7357 <span>Customer Email</span> 7358 </label> 7359 <div class="control"> 7360 <input type="email" 7361 name="email" 7362 autocomplete="email" 7363 id="amgroupcat-email" 7364 value="" class="input-text" 7365 data-validate="{required:true, 'validate-email':true}" 7366 aria-required="true"> 7367 </div> 7368 </div> 7369 <div class="field required"> 7370 <label for="amgroupcat-phone" class="label"> 7371 <span>Phone Number</span> 7372 </label> 7373 <div class="control"> 7374 <input type="text" name="phone" id="amgroupcat-phone" class="input-text " 7375 data-validate="{required:true}"> 7376 </div> 7377 </div> 7378 <div class="field"> 7379 <label for="amgroupcat-comment" class="label"> 7380 <span>Comment</span> 7381 </label> 7382 <div class="control"> 7383 <textarea title="Comment" 7384 type="textarea" 7385 id="amgroupcat-comment" 7386 name="comment" class="input-text" 7387 data-validate="{'validate-no-html-tags':true}"></textarea> 7388 </div> 7389 </div> 7390 <div class="actions-toolbar"> 7391 <div class="primary"> 7392 <button type="submit" class="action submit primary" 7393 title="Submit"> 7394 <span>Submit</span> 7395 </button> 7396 </div> 7397 </div> 7398 </div> 7399 </fieldset> 7400 </form> 7401 7402 7403 7404
7404<script> 7405 'use strict'; 7406 7407 function initAmScroll() { 7408 return { 7409 options: { 7410 product_container: '.products.products-grid, .products.products-list, .products-grid.grid', 7411 product_link: '.product-item-link', 7412 footerSelector: '.page-footer' 7413 }, 7414 type: 'auto', 7415 is_loading: 0, 7416 next_data_url: "", 7417 prev_data_url: "", 7418 next_data_cache: "", 7419 flag_next_cache: 0, 7420 prev_data_cache: "", 7421 flag_prev_cache: 0, 7422 pageFirst: 1, 7423 pagesCount: 1, 7424 pagesLoaded: [], 7425 currentPage: 1, 7426 last_scroll: 0, 7427 disabled: 0, 7428 additionalHeight: null, 7429 classes: { 7430 loadButton: 'amscroll-load-button', 7431 loadingBlock: 'amscroll-loading-block', 7432 loadingIcon: 'amscroll-loading-icon', 7433 loadingIconSource: '#amscroll-loading-icon-source', 7434 svgPathToFill: 'amscroll-fill-path', 7435 backToTopButton: '#amscroll-back-to-top', 7436 active: '-amscroll-active', 7437 text: 'amscroll-text', 7438 animate: '-amscroll-animate', 7439 pageNumber: 'amscroll-page-num' 7440 }, 7441 pageSelector: '[amscroll-page="%1"]', 7442 lastPageSelector: '.amscroll-page, .amscroll-pages', 7443 totalAmountSelector: '.toolbar-amount', 7444 totalNumberSelector: '.toolbar-number', 7445 toolbarSelector: '.toolbar.toolbar-products', 7446 amPageCountSelector: '#am-page-count', 7447 canShowAfter: false, 7448 canShowBefore: false, 7449 pagesBeforeButton: 0, 7450 isReinitialized: false, 7451 afterButton: null, 7452 beforeButton: null, 7453 insertedPages: 0, 7454 afterStepBackData: null, 7455 isBackToTopVisible: false, 7456 backToTopTriggerHeight: 400, 7457 loadingBlock: { 7458 before: { 7459 node: undefined, 7460 visible: false 7461 }, 7462 after: { 7463 node: undefined, 7464 visible: false 7465 } 7466 }, 7467 /** 7468 * @returns {void} 7469 */ 7470 processInit() { 7471 this.options = {"product_container":".products.products-grid:not(.amasty-banners), .products.products-list:not(.amasty-banners), .products.grid:not(.amasty-banners)","product_link":".product-item-link, .product-item-name>span","loadingImage":"https:\/\/www.huskee.co\/static\/version1789639671\/frontend\/Huskee\/default\/en_AU\/Amasty_Scroll\/images\/loader.svg","pageNumbers":"0","pageNumberContent":"Page #","pageNumberStyle":"button","buttonColor":"#4776F0","buttonColorPressed":"#1846BE","loadingafterTextButton":"Load next","loadingbeforeTextButton":"Load previous","backToTop":{"enabled":"0","style_desktop":"text","style_mobile":"arrow","color":"#4776F0","styles":"background: #3A4F62;"},"backToTopText":"Back to Top","current_page":1,"footerSelector":".page-footer","actionMode":"auto"}; 7472 7473 if (document.querySelectorAll(this.options.product_container).length) { 7474 this._initNodes(); 7475 this.initialize(); 7476 } 7477 7478 window.addEventListener('amfiltersUpdated', () => { 7479 typeof this.initialize === 'function' && this.initialize(); 7480 }); 7481 }, 7482 /** 7483 * @private 7484 * @returns {void} 7485 */ 7486 _initNodes() { 7487 this.loadButtonElement = document.createElement('button'); 7488 this.loadButtonElement.setAttribute('class', 'btn btn-primary ' + this.classes.loadButton); 7489 this.loadButtonElement.setAttribute('style', this.options['loadNextStyle']); 7490 7491 this.loadButtonTextElement = document.createElement('span'); 7492 this.loadButtonTextElement.setAttribute('class', this.classes.text); 7493 7494 const loader = document.querySelector(this.classes.loadingIconSource);
7495 const productContainer = document.querySelector(this.options.product_container); 7496 if (!productContainer) { 7497 return; 7498 } 7499 const productContainerParent = productContainer.parentElement; 7500 //fix compatibility with Hyva Csp for Magento_Catalog::product/list/item.phtml 7501 productContainerParent?.setAttribute('x-data', 'initAmScrollSliderItem'); 7502 this.loadingBlock.before.node = productContainerParent.insertBefore( 7503 loader.content.firstElementChild.cloneNode(true), 7504 productContainer 7505 ); 7506 this.loadingBlock.after.node = productContainerParent.insertBefore( 7507 loader.content.firstElementChild.cloneNode(true), 7508 productContainerParent.lastChild 7509 ); 7510 }, 7511 /** 7512 * @returns {void} 7513 */ 7514 initialize() { 7515 this.next_data_cache = ""; 7516 this.pagesLoaded = []; 7517 this._initPagesCount(); 7518 this.disabled = 1; 7519 7520 if (!this._validate()) { 7521 return; 7522 } 7523 7524 this.disabled = 0; 7525 this.type = this.options['actionMode']; 7526 this.pagesBeforeButton = this.options['pages_before_button']; 7527 7528 this.currentPage = this._getCurrentPage(); 7529 this.pagesLoaded.push(this.currentPage); 7530 7531 if (this.type === 'button') { 7532 this._generateButton('before'); 7533 this._generateButton('after'); 7534 } 7535 7536 this._preloadPages(); 7537 this._hideToolbars(); 7538 this.initPageStepForwardListener(this.currentPage); 7539 this._pagePositionAfterStepBack(); 7540 }, 7541 /** 7542 * @private 7543 * @returns {boolean} 7544 */ 7545 _validate() { 7546 switch (true) { 7547 case !this.options['product_container'] || document.querySelectorAll(this.options['product_container']).length === 0 : 7548 console.warn('Please specify "Products Group" DOM selector in module settings.'); 7549 return false; 7550 case !this.options['product_link'] || document.querySelectorAll(this.options['product_link']).length === 0 : 7551 console.warn('Please specify "Product Link" DOM selector in module settings.'); 7552 return false; 7553 case !this.options['footerSelector'] || document.querySelectorAll(this.options['footerSelector']).length === 0 : 7554 console.warn('Please specify "Footer Selector" DOM selector in module settings.'); 7555 return false; 7556 case this.pagesCount <= 1 : 7557 return false; 7558 } 7559 7560 return true; 7561 }, 7562 /** 7563 * @private 7564 * @returns {void} 7565 */ 7566 _externalAfterAjax() { 7567 this.insertedPages++; 7568 try { 7569 if (document.getElementsByTagName('head')[0].innerHTML.indexOf('Infortis') > -1) { 7570 document.dispatchEvent('last-swatch-found'); 7571 } 7572 7573 if (this.insertedPages < this.pagesBeforeButton) { 7574 return; 7575 } 7576 7577 if (this.type === 'auto' && this.options['origActionMode'] === 'combined') { 7578 this.changeType('button'); 7579 } 7580 7581 if (this.type === 'button' && this.options['origActionMode'] === 'combined_button_auto') { 7582 this.changeType('auto'); 7583 } 7584 } catch (e) { 7585 console.warn(e); 7586 } 7587 }, 7588 /** 7589 * @param {Node} productBlock 7590 * @returns {void} 7591 */ 7592 beforeInsertProductBlock(productBlock) { 7593 const formKey = hyva.getFormKey(); 7594 productBlock.querySelectorAll('[name="form_key"]').forEach((item) => item.value = formKey); 7595 }, 7596 /** 7597 * @param {string} requestUrl
7598 * @param {callback} successCallback 7599 * @returns {void} 7600 */ 7601 doAjax(requestUrl, successCallback) { 7602 fetch(requestUrl, { 7603 method: "GET", 7604 headers: { 7605 "X-Requested-With": "XMLHttpRequest", 7606 }, 7607 }) 7608 .then(response => { 7609 if (response.ok) { 7610 return response.json(); 7611 } else { 7612 console.warn("GET request failed", response); 7613 this._stop(); 7614 } 7615 }) 7616 .then(data => { 7617 if (data) { 7618 this.preprocessRawAjaxResponse(successCallback, data); 7619 } 7620 }); 7621 }, 7622 /** 7623 * @param {callback} successCallback 7624 * @param {Object} response 7625 */ 7626 preprocessRawAjaxResponse(successCallback, response) { 7627 if (typeof response === 'string' || response instanceof String) { 7628 try { 7629 response = JSON.parse(response) 7630 } catch (e) { 7631 return this.handleUnexpectedResponse(response); 7632 } 7633 } 7634 7635 if (response !== null 7636 && response.hasOwnProperty('categoryProducts') 7637 && response.hasOwnProperty('currentPage') 7638 && response.categoryProducts.trim().length !== 0 7639 ) { 7640 successCallback.call(this, response); 7641 } else { 7642 this.handleUnexpectedResponse(response); 7643 } 7644 }, 7645 /** 7646 * @param {Object} response 7647 */ 7648 handleUnexpectedResponse(response) { 7649 console.warn('Amasty_Scroll: invalid response from server.', {response: response}); 7650 this._stop(); 7651 }, 7652 /** 7653 * @private 7654 * @returns {void} 7655 */ 7656 _initPagesCount() { 7657 const amPager = document.querySelector(this.amPageCountSelector); 7658 let parent; 7659 let childs; 7660 let limit; 7661 let allProducts; 7662 let result; 7663 7664 this.pagesLoaded = []; 7665 7666 if (amPager) { 7667 this.pagesCount = parseInt(amPager.innerHTML); 7668 return; 7669 } 7670 7671 parent = document.querySelectorAll(this.totalAmountSelector)[0]; 7672 7673 if (parent) { 7674 childs = parent.querySelectorAll(this.totalNumberSelector); 7675 7676 if (parent && childs.length >= 3) { 7677 limit = document.querySelector('.limiter-options').value; 7678 7679 if (childs[2].textContent > 0 && limit) { 7680 allProducts = childs[2].textContent; 7681 result = Math.ceil(parseInt(allProducts) / parseInt(limit)); 7682 7683 if (result > 1) { 7684 this.pagesCount = result; 7685 7686 return; 7687 } 7688 } 7689 } 7690 } 7691 7692 this.pagesCount = 1; 7693 }, 7694 /** 7695 * @param {string} type 7696 */ 7697 changeType(type) { 7698 if (this.isReinitialized) { 7699 return; 7700 } 7701 7702 switch (type) { 7703 case 'button': 7704 this.type = 'button'; 7705 this.$nextTick(() => { 7706 this._generateButton('before'); 7707 this._generateButton('after'); 7708 }); 7709 break; 7710 case 'auto': 7711 this.type = 'auto'; 7712 break; 7713 } 7714 7715 this.isReinitialized = true; 7716 }, 7717 /** 7718 * @private 7719 * @returns {void} 7720 */ 7721 _preloadPages() {
7722 const productContainer = document.querySelector(this.options['product_container']); 7723 let pageNumEl; 7724 7725 productContainer.setAttribute('amscroll-page', this.currentPage); 7726 productContainer.classList.add('amscroll-page'); 7727 7728 if (this.options['pageNumbers'] === '1') { 7729 pageNumEl = this._generatePageTitle(this.currentPage); 7730 7731 if (pageNumEl) { 7732 productContainer.before(pageNumEl); 7733 } 7734 } 7735 7736 this._preloadPageAfter(this.currentPage); 7737 this._preloadPageBefore(this.currentPage); 7738 }, 7739 /** 7740 * @returns {number} 7741 * @private 7742 */ 7743 _getCurrentPage() { 7744 let currentPage = parseInt(this.options['current_page']); 7745 7746 if (currentPage > this.pagesCount) { 7747 currentPage = this.pagesCount; 7748 } 7749 7750 return currentPage; 7751 }, 7752 /** 7753 * @private 7754 * @param {number} page 7755 * @returns {void} 7756 */ 7757 _preloadPageAfter(page) { 7758 const nextPage = page + 1; 7759 7760 if (nextPage && nextPage <= this.pagesCount) { 7761 this.next_data_url = this._generateUrl(nextPage, 1); 7762 this.pagesLoaded.push(nextPage); 7763 this.flag_next_cache = 1; 7764 this.type !== 'button' && this.loadingBlock.after.node?.classList.remove('hidden'); 7765 7766 this.doAjax(this.next_data_url, (data) => { 7767 this.flag_next_cache = 0; 7768 this.next_data_cache = data; 7769 this.type !== 'button' && this.loadingBlock.after.node?.classList.add('hidden'); 7770 this.showButton(this.afterButton); 7771 }); 7772 7773 this.next_data_url = ''; 7774 } 7775 }, 7776 /** 7777 * @private 7778 * @param {number} page 7779 * @returns {void} 7780 */ 7781 _preloadPageBefore(page) { 7782 const prevPage = page - 1; 7783 7784 if (prevPage && prevPage >= 1) { 7785 this.prev_data_url = this._generateUrl(prevPage, 1); 7786 this.pagesLoaded.unshift(prevPage); 7787 this.flag_prev_cache = 1; 7788 this.type !== 'button' && this.loadingBlock.before.node?.classList.remove('hidden'); 7789 7790 this.doAjax(this.prev_data_url, (data) => { 7791 this.flag_prev_cache = 0; 7792 this.prev_data_cache = data; 7793 this.type !== 'button' && this.loadingBlock.before.node?.classList.add('hidden'); 7794 this.showButton(this.beforeButton); 7795 }); 7796 7797 this.prev_data_url = ''; 7798 } 7799 }, 7800 /** 7801 * @private 7802 * @returns {void} 7803 */ 7804 _stop() { 7805 this.disabled = 1; 7806 this._showToolbars(); 7807 document.querySelector(`.${this.classes.loadingBlock}`).style.display = 'none'; 7808 }, 7809 /** 7810 * @private 7811 * @returns {void} 7812 */ 7813 _getAdditionalBlockHeight() { 7814 if (this.additionalHeight === null) { 7815 let height = 0; 7816 const pageBottom = document.querySelectorAll('.page-bottom'); 7817 const blockAfterProducts = document.querySelectorAll('.main .products ~ .block-static-block'); 7818 7819 if (blockAfterProducts.length) { 7820 height += blockAfterProducts.offsetHeight; 7821 } 7822 7823 if (document.querySelectorAll(this.options.footerSelector).length) { 7824 document.querySelectorAll(this.options.footerSelector).forEach((footer)
7824=> { 7825 height += footer.offsetHeight; 7826 }); 7827 } 7828 7829 if (pageBottom.length > 0) { 7830 height += pageBottom[0].offsetHeight; 7831 } 7832 7833 this.additionalHeight = height; 7834 } 7835 7836 return this.additionalHeight; 7837 }, 7838 /** 7839 * @private 7840 * @returns {void} 7841 */ 7842 _initPaginator() { 7843 if (this.disabled) { 7844 return; 7845 } 7846 7847 let scroll_pos = window.scrollY; 7848 let diff = document.querySelector('body').offsetHeight - window.innerHeight; 7849 7850 diff -= this._getAdditionalBlockHeight(); 7851 diff = 0.8 * diff; 7852 7853 if (scroll_pos < this.lastScrollPos) { 7854 this.isScrolledBack = true; 7855 } 7856 7857 this.lastScrollPos = scroll_pos; 7858 7859 if (scroll_pos >= diff && this.is_loading === 0) { 7860 this._loadFollowing(); 7861 } 7862 7863 if (scroll_pos <= this._getTopContainersHeight() && (this.is_loading === 0 && this._isScrolledBack())) { 7864 this._loadPrevious(); 7865 } 7866 7867 this._calculateCurrentScrollPage(scroll_pos); 7868 7869 document.addEventListener("DOMContentLoaded", (event) => { 7870 if (window.innerHeight > document.querySelector(this.options['product_container']).offsetHeight && '' !== this.next_data_url) { 7871 this._loadFollowing(); 7872 } 7873 }); 7874 }, 7875 /** 7876 * @private 7877 * @returns {void} 7878 */ 7879 _isScrolledBack() { 7880 return this.isScrolledBack; 7881 }, 7882 /** 7883 * @param {number} scroll_pos 7884 * @private 7885 * @returns {void} 7886 */ 7887 _calculateCurrentScrollPage(scroll_pos) { 7888 if (Math.abs(scroll_pos - this.last_scroll) > window.innerHeight * 0.1) { 7889 this.last_scroll = scroll_pos; 7890 this._updateUrlAndCurrentPage(); 7891 } 7892 }, 7893 /** 7894 * @private 7895 * @returns {void} 7896 */ 7897 _updateUrlAndCurrentPage() { 7898 const productContainers = document.querySelectorAll(this.options['product_container']); 7899 7900 productContainers.forEach((container, index) => { 7901 if (this._mostlyVisible(container, index)) { 7902 var page = parseInt(container.getAttribute('amscroll-page')); 7903 7904 if (page && page !== this.currentPage) { 7905 var newUrl = this._generateUrl(page, 0); 7906 7907 if (!window.history.state || newUrl !== window.history.state.url) { 7908 window.history.replaceState({url: newUrl}, '', newUrl); 7909 } 7910 7911 this.currentPage = page; 7912 } 7913 7914 return false; 7915 } 7916 }); 7917 }, 7918 /** 7919 * @private 7920 * @returns {void} 7921 */ 7922 _loadFollowing() { 7923 if (this.flag_next_cache && this.type !== 'button') { 7924 this.loadingBlock.after.node?.classList.remove('hidden'); 7925 } 7926 7927 if (this.next_data_url !== "" || this.next_data_cache) { 7928 if (this.type !== 'button') { 7929 this.loadingBlock.after.node?.classList.remove('hidden'); 7930 } 7931 7932 if (this.next_data_cache) {
7933 this.showFollowing(this.next_data_cache); 7934 } else { 7935 if (!this.flag_next_cache) { 7936 this.is_loading = 1; 7937 7938 this.doAjax(this.next_data_url, (data) => { 7939 this.showFollowing(data); 7940 }); 7941 } 7942 } 7943 } 7944 }, 7945 /** 7946 * @param {Object} data 7947 * @returns {void} 7948 */ 7949 showFollowing(data) { 7950 if (data.categoryProducts) { 7951 if (this.type === 'button') { 7952 if (this.canShowAfter) { 7953 this.is_loading = 0; 7954 this.canShowAfter = false; 7955 } else { 7956 return; 7957 } 7958 } 7959 7960 this.next_data_url = ''; 7961 this.next_data_cache = false; 7962 this.$nextTick(() => { 7963 this.loadingBlock.after.node?.classList.add('hidden'); 7964 this._insertNewProductBlock(data, 'after'); 7965 this._afterShowFollowing(); 7966 }) 7967 7968 } 7969 }, 7970 /** 7971 * @private 7972 * @returns {void} 7973 */ 7974 _afterShowFollowing() { 7975 const nextPage = this.pagesLoaded[this.pagesLoaded.length - 1] + 1; 7976 const afterShowFollowingEvent = new CustomEvent("amscroll-after-show-following"); 7977 7978 if (nextPage && nextPage <= this.pagesCount && this.pagesLoaded.indexOf(nextPage) === -1) { 7979 this.next_data_url = this._generateUrl(nextPage, 1); 7980 this.pagesLoaded.push(nextPage); 7981 this.flag_next_cache = 1; 7982 7983 this.doAjax(this.next_data_url, (preview_data) => { 7984 this.flag_next_cache = 0; 7985 this.next_data_cache = preview_data; 7986 window.dispatchEvent(new Event('scroll')); 7987 this.showButton(this.afterButton); 7988 }); 7989 } 7990 7991 this.is_loading = 0; 7992 window.dispatchEvent(afterShowFollowingEvent); 7993 }, 7994 /** 7995 * @private 7996 * @returns {void} 7997 */ 7998 _loadPrevious() { 7999 if (this.flag_prev_cache && this.type !== 'button') { 8000 this.loadingBlock.before.node?.classList.remove('hidden'); 8001 } 8002 8003 if (this.prev_data_url !== "" || this.prev_data_cache) { 8004 if (this.type !== 'button') { 8005 this.loadingBlock.before.node?.classList.remove('hidden'); 8006 } 8007 8008 if (this.prev_data_cache) { 8009 this.showPrevious(this.prev_data_cache); 8010 } else { 8011 if (!this.flag_prev_cache) { 8012 this.is_loading = 1; 8013 8014 this.doAjax(this.prev_data_url, (data) => { 8015 this.showPrevious(data); 8016 }); 8017 } 8018 } 8019 } 8020 }, 8021 /** 8022 * @param {Object} data 8023 * @returns {void} 8024 */ 8025 showPrevious(data) { 8026 if (data.categoryProducts) { 8027 if (this.type === 'button') { 8028 if (this.canShowBefore) { 8029 this.is_loading = 0; 8030 this.canShowBefore = false;
8031 } else { 8032 return; 8033 } 8034 } 8035 8036 this.prev_data_cache = false; 8037 this.prev_data_url = ''; 8038 this._insertNewProductBlock(data, 'before'); 8039 this._afterShowPrevious(); 8040 this.loadingBlock.before.node?.classList.add('hidden'); 8041 } 8042 }, 8043 /** 8044 * @private 8045 * @returns {void} 8046 */ 8047 _afterShowPrevious() { 8048 const prevPage = this.pagesLoaded[0] - 1; 8049 const afterShowPreviousEvent = new CustomEvent("amscroll-after-show-previous"); 8050 8051 if (prevPage && prevPage <= this.pagesCount && this.pagesLoaded.indexOf(prevPage) === -1) { 8052 this.prev_data_url = this._generateUrl(prevPage, 1); 8053 this.pagesLoaded.unshift(prevPage); 8054 this.flag_prev_cache = 1; 8055 8056 this.doAjax(this.prev_data_url, (preview_data) => { 8057 this.flag_prev_cache = 0; 8058 this.prev_data_cache = preview_data; 8059 window.dispatchEvent(new Event('scroll')); 8060 this.showButton(this.beforeButton); 8061 }); 8062 } 8063 8064 this.is_loading = 0; 8065 window.dispatchEvent(afterShowPreviousEvent); 8066 }, 8067 /** 8068 * @private 8069 * @param {string} position 8070 * @returns {void} 8071 */ 8072 _generateButton(position) { 8073 if (this.type !== 'button') { 8074 return; 8075 } 8076 8077 if ((position === 'before' && this.pagesLoaded.indexOf(1) !== -1 8078 && (document.querySelector(this.pageSelector.replace('%1', this.pageFirst)) || this.currentPage === this.pageFirst)) 8079 ) { 8080 return; 8081 } 8082 8083 if (position === 'after' && this.pagesLoaded.indexOf(this.pagesCount) !== -1 8084 && (document.querySelector(this.pageSelector.replace('%1', this.pagesCount)) || this.currentPage === this.pagesCount)) { 8085 return; 8086 } 8087 8088 const buttonElement = this.loadButtonElement.cloneNode(true); 8089 const textElement = this.loadButtonTextElement.cloneNode(true); 8090 const color = this.options['buttonColor']; 8091 const buttonSelector = `.${this.classes.loadButton}.-${position}`; 8092 8093 textElement.textContent = this.options[`loading${position}TextButton`]; 8094 8095 buttonElement.append(textElement); 8096 buttonElement.style.color = color; 8097 buttonElement.style.borderColor = color; 8098 buttonElement.setAttribute('amscroll_type', position); 8099 buttonElement.classList.add(`-${position}`); 8100 buttonElement.style.display = 'none'; 8101 8102 if (position === 'after') { 8103 if (this.afterButton) { 8104 this.afterButton.remove(); 8105 } 8106 this._insertBlockInTheEnd(buttonElement.outerHTML); 8107 this.afterButton = document.querySelector(buttonSelector); 8108 } else { 8109 if (this.beforeButton) { 8110 this.beforeButton.remove(); 8111 } 8112 this._insertBlockInTheBegin(buttonElement.outerHTML); 8113 this.beforeButton = document.querySelector(buttonSelector); 8114 } 8115 8116 this.$nextTick(() => { 8117 const buttons = document.querySelectorAll(`.${this.classes.loadButton}[amscroll_type="${position}"]`); 8118 8119 buttons.forEach((button) => { 8120 if (!!button.dataset.amscrollHasEvent) { 8121 return; 8122 } 8123 8124 button.addEventListener('click', (event) => { 8125 this.buttonClick(event); 8126 }); 8127 8128 button.dataset.amscrollHasEvent = true; 8129 }); 8130 }); 8131 }, 8132 /** 8133 * @param {Node} buttonElement 8134 * @returns {void} 8135 */ 8136 showButton(buttonElement) { 8137 if (buttonElement) { 8138 buttonElement.style.display = 'block'; 8139 } 8140 }, 8141 /** 8142 * @param {Event} event 8143 * @returns {void} 8144 */ 8145 buttonClick(event) { 8146 const element = event.currentTarget; 8147 const type = element.getAttribute('amscroll_type'); 8148 8149 element.style.color = this.options['buttonColorPressed']; 8150 8151 if (type === 'after') { 8152 this.canShowAfter = true; 8153 this._loadFollowing(); 8154 } else { 8155 this.canShowBefore = true; 8156 this._loadPrevious(); 8157 } 8158 8159 this.$nextTick(() => { 8160 this._updateUrlAndCurrentPage(); 8161 }); 8162 }, 8163 /** 8164 * @private 8165 * @param {Object} data 8166 * @param {string} position 8167 * @returns {void} 8168 */ 8169 _insertNewProductBlock(data, position) { 8170 let htmlData = data.categoryProducts; 8171 let tmp = document.createElement('div'); 8172 tmp.innerHTML = htmlData; 8173 let html = tmp.querySelector('section'); 8174 8175 this._removeToolbars(html); 8176 this.removeScripts(html); 8177 this.normalizeSwatchXData(html); 8178 this.normalizeRatingName(html);
8179 const productContainer = html.querySelector(this.options['product_container']); 8180 productContainer.classList.add('amscroll-pages'); 8181 productContainer.setAttribute('amscroll-page', data.currentPage); 8182 this.beforeInsertProductBlock(productContainer); 8183 8184 if (this.options['pageNumbers'] == '1') { 8185 var pageNumEl = this._generatePageTitle(data.currentPage); 8186 if (pageNumEl) { 8187 productContainer.before(pageNumEl); 8188 } 8189 } 8190 8191 if ('after' === position) { 8192 this.afterButton?.remove(); 8193 this._moveScriptToTop(html); 8194 this._insertBlockInTheEnd(html.innerHTML); 8195 } else { 8196 this._moveScriptToTop(html); 8197 const element = this._insertBlockInTheBegin(html.innerHTML); 8198 const itemHeight = element !== undefined ? element.offsetHeight : 0; 8199 8200 if (this.type != 'button') { 8201 window.scrollTo(0, window.scrollTop + itemHeight); 8202 } 8203 } 8204 8205 this.$nextTick(() => { 8206 const currentProductContainer = document.querySelector(`.amscroll-pages[amscroll-page="${data.currentPage}"]`); 8207 this._addObserverToProductLink(currentProductContainer); 8208 8209 if (this.type == 'button') { 8210 this._generateButton(position); 8211 } 8212 window.dispatchEvent(new CustomEvent('amcart-set-forms-event')); 8213 }); 8214 8215 this.initPageStepForwardListener(data.currentPage); 8216 }, 8217 /** 8218 * @private 8219 * @param {Node} html 8220 * @returns {void} 8221 */ 8222 removeScripts(html) { 8223 const scripts = Array.from(html.getElementsByTagName('script')); 8224 8225 scripts.forEach((script) => { 8226 script.parentNode.removeChild(script); 8227 }) 8228 }, 8229 /** 8230 * @private 8231 * @param {Node} html 8232 * @returns {void} 8233 */ 8234 normalizeSwatchXData(html) { 8235 html.innerHTML = html.innerHTML.replaceAll(/"initConfigurableSwatchOptions[_0-9]*/gi, '"initConfigurableSwatchOptions'); 8236 }, 8237 /** 8238 * @private 8239 * @param {Node} html 8240 * @returns {void} 8241 */ 8242 normalizeRatingName(html) { 8243 html.innerHTML = html.innerHTML.replaceAll(/initRating[a-z0-9]+\(\)/gi, 'initRating'); 8244 }, 8245 /** 8246 * @private 8247 * @param {Node} productContainer 8248 * @returns {void} 8249 */ 8250 _addObserverToProductLink(productContainer) { 8251 this._externalAfterAjax(); 8252 const links = productContainer.querySelectorAll('.item a'); 8253 8254 links.forEach((link) => { 8255 link.addEventListener('click', (event) => { 8256 try { 8257 const parent = event.target.closest('.amscroll-pages'); 8258 const page = parent ? parent.getAttribute('amscroll-page') : null; 8259 8260 if (page) { 8261 var newUrl = this._generateUrl(page, 0); 8262 if (!window.history.state || newUrl !== window.history.state.url) { 8263 window.history.replaceState( 8264 { 8265 url: newUrl 8266 }, 8267 '', 8268 newUrl 8269 ); 8270 } 8271 } 8272 } catch (e) { 8273 console.warn(e); 8274 } 8275 }); 8276 }); 8277 }, 8278 /** 8279 * @private 8280 * @param {number} page 8281 * @param {number} addScroll 8282 * @returns {string} 8283 */ 8284 _generateUrl(page, addScroll) { 8285 const parameters = addScroll > 0 ? `p=${page}&is_scroll=${addScroll}` : `p=${page}`; 8286 const currentUrl = window.location.origin + window.location.pathname; 8287 const urlHasArgument = currentUrl.includes('?'); 8288 const parameterType = urlHasArgument ? '&' : '?'; 8289 const currentParameters = new URLSearchParams(window.location.search); 8290 const newParameters = new URLSearchParams(parameters); 8291 const newUrlParams = new URLSearchParams({ 8292 ...Object.fromEntries(currentParameters), 8293 ...Object.fromEntries(newParameters) 8294 }); 8295 8296 return `${currentUrl}${parameterType}${newUrlParams}`; 8297 }, 8298 /** 8299 * @private 8300 * @returns {void} 8301 */ 8302 _hideToolbars() { 8303 document.querySelector(this.totalAmountSelector).style.display = 'none'; 8304 document.querySelector('.products ~ ' + this.toolbarSelector).style.display = 'none'; 8305 }, 8306 /** 8307 * @private 8308 * @returns {void} 8309 */ 8310 _showToolbars() { 8311 document.querySelector(this.totalAmountSelector).style.display = 'block'; 8312 document.querySelector(`.products ~ ${this.toolbarSelector}`).style.display = 'block'; 8313 }, 8314 /** 8315 * @private 8316 * @param {number} page 8317 * @returns {HTMLDivElement|boolean} 8318 */ 8319 _generatePageTitle(page) { 8320 if (document.querySelector(`#amscroll-page-num${page}`)) { 8321 return false;
8322 } 8323 8324 const pageNumEl = document.createElement('div'); 8325 pageNumEl.setAttribute('class', `amscroll-page-num -amscroll-${this.options['pageNumberStyle']}`); 8326 pageNumEl.setAttribute('id', `#amscroll-page-num${page}`); 8327 pageNumEl.textContent = `${this.options['pageNumberContent']}${page}`; 8328 8329 return pageNumEl; 8330 }, 8331 /** 8332 * @private 8333 * @param {Node} element 8334 * @param {number} index 8335 * @returns {boolean} 8336 */ 8337 _mostlyVisible(element, index) { 8338 const visible = element.offsetParent !== null; 8339 const scroll_pos = window.scrollY; 8340 const window_height = window.innerHeight; 8341 const el_top = element.offsetTop; 8342 const el_height = element.offsetHeight; 8343 const el_bottom = el_top + el_height; 8344 8345 const isVisible = ((el_bottom - el_height * 0.25 > scroll_pos) 8346 && (el_top < (scroll_pos + 0.5 * window_height)) 8347 && visible) || (index == 0 && scroll_pos < el_top); 8348 8349 return ((el_bottom - el_height * 0.25 > scroll_pos) 8350 && (el_top < (scroll_pos + 0.5 * window_height)) 8351 && visible) || (index == 0 && scroll_pos < el_top); 8352 }, 8353 /** 8354 * @private 8355 * @returns {number} 8356 */ 8357 _getTopContainersHeight() { 8358 if (!this.topContainersHeight) { 8359 let result = document.querySelector('.page-header').offsetHeight; 8360 if (document.querySelectorAll(".main .block-static-block ~ .products, .main .block-static-block ~ #amasty-shopby-product-list").length) { 8361 result += document.querySelector(".main .block-static-block").offsetHeight; 8362 } 8363 this.topContainersHeight = 0.9 * result; 8364 } 8365 8366 return this.topContainersHeight; 8367 }, 8368 /** 8369 * @returns {void} 8370 */ 8371 resolveBackToTopVisibility() { 8372 if (this.options?.backToTop?.enabled !== '1') { 8373 return; 8374 } 8375 8376 this.isBackToTopVisible = document.body.scrollTop > this.backToTopTriggerHeight 8377 || document.documentElement.scrollTop > this.backToTopTriggerHeight; 8378 }, 8379 /** 8380 * @private 8381 * @returns {void} 8382 */ 8383 _scrollToTop() { 8384 const items = document.querySelectorAll(`[amscroll-page="${currentPage}"] .product-item a`); 8385 8386 items.forEach((item) => { 8387 item.addEventListener('click', (event) => { 8388 var scrollPositionAfterStepBackData = { 8389 pageUrl: this.getCurrentUrl(), 8390 clickedProductLink: event.currentTarget.attributes.href.value 8391 }; 8392 8393 hyva.getBrowserStorage().setItem('am-scroll-go-back-data', JSON.stringify(scrollPositionAfterStepBackData)); 8394 }); 8395 }); 8396 }, 8397 /** 8398 * @private 8399 * @param {Node} block 8400 * @returns {void} 8401 */ 8402 _insertBlockInTheEnd(block) { 8403 const productContainer = document.querySelectorAll(this.options['product_container']); 8404 productContainer[productContainer.length - 1].insertAdjacentHTML('afterend', block); 8405 }, 8406 /** 8407 * @private 8408 * @param {Node} block 8409 * @returns {Node} 8410 */ 8411 _insertBlockInTheBegin(block) { 8412 const element = document.querySelector(`.${this.classes.pageNumber}`) 8413 ?? document.querySelector(this.options['product_container']); 8414 this.beforeButton?.remove(); 8415 element.insertAdjacentHTML('beforebegin', block); 8416 8417 return element; 8418 }, 8419 /** 8420 * @returns {string} 8421 */ 8422 getCurrentUrl() { 8423 return location.href.split('?')[0]; 8424 }, 8425 /** 8426 * @returns {void} 8427 */ 8428 getSavedAfterStepBackData() { 8429 let savedData = hyva.getBrowserStorage().getItem('am-scroll-go-back-data'); 8430 8431 if (savedData) { 8432 savedData = JSON.parse(savedData); 8433 this.afterStepBackData = savedData; 8434 hyva.getBrowserStorage().removeItem('am-scroll-go-back-data'); 8435 } 8436 8437 return this.afterStepBackData; 8438 }, 8439 /** 8440 * @param {number} currentPage 8441 */ 8442 initPageStepForwardListener(currentPage) { 8443 const items = document.querySelectorAll(`[amscroll-page="${currentPage}"] .product-item a`); 8444
8445 items.forEach((item) => { 8446 item.addEventListener('click', (event) => { 8447 var scrollPositionAfterStepBackData = { 8448 pageUrl: this.getCurrentUrl(), 8449 clickedProductLink: event.currentTarget.attributes.href.value 8450 }; 8451 8452 hyva.getBrowserStorage().setItem('am-scroll-go-back-data', JSON.stringify(scrollPositionAfterStepBackData)); 8453 }); 8454 }); 8455 }, 8456 /** 8457 * @private 8458 * @returns {void} 8459 */ 8460 _pagePositionAfterStepBack() { 8461 const savedScrollData = this.getSavedAfterStepBackData(); 8462 8463 if (savedScrollData) { 8464 const productItem = document.querySelector(`a[href="${savedScrollData.clickedProductLink}"]`); 8465 history.scrollRestoration = 'auto'; 8466 8467 if (productItem) { 8468 history.scrollRestoration = 'manual'; 8469 productItem.scrollIntoView(); 8470 } 8471 } 8472 }, 8473 /** 8474 * @private 8475 * @param {string} html 8476 */ 8477 _moveScriptToTop(html) { 8478 const htmlScripts = html.getElementsByTagName('script'); 8479 8480 if (htmlScripts.length > 0) { 8481 const scripts = []; 8482 for (let i = 0; i < htmlScripts.length; i++) { 8483 scripts.push(htmlScripts[i]); 8484 } 8485 8486 for (let i = 0; i < scripts.length; i++) { 8487 let script = document.createElement('script'); 8488 script.innerHTML = scripts[i].innerHTML; 8489 8490 document.head.appendChild(script); 8491 scripts[i].parentNode.removeChild(scripts[i]); 8492 } 8493 } 8494 }, 8495 /** 8496 * @private 8497 * @param {string} html 8498 */ 8499 _removeToolbars(html) { 8500 const contentToolbars = html.querySelectorAll(this.toolbarSelector); 8501 if (!contentToolbars.length) { 8502 return; 8503 } 8504 8505 contentToolbars.forEach((toolbar) => { 8506 toolbar.previousElementSibling.remove(); 8507 toolbar.remove(); 8508 }); 8509 }, 8510 /** 8511 * @param {Node} elem 8512 * @param {string} selector 8513 * @returns {Element|null} 8514 */ 8515 getNext(elem, selector) { 8516 let nextElem = elem.nextElementSibling; 8517 8518 if (!selector) { 8519 return nextElem; 8520 } 8521 8522 if (nextElem && nextElem.matches(selector)) { 8523 return nextElem; 8524 } 8525 8526 return null; 8527 }, 8528 /** 8529 * @returns {Object<string, boolean>} 8530 */ 8531 listBackToTopButtonClasses() { 8532 return { 8533 'bottom-20 -right-8 pt-1 pb-[6px] pr-2 pl-3': this.options.backToTop?.style_mobile !== 'arrow', 8534 'right-3 bottom-5 p-0 h-[44px] w-[44px]': this.options.backToTop?.style_mobile === 'arrow', 8535 'md:h-auto md:w-auto md:bottom-20 md:-right-8 md:pt-1 md:pb-[6px] md:pr-2 md:pl-3': this.options.backToTop?.style_desktop !== 'arrow', 8536 'md:right-3 md:bottom-5 md:h-[44px] md:w-[44px] md:p-0': this.options.backToTop?.style_desktop === 'arrow', 8537 'md:right-[-3.5rem] md:pt-2 md:rounded-b-none md:rounded-t-2xl': this.options.backToTop?.style_desktop === 'edge' 8538 } 8539 }, 8540 /** 8541 * @returns {string} 8542 */ 8543 listBackToTopButtonStyle() { 8544 return `background-color: ${this.options.backToTop?.color ?? '#4776f0'};`; 8545 }, 8546 /** 8547 * @returns {Object<string, boolean>} 8548 */ 8549 listBackToTopSpanClasses() { 8550 return { 8551 'hidden': this.options.backToTop?.style_mobile === 'arrow', 8552 'inline-block': this.options.backToTop?.style_mobile !== 'arrow', 8553 'md:hidden': this.options.backToTop?.style_desktop === 'arrow', 8554 'md:inline-block': this.options.backToTop?.style_desktop !== 'arrow' 8555 } 8556 }, 8557 /** 8558 * @returns {Object<string, boolean>} 8559 */ 8560 listBackToTopSpanIconClasses() { 8561 return { 8562 'relative border': this.options.backToTop?.style_mobile !== 'arrow', 8563 'border-0 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2': this.options.backToTop?.style_mobile === 'arrow', 8564 'md:relative md:border md:translate-x-0 md:translate-y-0 md:left-0 md:top-0': this.options.backToTop?.style_desktop !== 'arrow', 8565 'md:border-0 md:absolute md:top-1/2 md:left-1/2 md:-translate-x-1/2 md:-translate-y-1/2': this.options.backToTop?.style_desktop === 'arrow' 8566 } 8567 }, 8568 /** 8569 * @returns {void} 8570 */ 8571 onBackToTopClick() { 8572 window.scrollTo({ top: 0, behavior: 'smooth'}) 8573 }, 8574 /** 8575 * @returns {void} 8576 */ 8577 debounceInitPaginator() { 8578 return this._initPaginator(); 8579 } 8580 }; 8581 } 8582 window.addEventListener( 8583 'alpine:init', 8584 () => Alpine.data('initAmScroll', initAmScroll), 8585 {once: true} 8586 ) 8587 </script>
8587 8588 8589 <div x-data="initAmScroll" 8590 x-init="processInit" 8591 @scroll.window.debounce="debounceInitPaginator"> 8592 <div x-cloak 8593 x-show="isBackToTopVisible" 8594 @scroll.window.debounce="resolveBackToTopVisibility"> 8595 <button type="button" 8596 id="amscroll-back-to-top" 8597 class="fixed text-xl rounded-full -rotate-90 text-white" 8598 :class="listBackToTopButtonClasses" 8599 :style="listBackToTopButtonStyle" 8600 @click="onBackToTopClick"> 8601 <span :class="listBackToTopSpanClasses"> 8602 Back to Top </span> 8603 <span class="inline-block align-middle rounded-full border h-6 w-6" 8604 :class="listBackToTopSpanIconClasses"> 8605 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="inline absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2" width="24" height="24" role="img"> 8606 <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/> 8607<title>chevron-right</title></svg> 8608 </span> 8609 </button> 8610 <template id="amscroll-loading-icon-source"> 8611 <div class="amscroll-loading-block hidden"> 8612 <div class="amscroll-loading-icon animate-spin" 8613 style="background-image: url('https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Amasty_Scroll/images/loader.svg')"></div> 8614 </div> 8615 </template> 8616 8617 </div> 8618 </div> 8619
8620<script> 8621 'use strict'; 8622 8623 window.addEventListener('DOMContentLoaded', () => { 8624 if (!window.magentoStorefrontEvents) { 8625 return; 8626 } 8627 8628 magentoStorefrontEvents.context.setEventForwarding({ 8629 aep: 'false', 8630 }); 8631 8632 magentoStorefrontEvents.context.setAEP({ 8633 imsOrgId: '', 8634 datastreamId: '', 8635 webSdkName: '', 8636 }); 8637 8638 magentoStorefrontEvents.context.setExperiencePlatformConnectorExtension({ 8639 version: '3.6.0', 8640 }); 8641 }); 8642</script>
8642 8643<div x-data="recommendedProducts_below_main_content" 8644 x-init="initProductRecBlock" 8645 id="recs_block_below_main_content" 8646 class="container" x-cloak> 8647 <template x-if="productBanners"> 8648 <div> 8649 <template x-for="banner in productBanners"> 8650 <div class="product-slider" x-intersect.once="scrollViewEvent"> 8651 <section class="my-12 text-gray-700 body-font"> 8652 <div class="relative"> 8653 <div class="container flex flex-col items-center pt-6 pb-3 mx-auto mb-6 border-b-2 border-gray-300 md:flex-row"> 8654 <h3 x-text='banner.storefrontLabel' 8655 class="text-2xl font-medium text-gray-900 title-font"></h3> 8656 </div> 8657 <div class="relative flex flex-wrap gap-2 w-full md:px-1 xl:px-2"> 8658 <template x-for="product in banner.products"> 8659 <div class="mr-1 p-1 w-64"> 8660 <form method="post" x-bind:action="createAddToCartUrl" 8661 @submit.prevent="addToCartRecsEvent" 8662 class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full"> 8663 <input name="form_key" type="hidden" :value="getFormKey"/> 8664 <input type="hidden" name="product" :value="product.productId"> 8665 <a @click="recsClickEvent" 8666 class="product photo product-item-photo block mx-auto mb-3 cursor-pointer" 8667 tabindex="-1"> 8668 <img class="hover:shadow-sm object-contain" loading="lazy" 8669 :alt="getImageAltTag" 8670 :title="getImageTitle" 8671 :src="getImageSrc" 8672 width="360" 8673 height="360" 8674 > 8675 </a> 8676 <div class="product-info flex flex-col flex-grow"> 8677 <div 8678 class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 8679 <a class="product-item-link cursor-pointer" 8680 @click="recsClickEvent" 8681 x-text="product.name"></a> 8682 </div> 8683 <template x-if="product.prices"> 8684 <div class="pt-1 text-gray-900 price-box price-final_price" x-bind:data-product-id="product.productId"> 8685 <span class="normal-price"> 8686 <span class="price-container price-final_price" x-show="showProductPrice"> 8687 <span class="price-label"> 8688 As low as </span> 8689 <span id="product-price" class="price-wrapper "> 8690 <span class="price" x-text="renderPrice"></span> 8691 </span> 8692 </span> 8693 </span> 8694 </div> 8695 </template> 8696 <template x-if="product.addToCartAllowed"> 8697 <div 8698 class="mt-auto pt-3 flex flex-wrap justify-center items-center"> 8699 <button 8700 class="w-auto btn btn-primary justify-center text-sm mr-auto" 8701 aria-label="Add to Cart"> 8702 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="h-6 w-6 border-current inline" width="25" height="25" role="img"> 8703 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
87030 014 0z"/> 8704<title>shopping-cart</title></svg> 8705 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 8706 Add to Cart </span> 8707 </button> 8708 </div> 8709 </template> 8710 </div> 8711 </form> 8712 </div> 8713 </template> 8714 </div> 8715 </div> 8716 </section> 8717 </div> 8718 </template> 8719 </div> 8720 </template> 8721</div> 8722
8723<script> 8724 'use strict'; 8725 window.addEventListener( 8726 'alpine:init', 8727 () => Alpine.data('recommendedProducts_below_main_content', () => ({ 8728 productBanners: [], 8729 renderedUnitIds: [], 8730 pagePreconfiguredDeferred: {}, 8731 bannerPlacement: 'below-main-content', 8732 initProductRecBlock: function () { 8733 let self = this; 8734 window.magentoStorefrontEvents.subscribe.recsResponseReceived(function () { 8735 let recsData = window.magentoStorefrontEvents.context.getRecommendations(); 8736 self.renderProductRecs(recsData); 8737 }); 8738 }, 8739 showProductPrice(){ 8740 return this.product.prices 8741 && this.product.prices.minimum 8742 && this.product.prices.minimum.final; 8743 }, 8744 renderPrice(){ 8745 return hyva.formatPrice(this.product.prices.minimum.final); 8746 }, 8747 renderProductRecs: function (recsData) { 8748 let self = this; 8749 8750 recsData.units.sort((a, b) => a.displayOrder - b.displayOrder); 8751 8752 const rendered = []; 8753 8754 recsData.units.forEach(function (unit) { 8755 if (unit.pagePlacement === self.bannerPlacement 8756 && !self.renderedUnitIds.includes(unit.unitId)) { 8757 self.renderedUnitIds.push(unit.unitId); 8758 self.productBanners.push(unit); 8759 rendered.push(unit.unitId); 8760 } 8761 }); 8762 8763 if (rendered.length) { 8764 setTimeout(function () { 8765 rendered.forEach(function (unitId) { 8766 window.magentoStorefrontEvents.publish.recsUnitRender(unitId); 8767 }); 8768 }, 0); 8769 } 8770 }, 8771 addToCartRecsEvent: function (event) { 8772 // requires recommendationsContext to be set 8773 Promise.resolve( 8774 magentoStorefrontEvents.publish.recsItemAddToCartClick( 8775 this.banner.unitId, this.product.productId, magentoStorefrontEvents.context 8776 ) 8777 ).then(() => event.target.submit()); 8778 }, 8779 scrollViewEvent: function () { 8780 magentoStorefrontEvents.publish.recsUnitView( 8781 this.banner.unitId, magentoStorefrontEvents.context 8782 ); 8783 }, 8784 recsClickEvent: function () { 8785 Promise.resolve( 8786 magentoStorefrontEvents.publish.recsItemClick( 8787 this.banner.unitId, this.product.productId, magentoStorefrontEvents.context 8788 ) 8789 ).then(() => { window.location.href = this.product.url; }); 8790 }, 8791 createAddToCartUrl: function () { 8792 const currentLocationUENC = encodeURIComponent( 8793 this.encodeUenc(window.location.href), 8794 ); 8795 return BASE_URL + 8796 "checkout/cart/add/uenc/" + 8797 currentLocationUENC + 8798 "/product/" + 8799 this.product.productId 8800 }, 8801 encodeUenc: function (value) { 8802 const regex = /=/gi; 8803 return btoa(value).replace(regex, ","); 8804 }, 8805 getImageAltTag() { 8806 return this.product?.image?.label || ''; 8807 }, 8808 getImageTitle() { 8809 return this.product?.image?.label || ''; 8810 }, 8811 getImageSrc(){ 8812 return this.product?.image?.url || 'https\u003A\u002F\u002Fwww.huskee.co\u002Fmedia\u002Fcatalog\u002Fproduct\u002Fplaceholder\u002Fdefault\u002F1204566951673160.fqSZYPyGbBHB3oKqfkg5_height640_1.png'; 8813 }, 8814 getFormKey() { 8815 return hyva.getFormKey(); 8816 } 8817 })), 8818 {once: true} 8819 ) 8820</script>
8820 8821</div></div></main><footer class="page-footer"><div class="footer content"><div class="container"> 8822 <style>#html-body [data-pb-style=DDY3FWN],#html-body [data-pb-style=H7WAPCN]{background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll}#html-body [data-pb-style=DDY3FWN]{justify-content:flex-start;display:flex;flex-direction:column}#html-body [data-pb-style=H7WAPCN]{align-self:stretch}#html-body [data-pb-style=T66JDUX]{display:flex;width:100%}#html-body [data-pb-style=DKGPH0P],#html-body [data-pb-style=NAWTGTX],#html-body [data-pb-style=QR205FN],#html-body [data-pb-style=T89K9WI],#html-body [data-pb-style=UFY1GST]{justify-content:flex-start;display:flex;flex-direction:column;background-position:left top;background-size:cover;background-repeat:no-repeat;background-attachment:scroll;width:20%;align-self:stretch}</style><div data-content-type="row" data-appearance="full-bleed" data-enable-parallax="0" data-parallax-speed="0.5" data-background-images="{}" data-background-type="image" data-video-loop="true" data-video-play-only-visible="true" data-video-lazy-load="true" data-video-fallback-src="" data-background-lazy-load="false" data-element="main" data-pb-style="DDY3FWN"><div class="pagebuilder-column-group" data-background-images="{}" data-content-type="column-group" data-appearance="default" data-grid-size="5" data-background-lazy-load="false" data-element="main" data-pb-style="H7WAPCN"><div class="pagebuilder-column-line" data-content-type="column-line" data-element="main" data-pb-style="T66JDUX"><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="false" data-element="main" data-pb-style="T89K9WI"><div data-content-type="text" data-appearance="default" data-element="main"><h5 class="footer-info-heading"><strong>Product Ranges</strong></h5> 8823<ul class="footer-info-list"> 8824<li><a href="https://www.huskee.co/huskeeoriginal/">HuskeeOriginal</a></li> 8825<li><a href="https://www.huskee.co/huskeerenew/">HuskeeRenew</a></li> 8826<li><a href="https://www.huskee.co/huskeesteel/">HuskeeSteel</a></li> 8827</ul></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="UFY1GST"><div data-content-type="text" data-appearance="default" data-element="main"><h5 class="footer-info-heading"><strong>Industry Solutions</strong></h5> 8828<ul class="footer-info-list"> 8829<li><a href="https://www.huskee.co/industry/hotels/">Hotels</a></li> 8830<li><a href="https://www.huskee.co/industry/restaurants/">Restaurants</a></li> 8831<li><a href="https://www.huskee.co/industry/coffee-roaster/">Coffee Roasters</a></li> 8832<li><a href="https://www.huskee.co/industry/offices/">Corporate Offices</a></li> 8833<li><a href="https://www.huskee.co/industry/education/">Universities</a></li> 8834<li><a href="https://www.huskee.co/industry/retail/">Retailers</a></li> 8835</ul></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="DKGPH0P"><div data-content-type="text" data-appearance="default" data-element="main"><article class="footer-info-col footer-info-col--small" data-section-type="footer-brands"> 8836<h5 class="footer-info-heading"><strong>Support</strong></h5> 8837<ul class="footer-info-list"> 8838<li><a href="https://www.huskee.co/faq">FAQ</a></li> 8839<li><a href="https://www.huskee.co/contact-form/">Contact Us</a></li> 8840<li><a href="https://www.huskee.co/privacy-policy/">Privacy Policy</a></li> 8841<li><a href="https://www.huskee.co/terms-conditions/">Terms & Conditions</a></li> 8842<li><a href="https://www.huskee.co/shipping-delivery/">Shipping & Delivery</a></li> 8843<li><a href="https://www.huskee.co/returns-policy/">Returns & Refunds</a></li> 8844<li><a href="https://www.huskee.co/product-warranty/">Warranty</a></li> 8845</ul> 8846</article></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="NAWTGTX"><div data-content-type="text" data-appearance="default" data-element="main"><h5 class="footer-info-heading"><strong>Information</strong></h5> 8847<ul class="footer-info-list"> 8848<li><a href="/customer/account/login/">Wholesale</a></li> 8849<li><a href="https://huskee.co/locations/">Find Us</a></li> 8850</ul></div></div><div class="pagebuilder-column" data-content-type="column" data-appearance="full-height" data-background-images="{}" data-background-lazy-load="" data-element="main" data-pb-style="QR205FN"><div data-content-type="text" data-appearance="default" data-element="main"><ul class="social-media"> 8851<li><a href="https://www.facebook.com/Huskee" target="_blank" rel="noopener"><img src="/media/wysiwyg/socialiconhuskee/fb_1_.png" alt="" width="24" height="24"></a></li> 8852<li><a href="https://www.pinterest.com.au/huskeecup/" target="_blank" rel="noopener"><img src="/media/wysiwyg/socialiconhuskee/pt_1_.png" alt="" width="24" height="24"></a></li> 8853<li><a href="https://www.instagram.com/huskee/" target="_blank" rel="noopener"><img src="/media/wysiwyg/socialiconhuskee/ins_1_.png" alt="" width="24" height="24"></a></li> 8854<li><a href="https://www.linkedin.com/company/huskee" target="_blank" rel="noopener"><img src="/media/wysiwyg/socialiconhuskee/in_1_.png" alt="" width="24" height="24"></a><br><br><br><br></li> 8855</ul></div></div></div></div></div><div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><style> 8856.partnership-logo h2 { 8857 text-transform: uppercase; 8858 font-size: 26px; 8859 margin-bottom: 30px; 8860} 8861 8862.partnership-logo .pagebuilder-column-group { 8863 padding: 0 40px; 8864} 8865 8866.partnership-logo .pagebuilder-column-group .pagebuilder-column-line { 8867 column-gap: 20px; 8868} 8869#corp_servedValue, #corp_daysValue, #corp_durationValue, #corp_targetValue { 8870 display: none; 8871} 8872#ind_servedValue, #ind_daysValue, #ind_durationValue, #ind_targetValue { 8873 display: none; 8874} 8875 8876.highlights-section img { 8877 max-width: 120px !important; 8878} 8879ul.check-list li:before { 8880 float: left; 8881 clear: both; 8882} 8883.logo-section h2 { 8884 font-size: 36px; 8885} 8886.how_it_works a.pagebuilder-button-secondary { 8887 color: #628982; 8888 font-weight: 600; 8889 border: 1px solid #628982; 8890} 8891.grey-button-section [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay { 8892 padding-top: 100px; 8893} 8894.icons-sections p { 8895 font-weight: bold; 8896}
8897.technical h3 { 8898 font-size: 24px; 8899 color: #333; 8900 padding-bottom: 20px; 8901} 8902.cms-huskeeoriginal .column.main h2, .cms-huskeeoriginal .column.main p, .cms-flag-and-wire .column.main h2, .cms-flag-and-wire .column.main p, .cms-cat-and-cow .column.main h2, .cms-cat-and-cow .column.main p, .cms-beach-side-barista .column.main h2, .cms-beach-side-barista .column.main p, .cms-canva .column.main h2, .cms-canva .column.main p, .cms-huskeerenew .column.main h2, .cms-huskeerenew .column.main p { 8903 font-family: 'Raleway'; 8904} 8905.cms-beach-side-barista ul.check-list{ 8906font-family: 'Raleway'; 8907color: #333; 8908font-size: 18px; 8909} 8910.cms-huskeeoriginal .column.main h2 { 8911 font-size: 36px; 8912color:#333; 8913} 8914.cta-section a.pagebuilder-button-primary:hover { 8915 background: #fff; 8916 border: 1px solid #628982; 8917 color: #628982; 8918} 8919.production_timeline h3 { 8920 color: #4a4a4c; 8921} 8922.pink-button a.pagebuilder-button-primary { 8923 background: #e81e76; 8924 font-weight: 600; 8925} 8926.huskeeloop.pink-button [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay { 8927 padding-top: 80px; 8928} 8929.huskeeloop.pink-button img { 8930 margin-bottom: 30px; 8931} 8932 8933.huskeeloop.pink-button [data-content-type=banner] .pagebuilder-banner-wrapper .pagebuilder-banner-button { 8934 background: #e81e76; 8935} 8936ul.circle li:before { 8937 content: "â"; 8938 color: white; 8939 font-size: 18px; 8940 font-weight: bold; 8941 min-width: 40px; 8942 min-height: 40px; 8943 border-radius: 50%; 8944 display: inline-block; 8945 vertical-align: middle; 8946 border: 3px solid #fff; 8947 text-align: center; 8948 line-height: 34px; 8949 transform: translate(-50%, -50%); 8950 position: absolute; 8951 top: 50%; 8952 left: 0; 8953} 8954 8955.stewardship_program .pagebuilder-column-line .pagebuilder-column.box-list ul.circle li { 8956 position: relative; 8957 padding-left: 35px; 8958 list-style: none; 8959} 8960 8961.resuse_huskee .pagebuilder-column-group .pagebuilder-column-line ol li { 8962 font-weight: normal; 8963} 8964.text-shadow-section [data-content-type=banner] .pagebuilder-banner-wrapper [data-element=content] p { 8965 text-shadow: 3px 3px 9px #333; 8966} 8967.cms-page-view p { 8968 color: #4a4a4c; 8969} 8970.cms-page-view .text-white p { 8971 color: #fff; 8972} 8973.cms-page-view h2 { 8974 color: #4a4a4c; 8975} 8976.text-shadow-section [data-content-type=banner] .pagebuilder-banner-wrapper [data-element=content] h2 { 8977 text-shadow: 3px 3px 9px #333; 8978} 8979[data-content-type=banner] .pagebuilder-banner-wrapper .pagebuilder-banner-button { 8980 font-weight: 600; 8981} 8982.grey-button-section [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay .pagebuilder-banner-button { 8983 background: #868d98; 8984 text-transform: uppercase; 8985 font-weight: 600; 8986} 8987.light-grey-button a.pagebuilder-button-primary, .light-grey-button button.pagebuilder-button-primary, div.pagebuilder-button-primary { 8988 background: #868d98; 8989 font-weight: 600; 8990} 8991.green-button-section [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay .pagebuilder-banner-button { 8992 background: #628982; 8993 text-transform: uppercase; 8994 font-weight: 600; 8995} 8996.restaurants-section [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay { 8997 padding-top: 60px; 8998} 8999.hotel-section [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay button.pagebuilder-banner-button.pagebuilder-button-primary { 9000 background: #67978c; 9001 font-weight: bold; 9002 font-size: 16px; 9003 padding: 10px 20px; 9004 border-radius: 5px; 9005} 9006[data-content-type=banner] .pagebuilder-banner-wrapper .pagebuilder-overlay.pagebuilder-poster-overlay h2 { 9007 color: #4a4a4c; 9008 margin-bottom: 10px; 9009} 9010 9011[data-content-type=banner] .pagebuilder-banner-wrapper .pagebuilder-overlay.pagebuilder-poster-overlay p { 9012color: #4a4a4c; 9013} 9014[data-content-type=banner] .pagebuilder-banner-wrapper .pagebuilder-overlay.pagebuilder-poster-overlay h2 { 9015 color: #4a4a4c; 9016 margin-bottom: 10px; 9017} 9018 9019.huskeerenew [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay { 9020 max-width: 60rem; 9021} 9022.pagebuilder-column-group.renew-content { 9023 margin-bottom: 60px; 9024} 9025.huskeerenew [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay h2 { 9026 font-weight: normal; 9027 font-family: 'Raleway'; 9028} 9029.huskeerenew [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay h2 span:first-child { 9030 font-size: 22px; 9031} 9032 9033.huskeerenew [data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay .pagebuilder-banner-button { 9034 background: #628982; 9035 text-transform: uppercase; 9036 font-weight: 600; 9037} 9038.grey-button a.pagebuilder-button-primary, .grey-button button.pagebuilder-button-primary, div.pagebuilder-button-primary { 9039 background: #4A4A4C; 9040color: #FFFFFF; 9041 font-weight: 600; 9042} 9043.green-button a.pagebuilder-button-primary, .green-button button.pagebuilder-button-primary{ 9044 background: #628982; 9045 font-weight: 600; 9046} 9047.education-section{padding-top:50px;} 9048.education-section [data-content-type=banner][data-appearance=collage-left] .pagebuilder-banner-wrapper .pagebuilder-overlay h2 { 9049 font-size: 38px; 9050} 9051.education-section [data-content-type=banner][data-appearance=collage-left] .pagebuilder-banner-wrapper .pagebuilder-overlay { 9052 padding-top: 50px; 9053max-width: 55rem; 9054} 9055 9056.education-section [data-content-type=banner][data-appearance=collage-left] .pagebuilder-banner-wrapper .pagebuilder-overlay [data-element=content] { 9057 background: rgba(74, 74, 76, .54); 9058} 9059 9060.education-section [data-content-type=banner][data-appearance=collage-left] .pagebuilder-banner-wrapper .pagebuilder-overlay button.pagebuilder-banner-button.pagebuilder-button-primary { 9061 background: #67978c; 9062 font-weight: bold; 9063 font-size: 16px; 9064 padding: 10px 20px; 9065 border-radius: 5px; 9066} 9067[data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay h2 { 9068 color: #333; 9069 font-size: 36px; 9070} 9071.huskeeloop h2 { 9072 font-size: 36px !important; 9073 font-weight: 400; 9074 margin: 0; 9075} 9076.container .page-title { 9077 padding: 0 15px; 9078 position: relative; 9079 text-align: center; 9080 width: 100%; 9081 color: #4a4a4c; 9082 font-size: 26px; 9083 max-width: 1200px; 9084 margin: 0 auto; 9085} 9086.catalog-category-view h1:before, .catalogsearch-result-index h1:before, .checkout-cart-index h1:before, .cms-page-view h1:before, .customer-account-create h1:before, .customer-account-forgotpassword h1:before, .customer-account-login h1:before, .page-layout-checkout h1:before { 9087 content: ""; 9088 border-top: 2px solid #ccc; 9089 position: absolute; 9090 left: 0; 9091 width: 100%; 9092 top: 48%; 9093 z-index: -1; 9094} 9095.catalog-category-view h1 span, .catalogsearch-result-index h1 span, .checkout-cart-index h1 span, .cms-page-view h1 span, .customer-account-create h1 span, .customer-account-forgotpassword h1 span, .customer-account-login h1 span, .page-layout-checkout h1 span { 9096 background: #fff; 9097 padding: 0 10px; 9098} 9099.accordion-content.open { 9100 text-align: left; 9101} 9102 9103.product-row .pagebuilder-column { 9104 position: relative; 9105 transition: ease-in-out .3s; 9106} 9107 9108.product-row .pagebuilder-column h3 { 9109 font-size: 30px; 9110 font-family: 'Raleway'; 9111 position: absolute; 9112 top: 30%; 9113 left: 0; 9114 width: 100%; 9115 text-align: center; 9116 opacity: 0; 9117 transition: ease-in-out .3s; 9118 z-index: 1; 9119} 9120 9121.product-row .pagebuilder-column div[data-content-type="button-item"] { 9122 position: absolute; 9123 left: 0; 9124 width: 100%; 9125 text-align: center; 9126 margin: 0; 9127 bottom: 30%; 9128 opacity: 0; 9129 visibility: hidden; 9130 transition: ease-in-out .3s; 9131 z-index: 1; 9132} 9133 9134.product-row .pagebuilder-column div[data-content-type="button-item"] a.pagebuilder-button-primary { 9135 padding: 6px 25px; 9136 font-size: 16px; 9137 font-weight: 600; 9138} 9139
9140.product-row .pagebuilder-column:hover div[data-content-type="button-item"] { 9141 opacity: 1; 9142 visibility: visible; 9143} 9144 9145.product-row .pagebuilder-column:hover h3 { 9146 opacity: 1; 9147} 9148 9149.product-row .pagebuilder-column:hover:after { 9150 content: ''; 9151 background: rgba(255, 255, 255, .2); 9152 width: 100%; 9153 height: 100%; 9154 position: absolute; 9155 left: 0; 9156 z-index: 0; 9157} 9158ul.zn-editor-list { 9159 list-style: disc; 9160 padding-left: 20px; 9161} 9162 9163ul.zn-editor-list li { 9164 margin-bottom: 10px; 9165} 9166ol.circle-list { 9167 list-style: circle; 9168 padding-left: 20px; 9169 margin-bottom: 20px; 9170} 9171 9172ol.circle-list li { 9173 margin-bottom: 15px; 9174} 9175 9176ol.number-list { 9177 list-style: decimal; 9178 padding-left: 20px; 9179 margin-bottom: 20px; 9180} 9181 9182ol.number-list li { 9183 margin-bottom: 15px; 9184} 9185[data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay { 9186 padding-top: 150px; 9187} 9188 9189[data-content-type=banner][data-appearance=collage-right] .pagebuilder-banner-wrapper .pagebuilder-overlay p { 9190 margin-bottom: 10px; 9191} 9192 9193[data-content-type=banner][data-appearance=collage-left] .pagebuilder-banner-wrapper .pagebuilder-overlay { 9194 padding-top: 150px; 9195} 9196 9197[data-content-type=banner][data-appearance=collage-left] .pagebuilder-banner-wrapper .pagebuilder-overlay p { 9198 margin-bottom: 10px; 9199} 9200
9201.benefits { 9202 padding: 60px 0; 9203} 9204 9205.benefits h2 { 9206 margin-bottom: 30px; 9207 color: #628982; 9208} 9209 9210.benefits li { 9211 padding: 30px; 9212 background: #ebf2ed; 9213 margin-bottom: 30px; 9214 border-radius: 10px; 9215 min-height: 120px; 9216 display: flex; 9217 align-items: center; 9218 color: #628982; 9219 font-family: 'Raleway'; 9220} 9221 9222.pagebuilder-column-group.get-started { 9223 margin: 40px 0; 9224} 9225 9226.pagebuilder-column-group.get-started .pagebuilder-column-line { 9227 gap: 30px; 9228} 9229 9230.pagebuilder-column-group.get-started img { 9231 max-width: 70px !important; 9232 position: absolute; 9233 left: 0; 9234 width: 100%; 9235 text-align: center; 9236 right: 0; 9237 margin: 0 auto; 9238 top: -35px; 9239} 9240 9241.pagebuilder-column-group.get-started .pagebuilder-column { 9242 position: relative; 9243 padding: 60px; 9244 color: #fff; 9245} 9246 9247.pagebuilder-column-group.get-started .pagebuilder-column p { 9248 color: #fff; 9249} 9250 9251.pagebuilder-column-group.get-started .pagebuilder-column a.pagebuilder-button-primary { 9252 background: #666; 9253} 9254 9255.tabbing [data-content-type=tabs] .tabs-navigation li.tab-header a.tab-title span { 9256 color: #628982; 9257 font-size: 16px; 9258} 9259 9260.tabbing [data-content-type=tabs] .tabs-content .pagebuilder-column-line { 9261 gap: 40px; 9262} 9263.tabbing [data-content-type=tabs] .tabs-content .pagebuilder-column-line ul { 9264padding-left:25px; 9265} 9266 9267.tabbing [data-content-type=tabs] .tabs-content .pagebuilder-column-line p, .tabbing [data-content-type=tabs] .tabs-content .pagebuilder-column-line ul li { 9268 color: #628982; 9269 margin-bottom: 15px; 9270list-style: disc; 9271} 9272 9273.tabbing [data-content-type=tabs] .tabs-content .pagebuilder-column-line .tab-text-bg { 9274 padding: 30px; 9275 background: #ebf2ed; 9276 margin-bottom: 30px; 9277} 9278.cms_hero_banner { 9279 padding-top: 35px; 9280} 9281@media (max-width:767px){ 9282.cms_hero_banner .pagebuilder-overlay.pagebuilder-poster-overlay { 9283 min-height: 400px !important; 9284} 9285 9286.cms_hero_banner .pagebuilder-banner-wrapper { 9287 background-position: center !important; 9288} 9289.tabbing [data-content-type=tabs] .tabs-navigation li.tab-header { 9290 display: block; 9291} 9292 9293[data-content-type=row][data-appearance=full-width]>.row-full-width-inner { 9294 max-width: 100%; 9295 } 9296.pagebuilder-column-group.renew-content { 9297 margin-bottom: 0; 9298} 9299.product-row{ 9300display:none; 9301} 9302[data-content-type=banner] .pagebuilder-banner-wrapper .pagebuilder-overlay.pagebuilder-poster-overlay p { 9303 padding-right: 0; 9304} 9305.partnership-logo h2 { 9306 text-align: center; 9307 font-size: 20px; 9308 margin-bottom: 20px; 9309} 9310 9311.partnership-logo .pagebuilder-column-group { 9312 padding: 5px 0 !important; 9313} 9314 9315.partnership-logo .pagebuilder-column-group .pagebuilder-column-line { 9316 display: grid !important; 9317 grid-template-columns: repeat(5, 1fr); 9318 column-gap: 10px; 9319} 9320 9321.partnership-logo .pagebuilder-column-group .pagebuilder-column-line .pagebuilder-column { 9322 min-width: 100%; 9323} 9324 9325.partnership-logo { 9326 padding: 30px 0 !important; 9327} 9328} 9329@media only screen and (min-device-width:991px) and (max-device-width:1200px){ 9330.header-menu.fixed .logo.sticky-menu { 9331 display: none; 9332} 9333.header-search { 9334 max-width: 250px; 9335} 9336} 9337</style></div> <div class="flex flex-wrap gap-8 pr-4 w-full lg:w-1/4 md:w-1/2"> 9338 9339 </div> 9340 </div> 9341
9342<script> 9343 'use strict'; 9344 9345 function amBrandsPopup() { 9346 let cached = false; 9347 9348 const displayError = (error) => { 9349 console.error(error); 9350 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 9351 [{ 9352 type: "error", 9353 text: "Something\u0020went\u0020wrong.\u0020Please\u0020try\u0020again." 9354 }], 10000 9355 ); 9356 }; 9357 9358 let letters = [ 9359 "A","B","C","D", 9360 "E","F","G","H","I","J","K","L","M", 9361 "N","O","P","Q","R","S","T","U","V","W","X","Y","Z","#" 9362 ]; 9363 9364 return { 9365 hoverPanelActiveId: null, 9366 selectedLetter: null, 9367 items: [], 9368 letters: [], 9369 showCount: null, 9370 showFilter: null, 9371 showImages: true, 9372 showSearch: null, 9373 loading: false, 9374 label: 'Brands', 9375 isPopupEnabled: false, 9376 isTopMenuItemEnabled: false, 9377 cache: false, 9378 isMobile: false, 9379 9380 initErrorMessages(errors) { 9381 const messages = []; 9382 for (let error in Object.keys(errors)) { 9383 messages.push({type: 'error', text: errors[error].message}); 9384 } 9385 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(messages, 10000); 9386 }, 9387 9388 chooseLetter(letter) { 9389 this.selectedLetter = this.selectedLetter === letter ? null : letter; 9390 }, 9391 9392 getQuery() { 9393 return "query BrandsPopupQuery { ambrandlist(\n showCount: true\n displayZero: true\n ) {\n all_letters,\n items {\n alt\n brandId\n cnt\n image\n img\n label\n letter\n url\n }\n }\n storeConfig {\n amshopby_brand_general_brands_brands_popup,\n amshopby_brand_general_menu_item_label,\n amshopby_brand_general_brands_page,\n amshopby_brand_general_topmenu_enabled,\n amshopby_brand_general_brands_popup_config {\n show_images,\n display_zero,\n filter_display_all,\n image_width,\n image_height,\n show_filter,\n show_count\n }\n } }" }, 9394 9395 /** 9396 * Retrieve media url for given path 9397 * 9398 * @param path 9399 * @returns {string} 9400 */ 9401 getMediaUrl(path) { 9402 const baseUrl = BASE_URL.replace('index.php/', ''); 9403 if (path.includes('://')) { 9404 return path; 9405 } 9406 9407 let productImageUrl = `${baseUrl}${path}`; 9408 const regexDuplicateDomain = new RegExp(`^(https?:\/\/[^/]+).*\\1`); 9409 productImageUrl = productImageUrl.replace(regexDuplicateDomain, '$1'); 9410 9411 return productImageUrl.includes('/product/placeholder/') 9412 ? productImageUrl.replace(/\/media(?=\/static)/, '') 9413 : productImageUrl; 9414 }, 9415 9416 checkIsMobileResolution() { 9417 this.isMobile = window.matchMedia('(max-width: 1023px)').matches; 9418 }, 9419 9420 getBrandUrl(url){ 9421 return BASE_URL + url; 9422 }, 9423 9424 init() { 9425 this.checkIsMobileResolution(); 9426 9427 this.$watch('hoverPanelActiveId', (value) => { 9428 if (value === 'am-brand') { 9429 this.sendRequest(); 9430 } 9431 }); 9432 }, 9433 9434 /** 9435 * Get brands by letter 9436 * 9437 * @param result 9438 * @returns {{imageWidth: *, isTopMenuItemEnabled: *, showCount: *, displayAll: *, showFilter: *, showImages: *, isPopupEnabled: *, label: *, imageHeight: *, filterDisplayAll: *, brandsPage: *}} 9439 */ 9440 settingsMapper(result) { 9441 const config = result.data.storeConfig?.amshopby_brand_general_brands_popup_config; 9442 return { 9443 showImages: config.show_images, 9444 showCount: config.show_count, 9445 showFilter: config.show_filter, 9446 imageWidth: config.image_width, 9447 imageHeight: config.image_height, 9448 filterDisplayAll: config.filter_display_all, 9449 displayAll: config.display_zero, 9450 isPopupEnabled: result.data.storeConfig?.amshopby_brand_general_brands_brands_popup, 9451 label: result.data.storeConfig?.amshopby_brand_general_menu_item_label, 9452 isTopMenuItemEnabled: result.data.storeConfig?.amshopby_brand_general_topmenu_enabled, 9453 brandsPage: result.data.storeConfig?.amshopby_brand_general_brands_page, 9454 } 9455 }, 9456 9457 /** 9458 * Send request to graphql 9459 * 9460 * @returns {Promise<any>} 9461 */ 9462 async sendRequest() { 9463 if (this.isMobile) { 9464 return false;
9465 } 9466 9467 this.loading = true; 9468 9469 try { 9470 if (cached) { 9471 this.loading = false; 9472 return false; 9473 } 9474 9475 const params = new URLSearchParams({ 9476 query: this.getQuery(), 9477 variables: JSON.stringify({}) 9478 }); 9479 9480 const response = await fetch(`https://www.huskee.co/graphql?${params}`, { 9481 method: 'GET', 9482 headers: { 'Store': 'huskee_au_store_view' }, 9483 credentials: 'include' 9484 }); 9485 9486 const result = await response.json(); 9487 9488 if (result?.errors) { 9489 this.initErrorMessages(result.errors); 9490 return; 9491 } 9492 9493 const data = result.data?.ambrandlist; 9494 const mapper = this.settingsMapper(result); 9495 9496 // Update component settings 9497 Object.keys(mapper).forEach(config => { 9498 this[config] = mapper[config]; 9499 }); 9500 9501 if (!this.filterDisplayAll) { 9502 letters = data.all_letters.split(','); 9503 } 9504 9505 const items = data.items.map(brand => { 9506 brand.image = brand.img 9507 ? this.getMediaUrl(brand.img) : (brand.image ? this.getMediaUrl(brand.image) : null); 9508 return brand; 9509 }); 9510 9511 this.letters = letters.map(letter => ({ 9512 letter, 9513 brands: this.getLetterItems(items, letter), 9514 })); 9515 9516 this.items = this.letters.filter(row => row.brands?.length > 0); 9517 9518 cached = true; 9519 } catch (error) { 9520 displayError(error); 9521 } finally { 9522 this.loading = false; 9523 } 9524 }, 9525 9526 /** 9527 * Get brands by letter 9528 * 9529 * @param items 9530 * @param letter 9531 * @returns {*} 9532 */ 9533 getLetterItems(items, letter) { 9534 return items.filter(row => row.letter === letter); 9535 }, 9536 9537 convertHtml(text) { 9538 let textField = document.createElement("textarea"); 9539 textField.innerHTML = text; 9540 return textField.value; 9541 } 9542 } 9543 } 9544 9545 function amBrandsPopupData() { 9546 return Object.assign( 9547 amBrandsPopup(), 9548 { 9549 brandPopupClass() { 9550 return { 9551 'hover:border-primary data-[active]:border-primary': !this.isPopupEnabled 9552 } 9553 }, 9554 labelPopupClass() { 9555 return {'w-14 opacity-0': this.label === ''} 9556 }, 9557 popupcontentClass() { 9558 return { 9559 'hidden' : this.hoverPanelActiveId !== 'am-brand', 9560 'left-0' : `${this.$el.dataset.first}`, 9561 'right-0' : `${this.$el.dataset.last}` 9562 } 9563 }, 9564 panelActiveMouseenterEvent() { 9565 return this.hoverPanelActiveId = 'am-brand' 9566 }, 9567 panelActiveMouseleaveEvent() { 9568 return this.hoverPanelActiveId = null 9569 }, 9570 isPopupShow() { 9571 return this.hoverPanelActiveId === 'am-brand' 9572 }, 9573 labelText(){ 9574 return this.label 9575 }, 9576 isEnabledPopup(){ 9577 return this.isPopupEnabled 9578 }, 9579 showExtMessage(){ 9580 return false 9581 }, 9582 notLoading() { 9583 return !this.loading 9584 }, 9585 brandLabel() { 9586 return this.brand.label 9587 }, 9588 letterClass() { 9589 return { 9590 'border-blue-400 text-blue-400 bg-blue-100': this.selectedLetter === this.letter.letter, 9591 'bg-gray-200 text-gray-700 cursor-default pointer-events-none': !this.letter.brands || this.letter.brands.length === 0 9592 } 9593 }, 9594 letterClickEvent(event) { 9595 return this.chooseLetter(this.letter.letter) 9596 }, 9597 letterBrands() { 9598 return this.letter.brands 9599 }, 9600 brandUrl() { 9601 return this.getBrandUrl(this.brand.url) 9602 }, 9603 brandImage() { 9604 return this.brand.image 9605 },
9606 notBrandImage() { 9607 return !this.brand.image 9608 }, 9609 letterLetter() { 9610 return this.letter.letter 9611 }, 9612 brandBrandId() { 9613 return this.brand.brandId 9614 }, 9615 isLetterShow() { 9616 return !this.selectedLetter || this.selectedLetter === this.letter.letter 9617 }, 9618 brandImageMediaUrl() { 9619 return this.getMediaUrl(this.brand.image) 9620 }, 9621 brandImageStyle() { 9622 return `max-width: ${this.imageWidth ? this.imageWidth + 'px' : '100px' };max-height: ${this.imageHeight ? this.imageHeight + 'px' : 'inherit'}` 9623 }, 9624 brandsLabelStyle() { 9625 return `min-width: ${this.imageWidth}px` 9626 }, 9627 brandLabelFilter() { 9628 return this.brand.label.charAt(0) 9629 }, 9630 brandCnt() { 9631 return '(' + this.brand.cnt + ')' 9632 } 9633 } 9634 ) 9635 } 9636 window.addEventListener('alpine:init', () => Alpine.data('amBrandsPopupData', amBrandsPopupData), {once: true}); 9637</script>
9637 9638</div></footer>
9639<script> 9640 function dataServicesShoppingCartContextData() { 9641 return { 9642 initContext() { 9643 const cartData = this.$event.detail.data.cart; 9644 let contextCartItems = []; 9645 9646 if (cartData && cartData.items) { 9647 cartData.items.forEach(function (item) { 9648 contextCartItems.push({ 9649 id: item.item_id, 9650 formattedPrice: parseFloat(item.product_price_value ? item.product_price_value : 0), 9651 quantity: item.qty, 9652 product: { 9653 productType: item.product_type, 9654 productId: item.product_id, 9655 name: item.product_name, 9656 sku: item.product_sku, 9657 mainImageUrl: item.product_image.src 9658 }, 9659 prices: { 9660 price: { 9661 value: parseFloat(item.product_price_value ? item.product_price_value : 0) 9662 } 9663 } 9664 }); 9665 }); 9666 9667 magentoStorefrontEvents.context.setShoppingCart({ 9668 id: cartData.dsCartId, 9669 totalQuantity: cartData.summary_count, 9670 prices: { 9671 subtotalExcludingTax: { 9672 value: parseFloat(cartData.subtotalAmountExclTax ? cartData.subtotalAmountExclTax : 0) 9673 }, 9674 subtotalIncludingTax: { 9675 value: parseFloat(cartData.subtotalAmount ? cartData.subtotalAmount : 0) 9676 } 9677 }, 9678 items: contextCartItems, 9679 possibleOnepageCheckout: cartData.possible_onepage_checkout, 9680 giftMessageSelected : false, 9681 giftWrappingSelected : false 9682 }); 9683 } 9684 9685 localStorage.setItem("ds-cart", JSON.stringify({ 9686 cart: { 9687 items: cartData.items 9688 } 9689 })); 9690 9691 window.dispatchEvent(new Event("shoppingCartContextSet")); 9692 } 9693 } 9694 } 9695 9696 window.addEventListener('alpine:init', () => Alpine.data('dataServicesShoppingCartContextData', dataServicesShoppingCartContextData), {once: true}); 9697</script>
9697 9698 9699<div x-data="dataServicesShoppingCartContextData" 9700 @private-content-loaded.window="initContext"></div> 9701
9702<script type="text/javascript">window.dataLayer = window.dataLayer || []; 9703 9704(function () { 9705 /** 9706 * @var {[Object]} 9707 */ 9708 const dataToPush = JSON.parse('\u005B\u005D'); 9709 9710 dataToPush.forEach((dataObject) => { 9711 window.dataLayer.push(dataObject); 9712 }); 9713})(); 9714</script>
9714 9715
9716<script> 9717 (_ => { 9718 'use strict'; 9719 9720 const SECTION_KEY = 'amga4_hyva_event_pool'; 9721 const MAGE_CACHE_SECTION_KEY = 'mage-cache-storage'; 9722 const storage = hyva.getBrowserStorage(); 9723 9724 window.addEventListener('private-content-loaded', ({ detail }) => { 9725 if (!detail?.data) { 9726 return; 9727 } 9728 9729 const data = detail?.data[SECTION_KEY]; 9730 if (!data) { 9731 return; 9732 } 9733 9734 let eventsPool = data?.events || []; 9735 if (eventsPool.length) { 9736 eventsPool.forEach(event => { 9737 window.dataLayer.push({ecommerce: null}); 9738 window.dataLayer.push(event); 9739 }); 9740 9741 setTimeout(() => { 9742 storage.setItem( 9743 MAGE_CACHE_SECTION_KEY, 9744 JSON.stringify({ 9745 ...detail.data, 9746 [SECTION_KEY]: {} 9747 }) 9748 ); 9749 }, 0); 9750 } 9751 }); 9752 })(); 9753</script>
9753 9754
9755<script type="text/x-magento-init"> 9756 { 9757 "*": { 9758 "Amasty_GeoipRedirect/js/storeCookieChecker": {} 9759 } 9760 } 9761</script>
9761 9762 9763
9763<script type="text/x-magento-init"> 9764 { 9765 "*": { 9766 "Amasty_GeoipRedirect/js/model/varnish/set-redirect-cookie": {} 9767 } 9768 } 9769 </script>
vendor: 1 bytes, line 9769
9769
9770<script> 9771 'use strict'; 9772 9773 (function() { 9774 const allGroups = ['0']; 9775 9776 const resolveAllowEvent = { 9777 getAllowedGroups: () => allGroups 9778 } 9779 9780 const resolveAcceptEvent = { 9781 getAllowedGroups: () => Alpine.store('AmastyCookieGroups').getCheckedGroupIds() ?? [] 9782 } 9783 9784 function updateConsent () { 9785 if (!window.amConsentManager) { 9786 return; 9787 } 9788 9789 window.amConsentManager.updateConsent( 9790 window.amConsentManager.getConsentTypeStateByGroupIds(this.getAllowedGroups()) 9791 ); 9792 } 9793 9794 function resetConsent () { 9795 if (!window.amConsentManager) { 9796 return; 9797 } 9798 9799 window.amConsentManager.resetConsentState(); 9800 } 9801 9802 window.addEventListener('cookiebar-action-accept-finish', updateConsent.bind(resolveAcceptEvent)); 9803 window.addEventListener('cookiebar-action-allow-finish', updateConsent.bind(resolveAllowEvent)); 9804 window.addEventListener('amcookie-show-notification-bar', resetConsent); 9805 })() 9806</script>
9806 9807
9808<script type="text/x-magento-init"> 9809 { 9810 "*": { 9811 "Biopak_CustomerLeadSource/js/url-param-cookie": {} 9812 } 9813 } 9814</script>
9814 9815 <style type="text/css"> 9816 .page.messages { 9817 width: 100% !important; 9818 } 9819 </style> 9820 9821 <style type="text/css"> 9822 .message-success.success.message { 9823 background-color: #77b54e; 9824 } 9825 </style> 9826 9827 <style type="text/css"> 9828 .message-success.success.message { 9829 color: #ffffff; 9830 } 9831 </style> 9832 <style type="text/css"> 9833 .message-error.error.message { 9834 background-color: #e74d3c; 9835 } 9836 </style> 9837 9838 <style type="text/css"> 9839 .message-error.error.message { 9840 color: #ffffff; 9841 } 9842 </style> 9843 <style type="text/css"> 9844 .message-notice.notice.message { 9845 background-color: #000000; 9846 } 9847 </style> 9848 9849 <style type="text/css"> 9850 .message-notice.notice.message { 9851 color: #ffffff; 9852 } 9853 </style> 9854 <style type="text/css"> 9855 .message-warning.warning.message { 9856 background-color: ; 9857 } 9858 </style> 9859 9860 <style type="text/css"> 9861 .message-warning.warning.message { 9862 color: #ffffff; 9863 } 9864 </style> 9865
9866<script> 9867 'use strict'; 9868 9869 (() => { 9870 const initCustomMessage = () => { 9871 const configs = 0; 9872 const closeOptions = "0"; 9873 const messagesElement = document.querySelector('.page.messages'); 9874 9875 if (!messagesElement) { 9876 return; 9877 } 9878 9879 const positions = { 9880 0: 'top', 9881 1: 'bottom', 9882 2: 'middle', 9883 3: 'topleft', 9884 4: 'topright', 9885 5: 'bottomleft', 9886 6: 'bottomright', 9887 7: 'middleleft', 9888 8: 'middleright' 9889 }; 9890 9891 // Add position class 9892 if (positions[configs]) { 9893 messagesElement.classList.add(positions[configs]); 9894 } 9895 9896 const hideMessages = () => { 9897 messagesElement.style.transition = 'opacity 1s'; 9898 messagesElement.style.opacity = '0'; 9899 setTimeout(() => { 9900 messagesElement.style.display = 'none'; 9901 }, 1000); 9902 }; 9903 9904 // Close option: 1 = Click on message to close 9905 if (closeOptions === '1') { 9906 messagesElement.classList.add('close_on_click'); 9907 messagesElement.style.cursor = 'pointer'; 9908 messagesElement.addEventListener('click', hideMessages); 9909 } 9910 // Close option: 0 = Auto close after 5 seconds 9911 else if (closeOptions === '0') { 9912 setTimeout(() => { 9913 hideMessages(); 9914 }, 5000); 9915 } 9916 // Close option: 2 = Click anywhere to close 9917 else if (closeOptions === '2') { 9918 document.body.addEventListener('click', (e) => { 9919 if (messagesElement.style.display !== 'none') { 9920 hideMessages(); 9921 } 9922 }); 9923 } 9924 }; 9925 9926 // Initialize when DOM is ready 9927 if (document.readyState === 'loading') { 9928 document.addEventListener('DOMContentLoaded', initCustomMessage); 9929 } else { 9930 initCustomMessage(); 9931 } 9932 9933 // Re-initialize when customer data is loaded (for messages that appear after AJAX) 9934 document.addEventListener('customer-data-invalidate', () => { 9935 setTimeout(initCustomMessage, 100); 9936 }); 9937 })(); 9938</script>
9938 9939 9940
9940<script wire:id="stripe.paymentrequest.minicart" wire:initial-data="{"fingerprint":{"id":"stripe.paymentrequest.minicart","name":"stripe-payments.minicart.shortcut","locale":"en_AU","path":"\/","method":"GET","resolver":"layout","handle":"cms_index_index","type":"default","v":"acj"},"effects":{"listeners":["shipping_method_selected","payment_method_selected","coupon_code_applied","coupon_code_revoked","shipping_address_saved","shipping_address_activated","billing_address_saved","billing_address_activated","refresh"]},"serverMemo":{"data":{"eceParams":[],"walletParams":[],"placeOrderResult":[],"shippingAddress":[],"isEmptyCart":false,"productId":null,"resolvePayload":[],"orderPlaced":false,"totalAmount":0},"evaluation":{"stripe.paymentrequest.minicart":{"arguments":[],"dispatch":false,"result":true,"group":"batch-stripe.paymentrequest.minicart","type":"batch","id":"stripe.paymentrequest.minicart","blocking":false,"name":null,"hash":"0e25d5254c9ee39f58cd12678761e37293bccd48"}},"htmlHash":"9f19372b","children":[],"checksum":"c8be69d1aeac9104ee8d5da9caba15c324c4b413dd0921f358ffb568c6253986"}}"> 9941 function initStripePaymentRequestButtonMinicart() { 9942 const component = Magewire.find('stripe.paymentrequest.minicart'); 9943 let StripeJs, expressCheckoutElement; 9944 9945 return { 9946 isLoading: false, 9947 initMinicartButton() { 9948 component.loadSettings('minicart').then(() => { 9949 if (component.isEmptyCart) { 9950 // Cart is empty, don't continue 9951 return; 9952 } 9953 9954 this.loadStripe() 9955 }); 9956 9957 window.addEventListener( 9958 'stripe-authenticate-customer', 9959 event => this.authenticateCustomer(event) 9960 ); 9961 }, 9962 9963 loadStripe() { 9964 if (typeof window.Stripe === 'undefined') { 9965 window.loadStripe(() => this.initPaymentRequest()); 9966 } else { 9967 this.initPaymentRequest(); 9968 } 9969 }, 9970 9971 initPaymentRequest() { 9972 9973 if (expressCheckoutElement) { expressCheckoutElement.destroy(); } 9974 9975 StripeJs = Stripe(component.walletParams.apiKey, component.walletParams.options); 9976 9977 const elements = StripeJs.elements(component.eceParams.elementOptions); 9978 expressCheckoutElement = elements.create('expressCheckout', {"buttonHeight":50,"buttonTheme":{"applePay":"black","googlePay":"black","paypal":"gold","klarna":"dark"},"buttonType":{"applePay":"plain","googlePay":"buy","paypal":"paypal","klarna":"pay"},"layout":{"overflow":"never"},"paymentMethods":{"applePay":"auto","googlePay":"auto","link":"auto","paypal":"auto","amazonPay":"auto","klarna":"auto"}}); 9979 9980 const element = document.getElementById('stripe-minicart-button'); 9981 element.classList.add('payment-request-button'); 9982 element.classList.add('cart'); 9983 expressCheckoutElement.mount(element); 9984 9985 expressCheckoutElement.on('click', (event) => { 9986 this.isLoading = true; 9987 event.resolve(component.resolvePayload); 9988 }); 9989 9990 expressCheckoutElement.on('shippingaddresschange', event => { 9991 const address = event.address 9992 // For some countries like Japan, the ECE does not set the City, only the region 9993 if (address.city.length === 0 && address.region.length > 0) { 9994 address.city = address.region; 9995 } 9996 component.estimateCart(event.address).then( 9997 () => { 9998 const amount = window.stripeUtils.addLineItemsAmounts(component.resolvePayload.lineItems); 9999 if (amount > 0) { 10000 elements.update({ amount: amount }); 10001 } 10002 const resolveObject = { 10003 shippingRates: component.resolvePayload.shippingRates, 10004 lineItems: component.resolvePayload.lineItems ?? [] 10005 }; 10006 event.resolve(resolveObject); 10007 } 10008 ); 10009 }); 10010 10011 expressCheckoutElement.on('shippingratechange', event => { 10012 const shippingMethod = event.shippingRate.hasOwnProperty('id') 10013 ? event.shippingRate.id 10014 : null; 10015 10016 component.updateShippingRate(shippingMethod).then( 10017 () => { 10018 const amount = window.stripeUtils.addLineItemsAmounts(component.resolvePayload.lineItems); 10019 if (amount > 0) { 10020 elements.update({ amount: amount }); 10021 } 10022 const resolveObject = { 10023 shippingRates: component.resolvePayload.shippingRates, 10024 lineItems: component.resolvePayload.lineItems 10025 }; 10026 event.resolve(resolveObject); 10027 10028 } 10029 ); 10030 }); 10031 10032 expressCheckoutElement.on('cancel', (ev) => { 10033 component.loadSettings('minicart').then(() => { 10034 if (component.isEmptyCart) { 10035 // Cart is empty, don't continue 10036 return; 10037 } 10038 10039 this.loadStripe() 10040 }); 10041 10042 this.isLoading = false;
10043 window.dispatchEvent(new Event('reload-customer-section-data')); 10044 10045 return window.dispatchMessages && window.dispatchMessages([{ 10046 type: 'error', 10047 text: 'Express\u0020Checkout\u0020has\u0020been\u0020canceled.' 10048 }], 5000); 10049 }); 10050 10051 expressCheckoutElement.on('confirm', confirm => { 10052 this.confirm = confirm; 10053 elements.submit().then(() => { 10054 const paymentMethodData = { 10055 elements: elements, 10056 params: { 10057 billing_details: confirm.billingDetails 10058 }, 10059 shipping: confirm.shippingAddress 10060 }; 10061 10062 StripeJs.createConfirmationToken(paymentMethodData).then(result => { 10063 confirm.confirmationToken = result.confirmationToken; 10064 10065 this.placeOrder(confirm); 10066 }); 10067 }); 10068 }); 10069 }, 10070 10071 placeOrder() { 10072 this.isLoading = true; 10073 component.placeOrder( 10074 this.confirm, 10075 'product' 10076 ).then(() => { 10077 if (component.placeOrderResult.redirect) { 10078 window.location = component.placeOrderResult.redirect; 10079 return; 10080 } 10081 10082 if (component.placeOrderResult.error) { 10083 this.isLoading = false; 10084 window.dispatchMessages && window.dispatchMessages([{ 10085 type: 'error', 10086 text: component.placeOrderResult.error 10087 }], 5000); 10088 } 10089 10090 if (component.orderPlaced) { 10091 this.isLoading = false; 10092 } 10093 }) 10094 }, 10095 10096 authenticateCustomer(event) { 10097 const intentId = event.detail.clientSecret; 10098 const handleIntent = result => { 10099 const intent = result.paymentIntent || result.setupIntent; 10100 const requiresActionStatuses = ['requires_action', 'requires_source_action']; 10101 10102 if (!requiresActionStatuses.includes(intent.status)) { 10103 this.placeOrder(); 10104 return; 10105 } 10106 10107 if (intent.next_action && intent.next_action.type === 'verify_with_microdeposits') { 10108 window.location = intent.next_action.verify_with_microdeposits.hosted_verification_url; 10109 return; 10110 } 10111 10112 StripeJs.handleNextAction({ 10113 clientSecret: intent.client_secret 10114 }).then(result => { 10115 if (result.error) { 10116 this.isLoading = false; 10117 10118 window.dispatchMessages && window.dispatchMessages([{ 10119 type: 'error', 10120 text: result.error.message 10121 }], 5000); 10122 10123 return; 10124 } 10125 10126 this.placeOrder(); 10127 }); 10128 }; 10129 10130 if (intentId.startsWith('pi_')) { 10131 StripeJs.retrievePaymentIntent(intentId).then(handleIntent); 10132 return; 10133 } 10134 10135 if (intentId.startsWith('seti_')) { 10136 StripeJs.retrieveSetupIntent(intentId).then(handleIntent); 10137 return; 10138 } 10139 10140 throw new Error("Invalid intent ID"); 10141 } 10142 } 10143 } 10144 10145 window.addEventListener( 10146 'alpine:init', 10147 () => Alpine.data('initStripePaymentRequestButtonMinicart', initStripePaymentRequestButtonMinicart), 10148 {once: true} 10149 ) 10150 </script>
10150 10151 <!-- Magewire Component wire-end:stripe.paymentrequest.minicart -->
10151<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 10152 document.addEventListener('livewire:available', () => { 10153 const orig = Element.prototype.setAttribute; 10154 10155 Element.prototype.setAttribute = function patchLivewireSetAttribute(name, value) { 10156 // In Alpine v2, ignore Alpine v3 x-bind attributes that are used as x-spread 10157 if (window.Alpine && Alpine.version.substring(0, 1) === '2' && ! name) return; 10158 orig.call(this, name, value); 10159 } 10160 }) 10161</script>
10161 10162
10163<script src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Magewirephp_Magewire/js/livewire.js"></script>
10163 10164
10164<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 10165 'use strict'; 10166 10167 if (window.magewire) { 10168 console.warn('Magewire: It looks like Magewire\'s JavaScript assets have already been loaded. Make sure you aren\'t loading them twice.'); 10169 } 10170 10171 window.magewire = new Livewire() 10172 10173 window.Livewire = window.magewire 10174 window.Magewire = window.magewire 10175 window.livewire_app_url = 'https://www.huskee.co/magewire/post' 10176 window.livewire_token = window.hyva.getFormKey() 10177 10178 if (window.Alpine) { 10179 document.addEventListener("DOMContentLoaded", () => { 10180 setTimeout(function() { 10181 console.warn("Magewire: It looks like AlpineJS has already been loaded. Make sure Magewire\'s scripts are loaded before AlpineJS.") 10182 }) 10183 }) 10184 } 10185 10186 (() => { 10187 const startMagewireOnce = () => { 10188 document.removeEventListener('DOMContentLoaded', startMagewireOnce) 10189 window.removeEventListener('alpine:init', startMagewireOnce) 10190 window.magewire.start() 10191 } 10192 10193 const initAlpine = window.deferLoadingAlpine || ((callback) => callback()) 10194 window.deferLoadingAlpine = (callback) => { 10195 window.addEventListener('magewire:load', () => { 10196 initAlpine(callback) 10197 }); 10198 } 10199 document.addEventListener('DOMContentLoaded', startMagewireOnce) 10200 10201 window.addEventListener('alpine:init', startMagewireOnce) 10202 })(); 10203 10204 Magewire.dispatchEvent = (name, event = {}, element = null) => { 10205 let e = new CustomEvent(`magewire:${ name }`, Object.assign({ 10206 'bubbles': true, 10207 'cancelable': false 10208 }, event)) 10209 10210 if (element) { 10211 element.dispatchEvent(e); 10212 } else { 10213 document.dispatchEvent(e) 10214 } 10215 } 10216 10217 ['load', 'update'].forEach(function (target, index) { 10218 document.addEventListener('livewire:' + target, (event) => { 10219 Magewire.dispatchEvent(target, event) 10220 }) 10221 }) 10222 10223 Magewire.plugins = {} 10224 </script>
10224 10225 10226
10226<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 10227 (() => { 10228 let queue = []; 10229 10230 const loaderTypeResolvers = { 10231 syncInput: (item, loader) => { 10232 let name = item.payload.name; 10233 10234 let expression = [ 10235 `${ name }:${ item.payload.value }`, 10236 `${ name.substr(0, name.lastIndexOf('.')) }:${ item.payload.value }`, 10237 `${ name }`, 10238 `${ name.substr(0, name.lastIndexOf('.')) }` 10239 ].filter((expression) => { 10240 return expression in loader 10241 })[0] || false; 10242 10243 if (expression) { 10244 return loader[expression]; 10245 } 10246 10247 return true; 10248 }, 10249 callMethod: (item, loader) => { 10250 return loader[item.method]; 10251 }, 10252 fireEvent: (item, loader) => { 10253 return loader[item.payload.event]; 10254 } 10255 } 10256 10257 const getLoaderData = function(message, component) { 10258 let loader = component.effects.loader || null; 10259 10260 if (! loader) { 10261 return null; 10262 } 10263 10264 let data = {}; 10265 10266 for (let item of message.updateQueue) { 10267 let value = true; 10268 10269 if (typeof loader === 'object' && item.type in loaderTypeResolvers) { 10270 value = loaderTypeResolvers[item.type](item, loader); 10271 } else if (typeof loader === 'string') { 10272 value = [loader]; 10273 } 10274 10275 if (value === undefined) { 10276 value = false; 10277 10278 if ('*' in loader) { 10279 value = loader['*']; 10280 } 10281 } 10282 10283 data[component.id] = { 10284 key: Date.now(), 10285 component: component, 10286 loader: value, 10287 update: item 10288 }; 10289 } 10290 10291 return data; 10292 } 10293 10294 const start = function(loader, component) { 10295 if (! loader || ! component) { 10296 Magewire.dispatchEvent(`loader:start`); 10297 return; 10298 } 10299 10300 let first = ! queue.some(({ parent }) => parent); 10301 let me = queue.findIndex(item => item && item.component && item.component.id === component.id); 10302 10303 if (first) { 10304 queue.push({ 10305 loader: loader, 10306 component: component 10307 }); 10308 } 10309 10310 if (queue[me]) { 10311 queue[me].loader = loader; 10312 } 10313 10314 queue.forEach((item, index) => { 10315 item.parent = item.parent ?? false; 10316 10317 if (me > index && item.loader) { 10318 stop(item.component, false, true); 10319 } 10320 }); 10321 10322 Magewire.dispatchEvent(`loader:${ first ? 'start' : 'tick' }`, { 10323 detail: loader 10324 }); 10325 } 10326 10327 const stop = function(component, failure = false, force = false) { 10328 if (failure) { 10329 force = true; 10330 } 10331 10332 let remove = []; 10333 const me = queue.findIndex(item => item && item.component && item.component.id === component.id); 10334 10335 if (queue[me]) { 10336 queue[me].done = true; 10337 } 10338 10339 if (force) { 10340 remove.push(me); 10341 } else if (queue.every(item => item && item.done)) { 10342 remove = Object.keys(queue); 10343 } 10344 10345 remove.forEach(index => { 10346 if (queue[index] && queue[index].loader) { 10347 Magewire.dispatchEvent(`loader:stop`, { 10348 detail: { 10349 loader: queue[index].loader, 10350 component: queue[index].component, 10351 failure: failure 10352 } 10353 }); 10354 } 10355 10356 delete queue[index]; 10357 }); 10358 10359 if (Object.values(queue).length === 0) { 10360 Magewire.dispatchEvent(`loader:done`, { 10361 detail: { 10362 component: component, 10363 failure: failure 10364 } 10365 }); 10366 10367 // Empty insurance. 10368 queue = []; 10369 } 10370 } 10371 10372 Magewire.plugins.loader = (() => { 10373 return { 10374 start(loader, component) { 10375 return start(loader, component); 10376 }, 10377 stop(component, failure = false, force = false) { 10378 return stop(component, failure, force); 10379 } 10380 } 10381 })(); 10382 10383 document.addEventListener('DOMContentLoaded', () => { 10384 Magewire.hook('message.sent', (message, component) => { 10385 let loader = getLoaderData(message, component) || {}; 10386 10387 if (component.id in loader && loader[component.id].loader) { 10388 Magewire.plugins.loader.start(loader[component.id], component); 10389 } 10390 }); 10391 10392 Magewire.hook('message.processed', (message, component) => { 10393 if (message.response && message.response.effects) { 10394 (message.response.effects.emits || []).forEac
10394h((emit) => { 10395 Magewire.components.componentsListeningForEvent(emit.event) 10396 .filter(listener => ! emit.to || emit.to === listener.id) 10397 .forEach(listener => queue.push({ component: listener, parent: component })); 10398 }); 10399 10400 let ids = []; 10401 10402 for (let i = queue.length - 1; i >= 0; i--) { 10403 if (queue[i]) { 10404 if (ids.indexOf(queue[i].component.id) !== -1) { 10405 queue.splice(i, 1); 10406 continue; 10407 } 10408 10409 ids.push(queue[i].component.id); 10410 } 10411 } 10412 } 10413 10414 Magewire.plugins.loader.stop(component); 10415 10416 if (component.effects && message.response.effects) { 10417 component.effects.loader = message.response.effects.loader; 10418 } 10419 }); 10420 10421 Magewire.hook('message.failed', (message, component) => { 10422 Magewire.plugins.loader.stop(component, true); 10423 }); 10424 }); 10425 })(); 10426</script>
vendor: 1 bytes, line 10426
10426
10427<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 10428 Magewire.onPageExpired(() => { 10429 alert('The\u0020session\u0020has\u0020expired.\u0020Therefore,\u0020the\u0020page\u0020will\u0020be\u0020refreshed\u0020when\u0020you\u0020confirm.') 10430 window.location.reload() 10431 }) 10432 10433 Magewire.onError((status, response) => { 10434 if (status === 419) { 10435 return 10436 } 10437 10438 response.clone().text().then((result) => { 10439 let messages = {"419":"The session has expired. Therefore, the page will be refreshed when you confirm."}; 10440 result = JSON.parse(result) 10441 10442 console.error( 10443 `Magewire: ${ messages[status] || result.message || 'Something went wrong' } (${ status })` 10444 ) 10445 }).catch((exception) => { 10446 console.error(exception) 10447 }) 10448 10449 return false 10450 }) 10451</script>
10451 10452 10453
10453<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 10454 Magewire.hasPlugin = function(name) { 10455 const plugins = Magewire.plugins 10456 10457 if (typeof plugins !== 'object' || plugins.length === 0) { 10458 return false; 10459 } else if (typeof name === 'object') { 10460 return name.every((name) => Magewire.hasPlugin(name)) 10461 } 10462 10463 return name in plugins 10464 } 10465</script>
10465 10466 10467
10467<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 10468 Magewire.dispatchEvent('available') 10469 </script>
10469 10470 10471
10471<script nonce="c2VreXlhbmxhamxpYnVmZWdvZHh4YmUyN2diZzF0OXI="> 10472 Magewire.directive('select', (element, directive, component) => { 10473 if (element.tagName !== 'SELECT') { 10474 console.error('Magewire: [wire:select] can only be applied on elements of type select.', element) 10475 return 10476 } 10477 if (directive.value) { 10478 console.error('Magewire: [wire:select] shouldn\'t have a value.', element) 10479 return 10480 } 10481 if (! element.hasAttribute('wire:model.defer')) { 10482 console.error('Magewire: [wire:model] requires a defer modifier for [wire:select] to function properly.', element) 10483 return 10484 } 10485 10486 let changed = false, 10487 timeout = null 10488 10489 const sync = () => { 10490 clearTimeout(timeout) 10491 10492 if (changed) { 10493 changed = false 10494 10495 component.fireMessage() 10496 } 10497 } 10498 10499 const debounce = () => { 10500 if (directive.modifiers.includes('debounce')) { 10501 clearTimeout(timeout) 10502 10503 timeout = setTimeout(() => sync(), directive.durationOr(1500)) 10504 } 10505 } 10506 10507 const change = () => { 10508 changed = true 10509 10510 debounce() 10511 } 10512 10513 element.addEventListener('keydown', debounce) 10514 element.addEventListener('change', change) 10515 10516 if (directive.modifiers.includes('blur')) { 10517 element.addEventListener('blur', () => sync()) 10518 } 10519 }) 10520</script>
10520 10521
10522<script> 10523 let SnapSlider = class { 10524 constructor(el, { 10525 labelSepparator = 'of', 10526 autoPager, 10527 groupPager, 10528 loop, 10529 forcePager 10530 } = {}) { 10531 this.el = el; 10532 this.track = this.el.querySelector("[data-track]"); 10533 if (!this.track) { 10534 console.warn( 10535 "No Slider track defined, reverting back to CSS slider.\nPlease create a wrapper for your slides with the attribute data-track", 10536 this.el 10537 ); 10538 return; 10539 } 10540 this.initialLoad = true; 10541 this.slides = []; 10542 this.inViewObserver; 10543 this.mutationObserver; 10544 this.resizeObserver; 10545 this.pager = this.el.querySelector("[data-pager]"); 10546 this.navBtns = Array.from( 10547 this.el.querySelectorAll("[data-next], [data-prev]") 10548 ); 10549 this.slideLabelSepparator = this.el.dataset.slideLabelSepparator || labelSepparator; 10550 this.useAutoPager = autoPager || this.el.hasAttribute("data-auto-pager") || false; 10551 this.useGroupPager = groupPager || this.el.hasAttribute("data-group-pager") || false; 10552 this.useLoop = loop || this.el.hasAttribute("data-loop") || false; 10553 this.forcePager = forcePager || this.el.hasAttribute("data-force-pager") || false; 10554 this.sliderLabel = this.el.hasAttribute("aria-label") && this.el.getAttribute("aria-label").toLowerCase().trim().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, ""); 10555 this.sliderId = this.el.id || this.sliderLabel || "slider"; 10556 this.markerIdName = "data-target-id"; 10557 this.pagerClasses = this.el.dataset.pagerClass || "snap-pager"; 10558 this.markerClasses = this.el.dataset.markerClass || "snap-marker"; 10559 this.init(); 10560 } 10561 init() { 10562 this.setupSlides(); 10563 this.setupNav(); 10564 if (this.useAutoPager) { 10565 this.createPager(); 10566 } else { 10567 this.setupPager(); 10568 } 10569 this.setupObservers(); 10570 this.setupMutationObserver(); 10571 this.setupResizeObserver(); 10572 this.el.addEventListener("click", this.eventHandler.bind(this)); 10573 this.el.addEventListener("keydown", this.eventHandler.bind(this)); 10574 if ("onscrollend" in window) { 10575 this.track.addEventListener("scrollend", () => { 10576 this.isNavigating = false; 10577 }); 10578 } else { 10579 this.track.addEventListener("scroll", () => { 10580 clearTimeout(this.scrollEndTimer); 10581 this.scrollEndTimer = setTimeout(() => { 10582 this.isNavigating = false; 10583 }, 100); 10584 }); 10585 } 10586 } 10587 destroy() { 10588 this.inViewObserver?.disconnect(); 10589 this.mutationObserver?.disconnect(); 10590 this.resizeObserver?.disconnect(); 10591 this.el.removeEventListener("click", this.eventHandler.bind(this)); 10592 this.el.removeEventListener("keydown", this.eventHandler.bind(this)); 10593 } 10594 roundUpIfGreaterThan(number, min = 8) { 10595 const decimalPart = number - Math.floor(number); 10596 return decimalPart >= min ? Math.ceil(number) : Math.floor(number); 10597 } 10598 getInViewItems() { 10599 const inViewSlides = Array.from(this.track.querySelectorAll("[data-in-view]")).filter((el) => this.slides.includes(el)); 10600 const firstInViewSlide = inViewSlides[0] || null; 10601 const lastInViewSlide = inViewSlides[inViewSlides.length - 1] || null; 10602 const isAtStart = firstInViewSlide === this.slides[0]; 10603 const isAtEnd = lastInViewSlide === this.slides[this.slides.length - 1]; 10604 return { 10605 inViewSlides, 10606 totalInViewSlides: inViewSlides.length, 10607 firstInViewSlide, 10608 lastInViewSlide, 10609 isAtStart, 10610 isAtEnd, 10611 hasNoOverflow: isAtStart && isAtEnd 10612 }; 10613 } 10614 groupPagerMarkers() { 10615 if (this.slides.length === 0 || !this.pager || !this.useGroupPager) return; 10616 const totalVisibleSlides = this.roundUpIfGreaterThan( 10617 this.track.offsetWidth / this.slides[0].offsetWidth 10618 ); 10619 const markers = Array.from(this.pager.querySelectorAll("a, button"));
10620 markers.forEach((marker, index) => { 10621 marker.style.display = index % totalVisibleSlides === 0 ? null : "none"; 10622 }); 10623 } 10624 handleInView(entries) { 10625 entries.forEach((entry) => { 10626 const marker = this.pager && (this.pager.querySelector(`[href="#${entry.target.id}"]`) || this.pager.querySelector( 10627 `[${this.markerIdName}="${entry.target.id}"]` 10628 )); 10629 entry.target.toggleAttribute("data-in-view", entry.isIntersecting); 10630 entry.target.toggleAttribute("inert", !entry.isIntersecting); 10631 marker?.setAttribute("aria-current", entry.isIntersecting); 10632 marker?.setAttribute("tabindex", entry.isIntersecting ? "0" : "-1"); 10633 }); 10634 const { isAtStart, isAtEnd, hasNoOverflow } = this.getInViewItems(); 10635 this.navBtns.forEach((btn) => { 10636 if (btn.hasAttribute("data-next")) { 10637 btn.style.visibility = hasNoOverflow ? "hidden" : null; 10638 isAtEnd && !this.useLoop ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled"); 10639 } 10640 if (btn.hasAttribute("data-prev")) { 10641 btn.style.visibility = hasNoOverflow ? "hidden" : null; 10642 isAtStart && !this.useLoop ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled"); 10643 } 10644 }); 10645 if (this.pager) { 10646 const hidePager = hasNoOverflow && !this.forcePager; 10647 this.pager.style.visibility = hidePager ? "hidden" : null; 10648 this.pager.toggleAttribute("inert", hidePager); 10649 } 10650 if (!this.initialLoad) { 10651 this.el.dispatchEvent( 10652 new CustomEvent("slideChange", { 10653 detail: this.getInViewItems() 10654 }) 10655 ); 10656 } else { 10657 this.initialLoad = false; 10658 } 10659 if (document.activeElement.parentElement.hasAttribute("data-pager")) { 10660 const activeItems = this.pager.querySelectorAll('[tabindex="0"]'); 10661 if (activeItems.length) { 10662 activeItems[0].focus({ preventScroll: true }); 10663 } 10664 } 10665 } 10666 getSlides() { 10667 if (!this.track) return []; 10668 return Array.from(this.track.children).filter( 10669 (child) => child.tagName.toLowerCase() !== "template" && child.tagName.toLowerCase() !== "style" && child.tagName.toLowerCase() !== "script" && child.style.display !== "none" 10670 ); 10671 } 10672 refreshSlides() { 10673 this.initialLoad = true; 10674 this.slides = this.getSlides(); 10675 this.setupSlides(); 10676 if (this.useAutoPager) { 10677 this.createPager(); 10678 } else { 10679 this.setupPager(); 10680 } 10681 this.groupPagerMarkers(); 10682 this.setupObservers(); 10683 } 10684 setupSlides() { 10685 this.slides = this.getSlides(); 10686 this.slides.forEach((slide, index) => { 10687 const totalSlides = this.slides.length; 10688 const currentSlide = index + 1; 10689 const existingLabel = slide.getAttribute("aria-label") || ""; 10690 const isImage = slide.tagName.toLowerCase() === "img" || slide.tagName.toLowerCase() === "picture"; 10691 const slideLabelTag = isImage ? "alt" : "aria-label"; 10692 const hasAutoLabel = existingLabel.startsWith( 10693 `${currentSlide} ${this.slideLabelSepparator} ` 10694 ); 10695 if (!slide.hasAttribute("id")) { 10696 slide.setAttribute( 10697 "id", 10698 `${this.sliderId}-item-${currentSlide}` 10699 ); 10700 } 10701 if (!slide.hasAttribute(slideLabelTag) || slide.getAttribute(slideLabelTag) === "" || hasAutoLabel) { 10702 slide.setAttribute( 10703 slideLabelTag, 10704 `${currentSlide} ${this.slideLabelSepparator} ${totalSlides}` 10705 ); 10706 } 10707 if (slide.tagName.toLowerCase() === "div" && !slide.hasAttribute("role")) { 10708 slide.setAttribute("role", "group"); 10709 } 10710 if (this.pager && !isImage) { 10711 slide.setAttribute("role", "tabpanel"); 10712 } 10713 if (slide.getAttribute("role") === "group") { 10714 slide.setAttribute("aria-roledescription", "item"); 10715 } 10716 }); 10717 if (!this.el.hasAttribute("role") && this.el.tagName !== "SECTION") { 10718 this.el.setAttribute("role", "region"); 10719 } 10720 if (!this.el.hasAttribute("aria-roledescription")) { 10721 this.el.setAttribute("aria-roledescription", "carousel"); 10722 } 10723 if (!this.track.hasAttribute("tabindex")) { 10724 this.track.setAttribute("tabindex", 0); 10725 } 10726 this.track.setAttribute("aria-live", "polite"); 10727 } 10728 setupNav() { 10729 this.navBtns.forEach((btn) => { 10730 btn.setAttribute("disabled", ""); 10731 btn.removeAttribute("hidden"); 10732 btn.classList.remove("invisible"); 10733 btn.style.visibility = null; 10734 }); 10735 } 10736 createPager() { 10737 const newPager = document.createElement("nav"); 10738 newPager.setAttribute("data-pager", ""); 10739 newPager.setAttribute("role", "tablist"); 10740 newPager.classList.add(...this.pagerClasses.split(" "));
10741 this.slides.forEach((slide, index) => { 10742 const marker = document.createElement("button"); 10743 const slideId = slide.id || `${this.sliderId}-item-${index + 1}`; 10744 marker.setAttribute(this.markerIdName, slideId); 10745 marker.classList.add(...this.markerClasses.split(" ")); 10746 this.setupPagerMarker(marker, index); 10747 newPager.appendChild(marker); 10748 if (slide.tagName.toLowerCase() !== "img" && slide.tagName.toLowerCase() !== "picture") { 10749 slide.removeAttribute("aria-roledescription"); 10750 slide.setAttribute("role", "tabpanel"); 10751 } 10752 }); 10753 if (this.pager) { 10754 this.pager.setAttribute("role", "tablist"); 10755 this.pager.replaceChildren(...newPager.children); 10756 } else { 10757 this.track.after(newPager); 10758 this.pager = this.el.querySelector("[data-pager]"); 10759 } 10760 } 10761 setupPager() { 10762 if (!this.pager) return; 10763 this.pager.setAttribute("role", "tablist"); 10764 const items = Array.from(this.pager.querySelectorAll("a, button")); 10765 items.forEach((marker, index) => this.setupPagerMarker(marker, index)); 10766 } 10767 setupPagerMarker(marker, index) { 10768 const markerId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName); 10769 const slideId = markerId || `${this.sliderId}-item-${index + 1}`; 10770 if (!markerId) { 10771 marker.setAttribute(this.markerIdName, slideId); 10772 } 10773 marker.setAttribute("role", "tab"); 10774 marker.setAttribute("aria-controls", slideId); 10775 marker.setAttribute("aria-posinset", index + 1); 10776 marker.setAttribute("aria-setsize", this.slides.length); 10777 marker.setAttribute("tabindex", "-1"); 10778 if (!marker.hasAttribute("aria-label")) { 10779 marker.setAttribute("aria-label", `Slide ${index + 1}`); 10780 } 10781 } 10782 setupObservers() { 10783 if (this.inViewObserver) { 10784 this.inViewObserver.disconnect(); 10785 } 10786 this.inViewObserver = new IntersectionObserver( 10787 this.handleInView.bind(this), { 10788 root: this.track, 10789 threshold: 0.8 10790 } 10791 ); 10792 this.slides.forEach((slide) => this.inViewObserver.observe(slide)); 10793 } 10794 setupMutationObserver() { 10795 this.mutationObserver = new MutationObserver((mutations) => { 10796 if (mutations.some((m) => m.type === "childList" || m.target.parentElement === this.track)) { 10797 this.refreshSlides(); 10798 } 10799 }); 10800 this.mutationObserver.observe(this.track, { 10801 childList: true, 10802 subtree: true, 10803 attributeFilter: ["style"] 10804 }); 10805 } 10806 setupResizeObserver() { 10807 this.resizeObserver = new ResizeObserver( 10808 this.groupPagerMarkers.bind(this) 10809 ); 10810 this.resizeObserver.observe(this.el); 10811 } 10812 goToSlideDir(dir = "next") { 10813 if (this.isNavigating) return; 10814 const { firstInViewSlide, lastInViewSlide } = this.getInViewItems(); 10815 const isPrev = dir === "prev"; 10816 const referenceSlide = isPrev ? firstInViewSlide : lastInViewSlide; 10817 const currentIndex = this.slides.indexOf(referenceSlide); 10818 let targetIndex = isPrev ? currentIndex - 1 : currentIndex + 1; 10819 if (targetIndex < 0 || targetIndex >= this.slides.length) { 10820 if (!this.useLoop) return; 10821 targetIndex = isPrev ? this.slides.length - 1 : 0; 10822 } 10823 const targetSlide = this.slides[targetIndex]; 10824 if (!targetSlide) return; 10825 this.isNavigating = true; 10826 targetSlide.scrollIntoView({ 10827 block: "nearest", 10828 inline: isPrev ? "end" : "start" 10829 }); 10830 } 10831 pagerToSlide(event) { 10832 if (!event.target.closest("[data-pager]")) return; 10833 const marker = event.target.closest("a, button"); 10834 if (!marker) return; 10835 event.preventDefault(); 10836 const slideId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName); 10837 const targetSlide = this.track.querySelector(`#${slideId}`);
10838 targetSlide?.scrollIntoView({ block: "nearest", inline: "start" }); 10839 } 10840 eventHandler(event) { 10841 const target = event.target.closest( 10842 "[data-next], [data-prev], [data-pager]" 10843 ); 10844 if (!target) return; 10845 if (event.type === "click") { 10846 if (target.hasAttribute("data-next")) { 10847 this.goToSlideDir("next"); 10848 } else if (target.hasAttribute("data-prev")) { 10849 this.goToSlideDir("prev"); 10850 } else if (target.closest("[data-pager]")) { 10851 this.pagerToSlide(event); 10852 } 10853 } 10854 if (event.type === "keydown" && target.closest("[data-pager]")) { 10855 if (event.key === "ArrowRight" || event.key === "ArrowDown") { 10856 event.preventDefault(); 10857 this.goToSlideDir("next"); 10858 } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") { 10859 event.preventDefault(); 10860 this.goToSlideDir("prev"); 10861 } 10862 } 10863 } 10864 }; 10865 10866 (() => { 10867 function alpineSnapSlider(Alpine) { 10868 Alpine.directive("snap-slider", (el, { 10869 modifiers 10870 }, { 10871 cleanup 10872 }) => { 10873 const slider = new SnapSlider(el, { 10874 autoPager: modifiers.includes("auto-pager"), 10875 groupPager: modifiers.includes("group-pager"), 10876 loop: modifiers.includes("loop"), 10877 forcePager: modifiers.includes("force-pager") 10878 }); 10879 cleanup(() => { 10880 slider.destroy(); 10881 }); 10882 }); 10883 } 10884 10885 document.addEventListener("alpine:init", () => { 10886 window.Alpine.plugin(alpineSnapSlider); 10887 }); 10888 })(); 10889 //# sourceURL=alpine-snap-slider.js 10890</script>
vendor: 1 bytes, line 10890
10890
10891<script> 10892 (() => { 10893 'use strict'; 10894 10895 if (typeof SnapSlider === 'undefined') { 10896 return; 10897 } 10898 10899 const RESIZE_DEBOUNCE_MS = 150; 10900 const SCROLL_FALLBACK_THROTTLE_MS = 100; 10901 10902 window.AmSnapSlider = class AmSnapSlider extends SnapSlider { 10903 init() { 10904 this.useInfinite = this.el.hasAttribute('data-am-infinity-loop'); 10905 10906 super.init(); 10907 10908 if (!this.useInfinite || !this.track) { 10909 return; 10910 } 10911 10912 this._cloneCount = 0; 10913 this._isCloning = false; 10914 this._isJumping = false; 10915 this._onInfiniteScrollEnd = this._onInfiniteScrollEnd.bind(this); 10916 10917 this._setupInfiniteLoop(); 10918 this._bindInfiniteScrollEnd(); 10919 } 10920 10921 getSlides() { 10922 const slides = super.getSlides(); 10923 10924 if (!this.useInfinite) { 10925 return slides; 10926 } 10927 10928 return slides.filter((child) => !child.hasAttribute('data-clone')); 10929 } 10930 10931 refreshSlides() { 10932 if (!this.useInfinite) { 10933 return super.refreshSlides(); 10934 } 10935 10936 if (this._isCloning) { 10937 return; 10938 } 10939 10940 this._removeClones(); 10941 super.refreshSlides(); 10942 this._setupInfiniteLoop(); 10943 } 10944 10945 setupResizeObserver() { 10946 this.resizeObserver = new ResizeObserver(() => { 10947 requestAnimationFrame(() => { 10948 if (this._isCloning || this._isJumping) { 10949 return; 10950 } 10951 10952 this.groupPagerMarkers(); 10953 10954 if (!this.useInfinite) { 10955 return; 10956 } 10957 10958 clearTimeout(this._resizeInfiniteTimer); 10959 this._resizeInfiniteTimer = setTimeout(() => { 10960 this._setupInfiniteLoop(); 10961 }, RESIZE_DEBOUNCE_MS); 10962 }); 10963 }); 10964 this.resizeObserver.observe(this.el); 10965 } 10966 10967 goToSlideDir(dir = 'next') { 10968 if (!this.useInfinite || !this._cloneCount) { 10969 return super.goToSlideDir(dir); 10970 } 10971 10972 if (this.isNavigating || this._isJumping) { 10973 return; 10974 } 10975 10976 const allSlides = this._getAllTrackSlides(); 10977 10978 if (!allSlides.length) { 10979 return; 10980 } 10981 10982 // Same page-step as SnapSlider: next from last in-view, prev from first. 10983 const inViewSlides = this._getVisibleTrackSlides(allSlides); 10984 const isPrev = dir === 'prev'; 10985 const referenceSlide = isPrev 10986 ? inViewSlides[0] 10987 : inViewSlides[inViewSlides.length - 1]; 10988 const currentIndex = allSlides.indexOf(referenceSlide); 10989 10990 if (currentIndex < 0) { 10991 return; 10992 } 10993 10994 const targetIndex = isPrev ? currentIndex - 1 : currentIndex + 1; 10995 10996 if (targetIndex < 0 || targetIndex >= allSlides.length) { 10997 return; 10998 } 10999 11000 const targetSlide = allSlides[targetIndex]; 11001 11002 if (!targetSlide) { 11003 return; 11004 } 11005 11006 this.isNavigating = true; 11007 targetSlide.scrollIntoView({ 11008 block: 'nearest', 11009 inline: isPrev ? 'end' : 'start' 11010 }); 11011 } 11012 11013 handleInView(entries) { 11014 this.focusOnActivePageMarker(); 11015 11016 if (!this.useInfinite) { 11017 const focusedNavBtn = this.navBtns.includes(document.activeElement) 11018 ? document.activeElement 11019 : null; 11020 11021 super.handleInView(entries); 11022 11023 if (focusedNavBtn?.disabled) { 11024 this.navBtns.find((btn) =>
11024 !btn.disabled)?.focus({ preventScroll: true });; 11025 } 11026 11027 return; 11028 } 11029 11030 const realEntries = []; 11031 11032 entries.forEach((entry) => { 11033 if (entry.target.hasAttribute('data-clone')) { 11034 entry.target.toggleAttribute('data-in-view', entry.isIntersecting); 11035 entry.target.setAttribute('inert', ''); 11036 entry.target.setAttribute('aria-hidden', 'true'); 11037 return; 11038 } 11039 11040 realEntries.push(entry); 11041 }); 11042 11043 if (realEntries.length) { 11044 super.handleInView(realEntries); 11045 } 11046 11047 this.navBtns.forEach((btn) => { 11048 btn.removeAttribute('disabled'); 11049 btn.style.visibility = null; 11050 }); 11051 } 11052 11053 focusOnActivePageMarker() { 11054 if (!this.useGroupPager || !this.pager || !this.slides.length) { 11055 return; 11056 } 11057 11058 const totalVisibleSlides = this.roundUpIfGreaterThan( 11059 this.track.offsetWidth / this.slides[0].offsetWidth 11060 ); 11061 const markers = Array.from(this.pager.querySelectorAll('a, button')); 11062 const { lastInViewSlide } = this.getInViewItems(); 11063 const activeIndex = lastInViewSlide ? this.slides.indexOf(lastInViewSlide) : -1; 11064 const activeGroupLeaderIndex = activeIndex >= 0 11065 ? activeIndex - (activeIndex % totalVisibleSlides) 11066 : -1; 11067 11068 let currentMarker = null; 11069 11070 markers.forEach((marker, index) => { 11071 const isGroupLeader = index % totalVisibleSlides === 0; 11072 11073 if (!isGroupLeader) { 11074 marker.setAttribute('aria-current', 'false'); 11075 marker.setAttribute('tabindex', '-1'); 11076 return; 11077 } 11078 11079 const isCurrent = index === activeGroupLeaderIndex; 11080 11081 marker.setAttribute('aria-current', String(isCurrent)); 11082 marker.setAttribute('tabindex', isCurrent ? '0' : '-1'); 11083 11084 if (isCurrent) { 11085 currentMarker = marker; 11086 } 11087 }); 11088 11089 if (currentMarker && this.pager.contains(document.activeElement) 11090 && document.activeElement !== currentMarker 11091 ) {
11092 currentMarker.focus({ preventScroll: true }); 11093 } 11094 } 11095 11096 destroy() { 11097 if (this.useInfinite) { 11098 this._unbindInfiniteScrollEnd(); 11099 clearTimeout(this._resizeInfiniteTimer); 11100 clearTimeout(this._infiniteScrollTimer); 11101 this._removeClones(); 11102 } 11103 11104 super.destroy(); 11105 } 11106 11107 _getAllTrackSlides() { 11108 return this.track ? super.getSlides() : []; 11109 } 11110 11111 _getClosestSlideIndex(slides) { 11112 const trackLeft = this.track.getBoundingClientRect().left; 11113 let closest = 0; 11114 let minDist = Infinity; 11115 11116 slides.forEach((slide, index) => { 11117 const dist = Math.abs(slide.getBoundingClientRect().left - trackLeft); 11118 if (dist < minDist) { 11119 minDist = dist; 11120 closest = index; 11121 } 11122 }); 11123 11124 return closest; 11125 } 11126 11127 _getVisibleTrackSlides(slides) { 11128 const marked = slides.filter((slide) => slide.hasAttribute('data-in-view')); 11129 if (marked.length) { 11130 return marked; 11131 } 11132 11133 const trackRect = this.track.getBoundingClientRect(); 11134 const threshold = 0.8; 11135 11136 return slides.filter((slide) => { 11137 const rect = slide.getBoundingClientRect(); 11138 const visibleWidth = Math.min(rect.right, trackRect.right) 11139 - Math.max(rect.left, trackRect.left); 11140 11141 if (visibleWidth <= 0) { 11142 return false; 11143 } 11144 11145 return (visibleWidth / Math.max(rect.width, 1)) >= threshold; 11146 }); 11147 } 11148 11149 _getSlideScrollLeft(slide) { 11150 const trackRect = this.track.getBoundingClientRect(); 11151 const slideRect = slide.getBoundingClientRect(); 11152 11153 return this.track.scrollLeft + (slideRect.left - trackRect.left); 11154 } 11155 11156 _createClone(slide, position, index) { 11157 const clone = slide.cloneNode(true); 11158 const baseId = slide.id || `${this.sliderId}-item-${index + 1}`; 11159 11160 clone.setAttribute('data-clone', position); 11161 clone.setAttribute('data-clone-of', baseId); 11162 clone.setAttribute('aria-hidden', 'true'); 11163 clone.setAttribute('inert', ''); 11164 clone.setAttribute('x-ignore', ''); 11165 clone.id = `${baseId}-clone-${position}-${index}`; 11166 clone.removeAttribute('data-in-view'); 11167 11168 clone.querySelectorAll('[id]').forEach((el) => { 11169 if (el !== clone) { 11170 el.removeAttribute('id'); 11171 } 11172 }); 11173 clone.querySelectorAll('[name]').forEach((el) => { 11174 el.removeAttribute('name'); 11175 }); 11176 11177 return clone; 11178 } 11179 11180 _removeClones() { 11181 if (!this.track) { 11182 return; 11183 } 11184 11185 this.track.querySelectorAll('[data-clone]').forEach((clone) => { 11186 clone.remove(); 11187 }); 11188 this._cloneCount = 0; 11189 } 11190 11191 _silentScrollTo(slide) { 11192 if (!slide || !this.track) { 11193 return; 11194 } 11195 11196 this._isJumping = true; 11197 11198 const track = this.track; 11199 const prevBehavior = track.style.scrollBehavior; 11200 const prevSnap = track.style.scrollSnapType; 11201 11202 track.style.scrollBehavior = 'auto'; 11203 track.style.scrollSnapType = 'none'; 11204 track.scrollLeft = this._getSlideScrollLeft(slide); 11205 11206 requestAnimationFrame(() => { 11207 track.style.scrollBehavior = prevBehavior; 11208 track.style.scrollSnapType = prevSnap; 11209 this._isJumping = false;
11210 this.isNavigating = false; 11211 }); 11212 } 11213 11214 _canUseInfinite(realSlides) { 11215 if (!realSlides.length || realSlides.length < 2) { 11216 return false; 11217 } 11218 11219 return this.track.scrollWidth > this.track.clientWidth + 1; 11220 } 11221 11222 _setupInfiniteLoop() { 11223 if (!this.useInfinite || !this.track || this._isCloning) { 11224 return; 11225 } 11226 11227 this._isCloning = true; 11228 this.mutationObserver?.disconnect(); 11229 11230 let restoreIndex = 0; 11231 const { firstInViewSlide } = this.getInViewItems(); 11232 const currentIndex = this.slides.indexOf(firstInViewSlide); 11233 11234 if (currentIndex >= 0) { 11235 restoreIndex = currentIndex; 11236 } 11237 11238 this._removeClones(); 11239 11240 const realSlides = this.getSlides(); 11241 11242 this.slides = realSlides; 11243 11244 if (!this._canUseInfinite(realSlides)) { 11245 this._cloneCount = 0; 11246 this._isCloning = false; 11247 this._reobserveMutations(); 11248 this.setupObservers(); 11249 11250 return; 11251 } 11252 11253 const slideWidth = realSlides[0].offsetWidth || 1; 11254 const visibleCount = Math.max( 11255 1, 11256 this.roundUpIfGreaterThan(this.track.offsetWidth / slideWidth) 11257 ); 11258 11259 this._cloneCount = Math.min(visibleCount, realSlides.length); 11260 11261 const prependFragment = document.createDocumentFragment(); 11262 11263 for (let i = realSlides.length - this._cloneCount; i < realSlides.length; i++) { 11264 prependFragment.appendChild( 11265 this._createClone(realSlides[i], 'pre', i) 11266 ); 11267 } 11268 this.track.insertBefore(prependFragment, this.track.firstChild); 11269 11270 const appendFragment = document.createDocumentFragment(); 11271 for (let i = 0; i < this._cloneCount; i++) { 11272 appendFragment.appendChild( 11273 this._createClone(realSlides[i], 'post', i) 11274 ); 11275 } 11276 this.track.appendChild(appendFragment); 11277 11278 this.slides = this.getSlides(); 11279 this.setupObservers(); 11280 11281 this._getAllTrackSlides().forEach((slide) => { 11282 if (slide.hasAttribute('data-clone')) { 11283 this.inViewObserver.observe(slide); 11284 } 11285 }); 11286 11287 const target = this.slides[restoreIndex] || this.slides[0]; 11288 requestAnimationFrame(() => { 11289 this._silentScrollTo(target); 11290 this._isCloning = false; 11291 this._reobserveMutations(); 11292 }); 11293 } 11294 11295 _reobserveMutations() { 11296 if (!this.mutationObserver || !this.track) { 11297 return; 11298 } 11299 11300 // Same as base SnapSlider. Watching `style` re-enters refreshSlides 11301 // when _silentScrollTo toggles scrollBehavior / scrollSnapType. 11302 this.mutationObserver.observe(this.track, { 11303 childList: true, 11304 subtree: false 11305 }); 11306 } 11307 11308 _bindInfiniteScrollEnd() { 11309 if (!this.track) { 11310 return; 11311 } 11312 11313 if ('onscrollend' in window) { 11314 this.track.addEventListener('scrollend', this._onInfiniteScrollEnd); 11315 return; 11316 } 11317 11318 this._onInfiniteScrollFallback = () => { 11319 clearTimeout(this._infiniteScrollTimer); 11320 this._infiniteScrollTimer = setTimeout(() => { 11321 this._onInfiniteScrollEnd(); 11322 }, SCROLL_FALLBACK_THROTTLE_MS); 11323 }; 11324 this.track.addEventListener('scroll', this._onInfiniteScrollFallback); 11325 } 11326 11327 _unbindInfiniteScrollEnd() { 11328 if (!this.track) { 11329 return; 11330 } 11331 11332 this.track.removeEventListener('scrollend', this._onInfiniteScrollEnd); 11333 if (this._onInfiniteScrollFallback) { 11334 this.track.removeEventListener('scroll', this._onInfiniteScrollFallback); 11335 } 11336 } 11337 11338 _onInfiniteScrollEnd() { 11339 if (this._isJumping || this._isCloning || this._isSimulateDragging) { 11340 return; 11341 } 11342 11343 if (!this.useInfinite || !this._cloneCount) { 11344 this.isNavigating = false;
11345 11346 return; 11347 } 11348 11349 const allSlides = this._getAllTrackSlides(); 11350 const realCount = this.slides.length; 11351 11352 if (!realCount || allSlides.length < realCount + this._cloneCount) { 11353 this.isNavigating = false; 11354 11355 return; 11356 } 11357 11358 const currentIndex = this._getClosestSlideIndex(allSlides); 11359 11360 if (currentIndex < this._cloneCount) { 11361 this._silentScrollTo(allSlides[currentIndex + realCount]); 11362 11363 return; 11364 } 11365 11366 if (currentIndex >= this._cloneCount + realCount) { 11367 this._silentScrollTo(allSlides[currentIndex - realCount]); 11368 11369 return; 11370 } 11371 11372 this.isNavigating = false; 11373 } 11374 }; 11375 11376 function alpineAmSnapSlider(Alpine) { 11377 Alpine.directive('am-snap-slider', (el, { modifiers }, { cleanup }) => { 11378 const slider = new window.AmSnapSlider(el, { 11379 autoPager: modifiers.includes('auto-pager'), 11380 groupPager: modifiers.includes('group-pager') 11381 }); 11382 cleanup(() => { 11383 slider.destroy(); 11384 }); 11385 }); 11386 } 11387 11388 document.addEventListener('alpine:init', () => { 11389 window.Alpine.plugin(alpineAmSnapSlider); 11390 }); 11391 })(); 11392</script>
vendor: 1 bytes, line 11392
11392
11393<script> 11394 (() => { 11395 'use strict'; 11396 11397 if (typeof window.AmSnapSlider === 'undefined') { 11398 return; 11399 } 11400 11401 // Ignore tiny jitter so a plain click on an item still opens the link. 11402 const DRAG_THRESHOLD_PX = 8; 11403 11404 // Listeners that only exist while an actual drag is in progress. 11405 const DOC_DRAG_EVENTS = ['pointermove', 'pointerup', 'pointercancel']; 11406 11407 const AmSnapSliderBase = window.AmSnapSlider; 11408 11409 window.AmSnapSlider = class AmSnapSlider extends AmSnapSliderBase { 11410 init() { 11411 this.useSimulateTouch = this.el.hasAttribute('data-am-simulate-touch'); 11412 11413 super.init(); 11414 11415 if (!this.useSimulateTouch || !this.track) { 11416 return; 11417 } 11418 11419 this._dragTracking = false; 11420 this._dragMoved = false; 11421 this._isSimulateDragging = false; 11422 this._dragStartX = 0; 11423 this._dragScrollLeft = 0; 11424 this._dragPointerId = null; 11425 this._suppressClick = false; 11426 11427 this._onSimulatePointerDown = this._onSimulatePointerDown.bind(this); 11428 this._onSimulatePointerMove = this._onSimulatePointerMove.bind(this); 11429 this._onSimulatePointerUp = this._onSimulatePointerUp.bind(this); 11430 this._onSimulateClickCapture = this._onSimulateClickCapture.bind(this); 11431 this._onSimulateDragStart = this._onSimulateDragStart.bind(this); 11432 11433 this._bindSimulateTouch(); 11434 } 11435 11436 destroy() { 11437 if (this.useSimulateTouch) { 11438 this._unbindSimulateTouch(); 11439 } 11440 11441 super.destroy(); 11442 } 11443 11444 _bindSimulateTouch() { 11445 if (!this.track) { 11446 return; 11447 } 11448 11449 this.track.classList.add('am-snap-simulate-touch'); 11450 this.track.addEventListener('pointerdown', this._onSimulatePointerDown); 11451 this.track.addEventListener('dragstart', this._onSimulateDragStart, true); 11452 this.track.addEventListener('click', this._onSimulateClickCapture, true); 11453 } 11454 11455 _unbindSimulateTouch() { 11456 if (!this.track) { 11457 return; 11458 } 11459 11460 this._endSimulateDrag(); 11461 this.track.classList.remove('am-snap-simulate-touch', 'is-dragging'); 11462 this.track.removeEventListener('pointerdown', this._onSimulatePointerDown); 11463 this.track.removeEventListener('dragstart', this._onSimulateDragStart, true); 11464 this.track.removeEventListener('click', this._onSimulateClickCapture, true); 11465 } 11466 11467 _onSimulateDragStart(event) { 11468 event.preventDefault(); 11469 } 11470 11471 _onSimulatePointerDown(event) { 11472 if (event.pointerType !== 'mouse' || event.button !== 0) { 11473 return; 11474 } 11475 11476 if (this._isJumping || this._isCloning || !this.track) { 11477 return; 11478 } 11479 11480 this._dragTracking = true; 11481 this._dragMoved = false; 11482 this._suppressClick = false; 11483 this._dragStartX = event.clientX; 11484 this._dragScrollLeft = this.track.scrollLeft; 11485 this._dragPointerId = event.pointerId; 11486 11487 DOC_DRAG_EVENTS.forEach((name) => { 11488 const handler = name === 'pointermove' 11489 ? this._onSimulatePointerMove 11490 : this._onSimulatePointerUp; 11491 document.addEventListener(name, handler); 11492 }); 11493 } 11494 11495 _onSimulatePointerMove(event) { 11496 if (!this._dragTracking || event.pointerId !== this._dragPointerId || !this.track) { 11497 return; 11498 } 11499 11500 const deltaX = event.clientX - this._dragStartX; 11501 11502 if (!this._dragMoved) { 11503 if (Math.abs(deltaX) < DRAG_THRESHOLD_PX) { 11504 return; 11505 } 11506 11507 this._dragMoved = true; 11508 this._isSimulateDragging = true; 11509 this._suppressClick = true; 11510 this.track.style.scrollSnapType = 'none'; 11511 this.track.style.scrollBehavior = 'auto'; 11512 this.track.classList.add('is-dragging'); 11513 11514 try { 11515 this.track.setPointerCapture(event.pointerId); 11516 } catch (e) {
11517 // Ignore capture errors on unsupported targets. 11518 } 11519 } 11520 11521 event.preventDefault(); 11522 this.track.scrollLeft = this._dragScrollLeft - deltaX; 11523 } 11524 11525 _onSimulatePointerUp(event) { 11526 if (!this._dragTracking || event.pointerId !== this._dragPointerId) { 11527 return; 11528 } 11529 11530 this._endSimulateDrag(); 11531 } 11532 11533 /** 11534 * Instantly align to the closest slide without CSS smooth scrolling. 11535 * Restoring snap+smooth from a mid-snap offset is what causes desktop jerk. 11536 */ 11537 _snapToNearestAfterDrag() { 11538 if (!this.track) { 11539 return; 11540 } 11541 11542 const slides = typeof this._getAllTrackSlides === 'function' 11543 ? this._getAllTrackSlides() 11544 : Array.from(this.track.children); 11545 11546 if (!slides.length) { 11547 return; 11548 } 11549 11550 if (typeof this._getClosestSlideIndex === 'function' 11551 && typeof this._getSlideScrollLeft === 'function' 11552 ) { 11553 const slide = slides[this._getClosestSlideIndex(slides)]; 11554 11555 if (slide) { 11556 this.track.scrollLeft = this._getSlideScrollLeft(slide); 11557 } 11558 11559 return; 11560 } 11561 11562 const trackLeft = this.track.getBoundingClientRect().left; 11563 let closest = null; 11564 let minDist = Infinity; 11565 11566 for (const slide of slides) { 11567 const dist = Math.abs(slide.getBoundingClientRect().left - trackLeft); 11568 if (dist < minDist) { 11569 minDist = dist; 11570 closest = slide; 11571 } 11572 } 11573 11574 if (closest) { 11575 this.track.scrollLeft += closest.getBoundingClientRect().left - trackLeft; 11576 } 11577 } 11578 11579 _endSimulateDrag() { 11580 DOC_DRAG_EVENTS.forEach((name) => { 11581 const handler = name === 'pointermove' 11582 ? this._onSimulatePointerMove 11583 : this._onSimulatePointerUp; 11584 document.removeEventListener(name, handler); 11585 }); 11586 11587 if (!this.track) { 11588 this._dragTracking = false; 11589 this._dragMoved = false; 11590 this._isSimulateDragging = false; 11591 this._dragPointerId = null; 11592 11593 return; 11594 } 11595 11596 if (this._dragPointerId !== null) { 11597 try { 11598 if (this.track.hasPointerCapture?.(this._dragPointerId)) { 11599 this.track.releasePointerCapture(this._dragPointerId); 11600 } 11601 } catch (e) { 11602 // Ignore release errors. 11603 } 11604 } 11605 11606 if (this._dragMoved) { 11607 // Keep snap off and behavior auto: settle instantly, then re-enable. 11608 this.track.style.scrollSnapType = 'none'; 11609 this.track.style.scrollBehavior = 'auto'; 11610 this._snapToNearestAfterDrag(); 11611 11612 // Allow infinite correction now that the gesture ended. 11613 this._isSimulateDragging = false; 11614 this._onInfiniteScrollEnd?.(); 11615 11616 // Two frames: 1) drop snap-none + dragging class, 2) restore scroll-behavior. 11617 requestAnimationFrame(() => { 11618 if (!this.track) { 11619 return; 11620 } 11621 this.track.style.scrollSnapType = ''; 11622 this.track.classList.remove('is-dragging'); 11623 11624 requestAnimationFrame(() => { 11625 if (this.track) { 11626 this.track.style.scrollBehavior = ''; 11627 } 11628 }); 11629 }); 11630 } else { 11631 this._isSimulateDragging = false;
11632 } 11633 11634 this._dragTracking = false; 11635 this._dragMoved = false; 11636 this._dragPointerId = null; 11637 11638 if (this._suppressClick) { 11639 requestAnimationFrame(() => { 11640 this._suppressClick = false; 11641 }); 11642 } 11643 } 11644 11645 _onSimulateClickCapture(event) { 11646 if (!this._suppressClick) { 11647 return; 11648 } 11649 11650 event.preventDefault(); 11651 event.stopPropagation(); 11652 this._suppressClick = false; 11653 } 11654 }; 11655 })(); 11656</script>
vendor: 1 bytes, line 11656
11656
11657<script> 11658 (() => { 11659 // src/dialog.js 11660 function dialog_default(Alpine) { 11661 Alpine.directive("htmldialog", fylgjaAlpineDialog); 11662 function fylgjaAlpineDialog(el, { expression, modifiers }, { evaluateLater, cleanup }) { 11663 const evaluate = expression.length ? evaluateLater(expression) : () => {}; 11664 const lockPageScroll = modifiers.includes("noscroll"); 11665 const isModeless = modifiers.includes("modeless"); 11666 const closeBy = el.getAttribute("closeby") || modifierValue(modifiers, "closeby", "closerequest"); 11667 el.style.display = null; 11668 el.style.length === 0 && el.removeAttribute("style"); 11669 if (el._x_isShown) { 11670 open(); 11671 } 11672 const closeBySupported = (() => { 11673 return "closedBy" in HTMLDialogElement.prototype; 11674 })(); 11675 function scrollLock(use = true) { 11676 document.body.style.overflow = use ? "hidden" : ""; 11677 } 11678 function dialogSubmit(event) { 11679 if (event.target.getAttribute("method") === "dialog" || event.submitter?.getAttribute("formmethod") === "dialog") { 11680 event.preventDefault(); 11681 evaluate(); 11682 } 11683 } 11684 function escapeDialog(event) { 11685 if (event.key !== "Escape") return; 11686 event.preventDefault(); 11687 if (closeBy === "none") return; 11688 evaluate(); 11689 } 11690 function handleCloseByEvent(event) { 11691 if (event.target !== el) return; 11692 const rect = el.getBoundingClientRect(); 11693 const isInDialog = rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width; 11694 if (!isInDialog) { 11695 if (closeBy === "any") { 11696 if (closeBySupported) { 11697 event.preventDefault(); 11698 } 11699 evaluate(); 11700 } 11701 } 11702 } 11703 function handleCloseEvent() { 11704 evaluate(); 11705 } 11706 function open() { 11707 if (el.hasAttribute("open")) return; 11708 isModeless ? el.show() : el.showModal(); 11709 scrollLock(lockPageScroll); 11710 } 11711 function close() { 11712 if (!el.hasAttribute("open")) return; 11713 el.close(); 11714 scrollLock(false); 11715 } 11716 el._x_doShow = () => open(); 11717 el._x_doHide = () => close(); 11718 el.addEventListener("keydown", escapeDialog); 11719 el.addEventListener("submit", dialogSubmit); 11720 el.addEventListener("click", handleCloseByEvent); 11721 el.addEventListener("cancel", handleCloseEvent); 11722 cleanup(() => { 11723 el.removeEventListener("keydown", escapeDialog); 11724 el.removeEventListener("submit", dialogSubmit); 11725 el.removeEventListener("click", handleCloseByEvent); 11726 el.removeEventListener("cancel", handleCloseEvent); 11727 scrollLock(false); 11728 }); 11729 } 11730 } 11731 function modifierValue(modifiers, key, fallback) { 11732 if (modifiers.indexOf(key) === -1) return fallback; 11733 const rawValue = modifiers[modifiers.indexOf(key) + 1]; 11734 if (!rawValue) return fallback; 11735 if (key === "closeby") { 11736 const allowedValues = ["auto", "none", "closerequest", "any"]; 11737 const options = allowedValues.join(", "); 11738 if (!allowedValues.includes(rawValue)) { 11739 console.warn( 11740 `"${rawValue}" is not one of the allowed values for closeby: ${options}` 11741 ); 11742 return fallback; 11743 } 11744 } 11745 return rawValue; 11746 } 11747 11748 // src/cdn.js 11749 document.addEventListener("alpine:init", () => { 11750 window.Alpine.plugin(dialog_default); 11751 }); 11752 })(); 11753 //# sourceURL=alpine-htmldialog.js 11754</script>
vendor: 1 bytes, line 11754
11754
vendor: 2,386 bytes, lines 11755-11815
11755<script> 11756 (() => { 11757 function src_default(Alpine) { 11758 Alpine.directive("intersect", Alpine.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => { 11759 let evaluate = evaluateLater(expression); 11760 let options = { 11761 rootMargin: getRootMargin(modifiers), 11762 threshold: getThreshold(modifiers) 11763 }; 11764 let observer = new IntersectionObserver((entries) => { 11765 entries.forEach((entry) => { 11766 if (entry.isIntersecting === (value === "leave")) 11767 return; 11768 evaluate(); 11769 modifiers.includes("once") && observer.disconnect(); 11770 }); 11771 }, options); 11772 observer.observe(el); 11773 cleanup(() => { 11774 observer.disconnect(); 11775 }); 11776 })); 11777 } 11778 function getThreshold(modifiers) { 11779 if (modifiers.includes("full")) 11780 return 0.99; 11781 if (modifiers.includes("half")) 11782 return 0.5; 11783 if (!modifiers.includes("threshold")) 11784 return 0; 11785 let threshold = modifiers[modifiers.indexOf("threshold") + 1]; 11786 if (threshold === "100") 11787 return 1; 11788 if (threshold === "0") 11789 return 0; 11790 return Number(`.${threshold}`); 11791 } 11792 function getLengthValue(rawValue) { 11793 let match = rawValue.match(/^(-?[0-9]+)(px|%)?$/); 11794 return match ? match[1] + (match[2] || "px") : void 0; 11795 } 11796 function getRootMargin(modifiers) { 11797 const key = "margin"; 11798 const fallback = "0px 0px 0px 0px"; 11799 const index = modifiers.indexOf(key); 11800 if (index === -1) 11801 return fallback; 11802 let values = []; 11803 for (let i = 1; i < 5; i++) { 11804 values.push(getLengthValue(modifiers[index + i] || "")); 11805 } 11806 values = values.filter((v) => v !== void 0); 11807 return values.length ? values.join(" ").trim() : fallback; 11808 } 11809 11810 document.addEventListener("alpine:init", () => { 11811 window.Alpine.plugin(src_default); 11812 }); 11813 })(); 11814 //# sourceURL=alpine-intersect.js 11815</script>
11815 11816
11817<script> 11818 for (const [selector, deferUntil] of Object.entries({".product-slider section[x-data]:not([x-data] [x-data])":"intersect",".product-info [x-data]:not([x-data] [x-data])":"intersect","#filters-content [x-data]:not([x-data] [x-data])":"intersect","#review_form":"intersect","section[x-data^=initRecentlyViewedProductsComponent]":"intersect","div[x-data^=initBundleOptions]":"intersect","#product_addtocart_form [x-data]:not([x-data] [x-data])":"intersect","#notice-cookie-block":"idle"})) { 11819 document.querySelectorAll(selector).forEach(el => el.setAttribute('x-defer', `${deferUntil}`)); 11820 } 11821 //# sourceURL=alpine-defer-rules.js 11822</script>
vendor: 1 bytes, line 11822
11822
11823<script> 11824 (() => { 11825 const hasAlpine = new Promise(resolve => { 11826 window.addEventListener('alpine:initialized', resolve, {once: true, passive: true}); 11827 }); 11828 11829 const hasInteract = new Promise(resolve => { 11830 (events => { 11831 const onInteract = () => { 11832 resolve(); 11833 events.forEach(type => window.removeEventListener(type, onInteract)); 11834 } 11835 events.forEach(type => window.addEventListener(type, onInteract, {once: true, passive: true})) 11836 })(['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown']) 11837 }); 11838 11839 const onIntersect = (el) => { 11840 return new Promise(resolve => { 11841 const observer = new IntersectionObserver(entries => { 11842 for (const entry of entries) { 11843 if (entry.isIntersecting) { 11844 observer.disconnect() 11845 resolve(); 11846 } 11847 } 11848 }, {}); 11849 observer.observe(el); 11850 }); 11851 } 11852 11853 function runComponent(el) { 11854 hasAlpine.then(() => { 11855 el.removeAttribute('x-ignore'); 11856 queueMicrotask(() => Alpine.initTree(el)); 11857 }); 11858 } 11859 11860 function initDeferredComponents() { 11861 document.querySelectorAll('[x-data][x-defer]').forEach(el => { 11862 el.setAttribute('x-ignore', ''); 11863 const deferUntil = (el.getAttribute('x-defer') || '').trim(); 11864 switch (deferUntil) { 11865 case 'interact': 11866 hasInteract.then(() => runComponent(el)); 11867 break; 11868 case 'intersect': 11869 onIntersect(el).then(() => runComponent(el)) 11870 break; 11871 case 'idle': 11872 window.requestIdleCallback 11873 ? window.requestIdleCallback(() => runComponent(el), {timeout: 4000}) 11874 : setTimeout(() => runComponent(el), 4000); 11875 break; 11876 case 'eager': 11877 runComponent(el); 11878 break; 11879 default: 11880 if (deferUntil.startsWith('event:') && deferUntil.length > 6) { 11881 window.addEventListener(deferUntil.substring(6), () => runComponent(el), {once: true, passive: true}); 11882 } 11883 } 11884 }); 11885 } 11886 11887 window.addEventListener('alpine:init', initDeferredComponents, {once: true, passive: true}); 11888 })(); 11889 //# sourceURL=alpine-defer.js 11890</script>
11890 11891
11892<script> 11893// x-teleport implementation 11894(_ => { 11895 document.addEventListener('DOMContentLoaded', event => { 11896 // x-teleport available only for template 11897 if (!Alpine.version.startsWith('2')) { 11898 return; 11899 } 11900 11901 const teleportDirective = (el, expression) => { 11902 let target = document.querySelector(expression) 11903 let clone = el.content.cloneNode(true).firstElementChild; 11904 11905 target.appendChild(clone); 11906 setTimeout(() => { 11907 target.querySelectorAll('[x-data]') 11908 .forEach(el => Alpine.initializeComponent(el)); 11909 }, 0) 11910 }; 11911 11912 document.querySelectorAll('template').forEach(el => { 11913 if (!el.hasAttribute('x-teleport')) { 11914 return; 11915 } 11916 11917 teleportDirective(el, el.getAttribute('x-teleport')); 11918 }); 11919 11920 document.querySelectorAll('[x-data]').forEach(el => { 11921 if (el.hasAttribute('x-init')) { 11922 return; 11923 } 11924 11925 setTimeout(() => { 11926 Alpine.initializeComponent(el); 11927 }, 0); 11928 }); 11929 }); 11930})() 11931</script>
11931 11932
11933<script type="module" 11934 src="https://www.huskee.co/static/version1789639671/frontend/Huskee/default/en_AU/Hyva_Theme/js/alpine3.min.js" 11935 defer 11936 crossorigin 11937></script>
vendor: 1 bytes, line 11937
11937
11938<script> 11939 function dispatchMessages(messages, hideAfter) { 11940 const messagesEvent = new CustomEvent("messages-loaded", { 11941 detail: { 11942 messages: messages, 11943 hideAfter: hideAfter 11944 } 11945 }); 11946 window.dispatchEvent(messagesEvent); 11947 } 11948 11949 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 11950 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 11951 } 11952 11953 (function( hyva, undefined ) { 11954 11955 hyva.initFormKey = () => { 11956 if (typeof hyva.getFormKey !== 'function') return; 11957 11958 const formKey = hyva.getFormKey(); 11959 if (!formKey) return; 11960 11961 document.querySelectorAll('input[name="form_key"]').forEach((input) => { 11962 input.value = formKey; 11963 }); 11964 } 11965 11966 hyva.initMessages = () => { 11967 try { 11968 const messages = hyva.getCookie('mage-messages'); 11969 window.mageMessages = messages ? JSON.parse(decodeURIComponent(messages).replace(/\+/g, ' ')) : []; 11970 11971 dispatchMessages(window.mageMessages); 11972 11973 // empty `mage-messages` cookie 11974 const skipSetDomain = true; 11975 hyva.setCookie('mage-messages','', -1, skipSetDomain); 11976 11977 } catch (error) { 11978 console.warn('Error parsing Cookie Messages:', error); 11979 } 11980 } 11981 11982 window.addEventListener('DOMContentLoaded', hyva.initFormKey); 11983 11984 window.addEventListener('pageshow', (event) => { 11985 if (event.persisted) { 11986 hyva.initFormKey(); 11987 } 11988 }); 11989 11990 hyva.alpineInitialized(hyva.initMessages) 11991 }( window.hyva = window.hyva || {} )); 11992 //# sourceURL=hyva-cookies.js 11993</script>
vendor: 1 bytes, line 11993
11993
11994<script> 11995 (() => { 11996 const private_content_key = 'mage-cache-storage'; 11997 const private_content_expire_key = 'mage-cache-timeout'; 11998 const private_content_version_key = 'private_content_version'; 11999 const section_data_ids_key = 'section_data_ids'; 12000 const mage_cache_session_id_key = 'mage-cache-sessid'; 12001 const last_visited_store_key = 'last_visited_store'; 12002 12003 const ttl = 3600; 12004 12005 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 12006 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 12007 } 12008 12009 function loadSectionData () { 12010 const browserStorage = hyva.getBrowserStorage(); 12011 if (!browserStorage) { 12012 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 12013 [{ 12014 type: "warning", 12015 text: "Please enable LocalStorage in your browser." 12016 }] 12017 ); 12018 return; 12019 } 12020 try { 12021 let isInvalid = false; 12022 12023 if (hyva.getCookie(last_visited_store_key) !== CURRENT_STORE_CODE) { 12024 isInvalid = true; 12025 } 12026 hyva.setCookie(last_visited_store_key, CURRENT_STORE_CODE, false, false); 12027 12028 if (!hyva.getCookie(mage_cache_session_id_key)) { 12029 isInvalid = true; 12030 browserStorage.removeItem(private_content_key); 12031 const skipSetDomain = true; 12032 const days = false; 12033 hyva.setCookie(mage_cache_session_id_key, true, days, skipSetDomain) 12034 } 12035 12036 const cookieVersion = hyva.getCookie(private_content_version_key); 12037 const storageVersion = browserStorage.getItem(private_content_version_key); 12038 12039 if (cookieVersion && !storageVersion || cookieVersion !== storageVersion) { 12040 isInvalid = true; 12041 } 12042 12043 const privateContentExpires = browserStorage.getItem(private_content_expire_key); 12044 if (privateContentExpires && new Date(privateContentExpires) < new Date()) { 12045 browserStorage.removeItem(private_content_key); 12046 } 12047 12048 if (isInvalid && cookieVersion) { 12049 fetchPrivateContent([]); 12050 } else if (cookieVersion && storageVersion && cookieVersion === storageVersion) { 12051 const privateContent = JSON.parse(browserStorage.getItem(private_content_key)); 12052 if ( 12053 privateContent && 12054 privateContentExpires && 12055 privateContent.cart && 12056 privateContent.customer 12057 ) { 12058 dispatchPrivateContent(privateContent); 12059 } else { 12060 fetchPrivateContent([]); 12061 } 12062 } else { 12063 if (document.getElementById('default-section-data')) { 12064 const privateContent = JSON.parse(document.getElementById('default-section-data').textContent.trim()); 12065 dispatchPrivateContent(privateContent); 12066 } else { 12067 dispatchPrivateContent({}); 12068 } 12069 } 12070 12071 } catch (error) { 12072 console.warn('Error retrieving Private Content:', error); 12073 } 12074 } 12075 12076 hyva.alpineInitialized(loadSectionData) 12077 window.addEventListener('reload-customer-section-data', loadSectionData); 12078 12079 window.processSectionDataBeforeDispatch = window.processSectionDataBeforeDispatch || (data => data); 12080 12081 function dispatchPrivateContent(data) { 12082 12083 const privateContentEvent = new CustomEvent("private-content-loaded", { 12084 detail: { 12085 data: window.processSectionDataBeforeDispatch(data) 12086 } 12087 }); 12088 window.dispatchEvent(privateContentEvent); 12089 } 12090 12091 function fetchPrivateContent(sections) { 12092 fetch(`${BASE_URL}customer/section/load/?sections=${encodeURIComponent(sections.join(','))}`, { 12093 method: 'GET', 12094 headers: { 12095 'Content-Type': 'application/json', 12096 'X-Requested-With': 'XMLHttpRequest' 12097 } 12098 }) 12099 .then(response => response.ok && response.json()) 12100 .then( 12101 data => { 12102 if (data) { 12103 try { 12104 const browserStorage = hyva.getBrowserStorage(); 12105 12106 // merge new data preserving non-invalidated sections 12107 const oldSectionData = JSON.parse(browserStorage.getItem(private_content_key) || '{}') || {}; 12108 12109 if ((! data.cart || ! data.cart.cartId) && oldSectionData['checkout-data']) { 12110 delete oldSectionData['checkout-data']; 12111 } 12112 const newSectionData = Object.assign(oldSectionData, data); 12113 12114 dispatchPrivateContent(newSectionData); 12115 12116 // don't persist messages, they've been dispatched already 12117 if (newSectionData.messages && newSectionData.messages.messages ) { 12118 newSectionData.messages.messages = []; 12119 } 12120 12121 browserStorage.setItem(private_content_key, JSON.stringify(newSectionData)); 12122 12123 const expiresAt = new Date(Date.now() + (ttl * 1000)).toISOString(); 12124 browserStorage.setItem(private_content_expire_key, expiresAt); 12125 12126 const newCookieVersion = hyva.getCookie(private_content_version_key); 12127 browserStorage.setItem(private_content_version_key, newCookieVersion); 12128 12129 // We don't need the section_data_ids in Hyvä, but we store them for compatibility 12130 // with Luma Fallback. Otherwise, not all sections are loaded in Luma Checkout 12131 hyva.setCookie( 12132 section_data_ids_key, 12133 JSON.stringify( 12134 Object.keys(data).reduce((sectionDataIds, sectionKey) => { 12135 sectionDataIds[sectionKey] = data[sectionKey]['data_id']; 12136 return sectionDataIds; 12137 }, {}) 12138 ), 12139 false, 12140 true 12141 ); 12142 } catch (error) { 12143 console.warn("Couldn't store privateContent", error); 12144 } 12145 } 12146 } 12147 ) 12148 .catch(error => { 12149 console.warn("Couldn't fetch privateContent", error); 12150 }); 12151 } 12152 })(); 12153 //# sourceURL=hyva-private-content.js 12154</script>
vendor: 1 bytes, line 12154
12154
12155<script> 12156 (() => { 12157 document.addEventListener('submit', event => event.target.action = event.target.action.replace('%25uenc%25', hyva.getUenc())); 12158 })() 12159</script>
vendor: 1 bytes, line 12159
12159
12160<script> 12161 (events => { 12162 const dispatchUserInteractionEvent = () => { 12163 events.forEach(type => window.removeEventListener(type, dispatchUserInteractionEvent)) 12164 window.dispatchEvent(new Event('init-external-scripts')) 12165 }; 12166 events.forEach(type => window.addEventListener(type, dispatchUserInteractionEvent, {once: true, passive: true})) 12167 })(['touchstart', 'mousemove', 'wheel', 'scroll', 'keydown']); 12168 //# sourceURL=hyva-init-external-scripts.js 12169</script>
12169 12170 12171
12172<script> 12173 function initProductItemImage() { 12174 return { 12175 updateImage() { 12176 const image = this.$event.detail.medium; 12177 if (!image) return; 12178 this.$el.src = image; 12179 } 12180 } 12181 } 12182 window.addEventListener('alpine:init', () => { 12183 Alpine.data('initProductItemImage', initProductItemImage); 12184 }, { once: true }); 12185</script>
vendor: 1 bytes, line 12185
12185
12186<script> 12187 'use strict'; 12188 12189 (() => { 12190 const initCarousels = (elements) => { 12191 if (!Glider) { 12192 return; 12193 } 12194 12195 const initProductCarousel = (carousel) => { 12196 const gliderElement = carousel.querySelector('[data-role="glider-content"]'); 12197 12198 if (!gliderElement) { 12199 return; 12200 } 12201 12202 const gliderDots = carousel.querySelector('.glider-dots'); 12203 const gliderPrev = carousel.querySelector('.glider-prev'); 12204 const gliderNext = carousel.querySelector('.glider-next'); 12205 12206 const glider = new Glider(gliderElement, { 12207 slidesToShow: 1, 12208 slidesToScroll: 1, 12209 scrollLock: true, 12210 draggable: true, 12211 dragVelocity: 2.5, 12212 dots: gliderDots, 12213 arrows: { 12214 prev: gliderPrev, 12215 next: gliderNext, 12216 }, 12217 responsive: [ 12218 { 12219 breakpoint: 640, 12220 settings: { 12221 slidesToShow: 2, 12222 slidesToScroll: 2, 12223 } 12224 }, 12225 { 12226 breakpoint: 1024, 12227 settings: { 12228 slidesToShow: 3, 12229 slidesToScroll: 3, 12230 } 12231 }, 12232 { 12233 breakpoint: 1280, 12234 settings: { 12235 slidesToShow: 4, 12236 slidesToScroll: 4, 12237 } 12238 }, 12239 ], 12240 }); 12241 12242 carousel.classList.remove('overflow-x-scroll'); 12243 gliderPrev.classList.remove('hidden'); 12244 gliderNext.classList.remove('hidden'); 12245 12246 if (carousel.dataset.autoplay !== 'false') { 12247 gliderAutoplay( 12248 glider, 12249 carousel.dataset.autoplaySpeed, 12250 carousel.dataset.infiniteLoop 12251 ); 12252 } 12253 window.addEventListener('page-builder-tab-activate', event => { 12254 const tab = event.detail.tab; 12255 if (tab && tab.contains(gliderElement)) { 12256 event.detail.nextTick.then(() => { 12257 requestAnimationFrame(() => glider.refresh(true)) 12258 }); 12259 } 12260 }); 12261 }; 12262 12263 const initSliderCarousel = (slider) => { 12264 slider.innerHTML = `<div data-role="glider-content">${slider.innerHTML}</div>`; 12265 slider.classList.add('glider-contain'); 12266 12267 slider.insertAdjacentHTML( 12268 'beforeend', 12269 '\u000A\u003Cdiv\u0020class\u003D\u0022carousel\u002Dnav\u0020flex\u0020items\u002Dcenter\u0020justify\u002Dcenter\u0020flex\u002D1\u0020p\u002D4\u0022\u003E\u000A\u0020\u0020\u0020\u0020\u003Cbutton\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020aria\u002Dlabel\u003D\u0022Previous\u0022\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020title\u003D\u0022Previous\u0022\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020class\u003D\u0022glider\u002Dprev\u0020w\u002D8\u0020h\u002D8\u0020mr\u002D1\u0020text\u002Dblack\u0020rounded\u002Dfull\u0020outline\u002Dnone\u0020focus\u003Aoutline\u002Dnone\u0020hidden\u0022\u000A\u0020\u0020\u0020\u0020\u003E\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u003Csvg\u0020xmlns\u003D\u0022http\u003A\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\u0022\u0020fill\u003D\u0022none\u0022\u0020viewBox\u003D\u00220\u00200\u002024\u002024\u0022\u0020stroke\u002Dwidth\u003D\u00222\u0022\u0020stroke\u003D\u0022currentColor\u0022\u0020width\u003D\u002224\u0022\u0020height\u003D\u002224\u0022\u0020aria\u002Dhidden\u003D\u0022true\u0022\u003E\u000A\u0020\u0020\u003Cpath\u0020stroke\u002Dlinecap\u003D\u0022round\u0022\u0020stroke\u002Dlinejoin\u003D\u0022round\u0022\u0020d\u003D\u0022M15\u002019l\u002D7\u002D7\u00207\u002D7\u0022\u002F\u003E\u000A\u003C\u002Fsvg\u003E\u000A\u0020\u0020\u0020\u0020\u003C\u002Fbutton\u003E\u000A\u0020\u0020\u0020\u0020\u003Cdiv\u0020role\u
12269003D\u0022tablist\u0022\u0020class\u003D\u0022glider\u002Ddots\u0020select\u002Dnone\u0020flex\u0020flex\u002Dwrap\u0020mx\u002D1\u0020justify\u002Dcenter\u0020p\u002D0\u0020focus\u003Aoutline\u002Dnone\u0022\u003E\u003C\u002Fdiv\u003E\u000A\u0020\u0020\u0020\u0020\u003Cbutton\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020aria\u002Dlabel\u003D\u0022Next\u0022\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020title\u003D\u0022Next\u0022\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020class\u003D\u0022glider\u002Dnext\u0020w\u002D8\u0020h\u002D8\u0020ml\u002D1\u0020text\u002Dblack\u0020rounded\u002Dfull\u0020outline\u002Dnone\u0020focus\u003Aoutline\u002Dnone\u0020hidden\u0022\u000A\u0020\u0020\u0020\u0020\u003E\u000A\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u003Csvg\u0020xmlns\u003D\u0022http\u003A\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\u0022\u0020fill\u003D\u0022none\u0022\u0020viewBox\u003D\u00220\u00200\u002024\u002024\u0022\u0020stroke\u002Dwidth\u003D\u00222\u0022\u0020stroke\u003D\u0022currentColor\u0022\u0020width\u003D\u002224\u0022\u0020height\u003D\u002224\u0022\u0020aria\u002Dhidden\u003D\u0022true\u0022\u003E\u000A\u0020\u0020\u003Cpath\u0020stroke\u002Dlinecap\u003D\u0022round\u0022\u0020stroke\u002Dlinejoin\u003D\u0022round\u0022\u0020d\u003D\u0022M9\u00205l7\u00207\u002D7\u00207\u0022\u002F\u003E\u000A\u003C\u002Fsvg\u003E\u000A\u0020\u0020\u0020\u0020\u003C\u002Fbutton\u003E\u000A\u003C\u002Fdiv\u003E\u000A' 12270 ); 12271 12272 const gliderElement = slider.querySelector('[data-role="glider-content"]'); 12273 const gliderDots = slider.querySelector('.glider-dots'); 12274 const gliderPrev = slider.querySelector('.glider-prev'); 12275 const gliderNext = slider.querySelector('.glider-next'); 12276 12277 const glider = new Glider(gliderElement, { 12278 slidesToShow: 1, 12279 slidesToScroll: 1, 12280 scrollLock: true, 12281 scrollLockDelay: 250, 12282 draggable: true, 12283 dragVelocity: 2.5, 12284 dots: gliderDots, 12285 arrows: { 12286 prev: gliderPrev, 12287 next: gliderNext, 12288 }, 12289 }); 12290 12291 slider.classList.add('glider-initialized'); 12292 if (slider.dataset.showArrows === 'true') { 12293 gliderPrev.classList.remove('hidden'); 12294 gliderNext.classList.remove('hidden'); 12295 } 12296 12297 if (slider.dataset.autoplay !== 'false') { 12298 gliderAutoplay( 12299 glider, 12300 slider.dataset.autoplaySpeed, 12301 slider.dataset.infiniteLoop 12302 ); 12303 } 12304 }; 12305 12306 const gliderAutoplay = (glider, milliseconds, loop) => { 12307 const pagesCount = glider.track.childElementCount; 12308 let slideTimeout = null; 12309 let nextIndex = 1; 12310 let paused = false; 12311 12312 const slide = () => { 12313 slideTimeout = setTimeout( 12314 () => { 12315 if (loop && nextIndex >= pagesCount) { 12316 nextIndex = 0; 12317 } 12318 glider.scrollItem(nextIndex); 12319 }, 12320 parseInt(milliseconds) 12321 ); 12322 }; 12323 12324 glider.ele.addEventListener('glider-animated', () => { 12325 nextIndex = glider.slide + glider.opt.slidesToScroll; 12326 window.clearInterval(slideTimeout); 12327 if (!paused && (loop || nextIndex < pagesCount)) { 12328 slide(); 12329 } 12330 }); 12331 12332 const pause = () => { 12333 if (!paused) { 12334 clearInterval(slideTimeout); 12335 paused = true; 12336 } 12337 }; 12338 12339 const unpause = () => { 12340 if (paused) { 12341 slide(); 12342 paused = false;
12343 } 12344 }; 12345 12346 glider.ele.parentElement.addEventListener('mouseover', pause, {passive: true}); 12347 glider.ele.parentElement.addEventListener('touchstart', pause, {passive: true}); 12348 glider.ele.parentElement.addEventListener('mouseout', unpause, {passive: true}); 12349 glider.ele.parentElement.addEventListener('touchend', unpause, {passive: true}); 12350 12351 slide(); 12352 }; 12353 12354 elements.forEach(element => { 12355 if (element.dataset.contentType === 'products') { 12356 initProductCarousel(element); 12357 } 12358 if (element.dataset.contentType === 'slider') { 12359 initSliderCarousel(element); 12360 } 12361 }); 12362 }; 12363 12364 window.addEventListener('DOMContentLoaded', () => { 12365 const carouselElements = document.querySelectorAll( 12366 `[data-content-type="products"][data-appearance="carousel"], 12367 [data-content-type="slider"]` 12368 ); 12369 12370 if (carouselElements.length > 0) { 12371 const script = document.createElement('script'); 12372 script.type = 'text/javascript'; 12373 12374 script.addEventListener('load', () => { 12375 initCarousels(carouselElements); 12376 }); 12377 12378 script.src = 'https\u003A\u002F\u002Fwww.huskee.co\u002Fstatic\u002Fversion1789639671\u002Ffrontend\u002F
12378Huskee\u002Fdefault\u002Fen_AU\u002FMagento_PageBuilder\u002Fjs\u002Fglider.min.js'; 12379 document.head.appendChild(script); 12380 } 12381 }); 12382 })(); 12383</script>
vendor: 1 bytes, line 12383
12383
12384<script> 12385 'use strict'; 12386 12387 (() => { 12388 const applyShowOnHover = (element) => { 12389 const showOverlayOnHover = (element) => { 12390 const overlay = element.querySelector('.pagebuilder-overlay'); 12391 const color = overlay.dataset.overlayColor; 12392 12393 element.addEventListener('mouseover', () => { 12394 overlay.style.backgroundColor = color; 12395 }); 12396 element.addEventListener('mouseout', () => { 12397 overlay.style.backgroundColor = 'transparent'; 12398 }); 12399 }; 12400 12401 const showButtonOnHover = (element) => { 12402 const button = element.querySelector('.pagebuilder-banner-button'); 12403 12404 element.addEventListener('mouseover', () => { 12405 button.style.opacity = '1'; 12406 button.style.visibility = 'visible'; 12407 }); 12408 element.addEventListener('mouseout', () => { 12409 button.style.opacity = '0'; 12410 button.style.visibility = 'hidden'; 12411 }); 12412 }; 12413 12414 if (element.dataset.showOverlay === 'hover') { 12415 showOverlayOnHover(element); 12416 } 12417 if (element.dataset.showButton === 'hover') { 12418 showButtonOnHover(element); 12419 } 12420 }; 12421 12422 window.addEventListener('DOMContentLoaded', () => { 12423 document.querySelectorAll('[data-content-type="banner"],[data-content-type="slider"]').forEach(element => { 12424 applyShowOnHover(element); 12425 }); 12426 }); 12427 })(); 12428</script>
vendor: 1 bytes, line 12428
12428
12429<script> 12430 // Overridden for exception handling 12431 function initPriceBox() { 12432 return { 12433 updatePrice(priceData) { 12434 const regularPriceLabel = this.$root.querySelector('.normal-price .price-label'); 12435 const regularPriceElement = this.$root.querySelector('.normal-price [data-price-type=finalPrice].price-wrapper .price'); const basePriceElement = this.$root.querySelector('.normal-price [data-price-type=basePrice].price-wrapper .price'); 12436 // Ensure regularPriceLabel exists before accessing classList 12437 if (regularPriceLabel) { 12438 if (priceData.finalPrice.amount < priceData.oldPrice.amount) { 12439 regularPriceLabel.classList.add('hidden'); 12440 } else { 12441 regularPriceLabel.classList.remove('hidden'); 12442 } 12443 } 12444 12445 // Ensure regularPriceElement exists before setting innerText 12446 if (regularPriceElement) { 12447 regularPriceElement.innerText = hyva.formatPrice(priceData.finalPrice.amount); 12448 } 12449 12450 // Ensure basePriceElement exists before setting innerText 12451 if (basePriceElement) { 12452 basePriceElement.innerText = hyva.formatPrice(priceData.basePrice.amount); 12453 } 12454 } 12455 } 12456 } 12457</script>
vendor: 1 bytes, line 12457
12457
12458<script> 12459 function initConfigurableOptions(productId, optionConfig) { 12460 12461 function findPathParam(key) { 12462 const baseUrl = (BASE_URL.substring(0, 2) === '//' ? 'http:' : '') + BASE_URL; 12463 const baseUrlParts = (new URL(baseUrl)).pathname.replace(/\/$/, '').split('/'); 12464 const pathParts = window.location.pathname.split('/').slice(baseUrlParts.length + 3); 12465 for (let i = 0; i < pathParts.length; i += 2) { 12466 if (pathParts[i] === key && pathParts.length > i) { 12467 return pathParts[i + 1]; 12468 } 12469 } 12470 } 12471 12472 return { 12473 optionConfig, 12474 productId, 12475 itemId: (new URLSearchParams(window.location.search)).get('id') || findPathParam('id'), 12476 allowedAttributeOptions: [], 12477 selectedValues: [], 12478 selectedOptions: {}, 12479 init() { 12480 this.baseInit(); 12481 }, 12482 baseInit() { 12483 this.buildLookupMap(); 12484 this.findAllowedAttributeOptions(); 12485 this.$nextTick(() => { 12486 if (typeof this.optionConfig.defaultValues === 'object') { 12487 for (const [attributeId, value] of Object.entries(this.optionConfig.defaultValues)) { 12488 this.changeOption(attributeId, value + ''); 12489 } 12490 } 12491 this.applyCheckedState(); 12492 }); 12493 }, 12494 applyCheckedState() { 12495 this.$root.querySelectorAll('input[data-attribute-id], select[data-attribute-id]').forEach(el => { 12496 const val = this.selectedValues[el.dataset.attributeId]; 12497 if (!val) return; 12498 if (el.tagName === 'SELECT') { 12499 el.value = val; 12500 this.selectedOptions[el.dataset.attributeId] = el.options[el.selectedIndex]?.text || ''; 12501 } else if (el.value === val && this.optionIsActive(el.dataset.attributeId, el.value)) { 12502 el.checked = true; 12503 this.selectedOptions[el.dataset.attributeId] = el.dataset.optionLabel || ''; 12504 } 12505 }); 12506 }, 12507 productIndex: 0, 12508 productIndexForPrice: 0, 12509 productIndexesMap: null, 12510 productIndexesKeys: null, 12511 attributeCount: 0, 12512 optionIsActive(attributeId, optionId) { 12513 return !!this.getAllowedAttributeOptions(attributeId).find( 12514 option => option.id === optionId 12515 ) 12516 }, 12517 optionIsDisabled() { 12518 const attributeId = this.$el.dataset.attributeId; 12519 if (!attributeId) { 12520 throw new Error('data-attribute-id missing, in item template'); 12521 } 12522 return !this.optionIsActive(attributeId, this.$el.value); 12523 }, 12524 findSimpleIndex() { 12525 this.productIndex = this.calculateSimpleIndexForPartialSelection(this.selectedValues); 12526 this.productIndexForPrice = this.findCheapestProductForPartialSelection(this.selectedValues); 12527 }, 12528 calculateSimpleIndexForPartialSelection(selectedValues) { 12529 if (selectedValues.length === 0) return 0; 12530 return this.findProductIdsForPartialSelection(selectedValues)[0]; 12531 }, 12532 buildLookupMap() { 12533 this.productIndexesMap = new Map(); 12534 this.attributeCount = Object.keys(this.optionConfig.attributes).length; 12535 for (const id in this.optionConfig.index) { 12536 const options = this.optionConfig.index[id]; 12537 if (! this.productIndexesKeys) { 12538 this.productIndexesKeys = Object.keys(options).sort(); 12539 } 12540 const key = JSON.stringify(options, this.productIndexesKeys); 12541 this.productIndexesMap.set(key, id); 12542 } 12543 }, 12544 findAllowedAttributeOptions() { 12545 this.allowedAttributeOptions = this.calculateAllowedAttributeOptions(this.selectedValues); 12546 }, 12547 calculateAllowedAttributeOptions(selectedValues) { 12548 const allAttributes = this.optionConfig.attributes; 12549 const allAttributesSorted = Object.values(allAttributes).sort((a,b) => { 12550 return a.position - b.position 12551 }); 12552 12553 const newAllowedAttributeOptions = []; 12554 12555 allAttributesSorted.forEach(attribute => { 12556 const selectionWithoutAttr = Object.assign({}, this.removeAttrFromSelection(selectedValues, attribute.id)); 12557 const availableIndexes = this.calculateAvailableProductIndexes(selectionWithoutAttr); 12558 newAllowedAttributeOptions[attribute.id] = allAttributes[attribute.id].options.filter(option => { 12559 return !!option.products.find(product => { 12560 return availableIndexes.includes(product); 12561 }) 12562 }); 12563 }); 12564 return newAllowedAttributeOptions; 12565 }, 12566 calculateAvailableProductIndexes(selectedOptions) { 12567 if (Object.keys(selectedOptions).length === 0) { 12568 if (Object.values(this.optionConfig.salable || {}).length) { 12569 return [].concat.apply([], [].concat.apply([], Object.values(this.optionConfig.salable).map(Object.values))).filter((x, i, a) => a.indexOf(x) === i) 12570 } 12571 return Object.keys(this.optionConfig.index); 12572 } 12573 12574 const selectedIds = Object.keys(selectedOptions); 12575 if (Object.values(this.optionConfig.salable || {}).length) { 12576 const selectedOptionIndexes = selectedIds.map(attrId => { 12577 const optionValue = selectedOptions[attrId]; 12578 return this.optionConfig.salable[attrId] && this.optionConfig.salable[attrId][optionValue] || [] 12579 }) 12580 return selectedOptionIndexes.reduce((acc, optionIndexes) => { 12581 return acc.filter(index => optionIndexes.includes(index)); 12582 }); 12583 } else { 12584 const productIndexes = this.optionConfig.index; 12585 return Object.keys(productIndexes).filter(index => { 12586 for (const attrId of selectedIds) { 12587 if (productIndexes[index][attrId] !== `${selectedOptions[attrId]}`) return false 12588 } 12589 return true 12590 }); 12591 } 12592 }, 12593 findAttributeByOptionId(optionId) { 12594 for (const attributeId in this.optionConfig.attributes) { 12595 const attributeOptions = this.optionConfig.attributes[attributeId].options || []; 12596 if (attributeOptions.find(option => option.id === optionId)) { 12597 return attributeId; 12598 } 12599 } 12600 }, 12601 getAllowedAttributeOptions(attributeId) { 12602 return this.allowedAttributeOptions[attributeId] || [] 12603 }, 12604 getAllAttributeOptions(attributeId) { 12605 return ( 12606 this.optionConfig.attributes[attributeId] && 12607 this.optionConfig.attributes[attributeId].options 12608 ) || [] 12609 }, 12610 getProductIdsForOption(option) { 12611 const attributeId = this.findAttributeByOptionId(option.id); 12612 const allOptions = this.optionConfig.attributes[attributeId]; 12613 const opt = (allOptions && allOptions.options || []).find(o => o.id === option.id); 12614 return opt && opt.products 12615 ? opt.products 12616 : []; 12617 }, 12618 findProductIdsForPartialSelection(optionSelection) { 12619 const candidateProducts = Object.values(optionSelection).reduce((candidates, optionId) => { 12620 const newCandidates = this.getProductIdsForOption({id: optionId}); 12621 return candidates === null 12622 ? newCandidates 12623 : candidates.filter(productId => newCandidates.includes(productId)); 12624 }, null); 12625 return candidateProducts || []; 12626 },
12627 findCheapestProductForPartialSelection(optionSelection) { 12628 const candidateProducts = this.findProductIdsForPartialSelection(optionSelection); 12629 return candidateProducts.reduce((cheapest, simpleIdx) => { 12630 // in the first iteration we start with simpleIdx as the currently cheapest product 12631 if (! this.optionConfig.optionPrices[cheapest]) return simpleIdx; 12632 const knownCheapestPrice = this.optionConfig.optionPrices[cheapest].finalPrice.amount; 12633 return knownCheapestPrice > this.optionConfig.optionPrices[simpleIdx].finalPrice.amount 12634 ? simpleIdx 12635 : cheapest; 12636 }, 0) 12637 }, 12638 removeAttrFromSelection(selectedValues, attributeId) { 12639 attributeId = parseInt(attributeId); 12640 return selectedValues.reduce((newSelection, val, attr) => { 12641 if (attr !== attributeId) { 12642 newSelection[attr] = val; 12643 } 12644 return newSelection; 12645 }, []); 12646 }, 12647 reflectOption(event) { 12648 const attributeId = event.target.dataset.attributeId; 12649 const label = event.target.dataset.optionLabel 12650 || event.target.options?.[event.target.selectedIndex]?.text 12651 || ''; 12652 this.selectedOptions[attributeId] = label; 12653 this.changeOption(attributeId, event.target.value); 12654 }, 12655 baseChangeOption(attributeId, value) { 12656 if (value === '') { 12657 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, attributeId) 12658 } else if (value && this.getAllowedAttributeOptions(attributeId).find(option => option.id === value)) { 12659 this.selectedValues[attributeId] = value; 12660 } 12661 this.findSimpleIndex(); 12662 this.findAllowedAttributeOptions(); 12663 this.updatePrices(); 12664 this.updateGallery(); 12665 }, 12666 changeOption(attributeId, value) { 12667 this.baseChangeOption(attributeId, value); 12668 12669 const candidates = this.findProductIdsForPartialSelection(this.selectedValues); 12670 12671 window.dispatchEvent( 12672 new CustomEvent( 12673 'configurable-selection-changed', 12674 { 12675 detail: { 12676 productId: this.productId, 12677 optionId: attributeId, 12678 value: value, 12679 productIndex: this.productIndex, 12680 selectedValues: this.selectedValues, 12681 candidates: candidates, 12682 skuCandidates: Object.values(candidates).map(id => this.optionConfig.sku?.[id] ?? null).filter(Boolean), 12683 } 12684 } 12685 ) 12686 ); 12687 }, 12688 calculateIsMinimalPrice() { 12689 return ! this.isFullSelection(this.selectedValues); 12690 }, 12691 isFullSelection(selectedValues) { 12692 return Object.keys(selectedValues).length === this.attributeCount; 12693 }, 12694 updatePrices() { 12695 const value = this.optionConfig.optionPrices[this.productIndexForPrice] || this.optionConfig.prices; 12696 window.dispatchEvent( 12697 new CustomEvent( 12698 "update-prices-" + this.productId, 12699 { 12700 detail: Object.assign( 12701 value, 12702 { isMinimalPrice: this.calculateIsMinimalPrice() } 12703 ) 12704 } 12705 ) 12706 ); 12707 }, 12708 updateGallery () { 12709 if (this.productIndex) { 12710 const images = this.optionConfig.images[this.productIndex]; 12711 images && window.dispatchEvent(new CustomEvent( 12712 "update-gallery", 12713 { detail: this.sortImagesByPosition(images) } 12714 )); 12715 } else { 12716 window.dispatchEvent(new Event("reset-gallery")); 12717 } 12718 }, 12719 sortImagesByPosition(images) { 12720 return images.sort((x, y) => { 12721 return x.position === y.position ? 0 : (parseInt(x.position) > parseInt(y.position) ? 1 : -1) 12722 }); 12723 }, 12724 preselectQuerystringItems() { 12725 // pre-select option like ?size=167 12726 const urlQueryParams = new URLSearchParams(window.location.search.replace('?','')); 12727 this.preselectItemsBasedOnLocation(attribute => urlQueryParams.get(attribute.code)); 12728 }, 12729 preselectLocationHashItems() { 12730 // pre-select option like #144=167 12731 const urlHashParams = new URLSearchParams(window.location.hash.replace('#','')); 12732 this.preselectItemsBasedOnLocation(attribute => urlHashParams.get(attribute.id)); 12733 }, 12734 preselectItemsBasedOnLocation(getLocationValue) { 12735 Object.values(this.optionConfig.attributes).map(attribute => { 12736 const v = getLocationValue(attribute); 12737 v && this.changeOption(attribute.id, v) 12738 }); 12739 }, 12740 findProductForOptionPrice(attributeId, value) { 12741 const options = this.optionConfig.attributes[attributeId]?.options || []; 12742 const option = options.find(o => o.id === value); 12743 if (!option?.products?.length) return null; 12744 return option.products.reduce((cheapest, productId) => { 12745 if (!cheapest) return productId; 12746 const cheapestPrice = this.optionConfig.optionPrices[cheapest]?.finalPrice?.amount ?? Infinity; 12747 const thisPrice = this.optionConfig.optionPrices[productId]?.finalPrice?.amount ?? Infinity; 12748 return thisPrice < cheapestPrice ? productId : cheapest; 12749 }, null); 12750 }, 12751 getOptionLabel() { 12752 const value = this.$el.value; 12753 const attributeId = this.$el.dataset.attributeId; 12754 const label = this.$el.dataset.optionLabel || ''; 12755 if (!value || !attributeId) return label; 12756 12757 const productId = this.findProductForOptionPrice(attributeId, value); 12758 if (!productId) return label; 12759 12760 const optionPrice = this.optionConfig.optionPrices[productId]?.finalPrice?.amount; 12761 const basePrice = this.optionConfig.prices?.finalPrice?.amount; 12762 if (optionPrice == null || basePrice == null) return label; 12763 12764 const adjustment = optionPrice - basePrice; 12765 if (Math.abs(adjustment) < 0.001) return label; 12766 12767 const formatted = hyva.formatPrice(Math.abs(adjustment)); 12768 return `${label} (${adjustment > 0 ? '+' : '-'}${formatted})`; 12769 }, 12770 } 12771 } 12772</script>
vendor: 1 bytes, line 12772
12772
12773<script> 12774 function initSwatchOptions() { 12775 return { 12776 activeTooltipItem: false, 12777 tooltipPositionElement: false, 12778 isTooltipVisible() { 12779 return !!this.activeTooltipItem; 12780 }, 12781 isCurrentTooltipVisual() { 12782 return this.activeTooltipItem && ['color', 'image'].includes(this.activeTooltipItem.type); 12783 }, 12784 getTooltipImageStyle() { 12785 const item = this.activeTooltipItem; 12786 if (item?.type === 'color') return { 12787 backgroundColor: item.preview, 12788 width: `${item.width ?? 90}px`, 12789 height: `${item.height ?? 90}px` 12790 }; 12791 if (item?.type === 'image') return { 12792 backgroundImage: `url(${item.preview})`, 12793 backgroundSize: 'cover', 12794 backgroundPosition: 'center', 12795 backgroundRepeat: 'no-repeat', 12796 width: `${item.width ?? 90}px`, 12797 height: `${item.height ?? 90}px` 12798 }; 12799 return null; 12800 }, 12801 getTooltipPosition() { 12802 if (!this.tooltipPositionElement) return null; 12803 const el = this.tooltipPositionElement; 12804 const elRect = el.getBoundingClientRect(); 12805 const left = elRect.left + elRect.width / 2; 12806 12807 return { 12808 bottom: `${window.innerHeight - elRect.top}px`, 12809 left: `${left}px`, 12810 translate: '-50% 0' 12811 }; 12812 }, 12813 getTooltipLabel() { 12814 return this.activeTooltipItem ? this.activeTooltipItem.label : ''; 12815 }, 12816 showTooltip(event) { 12817 if (!window.matchMedia('(hover: hover) and (pointer: fine)').matches) return; 12818 const input = event.currentTarget.querySelector('input'); 12819 this.activeTooltipItem = JSON.parse(input.dataset.tooltip); 12820 this.tooltipPositionElement = event.currentTarget; 12821 }, 12822 hideTooltip() { 12823 this.activeTooltipItem = false; 12824 }, 12825 } 12826 } 12827</script>
vendor: 1 bytes, line 12827
12827
12828<script> 12829 (function () { 12830 const modals = []; 12831 let lastHide = 0; 12832 12833 function resolveElement($refs, value) { 12834 if (!value) return null; 12835 if (value instanceof Element) return value; 12836 if (typeof value === 'string') { 12837 try { 12838 return ($refs && $refs[value]) || document.querySelector(value) || null; 12839 } catch (e) {} 12840 } 12841 return null; 12842 } 12843 12844 function determineTrigger($refs, dialog, trigger) { 12845 if (!trigger && dialog?.target instanceof HTMLElement) { 12846 return dialog.target; 12847 } 12848 12849 if (typeof dialog === 'string' && trigger?.target instanceof HTMLElement) { 12850 return trigger.target; 12851 } 12852 12853 return resolveElement($refs, trigger); 12854 } 12855 12856 function setFocusFirstElement(dialogElement) { 12857 if (!dialogElement) return; 12858 12859 window.setTimeout(() => { 12860 const candidates = dialogElement.querySelectorAll('[x-focus-first]'); 12861 for (const candidate of candidates) { 12862 if (candidate.closest('dialog') === dialogElement) { 12863 candidate.focus(); 12864 return; 12865 } 12866 } 12867 }, 50); 12868 } 12869 12870 window.hyva.modal = function(options = {}) { 12871 if (this.$el && this.$el.dataset && this.$el.dataset.options) { 12872 try { 12873 options = Object.assign(options, JSON.parse(this.$el.dataset.options || '{}')); 12874 } catch (e) { 12875 throw new Error('Cannot read the dialog options from the data-options attribute: not valid JSON'); 12876 } 12877 } 12878 12879 const config = Object.assign({ 12880 dialog: 'dialog', duration: 300, transitionEnter: 'transition ease-out duration-300', 12881 transitionEnterStart: 'opacity-0', 12882 transitionEnterEnd: 'opacity-100', 12883 transitionLeave: 'transition ease-in duration-300', 12884 transitionLeaveStart: 'opacity-100', 12885 transitionLeaveEnd: 'opacity-0', 12886 }, options); 12887 12888 return { 12889 opened: {}, 12890 show(dialog, trigger) { 12891 const name = typeof dialog === 'string' ? dialog : config.dialog; 12892 if (!name || this.opened[name]) return; 12893 12894 this.opened[name] = true; 12895 const focusTarget = determineTrigger(this.$refs, dialog, trigger); 12896 const dialogEl = this.$refs && this.$refs[name]; 12897 const frame = { name, instance: this, focusTarget, time: Date.now() }; 12898 modals.push(frame); 12899 setFocusFirstElement(dialogEl); 12900 return new Promise(resolve => { frame.resolve = resolve; }); 12901 }, 12902 cancel() { 12903 this.hide(false); 12904 }, 12905 ok() { 12906 this.hide(true); 12907 }, 12908 hide(value) { 12909 if (Date.now() - lastHide < config.duration) return; 12910 lastHide = Date.now(); 12911 12912 const modal = modals.pop(); 12913 if (!modal) return; 12914 12915 modal.instance.opened[modal.name] = false; 12916 12917 const focusTarget = modal.focusTarget; 12918 focusTarget && window.setTimeout(() => focusTarget.focus(), config.duration + 50); 12919 modal.resolve && modal.resolve(value); 12920 }, 12921 /** 12922 * @deprecated Use dialogConfig() instead, which is used by the default modal-container.phtml template. 12923 * This method is kept for backwards compatibility with custom container templates. 12924 */ 12925 overlay(dialog) { 12926 dialog = this.$el.dataset.dialogRefName || dialog; 12927 const name = typeof dialog === 'string' ? dialog : config.dialog; 12928 if (this.$el.tagName !== 'DIALOG' && ! window.hyva.modal.legacyModalCompatibility) { 12929 console.warn( 12930 '[hyva] Deprecated modal markup detected (x-bind="overlay" on a non-dialog element' + 12931 (name ? ` for "${name}"` : '') + ').\n' + 12932 'Escape and click-outside closing are not available for such dialogs anymore.\n' + 12933 'Install hyva-themes/magento2-legacy-modal-compatibility or migrate to the native <dialog> markup.' 12934 ); 12935 } 12936 return Object.assign({}, this.dialogConfig(name), { 12937 ['@hyva-modal-show.window'](event) { 12938 event.detail && event.detail.dialog === name && this.show(name, event.detail.focusAfterHide) 12939 } 12940 }); 12941 }, 12942 dialogConfig(name) { 12943 name = name || this.$el.dataset.dialogRefName || config.dialog; 12944 return { 12945 ['x-show']: () => name ? this.opened[name] : false, 12946 ['x-transition:enter']: config.transitionEnter, 12947 ['x-transition:enter-start']: config.transitionEnterStart, 12948 ['x-transition:enter-end']: config.transitionEnterEnd, 12949 ['x-transition:leave']: config.transitionLeave, 12950 ['x-transition:leave-start']: config.transitionLeaveStart, 12951 ['x-transition:leave-end']: config.transitionLeaveEnd, 12952 ['@hyva-modal-show.window'](event) { 12953 event.detail && event.detail.dialog === name && this.show(name, event.detail.focusAfterHide); 12954 }, 12955 }; 12956 },
12957 showDialogOnInit() { 12958 const name = this.$el.dataset.dialogRefName; 12959 if (name) { 12960 this.show(name); 12961 } 12962 } 12963 }; 12964 } 12965 12966 window.hyva.modal.peek = () => modals.length > 0 && modals[modals.length - 1] 12967 12968 window.hyva.modal.pop = function () { 12969 if (modals.length > 0) { 12970 modals[modals.length - 1].instance.hide(); 12971 } 12972 } 12973 12974 /** 12975 * @deprecated Native <dialog> handles focus trapping automatically. 12976 * Calling this function has no effect. Kept for backwards compatibility. 12977 */ 12978 window.hyva.modal.excludeSelectorsFromFocusTrap = function (selectors) {} 12979 12980 /** 12981 * @deprecated Native <dialog> with x-htmldialog handles ESC key and click-outside events. 12982 * These functions have no effect. Kept for backwards compatibility. 12983 */ 12984 window.hyva.modal.eventListeners = { 12985 keydown: event => {}, 12986 click: event => {} 12987 }; 12988 })(); 12989 12990 window.addEventListener('alpine:init', () => { 12991 Alpine.data('hyvaModal', window.hyva.modal); 12992 }, {once: true}); 12993</script>
vendor: 1 bytes, line 12993
12993
12994<script> 12995 (function () { 12996 if (! window.hyva || ! window.hyva.modal) { 12997 return; 12998 } 12999 13000 window.hyva.modal.legacyModalCompatibility = true; 13001 13002 const refElement = frame => 13003 frame && frame.instance && frame.instance.$refs && frame.instance.$refs[frame.name]; 13004 13005 const isLegacyElement = el => !! el && el.tagName !== 'DIALOG'; 13006 13007 const isOverlayDisabled = el => !! el && el.hasAttribute('x-no-overlay'); 13008 13009 window.hyva.modal.eventListeners = Object.assign(window.hyva.modal.eventListeners || {}, { 13010 keydown: event => { 13011 if (event.key !== 'Escape') { 13012 return; 13013 } 13014 const frame = window.hyva.modal.peek(); 13015 if (frame && isLegacyElement(refElement(frame))) { 13016 frame.instance.hide(); 13017 } 13018 }, 13019 click: event => { 13020 const frame = window.hyva.modal.peek(); 13021 const dialog = refElement(frame); 13022 if (! frame || ! isLegacyElement(dialog)) { 13023 return; 13024 } 13025 if (frame.time + 50 < Date.now() && ! isOverlayDisabled(dialog) && 13026 ! dialog.contains(event.target) 13027 ) { 13028 frame.instance.hide(); 13029 } 13030 } 13031 }); 13032 13033 document.addEventListener('keydown', window.hyva.modal.eventListeners.keydown); 13034 document.addEventListener('click', window.hyva.modal.eventListeners.click); 13035 13036 window.hyva.modal = new Proxy(window.hyva.modal, { 13037 apply(target, thisArg, args) { 13038 const instance = Reflect.apply(target, thisArg, args); 13039 13040 const originalShow = instance.show; 13041 instance.show = function (dialog, trigger) { 13042 const frameBefore = window.hyva.modal.peek(); 13043 const openerCandidate = document.activeElement; 13044 const result = originalShow.call(this, dialog, trigger); 13045 const frame = window.hyva.modal.peek(); 13046 if (! frame || frame === frameBefore) { 13047 return result; } 13048 const el = refElement(frame); 13049 if (isLegacyElement(el)) { 13050 frame.legacyFocusTarget = openerCandidate; 13051 if (! isOverlayDisabled(el)) { 13052 document.body.classList.add('overflow-hidden'); 13053 this.$nextTick 13054 ? this.$nextTick(() => hyva.trapFocus(el)) 13055 : hyva.trapFocus(el); 13056 } 13057 window.setTimeout(() => { 13058 const focusFirst = el.querySelector('[x-focus-first]'); 13059 focusFirst && focusFirst.focus(); 13060 }, 50); 13061 } 13062 return result; 13063 }; 13064 13065 const originalHide = instance.hide; 13066 instance.hide = function (value) { 13067 const frame = window.hyva.modal.peek(); 13068 const el = refElement(frame); 13069 const wasLegacy = frame && isLegacyElement(el); 13070 const result = originalHide.call(this, value); 13071 if (! wasLegacy || window.hyva.modal.peek() === frame) { 13072 return result; } 13073 hyva.releaseFocus(el); 13074 13075 const next = window.hyva.modal.peek(); 13076 const nextEl = next ? refElement(next) : null; 13077 if (isLegacyElement(nextEl) && ! isOverlayDisabled(nextEl)) { 13078 hyva.trapFocus(nextEl); 13079 } else { 13080 document.body.classList.remove('overflow-hidden'); 13081 } 13082 13083 const focusTarget = frame.legacyFocusTarget; 13084 focusTarget && window.setTimeout(() =>
13084 focusTarget.focus(), 50); 13085 13086 return result; 13087 }; 13088 13089 return instance; 13090 } 13091 }); 13092 })(); 13093</script>
vendor: 1 bytes, line 13093
13093
13094<script> 13095 'use strict'; 13096 13097 const grecaptchaV2LoadCallbacks = []; 13098 13099 function executeRecaptchaLoadCallbacks () { 13100 while (window.grecaptcha && grecaptchaV2LoadCallbacks.length > 0) { 13101 const callback = grecaptchaV2LoadCallbacks.pop(); 13102 typeof callback === 'string' ? window[callback]() : callback(); 13103 } 13104 } 13105 13106 (function() { 13107 const loadRecaptchaScript = (event) => { 13108 13109 if (window.grecaptcha || !event.target.form) { 13110 return; 13111 } 13112 13113 const url = 'https://www.google.com/recaptcha/api.js?onload=executeRecaptchaLoadCallbacks'; 13114 const siteKey = '6LfFG_UUAAAAAFygiEebrs9DolKMpN8Rh8Dz_Lc2'; 13115 const script = document.createElement('script'); 13116 script.type = 'text/javascript'; 13117 script.src = siteKey ? `${url}&render=${siteKey}` : `${url}&render=explicit`; 13118 script.async = true; 13119 document.head.appendChild(script); 13120 13121 script.addEventListener('load', () => { 13122 Array.from(document.querySelectorAll('button[data-recaptcha-btn]:disabled')).forEach(btn => btn.removeAttribute('disabled')); 13123 }); 13124 } 13125 13126 window.forceLoadRecaptchaScript = (form) => { 13127 loadRecaptchaScript({target: {form: form || 'dummy'}}); 13128 executeRecaptchaLoadCallbacks(); 13129 }; 13130 13131 document.body.addEventListener('input', loadRecaptchaScript, { once: true }); 13132 document.body.addEventListener('focus', loadRecaptchaScript, { once: true }); 13133 13134 }()); 13135 13136</script>
13136 13137 13138 13139<section x-data="initAmGdprLoader" 13140 @amgdpr-loading-start.window="showLoader" 13141 @amgdpr-loading-stop.window="hideLoader" 13142> 13143 <div 13144 class="z-50 fixed inset-0 flex justify-center items-center bg-white/70 select-none" 13145 x-show="isLoading" 13146 x-cloak 13147 x-transition.opacity.duration.200ms 13148> 13149 <div class="flex items-center"> 13150 <svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" class="animate-spin" aria-hidden="true"> 13151 <circle style="opacity: .3;" cx="12" cy="12" r="10"/> 13152 <path fill="none" stroke-width="2" d="M12,2 a10,10 0 0,1 10,10"/> 13153</svg> 13154 <div class="ml-10 text-xl"> 13155 Loading... </div> 13156 </div> 13157</div> 13158</section> 13159
13160<script data-cfasync="false"> 13161 'use strict'; 13162 13163 function initAmGdprLoader() { 13164 return { 13165 isLoading: false, 13166 13167 /** 13168 * @returns {void} 13169 */ 13170 showLoader: function () { 13171 this.isLoading = true; 13172 }, 13173 13174 /** 13175 * @returns {void} 13176 */ 13177 hideLoader: function () { 13178 this.isLoading = false; 13179 } 13180 } 13181 } 13182 13183 window.addEventListener( 13184 'alpine:init', 13185 () => Alpine.data('initAmGdprLoader', initAmGdprLoader), 13186 { once: true } 13187 ); 13188</script>
13188 13189 13190<div x-data="initAmRequestPopup" x-spread="eventListeners" x-bind="eventListeners"> 13191 <div x-cloak 13192 x-spread="amRequestPopupOverlay" 13193 x-bind="amRequestPopupOverlay" 13194 class="fixed inset-0 flex items-center justify-center text-left bg-black bg-opacity-50 bg-black/50 z-30"> 13195 <div x-ref="am-request-popup" 13196 role="dialog" 13197 aria-labelledby="am-request-popup-label" 13198 class="relative inline-block max-h-screen overflow-auto bg-white shadow-xl rounded-lg px-5 pb-5 w-full sm:max-w-screen-sm"> 13199 <div class="flex justify-end w-full my-3"> 13200 <button @click="hide" 13201 aria-label="Close Quote form" 13202 class="text-gray-300 transition hover:text-black focus:drop-shadow-md hover:drop-shadow-md"> 13203 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 13204 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 13205</svg> 13206 </button> 13207 </div> 13208 13209 <div id="am-request-popup-label" class="w-full pb-1 mb-4 border-b-2"> 13210 <span>Get a Quote for</span> 13211 <span class="font-bold" x-text="formConfig.name"></span> 13212 </div> 13213 <form x-ref="am-request-form" action="#" class="form" @submit.prevent="submitRequest"> 13214 <input type="hidden" name="product_id" :value="formConfig.id"> 13215 <fieldset class="fieldset" aria-labelledby="am-request-popup-label"> 13216 <div class="field w-full"> 13217 <label for="am-request-popup-name"> 13218 <span class="text-red-500">*</span> 13219 Customer Name </label> 13220 <input type="text" 13221 required 13222 class="w-full form-input" 13223 id="am-request-popup-name" 13224 name="name" 13225 x-model="formConfig.customer.name" 13226 x-focus-first/> 13227 </div> 13228 <div class="field w-full"> 13229 <label for="am-request-popup-email"> 13230 <span class="text-red-500">*</span> 13231 Customer Email </label> 13232 <input type="email" 13233 id="am-request-popup-email" 13234 required 13235 class="w-full form-input" 13236 name="email" 13237 x-model="formConfig.customer.email"/> 13238 </div> 13239 <div class="field w-full"> 13240 <label for="am-request-popup-phone"> 13241 <span class="text-red-500">*</span> 13242 Phone Number </label> 13243 <input 13244 type="text" 13245 required 13246 class="w-full form-input" 13247 name="phone" 13248 id="am-request-popup-phone" 13249 x-model="formConfig.customer.phone"/> 13250 </div> 13251 <div class="field w-full"> 13252 <label for="am-request-popup-comment"> 13253 Comment </label> 13254 <textarea name="comment" id="am-request-popup-comment" class="w-full form-input" rows="3"></textarea> 13255 </div> 13256 </fieldset> 13257 <div x-show="errorMessage.length" 13258 x-transition:enter="transition ease-out duration-200" 13259 x-transition:enter-start="opacity-0 transform" 13260 x-transition:enter-end="opacity-100 transform" 13261 class="bg-red-200 p-3 my-3 rounded-sm w-full"> 13262 <p class="text-red-900" x-text="errorMessage"></p> 13263 </div> 13264 <div class="mt-5 w-full flex justify-center"> 13265 <button 13266 :disabled="isLoading" 13267 type="submit" 13268 class="action submit primary btn btn-primary disabled:opacity-75"> 13269 <template x-if="isNotLoading">
13270 <span> 13271 Submit </span> 13272 </template> 13273 <template x-if="isLoading"> 13274 <span> 13275 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 38 38" class="w-5 h-5" role="img"> 13276 <defs> 13277 <linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a"> 13278 <stop stop-color="#fff" stop-opacity="0" offset="0%"/> 13279 <stop stop-color="#fff" stop-opacity=".631" offset="63.146%"/> 13280 <stop stop-color="#fff" offset="100%"/> 13281 </linearGradient> 13282 </defs> 13283 <g fill="none" fill-rule="evenodd"> 13284 <g transform="translate(1 1)"> 13285 <path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="url(#a)" stroke-width="2"> 13286 <animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="0.9s" repeatCount="indefinite"/> 13287 </path> 13288 <circle fill="#fff" cx="36" cy="18" r="1"> 13289 <animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="0.9s" repeatCount="indefinite"/> 13290 </circle> 13291 </g> 13292 </g> 13293<title>loading-indicator</title></svg> 13294 13295 Loading... <span class="sr-only"> Loading... </span> 13296 </span> 13297 </template> 13298 </button> 13299 </div> 13300 </form> 13301 13302 </div> 13303 13304 </div> 13305</div> 13306
13307<script> 13308 'use strict'; 13309 13310 function initAmRequestPopup() { 13311 return Object.assign( 13312 hyva.modal.call(this), 13313 { 13314 formConfig: { 13315 url: '', 13316 id: null, 13317 name: '', 13318 customer: { 13319 name: '', 13320 email: '', 13321 phone: '' 13322 }, 13323 }, 13324 isLoading: false, 13325 abortController: undefined, 13326 errorMessage: '', 13327 popupRefName: 'am-request-popup', 13328 pressedButton: null, 13329 eventListeners: { 13330 ['@open-am-request-popup.window'](event) { 13331 this.formConfig = { ...event.detail }; 13332 this.pressedButton = event.target; 13333 this.$nextTick(() => { 13334 !this.opened[this.popupRefName] && this.showPopup().then(() => { 13335 !!this.abortController && this.abortController.abort(); 13336 13337 // Wait close animation 13338 setTimeout(() => { 13339 this.isLoading = false; 13340 this.formConfig = { 13341 url: '', 13342 id: null, 13343 name: '', 13344 customer: { 13345 name: '', 13346 email: '', 13347 phone: '' 13348 } 13349 }; 13350 }, 400); 13351 }); 13352 }); 13353 } 13354 }, 13355 showPopup: function () { 13356 return this.show(this.popupRefName); 13357 }, 13358 hidePopup: function () { 13359 this.hide(this.popupRefName); 13360 this.pressedButton?.focus(); 13361 }, 13362 submitRequest: function () { 13363 const headers = { 13364 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 13365 }; 13366 const form = this.$refs['am-request-form']; 13367 const body = new URLSearchParams(new FormData(form)); 13368 body.append('form_key', hyva.getFormKey()); 13369 this.isLoading = true; 13370 this.errorMessage = ''; 13371 this.abortController = new AbortController(); 13372 13373 fetch(this.formConfig.url, { 13374 signal: this.abortController.signal, 13375 method: 'POST', 13376 body, 13377 headers: headers 13378 }).then((response) => { 13379 if (!response.ok) { 13380 this.errorMessage = 'Something went wrong. Please try again later'; 13381 } 13382 13383 return response.json(); 13384 }).then((response) => { 13385 if (!response.success) { 13386 this.errorMessage = response.error; 13387 return; 13388 } 13389 13390 typeof window.dispatchMessages !== undefined && window.dispatchMessages([{ 13391 type: 'success', 13392 text: response.success 13393 }], 5000); 13394 13395 form.reset(); 13396 this.hidePopup(); 13397 }).finally(() => { 13398 this.abortController = undefined; 13399 this.isLoading = false; 13400 }); 13401 }, 13402 amRequestPopupOverlay: function () { 13403 return this.overlay('am-request-popup'); 13404 }, 13405 isNotLoading: function () { 13406 return !this.isLoading; 13407 }, 13408 clickHandlerToOpenForm: function () { 13409 const formConfig = JSON.parse(this.$el.dataset.formConfig); 13410 13411 this.$dispatch('open-am-request-popup', formConfig); 13412 } 13413 } 13414 ); 13415 } 13416 (() => { 13417 const initFn = () => Alpine.data('initAmRequestPopup', initAmRequestPopup); 13418 window.Alpine ? initFn() : window.addEventListener('alpine:init', initFn, {once: true}); 13419 })() 13420</script>
13420 13421
13422<script> 13423 'use strict'; 13424 13425 function initAmRequestPopupShow() { 13426 return { 13427 clickHandler() { 13428 const formConfig = JSON.parse(this.$el.dataset.formConfig); 13429 13430 this.$dispatch('open-am-request-popup', formConfig); 13431 } 13432 }; 13433 } 13434 13435 (() => { 13436 const initFn = () => Alpine.data('initAmRequestPopupShow', initAmRequestPopupShow); 13437 window.Alpine ? initFn() : window.addEventListener('alpine:init', initFn, {once: true}); 13438 })() 13439</script>
vendor: 1 bytes, line 13439
13439
13440<script> 13441 'use strict'; 13442 13443 (function () { 13444 const amGroupCatHider = { 13445 parentSelector: null, 13446 element: null, 13447 performHide: function() { 13448 window.amGroupCatHidePool?.forEach((item) => { 13449 const parentNode = this.getParentNode(item); 13450 13451 if (!parentNode) { 13452 return; 13453 } 13454 13455 +item.hideConfig.hide_wishlist === 1 && this.hideWishlist(parentNode); 13456 +item.hideConfig.hide_compare === 1 && this.hideCompare(parentNode); 13457 }); 13458 }, 13459 13460 hideWishlist: function(node) { 13461 node?.querySelector('[x-data^="initWishlist"]')?.remove(); 13462 }, 13463 13464 hideCompare: function(node) { 13465 node?.querySelector('[x-data^="initCompareOnProductList"]')?.remove(); 13466 node?.querySelector('[x-data^="initCompareOnProductView"]')?.remove(); 13467 }, 13468 13469 getParentNode: function({hideConfig, node}) { 13470 return hideConfig.parent 13471 ? node.closest(hideConfig.parent) 13472 : node.parentNode?.parentNode?.parentNode; // 3 level up 13473 } 13474 }; 13475 13476 amGroupCatHider.performHide(); 13477 })(); 13478</script>
vendor: 1 bytes, line 13478
13478
13479<script> 13480 'use strict'; 13481 13482 function initParentContainerProcessor() { 13483 const selectors = { 13484 labelContainer: '.amasty-label-container', 13485 galleryImg: '#gallery img:not(.amasty-label-image)' 13486 } 13487 13488 return { 13489 /** 13490 * @param {AmlabelComponent} label 13491 * @return {void} 13492 */ 13493 process: function (label) { 13494 this.moveLabelToParent(label); 13495 this.setParentStyles(label); 13496 }, 13497 13498 /** 13499 * @param {AmlabelComponent} label 13500 * @return {void} 13501 */ 13502 moveLabelToParent: function (label) { 13503 const newParent = this.getNewParent(label); 13504 13505 if (!newParent) { 13506 return; 13507 } 13508 13509 const wrapperElement = newParent.querySelector(label.wrapperClass); 13510 label.parent = newParent; 13511 13512 // if same wrapper already exists - move label there 13513 if (wrapperElement && !wrapperElement.isSameNode(label.element)) { 13514 this.insertLabelByPriority(label, wrapperElement); 13515 } else { 13516 newParent.prepend(label.element); 13517 } 13518 }, 13519 13520 /** 13521 * @param {AmlabelComponent} label 13522 * @return {HTMLElement|null} 13523 */ 13524 getNewParent: function (label) { 13525 // move label to container from settings 13526 let newParent = label.element.closest(label.config.path) 13527 || this.findPreviousNodeExcludingScriptsAndDifferentLabels(label.element)?.querySelector(label.config.path) 13528 || label.parent.querySelector(label.config.path); 13529 13530 // Try to find default gallery container on product page if none of the config selectors found 13531 if (!newParent && label.config.move !== 1 && label.config.mode === 'prod') { 13532 newParent = document.querySelector(selectors.galleryImg)?.parentElement; 13533 } 13534 13535 return newParent; 13536 }, 13537 13538 /** 13539 * @param {HTMLElement} element 13540 * @return {HTMLElement|null} 13541 */ 13542 findPreviousNodeExcludingScriptsAndDifferentLabels: function (element) { 13543 const previousElement = element?.previousElementSibling; 13544 if (!previousElement) { 13545 return null; 13546 } 13547 13548 if (previousElement?.tagName !== 'SCRIPT' 13549 && !previousElement?.className?.includes?.('amlabel-position-wrapper') 13550 ) { 13551 return previousElement; 13552 } 13553 13554 return this.findPreviousNodeExcludingScriptsAndDifferentLabels(previousElement); 13555 }, 13556 13557 /** 13558 * @param {AmlabelComponent} label 13559 * @param {HTMLElement} wrapperElement 13560 * @return {void} 13561 */ 13562 insertLabelByPriority: function (label, wrapperElement) { 13563 const labelContainers = wrapperElement.querySelectorAll(selectors.labelContainer); 13564 const currentLabelOrder = label.labelContainer.dataset.amlabelOrder; 13565 let insertAfterContainer = null; 13566 13567 // find the label behind which we want to insert current one by priority 13568 labelContainers.forEach(labelContainer => { 13569 if (labelContainer.dataset.amlabelOrder < currentLabelOrder) { 13570 insertAfterContainer = labelContainer; 13571 } 13572 }); 13573 13574 insertAfterContainer === null 13575 ? wrapperElement.prepend(label.labelContainer) 13576 : insertAfterContainer.after(label.labelContainer); 13577 13578 label.element.remove(); 13579 label.element = wrapperElement; 13580 }, 13581 13582 /** 13583 * @param {AmlabelComponent} label 13584 * @return {void} 13585 */ 13586 setParentStyles: function (label) { 13587 /* required for child position absolute */ 13588 !(label.parent.style.position === 'absolute') && (label.parent.style.position = 'relative'); 13589 !label.parent.style.width && label.parent.classList.add('w-fit-content'); 13590 !label.parent.style.marginLeft && label.parent.classList.add('ml-auto'); 13591 !label.parent.style.marginRight && label.parent.classList.add('mr-auto'); 13592 }, 13593 } 13594 } 13595</script>
vendor: 1 bytes, line 13595
13595
13596<script> 13597 'use strict'; 13598 13599 function initLabelItemProcessor() { 13600 /** 13601 * @typedef {Object} AmlabelComponent 13602 * 13603 * @property {HTMLElement} element Label wrapper element 13604 * @property {HTMLElement} parent Element where label should be placed 13605 * @property {boolean} initialized Is label initialized 13606 * @property {string} wrapperClass Dynamic class for label wrapper to place multiple labels properly by position 13607 * @property {HTMLElement} labelContainer Container for label text image and tooltip 13608 * @property {Config} config Label Configuration 13609 * @property {Object} parentContainerProcessor 13610 * @property {Object} labelItemProcessor 13611 */ 13612 13613 /** 13614 * @typedef {Object} AmLabelConfig 13615 * 13616 * @property {0|1} alignment Labels Alignment config value (0 - Horizontal| 1 - Vertical) 13617 * @property {number} label Label Id 13618 * @property {number} margin Margin between labels config value 13619 * @property {('cat'|'prod')} mode Label mode 13620 * @property {number} move 13621 * @property {number} order Label order 13622 * @property {string} path The css selector to which label should place 13623 * @property {string} position Label position 13624 * @property {string} product Product Id 13625 * @property {string} redirect_url 13626 * @property {string} size Label size 13627 * @property {{ 13628 * backgroundColor: string, 13629 * color: string, 13630 * content: string, 13631 * status: 1|2|3 13632 * }} tooltip 13633 */ 13634 13635 return { 13636 /** 13637 * @param {AmlabelComponent} label 13638 * @return {void} 13639 */ 13640 process: function(label) { 13641 this.setLabelSize(label); 13642 this.setLinkProps(label); 13643 }, 13644 13645 /** 13646 * @param {AmlabelComponent} label 13647 * @return {void} 13648 */ 13649 setLabelSize: function (label) { 13650 if (!label.config.size) { 13651 return; 13652 } 13653 13654 label.labelContainer.style.width = label.parent.offsetWidth * label.config.size / 100 + 'px'; 13655 }, 13656 13657 /** 13658 * 13659 * @param {Object} $event 13660 * @param {HTMLElement} tooltipNode 13661 */ 13662 placeTooltip: function() { 13663 const labelNode = this.$event.target; 13664 const tooltipNode = document.getElementById(`label-tooltip-${this.$el.dataset.labelId}-${this.$el.dataset.productId}`); 13665 const labelNodeRect = labelNode.getBoundingClientRect(); 13666 13667 tooltipNode.style.bottom = (window.innerHeight - labelNodeRect.top) + 'px'; 13668 tooltipNode.style.left = (labelNodeRect.left + labelNode.offsetWidth / 2 ) + 'px'; 13669 }, 13670 13671 /** 13672 * @param {AmlabelComponent} label 13673 * @return {void} 13674 */ 13675 setLinkProps: function (label) { 13676 !!label.config.redirect_url && label.labelContainer.classList.add('-link', 'cursor-pointer'); 13677 }, 13678 13679 /** 13680 * @param {Object} $event Click event 13681 * @param {string} redirectUrl 13682 */ 13683 openLink: function($event, redirectUrl) { 13684 if (redirectUrl.length === 0) { 13685 return; 13686 } 13687 13688 $event.preventDefault(); 13689 window.open(redirectUrl, '_blank'); 13690 } 13691 } 13692 } 13693</script>
13693 13694
13695<script> 13696 (function () { 13697 'use strict'; 13698 13699 const observedEvent = 'amlabel-swatch-product-changed'; 13700 const selectors = { 13701 swatchOptions: 'div[x-data^=initConfigurable][x-data*=Options]', 13702 addToCartFormOnListing: 'form.product_addtocart_form', 13703 galleryImage: '#gallery img:not(.amasty-label-image)', 13704 labelWrapper: '.amlabel-position-wrapper' 13705 } 13706 13707 /** 13708 * @return {void} 13709 */ 13710 const injectLabelObserver = function () { 13711 const swatches = document.querySelectorAll(selectors.swatchOptions); 13712 13713 swatches.forEach(swatch => { 13714 const isListing = !!swatch.closest(selectors.addToCartFormOnListing); 13715 const injectedDispatch = `$dispatch('${observedEvent}', ` 13716 + `{productIndex: productIndex, element: $el, optionConfig: optionConfig, isListing: ${isListing ? 1 : 0}})`; 13717 13718 let mutatedInitAttribute = swatch.getAttribute('x-init'); 13719 mutatedInitAttribute += `; $watch('productIndex', (productIndex) => { ${injectedDispatch} });`; 13720 swatch.setAttribute('x-init', mutatedInitAttribute); 13721 }); 13722 } 13723 13724 /** 13725 * @param {{ 13726 * productIndex: number, 13727 * element: HTMLElement, 13728 * optionConfig: Object, 13729 * isListing: 0|1 13730 * }} payload 13731 * @return {void} 13732 */ 13733 const updateLabel = function ({detail: payload}) { 13734 const {productIndex, element, optionConfig, isListing} = payload; 13735 13736 if (!productIndex) { 13737 return; 13738 } 13739 13740 const reloadUrl = optionConfig.label_reload; 13741 const headers = { 13742 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 13743 }; 13744 const body = new URLSearchParams({ 13745 product_ids: productIndex, 13746 in_product_list: isListing 13747 }); 13748 const labelContainerSelector = isListing ? optionConfig.label_category : optionConfig.label_product; 13749 13750 fetch(`${reloadUrl}?${body.toString()}`, {headers: headers, method: 'GET'}).then((response) => { 13751 if (response.ok) { 13752 return response.json(); 13753 } 13754 }).then((result) => { 13755 const labelHtml = result.labels && result.labels[productIndex]; 13756 let labelContainer = findClosest(element, labelContainerSelector); 13757 13758 // Try to find default gallery container on product page if none of the config selectors found 13759 if (labelContainer === null && isListing === 0) { 13760 labelContainer = document.querySelector(selectors.galleryImage)?.parentElement; 13761 } 13762 13763 if (!!labelContainer) { 13764 !!labelHtml ? replaceLabels(labelContainer, labelHtml) : clearLabelWrapper(labelContainer); 13765 } 13766 }).catch((error) => { 13767 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 13768 [{ 13769 type: 'error', 13770 text: error 13771 }], 13772 5000 13773 ); 13774 }); 13775 } 13776 13777 /** 13778 * @param {HTMLElement} labelContainer 13779 * @param {string} labelHtml 13780 * @return {void} 13781 */ 13782 function replaceLabels(labelContainer, labelHtml) { 13783 const parser = new DOMParser(); 13784 const doc = parser.parseFromString(labelHtml, 'text/html'); 13785 const nodes = doc.querySelectorAll(selectors.labelWrapper); 13786 13787 if (!nodes) { 13788 return; 13789 } 13790 13791 clearLabelWrapper(labelContainer); 13792 labelContainer.append(...nodes); 13793 } 13794 13795 /** 13796 * @param {HTMLElement} labelContainer 13797 * @return {void} 13798 */ 13799 function clearLabelWrapper(labelContainer) { 13800 labelContainer.querySelectorAll(selectors.labelWrapper).forEach(wrapper => wrapper.remove()); 13801 } 13802 13803 /** 13804 * @param {HTMLElement} currentElement 13805 * @param {string} selector 13806 * @return {HTMLElement|null} 13807 */ 13808 function findClosest(currentElement, selector) { 13809 // We need to limit parent searches to prevent labels from being associated with the wrong product. 13810 const maxDepth = 3; 13811 let counter = 0; 13812 let closest = null; 13813 13814 while (currentElement.parentNode && closest === null && counter < maxDepth) { 13815 ++counter; 13816 currentElement = currentElement.parentNode; 13817 closest = currentElement.querySelector(selector); 13818 } 13819 13820 return closest; 13821 } 13822 13823 injectLabelObserver(); 13824 window.addEventListener(observedEvent, updateLabel); 13825 })(); 13826</script>
vendor: 1 bytes, line 13826
13826
13827<script> 13828 'use strict'; 13829 13830 (function () { 13831 window.amLabels = { 13832 productLabelsUrl: 'amasty_label/ajax/label', 13833 /** 13834 * @param {Array} productIds 13835 * @return {Promise<any>} 13836 */ 13837 getLabelsHtml: function (productIds) { 13838 const headers = { 13839 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 13840 }; 13841 const body = new URLSearchParams({ 13842 product_ids: productIds, 13843 in_product_list: true 13844 }); 13845 13846 return fetch(BASE_URL + this.productLabelsUrl + '?' + body.toString(), { 13847 headers: headers, 13848 method: 'GET' 13849 }).then((response) => { 13850 if (response.ok) { 13851 return response.json(); 13852 } 13853 }).then((result) => { 13854 return Object.values(result.labels); 13855 }).catch((error) => { 13856 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 13857 [{ 13858 type: 'error', 13859 text: error 13860 }], 13861 5000 13862 ); 13863 }); 13864 } 13865 }; 13866 })(); 13867</script>
vendor: 1 bytes, line 13867
13867
13868<script> 13869 'use strict'; 13870 13871 (function () { 13872 window.initAmRecentlyViewedLabels = window.initAmRecentlyViewedLabels || { 13873 selectors: { 13874 recentlyViewedContainer: '[x-data^=initRecentlyViewedProductsComponent]', 13875 productPhoto: '.product-item-photo' 13876 }, 13877 recentlyViewedProductsKey: 'recently_viewed_products', 13878 13879 /** 13880 * @return number[] 13881 */ 13882 getProductIds: function () { 13883 const startIndex = '' === '' ? 0 : 1; 13884 13885 return JSON.parse(hyva.getBrowserStorage().getItem(this.recentlyViewedProductsKey) ?? '[]') 13886 .slice(startIndex) 13887 .map((item) => item.id); 13888 }, 13889 13890 /** 13891 * @return {void} 13892 */ 13893 mutationObserver: function () { 13894 const recentlyViewedContainers = document.querySelectorAll(this.selectors.recentlyViewedContainer); 13895 13896 recentlyViewedContainers.forEach(recentlyViewedContainer => { 13897 const mutationObserver = new MutationObserver((mutationsList, observer) => { 13898 for(let mutation of mutationsList) { 13899 if (mutation.type === 'childList') { 13900 this.addLabels(recentlyViewedContainer, this.getProductIds()); 13901 observer.disconnect(); 13902 } 13903 } 13904 }); 13905 13906 mutationObserver.observe(recentlyViewedContainer, {childList: true}); 13907 }) 13908 }, 13909 13910 /** 13911 * @param {HTMLElement} element 13912 * @param {Array} productIds 13913 * @return {void} 13914 */ 13915 addLabels: function (element, productIds) { 13916 if (!productIds.length) { 13917 return; 13918 } 13919 13920 const productPhotos = element.querySelectorAll(this.selectors.productPhoto); 13921 13922 window.amLabels.getLabelsHtml(productIds).then( 13923 (result) => { 13924 result.length && productPhotos?.forEach((productPhoto, index) => { 13925 !!result[index] && this.appendLabel({ 13926 productPhoto: productPhoto, 13927 labelHtml: result[index], 13928 productId: productIds[index] 13929 }); 13930 }) 13931 } 13932 ); 13933 }, 13934 13935 /** 13936 * @param {{ 13937 * productContainer: Element, 13938 * labelHtml: string, 13939 * productId: number, 13940 * }} labelConfig 13941 * @return {void} 13942 */ 13943 appendLabel: function (labelConfig) { 13944 const {productPhoto, labelHtml, productId} = labelConfig; 13945 const labelClass = 'am-label-product-id-' + productId; 13946 13947 productPhoto.innerHTML += labelHtml; 13948 productPhoto.classList.add(labelClass); 13949 this.overrideParentLabelStyle(productPhoto); 13950 hyva.replaceDomElement( 13951 '.' + labelClass, 13952 productPhoto.outerHTML 13953 ); 13954 }, 13955 13956 /** 13957 * @param {HTMLElement} productPhoto 13958 * @return {void} 13959 */ 13960 overrideParentLabelStyle: function (productPhoto) { 13961 productPhoto.classList.add('!w-auto'); 13962 } 13963 }; 13964 13965 window.initAmRecentlyViewedLabels.mutationObserver(); 13966 })(); 13967</script>
vendor: 1 bytes, line 13967
13967
13968<script> 13969 (function () { 13970 'use strict'; 13971 13972 const selectors = { 13973 body: '.catalog-product-view', 13974 productItem: 'product-item', 13975 productGalleryImage: '#gallery img' 13976 } 13977 13978 window.addEventListener('alpine:init', () => { 13979 Alpine.directive('amintersect', (el, { expression }, { evaluate, cleanup }) => { 13980 const observer = new IntersectionObserver(entries => { 13981 entries.forEach(entry => { 13982 if (entry.isIntersecting) { 13983 evaluate(expression); 13984 observer.disconnect(); 13985 } 13986 }) 13987 }, { threshold: 0.5 }); 13988 13989 observer.observe(getElement()); 13990 13991 cleanup(() => { 13992 observer.disconnect() 13993 }); 13994 13995 function getElement() { 13996 return !!document.querySelector(selectors.body) ? getImageProductBlock(el) : getCategoryProductBlock(el); 13997 } 13998 13999 function getCategoryProductBlock(el) { 14000 if (el.previousElementSibling === null) { 14001 return el; 14002 } 14003 14004 if (!el.previousElementSibling.classList.contains(selectors.productItem)) { 14005 return getCategoryProductBlock(el.previousElementSibling); 14006 } 14007 14008 return el.previousElementSibling; 14009 } 14010 14011 function getImageProductBlock(el) { 14012 return el.parentNode.querySelector(selectors.productGalleryImage)?.parentNode ?? el.parentNode; 14013 } 14014 }); 14015 }); 14016 })(); 14017</script>
vendor: 1 bytes, line 14017
14017
14018<script> 14019 'use strict'; 14020 14021 function initAmlabel() { 14022 return { 14023 element: null, 14024 parent: null, 14025 initialized: false, 14026 wrapperClass: '', 14027 labelContainer: null, 14028 config: '', 14029 parentContainerProcessor: initParentContainerProcessor(), 14030 labelItemProcessor: initLabelItemProcessor(), 14031 14032 /** 14033 * @return {void} 14034 */ 14035 init() { 14036 this.config = JSON.parse(this.$el.dataset.amlabelConfig); 14037 14038 if (this.hasGliderParent(this.$el)) { 14039 this.$el.removeAttribute('x-amintersect'); 14040 } 14041 }, 14042 14043 /** 14044 * @return {void} 14045 */ 14046 initLabel: function () { 14047 this.element = this.$el; 14048 this.labelContainer = this.$refs.amLabel; 14049 this.parent = this.$el.parentElement; 14050 this.wrapperClass = `.amlabel-position-${this.config.position}-${this.config.product}-${this.config.mode}`; 14051 if (this.hasGliderParent(this.$el)) { 14052 this.render(this.$el); 14053 } 14054 }, 14055 14056 /** 14057 * @return {void} 14058 */ 14059 render: function (el) { 14060 if (el) { 14061 this.element = el; 14062 } 14063 14064 if (this.element.dataset.amlabelObserved) { 14065 return; 14066 } 14067 14068 this.element.dataset.amlabelObserved = true; 14069 this.parentContainerProcessor.process(this); 14070 this.labelItemProcessor.process(this); 14071 this.initialized = true; 14072 }, 14073 14074 /** 14075 * @return {void} 14076 */ 14077 resize: function () { 14078 this.config.size && this.labelItemProcessor.setLabelSize(this); 14079 }, 14080 14081 /** 14082 * @return {Object} 14083 */ 14084 labelClasses() { 14085 return { 14086 'top-0': this.config.position.includes('top'), 14087 'top-0 bottom-0': this.config.position.includes('middle'), 14088 'bottom-0': this.config.position.includes('bottom'), 14089 'left-0': this.config.position.includes('left'), 14090 'left-0 right-0': this.config.position.includes('center'), 14091 'right-0': this.config.position.includes('right') 14092 } 14093 }, 14094 14095 /** 14096 * @return {void} 14097 */ 14098 labelClick: function () { 14099 this.labelItemProcessor.openLink(this.$event, this.config.redirect_url); 14100 }, 14101 14102 /** 14103 * @param {Object} el 14104 * @return {boolean} 14105 */ 14106 hasGliderParent: function (el) { 14107 return !!el.closest('[data-role="glider-content"]'); 14108 } 14109 } 14110 } 14111 (() => { 14112 const initFn = () => Alpine.data('initAmlabel', initAmlabel); 14113 window.Alpine ? initFn() : window.addEventListener('alpine:init', initFn, {once: true}) 14114 })() 14115</script>
vendor: 1 bytes, line 14115
14115
14116<script> 14117 'use strict'; 14118 14119 let isAmlabelGliderLoad = false; 14120 14121 window.addEventListener('glider-loaded', (e) => { 14122 if (isAmlabelGliderLoad) { 14123 return; 14124 } 14125 14126 let gliderPrototype = Glider.prototype; 14127 let originalInit = gliderPrototype.init; 14128 let originalRemoveItem = gliderPrototype.removeItem; 14129 let originalAddItem = gliderPrototype.addItem; 14130 let originalUpdateControls = gliderPrototype.updateControls; 14131 14132 document.querySelectorAll('.glider-track .amlabel-position-wrapper').forEach((el) => { 14133 el.classList.remove('glider-slide'); 14134 el.removeAttribute('style'); 14135 }) 14136 14137 gliderPrototype.init = function (refresh, paging) { 14138 let _ = this; 14139 let track = this.track; 14140 14141 _.track = { 14142 children: Array.from(_.track.children).filter( 14143 (el) => { return !el.classList.contains('amlabel-position-wrapper') } 14144 ), 14145 style: { width: '' } 14146 } 14147 originalInit.call(_, refresh, paging); 14148 track.style.width = _.track.style.width; 14149 }; 14150 14151 gliderPrototype.removeItem = function (index) { 14152 let _ = this; 14153 14154 _.track = _.ele.querySelector('.glider-track'); 14155 originalRemoveItem.call(_, index); 14156 } 14157 14158 gliderPrototype.addItem = function (ele) { 14159 let _ = this; 14160 14161 _.track = _.ele.querySelector('.glider-track'); 14162 originalAddItem.call(_, ele); 14163 } 14164 14165 gliderPrototype.updateControls = function (event) { 14166 let _ = this; 14167 14168 _.slides = Array.from(_.slides).filter( 14169 (el) => { return !el.classList.contains('amlabel-position-wrapper') } 14170 ); 14171 originalUpdateControls.call(_, event); 14172 } 14173 14174 isAmlabelGliderLoad = true; 14175 }) 14176 14177 window.addEventListener('glider-loaded', (e) => { 14178 Glider(e.srcElement).refresh(true); 14179 }) 14180 14181</script>
14181 14182
14183<script> 14184 'use strict'; 14185 14186 (() => { 14187 const browserStorage = hyva.getBrowserStorage(); 14188 const storageKey = 'mage-banners-cache-storage'; 14189 const cacheTimeoutKey = 'mage-banners-cache-timeout'; 14190 const storeIdKey = 'mage-banners-storeId'; 14191 const cartIdKey = 'mage-banners-cartDataId'; 14192 const options = { 14193 cacheTtl: 30000, 14194 sectionLoadUrl: 'https://www.huskee.co/banner/ajax/load/' 14195 }; 14196 14197 if (browserStorage && browserStorage.getItem(storageKey) === null) { 14198 browserStorage.setItem(storageKey, "{}"); 14199 } 14200 14201 async function initBanners(elements) { 14202 invalidateCacheBySessionTimeOut(); 14203 14204 const storageData = JSON.parse(browserStorage.getItem(storageKey)); 14205 let items = []; 14206 14207 if (storageData.data) { 14208 items = getFromStorage(); 14209 } else { 14210 items = await getFromServer(); 14211 } 14212 14213 populateDynamicBlocks(elements, items); 14214 } 14215 14216 // Cache invalidation. Banner cache ttl is 30 sec by default.
14217 function invalidateCacheBySessionTimeOut() { 14218 const cacheEol = new Date(browserStorage.getItem(cacheTimeoutKey)) 14219 const dateTo = new Date(Date.now() + options.cacheTtl); 14220 const globalStoreId = hyva.getCookie('store') || 'default'; 14221 const mageCacheStorage = JSON.parse(browserStorage.getItem('mage-cache-storage')); 14222 let cartDataId = null; 14223 14224 if (browserStorage.getItem(storeIdKey) === null) { 14225 browserStorage.setItem(storeIdKey, globalStoreId); 14226 } 14227 14228 if (mageCacheStorage !== null && 14229 mageCacheStorage.hasOwnProperty('cart')) { 14230 cartDataId = mageCacheStorage.cart['data_id']; 14231 } 14232 14233 if (cacheEol < new Date() || 14234 browserStorage.getItem(storeIdKey) !== globalStoreId || 14235 Number(browserStorage.getItem(cartIdKey)) !== cartDataId) 14236 { 14237 browserStorage.setItem(storageKey, "{}"); 14238 browserStorage.setItem(cacheTimeoutKey, dateTo); 14239 browserStorage.setItem(storeIdKey, globalStoreId); 14240 14241 if (cartDataId) { 14242 browserStorage.setItem(cartIdKey, cartDataId); 14243 } 14244 } 14245 } 14246 14247 async function getFromServer() { 14248 try { 14249 const url = new URL(options.sectionLoadUrl); 14250 url.searchParams.append('requesting_page_url', url.origin); 14251 url.searchParams.append('_', Date.now()); 14252 14253 const response = await fetch(url.href, { 14254 headers: { 14255 'Content-Type': 'application/json', 14256 'X-Requested-With': 'XMLHttpRequest' 14257 } 14258 }); 14259 const storageData = await response.json(); 14260 14261 browserStorage.setItem(storageKey, JSON.stringify(storageData)); 14262 14263 if (storageData.data && storageData.data.items) { 14264 return storageData.data.items.fixed ? storageData.data.items.fixed : []; 14265 } 14266 } catch (error) { 14267 console.warn(error); 14268 14269 return []; 14270 } 14271 } 14272 14273 function getFromStorage() { 14274 const storageData = JSON.parse(browserStorage.getItem(storageKey)); 14275 if (storageData.data && storageData.data.items) { 14276 return storageData.data.items.fixed || []; 14277 } 14278 } 14279 14280 function populateDynamicBlocks(elements, items) { 14281 if (!items || items.length === 0) { 14282 console.log(`No\u0020banners\u0020found`); 14283 14284 return; 14285 } 14286 14287 for (const element of elements) { 14288 // Filter IDs by those allowed for current customer segment 14289 const ids = element.getAttribute('data-ids').split(',').filter((id) => items.hasOwnProperty(id)); 14290 const config = { 14291 type: element.getAttribute('data-rotate'), 14292 dynamicBlockId: element.getAttribute('data-banner-id'), 14293 }; 14294 14295 getByType(config, element, items, ids); 14296 } 14297 } 14298 14299 function getByType(config = null, element, items, ids) { 14300 if (!config) return; 14301 14302 if (config.type === 'random') { 14303 ids = [ids[Math.floor(Math.random() * ids.length)]]; 14304 } 14305 14306 if (config.type === 'series' || config.type === 'shuffle') { 14307 ids = [getNextInOrder(config, ids)]; 14308 } 14309 14310 for (const id of ids) { 14311 if (items[id]) { 14312 element.insertAdjacentHTML('beforeend', items[id].content); 14313 } 14314 } 14315 } 14316 14317 function getNextInOrder(config, ids) { 14318 const dynamicBlockId = config.dynamicBlockId; 14319 const localStorageKey = 'dynamic-block-banner-rotator'; 14320 let localStorageData = JSON.parse(localStorage.getItem(localStorageKey)) || {}; 14321 let nextId; 14322 14323 if (localStorageData[dynamicBlockId] && localStorageData[dynamicBlockId].length > 0) { 14324 nextId = localStorageData[dynamicBlockId].shift(0); 14325 } else { 14326 if (config.type === 'shuffle') { 14327 ids.sort(() => Math.random() - 0.5); 14328 } 14329 nextId = ids.shift(0); 14330 localStorageData[dynamicBlockId] = ids; 14331 } 14332 14333 localStorage.setItem( 14334 localStorageKey, 14335 JSON.stringify(localStorageData) 14336 ); 14337 14338 return nextId; 14339 } 14340 14341 const elements = document.querySelectorAll('[data-banner-id][data-ids]'); 14342 if (elements.length > 0) { 14343 initBanners(elements); 14344 } 14345 })(); 14346</script>
vendor: 1 bytes, line 14346
14346
14347<script> 14348 'use strict'; 14349 14350 function initAmInitConfigurableSwatchOptions() { 14351 const productId = this.$el.dataset.productId ?? this.$el.closest('form')?.querySelector('input[name="product"]').value; 14352 const component = window['initConfigurableSwatchOptions'] 14353 ?? window['initConfigurableSwatchOptions_' + productId] 14354 ?? window['initListingConfigurableSwatchOptions']; 14355 if (typeof component !== 'function') { 14356 // Underlying swatch renderer component is unavailable for this 14357 // product; stay inert so one card cannot break the others. 14358 return {}; 14359 } 14360 const originInitConfigurableSwatchOptions = component.call(this); 14361 const wrappedInit = typeof originInitConfigurableSwatchOptions.init === 'function' 14362 ? originInitConfigurableSwatchOptions.init 14363 : null; 14364 14365 return { 14366 ...originInitConfigurableSwatchOptions, 14367 init() { 14368 // Preserve the wrapped card component's own initialisation 14369 // (allowed-option calculation, show-on-intersect reveal, 14370 // query/hash preselect, gallery wiring, ...). 14371 if (wrappedInit) { 14372 wrappedInit.call(this); 14373 } else { 14374 this.findAllowedAttributeOptions(); 14375 } 14376 14377 this.$nextTick(() => { 14378 this.amApplyActiveFilterSelection(); 14379 }); 14380 }, 14381 14382 /** 14383 * Pre-select the swatch(es) matching the active layered-navigation 14384 * filters for THIS product. 14385 */ 14386 amApplyActiveFilterSelection() { 14387 const activeByAttribute = this.amGetActiveFilterValues(); 14388 14389 for (const attributeId of Object.keys(activeByAttribute)) { 14390 if (!this.optionConfig 14391 || !this.optionConfig.attributes 14392 || !this.optionConfig.attributes[attributeId]) { 14393 continue; 14394 } 14395 14396 const allowed = this.getAllowedAttributeOptions(attributeId) || []; 14397 const values = activeByAttribute[attributeId]; 14398 14399 let match; 14400 for (let i = values.length - 1; i >= 0; i--) { 14401 if (allowed.some(option => String(option.id) === String(values[i]))) { 14402 match = values[i]; 14403 break; 14404 } 14405 } 14406 14407 if (match !== undefined) { 14408 this.changeOption(attributeId, match + ''); 14409 } 14410 } 14411 }, 14412 14413 /** 14414 * Collect active filter values grouped by attribute id. 14415 * 14416 * 14417 * @returns {Object.<string, string[]>} 14418 */ 14419 amGetActiveFilterValues() { 14420 const activeByAttribute = {}; 14421 14422 document.querySelectorAll( 14423 '.filter-current .items .amshopby-remove-item[data-attribute-id]' 14424 ).forEach(chip => { 14425 const attributeId = chip.getAttribute('data-attribute-id'); 14426 if (!attributeId) { 14427 return; 14428 } 14429 14430 const values = activeByAttribute[attributeId] 14431 || (activeByAttribute[attributeId] = []); 14432 const massSet = chip.getAttribute('data-massset'); 14433 const swatchSet = chip.getAttribute('data-swatchset'); 14434 14435 if (massSet) { 14436 massSet.split(',').forEach(value => { 14437 value = value.trim(); 14438 if (value && !values.includes(value)) { 14439 values.push(value); 14440 } 14441 }); 14442 } 14443 if (swatchSet && !values.includes(swatchSet)) { 14444 values.push(swatchSet); 14445 } 14446 }); 14447 14448 if (this.optionConfig 14449 && typeof this.optionConfig.defaultValues === 'object' 14450 && this.optionConfig.defaultValues !== null) { 14451 for (const [attributeId, value] of Object.entries(this.optionConfig.defaultValues)) { 14452 const values = activeByAttribute[attributeId] 14453 || (activeByAttribute[attributeId] = []); 14454 const normalized = value + ''; 14455 if (!values.includes(normalized)) { 14456 values.push(normalized); 14457 } 14458 } 14459 } 14460 14461 return activeByAttribute; 14462 }, 14463 14464 dispatchConfigurableShowEvent() { 14465 window.dispatchEvent(new CustomEvent('configurable-show-swatches', { 14466 detail: { 14467 productId: this.productId, 14468 element: this.$el 14469 } 14470 })); 14471 } 14472 } 14473 } 14474 14475 window.addEventListener('alpine:init', () => { 14476 Alpine.data('initAmInitConfigurableSwatchOptions', initAmInitConfigurableSwatchOptions); 14477 }, {once: true}); 14478</script>
14478 14479 14480 14481<div x-data="initCookieBarSettings" x-bind="eventListeners"> 14482 <div x-cloak 14483 x-bind="overlay" 14484 class="fixed inset-0 flex flex-col items-center justify-center text-left bg-gray-500/50 14485 z-50 am-cookie-bar-settings" 14486 > 14487 <div x-ref="am-cookie-bar-settings" 14488 role="dialog" 14489 aria-modal="true" 14490 aria-label="Cookie Bar Settings" 14491 class="bg-ambar-settings-bar-group-background relative inline-block max-h-screen overflow-auto 14492 max-w-[90vw] shadow-xl rounded-lg p-5 lg:!p-10 text-gray-700 container bg-white" 14493 > 14494 <button @click="hideHandler" 14495 aria-label="Close Cookie Bar Settings" 14496 x-ref="ambar-settings-close" 14497 class="absolute right-2 top-2 text-gray-300 transition 14498 hover:text-black focus:drop-shadow-md hover:drop-shadow-md"> 14499 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" 14500 stroke="currentColor" 14501 stroke-width="2"> 14502 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 14503 </svg> 14504 </button> 14505 <div id="cookiebar-groups"> 14506 14507<div x-data="initAmCookieGroups" class="w-full flex flex-col"> 14508 <template x-for="group in $store.AmastyCookieGroups.groupData" :key="group.groupId"> 14509 <div class="mb-2"> 14510 <div class="header flex justify-between items-start py-2"> 14511 <h3 class="text-lg font-semibold pr-6 ambar-group-title text-ambar-settings-bar-group-title" x-text="group.name"></h3> 14512 <div> 14513 <input type="checkbox" 14514 :name="getInputGroupId" 14515 data-group-prefix="classic-" 14516 @change="toggleGroup" 14517 :checked="group.checked" 14518 :aria-checked="group.checked" 14519 :id="getInputGroupId" 14520 :disabled="group.isEssential" 14521 :value="group.groupId" 14522 class="amsettings-bar-input-toggle" 14523 :class="getInputStyles" 14524 > 14525 <label data-group-prefix="classic-" 14526 :for="getInputGroupId" class="sr-only" x-text="group.name"> 14527 </label> 14528 </div> 14529 </div> 14530 <p class="py-2 text-sm ambar-group-description text-ambar-settings-bar-group-description" x-html="group.description"></p> 14531 <button class="btn btn-secondary px-4 text-xs mb-4 rounded-xs before:content-none after:content-none" 14532 type="button" 14533 data-dispatch-action="classic-open-am-cookie-group-info" 14534 @click="moreInfoClickHandler"> 14535 <span class="text-ambar-settings-bar-group-links">More Information</span> 14536 <span class="sr-only" x-text="moreInformationAboutCookieGroup"></span> 14537 </button> 14538 </div> 14539 </template> 14540</div> 14541 </div> 14542 <div class="w-full flex"> 14543 <button type="button" 14544 class="ambar-action-btn rounded-sm text-center text-sm transition-colors focus:drop-shadow hover:drop-shadow px-2 py-4 w-full text-xs font-semibold uppercase bg-ambar-settings-bar-button 14545 hover:bg-ambar-settings-bar-button-hover text-ambar-settings-bar-button-text 14546 hover:text-ambar-settings-bar-button-hover-text" 14547 x-data="initAmCookieActionDispatcherBtn" 14548 @click.prevent="actiondispatchHandler" 14549 x-ref="ambar-settings-done" 14550 data-dispatch-action="cookiebar-action-accept" 14551 data-dispatch-payload='{"include_checked_groups":true}' 14552 > 14553 Done </button> 14554 </div> 14555 </div> 14556 </div> 14557</div> 14558
14559<script data-cfasync="false"> 14560 'use strict'; 14561 14562 function initCookieBarSettings() { 14563 return { 14564 ...hyva.modal({ 14565 dialog: 'am-cookie-bar-settings' 14566 }), 14567 eventListeners: { 14568 ['@close-am-cookie-bar-settings.window']() { 14569 this.hideHandler(); 14570 }, 14571 ['@open-am-cookie-bar-settings.window']() { 14572 !this.opened['am-cookie-bar-settings'] && 14573 this.show('am-cookie-bar-settings'); 14574 }, 14575 ['@cookiebar-action-accept-finish.window']() { 14576 this.hideHandler(); 14577 } 14578 }, 14579 hideHandler: function () { 14580 this.opened['am-cookie-bar-settings'] = false; 14581 }, 14582 focusDoneButton: function () { 14583 this.$refs['ambar-settings-done']?.focus(); 14584 }, 14585 focusCloseButton: function () { 14586 this.$refs['ambar-settings-close']?.focus(); 14587 } 14588 } 14589 } 14590 14591 window.addEventListener('alpine:init', () => Alpine.data('initCookieBarSettings', initCookieBarSettings), { once: true }); 14592</script>
14592 14593 14594<div x-data="initCookieGroupInfo_6ab50843b1e45" x-bind="eventListeners"> 14595 <dialog 14596 x-ref="am-cookie-group-info" 14597 data-dialog-ref-name="am-cookie-group-info" 14598 aria-labelledby="amcookie-group-info" 14599 closeby="any" 14600 class="open:flex flex-col max-h-[92vh] max-w-[90vw] overflow-auto 14601 bg-ambar-info-bar-background shadow-xl rounded-lg p-8 text-gray-700 relative backdrop:bg-gray-500/50" 14602 x-bind="dialogConfig" 14603 x-htmldialog="closeHandler" 14604 > 14605 <template x-if="group"> 14606 <div id="amcookie-group-info"> 14607 <button @click.stop="closeHandler" 14608 aria-label="Close" 14609 class="absolute right-2 top-2 text-gray-300 transition 14610 hover:text-black focus:drop-shadow-md hover:drop-shadow-md" 14611 > 14612 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" 14613 stroke="currentColor" 14614 stroke-width="2"> 14615 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 14616 </svg> 14617 </button> 14618 <div class="md:flex md:justify-start pb-4"> 14619 <h3 class="text-xl font-semibold text-ambar-info-bar-title" x-text="group.name"></h3> 14620 </div> 14621 <template x-if="group.description"> 14622 <p class="mb-4 text-ambar-info-bar-description" x-html="group.description"></p> 14623 </template> 14624 <div class="overflow-x-scroll md:overflow-x-auto"> 14625 <table class="table-auto w-full mb-4 text-left"> 14626 <thead class="text-ambar-info-bar-table-header"> 14627 <tr> 14628 <th class="pr-4">Cookie Name</th> 14629 <th class="pr-4">Cookie Provider</th> 14630 <th class="min-w-[30%] pr-4">Cookie Description</th> 14631 <th class="pr-4">Cookie Lifetime</th> 14632 <th class="pr-4">Cookie Type</th> 14633 </tr> 14634 </thead> 14635 <tbody class="text-ambar-info-bar-table-content"> 14636 <template x-for="(item, index) in group.cookies" :key="index"> 14637 <tr> 14638 <td x-text="item.name" class="font-semibold pr-4"></td> 14639 <td x-text="item.provider" class="pr-4"></td> 14640 <td x-html="item.description" class="min-w-[30%] pr-4"></td> 14641 <td x-text="item.lifetime" class="pr-4"></td> 14642 <td x-text="item.type" class="pr-4"></td> 14643 </tr> 14644 </template> 14645 </tbody> 14646 </table> 14647 </div> 14648 </div> 14649 </template> 14650 <button 14651 @click.stop="closeHandler" 14652 aria-label="Done" 14653 class="mt-4 w-full text-xs font-semibold text-ambar-info-bar-button-text 14654 hover:text-ambar-info-bar-button-hover-text uppercase bg-ambar-info-bar-button 14655 hover:bg-ambar-info-bar-button-hover ambar-action-btn rounded-sm text-center text-sm transition-colors focus:drop-shadow hover:drop-shadow px-2 py-4" 14656 > 14657 Done </button> 14658 </dialog> 14659 14660</div>
14661<script data-cfasync="false"> 14662 'use strict'; 14663 14664 function initCookieGroupInfo_6ab50843b1e45() { 14665 return { 14666 ...hyva.modal({ 14667 dialog: 'am-cookie-group-info' 14668 }), 14669 group: null, 14670 eventListeners: { 14671 ['@close-am-cookie-group-info.window']() { 14672 this.closeHandler(); 14673 }, 14674 ['@open-am-cookie-group-info.window'](event) { 14675 this.group = event.detail; 14676 14677 if (!!this.group) { 14678 /* 14679 If modal have big cookies list this can lead to 14680 trigger hide event in hyva.modal.eventListeners.click, 14681 so we should wait till this.group is reassigned 14682 */ 14683 this.$nextTick(() => this.show('am-cookie-group-info')); 14684 } 14685 } 14686 }, 14687 closeHandler: function () { 14688 this.hide('am-cookie-group-info'); 14689 } 14690 } 14691 } 14692 14693 window.addEventListener( 14694 'alpine:init', 14695 () => Alpine.data( 14696 'initCookieGroupInfo_6ab50843b1e45', 14697 initCookieGroupInfo_6ab50843b1e45 ), 14698 {once: true} 14699 ); 14700</script>
14700 14701
14702<script data-cfasync="false"> 14703 'use strict'; 14704 14705 /** 14706 * @typedef {Object} CookieGroupData 14707 * @prop {Boolean} checked 14708 * @prop {[{description: string, lifetime: number|null, name: string, provider: string, type: string}]} cookies 14709 * @prop {String} description 14710 * @prop {string|null} groupId 14711 * @prop {Boolean} isEssential 14712 * @prop {String} name 14713 */ 14714 14715 function initAmastyCookieGroupsStorage() { 14716 return { 14717 /** 14718 * @type {[CookieGroupData]} 14719 */ 14720 groupData: [], 14721 cookiePolicy: undefined, 14722 lastUpdate: undefined, 14723 14724 /** 14725 * @returns {string|number[]} 14726 */ 14727 getEssentialGroupIds: function () { 14728 return this.groupData.reduce(function (essentialGroupIds, group) { 14729 if (group.isEssential) { 14730 essentialGroupIds.push(group.groupId) 14731 } 14732 14733 return essentialGroupIds; 14734 }, []); 14735 }, 14736 14737 /** 14738 * @returns {string|number[]} 14739 */ 14740 getCheckedGroupIds: function () { 14741 return this.groupData.reduce(function (checkedGroupIds, group) { 14742 if (group.checked) { 14743 checkedGroupIds.push(group.groupId); 14744 } 14745 14746 return checkedGroupIds; 14747 }, []); 14748 }, 14749 14750 /** 14751 * @param {string} cookieName 14752 * @returns {boolean} 14753 */ 14754 isCookieEssential: function (cookieName) { 14755 const allEssentialCookies = this.getEssentialCookies(); 14756 14757 return !!allEssentialCookies.find(cookie => cookie.name === cookieName); 14758 }, 14759 14760 /** 14761 * @returns {array} 14762 */ 14763 getEssentialCookies: function () { 14764 return this.groupData 14765 .filter(group => group.isEssential) 14766 .map(group => group.cookies) 14767 .flat(); 14768 }, 14769 14770 /** 14771 * @returns {boolean} 14772 */ 14773 hasGroupData: function () { 14774 return !!this.groupData.length; 14775 }, 14776 14777 /** 14778 * @param {string} cookieName 14779 * @param {string} pattern 14780 * @returns {boolean} 14781 */ 14782 isEssentialByPattern: function (cookieName, pattern) { 14783 const essentialCookiePatterns = this.getEssentialCookiesPattern(pattern); 14784 14785 return essentialCookiePatterns.some((pattern) => !!cookieName.match(this.getCookiePattern(pattern))); 14786 }, 14787 14788 /** 14789 * @param {string} pattern 14790 * @return {Array} 14791 */ 14792 getEssentialCookiesPattern: function (pattern) { 14793 return this.getCookiesByPattern(pattern, this.getEssentialCookies()); 14794 }, 14795 14796 /** 14797 * @param {string} pattern 14798 * @param {Array|undefined} cookies 14799 * @return {Array} 14800 */ 14801 getCookiesByPattern: function (pattern, cookies) { 14802 if (typeof cookie === 'undefined') { 14803 cookies = this.getAllCookies(); 14804 } 14805 14806 return cookies
14807 .map((cookie) => typeof cookie === 'object' ? cookie.name : cookie.split('=')[0].trim()) 14808 .filter((cookie) => cookie.match(pattern) !== null); 14809 }, 14810 14811 /** 14812 * @returns {array} 14813 */ 14814 getDisallowedCookies: function () { 14815 const disallowedCookie = decodeURIComponent(hyva.getCookie('amcookie_disallowed') ?? ''); 14816 if (!disallowedCookie) { 14817 return []; 14818 } 14819 14820 return disallowedCookie.split(','); 14821 }, 14822 14823 /** 14824 * @returns {string} 14825 */ 14826 getCookiePattern: function (pattern) { 14827 return `^${pattern.replaceAll('*', '.*')}$`; 14828 }, 14829 14830 /** 14831 * @returns {array} 14832 */ 14833 getAllCookies: function () { 14834 return document.cookie.split(';'); 14835 }, 14836 14837 /** 14838 * @param {Array} groupData 14839 * @returns {array} 14840 */ 14841 updateGroupDataPermanently: function (groupData) { 14842 this.groupData = [...groupData]; 14843 }, 14844 14845 /** 14846 * @param {boolean} setAcceptance 14847 * @returns {Promise} 14848 */ 14849 updateGroupData: function (setAcceptance = false) { 14850 const cookieFetchUrl = `${BASE_URL}amcookie/cookie/cookies`; 14851 const requestUrl = new URL(cookieFetchUrl, window.location.origin); 14852 14853 requestUrl.searchParams.append('allowed', hyva.getCookie('amcookie_allowed')); 14854 requestUrl.searchParams.append('restriction', hyva.getCookie('amcookie_policy_restriction')); 14855 14856 return fetch(requestUrl.toString(), { 14857 method: "GET", 14858 headers: { 14859 'X-Requested-With': 'XMLHttpRequest' 14860 } 14861 }).then(response => { 14862 if (response.ok) { 14863 return response.json(); 14864 } 14865 14866 return Promise.reject(`${response.status} ${response.statusText}`); 14867 }).then(data => { 14868 this.cookiePolicy = data.cookiePolicy ?? 'allowed'; 14869 this.lastUpdate = data.lastUpdate ?? 0; 14870 this.groupData = [...data.groupData]; 14871 14872 hyva.setCookie('amcookie_policy_restriction', this.cookiePolicy, 10); 14873 if (setAcceptance) { 14874 amastyCookieManager.setLastCookieAcceptance(this.lastUpdate); 14875 } 14876 14877 return data; 14878 }).catch(error => { 14879 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 14880 { 14881 type: "error", 14882 text: error 14883 } 14884 ]); 14885 }).finally(() => window.dispatchEvent(new CustomEvent('amasty-cookie-group-updated'))); 14886 } 14887 } 14888 } 14889 14890 window.addEventListener( 14891 'alpine:init', 14892 () => Alpine.store('AmastyCookieGroups', initAmastyCookieGroupsStorage()), 14893 { once: true } 14894 ); 14895</script>
14895 14896
14897<script data-cfasync="false"> 14898 (function (amastyCookieManager) { 14899 'use strict'; 14900 14901 amastyCookieManager.pattern = '{(?<cookiePattern>.*)}'; 14902 14903 amastyCookieManager.getAmastyCookieGroupStorage = function () { 14904 return Alpine.store('AmastyCookieGroups') ?? {}; 14905 }; 14906 14907 amastyCookieManager.deleteDisallowedCookie = function () { 14908 const amastyCookieGroupStorage = this.getAmastyCookieGroupStorage(); 14909 const disallowedCookies = amastyCookieGroupStorage?.getDisallowedCookies() ?? []; 14910 14911 disallowedCookies.forEach((name) => { 14912 const pattern = name.match(this.pattern); 14913 if (!!pattern && !!amastyCookieGroupStorage) { 14914 const cookiePattern = amastyCookieGroupStorage.getCookiePattern(pattern.groups.cookiePattern) ?? ''; 14915 14916 amastyCookieGroupStorage.getCookiesByPattern(cookiePattern).forEach((cookieName) => { 14917 this.deleteNotEssentialCookie(cookieName); 14918 }); 14919 } else { 14920 this.deleteNotEssentialCookie(name); 14921 } 14922 }); 14923 }; 14924 14925 amastyCookieManager.isCookieAllowed = function (cookieName) { 14926 const allowedGroups = hyva.getCookie('amcookie_allowed'); 14927 const disallowedCookie = this.getAmastyCookieGroupStorage()?.getDisallowedCookies() ?? []; 14928 const isCookiePolicyAllowed = hyva.getCookie('amcookie_policy_restriction') === 'allowed'; 14929 const isCookieEssential = this.getAmastyCookieGroupStorage()?.isCookieEssential(cookieName); 14930 const isCookieEssentialByPattern = this.getAmastyCookieGroupStorage()?.isEssentialByPattern(cookieName, this.pattern); 14931 14932 if (!isCookiePolicyAllowed || isCookieEssential || isCookieEssentialByPattern) { 14933 return true; 14934 } 14935 14936 return !((!allowedGroups && !disallowedCookie) || this.isCookieDisallowed(cookieName)); 14937 }; 14938 14939 amastyCookieManager.isCookieDisallowed = function (cookieName) { 14940 const amastyCookieGroupStorage = this.getAmastyCookieGroupStorage(); 14941 const disallowedCookies = amastyCookieGroupStorage?.getDisallowedCookies() ?? []; 14942 14943 return disallowedCookies.some((cookie) => { 14944 const pattern = cookie.match(this.pattern); 14945 14946 return !!pattern && !!cookieName.match(amastyCookieGroupStorage?.getCookiePattern(pattern.groups.cookiePattern)); 14947 }) || disallowedCookies.indexOf(cookieName) !== -1; 14948 }; 14949 14950 amastyCookieManager.processManageableCookies = function () { 14951 let cookieScripts = document.querySelectorAll('script[data-amcookie-groupid][type="text/plain"]'); 14952 14953 cookieScripts.forEach((elem) => { 14954 if (this.isChecked(elem.dataset['amcookie-groupid'])) { 14955 let elClone = elem.cloneNode(true).setAttribute('type', 'text/javascript'); 14956 14957 elem.remove(); 14958 document.body.appendChild(elClone); 14959 } 14960 }); 14961 }; 14962 14963 amastyCookieManager.deleteNotEssentialCookie = function (cookieName) { 14964 if (!this.getAmastyCookieGroupStorage()?.isCookieEssential(cookieName)) { 14965 hyva.setCookie(cookieName, '', -1, true); 14966 } 14967 }; 14968 14969 amastyCookieManager.setLastCookieAcceptance = function (lastUpdate) { 14970 localStorage.setItem('am-last-cookie-acceptance', lastUpdate); 14971 }; 14972 14973 window.addEventListener('amasty-cookie-group-updated', () => { 14974 amastyCookieManager.deleteDisallowedCookie(); 14975 }); 14976 14977 window.addEventListener('private-content-loaded', () => { 14978 amastyCookieManager.getAmastyCookieGroupStorage()?.hasGroupData() && amastyCookieManager.deleteDisallowedCookie(); 14979 }); 14980 }(window.amastyCookieManager = window.amastyCookieManager || {})); 14981</script>
14981 14982
14983<script data-cfasync="false"> 14984 (function () { 14985 'use strict'; 14986 14987 window.addEventListener('cookiebar-action-accept', (event) => { 14988 const groups = event.detail?.include_checked_groups ? Alpine.store('AmastyCookieGroups').getCheckedGroupIds() : []; 14989 const url = `${BASE_URL}amcookie/cookie/savegroups`; 14990 const urlParams = event.detail?.params ?? {}; 14991 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 14992 const body = new URLSearchParams({ 14993 form_key: hyva.getFormKey() 14994 }); 14995 groups.length && groups.map(groupId => body.append('groups[]', groupId)); 14996 14997 for (const [key, value] of Object.entries(urlParams)) { 14998 body.append(key, value); 14999 } 15000 15001 fetch(url, {method: 'POST', body, headers: {'Content-Type': contentType}}) 15002 .then(response => { 15003 if (response.ok) { 15004 return response.json(); 15005 } 15006 15007 return Promise.reject(`${response.status} ${response.statusText}`); 15008 }) 15009 .then(data => { 15010 !!data.success && Alpine.store('AmastyCookieGroups').updateGroupData(true); 15011 }) 15012 .catch(error => { 15013 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 15014 { 15015 type: "error", 15016 text: error 15017 } 15018 ]); 15019 }) 15020 .finally(() => { 15021 amastyCookieManager.processManageableCookies(); 15022 window.dispatchEvent(new CustomEvent('cookiebar-action-accept-finish')); 15023 window.dispatchEvent(new CustomEvent('amasty-cookie-group-updated')); 15024 // Wait cookiebar close animation 15025 setTimeout(() => { 15026 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 15027 }, 100); 15028 }); 15029 }); 15030 })(); 15031</script>
15031 15032
15033<script data-cfasync="false"> 15034 (function () { 15035 'use strict'; 15036 15037 window.addEventListener('cookiebar-action-allow', () => { 15038 const url = `${BASE_URL}amcookie/cookie/allow`; 15039 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 15040 const body = new URLSearchParams({ 15041 form_key: hyva.getFormKey() 15042 }); 15043 fetch(url, {method: 'POST', body, headers: {'Content-Type': contentType}}) 15044 .then(response => { 15045 if (response.ok) { 15046 return response; 15047 } 15048 15049 return Promise.reject(`${response.status} ${response.statusText}`); 15050 }) 15051 .then(() => { 15052 Alpine.store('AmastyCookieGroups').updateGroupData(true); 15053 }) 15054 .catch(error => { 15055 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 15056 { 15057 type: "error", 15058 text: error 15059 } 15060 ]); 15061 }) 15062 .finally(() => { 15063 amastyCookieManager.processManageableCookies(); 15064 window.dispatchEvent(new CustomEvent('cookiebar-action-allow-finish')); 15065 // Wait cookiebar close animation 15066 setTimeout(() => { 15067 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 15068 }, 100); 15069 }); 15070 }); 15071 })(); 15072</script>
15072 15073
15074<script data-cfasync="false"> 15075 (function () { 15076 'use strict'; 15077 15078 window.addEventListener('cookiebar-action-settings', () => { 15079 window.dispatchEvent(new Event('open-am-cookie-bar-settings')); 15080 }); 15081 })(); 15082</script>
15082 15083 15084<style> 15085 :root { 15086 /* Bar Color Settings */ 15087 --ambar-background: #EEECE7; 15088 --ambar-links-color: #2C6549; 15089 --ambar-policy-text: #000000; 15090 --ambar-toggle-active-color: #2563eb; 15091 --ambar-toggle-inactive-color: #fff; 15092 15093 /* Buttons Color Settings */ 15094 --ambar-button-accept-color: #EC930C; 15095 --ambar-button-accept-hover-color: #FF7900; 15096 --ambar-button-accept-text: #FFFFFF; 15097 --ambar-button-accept-hover-text: #FFFFFF; 15098 15099 --ambar-button-allow-color: #337ab7; 15100 --ambar-button-allow-hover-color: #337ab7; 15101 --ambar-button-allow-text: #fff; 15102 --ambar-button-allow-hover-text: #fff; 15103 15104 --ambar-button-decline-color: #337ab7; 15105 --ambar-button-decline-hover-color: #337ab7; 15106 --ambar-button-decline-text: #fff; 15107 --ambar-button-decline-hover-text: #fff; 15108 15109 --ambar-button-settings-color: #337ab7; 15110 --ambar-button-settings-hover-color: #337ab7; 15111 --ambar-button-settings-text: #fff; 15112 --ambar-button-settings-hover-text: #fff; 15113 15114 /* Buttons Order Settings */ 15115 --ambar-button-accept-order: 0; 15116 --ambar-button-allow-order: 0; 15117 --ambar-button-settings-order: 0; 15118 --ambar-button-decline-order: 0; 15119 15120 /* Cookie Information Bar Color Settings */ 15121 --ambar-information-background: #fff; 15122 --ambar-information-title: #374151; 15123 --ambar-information-description: #374151; 15124 --ambar-information-table-header: #374151; 15125 --ambar-information-table-content: #374151; 15126 15127 /* Cookie Information Bar Done Button Settings */ 15128 --ambar-information-button-color: #1d4ed8; 15129 --ambar-information-button-hover-color: #1d4ed8; 15130 --ambar-information-button-text: #fff; 15131 --ambar-information-button-hover-text: #fff; 15132 15133 /* Settings Bar Done Button Settings */ 15134 --ambar-setting-button-color: #1d4ed8; 15135 --ambar-setting-button-hover-color: #1d4ed8; 15136 --ambar-setting-button-text: #fff; 15137 --ambar-setting-button-hover-text: #fff; 15138 15139 /* cookie_settings_bar_customisation */ 15140 --ambar-setting-bar-group-background: #EFEFEF; 15141 --ambar-setting-bar-group-title: #404040; 15142 --ambar-setting-bar-group-description: #404040; 15143 --ambar-setting-bar-group-links: #045D36; 15144 } 15145</style> 15146
15147<script data-cfasync="false"> 15148 'use strict'; 15149 15150 function initAmCookieActionDispatcherBtn() { 15151 return { 15152 actiondispatchHandler: function (event) { 15153 const { dispatchAction, dispatchPayload } = event.currentTarget.dataset; 15154 this.$dispatch(dispatchAction, this.parsePayload(dispatchPayload)); 15155 }, 15156 parsePayload: function (payload) { 15157 try { 15158 return JSON.parse(payload); 15159 } catch (e) { 15160 return null; 15161 } 15162 } 15163 }; 15164 } 15165 15166 window.addEventListener( 15167 'alpine:init', 15168 () => Alpine.data('initAmCookieActionDispatcherBtn', initAmCookieActionDispatcherBtn), 15169 { once: true } 15170 ); 15171</script>
15171 15172
15173<script data-cfasync="false"> 15174 'use strict'; 15175 15176 function initAmCookieGroups() { 15177 return { 15178 /** 15179 * @withContext x-for: $store.AmastyCookieGroups.groupData 15180 * @returns {string} 15181 */ 15182 getInputGroupId: function () { 15183 return `amcookie-${this.$el.dataset.groupPrefix}group-${this.group.groupId}` 15184 }, 15185 15186 /** 15187 * @withContext x-for: $store.AmastyCookieGroups.groupData 15188 * @returns {{"!bg-gray-400": boolean}} 15189 */ 15190 getInputStyles: function () { 15191 return { 15192 '!bg-gray-400': this.group.isEssential 15193 } 15194 }, 15195 15196 /** 15197 * @withContext x-for: $store.AmastyCookieGroups.groupData 15198 * @returns {void} 15199 */ 15200 toggleGroup: function () { 15201 this.group.checked = !this.group.checked; 15202 }, 15203 15204 /** 15205 * @withContext x-for: $store.AmastyCookieGroups.groupData 15206 * @returns {void} 15207 */ 15208 moreInfoClickHandler: function () { 15209 this.$dispatch(this.$el.dataset.dispatchAction, this.group); 15210 }, 15211 15212 moreInformationAboutCookieGroup: function () { 15213 return hyva.str('About\u0020\u0022\u00251\u0022\u0020Cookie\u0020Group', this.group.name); 15214 } 15215 } 15216 } 15217 15218 window.addEventListener( 15219 'alpine:init', 15220 () => Alpine.data('initAmCookieGroups', initAmCookieGroups), 15221 {once: true} 15222 ); 15223</script>
vendor: 1 bytes, line 15223
15223
15224<script> 15225 if (window.AmFB) { 15226 (() => { 15227 const gdprListeners = [ 15228 { 15229 event: 'cookiebar-action-accept-finish', 15230 callback: () => window.AmFB.initPixels(), 15231 options: { once: true } 15232 }, 15233 { 15234 event: 'cookiebar-action-allow-finish', 15235 callback: () => window.AmFB.initPixels(), 15236 options: { once: true } 15237 } 15238 ]; 15239 15240 const isAllowedToRunScript = function () { 15241 const facebookPixelCookieName = '_fbp'; 15242 const allowedCookies = hyva.getCookie('amcookie_allowed') || ''; 15243 const disallowedCookies = hyva.getCookie('amcookie_disallowed') || ''; 15244 return !!allowedCookies.length 15245 && (!disallowedCookies || disallowedCookies.indexOf(facebookPixelCookieName) === -1); 15246 }; 15247 15248 window.AmFB.setDataToSection = new Proxy(window.AmFB.setDataToSection, { 15249 apply(target, thisArg, argArray) { 15250 if (isAllowedToRunScript()) { 15251 return target.apply(thisArg, argArray); 15252 } 15253 15254 return { 15255 then: () => {} 15256 } 15257 } 15258 }); 15259 15260 window.AmFB.initPixels = new Proxy(window.AmFB.initPixels, { 15261 apply(target, thisArg, argArray) { 15262 if (isAllowedToRunScript()) { 15263 return target.apply(thisArg, argArray); 15264 } 15265 } 15266 }); 15267 15268 window.AmFB = new Proxy(window.AmFB, { 15269 get(target, prop, receiver) { 15270 if (prop === 'listeners') { 15271 return [...Reflect.get(...arguments), ...(gdprListeners)]; 15272 } 15273 15274 return Reflect.get(...arguments); 15275 } 15276 }); 15277 })(); 15278 } 15279</script>
vendor: 1 bytes, line 15279
15279
15280<script> 15281 'use strict'; 15282 15283 (function () { 15284 document.querySelectorAll('[data-amcookie-js="footer-link"]').forEach(link => { 15285 link.addEventListener('click', function (event) { 15286 event.preventDefault(); 15287 window.dispatchEvent(new CustomEvent('cookiebar-action-settings')); 15288 }); 15289 }); 15290 })(); 15291</script>
15291 15292
15293<script> 15294 window.addEventListener('load', function () { 15295 magentoStorefrontEvents.context.setPage({ 15296 pageType: "CMS" 15297 }); 15298 magentoStorefrontEvents.context.setContext("pageExtended", { 15299 action: "view" 15300 }); 15301 }); 15302</script>
15302 15303 15304<div x-data="dataServicesAddToCartTracking" 15305 @private-content-loaded.window="checkIfProductIsAddedToBasket"></div> 15306
15307<script> 15308 window.addEventListener('productAddToCart', function(event) { 15309 const browserStorage = hyva.getBrowserStorage(); 15310 const product = JSON.parse(browserStorage.getItem('dataservices_product_context')); 15311 let shoppingCart = magentoStorefrontEvents.context.getShoppingCart(); 15312 let changedProducts = event.detail; 15313 15314 magentoStorefrontEvents.context.setProduct(product); 15315 15316 if (changedProducts.length > 0) { 15317 magentoStorefrontEvents.context.setShoppingCart(shoppingCart); 15318 magentoStorefrontEvents.context.setChangedProducts({ 15319 items: createCartItems(changedProducts), 15320 }); 15321 magentoStorefrontEvents.publish.addToCart(); 15322 } 15323 15324 function getConfigurableOptions(item) { 15325 return item.options.map((function(item) { 15326 return { 15327 id: item.option_id, 15328 optionLabel: item.label, 15329 valueId: parseInt(item.option_value), 15330 valueLabel: item.value 15331 } 15332 } 15333 )) 15334 } 15335 15336 function getCartItemPrice(cartItem) { 15337 const productPrice = cartItem.product_price_value; 15338 15339 if (productPrice && productPrice.hasOwnProperty('excl_tax')) { 15340 return Number(parseFloat(productPrice.excl_tax).toFixed(2)); 15341 } else { 15342 return Number(parseFloat(productPrice).toFixed(2)); 15343 } 15344 } 15345 15346 function createCartItems(productList) { 15347 if (!Array.isArray(productList)) { 15348 console.error('Expected an array for productList, received:', productList); 15349 return []; 15350 } 15351 15352 return productList.map((item) => ({ 15353 id: item.item_id, 15354 formattedPrice: getCartItemPrice(item), 15355 quantity: item.qty, 15356 canApplyMsrp: item.canApplyMsrp, 15357 configurableOptions: getConfigurableOptions(item), 15358 product: { 15359 productType: item.product_type, 15360 productId: item.product_id, 15361 name: item.product_name, 15362 sku: item.product_sku, 15363 topLevelSku: item.topLevelSku, 15364 mainImageUrl: item.product_image.src, 15365 }, 15366 prices: { 15367 price: { 15368 value: getCartItemPrice(item), 15369 }, 15370 }, 15371 })); 15372 } 15373 15374 }); 15375 15376 function dataServicesAddToCartTracking() { 15377 return { 15378 checkIfProductIsAddedToBasket() { 15379 const data = this.$event.detail.data; 15380 const browserStorage = hyva.getBrowserStorage(); 15381 15382 if (!browserStorage || !data.cart) { 15383 return; 15384 } 15385 15386 let cartSummaryCount = data.cart.summary_count || 0; 15387 let storageSummaryCount = parseInt(browserStorage.getItem('cart_summary_count'), 10) || 0; 15388 15389 browserStorage.setItem('cart_summary_count', cartSummaryCount); 15390 browserStorage.setItem('dataservices_product_context', JSON.stringify(data.cart.dataservices_product_context)); 15391 15392 let storedData = browserStorage.getItem("mage-cache-storage"); 15393 let browserStorageCartItems = storedData ? JSON.parse(storedData).cart && JSON.parse(storedData).cart.items : []; 15394 let dataCartItems = data.cart.items || []; 15395 15396 if (cartSummaryCount > storageSummaryCount) { 15397 const results = this.findChangedItems(browserStorageCartItems, dataCartItems); 15398 15399 window.dispatchEvent(new CustomEvent("productAddToCart", { 15400 detail: results 15401 })); 15402 } 15403 }, 15404 findChangedItems(oldItems, newItems) { 15405 let changes = []; 15406
15407 newItems.forEach(newItem => { 15408 const oldItem = oldItems.find(old => old.item_id === newItem.item_id); 15409 if (!oldItem || oldItem.qty !== newItem.qty) { 15410 changes.push(newItem); 15411 } 15412 }); 15413 15414 return changes; 15415 } 15416 } 15417 } 15418 15419 window.addEventListener('alpine:init', () => Alpine.data('dataServicesAddToCartTracking', dataServicesAddToCartTracking), {once: true}); 15420</script>
vendor: 1 bytes, line 15420
15420
15421<script> 15422 'use strict'; 15423 window.addEventListener( 15424 'alpine:init', 15425 () => Alpine.data('adobeProductRecsDataFetcher', () => ({ 15426 pagePreconfiguredDeferred: {}, 15427 productBanners: [], 15428 fetchData: function () { 15429 let self = this; 15430 let storefrontEvents = window.magentoStorefrontEvents; 15431 15432 storefrontEvents.context.setContext("recsContext", { 15433 alternateEnvironmentId: "" 15434 }); 15435 15436 this.pagePreconfiguredDeferred = new Promise((resolve, reject) => { 15437 self.pagePreconfiguredDeferred.resolve = resolve; 15438 self.pagePreconfiguredDeferred.reject = reject; 15439 }) 15440 15441 document.addEventListener("recsRequestSent", (function () { 15442 storefrontEvents.publish.recsRequestSent(); 15443 })); 15444 15445 window.addEventListener("recsResponseReceived", (function (n) { 15446 let rec = storefrontEvents.context.getRecommendations(); 15447 15448 storefrontEvents.context.setRecommendations({ 15449 units: rec ? [...rec.units, ...n.detail] : n.detail 15450 }); 15451 storefrontEvents.publish.recsResponseReceived(); 15452 })); 15453 15454 this.fetchRecommendationsData(); 15455 }, 15456 fetchRecommendationsData: function () { 15457 let client; 15458 let promise = new Promise(function(resolve, reject) { 15459 let interval = setInterval(() => { 15460 client = new RecommendationsClient(); 15461 if(client._environmentId !== "") { 15462 clearInterval(interval); 15463 resolve(); 15464 } 15465 }, 300); 15466 setTimeout(() => { 15467 clearInterval(interval); 15468 reject(new Error('RecommendationsClient not ready')); 15469 }, 10000); 15470 }) 15471 .then(() => { 15472 document.dispatchEvent(new Event("recsRequestSent")); 15473 15474 const currentProduct = magentoStorefrontEvents.context.getProduct(); 15475 const params = currentProduct ? { currentSku: currentProduct.sku } : {}; 15476 15477 client.fetchPreconfigured(params).then((response) => { 15478 const results = Array.isArray(response?.data?.results) ? response.data.results : []; 15479 const filteredUnits = results.filter(unit => unit.products.length); 15480 window.dispatchEvent(new CustomEvent("recsResponseReceived", { detail: filteredUnits })); 15481 }).catch(() => { 15482 window.dispatchEvent(new CustomEvent("recsResponseReceived", { detail: [] })); 15483 }); 15484 }) 15485 .catch(() => { 15486 window.dispatchEvent(new CustomEvent("recsResponseReceived", { detail: [] })); 15487 }); 15488 } 15489 })), 15490 {once: true} 15491 ) 15492</script>
15492 15493 15494<div x-data="adobeProductRecsDataFetcher" x-init="fetchData"></div> 15495 15496<div x-data="initCookieGroupInfo_6ab50843b4940" x-bind="eventListeners"> 15497 <dialog 15498 x-ref="classic-am-cookie-group-info" 15499 data-dialog-ref-name="classic-am-cookie-group-info" 15500 aria-labelledby="amcookie-group-info" 15501 closeby="any" 15502 class="open:flex flex-col max-h-[92vh] max-w-[90vw] overflow-auto 15503 bg-ambar-info-bar-background shadow-xl rounded-lg p-8 text-gray-700 relative backdrop:bg-gray-500/50" 15504 x-bind="dialogConfig" 15505 x-htmldialog="closeHandler" 15506 > 15507 <template x-if="group"> 15508 <div id="amcookie-group-info"> 15509 <button @click.stop="closeHandler" 15510 aria-label="Close" 15511 class="absolute right-2 top-2 text-gray-300 transition 15512 hover:text-black focus:drop-shadow-md hover:drop-shadow-md" 15513 > 15514 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" 15515 stroke="currentColor" 15516 stroke-width="2"> 15517 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 15518 </svg> 15519 </button> 15520 <div class="md:flex md:justify-start pb-4"> 15521 <h3 class="text-xl font-semibold text-ambar-info-bar-title" x-text="group.name"></h3> 15522 </div> 15523 <template x-if="group.description"> 15524 <p class="mb-4 text-ambar-info-bar-description" x-html="group.description"></p> 15525 </template> 15526 <div class="overflow-x-scroll md:overflow-x-auto"> 15527 <table class="table-auto w-full mb-4 text-left"> 15528 <thead class="text-ambar-info-bar-table-header"> 15529 <tr> 15530 <th class="pr-4">Cookie Name</th> 15531 <th class="pr-4">Cookie Provider</th> 15532 <th class="min-w-[30%] pr-4">Cookie Description</th> 15533 <th class="pr-4">Cookie Lifetime</th> 15534 <th class="pr-4">Cookie Type</th> 15535 </tr> 15536 </thead> 15537 <tbody class="text-ambar-info-bar-table-content"> 15538 <template x-for="(item, index) in group.cookies" :key="index"> 15539 <tr> 15540 <td x-text="item.name" class="font-semibold pr-4"></td> 15541 <td x-text="item.provider" class="pr-4"></td> 15542 <td x-html="item.description" class="min-w-[30%] pr-4"></td> 15543 <td x-text="item.lifetime" class="pr-4"></td> 15544 <td x-text="item.type" class="pr-4"></td> 15545 </tr> 15546 </template> 15547 </tbody> 15548 </table> 15549 </div> 15550 </div> 15551 </template> 15552 <button 15553 @click.stop="closeHandler" 15554 aria-label="Done" 15555 class="mt-4 w-full text-xs font-semibold text-ambar-info-bar-button-text 15556 hover:text-ambar-info-bar-button-hover-text uppercase bg-ambar-info-bar-button 15557 hover:bg-ambar-info-bar-button-hover ambar-action-btn rounded-sm text-center text-sm transition-colors focus:drop-shadow hover:drop-shadow px-2 py-4" 15558 > 15559 Done </button> 15560 </dialog> 15561 15562</div>
15563<script data-cfasync="false"> 15564 'use strict'; 15565 15566 function initCookieGroupInfo_6ab50843b4940() { 15567 return { 15568 ...hyva.modal({ 15569 dialog: 'classic-am-cookie-group-info' 15570 }), 15571 group: null, 15572 eventListeners: { 15573 ['@classic-close-am-cookie-group-info.window']() { 15574 this.closeHandler(); 15575 }, 15576 ['@classic-open-am-cookie-group-info.window'](event) { 15577 this.group = event.detail; 15578 15579 if (!!this.group) { 15580 /* 15581 If modal have big cookies list this can lead to 15582 trigger hide event in hyva.modal.eventListeners.click, 15583 so we should wait till this.group is reassigned 15584 */ 15585 this.$nextTick(() => this.show('classic-am-cookie-group-info')); 15586 } 15587 } 15588 }, 15589 closeHandler: function () { 15590 this.hide('classic-am-cookie-group-info'); 15591 } 15592 } 15593 } 15594 15595 window.addEventListener( 15596 'alpine:init', 15597 () => Alpine.data( 15598 'initCookieGroupInfo_6ab50843b4940', 15599 initCookieGroupInfo_6ab50843b4940 ), 15600 {once: true} 15601 ); 15602</script>
15602 15603<div id="tmp_pricesystem_ajax_response"></div> 15604<style> 15605.bg-ambar-background { 15606 background: #fff;z-index:1000; 15607} 15608 15609.am-cookie-bar-settings { 15610 background: #fff; 15611 width: 30%; 15612} 15613#cookiebar-groups{ padding: 20px;} 15614 15615/* Mobile breakpoint */ 15616@media only screen and (max-width: 768px) { 15617 .am-cookie-bar-settings { 15618 width: 90%; 15619 padding: 15px; 15620 margin: 0 auto; 15621 } 15622 15623 .bg-ambar-background { 15624 padding: 10px; 15625 } 15626} 15627</style>
15628<script type="text/json" id="default-section-data"> 15629 {"messages":[],"customer":[],"compare-products":[],"last-ordered-items":[],"requisition":[],"cart":[],"directory-data":{"AU":{"name":"Australia","regions":{"569":{"code":"ACT","name":"Australian Capital Territory"},"570":{"code":"NSW","name":"New South Wales"},"576":{"code":"NT","name":"Northern Territory"},"572":{"code":"QLD","name":"Queensland"},"573":{"code":"SA","name":"South Australia"},"574":{"code":"TAS","name":"Tasmania"},"571":{"code":"VIC","name":"Victoria"},"575":{"code":"WA","name":"Western Australia"}}},"data_id":1790249027},"captcha":[],"wishlist":{"items":[]},"company":[],"company_authorization":[],"customer-companies":[],"negotiable_quote":[],"loggedAsCustomer":[],"multiplewishlist":[],"purchase_order":[],"persistent":[],"review":[],"payments":[],"faq":[],"amga4_event_pool":[],"amga4_hyva_event_pool":[],"ammessages":[],"hyva_checkout":[],"hyva_enterprise":[],"plastic-ban-region":[],"partner-portal":[],"recently_viewed_product":[],"recently_compared_product":[],"product_data_storage":[],"paypal-billing-agreement":[],"paypal-buyer-country":[]}</script>
15629 15630<div x-data="amMessage" x-bind="eventListeners"></div> 15631
15632<script> 15633 function amMessage() { 15634 let lastProcessedDataId = null; 15635 const processedMessageHashes = new Set(); 15636 let debounceTimer; 15637 15638 return { 15639 init() { 15640 this.processStoredMessages(); 15641 15642 window.addEventListener('beforeunload', () => { 15643 processedMessageHashes.clear(); 15644 lastProcessedDataId = null; 15645 }); 15646 }, 15647 15648 eventListeners: { 15649 ['@private-content-loaded.window'](event) { 15650 const { ammessages } = event.detail.data; 15651 15652 clearTimeout(debounceTimer); 15653 debounceTimer = setTimeout(() => { 15654 if (ammessages?.data_id && ammessages.data_id === lastProcessedDataId) { 15655 return; 15656 } 15657 15658 lastProcessedDataId = ammessages?.data_id; 15659 15660 // Checking hash message 15661 if (ammessages?.messages) { 15662 const messageHash = JSON.stringify(ammessages.messages); 15663 if (processedMessageHashes.has(messageHash)) { 15664 return; 15665 } 15666 processedMessageHashes.add(messageHash); 15667 } 15668 15669 const isCartPage = document.body.classList.contains('checkout-cart-index'); 15670 const isCheckoutPage = document.body.classList.contains('hyva_checkout-index-index'); 15671 const onePageSuccessPage = document.body.classList.contains('hyva_checkout_onepage_success'); 15672 15673 if (!isCartPage && !isCheckoutPage && !onePageSuccessPage && ammessages?.messages) { 15674 this.processMessages(ammessages.messages); 15675 } 15676 }, 500); 15677 }, 15678 ['@am-promo:initialized.window']() { 15679 this.processStoredMessages(); 15680 }, 15681 ['@am-promo-products-recollect.window']() { 15682 this.processStoredMessages(); 15683 } 15684 }, 15685 15686 processStoredMessages() { 15687 try { 15688 const messages = JSON.parse(localStorage.getItem('amFreeGiftCartMessages')); 15689 if (messages?.length) { 15690 messages.forEach(message => dispatchMessages([message], 5000)); 15691 localStorage.removeItem('amFreeGiftCartMessages'); 15692 } 15693 } catch (e) { 15694 console.error('Error processing stored messages:', e); 15695 } 15696 }, 15697 15698 processMessages(messages) { 15699 if (messages?.notice) { 15700 this.handleNoticeMessage(messages.notice); 15701 } 15702 }, 15703 15704 handleNoticeMessage(notice) { 15705 if (!notice) return; 15706 dispatchMessages([{ type: notice.type, text: notice.text }]); 15707 } 15708 } 15709 } 15710 15711 window.addEventListener( 15712 'alpine:init', 15713 () => Alpine.data( 15714 'amMessage', 15715 amMessage 15716 ), 15717 {once: true} 15718 ); 15719</script>
15719 15720
15721<script> 15722 'use strict'; 15723 15724 function attributeIcon() { 15725 return { 15726 labelledById: '', 15727 15728 init() { 15729 const timestamp = Date.now(); 15730 const randomNum = Math.floor(Math.random() * 1000); 15731 this.labelledById = `amshopby-option-id-${timestamp}-${randomNum}`; 15732 } 15733 } 15734 } 15735 window.addEventListener( 15736 'alpine:init', 15737 () => Alpine.data('attributeIcon',attributeIcon), 15738 {once: true} 15739 ); 15740</script>
15740 15741 15742</div></body> 15743</html>
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.