1window.theme = window.theme || {}; 2 3(function ($) { 4 'use strict'; 5 6 var main = { 7 8 init: function () { 9 var self = this; 10 11 self.footerPopup = '.footer-popup'; 12 self.footerPopupLabel = '.footer-popup .popup-label'; 13 self.footerPopupInner = '.footer-popup .popup-inner'; 14 self.footerPopupCloseBtn = '.footer-popup .close-btn'; 15 16 self.bindEvents(); 17 }, 18 19 bindEvents: function () { 20 var self = this; 21 22 document.querySelector(self.footerPopupLabel).addEventListener('click', function () { 23 self.footerPopupToggle(true); 24 }); 25 26 document.querySelector(self.footerPopupCloseBtn).addEventListener('click', function () { 27 self.footerPopupToggle(false); 28 }); 29 30 document.body.addEventListener('click', function (e) { 31 if (document.querySelector(self.footerPopup).classList.contains('popup-open') && !e.target.closest(self.footerPopup)) { 32 self.footerPopupToggle(false); 33 } 34 }); 35 }, 36 37 footerPopupToggle: function (state) { 38 var self = this; 39 40 if (state) { 41 document.querySelector(self.footerPopup).classList.add('popup-opening'); 42 document.querySelector(self.footerPopupInner).addEventListener(transitionEvent, self.footerPopupAnimationEnd); 43 } else { 44 document.querySelector(self.footerPopup).classList.add('popup-closing'); 45 document.querySelector(self.footerPopupInner).addEventListener(transitionEvent, self.footerPopupAnimationEnd); 46 } 47 }, 48 49 footerPopupAnimationEnd: function () { 50 var self = main; 51 52 if (document.querySelector(self.footerPopup).classList.contains('popup-opening')) { 53 document.querySelector(self.footerPopup).classList.add('popup-open'); 54 document.querySelector(self.footerPopup).classList.remove('popup-opening'); 55 document.querySelector(self.footerPopupInner).removeEventListener(transitionEvent, self.footerPopupAnimationEnd); 56 } else { 57 document.querySelector(self.footerPopup).classList.remove('popup-open', 'popup-closing'); 58 document.querySelector(self.footerPopupInner).removeEventListener(transitionEvent, self.footerPopupAnimationEnd); 59 } 60 } 61 62 }; 63 64 var frontPage = { 65 66 init: function () { 67 var self = this; 68 69 self.$sliderBanner = $('.slider-banner'); 70 71 self.sliderBannerInit(); 72 }, 73 74 sliderBannerInit: function () { 75 var self = this; 76 77 self.$sliderBanner.slick({ 78 dots: true, 79 autoplay: true, 80 autoplaySpeed: 8000, 81 cssEase: 'ease', 82 speed: 1000, 83 prevArrow: '<button type="button" class="slick-prev" aria-label="previous"></button>', 84 nextArrow: '<button type="button" class="slick-next" aria-label="next"></button>', 85 }); 86 }, 87 }; 88 89 var sliderContents = { 90 91 init: function () { 92 var self = this; 93 94 self.$sliderProducts = $('.slider-products'); 95 self.isSliderProductInited = null; 96 97 if (self.$sliderProducts.length === 0) { 98 return; 99 } 100 101 self.sliderProductInit(); 102 }, 103 104 bindEvents: function () { 105 var self = this; 106 107 $(document.body).CustomResize({ 108 resize: function () { 109 self.sliderProductInit(); 110 }, 111 timeout: 200 112 }); 113 }, 114 115 sliderProductInit: function () { 116 var self = this; 117 118 if (self.isSliderProductInited && window.innerWidth >= 750) { 119 return; 120 } 121 122 var sliderSettings = { 123 dots: false, 124 infinite: false, 125 slidesToShow: 4, 126 slidesToScroll: 4, 127 prevArrow: '<button type="button" class="slick-nav slick-slide-prev"></button>', 128 nextArrow: '<button type="button" class="slick-nav slick-slide-next"></button>', 129 responsive: [ 130 { 131 breakpoint: 1020, 132 settings: { 133 slidesToShow: 3, 134 slidesToScroll: 3 135 } 136 }, 137 { 138 breakpoint: 750, 139 settings: 'unslick' 140 } 141 ] 142 }; 143 144 self.$sliderProducts.each(function () { 145 var $slider = $(this); 146 147 if ($slider.hasClass('slick-initialized') === false && window.innerWidth >= 750) { 148 sliderSettings.appendArrows = $slider.next('.slider-product-nav'); 149 150 $slider.on('init', function () { 151 self.isSliderProductInited = true; 152 $slider.addClass('slider-initialized'); 153 }); 154 155 if ($slider.hasClass('slider-initialized') === false) { 156 $slider.slick(sliderSettings); 157 } else { 158 $slider.slick('resize'); 159 } 160 161 $slider.on('destroy', function () { 162 self.isSliderProductInited = false;
163 }); 164 165 self.isSliderProductInited = true; 166 } 167 }); 168 } 169 } 170 171 var shopPage = { 172 init: function () { 173 var self = this; 174 175 self.$showMoreButton = $('.button-link.show-more'); 176 177 self.toggleProductDescription(); 178 179 window.addEventListener("load", (event) => { 180 self.initAutoOpenExcerptLink(); 181 }); 182 }, 183 184 toggleProductDescription: function () { 185 var self = this; 186 187 self.$showMoreButton.on('click', function () { 188 var $btn = $(this), 189 $desc = $btn.closest('.woocommerce-product-details__short-description'), 190 $entry = $desc.find('.entry-content'), 191 $entryInner = $desc.find('.entry-content__inner'), 192 state = $desc.hasClass('open') === true; 193 194 if (state === true) { 195 $desc.removeClass('open'); 196 $entry.removeAttr('style'); 197 $btn.text($btn.data('more_text')); 198 } else { 199 $desc.addClass('open'); 200 $entry.css('max-height', $entryInner.outerHeight()); 201 $btn.text($btn.data('less_text')); 202 } 203 }); 204 205 }, 206 207 initAutoOpenExcerptLink: function () { 208 let $excerptBtn = $('.preview-pdf.uddrag'); 209 210 if (window.location.hash === '#uddrag' && $excerptBtn.length > 0) { 211 $excerptBtn.trigger('click'); 212 } 213 } 214 }; 215 216 var checkoutPage = { 217 init: function () { 218 var self = this; 219 220 self.$checkout = $('.woocommerce-checkout'); 221 self.$showMoreButton = $('.button-link.show-more'); 222 self._changePaymentForm = document.querySelector('.change-payment-method'); 223 224 // Inline validation 225 self.$checkout.on('input validate change', '.validate-house .input-text', self.validate_house_field); 226 self.$checkout.on('input validate change', '.validate-phone .input-text', self.validate_phone_field); 227 228 self.addressPlaces = { 229 'billing': {}, 230 'shipping': {} 231 }; 232 233 self.eventHandlers = {}; 234 self.eventHandlers.addressInputHandler = self.addressInputHandler.bind(self); 235 self.eventHandlers.addressCountryHandler = self.addressCountryHandler.bind(self); 236 self.eventHandlers.addressPostcodeHandler = self.addressPostcodeHandler.bind(self); 237 238 self.applyCouponOnCheckoutPage(); 239 self.changePaymentAutoSubmit(); 240 }, 241 242 initPlaces: function () { 243 this.addressPlaceInit(); 244 }, 245 246 applyCouponOnCheckoutPage: function () { 247 let self = this; 248 249 $('.woocommerce-checkout').on('click', 'button#nm-apply-coupon-btn', function (e) { 250 e.stopPropagation(); 251 252 $('tr.cart-coupon-error span').text(''); 253 254 var data = { 255 security: wc_checkout_params.apply_coupon_nonce, 256 coupon_code: $('input#nm-coupon-code').val() 257 }; 258 259 if (data.coupon_code === '') { 260 $('tr.cart-coupon-error span').text(woocommerce_errors.wc_coupon_please_enter); 261 return; 262 } 263 264 // Trigger "adding_to_cart" event 265 //self.$body.trigger('adding_coupon', [$(this), data.coupon_code]); 266 267 $('#order_review').block({ 268 message: null, 269 overlayCSS: { 270 background: '#fff', 271 opacity: 0.6 272 } 273 }); 274 275 $.ajax({ 276 type: 'POST', 277 url: wc_checkout_params.wc_ajax_url.toString().replace('%%endpoint%%', 'apply_coupon'), 278 data: data, 279 success: function (response) { 280 $('#order_review').unblock(); 281 282 if ($(response).find('.woocommerce-error').length || $(response).hasClass('woocommerce-error')) { 283 $('tr.cart-coupon-error span').text($(response).text().trim()); 284 } else { 285 $(document.body).trigger('update_checkout', {update_shipping_method: false}); 286 } 287 }, 288 dataType: 'html' 289 }); 290 291 }); 292 293 }, 294 295 changePaymentAutoSubmit: function () { 296 var self = this; 297 298 if (self._changePaymentForm === null) { 299 return; 300 } 301 302 if (self._changePaymentForm.querySelector('.payment_methods').children.length > 1) { 303 return; 304 } 305 306 $(self._changePaymentForm).parent().block({ 307 message: null, 308 overlayCSS: { 309 background: '#fff', 310 opacity: 0.6 311 } 312 }); 313 314 window.addEventListener('load', function () { 315 self._changePaymentForm.submit(); 316 }); 317 }, 318 319 validate_house_field: function (e) { 320 var $this = $(this), 321 $parent = $this.closest('.form-row'), 322 validated = true, 323 validate_required = $parent.is('.validate-required'), 324 validate_house = $parent.is('.validate-house'), 325 event_type = e.type; 326 327 if ('input' === event_type) { 328 $parent.removeClass('woocommerce-invalid woocommerce-invalid-required-field woocommerce-invalid-email woocommerce-validated woocommerce-invalid-house'); // eslint-disable-line max-len 329 } 330 331 if ('validate' === event_type || 'change' === event_type) { 332 333 if (validate_required && validate_house) { 334 if ($this.val()) { 335 var pattern = new RegExp(/\d/gi); 336 337 if (!pattern.test($this.val())) { 338 $parent.removeClass('woocommerce-validated'); 339 $parent.addClass('woocommerce-invalid woocommerce-invalid-house'); 340 validated = false;
341 } 342 } 343 } 344 345 if (validated) { 346 $parent.removeClass('woocommerce-invalid woocommerce-invalid-required-field woocommerce-invalid-house') 347 $parent.addClass('woocommerce-validated'); // eslint-disable-line max-len 348 } 349 } 350 }, 351 352 validate_phone_field: function (e) { 353 var $this = $(this), 354 $parent = $this.closest('.form-row'), 355 validated = true, 356 validate_required = $parent.is('.validate-required'), 357 validate_phone = $parent.is('.validate-phone'), 358 event_type = e.type; 359 360 if ('input' === event_type) { 361 $this.val($this.val().replace(/[^0-9]/gi, '')); 362 $parent.removeClass('woocommerce-invalid woocommerce-invalid-required-field woocommerce-invalid-email woocommerce-validated woocommerce-invalid-phone'); // eslint-disable-line max-len 363 } 364 365 if ('validate' === event_type || 'change' === event_type) { 366 367 if (validate_required && validate_phone) { 368 var pattern = new RegExp(/[\s\#0-9_\-\+\/\(\)\.]/g); 369 370 if ($this.val() === '') { 371 $parent.removeClass('woocommerce-validated'); 372 $parent.addClass('woocommerce-invalid woocommerce-invalid-phone'); 373 validated = false; 374 } else if ($this.val().length < 8) { 375 $parent.removeClass('woocommerce-validated'); 376 $parent.addClass('woocommerce-invalid woocommerce-invalid-phone'); 377 validated = false; 378 } else if (0 < $this.val().replace(pattern, '').length) { 379 $parent.removeClass('woocommerce-validated'); 380 $parent.addClass('woocommerce-invalid woocommerce-invalid-phone'); 381 validated = false; 382 } 383 } 384 385 if (validated) { 386 $parent.removeClass('woocommerce-invalid woocommerce-invalid-required-field woocommerce-invalid-phone') 387 $parent.addClass('woocommerce-validated'); // eslint-disable-line max-len 388 } 389 } 390 }, 391 392 addressPlaceInit: function () { 393 this.addressPlaceTypeInit('billing') 394 this.addressPlaceTypeInit('shipping') 395 }, 396 397 addressPlaceTypeInit: function (type) { 398 let self = this; 399 400 if (!document.querySelector('.woocommerce-' + type + '-fields')) { 401 return; 402 } 403 404 this.addressPlaces[type].fields = this.addressFieldsInit(type); 405 406 this.addressPlaces[type].autocomplete = new google.maps.places.Autocomplete(this.addressPlaces[type].fields.address, { 407 componentRestrictions: {country: [this.addressPlaces[type].fields.country.value]}, 408 fields: ['place_id', 'name', 'address_components', 'geometry'], 409 types: ['address'], 410 }) 411 412 this.addressPlaces[type].fields.country.addEventListener("change", this.eventHandlers.addressCountryHandler) 413 $(this.addressPlaces[type].fields.country).on("select2:select", {type: type}, this.eventHandlers.addressCountryHandler) 414 $(this.addressPlaces[type].fields.postcode).on("input change", {type: type}, this.eventHandlers.addressPostcodeHandler) 415 $(this.addressPlaces[type].fields.address).on("keydown", {type: type}, this.eventHandlers.addressInputHandler) 416 this.addressPlaces[type].autocomplete.addListener("place_changed", function () { 417 self.autocompleteAddressFill(self.addressPlaces[type]); 418 }); 419 }, 420 421 addressFieldsInit: function (type) { 422 return { 423 address: document.getElementById(type + '_address_1'), 424 house_number: document.getElementById(type + '_house_number'), 425 country: document.getElementById(type + '_country'), 426 state: document.getElementById(type + '_state'), 427 postcode: document.getElementById(type + '_postcode'), 428 city: document.getElementById(type + '_city'), 429 } 430 }, 431 432 addressInputHandler: function (e) { 433 if (e.keyCode === 13 && e.data.type) { 434 const place = this.addressPlaces[e.data.type].autocomplete.getPlace(); 435 436 if (place === undefined || Object.hasOwn(place, 'address_components')) { 437 e.preventDefault(); 438 e.stopPropagation(); 439 } 440 } 441 }, 442 443 addressCountryHandler: function (e) { 444 if (e.data.type) { 445 this.addressPlaces[e.data.type].autocomplete.setComponentRestrictions({ 446 country: [e.currentTarget.value], 447 }); 448 449 this.addressPlaces[e.data.type].fields = this.addressFieldsInit(e.data.type); 450 } 451 }, 452 453 addressPostcodeHandler: function (e) { 454 if (e.data.type) { 455 let self = this, 456 fieldType = e.data.type, 457 country = this.addressPlaces[e.data.type].fields.country.value, 458 countryText = this.addressPlaces[e.data.type].fields.country.querySelector('option[value="' + country + '"]').text, 459 postcode = this.addressPlaces[e.data.type].fields.postcode.value, 460 searchAddress = []; 461 462 //searchAddress.push(this.addressPlaces[e.data.type].fields.address.value); 463 //searchAddress.push(this.addressPlaces[e.data.type].fields.house_number.value); 464 searchAddress.push(this.addressPlaces[e.data.type].fields.postcode.value); 465 searchAddress.push(countryText); 466 467 searchAddress.filter(function (value) { 468 return value !== ''; 469 }); 470 471 if (countryText && postcode) {
472 $.ajax('https://maps.googleapis.com/maps/api/geocode/json?language=da&address=' + encodeURIComponent(searchAddress.join(' ')) + '&key=' + theme_params.google_map_geocode_api) 473 .done(function (response) { 474 if (response.status === 'OK') { 475 var localityName, subLocalityName; 476 477 if (response.results.length) { 478 for (const components of response.results[0].address_components) { 479 if (components.types.includes('sublocality')) { 480 subLocalityName = components.long_name; 481 } 482 if (components.types.includes('locality') || components.types.includes('postal_town')) { 483 localityName = components.long_name; 484 } 485 } 486 487 if (response.results[0].formatted_address.indexOf(subLocalityName) !== -1) { 488 self.addressPlaces[fieldType].fields.city.value = subLocalityName; 489 } else if (response.results[0].formatted_address.indexOf(localityName) !== -1) { 490 self.addressPlaces[fieldType].fields.city.value = localityName; 491 } 492 } 493 } 494 }); 495 } 496 } 497 }, 498 499 autocompleteAddressFill: function (data) { 500 // Get the place details from the autocomplete object. 501 const place = data.autocomplete.getPlace(); 502 let address1 = '', house_number = '', city = '', postcode = '', state = ''; 503 504 if (Object.hasOwn(place, 'address_components')) { 505 // Get each component of the address from the place details, 506 // and then fill-in the corresponding field on the form. 507 // place.address_components are google.maps.GeocoderAddressComponent objects 508 // which are documented at http://goo.gle/3l5i5Mr 509 for (const component of place.address_components) { 510 // @ts-ignore remove once typings fixed 511 const componentType = component.types[0]; 512 513 switch (componentType) { 514 case "subpremise": { 515 house_number = `, ${component.long_name}`; 516 break; 517 } 518 case "point_of_interest": { 519 house_number = `, ${component.long_name}`; 520 break; 521 } 522 case "street_number": { 523 house_number = `${component.long_name}${house_number}`; 524 break; 525 } 526 case "route": { 527 address1 = `${address1}${component.short_name}`; 528 break; 529 } 530 case "postal_code": { 531 postcode = `${component.long_name}${postcode}`; 532 break; 533 } 534 case "postal_code_suffix": { 535 postcode = `${postcode}-${component.long_name}`; 536 break; 537 } 538 case "sublocality_level_1": 539 city = component.long_name; 540 break; 541 case "locality": 542 city = city === '' ? component.long_name : city; 543 break; 544 case "administrative_area_level_1": { 545 state = component.short_name; 546 break; 547 } 548 } 549 } 550 551 data.fields.address.value = address1; 552 data.fields.house_number.value = house_number; 553 data.fields.state.value = state; 554 data.fields.postcode.value = postcode; 555 data.fields.city.value = city; 556 // postalField.value = postcode; 557 // After filling the form with address components from the Autocomplete 558 // prediction, set cursor focus on the second address line to encourage 559 // entry of subpremise information such as apartment, unit, or floor number. 560 data.fields.house_number.focus(); 561 562 $(document.body).trigger('update_checkout'); 563 } 564 } 565 }; 566 567 var myAccountPage = { 568 569 init: function () { 570 var self = this; 571 }, 572 573 }; 574 575 var blogInfiniteScrolling = { 576 init: function () { 577 let pages_found = $('#nm-blog-infinite-load').attr('data-pages_found'); 578 579 if (parseInt(pages_found) > 1) { 580 $(window).scroll(function () { 581 if ($('#nm-blog-infinite-load').is(":visible")) { 582 var scroll = $(window).scrollTop() + $(window).height(); 583 var offset = $("#nm-blog-infinite-load").offset().top; 584 585 if (scroll > offset) { 586 if ($('body').hasClass('blog') || $('body').hasClass('category')) { 587 $('#nm-blog-infinite-load a').trigger('click'); 588 } 589 } 590 } 591 }); 592 } 593 594 }, 595 }; 596 597 main.init(); 598 sliderContents.init();
599 frontPage.init(); 600 shopPage.init(); 601 checkoutPage.init(); 602 myAccountPage.init(); 603 blogInfiniteScrolling.init(); 604 605 theme.initGoogleMap = function () { 606 checkoutPage.initPlaces(); 607 } 608 609})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.