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.