1var cookies = (function() { 2 var get = function(key) { 3 if (!key || !hasCookie(key)) { 4 return null; 5 } 6 return decodeURIComponent( 7 document.cookie.replace( 8 new RegExp( 9 '(?:^|.*;\\s*)' + 10 encodeURIComponent(key).replace(/[-.+*]/g, '\\$&') + 11 '\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*' 12 ), 13 '$1' 14 ) 15 ); 16 }; 17 18 var hasCookie = function(key) { 19 return new RegExp( 20 '(?:^|;\\s*)' + encodeURIComponent(key).replace(/[-.+*]/g, '\\$&') + '\\s*\\=' 21 ).test(document.cookie); 22 }; 23 24 var set = function(key, value, end, path, domain, secure) { 25 if (!key || /^(?:expires|max-age|path|domain|secure)$/.test(key)) { 26 return; 27 } 28 var expires = ''; 29 if (end) { 30 switch (typeof end) { 31 case 'number': 32 expires = '; max-age=' + end; 33 break; 34 case 'string': 35 expires = '; expires=' + end; 36 break; 37 case 'object': 38 if (end.hasOwnProperty('toGMTString')) { 39 expires = '; expires=' + end.toUTCString(); 40 } 41 break; 42 } 43 } 44 document.cookie = 45 encodeURIComponent(key) + 46 '=' + 47 encodeURIComponent(value) + 48 expires + 49 (domain ? '; domain=' + domain : '') + 50 (path ? '; path=' + path : '') + 51 (secure ? '; secure' : ''); 52 }; 53 54 var remove = function(key) { 55 if (!key || !hasCookie(key)) { 56 return; 57 } 58 var expDate = new Date(); 59 expDate.setDate(expDate.getDate() - 1); 60 document.cookie = encodeURIComponent(key) + '=; expires=' + expDate.toUTCString() + '; path=/'; 61 }; 62 63 return { 64 get: get, 65 hasCookie: hasCookie, 66 set: set, 67 remove: remove 68 }; 69})(); 70function supportIntersectionObserver() { 71 return 'IntersectionObserver' in window; 72} 73/** 74 * Prevent submitting form before JS is fully loaded 75 * Otherwise users can accidentally submit the form 76 * Which causes an unnecessary page reload 77 * @param form 78 */ 79var enableFormSubmit = function(form) { 80 form.querySelector('[type="submit"]').removeAttribute('disabled'); 81}; 82var createGUID = function() { 83 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 84 var r = (Math.random() * 16) | 0, 85 v = c === 'x' ? r : (r & 0x3) | 0x8; 86 return v.toString(16); 87 }); 88}; 89function findParentByClass(element, cls) { 90 while ((element = element.parentElement) && !element.classList.contains(cls)); 91 return element; 92} 93 94function serializeArray(form) { 95 var field, 96 l, 97 s = []; 98 if (typeof form == 'object' && form.nodeName == 'FORM') { 99 var len = form.elements.length; 100 for (var i = 0; i < len; i++) { 101 field = form.elements[i]; 102 if ( 103 field.name && 104 !field.disabled && 105 field.type != 'file' && 106 field.type != 'reset' && 107 field.type != 'submit' && 108 field.type != 'button' 109 ) { 110 if (field.type == 'select-multiple') { 111 l = form.elements[i].options.length; 112 for (var j = 0; j < l; j++) { 113 if (field.options[j].selected) 114 s[s.length] = { name: field.name, value: field.options[j].value }; 115 } 116 } else if ((field.type != 'checkbox' && field.type != 'radio') || field.checked) { 117 s[s.length] = { name: field.name, value: field.value }; 118 } 119 } 120 } 121 } 122 return s; 123} 124 125var preparePatchArray = function(formgroups) { 126 var array = []; 127 for (var i = 0; i < formgroups.length; i++) { 128 var input = formgroups[i].querySelector('input[name]'); 129 array.push({ 130 op: 'replace', 131 path: '/' + input.getAttribute('name'), 132 value: input.value 133 }); 134 } 135 return array; 136}; 137/** 138 * Emit custom event for triggering toaster 139 * @param type 140 * @param text 141 */ 142var sendToastEvent = function(type, text) { 143 window.dispatchEvent( 144 new CustomEvent('toast', { 145 detail: { 146 type: type, 147 text: text 148 } 149 }) 150 ); 151}; 152 153/** 154 * Emit custom success event for triggering toaster 155 * @param text 156 */ 157var sendSuccessToast = function(text) { 158 sendToastEvent('success', text); 159}; 160 161/** 162 * Emit custom error event for triggering toaster 163 * @param text 164 */ 165var sendErrorToast = function(text) { 166 sendToastEvent('error', text); 167}; 168function getUrlParameters(url) { 169 var vars = []; 170 var hash; 171 var urlParameters = url ? '?' + url.split('?')[1] : window.location.search; 172 173 if (urlParameters) { 174 var hashes = urlParameters.slice(1).split('&'); 175 for (var i = 0; i < hashes.length; i++) { 176 hash = hashes[i].split('='); 177 vars[hash[0]] = decodeURIComponent(hash[1]); 178 } 179 return vars; 180 } 181 return false; 182} 183 184function openUrl(url) { 185 window.location = url; 186} 187 188function getCurrentUrl() { 189 return [window.location.protocol, '//', window.location.host, window.location.pathname].join(''); 190} 191 192function resetUrlPagination(url) { 193 var pathname = url.split('?')[0]; 194 var pathhash = url.split('?')[1]; 195 var patharray = pathname.split('/'); 196 var page = false; 197 198 // set pagination back to 1 199 pathname = ''; 200 for (var i = 0; i < patharray.length; i++) { 201 if (page) { 202 patharray[i] = '1'; 203 } 204 205 page = patharray[i] === 'page'; 206 207 pathname += patharray[i]; 208 209 if (i < patharray.length - 1) { 210 pathname += '/'; 211 } else if (pathhash) { 212 pathname += '?'; 213 } 214 } 215 216 return (url = pathname + (pathhash || '')); 217} 218(function(root, factory) { 219 if (typeof define === 'function' && define.amd) { 220 define(factory); 221 } else if (typeof exports === 'object') { 222 module.exports = factory(require, exports, module); 223 } else { 224 root.CountUp = factory(); 225 } 226})(this, function(require, exports, module) { 227 /* 228 229 countUp.js 230 by @inorganik 231 232 */ 233 234 // target = id of html element or var of previously selected html element where counting occurs 235 // startVal = the value you want to begin at 236 // endVal = the value you want to arrive at 237 // decimals = number of decimal places, default 0 238 // duration = duration of animation in seconds, default 2 239 // options = optional object of options (see below) 240 241 var CountUp = function(target, startVal, endVal, decimals, duration, options) { 242 // make sure requestAnimationFrame and cancelAnimationFrame are defined 243 // polyfill for browsers without native support 244 // by Opera engineer Erik M�ller 245 var lastTime = 0; 246 var vendors = ['webkit', 'moz', 'ms', 'o']; 247 for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 248 window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 249 window.cancelAnimationFrame = 250 window[vendors[x] + 'CancelAnimationFrame'] || 251 window[vendors[x] + 'CancelRequestAnimationFrame']; 252 } 253 if (!window.requestAnimationFrame) { 254 window.requestAnimationFrame = function(callback, element) { 255 var currTime = new Date().getTime(); 256 var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 257 var id = window.setTimeout(function() { 258 callback(currTime + timeToCall); 259 }, timeToCall); 260 lastTime = currTime + timeToCall; 261 return id; 262 }; 263 } 264 if (!window.cancelAnimationFrame) { 265 window.cancelAnimationFrame = function(id) { 266 clearTimeout(id); 267 }; 268 } 269 270 var self = this; 271 self.version = function() { 272 return '1.8.5'; 273 }; 274 275 function formatNumber(num) { 276 num = num.toFixed(self.decimals); 277 num += ''; 278 var x, x1, x2, rgx; 279 x = num.split('.'); 280 x1 = x[0]; 281 x2 = x.length > 1 ? self.options.decimal + x[1] : ''; 282 rgx = /(\d+)(\d{3})/; 283 if (self.options.useGrouping) { 284 while (rgx.test(x1)) { 285 x1 = x1.replace(rgx, '$1' + self.options.separator + '$2'); 286 } 287 } 288 return self.options.prefix + x1 + x2 + self.options.suffix; 289 } 290 // Robert Penner's easeOutExpo 291 function easeOutExpo(t, b, c, d) { 292 return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b; 293 } 294 function ensureNumber(n) { 295 return typeof n === 'number' && !isNaN(n); 296 } 297 298 // default options 299 self.options = { 300 useEasing: true, // toggle easing 301 useGrouping: true, // 1,000,000 vs 1000000 302 separator: ',', // character to use as a separator
303 decimal: '.', // character to use as a decimal 304 easingFn: easeOutExpo, // optional custom easing function, default is Robert Penner's easeOutExpo 305 formattingFn: formatNumber, // optional custom formatting function, default is formatNumber above 306 prefix: '', // optional text before the result 307 suffix: '' // optional text after the result 308 }; 309 310 // extend default options with passed options object 311 if (options && typeof options === 'object') { 312 for (var key in self.options) { 313 if (options.hasOwnProperty(key) && options[key] !== null) { 314 self.options[key] = options[key]; 315 } 316 } 317 } 318 319 if (self.options.separator === '') self.options.useGrouping = false; 320 321 self.initialize = function() { 322 if (self.initialized) return true; 323 self.d = typeof target === 'string' ? document.getElementById(target) : target; 324 if (!self.d) { 325 return false; 326 } 327 self.startVal = Number(startVal); 328 self.endVal = Number(endVal); 329 // error checks 330 if (ensureNumber(self.startVal) && ensureNumber(self.endVal)) { 331 self.decimals = Math.max(0, decimals || 0); 332 self.dec = Math.pow(10, self.decimals); 333 self.duration = Number(duration) * 1000 || 2000; 334 self.countDown = self.startVal > self.endVal; 335 self.frameVal = self.startVal; 336 self.initialized = true; 337 return true; 338 } else { 339 return false; 340 } 341 }; 342 343 // Print value to target 344 self.printValue = function(value) { 345 var result = self.options.formattingFn(value); 346 347 if (self.d.tagName === 'INPUT') { 348 this.d.value = result; 349 } else if (self.d.tagName === 'text' || self.d.tagName === 'tspan') { 350 this.d.textContent = result; 351 } else { 352 this.d.innerHTML = result; 353 } 354 }; 355 356 self.count = function(timestamp) { 357 if (!self.startTime) { 358 self.startTime = timestamp; 359 } 360 361 self.timestamp = timestamp; 362 var progress = timestamp - self.startTime; 363 self.remaining = self.duration - progress; 364 365 // to ease or not to ease 366 if (self.options.useEasing) { 367 if (self.countDown) { 368 self.frameVal = 369 self.startVal - 370 self.options.easingFn(progress, 0, self.startVal - self.endVal, self.duration); 371 } else { 372 self.frameVal = self.options.easingFn( 373 progress, 374 self.startVal, 375 self.endVal - self.startVal, 376 self.duration 377 ); 378 } 379 } else { 380 if (self.countDown) { 381 self.frameVal = 382 self.startVal - (self.startVal - self.endVal) * (progress / self.duration); 383 } else { 384 self.frameVal = 385 self.startVal + (self.endVal - self.startVal) * (progress / self.duration); 386 } 387 } 388 389 // don't go past endVal since progress can exceed duration in the last frame 390 if (self.countDown) { 391 self.frameVal = self.frameVal < self.endVal ? self.endVal : self.frameVal; 392 } else { 393 self.frameVal = self.frameVal > self.endVal ? self.endVal : self.frameVal; 394 } 395 396 // decimal 397 self.frameVal = Math.round(self.frameVal * self.dec) / self.dec; 398 399 // format and print value 400 self.printValue(self.frameVal); 401 402 // whether to continue 403 if (progress < self.duration) { 404 self.rAF = requestAnimationFrame(self.count); 405 } else { 406 if (self.callback) self.callback(); 407 } 408 }; 409 // start your animation 410 self.start = function(callback) { 411 if (!self.initialize()) return; 412 self.callback = callback; 413 self.rAF = requestAnimationFrame(self.count); 414 }; 415 // toggles pause/resume animation 416 self.pauseResume = function() { 417 if (!self.paused) { 418 self.paused = true; 419 cancelAnimationFrame(self.rAF); 420 } else { 421 self.paused = false; 422 delete self.startTime; 423 self.duration = self.remaining; 424 self.startVal = self.frameVal; 425 requestAnimationFrame(self.count); 426 } 427 }; 428 // reset to startVal so animation can be run again 429 self.reset = function() { 430 self.paused = false; 431 delete self.startTime; 432 self.initialized = false; 433 if (self.initialize()) { 434 cancelAnimationFrame(self.rAF); 435 self.printValue(self.startVal); 436 } 437 }; 438 // pass a new endVal and start animation 439 self.update = function(newEndVal) { 440 if (!self.initialize()) return; 441 newEndVal = Number(newEndVal); 442 if (!ensureNumber(newEndVal)) { 443 return; 444 } 445 if (newEndVal === self.frameVal) return; 446 cancelAnimationFrame(self.rAF); 447 self.paused = false; 448 delete self.startTime; 449 self.startVal = self.frameVal; 450 self.endVal = newEndVal; 451 self.countDown = self.startVal > self.endVal; 452 self.rAF = requestAnimationFrame(self.count); 453 }; 454 455 // format startVal on initialization 456 if (self.initialize()) self.printValue(self.startVal); 457 }; 458 459 return CountUp;
460}); 461var crossSellProducts = (function() { 462 var _config = { 463 moduleList: document.querySelectorAll('[data-module="CrossSellProducts"]') 464 }; 465 466 var init = function() { 467 if (!_config.moduleList) return; 468 469 _config.moduleList.forEach(function(module) { 470 var itemcodes = module.dataset.productIds; 471 472 apiManager.getProducts(itemcodes, 'CrossSellProducts', addProductToGrid, module); 473 474 function addProductToGrid(data, location) { 475 if (data.readyState === 4) { 476 if (data.status === 200) { 477 var productGrid = location.querySelector("[data-child-module='ProductGrid']"); 478 productGrid.innerHTML += data.response; 479 // convertToSlider(location); 480 } 481 } 482 } 483 484 // function convertToSlider(location) { 485 // $(location).slick({ 486 // infinite: false, 487 // speed: 200, 488 // slidesToShow: 4, 489 // pauseOnHover: true, 490 // autoplay: false, 491 // centerMode: false, 492 // swipteToSlide: true, 493 // dots: false, 494 // arrows: true, 495 // touchThreshold: 10 496 // }); 497 // } 498 }); 499 }; 500 501 return { 502 init: init 503 }; 504})();document.addEventListener('DOMContentLoaded', function() { 505 506 var categoryDropdown = document.querySelectorAll('.js-category-dropdown'); 507 for (i = 0; i < categoryDropdown.length; ++i) { 508 categoryDropdown[i].addEventListener('click', function(event) { 509 var events = $._data(document, 'events') || {}; 510 events = events.click || []; 511 for(var i = 0; i < events.length; i++) { 512 if(events[i].selector) { 513 //Check if the clicked element matches the event selector 514 if($(event.target).is(events[i].selector)) { 515 events[i].handler.call(event.target, event); 516 } 517 // Check if any of the clicked element parents matches the 518 // delegated event selector (Emulating propagation) 519 $(event.target).parents(events[i].selector).each(function(){ 520 events[i].handler.call(this, event); 521 }); 522 } 523 } 524 event.stopPropagation(); //Always stop propagation 525 }); 526 }; 527 528 529 if (getUrlParameters()['Sort']) { 530 var orderval = getUrlParameters()['Sort'] + ':' + getUrlParameters()['Dir']; 531 $('.js-sorting option').removeAttr('selected'); 532 $('.js-sorting option[value="' + orderval + '"]').attr('selected', 'selected'); 533 } 534}); 535 536$('.js-sorting').change(function(e) { 537 var selected = e.target; 538 setSortOrder(e.target); 539}); 540 541$('.js-selectpicker').selectpicker({ 542 width: 'fit', 543 style: 'btn-default btn-sm' 544}); 545 546$('.js-apply-filters').on('click', function(e) { 547 e.preventDefault(); 548 addFilters(); 549}); 550 551$('.js-filters-reset').on('click', function(e) { 552 e.preventDefault(); 553 openUrl(getCurrentUrl()); 554}); 555 556 557$('.js-filteritem input[type=checkbox]').change(function() { 558 $(this) 559 .closest('.js-filteritem') 560 .toggleClass('js-filteritem-checked', this.checked); 561 562 var currentCategory = document.querySelector('[data-category]').getAttribute('data-category'); 563 getTotalProducts(currentCategory); 564 565 countActiveFilterItems(this); 566}); 567 568 569 570function addFilters(from) { 571 from = from ? '.mobileFilterItem' : '.item '; 572 var url = getCurrentUrl(); 573 var selectedFilters = document.querySelectorAll('.js-filteritem-checked'); 574 var filterObjects = []; 575 var urlParams = getUrlParameters(); 576 var url = resetUrlPagination(url); 577 for (param in urlParams) { 578 if (param === 'Sort') { 579 filterObjects.push({ filtername: param, filtervalue: urlParams[param] }); 580 } 581 582 if (param === 'Dir') { 583 filterObjects.push({ filtername: param, filtervalue: urlParams[param] }); 584 } 585 } 586 for (var i = 0; i < selectedFilters.length; i++) { 587 var key = selectedFilters[i].getAttribute('data-kind'); 588 var value = selectedFilters[i].getAttribute('data-id'); 589 filterObjects.push({ filtername: key, filtervalue: value }); 590 } 591 592 url = addFiltersToUrl(filterObjects, url); 593
594 // Trigger datalayer Measuring Filter data 595 var amountOfProducts = document.querySelector('.js-filter').getAttribute('data-amountofproducts'); 596 datalayerFilterButtonClick(filterObjects, amountOfProducts); 597 openUrl(url); 598} 599 600function getTotalProducts(category) { 601 var selectedFilters = document.querySelectorAll('.js-filteritem-checked:not(.mobileFilterItem)'); 602 var filterObjects = []; 603 filterObjects.push({ filtername: 'category', filtervalue: category }); 604 for (var i = 0; i < selectedFilters.length; i++) { 605 var key = selectedFilters[i].getAttribute('data-kind'); 606 var value = selectedFilters[i].getAttribute('data-id'); 607 filterObjects.push({ filtername: key, filtervalue: value }); 608 } 609 var sliderFilters = document.querySelectorAll('.slider-filter'); 610 for (var y = 0; y < sliderFilters.length; y++) { 611 var slideRangeName = sliderFilters[y] 612 .querySelector('.cat-title') 613 .getAttribute('data-filtertitle'); 614 var slideRangeValueMin = sliderFilters[y] 615 .querySelector('[data-lower-value]') 616 .getAttribute('data-lower-value'); 617 var slideRangeValueMax = sliderFilters[y] 618 .querySelector('[data-upper-value]') 619 .getAttribute('data-upper-value'); 620 if ( 621 sliderFilters[y].querySelector('[data-minvalue]').getAttribute('data-minvalue') !== 622 slideRangeValueMin || 623 sliderFilters[y].querySelector('[data-maxvalue]').getAttribute('data-maxvalue') !== 624 slideRangeValueMax 625 ) { 626 var tempFilter = { 627 filtername: slideRangeName, 628 filtervalue: slideRangeValueMin + ',' + slideRangeValueMax 629 }; 630 if ( 631 filterObjects.filter(function(e) { 632 return ( 633 e.filtername === tempFilter['filtername'] && e.filtervalue === tempFilter['filtervalue'] 634 ); 635 }).length > 0 636 ) { 637 continue; 638 } 639 filterObjects.push({ 640 filtername: slideRangeName, 641 filtervalue: slideRangeValueMin + ',' + slideRangeValueMax 642 }); 643 } 644 } 645 filterObjects.push({ filtername: 'select', filtervalue: 'sortOrder' }); 646 var url = addFiltersToUrl(filterObjects, '/api/v2/catalog/product'); 647 apiManager.callApi(url, renderTotalProducts, null); 648} 649 650function setSortOrder(initiator) { 651 var urlParams = getUrlParameters(); 652 var filterObjects = []; 653 var value = $(initiator).val(); 654 var type = value.slice(0, value.indexOf(':')); 655 var sort = value.slice(value.lastIndexOf(':') + 1); 656 657 for (param in urlParams) { 658 if (param !== 'Sort' && param !== 'Dir') { 659 filterObjects.push({ filtername: param, filtervalue: urlParams[param] }); 660 } 661 } 662 663 filterObjects.push( 664 { filtername: 'Sort', filtervalue: type }, 665 { filtername: 'Dir', filtervalue: sort } 666 ); 667 668 url = addFiltersToUrl(filterObjects, getCurrentUrl()); 669 openUrl(url); 670} 671 672function addFiltersToUrl(filterObjects, url) { 673 for (var filters in filterObjects) { 674 var urlParams = getUrlParameters(url); 675 676 if (Object.keys(urlParams).indexOf(filterObjects[filters]['filtername']) === -1) { 677 var params = 678 url.indexOf('?') === -1 679 ? '?' + 680 filterObjects[filters]['filtername'] + 681 '=' + 682 encodeURIComponent(filterObjects[filters]['filtervalue']) 683 : '&' + 684 filterObjects[filters]['filtername'] + 685 '=' + 686 encodeURIComponent(filterObjects[filters]['filtervalue']); 687 url += params; 688 } else { 689 url = 690 url.slice( 691 0, 692 url.indexOf(filterObjects[filters]['filtername']) + 693 filterObjects[filters]['filtername'].length + 694 1 695 ) + 696 encodeURIComponent(filterObjects[filters]['filtervalue']) + 697 ',' + 698 url.slice( 699 url.indexOf(filterObjects[filters]['filtername']) + 700 filterObjects[filters]['filtername'].length + 701 1 702 ); 703 } 704 } 705 return url; 706} 707 708function countActiveFilterItems(e) { 709 // count active filter items per filter 710 var jsFilter = $(e).closest('.js-filter'); 711 var filterCounter = $(jsFilter).find('.js-count-activeitems'); 712 var activeItems = $(jsFilter).find('.js-filteritem-checked').length; 713 $(filterCounter).text(activeItems); 714 715 // count all active filter items 716 var totalCount = $('.js-count-allactiveitems'); 717 var allActiveItems = $('.js-filters').find('.js-filteritem-checked').length; 718 $(totalCount).text(allActiveItems); 719 720 // show/hide counters 721 if (activeItems === 0 && allActiveItems != 0) { 722 $(filterCounter).hide(); 723 $(totalCount).show(); 724 } else if (allActiveItems === 0) { 725 $(filterCounter).hide(); 726 $(totalCount).hide(); 727 } else { 728 $(filterCounter).show(); 729 $(totalCount).show(); 730 } 731} 732 733function renderTotalProducts(response) { 734 if (response.readyState === 4) { 735 if (response.status === 200) { 736 var data = JSON.parse(response.responseText); 737 var amountOfProducts = document.querySelectorAll('.js-amountOfProducts'); 738 var jsFilter = document.querySelector('.js-filter'); 739 if (jsFilter) { 740 jsFilter.setAttribute('data-amountofproducts', data.totalResults); 741 } 742 for (var i = 0; i < amountOfProducts.length; i++) { 743 var previousAmount = parseInt(amountOfProducts[i].innerText.replace(',', '')); 744 var resultAmount = data.totalResults; 745 var animationOptions = { 746 useEasing: true, 747 easingFn: function(t, b, c, d) { 748 var ts = (t /= d) * t; 749 var tc = ts * t; 750 return b + c * (tc * ts + -5 * ts * ts + 10 * tc + -10 * ts + 5 * t); 751 }, 752 useGrouping: false, 753 separator: '',
754 decimal: '.' 755 }; 756 757 var animateNumber = new CountUp( 758 amountOfProducts[i], 759 previousAmount, 760 data.totalResults, 761 0, 762 1, 763 animationOptions 764 ); 765 animateNumber.start(); 766 767 var applyButton = document.querySelectorAll('.js-apply-filters'); 768 for (var x = 0; x < applyButton.length; x++) { 769 if (resultAmount > 0) { 770 $('.js-apply-filters').prop('disabled', false); 771 } else { 772 $('.js-apply-filters').prop('disabled', true); 773 } 774 } 775 } 776 } 777 } 778} 779Hyphenator.config({ 780 // hyphenchar: "â¢", 781 classname : 'js-hyphen', 782 minwordlength : 14, 783 leftmin : 6, 784 rightmin : 6 785}); 786 787Hyphenator.run(); 788var InfiniteScroll = (function() { 789 var _config = { 790 listView: document.querySelector('[data-module="infinite-scroll"]'), 791 threshold: 1.0, 792 rootMargin: '0px', 793 template: 'Product', 794 loadHTML: '<div class="infinite-loader"><h1>Loading</h1></div>' 795 }; 796 797 var _lazyPage; 798 var _sentinel = { 799 el: null, 800 set: function set(el) { 801 this.el = el; 802 this.el.classList.add('infinite-sentinel'); 803 _sentinelObserver.observe(this.el); 804 }, 805 unset: function unset() { 806 if (!this.el) return; 807 _sentinelObserver.unobserve(this.el); 808 this.el.classList.remove('infinite-sentinel'); 809 this.el = null; 810 } 811 }; 812 813 var init = function(options) { 814 for (var prop in options) { 815 if (options.hasOwnProperty(prop)) { 816 if (prop === 'loadHTML') { 817 _config[prop] = '<div class="infinite-loader">' + options[prop] + '</div>'; 818 } else { 819 _config[prop] = options[prop]; 820 } 821 } 822 } 823 824 if (_config.listView) { 825 window.addEventListener( 826 'load', 827 function() { 828 _updateSentinel(); 829 }, 830 false 831 ); 832 } 833 }; 834 835 var _updateSentinel = function() { 836 _sentinel.set(_config.listView.children[_config.listView.children.length - 1]); 837 }; 838 839 var _sentinelListener = function(entries) { 840 if (entries[0].intersectionRatio > 0) { 841 _sentinel.unset(); 842 if (_lazyPage > parseInt(_config.listView.getAttribute('data-totalPages'))) return; 843 _config.listView.classList.add('infinite-loading'); 844 var nextPage = typeof _lazyPage !== 'undefined' ? _lazyPage : 2; 845 var category = _config.listView.getAttribute('data-category'); 846 var urlParameters = getUrlParameters(); 847 var template = _config.template; 848 var filters = ''; 849 for (var filter in urlParameters) { 850 if (filter !== 'page') { 851 filters += '&' + encodeURI(filter) + '=' + encodeURI(urlParameters[filter]); 852 } 853 } 854 var url = 855 '/api/v2/catalog/product?category=' + 856 category + 857 '&page=' + 858 nextPage + 859 filters + 860 '&template=' + 861 template + 862 '&omitfilterdata=true&nocount=1'; 863 apiManager.callTemplate(url, _lazyLoadProducts, this); 864 _config.listView.insertAdjacentHTML('beforeend', _config.loadHTML); 865 } 866 }; 867 868 var _sentinelObserver = new IntersectionObserver(_sentinelListener, { 869 threshold: _config.threshold, 870 rootMargin: _config.rootMargin 871 }); 872 873 var _lazyLoadProducts = function(data) { 874 if (apiManager.isValidResponse(data)) { 875 if (typeof _lazyPage !== 'undefined') { 876 _lazyPage += 1; 877 } else { 878 _lazyPage = 3; 879 } 880 data = data.response; 881 var loader = _config.listView.querySelector('.infinite-loader'); 882 loader.parentNode.removeChild(loader); 883 _config.listView.insertAdjacentHTML('beforeend', data); 884 _updateSentinel(); 885 _config.listView.classList.remove('infinite-loading'); 886 } 887 }; 888 889 return { 890 init: init 891 }; 892})(); 893var QueryCleanup = (function() { 894 var _config = { 895 module: document.querySelectorAll('[data-module="QueryCleanup"]') 896 }; 897 898 var init = function() { 899 _config.module.forEach(function(module) { 900 module.addEventListener('click', function() { 901 window.history.replaceState(null, null, window.location.pathname); 902 }); 903 });
904 }; 905 906 return { 907 init: init 908 }; 909})(); 910document.addEventListener('DOMContentLoaded', function() { 911 // Scroll to links with hashes on load 912 $('a[href*="#"]') 913 // Remove links that don't actually link to anything 914 .not('[href="#"]') 915 .not('[href^="#modal-"]') 916 .not('[href="#0"]') 917 .not('[data-toggle]') 918 .click(function(event) { 919 // On-page links 920 if ( 921 location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && 922 location.hostname == this.hostname 923 ) { 924 // Figure out element to scroll to 925 var target = $(this.hash.replace(/\+/g, '\\+')); 926 target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 927 // Does a scroll target exist? 928 if (target.length) { 929 // Only prevent default if animation is actually gonna happen 930 event.preventDefault(); 931 932 // exception for rows 933 if (this.hash.match('^#row-')) { 934 var target = $(this.hash).closest('.png-section'); 935 } 936 937 $('html, body').animate( 938 { 939 scrollTop: target.offset().top - $('header').outerHeight() - 40 + 'px' 940 }, 941 500, 942 function() { 943 // Callback after animation 944 // Must change focus! 945 // var $target = $(target); 946 // $target.focus(); 947 // if ($target.is(":focus")) { // Checking if the target was focused 948 // return false; 949 // } else { 950 // $target.attr('tabindex','-1'); // Adding tabindex for elements not focusable 951 // $target.focus(); // Set focus again 952 // }; 953 } 954 ); 955 } 956 } 957 }); 958 959 // Scroll to links with hashes on load 960 if (window.location.hash) { 961 var target = window.location.hash; 962 963 if (target === '#403') return; 964 965 // Needed to fix the syntax error, unrecognized expression: #/ when redirectUrl from checkout is filled 966 if (target.match('#/')) return; 967 968 var $target = $(target); 969 970 // exception for rows 971 if (target.match('^#row-')) { 972 var $target = $(target).closest('.png-section'); 973 } 974 975 $('html, body').animate( 976 { 977 scrollTop: $target.offset().top - $('header').outerHeight() - 40 + 'px' 978 }, 979 500, 980 function() { 981 // Callback after animation 982 // Must change focus! 983 // var $target = $(target); 984 // $target.focus(); 985 // if ($target.is(":focus")) { // Checking if the target was focused 986 // return false; 987 // } else { 988 // $target.attr('tabindex','-1'); // Adding tabindex for elements not focusable 989 // $target.focus(); // Set focus again 990 // }; 991 } 992 ); 993 } 994}); 995var createEventListeners = function() { 996 document.addEventListener( 997 'DOMContentLoaded', 998 function(event) { 999 crossSellProducts.init(); 1000 InfiniteScroll.init(); 1001 QueryCleanup.init(); 1002 }, 1003 false 1004 ); 1005}; 1006window.addEventListener('resize', function(e) {}); 1007var apiManager = (function() { 1008 var callTemplate = function(url, callback, initiator) { 1009 _callAsync('GET', url, null, callback, 'text/html', initiator); 1010 }; 1011 1012 var callApi = function(url, callback, initiator) { 1013 _callAsync('GET', url, null, callback, null, initiator); 1014 }; 1015 1016 var patch = function(url, data, callback, initiator) { 1017 _callAsync('PATCH', url, JSON.stringify(data), callback, null, initiator); 1018 }; 1019 1020 var post = function(url, data, callback, initiator) { 1021 _callAsync('POST', url, JSON.stringify(data), callback, null, initiator); 1022 }; 1023 1024 var deleteApi = function(url, callback, initiator) { 1025 _callAsync('DELETE', url, null, callback, null, initiator); 1026 }; 1027 1028 var remove = function(cartId, cartitemId, callback) { 1029 var url = '/api/v1/catalog/cart/' + cartId + '/remove/' + cartitemId; 1030 _callAsync('POST', url, null, callback, null, null); 1031 }; 1032 1033 var getApi = function(id, discount, callback) { 1034 var timestamp = Date.now(); 1035 var url = 1036 discount !== null 1037 ? '/api/v1/catalog/cart/' + id + '/discount' 1038 : '/api/v1/catalog/cart/' + id + '?' + timestamp; 1039 var requestMethod = discount !== null ? 'POST' : 'GET'; 1040 var params = discount !== null ? discount : null; 1041 _callAsync(requestMethod, url, params, callback, null, null); 1042 }; 1043 1044 var autocomplete = function(searchstring, callback, initiator) { 1045 _callAsync( 1046 'GET', 1047 '/api/v2/catalog/search/autocomplete?q=' + searchstring + '&template=SearchProductsResults', 1048 null, 1049 callback, 1050 'text/html', 1051 initiator 1052 ); 1053 }; 1054 1055 var getProducts = function(itemCode, template, callback, initiator) { 1056 _callAsync( 1057 'GET', 1058 '/api/v2/catalog/product?itemcode=' + 1059 itemCode + 1060 '&all=true&sort=unsorted&omitfilterdata=1&template=' + 1061 template, 1062 null, 1063 callback, 1064 'text/html', 1065 initiator 1066 ); 1067 }; 1068 1069 var isValidResponse = function(response) { 1070 return response.readyState === 4 && response.status === 200; 1071 }; 1072 1073 var isBadRequest = function(response) { 1074 return response.readyState === 4 && response.status === 400; 1075 }; 1076 1077 var _callAsync = function(method, url, params, callback, accept, initiator) { 1078 var lang = LANGUAGE !== undefined ? LANGUAGE : 'nl'; 1079 var request = new XMLHttpRequest(); 1080 request.onreadystatechange = function() { 1081 callback(request, initiator); 1082 }; 1083 request.open(method, url, true); 1084 request.setRequestHeader('Content-type', 'application/json'); 1085 request.setRequestHeader('Accept-language', lang);
1086 request.setRequestHeader('Authorization', 'bearer ' + cookies.get('token')); 1087 request.setRequestHeader('Cache-control', 'no-cache'); 1088 if (accept) { 1089 request.setRequestHeader('Accept', accept); 1090 } 1091 request.send(params); 1092 }; 1093 1094 return { 1095 callTemplate: callTemplate, 1096 callApi: callApi, 1097 patch: patch, 1098 post: post, 1099 deleteApi: deleteApi, 1100 getApi: getApi, 1101 autocomplete: autocomplete, 1102 getProducts: getProducts, 1103 isValidResponse: isValidResponse, 1104 isBadRequest: isBadRequest 1105 }; 1106})(); 1107var main = function() { 1108 createEventListeners(); 1109}; 1110 1111main(); 1112 1113function paginationSmall() { 1114 var totalPages = document.querySelector('.js-pagination-small li:nth-last-child(2) a'); 1115 var smallActivePagination = document.querySelector('.js-pagination-small .active a'); 1116 1117 if (totalPages && smallActivePagination) { 1118 smallActivePagination.append('/' + totalPages.innerText); 1119 } 1120} 1121 1122 1123// MAKE ROWS STICKY TO FLEXIBLE HEIGHT HEADER 1124$('.js-headroom-sticky').each(function() { 1125 1126 var header = $('.c-header'); 1127 var stickyElement = $('.js-headroom-sticky'); 1128 1129 // Function to check if the sticky element is aligned with the header 1130 function updateStickyPosition() { 1131 var headerHeight = header.outerHeight(true); 1132 stickyElement.css('top', headerHeight); 1133 1134 var stickyRect = stickyElement[0].getBoundingClientRect().top; 1135 1136 // Check if the top of the sticky element matches the header height 1137 if (Math.round(stickyRect) === Math.round(headerHeight)) { 1138 header.css('box-shadow', 'none'); 1139 stickyElement.addClass('is-sticky'); 1140 } else { 1141 header.css('box-shadow', ''); 1142 stickyElement.removeClass('is-sticky'); 1143 } 1144 } 1145 1146 // Call the function to update sticky state immediately on page load 1147 updateStickyPosition(); 1148 1149 $(window).scroll(function() { 1150 // Recalculate sticky state during scroll 1151 updateStickyPosition(); 1152 1153 // In case transitions are slower, recalculate after 150ms 1154 setTimeout(updateStickyPosition, 150); 1155 }); 1156}); 1157 1158 1159// HEADROOM.JS 1160(function() { 1161 var header = new Headroom(document.querySelector('.c-header'), { 1162 tolerance: 70, 1163 offset: 35, // testing: same as header-top-row? 1164 }); 1165 header.init(); 1166})(); 1167 1168 1169// document.load 1170document.addEventListener('DOMContentLoaded', function() { 1171 1172 1173 // create small pagination 1174 paginationSmall(); 1175 1176 1177 // PARSLEY.JS 1178 $('.js-parsley-form').parsley({ 1179 // append the error message to the form group 1180 errorsContainer: function(field) { 1181 return $(field.element).closest('.form-group'); 1182 }, 1183 }); 1184 1185 // move form-help after parsley-errors 1186 window.Parsley.on('field:error', function() { 1187 $(".parsley-errors-list").prev('.form-help').appendTo(this.$element.closest('.form-group')); 1188 }); 1189 1190 1191 if (window.innerWidth < 992) { 1192 $('.js-modalsearch') 1193 .keyup(function() { 1194 $('#modal-search').modal( 1195 { 1196 backdrop: false, 1197 }, 1198 'show' 1199 ); 1200 }) 1201 .focus(function() { 1202 if ($(this).val()) { 1203 $('#modal-search').modal( 1204 { 1205 backdrop: false, 1206 }, 1207 'show' 1208 ); 1209 } 1210 }); 1211 } 1212 1213 1214 // BENEFITS-PANEL: keep this script in case a customer wants the panel to open/close by default 1215 // open panel after timeout after document load 1216 // if (document.getElementById("js-benefitspanel") && (window.innerWidth >= 768) ) { 1217 // setTimeout(function() { 1218 // $('#js-benefitspanel').collapse('show'); 1219 // // panel close after timeout 1220 // setTimeout(function() { 1221 // $('#js-benefitspanel').collapse('hide'); 1222 // }, 2500); 1223 // }, 200); 1224 // }; 1225 1226 1227 // create href for 'terug-knop' from second to last breadcrumb-path 1228 var breadcrumbBackButton = document.querySelector('.js-backlink-button'); 1229 var breadcrumbBackOrigin = document.querySelector('.js-backlink-item:nth-last-child(2) .js-backlink-origin'); 1230 1231 if (breadcrumbBackButton !== null && breadcrumbBackOrigin !== null) { 1232 breadcrumbBackButton.setAttribute('href', breadcrumbBackOrigin.getAttribute('href')); 1233 } 1234 1235 1236 //remove ActiveCampaign styling and reset to our styling 1237 if ($('form[action*="activehosted"]').length) { 1238 $('form[action*="activehosted"]').siblings('style').remove(); 1239 $('._form_element').addClass('form-group'); 1240 $('._form_element').find('label').text($(this).find('label').text().replace('*', '')); 1241 $('._form_element').find('input').addClass('form-control'); 1242 $('._button-wrapper').find('button').addClass('btn btn-primary btn-block'); 1243 $('._form-thank-you').addClass('alert alert--info'); 1244 } 1245 1246 1247 //remove Mailchimp styling and reset to our styling 1248 if ($('#mc_embed_signup').length) { 1249 $('.mc-field-group').addClass('form-group'); 1250 $('.mc-field-group').find('label').text($(this).find('.mc-field-group').find('label').text().replace('*', '')); 1251 $('.mc-field-group').find('input').addClass('form-control'); 1252 $('.foot').find('input').addClass('btn btn-primary btn-block'); 1253 } 1254 1255 1256 //remove heading margins if heading follows heading 1257 $('.text-container h4, .text-container h5, .text-container h6').prev('h1,h2,h3').css('margin-bottom', '0'); 1258 1259 var ignoreHeadings = ['/winkelwagen', '/shopping-cart', '/my-account', '/mijn-account']; 1260 //wrong headings detection 1261 if (!ignoreHeadings.some(function (x) { return window.location.pathname.includes(x); })) { 1262 if ($("body").hasClass("js-heading-errors-on")) { 1263 // multiple h1 detection 1264 var lengthH1 = $('h1:not([class*="hidden"])'); 1265 // break used detection 1266 var doubleH1 = $('.text-container h1').next('h1'); 1267 var doubleH2 = $('.text-container h2').next('h2'); 1268 var doubleH3 = $('.text-container h3').next('h3'); 1269 1270 if (lengthH1.length == 0 || lengthH1.length > 1 || doubleH1.length > 0 || doubleH2.length > 0 || doubleH3.length > 0) { 1271 // error on body 1272 $('body').append('<div class="js-heading-errors-detected"><p>Heading errors detected:</p><ul></ul></div>'); 1273 1274 // error on each h1 1275 if (lengthH1.length == 0) {
1276 $('.js-heading-errors-detected ul').append('<li>No H1 detected. <span> ⤷ Use at least one H1 on the page</span></li>'); 1277 } 1278 if (lengthH1.length > 1) { 1279 $('.js-heading-errors-detected ul').append('<li>Multiple H1\'s (' + lengthH1.length + 'x). <span> ⤷ Use only one H1 on a page.</span></li>'); 1280 $('h1').each(function(index) { 1281 $(this).append("<div class='js-error-multiple-h1-on-page'>Multiple H1 (" + (index + 1) + ' of ' + lengthH1.length + ').</div>'); 1282 }); 1283 } 1284 if (doubleH1.length > 0 || doubleH2.length > 0 || doubleH3.length > 0) { 1285 $('.js-heading-errors-detected ul').append('<li>Heading followed by the same heading (' + (doubleH1.length + doubleH2.length + doubleH3.length) + 'x). <span> ⤷ Instead of <code>enter</code>, use <code>\\</code> to soft-break heading. Or replace by different <code>h2/h3/p</code>.</span></li>'); 1286 $(doubleH1).append('<div class="js-error-double-heading">H1 + H1 detected'); 1287 $(doubleH2).append('<div class="js-error-double-heading">H2 + H2 detected'); 1288 $(doubleH3).append('<div class="js-error-double-heading">H3 + H3 detected'); 1289 } 1290 } 1291 } 1292 } 1293 1294 1295 // nested dropdown functionality 1296 $('[data-toggle=dropdown]').on('click', function(e) { 1297 1298 // prevent follow link if NOT opened by hover 1299 if (!$(this).parent().hasClass('is-opened-on-hover')) { 1300 e.preventDefault(); // do not follow link 1301 } 1302 1303 // prevent bootstrap functionality, enables dropdown inside dropdown 1304 e.stopPropagation(); 1305 1306 // closes all open dropdowns that are not a parent 1307 $('.c-dropdown').not($(this).parents('.open')).removeClass('open'); 1308 1309 // open dropdown of clicked item 1310 $(this).parent().toggleClass('open active').parents('.active').removeClass('active'); 1311 1312 // modal no scroll 1313 if (window.innerWidth < 960) { 1314 $(this).closest('.c-modal__body').css("overflow", "hidden"); 1315 }; 1316 1317 }); 1318 1319 1320 // PREVENT DROPDOWN FROM CLOSING WHEN CLICKED INSIDE (if not <a>, stop dropdowntoggle from closing) 1321 $('.c-dropdown__menu').on('click', function(e) { 1322 if (!$(e.target).is('a')) { 1323 e.stopPropagation(); 1324 } 1325 }); 1326 1327 1328 // close all open / active dropdowns 1329 $('#modal-menus').on('hide.bs.modal', function(e) { 1330 $(this).find('.c-dropdown').removeClass('open active'); 1331 }); 1332 1333 1334 // close submenu by button 1335 $('.js-dropdown-close').on('click', function(e) { 1336 $(this).closest('.c-dropdown').removeClass('open active').closest('.open').addClass('active'); 1337 1338 }); 1339 1340 1341 // modal activate scroll 1342 $('.js-reactivate-modal').on('click', function(e) { 1343 $(this).closest('.c-modal__body').css("overflow", ""); 1344 }); 1345 1346 1347 // CLOSE MEGAMENU ON MOBILE 1348 $('.js-close-megamenu').click(function(e) { 1349 $(this).closest('.c-dropdown').find('[data-toggle="dropdown"]').dropdown('toggle'); 1350 }); 1351 1352 1353 // OPEN DROPDOWN ON HOVER (main menu) 1354 // because touch devices execute touchstart AND mouseenter on tap, it must be added to be stopped 1355 $('.js-open-dropdown-on-hover').on('touchstart mouseenter mouseleave', function(e) { 1356 1357 // for desktop-layout 1358 if (window.innerWidth >= 960) { 1359 1360 // on touch device 1361 if (e.type == 'touchstart') { 1362 $(this).off('mouseenter mouseleave'); 1363 } 1364 1365 // on mouse device 1366 if (e.type == 'mouseenter') { 1367 $(this).addClass('open is-opened-on-hover'); 1368 } 1369 else if (e.type == 'mouseleave') { 1370 $(this).removeClass('open is-opened-on-hover'); 1371 } 1372 1373 }; 1374 }); 1375 1376 1377 // CATEGORY PANELS 1378 // catch clicks and mouseenter evens (for desktop) 1379 $('.js-category-link').on('click mouseenter', function(e) { 1380 1381 // for all clicks and mouseenters on desktop 1382 if (e.type == 'click' || (e.type == 'mouseenter' && window.innerWidth > 960)) { 1383 1384 // if link has href (always) AND target (sometimes), prevent follow link if NOT opened by hover (only follow link if opened by hover) 1385 if (!$(this).hasClass('is-opened-on-hover') && $(this).attr('data-target') !== undefined && $(this).data('target').startsWith("#cat-")) { 1386 e.preventDefault(); 1387 } 1388 1389 // close all levels of next panels 1390 $(this).closest('.js-category-panel').nextAll().find('.js-category-level').removeClass('open').children().hide(); 1391 1392 // only for subcategories 1393 if ($(this).attr('data-target') !== undefined && $(this).data('target').startsWith("#cat-")) { 1394 1395 // remove active from this panel, open and active next panel, close and deactive all next panels 1396 $(this).closest('.js-category-panel').removeClass('active').next().addClass('open active').nextAll().removeClass('open active'); 1397 1398 // open referenced level 1399 $($(this).data('target')).addClass('open').children().show(); 1400 }; 1401 1402 // for desktop-layout show clicked state 1403 if (window.innerWidth >= 960) { 1404 // remove active (clicked) state of all links in this panel 1405 $(this).closest('.js-category-panel').find('.js-category-link').removeClass('active'); 1406 // remove active (clicked) state of all links in deeper panels 1407 $(this).closest('.js-category-panel').nextAll().find('.js-category-link').removeClass('active'); 1408 // // set link to active 1409 $(this).addClass('active'); 1410 }; 1411 1412 } 1413 1414 });
1415 1416 1417 // for mobile, back-function has to close/deactivate various things 1418 $('.js-category-level-back').click(function(e) { 1419 // close & deactivate panel, activate provious panel 1420 $(this).closest('.js-category-panel').removeClass('open active').prev().addClass('active'); 1421 // remove active state from links in this panel 1422 $(this).closest('.js-category-panel').find('.js-category-link').removeClass('active'); 1423 }); 1424 1425 1426 // reset open panels/levels/links on start 1427 $('.js-reset-panels').on('touchstart mouseenter', function(e) { 1428 // close all panels 1429 $('.js-category-panel').removeClass('open active'); 1430 // open first panel 1431 $('.js-category-panel:first-child').addClass('open active'); 1432 // close all levels 1433 $('.js-category-level').removeClass('open').children().hide(); 1434 // close all levels 1435 $('.js-category-link').removeClass('active'); 1436 }); 1437 1438 // END CATEGORY PANELS 1439 1440 1441 // remove imagedetails text when item is opened 1442 $('[data-child-module="imagedetails__link"').on('click', function(e) { 1443 $(this).closest('[data-module="imagedetails"]').find('[data-child-module="imagedetails__text"]').remove(); 1444 }); 1445 1446 1447 // set radiobutton to false when category is closed 1448 $('.js-category-check').on('click', function(event) { 1449 var input = $(this) 1450 1451 setTimeout(function() { 1452 if (input.hasClass("collapsed")) { 1453 input.prop("checked", false); 1454 } 1455 }, 50); 1456 }); 1457 1458 1459 // open active categories on load (only category page and only > 992px) 1460 if ($('.row-catalogcategory .c-categorylist__item.is-active').length && window.innerWidth >= 960) { 1461 $('.row-catalogcategory .c-categorylist__item.is-active > .form-check').children('.js-category-check').trigger('click'); 1462 }; 1463 1464 1465 // open modal when starting with modal prefix (resengo/bookingmask/etc) 1466 $("[href^='#modal-']").on("click", function(e) { 1467 e.preventDefault(); 1468 $($(this).attr('href')).modal('show'); 1469 }); 1470 1471 1472 // when submit is clicked, deactivate submit button 1473 $('button[data-module="recaptcha"]').click(function() { 1474 $(this).attr('disabled', 'disabled'); 1475 }); 1476 // when input is changed, activate submit button 1477 $('form').find('input, select, textarea').on('keyup change', function() { 1478 $(this).closest('form').find('button[data-module="recaptcha"]').removeAttr('disabled'); 1479 }); 1480 1481 1482 // if image is used as background, remove smaller source files, so high res is used 1483 $(".image-cover").find(".image-container").addClass('js-sources-removed').children("source").remove(); 1484 1485 1486 1487 $(".js-select-productchild").click(function() { 1488 $(".js-select-productchild").removeClass('is-selected'); 1489 $(this).addClass('is-selected').closest('.c-dropdown').removeClass('open active'); 1490 }); 1491 1492 1493 // animation demo page 1494 $(".js-animation-demo .btn-primary").click(function(e) { 1495 e.preventDefault(); 1496 var classes = $(this).closest('.o-macro__item').attr("class"); 1497 $(this).closest('.o-macro__item').attr('class', 'o-macro__item'); 1498 setTimeout(() => { 1499 $(this).closest('.o-macro__item').attr('class', classes); 1500 }, "10"); 1501 1502 }); 1503 1504 // Create table structure 1505 const tableSpecs = document.querySelectorAll('.c-table-specs .text-container p'); 1506 tableSpecs.forEach((p) => { 1507 let parentContainer = document.createElement('div'); 1508 parentContainer.classList.add('c-table'); 1509 1510 let nodes = Array.from(p.childNodes); 1511 let currentRow = null; 1512 let firstTextHandled = false; 1513 1514 nodes.forEach((node) => { 1515 if (node.nodeType === 1 && node.tagName === 'STRONG') { 1516 // Nieuwe rij starten bij een <strong> 1517 currentRow = document.createElement('div'); 1518 currentRow.classList.add('c-table-row'); 1519 1520 let strongElement = document.createElement('div'); 1521 strongElement.classList.add('c-table-cell', 'c-table-header'); 1522 strongElement.appendChild(node.cloneNode(true)); 1523 1524 currentRow.appendChild(strongElement); 1525 parentContainer.appendChild(currentRow); 1526 } else if (currentRow && node.nodeType === 3) { 1527 // Verwijder ALLE bullet-symbolen en voeg tekst toe in de tweede kolom 1528 let textContent = node.textContent.replace(/[\u2B24\u25CF\u2022]/g, '').trim(); 1529 if (textContent) { 1530 let textCell = document.createElement('div'); 1531 textCell.classList.add('c-table-cell'); 1532 textCell.textContent = textContent; 1533 currentRow.appendChild(textCell); 1534 } 1535 } else if (!firstTextHandled && node.nodeType === 3) { 1536 // Zorg dat de eerste tekst buiten strong behouden blijft en verwijder bullets 1537 let textContent = node.textContent.replace(/[\u2B24\u25CF\u2022]/g, '').trim(); 1538 if (textContent) { 1539 let fullWidthRow = document.createElement('div'); 1540 fullWidthRow.classList.add('c-table-row', 'c-table-full-width'); 1541 1542 let fullWidthCell = document.createElement('div'); 1543 fullWidthCell.classList.add('c-table-cell'); 1544 fullWidthCell.textContent = textContent; 1545 1546 fullWidthRow.appendChild(fullWidthCell); 1547 parentContainer.appendChild(fullWidthRow); 1548 firstTextHandled = true; 1549 } 1550 } 1551 }); 1552 1553 // Vervang de originele <p> met de nieuwe tabel 1554 p.replaceWith(parentContainer); 1555 });
1556 1557 1558 // Create table structure with headers 1559 const tables = document.querySelectorAll('.c-table-specs-headers table'); 1560 tables.forEach((table) => { 1561 const headers = table.querySelectorAll('th'); 1562 1563 // Voeg klik-events toe aan de koppen van elke tabel 1564 headers.forEach((header, index) => { 1565 header.addEventListener('click', () => { 1566 sortTable(table, index); 1567 }); 1568 }); 1569 }); 1570 1571 // Functie om de tabel te sorteren 1572 function sortTable(table, index) { 1573 const rows = Array.from(table.querySelectorAll('tr:nth-child(n+2)')); 1574 const sortedRows = rows.sort((rowA, rowB) => { 1575 const cellA = rowA.cells[index].innerText; 1576 const cellB = rowB.cells[index].innerText; 1577 return cellA.localeCompare(cellB); 1578 }); 1579 1580 // Voeg de gesorteerde rijen opnieuw toe aan de tabel 1581 sortedRows.forEach((row) => { 1582 table.appendChild(row); 1583 }); 1584 } 1585 1586 // Language selector with landcode 1587 var input = document.querySelector('#CharterPhoneNumber'); 1588 var iti = window.intlTelInput(input, { 1589 initialCountry: 'nl', // Standaard Nederland 1590 separateDialCode: true, // Zet de landcode los van het nummer 1591 preferredCountries: ['nl', 'be', 'de'], // Voorkeurslanden 1592 utilsScript: 'https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js', 1593 }); 1594 1595 1596});
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.