1<!doctype html> 2<html lang="en"> 3<head > 4 <meta charset="utf-8"/> 5<meta name="title" content="Blog"/> 6<meta name="robots" content="INDEX,FOLLOW"/> 7<meta name="viewport" content="width=device-width, initial-scale=1"/> 8<title>Blog</title><style data-jit-css data-liveview-style-type="cms_block" data-liveview-style-id="18">.cmsb18-text-center { 9text-align: center 10} 11.cmsb18-text-sm { 12font-size: 0.875rem; 13line-height: 1.25rem 14} 15.cmsb18-font-normal { 16font-weight: 400 17}</style> 18 <link rel="stylesheet" type="text/css" media="all" href="https://www.customandcommercial.com/static/version1778675079/_cache/merged/df8d0e0e767407f31e3cf63e565eb9e1.css" />
19<script type="text/javascript" src="https://www.customandcommercial.com/static/version1778675079/frontend/Actuate/c-and-c/en_US/Smile_ElasticsuiteTracker/js/tracking.js"></script>
19 20<link rel="canonical" href="https://www.customandcommercial.com/blog/" /> 21<link type="text/css" rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap" /> 22<link type="text/css" rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap" /> 23<link rel="icon" type="image/x-icon" href="https://www.customandcommercial.com/media/favicon/stores/1/favicon.png" /> 24<link rel="shortcut icon" type="image/x-icon" href="https://www.customandcommercial.com/media/favicon/stores/1/favicon.png" /> 25<style> 26.message-success, 27.messages .message, 28.message.notice, 29.message-success.success.message { 30display:none !important; 31} 32</style> <!-- NO Pixel ID is configured, please goto Admin -->
33<script type="text/javascript"> console.log('No Meta pixel is configured, please log in as a admin and then visit Stores -> Meta -> Setup -> Get Started');</script>
33<script nonce="dDlpdmJ2Y214eXB4aGtjOXZtenU2eWphNGp6Yzd4Nzg="> 34require(['sfgetid'], function(sfgetid) { 35 sfgetid(); 36}); 37</script>
vendor: 1 bytes, line 37
37
38<script nonce="dDlpdmJ2Y214eXB4aGtjOXZtenU2eWphNGp6Yzd4Nzg="> 39sfDataLayer = window.sfDataLayer || []; 40sfDataLayer.push({ 41 "salesfire": { 42 "platform": "magento2" 43 } 44}); 45</script>
vendor: 1 bytes, line 45
45
46<script async src='https://cdn.salesfire.co.uk/code/66bc4c55-6168-4741-b651-aeb84ea6a384.js'></script>
46 47
47<script nonce="dDlpdmJ2Y214eXB4aGtjOXZtenU2eWphNGp6Yzd4Nzg="> 48 //<![CDATA[ 49 function userConsentEsTraking(config) 50 { 51 return config.cookieRestrictionEnabled == false || hyva.getCookie(config.cookieRestrictionName) !== null; 52 } 53 54 try { 55 smileTracker.setConfig({ 56 beaconUrl : 'https://www.customandcommercial.com/elasticsuite/tracker/hit/image/h.png', 57 telemetryUrl : 'https://t.elasticsuite.io/track', 58 telemetryEnabled : '', 59 sessionConfig : {"visit_cookie_name":"STUID","visit_cookie_lifetime":"3600","visitor_cookie_lifetime":"365","visitor_cookie_name":"STVID","domain":"www.customandcommercial.com","path":"\/"}, 60 endpointUrl : 'https://www.customandcommercial.com/rest/V1/elasticsuite-tracker/hit' 61 }); 62 63 smileTracker.addPageVar('store_id', '1'); 64 65 document.addEventListener('DOMContentLoaded', function () { 66 if (userConsentEsTraking({"cookieRestrictionEnabled":"0","cookieRestrictionName":"user_allowed_save_cookie"})) { 67 smileTracker.sendTag(); 68 } 69 }); 70 } catch (err) { 71 ; 72 } 73 //]]> 74 </script>
vendor: 1 bytes, line 74
74
75<script nonce="dDlpdmJ2Y214eXB4aGtjOXZtenU2eWphNGp6Yzd4Nzg="> 76 var BASE_URL = 'https://www.customandcommercial.com/'; 77 var THEME_PATH = 'https://www.customandcommercial.com/static/version1778675079/frontend/Actuate/c-and-c/en_US'; 78 var COOKIE_CONFIG = { 79 "expires": null, 80 "path": "\u002F", 81 "domain": ".www.customandcommercial.com", 82 "secure": true, 83 "lifetime": "3600", 84 "cookie_restriction_enabled": false }; 85 var CURRENT_STORE_CODE = 'default'; 86 var CURRENT_WEBSITE_ID = '1'; 87 88 window.hyva = window.hyva || {} 89 90 window.cookie_consent_groups = window.cookie_consent_groups || {} 91 window.cookie_consent_groups['necessary'] = true; 92 93 window.cookie_consent_config = window.cookie_consent_config || {}; 94 window.cookie_consent_config['necessary'] = [].concat( 95 window.cookie_consent_config['necessary'] || [], 96 [ 97 'user_allowed_save_cookie', 98 'form_key', 99 'mage-messages', 100 'private_content_version', 101 'mage-cache-sessid', 102 'last_visited_store', 103 'section_data_ids' 104 ] 105 ); 106</script>
vendor: 1 bytes, line 106
106
107<script nonce="dDlpdmJ2Y214eXB4aGtjOXZtenU2eWphNGp6Yzd4Nzg="> 108 'use strict'; 109 (function( hyva, undefined ) { 110 111 function lifetimeToExpires(options, defaults) { 112 113 const lifetime = options.lifetime || defaults.lifetime; 114 115 if (lifetime) { 116 const date = new Date; 117 date.setTime(date.getTime() + lifetime * 1000); 118 return date; 119 } 120 121 return null; 122 } 123 124 function generateRandomString() { 125 126 const allowedCharacters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 127 length = 16; 128 129 let formKey = '', 130 charactersLength = allowedCharacters.length; 131 132 for (let i = 0; i < length; i++) { 133 formKey += allowedCharacters[Math.round(Math.random() * (charactersLength - 1))] 134 } 135 136 return formKey; 137 } 138 139 const sessionCookieMarker = {noLifetime: true} 140 141 const cookieTempStorage = {}; 142 143 const internalCookie = { 144 get(name) { 145 const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); 146 return v ? v[2] : null; 147 }, 148 set(name, value, days, skipSetDomain) { 149 let expires, 150 path, 151 domain, 152 secure, 153 samesite; 154 155 const defaultCookieConfig = { 156 expires: null, 157 path: '/', 158 domain: null, 159 secure: false, 160 lifetime: null, 161 samesite: 'lax' 162 }; 163 164 const cookieConfig = window.COOKIE_CONFIG || {}; 165 166 expires = days && days !== sessionCookieMarker 167 ? lifetimeToExpires({lifetime: 24 * 60 * 60 * days, expires: null}, defaultCookieConfig) 168 : lifetimeToExpires(window.COOKIE_CONFIG, defaultCookieConfig) || defaultCookieConfig.expires; 169 170 path = cookieConfig.path || defaultCookieConfig.path; 171 domain = !skipSetDomain && (cookieConfig.domain || defaultCookieConfig.domain); 172 secure = cookieConfig.secure || defaultCookieConfig.secure; 173 samesite = cookieConfig.samesite || defaultCookieConfig.samesite; 174 175 document.cookie = name + "=" + encodeURIComponent(value) + 176 (expires && days !== sessionCookieMarker ? '; expires=' + expires.toGMTString() : '') + 177 (path ? '; path=' + path : '') + 178 (domain ? '; domain=' + domain : '') + 179 (secure ? '; secure' : '') + 180 (samesite ? '; samesite=' + samesite : 'lax'); 181 }, 182 isWebsiteAllowedToSaveCookie() { 183 const allowedCookies = this.get('user_allowed_save_cookie'); 184 if (allowedCookies) { 185 const allowedWebsites = JSON.parse(unescape(allowedCookies)); 186 187 return allowedWebsites[CURRENT_WEBSITE_ID] === 1; 188 } 189 return false; 190 }, 191 getGroupByCookieName(name) { 192 const cookieConsentConfig = window.cookie_consent_config || {}; 193 let group = null; 194 for (let prop in cookieConsentConfig) { 195 if (!cookieConsentConfig.hasOwnProperty(prop)) continue; 196 if (cookieConsentConfig[prop].includes(name)) { 197 group = prop; 198 break; 199 } 200 } 201 return group; 202 }, 203 isCookieAllowed(name) { 204 const cookieGroup = this.getGroupByCookieName(name); 205 return cookieGroup 206 ? window.cookie_consent_groups[cookieGroup] 207 : this.isWebsiteAllowedToSaveCookie(); 208 }, 209 saveTempStorageCookies() { 210 for (const [name, data] of Object.entries(cookieTempStorage)) { 211 if (this.isCookieAllowed(name)) { 212 this.set(name, data['value'], data['days'], data['skipSetDomain']); 213 delete cookieTempStorage[name]; 214 } 215 } 216 } 217 }; 218 219 hyva.getCookie = (name) => { 220 const cookieConfig = window.COOKIE_CONFIG || {}; 221 222 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 223 return cookieTempStorage[name] ? cookieTempStorage[name]['value'] : null; 224 } 225 226 return internalCookie.get(name); 227 } 228 229 hyva.setCookie = (name, value, days, skipSetDomain) => { 230 const cookieConfig = window.COOKIE_CONFIG || {}; 231 232 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 233 cookieTempStorage[name] = {value, days, skipSetDomain}; 234 return; 235 } 236 return internalCookie.set(name, value, days, skipSetDomain); 237 } 238 239 240 hyva.setSessionCookie = (name, value, skipSetDomain) => { 241 return hyva.setCookie(name, value, sessionCookieMarker, skipSetDomain) 242 } 243 244 hyva.getBrowserStorage = () => { 245 const browserStorage = window.localStorage || window.sessionStorage; 246 if (!browserStorage) { 247 console.warn('Browser Storage is unavailable'); 248 return false;
249 } 250 try { 251 browserStorage.setItem('storage_test', '1'); 252 browserStorage.removeItem('storage_test'); 253 } catch (error) { 254 console.warn('Browser Storage is not accessible', error); 255 return false; 256 } 257 return browserStorage; 258 } 259 260 hyva.postForm = (postParams) => { 261 const form = document.createElement("form"); 262 263 let data = postParams.data; 264 265 if (! postParams.skipUenc && ! data.uenc) { 266 data.uenc = btoa(window.location.href); 267 } 268 form.method = "POST"; 269 form.action = postParams.action; 270 271 Object.keys(postParams.data).map(key => { 272 const field = document.createElement("input"); 273 field.type = 'hidden' 274 field.value = postParams.data[key]; 275 field.name = key; 276 form.appendChild(field); 277 }); 278 279 const form_key = document.createElement("input"); 280 form_key.type = 'hidden'; 281 form_key.value = hyva.getFormKey(); 282 form_key.name="form_key"; 283 form.appendChild(form_key); 284 285 document.body.appendChild(form); 286 287 form.submit(); 288 } 289 290 hyva.getFormKey = function () { 291 let formKey = hyva.getCookie('form_key'); 292 293 if (!formKey) { 294 formKey = generateRandomString(); 295 hyva.setCookie('form_key', formKey); 296 } 297 298 return formKey; 299 } 300 301 hyva.formatPrice = (value, showSign, options = {}) => { 302 const groupSeparator = options.groupSeparator; 303 const decimalSeparator = options.decimalSeparator 304 delete options.groupSeparator; 305 delete options.decimalSeparator; 306 const formatter = new Intl.NumberFormat( 307 'en\u002DUS', 308 Object.assign({ 309 style: 'currency', 310 currency: 'GBP', 311 signDisplay: showSign ? 'always' : 'auto' 312 }, options) 313 ); 314 return (typeof Intl.NumberFormat.prototype.formatToParts === 'function') ? 315 formatter.formatToParts(value).map(({type, value}) => { 316 switch (type) { 317 case 'currency': 318 return '\u00A3' || value; 319 case 'minusSign': 320 return '- '; 321 case 'plusSign': 322 return '+ '; 323 case 'group': 324 return groupSeparator !== undefined ? groupSeparator : value; 325 case 'decimal': 326 return decimalSeparator !== undefined ? decimalSeparator : value; 327 default : 328 return value; 329 } 330 }).reduce((string, part) => string + part) : 331 formatter.format(value); 332 } 333 334 const formatStr = function (str, nStart) { 335 const args = Array.from(arguments).slice(2); 336 337 return str.replace(/(%+)([0-9]+)/g, (m, p, n) => { 338 const idx = parseInt(n) - nStart; 339 340 if (args[idx] === null || args[idx] === void 0) { 341 return m; 342 } 343 return p.length % 2 344 ? p.slice(0, -1).replace('%%', '%') + args[idx] 345 : p.replace('%%', '%') + n; 346 }) 347 } 348 349 hyva.str = function (string) { 350 const args = Array.from(arguments); 351 args.splice(1, 0, 1); 352 353 return formatStr.apply(undefined, args); 354 } 355 356 hyva.strf = function () { 357 const args = Array.from(arguments); 358 args.splice(1, 0, 0); 359 360 return formatStr.apply(undefined, args); 361 } 362 363 /** 364 * Take a html string as `content` parameter and 365 * extract an element from the DOM to replace in 366 * the current page under the same selector, 367 * defined by `targetSelector` 368 */ 369 hyva.replaceDomElement = (targetSelector, content) => { 370 // Parse the content and extract the DOM node using the `targetSelector` 371 const parser = new DOMParser(); 372 const doc = parser.parseFromString(content, 'text/html');
373 const contentNode = doc.querySelector(targetSelector); 374 375 // Bail if content or target can't be found 376 if (!contentNode || !document.querySelector(targetSelector)) { 377 return; 378 } 379 380 hyva.activateScripts(contentNode); 381 382 // Replace the old DOM node with the new content 383 document.querySelector(targetSelector).replaceWith(contentNode); 384 385 // Reload customerSectionData and display cookie-messages if present 386 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 387 hyva.initMessages(); 388 } 389 390 hyva.removeScripts = (contentNode) => { 391 const scripts = contentNode.getElementsByTagName('script'); 392 for (let i = 0; i < scripts.length; i++) { 393 scripts[i].parentNode.removeChild(scripts[i]); 394 } 395 const templates = contentNode.getElementsByTagName('template'); 396 for (let i = 0; i < templates.length; i++) { 397 const container = document.createElement('div'); 398 container.innerHTML = templates[i].innerHTML; 399 hyva.removeScripts(container); 400 templates[i].innerHTML = container.innerHTML; 401 } 402 } 403 404 hyva.activateScripts = (contentNode) => { 405 // Create new array from HTMLCollection to avoid mutation of collection while manipulating the DOM. 406 const scripts = Array.from(contentNode.getElementsByTagName('script')); 407 408 // Iterate over all script tags to duplicate+inject each into the head 409 for (const original of scripts) { 410 const script = document.createElement('script'); 411 original.type && (script.type = original.type); 412 script.innerHTML = original.innerHTML; 413 414 // Remove the original (non-executing) script from the contentNode 415 original.parentNode.removeChild(original) 416 417 // Add script to head 418 document.head.appendChild(script); 419 } 420 421 return contentNode; 422 } 423 424 const replace = {['+']: '-', ['/']: '_', ['=']: ','}; 425 hyva.getUenc = () => btoa(window.location.href).replace(/[+/=]/g, match => replace[match]); 426 427 let currentTrap; 428 429 const focusableElements = (rootElement) => { 430 const selector = 'button, [href], input, select, textarea, details, [tabindex]:not([tabindex="-1"]'; 431 return Array.from(rootElement.querySelectorAll(selector)) 432 .filter(el => { 433 return el.style.display !== 'none' 434 && !el.disabled 435 && el.tabIndex !== -1 436 && (el.offsetWidth || el.offsetHeight || el.getClientRects().length) 437 }) 438 } 439 440 const focusTrap = (e) => { 441 const isTabPressed = e.key === 'Tab' || e.keyCode === 9; 442 if (!isTabPressed) return; 443 444 const focusable = focusableElements(currentTrap) 445 const firstFocusableElement = focusable[0] 446 const lastFocusableElement = focusable[focusable.length - 1] 447 448 e.shiftKey 449 ? document.activeElement === firstFocusableElement && (lastFocusableElement.focus(), e.preventDefault()) 450 : document.activeElement === lastFocusableElement && (firstFocusableElement.focus(), e.preventDefault()) 451 }; 452 453 hyva.releaseFocus = (rootElement) => { 454 if (currentTrap && (!rootElement || rootElement === currentTrap)) { 455 currentTrap.removeEventListener('keydown', focusTrap) 456 currentTrap = null 457 } 458 } 459 hyva.trapFocus = (rootElement) => { 460 if (!rootElement) return; 461 hyva.releaseFocus() 462 currentTrap = rootElement 463 rootElement.addEventListener('keydown', focusTrap) 464 const firstElement = focusableElements(rootElement)[0] 465 firstElement && firstElement.focus() 466 } 467 468 hyva.safeParseNumber = (rawValue) => { 469 const number = rawValue ? parseFloat(rawValue) : null; 470 471 return Array.isArray(number) || isNaN(number) ? rawValue : number; 472 } 473 474 const toCamelCase = s => s.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); 475 hyva.createBooleanObject = (name, value = false, additionalMethods = {}) => { 476 477 const camelCase = toCamelCase(name);
478 const key = '__hyva_bool_' + name 479 480 return new Proxy(Object.assign( 481 additionalMethods, 482 { 483 [key]: !!value, 484 [name]() {return !!this[key]}, 485 ['!' + name]() {return !this[key]}, // @deprecated This does not work with non-CSP Alpine 486 ['not' + camelCase]() {return !this[key]}, 487 ['toggle' + camelCase]() {this[key] = !this[key]}, 488 [`set${camelCase}True`]() {this[key] = true}, 489 [`set${camelCase}False`]() {this[key] = false}, 490 } 491 ), { 492 set(target, prop, value) { 493 return prop === name 494 ? (target[key] = !!value) 495 : Reflect.set(...arguments); 496 } 497 }) 498 } 499 500 hyva.alpineInitialized = (fn) => window.addEventListener('alpine:initialized', fn, {once: true}) 501 window.addEventListener('alpine:init', () => Alpine.data('{}', () => ({})), {once: true}); 502 window.addEventListener('user-allowed-save-cookie', () => internalCookie.saveTempStorageCookies()) 503 504 }( window.hyva = window.hyva || {} )); 505</script>
vendor: 1 bytes, line 505
505
506<script nonce="dDlpdmJ2Y214eXB4aGtjOXZtenU2eWphNGp6Yzd4Nzg="> 507 if (!window.IntersectionObserver) { 508 window.IntersectionObserver = function (callback) { 509 this.observe = el => el && callback(this.takeRecords()); 510 this.takeRecords = () => [{isIntersecting: true, intersectionRatio: 1}]; 511 this.disconnect = () => {}; 512 this.unobserve = () => {}; 513 } 514 } 515</script>
vendor: 1 bytes, line 515
515
516<script src="https://www.customandcommercial.com/static/version1778675079/frontend/Actuate/c-and-c/en_US/js/swiper-bundle.min.js"></script>
516</head> 517<body id="html-body" class="wordpress-posttype-view page-layout-2columns-left"> 518
519<script> 520 'use strict'; 521 (function( vatSwitcher, undefined ) { 522 const defaultVatMode = 'excluding'; 523 524 vatSwitcher.getVatModeFromBrowserStorage = () => { 525 const browserStorage = hyva.getBrowserStorage(); 526 if (! browserStorage) { 527 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 528 return null; 529 } 530 531 return browserStorage.getItem('vat-switch-mode'); 532 } 533 534 vatSwitcher.setVatMode = (value) => { 535 const browserStorage = hyva.getBrowserStorage(); 536 537 if (browserStorage) { 538 browserStorage.setItem('vat-switch-mode', value); 539 } else { 540 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 541 } 542 } 543 544 vatSwitcher.getVatMode = () => { 545 return vatSwitcher.getVatModeFromBrowserStorage() || defaultVatMode; 546 } 547 }( window.vatSwitcher = window.vatSwitcher || {} )); 548</script>
548 549<input name="form_key" type="hidden" value="8yyKEOyvlaL6bVEW" /> 550 <noscript> 551 <section class="message global noscript border-b-2 border-blue-500 bg-blue-50 shadow-none m-0 px-0 rounded-none font-normal"> 552 <div class="container text-center"> 553 <p> 554 <strong>JavaScript seems to be disabled in your browser.</strong> 555 <span> 556 For the best experience on our site, be sure to turn on Javascript in your browser. </span> 557 </p> 558 </div> 559 </section> 560 </noscript> 561 562
563<script> 564 document.body.addEventListener('touchstart', () => {}, {passive: true}) 565</script>
565 566
567<script data-cfasync="false"> 568 window.amastyCookieManager = window.amastyCookieManager || {}; 569</script>
569 570<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 571 contentarea" 572 href="#contentarea"> 573 <span> 574 Skip to Content </span> 575</a> 576<div class="header content"> 577<a x-data="initAmQuoteIcon" 578 @click.prevent.stop="toggleAmquoteEvent" 579 @private-content-loaded.window="privateContentLoadedEvent" 580 id="amquote-icon" 581 href="https://www.customandcommercial.com/request_quote/cart/" 582 class="quote-icon md:block text-center relative flex flex-col text-xs font-semibold leading-4 items-center mr-2.5"> 583 <svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 44 44" width="24" height="24" class="text-white bg-ctaprimary rounded-full w-[30px] md:w-11 md:h-11 h-[30px] mb-1.5 mx-auto" role="img"> 584 <defs> 585 <image width="24" height="24" id="img1" href="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik02IDJMMyA2VjIwQzMgMjAuNTMwNCAzLjIxMDcxIDIxLjAzOTEgMy41ODU3OSAyMS40MTQyQzMuOTYwODYgMjEuNzg5MyA0LjQ2OTU3IDIyIDUgMjJIMTlDMTkuNTMwNCAyMiAyMC4wMzkxIDIxLjc4OTMgMjAuNDE0MiAyMS40MTQyQzIwLjc4OTMgMjEuMDM5MSAyMSAyMC41MzA0IDIxIDIwVjZMMTggMkg2WiIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgogICAgPHBhdGggZD0iTTMgNkgyMSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgogICAgPHBhdGggZD0iTTE2IDEwQzE2IDExLjA2MDkgMTUuNTc4NiAxMi4wNzgzIDE0LjgyODQgMTIuODI4NEMxNC4wNzgzIDEzLjU3ODYgMTMuMDYwOSAxNCAxMiAxNEMxMC45MzkxIDE0IDkuOTIxNzIgMTMuNTc4NiA5LjE3MTU3IDEyLjgyODRDOC40MjE0MyAxMi4wNzgzIDggMTEuMDYwOSA4IDEwIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo="/> 586 </defs> 587 <style> 588 .s0 { fill: #e18438 } 589 </style> 590 <path fill-rule="evenodd" class="s0" d="m22 0c12.15 0 22 9.85 22 22 0 12.15-9.85 22-22 22-12.15 0-22-9.85-22-22 0-12.15 9.85-22 22-22z"/> 591 <use id="quote" href="#img1" transform="matrix(1,0,0,1,10,10)"/> 592<title>quote</title></svg> 593 <span 594 class="font-serif inline-block text-center text-xs font-semibold text-center mx-auto tabular-nums" 595 aria-hidden="true" 596 >Quote</span> 597 <span x-text="quotecartSummaryCount" 598 class="absolute top-0 right-0 hidden h-5 px-2 py-1 -mt-5 -mr-4 text-xs font-semibold 599 leading-none text-center text-white uppercase transform -translate-x-1 600 translate-y-1/2 rounded-full bg-ctasecondary" 601 :class="summaryCountClass" 602 ></span> 603</a> 604
605<script> 606 'use strict'; 607 608 (function () { 609 document.addEventListener('DOMContentLoaded', function () { 610 const quoteIcon = document.getElementById("amquote-icon"); 611 const insertBeforeNode = document.querySelector('\u0023menu\u002Dcart\u002Dicon'); 612 if (!insertBeforeNode) { 613 return; 614 } 615 616 const destination = insertBeforeNode.parentNode; 617 destination.insertBefore(quoteIcon, insertBeforeNode); 618 quoteIcon.classList.remove('hidden'); 619 }); 620 })() 621 622 function initAmQuoteIcon() { 623 return { 624 quotecart: {}, 625 626 getData(data) { 627 if (data.quotecart) { 628 this.quotecart = data.quotecart 629 } 630 }, 631 632 summaryCountClass() { 633 return { 634 'hidden': !this.quotecart.summary_count, 635 'block': this.quotecart.summary_count 636 } 637 }, 638 639 toggleAmquoteEvent() { 640 return this.$dispatch('toggle-amquote') 641 }, 642 643 privateContentLoadedEvent(event) { 644 return this.getData(this.$event.detail.data) 645 }, 646 647 quotecartSummaryCount() { 648 return this.quotecart.summary_count 649 } 650 } 651 } 652 653 window.addEventListener( 654 'alpine:init', 655 () => Alpine.data( 656 'initAmQuoteIcon', 657 initAmQuoteIcon 658 ), 659 {once: true} 660 ); 661</script>
661 662<section id="quotecart-drawer" 663 x-data="initAmQuoteCartDrawer" 664 x-bind="eventListeners" 665 @private-content-loaded.window="privateContentLoadedEvent" 666 @keydown.window.escape="hideDialog"> 667 <template x-if="quotecart"> 668 <div role="dialog" 669 aria-labelledby="quote-cart-drawer-title" 670 aria-modal="true" 671 @click.away="hideDialog" 672 class="fixed inset-y-0 right-0 z-40 flex max-w-full"> 673 <div class="backdrop" 674 x-show="open" 675 x-transition:enter="ease-in-out duration-500" 676 x-transition:enter-start="opacity-0" 677 x-transition:enter-end="opacity-100" 678 x-transition:leave="ease-in-out duration-500" 679 x-transition:leave-start="opacity-100" 680 x-transition:leave-end="opacity-0" 681 @click="hideDialog" 682 role="button" 683 aria-label="Close panel"></div> 684 685 <div class="relative w-screen max-w-md shadow-2xl" 686 x-show="open" 687 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" 688 x-transition:enter-start="translate-x-full" 689 x-transition:enter-end="translate-x-0" 690 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" 691 x-transition:leave-start="translate-x-0" 692 x-transition:leave-end="translate-x-full" 693 > 694 <div 695 x-show="open" 696 x-transition:enter="ease-in-out duration-500" 697 x-transition:enter-start="opacity-0" 698 x-transition:enter-end="opacity-100" 699 x-transition:leave="ease-in-out duration-500" 700 x-transition:leave-start="opacity-100" 701 x-transition:leave-end="opacity-0" class="absolute top-0 right-0 flex p-2 mt-2"> 702 <button @click="hideDialog" aria-label="Close panel" 703 class="p-2 text-gray-300 transition duration-150 ease-in-out hover:text-black"> 704 <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"> 705 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 706</svg> 707 </button> 708 </div> 709 <div class="flex flex-col h-full py-6 space-y-6 bg-white shadow-xl"> 710 711 <header class="px-4 sm:px-6"> 712 <h2 id="quote-cart-drawer-title" class="text-lg font-medium leading-7 text-gray-900"> 713 <span class="font-semibold">Quote Summary</span> 714 <span class="items-total text-xs" 715 x-show="isItemCountShow" 716 x-text="getItemCountTitle"></span> 717 </h2> 718 </header> 719 720 721 <div x-show="itemsCount" class="relative grid gap-6 px-4 py-6 overflow-y-auto bg-white border-b 722 sm:gap-8 sm:px-6 border-container"> 723 <template x-for="item in quoteItems"> 724 <div class="flex items-start p-3 -m-3 space-x-4 transition duration-150 725 ease-in-out rounded-lg hover:bg-gray-100"> 726 <a :href="productUrl" class="w-1/4"> 727 <img 728 :src="productImageSrc" 729 :width="productImageWidth" 730 :height="productImageHeight" 731 :alt="productImageAlt" 732 loading="lazy" 733 /> 734 </a> 735 <div class="w-3/4 space-y-2"> 736 <div> 737 <p class="text-xl">
738 <span x-html="itemQty"></span> x <span x-html="productName"></span> 739 </p> 740 741 <p class="text-sm"><span x-html="productSku"></span></p> 742 </div> 743 744 <template x-for="option in itemOptions"> 745 <div class="pt-2"> 746 <p class="font-semibold" x-text="optionLabel"></p> 747 <p class="text-secondary" x-html="optionValue"></p> 748 </div> 749 </template> 750 <p><span x-html="productPrice"></span></p> 751 <div class="333 pt-4 quote-buttons"> 752 <a :href="configureUrl" 753 x-show="isProductNotGrouped" 754 class="inline-flex p-2 mr-2 btn btn-primary"> 755 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" role="img"> 756 <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"/> 757<title>pencil</title></svg> 758 <span class="sr-only">Edit item</span> 759 </a> 760 <button class="inline-flex p-2 btn btn-primary" 761 @click="removeItem"> 762 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" role="img"> 763 <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"/> 764<title>trash</title></svg> 765 <span class="sr-only">Remove item</span> 766 </button> 767 </div> 768 769 </div> 770 </div> 771 </template> 772 </div> 773 774 775 <div x-show="itemsCount" class="relative grid gap-6 px-4 py-6 bg-white sm:gap-8 sm:px-6"> 776 <div class="w-full p-3 -m-3 space-x-4 transition duration-150 ease-in-out rounded-lg 777 hover:bg-gray-100"> 778 <p>Subtotal: <span x-html="quotecartSubtotal"></span></p> 779 </div> 780 <div class="flex justify-center w-full p-3 -m-3 space-x-4 transition duration-150 ease-in-out 781 rounded-lg hover:bg-gray-100"> 782 <a @click.prevent.stop="quoteCart" 783 data-url="https://www.customandcommercial.com/amasty_quote/cart/" 784 href="https://www.customandcommercial.com/amasty_quote/cart/" 785 class="inline-flex btn btn-primary"> 786 My Quote Cart </a> 787 </div> 788 </div> 789 790 <template x-if="quoteIsEmpty"> 791 <div class="relative px-4 py-6 bg-white border-bs sm:px-6 border-container"> 792 Quote is empty </div> 793 </template> 794 795 </div> 796 </div> 797 <div 798 class="z-50 fixed inset-0 grid place-items-center bg-white/70 text-slate-800" 799 x-cloak 800 x-show="isLoading" 801 x-transition.opacity.duration.200ms 802> 803 <div class="flex gap-10 items-center p-4"> 804 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57 57" width="57" height="57" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"> 805 <style>
806 @keyframes spinner-ball-triangle1 { 807 0% { transform: translate(0%, 0%); } 808 33% { transform: translate(38%, -79%); } 809 66% { transform: translate(77%, 0%); } 810 100% { transform: translate(0%, 0%); } 811 } 812 813 @keyframes spinner-ball-triangle2 { 814 0% { transform: translate(0%, 0%); } 815 33% { transform: translate(38%, 79%); } 816 66% { transform: translate(-38%, 79%); } 817 100% { transform: translate(0%, 0%); } 818 } 819 820 @keyframes spinner-ball-triangle3 { 821 0% { transform: translate(0%, 0%); } 822 33% { transform: translate(-77%, 0%); } 823 66% { transform: translate(-38%, -79%); } 824 100% { transform: translate(0%, 0%); } 825 } 826 </style> 827 <circle cx="5" cy="50" r="5" style="animation: spinner-ball-triangle1 2.2s linear infinite"/> 828 <circle cx="27" cy="5" r="5" style="animation: spinner-ball-triangle2 2.2s linear infinite"/> 829 <circle cx="49" cy="50" r="5" style="animation: spinner-ball-triangle3 2.2s linear infinite"/> 830</svg> 831 <span class="text-xl select-none"> 832 Loading... </span> 833 </div> 834</div> 835 </div> 836 </template> 837</section> 838
839<script> 840 function initAmQuoteCartDrawer() { 841 return { 842 open: false, 843 isLoading: false, 844 quotecart: {}, 845 maxItemsToDisplay: 10, 846 itemsCount: 0, 847 totalCartAmount: 0, 848 849 getData(data) { 850 if (data.quotecart) { 851 this.quotecart = data.quotecart; 852 this.itemsCount = data.quotecart.items && data.quotecart.items.length || 0; 853 this.totalCartAmount = this.quotecart.summary_count; 854 this.setQuoteItems(); 855 } 856 this.isLoading = false; 857 }, 858 859 quoteItems: [], 860 861 getItemCountTitle() { 862 return hyva.strf('(%0 of %1)', this.maxItemsToDisplay, this.itemsCount) 863 }, 864 865 setQuoteItems() { 866 this.quoteItems = this.quotecart.items && this.quotecart.items.sort((a, b) => b.item_id - a.item_id) || []; 867 868 if (this.maxItemsToDisplay > 0) { 869 this.quoteItems = this.quoteItems.slice(0, parseInt(this.maxItemsToDisplay, 10)); 870 } 871 }, 872 873 deleteItemFromQuote(itemId) { 874 this.isLoading = true; 875 876 const formKey = hyva.getFormKey(); 877 const postUrl = BASE_URL + 'amasty_quote/sidebar/removeItem/'; 878 879 fetch(postUrl, { 880 "headers": { 881 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 882 }, 883 "body": "form_key=" + formKey + "&item_id=" + itemId, 884 "method": "POST", 885 "mode": "cors", 886 "credentials": "include" 887 }).then(response => { 888 if (response.redirected) { 889 window.location.href = response.url; 890 } else if (response.ok) { 891 return response.json(); 892 } else { 893 window.dispatchMessages && window.dispatchMessages([{ 894 type: 'warning', 895 text: 'Could\u0020not\u0020remove\u0020item\u0020from\u0020quote.' 896 }]); 897 this.isLoading = false; 898 } 899 }).then(result => { 900 window.dispatchMessages && window.dispatchMessages([{ 901 type: result.success ? 'success' : 'error', 902 text: result.success 903 ? 'You\u0020removed\u0020the\u0020item.' 904 : result.error_message 905 }], result.success ? 5000 : 0) 906 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 907 }); 908 }, 909 910 quoteIsEmpty() { 911 return !this.itemsCount 912 }, 913 914 productImageAlt() { 915 return this.item.product_image.alt 916 }, 917 918 openDialog() { 919 return this.open = true 920 }, 921 922 hideDialog() { 923 return this.open = false 924 }, 925 926 removeItem() { 927 return this.deleteItemFromQuote(this.item.item_id) 928 }, 929 930 quoteCart() { 931 this.$dispatch( 932 'toggle-authentication', 933 { 934 url: `${this.$el.dataset.url}` 935 } 936 ); 937 }, 938 939 itemOptions() { 940 return this.item.options 941 }, 942 943 productImageHeight() { 944 return this.item.product_image.height 945 }, 946 947 productUrl() { 948 return this.item.product_url 949 }, 950 951 configureUrl() { 952 return this.item.configure_url 953 }, 954 955 itemQty() { 956 return this.item.qty 957 }, 958 959 productName() { 960 return this.item.product_name 961 }, 962 963 productSku() { 964 return this.item.product_sku 965 }, 966 967 optionValue() { 968 return this.option.value 969 }, 970 971 productPrice() { 972 return this.item.product_price 973 }, 974 975 quotecartSubtotal() {
976 return this.quotecart.subtotal 977 }, 978 979 privateContentLoadedEvent(event) { 980 return this.getData(this.$event.detail.data) 981 }, 982 983 isItemCountShow() { 984 return this.maxItemsToDisplay && this.maxItemsToDisplay < this.itemsCount 985 }, 986 987 isProductNotGrouped() { 988 return this.item.product_type !== 'grouped' 989 }, 990 991 productImageSrc() { 992 return this.item.product_image.src 993 }, 994 995 optionLabel() { 996 return this.option.label + ':' 997 }, 998 999 productImageWidth() { 1000 return this.item.product_image.width 1001 }, 1002 1003 eventListeners: { 1004 ['@toggle-amquote.window']() { 1005 this.openDialog(); 1006 } 1007 } 1008 } 1009 } 1010 1011 window.addEventListener( 1012 'alpine:init', 1013 () => Alpine.data( 1014 'initAmQuoteCartDrawer', 1015 initAmQuoteCartDrawer 1016 ), 1017 {once: true} 1018 ); 1019</script>
1019 1020</div>
1020<script> 1021 function initHeader() { 1022 return { 1023 vatMode: vatSwitcher.getVatMode() || 'excluding', 1024 searchOpen: false, 1025 cart: {}, 1026 isCartOpen: false, 1027 isMobile: true, 1028 prevScroll: 0, // Tracks the previous scroll position 1029 visible: true, // Tracks header visibility 1030 init() { 1031 this.checkIsSearchMobile(); 1032 1033 // Initialize scroll listener for header visibility 1034 this.prevScroll = window.pageYOffset || document.documentElement.scrollTop; 1035 window.addEventListener('scroll', this.handleScroll.bind(this)); 1036 1037 // Initialize header height sync 1038 this.updatePageHeaderHeight(); 1039 window.addEventListener('resize', this.updatePageHeaderHeight.bind(this)); 1040 }, 1041 handleScroll() { 1042 const currentScroll = window.pageYOffset || document.documentElement.scrollTop; 1043 1044 // Show header when scrolling up by more than 5px 1045 if (this.prevScroll - currentScroll > 5) { 1046 this.visible = true; 1047 } 1048 // Hide header when scrolling down 1049 else if (currentScroll > 100 && (currentScroll - this.prevScroll > 5)) { 1050 this.visible = false; 1051 } 1052 1053 this.prevScroll = currentScroll; 1054 }, 1055 updatePageHeaderHeight() { 1056 // Get the height of #header 1057 const headerHeight = this.$el.offsetHeight; 1058 1059 // Apply the height to the page header 1060 const pageHeader = document.querySelector('header.page-header'); 1061 if (pageHeader) { 1062 pageHeader.style.height = `${headerHeight}px`; 1063 } 1064 1065 this.checkIsSearchMobile(); 1066 }, 1067 getData(data) { 1068 if (data.cart) { 1069 this.cart = data.cart; 1070 } 1071 }, 1072 isCartEmpty() { 1073 return !this.cart.summary_count; 1074 }, 1075 checkIsSearchMobile() { 1076 const { 1077 searchContainerMobile: mobileEl, 1078 searchContainerDesktop: desktopEl, 1079 searchForm 1080 } = this.$refs; 1081 1082 if (!searchForm) return; 1083 1084 // Detect mobile 1085 const isMobile = mobileEl 1086 ? getComputedStyle(mobileEl).display !== 'none' 1087 : window.matchMedia('(max-width: 1023px)').matches; 1088 1089 this.isMobile = isMobile; 1090 1091 const targetContainer = isMobile ? mobileEl : desktopEl; 1092 1093 if (!targetContainer) return; 1094 1095 // Move only if not already inside target 1096 if (!targetContainer.contains(searchForm)) { 1097 targetContainer.appendChild(searchForm); 1098 } 1099 }, 1100 toggleCart(event) { 1101 if (event.detail && event.detail.isOpen !== undefined) { 1102 this.isCartOpen = event.detail.isOpen; 1103 if (!this.isCartOpen && this.$refs && this.$refs.cartButton) { 1104 this.$refs.cartButton.focus(); 1105 } 1106 } else { 1107 this.isCartOpen = true; 1108 } 1109 }, 1110 eventListeners: { 1111 ['@private-content-loaded.window'](event) { 1112 this.getData(event.detail.data); 1113 }, 1114 ['@resize.window.debounce']() { 1115 this.checkIsSearchMobile(); 1116 }, 1117 ['@visibilitychange.window.debounce']() { 1118 this.checkIsSearchMobile(); 1119 }, 1120 ['@update-minicart-vat-mode.window'](event) { 1121 this.vatMode = event.detail; 1122 }, 1123 }, 1124 }; 1125 } 1126 1127 function initCompareHeader() { 1128 return { 1129 compareProducts: null, 1130 itemCount: 0, 1131 receiveCompareData(data) { 1132 if (data['compare-products']) { 1133 this.compareProducts = data['compare-products']; 1134 this.itemCount = this.compareProducts.count; 1135 } 1136 } 1137 } 1138 } 1139</script>
1139 1140<div id="header" 1141 class="bg-white transition-transform duration-300 ease-in-out fixed top-0 left-0 w-full z-[10]" 1142 x-data="initHeader()" 1143 x-bind="eventListeners" 1144 x-init="init()" 1145 :class="{'-translate-y-full': !visible}" 1146 @private-content-loaded.window="getData(event.detail.data)" 1147> 1148 <div class="header-notification"> 1149 <div class="liveview-content" 1150 data-liveview-content 1151 > 1152 <div data-liveview-element="row" 1153 class="not-prose" 1154 > 1155 <div 1156 data-liveview-element="text" 1157 class="cmsb18-text-center cmsb18-text-sm cmsb18-font-normal" 1158 > 1159 <p>Sign Up To Our News Letter For All The Latest New Products</p></div> 1160 </div> 1161 </div> 1162 </div> 1163 <div class="header-info"> 1164 <div class="container py-3.5 flex gap-4 items-center"> 1165 <div class="header-contact"> 1166 <ul> 1167 <li><a href="mailto:[email protected]">[email protected]</a></li> 1168 </ul> 1169 </div> 1170 <div class="quick-links"> 1171 <ul> 1172 <li><a href="/kits.html">Kits</a></li> 1173 <li><a href="/clearance.html">Clearance</a></li> 1174 <li><a href="https://www.customandcommercial.com/line-drawings/">Line Drawings</a></li> 1175 <li><a href="https://www.customandcommercial.com/lock-shop/">Lock Shop</a></li> 1176 <li><a href="https://www.customandcommercial.com/vwshows/">Shows</a></li> 1177 <li><a href="https://www.customandcommercial.com/contact/">Contact</a></li> 1178 </ul> 1179 </div> 1180 </div> 1181 </div> 1182 <div class="container relative py-2.5 lg:py-3.5 flex gap-4 md:items-center"> 1183 1184<a 1185 class="inline-block align-middle w-4/12 lg:w-2/12 xl:w-3/12" 1186 href="https://www.customandcommercial.com/" 1187 title="Go to Home page" 1188> 1189 <img 1190 src="https://www.customandcommercial.com/media/logo/stores/1/PHOTO-2026-08-28-14-28-43_2_.jpg" 1191 alt="Custom & Commercial" 1192 width="700" 1193 height="400" 1194 loading="eager" 1195 fetchpriority="high" 1196 > 1197 </a> 1198 <div x-ref="searchContainerDesktop" class="hidden lg:block xl:w-5/12 lg:ml-auto min-h-a11y px-6"></div> 1199 <div class="ml-auto flex gap-0.5 w-9/12 lg:w-4/12 justify-end max767:flex-wrap md:items-center"> 1200 <div class="max767:hidden vat-switcher-block-header max767:my-5 max767:order-5 lg:relative lg:top-0 lg:inline-block lg:text-bodydark"> 1201 <div x-data="initVatSwitcher()" x-bind="eventListeners" class="vat-toggle-container mr-1.5 lg:mr-7"> 1202 <div class="vat-label"><span>Exc. Vat</span></div> 1203 <label class="toggle-switch"> 1204 <input type="checkbox" x-on:change="toggleVatMode(event)" x-model="isChecked" /> 1205 <span class="slider"></span> 1206 </label> 1207 <div class="vat-label"><span>Inc. Vat</span></div> 1208</div> 1209
1210<script> 1211 function initVatSwitcher() { 1212 const [excluding, including] = Object.keys({"excluding":"Excl. VAT","including":"Incl. VAT"}); 1213 1214 return { 1215 isChecked: vatSwitcher.getVatMode() === including, 1216 1217 toggleVatMode(event) { 1218 const value = event.target.checked ? including : excluding; 1219 this.setVatMode(value); 1220 1221 const isCategoryPage = document.body.classList.contains('catalog-category-view') 1222 || document.body.classList.contains('sales-order-invoice') 1223 || document.body.classList.contains('sales-order-view'); 1224 (isCategoryPage ? this.dispatchUpdate : this.updateVisibility).call(this, value); 1225 1226 const isProductPage = document.body.classList.contains('catalog-product-view'); 1227 if (isProductPage) { 1228 window.dispatchEvent(new CustomEvent('update-product-vat-switcher', { detail: value })); 1229 this.updateVisibility(value); 1230 } 1231 1232 const isLineDrawingCategory = document.body.classList.contains('line-drawing-layout') 1233 if (isLineDrawingCategory) { 1234 this.updateVisibility(value); 1235 } 1236 window.dispatchEvent(new CustomEvent('update-minicart-vat-mode', { detail: value })); 1237 window.dispatchEvent(new CustomEvent('update-cart-drawer-vat-mode', { detail: value })); 1238 }, 1239 1240 updateVatMode(value) { 1241 this.isChecked = value === including; 1242 this.updateVisibility(value); 1243 }, 1244 1245 setVatMode(value) { 1246 vatSwitcher.setVatMode(value); 1247 }, 1248 1249 dispatchUpdate(value) { 1250 window.dispatchEvent(new CustomEvent('update-toolbar-vat-switcher', { detail: value })); 1251 }, 1252 1253 updateVisibility(value) { 1254 const toggleVisibility = (taxType, action) => { 1255 document.querySelectorAll(`.price-${taxType}-tax`).forEach(el => el.classList[action]('hidden')); 1256 }; 1257 1258 toggleVisibility(value === including ? excluding : including, 'add'); 1259 toggleVisibility(value, 'remove'); 1260 }, 1261 eventListeners: { 1262 ['@update-vat-switcher.window'](event) { 1263 this.updateVatMode(event.detail); 1264 } 1265 } 1266 }; 1267 } 1268</script>
1268 1269 </div> 1270 <!--Customer Icon & Dropdown--> 1271 <!-- BLOCK header.customer --> 1272<div 1273 class="relative inline-block" 1274 x-data="{ open: false }" 1275 @keyup.escape="open = false" 1276 @click.outside="open = false" 1277> 1278 <button 1279 type="button" 1280 id="customer-menu" 1281 class="block flex flex-col text-xs font-semibold leading-4" 1282 @click="open = !open" 1283 :aria-expanded="open ? 'true' : 'false'" 1284 aria-label="Account Dashboard" 1285 aria-haspopup="true" 1286 > 1287 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 44 44" fill="none" class="text-white bg-ctaprimary rounded-full w-[30px] md:w-11 md:h-11 h-[30px] mb-1.5 mx-auto" aria-hidden="true"> 1288<rect width="44" height="44" rx="22" fill="#E18438"/> 1289<path d="M25.75 16C25.75 16.9946 25.3549 17.9484 24.6516 18.6517C23.9484 19.3549 22.9945 19.75 22 19.75C21.0054 19.75 20.0516 19.3549 19.3483 18.6517C18.6451 17.9484 18.25 16.9946 18.25 16C18.25 15.0054 18.6451 14.0516 19.3483 13.3483C20.0516 12.6451 21.0054 12.25 22 12.25C22.9945 12.25 23.9484 12.6451 24.6516 13.3483C25.3549 14.0516 25.75 15.0054 25.75 16ZM14.501 30.118C14.5331 28.1504 15.3373 26.2742 16.7402 24.894C18.143 23.5139 20.0321 22.7405 22 22.7405C23.9679 22.7405 25.857 23.5139 27.2598 24.894C28.6626 26.2742 29.4668 28.1504 29.499 30.118C27.1464 31.1968 24.5881 31.7535 22 31.75C19.324 31.75 16.784 31.166 14.501 30.118Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 1290</svg> 1291 <span class="font-serif inline-block">Account</span> 1292 </button> 1293 <nav 1294 class="absolute right-0 z-20 w-40 py-2 mt-2 -mr-4 px-1 overflow-auto origin-top-right rounded-sm 1295 shadow-lg sm:w-48 lg:mt-3 bg-container-lighter" 1296 x-cloak 1297 x-show="open" 1298 x-transition 1299 aria-labelledby="customer-menu" 1300 > 1301 1302<a id="customer.header.sign.in.link" 1303 class="block px-4 py-2 lg:px-5 lg:py-2 hover:bg-gray-100" 1304 onclick="hyva.setCookie && hyva.setCookie( 1305 'login_redirect', 1306 window.location.href, 1307 1 1308 )" 1309 href="https://www.customandcommercial.com/customer/account/index/" 1310 title="Sign In" 1311> 1312 Sign In</a> 1313 1314 <a id="customer.header.register.link" 1315 class="block px-4 py-2 lg:px-5 lg:py-2 hover:bg-gray-100" 1316 href="https://www.customandcommercial.com/customer/account/create/" 1317 title="Create an Account" 1318 > 1319 Create an Account </a> 1320 </nav> 1321</div> 1322<!-- /BLOCK header.customer --> <!-- Additional Header Elements --> 1323 <!-- Wishlist not showing needs fixing --> 1324 1325 <a 1326 id="wishlist-link" 1327 class="block flex flex-col text-xs font-semibold leading-4 items-center mx-2.5" 1328 href="https://www.customandcommercial.com/wishlist/" 1329 title="Wishlist" 1330 > 1331 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 44 44" fill="none" class="text-white bg-ctaprimary rounded-full w-[30px] md:w-11 md:h-11 h-[30px] mb-1.5 mx-auto" role="img"> 1332<rect width="44" height="44" rx="22" fill="#E18438"/> 1333<path d="M31 18.25C31 15.765 28.901 13.75 26.312 13.75C24.377 13.75 22.715 14.876 22 16.483C21.285 14.876 19.623 13.75 17.687 13.75C15.1 13.75 13 15.765 13 18.25C13 25.47 22 30.25 22 30.25C22 30.25 31 25.47 31 18.25Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 1334<title>wishlist</title></svg> 1335 <span 1336 class="font-serif inline-block text-center text-xs font-semibold text-center mx-auto tabular-nums" 1337 aria-hidden="true" 1338 >Wishlist</span> 1339 </a> 1340 1341 <!--Cart Icon--> 1342 <button 1343 id="menu-cart-icon" 1344 class="block flex flex-col text-xs font-semibold leading-4 items-center" 1345 x-ref="cartButton" 1346 :aria-disabled="isCartEmpty()" 1347 :aria-label="` 1348 Toggle minicart, 1349 ${isCartEmpty() 1350 ? 'Cart is empty' 1351 : cart.summary_count > 1 1352 ? hyva.str('%1 items', cart.summary_count) 1353 : hyva.str('%1 item', cart.summary_count) 1354 }`" 1355 @click.prevent.stop="() => {
1356 $dispatch('toggle-cart', { isOpen: true }) 1357 }" 1358 @toggle-cart.window="toggleCart($event)" 1359 :aria-expanded="isCartOpen" 1360 aria-haspopup="dialog" 1361 > 1362 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 25 24" fill="none" class="text-white bg-ctaprimary rounded-full p-1.5 w-[30px] h-[30px] md:w-11 md:h-11 mb-1.5 mx-auto" aria-hidden="true"> 1363<path d="M16.25 10.5V6C16.25 5.00544 15.8549 4.05161 15.1516 3.34835C14.4484 2.64509 13.4945 2.25 12.5 2.25C11.5054 2.25 10.5516 2.64509 9.84833 3.34835C9.14507 4.05161 8.74998 5.00544 8.74998 6V10.5M20.106 8.507L21.369 20.507C21.439 21.172 20.919 21.75 20.25 21.75H4.74998C4.59219 21.7502 4.43613 21.7171 4.29193 21.6531C4.14774 21.589 4.01863 21.4953 3.91301 21.3781C3.80738 21.2608 3.7276 21.1227 3.67884 20.9726C3.63008 20.8226 3.61343 20.6639 3.62999 20.507L4.89399 8.507C4.92314 8.23056 5.05361 7.9747 5.26024 7.78876C5.46686 7.60281 5.73501 7.49995 6.01298 7.5H18.987C19.563 7.5 20.046 7.935 20.106 8.507ZM9.12498 10.5C9.12498 10.5995 9.08548 10.6948 9.01515 10.7652C8.94482 10.8355 8.84944 10.875 8.74998 10.875C8.65053 10.875 8.55515 10.8355 8.48482 10.7652C8.41449 10.6948 8.37498 10.5995 8.37498 10.5C8.37498 10.4005 8.41449 10.3052 8.48482 10.2348C8.55515 10.1645 8.65053 10.125 8.74998 10.125C8.84944 10.125 8.94482 10.1645 9.01515 10.2348C9.08548 10.3052 9.12498 10.4005 9.12498 10.5ZM16.625 10.5C16.625 10.5995 16.5855 10.6948 16.5152 10.7652C16.4448 10.8355 16.3494 10.875 16.25 10.875C16.1505 10.875 16.0551 10.8355 15.9848 10.7652C15.9145 10.6948 15.875 10.5995 15.875 10.5C15.875 10.4005 15.9145 10.3052 15.9848 10.2348C16.0551 10.1645 16.1505 10.125 16.25 10.125C16.3494 10.125 16.4448 10.1645 16.5152 10.2348C16.5855 10.3052 16.625 10.4005 16.625 10.5Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 1364</svg> 1365 <span 1366 class="lg:inline-block text-center uppercase font-serif text-xs font-semibold leading-4 tabular-nums text-center" 1367 aria-hidden="true" 1368 > 1369 <template x-if="cart.subtotal !== undefined"> 1370 <div x-html="vatMode === 'excluding' ? cart.subtotal_excl_tax : cart.subtotal_incl_tax"></div> 1371 </template> 1372 <template x-if="cart.subtotal === undefined"> 1373 <span><span class="price" x-text="hyva.formatPrice(0)"></span></span> 1374 </template> 1375 </span> 1376 </button> 1377 <!--Mobile Navigation--> 1378 1379
1379<script> 1380 'use strict'; 1381 1382 const initMenuMobile_6ab315cb956cb = () => ({ 1383 open: false, 1384 openMenu(event) { 1385 this.open = true 1386 this.$nextTick(() => hyva.trapFocus(this.$refs['menu-dialog'])) 1387 // Prevent from body scrolling while mobile menu opened 1388 document.body.classList.add('fixed', 'w-full', 'menu-shown') 1389 }, 1390 closeMenu(event) { 1391 event.stopPropagation() 1392 document.body.classList.remove('fixed', 'w-full', 'menu-shown') 1393 this.open = false 1394 window.dispatchEvent(new CustomEvent('reset-menu')); 1395 this.$nextTick(() => this.$refs['open-menu-button'].focus() || hyva.releaseFocus()) 1396 }, 1397 toggleSubmenu(index) { 1398 this[index] = !this[index] 1399 1400 if (this[index]) { 1401 const focusableElements = 'button:not([disabled]), a[href]:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])' 1402 this.$nextTick(() => { 1403 this.$refs['submenu-' + index]?.querySelector(focusableElements)?.focus() 1404 }) 1405 } 1406 }, 1407 }) 1408 </script>
1408 1409 <nav 1410 class=" 1411 snowdog-topmenu-mobile z-20 order-2 ml-2.5 text-primary-darker sm:order-1 navigation lg:hidden 1412 " 1413 x-data="initMenuMobile_6ab315cb956cb()" 1414 @keydown.escape="open && closeMenu" 1415 @click.outside="open && closeMenu" 1416 aria-label="Main menu" 1417 role="navigation" 1418 > 1419 <button x-ref="open-menu-button" 1420 @click="openMenu" 1421 x-show="!open" 1422 type="button" 1423 aria-label="Open menu" 1424 :aria-expanded="open" 1425 aria-haspopup="menu" 1426 > 1427 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="text-white bg-ctaprimary rounded-full p-1.5 w-[30px] h-[30px] md:w-11 md:h-11 mx-auto" width="24" height="24" aria-hidden="true"> 1428 <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/> 1429</svg> 1430 <span class="font-serif inline-block text-center text-xs font-semibold text-center mx-auto tabular-nums"> 1431 Menu </span> 1432 </button> 1433 <div x-show="open" 1434 x-cloak 1435 class="flex justify-end border-b border-white bg-white h-full"> 1436 <button 1437 @click="closeMenu" 1438 class="flex items-center flex-col h-full justify-between" 1439 aria-label="Close menu" 1440 type="button" 1441 > 1442 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 30 30" fill="none" class="w-[30px] h-[30px] md:w-11 md:h-11 " role="img"> 1443<rect width="30" height="30" rx="15" fill="#48321F"/> 1444<path d="M10 20L20 10M10 10L20 20" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 1445<title>menu-close</title></svg> 1446 <span class="font-serif inline-block text-center text-xs font-semibold text-center mx-auto tabular-nums"> 1447 Close </span> 1448 </button> 1449 </div> 1450 <div class="backdrop" 1451 x-show="open" 1452 x-transition:enter="ease-in-out duration-500" 1453 x-transition:enter-start="opacity-0" 1454 x-transition:enter-end="opacity-100" 1455 x-transition:leave="ease-in-out duration-500" 1456 x-transition:leave-start="opacity-100" 1457 x-transition:leave-end="opacity-0" 1458 @click="closeMenu" 1459 x-cloak 1460 ></div> 1461 <div class="menu-content fixed z-30 top-0 left-0 w-full max-w-lg h-full bg-lightorange overflow-y-auto overflow-x-hidden sm:transform sm:transition sm:ease-in-out sm:duration-700" 1462 :class="{ 1463 '-translate-x-full' : !open, 1464 'translate-x-0 shadow-2xl' : open, 1465 }" 1466 x-cloak 1467 x-ref="menu-dialog" 1468 role="dialog" 1469 aria-modal="true" 1470 aria-label="Main menu" 1471 :aria-hidden="open ? 'false' : 'true'" 1472 > 1473 <div x-show="open" 1474 class="flex flex-col-reverse" 1475 > 1476 <ul class="static-links"> 1477 <li class="level-1 !bg-lightorange"> 1478 <a class="flex !text-bodydark !normal-case items-center w-full !no-underline" href="https://www.customandcommercial.com/kits.html">Kits</a> 1479 </li> 1480 <li class="level-1 !bg-lightorange"> 1481 <a class="flex !text-bodydark !normal-case items-center w-full !no-underline" href="https://www.customandcommercial.com/line-drawings/">Line Drawings</a> 1482 </li> 1483 <li class="level-1 !bg-lightorange"> 1484 <a class="flex !text-bodydark !normal-case items-center w-full !no-underline" href="https://www.customandcommercial.com/lock-shop/">Lock Shop</a> 1485 </li> 1486 <li class="level-1 !bg-lightorange"> 1487 <a class="flex !text-bodydark !normal-case items-center w-full !no-underline" href="https://www.customandcommercial.com/vwshows/">Shows</a> 1488 </li> 1489 <li class="level-1 !bg-lightorange"> 1490 <a class="flex !text-bodydark !normal-case items-center w-full !no-underline" href="https://www.customandcommercial.com/contact/">Contact</a> 1491 </li> 1492 <li class="level-1 !bg-lightorange"> 1493 <a class="flex !text-bodydark !normal-case items-center w-full !no-underline" href="https://www.customandcommercial.com/wishlist/">Wishlist</a> 1494 </li> 1495 </ul> 1496 <ul> 1497 <li x-data="{'node95': false }" 1498 @reset-menu.window="node95 = false" 1499 class="level-1 " 1500 > 1501 <div class="group"> 1502 <a href="https://www.customandcommercial.com/brazilian-s
1502plitscreen.html" 1503 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1504 data-menu="menu-95" 1505 > 1506 Brazilian Splitscreen</a> 1507 </div> 1508 </li> 1509 <li x-data="{'node97': false }" 1510 @reset-menu.window="node97 = false" 1511 class="level-1 " 1512 > 1513 <div class="group"> 1514 <a href="https://www.customandcommercial.com/split-screen.html" 1515 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1516 data-menu="menu-97" 1517 > 1518 Split Screen</a> 1519 </div> 1520 </li> 1521 <li x-data="{'node98': false }" 1522 @reset-menu.window="node98 = false" 1523 class="level-1 " 1524 > 1525 <div class="group"> 1526 <a href="https://www.customandcommercial.com/beetle.html" 1527 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1528 data-menu="menu-98" 1529 > 1530 Beetle</a> 1531 </div> 1532 </li> 1533 <li x-data="{'node99': false }" 1534 @reset-menu.window="node99 = false" 1535 class="level-1 " 1536 > 1537 <div class="group"> 1538 <a href="https://www.customandcommercial.com/baywindow.html" 1539 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1540 data-menu="menu-99" 1541 > 1542 Baywindow</a> 1543 </div> 1544 </li> 1545 <li x-data="{'node100': false }" 1546 @reset-menu.window="node100 = false" 1547 class="level-1 " 1548 > 1549 <div class="group"> 1550 <a href="https://www.customandcommercial.com/brazilian-bay.html" 1551 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1552 data-menu="menu-100" 1553 > 1554 Brazilian Bay</a> 1555 </div> 1556 </li> 1557 <li x-data="{'node101': false }" 1558 @reset-menu.window="node101 = false" 1559 class="level-1 " 1560 > 1561 <div class="group"> 1562 <a href="https://www.customandcommercial.com/type-25.html" 1563 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1564 data-menu="menu-101" 1565 > 1566 Type 25</a> 1567 </div> 1568 </li> 1569 <li x-data="{'node102': false }" 1570 @reset-menu.window="node102 = false" 1571 class="level-1 " 1572 > 1573 <div class="group"> 1574 <a href="https://www.customandcommercial.com/type-3.html" 1575 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1576 data-menu="menu-102" 1577 > 1578 Type 3 </a> 1579 </div> 1580 </li> 1581 <li x-data="{'node106': false }" 1582 @reset-menu.window="node106 = false" 1583 class="level-1 " 1584 > 1585 <div class="group"> 1586 <a href="https://www.customandcommercial.com/t4.html" 1587 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1588 data-menu="menu-106" 1589 > 1590 T4</a> 1591 </div> 1592 </li> 1593 <li x-data="{'node107': false }" 1594 @reset-menu.window="node107 = false" 1595 class="level-1 " 1596 > 1597 <div class="group"> 1598 <a href="https://www.customandcommercial.com/t5.html" 1599 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1600 data-menu="menu-107" 1601 > 1602 T5</a> 1603 </div> 1604 </li> 1605 <li x-data="{'node103': false }" 1606 @reset-menu.window="node103 = false" 1607 class="level-1 " 1608 > 1609 <div class="group"> 1610 <a href="https://www.customandcommercial.com/karmann-ghia.html" 1611 class="cursor-pointer snowdog-menu-link-topmenu-mobile " 1612 data-menu="menu-103" 1613 > 1614 Karmann Ghia </a> 1615 </div> 1616 </li> 1617 </ul> 1618 <div class="max767:hidden md:flex justify-end border-b border-white bg-white"> 1619 <button 1620 @click="closeMenu" 1621 class="flex justify-center items-center w-16 h-16" 1622 aria-label="Close menu" 1623 type="button" 1624 > 1625 <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"> 1626 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1627</svg> 1628 </button> 1629 </div> 1630 </div> 1631 </div> 1632 </nav> 1633 </div> 1634 </div> 1635 1636 <div class="bg-white lg:bg-darkbrown"> 1637 <div class="relative container hidden lg:block"> 1638 1639
1639<script> 1640 'use strict'; 1641 1642 const initTopmenuDesktop_6ab315cb99e0c = () => ({ 1643 openSubmenuId: null, 1644 toggleSubmenu(menuNode) { 1645 if (menuNode === this.openSubmenuId) { 1646 this.openSubmenuId = null 1647 hyva.releaseFocus() 1648 } else { 1649 this.openSubmenuId = menuNode 1650 this.$nextTick(() => { 1651 hyva.trapFocus(this.$refs['submenu-' + menuNode]) 1652 }) 1653 } 1654 }, 1655 onSubmenuEscape(event) { 1656 event.stopPropagation() 1657 this.$refs['sr-button-' + this.openSubmenuId]?.focus() 1658 this.openSubmenuId = null 1659 } 1660 }) 1661 </script>
1661 1662 1663 <div x-data="initTopmenuDesktop_6ab315cb99e0c()" 1664 class=" 1665 navigation hidden w-full lg:block 1666 " 1667 > 1668 <nav 1669 class="snowdog-topmenu-desktop container lg:block" 1670 aria-label="Main menu" 1671 > 1672 <ul 1673 class="relative flex justify-between h-12 font-serif" 1674 @mouseleave="openSubmenuId = null" 1675 > 1676 <li class=" 1677 flex items-center justify-between px-3 hover:!bg-ctaprimary 1678 " 1679 :class="{ 1680 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '85' 1681 }" 1682 @mouseenter="openSubmenuId = '85'" 1683 @keydown.escape="onSubmenuEscape" 1684 > 1685 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1686 1687<a href="https://www.customandcommercial.com/brazilian-splitscreen.html" 1688 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1689 data-menu="menu-85" 1690 > 1691 Brazilian Splitscreen</a> 1692 </div> 1693 </li> 1694 <li class=" 1695 flex items-center justify-between px-3 hover:!bg-ctaprimary 1696 " 1697 :class="{ 1698 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '87' 1699 }" 1700 @mouseenter="openSubmenuId = '87'" 1701 @keydown.escape="onSubmenuEscape" 1702 > 1703 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1704 1705<a href="https://www.customandcommercial.com/split-screen.html" 1706 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1707 data-menu="menu-87" 1708 > 1709 Split Screen</a> 1710 </div> 1711 </li> 1712 <li class=" 1713 flex items-center justify-between px-3 hover:!bg-ctaprimary 1714 " 1715 :class="{ 1716 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '88' 1717 }" 1718 @mouseenter="openSubmenuId = '88'" 1719 @keydown.escape="onSubmenuEscape" 1720 > 1721 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1722 1723<a href="https://www.customandcommercial.com/beetle.html" 1724 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1725 data-menu="menu-88" 1726 > 1727 Beetle</a> 1728 </div> 1729 </li> 1730 <li class=" 1731 flex items-center justify-between px-3 hover:!bg-ctaprimary 1732 " 1733 :class="{ 1734 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '89' 1735 }" 1736 @mouseenter="openSubmenuId = '89'" 1737 @keydown.escape="onSubmenuEscape" 1738 > 1739 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1740 1741<a href="https://www.customandcommercial.com/baywindow.html" 1742 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1743 data-menu="menu-89" 1744 > 1745 Baywindow</a> 1746 </div> 1747 </li> 1748 <li class=" 1749 flex items-center justify-between px-3 hover:!bg-ctaprimary 1750 " 1751 :class="{ 1752 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '90' 1753 }" 1754 @mouseenter="openSubmenuId = '90'" 1755 @keydown.escape="onSubmenuEscape" 1756 > 1757 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1758 1759<a href="https://www.customandcommercial.com/brazilian-bay.html" 1760 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1761 data-menu="menu-90" 1762 > 1763 Brazilian Bay</a> 1764 </div> 1765 </li> 1766 <li class=" 1767 flex items-center justify-between px-3 hover:!bg-ctaprimary 1768 " 1769 :class="{ 1770 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '91' 1771 }" 1772 @mouseenter="openSubmenuId = '91'" 1773 @keydown.escape="onSubmenuEscape" 1774 > 1775 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1776 1777<a href="https://www.customandcommercial.com/type-25.html" 1778 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1779 data-menu="menu-91" 1780 > 1781 Type 25</a> 1782 </div> 1783 </li> 1784 <li class=" 1785 flex items-center justify-between px-3 hover:!bg-ctaprimary 1786 " 1787 :class="{ 1788 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '92' 1789 }" 1790 @mouseenter="openSubmenuId = '92'" 1791 @keydown.escape="onSubmenuEscape" 1792 > 1793 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1794 1795<a href="https://www.customandcommercial.com/type-3.html" 1796 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1797 data-menu="menu-92" 1798 > 1799 Type 3 </a> 1800 </div> 1801 </li> 1802 <li class=" 1803 flex items-center justify-between px-3 hover:!bg-ctaprimary 1804 " 1805 :class="{ 1806 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '104' 1807 }" 1808 @mouseenter="openSubmenuId = '104'" 1809 @keydown.escape="onSubmenuEscape" 1810 > 1811 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1812 1813<a href="https://www.customandcommercial.com/t4.html" 1814 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1815 data-menu="menu-104" 1816 > 1817 T4</a> 1818 </div> 1819 </li> 1820 <li class=" 1821 flex items-center justify-between px-3 hover:!bg-ctaprimary 1822 " 1823 :class="{ 1824 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '105' 1825 }" 1826 @mouseenter="openSubmenuId = '105'" 1827 @keydown.escape="onSubmenuEscape" 1828 > 1829 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1830 1831<a href="https://www.customandcommercial.com/t5.html" 1832 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1833 data-menu="menu-105" 1834 > 1835 T5</a> 1836 </div> 1837 </li> 1838 <li class=" 1839 flex items-center justify-between px-3 hover:!bg-ctaprimary 1840 " 1841 :class="{ 1842 'bg-ctaprimary shadow-md' : '' && openSubmenuId === '93' 1843 }" 1844 @mouseenter="openSubmenuId = '93'" 1845 @keydown.escape="onSubmenuEscape" 1846 > 1847 <div class="group uppercase [&>a]:py-3.5 [&>a]:!text-base [&>a]:text-white [&>a]:font-semibold [&>a]:hover:!text-white"> 1848 1849<a href="https://www.customandcommercial.com/karmann-ghia.html" 1850 class="snowdog-menu-link font-serif text-sm text-bodydark font-normal hover:!text-black " 1851 data-menu="menu-93" 1852 > 1853 Karmann Ghia </a> 1854 </div> 1855 </li> 1856 </ul> 1857 </nav> 1858 </div> 1859 </div> 1860 <div x-ref="searchContainerMobile" class="container lg:hidden pb-4"> 1861 <div x-ref="searchForm"> 1862
1863<script> 1864 function initMiniSearchComponent() { 1865 "use strict"; 1866 1867 return { 1868 show:false, 1869 formSelector: "#search_mini_form", 1870 url: "https://www.customandcommercial.com/search/ajax/suggest/", 1871 destinationSelector: "#search_autocomplete", 1872 templates: {"term":{"title":"Search terms","template":"Hyva_SmileElasticsuite::core\/autocomplete\/term.phtml"},"product":{"title":"Products","template":"Hyva_SmileElasticsuite::catalog\/autocomplete\/product.phtml"},"category":{"title":"Categories","template":"Hyva_SmileElasticsuite::catalog\/autocomplete\/category.phtml"},"product_attribute":{"title":"Attributes","template":"Hyva_SmileElasticsuite::catalog\/autocomplete\/product_attribute.phtml","titleRenderer":"renderEsAutocompleteTitleAttribute"},"cms_page":{"title":"Cms page","template":"Hyva_SmileElasticsuite::cms-search\/autocomplete\/cms.phtml"}}, 1873 priceFormat: {"pattern":"\u00a3%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false}, 1874 minSearchLength: 2, 1875 searchResultsByType: {}, 1876 currentRequest: null, 1877 1878 /** 1879 * Get search results. 1880 */ 1881 getSearchResults: function () { 1882 let value = document.querySelector('#search').value.trim(); 1883 1884 if (value.length < parseInt(this.minSearchLength, 10)) { 1885 this.searchResultsByType = []; 1886 1887 return false; 1888 } 1889 1890 let url = this.url + '?' + new URLSearchParams({ 1891 q: document.querySelector('#search').value, 1892 _: Date.now() 1893 }).toString(); 1894 1895 if (this.currentRequest !== null) { 1896 this.currentRequest.abort(); 1897 } 1898 this.currentRequest = new AbortController(); 1899 1900 fetch(url, { 1901 method: 'GET', 1902 signal: this.currentRequest.signal, 1903 }).then((response) => { 1904 if (response.ok) { 1905 return response.json(); 1906 } 1907 }).then((data) => { 1908 this.show = data.length > 0; 1909 1910 this.searchResultsByType = data.reduce((acc, result) => { 1911 if (! acc[result.type]) acc[result.type] = []; 1912 acc[result.type].push(result); 1913 return acc; 1914 }, {}); 1915 }).catch((error) => { 1916 ; 1917 }); 1918 }, 1919 } 1920 } 1921</script>
1921 1922<div id="elasticsuite-search-container" x-show="true"> 1923 <div class="py-2 text-bodydark px-0" x-data="initMiniSearchComponent()" @click.away="show = false"> 1924 <form class="form minisearch" id="search_mini_form" action="https://www.customandcommercial.com/catalogsearch/result/" method="get" role="search"> 1925 <div class="relative w-full lg:max-w-[480px] mx-auto"> 1926 <label class="sr-only" for="search"> 1927 What are you looking for? </label> 1928 <input id="search" 1929 x-on:input.debounce="getSearchResults()" 1930 x-ref="searchInput" 1931 type="search" 1932 class="w-full px-5 py-3 text-lg leading-normal transition appearance-none text-bodydark 1933 focus:outline-none focus:border-transparent font-serif text-sm rounded-md border-bodydark" 1934 autocapitalize="off" autocomplete="off" autocorrect="off" 1935 name="q" 1936 value="" 1937 placeholder="What are you looking for?" 1938 maxlength="128" 1939 @search-open.window.debounce.10=" 1940 $el.focus(); 1941 $el.select(); 1942 " 1943 /> 1944 <button type="submit" class="absolute right-4 top-2.5"> 1945 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" role="img"> 1946<path d="M21 21L15.803 15.803M15.803 15.803C17.2096 14.3964 17.9998 12.4887 17.9998 10.4995C17.9998 8.51031 17.2096 6.60258 15.803 5.196C14.3965 3.78943 12.4887 2.99922 10.4995 2.99922C8.51035 2.99922 6.60262 3.78943 5.19605 5.196C3.78947 6.60258 2.99927 8.51031 2.99927 10.4995C2.99927 12.4887 3.78947 14.3964 5.19605 15.803C6.60262 17.2096 8.51035 17.9998 10.4995 17.9998C12.4887 17.9998 14.3965 17.2096 15.803 15.803Z" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 1947<title>search</title></svg> 1948 </button> 1949 </div> 1950 <div id="search_autocomplete" class="search-autocomplete relative w-full" x-show="show" style="display:none;"> 1951 <div class="search-autocomplete-block absolute z-50 w-full"> 1952 1953 <!-- Grouped block: Search terms + Categories --> 1954 <div class="search-block-group" 1955 x-show="Object.values(searchResultsByType).some(s => ['Search terms','Categories'].includes(templates[s[0].type].title))"> 1956 1957 <template x-for="searchResultByType in Object.values(searchResultsByType)"> 1958 <template x-if="['Search terms','Categories'].includes(templates[searchResultByType[0].type].title)"> 1959 <div class="search-block"> 1960 <!-- Titles --> 1961 <template x-if="templates[searchResultByType[0].type].titleRenderer === undefined"> 1962 <div class="font-serif text-lg font-semibold mb-[20px]" 1963 x-text="templates[searchResultByType[0].type].title"> 1964 </div> 1965 </template> 1966 1967 <template x-if="templates[searchResultByType[0].type].titleRenderer !== undefined"> 1968 <div class="font-serif text-lg font-semibold mb-[20px]" 1969 x-text="window[templates[searchResultByType[0].type].titleRenderer](searchResultByType)"> 1970 </div> 1971 </template> 1972 1973 <!-- Items --> 1974 <div class="item-grid"> 1975 <template x-for="searchResult in searchResultByType"> 1976 <div class="product-item"> 1977 1978<template x-if="searchResult.type == 'term'"> 1979 <a class="w-full block py-2" 1980 x-bind:href="'https://www.customandcommercial.com/catalogsearch/result/?q=' + searchResult.title" 1981 :title="searchResult.title">
1982 <span class="text-sm" x-text="searchResult.title"></span> 1983 <span class="text-sm" x-text="'(' + searchResult.num_results + ')'"></span> 1984 </a> 1985</template> 1986 1987<template x-if="searchResult.type == 'product'"> 1988 <a class="w-full block" :href="searchResult.url" :title="searchResult.title"> 1989 <div class="flex flex-col"> 1990 <div> 1991 <img :src="searchResult.image" class="inline-block" /> 1992 </div> 1993 <div> 1994 <span class="product-name text-sm font-semibold mb-2.5 block" x-text="searchResult.title"></span> 1995 <span class="product-price text-sm font-semibold" x-html="searchResult.price"></span> 1996 </div> 1997 </div> 1998 </a> 1999</template> 2000 2001<template x-if="searchResult.type == 'category'"> 2002 <a class="1 w-full block py-2" :href="searchResult.url" :title="searchResult.title"> 2003 <span class="text-sm italic block text-gray-500" x-text="searchResult.breadcrumb.join(' > ').concat(' > ')"></span> 2004 <span class="text-sm" x-text="searchResult.title"></span> 2005 </a> 2006</template> 2007 2008<template x-if="searchResult.type == 'product_attribute'"> 2009 <a class="2 w-full block py-2" :href="searchResult.url" :title="searchResult.title"> 2010 <span class="text-sm" x-text="searchResult.title"></span> 2011 <span class="text-xs uppercase text-gray-500" x-text="searchResult.attribute_label" style="vertical-align: super;"></span> 2012 </a> 2013</template> 2014
2015<script> 2016 function renderEsAutocompleteTitleAttribute(data) 2017 { 2018 data = data.filter(function(item) { 2019 return item.type === 'product_attribute'; 2020 }).map(function(item) { 2021 return item['attribute_label'] 2022 }).reduce(function(prev, item) { 2023 if (item in prev) { 2024 prev[item]++; 2025 } else { 2026 prev[item] = 1; 2027 } 2028 return prev; 2029 }, {}); 2030 2031 data = Object.entries(data).sort(function(item1, item2) { 2032 return item2[1] - item1[1] 2033 }).map(function(item) {return item[0]}); 2034 2035 if (data.length > 2) { 2036 data = data.slice(0, 2); 2037 data.push('...'); 2038 } 2039 2040 return data.join(', '); 2041 } 2042</script>
2042 2043 2044<template x-if="searchResult.type == 'cms_page'"> 2045 <a class="w-full block p-2" :href="searchResult.url" :title="searchResult.title"> 2046 <span class="text-sm" x-text="searchResult.title"></span> 2047 </a> 2048</template> 2049 </div> 2050 </template> 2051 </div> 2052 </div> 2053 </template> 2054 </template> 2055 </div> 2056 2057 2058 <!-- Normal block: All other result types --> 2059 <template x-for="searchResultByType in Object.values(searchResultsByType)"> 2060 <template x-if="!['Search terms','Categories'].includes(templates[searchResultByType[0].type].title)"> 2061 <div class="search-block" 2062 :class="{'product-block' : templates[searchResultByType[0].type].title === 'Products'}"> 2063 2064 <!-- Titles --> 2065 <template x-if="templates[searchResultByType[0].type].titleRenderer === undefined"> 2066 <div class="font-serif text-lg font-semibold mb-[20px]" 2067 x-text="templates[searchResultByType[0].type].title"></div> 2068 </template> 2069 2070 <template x-if="templates[searchResultByType[0].type].titleRenderer !== undefined"> 2071 <div class="font-serif text-lg font-semibold mb-[20px]" 2072 x-text="window[templates[searchResultByType[0].type].titleRenderer](searchResultByType)"> 2073 </div> 2074 </template> 2075 2076 <!-- Items --> 2077 <div class="item-grid"> 2078 <template x-for="searchResult in searchResultByType"> 2079 <div class="product-item"> 2080 2081<template x-if="searchResult.type == 'term'"> 2082 <a class="w-full block py-2" 2083 x-bind:href="'https://www.customandcommercial.com/catalogsearch/result/?q=' + searchResult.title" 2084 :title="searchResult.title"> 2085 <span class="text-sm" x-text="searchResult.title"></span> 2086 <span class="text-sm" x-text="'(' + searchResult.num_results + ')'"></span> 2087 </a> 2088</template> 2089 2090<template x-if="searchResult.type == 'product'"> 2091 <a class="w-full block" :href="searchResult.url" :title="searchResult.title"> 2092 <div class="flex flex-col"> 2093 <div> 2094 <img :src="searchResult.image" class="inline-block" /> 2095 </div> 2096 <div> 2097 <span class="product-name text-sm font-semibold mb-2.5 block" x-text="searchResult.title"></span> 2098 <span class="product-price text-sm font-semibold" x-html="searchResult.price"></span> 2099 </div> 2100 </div> 2101 </a> 2102</template> 2103 2104<template x-if="searchResult.type == 'category'"> 2105 <a class="1 w-full block py-2" :href="searchResult.url" :title="searchResult.title"> 2106 <span class="text-sm italic block text-gray-500" x-text="searchResult.breadcrumb.join(' > ').concat(' > ')"></span> 2107 <span class="text-sm" x-text="searchResult.title"></span> 2108 </a> 2109</template> 2110 2111<template x-if="searchResult.type == 'product_attribute'"> 2112 <a class="2 w-full block py-2" :href="searchResult.url" :title="searchResult.title"> 2113 <span class="text-sm" x-text="searchResult.title"></span>
2114 <span class="text-xs uppercase text-gray-500" x-text="searchResult.attribute_label" style="vertical-align: super;"></span> 2115 </a> 2116</template> 2117
2118<script> 2119 function renderEsAutocompleteTitleAttribute(data) 2120 { 2121 data = data.filter(function(item) { 2122 return item.type === 'product_attribute'; 2123 }).map(function(item) { 2124 return item['attribute_label'] 2125 }).reduce(function(prev, item) { 2126 if (item in prev) { 2127 prev[item]++; 2128 } else { 2129 prev[item] = 1; 2130 } 2131 return prev; 2132 }, {}); 2133 2134 data = Object.entries(data).sort(function(item1, item2) { 2135 return item2[1] - item1[1] 2136 }).map(function(item) {return item[0]}); 2137 2138 if (data.length > 2) { 2139 data = data.slice(0, 2); 2140 data.push('...'); 2141 } 2142 2143 return data.join(', '); 2144 } 2145</script>
2145 2146 2147<template x-if="searchResult.type == 'cms_page'"> 2148 <a class="w-full block p-2" :href="searchResult.url" :title="searchResult.title"> 2149 <span class="text-sm" x-text="searchResult.title"></span> 2150 </a> 2151</template> 2152 </div> 2153 </template> 2154 </div> 2155 2156 </div> 2157 </template> 2158 </template> 2159 </div> 2160 </div> 2161 </form> 2162 </div> 2163</div> 2164 </div> 2165 </div> 2166 </div> 2167 2168<div class="py-3.5 bg-lightorange font-serif text-xs text-center font-medium uppercase border-b border-black-300"> 2169 <div class="lg:container"> 2170 <div class="usps-bar"> 2171 <ul class="flex justify-between"> 2172 <li class="flex items-center gap-3"> 2173 <span>Worldwide shipping</span> 2174 </li> 2175 <li class="flex items-center gap-3"> 2176 <span>Wholesale and trade customers welcome</span> 2177 </li> 2178 <li class="flex items-center gap-3"> 2179 <span>1900 unique product lines</span> 2180 </li> 2181 <li class="flex items-center gap-3"> 2182 <span>Manufacturers & importers of classic vw parts</span> 2183 </li> 2184 </ul> 2185 </div> 2186 </div> 2187</div>
2188<script> 2189 document.addEventListener('DOMContentLoaded', function () { 2190 function uspsSwiperMode() { 2191 const bar = document.querySelector('.usps-bar'); 2192 if (!bar) return; 2193 2194 const list = bar.querySelector('ul'); 2195 const items = list.querySelectorAll('li'); 2196 const isSwiper = bar.classList.contains('swiper-initialized'); 2197 const isMobile = window.innerWidth < 1024; 2198 2199 if (isMobile && !isSwiper) { 2200 // Add necessary classes 2201 list.classList.add('swiper-wrapper'); 2202 items.forEach(el => el.classList.add('swiper-slide', 'justify-center')); 2203 2204 // Init Swiper 2205 new Swiper(".usps-bar", { 2206 slidesPerView: 1, 2207 autoplay: { 2208 delay: 4000, 2209 }, 2210 breakpoints: { 2211 640: { slidesPerView: 1 }, 2212 768: { slidesPerView: 4 }, 2213 1024: { slidesPerView: 4 }, 2214 }, 2215 }); 2216 2217 } else if (!isMobile && isSwiper) { 2218 // Remove classes 2219 list.classList.remove('swiper-wrapper'); 2220 items.forEach(el => el.classList.remove('swiper-slide', 'justify-center')); 2221 2222 // Destroy instance 2223 bar.swiper.destroy(true, true); 2224 } 2225 } 2226 2227 window.addEventListener('load', uspsSwiperMode); 2228 window.addEventListener('resize', uspsSwiperMode); 2229 }); 2230</script>
2230 2231 <!-- BLOCK authentication-popup -->
2231<script> 2232 function initAuthentication() { 2233 return { 2234 open: false, 2235 close() { 2236 this.open = false; 2237 }, 2238 forceAuthentication: false, 2239 checkoutUrl: 'https://www.customandcommercial.com/checkout/index/', 2240 errors: 0, 2241 hasCaptchaToken: 0, 2242 displayErrorMessage: false, 2243 errorMessages: [], 2244 setErrorMessages(message) { 2245 this.errorMessages = [message]; 2246 this.displayErrorMessage = true; 2247 }, 2248 submitForm() { 2249 // Do not rename $form, the variable is expected to be declared in the recaptcha output 2250 const $form = document.querySelector('#login-form'); 2251 2252 if (this.errors === 0) { 2253 this.dispatchLoginRequest($form); 2254 } 2255 }, 2256 onPrivateContentLoaded() { 2257 const data = this.$event.detail.data; 2258 const isLoggedIn = data.customer && data.customer.firstname; 2259 if (data.cart && !isLoggedIn) { 2260 this.forceAuthentication = !data.cart.isGuestCheckoutAllowed; 2261 } 2262 }, 2263 redirectIfAuthenticated() { 2264 const event = this.$event; 2265 this.open = this.forceAuthentication; 2266 2267 if (event.detail && event.detail.url) { 2268 this.checkoutUrl = event.detail.url; 2269 } 2270 if (!this.forceAuthentication) { 2271 window.location.href = this.checkoutUrl; 2272 } 2273 }, 2274 resetErrors() { 2275 this.errors = 0; 2276 }, 2277 dispatchLoginRequest(form) { 2278 this.isLoading = true; 2279 const username = this.$refs['customer-email'].value; 2280 const password = this.$refs['customer-password'].value; 2281 const formKey = hyva.getFormKey(); 2282 const bodyFields = { 2283 'username': username, 2284 'password': password, 2285 'formKey': formKey 2286 }; 2287 const fieldName = 'g\u002Drecaptcha\u002Dresponse'; 2288 const recaptchaField = fieldName && form[fieldName]; 2289 if (recaptchaField) { 2290 bodyFields[fieldName] = recaptchaField.value; 2291 } 2292 fetch('https://www.customandcommercial.com/customer/ajax/login/', { 2293 method: 'POST', 2294 headers: { 2295 'Content-Type': 'application/json', 2296 'X-Requested-With': 'XMLHttpRequest' 2297 }, 2298 body: JSON.stringify(bodyFields) 2299 } 2300 ).then(response => { 2301 return response.json() 2302 } 2303 ).then(data => { 2304 this.isLoading = false; 2305 if (data.errors) { 2306 this.setErrorMessages(data.message); 2307 this.errors = 1; 2308 this.hasCaptchaToken = 0; 2309 } else { 2310 window.location.href = this.checkoutUrl; 2311 } 2312 }); 2313 } 2314 } 2315 } 2316 2317 window.addEventListener('alpine:init', () => Alpine.data('initAuthentication', initAuthentication), {once: true}) 2318</script>
2318 2319<section id="authentication-popup" 2320 x-data="initAuthentication" 2321 @private-content-loaded.window="onPrivateContentLoaded" 2322 @toggle-authentication.window="redirectIfAuthenticated" 2323 @keydown.window.escape="close" 2324> 2325 <div 2326 class="backdrop" 2327 aria-hidden="true" 2328 x-cloak 2329 x-show="open" 2330 x-transition:enter="ease-in-out duration-500" 2331 x-transition:enter-start="opacity-0" 2332 x-transition:enter-end="opacity-100" 2333 x-transition:leave="ease-in-out duration-500" 2334 x-transition:leave-start="opacity-100" 2335 x-transition:leave-end="opacity-0" 2336 @click="close" 2337 ></div> 2338 <div role="dialog" 2339 aria-modal="true" 2340 @click.outside="close" 2341 class="inset-y-0 right-0 z-30 flex max-w-full fixed" 2342 x-cloak 2343 x-show="open" 2344 > 2345 <div class="relative w-screen max-w-md pt-16 bg-container-lighter" 2346 x-show="open" 2347 x-cloak 2348 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" 2349 x-transition:enter-start="translate-x-full" 2350 x-transition:enter-end="translate-x-0" 2351 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" 2352 x-transition:leave-start="translate-x-0" 2353 x-transition:leave-end="translate-x-full" 2354 > 2355 <div 2356 x-show="open" 2357 x-cloak 2358 x-transition:enter="ease-in-out duration-500" 2359 x-transition:enter-start="opacity-0" 2360 x-transition:enter-end="opacity-100" 2361 x-transition:leave="ease-in-out duration-500" 2362 x-transition:leave-start="opacity-100" 2363 x-transition:leave-end="opacity-0" class="absolute top-0 right-2 flex p-2 mt-2"> 2364 <button 2365 type="button" 2366 @click="close" 2367 aria-label="Close panel" 2368 class="p-2 text-gray-300 transition duration-150 ease-in-out hover:text-black" 2369 > 2370 <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"> 2371 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 2372</svg> 2373 </button> 2374 </div> 2375 <template x-if="open"> 2376 <div class="flex flex-col h-full py-6 space-y-6 bg-white shadow-xl overflow-y-auto"> 2377 <div class="mx-4"> 2378 <div class="message error mb-0" x-show="errors" x-cloak> 2379 <template x-for="(message, index) in errorMessages" :key="index"> 2380 <div> 2381 <template x-if="message"> 2382 <span x-html="message"></span> 2383 </template> 2384 </div> 2385 </template> 2386 </div> 2387 </div> 2388 <div class="block-customer-login bg-container border border-container mx-4 p-4 shadow-sm"> 2389 <p id="authenticate-customer-login" class="text-lg leading-7 text-gray-900"> 2390 <strong class="font-medium">Checkout using your account</strong> 2391 </p> 2392 2393 <form class="form form-login" 2394 method="post" 2395 @submit.prevent="submitForm" 2396 id="login-form" 2397 > 2398 <div class="fieldset login"> 2399 <div class="field email required"> 2400 <label class="label" for="form-login-username" form="login-form" > 2401 <span>Email Address</span> 2402 </label> 2403 <div class="control"> 2404 <input name="username" 2405 id="form-login-username" 2406 x-ref="customer-email" 2407 @change="resetErrors" 2408 type="email" 2409 required 2410 autocomplete="off" 2411 class="form-input" 2412 > 2413 </div> 2414 </div> 2415 <div class="field password required"> 2416 <label for="form-login-password" class="label" form="login-form">
2417 <span>Password</span> 2418 </label> 2419 <div class="control"> 2420 <input name="password" 2421 id="form-login-password" 2422 type="password" 2423 class="form-input" 2424 required 2425 x-ref="customer-password" 2426 autocomplete="off" 2427 @change="resetErrors" 2428 > 2429 </div> 2430 </div> 2431 2432 <input name="context" type="hidden" value="checkout" /> 2433 <div class="actions-toolbar pt-6 pb-2"> 2434 <button type="submit" class="inline-flex btn btn-primary disabled:opacity-75" 2435 > 2436 Sign In </button> 2437 <a href="https://www.customandcommercial.com/customer/account/forgotpassword/" 2438 > 2439 Forgot Your Password? </a> 2440 </div> 2441 2442 </div> 2443 </form> 2444 </div> 2445 <div class="mx-4"> 2446 </div> 2447 <div class="block-new-customer bg-container border border-container mx-4 p-4 shadow-sm"> 2448 <p id="authenticate-new-customer" class="text-lg mb-2 leading-7 text-gray-900"> 2449 <strong class="font-medium">Checkout as a new customer</strong> 2450 </p> 2451 2452 <div class="block-content"> 2453 <p class="mb-1"> 2454 Creating an account has many benefits: </p> 2455 <ul class="list-disc pl-5"> 2456 <li> See order and shipping status</li> 2457 <li> Track order history</li> 2458 <li> Check out faster</li> 2459 </ul> 2460 <div class="actions-toolbar mt-6 mb-2"> 2461 <a href="https://www.customandcommercial.com/customer/account/create/" 2462 class="inline-flex btn btn-primary"> 2463 Create an Account </a> 2464 </div> 2465 </div> 2466 </div> 2467 </div> 2468 </template> 2469 </div> 2470 </div> 2471</section> 2472<!-- /BLOCK authentication-popup --></div>
2473<script> 2474 function initCartDrawer() { 2475 return { 2476 vatMode: vatSwitcher.getVatMode() || 'excluding', 2477 open: false, 2478 isLoading: false, 2479 cart: {}, 2480 maxItemsToDisplay: 10, 2481 itemsCount: 0, 2482 getData(data) { 2483 if (data.cart) { 2484 this.cart = data.cart; 2485 this.itemsCount = data.cart.items && data.cart.items.length || 0; 2486 this.setCartItems(); 2487 } 2488 this.isLoading = false; 2489 }, 2490 cartItems: [], 2491 getItemCountTitle() { 2492 return hyva.strf('\u00250\u0020of\u0020\u00251\u0020products\u0020in\u0020cart\u0020displayed', this.maxItemsToDisplay, this.itemsCount) 2493 }, 2494 setCartItems() { 2495 this.cartItems = this.cart.items && this.cart.items.sort((a, b) => b.item_id - a.item_id) || []; 2496 2497 if (this.maxItemsToDisplay > 0) { 2498 this.cartItems = this.cartItems.slice(0, parseInt(this.maxItemsToDisplay, 10)); 2499 } 2500 }, 2501 deleteItemFromCart(itemId) { 2502 this.isLoading = true; 2503 2504 const formKey = hyva.getFormKey(); 2505 const postUrl = BASE_URL + 'checkout/sidebar/removeItem/'; 2506 2507 fetch(postUrl, { 2508 "headers": { 2509 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 2510 }, 2511 "body": "form_key=" + formKey + "&item_id=" + itemId, 2512 "method": "POST", 2513 "mode": "cors", 2514 "credentials": "include" 2515 }).then(response => { 2516 if (response.redirected) { 2517 window.location.href = response.url; 2518 } else if (response.ok) { 2519 return response.json(); 2520 } else { 2521 window.dispatchMessages && window.dispatchMessages([{ 2522 type: 'warning', 2523 text: 'Could\u0020not\u0020remove\u0020item\u0020from\u0020quote.' 2524 }]); 2525 this.isLoading = false; 2526 } 2527 }).then(result => { 2528 window.dispatchMessages && window.dispatchMessages([{ 2529 type: result.success ? 'success' : 'error', 2530 text: result.success 2531 ? 'You\u0020removed\u0020the\u0020item.' 2532 : result.error_message 2533 }], result.success ? 5000 : 0) 2534 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 2535 }); 2536 }, 2537 scrollLock(use = true) { 2538 document.body.style.overflow = use ? "hidden" : ""; 2539 }, 2540 toggleCartDrawer(event) { 2541 if (event.detail && event.detail.isOpen !== undefined) { 2542 if (event.detail.isOpen) { 2543 this.openCartDrawer(); 2544 } else { 2545 this.open = false; 2546 this.scrollLock(false); 2547 this.$refs && this.$refs.cartDialogContent && hyva.releaseFocus(this.$refs.cartDialogContent); 2548 } 2549 } else { 2550 this.openCartDrawer() 2551 } 2552 }, 2553 getPriceHtml(productPrice) { 2554 if (vatSwitcher.getVatMode() === 'excluding') { 2555 return productPrice.replace( 2556 /<span class="price-including-tax"/, 2557 `<span class="price-including-tax hidden"` 2558 ); 2559 } else { 2560 return productPrice.replace( 2561 /<span class="price-excluding-tax"/, 2562 `<span class="price-excluding-tax hidden"` 2563 ); 2564 } 2565 }, 2566 openCartDrawer() { 2567 this.open = true; 2568 this.scrollLock(true); 2569 this.$nextTick(() => { 2570 this.$refs && this.$refs.cartDialogContent && hyva.trapFocus(this.$refs.cartDialogContent) 2571 }) 2572 }, 2573 closeCartDrawer() { 2574 this.$dispatch('toggle-cart', { isOpen: false }) 2575 }, 2576 getSectionDataExtraActions() { 2577 if (!this.cart.extra_actions) { 2578 return ''; 2579 } 2580 2581 const contentNode = document.createElement('div'); 2582 contentNode.innerHTML = this.cart.extra_actions; 2583 2584 hyva.activateScripts(contentNode); 2585 2586 return contentNode.innerHTML; 2587 }, 2588 eventListeners: { 2589 ['@update-cart-drawer-vat-mode.window'](event) { 2590 this.vatMode = event.detail; 2591 }, 2592 } 2593 } 2594 } 2595</script>
2595 2596<section x-cloak 2597 x-show="cart" 2598 id="cart-drawer" 2599 x-data="initCartDrawer()" 2600 x-bind="eventListeners" 2601 @private-content-loaded.window="getData($event.detail.data)" 2602 @toggle-cart.window="toggleCartDrawer($event)" 2603 @keydown.escape="closeCartDrawer" 2604> 2605 <div role="dialog" 2606 aria-labelledby="cart-drawer-title" 2607 aria-modal="true" 2608 :aria-hidden="!open" 2609 class="fixed inset-y-0 right-0 z-[100] flex max-w-full"> 2610 <div class="backdrop" 2611 x-show="open" 2612 x-transition:enter="ease-in-out duration-500" 2613 x-transition:enter-start="opacity-0" 2614 x-transition:enter-end="opacity-100" 2615 x-transition:leave="ease-in-out duration-500" 2616 x-transition:leave-start="opacity-100" 2617 x-transition:leave-end="opacity-0" 2618 role="button" 2619 @click="closeCartDrawer" 2620 aria-label="Close minicart"></div> 2621 <div class="relative w-screen max-w-md shadow-2xl" 2622 x-show="open" 2623 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" 2624 x-transition:enter-start="translate-x-full" 2625 x-transition:enter-end="translate-x-0" 2626 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" 2627 x-transition:leave-start="translate-x-0" 2628 x-transition:leave-end="translate-x-full" 2629 x-ref="cartDialogContent" 2630 role="region" 2631 :tabindex="open ? 0 : -1" 2632 aria-label="My Cart" 2633 2634 > 2635 <div class="flex flex-col h-full max-h-screen bg-white shadow-xl"> 2636 2637 <header class="relative px-4 py-6 sm:px-6"> 2638 <p id="cart-drawer-title" class="text-lg font-medium leading-7 text-gray-900"> 2639 <strong>My Cart</strong> 2640 <span class="items-total text-xs" 2641 x-show="maxItemsToDisplay && maxItemsToDisplay < itemsCount" 2642 x-text="getItemCountTitle()"> 2643 </span> 2644 </p> 2645 </header> 2646 2647 2648 <template x-if="!itemsCount"> 2649 <div class="relative px-4 py-6 bg-white border-bs sm:px-6 border-container"> 2650 Cart is empty </div> 2651 </template> 2652 2653 <template x-if="itemsCount"> 2654 <div class="relative grid gap-6 sm:gap-8 px-1 py-3 sm:px-3 bg-white border-b border-container overflow-y-auto overscroll-y-contain"> 2655 <template x-for="item in cartItems"> 2656 <div class="flex items-start p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 2657 <a :href="item.product_url" 2658 class="w-1/4" 2659 :aria-label="hyva.strf('Product\u0020\u0022\u00250\u0022', item.product_name)" 2660 > 2661 <img 2662 :src="item.product_image.src" 2663 :width="item.product_image.width" 2664 :height="item.product_image.height" 2665 loading="lazy" 2666 alt="" 2667 /> 2668 </a> 2669 <div class="w-3/4 space-y-2"> 2670 <div> 2671 <p class="text-lg font-serif font-medium"> 2672 <span x-html="item.qty"></span> x <span x-html="item.product_name"></span> 2673 </p> 2674 <p class="text-sm">Part no: <span x-html="item.product_sku"></span></p> 2675 </div> 2676 <template x-if="item.product_type !== 'bundle'"> 2677 <template x-for="option in item.options"> 2678 <div class="pt-2"> 2679 <p class="font-semibold" x-text="option.label + ':'"></p> 2680 <p class="text-secondary" x-html="option.value"></p> 2681 </div> 2682 </template> 2683 </template> 2684 <p class="text-lg"><span x-html="getPriceHtml(item.product_price)"></span></p> 2685 <div class="pt-4"> 2686 <a :href="item.configure_url" 2687 x-show="item.product_type !== 'grouped' && item.is_visible_in_site_visibility" 2688 class="inline-flex p-2 mr-2" 2689 :aria-label="hyva.strf('Edit\u0020product\u
26890020\u0022\u00250\u0022', item.product_name)" 2690 > 2691 <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"> 2692 <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"/> 2693</svg> 2694 </a> 2695 <button type="button" 2696 class="inline-flex p-2" 2697 @click="deleteItemFromCart(item.item_id)" 2698 :aria-label="hyva.strf('Remove\u0020product\u0020\u0022\u00250\u0022\u0020from\u0020cart', item.product_name)" 2699 > 2700 <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"> 2701 <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"/> 2702</svg> 2703 </button> 2704 </div> 2705 2706 </div> 2707 </div> 2708 </template> 2709 </div> 2710 </template> 2711 2712 <template x-if="itemsCount"> 2713 <div> 2714 <template x-if="cart.discount_amount"> 2715 <div class="relative grid gap-6 sm:gap-8 px-1 pt-3 sm:px-3 bg-white"> 2716 <div class="am-discount-amount w-full p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 2717 <p>Discount: <span x-html="cart.discount_amount"></span></p> 2718 </div> 2719 </div> 2720</template> 2721 2722 <div class="relative grid gap-6 sm:gap-8 py-3 px-1 sm:px-3 bg-white"> 2723 <div class="w-full p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 2724 <p>Subtotal: <span x-html="vatMode === 'excluding' ? cart.subtotal_excl_tax : cart.subtotal_incl_tax"></span></p> 2725 </div> 2726 <div class="w-full p-3 space-x-4 transition duration-150 ease-in-out"> 2727 <a href="https://www.customandcommercial.com/checkout/cart/" class="btn btn-primary !rounded bg-brandblue px-16 h-12"> 2728 <span class="block mx-auto">Checkout</span> 2729 </a> 2730 </div> 2731 <div x-html="getSectionDataExtraActions()"></div> 2732 </div> 2733 </div> 2734 </template> 2735 2736
2736<script> 2737 function prepareMollieApplePayMinicart() { 2738 const objectToFormData = (object, prefix = null, formDataObject = null) => { 2739 const formData = formDataObject || new FormData(); 2740 Object.keys(object).forEach(key => { 2741 const value = object[key]; 2742 const name = prefix ? `${prefix}[${key}]` : key; 2743 if (value instanceof Object) { 2744 objectToFormData(value, name, formData); 2745 } else { 2746 formData.append(name, value); 2747 } 2748 }); 2749 2750 return new URLSearchParams(formData).toString(); 2751 }; 2752 2753 return { 2754 totalAmount: 0, 2755 2756 init: () => { 2757 // Normally we would use private-content-loaded to get this information, but this component is 2758 // rendered after this event has been dispatched. So get the information from the browser storage. 2759 const browserStorage = hyva.getBrowserStorage(); 2760 if (browserStorage) { 2761 const cacheStorage = browserStorage.getItem('mage-cache-storage'); 2762 const cart = JSON.parse(cacheStorage).cart; 2763 2764 this.totalAmount = cart.subtotalAmount; 2765 } 2766 2767 window.addEventListener('private-content-loaded', event => { 2768 this.totalAmount = event.detail.data.cart.subtotalAmount 2769 }) 2770 }, 2771 2772 pay: () => { 2773 const amount = this.totalAmount; 2774 let countryCode, postalCode, shippingMethods, selectedShippingMethod, quoteTotals; 2775 2776 const request = { 2777 countryCode: "GB", 2778 currencyCode: "GBP", 2779 supportedNetworks: ["amex","masterCard","visa","maestro","vPay"], 2780 merchantCapabilities: ['supports3DS'], 2781 total: { 2782 label: "Default\u0020Store\u0020View", 2783 amount: amount 2784 }, 2785 shippingType: 'shipping', 2786 requiredBillingContactFields: [ 2787 'postalAddress', 2788 'name', 2789 'email', 2790 'phone' 2791 ], 2792 requiredShippingContactFields: [ 2793 'postalAddress', 2794 'name', 2795 'email', 2796 'phone' 2797 ] 2798 } 2799 2800 let session = new ApplePaySession(3, request); 2801 2802 const getTotal = () => { 2803 const total = quoteTotals.find(item => item.code === 'grand_total') || quoteTotals[quoteTotals.length - 1]; 2804 2805 total.label = "Default\u0020Store\u0020View"; 2806 2807 const payload = { total }; 2808 window.dispatchEvent(new CustomEvent('mollie-applepay-total', { 2809 detail: payload, 2810 })); 2811 2812 return payload.total; 2813 }; 2814 2815 const getLineItems = () => { 2816 let totals = [...quoteTotals]; 2817 totals = totals.filter(item => item.code !== 'grand_total'); 2818 2819 const payload = { lineItems: totals }; 2820 window.dispatchEvent(new CustomEvent('mollie-applepay-lineitems', { 2821 detail: payload, 2822 })); 2823 2824 return payload.lineItems; 2825 }; 2826 2827 const handleAjaxError = (message) => { 2828 session.abort(); 2829 2830 dispatchMessages([ 2831 { 2832 text: message, 2833 type: 'error' 2834 } 2835 ]); 2836 }; 2837 2838 session.onpaymentmethodselected = () => { 2839 session.completePaymentMethodSelection(getTotal(), []); 2840 }; 2841 2842 session.onshippingcontactselected = (event) => { 2843 countryCode = event.shippingContact.countryCode; 2844 postalCode = event.shippingContact.postalCode; 2845 2846 fetch(`${BASE_URL}mollie/applePay/shippingMethods?form_key=${hyva.getFormKey()}`, { 2847 method: 'POST', 2848 headers: { 2849 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 2850 }, 2851 body: 'countryCode=' + event.shippingContact.countryCode + 2852 '&postalCode=' + event.shippingContact.postalCode 2853 }) 2854 .then(response => { 2855 if (!response.ok) { 2856 throw new Error('Network response was not ok'); 2857 } 2858 return response.json(); 2859 }) 2860 .then(result => { 2861 shippingMethods = result.shipping_methods; 2862 selectedShippingMethod = result.shipping_methods[0]; 2863 quoteTotals = result.totals; 2864 2865 session.completeShippingContactSelection( 2866 ApplePaySession.STATUS_SUCCESS, 2867 result.shipping_methods, 2868 getTotal(), 2869 getLineItems() 2870 ); 2871 }) 2872 .catch(error => handleAjaxError(error)); 2873 }; 2874 2875 session.onshippingmethodselected = (event) => { 2876 selectedShippingMethod = event.shippingMethod; 2877 2878 fetch(`${BASE_URL}mollie/applePay/shippingMethods?form_key=${hyva.getFormKey()}`, { 2879 method: 'POST', 2880 headers: { 2881 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 2882 }, 2883 body: objectToFormData({ 2884 shippingMethod: selectedShippingMethod, 2885 countryCode: countryCode, 2886 postalCode: postalCode 2887 }) 2888 }) 2889 .then(response => { 2890 if (!response.ok) { 2891 throw new Error('Network response was not ok'); 2892 } 2893 return response.json(); 2894 }) 2895 .then(result => { 2896 quoteTotals = result.totals; 2897 2898 session.completeShippingMethodSelection( 2899 ApplePaySession.STATUS_SUCCESS, 2900 getTotal(), 2901 getLineItems() 2902 ); 2903 }) 2904 .catch(error => handleAjaxError(error)); 2905 }; 2906 2907 session.onpaymentauthorized = (event) => { 2908 fetch(`${BASE_URL}mollie/applePay/placeOrder?form_key=${hyva.getFormKey()}`, { 2909 method: 'POST', 2910 headers: { 2911 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 2912 }, 2913 body: objectToFormData({ 2914 shippingMethod: selectedShippingMethod, 2915 billingAddress: event.payment.billingContact, 2916 shippingAddress: event.payment.shippingContact, 2917 applePayPaymentToken: JSON.stringify(event.payment.token) 2918 }) 2919 }) 2920 .then(response => { 2921 if (!response.ok) { 2922 throw new Error('Network response was not ok'); 2923 } 2924 return response.json(); 2925 }) 2926 .then(result => { 2927 if (!session) { 2928 console.warn('Payment canceled'); 2929 return; 2930 } 2931 2932 if (result.error) { 2933 handleAjaxError(result.error_message); 2934 return; 2935 } 2936 2937 if (!result.url) { 2938 handleAjaxError('Something went wrong, please try again later.'); 2939 return; 2940 } 2941 2942 session.completePayment(ApplePaySession.STATUS_SUCCESS); 2943 2944 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 2945 2946 setTimeout(() => { 2947 location.href = result.url; 2948 }, 1000); 2949 }) 2950 .catch(error => handleAjaxError(error)); 2951 }; 2952 2953 session.onvalidatemerchant = (event) => { 2954 fetch( 2955 `${BASE_URL}mollie/checkout/applePayValidation?form_key=${hyva.getFormKey()}`, 2956 { 2957 method: 'POST', 2958 headers: { 2959 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 2960 }, 2961 body: objectToFormData({validationURL: event.validationURL}), 2962 } 2963 ) 2964 .then((response) => { 2965 if (!response.ok) { 2966 throw new Error('Merchant validation failed'); 2967 } 2968 2969 return response.json() 2970 }) 2971 .then((result) => session.completeMerchantValidation(result)) 2972 .catch(error => handleAjaxError(error)); 2973 }; 2974 2975 session.oncancel = () => { 2976 session = null; 2977 } 2978 2979 session.begin(); 2980 } 2981 } 2982 } 2983 2984 window.addEventListener( 2985 'alpine:init', 2986 () => Alpine.data('prepareMollieApplePayMinicart', prepareMollieApplePayMinicart), 2987 {once: true} 2988 ) 2989</script>
2989 2990 </div> 2991 2992 <button 2993 type="button" 2994 @click="closeCartDrawer" 2995 aria-label="Close minicart" 2996 class="absolute top-0 right-2 p-4 mt-2 text-gray-300 transition-colors hover:text-black" 2997 > 2998 <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"> 2999 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 3000</svg> 3001 </button> 3002 </div> 3003 <div 3004 class="z-50 fixed inset-0 grid place-items-center bg-white/70 text-slate-800" 3005 x-cloak 3006 x-show="isLoading" 3007 x-transition.opacity.duration.200ms 3008> 3009 <div class="flex gap-10 items-center p-4"> 3010 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57 57" width="57" height="57" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"> 3011 <style> 3012 @keyframes spinner-ball-triangle1 { 3013 0% { transform: translate(0%, 0%); } 3014 33% { transform: translate(38%, -79%); } 3015 66% { transform: translate(77%, 0%); } 3016 100% { transform: translate(0%, 0%); } 3017 } 3018 3019 @keyframes spinner-ball-triangle2 { 3020 0% { transform: translate(0%, 0%); } 3021 33% { transform: translate(38%, 79%); } 3022 66% { transform: translate(-38%, 79%); } 3023 100% { transform: translate(0%, 0%); } 3024 } 3025 3026 @keyframes spinner-ball-triangle3 { 3027 0% { transform: translate(0%, 0%); } 3028 33% { transform: translate(-77%, 0%); } 3029 66% { transform: translate(-38%, -79%); } 3030 100% { transform: translate(0%, 0%); } 3031 } 3032 </style> 3033 <circle cx="5" cy="50" r="5" style="animation: spinner-ball-triangle1 2.2s linear infinite"/> 3034 <circle cx="27" cy="5" r="5" style="animation: spinner-ball-triangle2 2.2s linear infinite"/> 3035 <circle cx="49" cy="50" r="5" style="animation: spinner-ball-triangle3 2.2s linear infinite"/> 3036</svg> 3037 <span class="text-xl select-none"> 3038 Loading... </span> 3039 </div> 3040</div> 3041 </div> 3042 3043</section> 3044</header><div class="top-container"><nav class="breadcrumbs bg-white" aria-label="Breadcrumb"> 3045 <div class="container"> 3046 <ol class="items list-reset py-5 rounded flex flex-wrap text-bodydark font-serif font-semibold text-xs"> 3047 <li class="item flex home items-center"> 3048 <a href="https://www.customandcommercial.com" 3049 class="no-underline" 3050 title="Go to Home Page" 3051 >Home</a> 3052 </li> 3053 <li class="item flex blog items-center"> 3054 <span aria-hidden="true" class="separator text-ctaprimary px-2"><svg width="6" height="10" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg"> 3055<path d="M1.4375 1.875L4.5625 5L1.4375 8.125" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 3056</svg></span> 3057 <a href="https://www.customandcommercial.com/blog/" 3058 class="no-underline" 3059 title="Blog" 3060 >Blog</a> 3061 </li> 3062 </ol> 3063 </div> 3064</nav> 3065</div><main id="maincontent" class="page-main"><div id="contentarea" tabindex="-1"></div> 3066<div class="page messages">
3066<script> 3067 function initMessages() { 3068 "use strict"; 3069 return { 3070 messages: window.mageMessages || [], 3071 isEmpty() { 3072 return this.messages.reduce( 3073 function (isEmpty, message) { 3074 return isEmpty && message === undefined 3075 }, true 3076 ) 3077 }, 3078 hasMessages() { 3079 return !this.isEmpty(); 3080 }, 3081 hasMessage() { 3082 return !!this.message; 3083 }, 3084 removeMessage() { 3085 this.messages[this.index] = undefined; 3086 }, 3087 addMessages(messages, hideAfter) { 3088 messages.map((message) => { 3089 this.messages = this.messages.concat(message); 3090 if (hideAfter === undefined && message.type === 'success' && window.defaultSuccessMessageTimeout) { 3091 hideAfter = window.defaultSuccessMessageTimeout; 3092 } 3093 if (hideAfter) { 3094 this.setHideTimeOut(this.messages.length -1, hideAfter); 3095 } 3096 }); 3097 }, 3098 setHideTimeOut(messageIndex, hideAfter) { 3099 setTimeout((messageIndex) => { 3100 this.messages[messageIndex] = undefined; 3101 }, hideAfter, messageIndex); 3102 }, 3103 eventListeners: { 3104 ['@messages-loaded.window'](event) { 3105 this.addMessages(event.detail.messages, event.detail.hideAfter) 3106 }, 3107 ['@private-content-loaded.window'](event) { 3108 const data = event.detail.data; 3109 if ( 3110 data.messages && 3111 data.messages.messages && 3112 data.messages.messages.length 3113 ) { 3114 this.addMessages(data.messages.messages); 3115 } 3116 }, 3117 ['@clear-messages.window']() { 3118 this.messages = []; 3119 } 3120 }, 3121 getMessageUiId() { 3122 return 'message-' + this.message.type; 3123 } 3124 } 3125 } 3126 3127 window.addEventListener('alpine:init', () => Alpine.data('initMessages', initMessages), {once: true}) 3128</script>
3128 3129<section id="messages" 3130 x-data="initMessages" 3131 x-bind="eventListeners" 3132 role="alert" 3133> 3134 <template x-if="hasMessages"> 3135 <div class="w-full"> 3136 <div class="messages container mx-auto py-3"> 3137 <template x-for="(message, index) in messages" :key="index"> 3138 <div> 3139 <template x-if="hasMessage"> 3140 <div class="message" 3141 :class="message.type" 3142 :ui-id="getMessageUiId" 3143 > 3144 <span x-html="message.text"></span> 3145 <button 3146 type="button" 3147 class="text-gray-600 hover:text-black" 3148 aria-label="Close message" 3149 @click.prevent="removeMessage" 3150 > 3151 <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"> 3152 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 3153</svg> 3154 </button> 3155 </div> 3156 </template> 3157 </div> 3158 </template> 3159 </div> 3160 </div> 3161 </template> 3162</section> 3163</div> <div class="header-container flex flex-col md:flex-row flex-wrap py-8 font-bold page-title-wrapper text-xl md:text-2xl"> 3164 <h1 class="font-serif text-xl md:text-2xl text-[#212121] font-medium text-center" 3165 > 3166 <span class="base" data-ui-id="page-title-wrapper" >Blog</span> </h1> 3167 </div> 3168 <div class="columns"><div class="column main"> <div class="blog-filter flex w-full justify-end"> 3169 <div class="mb-[50px] sm:w-[300px] w-full relative" x-data="initializeBlogFilter()"> 3170 <button @click="toggleFilter" class="w-full border h-[50px] text-left px-5" :class="{'active': open}"> 3171 <span x-text="currentCategory">Filter by category</span> 3172 </button> 3173 <ul class="absolute z-10 bg-white w-full border" x-show="open" x-transition.opacity> 3174 <li class="hover:bg-eaeaea py-2.5 px-5"> 3175 <a class="hover:text-red duration-300 w-full block" href="https://www.customandcommercial.com/blog/"> 3176 Filter by category </a> 3177 </li> 3178 <template x-for="category in allCategories"> 3179 <li class="hover:bg-eaeaea py-2.5 px-5" :class="{'bg-eaeaea': category.name === currentCategory}"> 3180 <a class="hover:text-red duration-300 w-full block" :href="category.url" x-text="category.name"> 3181 Filter by category </a> 3182 </li> 3183 </template> 3184 </ul> 3185 </div> 3186
3186<script> 3187 function initializeBlogFilter() { 3188 return { 3189 open: false, 3190 currentCategory: 'Filter by category', 3191 allCategories: [{"name":"Uncategorized","slug":"uncategorized","url":"https:\/\/www.customandcommercial.com\/blog\/category\/uncategorized\/"}], 3192 init() { 3193 const segments = window.location.pathname.split('/').filter(Boolean); 3194 3195 // Find "category" and get the next segment 3196 const categoryIndex = segments.indexOf('category'); 3197 const category = categoryIndex !== -1 && segments.length > categoryIndex + 1 3198 ? segments[categoryIndex + 1] 3199 : null; 3200 3201 let curCategory = this.allCategories.find(_ => _.slug === category); 3202 if (curCategory) { 3203 this.currentCategory = curCategory.name || 'Filter by category'; 3204 } 3205 }, 3206 toggleFilter() { 3207 this.open = !this.open; 3208 } 3209 } 3210 } 3211 </script>
3211 3212 </div> 3213 <div class="post-list-wrapper"> 3214 <ol class="post-list grid"> 3215 <li class="item"> 3216 <div class="post-list-item"> 3217 <div class="post-entry"> 3218 <div class="post-image"> 3219 <a href="https://www.customandcommercial.com/blog/2025/12/11/made-by-cc-product-feature-wiper-blades/" title="Made By C&C Product Feature: Wiper Blades"> 3220 <img src="https://www.customandcommercial.com/media/wordpress/1fe1b085e0bab4a92ccf79747f2456bf.jpg" alt="Made By C&C Product Feature: Wiper Blades"/> 3221 </a> 3222 <div class="post-categories post-meta-item"> 3223 <a href="https://www.customandcommercial.com/blog/category/uncategorized/">Uncategorized</a> </div> 3224 </div> 3225 3226 3227 <div class="post-content"> 3228 <h2> 3229 <a href="https://www.customandcommercial.com/blog/2025/12/11/made-by-cc-product-feature-wiper-blades/" title="Made By C&C Product Feature: Wiper Blades"> 3230 Made By C&C Product Feature: Wiper Blades </a> 3231 </h2> 3232 <div class="post-excerpt post-text">Looking for wiper blades for your VW? 3233We have them for all vehicles in different colours, sizes and finishes</div> 3234 <a class="post-read-more hover:underline" 3235 href="https://www.customandcommercial.com/blog/2025/12/11/made-by-cc-product-feature-wiper-blades/" 3236 title="Made By C&C Product Feature: Wiper Blades" 3237 > 3238 Read more <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 20 20" fill="none" class="w-[20x] h-[20px]" role="img"> 3239<path d="M11.25 3.75L17.5 10M17.5 10L11.25 16.25M17.5 10H2.5" stroke="#E18438" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 3240<title>arrow-right</title></svg> 3241 </a> 3242 </div> 3243 3244 </div> 3245 <!--<div class="post-meta"> 3246 <div class="post-tags post-meta-item"> 3247 <strong><?/*= $block->escapeHtml(__('Tags')) */?>:</strong> <?/*= $block->escapeHtml($tagString, ['a']) */?> 3248 </div> 3249 <div class="post-date post-meta-item"> 3250 <strong><?/*= $block->escapeHtml(__('Posted On')) */?>:</strong> <?/*= $block->escapeHtml($post->getPostDate()) */?> 3251 </div> 3252 <div class="post-user post-meta-item"> 3253 <strong><?/*= $block->escapeHtml(__('Posted By')) */?>:</strong> <a href="<?/*= $block->escapeUrl($post->getUser()->getUrl()) */?>"><?/*= $block->escapeHtml($post->getUser()->getDisplayName()) */?></a> 3254 </div> 3255 </div>--> 3256 </div> 3257 </li> 3258 <li class="item"> 3259 <div class="post-list-item"> 3260 <div class="post-entry"> 3261 <div class="post-image"> 3262 <a href="https://www.customandcommercial.com/blog/2025/12/11/made-by-cc-product-feature-fuel-filler-parts/" title="Made By C&C Product Feature: Fuel Filler Parts"> 3263 <img src="https://www.customandcommercial.com/media/wordpress/c70652799c6c1fbc80d40df265d1651d.png" alt="Made By C&C Product Feature: Fuel Filler Parts"/> 3264 </a> 3265 <div class="post-categories post-meta-item"> 3266 <a href="https://www.customandcommercial.com/blog/category/uncategorized/">Uncategorized</a> </div> 3267 </div> 3268 3269 3270 <div class="post-content"> 3271 <h2> 3272 <a href="https://www.customandcommercial.com/blog/2025/12/11/made-by-cc-product-feature-fuel-filler-parts/" title="Made By C&C Product Feature: Fuel Filler Parts"> 3273 Made By C&C Product Feature: Fuel Filler Parts </a> 3274 </h2> 3275 <div class="post-excerpt post-text">Looking For Fuel Filler Parts? 3276 3277If you need to replace your fuel filler parts, we have you covered</div> 3278 <a class="post-read-more hover:underline" 3279 href="https://www.customandcommercial.com/blog/2025/12/11/made-by-cc-product-feature-fuel-filler-parts/" 3280 title="Made By C&C Product Feature: Fuel Filler Parts" 3281 > 3282 Read more <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 20 20" fill="none" class="w-[20x] h-[20px]" role="img"> 3283<path d="M11.25 3.75L17.5 10M17.5 10L11.25 16.25M17.5 10H2.5" stroke="#E18438" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 3284<title>arrow-right</title></svg> 3285 </a> 3286 </div> 3287 3288 </div> 3289 <!--<div class="post-meta"> 3290 <div class="post-tags post-meta-item"> 3291 <strong><?/*= $block->escapeHtml(__('Tags')) */?>:</strong> <?/*= $block->escapeHtml($tagString, ['a']) */?> 3292 </div> 3293 <div class="post-date post-meta-item"> 3294 <strong><?/*= $block->escapeHtml(__('Posted On')) */?>:</strong> <?/*= $block->escapeHtml($post->getPostDate()) */?> 3295 </div> 3296 <div class="post-user post-meta-item"> 3297 <strong><?/*= $block->escapeHtml(__('Posted By')) */?>:</strong> <a href="<?/*= $block->escapeUrl($post->getUser()->getUrl()) */?>"><?/*= $block->escapeHtml($post->getUser()->getDisplayName()) */?></a> 3298 </div> 3299 </div>--> 3300 </div> 3301 </li> 3302 </ol> 3303 3304 <div class="grid items-center grid-flow-row grid-cols-4 gap-2 pager 3305 sm:grid-cols-8 md:grid-cols-4 lg:grid-cols-8"> 3306 3307 <p class="flex order-3 col-span-2 text-sm leading-5 text-gray-700 3308 toolbar-amount sm:order-2 md:order-3 lg:order-2 gap-x-1">
3309 <span class="toolbar-number"> 3310 2 Item(s) </span> 3311 </p> 3312 3313 <div class="flex justify-center order-2 col-span-4"> 3314 </div> 3315 3316 3317 </div> 3318 3319 </div><div class="widget block block-static-block"> 3320 </div> 3321</div><aside class="sidebar sidebar-additional">
3322<script> 3323function compareSidebarFetchHandler(postUrl, postHeaders, isSingleItem = true) { 3324 const messages = { 3325 "success": isSingleItem 3326 ? "You removed product Product from the comparison list" 3327 : "You removed all products from the comparison list", 3328 "warning": isSingleItem 3329 ? "Could not remove item from compare." 3330 : "Could not remove all item from compare.", 3331 } 3332 3333 return fetch(postUrl, postHeaders).then(function (response) { 3334 if (response.redirected) { 3335 window.location.href = response.url; 3336 } else if (response.ok) { 3337 return response.json(); 3338 } else { 3339 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 3340 [{ type: "warning", text: messages.warning }], 5000 3341 ); 3342 } 3343 }).then(function (response) { 3344 if (!response) return; 3345 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 3346 [{ 3347 type: (response.success) ? "success" : "error", 3348 text: (response.success) ? messages.success : response.error_message 3349 }], 5000 3350 ); 3351 let reloadCustomerDataEvent = new CustomEvent("reload-customer-section-data"); 3352 window.dispatchEvent(reloadCustomerDataEvent); 3353 }).catch(function (error) { 3354 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 3355 [{ type: "error", text: error }], 5000 3356 ); 3357 }); 3358} 3359 3360function initCompareOnCompareSidebar() { 3361 return { 3362 compareProducts: null, 3363 itemCount: 0, 3364 compareItems: {}, 3365 receiveCompareData: function (data) { 3366 if (data['compare-products']) { 3367 this.compareProducts = data['compare-products']; 3368 this.itemCount = this.compareProducts.count; 3369 this.compareItems = this.compareProducts.items; 3370 } 3371 }, 3372 removeFromCompare: function(productId) { 3373 const formKey = hyva.getFormKey(); 3374 const postUrl = BASE_URL + 'catalog/product_compare/remove/'; 3375 const confirmMessage = "Are you sure you want to remove this item from your Compare Products list?"; 3376 if (!window.confirm(confirmMessage)) return; 3377 const postHeaders = { 3378 "headers": { 3379 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 3380 }, 3381 "body": "form_key=" + formKey + "&product=" + productId + "&uenc="
3381+ hyva.getUenc(), 3382 "method": "POST", 3383 "mode": "cors", 3384 "credentials": "include" 3385 }; 3386 3387 compareSidebarFetchHandler(postUrl, postHeaders); 3388 } 3389 } 3390} 3391 3392function initCompareSidebarClear() { 3393 return { 3394 clearCompare: function() { 3395 const formKey = hyva.getFormKey(); 3396 const postUrl = BASE_URL + 'catalog/product_compare/clear/'; 3397 const confirmMessage = "Are you sure you want to remove all items from your Compare Products list?"; 3398 if (!window.confirm(confirmMessage)) return; 3399 const postHeaders = { 3400 "headers": { 3401 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 3402 }, 3403 "body": "form_key=" + formKey + "&uenc=" + hyva.getUenc(), 3404 "method": "POST", 3405 "mode": "cors", 3406 "credentials": "include" 3407 }; 3408 3409 compareSidebarFetchHandler(postUrl, postHeaders, isSingleItem = false); 3410 } 3411 } 3412} 3413</script>
3413 3414 3415<div x-data="initCompareOnCompareSidebar()" 3416 @private-content-loaded.window="receiveCompareData($event.detail.data)" 3417 class="compare-widget" 3418> 3419 <template x-if="itemCount"> 3420 <div class="mt-8"> 3421 <h2 class="border-t border-container mb-6 pt-5 font-semibold text-lg" 3422 id="compare-sidebar" 3423 > 3424 Compare Products </h2> 3425 <ul role="list" class="my-4" aria-labelledby="compare-sidebar"> 3426 <template x-for="product in compareItems"> 3427 <li class="flex items-start mb-4"> 3428 <a 3429 :href="product.product_url" 3430 class="block mb-3 w-10 h-10 shrink-0 mr-4" 3431 aria-hidden="true" 3432 tabindex="-1" 3433 > 3434 <img 3435 :src="product.image" 3436 alt="" 3437 loading="lazy" 3438 > 3439 </a> 3440 <strong class="mr-2 text-sm leading-6 font-normal"> 3441 <a :href="product.product_url" :title="product.name" x-html="product.name"></a> 3442 </strong> 3443 <button 3444 type="button" 3445 @click.prevent="removeFromCompare(product.id)" 3446 :aria-label="hyva.str('Remove\u0020product\u0020\u0022\u00251\u0022\u0020from\u0020the\u0020comparison', product.name)" 3447 class="p-1 inline-flex shrink-0 items-center justify-center text-slate-400 3448 hover:text-slate-800 ml-auto" 3449 > 3450 <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"> 3451 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 3452</svg> 3453 </button> 3454 </li> 3455 </template> 3456 </ul> 3457 <div class="flex flex-wrap items-center md:mt-5 gap-2 text-sm"> 3458 <a href="https://www.customandcommercial.com/catalog/product_compare/index/" 3459 title="Compare Products" 3460 class="btn btn-secondary text-sm"> 3461 <span>Compare</span> 3462 </a> 3463 <button x-data="initCompareSidebarClear()" 3464 @click.prevent="clearCompare()" 3465 aria-label="Remove all Products" 3466 class="btn text-sm shadow-none bg-transparent text-inherit"> 3467 <span>Remove All</span> 3468 </button> 3469 </div> 3470 </div> 3471 </template> 3472</div> 3473 3474
3474<script> 3475 function initWishlistOnWishlistSidebar() { 3476 function wishlistSidebarFetchHandler(body, postUrl, isRemoveAction = true) { 3477 const messages = { 3478 "success": isRemoveAction 3479 ? "\u00251\u0020has\u0020been\u0020removed\u0020from\u0020your\u0020Wish\u0020List." 3480 : "\u00251\u0020has\u0020been\u0020added\u0020to\u0020your\u0020Wish\u0020List.", 3481 "warning": isRemoveAction 3482 ? "Could\u0020not\u0020remove\u0020item\u0020from\u0020your\u0020Wish\u0020List." 3483 : "Could\u0020not\u0020add\u0020item\u0020to\u0020your\u0020Wish\u0020List.", 3484 } 3485 3486 const postHeaders = { 3487 "headers": { 3488 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 3489 }, 3490 body: body, 3491 "method": "POST", 3492 "mode": "cors", 3493 "credentials": "include" 3494 }; 3495 3496 return fetch(postUrl, postHeaders).then(function (response) { 3497 if (response.redirected) { 3498 window.location.href = response.url; 3499 } else if (response.ok) { 3500 return response.json(); 3501 } else { 3502 const message = { type: "warning", text: messages.warning }; 3503 window.dispatchMessages && window.dispatchMessages([message], 5000); 3504 } 3505 }).then(function (response) { 3506 if (!response) return; 3507 const message = { 3508 type: (response.success) ? "success" : "error", 3509 text: (response.success) ? messages.success : response.error_message 3510 } 3511 window.dispatchMessages && window.dispatchMessages([message], 5000); 3512 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 3513 }).catch(function (error) { 3514 const message = { type: "error", text: error }; 3515 window.dispatchMessages && window.dispatchMessages([message], 5000); 3516 }); 3517 } 3518 3519 return { 3520 wishlistProducts: null, 3521 itemCount: 0, 3522 wishlistCountLabel: null, 3523 wishlistItems: {}, 3524 receiveWishlistData: function (data) { 3525 if (data['wishlist']) { 3526 this.wishlistProducts = data['wishlist']; 3527 this.wishlistCountLabel = '(' + this.wishlistProducts.counter + ')'; 3528 this.itemCount = this.wishlistProducts.items.length; 3529 this.wishlistItems = this.wishlistProducts.items; 3530 } 3531 }, 3532 addToCart: function (json) { 3533 const obj = JSON.parse(json); 3534 const postUrl = obj.action; 3535 const body = "form_key=" + hyva.getFormKey() + "&item=" + obj.data.item + "&qty=" + obj.data.qty + "&uenc="
3535+ hyva.getUenc(); 3536 wishlistSidebarFetchHandler(body, postUrl, /* isRemoveAction */ false); 3537 }, 3538 removeFromWishlist: function(json) { 3539 const obj = JSON.parse(json); 3540 const postUrl = obj.action; 3541 const body = "form_key=" + hyva.getFormKey() + "&item=" + obj.data.item+"&uenc=" + hyva.getUenc(); 3542 wishlistSidebarFetchHandler(body, postUrl); 3543 } 3544 } 3545 } 3546 3547 </script>
3547 3548 3549 <div x-data="initWishlistOnWishlistSidebar()" 3550 @private-content-loaded.window="receiveWishlistData($event.detail.data)" 3551 class="wishlist-widget" 3552 > 3553 <template x-if="itemCount"> 3554 <div class="mt-8"> 3555 <h2 class="border-t border-container mb-6 pt-5" id="wishlist-sidebar"> 3556 <strong class="title font-semibold text-lg">My Wish List</strong> 3557 <span class="counter text-xs" x-html="wishlistCountLabel"></span> 3558 </h2> 3559 <ul role="list" class="my-4" aria-labelledby="wishlist-sidebar"> 3560 <template x-for="product in wishlistItems"> 3561 <li class="flex items-start mb-4"> 3562 <a 3563 :href="product.product_url" 3564 class="block mb-3 w-10 h-10 shrink-0 mr-4" 3565 tabindex="-1" 3566 aria-hidden="true" 3567 > 3568 <img 3569 :src="product.image.src" 3570 alt="" 3571 loading="lazy" 3572 > 3573 </a> 3574 <div class="product-item-details"> 3575 <strong class="mr-2 text-sm leading-6 font-normal"> 3576 <a :href="product.product_url" :title="product.product_name" x-html="product.product_name"></a> 3577 </strong> 3578 <div class="mr-2 text-sm leading-6 font-bold" x-html="product.product_price"></div> 3579 <template x-if="product.product_is_saleable_and_visible"> 3580 <div class="actions-primary my-2"> 3581 <template x-if="!product.product_has_required_options"> 3582 <button 3583 type="button" 3584 @click.prevent="addToCart(product.add_to_cart_params)" 3585 class="action tocart primary btn btn-primary text-sm" 3586 :aria-label="hyva.str('Add\u0020to\u0020Cart\u0020\u00251', product.product_name)" 3587 data-addto="cart" 3588 > 3589 Add to Cart </button> 3590 </template> 3591 </div> 3592 </template> 3593 </div> 3594 <button 3595 type="button" 3596 @click.prevent="removeFromWishlist(product.delete_item_params)" 3597 :aria-label="hyva.str('Remove\u0020product\u0020\u0022\u00251\u0022\u0020from\u0020My\u0020Wish\u0020List', product.product_name)" 3598 class=" 3599 p-1 inline-flex shrink-0 items-center 3600 justify-center text-slate-400 hover:text-slate-800 ml-auto 3601 " 3602 > 3603 <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"> 3604 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 3605</svg> 3606 </button> 3607 </li> 3608 </template> 3609 </ul> 3610 <div class="flex flex-wrap items-center md:mt-5 gap-2 text-sm"> 3611 <a href="https://www.customandcommercial.com/wishlist/" 3612 aria-label="Go to Wish List page" 3613 class="btn btn-secondary text-sm" 3614 > 3615 Go to Wish List </a> 3616 </div> 3617 </div> 3618 </template> 3619 </div> 3620</aside></div></main><footer class="page-footer"><div class="footer content">
3620<script> 3621 addEventListener('DOMContentLoaded', () => { 3622 for (const [selector, label] of Object.entries({"aside.sidebar-main":"Sidebar Main","aside.sidebar-additional":"Sidebar Additional"})) { 3623 const target = document.querySelector(selector); 3624 target && target.setAttribute('aria-label', label); 3625 } 3626 }, {once: true}) 3627</script>
3627 3628 3629<div class="px-5 bg-lightorange text-white"> 3630 <div class="lg:container lg:flex lg:justify-between lg:gap-4 py-7"> 3631 <div class="w-full lg:w-6/12 lg:mx-auto"> 3632 <div class="reviews-io-rating-bar-widget" 3633 widget-id="9RBX5208MQlWNmAe" 3634 data-store-name="www.bullerltd.co.uk" 3635 lang="en" 3636 ></div> 3637
3637<script src='//widget.reviews.io/modern-widgets/rating-bar.js'></script>
3637 3638 </div> 3639 </div> 3640</div> 3641<div class="bg-darkbrown font-serif text-white text-sm font-medium"> 3642 <div class="container py-8 mx-auto"> 3643 <div class="flex flex-col lg:flex-row"> 3644 3645 3646 <div 3647 x-data="{ 3648 open: true, 3649 isMobile: false, 3650 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 3651 }" 3652 x-id="['collapse-id']" 3653 x-init="checkIsMobileResolution()" 3654 @resize.window.debounce="checkIsMobileResolution()" 3655 @visibilitychange.window.debounce="checkIsMobileResolution()" 3656 class="border-b border-white lg:border-b-0 last:border-b-0 w-full address-col lg:w-4/12" 3657 > 3658 3659 <div 3660 :id="$id('collapse-id')" 3661 :role="isMobile && 'button'" 3662 :tabindex="isMobile && '0'" 3663 class="group flex w-full items-center justify-between pt-4 gap-2 font-bold text-white lg:pt-0" 3664 @click="isMobile && (open = !open)" 3665 @keyup.enter="isMobile && (open = !open)" 3666 @keyup.space="isMobile && (open = !open)" 3667 :aria-expanded="isMobile ? open : null" 3668 :aria-controls="isMobile ? $id('collapse-id') : null" 3669 > 3670 <h5 class="font-serif text-sm text-semibold w-full pb-1.5">Custom & Commercial</h5> 3671 3672 <span 3673 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 3674 x-ref="toggleIcon" 3675 > 3676 3677 </span> 3678 </div> 3679 <div 3680 class="lg:block" 3681 :class="{ 'hidden': isMobile && !open }" 3682 :aria-labelledby="isMobile ? $id('collapse-id') : null" 3683 :aria-hidden="isMobile && (open ? 'false' : 'true')" 3684 > 3685 <div class="pt-0.5 pb-2.5"> 3686 <address class="text-sm font-normal"> 3687 <p>Unit 32 Bookham Industrial Estate<br> 3688 Church Rd<br> 3689 Great Bookham<br> 3690 Surrey<br> 3691 KT23 3EU</p> 3692</address> 3693<p class="text-sm font-bold">Email: <a href="mailto:[email protected]">[email protected]</a></p> 3694<div class="flex justify-start gap-3 pb-8 pt-2.5"> 3695 <a 3696 href="https://www.instagram.com/candcparts/" 3697 target="_blank" 3698 rel="noopener noreferrer" 3699 class="text-white transition-colors" 3700 > 3701 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 32 32" role="img"> 3702 <path d="M12.668 16a3.333 3.333 0 1 1 6.666 0 3.333 3.333 0 0 1-6.666 0Zm-1.802 0a5.135 5.135 0 1 0 10.27 0 5.135 5.135 0 0 0-10.27 0Zm9.273-5.338a1.2 1.2 0 1 0 1.2-1.2 1.2 1.2 0 0 0-1.2 1.2ZM11.96 24.14c-.975-.045-1.505-.207-1.857-.344a3.109 3.109 0 0 1-1.15-.748 3.086 3.086 0 0 1-.748-1.15c-.137-.352-.3-.882-.344-1.857-.048-1.054-.058-1.37-.058-4.04 0-2.671.01-2.987.058-4.042.045-.975.208-1.504.344-1.857.182-.467.398-.8.748-1.15.35-.35.683-.567 1.15-.748.352-.138.882-.3 1.857-.344C13.014 7.81 13.331 7.8 16 7.8c2.67 0 2.987.01 4.042.059.975.044 1.504.208 1.857.344.466.18.8.398 1.15.748.35.35.566.683.748 1.15.137.352.3.882.344 1.857.048 1.055.058 1.37.058 4.041 0 2.67-.01 2.986-.058 4.041-.044.975-.208 1.505-.344 1.857a3.1 3.1 0 0 1-.748 1.15 3.11 3.11 0 0 1-1.15.748c-.352.137-.882.3-1.857.344-1.055.048-1.371.058-4.042.058-2.67 0-2.986-.01-4.04-.058Zm-.082-18.08c-1.065.049-1.792.218-2.428.465A4.905 4.905 0 0 0 7.68 7.678 4.886 4.886 0 0 0 6.525 9.45c-.247.636-.416 1.363-.464 2.427-.05 1.066-.06 1.407-.06 4.123s.01 3.057.06 4.123c.048 1.064.217 1.791.464 2.427a4.888 4.888 0 0 0 1.154 1.771 4.916 4.916 0 0 0 1.771 1.154c.637.247 1.363.416 2.428.464 1.066.049 1.407.061 4.122.061 2.716 0 3.057-.011 4.123-.06 1.065-.049 1.792-.218 2.427-.465a4.917 4.917 0 0 0 1.772-1.154 4.899 4.899 0 0 0 1.153-1.77c.248-.637.417-1.364.465-2.428.048-1.067.06-1.407.06-4.123s-.012-3.057-.06-4.123c-.049-1.064-.217-1.792-.465-2.427a4.918 4.918 0 0 0-1.153-1.772 4.893 4.893 0 0 0-1.77-1.153c-.637-.247-1.364-.417-2.428-.464C19.058 6.012 18.717 6 16.001 6s-3.057.011-4.123.06Z"/> 3703<title>Follow us on Instagram</title></svg> 3704 </a> 3705 <a 3706 href="https://www.facebook.com/customandcommercial.vw/" 3707 target="_blank" 3708 rel="noopener noreferrer" 3709 class="text-white transition-colors" 3710 > 3711 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 32 32" role="img"> 3712 <path d="M17.068 26.667v-9.732h3.267l.489-3.792h-3.756v-2.421c0-1.098.305-1.847 1.88-1.847h2.008V5.483c-.347-.046-1.54-.15-2.926-.15-2.896 0-4.878 1.768-4.878 5.013v2.797H9.877v3.793h3.275v9.73h3.916Z"/> 3713<title>Follow us on Facebook</title></svg> 3714 </a> 3715 <a 3716 href="https://twitter.com/candcparts" 3717 target="_blank" 3718 rel="noopener noreferrer" 3719 class="text-white transition-colors" 3720 > 3721 <?xml version="1.0" encoding="UTF-8"?> 3722<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 35 35" version="1.1" role="img"> 3723 <title>Group 9@1x</title> 3724 <g id="🧠-Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> 3725 <g id="Components-/-Footer-/-Desktop" transform="translate(-320, -465)">
3726 <g id="Group-7" transform="translate(220, 465)"> 3727 <g id="Group-9" transform="translate(100, 0)"> 3728 <circle id="Oval-Copy-2" fill="#807E7E" cx="17.5" cy="17.5" r="17.5"/> 3729 <g id="Icon/search-Copy-23" transform="translate(10, 10.625)" fill="#FFFFFF"> 3730 <path d="M11.8133496,-1.77635684e-15 L8.0230957,4.33242187 L4.74606445,-1.77635684e-15 L0,-1.77635684e-15 L5.67082031,7.41550781 L0.296162109,13.5585645 L2.59772461,13.5585645 L6.74595703,8.81856445 L10.3711816,13.5585645 L15,13.5585645 L9.08844727,5.74335937 L14.1133301,-1.77635684e-15 L11.8133496,-1.77635684e-15 Z M2.68602539,1.30450195 L4.05363281,1.30450195 L12.2806055,12.1817871 L11.0060449,12.1817871 L2.68602539,1.30450195 Z" id="Fill-1"/> 3731 </g> 3732 </g> 3733 </g> 3734 </g> 3735 </g> 3736</svg> 3737 </a> 3738</div> 3739<div class="pb-8"> 3740 <form 3741 class="form subscribe lg:max-w-[280px]" 3742 action="https://www.customandcommercial.com/newsletter/subscriber/new/" 3743 method="post" 3744 x-data="initNewsletterForm()" 3745 @submit.prevent="submitForm()" 3746 id="newsletter-validate-detail" 3747 aria-label="Subscribe to Newsletter" 3748 > 3749 <div class="text-white space-y-1 mb-5"> 3750 <p class="font-normal">Sign up to our newsletter and receive our latest special offers and discounts</p> 3751 </div> 3752 <div class="flex flex-col gap-5"> 3753 <label class="grow m-0"> 3754 <span class="sr-only">Email Address</span> 3755 <input 3756 type="email" 3757 id="newsletter-subscribe" 3758 name="email" 3759 class="form-input w-full py-2.5 px-5 rounded-[5px]" 3760 required 3761 placeholder="Enter your email address" 3762 > 3763 <input name="form_key" type="hidden" value="8yyKEOyvlaL6bVEW" /> </label> 3764 3765
3766<script data-cfasync="false"> 3767 'use strict'; 3768 3769 function initAmGdprCheckbox() { 3770 return { 3771 checked: false, 3772 consentId: null, 3773 3774 /** 3775 * @returns {void} 3776 */ 3777 init: function () { 3778 this.checked = this.$refs['checkbox'].checked; 3779 this.consentId = this.$el.dataset.consentId; 3780 }, 3781 3782 /** 3783 * @param {Event & { detail: string}} event 3784 * @returns {void} 3785 */ 3786 consentAcceptedHandler: function ({ detail: acceptedConsentId }) { 3787 if (acceptedConsentId === this.consentId) { 3788 this.checked = true; 3789 } 3790 }, 3791 3792 /** 3793 * @param {Event & { target: HTMLElement }} event 3794 * @returns {void} 3795 */ 3796 consentTextClickHandler: function ({ target }) { 3797 target.tagName === 'A' && this.openLink(target); 3798 }, 3799 3800 /** 3801 * @param {HTMLLinkElement} linkNode 3802 * @returns {void} 3803 */ 3804 openLink: function (linkNode) { 3805 const linkHref = linkNode.getAttribute('href'); 3806 linkHref === '#' 3807 ? this.$dispatch('amgdpr-show-policy', this.consentId) 3808 : window.location.href = linkHref; 3809 } 3810 } 3811 } 3812 3813 window.addEventListener( 3814 'alpine:init', 3815 () => Alpine.data('initAmGdprCheckbox', initAmGdprCheckbox), 3816 { once: true } 3817 ); 3818</script>
3818 3819<button class="shrink-0 btn justify-center rounded-[5px] py-2.5 px-5 text-base shadow-none hover:shadow-lg active:shadow transition bg-[#e18438] text-white border border-transparent hover:bg-[#e18438] active:bg-[#e18438] disabled:bg-[#e18438] disabled:bg-[#e18438]"> 3820 Subscribe </button> 3821 </div> 3822 <template x-if="displayErrorMessage"> 3823 <p class="flex items-center text-red mt-2"> 3824 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="inline-block mr-3" width="24" height="24" role="img"> 3825 <path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> 3826<title>exclamation-circle</title></svg> 3827 <template x-for="errorMessage in errorMessages"> 3828 <span x-html="errorMessage"></span> 3829 </template> 3830 </p> 3831 </template> 3832 </form> 3833 <div class="w-full"> 3834 </div> 3835
3835<script> 3836 function initNewsletterForm() { 3837 return { 3838 errors: 0, 3839 hasCaptchaToken: 0, 3840 displayErrorMessage: false, 3841 errorMessages: [], 3842 setErrorMessages(messages) { 3843 this.errorMessages = [messages] 3844 this.displayErrorMessage = this.errorMessages.length 3845 }, 3846 submitForm() { 3847 // Do not rename $form, the variable is expected to be declared in the recaptcha output 3848 const $form = document.querySelector('#newsletter-validate-detail'); 3849 3850 if (this.errors === 0) { 3851 $form.submit(); 3852 } 3853 } 3854 } 3855 } 3856 </script>
3856 3857</div> 3858 </div> 3859 </div> 3860 </div> 3861 3862 <div 3863 x-data="{ 3864 open: false, 3865 isMobile: false, 3866 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 3867 }" 3868 x-id="['collapse-id']" 3869 x-init="checkIsMobileResolution()" 3870 @resize.window.debounce="checkIsMobileResolution()" 3871 @visibilitychange.window.debounce="checkIsMobileResolution()" 3872 class="border-b border-white lg:border-b-0 last:border-b-0 w-full shop-model-col lg:w-4/12" 3873 > 3874 3875 <div 3876 :id="$id('collapse-id')" 3877 :role="isMobile && 'button'" 3878 :tabindex="isMobile && '0'" 3879 class="group flex w-full items-center justify-between pt-4 gap-2 font-bold text-white lg:pt-0" 3880 @click="isMobile && (open = !open)" 3881 @keyup.enter="isMobile && (open = !open)" 3882 @keyup.space="isMobile && (open = !open)" 3883 :aria-expanded="isMobile ? open : null" 3884 :aria-controls="isMobile ? $id('collapse-id') : null" 3885 > 3886 <h5 class="font-serif text-base text-semibold uppercase pb-2 mb-4 lg:border-b lg:border-white w-full">Customer Service</h5> 3887 <span 3888 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 3889 x-ref="toggleIcon" 3890 > 3891 3892 </span> 3893 </div> 3894 <div 3895 class="hidden lg:block" 3896 :class="{ 'hidden': isMobile && !open }" 3897 :aria-labelledby="isMobile ? $id('collapse-id') : null" 3898 :aria-hidden="isMobile && (open ? 'false' : 'true')" 3899 > 3900 <div class="pt-0.5 pb-2.5"> 3901 3902<nav aria-label="Customer Service"> 3903 <ul> 3904 <li> 3905 <a 3906 href="https://www.customandcommercial.com/contact/" 3907 class="block pb-1.5 hover:underline" 3908 >Contact Us</a> 3909 </li> 3910 <li> 3911 <a 3912 href="https://www.customandcommercial.com/terms/" 3913 class="block pb-1.5 hover:underline" 3914 >Terms</a> 3915 </li> 3916 <li> 3917 <a 3918 href="https://www.customandcommercial.com/delivery/" 3919 class="block pb-1.5 hover:underline" 3920 >Delivery</a> 3921 </li> 3922 <li> 3923 <a 3924 href="https://www.customandcommercial.com/returns/" 3925 class="block pb-1.5 hover:underline" 3926 >Returns</a> 3927 </li> 3928 <li> 3929 <a 3930 href="https://www.customandcommercial.com/privacy-policy/" 3931 class="block pb-1.5 hover:underline" 3932 >Privacy Policy</a> 3933 </li> 3934 <li> 3935 <a 3936 href="https://www.customandcommercial.com/faqs/" 3937 class="block pb-1.5 hover:underline" 3938 >FAQs</a> 3939 </li> 3940 </ul> 3941</nav> 3942 </div> 3943 </div> 3944 </div> 3945 3946 <div 3947 x-data="{ 3948 open: false, 3949 isMobile: false, 3950 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 3951 }" 3952 x-id="['collapse-id']" 3953 x-init="checkIsMobileResolution()" 3954 @resize.window.debounce="checkIsMobileResolution()" 3955 @visibilitychange.window.debounce="checkIsMobileResolution()" 3956 class="border-b border-white lg:border-b-0 last:border-b-0 w-full shop-parts-col lg:w-4/12" 3957 > 3958 3959 <div 3960 :id="$id('collapse-id')" 3961 :role="isMobile && 'button'" 3962 :tabindex="isMobile && '0'" 3963 class="group flex w-full items-center justify-between pt-4 gap-2 font-bold text-white lg:pt-0" 3964 @click="isMobile && (open = !open)" 3965 @keyup.enter="isMobile && (open = !open)" 3966 @keyup.space="isMobile && (open = !open)" 3967 :aria-expanded="isMobile ? open : null" 3968 :aria-controls="isMobile ? $id('collapse-id') : null" 3969 > 3970 <h5 class="font-serif text-base text-semibold uppercase pb-2 mb-4 lg:border-b lg:border-white w-full">Useful Links</h5>
3971 <span 3972 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 3973 x-ref="toggleIcon" 3974 > 3975 3976 </span> 3977 </div> 3978 <div 3979 class="hidden lg:block" 3980 :class="{ 'hidden': isMobile && !open }" 3981 :aria-labelledby="isMobile ? $id('collapse-id') : null" 3982 :aria-hidden="isMobile && (open ? 'false' : 'true')" 3983 > 3984 <div class="pt-0.5 pb-2.5"> 3985 3986<nav aria-label="Useful Links"> 3987 <ul> 3988 <li> 3989 <a 3990 href="https://www.customandcommercial.com/ourdistributors/" 3991 class="block pb-1.5 hover:underline" 3992 >Our Distributors</a> 3993 </li> 3994 <li> 3995 <a 3996 href="https://www.customandcommercial.com/ourtraders/" 3997 class="block pb-1.5 hover:underline" 3998 >Our Traders</a> 3999 </li> 4000 <li> 4001 <a 4002 href="https://www.customandcommercial.com/blog/" 4003 class="block pb-1.5 hover:underline" 4004 >Blog</a> 4005 </li> 4006 <li> 4007 <a 4008 href="https://www.customandcommercial.com/newsletters/" 4009 class="block pb-1.5 hover:underline" 4010 >Newsletters</a> 4011 </li> 4012 4013 <li> 4014 <a 4015 href="https://www.customandcommercial.com/about-us/" 4016 class="block pb-1.5 hover:underline" 4017 >About Us</a> 4018 </li> 4019 </ul> 4020</nav> 4021 </div> 4022 </div> 4023 </div> 4024 4025 <div 4026 x-data="{ 4027 open: false, 4028 isMobile: false, 4029 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 4030 }" 4031 x-id="['collapse-id']" 4032 x-init="checkIsMobileResolution()" 4033 @resize.window.debounce="checkIsMobileResolution()" 4034 @visibilitychange.window.debounce="checkIsMobileResolution()" 4035 class="border-b border-white lg:border-b-0 last:border-b-0 w-full info-col lg:w-4/12" 4036 > 4037 4038 <div 4039 :id="$id('collapse-id')" 4040 :role="isMobile && 'button'" 4041 :tabindex="isMobile && '0'" 4042 class="group flex w-full items-center justify-between pt-4 gap-2 font-bold text-white lg:pt-0" 4043 @click="isMobile && (open = !open)" 4044 @keyup.enter="isMobile && (open = !open)" 4045 @keyup.space="isMobile && (open = !open)" 4046 :aria-expanded="isMobile ? open : null" 4047 :aria-controls="isMobile ? $id('collapse-id') : null" 4048 > 4049 <h5 class="font-serif text-base text-semibold uppercase pb-2 mb-4 lg:border-b lg:border-white w-full">Vehicles</h5> 4050 <span 4051 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 4052 x-ref="toggleIcon" 4053 > 4054 4055 </span> 4056 </div> 4057 <div 4058 class="hidden lg:block" 4059 :class="{ 'hidden': isMobile && !open }" 4060 :aria-labelledby="isMobile ? $id('collapse-id') : null" 4061 :aria-hidden="isMobile && (open ? 'false' : 'true')" 4062 > 4063 <div class="pt-0.5 pb-2.5"> 4064 4065<nav aria-label="Vehicles"> 4066 <ul> 4067 <li> 4068 <a 4069 href="https://www.customandcommercial.com/brazilian-splitscreen.html" 4070 class="block pb-1.5 hover:underline" 4071 >Brazilian Splitscreen</a> 4072 </li> 4073 <li> 4074 <a 4075 href="https://www.customandcommercial.com/split-screen.html" 4076 class="block pb-1.5 hover:underline" 4077 >Split Screen</a> 4078 </li> 4079 <li> 4080 <a 4081 href="https://www.customandcommercial.com/beetle.html" 4082 class="block pb-1.5 hover:underline" 4083 >Beetle</a> 4084 </li> 4085 <li> 4086 <a 4087 href="https://www.customandcommercial.com/baywindow.html" 4088 class="block pb-1.5 hover:underline" 4089 >Baywindow</a> 4090 </li> 4091 <li> 4092 <a 4093 href="https://www.customandcommercial.com/brazilian-bay.html" 4094 class="block pb-1.5 hover:underline" 4095 >Brazilian Bay</a> 4096 </li> 4097 <li> 4098 <a 4099 href="https://www.customandcommercial.com/type-25.html" 4100 class="block pb-1.5 hover:underline" 4101 >Type 25</a> 4102 </li> 4103 <li> 4104 <a 4105 href="https://www.customandcommercial.com/type-3.html" 4106 class="block pb-1.5 hover:underline" 4107 >Type 3</a> 4108 </li> 4109 <li> 4110 <a 4111 href="https://www.customandcommercial.com/karmann-ghia.html" 4112 class="block pb-1.5 hover:underline" 4113 >Karmann Ghia</a> 4114 </li> 4115 <li> 4116 <a 4117 href="https://www.customandcommercial.com/t4.html" 4118 class="block pb-1.5 hover:underline" 4119 >T4</a> 4120 </li> 4121 <li> 4122 <a 4123 href="https://www.customandcommercial.com/t5.html" 4124 class="block pb-1.5 hover:underline" 4125 >T5</a> 4126 </li> 4127 </ul> 4128</nav> 4129<div class="lg:mt-4"> 4130 <div class="flex gap-2.5 flex-wrap justify-start" aria-label="Payment Providers"> 4131 <svg xmlns="http://www.w3.org/2000/svg" width="47" height="31" fill="none" viewBox="0 0 48 32" role="img"><g clip-path="url(#a)"><path fill="#fff" d="M0 4a4 4 0 0 1 4-4h40a4 4 0 0 1 4 4v24a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4z"/><path fill="#1434CB" d="M19.108 10.212 14.243 21.82H11.07l-2.393-9.263c-.146-.57-.272-.78-.714-1.02-.722-.391-1.913-.759-2.962-.987l.071-.337h5.109c.651 0 1.237.434 1.385 1.184l1.264 6.715 3.124-7.9zm12.435 7.817c.013-3.063-4.236-3.232-4.206-4.6.009-.417.405-.86 1.273-.973.43-.056 1.616-.099 2.96.52l.527-2.461A8.1 8.1 0 0 0 29.29 10c-2.968 0-5.056 1.578-5.074 3.837-.019 1.67 1.491 2.603 2.629 3.158 1.17.569 1.563.934 1.558 1.443-.008.778-.933 1.122-1.798 1.135-1.509.024-2.385-.408-3.083-.733l-.544 2.543c.702.322 1.997.603 3.339.617 3.154 0 5.218-1.558 5.227-3.97m7.837 3.79h2.777l-2.424-11.607H37.17c-.577 0-1.063.336-1.278.852L31.387 21.82h3.152l.626-1.734h3.852zm-3.35-4.113 1.58-4.358.91 4.358zm-12.632-7.494L20.915 21.82h-3.002l2.483-11.607z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h48v32H0z"/></clipPath></defs><title>visa</title></svg> 4132 <svg xmlns="http://www.w3.org/2000/svg" width="47" height="31" fill="none" viewBox="0 0 48 32" role="img"><g clip-path="url(#a_2)"><path fill="#fff" d="M0 4a4 4 0 0 1 4-4h40a4 4 0 0 1 4 4v24a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4z"/><path fill="#FF5F00" d="M28.376 8.139h-8.75v15.725h8.75z"/><path fill="#EB001B" d="M20.181 16a9.98 9.98 0 0 1 3.82-7.861A9.96 9.96 0 0 0 17.82 6c-5.522 0-10 4.478-10 10s4.478 10 10 10A9.96 9.96 0 0 0 24 23.861 9.98 9.98 0 0 1 20.182 16"/><path fill="#F79E1B" d="M40.178 16c0 5.522-4.477 10-10 10-2.333 0-4.477-.8-6.18-2.139A9.98 9.98 0 0 0 27.818 16a9.98 9.98 0 0 0-3.82-7.861A9.96 9.96 0 0 1 30.178 6c5.522 0 10 4.478 10 10"/></g><defs><clipPath id="a_2"><path fill="#fff" d="M0 0h48v32H0z"/></clipPath></defs><title>mastercard</title></svg> 4133 <svg xmlns="http://www.w3.org/2000/svg" width="47" height="31" fill="none" viewBox="0 0 48 32" role="img"><g clip-path="url(#a_3)"><path fill="#fff" d="M0 4a4 4 0 0 1 4-4h40a4 4 0 0 1 4 4v24a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4z"/><path fill="#000" d="M13.422 11.356a2.62 2.62 0 0 0 .604-1.856 2.6 2.6 0 0 0-1.718.88 2.47 2.47 0 0 0-.615 1.793 2.12 2.12 0 0 0 1.729-.817m.605.944c-.955-.063-1.761.541-2.217.541s-1.157-.509-1.91-.498a2.81 2.81 0 0 0-2.397 1.453c-1.06 1.76-.265 4.38.732 5.823.488.71 1.06 1.496 1.835 1.464s1.008-.467 1.888-.467 1.146.467 1.91.456c.763-.01 1.293-.71 1.781-1.421a6.4 6.4 0 0 0 .796-1.644 2.57 2.57 0 0 1-1.549-2.344 2.61 2.61 0 0 1 1.252-2.207 2.73 2.73 0 0 0-2.121-1.156m8.305-1.983a3.34 3.34 0 0 1 3.51 3.51 3.393 3.393 0 0 1-3.563 3.48h-2.291v3.648h-1.666V10.317zm-2.344 5.643h1.92a2.005 2.005 0 0 0 2.259-2.122 1.983 1.983 0 0 0-2.249-2.121h-1.91zm6.29 2.821c0-1.358 1.06-2.196 2.895-2.302l2.122-.127v-.594c0-.87-.584-1.39-1.56-1.39a1.485 1.485 0 0 0-1.644 1.146h-1.506c.096-1.39 1.294-2.45 3.214-2.45s3.097 1.008 3.097 2.567v5.367H31.37v-1.283a2.76 2.76 0 0 1-2.46 1.41 2.364 2.364 0 0 1-2.631-2.344m5.027-.7v-.615l-1.92.116c-.954.064-1.495.488-1.495 1.157 0 .668.562 1.124 1.421 1.124a1.87 1.87 0 0 0 1.994-1.782m3.033 5.781V22.61q.26.024.52 0a1.305 1.305 0 0 0 1.39-1.06l.137-.478-2.81-7.86h1.728l1.973 6.365 1.962-6.365H41l
4133-2.917 8.189c-.657 1.888-1.432 2.493-3.044 2.493-.18 0-.573-.011-.7-.032"/></g><defs><clipPath id="a_3"><path fill="#fff" d="M0 0h48v32H0z"/></clipPath></defs><title>apple-pay</title></svg> 4134 <svg xmlns="http://www.w3.org/2000/svg" width="47" height="31" fill="none" viewBox="0 0 48 32" role="img"><g clip-path="url(#a_4)"><path fill="#fff" d="M0 4a4 4 0 0 1 4-4h40a4 4 0 0 1 4 4v24a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4z"/><path fill="#3C4043" d="M22.869 16.488v3.933H21.62v-9.712h3.309q1.259 0 2.139.838.897.84.897 2.048c0 .826-.3 1.508-.898 2.06q-.869.828-2.138.827h-2.061zm0-4.583v3.387h2.087c.494 0 .91-.17 1.235-.5.331-.332.5-.735.5-1.19a1.62 1.62 0 0 0-.5-1.177q-.486-.516-1.235-.514h-2.087zm8.361 1.651q1.383 0 2.183.741.8.74.8 2.029v4.095h-1.19v-.923h-.052q-.771 1.139-2.06 1.138-1.1.001-1.84-.65-.741-.652-.741-1.626 0-1.033.78-1.638.781-.613 2.08-.611 1.113 0 1.827.41v-.287c0-.435-.17-.8-.514-1.105a1.76 1.76 0 0 0-1.209-.455c-.695 0-1.248.293-1.651.884l-1.099-.689q.909-1.314 2.685-1.313m-1.613 4.824c0 .325.136.598.416.812q.411.324.968.325.79 0 1.405-.585.616-.583.617-1.371-.584-.46-1.625-.462-.76.002-1.268.364-.515.381-.513.917M41 13.77l-4.16 9.57h-1.288l1.547-3.348-2.743-6.221h1.359l1.976 4.771h.026l1.924-4.771z"/><path fill="#4285F4" d="M17.904 15.695q-.001-.61-.104-1.171h-5.232v2.145l3.013.001a2.58 2.58 0 0 1-1.118 1.728v1.392h1.794c1.047-.97 1.647-2.402 1.647-4.095"/><path fill="#34A853" d="M14.464 18.398c-.5.337-1.142.534-1.895.534-1.453 0-2.685-.98-3.127-2.3h-1.85v1.436a5.57 5.57 0 0 0 4.977 3.067c1.505 0 2.768-.494 3.688-1.346z"/><path fill="#FABB05" d="M9.268 15.568c0-.37.062-.729.174-1.065v-1.436h-1.85A5.54 5.54 0 0 0 7 15.568c0 .9.214 1.749.592 2.501l1.85-1.435a3.4 3.4 0 0 1-.174-1.066"/><path fill="#E94235" d="M12.57 12.204c.82 0 1.556.283 2.136.835l1.59-1.588c-.966-.9-2.224-1.451-3.727-1.451a5.57 5.57 0 0 0-4.977 3.067l1.85 1.436c.442-1.32 1.674-2.3 3.127-2.3"/></g><defs><clipPath id="a_4"><path fill="#fff" d="M0 0h48v32H0z"/></clipPath></defs><title>google-pay</title></svg> 4135 <svg xmlns="http://www.w3.org/2000/svg" width="47" height="31" viewBox="0 0 47 31" fill="none" class="bg-white rounded-[3px] p-1.5" role="img"> 4136<path d="M3.5 0.5H43.5C45.1439 0.5 46.5 1.85614 46.5 3.5V27.5C46.5 29.1439 45.1439 30.5 43.5 30.5H3.50098C1.89739 30.4952 0.58863 29.2369 0.503906 27.6533L0.5 27.499V3.5C0.5 1.85614 1.85614 0.5 3.5 0.5Z" fill="white" stroke="#D8DADF"/> 4137<g clip-path="url(#clip0_2745_13775)"> 4138<path d="M20.1333 11L19.2139 15.3856H19.2422C19.8505 14.4895 20.7277 14.0201 21.6473 14.0201C23.1471 14.0201 23.713 15.2343 23.713 16.4205C23.713 18.4618 22.199 21 19.4546 21C18.252 21 17.3467 20.5173 17.3467 19.2761C17.3467 18.9587 17.4031 18.5585 17.46 18.2411L18.959 11H20.1335H20.1333ZM18.6341 18.2685C18.5773 18.4893 18.535 18.7652 18.5489 19.1104C18.5489 19.7447 18.9169 20.1033 19.6527 20.1033C21.4211 20.1033 22.4962 18.0482 22.4962 16.572C22.4962 15.7442 22.1282 14.9718 21.1948 14.9718C20.1758 14.9718 19.1291 15.9923 18.7897 17.5654L18.6341 18.2684V18.2685ZM28.5651 20.8483C28.5651 20.4483 28.6363 19.8138 28.7206 19.124H28.6923C27.9424 20.5035 27.0086 21 26.0325 21C24.8303 21 24.0664 20.0625 24.0664 18.7928C24.0664 16.4758 25.8201 14.0203 28.8056 14.0203C29.4566 14.0203 30.1776 14.1307 30.6447 14.2826L29.9516 17.7586C29.7252 18.9175 29.6257 20.1041 29.6544 20.8485H28.5651V20.8483ZM29.3289 15.0274C29.1594 14.986 28.9047 14.9306 28.5084 14.9306C26.7401 14.9306 25.297 16.7379 25.2826 18.5859C25.2826 19.3307 25.5517 20.0623 26.4571 20.0623C27.4334 20.0623 28.5932 18.8206 28.933 17.0415L29.3289 15.0274ZM35.3255 19.6757C34.9717 19.8416 34.4344 20.0485 33.7409 20.0485C32.6236 20.0485 32.0434 19.3445 32.0434 18.2687C32.0434 16.6965 33.1611 14.9862 34.9432 14.9862C35.4531 14.9862 35.8343 15.1098 36.1033 15.2345L36.443 14.324C36.231 14.2137 35.6506 14.0343 35.014 14.0343C32.5528 14.0343 30.8268 16.131 30.8268 18.4068C30.8268 19.8281 31.6898 21.0002 33.4583 21.0002C34.3777 21.0002 35.085 20.7519 35.4388 20.5862L35.3255 19.6759V19.6757ZM36.1759 20.531C36.4874 20.7653 37.1378 20.986 37.8736 20.9996C39.3166 20.9996 40.4907 20.1998 40.4907 18.7928C40.4907 18.0484 39.9675 17.4691 39.2037 17.0416C38.5954 16.7101 38.2699 16.4067 38.2699 15.9241C38.2699 15.3446 38.779 14.9167 39.4869 14.9167C39.9957 14.9167 40.4342 15.0828 40.675 15.2205L41.0001 14.3516C40.7458 14.1859 40.1939 14.0204 39.5714 14.0204C38.1285 14.0204 37.0959 14.9169 37.0959 16.0896C37.0959 16.7657 37.5341 17.3584 38.3265 17.7863C39.0339 18.1723 39.2743 18.4896 39.2743 19.0137C39.2743 19.6069 38.7651 20.1037 37.9729 20.1037C37.421 20.1037 3
41386.8271 19.8826 36.5016 19.6899L36.1761 20.5313L36.1759 20.531Z" fill="black"/> 4139<path d="M7.76188 17.8014C7.37742 17.8014 7.00421 17.7677 6.6471 17.7057L7.9192 11.23C10.4213 11.2888 12.4241 12.7362 12.4241 14.5139C12.4241 16.2915 10.3367 17.8014 7.76188 17.8014ZM10.1115 18.1401C12.3909 17.6705 14.0479 16.255 14.0479 14.5793C14.0479 12.9036 12.3455 11.4499 10.018 11H9.4516C11.7929 11.3635 13.5449 12.8312 13.5449 14.588C13.5449 16.3449 11.6265 17.9499 9.12243 18.2209C9.18566 18.2184 9.24909 18.2171 9.31291 18.2141C9.58428 18.2003 9.85042 18.1744 10.1117 18.1399M11.34 11C13.7852 11.5112 15.5634 12.9361 15.6586 14.7013C15.7853 17.0384 12.9127 19.0865 9.24269 19.2749C8.22975 19.3273 7.2622 19.232 6.3895 19.0181L6 20.9983H11.8709C14.1992 20.983 15.6474 19.1346 16.0256 17.3261L17.2634 11H11.3398H11.34ZM15.1448 14.2633C15.0558 12.6199 13.1771 11.3251 10.6762 11H10.3984C12.8454 11.4509 14.6354 12.8821 14.6354 14.5791C14.6354 15.8658 13.6075 17.0006 12.0429 17.6707C13.9555 16.9664 15.221 15.675 15.1446 14.2633" fill="#E40138"/> 4140</g> 4141<defs> 4142<clipPath id="clip0_2745_13775"> 4143<rect width="35" height="10" fill="white" transform="translate(6 11)"/> 4144</clipPath> 4145</defs> 4146<title>bacs</title></svg> 4147 </div> 4148</div> </div> 4149 </div> 4150 </div> 4151 </div> 4152 </div> 4153 </div> 4154<div class="lower-footer bg-lightorange"> 4155 <div class="container py-3.5 flex justify-between items-center flex-col sm:flex-row text-sm text-center"> 4156 <small class="font-serif text-sm font-normal"> 4157 <span>Copyright © 2026 Custom & Commercial All right reserved</span> 4158</small> 4159 <nav aria-label="Legal"> 4160 <ul class="m-0 p-0 list-none flex gap-4"> 4161 <li class="m-0 p-0"> 4162 <a 4163 href="https://www.customandcommercial.com/terms/" 4164 class="block hover:underline" 4165 >Terms & Conditions</a> 4166 </li> 4167 <li class="m-0 p-0"> 4168 <a 4169 href="https://www.customandcommercial.com/cookie-policy/" 4170 class="block hover:underline" 4171 >Cookie Policy</a> 4172 </li> 4173 <li class="m-0 p-0"> 4174 <a 4175 href="https://www.customandcommercial.com/privacy-policy/" 4176 class="block hover:underline" 4177 >Privacy Policy</a> 4178 </li> 4179 </ul> 4180 </nav> 4181 </div> 4182</div> 4183</div></footer> 4184<div x-data="initAmGdprPolicyPopup" x-bind="eventListeners"> 4185 <div x-cloak 4186 x-bind="overlay" 4187 class="fixed inset-0 flex items-center justify-center text-left bg-black bg-opacity-50 bg-black/50 z-40" 4188 > 4189 <div x-ref="amgdpr-policy-popup" role="dialog" aria-labelledby="amgdpr-policy-content" 4190 class="flex flex-col bg-white shadow-xl rounded-lg p-10 max-h-[95vh] max-w-6xl relative"> 4191 <div tabindex="0" @focusin="focusAcceptButton"></div> 4192 <button @click="hidePopup" 4193 x-ref="amgdpr-close-policy" 4194 aria-label="Close" 4195 class="absolute right-4 top-4 text-gray-300 transition 4196 hover:text-black focus:drop-shadow-md hover:drop-shadow-md" 4197 > 4198 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="h-6 w-6" width="24" height="24" role="img"> 4199 <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/> 4200<title>x</title></svg> 4201 </button> 4202 <div class="flex w-full"> 4203 <p class="w-full text-blue-500 text-3xl mb-5 ml-3" x-text="title"></p> 4204 </div> 4205 <div x-show="isNotificationTextVisible" class="flex w-full"> 4206 <p class="mb-4 p-4 bg-yellow-100 w-full" x-text="notificationText"></p> 4207 </div> 4208 <div id="amgdpr-policy-content" 4209 class="max-h-[500px] overflow-auto first:mx-0 child:mx-0" 4210 tabindex="0" 4211 x-html="policyText" 4212 ></div> 4213 <div class="mt-8 flex w-full justify-center"> 4214 <button x-focus-first 4215 @click="triggerAcceptPolicy" 4216 x-ref="amgdpr-accept-policy" 4217 type="button" 4218 class="btn btn-primary uppercase font-bold" 4219 aria-label="I have read and accept" 4220 > 4221 I have read and accept </button> 4222 </div> 4223 <div tabindex="0" @focusin="focusCloseButton"></div> 4224 </div> 4225 4226 </div> 4227</div> 4228
4229<script data-cfasync="false"> 4230 'use strict'; 4231 4232 /** 4233 * @typedef PolicyData 4234 * @property {string} policyVersion 4235 * @property {boolean} show 4236 * @property {string} title 4237 * @property{boolean} versionChanged 4238 * @property {boolean} hideClose 4239 * @property {boolean} action 4240 */ 4241 4242 function initAmGdprPolicyPopup() { 4243 return { 4244 ...hyva.modal({ 4245 dialog: 'amgdpr-policy-popup' 4246 }), 4247 eventListeners: { 4248 ['@amgdpr-accept-policy']() { 4249 if (this.consentId) { 4250 window.dispatchEvent(new CustomEvent('amgdpr-consent-accepted', {detail: this.consentId})); 4251 this.consentId = undefined; 4252 this.hidePopup(); 4253 } else { 4254 this.acceptPolicy(); 4255 } 4256 }, 4257 ['@amgdpr-show-policy.window'](event) { 4258 this.consentId = event.detail; 4259 this.fetchPolicyContent().then(() => { 4260 this.$nextTick(() => { 4261 this.showPopup(); 4262 }); 4263 }); 4264 }, 4265 ['@amgdpr-close-policy']() { 4266 this.hidePopup(); 4267 } 4268 }, 4269 textUrl: 'https://www.customandcommercial.com/gdpr/policy/policytext/', 4270 acceptUrl: 'https://www.customandcommercial.com/gdpr/policy/accept/', 4271 popupDataUrl: 'https://www.customandcommercial.com/gdpr/policy/popupData/', 4272 notificationText: 'We would like to inform you that our Privacy Policy has been amended.Please, read and accept the new terms.', 4273 showOnPageLoad: false, 4274 /** 4275 * @type PolicyData 4276 */ 4277 policyData: { 4278 policyVersion: '', 4279 show: false, 4280 title: '', 4281 versionChanged: false, 4282 hideClose: false, 4283 action: true 4284 }, 4285 title: '', 4286 policyText: '', 4287 consentId: undefined, 4288 init: async function () { 4289 await this.fetchPolicyData(); 4290 if ((this.showOnPageLoad && this.policyData.show) || this.policyData.versionChanged) { 4291 await this.fetchPolicyContent(); 4292 this.$nextTick(() => { 4293 this.show('amgdpr-policy-popup'); 4294 }); 4295 } 4296 }, 4297 4298 /** 4299 * @returns {void} 4300 */ 4301 hidePopup: function () { 4302 this.hide('amgdpr-policy-popup'); 4303 }, 4304 4305 /** 4306 * @returns {void} 4307 */ 4308 showPopup: function () { 4309 this.show('amgdpr-policy-popup'); 4310 }, 4311 4312 /** 4313 * @returns {void} 4314 */ 4315 triggerAcceptPolicy: function () { 4316 this.$dispatch('amgdpr-accept-policy'); 4317 }, 4318 4319 /** 4320 * @returns {Boolean} 4321 */ 4322 isNotificationTextVisible: function () { 4323 return this.notificationText && this.policyData.versionChanged; 4324 }, 4325 4326 /** 4327 * @returns {Promise} 4328 */ 4329 fetchPolicyData: function () { 4330 return fetch(this.popupDataUrl) 4331 .then(response => response.json()) 4332 .then((data) => { 4333 this.policyData = {...data} 4334 }) 4335 .catch(error => this.dispatchErrorMessage(error)); 4336 }, 4337 4338 /** 4339 * @returns {Promise} 4340 */ 4341 fetchPolicyContent: function () { 4342 this.title = ''; 4343 this.policyText = ''; 4344 4345 const params = !!this.consentId 4346 ? '?' + new URLSearchParams({consent_id: this.consentId}) 4347 : '' 4348 4349 return fetch(this.textUrl + params) 4350 .then(response => response.json()) 4351 .then(policyContent => { 4352 this.title = policyContent.title; 4353 this.policyText = policyContent.content; 4354 }).catch(error => this.dispatchErrorMessage(error)); 4355 }, 4356 4357 /** 4358 * @returns {void} 4359 */ 4360 acceptPolicy: function () { 4361 window.dispatchEvent(new Event('amgdpr-loading-start')); 4362 4363 const body = new URLSearchParams({form_key: hyva.getFormKey(), ...this.policyData}); 4364 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 4365 4366 fetch(this.acceptUrl, {method: 'POST', body, headers: {'Content-Type': contentType}}) 4367 .then(response => response.json()) 4368 .then(data => { 4369 if (data.error) { 4370 this.dispatchErrorMessage(data.message) 4371 } else { 4372 this.notificationText = ''; 4373 this.hide('amgdpr-policy-popup'); 4374 } 4375 }) 4376 .catch(error => this.dispatchErrorMessage(error)) 4377 .finally(() => window.dispatchEvent(new Event('amgdpr-loading-stop'))); 4378 }, 4379 4380 /** 4381 * @param {string} message 4382 * @returns {void} 4383 */ 4384 dispatchErrorMessage: function (message) { 4385 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 4386 { 4387 type: "error", 4388 text: message 4389 } 4390 ]); 4391 }, 4392 4393 /** 4394 * @returns {void} 4395 */ 4396 focusAcceptButton: function () { 4397 this.$refs['amgdpr-accept-policy']?.focus(); 4398 }, 4399 4400 /** 4401 * @returns {void} 4402 */ 4403 focusCloseButton: function () { 4404 this.$refs['amgdpr-close-policy']?.focus(); 4405 } 4406 } 4407 } 4408 4409 window.addEventListener( 4410 'alpine:init', 4411 () => Alpine.data('initAmGdprPolicyPopup', initAmGdprPolicyPopup), 4412 { once: true } 4413 ); 4414</script>
4414 4415
4416<script> 4417 (() => { 4418 function alpineSnapSlider(Alpine) { 4419 function roundUpIfGreaterThan(number, min = 8) { 4420 const decimalPart = number - Math.floor(number); 4421 return decimalPart >= min ? Math.ceil(number) : Math.floor(number); 4422 } 4423 Alpine.directive("snap-slider", (el, { modifiers }, { cleanup }) => { 4424 const track = el.querySelector("[data-track]"); 4425 if (!track) { 4426 console.warn( 4427 "No Slider track defined, reverting back to CSS slider.\nPlease create a wrapper element for your slides with the attribute data-track" 4428 ); 4429 return; 4430 } 4431 let initialLoad = true; 4432 let pager = el.querySelector("[data-pager]"); 4433 let slides = []; 4434 let inViewObserver, mutationObserver, resizeObserver; 4435 const navBtns = Array.from( 4436 el.querySelectorAll("[data-next], [data-prev]") 4437 ); 4438 const slideLabelSepparator = el.dataset.slideLabelSepparator || "of"; 4439 const useAutoPager = modifiers.includes("auto-pager"); 4440 const useGroupPager = modifiers.includes("group-pager"); 4441 const sliderLabel = el.hasAttribute("aria-label") && el.getAttribute("aria-label").toLowerCase().trim().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, ""); 4442 const sliderId = el.id || sliderLabel || "slider"; 4443 const markerIdName = "data-target-id"; 4444 const pagerClasses = el.dataset.pagerClass || "pager"; 4445 const markerClasses = el.dataset.markerClass || "pager-item"; 4446 function getInViewItems() { 4447 const inViewSlides = track.querySelectorAll("[data-in-view]"); 4448 const firstInViewSlide = inViewSlides[0] || null; 4449 const lastInViewSlide = inViewSlides[inViewSlides.length - 1] || null; 4450 const isAtStart = firstInViewSlide === slides[0]; 4451 const isAtEnd = lastInViewSlide === slides[slides.length - 1]; 4452 return { 4453 inViewSlides, 4454 totalInViewSlides: inViewSlides.length, 4455 firstInViewSlide, 4456 lastInViewSlide, 4457 isAtStart, 4458 isAtEnd, 4459 hasNoOverflow: isAtStart && isAtEnd 4460 }; 4461 } 4462 function groupPagerMarkers() { 4463 if (!pager || !useGroupPager) return; 4464 const totalVisibleSlides = roundUpIfGreaterThan( 4465 track.offsetWidth / slides[0].offsetWidth 4466 ); 4467 const markers = Array.from(pager.querySelectorAll("a, button")); 4468 markers.forEach((marker, index) => { 4469 marker.style.display = index % totalVisibleSlides === 0 ? null : "none"; 4470 }); 4471 } 4472 function handleInView(entries) { 4473 entries.forEach((entry) => { 4474 const marker = pager && (pager.querySelector(`[href="#${entry.target.id}"]`) || pager.querySelector( 4475 `[${markerIdName}="${entry.target.id}"]` 4476 )); 4477 entry.target.toggleAttribute( 4478 "data-in-view", 4479 entry.isIntersecting 4480 ); 4481 entry.target.toggleAttribute("inert", !entry.isIntersecting); 4482 marker?.setAttribute("aria-current", entry.isIntersecting); 4483 marker?.setAttribute( 4484 "tabindex", 4485 entry.isIntersecting ? "0" : "-1" 4486 ); 4487 }); 4488 const { isAtStart, isAtEnd, hasNoOverflow } = getInViewItems(); 4489 navBtns.forEach((btn) => { 4490 if (btn.hasAttribute("data-next")) { 4491 btn.style.visibility = hasNoOverflow ? "hidden" : null; 4492 isAtEnd ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled"); 4493 } 4494 if (btn.hasAttribute("data-prev")) { 4495 btn.style.visibility = hasNoOverflow ? "hidden" : null; 4496 isAtStart ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled"); 4497 } 4498 }); 4499 if (pager) { 4500 pager.style.visibility = hasNoOverflow ? "hidden" : null; 4501 } 4502 if (!initialLoad) { 4503 el.dispatchEvent( 4504 new CustomEvent("slideChange", { detail: getInViewItems() }) 4505 ); 4506 } else { 4507 initialLoad = false;
4508 } 4509 if (document.activeElement.parentElement.hasAttribute("data-pager")) { 4510 const activeItems = pager.querySelectorAll('[tabindex="0"]'); 4511 if (activeItems.length) { 4512 activeItems[0].focus(); 4513 } 4514 } 4515 } 4516 function getSlides() { 4517 if (!track) return []; 4518 return Array.from(track.children).filter( 4519 (child) => child.tagName.toLowerCase() !== "template" && child.tagName.toLowerCase() !== "script" 4520 ); 4521 } 4522 function refreshSlides() { 4523 initialLoad = true; 4524 slides = getSlides(); 4525 setupSlides(); 4526 if (useAutoPager) { 4527 createPager(); 4528 } else { 4529 setupPager(); 4530 } 4531 groupPagerMarkers(); 4532 setupObservers(); 4533 } 4534 function setupSlides() { 4535 slides = getSlides(); 4536 slides.forEach((slide, index) => { 4537 const totalSlides = slides.length; 4538 const currentSlide = index + 1; 4539 const existingLabel = slide.getAttribute("aria-label") || ""; 4540 const isImage = slide.tagName.toLowerCase() === "img" || slide.tagName.toLowerCase() === "picture"; 4541 const slideLabelTag = isImage ? "alt" : "aria-label"; 4542 const hasAutoLabel = existingLabel.startsWith(`${currentSlide} ${slideLabelSepparator} `); 4543 if (!slide.hasAttribute("id")) { 4544 slide.setAttribute("id", `${sliderId}-item-${currentSlide}`); 4545 } 4546 4547 if ((!slide.hasAttribute(slideLabelTag) || slide.getAttribute(slideLabelTag) === "") || hasAutoLabel) { 4548 slide.setAttribute(slideLabelTag, `${currentSlide} ${slideLabelSepparator} ${totalSlides}`); 4549 } 4550 4551 if (slide.tagName.toLowerCase() === "div" && !slide.hasAttribute("role")) { 4552 slide.setAttribute("role", "group"); 4553 } 4554 4555 if (pager && !isImage) { 4556 slide.setAttribute("role", "tabpanel"); 4557 } 4558 4559 if (slide.getAttribute('role') === "group") { 4560 slide.setAttribute("aria-roledescription", "item"); 4561 } 4562 }); 4563 if (!el.hasAttribute("role")) { 4564 el.setAttribute("role", "region"); 4565 } 4566 if (!el.hasAttribute("aria-roledescription")) { 4567 el.setAttribute("aria-roledescription", "carousel"); 4568 } 4569 track.setAttribute("tabindex", 0); 4570 track.setAttribute("aria-live", "polite"); 4571 } 4572 function setupNav() { 4573 navBtns.forEach((btn) => { 4574 btn.setAttribute("disabled", ""); 4575 btn.removeAttribute("hidden"); 4576 btn.classList.remove("invisible"); 4577 btn.style.visibility = null; 4578 }); 4579 } 4580 function createPager() { 4581 const newPager = document.createElement("nav"); 4582 newPager.setAttribute("data-pager", ""); 4583 newPager.setAttribute("role", "tablist"); 4584 newPager.classList.add(...pagerClasses.split(" ")); 4585 4586 slides.forEach((slide, index) => { 4587 const marker = document.createElement("button"); 4588 const slideId = slide.id || `${sliderId}-item-${index + 1}`; 4589 marker.setAttribute(markerIdName, slideId); 4590 marker.classList.add(...markerClasses.split(" ")); 4591 setupPagerMarker(marker, index); 4592 newPager.appendChild(marker); 4593 4594 if (slide.tagName.toLowerCase() !== "img" && slide.tagName.toLowerCase() !== "picture") { 4595 slide.removeAttribute("aria-roledescription"); 4596 slide.setAttribute("role", "tabpanel"); 4597 } 4598 }); 4599 4600 if (pager) { 4601 pager.setAttribute("role", "tablist"); 4602 pager.replaceChildren(...newPager.children); 4603 } else { 4604 track.after(newPager); 4605 pager = el.querySelector("[data-pager]"); 4606 } 4607 } 4608 function setupPager() { 4609 if (!pager) return; 4610 pager.setAttribute("role", "tablist"); 4611 const items = Array.from(pager.querySelectorAll("a, button"));
4612 items.forEach((marker, index) => setupPagerMarker(marker, index)); 4613 } 4614 function setupPagerMarker(marker, index) { 4615 const markerId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(markerIdName); 4616 const slideId = markerId || `${sliderId}-item-${index + 1}`; 4617 if (!markerId) { 4618 marker.setAttribute(markerIdName, slideId); 4619 } 4620 marker.setAttribute("role", "tab"); 4621 marker.setAttribute("aria-controls", slideId); 4622 marker.setAttribute("aria-posinset", index + 1); 4623 marker.setAttribute("aria-setsize", slides.length); 4624 marker.setAttribute("tabindex", "-1"); 4625 if (!marker.hasAttribute("aria-label")) { 4626 marker.setAttribute("aria-label", `Slide ${index + 1}`); 4627 } 4628 } 4629 function setupObservers() { 4630 if (inViewObserver) { 4631 inViewObserver.disconnect(); 4632 } 4633 inViewObserver = new IntersectionObserver( 4634 handleInView.bind(track), 4635 { root: track, threshold: 0.8 } 4636 ); 4637 slides.forEach((slide) => inViewObserver.observe(slide)); 4638 } 4639 function setupMutationObserver() { 4640 mutationObserver = new MutationObserver(refreshSlides.bind(track)); 4641 mutationObserver.observe(track, { 4642 childList: true, 4643 subtree: false 4644 }); 4645 } 4646 function setupResizeObserver() { 4647 resizeObserver = new ResizeObserver(groupPagerMarkers.bind(el)); 4648 resizeObserver.observe(el); 4649 } 4650 function goToSlideDir(dir = "next") { 4651 const { firstInViewSlide, lastInViewSlide } = getInViewItems(); 4652 const isPrev = dir === "prev"; 4653 let targetSlide = isPrev ? firstInViewSlide?.previousElementSibling : lastInViewSlide?.nextElementSibling; 4654 if (!targetSlide) return; 4655 if (targetSlide.tagName.toLowerCase() === "template") { 4656 targetSlide = isPrev ? targetSlide?.previousElementSibling : targetSlide?.nextElementSibling; 4657 } 4658 targetSlide.scrollIntoView({ 4659 block: "nearest", 4660 inline: isPrev ? "end" : "start", 4661 behavior: "smooth" 4662 }); 4663 } 4664 function pagerToSlide(event) { 4665 if (!event.target.closest("[data-pager]")) return; 4666 const marker = event.target.closest("a, button"); 4667 if (!marker) return; 4668 event.preventDefault(); 4669 const slideId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(markerIdName); 4670 const targetSlide = track.querySelector(`#${slideId}`); 4671 targetSlide?.scrollIntoView({ 4672 block: "nearest", 4673 inline: "start", 4674 behavior: "smooth" 4675 }); 4676 } 4677 setupSlides(); 4678 setupNav(); 4679 if (useAutoPager) { 4680 createPager(); 4681 } else { 4682 setupPager(); 4683 } 4684 setupObservers(); 4685 setupMutationObserver(); 4686 setupResizeObserver(); 4687 function eventHandler(event) { 4688 const target = event.target.closest( 4689 "[data-next], [data-prev], [data-pager]" 4690 ); 4691 if (!target) return; 4692 if (event.type === "click") { 4693 if (target.hasAttribute("data-next")) { 4694 goToSlideDir("next"); 4695 } else if (target.hasAttribute("data-prev")) { 4696 goToSlideDir("prev"); 4697 } else if (target.closest("[data-pager]")) { 4698 pagerToSlide(event); 4699 } 4700 } 4701 if (event.type === "keydown" && target.closest("[data-pager]")) { 4702 if (event.key === "ArrowRight") { 4703 goToSlideDir("next"); 4704 } else if (event.key === "ArrowLeft") { 4705 goToSlideDir("prev"); 4706 } 4707 } 4708 } 4709 el.addEventListener("click", eventHandler); 4710 el.addEventListener("keydown", eventHandler); 4711 cleanup(() => { 4712 inViewObserver?.disconnect(); 4713 mutationObserver?.disconnect(); 4714 resizeObserver?.disconnect(); 4715 el.removeEventListener("click", eventHandler); 4716 el.removeEventListener("keydown", eventHandler); 4717 }); 4718 }); 4719 } 4720 4721 document.addEventListener("alpine:init", () => { 4722 window.Alpine.plugin(alpineSnapSlider); 4723 }); 4724 })(); 4725</script>
vendor: 1 bytes, line 4725
4725
4726<script> 4727 (() => { 4728 function src_default(Alpine) { 4729 Alpine.directive("intersect", Alpine.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => { 4730 let evaluate = evaluateLater(expression); 4731 let options = { 4732 rootMargin: getRootMargin(modifiers), 4733 threshold: getThreshold(modifiers) 4734 }; 4735 let observer = new IntersectionObserver((entries) => { 4736 entries.forEach((entry) => { 4737 if (entry.isIntersecting === (value === "leave")) 4738 return; 4739 evaluate(); 4740 modifiers.includes("once") && observer.disconnect(); 4741 }); 4742 }, options); 4743 observer.observe(el); 4744 cleanup(() => { 4745 observer.disconnect(); 4746 }); 4747 })); 4748 } 4749 function getThreshold(modifiers) { 4750 if (modifiers.includes("full")) 4751 return 0.99; 4752 if (modifiers.includes("half")) 4753 return 0.5; 4754 if (!modifiers.includes("threshold")) 4755 return 0; 4756 let threshold = modifiers[modifiers.indexOf("threshold") + 1]; 4757 if (threshold === "100") 4758 return 1; 4759 if (threshold === "0") 4760 return 0; 4761 return Number(`.${threshold}`); 4762 } 4763 function getLengthValue(rawValue) { 4764 let match = rawValue.match(/^(-?[0-9]+)(px|%)?$/); 4765 return match ? match[1] + (match[2] || "px") : void 0; 4766 } 4767 function getRootMargin(modifiers) { 4768 const key = "margin"; 4769 const fallback = "0px 0px 0px 0px"; 4770 const index = modifiers.indexOf(key); 4771 if (index === -1) 4772 return fallback; 4773 let values = []; 4774 for (let i = 1; i < 5; i++) { 4775 values.push(getLengthValue(modifiers[index + i] || "")); 4776 } 4777 values = values.filter((v) => v !== void 0); 4778 return values.length ? values.join(" ").trim() : fallback; 4779 } 4780 4781 document.addEventListener("alpine:init", () => { 4782 window.Alpine.plugin(src_default); 4783 }); 4784 })(); 4785</script>
vendor: 1 bytes, line 4785
4785
4786<script> 4787 for (const [selector, deferUntil] of Object.entries({".product-slider section[x-data]":"intersect",".product-info [x-data]":"intersect","#filters-content [x-data]":"intersect","#review_form":"intersect","section[x-data^=initRecentlyViewedProductsComponent]":"intersect","div[x-data^=initBundleOptions]":"intersect","#product_addtocart_form [x-data]":"intersect","#notice-cookie-block":"idle"})) { 4788 document.querySelectorAll(selector).forEach(el => el.setAttribute('x-defer', `${deferUntil}`)); 4789 } 4790</script>
vendor: 1 bytes, line 4790
4790
4791<script> 4792 (function () { 4793 "use strict"; 4794 4795 const hasAlpine = new Promise(resolve => { 4796 window.addEventListener('alpine:initialized', resolve, {once: true, passive: true}); 4797 }); 4798 4799 const hasInteract = new Promise(resolve => { 4800 (events => { 4801 const onInteract = () => { 4802 resolve(); 4803 events.forEach(type => window.removeEventListener(type, onInteract)); 4804 } 4805 events.forEach(type => window.addEventListener(type, onInteract, {once: true, passive: true})) 4806 })(['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown']) 4807 }); 4808 4809 const onIntersect = (el) => { 4810 return new Promise(resolve => { 4811 const observer = new IntersectionObserver(entries => { 4812 for (const entry of entries) { 4813 if (entry.isIntersecting) { 4814 observer.disconnect() 4815 resolve(); 4816 } 4817 } 4818 }, {}); 4819 observer.observe(el); 4820 }); 4821 } 4822 4823 function runComponent(el) { 4824 hasAlpine.then(() => { 4825 el.removeAttribute('x-ignore'); 4826 queueMicrotask(() => Alpine.initTree(el)); 4827 }); 4828 } 4829 4830 function initDeferredComponents() { 4831 document.querySelectorAll('[x-data][x-defer]').forEach(el => { 4832 el.setAttribute('x-ignore', ''); 4833 const deferUntil = (el.getAttribute('x-defer') || '').trim(); 4834 switch (deferUntil) { 4835 case 'interact': 4836 hasInteract.then(() => runComponent(el)); 4837 break; 4838 case 'intersect': 4839 onIntersect(el).then(() => runComponent(el)) 4840 break; 4841 case 'idle': 4842 window.requestIdleCallback 4843 ? window.requestIdleCallback(() => runComponent(el), {timeout: 4000}) 4844 : setTimeout(() => runComponent(el), 4000); 4845 break; 4846 case 'eager': 4847 runComponent(el); 4848 break; 4849 default: 4850 if (deferUntil.startsWith('event:') && deferUntil.length > 6) { 4851 window.addEventListener(deferUntil.substring(6), () => runComponent(el), {once: true, passive: true}); 4852 } 4853 } 4854 }); 4855 } 4856 4857 window.addEventListener('alpine:init', initDeferredComponents, {once: true, passive: true}); 4858 })() 4859</script>
vendor: 1 bytes, line 4859
4859
vendor: 4,405 bytes, lines 4860-4967
4860<script> 4861 (() => { 4862 // packages/collapse/src/index.js 4863 function src_default(Alpine) { 4864 Alpine.directive("collapse", collapse); 4865 collapse.inline = (el, { 4866 modifiers 4867 }) => { 4868 if (!modifiers.includes("min")) 4869 return; 4870 el._x_doShow = () => {}; 4871 el._x_doHide = () => {}; 4872 }; 4873 4874 function collapse(el, { 4875 modifiers 4876 }) { 4877 let duration = modifierValue(modifiers, "duration", 250) / 1e3; 4878 let floor = modifierValue(modifiers, "min", 0); 4879 let fullyHide = !modifiers.includes("min"); 4880 if (!el._x_isShown) 4881 el.style.height = `${floor}px`; 4882 if (!el._x_isShown && fullyHide) 4883 el.hidden = true; 4884 if (!el._x_isShown) 4885 el.style.overflow = "hidden"; 4886 let setFunction = (el2, styles) => { 4887 let revertFunction = Alpine.setStyles(el2, styles); 4888 return styles.height ? () => {} : revertFunction; 4889 }; 4890 let transitionStyles = { 4891 transitionProperty: "height", 4892 transitionDuration: `${duration}s`, 4893 transitionTimingFunction: "cubic-bezier(0.4, 0.0, 0.2, 1)" 4894 }; 4895 el._x_transition = { 4896 in(before = () => {}, after = () => {}) { 4897 if (fullyHide) 4898 el.hidden = false; 4899 if (fullyHide) 4900 el.style.display = null; 4901 let current = el.getBoundingClientRect().height; 4902 el.style.height = "auto"; 4903 let full = el.getBoundingClientRect().height; 4904 if (current === full) { 4905 current = floor; 4906 } 4907 Alpine.transition(el, Alpine.setStyles, { 4908 during: transitionStyles, 4909 start: { 4910 height: current + "px" 4911 }, 4912 end: { 4913 height: full + "px" 4914 } 4915 }, () => el._x_isShown = true, () => { 4916 if (Math.abs(el.getBoundingClientRect().height - full) < 1) { 4917 el.style.overflow = null; 4918 } 4919 }); 4920 }, 4921 out(before = () => {}, after = () => {}) { 4922 let full = el.getBoundingClientRect().height; 4923 Alpine.transition(el, setFunction, { 4924 during: transitionStyles, 4925 start: { 4926 height: full + "px" 4927 }, 4928 end: { 4929 height: floor + "px" 4930 } 4931 }, () => el.style.overflow = "hidden", () => { 4932 el._x_isShown = false; 4933 if (el.style.height == `${floor}px` && fullyHide) { 4934 el.style.display = "none"; 4935 el.hidden = true; 4936 } 4937 }); 4938 } 4939 }; 4940 } 4941 } 4942 4943 function modifierValue(modifiers, key, fallback) { 4944 if (modifiers.indexOf(key) === -1) 4945 return fallback; 4946 const rawValue = modifiers[modifiers.indexOf(key) + 1]; 4947 if (!rawValue) 4948 return fallback; 4949 if (key === "duration") { 4950 let match = rawValue.match(/([0-9]+)ms/); 4951 if (match) 4952 return match[1]; 4953 } 4954 if (key === "min") { 4955 let match = rawValue.match(/([0-9]+)px/); 4956 if (match) 4957 return match[1]; 4958 } 4959 return rawValue; 4960 } 4961 4962 // packages/collapse/builds/cdn.js 4963 document.addEventListener("alpine:init", () => { 4964 window.Alpine.plugin(src_default); 4965 }); 4966 })(); 4967</script>
4967 4968
4969<script> 4970 function createAjaxLoader() { 4971 const loader = document.createElement('div'); 4972 loader.className = 'absolute inset-0 flex justify-center items-center loader'; 4973 loader.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57 57" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" class="w-full h-full p-2" role="img"> 4974 <style> 4975 @keyframes spinner-ball-triangle1 { 4976 0% { transform: translate(0%, 0%); } 4977 33% { transform: translate(38%, -79%); } 4978 66% { transform: translate(77%, 0%); } 4979 100% { transform: translate(0%, 0%); } 4980 } 4981 4982 @keyframes spinner-ball-triangle2 { 4983 0% { transform: translate(0%, 0%); } 4984 33% { transform: translate(38%, 79%); } 4985 66% { transform: translate(-38%, 79%); } 4986 100% { transform: translate(0%, 0%); } 4987 } 4988 4989 @keyframes spinner-ball-triangle3 { 4990 0% { transform: translate(0%, 0%); } 4991 33% { transform: translate(-77%, 0%); } 4992 66% { transform: translate(-38%, -79%); } 4993 100% { transform: translate(0%, 0%); } 4994 } 4995 </style> 4996 <circle cx="5" cy="50" r="5" style="animation: spinner-ball-triangle1 2.2s linear infinite"/> 4997 <circle cx="27" cy="5" r="5" style="animation: spinner-ball-triangle2 2.2s linear infinite"/> 4998 <circle cx="49" cy="50" r="5" style="animation: spinner-ball-triangle3 2.2s linear infinite"/> 4999<title>Loading...</title></svg> 5000`; 5001 return loader; 5002 } 5003 5004 function addAjaxLoader(button, loader) { 5005 button.setAttribute('data-label', button.getAttribute('aria-label')); 5006 button.removeAttribute('aria-label'); 5007 button.prepend(loader); 5008 button.classList.add('relative', '[&>:not(.loader)]:invisible'); 5009 button.disabled = true; 5010 } 5011 5012 function removeAjaxLoader(button, loader) { 5013 button.setAttribute('aria-label', button.getAttribute('data-label')); 5014 button.removeAttribute('data-label'); 5015 loader.remove(); 5016 button.classList.remove('[&>:not(.loader)]:invisible'); 5017 button.disabled = false; 5018 } 5019 5020 async function addAjaxCartForm(form, extraDelay = 500) { 5021 if (!form) return; 5022 event.preventDefault(); 5023 5024 if (form.hasAttribute('enctype') || !form.reportValidity()) { 5025 return form.submit(); 5026 } 5027 5028 const formData = new FormData(form); 5029 const button = form.querySelector('button:not([type=button], [type=reset])') 5030 ? form.querySelector('button:not([type=button], [type=reset])') 5031 : document.getElementById('product-addtocart-button'); 5032 const loader = createAjaxLoader(button); 5033 const formUenc = hyva.getUenc(); 5034 const postUrl = event.target.action.replace('%25uenc%25', formUenc); 5035 5036 let bodyUrl = new URLSearchParams(formData); 5037 bodyUrl.append("uenc", formUenc); 5038 addAjaxLoader(button, loader); 5039 5040 try { 5041 const response = await fetch(postUrl, { 5042 method: 'POST', 5043 body: bodyUrl.toString(), 5044 mode: "cors", 5045 credentials: "include", 5046 headers: { 5047 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 5048 'X-Requested-With': 'XMLHttpRequest' 5049 } 5050 }); 5051 5052 if (!response.ok) { 5053 return form.submit(); 5054 } 5055 5056 if (response.redirected) { 5057 return window.location.href = response.url; 5058 } 5059 5060 const responseData = await response.json(); 5061 if (responseData.backUrl) { 5062 return window.location.href = responseData.backUrl; 5063 } 5064 5065 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 5066 5067 const productFormData = { 5068 productId: formData.get("product"), 5069 options: {} 5070 } 5071 5072 for (const formEntry of formData.entries()) { 5073 if (!formEntry[0].startsWith('super_attribute')) continue; 5074 const formEntryOption = { [formEntry[0].match(/\[(\d+)\]/)[1]]: formEntry[
50741] }; 5075 productFormData.options = { ...productFormData.options, ...formEntryOption }; 5076 } 5077 5078 window.dispatchEvent(new CustomEvent('open-ajax-cart', { detail: productFormData })); 5079 } catch (err) { 5080 console.warn(err); 5081 window.dispatchEvent(new CustomEvent('product-addtocart-error')); 5082 5083 if (typeof window.dispatchMessages === "undefined") return; 5084 window.dispatchMessages([{ 5085 text:'There\u0020was\u0020a\u0020problem\u0020adding\u0020your\u0020item\u0020to\u0020the\u0020cart.', 5086 type: 'error' 5087 }], 5000); 5088 } finally { 5089 setTimeout(() => removeAjaxLoader(button, loader), extraDelay); 5090 } 5091 } 5092 5093 function removeAjaxCartForm(form) { 5094 removeEventListener('submit', form); 5095 } 5096 5097 document.addEventListener('submit', function(event) { 5098 if (!event.target.matches('#product_addtocart_form, .product_addtocart_form')) return; 5099 addAjaxCartForm(event.target, 500); 5100 }); 5101</script>
5101 5102 5103 <div x-data="initAjaxCartModal" x-bind="eventListeners"></div> 5104 5105 5106
5106<script> 5107 function initAjaxCartModal() { 5108 return { 5109 open: false, 5110 cart: {}, 5111 initialTotalCartAmount: 0, 5112 totalCartAmount: 0, 5113 cartItem: null, 5114 showSku: true, 5115 init() { 5116 window.addEventListener('private-content-loaded', (event) => { 5117 const cartData = event.detail.data && event.detail.data.cart; 5118 if (!cartData) return; 5119 5120 this.cart = cartData; 5121 this.totalCartAmount = this.cart.summary_count; 5122 this.initialTotalCartAmount = this.cart.summary_count; 5123 }, { once: true }); 5124 5125 this.$watch('totalCartAmount', (value, oldValue) => { 5126 if (this.initialTotalCartAmount === this.totalCartAmount) return; 5127 this.initialTotalCartAmount = this.totalCartAmount; 5128 }) 5129 }, 5130 sortObjectByNumberKey(obj) { 5131 let keys = Object.keys(obj).map(Number).sort((a, b) => a - b); 5132 let sortedObj = {}; 5133 keys.forEach(key => sortedObj[key] = obj[key]); 5134 return sortedObj; 5135 }, 5136 setNewCartData(event) { 5137 const productData = event.detail; 5138 window.addEventListener('private-content-loaded', (event) => { 5139 const cartData = event.detail.data && event.detail.data.cart; 5140 if (!cartData) return; 5141 this.cart = cartData; 5142 this.totalCartAmount = this.cart.summary_count; 5143 5144 if (!productData) return; 5145 this.getCurrentProductFromCart(productData); 5146 }); 5147 }, 5148 getCurrentProductFromCart(productData) { 5149 if (!this.cart.items) return; 5150 this.cartItem = this.cart.items.filter((item) => { 5151 if (item.product_id !== productData.productId) return; 5152 if (item.product_type !== "configurable") { 5153 return item; 5154 } 5155 5156 let cartItemOptions = {}; 5157 for (const option of item.options) { 5158 const filterOptions = { [option.option_id]: option.option_value }; 5159 cartItemOptions = { ...cartItemOptions, ...filterOptions } 5160 } 5161 5162 const cartItemOptionsCompare = JSON.stringify(this.sortObjectByNumberKey(cartItemOptions)); 5163 const productDataOptionsCompare = JSON.stringify(this.sortObjectByNumberKey(productData.options)); 5164 if (cartItemOptionsCompare === productDataOptionsCompare) { 5165 return item; 5166 } 5167 }); 5168 5169 if (this.cartItem > 1) { 5170 this.cartItem = this.cartItem.slice(0, 1); 5171 } 5172 5173 this.cartItem = this.cartItem[0]; 5174 }, 5175 getItemCountText() { 5176 if (this.totalCartAmount > 1) { 5177 return hyva.str('(\u00251\u0020items)', this.totalCartAmount); 5178 } 5179 5180 return hyva.str('(\u00251\u0020item)', this.totalCartAmount); 5181 }, 5182 getProductAlt() { 5183 const name = this.$el.dataset.productName; 5184 return name ? hyva.str('Product\u0020\u0022\u00251\u0022', name) : ''; 5185 }, 5186 hasCartItem() { 5187 return !!this.cartItem; 5188 }, 5189 canShowDetails() { 5190 return (this.cartItem.options && this.cartItem.options.length > 0) || this.showSku; 5191 }, 5192 canShowSku() { 5193 return this.cartItem.product_sku && this.showSku; 5194 }, 5195 openDialog() { 5196 this.open = true; 5197 }, 5198 closeDialog() { 5199 this.open = false;
5200 }, 5201 openAuthentication() { 5202 this.closeDialog(); 5203 this.$dispatch('toggle-authentication', { 5204 url: 'https://www.customandcommercial.com/checkout/' 5205 }); 5206 }, 5207 eventListeners: { 5208 ['@open-ajax-cart.window'](event) { 5209 this.setNewCartData(event); 5210 window.dispatchEvent(new CustomEvent('toggle-cart')); 5211 } 5212 } 5213 } 5214 } 5215 window.addEventListener('alpine:init', () => Alpine.data('initAjaxCartModal', initAjaxCartModal), { once: true }); 5216 </script>
5216 5217
5218<script type="module" 5219 src="https://www.customandcommercial.com/static/version1778675079/frontend/Actuate/c-and-c/en_US/Hyva_Theme/js/alpine3.min.js" 5220 defer 5221 crossorigin 5222></script>
vendor: 1 bytes, line 5222
5222
5223<script>try { 5224smileTracker.addPageVar('type.identifier', 'wordpress_postType_view'); 5225smileTracker.addPageVar('type.label', ''); 5226smileTracker.addPageVar('locale', 'en_US'); 5227} catch (err) { ; }</script>
5227<script> 5228 'use strict'; 5229 function dispatchMessages(messages, hideAfter) { 5230 const messagesEvent = new CustomEvent("messages-loaded", { 5231 detail: { 5232 messages: messages, 5233 hideAfter: hideAfter 5234 } 5235 }); 5236 window.dispatchEvent(messagesEvent); 5237 } 5238 5239 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 5240 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 5241 } 5242 5243 (function( hyva, undefined ) { 5244 5245 hyva.initFormKey = () => { 5246 const inputSelector = 'input[name="form_key"]', 5247 formKey = hyva.getFormKey(); 5248 5249 Array.from(document.querySelectorAll(inputSelector)).map(function (input) { 5250 input.value = formKey 5251 }); 5252 } 5253 5254 hyva.initMessages = () => { 5255 try { 5256 const messages = hyva.getCookie('mage-messages'); 5257 window.mageMessages = messages ? JSON.parse(decodeURIComponent(messages).replace(/\+/g, ' ')) : []; 5258 5259 dispatchMessages(window.mageMessages); 5260 5261 // empty `mage-messages` cookie 5262 const skipSetDomain = true; 5263 hyva.setCookie('mage-messages','', -1, skipSetDomain); 5264 5265 } catch (error) { 5266 console.warn('Error parsing Cookie Messages:', error); 5267 } 5268 } 5269 5270 window.addEventListener('DOMContentLoaded', hyva.initFormKey); 5271 hyva.alpineInitialized(hyva.initMessages) 5272 5273 }( window.hyva = window.hyva || {} )); 5274</script>
vendor: 1 bytes, line 5274
5274
5275<script> 5276 'use strict'; 5277 { 5278 const private_content_key = 'mage-cache-storage'; 5279 const private_content_expire_key = 'mage-cache-timeout'; 5280 const private_content_version_key = 'private_content_version'; 5281 const section_data_ids_key = 'section_data_ids'; 5282 const mage_cache_session_id_key = 'mage-cache-sessid'; 5283 const last_visited_store_key = 'last_visited_store'; 5284 5285 const ttl = 3600; 5286 5287 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 5288 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 5289 } 5290 5291 function loadSectionData () { 5292 const browserStorage = hyva.getBrowserStorage(); 5293 if (!browserStorage) { 5294 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 5295 [{ 5296 type: "warning", 5297 text: "Please enable LocalStorage in your browser." 5298 }] 5299 ); 5300 return; 5301 } 5302 try { 5303 let isInvalid = false; 5304 5305 if (hyva.getCookie(last_visited_store_key) !== CURRENT_STORE_CODE) { 5306 isInvalid = true; 5307 } 5308 hyva.setCookie(last_visited_store_key, CURRENT_STORE_CODE, false, false); 5309 5310 if (!hyva.getCookie(mage_cache_session_id_key)) { 5311 isInvalid = true; 5312 browserStorage.removeItem(private_content_key); 5313 const skipSetDomain = true; 5314 const days = false; 5315 hyva.setCookie(mage_cache_session_id_key, true, days, skipSetDomain) 5316 } 5317 5318 const cookieVersion = hyva.getCookie(private_content_version_key); 5319 const storageVersion = browserStorage.getItem(private_content_version_key); 5320 5321 if (cookieVersion && !storageVersion || cookieVersion !== storageVersion) { 5322 isInvalid = true; 5323 } 5324 5325 const privateContentExpires = browserStorage.getItem(private_content_expire_key); 5326 if (privateContentExpires && new Date(privateContentExpires) < new Date()) { 5327 browserStorage.removeItem(private_content_key); 5328 } 5329 5330 if (isInvalid && cookieVersion) { 5331 fetchPrivateContent([]); 5332 } else if (cookieVersion && storageVersion && cookieVersion === storageVersion) { 5333 const privateContent = JSON.parse(browserStorage.getItem(private_content_key)); 5334 if ( 5335 privateContent && 5336 privateContentExpires && 5337 privateContent.cart && 5338 privateContent.customer 5339 ) { 5340 dispatchPrivateContent(privateContent); 5341 } else { 5342 fetchPrivateContent([]); 5343 } 5344 } else { 5345 if (document.getElementById('default-section-data')) { 5346 const privateContent = JSON.parse(document.getElementById('default-section-data').innerText.trim()); 5347 dispatchPrivateContent(privateContent); 5348 } else { 5349 dispatchPrivateContent({}); 5350 } 5351 } 5352 5353 } catch (error) { 5354 console.warn('Error retrieving Private Content:', error); 5355 } 5356 } 5357 5358 hyva.alpineInitialized(loadSectionData) 5359 window.addEventListener('reload-customer-section-data', loadSectionData); 5360 5361 window.processSectionDataBeforeDispatch = window.processSectionDataBeforeDispatch || (data => data); 5362 5363 function dispatchPrivateContent(data) { 5364 5365 const privateContentEvent = new CustomEvent("private-content-loaded", { 5366 detail: { 5367 data: window.processSectionDataBeforeDispatch(data) 5368 } 5369 }); 5370 window.dispatchEvent(privateContentEvent); 5371 } 5372 5373 function fetchPrivateContent(sections) { 5374 fetch(`${BASE_URL}customer/section/load/?sections=${encodeURIComponent(sections.join(','))}`, { 5375 method: 'GET', 5376 headers: { 5377 'Content-Type': 'application/json', 5378 'X-Requested-With': 'XMLHttpRequest' 5379 } 5380 }) 5381 .then(response => response.ok && response.json()) 5382 .then( 5383 data => { 5384 if (data) { 5385 try { 5386 const browserStorage = hyva.getBrowserStorage(); 5387 5388 // merge new data preserving non-invalidated sections 5389 const oldSectionData = JSON.parse(browserStorage.getItem(private_content_key) || '{}') || {}; 5390 5391 if ((! data.cart || ! data.cart.cartId) && oldSectionData['checkout-data']) { 5392 delete oldSectionData['checkout-data']; 5393 } 5394 const newSectionData = Object.assign(oldSectionData, data); 5395 5396 dispatchPrivateContent(newSectionData); 5397 5398 // don't persist messages, they've been dispatched already 5399 if (newSectionData.messages && newSectionData.messages.messages ) { 5400 newSectionData.messages.messages = []; 5401 } 5402 5403 browserStorage.setItem(private_content_key, JSON.stringify(newSectionData)); 5404 5405 const expiresAt = new Date(Date.now() + (ttl * 1000)).toISOString(); 5406 browserStorage.setItem(private_content_expire_key, expiresAt); 5407 5408 const newCookieVersion = hyva.getCookie(private_content_version_key); 5409 browserStorage.setItem(private_content_version_key, newCookieVersion); 5410 5411 // We don't need the section_data_ids in Hyvä, but we store them for compatibility 5412 // with Luma Fallback. Otherwise, not all sections are loaded in Luma Checkout 5413 hyva.setCookie( 5414 section_data_ids_key, 5415 JSON.stringify( 5416 Object.keys(data).reduce((sectionDataIds, sectionKey) => { 5417 sectionDataIds[sectionKey] = data[sectionKey]['data_id']; 5418 return sectionDataIds; 5419 }, {}) 5420 ), 5421 false, 5422 true 5423 ); 5424 } catch (error) { 5425 console.warn("Couldn't store privateContent", error); 5426 } 5427 } 5428 } 5429 ); 5430 } 5431 } 5432</script>
5432 5433<!-- View Model Identities: -->
5433<script> 5434 (() => { 5435 document.addEventListener('submit', event => event.target.action = event.target.action.replace('%25uenc%25', hyva.getUenc())); 5436 })() 5437</script>
vendor: 1 bytes, line 5437
5437
5438<script> 5439 (events => { 5440 const dispatchUserInteractionEvent = () => { 5441 events.forEach(type => window.removeEventListener(type, dispatchUserInteractionEvent)) 5442 window.dispatchEvent(new Event('init-external-scripts')) 5443 }; 5444 events.forEach(type => window.addEventListener(type, dispatchUserInteractionEvent, {once: true, passive: true})) 5445 })(['touchstart', 'mousemove', 'wheel', 'scroll', 'keydown']) 5446</script>
vendor: 1 bytes, line 5446
5446
5447<script> 5448 function initConfigurableOptions(productId, optionConfig) { 5449 5450 function findPathParam(key) { 5451 const baseUrl = (BASE_URL.substring(0, 2) === '//' ? 'http:' : '') + BASE_URL; 5452 const baseUrlParts = (new URL(baseUrl)).pathname.replace(/\/$/, '').split('/'); 5453 const pathParts = window.location.pathname.split('/').slice(baseUrlParts.length + 3); 5454 for (let i = 0; i < pathParts.length; i += 2) { 5455 if (pathParts[i] === key && pathParts.length > i) { 5456 return pathParts[i + 1]; 5457 } 5458 } 5459 } 5460 5461 return { 5462 optionConfig, 5463 productId, 5464 itemId: (new URLSearchParams(window.location.search)).get('id') || findPathParam('id'), 5465 allowedAttributeOptions: [], 5466 selectedValues: [], 5467 init() { 5468 this.findAllowedAttributeOptions(); 5469 this.$nextTick(() => { 5470 if (typeof this.optionConfig.defaultValues === 'object') { 5471 for (const [attributeId, value] of Object.entries(this.optionConfig.defaultValues)) { 5472 this.changeOption(attributeId, value + ''); 5473 } 5474 } 5475 this.preselectQuerystringItems(); 5476 this.preselectLocationHashItems(); 5477 }); 5478 }, 5479 productIndex: 0, 5480 productIndexForPrice: 0, 5481 optionIsActive(attributeId, optionId) { 5482 return !!this.getAllowedAttributeOptions(attributeId).find( 5483 option => option.id === optionId 5484 ) 5485 }, 5486 optionIsEnabled(attributeId, optionId) { 5487 for (const productId in this.optionConfig.index) { 5488 if (this.optionConfig.index[productId][attributeId] === optionId) { 5489 return true; 5490 } 5491 } 5492 return false; 5493 }, 5494 findSimpleIndex() { 5495 this.productIndex = this.calculateSimpleIndexForPartialSelection(this.selectedValues); 5496 this.productIndexForPrice = this.findCheapestProductForPartialSelection(this.selectedValues); 5497 }, 5498 calculateSimpleIndexForPartialSelection(selectedValues) { 5499 if (selectedValues.length === 0) return 0; 5500 return this.findProductIdsForPartialSelection(selectedValues)[0]; 5501 }, 5502 calculateSimpleIndexForFullSelection(selectedValues) { 5503 if (! this.isFullSelection(selectedValues)) return false; 5504 5505 const productIndexes = this.optionConfig.index; 5506 return Object.keys(productIndexes).find(productIndex => { 5507 const productCandidateOptions = productIndexes[productIndex]; 5508 5509 for (const productOption in productCandidateOptions) { 5510 if ( 5511 !selectedValues[productOption] || 5512 selectedValues[productOption] !== productCandidateOptions[productOption] 5513 ) { 5514 return false; 5515 } 5516 } 5517 return productIndex; 5518 }); 5519 }, 5520 findAllowedAttributeOptions() { 5521 this.allowedAttributeOptions = this.calculateAllowedAttributeOptions(this.selectedValues); 5522 }, 5523 calculateAllowedAttributeOptions(selectedValues) { 5524 const allAttributes = this.optionConfig.attributes; 5525 const allAttributesSorted = Object.values(allAttributes).sort((a,b) => { 5526 return a.position - b.position 5527 }); 5528 5529 const newAllowedAttributeOptions = []; 5530 5531 allAttributesSorted.forEach(attribute => { 5532 const selectionWithoutAttr = Object.assign({}, this.removeAttrFromSelection(selectedValues, attribute.id)); 5533 const availableIndexes = this.calculateAvailableProductIndexes(selectionWithoutAttr); 5534 newAllowedAttributeOptions[attribute.id] = allAttributes[attribute.id].options.filter(option => { 5535 return !!option.products.find(product => { 5536 return availableIndexes.includes(product); 5537 }) 5538 }); 5539 }); 5540 return newAllowedAttributeOptions; 5541 }, 5542 calculateAvailableProductIndexes(selectedOptions) { 5543 if (Object.keys(selectedOptions).length === 0) { 5544 if (Object.values(this.optionConfig.salable || {}).length) { 5545 return [].concat.apply([], [].concat.apply([], Object.values(this.optionConfig.salable).map(Object.values))).filter((x, i, a) => a.indexOf(x) === i) 5546 } 5547 return Object.keys(this.optionConfig.index); 5548 } 5549 5550 const selectedIds = Object.keys(selectedOptions); 5551 if (Object.values(this.optionConfig.salable || {}).length) { 5552 const selectedOptionIndexes = selectedIds.map(attrId => { 5553 const optionValue = selectedOptions[attrId]; 5554 return this.optionConfig.salable[attrId] && this.optionConfig.salable[attrId][optionValue] || [] 5555 }) 5556 return selectedOptionIndexes.reduce((acc, optionIndexes) => { 5557 return acc.filter(index => optionIndexes.includes(index)); 5558 }); 5559 } else { 5560 const productIndexes = this.optionConfig.index; 5561 return Object.keys(productIndexes).filter(index => { 5562 for (const attrId of selectedIds) { 5563 if (productIndexes[index][attrId] !== `${selectedOptions[attrId]}`) return false 5564 } 5565 return true 5566 }); 5567 } 5568 }, 5569 findAttributeByOptionId(optionId) { 5570 for (const attributeId in this.optionConfig.attributes) { 5571 const attributeOptions = this.optionConfig.attributes[attributeId].options || []; 5572 if (attributeOptions.find(option => option.id === optionId)) { 5573 return attributeId; 5574 } 5575 } 5576 }, 5577 getAllowedAttributeOptions(attributeId) { 5578 return this.allowedAttributeOptions[attributeId] || [] 5579 }, 5580 getAllAttributeOptions(attributeId) { 5581 return ( 5582 this.optionConfig.attributes[attributeId] && 5583 this.optionConfig.attributes[attributeId].options 5584 ) || [] 5585 }, 5586 getProductIdsForOption(option) { 5587 const attributeId = this.findAttributeByOptionId(option.id); 5588 const allOptions = this.optionConfig.attributes[attributeId]; 5589 const opt = (allOptions && allOptions.options || []).find(o => o.id === option.id); 5590 return opt && opt.products 5591 ? opt.products 5592 : []; 5593 }, 5594 findProductIdsForPartialSelection(optionSelection) { 5595 const candidateProducts = Object.values(optionSelection).reduce((candidates, optionId) => { 5596 const newCandidates = this.getProductIdsForOption({id: optionId}); 5597 return candidates === null 5598 ? newCandidates 5599 : candidates.filter(productId => newCandidates.includes(productId)); 5600 }, null); 5601 return candidateProducts || []; 5602 },
5603 findCheapestProductForPartialSelection(optionSelection) { 5604 const candidateProducts = this.findProductIdsForPartialSelection(optionSelection); 5605 return candidateProducts.reduce((cheapest, simpleIdx) => { 5606 // in the first iteration we start with simpleIdx as the currently cheapest product 5607 if (! this.optionConfig.optionPrices[cheapest]) return simpleIdx; 5608 const knownCheapestPrice = this.optionConfig.optionPrices[cheapest].finalPrice.amount; 5609 return knownCheapestPrice > this.optionConfig.optionPrices[simpleIdx].finalPrice.amount 5610 ? simpleIdx 5611 : cheapest; 5612 }, 0) 5613 }, 5614 findProductIdToUseForOptionPrice(option) { 5615 // try to find a product for a complete selection 5616 const attributeId = this.findAttributeByOptionId(option.id); 5617 const optionSelection = Object.assign({}, this.selectedValues, {[attributeId]: option.id}); 5618 const matchingSimpleIndex = this.calculateSimpleIndexForFullSelection(optionSelection); 5619 // if there is no complete selection, use the cheapest product for the option 5620 return matchingSimpleIndex || this.findCheapestProductForPartialSelection(optionSelection); 5621 }, 5622 getAttributeOptionLabel(option) { 5623 const optionProduct = this.findProductIdToUseForOptionPrice(option); 5624 5625 if ((! optionProduct) || (optionProduct === this.productIndexForPrice)) { 5626 return option.label; 5627 } 5628 5629 const currentPrice = this.getOptionPriceAdjustmentBasePrice(); 5630 5631 if (this.optionConfig.optionPrices[optionProduct]) { 5632 const optionPrice = this.optionConfig.optionPrices[optionProduct].finalPrice.amount; 5633 if (optionPrice !== currentPrice){ 5634 return option.label + ' ' + hyva.formatPrice(optionPrice - currentPrice, true); 5635 } 5636 } 5637 return option.label; 5638 }, 5639 getOptionPriceAdjustmentBasePrice() { 5640 if (this.optionConfig.optionPrices[this.productIndexForPrice]) { 5641 return this.optionConfig.optionPrices[this.productIndexForPrice].finalPrice.amount 5642 } 5643 return this.optionConfig.prices.finalPrice.amount; // default price if no option selection 5644 }, 5645 clearOptionIfActive(optionId, value) { 5646 if (this.selectedValues[optionId] === value) { 5647 this.blurLabel() 5648 this.changeOption(optionId, '') 5649 } 5650 }, 5651 removeAttrFromSelection(selectedValues, attributeId) { 5652 attributeId = parseInt(attributeId); 5653 return selectedValues.reduce((newSelection, val, attr) => { 5654 if (attr !== attributeId) { 5655 newSelection[attr] = val; 5656 } 5657 return newSelection; 5658 }, []); 5659 }, 5660 changeOption(attributeId, value) { 5661 if (value === '') { 5662 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, attributeId) 5663 } else if (value && this.getAllowedAttributeOptions(attributeId).find(option => option.id === value)) { 5664 this.selectedValues[attributeId] = value; 5665 } 5666 this.findSimpleIndex(); 5667 this.findAllowedAttributeOptions(); 5668 this.updatePrices(); 5669 this.updateGallery(); 5670 5671 const candidates = this.findProductIdsForPartialSelection(this.selectedValues); 5672 5673 window.dispatchEvent( 5674 new CustomEvent( 5675 'configurable-selection-changed', 5676 { 5677 detail: { 5678 productId: this.productId, 5679 optionId: attributeId, 5680 value: value, 5681 productIndex: this.productIndex, 5682 selectedValues: this.selectedValues, 5683 candidates: candidates, 5684 skuCandidates: Object.values(candidates).map(id =>
5684 this.optionConfig.sku?.[id] ?? null).filter(Boolean), 5685 } 5686 } 5687 ) 5688 ); 5689 }, 5690 calculateIsMinimalPrice() { 5691 return ! this.isFullSelection(this.selectedValues); 5692 }, 5693 isFullSelection(selectedValues) { 5694 return Object.values(selectedValues).length === Object.keys(this.optionConfig.attributes).length; 5695 }, 5696 updatePrices() { 5697 const value = this.optionConfig.optionPrices[this.productIndexForPrice] || this.optionConfig.prices; 5698 window.dispatchEvent( 5699 new CustomEvent( 5700 "update-prices-" + this.productId, 5701 { 5702 detail: Object.assign( 5703 value, 5704 { isMinimalPrice: this.calculateIsMinimalPrice() } 5705 ) 5706 } 5707 ) 5708 ); 5709 }, 5710 updateGallery () { 5711 if (this.productIndex) { 5712 const images = this.optionConfig.images[this.productIndex]; 5713 images && window.dispatchEvent(new CustomEvent( 5714 "update-gallery", 5715 { detail: this.sortImagesByPosition(images) } 5716 )); 5717 } else { 5718 window.dispatchEvent(new Event("reset-gallery")); 5719 } 5720 }, 5721 sortImagesByPosition(images) { 5722 return images.sort((x, y) => { 5723 return x.position === y.position ? 0 : (parseInt(x.position) > parseInt(y.position) ? 1 : -1) 5724 }); 5725 }, 5726 onGetCartData(data) { 5727 }, 5728 preselectCartItems(data) { 5729 // pre-select options based on cart data for current (quote) itemId 5730 const cart = data && data.cart; 5731 if (cart && cart.items) { 5732 const cartItem = cart.items.find((item) => { 5733 return ( 5734 item.item_id === this.itemId 5735 && item.product_id === this.productId 5736 ) 5737 }); 5738 if (cartItem && cartItem.options && cartItem.options.length) { 5739 cartItem.options.map(option => { 5740 this.changeOption(option.option_id, option.option_value); 5741 }) 5742 } 5743 } 5744 }, 5745 preselectQuerystringItems() { 5746 // pre-select option like ?size=167 5747 const urlQueryParams = new URLSearchParams(window.location.search.replace('?','')); 5748 this.preselectItemsBasedOnLocation(attribute => urlQueryParams.get(attribute.code)); 5749 }, 5750 preselectLocationHashItems() { 5751 // pre-select option like #144=167 5752 const urlHashParams = new URLSearchParams(window.location.hash.replace('#','')); 5753 this.preselectItemsBasedOnLocation(attribute => urlHashParams.get(attribute.id)); 5754 }, 5755 preselectItemsBasedOnLocation(getLocationValue) { 5756 Object.values(this.optionConfig.attributes).map(attribute => { 5757 const v = getLocationValue(attribute); 5758 v && this.changeOption(attribute.id, v) 5759 }); 5760 } 5761 } 5762 } 5763</script>
vendor: 1 bytes, line 5763
5763
5764<script> 5765 function initSwatchOptions(swatchConfig) { 5766 return { 5767 swatchConfig, 5768 getAttributeSwatchData(attributeId) { 5769 const swatchConfig = Object.assign({}, this.swatchConfig[attributeId]); 5770 swatchConfig['details'] = JSON.parse(swatchConfig['additional_data']); 5771 5772 return swatchConfig; 5773 }, 5774 mapSwatchTypeNumberToTypeCode(typeNumber) { 5775 switch ("" + typeNumber) { 5776 case "1": 5777 return "color" 5778 case "2": 5779 return "image" 5780 case "3": 5781 return "empty" 5782 case "0": 5783 default: 5784 return "text" 5785 } 5786 }, 5787 getTypeOfFirstOption(attributeId) { 5788 for (const optionId in this.swatchConfig[attributeId]) { 5789 const option = this.swatchConfig[attributeId][optionId]; 5790 if (typeof option.type !== 'undefined') { 5791 return this.mapSwatchTypeNumberToTypeCode(option.type); 5792 } 5793 } 5794 }, 5795 getVisualSwatchType(attributeId, targetOptionId) { 5796 // If a type configuration is present for the given option id, use it 5797 const config = this.swatchConfig[attributeId]; 5798 if (config[targetOptionId] && typeof config[targetOptionId].type !== 'undefined') { 5799 return this.mapSwatchTypeNumberToTypeCode(config[targetOptionId].type); 5800 } 5801 5802 // Otherwise - if no config is present for the target option - use the type of the first option 5803 // with a type property from the attribute, thus assuming its the same type as the target option. 5804 // (This edge case condition can occur on single swatch products if some options are not salable) 5805 return this.getTypeOfFirstOption(attributeId); 5806 }, 5807 getSwatchType(attributeId, optionId) { 5808 // Deserialize the attribute details the first time they are used 5809 if (this.swatchConfig[attributeId] && ! this.swatchConfig[attributeId].details) { 5810 this.swatchConfig[attributeId] = this.getAttributeSwatchData(attributeId); 5811 } 5812 const type = this.swatchConfig[attributeId] && 5813 this.swatchConfig[attributeId].details && 5814 this.swatchConfig[attributeId].details.swatch_input_type || 5815 "empty"; 5816 return type === 'visual' ? this.getVisualSwatchType(attributeId, optionId) : type; 5817 }, 5818 isTextSwatch(attributeId, optionId) { 5819 return this.getSwatchType(attributeId, optionId) === 'text'; 5820 }, 5821 isVisualSwatch(attributeId, optionId) { 5822 const type = this.getSwatchType(attributeId, optionId); 5823 5824 return ['image', 'color'].includes(type); 5825 }, 5826 getSwatchBackgroundStyle(attributeId, optionId) { 5827 const config = this.getSwatchConfig(attributeId, optionId); 5828 const type = this.getSwatchType(attributeId, optionId); 5829 5830 if (type === "color") { 5831 return 'background-color:' + config.value; 5832 } else if (type === "image") { 5833 return "background: #fff url('" + config.value + "') no-repeat center"; 5834 } else { 5835 return ''; 5836 } 5837 }, 5838 getSwatchText(attributeId, optionId) { 5839 const config = this.getSwatchConfig(attributeId, optionId); 5840 return config.label || config.value || this.getOptionLabelFromOptionConfig(attributeId, optionId); 5841 }, 5842 getOptionLabelFromOptionConfig(attributeId, optionId) { 5843 // Fallback if no value is present in swatchConfig data 5844 // Reference issue https://gitlab.hyva.io/hyva-themes/magento2-default-theme/-/issues/190 5845 const option = this.getAllAttributeOptions(attributeId).filter(option => option.id === optionId); 5846 return option && option[0] && option[0].label ||''; 5847 }, 5848 getSwatchConfig(attributeId, optionId) { 5849 return this.swatchConfig[attributeId] && this.swatchConfig[attributeId][optionId] 5850 ? this.swatchConfig[attributeId][optionId] 5851 : false;
5852 }, 5853 activeTooltipItem: false, 5854 tooltipPositionElement: false, 5855 isTooltipVisible() { 5856 return this.activeTooltipItem && 5857 this.getSwatchConfig( 5858 this.activeTooltipItem.attribute, 5859 this.activeTooltipItem.item 5860 ); 5861 }, 5862 isFirstItemCol() { 5863 return this.activeTooltipItem.index === 0; 5864 }, 5865 getTooltipImageStyle(attributeId, optionId) { 5866 const config = this.getSwatchConfig(attributeId, optionId); 5867 const type = this.getSwatchType(attributeId, optionId); 5868 5869 if (type === "color") { 5870 return 'background-color:' + config.value + '; width: 110px; height: 90px;'; 5871 } else if (type === "image") { 5872 return "background: #ffffff url('" + config.thumb + 5873 "') center center no-repeat; width: 110px; height: 90px;"; 5874 } else { 5875 return 'display:none'; 5876 } 5877 }, 5878 getTooltipPosition() { 5879 return this.tooltipPositionElement ? 5880 `top: ${this.tooltipPositionElement.offsetTop}px;` + 5881 `left: ${ 5882 this.tooltipPositionElement.offsetLeft - ( 5883 this.tooltipPositionElement.closest('.snap') && 5884 this.tooltipPositionElement.closest('.snap').scrollLeft || 5885 0 5886 ) 5887 }px;` : '' 5888 }, 5889 getTooltipLabel() { 5890 return this.getSwatchConfig(this.activeTooltipItem.attribute, this.activeTooltipItem.item).label 5891 }, 5892 focusedLabel: false, 5893 focusLabel(optionId) { 5894 this.focusedLabel = optionId; 5895 }, 5896 blurLabel() { 5897 this.focusedLabel = false; 5898 }, 5899 showSwatches: false, 5900 initShowSwatchesIntersect() { 5901 if ('IntersectionObserver' in window && !window.scrollY) { 5902 let io = new IntersectionObserver( 5903 entries => { 5904 entries.map(entry => { 5905 if (entry.isIntersecting) { 5906 this.showSwatches = true; 5907 io.unobserve(this.$root); 5908 } 5909 }) 5910 } 5911 ); 5912 io.observe(this.$root); 5913 } else { 5914 this.showSwatches = true 5915 } 5916 } 5917 } 5918 } 5919</script>
vendor: 1 bytes, line 5919
5919
5920<script> 5921 'use strict'; 5922 5923 const grecaptchaV2LoadCallbacks = []; 5924 5925 function executeRecaptchaLoadCallbacks () { 5926 while (window.grecaptcha && grecaptchaV2LoadCallbacks.length > 0) { 5927 const callback = grecaptchaV2LoadCallbacks.pop(); 5928 typeof callback === 'string' ? window[callback]() : callback(); 5929 } 5930 } 5931 5932 (function() { 5933 const loadRecaptchaScript = (event) => { 5934 5935 if (window.grecaptcha || !event.target.form) { 5936 return; 5937 } 5938 5939 const url = 'https://www.google.com/recaptcha/api.js?onload=executeRecaptchaLoadCallbacks'; 5940 const siteKey = ''; 5941 const script = document.createElement('script'); 5942 script.type = 'text/javascript'; 5943 script.src = siteKey ? `${url}&render=${siteKey}` : `${url}&render=explicit`; 5944 script.async = true; 5945 document.head.appendChild(script); 5946 5947 script.addEventListener('load', () => { 5948 Array.from(document.querySelectorAll('button[data-recaptcha-btn]:disabled')).forEach(btn => btn.removeAttribute('disabled')); 5949 }); 5950 } 5951 5952 window.forceLoadRecaptchaScript = (form) => { 5953 loadRecaptchaScript({target: {form: form || 'dummy'}}); 5954 executeRecaptchaLoadCallbacks(); 5955 }; 5956 5957 document.body.addEventListener('input', loadRecaptchaScript, { once: true }); 5958 document.body.addEventListener('focus', loadRecaptchaScript, { once: true }); 5959 5960 }()); 5961 5962</script>
vendor: 1 bytes, line 5962
5962
5963<script> 5964 'use strict'; 5965 5966 (function () { 5967 const modals = []; 5968 const excludedFromFocusTrapping = new Set(); 5969 5970 function trapFocusInNextModalWithOverlay() { 5971 for (let idx = modals.length -1; idx >= 0; idx--) { 5972 const nextOnStack = modals[idx]; 5973 const nextDialogElement = nextOnStack.instance.$refs[nextOnStack.name]; 5974 if (! isOverlayDisabled(nextDialogElement)) { 5975 hyva.trapFocus(nextDialogElement); 5976 break; 5977 } 5978 } 5979 } 5980 5981 function focusables(dialogElement) { 5982 const selector = 'button, [href], input, textarea, select, details, [tabindex]:not([tabindex="-1"])'; 5983 return Array.from(dialogElement.querySelectorAll(selector)) 5984 .filter(el => !el.hasAttribute('disabled')); 5985 } 5986 5987 function firstVisible(elements) { 5988 const a = Array.from(elements); 5989 for (let i = 0; i < a.length; i++) { 5990 if (a[i].offsetWidth || a[i].offsetHeight || a[i].getClientRects().length) return a[i]; 5991 } 5992 return null; 5993 } 5994 5995 function isInViewport(element) { 5996 const rect = element && element.getBoundingClientRect(); 5997 return rect && 5998 rect.top >= 0 && 5999 rect.left >= 0 && 6000 rect.right <= window.innerWidth && 6001 rect.bottom <= window.innerHeight; 6002 } 6003 6004 function setFocusAfterTransition(dialogElement, duration) { 6005 const nested = Array.from(dialogElement.querySelectorAll('[role="dialog"]')); 6006 const candidates = Array.from(dialogElement.querySelectorAll('[x-focus-first]')); 6007 next: for (let candidate of candidates) { 6008 for (let child of nested) { 6009 if (child.contains(candidate)) continue next; 6010 } 6011 setTimeout(() => candidate.focus(), 50); 6012 break; 6013 } 6014 window.setTimeout(() => { 6015 const focusElement = firstVisible(dialogElement.querySelectorAll('[x-focus-first]')) || 6016 focusables(dialogElement)[0] || 6017 null; 6018 focusElement && isInViewport(focusElement) && focusElement.focus(); 6019 }, Math.max(1, duration)); 6020 } 6021 6022 function determineTrigger($refs, dialog, trigger) { 6023 if (typeof trigger === 'undefined' && typeof dialog === 'object' && dialog.target instanceof HTMLElement) { 6024 return dialog.target; 6025 } 6026 if (typeof dialog === 'string' && typeof trigger === 'object' && trigger.target instanceof HTMLElement) { 6027 return trigger.target; 6028 } 6029 if (typeof trigger === 'string') { 6030 try { 6031 return $refs[trigger] || document.querySelector(trigger) 6032 } catch (e) {} 6033 } 6034 if (trigger instanceof Element) { 6035 return trigger; 6036 } 6037 6038 return null; 6039 } 6040 6041 function isOverlayDisabled(dialog) { 6042 return dialog && dialog.hasAttribute('x-no-overlay') 6043 } 6044 6045 function areRemainingModalsWithoutOverlay(modals) 6046 { 6047 const overflowDisabled = modals.map(modal => modal.instance.$refs[modal.name]).filter(isOverlayDisabled); 6048 6049 return overflowDisabled.length === modals.length; 6050 } 6051 6052 window.hyva.modal = function(options = {}) { 6053 if (this.$el && this.$el.dataset && this.$el.dataset.options) { 6054 try { 6055 options = Object.assign(options, JSON.parse(this.$el.dataset.options || '{}')); 6056 } catch (e) { 6057 throw new Error('Cannot read the dialog options from the data-options attribute: not valid JSON'); 6058 } 6059 } 6060 6061 const config = Object.assign({ 6062 dialog: 'dialog', duration: 300, transitionEnter: 'transition ease-out duration-300', 6063 transitionEnterStart: 'opacity-0', 6064 transitionEnterEnd: 'opacity-100', 6065 transitionLeave: 'transition ease-in duration-300', 6066 transitionLeaveStart: 'opacity-100', 6067 transitionLeaveEnd: 'opacity-0', 6068 }, options); 6069 let lastHide = 0; 6070 6071 return { 6072 opened: {}, 6073 show(dialog, trigger) { 6074 const focusTargetAfterHide = determineTrigger(this.$refs, dialog, trigger); 6075 const name = typeof dialog === 'string' ? dialog : config.dialog; 6076 const dialogElement = this.$refs[name]; 6077 if (! dialogElement) { 6078 console.error(`Use $modal->getShowJs() in the open trigger, or specify a custom name with\n$modal->withDialogRefName("my-name") and use show("my-name", $event).`); 6079 return; 6080 } 6081 const useOverlay = ! dialogElement.hasAttribute('x-no-overlay'); 6082 6083 dialogElement.scrollTop = 0; 6084 6085 if (this.opened[name]) { 6086 return; 6087 } 6088 6089 if (focusTargetAfterHide) { 6090 focusTargetAfterHide.setAttribute('aria-expanded', 'true'); 6091 } 6092 6093 this.opened[name] = true; 6094 useOverlay && this.$nextTick(() => hyva.trapFocus(dialogElement)); 6095 setFocusAfterTransition(dialogElement, config.duration); 6096 6097 const frame = {name, instance: this, focusTarget: focu
6097sTargetAfterHide, time: Date.now()}; 6098 6099 modals.push(frame); 6100 if (useOverlay) { 6101 document.body.classList.add('overflow-hidden'); 6102 } 6103 return new Promise(resolve => frame.resolve = resolve); 6104 }, 6105 cancel() { 6106 this.hide(false); 6107 }, 6108 ok() { 6109 this.hide(true); 6110 }, 6111 hide(value) { 6112 if (Date.now() - lastHide < config.duration) { 6113 return; 6114 } 6115 lastHide = Date.now(); 6116 6117 const modal = modals.pop() || {}; 6118 if (! modal.instance) { 6119 return; 6120 } 6121 6122 const name = modal.name; 6123 this.opened[name] = false; 6124 hyva.releaseFocus(modal.instance.$refs[modal.name]) 6125 trapFocusInNextModalWithOverlay(); 6126 6127 const nextFocusAfterHide = modal.focusTarget; 6128 nextFocusAfterHide && setTimeout(() => { 6129 nextFocusAfterHide.setAttribute('aria-expanded', 'false'); 6130 nextFocusAfterHide.focus() 6131 }, config.duration); 6132 6133 if (modals.length === 0 || areRemainingModalsWithoutOverlay(modals)) { 6134 document.body.classList.remove('overflow-hidden'); 6135 } 6136 6137 modal.resolve(value); 6138 }, 6139 overlay(dialog) { 6140 dialog = this.$el.dataset.dialogRefName || dialog; 6141 const name = typeof dialog === 'string' ? dialog : config.dialog; 6142 return { 6143 ['x-show']() { 6144 return this.opened[name] 6145 }, 6146 ['x-transition:enter']: config.transitionEnter, 6147 ['x-transition:enter-start']: config.transitionEnterStart, 6148 ['x-transition:enter-end']: config.transitionEnterEnd, 6149 ['x-transition:leave']: config.transitionLeave, 6150 ['x-transition:leave-start']: config.transitionLeaveStart, 6151 ['x-transition:leave-end']: config.transitionLeaveEnd, 6152 ['@hyva-modal-show.window'](event) { 6153 event.detail && event.detail.dialog === name && this.show(name, event.detail.focusAfterHide) 6154 } 6155 }; 6156 }, 6157 showDialogOnInit() { 6158 const parent = this.$el.closest('[data-dialog-ref-name]'); 6159 if (parent && parent.dataset.dialogRefName) { 6160 this.$dispatch('hyva-modal-show', {dialog: parent.dataset.dialogRefName}) 6161 } 6162 } 6163 }; 6164 } 6165 6166 window.hyva.modal.peek = () => modals.length > 0 && modals[modals.length -1] 6167 6168 window.hyva.modal.pop = function () { 6169 if (modals.length > 0) { 6170 const modal = modals[modals.length -1]; 6171 modal.instance.hide(); 6172 } 6173 } 6174 6175 window.hyva.modal.excludeSelectorsFromFocusTrap = function (selectors) { 6176 typeof selectors === 'string' || selectors instanceof String 6177 ? excludedFromFocusTrapping.add(selectors) 6178 : selectors.map(selector => excludedFromFocusTrapping.add(selector)); 6179 } 6180 6181 window.hyva.modal.eventListeners = { 6182 keydown: event => { 6183 if (event.key === 'Escape') { 6184 window.hyva.modal.pop(); 6185 } 6186 }, 6187 click: event => { 6188 if (modals.length > 0) { 6189 const modal = modals[modals.length -1]; 6190 const dialog = modal.instance.$refs[modal.name]; 6191 if (modal.time + 50 < Date.now() && // if last click processing is more than 50ms ago 6192 ! isOverlayDisabled(dialog) && // if dialog has overlay 6193 ! dialog.contains(event.target)) { // if click is outside of dialog 6194 modal.instance.hide(); 6195 } 6196 } 6197 } 6198 }; 6199 6200 document.addEventListener('keydown', window.hyva.modal.eventListeners.keydown); 6201 6202 document.addEventListener('click', window.hyva.modal.eventListeners.click); 6203 })(); 6204 window.addEventListener('alpine:init', () => { 6205 Alpine.data('hyvaModal', window.hyva.modal); 6206 }, {once: true}); 6207</script>
6207 6208
6209<script data-cfasync="false"> 6210 'use strict'; 6211 6212 /** 6213 * @typedef {Object} CookieGroupData 6214 * @prop {Boolean} checked 6215 * @prop {[{description: string, lifetime: number|null, name: string, provider: string, type: string}]} cookies 6216 * @prop {String} description 6217 * @prop {string|null} groupId 6218 * @prop {Boolean} isEssential 6219 * @prop {String} name 6220 */ 6221 6222 function initAmastyCookieGroupsStorage() { 6223 return { 6224 /** 6225 * @type {[CookieGroupData]} 6226 */ 6227 groupData: [], 6228 cookiePolicy: undefined, 6229 lastUpdate: undefined, 6230 6231 /** 6232 * @returns {string|number[]} 6233 */ 6234 getEssentialGroupIds: function () { 6235 return this.groupData.reduce(function (essentialGroupIds, group) { 6236 if (group.isEssential) { 6237 essentialGroupIds.push(group.groupId) 6238 } 6239 6240 return essentialGroupIds; 6241 }, []); 6242 }, 6243 6244 /** 6245 * @returns {string|number[]} 6246 */ 6247 getCheckedGroupIds: function () { 6248 return this.groupData.reduce(function (checkedGroupIds, group) { 6249 if (group.checked) { 6250 checkedGroupIds.push(group.groupId); 6251 } 6252 6253 return checkedGroupIds; 6254 }, []); 6255 }, 6256 6257 /** 6258 * @param {string} cookieName 6259 * @returns {boolean} 6260 */ 6261 isCookieEssential: function (cookieName) { 6262 const allEssentialCookies = this.getEssentialCookies(); 6263 6264 return !!allEssentialCookies.find(cookie => cookie.name === cookieName); 6265 }, 6266 6267 /** 6268 * @returns {array} 6269 */ 6270 getEssentialCookies: function () { 6271 return this.groupData 6272 .filter(group => group.isEssential) 6273 .map(group => group.cookies) 6274 .flat(); 6275 }, 6276 6277 /** 6278 * @returns {boolean} 6279 */ 6280 hasGroupData: function () { 6281 return !!this.groupData.length; 6282 }, 6283 6284 /** 6285 * @param {string} cookieName 6286 * @param {string} pattern 6287 * @returns {boolean} 6288 */ 6289 isEssentialByPattern: function (cookieName, pattern) { 6290 const essentialCookiePatterns = this.getEssentialCookiesPattern(pattern); 6291 6292 return essentialCookiePatterns.some((pattern) => !!cookieName.match(this.getCookiePattern(pattern))); 6293 }, 6294 6295 /** 6296 * @param {string} pattern 6297 * @return {Array} 6298 */ 6299 getEssentialCookiesPattern: function (pattern) { 6300 return this.getCookiesByPattern(pattern, this.getEssentialCookies()); 6301 }, 6302 6303 /** 6304 * @param {string} pattern 6305 * @param {Array|undefined} cookies 6306 * @return {Array} 6307 */ 6308 getCookiesByPattern: function (pattern, cookies) { 6309 if (typeof cookie === 'undefined') { 6310 cookies = this.getAllCookies(); 6311 } 6312 6313 return cookies
6314 .map((cookie) => typeof cookie === 'object' ? cookie.name : cookie.split('=')[0].trim()) 6315 .filter((cookie) => cookie.match(pattern) !== null); 6316 }, 6317 6318 /** 6319 * @returns {array} 6320 */ 6321 getDisallowedCookies: function () { 6322 const disallowedCookie = decodeURIComponent(hyva.getCookie('amcookie_disallowed') ?? ''); 6323 if (!disallowedCookie) { 6324 return []; 6325 } 6326 6327 return disallowedCookie.split(','); 6328 }, 6329 6330 /** 6331 * @returns {string} 6332 */ 6333 getCookiePattern: function (pattern) { 6334 return `^${pattern.replaceAll('*', '.*')}$`; 6335 }, 6336 6337 /** 6338 * @returns {array} 6339 */ 6340 getAllCookies: function () { 6341 return document.cookie.split(';'); 6342 }, 6343 6344 /** 6345 * @param {boolean} setAcceptance 6346 * @returns {Promise} 6347 */ 6348 updateGroupData: function (setAcceptance = false) { 6349 const cookieFetchUrl = `${BASE_URL}amcookie/cookie/cookies`; 6350 return fetch(cookieFetchUrl, { 6351 method: "POST", 6352 headers: { 6353 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 6354 'X-Requested-With': 'XMLHttpRequest' 6355 }, 6356 body: new URLSearchParams({ 6357 allowed: hyva.getCookie('amcookie_allowed'), 6358 restriction: hyva.getCookie('amcookie_policy_restriction') 6359 }) 6360 }).then(response => { 6361 if (response.ok) { 6362 return response.json(); 6363 } 6364 6365 return Promise.reject(`${response.status} ${response.statusText}`); 6366 }).then(data => { 6367 this.cookiePolicy = data.cookiePolicy ?? 'allowed'; 6368 this.lastUpdate = data.lastUpdate ?? 0; 6369 this.groupData = [...data.groupData]; 6370 6371 hyva.setCookie('amcookie_policy_restriction', this.cookiePolicy, 10); 6372 if (setAcceptance) { 6373 amastyCookieManager.setLastCookieAcceptance(this.lastUpdate); 6374 } 6375 6376 return data; 6377 }).catch(error => { 6378 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 6379 { 6380 type: "error", 6381 text: error 6382 } 6383 ]); 6384 }).finally(() => window.dispatchEvent(new CustomEvent('amasty-cookie-group-updated'))); 6385 } 6386 } 6387 } 6388 6389 window.addEventListener( 6390 'alpine:init', 6391 () => Alpine.store('AmastyCookieGroups', initAmastyCookieGroupsStorage()), 6392 { once: true } 6393 ); 6394</script>
6394 6395
6396<script data-cfasync="false"> 6397 (function (amastyCookieManager) { 6398 'use strict'; 6399 6400 amastyCookieManager.pattern = '{(?<cookiePattern>.*)}'; 6401 6402 amastyCookieManager.getAmastyCookieGroupStorage = function () { 6403 return Alpine.store('AmastyCookieGroups') ?? {}; 6404 }; 6405 6406 amastyCookieManager.deleteDisallowedCookie = function () { 6407 const amastyCookieGroupStorage = this.getAmastyCookieGroupStorage(); 6408 const disallowedCookies = amastyCookieGroupStorage?.getDisallowedCookies() ?? []; 6409 6410 disallowedCookies.forEach((name) => { 6411 const pattern = name.match(this.pattern); 6412 if (!!pattern && !!amastyCookieGroupStorage) { 6413 const cookiePattern = amastyCookieGroupStorage.getCookiePattern(pattern.groups.cookiePattern) ?? ''; 6414 6415 amastyCookieGroupStorage.getCookiesByPattern(cookiePattern).forEach((cookieName) => { 6416 this.deleteNotEssentialCookie(cookieName); 6417 }); 6418 } else { 6419 this.deleteNotEssentialCookie(name); 6420 } 6421 }); 6422 }; 6423 6424 amastyCookieManager.isCookieAllowed = function (cookieName) { 6425 const allowedGroups = hyva.getCookie('amcookie_allowed'); 6426 const disallowedCookie = this.getAmastyCookieGroupStorage()?.getDisallowedCookies() ?? []; 6427 const isCookiePolicyAllowed = hyva.getCookie('amcookie_policy_restriction') === 'allowed'; 6428 const isCookieEssential = this.getAmastyCookieGroupStorage()?.isCookieEssential(cookieName); 6429 const isCookieEssentialByPattern = this.getAmastyCookieGroupStorage()?.isEssentialByPattern(cookieName, this.pattern); 6430 6431 if (!isCookiePolicyAllowed || isCookieEssential || isCookieEssentialByPattern) { 6432 return true; 6433 } 6434 6435 return !((!allowedGroups && !disallowedCookie) || this.isCookieDisallowed(cookieName)); 6436 }; 6437 6438 amastyCookieManager.isCookieDisallowed = function (cookieName) { 6439 const amastyCookieGroupStorage = this.getAmastyCookieGroupStorage(); 6440 const disallowedCookies = amastyCookieGroupStorage?.getDisallowedCookies() ?? []; 6441 6442 return disallowedCookies.some((cookie) => { 6443 const pattern = cookie.match(this.pattern); 6444 6445 return !!pattern && !!cookieName.match(amastyCookieGroupStorage?.getCookiePattern(pattern.groups.cookiePattern)); 6446 }) || disallowedCookies.indexOf(cookieName) !== -1; 6447 }; 6448 6449 amastyCookieManager.processManageableCookies = function () { 6450 let cookieScripts = document.querySelectorAll('script[data-amcookie-groupid][type="text/plain"]'); 6451 6452 cookieScripts.forEach((elem) => { 6453 if (this.isChecked(elem.dataset['amcookie-groupid'])) { 6454 let elClone = elem.cloneNode(true).setAttribute('type', 'text/javascript'); 6455 6456 elem.remove(); 6457 document.body.appendChild(elClone); 6458 } 6459 }); 6460 }; 6461 6462 amastyCookieManager.deleteNotEssentialCookie = function (cookieName) { 6463 if (!this.getAmastyCookieGroupStorage()?.isCookieEssential(cookieName)) { 6464 hyva.setCookie(cookieName, '', -1, true); 6465 } 6466 }; 6467 6468 amastyCookieManager.setLastCookieAcceptance = function (lastUpdate) { 6469 localStorage.setItem('am-last-cookie-acceptance', lastUpdate); 6470 }; 6471 6472 window.addEventListener('amasty-cookie-group-updated', () => { 6473 amastyCookieManager.deleteDisallowedCookie(); 6474 }); 6475 6476 window.addEventListener('private-content-loaded', () => { 6477 amastyCookieManager.getAmastyCookieGroupStorage()?.hasGroupData() && amastyCookieManager.deleteDisallowedCookie(); 6478 }); 6479 }(window.amastyCookieManager = window.amastyCookieManager || {})); 6480</script>
6480 6481
6482<script data-cfasync="false"> 6483 (function () { 6484 'use strict'; 6485 6486 window.addEventListener('cookiebar-action-accept', (event) => { 6487 const groups = event.detail?.include_checked_groups ? Alpine.store('AmastyCookieGroups').getCheckedGroupIds() : []; 6488 const url = `${BASE_URL}amcookie/cookie/savegroups`; 6489 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 6490 const body = new URLSearchParams({ 6491 form_key: hyva.getFormKey() 6492 }); 6493 groups.length && groups.map(groupId => body.append('groups[]', groupId)); 6494 6495 fetch(url, {method: 'POST', body, headers: {'Content-Type': contentType}}) 6496 .then(response => { 6497 if (response.ok) { 6498 return response.json(); 6499 } 6500 6501 return Promise.reject(`${response.status} ${response.statusText}`); 6502 }) 6503 .then(data => { 6504 !!data.success && Alpine.store('AmastyCookieGroups').updateGroupData(true); 6505 }) 6506 .catch(error => { 6507 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 6508 { 6509 type: "error", 6510 text: error 6511 } 6512 ]); 6513 }) 6514 .finally(() => { 6515 amastyCookieManager.processManageableCookies(); 6516 window.dispatchEvent(new CustomEvent('cookiebar-action-accept-finish')); 6517 window.dispatchEvent(new CustomEvent('amasty-cookie-group-updated')); 6518 // Wait cookiebar close animation 6519 setTimeout(() => { 6520 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 6521 }, 100); 6522 }); 6523 }); 6524 })(); 6525</script>
6525 6526
6527<script data-cfasync="false"> 6528 (function () { 6529 'use strict'; 6530 6531 window.addEventListener('cookiebar-action-allow', () => { 6532 const url = `${BASE_URL}amcookie/cookie/allow`; 6533 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 6534 const body = new URLSearchParams({ 6535 form_key: hyva.getFormKey() 6536 }); 6537 fetch(url, {method: 'POST', body, headers: {'Content-Type': contentType}}) 6538 .then(response => { 6539 if (response.ok) { 6540 return response; 6541 } 6542 6543 return Promise.reject(`${response.status} ${response.statusText}`); 6544 }) 6545 .then(() => { 6546 Alpine.store('AmastyCookieGroups').updateGroupData(true); 6547 }) 6548 .catch(error => { 6549 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 6550 { 6551 type: "error", 6552 text: error 6553 } 6554 ]); 6555 }) 6556 .finally(() => { 6557 amastyCookieManager.processManageableCookies(); 6558 window.dispatchEvent(new CustomEvent('cookiebar-action-allow-finish')); 6559 // Wait cookiebar close animation 6560 setTimeout(() => { 6561 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 6562 }, 100); 6563 }); 6564 }); 6565 })(); 6566</script>
6566 6567 6568<style> 6569 :root { 6570 /* Bar Color Settings */ 6571 --ambar-background: #fff; 6572 --ambar-links-color: #326ed1; 6573 --ambar-policy-text: #374151; 6574 --ambar-toggle-active-color: #2563eb; 6575 --ambar-toggle-inactive-color: #fff; 6576 6577 /* Buttons Color Settings */ 6578 --ambar-button-accept-color: #337ab7; 6579 --ambar-button-accept-hover-color: #337ab7; 6580 --ambar-button-accept-text: #fff; 6581 --ambar-button-accept-hover-text: #fff; 6582 6583 --ambar-button-allow-color: #337ab7; 6584 --ambar-button-allow-hover-color: #337ab7; 6585 --ambar-button-allow-text: #fff; 6586 --ambar-button-allow-hover-text: #fff; 6587 6588 --ambar-button-decline-color: #337ab7; 6589 --ambar-button-decline-hover-color: #337ab7; 6590 --ambar-button-decline-text: #fff; 6591 --ambar-button-decline-hover-text: #fff; 6592 6593 --ambar-button-settings-color: #337ab7; 6594 --ambar-button-settings-hover-color: #337ab7; 6595 --ambar-button-settings-text: #fff; 6596 --ambar-button-settings-hover-text: #fff; 6597 6598 /* Buttons Order Settings */ 6599 --ambar-button-accept-order: 0; 6600 --ambar-button-allow-order: 0; 6601 --ambar-button-settings-order: 0; 6602 --ambar-button-decline-order: 0; 6603 6604 /* Cookie Information Bar Color Settings */ 6605 --ambar-information-background: #fff; 6606 --ambar-information-title: #374151; 6607 --ambar-information-description: #374151; 6608 --ambar-information-table-header: #374151; 6609 --ambar-information-table-content: #374151; 6610 6611 /* Cookie Information Bar Done Button Settings */ 6612 --ambar-information-button-color: #1d4ed8; 6613 --ambar-information-button-hover-color: #1d4ed8; 6614 --ambar-information-button-text: #fff; 6615 --ambar-information-button-hover-text: #fff; 6616 6617 /* Settings Bar Done Button Settings */ 6618 --ambar-setting-button-color: #1d4ed8; 6619 --ambar-setting-button-hover-color: #1d4ed8; 6620 --ambar-setting-button-text: #fff; 6621 --ambar-setting-button-hover-text: #fff; 6622 6623 /* cookie_settings_bar_customisation */ 6624 --ambar-setting-bar-group-background: #fff; 6625 --ambar-setting-bar-group-title: #000; 6626 --ambar-setting-bar-group-description: #000; 6627 --ambar-setting-bar-group-links: #326ed1; 6628 } 6629</style> 6630 6631<style> 6632 :root { 6633 /* Sidebar Color Settings */ 6634 --ambar-sidebar-group-title-text: #374151; 6635 --ambar-sidebar-group-description-text: #374151; 6636 --ambar-sidebar-toggle-active-color: #2563eb; 6637 --ambar-sidebar-toggle-inactive-color: #fff; 6638 } 6639</style> 6640
6641<script data-cfasync="false"> 6642 'use strict'; 6643 6644 function initAmCookieActionDispatcherBtn() { 6645 return { 6646 actiondispatchHandler: function (event) { 6647 const { dispatchAction, dispatchPayload } = event.currentTarget.dataset; 6648 this.$dispatch(dispatchAction, this.parsePayload(dispatchPayload)); 6649 }, 6650 parsePayload: function (payload) { 6651 try { 6652 return JSON.parse(payload); 6653 } catch (e) { 6654 return null; 6655 } 6656 } 6657 }; 6658 } 6659 6660 window.addEventListener( 6661 'alpine:init', 6662 () => Alpine.data('initAmCookieActionDispatcherBtn', initAmCookieActionDispatcherBtn), 6663 { once: true } 6664 ); 6665</script>
6665 6666
6667<script data-cfasync="false"> 6668 'use strict'; 6669 6670 function initAmCookieGroups() { 6671 return { 6672 /** 6673 * @withContext x-for: $store.AmastyCookieGroups.groupData 6674 * @returns {string} 6675 */ 6676 getInputGroupId: function () { 6677 return `amcookie-${this.$el.dataset.groupPrefix}group-${this.group.groupId}` 6678 }, 6679 6680 /** 6681 * @withContext x-for: $store.AmastyCookieGroups.groupData 6682 * @returns {{"!bg-gray-400": boolean}} 6683 */ 6684 getInputStyles: function () { 6685 return { 6686 '!bg-gray-400': this.group.isEssential 6687 } 6688 }, 6689 6690 /** 6691 * @withContext x-for: $store.AmastyCookieGroups.groupData 6692 * @returns {void} 6693 */ 6694 toggleGroup: function () { 6695 this.group.checked = !this.group.checked; 6696 }, 6697 6698 /** 6699 * @withContext x-for: $store.AmastyCookieGroups.groupData 6700 * @returns {void} 6701 */ 6702 moreInfoClickHandler: function () { 6703 this.$dispatch(this.$el.dataset.dispatchAction, this.group); 6704 }, 6705 6706 moreInformationAboutCookieGroup: function () { 6707 return hyva.str('About\u0020\u0022\u00251\u0022\u0020Cookie\u0020Group', this.group.name); 6708 } 6709 } 6710 } 6711 6712 window.addEventListener( 6713 'alpine:init', 6714 () => Alpine.data('initAmCookieGroups', initAmCookieGroups), 6715 {once: true} 6716 ); 6717</script>
6717 6718 6719<section x-data="initAmGdprLoader" 6720 @amgdpr-loading-start.window="showLoader" 6721 @amgdpr-loading-stop.window="hideLoader" 6722> 6723 <div 6724 class="z-50 fixed inset-0 grid place-items-center bg-white/70 text-slate-800" 6725 x-cloak 6726 x-show="isLoading" 6727 x-transition.opacity.duration.200ms 6728> 6729 <div class="flex gap-10 items-center p-4"> 6730 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57 57" width="57" height="57" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"> 6731 <style> 6732 @keyframes spinner-ball-triangle1 { 6733 0% { transform: translate(0%, 0%); } 6734 33% { transform: translate(38%, -79%); } 6735 66% { transform: translate(77%, 0%); } 6736 100% { transform: translate(0%, 0%); } 6737 } 6738 6739 @keyframes spinner-ball-triangle2 { 6740 0% { transform: translate(0%, 0%); } 6741 33% { transform: translate(38%, 79%); } 6742 66% { transform: translate(-38%, 79%); } 6743 100% { transform: translate(0%, 0%); } 6744 } 6745 6746 @keyframes spinner-ball-triangle3 { 6747 0% { transform: translate(0%, 0%); } 6748 33% { transform: translate(-77%, 0%); } 6749 66% { transform: translate(-38%, -79%); } 6750 100% { transform: translate(0%, 0%); } 6751 } 6752 </style> 6753 <circle cx="5" cy="50" r="5" style="animation: spinner-ball-triangle1 2.2s linear infinite"/> 6754 <circle cx="27" cy="5" r="5" style="animation: spinner-ball-triangle2 2.2s linear infinite"/> 6755 <circle cx="49" cy="50" r="5" style="animation: spinner-ball-triangle3 2.2s linear infinite"/> 6756</svg> 6757 <span class="text-xl select-none"> 6758 Loading... </span> 6759 </div> 6760</div> 6761</section> 6762
6763<script data-cfasync="false"> 6764 'use strict'; 6765 6766 function initAmGdprLoader() { 6767 return { 6768 isLoading: false, 6769 6770 /** 6771 * @returns {void} 6772 */ 6773 showLoader: function () { 6774 this.isLoading = true; 6775 }, 6776 6777 /** 6778 * @returns {void} 6779 */ 6780 hideLoader: function () { 6781 this.isLoading = false; 6782 } 6783 } 6784 } 6785 6786 window.addEventListener( 6787 'alpine:init', 6788 () => Alpine.data('initAmGdprLoader', initAmGdprLoader), 6789 { once: true } 6790 ); 6791</script>
vendor: 1 bytes, line 6791
6791
6792<script> 6793 'use strict'; 6794 6795 function initAmQuoteButton(addUrl, isProductListing, isLoggedIn) { 6796 return { 6797 addUrl: addUrl, 6798 isProductListing: isProductListing, 6799 isLoggedIn: isLoggedIn, 6800 initButton: function ($el) { 6801 if (!this.isProductListing) { 6802 $el.parentNode.classList.add('flex-wrap'); 6803 } 6804 }, 6805 submitQuote: async function ($el) { 6806 const originalForm = this.getFormNode($el); 6807 if (!originalForm || !originalForm.checkValidity()) { 6808 originalForm?.reportValidity(); 6809 return; 6810 } 6811 6812 const formToSubmit = this.cloneForm(originalForm); 6813 const action = this.isProductListing 6814 ? formToSubmit.action.replace('checkout', 'amasty_quote') 6815 : this.addUrl; 6816 6817 const formData = new FormData(formToSubmit); 6818 const bodyUrl = new URLSearchParams(formData); 6819 6820 try { 6821 const response = await fetch(action, { 6822 method: 'POST', 6823 body: bodyUrl.toString(), 6824 mode: 'cors', 6825 credentials: 'include', 6826 headers: { 6827 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 6828 'X-Requested-With': 'XMLHttpRequest' 6829 } 6830 }); 6831 6832 if (!response.ok) { 6833 formToSubmit.classList.add('hidden'); 6834 document.body.appendChild(formToSubmit); 6835 formToSubmit.requestSubmit(); 6836 return; 6837 } 6838 6839 const responseData = await response.json(); 6840 6841 if (responseData.backUrl) { 6842 window.location.href = responseData.backUrl; 6843 return; 6844 } 6845 6846 // Reload quote cart customer section data 6847 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 6848 6849 // Open the quote minicart drawer 6850 window.dispatchEvent(new CustomEvent('toggle-amquote')); 6851 6852 } catch (err) { 6853 console.warn('Add to Quote error:', err); 6854 // Fallback to full page submit on network error 6855 formToSubmit.classList.add('hidden'); 6856 document.body.appendChild(formToSubmit); 6857 formToSubmit.requestSubmit(); 6858 } 6859 }, 6860 getFormNode: function ($el) { 6861 if (this.isProductListing) { 6862 return $el.closest('form'); 6863 } 6864 6865 return $el.form; 6866 }, 6867 cloneForm: function(originalForm) { 6868 const clonedForm = originalForm.cloneNode(); 6869 Array.from(originalForm.elements).forEach((element) => { 6870 const clonedElement = element.cloneNode(); 6871 this.removeAlpineAttributes(clonedElement.attributes); 6872 clonedElement.removeAttribute('form'); 6873 this.cloneSelectedElements(element, clonedElement); 6874 clonedForm.appendChild(clonedElement); 6875 }); 6876 6877 return clonedForm; 6878 }, 6879 removeAlpineAttributes: function (AttrNodeMap) { 6880 Array.from(AttrNodeMap) 6881 .filter((attribute) => /^(:|x-|@)\S*/.test(attribute.name)) 6882 .forEach((attribute) => AttrNodeMap.removeNamedItem(attribute.name)); 6883 }, 6884 cloneSelectedElements: function (element, clonedElement) { 6885 if (element.tagName === 'SELECT') { 6886 for (const option of element.options) { 6887 const clonedOption = option.cloneNode(); 6888 this.removeAlpineAttributes(clonedOption.attributes); 6889 clonedOption.selected = option.selected; 6890 clonedElement.appendChild(clonedOption); 6891 } 6892 } 6893 } 6894 }; 6895 } 6896</script>
6896 6897 6898<div x-cloak 6899 x-data="initModifyCartQuoteRemoveRequest" 6900 @toggle-cart.window="modifyRemoveRequest" 6901 @amquote-remove-request.window="amquoteRemoveRequest"> 6902 <dialog class="amquote-dialog-request"> 6903 <div x-cloak data-dialog-ref-name="amquote-remove-request" 6904 x-bind="overlay" 6905 class="fixed inset-0 bg-black bg-opacity-50 z-50"> 6906 <div class="fixed flex justify-center items-center text-left z-40 inset-0"> 6907 <div x-ref="amquote-remove-request" role="dialog" aria-modal="true" 6908 class="inline-block bg-white shadow-xl rounded-lg p-10 max-h-screen overflow-auto overscroll-y-contain max-w-lg"> 6909 6910<div class="flex flex-col w-full"> 6911 <p> 6912 Are you sure you would like to remove this item from the shopping cart? </p> 6913 <p> 6914 This item is a part of the approved quote. Removing it will remove all quote items from the cart. </p> 6915</div> 6916<div class="w-full flex flex-row gap-3 mt-4"> 6917 <button type="button" 6918 class="btn btn-secondary" 6919 @click="hideDialog" 6920 data-dialog-name="amquote-remove-request"> 6921 Cancel </button> 6922 <button type="button" 6923 class="btn btn-primary" 6924 @click="removeFromMiniCart"> 6925 OK </button> 6926</div> 6927 </div> 6928 </div> 6929 </div> 6930 </dialog> 6931</div> 6932
6933<script> 6934 'use strict'; 6935 6936 function initModifyCartQuoteRemoveRequest() { 6937 const selectors = { 6938 configureQuoteButtons: '#cart-drawer .btn[href^="#hide-element"]', 6939 deleteQuoteButton: 'button.btn.btn-primary, button[\\@click="deleteItemFromCart"]', 6940 dialogContainer: 'dialog.amquote-dialog-request' 6941 } 6942 const originalModal = hyva.modal(); 6943 const originalHide = originalModal.hide; 6944 6945 originalModal.hide = function (value) { 6946 this.getAmRequestModalDialog()?.close(); 6947 6948 return originalHide.call(this, value); 6949 }; 6950 6951 return Object.assign( 6952 originalModal, 6953 { 6954 itemId: null, 6955 6956 modifyRemoveRequest: function () { 6957 const configureQuoteButtons = document.querySelectorAll(selectors.configureQuoteButtons); 6958 6959 configureQuoteButtons.forEach((configureButton) => { 6960 const itemId = configureButton.href?.match(/(?:#hide-element-)(?<itemId>\d+)/).groups?.itemId; 6961 6962 !!itemId && this.replaceDeleteAction( 6963 itemId.toString(), 6964 this.findDeleteQuoteButton(configureButton.parentNode) 6965 ); 6966 }); 6967 }, 6968 6969 findDeleteQuoteButton: function (parentNode) { 6970 const allButtons = parentNode.querySelectorAll(selectors.deleteQuoteButton); 6971 return Array.from(allButtons).filter((button) => { 6972 return button.attributes['@click']?.value.includes('deleteItemFromCart'); 6973 }); 6974 }, 6975 6976 replaceDeleteAction: function (itemId, deleteButtons) { 6977 deleteButtons.forEach((deleteButton) => { 6978 const cloneButton = deleteButton.cloneNode(true); 6979 cloneButton.removeAttribute('@click'); 6980 cloneButton.addEventListener('click', () => { 6981 window.dispatchEvent(new CustomEvent('amquote-remove-request', { detail: itemId })); 6982 }); 6983 deleteButton.parentNode.insertBefore(cloneButton, deleteButton); 6984 deleteButton.remove(); 6985 }); 6986 }, 6987 6988 showConfirm: function ({ detail: itemId }) { 6989 this.itemId = itemId; 6990 this.show('amquote\u002Dremove\u002Drequest'); 6991 6992 // Open <dialog> on top, so it’s not blocked by other dialogs 6993 this.$nextTick(() => { 6994 this.getAmRequestModalDialog().showModal(); 6995 }); 6996 }, 6997 6998 getAmRequestModalDialog: function () { 6999 return document.querySelector(selectors.dialogContainer); 7000 }, 7001 7002 removeFromMiniCart: function () { 7003 const url = BASE_URL + 'amasty_quote/quote/removeFromMinicart'; 7004 7005 fetch(url, { 7006 headers: { 7007 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 7008 }, 7009 body: 'form_key=' + hyva.getFormKey() + '&item_id=' + this.itemId, 7010 method: 'POST' 7011 }).then((response) => { 7012 if (response.redirected) { 7013 window.location.href = response.url; 7014 } else if (response.ok) { 7015 return response.json(); 7016 } 7017 }).then((response) => { 7018 if (response.success === true) { 7019 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 7020 } else if (!!response.error_message) { 7021 window.dispatchMessages && window.dispatchMessages([{ 7022 type: 'error', 7023 text: response.error_message 7024 }]); 7025 } 7026 }).finally(() => { 7027 this.itemId = null; 7028 this.hide('amquote\u002Dremove\u002Drequest'); 7029 }); 7030 }, 7031 7032 amquoteRemoveRequest(event) { 7033 return this.showConfirm(this.$event) 7034 }, 7035 7036 hideDialog(event) { 7037 return this.hide(`${this.$el.dataset.dialogName}`) 7038 } 7039 }) 7040 } 7041 7042 window.addEventListener( 7043 'alpine:init', 7044 () => Alpine.data( 7045 'initModifyCartQuoteRemoveRequest', 7046 initModifyCartQuoteRemoveRequest 7047 ), 7048 {once: true} 7049 ); 7050</script>
7050 7051 7052<div x-data="initAmastyCookieBar" 7053 x-bind="eventListeners" 7054 x-cloak 7055 id="am-cookie-bar" 7056 > 7057 <div x-cloak data-dialog-ref-name="am-cookie-bar" 7058 x-bind="overlay" 7059 class="overflow-y-auto overflow-x-hidden fixed z-40 inset-0 backdrop-blur-sm bg-white/30 am-cookie-sidebar"> 7060 <div class="fixed left-0 top-0 flex h-full max-w-md min-w-[320px] w-[20%] 7061 border-t border-b border-container-darker bg-container-darker shadow inset-0"> 7062 <div x-ref="am-cookie-bar" role="dialog" aria-modal="true" 7063 aria-label="Cookie Bar" 7064 class="max-h-full w-full visible p-4 pt-2 pb-6 bg-ambar-background"> 7065 7066<div class="flex flex-col flex-nowrap justify-start h-full text-sm"> 7067 <div class="flex justify-end"> 7068 <button @click="closeCookieBarHandler" 7069 aria-label="Close Cookie Bar" 7070 class="text-gray-300 transition hover:text-black focus:drop-shadow-md hover:drop-shadow-md mt-1" 7071 > 7072 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" 7073 stroke="currentColor" stroke-width="2"> 7074 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 7075 </svg> 7076 </button> 7077 </div> 7078 7079 <div class="flex flex-wrap overflow-y-auto ambar-sidebar-group-list mb-3 mt-4 ml-3 pr-2"> 7080 7081<div x-data="initAmCookieGroups" class="w-full flex flex-col"> 7082 <template x-for="group in $store.AmastyCookieGroups.groupData" :key="group.groupId"> 7083 <div class="mb-2"> 7084 <div class="header flex justify-between items-start py-2"> 7085 <h3 class="text-lg font-semibold pr-6 ambar-group-title text-ambar-settings-bar-group-title" x-text="group.name"></h3> 7086 <div> 7087 <input type="checkbox" 7088 :name="getInputGroupId" 7089 data-group-prefix="sidebar-" 7090 @change="toggleGroup" 7091 :checked="group.checked" 7092 :aria-checked="group.checked" 7093 :id="getInputGroupId" 7094 :disabled="group.isEssential" 7095 :value="group.groupId" 7096 class="amsettings-bar-input-toggle" 7097 :class="getInputStyles" 7098 > 7099 <label data-group-prefix="sidebar-" 7100 :for="getInputGroupId" class="sr-only" x-text="group.name"> 7101 </label> 7102 </div> 7103 </div> 7104 <p class="py-2 text-sm ambar-group-description text-ambar-settings-bar-group-description" x-html="group.description"></p> 7105 <button class="btn btn-secondary px-4 text-xs mb-4 rounded-xs before:content-none after:content-none" 7106 type="button" 7107 data-dispatch-action="sidebar-open-am-cookie-group-info" 7108 @click="moreInfoClickHandler"> 7109 <span class="">More Information</span> 7110 <span class="sr-only" x-text="moreInformationAboutCookieGroup"></span> 7111 </button> 7112 </div> 7113 </template> 7114</div> 7115 7116 7117<div x-data="initCookieGroupInfo_6ab6be2810b9e" x-bind="eventListeners"> 7118 <div x-cloak 7119 x-bind="overlay" 7120 class="fixed inset-0 flex items-center justify-center text-left bg-gray-500/50 z-50" 7121 > 7122 <div x-ref="sidebar-am-cookie-group-info" 7123 role="dialog" 7124 aria-labelledby="amcookie-group-info" 7125 class="inline-block max-h-[92vh] max-w-[90vw] overflow-auto 7126 bg-ambar-info-bar-background shadow-xl rounded-lg p-8 text-gray-700 relative" 7127 > 7128 <template x-if="group"> 7129 <div id="amcookie-group-info"> 7130 <button @click="closeHandler" 7131 aria-label="Close" 7132 class="absolute right-2 top-2 text-gray-300 transition 7133 hover:text-black focus:drop-shadow-md hover:drop-shadow-md" 7134 > 7135 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" 7136 stroke="currentColor" 7137 stroke-width="2"> 7138 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 7139 </svg> 7140 </button> 7141 <div class="md:flex md:justify-start pb-4"> 7142 <h3 class="text-xl font-semibold text-ambar-info-bar-title" x-text="group.name"></h3> 7143 </div> 7144 <template x-if="group.description"> 7145 <p class="mb-4 text-ambar-info-bar-description" x-html="group.description"></p> 7146 </template> 7147 <div class="overflow-x-scroll md:overflow-x-auto"> 7148 <table class="table-auto w-full mb-4 text-left"> 7149 <thead class="text-ambar-info-bar-table-header"> 7150 <tr> 7151 <th class="pr-4">Cookie Name</th> 7152 <th class="pr-4">Cookie Provider</th> 7153 <th class="min-w-[30%] pr-4">Cookie Description</th> 7154 <th class="pr-4">Cookie Lifetime</th> 7155 <th class="pr-4">Cookie Type</th> 7156 </tr> 7157 </thead> 7158 <tbody class="text-ambar-info-bar-table-content"> 7159 <template x-for="(item, index) in group.cookies" :key="index"> 7160 <tr> 7161 <td x-text="item.name" class="font-semibold pr-4"></td> 7162 <td x-text="item.provider" class="pr-4"></td> 7163 <td x-html="item.description" class="min-w-[30%] pr-4"></td> 7164 <td x-text="item.lifetime" class="pr-4"></td> 7165 <td x-text="item.type" class="pr-4"></td> 7166 </tr> 7167 </template> 7168 </tbody> 7169 </table> 7170 </div> 7171 </div> 7172 </template> 7173 <button 7174 @click="closeHandler" 7175 aria-label="Done" 7176 class="mt-4 w-full text-xs font-semibold text-ambar-info-bar-button-text 7177 hover:text-ambar-info-bar-button-hover-text uppercase bg-ambar-info-bar-button 7178 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" 7179 > 7180 Done </button> 7181 </div> 7182 </div> 7183 7184</div>
7185<script data-cfasync="false"> 7186 'use strict'; 7187 7188 function initCookieGroupInfo_6ab6be2810b9e() { 7189 return { 7190 ...hyva.modal({ 7191 dialog: 'sidebar-am-cookie-group-info' 7192 }), 7193 group: null, 7194 eventListeners: { 7195 ['@sidebar-close-am-cookie-group-info.window']() { 7196 this.closeHandler(); 7197 }, 7198 ['@sidebar-open-am-cookie-group-info.window'](event) { 7199 this.group = event.detail; 7200 7201 if (!!this.group) { 7202 /* 7203 If modal have big cookies list this can lead to 7204 trigger hide event in hyva.modal.eventListeners.click, 7205 so we should wait till this.group is reassigned 7206 */ 7207 this.$nextTick(() => this.show('sidebar-am-cookie-group-info')); 7208 } 7209 } 7210 }, 7211 closeHandler: function () { 7212 this.hide('sidebar-am-cookie-group-info'); 7213 } 7214 } 7215 } 7216 7217 window.addEventListener( 7218 'alpine:init', 7219 () => Alpine.data( 7220 'initCookieGroupInfo_6ab6be2810b9e', 7221 initCookieGroupInfo_6ab6be2810b9e ), 7222 {once: true} 7223 ); 7224</script>
7224 7225 </div> 7226 7227 <div class="flex flex-wrap flex-col mt-auto border-t pt-4"> 7228 <div id="amcookie-bar-content" class="flex flex-wrap w-full text-ambar-policy-text py-2"> 7229 We use cookies to 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> </div> 7230 7231 <div class="flex flex-wrap w-full"> 7232 7233 7234<button 7235 type="button" 7236 x-data="initAmCookieActionDispatcherBtn" 7237 data-dispatch-action="cookiebar-action-accept" 7238 data-dispatch-payload='{"include_checked_groups":true}' 7239 @click.prevent="actiondispatchHandler" 7240 class="ambar-btn-accept ambar-action-btn w-full justify-center mt-2" 7241> 7242 Accept Cookies</button> 7243 7244<button 7245 type="button" 7246 x-data="initAmCookieActionDispatcherBtn" 7247 data-dispatch-action="cookiebar-action-accept" 7248 @click.prevent="actiondispatchHandler" 7249 class="ambar-btn-decline ambar-action-btn w-full justify-center mt-2" 7250> 7251 Decline Cookies</button> 7252 7253<button 7254 type="button" 7255 x-data="initAmCookieActionDispatcherBtn" 7256 data-dispatch-action="cookiebar-action-allow" 7257 @click.prevent="actiondispatchHandler" 7258 class="ambar-btn-allow ambar-action-btn w-full justify-center mt-2" 7259> 7260 Allow all Cookies</button> 7261 </div> 7262 </div> 7263</div> 7264 </div> 7265 </div> 7266 </div> 7267</div> 7268
7269<script data-cfasync="false"> 7270 'use strict'; 7271 7272 function initAmastyCookieBar() { 7273 return { 7274 ...hyva.modal({ 7275 duration: 150, 7276 transitionEnter: 'transform duration-150', 7277 transitionLeave: 'transform duration-150' 7278 }), 7279 eventListeners: { 7280 ['@close-cookie-bar']() { 7281 this.hide('am-cookie-bar'); 7282 }, 7283 ['@open-cookie-bar']() { 7284 this.show('am-cookie-bar'); 7285 this.$nextTick(() => hyva.trapFocus(this.modalElement)); 7286 }, 7287 ['@amasty-cookie-group-updated.window']() { 7288 const lastUpdate = Alpine.store('AmastyCookieGroups').lastUpdate; 7289 if (this.isShowNotificationBar(this.firstShowProcess, lastUpdate)) { 7290 !this.opened['am-cookie-bar'] && 7291 this.show('am-cookie-bar'); 7292 this.$nextTick(() => hyva.trapFocus(this.modalElement)); 7293 } else { 7294 !!this.opened['am-cookie-bar'] && 7295 this.hide('am-cookie-bar'); 7296 } 7297 } 7298 }, 7299 7300 firstShowProcess: '0', 7301 7302 /** 7303 * @returns {void} 7304 */ 7305 init: function () { 7306 window.isGdprCookieEnabled = true; 7307 Alpine.store('AmastyCookieGroups').updateGroupData(); 7308 this.modalElement = this.$refs['am-cookie-bar']; 7309 this.modalElement.setAttribute('aria-describedby', 'amcookie-bar-content'); 7310 }, 7311 7312 /** 7313 * @param {string|number} firstShowProcess 7314 * @param {number} lastUpdate 7315 * @returns {boolean} 7316 */ 7317 isShowNotificationBar: function (firstShowProcess, lastUpdate) { 7318 return (this.isNeedFirstShow(firstShowProcess, lastUpdate) && !hyva.getCookie('amcookie_allowed') 7319 || this.isNeedShowOnUpdate(lastUpdate)); 7320 }, 7321 7322 /** 7323 * @param {string|number} firstShowProcess 7324 * @param {number} lastUpdate 7325 * @returns {boolean} 7326 */ 7327 isNeedFirstShow: function (firstShowProcess, lastUpdate) { 7328 hyva.getBrowserStorage().setItem('amCookieBarFirstShowTime', lastUpdate); 7329 7330 if (firstShowProcess === '0') { 7331 return true; 7332 } 7333 7334 if (hyva.getBrowserStorage().getItem('amCookieBarFirstShow') !== '1') { 7335 hyva.getBrowserStorage().setItem('amCookieBarFirstShow', '1'); 7336 7337 return true; 7338 } 7339 7340 return false; 7341 }, 7342 7343 /** 7344 * @param {number} lastUpdate 7345 * @returns {boolean} 7346 */ 7347 isNeedShowOnUpdate: function (lastUpdate) { 7348 if (!lastUpdate) { 7349 return false; 7350 } 7351 7352 return this.isNeedShowAfterLastVisit(lastUpdate) || this.isNeedShowAfterLastAccept(lastUpdate); 7353 }, 7354 7355 /** 7356 * @param {number} lastUpdate 7357 * @returns {boolean} 7358 */ 7359 isNeedShowAfterLastVisit: function (lastUpdate) { 7360 let needToShowAfterLastVisit = 7361 lastUpdate > hyva.getBrowserStorage().getItem('amCookieBarFirstShowTime'); 7362 7363 if (needToShowAfterLastVisit) { 7364 hyva.getBrowserStorage().setItem('amCookieBarFirstShow', null); 7365 hyva.setCookie('amcookie_allowed', '', -1, true); 7366 } 7367 7368 return needToShowAfterLastVisit; 7369 }, 7370 7371 /** 7372 * @param {number} lastUpdate 7373 * @returns {boolean} 7374 */ 7375 isNeedShowAfterLastAccept: function (lastUpdate) { 7376 let needToShowAfterLastAccept = false; 7377 7378 if (hyva.getBrowserStorage().getItem('am-last-cookie-acceptance')) { 7379 needToShowAfterLastAccept = 7380 lastUpdate > hyva.getBrowserStorage().getItem('am-last-cookie-acceptance'); 7381 } 7382 7383 return needToShowAfterLastAccept; 7384 }, 7385 7386 /** 7387 * @returns {void} 7388 */ 7389 closeCookieBarHandler: function () { 7390 this.$dispatch('close-cookie-bar'); 7391 } 7392 }; 7393 } 7394 7395 window.addEventListener('alpine:init', () => Alpine.data('initAmastyCookieBar', initAmastyCookieBar), {once: true}); 7396</script>
vendor: 1 bytes, line 7396
7396
7397<script type="text/json" id="default-section-data"> 7398 {"messages":[],"customer":[],"compare-products":[],"last-ordered-items":[],"cart":[],"directory-data":{"AD":{"name":"Andorra"},"AE":{"name":"United Arab Emirates"},"AF":{"name":"Afghanistan"},"AG":{"name":"Antigua & Barbuda"},"AI":{"name":"Anguilla"},"AL":{"name":"Albania","regions":{"512":{"code":"AL-01","name":"Berat"},"513":{"code":"AL-09","name":"Dib\u00ebr"},"514":{"code":"AL-02","name":"Durr\u00ebs"},"515":{"code":"AL-03","name":"Elbasan"},"516":{"code":"AL-04","name":"Fier"},"517":{"code":"AL-05","name":"Gjirokast\u00ebr"},"518":{"code":"AL-06","name":"Kor\u00e7\u00eb"},"519":{"code":"AL-07","name":"Kuk\u00ebs"},"520":{"code":"AL-08","name":"Lezh\u00eb"},"521":{"code":"AL-10","name":"Shkod\u00ebr"},"522":{"code":"AL-11","name":"Tiran\u00eb"},"523":{"code":"AL-12","name":"Vlor\u00eb"}}},"AM":{"name":"Armenia"},"AN":{"name":null},"AO":{"name":"Angola"},"AQ":{"name":"Antarctica"},"AR":{"name":"Argentina","regions":{"525":{"code":"AR-B","name":"Buenos Aires"},"526":{"code":"AR-K","name":"Catamarca"},"527":{"code":"AR-H","name":"Chaco"},"528":{"code":"AR-U","name":"Chubut"},"524":{"code":"AR-C","name":"Ciudad Aut\u00f3noma de Buenos Aires"},"529":{"code":"AR-X","name":"C\u00f3rdoba"},"530":{"code":"AR-W","name":"Corrientes"},"531":{"code":"AR-E","name":"Entre R\u00edos"},"532":{"code":"AR-P","name":"Formosa"},"533":{"code":"AR-Y","name":"Jujuy"},"534":{"code":"AR-L","name":"La Pampa"},"535":{"code":"AR-F","name":"La Rioja"},"536":{"code":"AR-M","name":"Mendoza"},"537":{"code":"AR-N","name":"Misiones"},"538":{"code":"AR-Q","name":"Neuqu\u00e9n"},"539":{"code":"AR-R","name":"R\u00edo Negro"},"540":{"code":"AR-A","name":"Salta"},"541":{"code":"AR-J","name":"San Juan"},"542":{"code":"AR-D","name":"San Luis"},"543":{"code":"AR-Z","name":"Santa Cruz"},"544":{"code":"AR-S","name":"Santa Fe"},"545":{"code":"AR-G","name":"Santiago del Estero"},"546":{"code":"AR-V","name":"Tierra del Fuego"},"547":{"code":"AR-T","name":"Tucum\u00e1n"}}},"AS":{"name":"American Samoa"},"AT":{"name":"Austria","regions":{"102":{"code":"BL","name":"Burgenland"},"99":{"code":"KN","name":"K\u00e4rnten"},"96":{"code":"NO","name":"Nieder\u00f6sterreich"},"97":{"code":"OO","name":"Ober\u00f6sterreich"},"98":{"code":"SB","name":"Salzburg"},"100":{"code":"ST","name":"Steiermark"},"101":{"code":"TI","name":"Tirol"},"103":{"code":"VB","name":"Vorarlberg"},"95":{"code":"WI","name":"Wien"}}},"AU":{"name":"Australia","regions":{"605":{"code":"ACT","name":"Australian Capital Territory"},"606":{"code":"NSW","name":"New South Wales"},"612":{"code":"NT","name":"Northern Territory"},"608":{"code":"QLD","name":"Queensland"},"609":{"code":"SA","name":"South Australia"},"610":{"code":"TAS","name":"Tasmania"},"607":{"code":"VIC","name":"Victoria"},"611":{"code":"WA","name":"Western Australia"}}},"AW":{"name":"Aruba"},"AX":{"name":"\u00c5land Islands"},"AZ":{"name":"Azerbaijan"},"BA":{"name":"Bosnia & Herzegovina"},"BB":{"name":"Barbados"},"BD":{"name":"Bangladesh"},"BE":{"name":"Belgium","regions":{"620":{"code":"VAN","name":"Antwerpen"},"621":{"code":"WBR","name":"Brabant wallon"},"622":{"code":"BRU","name":"Brussels-Capital Region"},"623":{"code":"WHT","name":"Hainaut"},"625":{"code":"WLG","name":"Li\u00e8ge"},"624":{"code":"VLI","name":"Limburg"},"626":{"code":"WLX","name":"Luxembourg"},"627":{"code":"WNA","name":"Namur"},"628":{"code":"VOV","name":"Oost-Vlaanderen"},"629":{"code":"VBR","name":"Vlaams-Brabant"},"630":{"code":"VWV","name":"West-Vlaanderen"}}},"BF":{"name":"Burkina Faso"},"BG":{"name":"Bulgaria","regions":{"640":{"code":"BG-01","name":"Blagoevgrad"},"641":{"code":"BG-02","name":"Burgas"},"647":{"code":"BG-08","name":"Dobrich"},"646":{"code":"BG-07","name":"Gabrovo"},"665":{"code":"BG-26","name":"Haskovo"},"648":{"code":"BG-09","name":"Kardzhali"},"649":{"code":"BG-10","name":"Kyustendil"},"650":{"code":"BG-11","name":"Lovech"},"651":{"code":"BG-12","name":"Montana"},"652":{"code":"BG-13","name":"Pazardzhik"},"653":{"code":"BG-14","name":"Pernik"},"654":{"code":"BG-15","name":"Pleven"},"655":{"code":"BG-16","name":"Plovdiv"},"656":{"code":"BG-17","name":"Razgrad"},"657":{"code":"BG-18","name":"Ruse"},"666":{"code":"BG-27","name":"Shumen"},"658":{"code":"BG-19","name":"Silistra"},"659":{"code":"BG-20","name":"Sliven"},"660":{"code":"BG-21","name":"Smolyan"},"661":{"code":"BG-22","name":"Sofia City"},"662":{"code":"BG-23","name":"Sofia Province"},"663":{"code":"BG-24","name":"Stara Zagora"},"664":{"code":"BG-25","name":"Targovishte"},"642":{"code":"BG-03","name":"Varna"},"643":{"code":"BG-04","name":"Veliko Tarnovo"},"644":{"code":"BG-05","name":"Vidin"},"645":{"code":"BG-06","name":"Vratsa"},"667":{"code":"BG-28","name":"Yambol"}}},"BH":{"name":"Bahrain"},"BI":{"name":"Burundi"},"BJ":{"name":"Benin"},"BL":{"name":"St. Barth\u00e9lemy"},"BM":{"name":"Bermuda"},"BN":{"name":"Brunei"}
7398,"BO":{"name":"Bolivia","regions":{"632":{"code":"BO-H","name":"Chuquisaca"},"631":{"code":"BO-C","name":"Cochabamba"},"633":{"code":"BO-B","name":"El Beni"},"634":{"code":"BO-L","name":"La Paz"},"635":{"code":"BO-O","name":"Oruro"},"636":{"code":"BO-N","name":"Pando"},"637":{"code":"BO-P","name":"Potos\u00ed"},"638":{"code":"BO-S","name":"Santa Cruz"},"639":{"code":"BO-T","name":"Tarija"}}},"BQ":{"name":"Caribbean Netherlands"},"BR":{"name":"Brazil","regions":{"485":{"code":"AC","name":"Acre"},"486":{"code":"AL","name":"Alagoas"},"487":{"code":"AP","name":"Amap\u00e1"},"488":{"code":"AM","name":"Amazonas"},"489":{"code":"BA","name":"Bahia"},"490":{"code":"CE","name":"Cear\u00e1"},"511":{"code":"DF","name":"Distrito Federal"},"491":{"code":"ES","name":"Esp\u00edrito Santo"},"492":{"code":"GO","name":"Goi\u00e1s"},"493":{"code":"MA","name":"Maranh\u00e3o"},"494":{"code":"MT","name":"Mato Grosso"},"495":{"code":"MS","name":"Mato Grosso do Sul"},"496":{"code":"MG","name":"Minas Gerais"},"497":{"code":"PA","name":"Par\u00e1"},"498":{"code":"PB","name":"Para\u00edba"},"499":{"code":"PR","name":"Paran\u00e1"},"500":{"code":"PE","name":"Pernambuco"},"501":{"code":"PI","name":"Piau\u00ed"},"502":{"code":"RJ","name":"Rio de Janeiro"},"503":{"code":"RN","name":"Rio Grande do Norte"},"504":{"code":"RS","name":"Rio Grande do Sul"},"505":{"code":"RO","name":"Rond\u00f4nia"},"506":{"code":"RR","name":"Roraima"},"507":{"code":"SC","name":"Santa Catarina"},"508":{"code":"SP","name":"S\u00e3o Paulo"},"509":{"code":"SE","name":"Sergipe"},"510":{"code":"TO","name":"Tocantins"}}},"BS":{"name":"Bahamas"},"BT":{"name":"Bhutan"},"BV":{"name":"Bouvet Island"},"BW":{"name":"Botswana"},"BY":{"name":"Belarus","regions":{"613":{"code":"BY-BR","name":"Bresckaja voblas\u0107"},"614":{"code":"BY-HO","name":"Homie\u013askaja voblas\u0107"},"615":{"code":"BY-HM","name":"Horad Minsk"},"616":{"code":"BY-HR","name":"Hrodzienskaja voblas\u0107"},"617":{"code":"BY-MA","name":"Mahilio\u016dskaja voblas\u0107"},"618":{"code":"BY-MI","name":"Minskaja voblas\u0107"},"619":{"code":"BY-VI","name":"Viciebskaja voblas\u0107"}}},"BZ":{"name":"Belize"},"CA":{"name":"Canada","regions":{"66":{"code":"AB","name":"Alberta"},"67":{"code":"BC","name":"British Columbia"},"68":{"code":"MB","name":"Manitoba"},"70":{"code":"NB","name":"New Brunswick"},"69":{"code":"NL","name":"Newfoundland and Labrador"},"72":{"code":"NT","name":"Northwest Territories"},"71":{"code":"NS","name":"Nova Scotia"},"73":{"code":"NU","name":"Nunavut"},"74":{"code":"ON","name":"Ontario"},"75":{"code":"PE","name":"Prince Edward Island"},"76":{"code":"QC","name":"Quebec"},"77":{"code":"SK","name":"Saskatchewan"},"78":{"code":"YT","name":"Yukon Territory"}}},"CC":{"name":"Cocos (Keeling) Islands"},"CD":{"name":"Congo - Kinshasa"},"CF":{"name":"Central African Republic"},"CG":{"name":"Congo - Brazzaville"},"CH":{"name":"Switzerland","regions":{"104":{"code":"AG","name":"Aargau"},"106":{"code":"AR","name":"Appenzell Ausserrhoden"},"105":{"code":"AI","name":"Appenzell Innerrhoden"},"108":{"code":"BL","name":"Basel-Landschaft"},"109":{"code":"BS","name":"Basel-Stadt"},"107":{"code":"BE","name":"Bern"},"110":{"code":"FR","name":"Friburg"},"111":{"code":"GE","name":"Geneva"},"112":{"code":"GL","name":"Glarus"},"113":{"code":"GR","name":"Graub\u00fcnden"},"114":{"code":"JU","name":"Jura"},"115":{"code":"LU","name":"Lucerne"},"116":{"code":"NE","name":"Neuch\u00e2tel"},"117":{"code":"NW","name":"Nidwalden"},"118":{"code":"OW","name":"Obwalden"},"120":{"code":"SH","name":"Schaffhausen"},"122":{"code":"SZ","name":"Schwyz"},"121":{"code":"SO","name":"Solothurn"},"119":{"code":"SG","name":"St. Gallen"},"123":{"code":"TG","name":"Thurgau"},"124":{"code":"TI","name":"Ticino"},"125":{"code":"UR","name":"Uri"},"126":{"code":"VD","name":"Vaud"},"127":{"code":"VS","name":"Wallis"},"128":{"code":"ZG","name":"Zug"},"129":{"code":"ZH","name":"Z\u00fcrich"}}},"CI":{"name":"C\u00f4te d\u2019Ivoire"},"CK":{"name":"Cook Islands"},"CL":{"name":"Chile","regions":{"668":{"code":"CL-AI","name":"Ais\u00e9n del General Carlos Iba\u00f1ez del Campo"},"669":{"code":"CL-AN","name":"Antofagasta"},"670":{"code":"CL-AP","name":"Arica y Parinacota"},"672":{"code":"CL-AT","name":"Atacama"},"673":{"code":"CL-BI","name":"Biob\u00edo"},"674":{"code":"CL-CO","name":"Coquimbo"},"671":{"code":"CL-AR","name":"La Araucan\u00eda"},"675":{"code":"CL-LI","name":"Libertador General Bernardo O'Higgins"}
7398,"676":{"code":"CL-LL","name":"Los Lagos"},"677":{"code":"CL-LR","name":"Los R\u00edos"},"678":{"code":"CL-MA","name":"Magallanes"},"679":{"code":"CL-ML","name":"Maule"},"680":{"code":"CL-NB","name":"\u00d1uble"},"681":{"code":"CL-RM","name":"Regi\u00f3n Metropolitana de Santiago"},"682":{"code":"CL-TA","name":"Tarapac\u00e1"},"683":{"code":"CL-VS","name":"Valpara\u00edso"}}},"CM":{"name":"Cameroon"},"CN":{"name":"China","regions":{"684":{"code":"CN-AH","name":"Anhui Sheng"},"685":{"code":"CN-BJ","name":"Beijing Shi"},"686":{"code":"CN-CQ","name":"Chongqing Shi"},"687":{"code":"CN-FJ","name":"Fujian Sheng"},"688":{"code":"CN-GS","name":"Gansu Sheng"},"689":{"code":"CN-GD","name":"Guangdong Sheng"},"690":{"code":"CN-GX","name":"Guangxi Zhuangzu Zizhiqu"},"691":{"code":"CN-GZ","name":"Guizhou Sheng"},"692":{"code":"CN-HI","name":"Hainan Sheng"},"693":{"code":"CN-HE","name":"Hebei Sheng"},"694":{"code":"CN-HL","name":"Heilongjiang Sheng"},"695":{"code":"CN-HA","name":"Henan Sheng"},"696":{"code":"CN-HK","name":"Hong Kong SAR"},"697":{"code":"CN-HB","name":"Hubei Sheng"},"698":{"code":"CN-HN","name":"Hunan Sheng"},"699":{"code":"CN-JS","name":"Jiangsu Sheng"},"700":{"code":"CN-JX","name":"Jiangxi Sheng"},"701":{"code":"CN-JL","name":"Jilin Sheng"},"702":{"code":"CN-LN","name":"Liaoning Sheng"},"703":{"code":"CN-MO","name":"Macao SAR"},"704":{"code":"CN-NM","name":"Nei Mongol Zizhiqu"},"705":{"code":"CN-NX","name":"Ningxia Huizi Zizhiqu"},"706":{"code":"CN-QH","name":"Qinghai Sheng"},"707":{"code":"CN-SN","name":"Shaanxi Sheng"},"708":{"code":"CN-SD","name":"Shandong Sheng"},"709":{"code":"CN-SH","name":"Shanghai Shi"},"710":{"code":"CN-SX","name":"Shanxi Sheng"},"711":{"code":"CN-SC","name":"Sichuan Sheng"},"712":{"code":"CN-TW","name":"Taiwan Sheng"},"713":{"code":"CN-TJ","name":"Tianjin Shi"},"714":{"code":"CN-XJ","name":"Xinjiang Uygur Zizhiqu"},"715":{"code":"CN-XZ","name":"Xizang Zizhiqu"},"716":{"code":"CN-YN","name":"Yunnan Sheng"},"717":{"code":"CN-ZJ","name":"Zhejiang Sheng"}}},"CO":{"name":"Colombia","regions":{"718":{"code":"CO-AMA","name":"Amazonas"},"719":{"code":"CO-ANT","name":"Antioquia"},"720":{"code":"CO-ARA","name":"Arauca"},"721":{"code":"CO-ATL","name":"Atl\u00e1ntico"},"722":{"code":"CO-BOL","name":"Bol\u00edvar"},"723":{"code":"CO-BOY","name":"Boyac\u00e1"},"724":{"code":"CO-CAL","name":"Caldas"},"725":{"code":"CO-CAQ","name":"Caquet\u00e1"},"726":{"code":"CO-CAS","name":"Casanare"},"727":{"code":"CO-CAU","name":"Cauca"},"728":{"code":"CO-CES","name":"Cesar"},"729":{"code":"CO-CHO","name":"Choc\u00f3"},"730":{"code":"CO-COR","name":"C\u00f3rdoba"},"731":{"code":"CO-CUN","name":"Cundinamarca"},"732":{"code":"CO-GUA","name":"Guain\u00eda"},"733":{"code":"CO-GUV","name":"Guaviare"},"734":{"code":"CO-HUL","name":"Huila"},"735":{"code":"CO-LAG","name":"La Guajira"},"736":{"code":"CO-MAG","name":"Magdalena"},"737":{"code":"CO-MET","name":"Meta"},"738":{"code":"CO-NAR","name":"Nari\u00f1o"},"739":{"code":"CO-NSA","name":"Norte de Santander"},"740":{"code":"CO-PUT","name":"Putumayo"},"741":{"code":"CO-QUI","name":"Quind\u00edo"},"742":{"code":"CO-RIS","name":"Risaralda"},"743":{"code":"CO-SAP","name":"San Andr\u00e9s y Providencia"},"744":{"code":"CO-SAN","name":"Santander"},"745":{"code":"CO-SUC","name":"Sucre"},"746":{"code":"CO-TOL","name":"Tolima"},"747":{"code":"CO-VAC","name":"Valle del Cauca"},"748":{"code":"CO-VAU","name":"Vaup\u00e9s"},"749":{"code":"CO-VID","name":"Vichada"}}},"CR":{"name":"Costa Rica","regions":{"751":{"code":"CR-AL","name":"Alajuela"},"752":{"code":"CR-CA","name":"Cartago"},"754":{"code":"CR-GU","name":"Guanacaste"},"753":{"code":"CR-HE","name":"Heredia"},"756":{"code":"CR-LI","name":"Lim\u00f3n"},"755":{"code":"CR-PU","name":"Puntarenas"},"750":{"code":"CR-SJ","name":"San Jos\u00e9"}}},"CU":{"name":"Cuba"},"CV":{"name":"Cape Verde"},"CW":{"name":"Cura\u00e7ao"},"CX":{"name":"Christmas Island"},"CY":{"name":"Cyprus"},"CZ":{"name":"Czechia","regions":{"759":{"code":"CZ-31","name":"Jiho\u010desk\u00fd kraj"},"767":{"code":"CZ-64","name":"Jihomoravsk\u00fd kraj"},"761":{"code":"CZ-41","name":"Karlovarsk\u00fd kraj"},"766":{"code":"CZ-63","name":"Kraj Vyso\u010dina"},"764":{"code":"CZ-52","name":"Kr\u00e1lov\u00e9hradeck\u00fd kraj"},"763":{"code":"CZ-51","name":"Libereck\u00fd kraj"},"770":{"code":"CZ-80","name":"Moravskoslezsk\u00fd kraj"}
7398,"768":{"code":"CZ-71","name":"Olomouck\u00fd kraj"},"765":{"code":"CZ-53","name":"Pardubick\u00fd kraj"},"760":{"code":"CZ-32","name":"Plze\u0148sk\u00fd kraj"},"757":{"code":"CZ-10","name":"Praha, Hlavn\u00ed m\u011bsto"},"758":{"code":"CZ-20","name":"St\u0159edo\u010desk\u00fd kraj"},"762":{"code":"CZ-42","name":"\u00dasteck\u00fd kraj"},"769":{"code":"CZ-72","name":"Zl\u00ednsk\u00fd kraj"}}},"DE":{"name":"Germany","regions":{"80":{"code":"BAW","name":"Baden-W\u00fcrttemberg"},"81":{"code":"BAY","name":"Bayern"},"82":{"code":"BER","name":"Berlin"},"83":{"code":"BRG","name":"Brandenburg"},"84":{"code":"BRE","name":"Bremen"},"85":{"code":"HAM","name":"Hamburg"},"86":{"code":"HES","name":"Hessen"},"87":{"code":"MEC","name":"Mecklenburg-Vorpommern"},"79":{"code":"NDS","name":"Niedersachsen"},"88":{"code":"NRW","name":"Nordrhein-Westfalen"},"89":{"code":"RHE","name":"Rheinland-Pfalz"},"90":{"code":"SAR","name":"Saarland"},"91":{"code":"SAS","name":"Sachsen"},"92":{"code":"SAC","name":"Sachsen-Anhalt"},"93":{"code":"SCN","name":"Schleswig-Holstein"},"94":{"code":"THE","name":"Th\u00fcringen"}}},"DJ":{"name":"Djibouti"},"DK":{"name":"Denmark","regions":{"771":{"code":"DK-84","name":"Hovedstaden"},"772":{"code":"DK-82","name":"Midtjylland"},"773":{"code":"DK-81","name":"Nordjylland"},"774":{"code":"DK-85","name":"Sj\u00e6lland"},"775":{"code":"DK-83","name":"Syddanmark"}}},"DM":{"name":"Dominica"},"DO":{"name":"Dominican Republic"},"DZ":{"name":"Algeria"},"EC":{"name":"Ecuador","regions":{"776":{"code":"EC-A","name":"Azuay"},"777":{"code":"EC-B","name":"Bol\u00edvar"},"778":{"code":"EC-F","name":"Ca\u00f1ar"},"779":{"code":"EC-C","name":"Carchi"},"780":{"code":"EC-H","name":"Chimborazo"},"781":{"code":"EC-X","name":"Cotopaxi"},"782":{"code":"EC-O","name":"El Oro"},"783":{"code":"EC-E","name":"Esmeraldas"},"784":{"code":"EC-W","name":"Gal\u00e1pagos"},"785":{"code":"EC-G","name":"Guayas"},"786":{"code":"EC-I","name":"Imbabura"},"787":{"code":"EC-L","name":"Loja"},"788":{"code":"EC-R","name":"Los R\u00edos"},"789":{"code":"EC-M","name":"Manab\u00ed"},"790":{"code":"EC-S","name":"Morona Santiago"},"791":{"code":"EC-N","name":"Napo"},"792":{"code":"EC-D","name":"Orellana"},"793":{"code":"EC-Y","name":"Pastaza"},"794":{"code":"EC-P","name":"Pichincha"},"795":{"code":"EC-SE","name":"Santa Elena"},"796":{"code":"EC-SD","name":"Santo Domingo de los Ts\u00e1chilas"},"797":{"code":"EC-U","name":"Sucumb\u00edos"},"798":{"code":"EC-T","name":"Tungurahua"},"799":{"code":"EC-Z","name":"Zamora Chinchipe"}}},"EE":{"name":"Estonia","regions":{"340":{"code":"EE-37","name":"Harjumaa"},"341":{"code":"EE-39","name":"Hiiumaa"},"342":{"code":"EE-44","name":"Ida-Virumaa"},"344":{"code":"EE-51","name":"J\u00e4rvamaa"},"343":{"code":"EE-49","name":"J\u00f5gevamaa"},"346":{"code":"EE-59","name":"L\u00e4\u00e4ne-Virumaa"},"345":{"code":"EE-57","name":"L\u00e4\u00e4nemaa"},"348":{"code":"EE-67","name":"P\u00e4rnumaa"},"347":{"code":"EE-65","name":"P\u00f5lvamaa"},"349":{"code":"EE-70","name":"Raplamaa"},"350":{"code":"EE-74","name":"Saaremaa"},"351":{"code":"EE-78","name":"Tartumaa"},"352":{"code":"EE-82","name":"Valgamaa"},"353":{"code":"EE-84","name":"Viljandimaa"},"354":{"code":"EE-86","name":"V\u00f5rumaa"}}},"EG":{"name":"Egypt"},"EH":{"name":"Western Sahara"},"ER":{"name":"Eritrea"},"ES":{"name":"Spain","regions":{"130":{"code":"A Coru\u0441a","name":"A Coru\u00f1a"},"131":{"code":"Alava","name":"Alava"},"132":{"code":"Albacete","name":"Albacete"},"133":{"code":"Alicante","name":"Alicante"},"134":{"code":"Almeria","name":"Almeria"},"135":{"code":"Asturias","name":"Asturias"},"136":{"code":"Avila","name":"Avila"},"137":{"code":"Badajoz","name":"Badajoz"},"138":{"code":"Baleares","name":"Baleares"},"139":{"code":"Barcelona","name":"Barcelona"},"140":{"code":"Burgos","name":"Burgos"},"141":{"code":"Caceres","name":"Caceres"},"142":{"code":"Cadiz","name":"Cadiz"},"143":{"code":"Cantabria","name":"Cantabria"},"144":{"code":"Castellon","name":"Castellon"},"145":{"code":"Ceuta","name":"Ceuta"},"146":{"code":"Ciudad Real","name":"Ciudad Real"},"147":{"code":"Cordoba","name":"Cordoba"},"148":{"code":"Cuenca","name":"Cuenca"},"149":{"code":"Girona","name":"Girona"},"150":{"code":"Granada","name":"Granada"},"151":{"code":"Guadalajara","name":"Guadalajara"},"152":{"code":"Guipuzcoa","name":"Guipuzcoa"},"153":{"code":"Huelva","name":"Huelva"},"154":{"code":"Huesca","name":"Huesca"},"155":{"code":"Jaen","name":"Jaen"},"156":{"code":"La Rioja","name":"La Rioja"},"157":{"code":"Las Palmas","name":"Las Palmas"},"158":{"code":"Leon","name":"Leon"},"159":{"code":"Lleida","name":"Lleida"},"160":{"code":"Lugo","name":"Lugo"},"161":{"code":"Madrid","name":"Madrid"},"162":{"code":"Malaga","name":"Malaga"},"163":{"code":"Melilla","name":"Melilla"},"164":{"code":"Murcia","name":"Murcia"},"165":{"code":"Navarra","name":"Navarra"},"166":{"code":"Ourense","name":"Ourense"},"167":{"code":"Palencia","name":"Palencia"},"168":{"code":"Pontevedra","name":"Pontevedra"},"169":{"code":"Salamanca","name":"Salamanca"},"170":{"code":"Santa Cruz de Tenerife","name":"Santa Cruz de Tenerife"},"171":{"code":"Segovia","name":"Segovia"},"172":{"code":"Sevilla","name":"Sevilla"},"173":{"code":"Soria","name":"Soria"},"174":{"code":"Tarragona","name":"Tarragona"},"175":{"code":"Teruel","name":"Teruel"},"176":{"code":"Toledo","name":"Toledo"},"177":{"code":"Valencia","name":"Valencia"},"178":{"code":"Valladolid","name":"Valladolid"}
7398,"179":{"code":"Vizcaya","name":"Vizcaya"},"180":{"code":"Zamora","name":"Zamora"},"181":{"code":"Zaragoza","name":"Zaragoza"}}},"ET":{"name":"Ethiopia"},"FI":{"name":"Finland","regions":{"339":{"code":"Ahvenanmaa","name":"Ahvenanmaa"},"333":{"code":"Etel\u00e4-Karjala","name":"Etel\u00e4-Karjala"},"326":{"code":"Etel\u00e4-Pohjanmaa","name":"Etel\u00e4-Pohjanmaa"},"325":{"code":"Etel\u00e4-Savo","name":"Etel\u00e4-Savo"},"337":{"code":"It\u00e4-Uusimaa","name":"It\u00e4-Uusimaa"},"322":{"code":"Kainuu","name":"Kainuu"},"335":{"code":"Kanta-H\u00e4me","name":"Kanta-H\u00e4me"},"330":{"code":"Keski-Pohjanmaa","name":"Keski-Pohjanmaa"},"331":{"code":"Keski-Suomi","name":"Keski-Suomi"},"338":{"code":"Kymenlaakso","name":"Kymenlaakso"},"320":{"code":"Lappi","name":"Lappi"},"334":{"code":"P\u00e4ij\u00e4t-H\u00e4me","name":"P\u00e4ij\u00e4t-H\u00e4me"},"328":{"code":"Pirkanmaa","name":"Pirkanmaa"},"327":{"code":"Pohjanmaa","name":"Pohjanmaa"},"323":{"code":"Pohjois-Karjala","name":"Pohjois-Karjala"},"321":{"code":"Pohjois-Pohjanmaa","name":"Pohjois-Pohjanmaa"},"324":{"code":"Pohjois-Savo","name":"Pohjois-Savo"},"329":{"code":"Satakunta","name":"Satakunta"},"336":{"code":"Uusimaa","name":"Uusimaa"},"332":{"code":"Varsinais-Suomi","name":"Varsinais-Suomi"}}},"FJ":{"name":"Fiji"},"FK":{"name":"Falkland Islands"},"FM":{"name":"Micronesia"},"FO":{"name":"Faroe Islands"},"FR":{"name":"France","regions":{"182":{"code":"1","name":"Ain"},"183":{"code":"2","name":"Aisne"},"184":{"code":"3","name":"Allier"},"185":{"code":"4","name":"Alpes-de-Haute-Provence"},"187":{"code":"6","name":"Alpes-Maritimes"},"188":{"code":"7","name":"Ard\u00e8che"},"189":{"code":"8","name":"Ardennes"},"190":{"code":"9","name":"Ari\u00e8ge"},"191":{"code":"10","name":"Aube"},"192":{"code":"11","name":"Aude"},"193":{"code":"12","name":"Aveyron"},"249":{"code":"67","name":"Bas-Rhin"},"194":{"code":"13","name":"Bouches-du-Rh\u00f4ne"},"195":{"code":"14","name":"Calvados"},"196":{"code":"15","name":"Cantal"},"197":{"code":"16","name":"Charente"},"198":{"code":"17","name":"Charente-Maritime"},"199":{"code":"18","name":"Cher"},"200":{"code":"19","name":"Corr\u00e8ze"},"201":{"code":"2A","name":"Corse-du-Sud"},"203":{"code":"21","name":"C\u00f4te-d'Or"},"204":{"code":"22","name":"C\u00f4tes-d'Armor"},"205":{"code":"23","name":"Creuse"},"261":{"code":"79","name":"Deux-S\u00e8vres"},"206":{"code":"24","name":"Dordogne"},"207":{"code":"25","name":"Doubs"},"208":{"code":"26","name":"Dr\u00f4me"},"273":{"code":"91","name":"Essonne"},"209":{"code":"27","name":"Eure"},"210":{"code":"28","name":"Eure-et-Loir"},"211":{"code":"29","name":"Finist\u00e8re"},"212":{"code":"30","name":"Gard"},"214":{"code":"32","name":"Gers"},"215":{"code":"33","name":"Gironde"},"250":{"code":"68","name":"Haut-Rhin"},"202":{"code":"2B","name":"Haute-Corse"},"213":{"code":"31","name":"Haute-Garonne"},"225":{"code":"43","name":"Haute-Loire"},"234":{"code":"52","name":"Haute-Marne"},"252":{"code":"70","name":"Haute-Sa\u00f4ne"},"256":{"code":"74","name":"Haute-Savoie"},"269":{"code":"87","name":"Haute-Vienne"},"186":{"code":"5","name":"Hautes-Alpes"},"247":{"code":"65","name":"Hautes-Pyr\u00e9n\u00e9es"},"274":{"code":"92","name":"Hauts-de-Seine"},"216":{"code":"34","name":"H\u00e9rault"},"217":{"code":"35","name":"Ille-et-Vilaine"},"218":{"code":"36","name":"Indre"},"219":{"code":"37","name":"Indre-et-Loire"},"220":{"code":"38","name":"Is\u00e8re"},"221":{"code":"39","name":"Jura"},"222":{"code":"40","name":"Landes"},"223":{"code":"41","name":"Loir-et-Cher"},"224":{"code":"42","name":"Loire"},"226":{"code":"44","name":"Loire-Atlantique"},"227":{"code":"45","name":"Loiret"},"228":{"code":"46","name":"Lot"},"229":{"code":"47","name":"Lot-et-Garonne"},"230":{"code":"48","name":"Loz\u00e8re"},"231":{"code":"49","name":"Maine-et-Loire"},"232":{"code":"50","name":"Manche"},"233":{"code":"51","name":"Marne"},"235":{"code":"53","name":"Mayenne"},"236":{"code":"54","name":"Meurthe-et-Moselle"},"237":{"code":"55","name":"Meuse"},"238":{"code":"56","name":"Morbihan"},"239":{"code":"57","name":"Moselle"},"240":{"code":"58","name":"Ni\u00e8vre"},"241":{"code":"59","name":"Nord"},"242":{"code":"60","name":"Oise"},"243":{"code":"61","name":"Orne"},"257":{"code":"75","name":"Paris"},"244":{"code":"62","name":"Pas-de-Calais"},"245":{"code":"63","name":"Puy-de-D\u00f4me"},"246":{"code":"64","name":"Pyr\u00e9n\u00e9es-Atlantiques"},"248":{"code":"66","name":"Pyr\u00e9n\u00e9es-Orientales"},"251":{"code":"69","name":"Rh\u00f4ne"},"253":{"code":"71","name":"Sa\u00f4ne-et-Loire"},"254":{"code":"72","name":"Sarthe"},"255":{"code":"73","name":"Savoie"},"259":{"code":"77","name":"Seine-et-Marne"},"258":{"code":"76","name":"Seine-Maritime"},"275":{"code":"93","name":"Seine-Saint-Denis"},"262":{"code":"80","name":"Somme"},"263":{"code":"81","name":"Tarn"},"264":{"code":"82","name":"Tarn-et-Garonne"},"272":{"code":"90","name":"Territoire-de-Belfort"},"277":{"code":"95","name":"Val-d'Oise"}
7398,"276":{"code":"94","name":"Val-de-Marne"},"265":{"code":"83","name":"Var"},"266":{"code":"84","name":"Vaucluse"},"267":{"code":"85","name":"Vend\u00e9e"},"268":{"code":"86","name":"Vienne"},"270":{"code":"88","name":"Vosges"},"271":{"code":"89","name":"Yonne"},"260":{"code":"78","name":"Yvelines"}}},"GA":{"name":"Gabon"},"GB":{"name":"United Kingdom"},"GD":{"name":"Grenada"},"GE":{"name":"Georgia"},"GF":{"name":"French Guiana"},"GG":{"name":"Guernsey"},"GH":{"name":"Ghana"},"GI":{"name":"Gibraltar"},"GL":{"name":"Greenland"},"GM":{"name":"Gambia"},"GN":{"name":"Guinea"},"GP":{"name":"Guadeloupe"},"GQ":{"name":"Equatorial Guinea"},"GR":{"name":"Greece","regions":{"813":{"code":"GR-69","name":"\u00c1gion \u00d3ros"},"800":{"code":"GR-A","name":"Anatolik\u00ed Makedon\u00eda kai Thr\u00e1ki"},"801":{"code":"GR-I","name":"Attik\u00ed"},"802":{"code":"GR-G","name":"Dytik\u00ed Ell\u00e1da"},"803":{"code":"GR-C","name":"Dytik\u00ed Makedon\u00eda"},"804":{"code":"GR-F","name":"Ion\u00eda N\u00edsia"},"805":{"code":"GR-D","name":"\u00cdpeiros"},"806":{"code":"GR-B","name":"Kentrik\u00ed Makedon\u00eda"},"807":{"code":"GR-M","name":"Kr\u00edti"},"808":{"code":"GR-L","name":"N\u00f3tio Aiga\u00edo"},"809":{"code":"GR-J","name":"Pelop\u00f3nnisos"},"810":{"code":"GR-H","name":"Stere\u00e1 Ell\u00e1da"},"811":{"code":"GR-E","name":"Thessal\u00eda"},"812":{"code":"GR-K","name":"V\u00f3reio Aiga\u00edo"}}},"GS":{"name":"South Georgia & South Sandwich Islands"},"GT":{"name":"Guatemala"},"GU":{"name":"Guam"},"GW":{"name":"Guinea-Bissau"},"GY":{"name":"Guyana","regions":{"814":{"code":"GY-BA","name":"Barima-Waini"},"815":{"code":"GY-CU","name":"Cuyuni-Mazaruni"},"816":{"code":"GY-DE","name":"Demerara-Mahaica"},"817":{"code":"GY-EB","name":"East Berbice-Corentyne"},"818":{"code":"GY-ES","name":"Essequibo Islands-West Demerara"},"819":{"code":"GY-MA","name":"Mahaica-Berbice"},"820":{"code":"GY-PM","name":"Pomeroon-Supenaam"},"821":{"code":"GY-PT","name":"Potaro-Siparuni"},"822":{"code":"GY-UD","name":"Upper Demerara-Berbice"},"823":{"code":"GY-UT","name":"Upper Takutu-Upper Essequibo"}}},"HK":{"name":"Hong Kong SAR China"},"HM":{"name":"Heard & McDonald Islands"},"HN":{"name":"Honduras"},"HR":{"name":"Croatia","regions":{"554":{"code":"HR-07","name":"Bjelovarsko-bilogorska \u017eupanija"},"559":{"code":"HR-12","name":"Brodsko-posavska \u017eupanija"},"566":{"code":"HR-19","name":"Dubrova\u010dko-neretvanska \u017eupanija"},"568":{"code":"HR-21","name":"Grad Zagreb"},"565":{"code":"HR-18","name":"Istarska \u017eupanija"},"551":{"code":"HR-04","name":"Karlova\u010dka \u017eupanija"},"553":{"code":"HR-06","name":"Koprivni\u010dko-kri\u017eeva\u010dka \u017eupanija"},"549":{"code":"HR-02","name":"Krapinsko-zagorska \u017eupanija"},"556":{"code":"HR-09","name":"Li\u010dko-senjska \u017eupanija"},"567":{"code":"HR-20","name":"Me\u0111imurska \u017eupanija"},"561":{"code":"HR-14","name":"Osje\u010dko-baranjska \u017eupanija"},"558":{"code":"HR-11","name":"Po\u017ee\u0161ko-slavonska \u017eupanija"},"555":{"code":"HR-08","name":"Primorsko-goranska \u017eupanija"},"562":{"code":"HR-15","name":"\u0160ibensko-kninska \u017eupanija"},"550":{"code":"HR-03","name":"Sisa\u010dko-moslava\u010dka \u017eupanija"},"564":{"code":"HR-17","name":"Splitsko-dalmatinska \u017eupanija"},"552":{"code":"HR-05","name":"Vara\u017edinska \u017eupanija"},"557":{"code":"HR-10","name":"Viroviti\u010dko-podravska \u017eupanija"},"563":{"code":"HR-16","name":"Vukovarsko-srijemska \u017eupanija"},"560":{"code":"HR-13","name":"Zadarska \u017eupanija"},"548":{"code":"HR-01","name":"Zagreba\u010dka \u017eupanija"}}},"HT":{"name":"Haiti"},"HU":{"name":"Hungary"},"ID":{"name":"Indonesia"},"IE":{"name":"Ireland"},"IL":{"name":"Israel"},"IM":{"name":"Isle of Man"},"IN":{"name":"India","regions":{"569":{"code":"AN","name":"Andaman and Nicobar Islands"},"570":{"code":"AP","name":"Andhra Pradesh"},"571":{"code":"AR","name":"Arunachal Pradesh"},"572":{"code":"AS","name":"Assam"},"573":{"code":"BR","name":"Bihar"},"574":{"code":"CH","name":"Chandigarh"},"575":{"code":"CT","name":"Chhattisgarh"},"576":{"code":"DN","name":"Dadra and Nagar Haveli"},"577":{"code":"DD","name":"Daman and Diu"},"578":{"code":"DL","name":"Delhi"},"579":{"code":"GA","name":"Goa"},"580":{"code":"GJ","name":"Gujarat"},"581":{"code":"HR","name":"Haryana"},"582":{"code":"HP","name":"Himachal Pradesh"},"583":{"code":"JK","name":"Jammu and Kashmir"},"584":{"code":"JH","name":"Jharkhand"},"585":{"code":"KA","name":"Karnataka"},"586":{"code":"KL","name":"Kerala"},"1156":{"code":"LA","name":"Ladakh"},"587":{"code":"LD","name":"Lakshadweep"},"588":{"code":"MP","name":"Madhya Pradesh"},"589":{"code":"MH","name":"Maharashtra"},"590":{"code":"MN","name":"Manipur"},"591":{"code":"ML","name":"Meghalaya"},"592":{"code":"MZ","name":"Mizoram"},"593":{"code":"NL","name":"Nagaland"}
7398,"594":{"code":"OR","name":"Odisha"},"595":{"code":"PY","name":"Puducherry"},"596":{"code":"PB","name":"Punjab"},"597":{"code":"RJ","name":"Rajasthan"},"598":{"code":"SK","name":"Sikkim"},"599":{"code":"TN","name":"Tamil Nadu"},"600":{"code":"TG","name":"Telangana"},"601":{"code":"TR","name":"Tripura"},"602":{"code":"UP","name":"Uttar Pradesh"},"603":{"code":"UT","name":"Uttarakhand"},"604":{"code":"WB","name":"West Bengal"}}},"IO":{"name":"British Indian Ocean Territory"},"IQ":{"name":"Iraq"},"IR":{"name":"Iran"},"IS":{"name":"Iceland","regions":{"830":{"code":"IS-07","name":"Austurland"},"824":{"code":"IS-01","name":"H\u00f6fu\u00f0borgarsv\u00e6\u00f0i"},"829":{"code":"IS-06","name":"Nor\u00f0urland eystra"},"828":{"code":"IS-05","name":"Nor\u00f0urland vestra"},"831":{"code":"IS-08","name":"Su\u00f0urland"},"825":{"code":"IS-02","name":"Su\u00f0urnes"},"827":{"code":"IS-04","name":"Vestfir\u00f0ir"},"826":{"code":"IS-03","name":"Vesturland"}}},"IT":{"name":"Italy","regions":{"832":{"code":"AG","name":"Agrigento"},"833":{"code":"AL","name":"Alessandria"},"834":{"code":"AN","name":"Ancona"},"835":{"code":"AO","name":"Aosta"},"837":{"code":"AR","name":"Arezzo"},"838":{"code":"AP","name":"Ascoli-Piceno"},"839":{"code":"AT","name":"Asti"},"840":{"code":"AV","name":"Avellino"},"841":{"code":"BA","name":"Bari"},"842":{"code":"BT","name":"Barletta-Andria-Trani"},"843":{"code":"BL","name":"Belluno"},"844":{"code":"BN","name":"Benevento"},"845":{"code":"BG","name":"Bergamo"},"846":{"code":"BI","name":"Biella"},"847":{"code":"BO","name":"Bologna"},"848":{"code":"BZ","name":"Bolzano"},"849":{"code":"BS","name":"Brescia"},"850":{"code":"BR","name":"Brindisi"},"851":{"code":"CA","name":"Cagliari"},"852":{"code":"CL","name":"Caltanissetta"},"853":{"code":"CB","name":"Campobasso"},"854":{"code":"CI","name":"Carbonia Iglesias"},"855":{"code":"CE","name":"Caserta"},"856":{"code":"CT","name":"Catania"},"857":{"code":"CZ","name":"Catanzaro"},"858":{"code":"CH","name":"Chieti"},"859":{"code":"CO","name":"Como"},"860":{"code":"CS","name":"Cosenza"},"861":{"code":"CR","name":"Cremona"},"862":{"code":"KR","name":"Crotone"},"863":{"code":"CN","name":"Cuneo"},"864":{"code":"EN","name":"Enna"},"865":{"code":"FM","name":"Fermo"},"866":{"code":"FE","name":"Ferrara"},"867":{"code":"FI","name":"Firenze"},"868":{"code":"FG","name":"Foggia"},"869":{"code":"FC","name":"Forli-Cesena"},"870":{"code":"FR","name":"Frosinone"},"871":{"code":"GE","name":"Genova"},"872":{"code":"GO","name":"Gorizia"},"873":{"code":"GR","name":"Grosseto"},"874":{"code":"IM","name":"Imperia"},"875":{"code":"IS","name":"Isernia"},"836":{"code":"AQ","name":"L'Aquila"},"876":{"code":"SP","name":"La-Spezia"},"877":{"code":"LT","name":"Latina"},"878":{"code":"LE","name":"Lecce"},"879":{"code":"LC","name":"Lecco"},"880":{"code":"LI","name":"Livorno"},"881":{"code":"LO","name":"Lodi"},"882":{"code":"LU","name":"Lucca"},"883":{"code":"MC","name":"Macerata"},"884":{"code":"MN","name":"Mantova"},"885":{"code":"MS","name":"Massa-Carrara"},"886":{"code":"MT","name":"Matera"},"887":{"code":"VS","name":"Medio Campidano"},"888":{"code":"ME","name":"Messina"},"889":{"code":"MI","name":"Milano"},"890":{"code":"MO","name":"Modena"},"891":{"code":"MB","name":"Monza-Brianza"},"892":{"code":"NA","name":"Napoli"},"893":{"code":"NO","name":"Novara"},"894":{"code":"NU","name":"Nuoro"},"895":{"code":"OG","name":"Ogliastra"},"896":{"code":"OT","name":"Olbia Tempio"},"897":{"code":"OR","name":"Oristano"},"898":{"code":"PD","name":"Padova"},"899":{"code":"PA","name":"Palermo"},"900":{"code":"PR","name":"Parma"},"901":{"code":"PV","name":"Pavia"},"902":{"code":"PG","name":"Perugia"},"903":{"code":"PU","name":"Pesaro-Urbino"},"904":{"code":"PE","name":"Pescara"},"905":{"code":"PC","name":"Piacenza"},"906":{"code":"PI","name":"Pisa"},"907":{"code":"PT","name":"Pistoia"},"908":{"code":"PN","name":"Pordenone"},"909":{"code":"PZ","name":"Potenza"},"910":{"code":"PO","name":"Prato"},"911":{"code":"RG","name":"Ragusa"},"912":{"code":"RA","name":"Ravenna"},"913":{"code":"RC","name":"Reggio-Calabria"},"914":{"code":"RE","name":"Reggio-Emilia"},"915":{"code":"RI","name":"Rieti"},"916":{"code":"RN","name":"Rimini"},"917":{"code":"RM","name":"Roma"},"918":{"code":"RO","name":"Rovigo"},"919":{"code":"SA","name":"Salerno"},"920":{"code":"SS","name":"Sassari"},"921":{"code":"SV","name":"Savona"},"922":{"code":"SI","name":"Siena"},"923":{"code":"SR","name":"Siracusa"},"924":{"code":"SO","name":"Sondrio"},"925":{"code":"TA","name":"Taranto"},"926":{"code":"TE","name":"Teramo"},"927":{"code":"TR","name":"Terni"},"928":{"code":"TO","name":"Torino"},"929":{"code":"TP","name":"Trapani"},"930":{"code":"TN","name":"Trento"},"931":{"code":"TV","name":"Treviso"},"932":{"code":"TS","name":"Trieste"},"933":{"code":"UD","name":"Udine"},"934":{"code":"VA","name":"Varese"},"935":{"code":"VE","name":"Venezia"},"936":{"code":"VB","name":"Verbania"},"937":{"code":"VC","name":"Vercelli"},"938":{"code":"VR","name":"Verona"}
7398,"939":{"code":"VV","name":"Vibo-Valentia"},"940":{"code":"VI","name":"Vicenza"},"941":{"code":"VT","name":"Viterbo"}}},"JE":{"name":"Jersey"},"JM":{"name":"Jamaica"},"JO":{"name":"Jordan"},"JP":{"name":"Japan"},"KE":{"name":"Kenya"},"KG":{"name":"Kyrgyzstan"},"KH":{"name":"Cambodia"},"KI":{"name":"Kiribati"},"KM":{"name":"Comoros"},"KN":{"name":"St. Kitts & Nevis"},"KP":{"name":"North Korea"},"KR":{"name":"South Korea"},"KW":{"name":"Kuwait"},"KY":{"name":"Cayman Islands"},"KZ":{"name":"Kazakhstan"},"LA":{"name":"Laos"},"LB":{"name":"Lebanon"},"LC":{"name":"St. Lucia"},"LI":{"name":"Liechtenstein"},"LK":{"name":"Sri Lanka"},"LR":{"name":"Liberia"},"LS":{"name":"Lesotho"},"LT":{"name":"Lithuania","regions":{"475":{"code":"LT-AL","name":"Alytaus Apskritis"},"476":{"code":"LT-KU","name":"Kauno Apskritis"},"477":{"code":"LT-KL","name":"Klaip\u0117dos Apskritis"},"478":{"code":"LT-MR","name":"Marijampol\u0117s Apskritis"},"479":{"code":"LT-PN","name":"Panev\u0117\u017eio Apskritis"},"480":{"code":"LT-SA","name":"\u0160iauli\u0173 Apskritis"},"481":{"code":"LT-TA","name":"Taurag\u0117s Apskritis"},"482":{"code":"LT-TE","name":"Tel\u0161i\u0173 Apskritis"},"483":{"code":"LT-UT","name":"Utenos Apskritis"},"484":{"code":"LT-VL","name":"Vilniaus Apskritis"}}},"LU":{"name":"Luxembourg"},"LV":{"name":"Latvia","regions":{"471":{"code":"\u0100da\u017eu novads","name":"\u0100da\u017eu novads"},"366":{"code":"Aglonas novads","name":"Aglonas novads"},"367":{"code":"LV-AI","name":"Aizkraukles novads"},"368":{"code":"Aizputes novads","name":"Aizputes novads"},"369":{"code":"Akn\u012bstes novads","name":"Akn\u012bstes novads"},"370":{"code":"Alojas novads","name":"Alojas novads"},"371":{"code":"Alsungas novads","name":"Alsungas novads"},"372":{"code":"LV-AL","name":"Al\u016bksnes novads"},"373":{"code":"Amatas novads","name":"Amatas novads"},"374":{"code":"Apes novads","name":"Apes novads"},"375":{"code":"Auces novads","name":"Auces novads"},"376":{"code":"Bab\u012btes novads","name":"Bab\u012btes novads"},"377":{"code":"Baldones novads","name":"Baldones novads"},"378":{"code":"Baltinavas novads","name":"Baltinavas novads"},"379":{"code":"LV-BL","name":"Balvu novads"},"380":{"code":"LV-BU","name":"Bauskas novads"},"381":{"code":"Bever\u012bnas novads","name":"Bever\u012bnas novads"},"382":{"code":"Broc\u0113nu novads","name":"Broc\u0113nu novads"},"383":{"code":"Burtnieku novads","name":"Burtnieku novads"},"384":{"code":"Carnikavas novads","name":"Carnikavas novads"},"387":{"code":"LV-CE","name":"C\u0113su novads"},"385":{"code":"Cesvaines novads","name":"Cesvaines novads"},"386":{"code":"Ciblas novads","name":"Ciblas novads"},"388":{"code":"Dagdas novads","name":"Dagdas novads"},"355":{"code":"LV-DGV","name":"Daugavpils"},"389":{"code":"LV-DA","name":"Daugavpils novads"},"390":{"code":"LV-DO","name":"Dobeles novads"},"391":{"code":"Dundagas novads","name":"Dundagas novads"},"392":{"code":"Durbes novads","name":"Durbes novads"},"393":{"code":"Engures novads","name":"Engures novads"},"472":{"code":"\u0112rg\u013cu novads","name":"\u0112rg\u013cu novads"},"394":{"code":"Garkalnes novads","name":"Garkalnes novads"},"395":{"code":"Grobi\u0146as novads","name":"Grobi\u0146as novads"},"396":{"code":"LV-GU","name":"Gulbenes novads"},"397":{"code":"Iecavas novads","name":"Iecavas novads"},"398":{"code":"Ik\u0161\u0137iles novads","name":"Ik\u0161\u0137iles novads"},"399":{"code":"Il\u016bkstes novads","name":"Il\u016bkstes novads"},"400":{"code":"In\u010dukalna novads","name":"In\u010dukalna novads"},"401":{"code":"Jaunjelgavas novads","name":"Jaunjelgavas novads"},"402":{"code":"Jaunpiebalgas novads","name":"Jaunpiebalgas novads"},"403":{"code":"Jaunpils novads","name":"Jaunpils novads"},"357":{"code":"J\u0113kabpils","name":"J\u0113kabpils"},"405":{"code":"LV-JK","name":"J\u0113kabpils novads"},"356":{"code":"LV-JEL","name":"Jelgava"},"404":{"code":"LV-JL","name":"Jelgavas novads"},"358":{"code":"LV-JUR","name":"J\u016brmala"},"406":{"code":"Kandavas novads","name":"Kandavas novads"},"412":{"code":"K\u0101rsavas novads","name":"K\u0101rsavas novads"},"473":{"code":"\u0136eguma novads","name":"\u0136eguma novads"},"474":{"code":"\u0136ekavas novads","name":"\u0136ekavas novads"},"407":{"code":"Kokneses novads","name":"Kokneses novads"},"410":{"code":"LV-KR","name":"Kr\u0101slavas novads"},"408":{"code":"Krimuldas novads","name":"Krimuldas novads"},"409":{"code":"Krustpils novads","name":"Krustpils novads"},"411":{"code":"LV-KU","name":"Kuld\u012bgas novads"},"413":{"code":"Lielv\u0101rdes novads","name":"Lielv\u0101rdes novads"},"359":{"code":"LV-LPX","name":"Liep\u0101ja"},"360":{"code":"LV-LE","name":"Liep\u0101jas novads"},"417":{"code":"L\u012bgatnes novads","name":"L\u012bgatnes novads"},"414":{"code":"LV-LM","name":"Limba\u017eu novads"},"418":{"code":"L\u012bv\u0101nu novads","name":"L\u012bv\u0101nu novads"},"415":{"code":"Lub\u0101nas novads","name":"Lub\u0101nas novads"},"416":{"code":"LV-LU","name":"Ludzas novads"},"419":{"code":"LV-MA","name":"Madonas novads"},"421":{"code":"M\u0101lpils novads","name":"M\u0101lpils novads"},"422":{"code":"M\u0101rupes novads","name":"M\u0101rupes novads"},"420":{"code":"Mazsalacas novads","name":"Mazsalacas novads"},"423":{"code":"Nauk\u0161\u0113nu novads","name":"Nauk\u0161\u0113nu novads"},"424":{"code":"Neretas novads","name":"Neretas novads"},"425":{"code":"N\u012bcas novads","name":"N\u012bcas novads"},"426":{"code":"LV-OG","name":"Ogres novads"},"427":{"code":"Olaines novads","name":"Olaines novads"},"428":{"code":"Ozolnieku novads","name":"Ozolnieku novads"},"432":{"code":"P\u0101rgaujas novads","name":"P\u0101rgaujas novads"},"433":{"code":"P\u0101vilostas novads","name":"P\u0101vilostas novads"},"434":{"code":"P\u013cavi\u0146u novads","name":"P\u013cavi\u0146u novads"},"429":{"code":"LV-PR","name":"Prei\u013cu novads"},"430":{"code":"Priekules novads","name":"Priekules novads"},"431":{"code":"Prieku\u013cu novads","name":"Prieku\u013cu novads"},"435":{"code":"Raunas novads","name":"Raunas novads"},"361":{"code":"LV-REZ","name":"R\u0113zekne"},"442":{"code":"LV-RE","name":"R\u0113zeknes novads"},"436":{"code":"Riebi\u0146u novads","name":"Riebi\u0146u novads"},"362":{"code":"LV-RIX","name":"R\u012bga"},"363":{"code":"LV-RI","name":"R\u012bgas novads"},"437":{"code":"Rojas novads","name":"Rojas novads"},"438":{"code":"Ropa\u017eu novads","name":"Ropa\u017eu novads"},"439":{"code":"Rucavas novads","name":"Rucavas novads"},"440":{"code":"Rug\u0101ju novads","name":"Rug\u0101ju novads"},"443":{"code":"R\u016bjienas novads","name":"R\u016bjienas novads"},"441":{"code":"Rund\u0101les novads","name":"Rund\u0101les novads"},"444":{"code":"Salacgr\u012bvas novads","name":"Salacgr\u012bvas novads"},"445":{"code":"Salas novads","name":"Salas novads"},"446":{"code":"Salaspils novads","name":"Salaspils novads"},"447":{"code":"LV-SA","name":"Saldus novads"},"448":{"code":"Saulkrastu novads","name":"Saulkrastu novads"},"455":{"code":"S\u0113jas novads","name":"S\u0113jas novads"},"449":{"code":"Siguldas novads","name":"Siguldas novads"}
7398,"451":{"code":"Skr\u012bveru novads","name":"Skr\u012bveru novads"},"450":{"code":"Skrundas novads","name":"Skrundas novads"},"452":{"code":"Smiltenes novads","name":"Smiltenes novads"},"453":{"code":"Stopi\u0146u novads","name":"Stopi\u0146u novads"},"454":{"code":"Stren\u010du novads","name":"Stren\u010du novads"},"456":{"code":"LV-TA","name":"Talsu novads"},"458":{"code":"T\u0113rvetes novads","name":"T\u0113rvetes novads"},"457":{"code":"LV-TU","name":"Tukuma novads"},"459":{"code":"Vai\u0146odes novads","name":"Vai\u0146odes novads"},"460":{"code":"LV-VK","name":"Valkas novads"},"364":{"code":"Valmiera","name":"Valmiera"},"461":{"code":"LV-VM","name":"Valmieras novads"},"462":{"code":"Varak\u013c\u0101nu novads","name":"Varak\u013c\u0101nu novads"},"469":{"code":"V\u0101rkavas novads","name":"V\u0101rkavas novads"},"463":{"code":"Vecpiebalgas novads","name":"Vecpiebalgas novads"},"464":{"code":"Vecumnieku novads","name":"Vecumnieku novads"},"365":{"code":"LV-VEN","name":"Ventspils"},"465":{"code":"LV-VE","name":"Ventspils novads"},"466":{"code":"Vies\u012btes novads","name":"Vies\u012btes novads"},"467":{"code":"Vi\u013cakas novads","name":"Vi\u013cakas novads"},"468":{"code":"Vi\u013c\u0101nu novads","name":"Vi\u013c\u0101nu novads"},"470":{"code":"Zilupes novads","name":"Zilupes novads"}}},"LY":{"name":"Libya"},"MA":{"name":"Morocco"},"MC":{"name":"Monaco"},"MD":{"name":"Moldova"},"ME":{"name":"Montenegro"},"MF":{"name":"St. Martin"},"MG":{"name":"Madagascar"},"MH":{"name":"Marshall Islands"},"MK":{"name":"North Macedonia"},"ML":{"name":"Mali"},"MM":{"name":"Myanmar (Burma)"},"MN":{"name":"Mongolia"},"MO":{"name":"Macao SAR China"},"MP":{"name":"Northern Mariana Islands"},"MQ":{"name":"Martinique"},"MR":{"name":"Mauritania"},"MS":{"name":"Montserrat"},"MT":{"name":"Malta"},"MU":{"name":"Mauritius"},"MV":{"name":"Maldives"},"MW":{"name":"Malawi"},"MX":{"name":"Mexico","regions":{"942":{"code":"AGU","name":"Aguascalientes"},"943":{"code":"BCN","name":"Baja California"},"944":{"code":"BCS","name":"Baja California Sur"},"945":{"code":"CAM","name":"Campeche"},"946":{"code":"CHP","name":"Chiapas"},"947":{"code":"CHH","name":"Chihuahua"},"948":{"code":"CMX","name":"Ciudad de M\u00e9xico"},"949":{"code":"COA","name":"Coahuila"},"950":{"code":"COL","name":"Colima"},"951":{"code":"DUR","name":"Durango"},"952":{"code":"MEX","name":"Estado de M\u00e9xico"},"953":{"code":"GUA","name":"Guanajuato"},"954":{"code":"GRO","name":"Guerrero"},"955":{"code":"HID","name":"Hidalgo"},"956":{"code":"JAL","name":"Jalisco"},"957":{"code":"MIC","name":"Michoac\u00e1n"},"958":{"code":"MOR","name":"Morelos"},"959":{"code":"NAY","name":"Nayarit"},"960":{"code":"NLE","name":"Nuevo Le\u00f3n"},"961":{"code":"OAX","name":"Oaxaca"},"962":{"code":"PUE","name":"Puebla"},"963":{"code":"QUE","name":"Quer\u00e9taro"},"964":{"code":"ROO","name":"Quintana Roo"},"965":{"code":"SLP","name":"San Luis Potos\u00ed"},"966":{"code":"SIN","name":"Sinaloa"},"967":{"code":"SON","name":"Sonora"},"968":{"code":"TAB","name":"Tabasco"},"969":{"code":"TAM","name":"Tamaulipas"},"970":{"code":"TLA","name":"Tlaxcala"},"971":{"code":"VER","name":"Veracruz"},"972":{"code":"YUC","name":"Yucat\u00e1n"},"973":{"code":"ZAC","name":"Zacatecas"}}},"MY":{"name":"Malaysia"},"MZ":{"name":"Mozambique"},"NA":{"name":"Namibia"},"NC":{"name":"New Caledonia"},"NE":{"name":"Niger"},"NF":{"name":"Norfolk Island"},"NG":{"name":"Nigeria"},"NI":{"name":"Nicaragua"},"NL":{"name":"Netherlands"},"NO":{"name":"Norway"},"NP":{"name":"Nepal"},"NR":{"name":"Nauru"},"NU":{"name":"Niue"},"NZ":{"name":"New Zealand"},"OM":{"name":"Oman"},"PA":{"name":"Panama"},"PE":{"name":"Peru","regions":{"993":{"code":"PE-AMA","name":"Amazonas"},"994":{"code":"PE-ANC","name":"Ancash"},"995":{"code":"PE-APU","name":"Apur\u00edmac"},"996":{"code":"PE-ARE","name":"Arequipa"},"997":{"code":"PE-AYA","name":"Ayacucho"},"998":{"code":"PE-CAJ","name":"Cajamarca"},"999":{"code":"PE-CUS","name":"Cusco"},"1000":{"code":"PE-CAL","name":"El Callao"},"1001":{"code":"PE-HUV","name":"Huancavelica"},"1002":{"code":"PE-HUC","name":"Hu\u00e1nuco"},"1003":{"code":"PE-ICA","name":"Ica"},"1004":{"code":"PE-JUN","name":"Jun\u00edn"},"1005":{"code":"PE-LAL","name":"La Libertad"},"1006":{"code":"PE-LAM","name":"Lambayeque"},"1007":{"code":"PE-LIM","name":"Lima"},"1008":{"code":"PE-LOR","name":"Loreto"},"1009":{"code":"PE-MDD","name":"Madre de Dios"},"1010":{"code":"PE-MOQ","name":"Moquegua"}
7398,"992":{"code":"PE-LMA","name":"Municipalidad Metropolitana de Lima"},"1011":{"code":"PE-PAS","name":"Pasco"},"1012":{"code":"PE-PIU","name":"Piura"},"1013":{"code":"PE-PUN","name":"Puno"},"1014":{"code":"PE-SAM","name":"San Mart\u00edn"},"1015":{"code":"PE-TAC","name":"Tacna"},"1016":{"code":"PE-TUM","name":"Tumbes"},"1017":{"code":"PE-UCA","name":"Ucayali"}}},"PF":{"name":"French Polynesia"},"PG":{"name":"Papua New Guinea"},"PH":{"name":"Philippines"},"PK":{"name":"Pakistan"},"PL":{"name":"Poland","regions":{"1018":{"code":"PL-02","name":"dolno\u015bl\u0105skie"},"1019":{"code":"PL-04","name":"kujawsko-pomorskie"},"1020":{"code":"PL-06","name":"lubelskie"},"1021":{"code":"PL-08","name":"lubuskie"},"1024":{"code":"PL-14","name":"mazowieckie"},"1023":{"code":"PL-12","name":"ma\u0142opolskie"},"1025":{"code":"PL-16","name":"opolskie"},"1026":{"code":"PL-18","name":"podkarpackie"},"1027":{"code":"PL-20","name":"podlaskie"},"1028":{"code":"PL-22","name":"pomorskie"},"1029":{"code":"PL-24","name":"\u015bl\u0105skie"},"1030":{"code":"PL-26","name":"\u015bwi\u0119tokrzyskie"},"1031":{"code":"PL-28","name":"warmi\u0144sko-mazurskie"},"1032":{"code":"PL-30","name":"wielkopolskie"},"1033":{"code":"PL-32","name":"zachodniopomorskie"},"1022":{"code":"PL-10","name":"\u0142\u00f3dzkie"}}},"PM":{"name":"St. Pierre & Miquelon"},"PN":{"name":"Pitcairn Islands"},"PS":{"name":"Palestinian Territories"},"PT":{"name":"Portugal","regions":{"1034":{"code":"PT-01","name":"Aveiro"},"1035":{"code":"PT-02","name":"Beja"},"1036":{"code":"PT-03","name":"Braga"},"1037":{"code":"PT-04","name":"Bragan\u00e7a"},"1038":{"code":"PT-05","name":"Castelo Branco"},"1039":{"code":"PT-06","name":"Coimbra"},"1040":{"code":"PT-07","name":"\u00c9vora"},"1041":{"code":"PT-08","name":"Faro"},"1042":{"code":"PT-09","name":"Guarda"},"1043":{"code":"PT-10","name":"Leiria"},"1044":{"code":"PT-11","name":"Lisboa"},"1045":{"code":"PT-12","name":"Portalegre"},"1046":{"code":"PT-13","name":"Porto"},"1053":{"code":"PT-30","name":"Regi\u00e3o Aut\u00f3noma da Madeira"},"1052":{"code":"PT-20","name":"Regi\u00e3o Aut\u00f3noma dos A\u00e7ores"},"1047":{"code":"PT-14","name":"Santar\u00e9m"},"1048":{"code":"PT-15","name":"Set\u00fabal"},"1049":{"code":"PT-16","name":"Viana do Castelo"},"1050":{"code":"PT-17","name":"Vila Real"},"1051":{"code":"PT-18","name":"Viseu"}}},"PW":{"name":"Palau"},"PY":{"name":"Paraguay","regions":{"975":{"code":"PY-16","name":"Alto Paraguay"},"976":{"code":"PY-10","name":"Alto Paran\u00e1"},"977":{"code":"PY-13","name":"Amambay"},"974":{"code":"PY-ASU","name":"Asunci\u00f3n"},"978":{"code":"PY-19","name":"Boquer\u00f3n"},"979":{"code":"PY-5","name":"Caaguaz\u00fa"},"980":{"code":"PY-6","name":"Caazap\u00e1"},"981":{"code":"PY-14","name":"Canindey\u00fa"},"982":{"code":"PY-11","name":"Central"},"983":{"code":"PY-1","name":"Concepci\u00f3n"},"984":{"code":"PY-3","name":"Cordillera"},"985":{"code":"PY-4","name":"Guair\u00e1"},"986":{"code":"PY-7","name":"Itap\u00faa"},"987":{"code":"PY-8","name":"Misiones"},"988":{"code":"PY-12","name":"\u00d1eembuc\u00fa"},"989":{"code":"PY-9","name":"Paraguar\u00ed"},"990":{"code":"PY-15","name":"Presidente Hayes"},"991":{"code":"PY-2","name":"San Pedro"}}},"QA":{"name":"Qatar"},"RE":{"name":"R\u00e9union"},"RO":{"name":"Romania","regions":{"278":{"code":"AB","name":"Alba"},"279":{"code":"AR","name":"Arad"},"280":{"code":"AG","name":"Arge\u015f"},"281":{"code":"BC","name":"Bac\u0103u"},"282":{"code":"BH","name":"Bihor"},"283":{"code":"BN","name":"Bistri\u0163a-N\u0103s\u0103ud"},"284":{"code":"BT","name":"Boto\u015fani"},"286":{"code":"BR","name":"Br\u0103ila"},"285":{"code":"BV","name":"Bra\u015fov"},"287":{"code":"B","name":"Bucure\u015fti"},"288":{"code":"BZ","name":"Buz\u0103u"},"290":{"code":"CL","name":"C\u0103l\u0103ra\u015fi"},"289":{"code":"CS","name":"Cara\u015f-Severin"},"291":{"code":"CJ","name":"Cluj"},"292":{"code":"CT","name":"Constan\u0163a"},"293":{"code":"CV","name":"Covasna"},"294":{"code":"DB","name":"D\u00e2mbovi\u0163a"},"295":{"code":"DJ","name":"Dolj"},"296":{"code":"GL","name":"Gala\u0163i"},"297":{"code":"GR","name":"Giurgiu"},"298":{"code":"GJ","name":"Gorj"},"299":{"code":"HR","name":"Harghita"},"300":{"code":"HD","name":"Hunedoara"},"301":{"code":"IL","name":"Ialomi\u0163a"},"302":{"code":"IS","name":"Ia\u015fi"},"303":{"code":"IF","name":"Ilfov"},"304":{"code":"MM","name":"Maramure\u015f"},"305":{"code":"MH","name":"Mehedin\u0163i"},"306":{"code":"MS","name":"Mure\u015f"},"307":{"code":"NT","name":"Neam\u0163"},"308":{"code":"OT","name":"Olt"},"309":{"code":"PH","name":"Prahova"},"311":{"code":"SJ","name":"S\u0103laj"},"310":{"code":"SM","name":"Satu-Mare"},"312":{"code":"SB","name":"Sibiu"},"313":{"code":"SV","name":"Suceava"},"314":{"code":"TR","name":"Teleorman"},"315":{"code":"TM","name":"Timi\u015f"},"316":{"code":"TL","name":"Tulcea"},"318":{"code":"VL","name":"V\u00e2lcea"},"317":{"code":"VS","name":"Vaslui"},"319":{"code":"VN","name":"Vrancea"}}},"RS":{"name":"Serbia"},"RU":{"name":"Russia"},"RW":{"name":"Rwanda"},"SA":{"name":"Saudi Arabia"},"SB":{"name":"Solomon Islands"},"SC":{"name":"Seychelles"},"SD":{"name":"Sudan"},"SE":{"name":"Sweden","regions":{"1064":{"code":"SE-K","name":"Blekinge l\u00e4n"}
7398,"1065":{"code":"SE-W","name":"Dalarnas l\u00e4n"},"1067":{"code":"SE-X","name":"G\u00e4vleborgs l\u00e4n"},"1066":{"code":"SE-I","name":"Gotlands l\u00e4n"},"1068":{"code":"SE-N","name":"Hallands l\u00e4n"},"1069":{"code":"SE-Z","name":"J\u00e4mtlands l\u00e4n"},"1070":{"code":"SE-F","name":"J\u00f6nk\u00f6pings l\u00e4n"},"1071":{"code":"SE-H","name":"Kalmar l\u00e4n"},"1072":{"code":"SE-G","name":"Kronobergs l\u00e4n"},"1073":{"code":"SE-BD","name":"Norrbottens l\u00e4n"},"1083":{"code":"SE-T","name":"\u00d6rebro l\u00e4n"},"1084":{"code":"SE-E","name":"\u00d6sterg\u00f6tlands l\u00e4n"},"1074":{"code":"SE-M","name":"Sk\u00e5ne l\u00e4n"},"1076":{"code":"SE-D","name":"S\u00f6dermanlands l\u00e4n"},"1075":{"code":"SE-AB","name":"Stockholms l\u00e4n"},"1077":{"code":"SE-C","name":"Uppsala l\u00e4n"},"1078":{"code":"SE-S","name":"V\u00e4rmlands l\u00e4n"},"1079":{"code":"SE-AC","name":"V\u00e4sterbottens l\u00e4n"},"1080":{"code":"SE-Y","name":"V\u00e4sternorrlands l\u00e4n"},"1081":{"code":"SE-U","name":"V\u00e4stmanlands l\u00e4n"},"1082":{"code":"SE-O","name":"V\u00e4stra G\u00f6talands l\u00e4n"}}},"SG":{"name":"Singapore"},"SH":{"name":"St. Helena"},"SI":{"name":"Slovenia"},"SJ":{"name":"Svalbard & Jan Mayen"},"SK":{"name":"Slovakia"},"SL":{"name":"Sierra Leone"},"SM":{"name":"San Marino"},"SN":{"name":"Senegal"},"SO":{"name":"Somalia"},"SR":{"name":"Suriname","regions":{"1054":{"code":"SR-BR","name":"Brokopondo"},"1055":{"code":"SR-CM","name":"Commewijne"},"1056":{"code":"SR-CR","name":"Coronie"},"1057":{"code":"SR-MA","name":"Marowijne"},"1058":{"code":"SR-NI","name":"Nickerie"},"1059":{"code":"SR-PR","name":"Para"},"1060":{"code":"SR-PM","name":"Paramaribo"},"1061":{"code":"SR-SA","name":"Saramacca"},"1062":{"code":"SR-SI","name":"Sipaliwini"},"1063":{"code":"SR-WA","name":"Wanica"}}},"ST":{"name":"S\u00e3o Tom\u00e9 & Pr\u00edncipe"},"SV":{"name":"El Salvador"},"SX":{"name":"Sint Maarten"},"SY":{"name":"Syria"},"SZ":{"name":"Eswatini"},"TC":{"name":"Turks & Caicos Islands"},"TD":{"name":"Chad"},"TF":{"name":"French Southern Territories"},"TG":{"name":"Togo"},"TH":{"name":"Thailand"},"TJ":{"name":"Tajikistan"},"TK":{"name":"Tokelau"},"TL":{"name":"Timor-Leste"},"TM":{"name":"Turkmenistan"},"TN":{"name":"Tunisia"},"TO":{"name":"Tonga"},"TR":{"name":"T\u00fcrkiye"},"TT":{"name":"Trinidad & Tobago"},"TV":{"name":"Tuvalu"},"TW":{"name":"Taiwan"},"TZ":{"name":"Tanzania"},"UA":{"name":"Ukraine","regions":{"1109":{"code":"UA-43","name":"Avtonomna Respublika Krym"},"1085":{"code":"UA-71","name":"Cherkaska oblast"},"1086":{"code":"UA-74","name":"Chernihivska oblast"},"1087":{"code":"UA-77","name":"Chernivetska oblast"},"1088":{"code":"UA-12","name":"Dnipropetrovska oblast"},"1089":{"code":"UA-14","name":"Donetska oblast"},"1090":{"code":"UA-26","name":"Ivano-Frankivska oblast"},"1091":{"code":"UA-63","name":"Kharkivska oblast"},"1092":{"code":"UA-65","name":"Khersonska oblast"},"1093":{"code":"UA-68","name":"Khmelnytska oblast"},"1094":{"code":"UA-35","name":"Kirovohradska oblast"},"1110":{"code":"UA-30","name":"Kyiv"},"1095":{"code":"UA-32","name":"Kyivska oblast"},"1096":{"code":"UA-09","name":"Luhanska oblast"},"1097":{"code":"UA-46","name":"Lvivska oblast"},"1098":{"code":"UA-48","name":"Mykolaivska oblast"},"1099":{"code":"UA-51","name":"Odeska oblast"},"1100":{"code":"UA-53","name":"Poltavska oblast"},"1101":{"code":"UA-56","name":"Rivnenska oblast"},"1111":{"code":"UA-40","name":"Sevastopol"},"1102":{"code":"UA-59","name":"Sumska oblast"},"1103":{"code":"UA-61","name":"Ternopilska oblast"},"1104":{"code":"UA-05","name":"Vinnytska oblast"},"1105":{"code":"UA-07","name":"Volynska oblast"},"1106":{"code":"UA-21","name":"Zakarpatska oblast"},"1107":{"code":"UA-23","name":"Zaporizka oblast"},"1108":{"code":"UA-18","name":"Zhytomyrska oblast"}}},"UG":{"name":"Uganda"},"UM":{"name":"U.S. Outlying Islands"},"US":{"name":"United States","regions":{"1":{"code":"AL","name":"Alabama"},"2":{"code":"AK","name":"Alaska"},"3":{"code":"AS","name":"American Samoa"},"4":{"code":"AZ","name":"Arizona"},"5":{"code":"AR","name":"Arkansas"},"6":{"code":"AE","name":"Armed Forces Africa"},"7":{"code":"AA","name":"Armed Forces Americas"},"8":{"code":"AE","name":"Armed Forces Canada"},"9":{"code":"AE","name":"Armed Forces Europe"},"10":{"code":"AE","name":"Armed Forces Middle East"},"11":{"code":"AP","name":"Armed Forces Pacific"},"12":{"code":"CA","name":"California"},"13":{"code":"CO","name":"Colorado"},"14":{"code":"CT","name":"Connecticut"},"15":{"code":"DE","name":"Delaware"},"16":{"code":"DC","name":"District of Columbia"},"17":{"code":"FM","name":"Federated States Of Micronesia"}
7398,"18":{"code":"FL","name":"Florida"},"19":{"code":"GA","name":"Georgia"},"20":{"code":"GU","name":"Guam"},"21":{"code":"HI","name":"Hawaii"},"22":{"code":"ID","name":"Idaho"},"23":{"code":"IL","name":"Illinois"},"24":{"code":"IN","name":"Indiana"},"25":{"code":"IA","name":"Iowa"},"26":{"code":"KS","name":"Kansas"},"27":{"code":"KY","name":"Kentucky"},"28":{"code":"LA","name":"Louisiana"},"29":{"code":"ME","name":"Maine"},"30":{"code":"MH","name":"Marshall Islands"},"31":{"code":"MD","name":"Maryland"},"32":{"code":"MA","name":"Massachusetts"},"33":{"code":"MI","name":"Michigan"},"34":{"code":"MN","name":"Minnesota"},"35":{"code":"MS","name":"Mississippi"},"36":{"code":"MO","name":"Missouri"},"37":{"code":"MT","name":"Montana"},"38":{"code":"NE","name":"Nebraska"},"39":{"code":"NV","name":"Nevada"},"40":{"code":"NH","name":"New Hampshire"},"41":{"code":"NJ","name":"New Jersey"},"42":{"code":"NM","name":"New Mexico"},"43":{"code":"NY","name":"New York"},"44":{"code":"NC","name":"North Carolina"},"45":{"code":"ND","name":"North Dakota"},"46":{"code":"MP","name":"Northern Mariana Islands"},"47":{"code":"OH","name":"Ohio"},"48":{"code":"OK","name":"Oklahoma"},"49":{"code":"OR","name":"Oregon"},"50":{"code":"PW","name":"Palau"},"51":{"code":"PA","name":"Pennsylvania"},"52":{"code":"PR","name":"Puerto Rico"},"53":{"code":"RI","name":"Rhode Island"},"54":{"code":"SC","name":"South Carolina"},"55":{"code":"SD","name":"South Dakota"},"56":{"code":"TN","name":"Tennessee"},"57":{"code":"TX","name":"Texas"},"58":{"code":"UT","name":"Utah"},"59":{"code":"VT","name":"Vermont"},"60":{"code":"VI","name":"Virgin Islands"},"61":{"code":"VA","name":"Virginia"},"62":{"code":"WA","name":"Washington"},"63":{"code":"WV","name":"West Virginia"},"64":{"code":"WI","name":"Wisconsin"},"65":{"code":"WY","name":"Wyoming"}}},"UY":{"name":"Uruguay","regions":{"1112":{"code":"UY-AR","name":"Artigas"},"1113":{"code":"UY-CA","name":"Canelones"},"1114":{"code":"UY-CL","name":"Cerro Largo"},"1115":{"code":"UY-CO","name":"Colonia"},"1116":{"code":"UY-DU","name":"Durazno"},"1117":{"code":"UY-FS","name":"Flores"},"1118":{"code":"UY-FD","name":"Florida"},"1119":{"code":"UY-LA","name":"Lavalleja"},"1120":{"code":"UY-MA","name":"Maldonado"},"1121":{"code":"UY-MO","name":"Montevideo"},"1122":{"code":"UY-PA","name":"Paysandu"},"1123":{"code":"UY-RN","name":"R\u00edo Negro"},"1124":{"code":"UY-RV","name":"Rivera"},"1125":{"code":"UY-RO","name":"Rocha"},"1126":{"code":"UY-SA","name":"Salto"},"1127":{"code":"UY-SJ","name":"San Jos\u00e9"},"1128":{"code":"UY-SO","name":"Soriano"},"1129":{"code":"UY-TA","name":"Tacuaremb\u00f3"},"1130":{"code":"UY-TT","name":"Treinta y Tres"}}},"UZ":{"name":"Uzbekistan"},"VA":{"name":"Vatican City"},"VC":{"name":"St. Vincent & Grenadines"},"VE":{"name":"Venezuela","regions":{"1133":{"code":"VE-Z","name":"Amazonas"},"1134":{"code":"VE-B","name":"Anzo\u00e1tegui"},"1135":{"code":"VE-C","name":"Apure"},"1136":{"code":"VE-D","name":"Aragua"},"1137":{"code":"VE-E","name":"Barinas"},"1138":{"code":"VE-F","name":"Bol\u00edvar"},"1139":{"code":"VE-G","name":"Carabobo"},"1140":{"code":"VE-H","name":"Cojedes"},"1141":{"code":"VE-Y","name":"Delta Amacuro"},"1131":{"code":"VE-W","name":"Dependencias Federales"},"1132":{"code":"VE-A","name":"Distrito Capital"},"1142":{"code":"VE-I","name":"Falc\u00f3n"},"1143":{"code":"VE-J","name":"Gu\u00e1rico"},"1144":{"code":"VE-K","name":"Lara"},"1145":{"code":"VE-L","name":"M\u00e9rida"},"1146":{"code":"VE-M","name":"Miranda"},"1147":{"code":"VE-N","name":"Monagas"},"1148":{"code":"VE-O","name":"Nueva Esparta"},"1149":{"code":"VE-P","name":"Portuguesa"},"1150":{"code":"VE-R","name":"Sucre"},"1151":{"code":"VE-S","name":"T\u00e1chira"},"1152":{"code":"VE-T","name":"Trujillo"},"1153":{"code":"VE-X","name":"Vargas"},"1154":{"code":"VE-U","name":"Yaracuy"},"1155":{"code":"VE-V","name":"Zulia"}}},"VG":{"name":"British Virgin Islands"},"VI":{"name":"U.S. Virgin Islands"},"VN":{"name":"Vietnam"},"VU":{"name":"Vanuatu"},"WF":{"name":"Wallis & Futuna"},"WS":{"name":"Samoa"},"XK":{"name":"Kosovo"},"YE":{"name":"Yemen"},"YT":{"name":"Mayotte"},"ZA":{"name":"South Africa"},"ZM":{"name":"Zambia"},"ZW":{"name":"Zimbabwe"},"data_id":1790361128},"captcha":[],"instant-purchase":[],"loggedAsCustomer":[],"persistent":[],"review":[],"payments":[],"wishlist":{"items":[]},"quotecart":[],"hyva_checkout":[],"hyva_commerce_messages":[],"capi-event-ids":[],"recently_viewed_product":[],"recently_compared_product":[],"product_data_storage":[],"paypal-billing-agreement":[],"paypal-buyer-country":[]}</script>
vendor: 1 bytes, line 7398
7398
7399<script> 7400 window.processSectionDataBeforeDispatch && (window.processSectionDataBeforeDispatch = new Proxy(window.processSectionDataBeforeDispatch, { 7401 apply(target, thisArg, [data]) { 7402 data.hyva_commerce_messages = data.hyva_commerce_messages || {}; 7403 return target.call(thisArg, data); 7404 } 7405 })); 7406</script>
7406 7407
7407<script> 7408 let vatMode = vatSwitcher.getVatMode() || excluding; 7409 let elemSelector = vatMode === 'including' ? '.price-excluding-tax' : '.price-including-tax'; 7410 7411 if (document.body.classList.contains('sales-order-view') 7412 || document.body.classList.contains('sales-order-invoice')) { 7413 elemSelector = null; 7414 } 7415 if (elemSelector) { 7416 document.querySelectorAll(elemSelector).forEach(function (el) { 7417 if (!el.classList.contains('hidden')) { 7418 el.classList.add('hidden') 7419 } 7420 }); 7421 } 7422 </script>
7422 7423</div></body> 7424</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.