PageSourceSearch

https://www.xn--forbrukslnoversikt-dub.com/wp-content/themes/best-shop/js/modal.js?ver=1.6.3

js xn--forbrukslnoversikt-dub.com collected 2026-10-09 22:41:55 UTC 17,395 bytes, 475 lines download raw bytes

1var bestshop = bestshop || {};
2
3// event "polyfill"
4bestshop.createEvent = function( eventName ) {
5    var event;
6    if ( typeof window.Event === 'function' ) {
7        event = new Event( eventName );
8    } else {
9        event = document.createEvent( 'Event' );
10        event.initEvent( eventName, true, false );
11    }
12    return event;
13};
14
15/*  -----------------------------------------------------------------------------------------------
16    Cover Modals
17--------------------------------------------------------------------------------------------------- */
18
19bestshop.coverModals = {
20
21    init: function() {
22        if ( document.querySelector( '.cover-modal' ) ) {
23            // Handle cover modals when they're toggled.
24            this.onToggle();
25
26            // Close on escape key press.
27            this.closeOnEscape();
28
29            // Hide and show modals before and after their animations have played out.
30            this.hideAndShowModals();
31
32            this.keepFocusInModal();
33        }
34    },
35
36    // Handle cover modals when they're toggled.
37    onToggle: function() {
38        document.querySelectorAll( '.cover-modal' ).forEach( function( element ) {
39            element.addEventListener( 'toggled', function( event ) {
40                var modal = event.target,
41                    body = document.body;
42
43                if ( modal.classList.contains( 'active' ) ) {
44                    body.classList.add( 'showing-modal' );
45                } else {
46                    body.classList.remove( 'showing-modal' );
47                    body.classList.add( 'hiding-modal' );
48
49                    // Remove the hiding class after a delay, when animations have been run.
50                    setTimeout( function() {
51                        body.classList.remove( 'hiding-modal' );
52                    }, 500 );
53                }
54            } );
55        } );
56    },
57
58    // Close modal on escape key press.
59    closeOnEscape: function() {
60        document.addEventListener( 'keydown', function( event ) {
61            if ( event.keyCode === 27 ) {
62                event.preventDefault();
63                document.querySelectorAll( '.cover-modal.active' ).forEach( function( element ) {
64                    this.untoggleModal( element );
65                }.bind( this ) );
66            }
67        }.bind( this ) );
68    },
69
70    // Hide and show modals before and after their animations have played out.
71    hideAndShowModals: function() {
72        var _doc = document,
73            _win = window,
74            modals = _doc.querySelectorAll( '.cover-modal' ),
75            htmlStyle = _doc.documentElement.style,
76            adminBar = _doc.querySelector( '#wpadminbar' );
77
78        function getAdminBarHeight( negativeValue ) {
79            var height,
80                currentScroll = _win.pageYOffset;
81
82            if ( adminBar ) {
83                height = currentScroll + adminBar.getBoundingClientRect().height;
84
85                return negativeValue ? -height : height;
86            }
87
88            return currentScroll === 0 ? 0 : -currentScroll;
89        }
90
91        function htmlStyles() {
92            var overflow = _win.innerHeight > _doc.documentElement.getBoundingClientRect().height;
93
94            return {
95                'overflow-y': overflow ? 'hidden' : 'scroll',
96                position: 'fixed',
97                width: '100%',
98                top: getAdminBarHeight( true ) + 'px',
99                left: 0
100            };
101        }
102
103        // Show the modal.
104        modals.forEach( function( modal ) {
105            modal.addEventListener( 'toggle-target-before-inactive', function( event ) {
106                var styles = htmlStyles(),
107                    offsetY = _win.pageYOffset,
108                    paddingTop = ( Math.abs( getAdminBarHeight() ) - offsetY ) + 'px',
109                    mQuery = _win.matchMedia( '(max-width: 600px)' );
110
111                if ( event.target !== modal ) {
112                    return;
113                }
114
115                Object.keys( styles ).forEach( function( styleKey ) {
116                    htmlStyle.setProperty( styleKey, styles[ styleKey ] );
117                } );
118
119                _win.bestshop.scrolled = parseInt( styles.top, 10 );
120
121                if ( adminBar ) {
122                    _doc.body.style.setProperty( 'padding-top', paddingTop );
123
124                    if ( mQuery.matches ) {
125                        if ( offsetY >= getAdminBarHeight() ) {
126                            modal.style.setProperty( 'top', 0 );
127                        } else {
128                            modal.style.setProperty( 'top', ( getAdminBarHeight() - offsetY ) + 'px' );
129                        }
130                    }
131                }
132
133                modal.classList.add( 'show-modal' );
134            } );
135
136            // Hide the modal after a delay, so animations have time to play out.
137            modal.addEventListener( 'toggle-target-after-inactive', function( event ) {
138                if ( event.target !== modal ) {
139                    return;
140                }
141
142                setTimeout( function() {
143                    var clickedEl = bestshop.toggles.clickedEl;
144
145                    modal.classList.remove( 'show-modal' );
146
147                    Object.keys( htmlStyles() ).forEach( function( styleKey ) {
148                        htmlStyle.removeProperty( styleKey );
149                    } );
150
151                    if ( adminBar ) {
152                        _doc.body.style.removeProperty( 'padding-top' );
153                        modal.style.removeProperty( 'top' );
154                    }
155
156                    if ( clickedEl !== false ) {
157                        clickedEl.focus();
158                        clickedEl = false;
159                    }
160
161                    _win.scrollTo( 0, Math.abs( _win.bestshop.scrolled + getAdminBarHeight() ) );
162
163                    _win.bestshop.scrolled = 0;
164                }, 500 );
165            } );
166        } );
167    },
168
169    // Untoggle a modal.
170    untoggleModal: function( modal ) {
171        var modalTargetClass,
172            modalToggle = false;
173
174        // If the modal has specified the string (ID or class) used by toggles to target it, untoggle the toggles with that target string.
175        // The modal-target-string must match the string toggles use to target the modal.
176        if ( modal.dataset.modalTargetString ) {
177            modalTargetClass = modal.dataset.modalTargetString;
178
179            modalToggle = document.querySelector( '*[data-toggle-target="' + modalTargetClass + '"]' );
180        }
181
182        // If a modal toggle exists, trigger it so all of the toggle options are included.
183        if ( modalToggle ) {
184            modalToggle.click();
185
186            // If one doesn't exist, just hide the modal.
187        } else {
188            modal.classList.remove( 'active' );
189        }
190    },
191
192    keepFocusInModal: function() {
193        var _doc = document;
194
195        _doc.addEventListener( 'keydown', function( event ) {
196            var toggleTarget, modal, selectors, elements, menuType, bottomMenu, activeEl, lastEl, firstEl, tabKey, shiftKey,
197                clickedEl = bestshop.toggles.clickedEl;
198
199            if ( clickedEl && _doc.body.classList.contains( 'showing-modal' ) ) {
200                toggleTarget = clickedEl.dataset.toggleTarget;
201                selectors = 'input, a, button';
202                modal = _doc.querySelector( toggleTarget );
203
204                elements = modal.querySelectorAll( selectors );
205                elements = Array.prototype.slice.call( elements );
206
207                if ( '.menu-modal' === toggleTarget ) {
208                    menuType = window.matchMedia( '(min-width: 9999px)' ).matches;
209                    menuType = menuType ? '.expanded-menu' : '.mobile-menu';
210
211                    elements = elements.filter( function( element ) {
212                        return null !== element.closest( menuType ) && null !== element.offsetParent;
213                    } );
214
215                    elements.unshift( _doc.querySelector( '.close-nav-toggle' ) );
216
217                    bottomMenu = _doc.querySelector( '.menu-bottom > nav' );
218
219                    if ( bottomMenu ) {
220                        bottomMenu.querySelectorAll( selectors ).forEach( function( element ) {
221                            elements.push( element );
222                        } );
223                    }
224                }
225
226                if ( '.main-menu-modal' === toggleTarget ) {
227                    menuType = window.matchMedia( '(min-width: 1025px)' ).matches;
228                    menuType = menuType ? '.expanded-menu' : '.mobile-menu';
229
230                    elements = elements.filter( function( element ) {
231                        return null !== element.closest( menuType ) && null !== element.offsetParent;
232                    } );
233
234                    elements.unshift( _doc.querySelector( '.close-main-nav-toggle' ) );
235
236                    bottomMenu = _doc.querySelector( '.menu-bottom > nav' );
237
238                    if ( bottomMenu ) {
239                        bottomMenu.querySelectorAll( selectors ).forEach( function( element ) {
240                            elements.push( element );
241                        } );
242                    }
243                }
244
245                lastEl = elements[ elements.length - 1 ];
246                firstEl = elements[0];
247                activeEl = _doc.activeElement;
248                tabKey = event.keyCode === 9;
249                shiftKey = event.shiftKey;
250
251                if ( ! shiftKey && tabKey && lastEl === activeEl ) {
252                    event.preventDefault();
253                    firstEl.focus();
254                }
255
256                if ( shiftKey && tabKey && firstEl === activeEl ) {
257                    event.preventDefault();
258                    lastEl.focus();
259                }
260            }
261        } );
262    }
263
264}; // bestshop.coverModals
265
266bestshop.modalMenu = {
267
268    init: function() {
269        // If the current menu item is in a sub level, expand all the levels higher up on load.
270        this.expandLevel();
271    },
272
273    expandLevel: function() {
274        var modalMenus = document.querySelectorAll( '.modal-menu' );
275
276        modalMenus.forEach( function( modalMenu ) {
277            var activeMenuItem = modalMenu.querySelector( '.current-menu-item' );
278
279            if ( activeMenuItem ) {
280                bestshopFindParents( activeMenuItem, 'li' ).forEach( function( element ) {
281                    var subMenuToggle = element.querySelector( '.submenu-toggle' );
282                    if ( subMenuToggle ) {
283                        bestshop.toggles.performToggle( subMenuToggle, true );
284                    }
285                } );
286            }
287        } );
288    },
289}; // bestshop.modalMenu
290
291bestshop.toggles = {
292
293    clickedEl: false,
294
295    init: function() {
296        // Do the toggle.
297        this.toggle();
298    },
299
300    performToggle: function( element, instantly ) {
301        var target, timeOutTime, classToToggle,
302            self = this,
303            _doc = document,
304            // Get our targets.
305            toggle = element,
306            targetString = toggle.dataset.toggleTarget,
307            activeClass = 'active';
308
309        // Elements to focus after modals are closed.
310        if ( ! _doc.querySelectorAll( '.show-modal' ).length ) {
311            self.clickedEl = _doc.activeElement;
312        }
313
314        if ( targetString === 'next' ) {
315            target = toggle.nextSibling;
316        } else {
317            target = _doc.querySelector( targetString );
318        }
319
320        // Trigger events on the toggle targets before they are toggled.
321        if ( target.classList.contains( activeClass ) ) {
322            target.dispatchEvent( bestshop.createEvent( 'toggle-target-before-active' ) );
323        } else {
324            target.dispatchEvent( bestshop.createEvent( 'toggle-target-before-inactive' ) );
325        }
326
327        // Get the class to toggle, if specified.
328        classToToggle = toggle.dataset.classToToggle ? toggle.dataset.classToToggle : activeClass;
329
330        // For cover modals, set a short timeout duration so the class animations have time to play out.
331        timeOutTime = 0;
332
333        if ( target.classList.contains( 'cover-modal' ) ) {
334            timeOutTime = 10;
335        }
336
337        setTimeout( function() {
338            var focusElement,
339                subMenued = target.classList.contains( 'sub-menu' ),
340                newTarget = subMenued ? toggle.closest( '.menu-item' ).querySelector( '.sub-menu' ) : target,
341                duration = toggle.dataset.toggleDuration;
342
343            // Toggle the target of the clicked toggle.
344            if ( toggle.dataset.toggleType === 'slidetoggle' && ! instantly && duration !== '0' ) {
345                bestshopMenuToggle( newTarget, duration );
346            } else {
347                newTarget.classList.toggle( classToToggle );
348            }
349
350            // If the toggle target is 'next', only give the clicked toggle the active class.
351            if ( targetString === 'next' ) {
352                toggle.classList.toggle( activeClass );
353            } else if ( target.classList.contains( 'sub-menu' ) ) {
354                toggle.classList.toggle( activeClass );
355            } else {
356                // If not, toggle all toggles with this toggle target.
357                _doc.querySelector( '*[data-toggle-target="' + targetString + '"]' ).classList.toggle( activeClass );
358            }
359
360            // Toggle aria-expanded on the toggle.
361            bestshopToggleAttribute( toggle, 'aria-expanded', 'true', 'false' );
362
363            if ( self.clickedEl && -1 !== toggle.getAttribute( 'class' ).indexOf( 'close-' ) ) {
364                bestshopToggleAttribute( self.clickedEl, 'aria-expanded', 'true', 'false' );
365            }
366
367            // Toggle body class.
368            if ( toggle.dataset.toggleBodyClass ) {
369                _doc.body.classList.toggle( toggle.dataset.toggleBodyClass );
370            }
371
372            // Check whether to set focus.
373            if ( toggle.dataset.setFocus ) {
374                focusElement = _doc.querySelector( toggle.dataset.setFocus );
375
376                if ( focusElement ) {
377                    if ( target.classList.contains( activeClass ) ) {
378                        focusElement.focus();
379                    } else {
380                        focusElement.blur();
381                    }
382                }
383            }
384
385            // Trigger the toggled event on the toggle target.
386            target.dispatchEvent( bestshop.createEvent( 'toggled' ) );
387
388            // Trigger events on the toggle targets after they are toggled.
389            if ( target.classList.contains( activeClass ) ) {
390                target.dispatchEvent( bestshop.createEvent( 'toggle-target-after-active' ) );
391            } else {
392                target.dispatchEvent( bestshop.createEvent( 'toggle-target-after-inactive' ) );
393            }
394        }, timeOutTime );
395    },
396
397    // Do the toggle.
398    toggle: function() {
399        var self = this;
400
401        document.querySelectorAll( '*[data-toggle-target]' ).forEach( function( element ) {
402            element.addEventListener( 'click', function( event ) {
403                event.preventDefault();
404                self.performToggle( element );
405            } );
406        } );
407    },
408
409}; // bestshop.toggles
410
411/**
412 * Is the DOM ready?
413 *
414 * This implementation is coming from https://gomakethings.com/a-native-javascript-equivalent-of-jquerys-ready-method/
415 *
416 * @param {Function} fn Callback function to run.
417 */
418function bestshopDomReady( fn ) {
419    if ( typeof fn !== 'function' ) {
420        return;
421    }
422
423    if ( document.readyState === 'interactive' || document.readyState === 'complete' ) {
424        return fn();
425    }
426
427    document.addEventListener( 'DOMContentLoaded', fn, false );
428}
429
430bestshopDomReady( function() {
431    bestshop.toggles.init();              // Handle toggles.
432    bestshop.coverModals.init();          // Handle cover modals.
433} );
434
435/* Toggle an attribute ----------------------- */
436
437function bestshopToggleAttribute( element, attribute, trueVal, falseVal ) {
438    if ( trueVal === undefined ) {
439        trueVal = true;
440    }
441    if ( falseVal === undefined ) {
442        falseVal = false;
443    }
444    if ( element.getAttribute( attribute ) !== trueVal ) {
445        element.setAttribute( attribute, trueVal );
446    } else {
447        element.setAttribute( attribute, falseVal );
448    }
449}
450
451/**
452 * Traverses the DOM up to find elements matching the query.
453 *
454 * @param {HTMLElement} target
455 * @param {string} query
456 * @return {NodeList} parents matching query
457 */
458function bestshopFindParents( target, query ) {
459    var parents = [];
460
461    // Recursively go up the DOM adding matches to the parents array.
462    function traverse( item ) {
463        var parent = item.parentNode;
464        if ( parent instanceof HTMLElement ) {
465            if ( parent.matches( query ) ) {
466                parents.push( parent );
467            }
468            traverse( parent );
469        }
470    }
471
472    traverse( target );
473
474    return parents;
475}

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.