PageSourceSearch

https://muusmann-forlag.dk/wp-content/themes/muusmann/assets/js/main.js?ver=0.0.1.1785733294

js muusmann-forlag.dk collected 2026-10-02 08:22:48 UTC 24,117 bytes, 609 lines download raw bytes

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.