1var webshop = { 2 '_': {}, 3 extend: function (object, beforeOrAfter, after) { 4 var oldObject = eval('' + object + ';'); 5 eval(object + ' = function() { \ 6 if(typeof after === \'object\') { \ 7 beforeOrAfter.apply(this, arguments); \ 8 } else { after = beforeOrAfter; } \ 9 \ 10 var result = oldObject.apply(this, arguments); \ 11 \ 12 if(typeof result === \'function\') { \ 13 var mainArguments = [].slice.call(arguments, 0); \ 14 mainArguments.push(result);\ 15 result = after.apply(this, mainArguments); \ 16 }\ 17 if(typeof after === \'function\') { \ 18 after.apply(this, arguments); \ 19 }\ 20 \ 21 return result; \ 22 }'); 23 }, 24 calendarOptionsExtended: {}, 25 googleAnalytics: { 26 createCartViewEvent: function (productDetails) { 27 if (shopconfig.gaEnabled) { 28 var ga4Event = { 29 currency: shopconfig.currency_code, 30 value: 0, 31 items: [] 32 }; 33 34 // Loop through each item in the cart 35 for (var i = 0; i < productDetails.length; i++) { 36 // Update the total value of the order by adding the items price to the total 37 ga4Event.value += productDetails[i].Price * productDetails[i].Quantity; 38 ga4Event.items.push({ 39 item_id: productDetails[i].SKU, 40 item_name: productDetails[i].Name, 41 price: productDetails[i].Price, 42 quantity: productDetails[i].Quantity 43 }); 44 } 45 46 gtag("event", "view_cart", ga4Event); 47 } 48 }, 49 /** 50 * Send a Google Analytics checkout event. 51 * 52 * Takes in the product details, an actionField object which specifies the checkout step and adds the items in the cart to the event 53 * 54 * @param {Object} productDetails - Has all the products that we will add to the checkout event. 55 * @param {Object} actionField - Specify the step in the checkout process i.e. 1 = Checked out, 2 = Address details etc. 56 * @return {void} 57 */ 58 createCheckoutEvent: function (productDetails, actionField) { 59 if (shopconfig.gaEnabled) { 60 var ga4Event = { 61 currency: shopconfig.currency_code, 62 value: 0, 63 items: [] 64 }; 65 66 // Loop through each item in the cart 67 for (var i = 0; i < productDetails.length; i++) { 68 // Update the total value of the order by adding the items price to the total 69 ga4Event.value += productDetails[i].Price * productDetails[i].Quantity; 70 ga4Event.items.push({ 71 item_id: productDetails[i].SKU, 72 item_name: productDetails[i].Name, 73 price: productDetails[i].Price, 74 quantity: productDetails[i].Quantity 75 }); 76 } 77 78 switch (actionField.step) { 79 case 1: 80 gtag("event", "begin_checkout", ga4Event); 81 break; 82 case 2: 83 gtag("event", "add_shipping_info", ga4Event); 84 break; 85 case 3: 86 ga4Event.payment_type = actionField.option; 87 gtag("event", "add_payment_info", ga4Event); 88 break; 89 } 90 } 91 } 92 }, 93 googleTagManager: { 94 /** 95 * Add step of the checkout event and the customers products to the dataLayer. 96 * 97 * Takes in the product details and an actionField object which specifies the checkout step 98 * and adds each product to the Google Tag Manager dataLayer. 99 * 100 * @param {Object} productDetails Has all the products that we will add to the dataLayer. 101 * @param {Object} actionField Specify the step in the checkout process i.e. 1 = Checked out, 2 = Address details etc. 102 * @return {void} 103 */ 104 addCheckoutStepToDataLayer: function (productDetails, actionField) { 105 if (typeof dataLayer === 'object') { 106 var dataLayerProducts = []; 107 108 let gtagEvent = ''; 109 switch (actionField.step) { 110 case 1: 111 gtagEvent = 'begin_checkout'; 112 break; 113 case 2: 114 gtagEvent = 'add_shipping_info'; 115 break; 116 case 3: 117 gtagEvent = 'add_payment_info'; 118 break; 119 default: 120 gtagEvent = 'begin_checkout'; 121 break; 122 } 123 124 let value = 0; 125 for (var i = 0; i < productDetails.length; i++) { 126 value += productDetails[i].Price * productDetails[i].Quantity; 127 dataLayerProducts.push({ 128 'item_name': productDetails[i].Name, 129 'item_id': productDetails[i].SKU, 130 'price': productDetails[i].Price, 131 'quantity': productDetails[i].Quantity 132 }); 133 } 134 135 dataLayer.push({ 136 'event': gtagEvent, 137 'ecommerce': { 138 'currency': shopconfig.currency_code, 139 'value': value, 140 'items': dataLayerProducts 141 } 142 }); 143 } 144 } 145 }, 146 cart: { 147 ajaxAddToCartEvents: { 148 useToolTip: true, 149 onclick: function () { 150 if ($(this).attr('href') == 'javascript:;') { 151 return false;
152 } 153 154 var packSize = webshop.cart.addToCart.validatePackSize($(this)); 155 if (packSize.error) { 156 alert(packSize.error); 157 return false; 158 } 159 160 var holdpass = $(this); 161 var btn_html = $(this)[0].innerHTML; 162 $(this).html(webshop._.webshop.loading.text); 163 164 var addToHref = $(this).attr('href'); 165 166 // Get quantity of added product 167 var quantity = addToHref.split('||').pop(); 168 169 if (packSize.hasPackSize) { 170 addToHref += '&packSize=' + packSize.PackSize.code + '&packSizeValue=' + packSize.PackSizeValue; 171 } 172 173 try { 174 if (manpacks && manpacks.id) { 175 addToHref += '&manpackid=' + manpacks.id; 176 } 177 } catch (e) { 178 //Manpack not activated 179 } 180 181 var extraParams = webshop.cart.addToCart.validateExtraAttributes($(this).data('productid')); 182 if (extraParams) { 183 addToHref += extraParams; 184 } 185 186 webshop.cart.ajaxAddToCartEvents.queue.push({ 187 url: addToHref + '&route=ajax' + webshop.cart.ajaxAddToCartEvents.additionalRequestParam
187s, 188 'dataType': 'json', 189 success: webshop.cart.ajaxAddToCartEvents.success(holdpass, btn_html) 190 }); 191 192 const multiAdd = addToHref.includes('addmulti'); 193 194 // Check if Google Tag Manager is enabled 195 if (shopconfig.tagManagerEnabled && typeof dataLayer === 'object') { 196 let price = parseFloat($(this).attr('data-analytics-value') || '0.00'); 197 let products = [{ 198 item_id: $(this).attr('data-product-sku'), 199 item_name: decodeURIComponent(($(this).attr('data-product-name') + '').replace(/\+/g, '%20')), 200 quantity: parseInt(quantity), 201 price: price 202 }]; 203 204 if (multiAdd) { 205 const rawProducts = $(this).attr('data-gtm-add-to-cart'); 206 if (!rawProducts) { 207 console.info('Cannot send analytics event as the items are missing') 208 return false; 209 } 210 211 products = JSON.parse(rawProducts); 212 } 213 214 let value = products.reduce((sum, product) => { 215 const price = parseFloat(product.price) || 0; 216 const quantity = parseInt(product.quantity) || 0; 217 return sum + (price * quantity); 218 }, 0); 219 220 // Add product data to dataLayer on product view 221 dataLayer.push({ 222 'event': 'add_to_cart', 223 'ecommerce': { 224 'currency': shopconfig.currency_code, 225 'value': value, 226 'items': products 227 } 228 }); 229 } 230 231 if (shopconfig.gaEnabled) { 232 let price = parseFloat($(this).attr('data-analytics-value') || '0.00'); 233 let items = [{ 234 item_id: $(this).attr('data-product-sku'), 235 item_name: decodeURIComponent(($(this).attr('data-product-name') + '').replace(/\+/g, '%20')), 236 quantity: parseInt(quantity), 237 price: price 238 }]; 239 240 if (multiAdd) { 241 const rawItems = $(this).attr('data-ga-add-to-cart'); 242 if (!rawItems) { 243 console.info('Cannot send analytics event as the items are missing') 244 return false; 245 } 246 247 items = JSON.parse(rawItems); 248 } 249 250 let value = items.reduce((sum, product) => { 251 const price = parseFloat(product.price) || 0; 252 const quantity = parseInt(product.quantity) || 0; 253 return sum + (price * quantity); 254 }, 0); 255 256 gtag("event", "add_to_cart", { 257 currency: shopconfig.currency_code, 258 value: value, 259 items: items 260 }); 261 } 262 return false; 263 }, 264 queue: { 265 running: false, 266 list: [], 267 push: function (params) { 268 params.complete = function () { 269 webshop.cart.ajaxAddToCartEvents.queue.running = false; 270 webshop.cart.ajaxAddToCartEvents.queue.next(); 271 }; 272 273 if (webshop.cart.ajaxAddToCartEvents.queue.running) { 274 webshop.cart.ajaxAddToCartEvents.queue.list.push(params); 275 } else { 276 webshop.cart.ajaxAddToCartEvents.queue.run(params); 277 278 } 279 }, 280 next: function () { 281 if (webshop.cart.ajaxAddToCartEvents.queue.list.length > 0) { 282 var params = webshop.cart.ajaxAddToCartEvents.queue.list.pop(); 283 webshop.cart.ajaxAddToCartEvents.queue.run(params); 284 } 285 }, 286 run: function (params) { 287 webshop.cart.ajaxAddToCartEvents.queue.running = $.ajax(
287params); 288 } 289 }, 290 additionalRequestParams: '', 291 success: function ($btn, btnHtml) { 292 var _this = this; 293 294 return function (data) { 295 // tooltip events 296 if (webshop.cart.ajaxAddToCartEvents.useToolTip) { 297 _this.handleResponse($btn, btnHtml, data); 298 } else if ($btn) { 299 $btn.html(webshop._.webshop.added); 300 } 301 // minicart 302 if (data.mini_cart) { 303 $('.js-cart-mini').replaceWith(data.mini_cart); 304 intact_products(); 305 } 306 307 if (shopconfig.isKlaviyoAvailable) { 308 var quantity = $btn.attr('href').split('||').pop().replace('&', ''); 309 310 $.get('/system/klaviyo/triggerAddedToCartEvent.html', 311 { 312 productId: $btn.data('productid'), 313 quantity: parseInt(quantity), 314 } 315 ); 316 } 317 }; 318 }, 319 handleResponse: function ($btn, btnHtml, data) { 320 if (data.type === "wishlist") { 321 $btn.html(btnHtml); 322 webshop.tooltip.show($btn.parent(), 'Added to Wedding List!'); 323 } else if (!data.outOfStockErrors && !data.cart_check) { 324 this.handleSuccessAdded($btn, btnHtml, data) 325 } else { 326 this.handleCartErrors($btn, btnHtml, data); 327 } 328 329 webshop.tooltip.timeout($btn.parent(), 4000); 330 }, 331 handleSuccessAdded: function ($btn, btnHtml, data) { 332 $btn.html(webshop._.webshop.added); 333 webshop.tooltip.show($btn.parent(), utils.decodeEntities(webshop._.global.addedtocart)); 334 335 webshop.cart.ajaxAddToCartEvents.updateMiniCart(data); 336 337 webshop.accessibility.screenReaderMessage(webshop._.global.addedtocart); 338 }, 339 handleCartErrors: function ($btn, btnHtml, data) { 340 var sku = $btn.attr('title'); 341 $btn.html(webshop._.global.addtocart); 342 if (data.errors.hasOwnProperty(sku)) { 343 webshop.tooltip.show($btn.parent(), data.errors[sku]); 344 } 345 }, 346 updateMiniCart: function (data, useNetWithDelivery = false) { 347 const grossTotalContent = shopconfig.useFortaPlusNavStyling ? `${data.count} item(s) ${data.formatted.gross}` : data.formatted.gross; 348 let netTotalContent; 349 350 if (useNetWithDelivery) { 351 netTotalContent = shopconfig.useFortaPlusNavStyling ? `${data.count} item(s) ${data.formatted.net_with_delivery}` : data.formatted.net_with_delivery; 352 } else { 353 netTotalContent = shopconfig.useFortaPlusNavStyling ? `${data.count} item(s) ${data.formatted.net}` : data.formatted.net; 354 } 355 356 $('.items-total').html(data.count + ' <span class="items-total__text c-cart__copy"> item(s) - </span> '); 357 $('.gross-total').html(grossTotalContent); 358 $('.net-total').html(netTotalContent); 359 } 360 }, 361 addToCart: { 362 _selectedUnitsForProducts: {/* populated once a selection is made */ }, 363 _PackSizeOptions: {}, 364 loginToSeePrice: function (redirectUrl) { 365 return '<span class="intact-pricelogin"><a href="/account/login.html?r=' + encodeURIComponent(redirectUrl) + '">' + webshop._.global.button.Login + '</a> ' + webshop._.global.toseeyourprice + '.</a>'; 366 }, 367 getProductIdFromButton: function (addButton) { 368 var productId = addButton.attr('data-productid'); 369 if (addButton.attr('data-optionid')) { 370 productId += '-' + addButton.attr('data-optionid'); 371 } 372 373 return productId; 374 }, 375 _optionsForPacks: [], 376 setPacksForOptions: function (set) { 377 this._optionsForPacks = set; 378 }, 379 getPacksForOption: function (productId) { 380 return this._optionsForPacks[productId]; 381 }, 382 selectPackSizeOption: function (productId, optionsIndex) { 383 const _options = this.getPackSize(productId)[optionsIndex]; 384 const options = JSON.parse(JSON.stringify(_options)); // Clone the object and use it 385 386 options.code = optionsIndex; // Used to identify which pack to add to cart, needs to be the index (cart.ajaxAddToCartEvents.onclick) 387 388 let label = options.label; 389 label += (options.showInputBox) ? ':' : ''; 390 $('.products__add__selling-units-label[data-productid="' + productId + '"]').text(label); 391 392 const inputBox = this.getPackSizeInputForProduct(productId); 393 if (options.showInputBox) { 394 inputBox.show(); 395 } else { 396 inputBox.hide(); 397 } 398 399 this._selectedUnitsForProducts[productId] = options; 400 401 this.updatePriceWithPackSize(options, productId); 402 this.showPriceBreaksTable(options.customPricingValues?.SellingUnits); 403 }, 404 updatePriceWithPackSize: function (options, productId) { 405 try { 406 if (!options.lowestPrice && !options.customPricingValues?.Price) { 407 // No price to update. Most likely a custom pricing processor 408 return; 409 } 410 411 const hasLowerPrice = options.lowestPrice < options.customPricingValues.Price; 412 const priceSplit = ('' + (options.lowestPrice || options.customPricingValues.Price)).split('.'); 413 const stock = options.customPricingValues.Stock; 414 415 let $priceAndStock = $('.l-product__priceblock-price-and-stock'); // Product Page 416 if (!$priceAndStock.length) { // Listing Page 417 $priceAndStock = $('.products__add__selling-units-label[data-productid="' + productId + '"]').parents('.products__item'); 418 } 419 420 const $from = $priceAndStock.find('.price__prefix'); 421 const $priceDigit = $priceAndStock.find('.price__digit'); 422 const $priceDecimal = $priceAndStock.find('.price__decimal'); 423 const $wasPrice = $priceAndStock.find('.was-price'); 424 const $stock = $priceAndStock.find('.stock-available-value'); 425 426 $priceDigit.text(priceSplit[0]); 427 $priceDecimal.text(`.${priceSplit[1] || '00'}`); 428 429 if ($from) { 430 if (hasLowerPrice) { 431 $from.show(); 432 } else { 433 $from.hide(); 434 } 435 } 436 if ($wasPrice) { 437 if (options.packValue != 1) { 438 $wasPrice.hide(); 439 } else { 440 $wasPrice.show(); 441 } 442 } 443 if ($stock && stock) { 444 $stock.text(stock); 445 } 446 } catch (e) { 447 // console.error('Unable to set price of UoM', e); 448 } 449 }, 450 showPriceBreaksTable: function (sellingUnit) { 451 try { 452 $('.qtybreaks-table').hide(); 453 $(`[data-uom=${sellingUnit}]`).show(); 454 } catch (e) { 455 // console.error(e); 456 } 457 }, 458 getPackSizeInputForProduct: function (productId) { 459 return $('.products__add__selling-units__input[data-productid="' + productId + '"]'); 460 }, 461 getPackSize: function (productId) { 462 return this._PackSizeOptions[productId]; 463 }, 464 setPackSizeOptions: function (productId, options) { 465 this._PackSizeOptions[productId] = options; 466 }, 467 getPackSizeForProduct: function (productId) { 468 if (this._selectedUnitsForProducts[productId]) { 469 return this._selectedUnitsForProducts[productId]; 470 } else { 471 return false;
472 } 473 }, 474 validatePackSize: function (addButton) { 475 var prodId = this.getProductIdFromButton(addButton); 476 if (webshop.cart.addToCart.getPackSize(prodId)) { 477 // product has selling units - ensure its selected 478 var sellingOption = webshop.cart.addToCart.getPackSizeForProduct(prodId); 479 if (!sellingOption) { 480 return { 'error': 'You MUST select a pack size for this product before adding to your order.' }; 481 } 482 483 // ensure the input was filled in > 0 also if this option requires it 484 var sellingInput = webshop.cart.addToCart.getPackSizeInputForProduct(prodId); 485 if (sellingOption.showInputBox) { 486 if (parseInt(sellingInput.val()) <= 0) { 487 return { 'error': 'You MUST enter a value for ' + sellingOption.label }; 488 } 489 } 490 491 return { 492 'success': true, 493 'hasPackSize': true, 494 'PackSize': sellingOption, 495 'PackSizeValue': sellingInput.val() 496 }; 497 } 498 499 return { 500 'success': true, 501 'hasPackSize': false 502 }; 503 }, 504 validateExtraAttributes: function (productid) { 505 if (!productid) return false; 506 507 var inputs = $('.addtocart-extra[data-productid="' + productid + '"]'); 508 var set = []; 509 for (var i = 0; i < inputs.length; i++) { 510 var input = $(inputs[i]); 511 512 var value = encodeURIComponent(input.val()); 513 var label = input.data('extra').replace(/[\W]+/g, " "); 514 set.push('&extra[' + label + ']=' + value); 515 } 516 517 return set.length > 0 ? set.join('') : false; 518 } 519 }, 520 generateMultiAddToCartLink: function (inputs) { 521 var url = '/checkout.html?addmulti='; 522 523 for (var i = 0; i < inputs.length; i++) { 524 var product = inputs[i].getAttribute('data-productid'); 525 var qty = inputs[i].value; 526 url += product + '|||' + qty; 527 if (i + 1 < inputs.length) { 528 url += '||||'; 529 } 530 } 531 return url; 532 }, 533 generateAddToCartLink: function () { 534 //Find out the type of button it is 535 var isQtyBox = $(this).hasClass('addmultiinput'); 536 537 var $addToCartInput = isQtyBox ? $(this) : $('.addmultiinput[data-productid="' + $(this).attr('data-productid') + '"]'); 538 var $usingAddToCartButton = isQtyBox && $(this).next().is('.addbtn') ? $(this).next() : $('.addbtn[data-productid="' + $(this).attr('data-productid') + '"]'); 539 540 $.each($usingAddToCartButton, function () { 541 if ($(this).hasClass('disabled')) { 542 $(this).attr('href', 'javascript:;'); 543 return false; 544 } 545 546 var prodid = $(this).attr('data-productid'); 547 var val = parseFloat($addToCartInput.val()); 548 val = (val > 0) ? val : 1; 549 550 if ($(this).attr('data-optionid')) { 551 prodid += '-' + $(this).attr('data-optionid'); 552 } 553 554 var lineRef = ''; 555 var lineRefInput = $('.addtocart-ref[data-productid="' + $(this).attr('data-productid') + '"]'); 556 if (lineRefInput.is('*')) { 557 lineRef += '&ref=' + lineRefInput.val(); 558 } 559 var wishlistLink = ''; 560 if ($(this).attr('data-wishlist-id')) { 561 wishlistLink += '&wishlist_id=' + $(this).attr('data-wishlist-id'); 562 } 563 564 var batchMatchLink = '',
565 $batchMatch = $('input[name="batchMatch"]'); 566 if ($batchMatch.length && $batchMatch.is(':checked')) { 567 batchMatchLink = '&batchMatch=1'; 568 } 569 570 var parts = $(this).attr('href').split('&'); 571 var retained = []; 572 parts.forEach(function (part) { 573 if (/extra.*/.test(part)) { 574 retained.push(part); 575 } 576 }); 577 var extra = ('&' + retained.join('&')); 578 579 var href = '/checkout.html?addqty=' + prodid + '||' + val + lineRef + wishlistLink + batchMatchLink + extra; 580 $(this).attr('href', href); 581 582 var $_wish_list = $('.wish-list-btn[data-productid="' + $(this).attr('data-productid') + '"]'); 583 var wishListID = $_wish_list.attr('data-wishlist-id'); 584 $_wish_list.attr('href', '/account/wishlist/view.html?id=' + wishListID + '&add=' + prodid + '&qty=' + val); 585 }); 586 } 587 }, 588 delivery: { 589 addressBook: { 590 addresses: [], 591 GLSDeliveryEnabled: false, 592 hasInitialized: false, 593 init: function (data, isGLS, selected) { 594 var _this = this, 595 $addressBookSelect = $('#frm-addressbook'), 596 selectOptions = ''; 597 data = webshop.delivery.addressBook.beforeInit(data); 598 599 selected = (selected || false); 600 601 _this.GLSDeliveryEnabled = isGLS || false; 602 603 $.each(data, function (key, address) { 604 var labelArray = []; 605 606 if (address['Address']['company_name']) { 607 labelArray.push(address['Address']['company_name']); 608 } 609 610 611 if (address['Address']['custname']) { 612 labelArray.push(address['Address']['custname']); 613 } 614 615 // address lines 616 for (var i = 1; i < 6; i++) { 617 if (address['Address']['address' + i]) { 618 labelArray.push(address['Address']['address' + i]); 619 } 620 } 621 622 // postcode 623 if (address['Address']['postcode']) { 624 labelArray.push(address['Address']['postcode']); 625 } 626 627 // phone 628 if (address['Address']['phonenumber']) { 629 labelArray.push(address['Address']['phonenumber']); 630 } 631 632 // email 633 if (address['Address']['email']) { 634 labelArray.push(address['Address']['email']); 635 } 636 637 delete address['Address']['Raw']; 638 639 var selectedOption = (address.DeliveryAddressesID == selected ? 'selected="selected"' : ''); 640 selectOptions += '<option ' + selectedOption + ' value="' + address.DeliveryAddressesID + '">' + labelArray.join(', ') + '</option>'; 641 _this.addresses[address.DeliveryAddressesID] = address.Address; 642 }); 643 644 // Set Select Options for Address Book 645 $addressBookSelect.append(selectOptions); 646 647 if ($('#frm-addressbook').is('*')) { 648 var pseudoSelect = $('#frm-addressbook'); 649 } else { 650 var pseudoSelect = document.createElement('option'); 651 pseudoSelect.value = selected; 652 } 653 654 // default the address from the list based on what's in the session 655 var defaultAddressId = parseInt($($addressBookSelect).attr('data-id')); 656 657 if (defaultAddressId) { 658 $addressBookSelect.val(defaultAddressId); 659 } 660 661 $('.delivery-details-section input').on('input', function () { 662 $($addressBookSelect).val(''); 663 }); 664 665 if (shopconfig.delivery_contact_linked_inputs) { 666 $(shopconfig.delivery_contact_linked_inputs).on('change', function () { 667 $($addressBookSelect).val(''); 668 }) 669 } 670 671 webshop.delivery.addressBook.fillAddress(pseudoSelect, false); 672 this.hasInitialized = true; 673 webshop.delivery.addressBook.afterInit(); 674 }, 675 afterInit: function () { 676 return; // placeholder to chain another action after the init script 677 }, 678 beforeInit: function (data) { 679 return data; 680 }, 681 fillAddress: function (el, updateCart) { 682 if (!this.hasInitialized) { 683 return; 684 } 685 686 var updateCart = !!updateCart; 687 688 var addressId = $(el).val(), 689 addressLines = this.addresses[addressId], 690 addressKeys = ['address1', 'address2', 'address3', 'address4', 'address5', 'postcode', 'firstname', 'lastname', 'company_name', 'phone_number']; 691 $countryEl = $('#frm-address5'); 692 693 if (addressLines) { 694 if ($('#onePageDeliveryAddress').length !== 0) { 695 var formattedAddress = ''; 696 $.each(addressLines, function (key) { 697 if (addressLines[key] && (addressKeys.indexOf(key) !== -1) && addressLines[key].length > 0) formattedAddress += addressLines[key] + '\n'; 698 }); 699 700 $('#onePageDeliveryAddress').text(formattedAddress); 701 702 if (updateCart) { 703 checkout.unsetCart(); 704 } 705 706 } else if (!this.GLSDeliveryEnabled) { 707 //If the selected address has a different c
707ountry code then alert user other wise update form with selected address details 708 // address lines 709 for (var i = 1; i <= 4; i++) { 710 // if address line is not exist fill it as empty string 711 addressLines['address' + i] = addressLines['address' + i] || ''; 712 } 713 // set all provided fields 714 $.each(addressLines, function (key) { 715 if (key !== 'address5') { 716 var $input = $('#frm-' + key + ', #frm-delivery-' + key); 717 if ($input.length) { 718 var len = addressLines[key] || ''; 719 var unlen = $('<div>').html(len).text(); 720 $input.val(unlen); // convert false value to empty string 721 } 722 } 723 }); 724 725 if (shopconfig.delivery_edit_country) { 726 $('#frm-editcountry').val(addressLines['address5']); 727 } 728 729 if (shopconfig.delivery_company_name) { 730 $('#frm-company_name').val(addressLines['company_name']); 731 } 732 } else { 733 //Address line 5 is always the country 734 webshop.delivery.GLSDelivery.usedFindInAddress = false; 735 736 if (!webshop.delivery.GLSDelivery.isIreland(addressLines) && $('#frm-address5').val() === '58') { 737 alert('The Address you have chosen has a different Country. Please return to the Cart and select the correct Delivery country.'); 738 return false; 739 } 740 741 var foundCountry = $('#frm-address5').val(); 742 webshop.delivery.GLSDelivery.changeCountry(foundCountry, false, function () { 743 //See if we have an address line 4, 3 or 2 which matches any county 744 var foundCounty = webshop.delivery.GLSDelivery.findInAddress($('#frm-address4ie').find('option'), addressLines, addressLines['address3']); 745 webshop.delivery.GLSDelivery.setCountyName(foundCounty, function () { 746 var foundTownland = webshop.delivery.GLSDelivery.findInAddress($('#frm-address3ie').find('option'), addressLines, addressLines['address4']); 747 webshop.delivery.GLSDelivery.setTownName(foundTownland); 748 }) 749 }); 750 751 $('#frm-address1').val(addressLines['address1'] || ''); 752 $('#frm-address2').val(addressLines['address2'] || ''); 753 if ($('#frm-address5').val() !== '58') { 754 //set the default values 755 for (var k = 3; k < 5; k++) { 756 $('#frm-address' + k).val(addressLines['address' + k] || ''); 757 } 758 $('#frm-postcode').val(addressLines['postcode'] || ''); 759 } 760 761 if ($('#frm-company_name').length) { 762 $('#frm-company_name').val(addressLines['company_name']); 763 $('#frm-contact_name').val($.trim(addressLines['custname'].split('.')[0])); 764 } 765 } 766 } else { 767 // clear address lines fields 768 for (var i = 1; i <= 4; i++) { 769 $('#frm-address' + i).val(''); 770 } 771 772 // clear postcode field 773 $('#frm-postcode').val(''); 774 775 // clear phone field 776 $('#frm-phonenumber').val(''); 777 } 778 } 779 }, 780 GLSDelivery: { 781 //Skip over a used county value in findInAddress 782 usedFindInAddress: false, 783 /** 784 * For reading from the address book - we don't know
784what way the address might be formatted 785 * - Using this function we scan the address for any line 1-4 which might match county or townland 786 */ 787 findInAddress: function (options, addressLines, defaultValue) { 788 var foundValue = defaultValue; 789 for (var i = 5; i > 1; i--) { 790 if (webshop.delivery.GLSDelivery.usedFindInAddress == i) { 791 continue; 792 } 793 794 var suggestedValue = addressLines['address' + i].toLowerCase(); 795 if (!suggestedValue) { 796 continue; 797 } 798 799 options.each(function () { 800 //Try to find a matching county 801 var value = $(this).val(); 802 if (value.toLowerCase() === suggestedValue || $(this).text().toLowerCase() === suggestedValue) { 803 foundValue = value; 804 webshop.delivery.GLSDelivery.usedFindInAddress = i; 805 return false; 806 } 807 }); 808 } 809 810 return foundValue; 811 }, 812 isIreland: function (addressLines) { 813 var foundValue = false; 814 for (var i = 5; i > 1; i--) { 815 var suggestedValue = addressLines['address' + i].toLowerCase(); 816 //Try to find a matching county 817 if (suggestedValue === 'ireland' || suggestedValue === 'ie') { 818 foundValue = true; 819 } 820 } 821 822 return foundValue; 823 }, 824 setCountyName: function (value, callback) { 825 var $county = $('#frm-address4ie'), 826 $town = $('#frm-address3ie'), 827 $postcode = $('#frm-postcode'); 828 829 // set county force 830 if (value) { 831 $county.val(value); 832 } 833 834 $('#frm-address4').val($county.val()); 835 836 $postcode.val(''); 837 $town.html('<option>Loading...</option>'); 838 $.get('/checkout/delivery/ajax/getTowns.html', { county: $county.val() }, function (data) { 839 if (data.ret) { 840 $town.html('<option value="">Select a Townland</option>'); 841 $.each(data.towns, function (key, value) { 842 $town.append('<option value="' + value + '">' + value + '</option>'); 843 }); 844 if (callback) { 845 callback(); 846 } 847 } else { 848 $town.html('<option></option>'); 849 } 850 }); 851 }, 852 setTownName: function (value) { 853 var $county = $('#frm-address4ie'), 854 $town = $('#frm-address3ie'), 855 $postcode = $('#frm-postcode'); 856 857 // set town force 858 if (value) { 859 $town.val(value); 860 } 861 862 $('#frm-address3').val($town.val()); 863 864 $postcode.val('Loading...'); 865 $.get('/checkout/delivery/ajax/getPostcode.html', { 866 county: $county.val(), 867 town: $town.val() 868 }, function (data) { 869 if (data.ret) { 870 $postcode.val(data.postcode); 871 } else { 872 $postcode.val(''); 873 } 874 }); 875 }, 876 changeCountry: function (value, notClear, callback) { 877 value = value || false; 878 notClear = notClear || false; 879 880 var $country = $('#frm-address5'), 881 $ieCountyWrap = $('.control-group-frm-address4ie'), 882 $ieTownlandWrap = $('.control-group-frm-address3ie'), 883 $countyWrap = $('.control-group-frm-address4'), 884 $townWrap = $('.control-group-frm-address3'), 885 $postcode = $('#frm-postcode'); 886 887 if (!notClear) { 888 $ieCountyWrap.find('select').val(''); 889 $ieTownlandWrap.find('select').val(''); 890 $countyWrap.find('input').val(''); 891 $townWrap.find('input').val(''); 892 $postcode.val(''); 893 } 894 895 // set country code force 896 if (value) { 897 $country.val(value); 898 } 899 900 if ($country.val() === '58') { 901 $countyWrap.hide(); 902 $townWrap.hide(); 903 $ieCountyWrap.show(); 904 $ieTownlandWrap.show(); 905 $postcode.attr('readonly', 'readonly'); 906 907 if (!notClear) { 908 var $county = $ieCountyWrap.find('select'); 909 $county.html('<option>Loading...</option>'); 910 $.get('/checkout/delivery/ajax/getCounties.html', function (data) { 911 if (data.ret) { 912 $county.html('<option value="">Select a County</option>'); 913 $.each(data.counties, function (key, value) { 914 $county.append('<option value="' + value + '">' + value + '</option>'); 915 }); 916 if (callback) { 917 callback(); 918 } 919 } else { 920 $county.html('<option>Error</option>'); 921 } 922 }); 923 } 924 } else { 925 $ieCountyWrap.hide(); 926 $ieTownlandWrap.hide(); 927 $countyWrap.show(); 928 $townWrap.show(); 929 $postcode.attr('readonly', false); 930 } 931 } 932 }, 933 ePostcode: { 934 requestLock: false, 935 init: function () { 936 $(window).on('click', function (event) { 937 $('.epostcode-addresses-dropdown').hide(); 938 }); 939 }, 940 showAddresses: function (el, type) { 941 942 var _this = this, 943 $el = $(el), 944 postcode = $el.closest('.controls').find('input').val(), 945 buttonText = $el.text(); 946 947 if (_this.requestLock) { 948 return; 949 } 950 951 $el.text('Loading...'); 952 _this.requestLock = true; 953 $.get('/checkout/delivery/epostcode/getAddressesByPostcode.html', { postcode: postcode }, function (data) { 954 $el.text(buttonText); 955 _this.requestLock = false;
956 957 if (data.ret) { 958 var $ul = $('<ul class="dropdown-menu epostcode-addresses-dropdown">') 959 $.each(data.addresses, function (key, value) { 960 $ul.append('<li><a href="javascript:" onclick="webshop.delivery.ePostcode.fillAddress(\'' + key + '\', \'' + type + '\')">' + value + '</a></li>'); 961 }); 962 963 $el.closest('.controls').append($ul); 964 $ul.show(); 965 } else { 966 alert('Please enter a valid postcode'); 967 } 968 }); 969 }, 970 fillAddress: function (addressNum, type) { 971 $.get('/checkout/delivery/epostcode/getAddress.html', { addressNum: addressNum }, function (data) { 972 if (data.ret) { 973 974 if (type === 'quote') { 975 var prefix = '#quote-company-'; 976 } 977 else { 978 var prefix = '#frm-'; 979 } 980 981 // address lines 982 if (type === 'billing' || type === 'card') { 983 var fieldName = 'card'; 984 } else if (type === 'signup') { 985 var fieldName = 'billing'; 986 } else { 987 var fieldName = 'address'; 988 } 989 990 for (var i = 1; i <= 4; i++) { 991 $(prefix + fieldName + i).val(data.address['AddressLine' + i]); 992 } 993 $(prefix + 'firstname').val(data.address['firstname']); 994 if ($(prefix + 'custname').is("*")) { 995 $(prefix + 'custname').val(data.address['firstname']); 996 } 997 998 // postcode 999 var postcodeInputId = ((type === 'billing') ? prefix + 'billingpostcode' : prefix + 'postcode'); 1000 $(postcodeInputId).val(data.address['PostCode']); 1001 1002 1003 // country 1004 if (type === 'billing') { 1005 $(prefix + 'billing5').val('GB'); 1006 } 1007 } 1008 }); 1009 } 1010 }, 1011 onDeliveryDueDateChange: function (silent) { 1012 var selectedValue = document.querySelector('input[name="delivery-due-date"]:checked').value; 1013 if (selectedValue === 'custom_due_date') { 1014 $('.delivery-due-date-input').removeClass('delivery-due-date-input-hidden'); 1015 } else { 1016 $('.delivery-due-date-input').addClass('delivery-due-date-input-hidden'); 1017 } 1018 1019 if (!silent) { 1020 checkout.unsetCart(); 1021 } 1022 } 1023 }, 1024 favourites: { 1025 requestLock: false, 1026 updateFavourites: function (el) { 1027 var _this = this, 1028 $btn = $(el), 1029 productId = $btn.attr('data-product-id'), 1030 optionId = $btn.attr('data-option-id'), 1031 groupId = $btn.attr('data-group-id'), 1032 favouriteId = $btn.attr('data-favourite-id'), 1033 requestUrl; 1034 1035 if (_this.requestLock) { 1036 return; 1037 } 1038 1039 if (!$btn.is('.is-active')) { 1040 requestUrl = '/account/favourites/ajax/addToFavourites.html'; 1041 } else if (!$('.table-favourites').is('*')) { 1042 //Don't remove it on the fly - redirect to the favourites page 1043 window.location = '/account/favourites.html'; 1044 return; 1045 } else { 1046 //We're likely on the favourites page so we'll on the fly toggle the status of the button 1047 requestUrl = '/account/favourites/ajax/removeFromFavourites.html'; 1048 } 1049 1050 _this.requestLock = true; 1051 var payload = { 1052 productId: productId, 1053 optionId: optionId, 1054 groupId: groupId, 1055 favouriteId: favouriteId 1056 }; 1057 if (window.shopconfig.favouritesGroups) { 1058 payload = Object.assign(payload, {}); 1059 } 1060 $.post(requestUrl, payload, function (data) { 1061 if (data.ret) { 1062 _this.setButtonState($btn, !$btn.is('.is-active')); 1063 _this.setAriaForFavourites($btn, $btn.is('.is-active')); 1064 if (!$btn.is('.is-active')) { 1065 _this.removeRow(productId); 1066 } 1067 } 1068 _this.requestLock = false;
1069 }); 1070 }, 1071 setAriaForFavourites($btn, isNowFavourite) { 1072 var groupsEnabled = window.shopconfig && window.shopconfig.favouritesGroups; 1073 1074 // 1. Update button ARIA state 1075 $btn.attr('aria-pressed', isNowFavourite); 1076 1077 if (groupsEnabled) { 1078 $btn.attr( 1079 'aria-label', 1080 isNowFavourite ? $btn.data('aria-view') : $btn.data('aria-add') 1081 ); 1082 } else { 1083 $btn.attr( 1084 'aria-label', 1085 isNowFavourite ? $btn.data('aria-remove') : $btn.data('aria-add') 1086 ); 1087 } 1088 1089 // 2. Speak the result 1090 var addedMsg = $btn.data('aria-added'); 1091 if (groupsEnabled && isNowFavourite) { 1092 webshop.accessibility.screenReaderMessage(addedMsg); 1093 } else { 1094 var removedMsg = $btn.data('aria-removed') 1095 webshop.accessibility.screenReaderMessage( 1096 (isNowFavourite ? addedMsg : removedMsg) 1097 ); 1098 } 1099 }, 1100 checkButtonState: function (el) { 1101 var _this = this, 1102 $btn = $(el), 1103 productId = $btn.attr('data-product-id'), 1104 optionId = $btn.attr('data-option-id'); 1105 1106 $.post('/account/favourites/ajax/getFavourite.html', { 1107 productId: productId, 1108 optionId: optionId 1109 }, function (data) { 1110 if (data.ret && (optionId == $btn.attr('data-option-id'))) { 1111 _this.setButtonState($btn, data.isFavourite); 1112 } 1113 }); 1114 }, 1115 setButtonState: function (el, setActive) { 1116 var $btn = $(el), 1117 isBtnActive = $btn.is('.is-active'); 1118
1119 setActive = setActive || false; 1120 1121 if (setActive && !isBtnActive) { 1122 $btn.addClass('is-active'); 1123 $btn.html($btn.data('text-remove')); 1124 } else if (!setActive && isBtnActive) { 1125 $btn.removeClass('is-active'); 1126 $btn.html($btn.data('text-add')); 1127 } 1128 }, 1129 removeRow: function (productId) { 1130 $row = $('[data-product-id=' + productId + ']'); 1131 if ($row.is('*')) { 1132 $row.remove(); 1133 } 1134 }, 1135 updateFavouritesGroupItems: function (el) { 1136 var _this = this, 1137 $btn = $(el), 1138 productId = $btn.attr('data-product-id'), 1139 optionId = $btn.attr('data-option-id'), 1140 groupId = $btn.attr('data-group-id'), 1141 favouriteId = $btn.attr('data-favourite-id'), 1142 groupItemId = $btn.attr('data-group-item-id'), 1143 requestUrl; 1144 1145 if (_this.requestLock) { 1146 return; 1147 } 1148 1149 if (!$btn.is('.is-active')) { 1150 requestUrl = '/account/favourites/ajax/addToFavourites.html'; 1151 } else if (!$('.table-favourites').is('*')) { 1152 //Don't remove it on the fly - redirect to the favourites page 1153 window.location = '/account/favourites.html'; 1154 return; 1155 } else { 1156 //We're likely on the favourites page so we'll on the fly toggle the status of the button 1157 requestUrl = '/account/favourites/ajax/removeFromFavourites.html'; 1158 } 1159 1160 _this.requestLock = true; 1161 var payload = { 1162 productId: productId, 1163 optionId: optionId, 1164 groupId: groupId, 1165 favouriteId: favouriteId, 1166 groupItemId: groupItemId, 1167 }; 1168 if (window.shopconfig.favouritesGroups) { 1169 payload = Object.assign(payload, {}); 1170 } 1171 $.post(requestUrl, payload, function (data) { 1172 if (data.ret) { 1173 _this.setButtonState($btn, !$btn.is('.is-active')); 1174 if (!$btn.is('.is-active') && groupItemId) { 1175 _this.removeRowItem(groupItemId); 1176 } 1177 } 1178 _this.requestLock = false; 1179 }); 1180 }, 1181 checkButtonState: function (el) { 1182 var _this = this, 1183 $btn = $(el), 1184 productId = $btn.attr('data-product-id'), 1185 optionId = $btn.attr('data-option-id'); 1186 1187 $.post('/account/favourites/ajax/getFavourite.html', { 1188 productId: productId, 1189 optionId: optionId 1190 }, function (data) { 1191 if (data.ret && (optionId == $btn.attr('data-option-id'))) { 1192 _this.setButtonState($btn, data.isFavourite); 1193 } 1194 }); 1195 }, 1196 setButtonState: function (el, setActive) { 1197 var $btn = $(el), 1198 isBtnActive = $btn.is('.is-active'); 1199
1200 setActive = setActive || false; 1201 1202 if (setActive && !isBtnActive) { 1203 $btn.addClass('is-active'); 1204 $btn.html($btn.data('text-remove')); 1205 } else if (!setActive && isBtnActive) { 1206 $btn.removeClass('is-active'); 1207 $btn.html($btn.data('text-add')); 1208 } 1209 }, 1210 removeRowItem: function (groupItemId) { 1211 $row = $('[data-group-item-id=' + groupItemId + ']'); 1212 if ($row.is('*')) { 1213 $row.remove(); 1214 } 1215 } 1216 }, 1217 cookies: { 1218 accept: function () { 1219 webshop.cookies.create('acceptCookies', true, 365); 1220 $('#cookie-notice').hide(); 1221 }, 1222 create: function (name, value, days) { 1223 var expires = ""; 1224 if (days) { 1225 var date = new Date(); 1226 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 1227 expires = "; expires=" + date.toUTCString(); 1228 } 1229 document.cookie = name + "=" + value + expires + "; path=/"; 1230 }, 1231 read: function (name) { 1232 var nameEQ = name + "="; 1233 var ca = document.cookie.split(';'); 1234 1235 for (var i = 0; i < ca.length; i++) { 1236 var c = ca[i]; 1237 while (c.charAt(0) === ' ') c = c.substring(1, c.length); 1238 1239 if (c.indexOf(nameEQ) === 0) { 1240 return c.substring(nameEQ.length, c.length); 1241 } 1242 } 1243 1244 return null; 1245 }, 1246 delete: function (name) { 1247 webshop.cookies.create(name, "", -1); 1248 } 1249 }, 1250 newEvent: function (eventName) { 1251 var event; 1252 if (typeof (Event) === 'function') { 1253 event = new Event(eventName); 1254 } else { 1255 event = document.createEvent('Event'); 1256 event.initEvent(eventName, true, true); 1257 } 1258 return event; 1259 }, 1260 search: { 1261 searchEvents: [], 1262 1263 init: function () { 1264 if ($('.ws-filter__title').is('*')) { 1265 // there are filter options on the page, lets get going. 1266 webshop.search.bindEvents(); 1267 } 1268 }, 1269 /** 1270 * Take the click-event from the checkboxes on search the build up the new search results. 1271 */ 1272 activeFilterTabs: {}, 1273 bindEvents: function () { 1274 this.searchEvents.filterSearchComplete = webshop.newEvent("filterSearchComplete"); 1275 1276 $('.sidebar').on('click', '.ws-filter__title', function () { 1277 $(this).parent().toggleClass('is-active'); 1278 1279 var selected = $(this).parent().hasClass('is-active'); 1280 var tabID = $(this).parent().data('tab-id'); 1281 var accordionCookie = webshop.cookies.read('ws_filter') || false; 1282 var accordionActive = {}; 1283 if (!accordionCookie) { 1284 accordionActive[tabID] = selected; 1285 webshop.cookies.create('ws_filter', JSON.stringify(accordionActive)); 1286 } else { 1287 accordionActive = JSON.parse(accordionCookie); 1288 accordionActive[tabID] = selected; 1289 webshop.cookies.create('ws_filter', JSON.stringify(accordionActive), 365); 1290 } 1291 webshop.search.activeFilterTabs = accordionActive; 1292 1293 }); 1294 1295 $('.sidebar').on('click', '.ws-filter__checkbox', function () { 1296 // ensure the starting url is set 1297 if (!webshop.search._tmpUrl) { 1298 webshop.search.setStartingURL(); 1299 } 1300 1301 $(this).closest('.ws-filter__item').addClass('is-loading'); 1302 1303 if ($(this).is(':checked')) { 1304 // add to the query 1305 webshop.search.addFilter($(this).val()); 1306 } else { 1307 // remove the query 1308 webshop.search.removeFilter($(this).val()); 1309 } 1310 1311 $(this).replaceWith('<img src="' + shopconfig.search_loading + '" alt="Loading..." />'); 1312 1313 window.history.replaceState(null, null, webshop.search._tmpUrl); 1314 1315 if (webshop.search._tmpAjaxQuery) { 1316 // there is a query running, cancel it 1317 webshop.search._tmpAjaxQuery.abort(); 1318 } 1319 1320 // todo: run a new ajax query 1321 webshop.search._tmpAjaxQuery = $.ajax({ 1322 'url': webshop.search._tmpUrl, 1323 'success': function (data) { 1324 $('.sidebar').html($(data).find('.sidebar').html()); 1325 const mainH1 = $('#mainh1'); 1326 const mainContentArea = $('#main-content-area'); 1327 mainContentArea.html($(data).find('#main-content-area').html()); 1328 if (mainH1 && mainH1.is('*')) { 1329 // Set the title (brand/category) back. 1330 mainH1.prependTo($('#main-content-area')); 1331 } 1332 1333 window.history.pushState(null, null, webshop.search._tmpUrl); 1334 1335 var accordionCookie = webshop.cookies.read('ws_filter') || false; 1336 if (accordionCookie) { 1337 webshop.search.activeFilterTabs = JSON.parse(accordionCookie); 1338 } 1339 for (var i in webshop.search.activeFilterTabs) { 1340 if (webshop.search.activeFilterTabs[i] === false) { 1341 continue; 1342 } 1343 1344 var $tab = $('div [data-tab-id="' + i + '"]'); 1345 $tab.toggleClass('is-active'); 1346 } 1347 1348 if ($(window).width() < 768) { 1349 var $sortingBtn = $('#sorting-change').detach(); 1350 $('.col-sorting-options').append($sortingBtn); 1351 $('#sorting-change .dropdown-toggle').prepend($('#sorting-change__label').text()); 1352 } 1353 1354 // Update prices if B2B 1355 intact_products(); 1356 var observer = lozad(); 1357 observer.observe(); 1358 1359 webshop.search.searchComplete(); 1360 } 1361 1362 1363 }); 1364 }); 1365 1366 if (shopconfig.categoriesFullWidthLayout) { 1367 $(document).on('click', '.ws-filter__dropdown', function (e) { 1368 e.stopPropagation();
1368 // Prevent dropdown from closing on internal click. 1369 }); 1370 1371 // Ensure other click events on child elements are allowed to propagate 1372 $(document).on('click', '.search-criteria-container .ws-filter__dropdown .ws-filter__checkbox', function (e) { 1373 // ensure the starting url is set 1374 if (!webshop.search._tmpUrl) { 1375 webshop.search.setStartingURL(); 1376 } 1377 1378 $(this).closest('.ws-filter__item').addClass('is-loading'); 1379 1380 if ($(this).is(':checked')) { 1381 // add to the query 1382 webshop.search.addFilter($(this).val()); 1383 } else { 1384 // remove the query 1385 webshop.search.removeFilter($(this).val()); 1386 } 1387 1388 $(this).replaceWith('<img src="' + shopconfig.search_loading + '" alt="Loading..." />'); 1389 1390 window.history.replaceState(null, null, webshop.search._tmpUrl); 1391 1392 if (webshop.search._tmpAjaxQuery) { 1393 // there is a query running, cancel it 1394 webshop.search._tmpAjaxQuery.abort(); 1395 } 1396 1397 // todo: run a new ajax query 1398 webshop.search._tmpAjaxQuery = $.ajax({ 1399 'url': webshop.search._tmpUrl, 1400 'success': function (data) { 1401 $('#main-content-area').html($(data).find('#main-content-area').html()); 1402 1403 initListView(); 1404 1405 window.history.pushState(null, null, webshop.search._tmpUrl); 1406 1407 if ($(window).width() < 768) { 1408 var $sortingBtn = $('#sorting-change').detach(); 1409 $('.col-sorting-options').append($sortingBtn); 1410 $('#sorting-change .dropdown-toggle').prepend($('#sorting-change__label').text()); 1411 } 1412 1413 // Update prices if B2B 1414 intact_products(); 1415 var observer = lozad(); 1416 observer.observe(); 1417 1418 webshop.search.searchComplete(); 1419 } 1420 }); 1421 }); 1422 } 1423 }, 1424 searchComplete: function () { 1425 document.dispatchEvent(this.searchEvents.filterSearchComplete); 1426 }, 1427 // holding place for the query URL with all search alterations before it returns 1428 _tmpUrl: false, 1429 // holding for ajax query currently running search. 1430 _tmpAjaxQuery: false, 1431 addFilter: function (filter) { 1432 this._tmpUrl += '&' + filter; 1433 }, 1434 removeFilter: function (filter) { 1435 var filterClean = ('&' + filter).replace(/[-[\]{}()*+?.\\^$|#\s]/g, "\\$&"); 1436 var replace = new RegExp(filterClean, 'g'); 1437 this._tmpUrl = this._tmpUrl.replace(replace, ''); 1438 }, 1439 setStartingURL: function () { 1440 this._tmpUrl = window.location.href.replace(window.location.origin, ''); 1441 if (this._tmpUrl.indexOf('?') == -1) { 1442 this._tmpUrl += '?'; 1443 } 1444 1445 // clean urls to have proper non-encoded brackets 1446 var findStr = new RegExp('%5B(\\d+)%5D', 'g'); 1447 var findPageNum = new RegExp('[?&]page=(\\d+)', 'g'); 1448 var pageNumDelimeter = this._tmpUrl.includes('?page=') ? '?' : '&'; 1449 1450 this._tmpUrl = this._tmpUrl.replace(findStr, '[]').replace(/%2C/g, ',').replace(findPageNum, pageNumDelimeter + 'page=1'); 1451 } 1452 }, 1453 newsletter: { 1454 //Signup form, send the email and hide the area. 1455 signup: function (email, area) { 1456 var mail = $('#' + email).val(); 1457 var name = $('#news-letter__input-name').val() ? $('#news-letter__input-name').val() : null; 1458 var lname = $('#news-letter__input-lname').val() ? $('#news-letter__input-lname').val() : null; 1459 $(area).hide(); 1460 $(area).next().show(); 1461 $.ajax({ 1462 url: '/system/newsletter.html', 1463 data: { mail, name, lname } 1464 }); 1465 return false;
1466 } 1467 }, 1468 utils: { 1469 isTouchDevice: function () { 1470 return 'ontouchstart' in window // works on most browsers 1471 || navigator.maxTouchPoints; 1472 }, 1473 dateToYmd: function (date) { 1474 var parsedDate = (new Date(Date.parse(date))); 1475 var month = (parsedDate.getMonth() + 1).toString().length === 1 ? '0' + (parsedDate.getMonth() + 1) : (parsedDate.getMonth() + 1); 1476 var day = parsedDate.getDate().toString().length === 1 ? '0' + parsedDate.getDate() : parsedDate.getDate(); 1477 return (parsedDate.getFullYear() + '-' + month + '-' + day); 1478 } 1479 }, 1480 customer: { 1481 type: '', // set in footer/js/main 1482 types: { // also update Core_Table_Customer_Account 1483 b2b: 'B2B', 1484 b2c: 'B2C' 1485 } 1486 }, 1487 categories: { 1488 set: [], 1489 currentCategory: false, 1490 promise: false, 1491 requestListHtml: function (currentId) { 1492 webshop.categories.promise = new Promise(function (resolve, reject) { 1493 var listingHtml = ''; 1494 var title = ''; 1495 for (var c in webshop.categories.set['categories']) { 1496 var category = webshop.categories.set['categories'][c]; 1497 if (category.parentId === webshop.categories.currentCategory) { 1498 var currentIdClass = ''; 1499 if (currentId && currentId === category.id) { 1500 currentIdClass = 'current-category'; 1501 } 1502 var expandHtml = (category.hasChildren ? '<td style="width: 30px;"><a class="category-expand" href="javascript:;" onclick="webshop.categories.list(\'' + category.id + '\');"><i class="fa fa-angle-right" aria-hidden="true"></i></a></td>' : ''); 1503 listingHtml += '<tr>' 1504 + '<td><a class="category-link ' + currentIdClass + '" href="' + category.link + '">' + category.title + '</a></td>' 1505 + expandHtml 1506 + '</tr>'; 1507 1508 } 1509 1510 if (category.id === webshop.categories.currentCategory) { 1511 if (!category.hasChildren) { 1512 webshop.categories.list(webshop.categories.findParent(webshop.categories.currentCategory), category.id); 1513 reject(); 1514 } 1515 title = category.title; 1516 } 1517 } 1518 resolve({ html: listingHtml, title: title }); 1519 }); 1520 1521 webshop.categories.promise.then(function (obj) { 1522 document.getElementsByClassName('sidebar-categories-children')[0].innerHTML = '<tbody>' + obj.html + '</tbody>'; 1523 document.getElementsByClassName('sidebar-right-title')[0].innerHTML = obj.title; 1524 1525 if (webshop.categories.set['roots'].indexOf(webshop.categories.currentCategory) !== -1) { 1526 document.getElementById('btn-category-back').classList.add('btn-back-hidden'); 1527 } else { 1528 document.getElementById('btn-category-back').classList.remove('btn-back-hidden'); 1529 } 1530 1531 document.getElementsByClassName('loading-overlay')[0].classList.add('loading-hidden'); 1532 }) 1533 }, 1534 list: function (id, currentId) { 1535 var id = id ? id : (document.getElementsByClassName('sidebar-categories-children')[0] ? document.getElementsByClassName('sidebar-categories-children')[0].getAttribute('data-category-root') : null); 1536 if (id == null) { 1537 return; 1538 } 1539 var currentId = currentId || false; 1540 document.getElementsByClassName('sidebar-categories-children')[0].innerHTML = '<tbody>'; 1541 document.getElementsByClassName('loading-overlay')[0].classList.remove('loading-hidden'); 1542 webshop.categories.currentCategory = id; 1543 webshop.categories.requestListHtml(currentId); 1544 }, 1545 findParent: function (id) { 1546 for (var c in webshop.categories.set['categories']) { 1547 var category = webshop.categories.set['categories'][c]; 1548 if (category.id === webshop.categories.currentCategory) { 1549 return category.parentId; 1550 } 1551 } 1552 1553 return false;
1554 }, 1555 showSidebar: function () { 1556 document.getElementsByClassName('sidebar-right')[0].classList.remove('sidebar-right-hidden'); 1557 document.getElementsByTagName('body')[0].style.overflow = 'hidden'; 1558 webshop.categories.list(); 1559 1560 var $sidebarRight = $('.sidebar-right'); 1561 if ($sidebarRight.is('*')) { 1562 $sidebarRight.stop().animate({ 1563 right: 0 1564 }, 300); 1565 } 1566 }, 1567 hideSidebar: function () { 1568 document.getElementsByTagName('body')[0].style.overflow = 'visible'; 1569 1570 var $sidebarRight = $('.sidebar-right'); 1571 if ($sidebarRight.is('*')) { 1572 $sidebarRight.stop().animate({ 1573 right: -$(window).width() 1574 }, 300, function () { 1575 document.getElementsByClassName('sidebar-right')[0].classList.add('sidebar-right-hidden'); 1576 }); 1577 } 1578 } 1579 }, 1580 mobile: { 1581 showSearch: function (btn, e) { 1582 var e = e || window.event; 1583 e.preventDefault(); 1584 e.stopPropagation(); 1585 if ($(btn).hasClass('active')) { 1586 webshop.mobile.hideSearch(btn); 1587 } else { 1588 if ($('.nav-collapse.in.collapse').is('*')) { 1589 $('#mobile-menu').trigger('click'); 1590 } 1591 mobilesearch.stop().offset({ 'left': -windowwidth }).css({ 'display': 'block' }).animate({ 1592 left: 0 1593 }); 1594 mobilesearch.addClass('is-active'); 1595 $(btn).addClass('active'); 1596 1597 $('body').css({ 'overflow': 'hidden' }); 1598 } 1599 }, 1600 hideSearch: function (btn) { 1601 mobilesearch.stop().animate({ 1602 left: (windowwidth * -1) 1603 }, { 1604 complete: function () { 1605 $(btn).removeClass('is-active'); 1606 $(btn).removeClass('active'); 1607 } 1608 }); 1609 $('#mobile-search').removeClass('active'); 1610 $('html, body').css({ 'overflow': 'visible' }); 1611 } 1612 }, 1613 drawerMenu: { 1614 /** 1615 * Add event listener to load new menu categories 1616 * 1617 * @param {String} menuSelector - Must be an element selector e.g. '#mobileMenu' or '.mobileMenu' 1618 * @param {Object} urlParamsObj - An object containing a key => value pair of the URL parameters we wish to send 1619 */ 1620 initCategoryLoader: function (menuSelector, urlParamsObj = {}) { 1621 var categoryAjax = { 1622 counter: {} 1623 }; 1624 //get top level 1625 $(`${menuSelector} .mobile-nav`).on('click', '.mobile-nav__item', function () { 1626 var categoryID = $(this).data('category-id'); 1627 if (!categoryAjax.counter[categoryID]) { 1628 if ($(this).attr('name') == 'categoryMenuParent') { 1629 // Add default 'category' parameter 1630 urlParamsObj['category'] = categoryID; 1631 $urlParams = new URLSearchParams(urlParamsObj).toString(); 1632 1633 categoryAjax.counter[categoryID] = true; 1634 $target = $(this).find('> ul'); 1635 $spinner = $('<img style="margin-top: 20%; margin-left: 48%;" src="/common/images/loading.gif"/>'); 1636 $.ajax({ 1637 'url': '/system/service/mobile/menu.html?' + $urlParams, 1638 'dataType': 'json', 1639 'beforeSend': function () { 1640 $target.append($spinner); 1641 }, 1642 'success': function (result) { 1643 $spinner.remove(); 1644 $target.append(result.menu); 1645 } 1646 }); 1647 } 1648 } 1649 }); 1650 }, 1651 /** 1652 * Add event listener to toggle menu on and off 1653 * 1654 * @param {String} menuSelector - Must be an element selector e.g. '#mobileMenu' or '.mobileMenu' 1655 */ 1656 initMenuButton: function (menuSelector) { 1657 $(`#mobile-menu, ${menuSelector} #closeMenu`).on('click', function (e) { 1658 e.preventDefault(); 1659 $(this).toggleClass('is-active'); 1660 $(menuSelector).toggleClass('is-active'); 1661 }); 1662 }, 1663 1664 /** 1665 * Add event listener to toggle the menu sub-menus 1666 * 1667 * @param {String} menuSelector - Must be an element selector e.g. '#mobileMenu' or '.mobileMenu' 1668 */ 1669 initSubMenuToggle: function (menuSelector) { 1670 $(`${menuSelector} .mobile-nav`).on('click', '.mobile-nav__parent', function (e) { 1671 e.preventDefault(); 1672 $(this).toggleClass('active'); 1673 $(this).parent().siblings().toggle(); 1674 $(this).parent().find('.mobile-nav__sub-menu:first').toggle(); 1675 }); 1676 } 1677 } 1678}; 1679 1680webshop.tooltip = { 1681 usingPopover: false, 1682 create: function (element, text, placement, trigger) { 1683 $(element).tooltip('destroy'); 1684 placement = placement || 'bottom'; 1685 trigger = trigger || 'manual'; 1686 1687 var options = { 1688 'title': text, 1689 'placement': placement, 1690 'trigger': trigger 1691 }; 1692 1693 return element.tooltip(options); 1694 }, 1695 show: function (element, text, placement, trigger) { 1696 var tooltip_instance = this.create(element, text, placement, trigger); 1697 tooltip_instance.tooltip('show'); 1698 }, 1699 timeout: function (element, milliseconds) { 1700 setTimeout(function () { 1701 element.tooltip('hide'); 1702 }, milliseconds); 1703 } 1704}; 1705 1706webshop.popover = { 1707 create: function (element, text, html, placement, trigger) { 1708 $(element).popover('destroy'); 1709 1710 placement = placement || 'bottom'; 1711 trigger = trigger || 'manual'; 1712 1713 var options = { 1714 'title': text, 1715 'content': html, 1716 'placement': placement, 1717 'trigger': trigger, 1718 'html': true, 1719 }; 1720 1721 return element.popover(options); 1722 }, 1723 show: function (element, text, html, placement, trigger) { 1724 var popover_instance = this.create(element, text, html, placement, trigger); 1725 popover_instance.popover('show'); 1726 }, 1727 timeout: function (element, milliseconds) { 1728 setTimeout(function () { 1729 element.popover('hide'); 1730 }, milliseconds); 1731 } 1732}; 1733 1734webshop.accessibility = { 1735 screenReaderMessage: function (message) { 1736 const el = document.createElement("div"); 1737 const id = "speak-" + Date.now(); 1738 el.setAttribute("id", id); 1739 el.setAttribute("aria-live", "polite"); 1740 el.classList.add("visually-hidden"); 1741 document.body.appendChild(el); 1742 1743 window.setTimeout(function () { 1744 document.getElementById(id).innerHTML = message; 1745 }, 100); 1746 1747 window.setTimeout(function () { 1748 document.body.removeChild(document.getElementById(id)); 1749 }, 1000); 1750 }, 1751} 1752 1753$(document).ready(function () { 1754 $('.rusure').on('click', function () { 1755 var confirmMessage = $(this).data('confirmmessage'); 1756 var extraMessage = $(this).data('rusure'); 1757 1758 if (!confirmMessage) { 1759 confirmMessage = 'Are you sure you wish to continue?' 1760 } 1761 1762 extraConfirm = true; 1763 if (extraMessage && extraMessage != '') { 1764 extraConfirm = confirm(extraMessage); 1765 } 1766 1767 return extraConfirm ? confirm(confirmMessage) : false;
1768 }); 1769 1770 $('.logoutbtn').on('click', function () { 1771 return confirm(webshop._.webshop.logout.confirmation + "\n" + webshop._.webshop.logout.notification); 1772 }); 1773 1774 // these must be global events as we sometimes dynamically add add to cart links ... 1775 $('body').on('keyup', '.addmultiinput', webshop.cart.generateAddToCartLink); 1776 $('body').on('change', '.addmultiinput', webshop.cart.generateAddToCartLink); 1777 $('.addtocart-ref').on('keyup', webshop.cart.generateAddToCartLink); 1778 $('.addtocart-ref').on('change', webshop.cart.generateAddToCartLink); 1779 $('input[name="batchMatch"]').on('change', webshop.cart.generateAddToCartLink); 1780 1781 if (webshop.utils.isTouchDevice()) { 1782 $('.js-force-hover').one('click', function (event) { 1783 event.preventDefault(); 1784 }); 1785 } 1786 1787 var classes = { 1788 grid: 'product-grid products--grid', 1789 list: 'product-list products--list', 1790 detailed: 'product-detailed products--detailed' 1791 }; 1792 1793 if ($('#products-results').is('*')) { 1794 //a.btn-inverse i.icon-white 1795 $(document).on('click', '.btn-group.view a', function () { 1796 $('.btn-group.view a.btn-inverse').removeClass('btn-inverse'); 1797 $('.btn-group.view a i.icon-white').removeClass('icon-white'); 1798 $(this).addClass('btn-inverse'); 1799 $(this).find('i').addClass('icon-white'); 1800 1801 var setclass; 1802 var setcookie; 1803 if ($(this).attr('title') == 'Grid view') { 1804 setclass = 'grid'; 1805 setcookie = 'product-grid'; 1806 } 1807 if ($(this).attr('title') == 'Detailed view') { 1808 setclass = 'detailed'; 1809 setcookie = 'product-detailed'; 1810 } 1811 if ($(this).attr('title') == 'List view') { 1812 setclass = 'list'; 1813 setcookie = 'product-detailed'; 1814 } 1815 1816 $('#products-results').removeClass('product-list products--list'); 1817 $('#products-results').removeClass('product-detailed products--detailed'); 1818 $('#products-results').removeClass('product-grid products--grid');
1819 $('#products-results').addClass(classes[setclass]); 1820 1821 if ($('#products-results-sku').is('*')) { 1822 $('#products-results-sku').removeClass('product-list products--list'); 1823 $('#products-results-sku').removeClass('product-detailed products--detailed'); 1824 $('#products-results-sku').removeClass('product-grid products--grid'); 1825 $('#products-results').addClass(classes[setclass]); 1826 } 1827 if ($('#products-results-name').is('*')) { 1828 $('#products-results-name').removeClass('product-list products--list'); 1829 $('#products-results-name').removeClass('product-detailed products--detailed'); 1830 $('#products-results-name').removeClass('product-grid products--grid'); 1831 $('#products-results').addClass(classes[setclass]); 1832 } 1833 1834 $.cookie('product_grid', setclass); 1835 1836 return false; 1837 }); 1838 1839 if ($.cookie('product_grid')) { 1840 $('a.product-' + $.cookie('product_grid')).trigger('click'); 1841 } else if (!$.cookie('product_grid') && !$('#products-results').is('[class^=product]')) { 1842 $('#products-results').addClass(shopconfig.defaultlistview); 1843 } 1844 } 1845 1846 $('.b2c-menu > li').on({ 1847 mouseenter: function () { 1848 $(this).addClass('active'); 1849 }, 1850 mouseleave: function () { 1851 $(this).removeClass('active'); 1852 } 1853 }); 1854 1855 $('body').on('click', '.addajax', webshop.cart.ajaxAddToCartEvents.onclick); 1856 1857 var filters = $('.ws-filter'); 1858 if ((filters.length) == 1) { 1859 $(filters[0]).addClass('is-active'); 1860 } 1861 1862 // disable any buttons which should not be pressed more than once 1863 $('body').on('submit', 'form', function () { 1864 $(this).find('.btn-disable-onclick').attr('disabled', true).addClass('disabled'); 1865 return true; 1866 }); 1867 1868 var id = document.getElementsByClassName('sidebar-categories-children')[0] ? document.getElementsByClassName('sidebar-categories-children')[0].getAttribute('data-category-root') : null; 1869 if (id != null) { 1870 $.get('/system/categories.html', {}, function (data) { 1871 webshop.categories.set = JSON.parse(data); 1872 webshop.categories.list(); 1873 }); 1874 } 1875 1876 1877 if (document.getElementsByClassName('delivery-due-date').length) { 1878 $('.delivery-due-date').each(function () { 1879 this.addEventListener('change', webshop.delivery.onDeliveryDueDateChange); 1880 }); 1881 1882 if (document.querySelector('input[name="delivery-due-date"]:checked').value === 'custom_due_date') { 1883 $('.delivery-due-date-input').removeClass('delivery-due-date-input-hidden'); 1884 } 1885 1886 var calendarOptions = { 1887 field: document.getElementsByClassName('delivery-due-date-input')[0], 1888 firstDay: 1, 1889 disableWeekends: false, 1890 minDate: new Date(), 1891 maxDate: new Date(2020, 12, 31), 1892 yearRange: [2000, 2020], 1893 disableDayFn: function (date) { 1894 var parsedDate = (new Date(Date.parse(date))); 1895 var month = (parsedDate.getMonth() + 1).toString().length === 1 ? '0' + (parsedDate.getMonth() + 1) : (parsedDate.getMonth() + 1); 1896 var day = parsedDate.getDate().toString().length === 1 ? '0' + parsedDate.getDate() : parsedDate.getDate(); 1897 var formattedDate = parsedDate.getFullYear() + '-' + month + '-' + day; 1898 var formattedDateAbstract = 'YYYY' + '-' + month + '-' + day; 1899 1900 var weekdays = [ 1901 'Sunday', 1902 'Monday', 1903 'Tuesday', 1904 'Wednesday', 1905 'Thursday', 1906 'Friday', 1907 'Saturday' 1908 ]; 1909 var dayString = weekdays[date.getDay()]; 1910 var predefinedCheck = (webshop.delivery.predefinedDeliveryDays.indexOf(dayString) === -1); 1911 1912 return ( 1913 predefinedCheck || 1914 webshop.delivery.excludedDueDates.indexOf(formattedDate) !== -1) || 1915 (webshop.delivery.excludedDueDates.indexOf(formattedDateAbstract) !== -1 1916 ); 1917 } 1918 }; 1919 1920 var picker = new Pikaday(Object.assign(calendarOptions, webshop.delivery.calendarOptionsExtended)); 1921 } 1922 1923 webshop.search.init(); 1924}); 1925 1926function refreshCart() { 1927 $.ajax({ 1928 'url': '/checkout.html?refreshCart=true&route=ajax', 1929 'dataType': 'json', 1930 'success': function (data) { 1931 $('.gross-total').html(data.formatted.gross); 1932 $('.items-total').html(data.count + ' Items'); 1933 } 1934 }); 1935} 1936 1937$(document).on('change', '.btn-file :file', function () { 1938 var input = $(this), 1939 numFiles = input.get(0).files ? input.get(0).files.length : 1, 1940 label = input.val().replace(/\\/g, '/').replace(/.*\//, ''); 1941 input.trigger('fileselect', [numFiles, label]); 1942}); 1943 1944$(document).ready(function () { 1945 $('.btn-file :file').on('fileselect', function (event, numFiles, label) { 1946 $('#filename').html(': ' + label); 1947 }); 1948 1949 var accordionCookie = webshop.cookies.read('ws_filter') || false; 1950 if (accordionCookie) { 1951 webshop.search.activeFilterTabs = JSON.parse(accordionCookie); 1952 } 1953 1954 for (var i in webshop.search.activeFilterTabs) { 1955 var $tab = $('div [data-tab-id="' + i + '"]'); 1956 1957 if (webshop.search.activeFilterTabs[i] === false) { 1958 $tab.removeClass('is-active'); 1959 } else { 1960 $tab.addClass('is-active'); 1961 } 1962 } 1963}); 1964 1965function brokenImg(el) { 1966 $.post('/system/s3notify.html', { imgURL: $(el).attr('src') }); 1967} 1968 1969$(function () { 1970 var isIE = window.ActiveXObject || "ActiveXObject" in window; 1971 if (isIE) { 1972 $('.modal').removeClass('fade'); 1973 } 1974}); 1975 1976 1977if (typeof Object.assign != 'function') { 1978 // Must be writable: true, enumerable: false, configurable: true 1979 Object.defineProperty(Object, "assign", { 1980 value: function assign(target, varArgs) { // .length of function is 2 1981 'use strict'; 1982 if (target == null) { // TypeError if undefined or null 1983 throw new TypeError('Cannot convert undefined or null to object'); 1984 } 1985 1986 var to = Object(target); 1987 1988 for (var index = 1; index < arguments.length; index++) { 1989 var nextSource = arguments[index]; 1990 1991 if (nextSource != null) { // Skip over if undefined or null 1992 for (var nextKey in nextSource) { 1993 // Avoid bugs when hasOwnProperty is shadowed 1994 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 1995 to[nextKey] = nextSource[nextKey]; 1996 } 1997 } 1998 } 1999 } 2000 return to; 2001 }, 2002 writable: true, 2003 configurable: true 2004 }); 2005} 2006 2007 2008 2009$(document).ready(function () { 2010 webshopLoaded(); 2011 2012 // will remember the user using a cookie in the browser to keep the category scroll position int the sidebar for each category view page 2013 if (window.shopconfig && window.shopconfig.category_scroll_sticky) { 2014 2015 var scrollCategoryArea = ($.cookie('scrollAble') || '{}'); 2016 scrollCategoryArea = JSON.parse(scrollCategoryArea); 2017 2018 $('.search-filter-box ul').on('scroll', function (e) { 2019 var scrollTopValue = $(this).scrollTop(); 2020 var classes = $(this).parent().attr('class').split(/\s+/); 2021 var foundClass = false;
2022 2023 for (var i = 0; i < classes.length; i++) { 2024 foundClass = classes[i].match(/search-base-category-(\d+)/); 2025 if (foundClass != null) { 2026 scrollCategoryArea[classes[i]] = scrollTopValue; 2027 $.cookie('scrollAble', JSON.stringify(scrollCategoryArea), { expires: 7, path: '/' }); 2028 break; 2029 } 2030 } 2031 }); 2032 2033 for (var i in scrollCategoryArea) { 2034 $('.' + i + ' ul').scrollTop(scrollCategoryArea[i]); 2035 } 2036 } 2037});
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.