1/** 2 * Sparkling theme scripts. 3 * 4 * Plain DOM APIs -- no jQuery. The two globals (SparklingIsMobile and 5 * generateMobileMenu) are kept on window because child themes may call them. 6 */ 7( function () { 8 'use strict'; 9 10 /** 11 * Run a callback once the DOM is ready. 12 * 13 * @param {Function} fn Callback. 14 */ 15 function ready( fn ) { 16 if ( document.readyState !== 'loading' ) { 17 fn(); 18 } else { 19 document.addEventListener( 'DOMContentLoaded', fn ); 20 } 21 } 22 23 /** 24 * Add class names to every element matching a selector. 25 * 26 * @param {string} selector CSS selector. 27 * @param {Array} classes Class names to add. 28 */ 29 function addClasses( selector, classes ) { 30 document.querySelectorAll( selector ).forEach( function ( el ) { 31 el.classList.add.apply( el.classList, classes ); 32 } ); 33 } 34 35 /** 36 * jQuery's "swing" easing, so the scroll-to-top motion is unchanged. 37 * 38 * @param {number} p Progress from 0 to 1. 39 * @return {number} Eased progress. 40 */ 41 function swing( p ) { 42 return 0.5 - Math.cos( p * Math.PI ) / 2; 43 } 44 45 function prefersReducedMotion() { 46 return window.matchMedia && window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches; 47 } 48 49 /** 50 * Fade an element in or out, matching jQuery's default 400ms fade. 51 * 52 * @param {Element} el Target element. 53 * @param {boolean} into True to fade in, false to fade out. 54 */ 55 function fade( el, into ) { 56 if ( ! el || el.dataset.sparklingFading === String( into ) ) { 57 return; 58 } 59 60 el.dataset.sparklingFading = String( into ); 61 62 if ( prefersReducedMotion() ) { 63 el.style.opacity = into ? '1' : ''; 64 el.style.display = into ? 'block' : 'none'; 65 return; 66 } 67 68 var duration = 400; 69 var start = null; 70 var from = parseFloat( window.getComputedStyle( el ).opacity ); 71 72 if ( isNaN( from ) ) { 73 from = into ? 0 : 1; 74 } 75 76 if ( into ) { 77 el.style.display = 'block'; 78 } 79 80 function step( ts ) { 81 if ( start === null ) { 82 start = ts; 83 } 84 85 var p = Math.min( ( ts - start ) / duration, 1 ); 86 var v = from + ( ( into ? 1 : 0 ) - from ) * swing( p ); 87 88 el.style.opacity = String( v ); 89 90 if ( p < 1 ) { 91 window.requestAnimationFrame( step ); 92 } else if ( ! into ) { 93 el.style.display = 'none'; 94 el.style.opacity = ''; 95 } 96 } 97 98 window.requestAnimationFrame( step ); 99 } 100 101 /** 102 * Detect a mobile user agent. 103 * 104 * Kept as-is (including the UA sniff) because the mobile menu styling below 105 * depends on it and child themes may override this function. 106 * 107 * @return {boolean} True on a mobile user agent. 108 */ 109 window.SparklingIsMobile = function () { 110 return ( 111 navigator.userAgent.match( /Android/i ) || 112 navigator.userAgent.match( /webOS/i ) || 113 navigator.userAgent.match( /iPhone/i ) || 114 navigator.userAgent.match( /iPod/i ) || 115 navigator.userAgent.match( /iPad/i ) || 116 navigator.userAgent.match( /BlackBerry/ ) 117 ); 118 }; 119 120 /** 121 * Toggle the tap-friendly menu class on touch devices at tablet width and up. 122 */ 123 window.generateMobileMenu = function () { 124 var menu = document.querySelector( '#masthead .site-navigation-inner .navbar-collapse > ul.nav' ); 125 126 if ( ! menu ) { 127 return; 128 } 129 130 if ( window.SparklingIsMobile() && window.innerWidth > 767 ) { 131 menu.classList.add( 'sparkling-mobile-menu' ); 132 } else { 133 menu.classList.remove( 'sparkling-mobile-menu' ); 134 } 135 }; 136 137 /** 138 * Apply Bootstrap classes to markup WordPress generates. 139 */ 140 function styleCoreMarkup() { 141 addClasses( '.comment-reply-link', [ 'btn', 'btn-sm', 'btn-default' ] ); 142 addClasses( 143 '#submit, button[type=submit], html input[type=button], input[type=reset], input[type=submit]', 144 [ 'btn', 'btn-default' ] 145 ); 146 addClasses( '.widget_rss ul', [ 'media-list' ] ); 147 addClasses( '.postform', [ 'form-control' ] ); 148 addClasses( 'table#wp-calendar', [ 'table', 'table-striped' ] ); 149 } 150 151 /** 152 * Scroll-to-top button: reveal past 100px, and smooth-scroll on click. 153 */ 154 function initScrollToTop() { 155 var button = document.querySelector( '.scroll-to-top' ); 156 157 if ( ! button ) { 158 return; 159 } 160 161 function onScroll() { 162 fade( button, ( window.pageYOffset || document.documentElement.scrollTop ) > 100 ); 163 } 164 165 window.addEventListener( 'scroll', onScroll, { passive: true } ); 166 onScroll(); 167 168 button.addEventListener( 'click', function ( event ) { 169 event.preventDefault(); 170 171 var from = window.pageYOffset || document.documentElement.scrollTop; 172 173 if ( ! from ) { 174 return; 175 } 176 177 if ( prefersReducedMotion() ) { 178 window.scrollTo( 0, 0 ); 179 return; 180 } 181 182 var duration = 800; 183 var start = null; 184 185 function step( ts ) { 186 if ( start === null ) { 187 start = ts; 188 } 189 190 var p = Math.min( ( ts - start ) / duration, 1 ); 191 192 window.scrollTo( 0, from * ( 1 - swing( p ) ) ); 193 194 if ( p < 1 ) { 195 window.requestAnimationFrame( step ); 196 } 197 } 198 199 window.requestAnimationFrame( step ); 200 } ); 201 } 202 203 /** 204 * Dropdown toggles in the navbar. 205 */ 206 function initDropdowns() { 207 document.querySelectorAll( '.sparkling-dropdown' ).forEach( function ( el ) { 208 el.addEventListener( 'click', function () { 209 if ( el.parentNode ) { 210 el.parentNode.classList.toggle( 'open' ); 211 } 212 } ); 213 } ); 214 } 215 216 /** 217 * Reserve space under a fixed navbar so content is not hidden behind it. 218 */ 219 function initStickyHeader() { 220 var navbar = document.querySelector( '.navbar-fixed-top' ); 221 222 if ( ! navbar ) { 223 return; 224 } 225 226 var header = navbar.closest( 'header' ); 227 228 if ( ! header ) { 229 return; 230 } 231 232 // getComputedStyle().height resolves to the content box, matching the 233 // jQuery .height() this replaced. 234 var adminbar = document.getElementById( 'wpadminbar' ); 235 var navHeight = parseFloat( window.getComputedStyle( navbar ).height ) || 0; 236 var adminHeight = adminbar ? parseFloat( window.getComputedStyle( adminbar ).height ) || 0 : 0; 237 238 header.style.marginBottom = ( adminbar ? Math.abs( navHeight - adminHeight ) : navHeight ) + 'px'; 239 } 240 241 ready( function () { 242 styleCoreMarkup(); 243 initScrollToTop(); 244 initDropdowns(); 245 initStickyHeader(); 246 window.generateMobileMenu(); 247 248 window.addEventListener( 'resize', window.generateMobileMenu ); 249 } ); 250}() );
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.