1/* global wpcom_reblog */ 2 3var jetpackLikesWidgetBatch = []; 4var jetpackLikesMasterReady = false; 5 6// Due to performance problems on pages with a large number of widget iframes that need to be loaded, 7// we are limiting the processing at any instant to unloaded widgets that are currently in viewport, 8// plus this constant that will allow processing of widgets above and bellow the current fold. 9// This aim of it is to improve the UX and hide the transition from unloaded to loaded state from users. 10var jetpackLikesLookAhead = 2000; // pixels 11 12// Keeps track of loaded comment likes widget so we can unload them when they are scrolled out of view. 13var jetpackCommentLikesLoadedWidgets = []; 14 15var jetpackLikesDocReadyPromise = new Promise( resolve => { 16 if ( document.readyState !== 'loading' ) { 17 resolve(); 18 } else { 19 window.addEventListener( 'DOMContentLoaded', () => resolve() ); 20 } 21} ); 22 23function JetpackLikesPostMessage( message, target ) { 24 if ( typeof message === 'string' ) { 25 try { 26 message = JSON.parse( message ); 27 } catch ( e ) { 28 return; 29 } 30 } 31 32 if ( target && typeof target.postMessage === 'function' ) { 33 try { 34 target.postMessage( 35 JSON.stringify( { 36 type: 'likesMessage', 37 data: message, 38 } ), 39 '*' 40 ); 41 } catch ( e ) { 42 return; 43 } 44 } 45} 46 47function JetpackLikesBatchHandler() { 48 const requests = []; 49 document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ).forEach( widget => { 50 if ( jetpackLikesWidgetBatch.indexOf( widget.id ) > -1 ) { 51 return; 52 } 53 54 if ( ! jetpackIsScrolledIntoView( widget ) ) { 55 return; 56 } 57 58 jetpackLikesWidgetBatch.push( widget.id ); 59 60 var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/, 61 match = regex.exec( widget.id ), 62 info; 63 64 if ( ! match || match.length !== 5 ) { 65 return; 66 } 67 68 info = { 69 blog_id: match[ 2 ], 70 width: widget.width, 71 }; 72 73 if ( 'post' === match[ 1 ] ) { 74 info.post_id = match[ 3 ]; 75 } else if ( 'comment' === match[ 1 ] ) { 76 info.comment_id = match[ 3 ]; 77 } 78 79 info.obj_id = match[ 4 ]; 80 81 requests.push( info ); 82 } ); 83 84 if ( requests.length > 0 ) { 85 JetpackLikesPostMessage( 86 { event: 'initialBatch', requests: requests }, 87 window.frames[ 'likes-master' ] 88 ); 89 } 90} 91 92function JetpackLikesMessageListener( event ) { 93 let message = event && event.data; 94 if ( typeof message === 'string' ) { 95 try { 96 message = JSON.parse( message ); 97 } catch ( err ) { 98 return; 99 } 100 } 101 102 const type = message && message.type; 103 const data = message && message.data; 104 105 if ( type !== 'likesMessage' || typeof data.event === 'undefined' ) { 106 return; 107 } 108 109 // We only allow messages from one origin 110 const allowedOrigin = 'https://widgets.wp.com'; 111 if ( allowedOrigin !== event.origin ) { 112 return; 113 } 114 115 switch ( data.event ) { 116 case 'masterReady': 117 jetpackLikesDocReadyPromise.then( () => { 118 jetpackLikesMasterReady = true; 119 120 const stylesData = { 121 event: 'injectStyles', 122 }; 123 const sdTextColor = document.querySelector( '.sd-text-color' ); 124 const sdLinkColor = document.querySelector( '.sd-link-color' ); 125 const sdTextColorStyles = ( sdTextColor && getComputedStyle( sdTextColor ) ) || {}; 126 const sdLinkColorStyles = ( sdLinkColor && getComputedStyle( sdLinkColor ) ) || {}; 127 128 // enable reblogs if they are enabled for the page 129 if ( document.body.classList.contains( 'jetpack-reblog-enabled' ) ) { 130 JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] ); 131 } 132 133 stylesData.textStyles = { 134 color: sdTextColorStyles[ 'color' ],
135 fontFamily: sdTextColorStyles[ 'font-family' ], 136 fontSize: sdTextColorStyles[ 'font-size' ], 137 direction: sdTextColorStyles[ 'direction' ], 138 fontWeight: sdTextColorStyles[ 'font-weight' ], 139 fontStyle: sdTextColorStyles[ 'font-style' ], 140 textDecoration: sdTextColorStyles[ 'text-decoration' ], 141 }; 142 143 stylesData.linkStyles = { 144 color: sdLinkColorStyles[ 'color' ], 145 fontFamily: sdLinkColorStyles[ 'font-family' ], 146 fontSize: sdLinkColorStyles[ 'font-size' ], 147 textDecoration: sdLinkColorStyles[ 'text-decoration' ], 148 fontWeight: sdLinkColorStyles[ 'font-weight' ], 149 fontStyle: sdLinkColorStyles[ 'font-style' ], 150 }; 151 152 JetpackLikesPostMessage( stylesData, window.frames[ 'likes-master' ] ); 153 154 JetpackLikesBatchHandler(); 155 } ); 156 157 break; 158 159 // We're keeping this for planned future follow ups. 160 // @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815 161 case 'showLikeWidget': 162 break; 163 164 // We're keeping this for planned future follow ups. 165 // @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815 166 case 'showCommentLikeWidget': 167 break; 168 169 case 'killCommentLikes': 170 // If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders. 171 document 172 .querySelectorAll( '.jetpack-comment-likes-widget-wrapper' ) 173 .forEach( wrapper => wrapper.remove() ); 174 break; 175 176 case 'clickReblogFlair': 177 if ( wpcom_reblog && typeof wpcom_reblog.toggle_reblog_box_flair === 'function' ) { 178 wpcom_reblog.toggle_reblog_box_flair( data.obj_id, data.post_id ); 179 } 180 break; 181 182 case 'hideOtherGravatars': { 183 hideLikersPopover(); 184 break; 185 } 186 187 case 'showOtherGravatars': { 188 const container = document.querySelector( '#likes-other-gravatars' ); 189 190 if ( ! container ) { 191 break; 192 } 193 194 const list = container.querySelector( 'ul' ); 195 196 container.style.display = 'none'; 197 list.innerHTML = ''; 198 199 container 200 .querySelectorAll( '.likes-text span' ) 201 .forEach( item => ( item.textContent = data.totalLikesLabel ) ); 202 203 ( data.likers || [] ).forEach( async ( liker, index ) => { 204 if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) { 205 // We only display gravatars with http or https schema 206 return; 207 } 208 209 const element = document.createElement( 'li' ); 210 list.append( element ); 211 212 element.innerHTML = ` 213 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker"> 214 <img src="${ encodeURI( liker.avatar_URL ) }" 215 alt="" 216 style="width: 28px; height: 28px;" /> 217 <span></span> 218 </a> 219 `; 220 221 222 // Add some extra attributes through native methods, to ensure strings are sanitized. 223 element.classList.add( liker.css_class ); 224 element.querySelector( 'img' ).alt = data.avatarAltTitle.replace( '%s', liker.name ); 225 element.querySelector( 'span' ).innerText = liker.name; 226 227 if ( index === data.likers.length - 1 ) { 228 element.addEventListener( 'keydown', ( e ) => { 229 if ( e.key === 'Tab' && ! e.shiftKey ) { 230 e.preventDefault(); 231 hideLikersPopover(); 232 233 JetpackLikesPostMessage( 234 { event: 'focusLikesCount', parent: data.parent }, 235 window.frames[ 'likes-master' ] 236 ); 237 } 238 } ); 239 } 240 } ); 241 242 const positionPopup = function() { 243 const containerStyle = getComputedStyle(container); 244 const isRtl = containerStyle.direction === 'rtl'; 245 246 const el = document.querySelector( `*[name='${ data.parent }']` ); 247 const rect = el.getBoundingClientRect(); 248 const win = el.ownerDocument.defaultView; 249 250 const offset = { 251 top: rect.top + win.pageYOffset, 252 left: rect.left + win.pageXOffset, 253 }; 254 255 // don't display yet or we get skewed window.innerWidth later 256 container.style.display = 'none'; 257 258 let containerLeft = 0; 259 container.style.top = offset.top + data.position.top - 1 + 'px'; 260 261 if ( isRtl ) { 262 const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0; 263 // 24px is the width of the avatar + 4px is the padding between avatars 264 containerLeft = offset.left + data.position.left + 24 * visibleAvatarsCount + 4; 265 container.style.transform = 'translateX(-100%)'; 266 } else { 267 containerLeft = offset.left + data.position.left; 268 } 269 container.style.left = containerLeft + 'px'; 270 271 // Container width - padding
272 const initContainerWidth = data.width - 20; 273 const rowLength = Math.floor( initContainerWidth / 37 ); 274 // # of rows + (avatar + avatar padding) + text above + container padding 275 let height = Math.ceil( data.likers.length / rowLength ) * 37 + 17 + 22; 276 if ( height > 204 ) { 277 height = 204; 278 } 279 280 // If the popup is overflows viewport width, we should show it on the next line 281 // Push it offscreen to calculated rendered width 282 const windowWidth = win.innerWidth; 283 container.style.left = '-9999px'; 284 container.style.display = 'block'; 285 286 // If the popup exceeds the viewport width, 287 // flip the position of the popup. 288 const containerWidth = container.offsetWidth; 289 const containerRight = containerLeft + containerWidth; 290 291 if ( containerRight > windowWidth && ! isRtl) { 292 containerLeft = rect.left + rect.width - containerWidth; 293 } else if ( containerLeft - containerWidth < 0 && isRtl ) { 294 container.style.transform = 'none'; 295 containerLeft = rect.left; 296 } 297 // Set the container left 298 container.style.left = containerLeft + 'px'; 299 300 container.setAttribute( 'aria-hidden', 'false' ); 301 } 302 303 positionPopup(); 304 container.focus(); 305 306 const debounce = function( func, wait ) { 307 var timeout; 308 return function() { 309 var context = this; 310 var args = arguments; 311 clearTimeout( timeout ); 312 timeout = setTimeout( function() { 313 func.apply( context, args ); 314 }, wait ); 315 }; 316 }; 317 318 const debouncedPositionPopup = debounce( positionPopup, 100 ); 319 320 // Keep a reference of this function in the element itself 321 // so that we can destroy it later 322 container.__resizeHandler = debouncedPositionPopup; 323 324 // When window is resized, resize the popup. 325 window.addEventListener( "resize", debouncedPositionPopup ); 326 } 327 } 328} 329 330window.addEventListener( 'message', JetpackLikesMessageListener ); 331 332function hideLikersPopover() { 333 const container = document.querySelector( '#likes-other-gravatars' ); 334 335 if ( container ) { 336 container.style.display = 'none'; 337 container.setAttribute( 'aria-hidden', 'true' ); 338 339 // Remove the resize event listener and cleanup. 340 const resizeHandler = container.__resizeHandler; 341 if ( resizeHandler ) { 342 window.removeEventListener( "resize", resizeHandler ); 343 delete container.__resizeHandler; 344 } 345 } 346} 347 348document.addEventListener( 'click', hideLikersPopover ); 349 350function JetpackLikesWidgetQueueHandler() { 351 var wrapperID; 352 353 if ( ! jetpackLikesMasterReady ) { 354 setTimeout( JetpackLikesWidgetQueueHandler, 500 ); 355 return; 356 } 357 358 // Restore widgets to initial unloaded state when they are scrolled out of view. 359 jetpackUnloadScrolledOutWidgets(); 360 361 var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView(); 362 363 if ( unloadedWidgetsInView.length > 0 ) { 364 // Grab any unloaded widgets for a batch request 365 JetpackLikesBatchHandler(); 366 } 367 368 for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) { 369 wrapperID = unloadedWidgetsInView[ i ].id; 370 371 if ( ! wrapperID ) { 372 continue; 373 } 374 375 jetpackLoadLikeWidgetIframe( wrapperID ); 376 } 377} 378 379function jetpackLoadLikeWidgetIframe( wrapperID ) { 380 if ( typeof wrapperID === 'undefined' ) { 381 return; 382 } 383 384 const wrapper = document.querySelector( '#' + wrapperID ); 385 wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() ); 386 387 const placeholder = wrapper.querySelector( '.likes-widget-placeholder' ); 388 389 // Post like iframe 390 if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) { 391 const postLikesFrame = document.createElement( 'iframe' ); 392 393 postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' ); 394 postLikesFrame.name = wrapper.dataset.name; 395 postLikesFrame.src = wrapper.dataset.src; 396 postLikesFrame.height = '55px'; 397 postLikesFrame.width = '100%'; 398 postLikesFrame.frameBorder = '0'; 399 postLikesFrame.scrolling = 'no'; 400 postLikesFrame.title = wrapper.dataset.title; 401 402 placeholder.after( postLikesFrame ); 403 } 404 405 // Comment like iframe 406 if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) { 407 const commentLikesFrame = document.createElement( 'iframe' ); 408 409 commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame'; 410 commentLikesFrame.name = wrapper.dataset.name; 411 commentLikesFrame.src = wrapper.dataset.src; 412 commentLikesFrame.height = '18px'; 413 commentLikesFrame.width = '100%'; 414 commentLikesFrame.frameBorder = '0'; 415 commentLikesFrame.scrolling = 'no'; 416 417 wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame ); 418 419 jetpackCommentLikesLoadedWidgets.push( commentLikesFrame ); 420 } 421 422 wrapper.classList.remove( 'jetpack-likes-widget-unloaded' ); 423 wrapper.classList.add( 'jetpack-likes-widget-loading' ); 424 425 wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => { 426 JetpackLikesPostMessage( 427 { event: 'loadLikeWidget', name: e.target.name, width: e.target.width }, 428 window.frames[ 'likes-master' ] 429 ); 430 431 wrapper.classList.remove( 'jetpack-likes-widget-loading' ); 432 wrapper.classList.add( 'jetpack-likes-widget-loaded' ); 433 } ); 434} 435 436function jetpackGetUnloadedWidgetsInView() { 437 const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ); 438 439 return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) ); 440} 441 442function jetpackIsScrolledIntoView( element ) { 443 const top = element.getBoundingClientRect().top; 444 const bottom = element.getBoundingClientRect().bottom; 445
446 // Allow some slack above and bellow the fold with jetpackLikesLookAhead, 447 // with the aim of hiding the transition from unloaded to loaded widget from users. 448 return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead; 449} 450 451function jetpackUnloadScrolledOutWidgets() { 452 for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) { 453 const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ]; 454 455 if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) { 456 const widgetWrapper = 457 currentWidgetIframe && 458 currentWidgetIframe.parentElement && 459 currentWidgetIframe.parentElement.parentElement; 460 461 // Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed. 462 widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' ); 463 widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' ); 464 widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' ); 465 466 // Bring back the loading placeholder into view. 467 widgetWrapper 468 .querySelectorAll( '.comment-likes-widget-placeholder' ) 469 .forEach( item => ( item.style.display = 'block' ) ); 470 471 // Remove it from the list of loaded widgets. 472 jetpackCommentLikesLoadedWidgets.splice( i, 1 ); 473 474 // Remove comment like widget iFrame. 475 currentWidgetIframe.remove(); 476 } 477 } 478} 479 480var jetpackWidgetsDelayedExec = function ( after, fn ) { 481 var timer; 482 return function () { 483 clearTimeout( timer ); 484 timer = setTimeout( fn, after ); 485 }; 486}; 487 488var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler ); 489 490// Load initial batch of widgets, prior to any scrolling events. 491JetpackLikesWidgetQueueHandler(); 492 493// Add event listener to execute queue handler after scroll. 494window.addEventListener( 'scroll', jetpackOnScrollStopped, true ); 495; 496/** 497 * Bricklayer - Cascading Grid Layouts 498 * https://github.com/ademilter/bricklayer 499 * Copyright © 2016 Adem İlter 500 * 501 * Permission is hereby granted, free of charge, to any person obtaining 502 * a copy of this software and associated documentation files (the "Software"), 503 * to deal in the Software without restriction, including without limitation 504 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 505 * and/or sell copies of the Software, and to permit persons to whom the 506 * Software is furnished to do so, subject to the following conditions: 507 * 508 * The above copyright notice and this permission notice shall be included 509 * in all copies or substantial portions of the Software. 510 * 511 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 512 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 513 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 514 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 515 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 516 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 517 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 518 */ 519(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 520 var __extends = (this && this.__extends) || function (d, b) { 521 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 522 function __() { this.constructor = d; } 523 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 524 }; 525 var Bricklayer; 526 (function (Bricklayer) { 527 // Helper Functions 528 function toArray(arrayLike) { 529 return [].slice.call(arrayLike); 530 } 531 function triggerEvent(el, eventName, data) { 532 var event; 533 try { 534 event = new CustomEvent(eventName, { detail: data }); 535 } 536 catch(err) { 537 event = document.createEvent('CustomEvent'); 538 event.initCustomEvent(eventName, true, true, data); 539 } 540 return el.dispatchEvent(event); 541 } 542 var DEFAULTS = { 543 rulerClassName: "bricklayer-column-sizer", 544 columnClassName: "bricklayer-column" 545 }; 546 var SimpleElement = (function () { 547 function SimpleElement(className) { 548 this.element = document.createElement("div"); 549 this.element.className = className; 550 } 551 SimpleElement.prototype.destroy = function () { 552 this.element.parentNode.removeChild(this.element); 553 }; 554 return SimpleElement; 555 }()); 556 var Ruler = (function (_super) { 557 __extends(Ruler, _super); 558 function Ruler() { 559 _super.apply(this, arguments); 560 } 561 Ruler.prototype.getWidth = function () { 562 this.element.setAttribute('style', "\n display: block;\n visibility: hidden !important;\n top: -1000px !important;\n "); 563 var width = this.element.offsetWidth; 564 this.element.removeAttribute('style'); 565 return width; 566 }; 567 return Ruler; 568 }(SimpleElement)); 569 var Column = (function (_super) { 570 __extends(Column, _super); 571 function Column() { 572 _super.apply(this, arguments); 573 } 574 return Column; 575 }(SimpleElement)); 576 var Container = (function () { 577 function Container(element, options) { 578 if (options === void 0) { options = DEFAULTS; } 579 this.element = element; 580 this.options = options; 581 this.build(); 582 this.buildResponsive(); 583 }
584 Container.prototype.append = function (item) { 585 var _this = this; 586 if (Array.isArray(item)) { 587 item.forEach(function (item) { return _this.append(item); }); 588 return; 589 } 590 var column = this.findMinHeightColumn(); 591 this.elements = toArray(this.elements).concat([item]); 592 this.applyPosition('append', column, item); 593 }; 594 Container.prototype.prepend = function (item) { 595 var _this = this; 596 if (Array.isArray(item)) { 597 item.forEach(function (item) { return _this.prepend(item); }); 598 return; 599 } 600 var column = this.findMinHeightColumn(); 601 this.elements = [item].concat(toArray(this.elements)); 602 this.applyPosition('prepend', column, item); 603 }; 604 Container.prototype.on = function (eventName, handler) { 605 // eventName may be: 606 // - breakpoint 607 // - afterAppend 608 // - beforeAppend 609 // - afterPrepend 610 // - beforePrepend 611 this.element.addEventListener("bricklayer." + eventName, handler); 612 return this; 613 }; 614 Container.prototype.redraw = function () { 615 var columnCount = this.columnCount; 616 this.checkColumnCount(false); 617 this.reorderElements(columnCount); 618 triggerEvent(this.element, "bricklayer.redraw", { columnCount: columnCount }); 619 }; 620 Container.prototype.destroy = function () { 621 var _this = this; 622 this.ruler.destroy(); 623 toArray(this.elements).forEach(function (el) { return _this.element.appendChild(el); }); 624 toArray(this.getColumns()).forEach(function (el) { return el.parentNode.removeChild(el); }); 625 triggerEvent(this.element, "bricklayer.destroy", {}); 626 }; 627 Container.prototype.build = function () { 628 this.ruler = new Ruler(this.options.rulerClassName); 629 this.elements = this.getElementsInOrder(); 630 this.element.insertBefore(this.ruler.element, this.element.firstChild); 631 }; 632 Container.prototype.buildResponsive = function () { 633 var _this = this; 634 window.addEventListener("resize", function (e) { return _this.checkColumnCount(); }); 635 this.checkColumnCount(); 636 this.on("breakpoint", function (e) { return _this.reorderElements(e.detail.columnCount); }); 637 if (this.columnCount >= 1) { 638 this.reorderElements(this.columnCount); 639 } 640 }; 641 Container.prototype.getColumns = function() { 642 var _this = this; 643 return Array.prototype.filter.call( _this.element.children, function( element, index, aElements ) { 644 var classNames = element.className.split( ' ' ); 645 return classNames.indexOf( _this.options.columnClassName ) > - 1; 646 } ); 647 }; 648 Container.prototype.findMinHeightColumn = function () { 649 var allColumns = toArray(this.getColumns()); 650 var heights = allColumns.map(function (column) { return column.offsetHeight; }); 651 var minHeight = Math.min.apply(null, heights); 652 return allColumns[heights.indexOf(minHeight)]; 653 }; 654 Container.prototype.getElementsInOrder = function() { 655 var _this = this; 656 return Array.prototype.filter.call( _this.element.children, function( element, index, aElements ) { 657 var classNames = element.className.split( ' ' ); 658 return classNames.indexOf( _this.options.columnClassName ) == - 1 && classNames.indexOf( _this.options.rulerClassName == - 1 ); 659 } ); 660 }; 661 Container.prototype.checkColumnCount = function (publish) { 662 if (publish === void 0) { publish = true; } 663 var columnCount = this.getColumnCount(); 664 if (this.columnCount !== columnCount) { 665 if (publish) { 666 triggerEvent(this.element, "bricklayer.breakpoint", { columnCount: columnCount }); 667 } 668 this.columnCount = columnCount; 669 } 670 }; 671 Container.prototype.reorderElements = function (columnCount) { 672 var _this = this; 673 if (columnCount === void 0) { columnCount = 1; } 674 var elements = toArray(this.elements).map(function (item) { 675 var element = item.parentNode ? item.parentNode.removeChild(item) : item; 676 return element; 677 }); 678 var columns = this.getColumns(); 679 for (var i = 0; i < columns.length; i++) { 680 columns[i].parentNode.removeChild(columns[i]); 681 } 682 for (var i = 0; i < columnCount; i++) { 683 var element = (new Column(this.options.columnClassName)).element; 684 this.element.appendChild(element); 685 }
686 elements.forEach(function (item) { 687 var column = _this.findMinHeightColumn(); 688 column.appendChild(item); 689 }); 690 }; 691 Container.prototype.getColumnCount = function () { 692 var containerWidth = this.element.offsetWidth; 693 var columnWidth = this.ruler.getWidth(); 694 if (columnWidth == 0) { 695 return 1; 696 } 697 return Math.round(containerWidth / columnWidth); 698 }; 699 Container.prototype.applyPosition = function (pos, column, item) { 700 var _this = this; 701 var trigger = function (timing) { 702 var eventName = timing + pos.charAt(0).toUpperCase() + pos.substr(1); 703 triggerEvent(_this.element, "bricklayer." + eventName, { item: item, column: column }); 704 }; 705 trigger('before'); 706 switch (pos) { 707 case 'append': 708 column.appendChild(item); 709 break; 710 case 'prepend': 711 column.insertBefore(item, column.firstChild); 712 break; 713 } 714 trigger('after'); 715 }; 716 return Container; 717 }()); 718 Bricklayer.Container = Container; 719 })(Bricklayer || (Bricklayer = {})); 720 (function (root, factory) { 721 if (typeof define === 'function' && define.amd) { 722 define(function () { return factory(); }); 723 } 724 else if (typeof window !== "undefined" && root === window) { 725 root.Bricklayer = factory(); 726 } 727 else if (typeof module === 'object' && module.exports) { 728 module.exports = factory(); 729 } 730 }(typeof window !== "undefined" ? window : this, function () { 731 return Bricklayer.Container; 732 })); 733 734},{}]},{},[1]);; 735/*! 736 * modernizr v3.3.1 737 * Build http://modernizr.com/download?-applicationcache-audio-backgroundsize-borderimage-borderradius-boxshadow-canvas-canvastext-cssanimations-csscolumns-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-flexbox-fontface-generatedcontent-geolocation-hashchange-history-hsla-indexeddb-inlinesvg-input-inputtypes-localstorage-multiplebgs-opacity-postmessage-rgba-sessionstorage-smil-svg-svgclippaths-textshadow-video-webgl-websockets-websqldatabase-webworkers-addtest-domprefixes-hasevent-mq-prefixed-prefixes-setclasses-shiv-testallprops-testprop-teststyles-dontmin 738 * 739 * Copyright (c) 740 * Faruk Ates 741 * Paul Irish 742 * Alex Sexton 743 * Ryan Seddon 744 * Patrick Kettner 745 * Stu Cox 746 * Richard Herrera 747 748 * MIT License 749 */ 750 751/* 752 * Modernizr tests which native CSS3 and HTML5 features are available in the 753 * current UA and makes the results available to you in two ways: as properties on 754 * a global `Modernizr` object, and as classes on the `<html>` element. This 755 * information allows you to progressively enhance your pages with a granular level 756 * of control over the experience. 757 */ 758 759;(function(window, document, undefined){ 760 var classes = []; 761 762 763 var tests = []; 764 765 766 /** 767 * 768 * ModernizrProto is the constructor for Modernizr 769 * 770 * @class 771 * @access public 772 */ 773 774 var ModernizrProto = { 775 // The current version, dummy 776 _version: '3.3.1', 777 778 // Any settings that don't work as separate modules 779 // can go in here as configuration. 780 _config: { 781 'classPrefix': '', 782 'enableClasses': true, 783 'enableJSClass': true, 784 'usePrefixes': true 785 }, 786 787 // Queue of tests 788 _q: [], 789 790 // Stub these for people who are listening 791 on: function(test, cb) { 792 // I don't really think people should do this, but we can 793 // safe guard it a bit. 794 // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 795 // This is in case people listen to synchronous tests. I would leave it out, 796 // but the code to *disallow* sync tests in the real version of this 797 // function is actually larger than this. 798 var self = this; 799 setTimeout(function() { 800 cb(self[test]); 801 }, 0); 802 }, 803 804 addTest: function(name, fn, options) { 805 tests.push({name: name, fn: fn, options: options}); 806 }, 807 808 addAsyncTest: function(fn) { 809 tests.push({name: null, fn: fn}); 810 } 811 }; 812 813 814 815 // Fake some of Object.create so we can force non test results to be non "own" properties. 816 var Modernizr = function() {}; 817 Modernizr.prototype = ModernizrProto; 818 819 // Leak modernizr globally when you `require` it rather than force it here. 820 // Overwrite name so constructor name is nicer :D 821 Modernizr = new Modernizr(); 822 823 824 /*! 825 { 826 "name": "Application Cache", 827 "property": "applicationcache", 828 "caniuse": "offline-apps", 829 "tags": ["storage", "offline"],
830 "notes": [{ 831 "name": "MDN documentation", 832 "href": "https://developer.mozilla.org/en/docs/HTML/Using_the_application_cache" 833 }], 834 "polyfills": ["html5gears"] 835 } 836 !*/ 837 /* DOC 838 Detects support for the Application Cache, for storing data to enable web-based applications run offline. 839 840 The API has been [heavily criticized](http://alistapart.com/article/application-cache-is-a-douchebag) and discussions are underway to address this. 841 */ 842 843 Modernizr.addTest('applicationcache', 'applicationCache' in window); 844 845 /*! 846 { 847 "name": "Geolocation API", 848 "property": "geolocation", 849 "caniuse": "geolocation", 850 "tags": ["media"], 851 "notes": [{ 852 "name": "MDN documentation", 853 "href": "https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation" 854 }], 855 "polyfills": [ 856 "joshuabell-polyfill", 857 "webshims", 858 "geo-location-javascript", 859 "geolocation-api-polyfill" 860 ] 861 } 862 !*/ 863 /* DOC 864 Detects support for the Geolocation API for users to provide their location to web applications. 865 */ 866 867 // geolocation is often considered a trivial feature detect... 868 // Turns out, it's quite tricky to get right: 869 // 870 // Using !!navigator.geolocation does two things we don't want. It: 871 // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513 872 // 2. Disables page caching in WebKit: webk.it/43956 873 // 874 // Meanwhile, in Firefox < 8, an about:config setting could expose 875 // a false positive that would throw an exception: bugzil.la/688158 876 877 Modernizr.addTest('geolocation', 'geolocation' in navigator); 878 879 /*! 880 { 881 "name": "History API", 882 "property": "history", 883 "caniuse": "history", 884 "tags": ["history"], 885 "authors": ["Hay Kranen", "Alexander Farkas"], 886 "notes": [{ 887 "name": "W3C Spec", 888 "href": "https://www.w3.org/TR/html51/browsers.html#the-history-interface" 889 }, { 890 "name": "MDN documentation", 891 "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.history" 892 }], 893 "polyfills": ["historyjs", "html5historyapi"] 894 } 895 !*/ 896 /* DOC 897 Detects support for the History API for manipulating the browser session history. 898 */ 899 900 Modernizr.addTest('history', function() { 901 // Issue #733 902 // The stock browser on Android 2.2 & 2.3, and 4.0.x returns positive on history support 903 // Unfortunately support is really buggy and there is no clean way to detect 904 // these bugs, so we fall back to a user agent sniff :( 905 var ua = navigator.userAgent; 906 907 // We only want Android 2 and 4.0, stock browser, and not Chrome which identifies 908 // itself as 'Mobile Safari' as well, nor Windows Phone (issue #1471). 909 if ((ua.indexOf('Android 2.') !== -1 || 910 (ua.indexOf('Android 4.0') !== -1)) && 911 ua.indexOf('Mobile Safari') !== -1 && 912 ua.indexOf('Chrome') === -1 && 913 ua.indexOf('Windows Phone') === -1) { 914 return false; 915 } 916 917 // Return the regular check 918 return (window.history && 'pushState' in window.history); 919 }); 920 921 /*! 922 { 923 "name": "postMessage", 924 "property": "postmessage", 925 "caniuse": "x-doc-messaging", 926 "notes": [{ 927 "name": "W3C Spec", 928 "href": "http://www.w3.org/TR/html5/comms.html#posting-messages" 929 }], 930 "polyfills": ["easyxdm", "postmessage-jquery"] 931 } 932 !*/ 933 /* DOC 934 Detects support for the `window.postMessage` protocol for cross-document messaging. 935 */ 936 937 Modernizr.addTest('postmessage', 'postMessage' in window); 938 939 /*! 940 { 941 "name": "SVG", 942 "property": "svg", 943 "caniuse": "svg", 944 "tags": ["svg"], 945 "authors": ["Erik Dahlstrom"], 946 "polyfills": [ 947 "svgweb", 948 "raphael", 949 "amplesdk", 950 "canvg", 951 "svg-boilerplate", 952 "sie", 953 "dojogfx", 954 "fabricjs" 955 ] 956 } 957 !*/ 958 /* DOC 959 Detects support for SVG in `<embed>` or `<object>` elements. 960 */ 961 962 Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect); 963 964 /*! 965 { 966 "name": "WebSockets Support", 967 "property": "websockets", 968 "authors": ["Phread [fearphage]", "Mike Sherov [mikesherov]", "Burak Yigit Kaya [BYK]"], 969 "caniuse": "websockets", 970 "tags": ["html5"], 971 "warnings": [ 972 "This test will reject any old version of WebSockets even if it is not prefixed such as in Safari 5.1" 973 ],
974 "notes": [{ 975 "name": "CLOSING State and Spec", 976 "href": "https://www.w3.org/TR/websockets/#the-websocket-interface" 977 }], 978 "polyfills": [ 979 "sockjs", 980 "socketio", 981 "kaazing-websocket-gateway", 982 "websocketjs", 983 "atmosphere", 984 "graceful-websocket", 985 "portal", 986 "datachannel" 987 ] 988 } 989 !*/ 990 991 Modernizr.addTest('websockets', 'WebSocket' in window && window.WebSocket.CLOSING === 2); 992 993 /*! 994 { 995 "name": "Local Storage", 996 "property": "localstorage", 997 "caniuse": "namevalue-storage", 998 "tags": ["storage"], 999 "knownBugs": [], 1000 "notes": [], 1001 "warnings": [], 1002 "polyfills": [ 1003 "joshuabell-polyfill", 1004 "cupcake", 1005 "storagepolyfill", 1006 "amplifyjs", 1007 "yui-cacheoffline" 1008 ] 1009 } 1010 !*/ 1011 1012 // In FF4, if disabled, window.localStorage should === null. 1013 1014 // Normally, we could not test that directly and need to do a 1015 // `('localStorage' in window) && ` test first because otherwise Firefox will 1016 // throw bugzil.la/365772 if cookies are disabled 1017 1018 // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem 1019 // will throw the exception: 1020 // QUOTA_EXCEEDED_ERROR DOM Exception 22. 1021 // Peculiarly, getItem and removeItem calls do not throw. 1022 1023 // Because we are forced to try/catch this, we'll go aggressive. 1024 1025 // Just FWIW: IE8 Compat mode supports these features completely: 1026 // www.quirksmode.org/dom/html5.html 1027 // But IE8 doesn't support either with local files 1028 1029 Modernizr.addTest('localstorage', function() { 1030 var mod = 'modernizr'; 1031 try { 1032 localStorage.setItem(mod, mod); 1033 localStorage.removeItem(mod); 1034 return true; 1035 } catch (e) { 1036 return false; 1037 } 1038 }); 1039 1040 /*! 1041 { 1042 "name": "Session Storage", 1043 "property": "sessionstorage", 1044 "tags": ["storage"], 1045 "polyfills": ["joshuabell-polyfill", "cupcake", "sessionstorage"] 1046 } 1047 !*/ 1048 1049 // Because we are forced to try/catch this, we'll go aggressive. 1050 1051 // Just FWIW: IE8 Compat mode supports these features completely: 1052 // www.quirksmode.org/dom/html5.html 1053 // But IE8 doesn't support either with local files 1054 Modernizr.addTest('sessionstorage', function() { 1055 var mod = 'modernizr'; 1056 try { 1057 sessionStorage.setItem(mod, mod); 1058 sessionStorage.removeItem(mod); 1059 return true; 1060 } catch (e) { 1061 return false; 1062 } 1063 }); 1064 1065 /*! 1066 { 1067 "name": "Web SQL Database", 1068 "property": "websqldatabase", 1069 "caniuse": "sql-storage", 1070 "tags": ["storage"] 1071 } 1072 !*/ 1073 1074 // Chrome incognito mode used to throw an exception when using openDatabase 1075 // It doesn't anymore. 1076 Modernizr.addTest('websqldatabase', 'openDatabase' in window); 1077 1078 /*! 1079 { 1080 "name": "Web Workers", 1081 "property": "webworkers", 1082 "caniuse" : "webworkers", 1083 "tags": ["performance", "workers"], 1084 "notes": [{ 1085 "name": "W3C Reference", 1086 "href": "https://www.w3.org/TR/workers/" 1087 }, { 1088 "name": "HTML5 Rocks article", 1089 "href": "http://www.html5rocks.com/en/tutorials/workers/basics/" 1090 }, { 1091 "name": "MDN documentation", 1092 "href": "https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers" 1093 }], 1094 "polyfills": ["fakeworker", "html5shims"] 1095 } 1096 !*/ 1097 /* DOC 1098 Detects support for the basic `Worker` API from the Web Workers spec. Web Workers provide a simple means for web content to run scripts in background threads. 1099 */ 1100 1101 Modernizr.addTest('webworkers', 'Worker' in window); 1102 1103 1104 /** 1105 * List of property values to set for css tests. See ticket #21 1106 * http://git.io/vUGl4 1107 * 1108 * @memberof Modernizr 1109 * @name Modernizr._prefixes 1110 * @optionName Modernizr._prefixes 1111 * @optionProp prefixes 1112 * @access public 1113 * @example 1114 * 1115 * Modernizr._prefixes is the internal list of prefixes that we test against 1116 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply 1117 * an array of kebab-case vendor prefixes you can use within your code. 1118 * 1119 * Some common use cases include 1120 * 1121 * Generating all possible prefixed version of a CSS property 1122 * ```js 1123 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); 1124 * 1125 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' 1126 * ``` 1127 * 1128 * Generating all possible prefixed version of a CSS value 1129 * ```js 1130 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; 1131 * 1132 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' 1133 * ``` 1134 */ 1135 1136 // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test 1137 // values in feature detects to continue to work 1138 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']); 1139 1140 // expose these for the plugin API. Look in the source for how to join() them against your input 1141 ModernizrProto._prefixes = prefixes; 1142 1143 1144 1145 /** 1146 * is returns a boolean if the typeof an obj is exactly type. 1147 * 1148 * @access private 1149 * @function is 1150 * @param {*} obj - A thing we want to check the type of 1151 * @param {string} type - A string to compare the typeof against 1152 * @returns {boolean} 1153 */ 1154 1155 function is(obj, type) { 1156 return typeof obj === type; 1157 } 1158 ; 1159 1160 /** 1161 * Run through all tests and detect their support in the current UA. 1162 * 1163 * @access private 1164 */ 1165 1166 function testRunner() { 1167 var featureNames; 1168 var feature; 1169 var aliasIdx; 1170 var result; 1171 var nameIdx; 1172 var featureName; 1173 var featureNameSplit; 1174 1175 for (var featureIdx in tests) { 1176 if (tests.hasOwnProperty(featureIdx)) { 1177 featureNames = []; 1178 feature = tests[featureIdx]; 1179 // run the test, throw the return value into the Modernizr, 1180 // then based on that boolean, define an appropriate className 1181 // and push it into an array of classes we'll join later. 1182 // 1183 // If there is no name, it's an 'async' test that is run, 1184 // but not directly added to the object. That should 1185 // be done with a post-run addTest call. 1186 if (feature.name) { 1187 featureNames.push(feature.name.toLowerCase()); 1188 1189 if (feature.options && feature.options.aliases && feature.options.aliases.length) { 1190 // Add all the aliases into the names list 1191 for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 1192 featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 1193 } 1194 } 1195 } 1196 1197 // Run the test, or use the raw value if it's not a function 1198 result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 1199 1200 1201 // Set each of the names on the Modernizr object 1202 for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 1203 featureName = featureNames[nameIdx];
1204 // Support dot properties as sub tests. We don't do checking to make sure 1205 // that the implied parent tests have been added. You must call them in 1206 // order (either in the test, or make the parent test a dependency). 1207 // 1208 // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 1209 // hashtag famous last words 1210 featureNameSplit = featureName.split('.'); 1211 1212 if (featureNameSplit.length === 1) { 1213 Modernizr[featureNameSplit[0]] = result; 1214 } else { 1215 // cast to a Boolean, if not one already 1216 /* jshint -W053 */ 1217 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 1218 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 1219 } 1220 1221 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 1222 } 1223 1224 classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 1225 } 1226 } 1227 } 1228 } 1229 ; 1230 1231 /** 1232 * docElement is a convenience wrapper to grab the root element of the document 1233 * 1234 * @access private 1235 * @returns {HTMLElement|SVGElement} The root element of the document 1236 */ 1237 1238 var docElement = document.documentElement; 1239 1240 1241 /** 1242 * A convenience helper to check if the document we are running in is an SVG document 1243 * 1244 * @access private 1245 * @returns {boolean} 1246 */ 1247 1248 var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 1249 1250 1251 /** 1252 * setClasses takes an array of class names and adds them to the root element 1253 * 1254 * @access private 1255 * @function setClasses 1256 * @param {string[]} classes - Array of class names 1257 */ 1258 1259 // Pass in an and array of class names, e.g.:
1260 // ['no-webp', 'borderradius', ...] 1261 function setClasses(classes) { 1262 var className = docElement.className; 1263 var classPrefix = Modernizr._config.classPrefix || ''; 1264 1265 if (isSVG) { 1266 className = className.baseVal; 1267 } 1268 1269 // Change `no-js` to `js` (independently of the `enableClasses` option) 1270 // Handle classPrefix on this too 1271 if (Modernizr._config.enableJSClass) { 1272 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 1273 className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 1274 } 1275 1276 if (Modernizr._config.enableClasses) { 1277 // Add the new classes 1278 className += ' ' + classPrefix + classes.join(' ' + classPrefix); 1279 isSVG ? docElement.className.baseVal = className : docElement.className = className; 1280 } 1281 1282 } 1283 1284 ; 1285 1286 /** 1287 * @optionName html5shiv 1288 * @optionProp html5shiv 1289 */ 1290 1291 // Take the html5 variable out of the html5shiv scope so we can return it. 1292 var html5; 1293 if (!isSVG) { 1294 /** 1295 * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 1296 */ 1297 ;(function(window, document) { 1298 /*jshint evil:true */ 1299 /** version */ 1300 var version = '3.7.3'; 1301 1302 /** Preset options */ 1303 var options = window.html5 || {}; 1304 1305 /** Used to skip problem elements */ 1306 var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 1307 1308 /** Not all elements can be cloned in IE **/ 1309 var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; 1310 1311 /** Detect whether the browser supports default html5 styles */ 1312 var supportsHtml5Styles; 1313 1314 /** Name of the expando, to work with multiple documents or to re-shiv one document */ 1315 var expando = '_html5shiv'; 1316 1317 /** The id for the the documents expando */ 1318 var expanID = 0; 1319 1320 /** Cached data for each document */ 1321 var expandoData = {}; 1322 1323 /** Detect whether the browser supports unknown elements */ 1324 var supportsUnknownElements; 1325 1326 (function() { 1327 try { 1328 var a = document.createElement('a'); 1329 a.innerHTML = '<xyz></xyz>'; 1330 //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles 1331 supportsHtml5Styles = ('hidden' in a); 1332 1333 supportsUnknownElements = a.childNodes.length == 1 || (function() { 1334 // assign a false positive if unable to shiv 1335 (document.createElement)('a'); 1336 var frag = document.createDocumentFragment(); 1337 return ( 1338 typeof frag.cloneNode == 'undefined' || 1339 typeof frag.createDocumentFragment == 'undefined' || 1340 typeof frag.createElement == 'undefined' 1341 ); 1342 }()); 1343 } catch(e) { 1344 // assign a false positive if detection fails => unable to shiv 1345 supportsHtml5Styles = true; 1346 supportsUnknownElements = true; 1347 } 1348 1349 }()); 1350 1351 /*--------------------------------------------------------------------------*/ 1352 1353 /** 1354 * Creates a style sheet with the given CSS text and adds it to the document. 1355 * @private 1356 * @param {Document} ownerDocument The document. 1357 * @param {String} cssText The CSS text. 1358 * @returns {StyleSheet} The style element. 1359 */ 1360 function addStyleSheet(ownerDocument, cssText) { 1361 var p = ownerDocument.createElement('p'), 1362 parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 1363 1364 p.innerHTML = 'x<style>' + cssText + '</style>'; 1365 return parent.insertBefore(p.lastChild, parent.firstChild); 1366 } 1367 1368 /** 1369 * Returns the value of `html5.elements` as an array.
vendor: 6,804 bytes, lines 1370-1527
1370 * @private 1371 * @returns {Array} An array of shived element node names. 1372 */ 1373 function getElements() { 1374 var elements = html5.elements; 1375 return typeof elements == 'string' ? elements.split(' ') : elements; 1376 } 1377 1378 /** 1379 * Extends the built-in list of html5 elements 1380 * @memberOf html5 1381 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv 1382 * @param {Document} ownerDocument The context document. 1383 */ 1384 function addElements(newElements, ownerDocument) { 1385 var elements = html5.elements; 1386 if(typeof elements != 'string'){ 1387 elements = elements.join(' '); 1388 } 1389 if(typeof newElements != 'string'){ 1390 newElements = newElements.join(' '); 1391 } 1392 html5.elements = elements +' '+ newElements; 1393 shivDocument(ownerDocument); 1394 } 1395 1396 /** 1397 * Returns the data associated to the given document 1398 * @private 1399 * @param {Document} ownerDocument The document. 1400 * @returns {Object} An object of data. 1401 */ 1402 function getExpandoData(ownerDocument) { 1403 var data = expandoData[ownerDocument[expando]]; 1404 if (!data) { 1405 data = {}; 1406 expanID++; 1407 ownerDocument[expando] = expanID; 1408 expandoData[expanID] = data; 1409 } 1410 return data; 1411 } 1412 1413 /** 1414 * returns a shived element for the given nodeName and document 1415 * @memberOf html5 1416 * @param {String} nodeName name of the element 1417 * @param {Document|DocumentFragment} ownerDocument The context document. 1418 * @returns {Object} The shived element. 1419 */ 1420 function createElement(nodeName, ownerDocument, data){ 1421 if (!ownerDocument) { 1422 ownerDocument = document; 1423 } 1424 if(supportsUnknownElements){ 1425 return ownerDocument.createElement(nodeName); 1426 } 1427 if (!data) { 1428 data = getExpandoData(ownerDocument); 1429 } 1430 var node; 1431 1432 if (data.cache[nodeName]) { 1433 node = data.cache[nodeName].cloneNode(); 1434 } else if (saveClones.test(nodeName)) { 1435 node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 1436 } else { 1437 node = data.createElem(nodeName); 1438 } 1439 1440 // Avoid adding some elements to fragments in IE < 9 because 1441 // * Attributes like `name` or `type` cannot be set/changed once an element 1442 // is inserted into a document/fragment 1443 // * Link elements with `src` attributes that are inaccessible, as with 1444 // a 403 response, will cause the tab/window to crash 1445 // * Script elements appended to fragments will execute when their `src` 1446 // or `text` property is set 1447 return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; 1448 } 1449 1450 /** 1451 * returns a shived DocumentFragment for the given document 1452 * @memberOf html5 1453 * @param {Document} ownerDocument The context document. 1454 * @returns {Object} The shived DocumentFragment. 1455 */ 1456 function createDocumentFragment(ownerDocument, data){ 1457 if (!ownerDocument) { 1458 ownerDocument = document; 1459 } 1460 if(supportsUnknownElements){ 1461 return ownerDocument.createDocumentFragment(); 1462 } 1463 data = data || getExpandoData(ownerDocument); 1464 var clone = data.frag.cloneNode(), 1465 i = 0, 1466 elems = getElements(), 1467 l = elems.length; 1468 for(;i<l;i++){ 1469 clone.createElement(elems[i]); 1470 } 1471 return clone; 1472 } 1473 1474 /** 1475 * Shivs the `createElement` and `createDocumentFragment` methods of the document. 1476 * @private 1477 * @param {Document|DocumentFragment} ownerDocument The document. 1478 * @param {Object} data of the document. 1479 */ 1480 function shivMethods(ownerDocument, data) { 1481 if (!data.cache) { 1482 data.cache = {}; 1483 data.createElem = ownerDocument.createElement; 1484 data.createFrag = ownerDocument.createDocumentFragment; 1485 data.frag = data.createFrag(); 1486 } 1487 1488 1489 ownerDocument.createElement = function(nodeName) { 1490 //abort shiv 1491 if (!html5.shivMethods) { 1492 return data.createElem(nodeName); 1493 } 1494 return createElement(nodeName, ownerDocument, data); 1495 }; 1496 1497 ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' + 1498 'var n=f.cloneNode(),c=n.createElement;' + 1499 'h.shivMethods&&(' + 1500 // unroll the `createElement` calls 1501 getElements().join().replace(/[\w\-:]+/g, function(nodeName) { 1502 data.createElem(nodeName); 1503 data.frag.createElement(nodeName); 1504 return 'c("' + nodeName + '")'; 1505 }) + 1506 ');return n}' 1507 )(html5, data.frag); 1508 } 1509 1510 /*--------------------------------------------------------------------------*/ 1511 1512 /** 1513 * Shivs the given document. 1514 * @memberOf html5 1515 * @param {Document} ownerDocument The document to shiv. 1516 * @returns {Document} The shived document. 1517 */ 1518 function shivDocument(ownerDocument) { 1519 if (!ownerDocument) { 1520 ownerDocument = document; 1521 } 1522 var data = getExpandoData(ownerDocument); 1523 1524 if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) { 1525 data.hasCSS = !!addStyleSheet(ownerDocument, 1526 // corrects block display not defined in IE6/7/8/9 1527 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{dis
1527play:block}' + 1528 // adds styling not present in IE6/7/8/9 1529 'mark{background:#FF0;color:#000}' + 1530 // hides non-rendered elements 1531 'template{display:none}' 1532 ); 1533 } 1534 if (!supportsUnknownElements) { 1535 shivMethods(ownerDocument, data); 1536 } 1537 return ownerDocument; 1538 } 1539 1540 /*--------------------------------------------------------------------------*/ 1541 1542 /** 1543 * The `html5` object is exposed so that more elements can be shived and 1544 * existing shiving can be detected on iframes. 1545 * @type Object 1546 * @example 1547 * 1548 * // options can be changed before the script is included 1549 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false }; 1550 */ 1551 var html5 = { 1552 1553 /** 1554 * An array or space separated string of node names of the elements to shiv. 1555 * @memberOf html5 1556 * @type Array|String 1557 */ 1558 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video', 1559 1560 /** 1561 * current version of html5shiv 1562 */ 1563 'version': version, 1564 1565 /** 1566 * A flag to indicate that the HTML5 style sheet should be inserted. 1567 * @memberOf html5 1568 * @type Boolean 1569 */ 1570 'shivCSS': (options.shivCSS !== false), 1571 1572 /** 1573 * Is equal to true if a browser supports creating unknown/HTML5 elements 1574 * @memberOf html5 1575 * @type boolean 1576 */ 1577 'supportsUnknownElements': supportsUnknownElements, 1578 1579 /** 1580 * A flag to indicate that the document's `createElement` and `createDocumentFragment` 1581 * methods should be overwritten. 1582 * @memberOf html5 1583 * @type Boolean 1584 */ 1585 'shivMethods': (options.shivMethods !== false), 1586 1587 /** 1588 * A string to describe the type of `html5` object ("default" or "default print"). 1589 * @memberOf html5
1590 * @type String 1591 */ 1592 'type': 'default', 1593 1594 // shivs the document according to the specified `html5` object options 1595 'shivDocument': shivDocument, 1596 1597 //creates a shived element 1598 createElement: createElement, 1599 1600 //creates a shived documentFragment 1601 createDocumentFragment: createDocumentFragment, 1602 1603 //extends list of elements 1604 addElements: addElements 1605 }; 1606 1607 /*--------------------------------------------------------------------------*/ 1608 1609 // expose html5 1610 window.html5 = html5; 1611 1612 // shiv the document 1613 shivDocument(document); 1614 1615 if(typeof module == 'object' && module.exports){ 1616 module.exports = html5; 1617 } 1618 1619 }(typeof window !== "undefined" ? window : this, document)); 1620 } 1621 ; 1622 1623 /** 1624 * If the browsers follow the spec, then they would expose vendor-specific style as: 1625 * elem.style.WebkitBorderRadius 1626 * instead of something like the following, which would be technically incorrect: 1627 * elem.style.webkitBorderRadius 1628 1629 * Webkit ghosts their properties in lowercase but Opera & Moz do not. 1630 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ 1631 * erik.eae.net/archives/2008/03/10/21.48.10/ 1632 1633 * More here: github.com/Modernizr/Modernizr/issues/issue/21 1634 * 1635 * @access private 1636 * @returns {string} The string representing the vendor-specific style properties 1637 */ 1638 1639 var omPrefixes = 'Moz O ms Webkit'; 1640 1641 1642 /** 1643 * List of JavaScript DOM values used for tests 1644 * 1645 * @memberof Modernizr 1646 * @name Modernizr._domPrefixes 1647 * @optionName Modernizr._domPrefixes 1648 * @optionProp domPrefixes 1649 * @access public 1650 * @example 1651 * 1652 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather 1653 * than kebab-case properties, all properties are their Capitalized variant 1654 * 1655 * ```js 1656 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; 1657 * ``` 1658 */ 1659 1660 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); 1661 ModernizrProto._domPrefixes = domPrefixes; 1662 1663 1664 /** 1665 * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support 1666 * 1667 * @author kangax 1668 * @access private 1669 * @function hasOwnProp 1670 * @param {object} object - The object to check for a property 1671 * @param {string} property - The property to check for 1672 * @returns {boolean} 1673 */ 1674 1675 // hasOwnProperty shim by kangax needed for Safari 2.0 support 1676 var hasOwnProp; 1677 1678 (function() { 1679 var _hasOwnProperty = ({}).hasOwnProperty; 1680 /* istanbul ignore else */ 1681 /* we have no way of testing IE 5.5 or safari 2, 1682 * so just assume the else gets hit */ 1683 if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) { 1684 hasOwnProp = function(object, property) { 1685 return _hasOwnProperty.call(object, property); 1686 }; 1687 } 1688 else { 1689 hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ 1690 return ((property in object) && is(object.constructor.prototype[property], 'undefined')); 1691 }; 1692 } 1693 })(); 1694 1695 1696 1697 1698 // _l tracks listeners for async tests, as well as tests that execute after the initial run 1699 ModernizrProto._l = {}; 1700 1701 /** 1702 * Modernizr.on is a way to listen for the completion of async tests. Being 1703 * asynchronous, they may not finish before your scripts run. As a result you 1704 * will get a possibly false negative `undefined` value. 1705 * 1706 * @memberof Modernizr 1707 * @name Modernizr.on 1708 * @access public 1709 * @function on 1710 * @param {string} feature - String name of the feature detect 1711 * @param {function} cb - Callback function returning a Boolean - true if feature is supported, false if not 1712 * @example 1713 * 1714 * ```js 1715 * Modernizr.on('flash', function( result ) { 1716 * if (result) { 1717 * // the browser has flash 1718 * } else { 1719 * // the browser does not have flash 1720 * } 1721 * }); 1722 * ``` 1723 */ 1724 1725 ModernizrProto.on = function(feature, cb) { 1726 // Create the list of listeners if it doesn't exist 1727 if (!this._l[feature]) { 1728 this._l[feature] = []; 1729 } 1730 1731 // Push this test on to the listener list 1732 this._l[feature].push(cb); 1733 1734 // If it's already been resolved, trigger it on next tick 1735 if (Modernizr.hasOwnProperty(feature)) { 1736 // Next Tick 1737 setTimeout(function() { 1738 Modernizr._trigger(feature, Modernizr[feature]); 1739 }, 0); 1740 } 1741 }; 1742 1743 /** 1744 * _trigger is the private function used to signal test completion and run any 1745 * callbacks registered through [Modernizr.on](#modernizr-on) 1746 * 1747 * @memberof Modernizr 1748 * @name Modernizr._trigger 1749 * @access private 1750 * @function _trigger 1751 * @param {string} feature - string name of the feature detect
1752 * @param {function|boolean} [res] - A feature detection function, or the boolean = 1753 * result of a feature detection function 1754 */ 1755 1756 ModernizrProto._trigger = function(feature, res) { 1757 if (!this._l[feature]) { 1758 return; 1759 } 1760 1761 var cbs = this._l[feature]; 1762 1763 // Force async 1764 setTimeout(function() { 1765 var i, cb; 1766 for (i = 0; i < cbs.length; i++) { 1767 cb = cbs[i]; 1768 cb(res); 1769 } 1770 }, 0); 1771 1772 // Don't trigger these again 1773 delete this._l[feature]; 1774 }; 1775 1776 /** 1777 * addTest allows you to define your own feature detects that are not currently 1778 * included in Modernizr (under the covers it's the exact same code Modernizr 1779 * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result 1780 * will be added onto the Modernizr object, as well as an appropriate className set on 1781 * the html element when configured to do so 1782 * 1783 * @memberof Modernizr 1784 * @name Modernizr.addTest 1785 * @optionName Modernizr.addTest() 1786 * @optionProp addTest 1787 * @access public 1788 * @function addTest 1789 * @param {string|object} feature - The string name of the feature detect, or an 1790 * object of feature detect names and test 1791 * @param {function|boolean} test - Function returning true if feature is supported, 1792 * false if not. Otherwise a boolean representing the results of a feature detection 1793 * @example 1794 * 1795 * The most common way of creating your own feature detects is by calling 1796 * `Modernizr.addTest` with a string (preferably just lowercase, without any 1797 * punctuation), and a function you want executed that will return a boolean result 1798 * 1799 * ```js 1800 * Modernizr.addTest('itsTuesday', function() { 1801 * var d = new Date(); 1802 * return d.getDay() === 2; 1803 * }); 1804 * ``` 1805 * 1806 * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday, 1807 * and to `false` every other day of the week. One thing to notice is that the names of 1808 * feature detect functions are always lowercased when added to the Modernizr object. That 1809 * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will. 1810 * 1811 * 1812 * Since we only look at the returned value from any feature detection function, 1813 * you do not need to actually use a function. For simple detections, just passing 1814 * in a statement that will return a boolean value works just fine. 1815 * 1816 * ```js 1817 * Modernizr.addTest('hasJquery', 'jQuery' in window); 1818 * ``` 1819 * 1820 * Just like before, when the above runs `Modernizr.hasjquery` will be true if 1821 * jQuery has been included on the page. Not using a function saves a small amount 1822 * of overhead for the browser, as well as making your code much more readable. 1823 * 1824 * Finally, you also have the ability to pass in an object of feature names and 1825 * their tests. This is handy if you want to add multiple detections in one go. 1826 * The keys should always be a string, and the value can be either a boolean or 1827 * function that returns a boolean. 1828 * 1829 * ```js 1830 * var detects = { 1831 * 'hasjquery': 'jQuery' in window, 1832 * 'itstuesday': function() { 1833 * var d = new Date(); 1834 * return d.getDay() === 2; 1835 * } 1836 * } 1837 * 1838 * Modernizr.addTest(detects); 1839 * ``` 1840 * 1841 * There is really no difference between the first methods and this one, it is 1842 * just a convenience to let you write more readable code. 1843 */ 1844 1845 function addTest(feature, test) { 1846 1847 if (typeof feature == 'object') { 1848 for (var key in feature) { 1849 if (hasOwnProp(feature, key)) { 1850 addTest(key, feature[ key ]); 1851 } 1852 } 1853 } else { 1854 1855 feature = feature.toLowerCase(); 1856 var featureNameSplit = feature.split('.'); 1857 var last = Modernizr[featureNameSplit[0]]; 1858 1859 // Again, we don't check for parent test existence. Get that right, though. 1860 if (featureNameSplit.length == 2) { 1861 last = last[featureNameSplit[1]]; 1862 } 1863 1864 if (typeof last != 'undefined') { 1865 // we're going to quit if you're trying to overwrite an existing test 1866 // if we were to allow it, we'd do this: 1867 // var re = new RegExp("\\b(no-)?" + feature + "\\b"); 1868 // docElement.className = docElement.className.replace( re, '' ); 1869 // but, no rly, stuff 'em. 1870 return Modernizr; 1871 } 1872 1873 test = typeof test == 'function' ? test() : test; 1874 1875 // Set the value (this is the magic, right here). 1876 if (featureNameSplit.length == 1) { 1877 Modernizr[featureNameSplit[0]] = test; 1878 } else { 1879 // cast to a Boolean, if not one already 1880 /* jshint -W053 */ 1881 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 1882 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 1883 } 1884 1885 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test; 1886 } 1887 1888 // Set a single class (either `feature` or `no-feature`) 1889 /* jshint -W041 */ 1890 setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); 1891 /* jshint +W041 */ 1892 1893 // Trigger the event 1894 Modernizr._trigger(feature, test); 1895 } 1896 1897 return Modernizr; // allow chaining. 1898 } 1899 1900 // After all the tests are run, add self to the Modernizr prototype 1901 Modernizr._q.push(function() { 1902 ModernizrProto.addTest = addTest; 1903 }); 1904 1905 1906 1907 1908 /** 1909 * createElement is a convenience wrapper around document.createElement. Since we 1910 * use createElement all over the place, this allows for (slightly) smaller code 1911 * as well as abstracting away issues with creating elements in contexts other than 1912 * HTML documents (e.g. SVG documents). 1913 * 1914 * @access private 1915 * @function createElement 1916 * @returns {HTMLElement|SVGElement} An HTML or SVG element 1917 */ 1918 1919 function createElement() { 1920 if (typeof document.createElement !== 'function') { 1921 // This is the case in IE7, where the type of createElement is "object". 1922 // For this reason, we cannot call apply() as Object is not a Function. 1923 return document.createElement(arguments[0]); 1924 } else if (isSVG) {
1925 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 1926 } else { 1927 return document.createElement.apply(document, arguments); 1928 } 1929 } 1930 1931 ; 1932 1933 /** 1934 * Modernizr.hasEvent() detects support for a given event 1935 * 1936 * @memberof Modernizr 1937 * @name Modernizr.hasEvent 1938 * @optionName Modernizr.hasEvent() 1939 * @optionProp hasEvent 1940 * @access public 1941 * @function hasEvent 1942 * @param {string|*} eventName - the name of an event to test for (e.g. "resize") 1943 * @param {Element|string} [element=HTMLDivElement] - is the element|document|window|tagName to test on 1944 * @returns {boolean} 1945 * @example 1946 * `Modernizr.hasEvent` lets you determine if the browser supports a supplied event. 1947 * By default, it does this detection on a div element 1948 * 1949 * ```js 1950 * hasEvent('blur') // true; 1951 * ``` 1952 * 1953 * However, you are able to give an object as a second argument to hasEvent to 1954 * detect an event on something other than a div. 1955 * 1956 * ```js 1957 * hasEvent('devicelight', window) // true; 1958 * ``` 1959 * 1960 */ 1961 1962 var hasEvent = (function() { 1963 1964 // Detect whether event support can be detected via `in`. Test on a DOM element 1965 // using the "blur" event b/c it should always exist. bit.ly/event-detection 1966 var needsFallback = !('onblur' in document.documentElement); 1967 1968 function inner(eventName, element) { 1969 1970 var isSupported; 1971 if (!eventName) { return false; } 1972 if (!element || typeof element === 'string') { 1973 element = createElement(element || 'div'); 1974 } 1975 1976 // Testing via the `in` operator is sufficient for modern browsers and IE. 1977 // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and 1978 // "resize", whereas `in` "catches" those. 1979 eventName = 'on' + eventName; 1980 isSupported = eventName in element; 1981 1982 // Fallback technique for old Firefox - bit.ly/event-detection 1983 if (!isSupported && needsFallback) { 1984 if (!element.setAttribute) { 1985 // Switch to generic element if it lacks `setAttribute`. 1986 // It could be the `document`, `window`, or something else. 1987 element = createElement('div'); 1988 } 1989 1990 element.setAttribute(eventName, ''); 1991 isSupported = typeof element[eventName] === 'function'; 1992 1993 if (element[eventName] !== undefined) { 1994 // If property was created, "remove it" by setting value to `undefined`. 1995 element[eventName] = undefined; 1996 } 1997 element.removeAttribute(eventName); 1998 } 1999 2000 return isSupported; 2001 } 2002 return inner; 2003 })(); 2004 2005 2006 ModernizrProto.hasEvent = hasEvent; 2007 2008 /*! 2009 { 2010 "name": "Hashchange event", 2011 "property": "hashchange", 2012 "caniuse": "hashchange", 2013 "tags": ["history"], 2014 "notes": [{ 2015 "name": "MDN documentation", 2016 "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.onhashchange" 2017 }], 2018 "polyfills": [ 2019 "jquery-hashchange", 2020 "moo-historymanager", 2021 "jquery-ajaxy", 2022 "hasher", 2023 "shistory" 2024 ] 2025 } 2026 !*/ 2027 /* DOC 2028 Detects support for the `hashchange` event, fired when the current location fragment changes. 2029 */ 2030 2031 Modernizr.addTest('hashchange', function() { 2032 if (hasEvent('hashchange', window) === false) { 2033 return false; 2034 } 2035 2036 // documentMode logic from YUI to filter out IE8 Compat Mode 2037 // which false positives. 2038 return (document.documentMode === undefined || document.documentMode > 7); 2039 }); 2040 2041 /*! 2042 { 2043 "name" : "HTML5 Audio Element", 2044 "property": "audio", 2045 "tags" : ["html5", "audio", "media"] 2046 } 2047 !*/ 2048 /* DOC 2049 Detects the audio element 2050 */ 2051 2052 // This tests evaluates support of the audio element, as well as 2053 // testing what types of content it supports. 2054 // 2055 // We're using the Boolean constructor here, so that we can extend the value 2056 // e.g. Modernizr.audio // true 2057 // Modernizr.audio.ogg // 'probably' 2058 // 2059 // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 2060 // thx to NielsLeenheer and zcorpan 2061 2062 // Note: in some older browsers, "no" was a return value instead of empty string. 2063 // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 2064 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 2065 Modernizr.addTest('audio', function() { 2066 /* jshint -W053 */ 2067 var elem = createElement('audio'); 2068 var bool = false;
2069 2070 try { 2071 if (bool = !!elem.canPlayType) { 2072 bool = new Boolean(bool); 2073 bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"') .replace(/^no$/, ''); 2074 bool.mp3 = elem.canPlayType('audio/mpeg; codecs="mp3"') .replace(/^no$/, ''); 2075 bool.opus = elem.canPlayType('audio/ogg; codecs="opus"') || 2076 elem.canPlayType('audio/webm; codecs="opus"') .replace(/^no$/, ''); 2077 2078 // Mimetypes accepted: 2079 // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements 2080 // bit.ly/iphoneoscodecs 2081 bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/, ''); 2082 bool.m4a = (elem.canPlayType('audio/x-m4a;') || 2083 elem.canPlayType('audio/aac;')) .replace(/^no$/, ''); 2084 } 2085 } catch (e) { } 2086 2087 return bool; 2088 }); 2089 2090 /*! 2091 { 2092 "name": "Canvas", 2093 "property": "canvas", 2094 "caniuse": "canvas", 2095 "tags": ["canvas", "graphics"], 2096 "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"] 2097 } 2098 !*/ 2099 /* DOC 2100 Detects support for the `<canvas>` element for 2D drawing. 2101 */ 2102 2103 // On the S60 and BB Storm, getContext exists, but always returns undefined 2104 // so we actually have to call getContext() to verify 2105 // github.com/Modernizr/Modernizr/issues/issue/97/ 2106 Modernizr.addTest('canvas', function() { 2107 var elem = createElement('canvas'); 2108 return !!(elem.getContext && elem.getContext('2d')); 2109 }); 2110 2111 /*! 2112 { 2113 "name": "Canvas text", 2114 "property": "canvastext", 2115 "caniuse": "canvas-text", 2116 "tags": ["canvas", "graphics"], 2117 "polyfills": ["canvastext"] 2118 } 2119 !*/ 2120 /* DOC 2121 Detects support for the text APIs for `<canvas>` elements. 2122 */ 2123 2124 Modernizr.addTest('canvastext', function() { 2125 if (Modernizr.canvas === false) { 2126 return false; 2127 } 2128 return typeof createElement('canvas').getContext('2d').fillText == 'function'; 2129 }); 2130 2131 /*! 2132 { 2133 "name": "HTML5 Video", 2134 "property": "video", 2135 "caniuse": "video", 2136 "tags": ["html5"], 2137 "knownBugs": [ 2138 "Without QuickTime, `Modernizr.video.h264` will be `undefined`; https://github.com/Modernizr/Modernizr/issues/546" 2139 ], 2140 "polyfills": [ 2141 "html5media", 2142 "mediaelementjs", 2143 "sublimevideo", 2144 "videojs", 2145 "leanbackplayer", 2146 "videoforeverybody" 2147 ] 2148 } 2149 !*/ 2150 /* DOC 2151 Detects support for the video element, as well as testing what types of content it supports. 2152 2153 Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.: 2154 2155 ```javascript 2156 Modernizr.video // true 2157 Modernizr.video.ogg // 'probably' 2158 ``` 2159 */ 2160 2161 // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 2162 // thx to NielsLeenheer and zcorpan 2163 2164 // Note: in some older browsers, "no" was a return value instead of empty string. 2165 // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 2166 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 2167 2168 Modernizr.addTest('video', function() { 2169 /* jshint -W053 */ 2170 var elem = createElement('video'); 2171 var bool = false;
2172 2173 // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 2174 try { 2175 if (bool = !!elem.canPlayType) { 2176 bool = new Boolean(bool); 2177 bool.ogg = elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, ''); 2178 2179 // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546 2180 bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, ''); 2181 2182 bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, ''); 2183 2184 bool.vp9 = elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/, ''); 2185 2186 bool.hls = elem.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/, ''); 2187 } 2188 } catch (e) {} 2189 2190 return bool; 2191 }); 2192 2193 /*! 2194 { 2195 "name": "WebGL", 2196 "property": "webgl", 2197 "caniuse": "webgl", 2198 "tags": ["webgl", "graphics"], 2199 "polyfills": ["jebgl", "cwebgl", "iewebgl"] 2200 } 2201 !*/ 2202 2203 Modernizr.addTest('webgl', function() { 2204 var canvas = createElement('canvas'); 2205 var supports = 'probablySupportsContext' in canvas ? 'probablySupportsContext' : 'supportsContext'; 2206 if (supports in canvas) { 2207 return canvas[supports]('webgl') || canvas[supports]('experimental-webgl'); 2208 } 2209 return 'WebGLRenderingContext' in window; 2210 }); 2211 2212 /*! 2213 { 2214 "name": "CSS Gradients", 2215 "caniuse": "css-gradients", 2216 "property": "cssgradients", 2217 "tags": ["css"], 2218 "knownBugs": ["False-positives on webOS (https://github.com/Modernizr/Modernizr/issues/202)"], 2219 "notes": [{ 2220 "name": "Webkit Gradient Syntax", 2221 "href": "https://webkit.org/blog/175/introducing-css-gradients/" 2222 },{ 2223 "name": "Linear Gradient Syntax", 2224 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient" 2225 },{ 2226 "name": "W3C Gradient Spec", 2227 "href": "https://drafts.csswg.org/css-images-3/#gradients" 2228 }] 2229 } 2230 !*/ 2231 2232 2233 Modernizr.addTest('cssgradients', function() { 2234 2235 var str1 = 'background-image:'; 2236 var str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));'; 2237 var css = ''; 2238 var angle; 2239 2240 for (var i = 0, len = prefixes.length - 1; i < len; i++) { 2241 angle = (i === 0 ? 'to ' : ''); 2242 css += str1 + prefixes[i] + 'linear-gradient(' + angle + 'left top, #9f9, white);'; 2243 } 2244 2245 if (Modernizr._config.usePrefixes) { 2246 // legacy webkit syntax (FIXME: remove when syntax not in use anymore) 2247 css += str1 + '-webkit-' + str2; 2248 } 2249 2250 var elem = createElement('a'); 2251 var style = elem.style; 2252 style.cssText = css; 2253 2254 // IE6 returns undefined so cast to string 2255 return ('' + style.backgroundImage).indexOf('gradient') > -1; 2256 }); 2257 2258 /*! 2259 {
2260 "name": "CSS Multiple Backgrounds", 2261 "caniuse": "multibackgrounds", 2262 "property": "multiplebgs", 2263 "tags": ["css"] 2264 } 2265 !*/ 2266 2267 // Setting multiple images AND a color on the background shorthand property 2268 // and then querying the style.background property value for the number of 2269 // occurrences of "url(" is a reliable method for detecting ACTUAL support for this! 2270 2271 Modernizr.addTest('multiplebgs', function() { 2272 var style = createElement('a').style; 2273 style.cssText = 'background:url(https://),url(https://),red url(https://)'; 2274 2275 // If the UA supports multiple backgrounds, there should be three occurrences 2276 // of the string "url(" in the return value for elemStyle.background 2277 return (/(url\s*\(.*?){3}/).test(style.background); 2278 }); 2279 2280 /*! 2281 { 2282 "name": "CSS Opacity", 2283 "caniuse": "css-opacity", 2284 "property": "opacity", 2285 "tags": ["css"] 2286 } 2287 !*/ 2288 2289 // Browsers that actually have CSS Opacity implemented have done so 2290 // according to spec, which means their return values are within the 2291 // range of [0.0,1.0] - including the leading zero. 2292 2293 Modernizr.addTest('opacity', function() { 2294 var style = createElement('a').style; 2295 style.cssText = prefixes.join('opacity:.55;'); 2296 2297 // The non-literal . in this regex is intentional: 2298 // German Chrome returns this value as 0,55 2299 // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632 2300 return (/^0.55$/).test(style.opacity); 2301 }); 2302 2303 /*! 2304 { 2305 "name": "CSS rgba", 2306 "caniuse": "css3-colors", 2307 "property": "rgba", 2308 "tags": ["css"], 2309 "notes": [{ 2310 "name": "CSSTricks Tutorial", 2311 "href": "https://css-tricks.com/rgba-browser-support/" 2312 }] 2313 } 2314 !*/ 2315 2316 Modernizr.addTest('rgba', function() { 2317 var style = createElement('a').style; 2318 style.cssText = 'background-color:rgba(150,255,150,.5)'; 2319 2320 return ('' + style.backgroundColor).indexOf('rgba') > -1; 2321 }); 2322 2323 /*! 2324 { 2325 "name": "Inline SVG", 2326 "property": "inlinesvg", 2327 "caniuse": "svg-html5", 2328 "tags": ["svg"], 2329 "notes": [{ 2330 "name": "Test page", 2331 "href": "https://paulirish.com/demo/inline-svg" 2332 }, { 2333 "name": "Test page and results", 2334 "href": "https://codepen.io/eltonmesquita/full/GgXbvo/" 2335 }], 2336 "polyfills": ["inline-svg-polyfill"], 2337 "knownBugs": ["False negative on some Chromia browsers."] 2338 } 2339 !*/ 2340 /* DOC 2341 Detects support for inline SVG in HTML (not within XHTML). 2342 */ 2343 2344 Modernizr.addTest('inlinesvg', function() { 2345 var div = createElement('div'); 2346 div.innerHTML = '<svg/>'; 2347 return (typeof SVGRect != 'undefined' && div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg'; 2348 }); 2349 2350 2351 /** 2352 * cssToDOM takes a kebab-case string and converts it to camelCase 2353 * e.g. box-sizing -> boxSizing 2354 * 2355 * @access private 2356 * @function cssToDOM 2357 * @param {string} name - String name of kebab-case prop we want to convert 2358 * @returns {string} The camelCase version of the supplied name 2359 */ 2360 2361 function cssToDOM(name) { 2362 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 2363 return m1 + m2.toUpperCase(); 2364 }).replace(/^-/, ''); 2365 } 2366 ; 2367 2368 /** 2369 * since we have a fairly large number of input tests that don't mutate the input 2370 * we create a single element that can be shared with all of those tests for a 2371 * minor perf boost 2372 * 2373 * @access private 2374 * @returns {HTMLInputElement} 2375 */ 2376 var inputElem = createElement('input'); 2377 2378 /*! 2379 { 2380 "name": "Input attributes", 2381 "property": "input", 2382 "tags": ["forms"], 2383 "authors": ["Mike Taylor"], 2384 "notes": [{ 2385 "name": "WHATWG spec", 2386 "href": "https://html.spec.whatwg.org/multipage/forms.html#input-type-attr-summary" 2387 }], 2388 "knownBugs": ["Some blackberry devices report false positive for input.multiple"] 2389 } 2390 !*/ 2391 /* DOC 2392 Detects support for HTML5 `<input>` element attributes and exposes Boolean subproperties with the results: 2393 2394 ```javascript 2395 Modernizr.input.autocomplete 2396 Modernizr.input.autofocus 2397 Modernizr.input.list 2398 Modernizr.input.max 2399 Modernizr.input.min 2400 Modernizr.input.multiple 2401 Modernizr.input.pattern 2402 Modernizr.input.placeholder 2403 Modernizr.input.required 2404 Modernizr.input.step 2405 ``` 2406 */ 2407 2408 // Run through HTML5's new input attributes to see if the UA understands any. 2409 // Mike Taylr has created a comprehensive resource for testing these attributes 2410 // when applied to all input types: 2411 // miketaylr.com/code/input-type-attr.html 2412 2413 // Only input placeholder is tested while textarea's placeholder is not. 2414 // Currently Safari 4 and Opera 11 have support only for the input placeholder 2415 // Both tests are available in feature-detects/forms-placeholder.js 2416 2417 var inputattrs = 'autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '); 2418 var attrs = {}; 2419 2420 Modernizr.input = (function(props) { 2421 for (var i = 0, len = props.length; i < len; i++) { 2422 attrs[ props[i] ] = !!(props[i] in inputElem); 2423 } 2424 if (attrs.list) { 2425 // safari false positive's on datalist: webk.it/74252 2426 // see also github.com/Modernizr/Modernizr/issues/146 2427 attrs.list = !!(createElement('datalist') && window.HTMLDataListElement); 2428 } 2429 return attrs; 2430 })(inputattrs); 2431 2432 /*! 2433 { 2434 "name": "Form input types", 2435 "property": "inputtypes", 2436 "caniuse": "forms", 2437 "tags": ["forms"], 2438 "authors": ["Mike Taylor"],
2439 "polyfills": [ 2440 "jquerytools", 2441 "webshims", 2442 "h5f", 2443 "webforms2", 2444 "nwxforms", 2445 "fdslider", 2446 "html5slider", 2447 "galleryhtml5forms", 2448 "jscolor", 2449 "html5formshim", 2450 "selectedoptionsjs", 2451 "formvalidationjs" 2452 ] 2453 } 2454 !*/ 2455 /* DOC 2456 Detects support for HTML5 form input types and exposes Boolean subproperties with the results: 2457 2458 ```javascript 2459 Modernizr.inputtypes.color 2460 Modernizr.inputtypes.date 2461 Modernizr.inputtypes.datetime 2462 Modernizr.inputtypes['datetime-local'] 2463 Modernizr.inputtypes.email 2464 Modernizr.inputtypes.month 2465 Modernizr.inputtypes.number 2466 Modernizr.inputtypes.range 2467 Modernizr.inputtypes.search 2468 Modernizr.inputtypes.tel 2469 Modernizr.inputtypes.time 2470 Modernizr.inputtypes.url 2471 Modernizr.inputtypes.week 2472 ``` 2473 */ 2474 2475 // Run through HTML5's new input types to see if the UA understands any. 2476 // This is put behind the tests runloop because it doesn't return a 2477 // true/false like all the other tests; instead, it returns an object 2478 // containing each input type with its corresponding true/false value 2479 2480 // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/ 2481 var inputtypes = 'search tel url email datetime date month week time datetime-local number range color'.split(' '); 2482 var inputs = {}; 2483 2484 Modernizr.inputtypes = (function(props) { 2485 var len = props.length; 2486 var smile = '1)'; 2487 var inputElemType; 2488 var defaultView; 2489 var bool; 2490 2491 for (var i = 0; i < len; i++) { 2492 2493 inputElem.setAttribute('type', inputElemType = props[i]); 2494 bool = inputElem.type !== 'text' && 'style' in inputElem; 2495 2496 // We first check to see if the type we give it sticks.. 2497 // If the type does, we feed it a textual value, which shouldn't be valid. 2498 // If the value doesn't stick, we know there's input sanitization which infers a custom UI 2499 if (bool) { 2500 2501 inputElem.value = smile; 2502 inputElem.style.cssText = 'position:absolute;visibility:hidden;'; 2503 2504 if (/^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined) { 2505 2506 docElement.appendChild(inputElem); 2507 defaultView = document.defaultView; 2508 2509 // Safari 2-4 allows the smiley as a value, despite making a slider 2510 bool = defaultView.getComputedStyle && 2511 defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && 2512 // Mobile android web browser has false positive, so must 2513 // check the height to see if the widget is actually there. 2514 (inputElem.offsetHeight !== 0); 2515 2516 docElement.removeChild(inputElem); 2517 2518 } else if (/^(search|tel)$/.test(inputElemType)) { 2519 // Spec doesn't define any special parsing or detectable UI 2520 // behaviors so we pass these through as true 2521 2522 // Interestingly, opera fails the earlier test, so it doesn't 2523 // even make it here. 2524 2525 } else if (/^(url|email)$/.test(inputElemType)) { 2526 // Real url and email support comes with prebaked validation. 2527 bool = inputElem.checkValidity && inputElem.checkValidity() === false; 2528 2529 } else { 2530 // If the upgraded input compontent rejects the :) text, we got a winner 2531 bool = inputElem.value != smile; 2532 } 2533 } 2534 2535 inputs[ props[i] ] = !!bool; 2536 } 2537 return inputs; 2538 })(inputtypes); 2539 2540 2541 2542 /** 2543 * contains checks to see if a string contains another string 2544 * 2545 * @access private 2546 * @function contains 2547 * @param {string} str - The string we want to check for substrings 2548 * @param {string} substr - The substring we want to search the first string for 2549 * @returns {boolean} 2550 */ 2551 2552 function contains(str, substr) {
2553 return !!~('' + str).indexOf(substr); 2554 } 2555 2556 ; 2557 /*! 2558 { 2559 "name": "CSS HSLA Colors", 2560 "caniuse": "css3-colors", 2561 "property": "hsla", 2562 "tags": ["css"] 2563 } 2564 !*/ 2565 2566 Modernizr.addTest('hsla', function() { 2567 var style = createElement('a').style; 2568 style.cssText = 'background-color:hsla(120,40%,100%,.5)'; 2569 return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla'); 2570 }); 2571 2572 /*! 2573 { 2574 "name": "CSS Supports", 2575 "property": "supports", 2576 "caniuse": "css-featurequeries", 2577 "tags": ["css"], 2578 "builderAliases": ["css_supports"], 2579 "notes": [{ 2580 "name": "W3 Spec", 2581 "href": "http://dev.w3.org/csswg/css3-conditional/#at-supports" 2582 },{ 2583 "name": "Related Github Issue", 2584 "href": "github.com/Modernizr/Modernizr/issues/648" 2585 },{ 2586 "name": "W3 Info", 2587 "href": "http://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface" 2588 }] 2589 } 2590 !*/ 2591 2592 var newSyntax = 'CSS' in window && 'supports' in window.CSS; 2593 var oldSyntax = 'supportsCSS' in window; 2594 Modernizr.addTest('supports', newSyntax || oldSyntax); 2595 2596 2597 /** 2598 * Object.prototype.toString can be used with every object and allows you to 2599 * get its class easily. Abstracting it off of an object prevents situations 2600 * where the toString property has been overridden 2601 * 2602 * @access private 2603 * @function toStringFn 2604 * @returns {function} An abstracted toString function 2605 */ 2606 2607 var toStringFn = ({}).toString; 2608 2609 /*! 2610 { 2611 "name": "SVG clip paths", 2612 "property": "svgclippaths", 2613 "tags": ["svg"], 2614 "notes": [{ 2615 "name": "Demo", 2616 "href": "http://srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg" 2617 }] 2618 } 2619 !*/ 2620 /* DOC 2621 Detects support for clip paths in SVG (only, not on HTML content). 2622 2623 See [this discussion](https://github.com/Modernizr/Modernizr/issues/213) regarding applying SVG clip paths to HTML content. 2624 */ 2625 2626 Modernizr.addTest('svgclippaths', function() { 2627 return !!document.createElementNS && 2628 /SVGClipPath/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'clipPath'))); 2629 }); 2630 2631 /*! 2632 { 2633 "name": "SVG SMIL animation", 2634 "property": "smil", 2635 "caniuse": "svg-smil", 2636 "tags": ["svg"], 2637 "notes": [{ 2638 "name": "W3C Synchronised Multimedia spec", 2639 "href": "https://www.w3.org/AudioVideo/" 2640 }] 2641 } 2642 !*/ 2643 2644 // SVG SMIL animation 2645 Modernizr.addTest('smil', function() { 2646 return !!document.createElementNS && 2647 /SVGAnimate/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'animate'))); 2648 }); 2649 2650 2651 /** 2652 * getBody returns the body of a document, or an element that can stand in for 2653 * the body if a real body does not exist 2654 * 2655 * @access private 2656 * @function getBody 2657 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 2658 * artificially created element that stands in for the body 2659 */ 2660 2661 function getBody() { 2662 // After page load injecting a fake body doesn't work so check if body exists 2663 var body = document.body; 2664 2665 if (!body) { 2666 // Can't use the real body create a fake one. 2667 body = createElement(isSVG ? 'svg' : 'body'); 2668 body.fake = true; 2669 } 2670 2671 return body; 2672 } 2673 2674 ; 2675 2676 /** 2677 * injectElementWithStyles injects an element with style element and some CSS rules 2678 * 2679 * @access private 2680 * @function injectElementWithStyles 2681 * @param {string} rule - String representing a css rule 2682 * @param {function} callback - A function that is used to test the injected element 2683 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 2684 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 2685 * @returns {boolean} 2686 */ 2687 2688 function injectElementWithStyles(rule, callback, nodes, testnames) { 2689 var mod = 'modernizr'; 2690 var style; 2691 var ret; 2692 var node; 2693 var docOverflow; 2694 var div = createElement('div'); 2695 var body = getBody(); 2696 2697 if (parseInt(nodes, 10)) { 2698 // In order not to give false positives we create a node for each test
2699 // This also allows the method to scale for unspecified uses 2700 while (nodes--) { 2701 node = createElement('div'); 2702 node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 2703 div.appendChild(node); 2704 } 2705 } 2706 2707 style = createElement('style'); 2708 style.type = 'text/css'; 2709 style.id = 's' + mod; 2710 2711 // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. 2712 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 2713 (!body.fake ? div : body).appendChild(style); 2714 body.appendChild(div); 2715 2716 if (style.styleSheet) { 2717 style.styleSheet.cssText = rule; 2718 } else { 2719 style.appendChild(document.createTextNode(rule)); 2720 } 2721 div.id = mod; 2722 2723 if (body.fake) { 2724 //avoid crashing IE8, if background image is used 2725 body.style.background = ''; 2726 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 2727 body.style.overflow = 'hidden'; 2728 docOverflow = docElement.style.overflow; 2729 docElement.style.overflow = 'hidden'; 2730 docElement.appendChild(body); 2731 } 2732 2733 ret = callback(div, rule); 2734 // If this is done after page load we don't want to remove the body so check if body exists 2735 if (body.fake) { 2736 body.parentNode.removeChild(body); 2737 docElement.style.overflow = docOverflow; 2738 // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 2739 docElement.offsetHeight; 2740 } else { 2741 div.parentNode.removeChild(div); 2742 } 2743 2744 return !!ret; 2745 2746 } 2747 2748 ; 2749 2750 /** 2751 * Modernizr.mq tests a given media query, live against the current state of the window 2752 * adapted from matchMedia polyfill by Scott Jehl and Paul Irish 2753 * gist.github.com/786768 2754 * 2755 * @memberof Modernizr 2756 * @name Modernizr.mq 2757 * @optionName Modernizr.mq() 2758 * @optionProp mq 2759 * @access public 2760 * @function mq 2761 * @param {string} mq - String of the media query we want to test 2762 * @returns {boolean} 2763 * @example 2764 * Modernizr.mq allows for you to programmatically check if the current browser 2765 * window state matches a media query. 2766 * 2767 * ```js 2768 * var query = Modernizr.mq('(min-width: 900px)'); 2769 * 2770 * if (query) { 2771 * // the browser window is larger than 900px 2772 * } 2773 * ``` 2774 * 2775 * Only valid media queries are supported, therefore you must always include values 2776 * with your media query 2777 * 2778 * ```js 2779 * // good 2780 * Modernizr.mq('(min-width: 900px)'); 2781 * 2782 * // bad 2783 * Modernizr.mq('min-width'); 2784 * ``` 2785 * 2786 * If you would just like to test that media queries are supported in general, use 2787 * 2788 * ```js 2789 * Modernizr.mq('only all'); // true if MQ are supported, false if not 2790 * ``` 2791 * 2792 * 2793 * Note that if the browser does not support media queries (e.g. old IE) mq will 2794 * always return false. 2795 */ 2796 2797 var mq = (function() { 2798 var matchMedia = window.matchMedia || window.msMatchMedia; 2799 if (matchMedia) { 2800 return function(mq) { 2801 var mql = matchMedia(mq); 2802 return mql && mql.matches || false; 2803 }; 2804 } 2805 2806 return function(mq) { 2807 var bool = false;
2808 2809 injectElementWithStyles('@media ' + mq + ' { #modernizr { position: absolute; } }', function(node) { 2810 bool = (window.getComputedStyle ? 2811 window.getComputedStyle(node, null) : 2812 node.currentStyle).position == 'absolute'; 2813 }); 2814 2815 return bool; 2816 }; 2817 })(); 2818 2819 2820 ModernizrProto.mq = mq; 2821 2822 2823 2824 /** 2825 * testStyles injects an element with style element and some CSS rules 2826 * 2827 * @memberof Modernizr 2828 * @name Modernizr.testStyles 2829 * @optionName Modernizr.testStyles() 2830 * @optionProp testStyles 2831 * @access public 2832 * @function testStyles 2833 * @param {string} rule - String representing a css rule 2834 * @param {function} callback - A function that is used to test the injected element 2835 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 2836 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 2837 * @returns {boolean} 2838 * @example 2839 * 2840 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page 2841 * along with (possibly multiple) DOM elements. This lets you check for features 2842 * that can not be detected by simply checking the [IDL](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Interface_development_guide/IDL_interface_rules). 2843 * 2844 * ```js 2845 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { 2846 * // elem is the first DOM node in the page (by default #modernizr) 2847 * // rule is the first argument you supplied - the CSS rule in string form 2848 * 2849 * addTest('widthworks', elem.style.width === '9px') 2850 * }); 2851 * ``` 2852 * 2853 * If your test requires multiple nodes, you can include a third argument 2854 * indicating how many additional div elements to include on the page. The 2855 * additional nodes are injected as children of the `elem` that is returned as 2856 * the first argument to the callback. 2857 * 2858 * ```js 2859 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { 2860 * document.getElementById('modernizr').style.width === '1px'; // true 2861 * document.getElementById('modernizr2').style.width === '2px'; // true 2862 * elem.firstChild === document.getElementById('modernizr2'); // true 2863 * }, 1); 2864 * ``` 2865 * 2866 * By default, all of the additional elements have an ID of `modernizr[n]`, where 2867 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, 2868 * the second additional is `#modernizr3`, etc.). 2869 * If you want to have more meaningful IDs for your function, you can provide 2870 * them as the fourth argument, as an array of strings 2871 * 2872 * ```js 2873 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { 2874 * elem.firstChild === document.getElementById('foo'); // true 2875 * elem.lastChild === document.getElementById('bar'); // true 2876 * }, 2, ['foo', 'bar']); 2877 * ``` 2878 * 2879 */ 2880 2881 var testStyles = ModernizrProto.testStyles = injectElementWithStyles; 2882 2883 /*! 2884 { 2885 "name": "@font-face", 2886 "property": "fontface", 2887 "authors": ["Diego Perini", "Mat Marquis"], 2888 "tags": ["css"], 2889 "knownBugs": [ 2890 "False Positive: WebOS https://github.com/Modernizr/Modernizr/issues/342", 2891 "False Postive: WP7 https://github.com/Modernizr/Modernizr/issues/538" 2892 ], 2893 "notes": [{ 2894 "name": "@font-face detection routine by Diego Perini", 2895 "href": "http://javascript.nwbox.com/CSSSupport/" 2896 },{ 2897 "name": "Filament Group @font-face compatibility research", 2898 "href": "https://docs.google.com/presentation/d/1n4NyG4uPRjAA8zn_pSQ_Ket0RhcWC6QlZ6LMjKeECo0/edit#slide=id.p" 2899 },{ 2900 "name": "Filament Grunticon/@font-face device testing results", 2901 "href": "https://docs.google.com/spreadsheet/ccc?key=0Ag5_yGvxpINRdHFYeUJPNnZMWUZKR2ItMEpRTXZPdUE#gid=0" 2902 },{ 2903 "name": "CSS fonts on Android", 2904 "href": "https://stackoverflow.com/questions/3200069/css-fonts-on-android" 2905 },{ 2906 "name": "@font-face and Android", 2907 "href": "http://archivist.incutio.com/viewlist/css-discuss/115960" 2908 }] 2909 } 2910 !*/ 2911 2912 var blacklist = (function() { 2913 var ua = navigator.userAgent; 2914 var wkvers = ua.match(/applewebkit\/([0-9]+)/gi) && parseFloat(RegExp.$1); 2915 var webos = ua.match(/w(eb)?osbrowser/gi); 2916 var wppre8 = ua.match(/windows phone/gi) && ua.match(/iemobile\/([0-9])+/gi) && parseFloat(RegExp.$1) >= 9; 2917 var oldandroid = wkvers < 533 && ua.match(/android/gi); 2918 return webos || oldandroid || wppre8; 2919 }()); 2920 if (blacklist) { 2921 Modernizr.addTest('fontface', false); 2922 } else { 2923 testStyles('@font-face {font-family:"font";src:url("https://")}', function(node, rule) { 2924 var style = document.getElementById('smodernizr'); 2925 var sheet = style.sheet || style.styleSheet; 2926 var cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : ''; 2927 var bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0; 2928 Modernizr.addTest('fontface', bool); 2929 }); 2930 } 2931 ; 2932 /*! 2933 { 2934 "name": "CSS Generated Content", 2935 "property": "generatedcontent", 2936 "tags": ["css"], 2937 "warnings": ["Android won't return correct height for anything below 7px #738"],
2938 "notes": [{ 2939 "name": "W3C CSS Selectors Level 3 spec", 2940 "href": "https://www.w3.org/TR/css3-selectors/#gen-content" 2941 },{ 2942 "name": "MDN article on :before", 2943 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/::before" 2944 },{ 2945 "name": "MDN article on :after", 2946 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/::before" 2947 }] 2948 } 2949 !*/ 2950 2951 testStyles('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}', function(node) { 2952 Modernizr.addTest('generatedcontent', node.offsetHeight >= 7); 2953 }); 2954 2955 2956 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); 2957 ModernizrProto._cssomPrefixes = cssomPrefixes; 2958 2959 2960 /** 2961 * atRule returns a given CSS property at-rule (eg @keyframes), possibly in 2962 * some prefixed form, or false, in the case of an unsupported rule 2963 * 2964 * @memberof Modernizr 2965 * @name Modernizr.atRule 2966 * @optionName Modernizr.atRule() 2967 * @optionProp atRule 2968 * @access public 2969 * @function atRule 2970 * @param {string} prop - String name of the @-rule to test for 2971 * @returns {string|boolean} The string representing the (possibly prefixed) 2972 * valid version of the @-rule, or `false` when it is unsupported. 2973 * @example 2974 * ```js 2975 * var keyframes = Modernizr.atRule('@keyframes'); 2976 * 2977 * if (keyframes) { 2978 * // keyframes are supported 2979 * // could be `@-webkit-keyframes` or `@keyframes` 2980 * } else { 2981 * // keyframes === `false` 2982 * } 2983 * ``` 2984 * 2985 */ 2986 2987 var atRule = function(prop) { 2988 var length = prefixes.length; 2989 var cssrule = window.CSSRule; 2990 var rule; 2991 2992 if (typeof cssrule === 'undefined') { 2993 return undefined; 2994 } 2995 2996 if (!prop) { 2997 return false; 2998 } 2999 3000 // remove literal @ from beginning of provided property 3001 prop = prop.replace(/^@/, ''); 3002 3003 // CSSRules use underscores instead of dashes 3004 rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE'; 3005 3006 if (rule in cssrule) { 3007 return '@' + prop; 3008 } 3009 3010 for (var i = 0; i < length; i++) { 3011 // prefixes gives us something like -o-, and we want O_ 3012 var prefix = prefixes[i]; 3013 var thisRule = prefix.toUpperCase() + '_' + rule; 3014 3015 if (thisRule in cssrule) { 3016 return '@-' + prefix.toLowerCase() + '-' + prop; 3017 } 3018 } 3019 3020 return false; 3021 }; 3022 3023 ModernizrProto.atRule = atRule; 3024 3025 3026 3027 /** 3028 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. 3029 * 3030 * @access private 3031 * @function fnBind 3032 * @param {function} fn - a function you want to change `this` reference to 3033 * @param {object} that - the `this` you want to call the function with 3034 * @returns {function} The wrapped version of the supplied function 3035 */ 3036 3037 function fnBind(fn, that) { 3038 return function() { 3039 return fn.apply(that, arguments); 3040 }; 3041 } 3042 3043 ; 3044 3045 /** 3046 * testDOMProps is a generic DOM property test; if a browser supports 3047 * a certain property, it won't return undefined for it. 3048 * 3049 * @access private 3050 * @function testDOMProps 3051 * @param {array.<string>} props - An array of properties to test for 3052 * @param {object} obj - An object or Element you want to use to test the parameters again 3053 * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check 3054 */ 3055 function testDOMProps(props, obj, elem) { 3056 var item; 3057 3058 for (var i in props) { 3059 if (props[i] in obj) { 3060 3061 // return the property name as a string 3062 if (elem === false) { 3063 return props[i]; 3064 } 3065 3066 item = obj[props[i]]; 3067 3068 // let's bind a function 3069 if (is(item, 'function')) { 3070 // bind to obj unless overriden 3071 return fnBind(item, elem || obj); 3072 } 3073 3074 // return the unbound function or obj or value 3075 return item; 3076 } 3077 } 3078 return false;
3079 } 3080 3081 ; 3082 3083 /** 3084 * Create our "modernizr" element that we do most feature tests on. 3085 * 3086 * @access private 3087 */ 3088 3089 var modElem = { 3090 elem: createElement('modernizr') 3091 }; 3092 3093 // Clean up this element 3094 Modernizr._q.push(function() { 3095 delete modElem.elem; 3096 }); 3097 3098 3099 3100 var mStyle = { 3101 style: modElem.elem.style 3102 }; 3103 3104 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 3105 // the front of the queue. 3106 Modernizr._q.unshift(function() { 3107 delete mStyle.style; 3108 }); 3109 3110 3111 3112 /** 3113 * domToCSS takes a camelCase string and converts it to kebab-case 3114 * e.g. boxSizing -> box-sizing 3115 * 3116 * @access private 3117 * @function domToCSS 3118 * @param {string} name - String name of camelCase prop we want to convert 3119 * @returns {string} The kebab-case version of the supplied name 3120 */ 3121 3122 function domToCSS(name) { 3123 return name.replace(/([A-Z])/g, function(str, m1) { 3124 return '-' + m1.toLowerCase(); 3125 }).replace(/^ms-/, '-ms-'); 3126 } 3127 ; 3128 3129 /** 3130 * nativeTestProps allows for us to use native feature detection functionality if available. 3131 * some prefixed form, or false, in the case of an unsupported rule 3132 * 3133 * @access private 3134 * @function nativeTestProps 3135 * @param {array} props - An array of property names 3136 * @param {string} value - A string representing the value we want to check via @supports 3137 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 3138 */ 3139 3140 // Accepts a list of property names and a single value 3141 // Returns `undefined` if native detection not available 3142 function nativeTestProps(props, value) { 3143 var i = props.length; 3144 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface 3145 if ('CSS' in window && 'supports' in window.CSS) { 3146 // Try every prefixed variant of the property 3147 while (i--) { 3148 if (window.CSS.supports(domToCSS(props[i]), value)) { 3149 return true; 3150 } 3151 } 3152 return false; 3153 } 3154 // Otherwise fall back to at-rule (for Opera 12.x) 3155 else if ('CSSSupportsRule' in window) { 3156 // Build a condition string for every prefixed variant 3157 var conditionText = []; 3158 while (i--) { 3159 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 3160 } 3161 conditionText = conditionText.join(' or '); 3162 return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { 3163 return getComputedStyle(node, null).position == 'absolute'; 3164 }); 3165 } 3166 return undefined; 3167 } 3168 ; 3169 3170 // testProps is a generic CSS / DOM property test. 3171 3172 // In testing support for a given CSS property, it's legit to test: 3173 // `elem.style[styleName] !== undefined` 3174 // If the property is supported it will return an empty string, 3175 // if unsupported it will return undefined. 3176 3177 // We'll take advantage of this quick test and skip setting a style 3178 // on our modernizr element, but instead just testing undefined vs 3179 // empty string. 3180 3181 // Property names can be provided in either camelCase or kebab-case. 3182 3183 function testProps(props, prefixed, value, skipValueTest) { 3184 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 3185 3186 // Try native detect first 3187 if (!is(value, 'undefined')) { 3188 var result = nativeTestProps(props, value); 3189 if (!is(result, 'undefined')) { 3190 return result; 3191 } 3192 } 3193 3194 // Otherwise do it properly 3195 var afterInit, i, propsLength, prop, before; 3196 3197 // If we don't have a style element, that means we're running async or after 3198 // the core tests, so we'll need to create our own elements to use 3199 3200 // inside of an SVG element, in certain browsers, the `style` element is only 3201 // defined for valid tags. Therefore, if `modernizr` does not have one, we 3202 // fall back to a less used element and hope for the best. 3203 var elems = ['modernizr', 'tspan']; 3204 while (!mStyle.style) { 3205 afterInit = true; 3206 mStyle.modElem = createElement(elems.shift()); 3207 mStyle.style = mStyle.modElem.style; 3208 } 3209 3210 // Delete the objects if we created them. 3211 function cleanElems() { 3212 if (afterInit) { 3213 delete mStyle.style; 3214 delete mStyle.modElem; 3215 } 3216 } 3217 3218 propsLength = props.length; 3219 for (i = 0; i < propsLength; i++) { 3220 prop = props[i]; 3221 before = mStyle.style[prop]; 3222 3223 if (contains(prop, '-')) { 3224 prop = cssToDOM(prop); 3225 } 3226 3227 if (mStyle.style[prop] !== undefined) { 3228 3229 // If value to test has been passed in, do a set-and-check test. 3230 // 0 (integer) is a valid property value, so check that `value` isn't 3231 // undefined, rather than just checking it's truthy. 3232 if (!skipValueTest && !is(value, 'undefined')) { 3233 3234 // Needs a try catch block because of old IE. This is slow, but will 3235 // be avoided in most cases because `skipValueTest` will be used. 3236 try { 3237 mStyle.style[prop] = value; 3238 } catch (e) {} 3239 3240 // If the property value has changed, we assume the value used is 3241 // supported. If `value` is empty string, it'll fail here (because 3242 // it hasn't changed), which matches how browsers have implemented 3243 // CSS.supports() 3244 if (mStyle.style[prop] != before) { 3245 cleanElems(); 3246 return prefixed == 'pfx' ? prop : true; 3247 } 3248 } 3249 // Otherwise just return true, or the property name if this is a 3250 // `prefixed()` call 3251 else { 3252 cleanElems(); 3253 return prefixed == 'pfx' ? prop : true; 3254 } 3255 } 3256 } 3257 cleanElems(); 3258 return false;
3259 } 3260 3261 ; 3262 3263 /** 3264 * testProp() investigates whether a given style property is recognized 3265 * Property names can be provided in either camelCase or kebab-case. 3266 * 3267 * @memberof Modernizr 3268 * @name Modernizr.testProp 3269 * @access public 3270 * @optionName Modernizr.testProp() 3271 * @optionProp testProp 3272 * @function testProp 3273 * @param {string} prop - Name of the CSS property to check 3274 * @param {string} [value] - Name of the CSS value to check 3275 * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported 3276 * @returns {boolean} 3277 * @example 3278 * 3279 * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed 3280 * version of the string. 3281 * 3282 * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) 3283 * 3284 * ```js 3285 * Modernizr.testProp('pointerEvents') // true 3286 * ``` 3287 * 3288 * You can also provide a value as an optional second argument to check if a 3289 * specific value is supported 3290 * 3291 * ```js
3292 * Modernizr.testProp('pointerEvents', 'none') // true 3293 * Modernizr.testProp('pointerEvents', 'penguin') // false 3294 * ``` 3295 */ 3296 3297 var testProp = ModernizrProto.testProp = function(prop, value, useValue) { 3298 return testProps([prop], undefined, value, useValue); 3299 }; 3300 3301 /*! 3302 { 3303 "name": "CSS textshadow", 3304 "property": "textshadow", 3305 "caniuse": "css-textshadow", 3306 "tags": ["css"], 3307 "knownBugs": ["FF3.0 will false positive on this test"] 3308 } 3309 !*/ 3310 3311 Modernizr.addTest('textshadow', testProp('textShadow', '1px 1px')); 3312 3313 3314 /** 3315 * testPropsAll tests a list of DOM properties we want to check against. 3316 * We specify literally ALL possible (known and/or likely) properties on 3317 * the element including the non-vendor prefixed one, for forward- 3318 * compatibility. 3319 * 3320 * @access private 3321 * @function testPropsAll 3322 * @param {string} prop - A string of the property to test for 3323 * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip 3324 * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against 3325 * @param {string} [value] - A string of a css value 3326 * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set 3327 */ 3328 function testPropsAll(prop, prefixed, elem, value, skipValueTest) { 3329 3330 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 3331 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 3332 3333 // did they call .prefixed('boxSizing') or are we just testing a prop? 3334 if (is(prefixed, 'string') || is(prefixed, 'undefined')) { 3335 return testProps(props, prefixed, value, skipValueTest); 3336 3337 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) 3338 } else { 3339 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 3340 return testDOMProps(props, prefixed, elem); 3341 } 3342 } 3343 3344 // Modernizr.testAllProps() investigates whether a given style property, 3345 // or any of its vendor-prefixed variants, is recognized 3346 // 3347 // Note that the property names must be provided in the camelCase variant. 3348 // Modernizr.testAllProps('boxSizing') 3349 ModernizrProto.testAllProps = testPropsAll; 3350 3351 3352 3353 /** 3354 * prefixed returns the prefixed or nonprefixed property name variant of your input 3355 * 3356 * @memberof Modernizr 3357 * @name Modernizr.prefixed 3358 * @optionName Modernizr.prefixed() 3359 * @optionProp prefixed 3360 * @access public 3361 * @function prefixed 3362 * @param {string} prop - String name of the property to test for 3363 * @param {object} [obj] - An object to test for the prefixed properties on 3364 * @param {HTMLElement} [elem] - An element used to test specific properties against 3365 * @returns {string|false} The string representing the (possibly prefixed) valid 3366 * version of the property, or `false` when it is unsupported. 3367 * @example 3368 * 3369 * Modernizr.prefixed takes a string css value in the DOM style camelCase (as 3370 * opposed to the css style kebab-case) form and returns the (possibly prefixed) 3371 * version of that property that the browser actually supports. 3372 * 3373 * For example, in older Firefox... 3374 * ```js 3375 * prefixed('boxSizing') 3376 * ``` 3377 * returns 'MozBoxSizing' 3378 * 3379 * In newer Firefox, as well as any other browser that support the unprefixed 3380 * version would simply return `boxSizing`. Any browser that does not support 3381 * the property at all, it will return `false`. 3382 * 3383 * By default, prefixed is checked against a DOM element. If you want to check 3384 * for a property on another object, just pass it as a second argument 3385 * 3386 * ```js 3387 * var rAF = prefixed('requestAnimationFrame', window); 3388 * 3389 * raf(function() { 3390 * renderFunction(); 3391 * }) 3392 * ``` 3393 * 3394 * Note that this will return _the actual function_ - not the name of the function. 3395 * If you need the actual name of the property, pass in `false` as a third argument 3396 * 3397 * ```js 3398 * var rAFProp = prefixed('requestAnimationFrame', window, false); 3399 * 3400 * rafProp === 'WebkitRequestAnimationFrame' // in older webkit 3401 * ``` 3402 * 3403 * One common use case for prefixed is if you're trying to determine which transition 3404 * end event to bind to, you might do something like... 3405 * ```js 3406 * var transEndEventNames = { 3407 * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser 3408 * 'MozTransition' : 'transitionend', * only for FF < 15 3409 * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ 3410 * }; 3411 * 3412 * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; 3413 * ``` 3414 * 3415 * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). 3416 */ 3417 3418 var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) { 3419 if (prop.indexOf('@') === 0) { 3420 return atRule(prop); 3421 } 3422 3423 if (prop.indexOf('-') != -1) { 3424 // Convert kebab-case to camelCase 3425 prop = cssToDOM(prop); 3426 } 3427 if (!obj) { 3428 return testPropsAll(prop, 'pfx'); 3429 } else { 3430 // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' 3431 return testPropsAll(prop, obj, elem); 3432 } 3433 }; 3434 3435 3436 /*! 3437 { 3438 "name": "IndexedDB", 3439 "property": "indexeddb", 3440 "caniuse": "indexeddb", 3441 "tags": ["storage"], 3442 "polyfills": ["indexeddb"] 3443 } 3444 !*/ 3445 /* DOC 3446 Detects support for the IndexedDB client-side storage API (final spec). 3447 */ 3448 3449 // Vendors had inconsistent prefixing with the experimental Indexed DB: 3450 // - Webkit's implementation is accessible through webkitIndexedDB 3451 // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB 3452 // For speed, we don't test the legacy (and beta-only) indexedDB 3453 3454 var indexeddb; 3455 try { 3456 indexeddb = prefixed('indexedDB', window); 3457 } catch (e) { 3458 } 3459 3460 Modernizr.addTest('indexeddb', !!indexeddb); 3461 3462 if (!!indexeddb) { 3463 Modernizr.addTest('indexeddb.deletedatabase', 'deleteDatabase' in indexeddb); 3464 } 3465 ; 3466 3467 /** 3468 * testAllProps determines whether a given CSS property is supported in the browser 3469 * 3470 * @memberof Modernizr 3471 * @name Modernizr.testAllProps 3472 * @optionName Modernizr.testAllProps() 3473 * @optionProp testAllProps 3474 * @access public 3475 * @function testAllProps 3476 * @param {string} prop - String naming the property to test (either camelCase or kebab-case) 3477 * @param {string} [value] - String of the value to test 3478 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection 3479 * @example 3480 * 3481 * testAllProps determines whether a given CSS property, in some prefixed form, 3482 * is supported by the browser. 3483 * 3484 * ```js 3485 * testAllProps('boxSizing') // true 3486 * ``` 3487 * 3488 * It can optionally be given a CSS value in string form to test if a property 3489 * value is valid 3490 * 3491 * ```js 3492 * testAllProps('display', 'block') // true 3493 * testAllProps('display', 'penguin') // false 3494 * ``` 3495 * 3496 * A boolean can be passed as a third parameter to skip the value check when 3497 * native detection (@supports) isn't available. 3498 * 3499 * ```js 3500 * testAllProps('shapeOutside', 'content-box', true); 3501 * ``` 3502 */ 3503 3504 function testAllProps(prop, value, skipValueTest) { 3505 return testPropsAll(prop, undefined, undefined, value, skipValueTest); 3506 } 3507 ModernizrProto.testAllProps = testAllProps; 3508 3509 /*! 3510 { 3511 "name": "CSS Animations", 3512 "property": "cssanimations", 3513 "caniuse": "css-animation", 3514 "polyfills": ["transformie", "csssandpaper"], 3515 "tags": ["css"], 3516 "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"],
3517 "notes": [{ 3518 "name" : "Article: 'Dispelling the Android CSS animation myths'", 3519 "href": "https://goo.gl/OGw5Gm" 3520 }] 3521 } 3522 !*/ 3523 /* DOC 3524 Detects whether or not elements can be animated using CSS 3525 */ 3526 3527 Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); 3528 3529 /*! 3530 { 3531 "name": "Background Size", 3532 "property": "backgroundsize", 3533 "tags": ["css"], 3534 "knownBugs": ["This will false positive in Opera Mini - https://github.com/Modernizr/Modernizr/issues/396"], 3535 "notes": [{ 3536 "name": "Related Issue", 3537 "href": "https://github.com/Modernizr/Modernizr/issues/396" 3538 }] 3539 } 3540 !*/ 3541 3542 Modernizr.addTest('backgroundsize', testAllProps('backgroundSize', '100%', true)); 3543 3544 /*! 3545 { 3546 "name": "Border Image", 3547 "property": "borderimage", 3548 "caniuse": "border-image", 3549 "polyfills": ["css3pie"], 3550 "knownBugs": ["Android < 2.0 is true, but has a broken implementation"], 3551 "tags": ["css"] 3552 } 3553 !*/ 3554 3555 Modernizr.addTest('borderimage', testAllProps('borderImage', 'url() 1', true)); 3556 3557 /*! 3558 { 3559 "name": "Border Radius",
3560 "property": "borderradius", 3561 "caniuse": "border-radius", 3562 "polyfills": ["css3pie"], 3563 "tags": ["css"], 3564 "notes": [{ 3565 "name": "Comprehensive Compat Chart", 3566 "href": "https://muddledramblings.com/table-of-css3-border-radius-compliance" 3567 }] 3568 } 3569 !*/ 3570 3571 Modernizr.addTest('borderradius', testAllProps('borderRadius', '0px', true)); 3572 3573 /*! 3574 { 3575 "name": "Box Shadow", 3576 "property": "boxshadow", 3577 "caniuse": "css-boxshadow", 3578 "tags": ["css"], 3579 "knownBugs": [ 3580 "WebOS false positives on this test.", 3581 "The Kindle Silk browser false positives" 3582 ] 3583 } 3584 !*/ 3585 3586 Modernizr.addTest('boxshadow', testAllProps('boxShadow', '1px 1px', true)); 3587 3588 /*! 3589 { 3590 "name": "CSS Columns", 3591 "property": "csscolumns", 3592 "caniuse": "multicolumn", 3593 "polyfills": ["css3multicolumnjs"], 3594 "tags": ["css"] 3595 } 3596 !*/ 3597 3598 3599 (function() { 3600 3601 /* jshint -W053 */ 3602 Modernizr.addTest('csscolumns', function() { 3603 var bool = false; 3604 var test = testAllProps('columnCount'); 3605 try { 3606 if (bool = !!test) { 3607 bool = new Boolean(bool); 3608 } 3609 } catch (e) {} 3610 3611 return bool; 3612 }); 3613 3614 var props = ['Width', 'Span', 'Fill', 'Gap', 'Rule', 'RuleColor', 'RuleStyle', 'RuleWidth', 'BreakBefore', 'BreakAfter', 'BreakInside']; 3615 var name, test; 3616 3617 for (var i = 0; i < props.length; i++) { 3618 name = props[i].toLowerCase(); 3619 test = testAllProps('column' + props[i]); 3620 3621 // break-before, break-after & break-inside are not "column"-prefixed in spec 3622 if (name === 'breakbefore' || name === 'breakafter' || name == 'breakinside') { 3623 test = test || testAllProps(props[i]); 3624 } 3625 3626 Modernizr.addTest('csscolumns.' + name, test); 3627 } 3628 3629 3630 })(); 3631 3632 3633 /*! 3634 { 3635 "name": "Flexbox", 3636 "property": "flexbox", 3637 "caniuse": "flexbox", 3638 "tags": ["css"], 3639 "notes": [{ 3640 "name": "The _new_ flexbox", 3641 "href": "http://dev.w3.org/csswg/css3-flexbox" 3642 }], 3643 "warnings": [ 3644 "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect." 3645 ] 3646 } 3647 !*/ 3648 /* DOC 3649 Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container. 3650 */ 3651 3652 Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', true)); 3653 3654 /*! 3655 { 3656 "name": "CSS Reflections", 3657 "caniuse": "css-reflections", 3658 "property": "cssreflections", 3659 "tags": ["css"] 3660 } 3661 !*/ 3662 3663 Modernizr.addTest('cssreflections', testAllProps('boxReflect', 'above', true)); 3664 3665 /*! 3666 { 3667 "name": "CSS Transforms", 3668 "property": "csstransforms", 3669 "caniuse": "transforms2d", 3670 "tags": ["css"] 3671 } 3672 !*/ 3673 3674 Modernizr.addTest('csstransforms', function() { 3675 // Android < 3.0 is buggy, so we sniff and blacklist 3676 // http://git.io/hHzL7w 3677 return navigator.userAgent.indexOf('Android 2.') === -1 && 3678 testAllProps('transform', 'scale(1)', true); 3679 }); 3680 3681 /*! 3682 { 3683 "name": "CSS Transforms 3D", 3684 "property": "csstransforms3d", 3685 "caniuse": "transforms3d", 3686 "tags": ["css"], 3687 "warnings": [ 3688 "Chrome may occassionally fail this test on some systems; more info: https://code.google.com/p/chromium/issues/detail?id=129004" 3689 ] 3690 } 3691 !*/ 3692 3693 Modernizr.addTest('csstransforms3d', function() { 3694 var ret = !!testAllProps('perspective', '1px', true); 3695 var usePrefix = Modernizr._config.usePrefixes; 3696 3697 // Webkit's 3D transforms are passed off to the browser's own graphics renderer. 3698 // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in 3699 // some conditions. As a result, Webkit typically recognizes the syntax but 3700 // will sometimes throw a false positive, thus we must do a more thorough check: 3701 if (ret && (!usePrefix || 'webkitPerspective' in docElement.style)) { 3702 var mq; 3703 var defaultStyle = '#modernizr{width:0;height:0}'; 3704 // Use CSS Conditional Rules if available 3705 if (Modernizr.supports) { 3706 mq = '@supports (perspective: 1px)'; 3707 } else { 3708 // Otherwise, Webkit allows this media query to succeed only if the feature is enabled. 3709 // `@media (transform-3d),(-webkit-transform-3d){ ... }` 3710 mq = '@media (transform-3d)'; 3711 if (usePrefix) { 3712 mq += ',(-webkit-transform-3d)'; 3713 } 3714 } 3715 3716 mq += '{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}'; 3717 3718 testStyles(defaultStyle + mq, function(elem) { 3719 ret = elem.offsetWidth === 7 && elem.offsetHeight === 18; 3720 }); 3721 } 3722 3723 return ret; 3724 }); 3725 3726 /*! 3727 { 3728 "name": "CSS Transitions", 3729 "property": "csstransitions", 3730 "caniuse": "css-transitions", 3731 "tags": ["css"] 3732 } 3733 !*/ 3734 3735 Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); 3736 3737 3738 // Run each test 3739 testRunner(); 3740 3741 // Remove the "no-js" class if it exists 3742 setClasses(classes); 3743 3744 delete ModernizrProto.addTest; 3745 delete ModernizrProto.addAsyncTest; 3746 3747 // Run the things that are supposed to run after the tests 3748 for (var i = 0; i < Modernizr._q.length; i++) { 3749 Modernizr._q[i](); 3750 } 3751 3752 // Leak Modernizr namespace 3753 window.Modernizr = Modernizr; 3754 3755 3756 ; 3757 3758})(window, document);; 3759/** 3760 * File skip-link-focus-fix.js. 3761 * 3762 * Helps with accessibility for keyboard only users. 3763 * 3764 * This is the source file for what is minified in the varia_skip_link_focus_fix() PHP function. 3765 * 3766 * Learn more: https://git.io/vWdr2 3767 */ 3768( function() { 3769 var isIe = /(trident|msie)/i.test( navigator.userAgent ); 3770 3771 if ( isIe && document.getElementById && window.addEventListener ) { 3772 window.addEventListener( 'hashchange', function() { 3773 var id = location.hash.substring( 1 ), 3774 element; 3775 3776 if ( ! ( /^[A-z0-9_-]+$/.test( id ) ) ) { 3777 return; 3778 } 3779 3780 element = document.getElementById( id ); 3781 3782 if ( element ) { 3783 if ( ! ( /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) ) { 3784 element.tabIndex = -1; 3785 } 3786 3787 element.focus(); 3788 } 3789 }, false ); 3790 } 3791} )(); 3792; 3793/*! This file is auto-generated */ 3794/*! 3795 * imagesLoaded PACKAGED v5.0.0 3796 * JavaScript is all like "You images are done yet or what?" 3797 * MIT License 3798 */ 3799!function(t,e){"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,(function(){function t(){}let e=t.prototype;return e.on=function(t,e){if(!t||!e)return this;let i=this._events=this._events||{},s=i[t]=i[t]||[];return s.includes(e)||s.push(e),this},e.once=function(t,e){if(!t||!e)return this;this.on(t,e);let i=this._onceEvents=this._onceEvents||{};return(i[t]=i[t]||{})[e]=!0,this},e.off=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;let s=i.indexOf(e);return-1!=s&&i.splice(s,1),this},e.emitEvent=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;i=i.slice(0),e=e||[];let s=this._onceEvents&&this._onceEvents[t];for(let n of i){s&&s[n]&&(this.off(t,n),delete s[n]),n.apply(this,e)}return this},e.allOff=function(){return delete this._events,delete this._onceEvents,this},t})), 3800/*! 3801 * imagesLoaded v5.0.0 3802 * JavaScript is all like "You images are done yet or what?" 3803 * MIT License 3804 */ 3805function(t,e){"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter")):t.imagesLoaded=e(t,t.EvEmitter)}("undefined"!=typeof window?window:this,(function(t,e){let i=t.jQuery,s=t.console;
3805function n(t,e,o){if(!(this instanceof n))return new n(t,e,o);let r=t;var h;("string"==typeof t&&(r=document.querySelectorAll(t)),r)?(this.elements=(h=r,Array.isArray(h)?h:"object"==typeof h&&"number"==typeof h.length?[...h]:[h]),this.options={},"function"==typeof e?o=e:Object.assign(this.options,e),o&&this.on("always",o),this.getImages(),i&&(this.jqDeferred=new i.Deferred),setTimeout(this.check.bind(this))):s.error(`Bad element for imagesLoaded ${r||t}`)}n.prototype=Object.create(e.prototype),n.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)};const o=[1,9,11];n.prototype.addElementImages=function(t){"IMG"===t.nodeName&&this.addImage(t),!0===this.options.background&&this.addElementBackgroundImages(t);let{nodeType:e}=t;if(!e||!o.includes(e))return;let i=t.querySelectorAll("img");for(let t of i)this.addImage(t);if("string"==typeof this.options.background){let e=t.querySelectorAll(this.options.background);for(let t of e)this.addElementBackgroundImages(t)}};const r=/url\((['"])?(.*?)\1\)/gi;function h(t){this.img=t}function d(t,e){this.url=t,this.element=e,this.img=new Image}return n.prototype.addElementBackgroundImages=function(t){let e=getComputedStyle(t);if(!e)return;let i=r.exec(e.backgroundImage);for(;null!==i;){let s=i&&i[2];s&&this.addBackground(s,t),i=r.exec(e.backgroundImage)}},n.prototype.addImage=function(t){let e=new h(t);this.images.push(e)},n.prototype.addBackground=function(t,e){let i=new d(t,e);this.images.push(i)},n.prototype.check=function(){if(this.progressedCount=0,this.hasAnyBroken=!1,!this.images.length)return void this.complete();let t=(t,e,i)=>{setTimeout((()=>{this.progress(t,e,i)}))};this.images.forEach((function(e){e.once("progress",t),e.check()}))},n.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent("progress",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount===this.images.length&&this.complete(),this.options.debug&&s&&s.log(`progress: ${i}`,t,e)},n.prototype.complete=function(){let t=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent("always",[this]),this.jqDeferred){let t=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[t](this)}},h.prototype=Object.create(e.prototype),h.prototype.check=function(){this.getIsImageComplete()?this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,this.img.crossOrigin&&(this.proxyImage.crossOrigin=this.img.crossOrigin),this.proxyImage.addEventListener("load",this),this.proxyImage.addEventListener("error",this),this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.proxyImage.src=this.img.currentSrc||this.img.src)},h.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},h.prototype.confirm=function(t,e){this.isLoaded=t;let{parentNode:i}=this.img,s="PICTURE"===i.nodeName?i:this.img;this.emitEvent("progress",[this,s,e])},h.prototype.handleEvent=function(t){let e="on"+t.type;this[e]&&this[e](t)},h.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},h.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},h.prototype.unbindEvents=function(){this.proxyImage.removeEventListener("load",this),this.proxyImage.removeEventListener("error",this),this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype=Object.create(h.prototype),d.prototype.check=function(){this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.img.src=this.url,this.getIsImageComplete()&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},d.prototype.unbindEvents=function(){this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.element,e])},n.makeJQueryPlugin=function(e){(e=e||t.jQuery)&&(i=e,i.fn.imagesLoaded=function(t,e){return new n(this,t,e).jqDeferred.promise(i(this))})},n.makeJQueryPlugin(),n}));; 3806/** 3807 * jQuery plugin to make the main navigation WAI-ARIA compatible 3808 * Inspired by http://simplyaccessible.com/examples/css-menu/option-6/ 3809 * 3810 * It needs jquery.hoverIntent 3811 */ 3812(function($) { 3813 3814 $.fn.ariaNavigation = function(settings) { 3815 3816 //Map of all the alphanumeric keys so one can jump through submenus by typing the first letter 3817 //Also use the ESC key to close a submenu 3818 var keyCodeMap = { 3819 48: "0", 3820 49: "1", 3821 50: "2", 3822 51: "3", 3823 52: "4", 3824 53: "5", 3825 54: "6", 3826 55: "7", 3827 56: "8", 3828 57: "9", 3829 59: ";", 3830 65: "a", 3831 66: "b", 3832 67: "c", 3833 68: "d", 3834 69: "e", 3835 70: "f", 3836 71: "g", 3837 72: "h", 3838 73: "i", 3839 74: "j", 3840 75: "k", 3841 76: "l", 3842 77: "m", 3843 78: "n", 3844 79: "o", 3845 80: "p", 3846 81: "q", 3847 82: "r", 3848 83: "s", 3849 84: "t", 3850 85: "u", 3851 86: "v", 3852 87: "w", 3853 88: "x", 3854 89: "y", 3855 90: "z", 3856 96: "0", 3857 97: "1", 3858 98: "2", 3859 99: "3", 3860 100: "4", 3861 101: "5", 3862 102: "6", 3863 103: "7", 3864 104: "8", 3865 105: "9" 3866 }, 3867 $nav = $(this), 3868 $allLinks = $nav.find('li.menu-item > a, li.page_item > a'), 3869 $topLevelLinks = $nav.find('> li > a'), 3870 subLevelLinks = $topLevelLinks.parent('li').find('ul').find('a'); 3871 navWidth = $nav.outerWidth(); 3872 3873 //default settings 3874 settings = jQuery.extend({ 3875 menuHoverClass: 'show-menu', 3876 topMenuHoverClass: 'hover' 3877 }, settings); 3878 3879 3880 /** 3881 * First add the needed WAI-ARIA markup - supercharge the menu 3882 */ 3883 3884 // Add ARIA role to menubar and menu items 3885 //$nav.find( 'li' ).attr( 'role', 'menuitem' ); 3886 3887 $topLevelLinks.each(function() { 3888 //for regular sub-menus 3889 // Set tabIndex to -1 so that links can't receive focus until menu is open 3890 $(this).next('ul') 3891 .attr({ 3892 'aria-hidden': 'true', 3893 'role': 'menu' 3894 }) 3895 .find('a') 3896 .attr('tabIndex', -1); 3897 3898 // Add aria-haspopup for appropriate items 3899 if ($(this).next('ul').length > 0) { 3900 $(this).parent('li').attr('aria-haspopup', 'true'); 3901 } 3902 3903 // Set tabIndex to -1 so that links can't receive focus until menu is open 3904 $(this).next('.menu-item-has-children').children('ul') 3905 .attr({ 3906 'aria-hidden': 'true', 3907 'role': 'menu' 3908 }) 3909 .find('a').attr('tabIndex', -1); 3910 3911 $(this).next('.menu-item-has-children') 3912 .find('a').attr('tabIndex', -1); 3913 3914 // Add aria-haspopup for appropriate items 3915 if ($(this).next('.sub-menu').length > 0) 3916 $(this).parent('li').attr('aria-haspopup', 'true'); 3917 3918 // Set tabIndex to -1 so that links can't receive focus until menu is open 3919 $(this).next('.page_item_has_children').children('ul') 3920 .attr({ 3921 'aria-hidden': 'true', 3922 'role': 'menu' 3923 }) 3924 .find('a').attr('tabIndex', -1); 3925 3926 $(this).next('.page_item_has_children') 3927 .find('a').attr('tabIndex', -1); 3928 3929 // Add aria-haspopup for appropriate items 3930 if ($(this).next('.children').length > 0) 3931 $(this).parent('li').attr('aria-haspopup', 'true'); 3932 }); 3933 3934 3935 /** 3936 * Now let's begin binding things to their proper events 3937 */ 3938 3939 // First, bind to the hover event 3940 // use hoverIntent to make sure we avoid flicker 3941 $allLinks.closest('li').hoverIntent({ 3942 over: function() { 3943 //clean up first 3944 $(this).closest('ul') 3945 .find('ul.' + settings.menuHoverClass) 3946 .attr('aria-hidden', 'true') 3947 .removeClass(settings.menuHoverClass) 3948 .find('a') 3949 .attr('tabIndex', -1); 3950 3951 $(this).closest('ul') 3952 .find('.' + settings.topMenuHoverClass) 3953 .removeClass(settings.topMenuHoverClass); 3954 3955 //now do things 3956 showSubMenu($(this)); 3957 3958 }, 3959 out: function() { 3960 hideSubMenu($(this)); 3961 }, 3962 timeout: 10 3963 }); 3964 3965 // Secondly, bind to the focus event - very important for WAI-ARIA purposes 3966 $allLinks.focus(function() { 3967 //clean up first 3968 $(this).closest('ul') 3969 .find('ul.' + settings.menuHoverClass) 3970 .attr('aria-hidden', 'true') 3971 .removeClass(settings.menuHoverClass) 3972 .find('a') 3973 .attr('tabIndex', -1); 3974 3975 $(this).closest('ul') 3976 .find('.' + settings.topMenuHoverClass) 3977 .removeClass(settings.topMenuHoverClass); 3978 3979 //now do things 3980 showSubMenu($(this).closest('li')); 3981 3982 }); 3983 3984 3985 // Now bind arrow keys for navigating the menu 3986 3987 // First the top level links (the permanent visible links) 3988 $topLevelLinks.keydown(function(e) { 3989 var $item = $(this); 3990 3991 if (e.keyCode == 37) { //left arrow 3992 e.preventDefault(); 3993 // This is the first item 3994 if ($item.parent('li').prev('li').length == 0) { 3995 $item.parents('ul').find('> li').last().find('a').first().focus(); 3996 } else { 3997 $item.parent('li').prev('li').find('a').first().focus(); 3998 } 3999 } else if (e.keyCode == 38) { //up arrow 4000 e.preventDefault(); 4001 if ($item.parent('li').find('ul').length > 0) { 4002 $item.parent('li').find('ul') 4003 .attr('aria-hidden', 'false') 4004 .addClass(settings.menuHoverClass) 4005 .find('a').attr('tabIndex', 0) 4006 .last().focus(); 4007 } 4008 } else if (e.keyCode == 39) { //right arrow 4009 e.preventDefault(); 4010 4011 // This is the last item 4012 if ($item.parent('li').next('li').length == 0) { 4013 $item.parents('ul').find('> li').first().find('a').first().focus(); 4014 } else { 4015 $item.parent('li').next('li').find('a').first().focus(); 4016 } 4017 } else if (e.keyCode == 40) { //down arrow 4018 e.preventDefault(); 4019 if ($item.parent('li').find('ul').length > 0) { 4020 $item.parent('li').find('ul') 4021 .attr('aria-hidden', 'false') 4022 .addClass(settings.menuHoverClass) 4023 .find('a').attr('tabIndex', 0) 4024 .first().focus(); 4025 } 4026 } else if (e.keyCode == 32) { //space key 4027 // If submenu is hidden, open it 4028 e.preventDefault(); 4029 $item.parent('li').find('ul[aria-hidden=true]') 4030 .attr('aria-hidden', 'false') 4031 .addClass(settings.menuHoverClass) 4032 .find('a').attr('tabIndex', 0) 4033 .first().focus(); 4034 } else if (e.keyCode == 27) { //escape key 4035 e.preventDefault(); 4036 $('.' + settings.menuHoverClass) 4037 .attr('aria-hidden', 'true') 4038 .removeClass(settings.menuHoverClass) 4039 .find('a') 4040 .attr('tabIndex', -1); 4041 } else { //cycle through the child submenu items based on the first letter 4042 $item.parent('li').find('ul[aria-hidden=false] > li > a').each(function() { 4043 if ($(this).text().substring(0, 1).toLowerCase() == keyCodeMap[e.keyCode]) { 4044 $(this).focus(); 4045 return false;
4046 } 4047 }); 4048 } 4049 }); 4050 4051 // Now do the keys bind for the submenus links 4052 $(subLevelLinks).keydown(function(e) { 4053 var $item = $(this); 4054 4055 if (e.keyCode == 38) { //up arrow 4056 e.preventDefault(); 4057 // This is the first item 4058 if ($item.parent('li').prev('li').length == 0) { 4059 $item.parents('ul').parents('li').find('a').first().focus(); 4060 } else { 4061 $item.parent('li').prev('li').find('a').first().focus(); 4062 } 4063 } else if (e.keyCode == 39) { //right arrow 4064 e.preventDefault(); 4065 4066 //if it has sub-menus we should go into them 4067 if ($item.parent('li').hasClass('menu-item-has-children')) { 4068 $item.next('ul').find('> li').first().find('a').first().focus(); 4069 } else { 4070 // This is the last item 4071 if ($item.parent('li').next('li').length == 0) { 4072 $item.closest('ul').closest('li').children('a').first().focus(); 4073 } else { 4074 $item.parent('li').next('li').find('a').first().focus(); 4075 } 4076 } 4077 } else if (e.keyCode == 40) { //down arrow 4078 e.preventDefault(); 4079 4080 // This is the last item 4081 if ($item.parent('li').next('li').length == 0) { 4082 $item.closest('ul').closest('li').children('a').first().focus(); 4083 } else { 4084 $item.parent('li').next('li').find('a').first().focus(); 4085 } 4086 } else if (e.keyCode == 27 || e.keyCode == 37) { //escape key or left arrow => jump to the upper level links 4087 e.preventDefault(); 4088 4089 //focus on the upper level link 4090 $item.closest('ul').closest('li') 4091 .children('a').first().focus(); 4092 4093 } else if (e.keyCode == 32) { //space key 4094 e.preventDefault(); 4095 window.location = $item.attr('href'); 4096 } else { 4097 4098 //cycle through the menu items based on the first letter 4099 var found = false; 4100 $item.parent('li').nextAll('li').find('a').each(function() { 4101 if ($(this).text().substring(0, 1).toLowerCase() == keyCodeMap[e.keyCode]) { 4102 $(this).focus(); 4103 found = true; 4104 return false; 4105 } 4106 }); 4107 4108 if (!found) { 4109 $item.parent('li').prevAll('li').find('a').each(function() { 4110 if ($(this).text().substring(0, 1).toLowerCase() == keyCodeMap[e.keyCode]) { 4111 $(this).focus(); 4112 return false; 4113 } 4114 }); 4115 } 4116 } 4117 }); 4118 4119 4120 // Hide menu if click or focus occurs outside of navigation 4121 $nav.find('a').last().keydown(function(e) { 4122 if (e.keyCode == 9) { //tab key 4123 // If the user tabs out of the navigation hide all menus 4124 hideSubMenus(); 4125 } 4126 }); 4127 4128 //close all menus when pressing ESC key 4129 $(document).keydown(function(e) { 4130 if (e.keyCode == 27) { //esc key 4131 hideSubMenus(); 4132 } 4133 }); 4134 4135 //close all menus on click outside 4136 $(document).click(function() { 4137 hideSubMenus(); 4138 }); 4139 4140 $nav.on('click touchstart', function(e) { 4141 e.stopPropagation(); 4142 }); 4143 4144 function getIOSVersion(ua) { 4145 ua = ua || navigator.userAgent; 4146 return parseFloat( 4147 ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(ua) || [0, ''])[1]) 4148 .replace('undefined', '3_2').replace('_', '.').replace('_', '') 4149 ) || false; 4150 } 4151 4152 function showSubMenu($item) { 4153 4154 $item.addClass(settings.topMenuHoverClass); 4155 4156 $item.find('.sub-menu, .children').first() //affect only the first ul found - the one with the submenus, ignore the mega menu items 4157 .attr('aria-hidden', 'false') 4158 .addClass(settings.menuHoverClass); 4159 4160 $item.find('a').attr('tabIndex', 0); //set the tabIndex to 0 so we let the browser figure out the tab order 4161 4162 } 4163 4164 function hideSubMenu($item) { 4165 4166 if ($item.hasClass('menu-item-has-children')) { 4167 $item.children('.sub-menu').css('left', ''); 4168 } 4169 4170 if ($item.hasClass('page_item_has_children')) { 4171 $item.children('.children').css('left', ''); 4172 } 4173 4174 $item.children('a').first().next('ul') 4175 .attr('aria-hidden', 'true') 4176 .removeClass(settings.menuHoverClass) 4177 .find('a') 4178 .attr('tabIndex', -1); 4179 4180 //when dealing with first level submenus - they are wrapped 4181 $item.children('a').first().next('.sub-menu') 4182 .attr('aria-hidden', 'true') 4183 .removeClass(settings.menuHoverClass) 4184 .find('a') 4185 .attr('tabIndex', -1); 4186 4187 $item.children('a').first().next('.children') 4188 .attr('aria-hidden', 'true') 4189 .removeClass(settings.menuHoverClass) 4190 .find('a') 4191 .attr('tabIndex', -1); 4192 4193 $item.removeClass(settings.topMenuHoverClass); 4194 } 4195 4196 function hideSubMenus() { 4197 4198 $('.' + settings.menuHoverClass) 4199 .attr('aria-hidden', 'true') 4200 .removeClass(settings.menuHoverClass) 4201 .find('a') 4202 .attr('tabIndex', -1); 4203 4204 $('.' + settings.topMenuHoverClass).removeClass(settings.topMenuHoverClass); 4205 4206 } 4207 } 4208 4209})(jQuery); 4210 4211/*! 4212 * hoverIntent v1.8.0 // 2014.06.29 // jQuery v1.9.1+ 4213 * http://cherne.net/brian/resources/jquery.hoverIntent.html 4214 * 4215 * You may use hoverIntent under the terms of the MIT license. Basically that 4216 * means you are free to use hoverIntent as long as this header is left intact. 4217 * Copyright 2007, 2014 Brian Cherne 4218 */ 4219(function($) { 4220 $.fn.hoverIntent = function(handlerIn, handlerOut, selector) { 4221 var cfg = { 4222 interval: 100, 4223 sensitivity: 6, 4224 timeout: 0 4225 };
4226 if (typeof handlerIn === "object") { 4227 cfg = $.extend(cfg, handlerIn) 4228 } else { 4229 if ($.isFunction(handlerOut)) { 4230 cfg = $.extend(cfg, { 4231 over: handlerIn, 4232 out: handlerOut, 4233 selector: selector 4234 }) 4235 } else { 4236 cfg = $.extend(cfg, { 4237 over: handlerIn, 4238 out: handlerIn, 4239 selector: handlerOut 4240 }) 4241 } 4242 } 4243 var cX, cY, pX, pY; 4244 var track = function(ev) { 4245 cX = ev.pageX; 4246 cY = ev.pageY 4247 }; 4248 var compare = function(ev, ob) { 4249 ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); 4250 if (Math.sqrt((pX - cX) * (pX - cX) + (pY - cY) * (pY - cY)) < cfg.sensitivity) { 4251 $(ob).off("mousemove.hoverIntent", track); 4252 ob.hoverIntent_s = true; 4253 return cfg.over.apply(ob, [ev]) 4254 } else { 4255 pX = cX; 4256 pY = cY; 4257 ob.hoverIntent_t = setTimeout(function() { 4258 compare(ev, ob) 4259 }, cfg.interval) 4260 } 4261 }; 4262 var delay = function(ev, ob) { 4263 ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); 4264 ob.hoverIntent_s = false; 4265 return cfg.out.apply(ob, [ev]) 4266 }; 4267 var handleHover = function(e) { 4268 var ev = $.extend({}, e); 4269 var ob = this; 4270 if (ob.hoverIntent_t) { 4271 ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t) 4272 } 4273 if (e.type === "mouseenter") { 4274 pX = ev.pageX; 4275 pY = ev.pageY; 4276 $(ob).on("mousemove.hoverIntent", track); 4277 if (!ob.hoverIntent_s) { 4278 ob.hoverIntent_t = setTimeout(function() { 4279 compare(ev, ob) 4280 }, cfg.interval) 4281 } 4282 } else { 4283 $(ob).off("mousemove.hoverIntent", track); 4284 if (ob.hoverIntent_s) { 4285 ob.hoverIntent_t = setTimeout(function() { 4286 delay(ev, ob) 4287 }, cfg.timeout) 4288 } 4289 } 4290 }; 4291 return this.on({ 4292 "mouseenter.hoverIntent": handleHover, 4293 "mouseleave.hoverIntent": handleHover 4294 }, cfg.selector) 4295 } 4296})(jQuery); 4297// /* ====== Smart Resize Logic ====== */ 4298// It's best to debounce the resize event to a void performance hiccups 4299(function($, sr) { 4300 4301 /** 4302 * debouncing function from John Hann 4303 * http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ 4304 */ 4305 var debounce = function(func, threshold, execAsap) { 4306 var timeout; 4307 4308 return function debounced() { 4309 var obj = this, 4310 args = arguments; 4311 4312 function delayed() { 4313 if (!execAsap) func.apply(obj, args); 4314 timeout = null; 4315 } 4316 4317 if (timeout) clearTimeout(timeout); 4318 else if (execAsap) func.apply(obj, args); 4319 4320 timeout = setTimeout(delayed, threshold || 200); 4321 }; 4322 } 4323 // smartresize 4324 jQuery.fn[sr] = function(fn) { 4325 return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); 4326 }; 4327 4328})(jQuery, 'smartresize'); 4329(function($, undefined) { 4330 // /* ====== SHARED VARS - jQuery ====== */ 4331 // These depend on jQuery 4332 var $window = $(window), 4333 windowHeight = $window.height(), 4334 windowWidth = $window.width(), 4335 latestKnownScrollY = window.scrollY, 4336 ticking = false, 4337 isTouchDevice = !!('ontouchstart' in window), 4338 $body = $('body'), 4339 isWekbit = ('WebkitAppearance' in document.documentElement.style) ? true : false, 4340 isIE = typeof(is_ie) !== "undefined" || (!(window.ActiveXObject) && "ActiveXObject" in window), 4341 isiele10 = navigator.userAgent.match(/msie (9|([1-9][0-9]))/i); 4342 var Grid = function() { 4343 4344 }; 4345 4346 Grid.prototype.refresh = function() { 4347 this.alignTitles(); 4348 this.addMargins(); 4349 this.adjustCardMeta(); 4350 this.offsetFirstColumn(); 4351 }; 4352 4353 Grid.prototype.alignTitles = function() { 4354 4355 $('.card-title-wrap').each(function(i, obj) { 4356 var $title = $(obj); 4357 $title.closest('.card__wrap').css('paddingBottom', $title.outerHeight() * 0.5); 4358 }); 4359 4360 }; 4361 4362 Grid.prototype.addMargins = function() { 4363 4364 var $columns = $('.column'), 4365 $compare; 4366 4367 $('.grid__item--mb').removeClass('grid__item--mb'); 4368 4369 $columns.each(function(i, column) { 4370 var $column = $(column); 4371 4372 if (i % 2 == 1) { 4373 4374 $column.children('.entry-card--portrait, .entry-card--text').each(function(j, obj) { 4375 var $obj = $(obj); 4376 4377 if ($obj.is(':nth-child(2n+1)')) { 4378 $compare = $columns.eq(i - 1); 4379 } else { 4380 $compare = $columns.eq(i + 1); 4381 } 4382 4383 if (typeof $compare == "undefined") { 4384 return; 4385 } 4386 4387 var bottom = $obj.offset().top + $obj.outerHeight(), 4388 $neighbour; 4389 4390 $compare.children().each(function(j, item) { 4391 var $item = $(item), 4392 thisBottom = $(item).offset().top + $(item).outerHeight(); 4393 4394 if (thisBottom < bottom) { 4395 $neighbour = $item; 4396 } else { 4397 return false;
4398 } 4399 }); 4400 4401 if (typeof $neighbour !== "undefined") { 4402 $neighbour.addClass('grid__item--mb'); 4403 } 4404 }); 4405 } 4406 }); 4407 }; 4408 4409 Grid.prototype.showCards = function($cards) { 4410 4411 $cards.each(function(i, obj) { 4412 var $obj = $(obj); 4413 4414 setTimeout(function() { 4415 $obj.addClass('is-visible'); 4416 }, i * 150); 4417 4418 setTimeout(function() { 4419 $obj.removeClass('ajax-loaded'); 4420 }, 400 + i * 150); 4421 4422 $obj.imagesLoaded(function() { 4423 4424 var $img = $('<img>'), 4425 $thumbnail = $obj.find('.card__image--large'); 4426 4427 $img.attr('src', $thumbnail.data('src')); 4428 4429 $img.imagesLoaded(function() { 4430 $obj.addClass('is-loaded'); 4431 }); 4432 4433 $thumbnail.replaceWith($img); 4434 }); 4435 }); 4436 4437 }; 4438 4439 Grid.prototype.adjustCardMeta = function() { 4440 4441 if ($body.is('.singular') || windowWidth < 480) { 4442 $('.card__meta').attr('style', ''); 4443 } else { 4444 $('.card--image').each(function(i, obj) { 4445 var $cardMeta = $(obj).find('.card__meta'); 4446 $cardMeta.css('marginTop', -1 * $cardMeta.height()); 4447 }); 4448 } 4449 4450 }; 4451 4452 Grid.prototype.offsetFirstColumn = function() { 4453 var $columns = $('.bricklayer-column'); 4454 if ($columns.length > 1) { 4455 var height = $('.header .site-branding').outerHeight(); 4456 $columns.css('marginTop', '').eq(1).css('marginTop', height); 4457 } 4458 }; 4459 4460 Logo = function() { 4461 4462 this.$header = $('.header'); 4463 this.$logo = $('.site-branding'); 4464 this.$clone = null; 4465 this.distance = null; 4466 this.initialized = false; 4467 4468 }; 4469 4470 Logo.prototype.adjustSiteTitle = function() { 4471 4472 $('.site-title').each(function(i, obj) { 4473 var $title = $(obj).removeAttr('style').css('fontSize', ''), 4474 $branding = $title.closest('.site-branding'), 4475 $span = $title.find('span'), 4476 titleWidth = $title.width(), 4477 brandingHeight, 4478 spanWidth = $span.width(), 4479 fontSize = parseInt($title.css('fontSize', '').css('fontSize')), 4480 scaling = spanWidth / parseFloat(titleWidth), 4481 maxLines = $body.is('.singular') ? 3 : 2; 4482 4483 /* if site title is too long use a smaller font size */ 4484 if (spanWidth > titleWidth) { 4485 fontSize = parseInt(fontSize / scaling); 4486 $title.css('fontSize', fontSize); 4487 } 4488 4489 var titleHeight = $title.outerHeight(); 4490 4491 if ($title.closest('.mobile-logo').length) { 4492 var mobileHeight = $('.mobile-logo').outerHeight(); 4493 fontSize = parseInt(fontSize * mobileHeight / titleHeight); 4494 if (mobileHeight < titleHeight) { 4495 $title.css('fontSize', fontSize); 4496 } 4497 return; 4498 } 4499 4500 brandingHeight = $branding.outerHeight(); 4501 4502 /* if site title is too long use a smaller font size */ 4503 if (brandingHeight < titleHeight) { 4504 fontSize = parseInt(fontSize * brandingHeight / t
4504itleHeight); 4505 $title.css('fontSize', fontSize); 4506 return; 4507 } 4508 4509 /* if site title is too tall, again, use a smaller font size */ 4510 var lineHeight = parseFloat($title.css('lineHeight')) / fontSize, 4511 lines = Math.round(titleHeight / fontSize / lineHeight); 4512 4513 while (lines > maxLines) { 4514 fontSize = fontSize - 1; 4515 $title.css('fontSize', fontSize); 4516 titleHeight = $title.outerHeight(); 4517 lines = titleHeight / fontSize / lineHeight; 4518 } 4519 }); 4520 }; 4521 4522 Logo.prototype.adjustArchiveTitle = function() { 4523 4524 $('.archive-title').each(function(i, obj) { 4525 var $title = $(obj).removeAttr('style').css('fontSize', ''), 4526 fontSize = parseInt($title.css('font-size')), 4527 $span = $title.find('span'), 4528 titleWidth = $title.width(), 4529 spanWidth = $span.width(), 4530 scaling = spanWidth / parseFloat(titleWidth); 4531 4532 /* if site title is too long use a smaller font size */ 4533 if (spanWidth > titleWidth) { 4534 fontSize = parseInt(fontSize / scaling); 4535 $title.css('fontSize', fontSize); 4536 } 4537 }); 4538 4539 }; 4540 4541 Logo.prototype.prepare = function(scrollY) { 4542 4543 var that = this; 4544 4545 scrollY = scrollY || (window.pageYOffset || document.documentElement.scrollTop) - (document.documentElement.clientTop || 0); 4546 4547 if (that.$logo.length) { 4548 4549 that.$logo.imagesLoaded(function() { 4550 4551 if (that.$clone === null) { 4552 that.$clone = that.$logo.clone().appendTo('.mobile-logo'); 4553 } else { 4554 that.$clone.removeAttr('style'); 4555 } 4556 4557 that.logoMid = that.$logo.offset().top + that.$logo.height() / 2; 4558 that.cloneMid = that.$clone.offset().top + that.$clone.height() / 2 - scrollY; 4559 that.distance = that.logoMid - that.cloneMid; 4560 4561 that.initialized = true; 4562 4563 that.update(scrollY); 4564 4565 that.$clone.css('opacity', 1); 4566 4567 }); 4568 } 4569 }; 4570 4571 Logo.prototype.update = function(scrollY) { 4572 4573 if (!this.initialized) { 4574 return; 4575 } 4576 4577 if (this.distance < scrollY) { 4578 this.$clone.css('transform', 'none'); 4579 return; 4580 } 4581 4582 this.$clone.css('transform', 'translateY(' + (this.distance - scrollY) + 'px)'); 4583 }; 4584 4585 /*! 4586 * pixelgradeTheme v1.0.1 4587 * Copyright (c) 2017 PixelGrade http://www.pixelgrade.com 4588 * Licensed under MIT http://www.opensource.org/licenses/mit-license.php/ 4589 */ 4590 var pixelgradeTheme = function() { 4591 4592 var _this = this, 4593 windowWidth = window.innerWidth, 4594 windowHeight = window.innerHeight, 4595 lastScrollY = (window.pageYOffset || document.documentElement.scrollTop) - (document.documentElement.clientTop || 0), 4596 orientation = windowWidth > windowHeight ? 'landscape' : 'portrait'; 4597 4598 _this.ev = $({}); 4599 _this.frameRendered = false; 4600 _this.debug = false; 4601 4602 _this.log = function() { 4603 if (_this.debug) { 4604 console.log.apply(this, arguments) 4605 } 4606 }; 4607
4608 _this.getScroll = function() { 4609 return lastScrollY; 4610 }; 4611 4612 _this.getWindowWidth = function() { 4613 return windowWidth; 4614 }; 4615 4616 _this.getWindowHeight = function() { 4617 return windowHeight; 4618 }; 4619 4620 _this.getOrientation = function() { 4621 return orientation; 4622 }; 4623 4624 _this.onScroll = function() { 4625 if (_this.frameRendered === false) { 4626 return; 4627 } 4628 lastScrollY = (window.pageYOffset || document.documentElement.scrollTop) - (document.documentElement.clientTop || 0); 4629 _this.frameRendered = false; 4630 }; 4631 4632 _this.onResize = function() { 4633 windowWidth = window.innerWidth; 4634 windowHeight = window.innerHeight; 4635 4636 var newOrientation = windowWidth > windowHeight ? 'landscape' : 'portrait'; 4637 4638 _this.debouncedResize(); 4639 4640 if (orientation !== newOrientation) { 4641 _this.debouncedOrientationChange(); 4642 } 4643 4644 orientation = newOrientation; 4645 }; 4646 4647 _this.debouncedResize = Util.debounce(function() { 4648 $(window).trigger('pxg:resize'); 4649 }, 300); 4650 4651 _this.debouncedOrientationChange = Util.debounce(function() { 4652 $(window).trigger('pxg:orientationchange'); 4653 }, 300); 4654 4655 _this.renderLoop = function() { 4656 if (_this.frameRendered === false) { 4657 _this.ev.trigger('render'); 4658 } 4659 requestAnimationFrame(function() { 4660 _this.renderLoop(); 4661 _this.frameRendered = true; 4662 _this.ev.trigger('afterRender'); 4663 }); 4664 }; 4665 4666 _this.eventHandlers = function() { 4667 $(document).ready(_this.onReady); 4668 $(window) 4669 .on('scroll', _this.onScroll) 4670 .on('resize', _this.onResize) 4671 .on('load', _this.onLoad); 4672 }; 4673 4674 _this.eventHandlers(); 4675 _this.renderLoop(); 4676 }; 4677 4678 pixelgradeTheme.prototype.onReady = function() { 4679 $('html').addClass('is-ready'); 4680 }; 4681 4682 pixelgradeTheme.prototype.onLoad = function() { 4683 $('html').addClass('is-loaded'); 4684 }; 4685 4686 var Util = { 4687 /** 4688 * 4689 * @returns {boolean} 4690 */ 4691 isTouch: function() { 4692 return !!( 4693 "ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch 4694 ); 4695 }, 4696 4697 handleCustomCSS: function($container) { 4698 var $elements = typeof $container !== "undefined" ? $container.find("[data-css]") : $("[data-css]"); 4699 4700 if ($elements.length) { 4701 $elements.each(function(i, obj) { 4702 var $element = $(obj), 4703 css = $element.data('css'); 4704 4705 if (typeof css !== "undefined") { 4706 $element.replaceWith('<style type="text/css">' + css + '</style>'); 4707 } 4708 }); 4709 } 4710 }, 4711 4712 4713 /** 4714 * Search every image that is alone in a p tag and wrap it 4715 * in a figure element to behave like images with captions 4716 * 4717 * @param $container 4718 */ 4719 unwrapImages: function($container) { 4720 4721 $container = typeof $container !== "undefined" ? $container : $body; 4722 4723 $container.find('p > img:first-child:last-child, p > a:first-child:last-child > img').each(function(i, obj) { 4724 var $obj = $(obj), 4725 $image = $obj.closest('img'), 4726 className = $image.attr('class'), 4727 $p = $image.closest('p'), 4728 $figure = $('<figure />').attr('class', className); 4729 4730 if ($.trim($p.text()).length) { 4731 return; 4732 } 4733 4734 $figure.append($image.removeAttr('class')); 4735 $p.replaceWith($figure); 4736 }); 4737 }, 4738 4739 wrapEmbeds: function($container) { 4740 $container = typeof $container !== "undefined" ? $container : $('body'); 4741 $container.children('iframe, embed, object').wrap('<p>'); 4742 }, 4743 4744 /** 4745 * Initialize video elements on demand from placeholders 4746 * 4747 * @param $container 4748 */ 4749 handleVideos: function($container) { 4750 $container = typeof $container !== "undefined" ? $container : $body; 4751 4752 $container.find('.video-placeholder').each(function(i, obj) { 4753 var $placeholder = $(obj), 4754 video = document.createElement('video'), 4755 $video = $(video).addClass('c-hero__video'); 4756 4757 // play as soon as possible 4758 video.onloadedmetadata = function() { 4759 video.play(); 4760 }; 4761 4762 video.src = $placeholder.data('src'); 4763 video.poster = $placeholder.data('poster'); 4764 video.muted = true; 4765 video.loop = true; 4766 4767 $placeholder.replaceWith($video); 4768 }); 4769 }, 4770 4771 smoothScrollTo: function(to, duration, easing) { 4772 to = to || 0; 4773 duration = duration || 1000; 4774 easing = easing || 'swing'; 4775 4776 $("html, body").stop().animate({ 4777 scrollTop: to 4778 }, duration, easing); 4779 4780 }, 4781 4782 // Returns a function, that, as long as it continues to be invoked, will not 4783 // be triggered. The function will be called after it stops being called for 4784 // N milliseconds. If `immediate` is passed, trigger the function on the 4785 // leading edge, instead of the trailing. 4786 debounce: function(func, wait, immediate) { 4787 var timeout; 4788 return function() { 4789 var context = this, 4790 args = arguments; 4791 var later = function() { 4792 timeout = null; 4793 if (!immediate) {
4794 func.apply(context, args); 4795 } 4796 }; 4797 var callNow = immediate && !timeout; 4798 clearTimeout(timeout); 4799 timeout = setTimeout(later, wait); 4800 if (callNow) { 4801 func.apply(context, args); 4802 } 4803 }; 4804 }, 4805 4806 // Returns a function, that, when invoked, will only be triggered at most once 4807 // during a given window of time. Normally, the throttled function will run 4808 // as much as it can, without ever going more than once per `wait` duration; 4809 // but if you'd like to disable the execution on the leading edge, pass 4810 // `{leading: false}`. To disable execution on the trailing edge, ditto. 4811 throttle: function(callback, limit) { 4812 var wait = false; 4813 return function() { 4814 if (!wait) { 4815 callback.call(); 4816 wait = true; 4817 setTimeout(function() { 4818 wait = false; 4819 }, limit); 4820 } 4821 } 4822 }, 4823 4824 mq: function(direction, string) { 4825 var $temp = $('<div class="u-mq-' + direction + '-' + string + '">').appendTo('body'), 4826 response = $temp.is(':visible'); 4827 4828 $temp.remove(); 4829 return response; 4830 }, 4831 4832 below: function(string) { 4833 return this.mq('below', string); 4834 }, 4835 4836 above: function(string) { 4837 return this.mq('above', string); 4838 }, 4839 4840 getParamFromURL: function(param, url) { 4841 var parameters = ( 4842 url.split('?') 4843 )[1]; 4844 4845 if (typeof parameters === "undefined") { 4846 return parameters; 4847 } 4848 4849 parameters = parameters.split('&'); 4850 4851 for (var i = 0; i < parameters.length; i++) { 4852 var parameter = parameters[i].split('='); 4853 if (parameter[0] === param) { 4854 return parameter[1]; 4855 } 4856 } 4857 }, 4858 4859 reloadScript: function(filename) { 4860 var $old = $('script[src*="' + filename + '"]'), 4861 $new = $('<script>'), 4862 src = $old.attr('src'); 4863 4864 if (!$old.length) { 4865 return; 4866 } 4867 4868 $old.replaceWith($new); 4869 $new.attr('src', src); 4870 }, 4871 4872 // here we change the link of the Edit button in the Admin Bar 4873 // to make sure it reflects the current page 4874 adminBarEditFix: function(id, editString, taxonomy) { 4875 // get the admin ajax url and clean it 4876 var baseEditURL = juliaStrings.ajaxurl.replace('admin-ajax.php', 'post.php'), 4877 baseEditTaxURL = juliaStrings.ajaxurl.replace('admin-ajax.php', 'edit-tags.php'), 4878 $editButton = $('#wp-admin-bar-edit a'); 4879 4880 if (!empty($editButton)) { 4881 if (id !== undefined && editString !== undefined) { //modify the current Edit button 4882 if (!empty(taxonomy)) { //it seems we need to edit a taxonomy 4883 $editButton.attr('href', baseEditTaxURL + '?tag_ID=' + id + '&taxonomy=' + taxonomy + '&action=edit'); 4884 } else { 4885 $editButton.attr('href', baseEditURL + '?post=' + id + '&action=edit'); 4886 } 4887 $editButton.html(editString); 4888 } else { // we have found an edit button but right now we don't need it anymore since we have no id 4889 $('#wp-admin-bar-edit').remove(); 4890 } 4891 } else { // upss ... no edit button 4892 // lets see if we need one 4893 if (id !== undefined && editString !== undefined) { //we do need one after all 4894 //locate the New button because we need to add stuff after it 4895 var $newButton = $('#wp-admin-bar-new-content'); 4896 4897 if (!empty($newButton)) { 4898 if (!empty(taxonomy)) { //it seems we need to generate a taxonomy edit thingy 4899 $newButton.after('<li id="wp-admin-bar-edit"><a class="ab-item dJAX_internal" href="' + baseEditTaxURL + '?tag_ID=' + id + '&taxonomy='
4899+ taxonomy + '&action=edit">' + editString + '</a></li>'); 4900 } else { //just a regular edit 4901 $newButton.after('<li id="wp-admin-bar-edit"><a class="ab-item dJAX_internal" href="' + baseEditURL + '?post=' + id + '&action=edit">' + editString + '</a></li>'); 4902 } 4903 } 4904 } 4905 } 4906 4907 //Also we need to fix the (no-)customize-support class on body by running the WordPress inline script again 4908 // The original code is generated by the wp_customize_support_script() function in wp-includes/theme.php @3007 4909 var request, b = document.body, 4910 c = 'className', 4911 cs = 'customize-support', 4912 rcs = new RegExp('(^|\\s+)(no-)?' + cs + '(\\s+|$)'); 4913 4914 // No CORS request 4915 request = true; 4916 4917 b[c] = b[c].replace(rcs, ' '); 4918 // The customizer requires postMessage and CORS (if the site is cross domain) 4919 b[c] += (window.postMessage && request ? ' ' : ' no-') + cs; 4920 4921 //Plus, we need to change the url of the Customize button to the current url 4922 var $customizeButton = $('#wp-admin-bar-customize a'), 4923 baseCustomizeURL = juliaStrings.ajaxurl.replace('admin-ajax.php', 'customize.php'); 4924 if (!empty($customizeButton)) { 4925 $customizeButton.attr('href', baseCustomizeURL + '?url=' + encodeURIComponent(window.location.href)); 4926 } 4927 4928 } 4929 4930 }; 4931 4932 var HandleSubmenusOnTouch = (function() { 4933 4934 var $theUsualSuspects, 4935 $theUsualAnchors, 4936 initialInit = false; 4937 4938 function init() { 4939 if (initialInit || !isTouchDevice) return; 4940 4941 $theUsualSuspects = $('li[class*=children]').removeClass('hover'); 4942 $theUsualAnchors = $theUsualSuspects.find('> a'); 4943 4944 unbind(); 4945 4946 $theUsualAnchors.on('click', function(e) { 4947 e.preventDefault(); 4948 e.stopPropagation(); 4949 4950 if ($(this).hasClass('active')) { 4951 window.location.href = $(this).attr('href'); 4952 } 4953 4954 $theUsualAnchors.removeClass('active'); 4955 $(this).addClass('active'); 4956 4957 // When a parent menu item is activated, 4958 // close other menu items on the same level 4959 $(this).parent().siblings().removeClass('hover'); 4960 4961 // Open the sub menu of this parent item 4962 $(this).parent().addClass('hover'); 4963 }); 4964 4965 bindOuterNavClick(); 4966 4967 initialInit = true; 4968 } 4969 4970 function unbind() { 4971 $theUsualAnchors.unbind(); 4972 isHorizontalInitiated = false; 4973 } 4974 4975 // When a sub menu is open, close it by a touch on 4976 // any other part of the viewport than navigation. 4977 // use case: normal, horizontal menu, touch events, 4978 // sub menus are not visible. 4979 function bindOuterNavClick() { 4980 $body.on('touchstart', function(e) { 4981 var container = $('.main-navigation'); 4982 4983 if (!container.is(e.target) // if the target of the click isn't the container... 4984 && 4985 container.has(e.target).length === 0) // ... nor a descendant of the container 4986 { 4987 $theUsualSuspects.removeClass('hover').removeClass('active'); 4988 } 4989 }); 4990 } 4991 4992 return { 4993 init: init 4994 } 4995 }()); 4996 4997 function checkForSmallImageOnSingle() { 4998 if (!$body.hasClass('singular') || $body.hasClass('no-featured-image')) { 4999 return; 5000 } 5001 5002 if (windowWidth > 900) { 5003 if ($('.entry-featured img').width() < 500) { 5004 $body.addClass('has--small-featured-image'); 5005 $('.post__content').css('paddingTop', $('.entry-featured').height() + 30); 5006 } 5007 } else { 5008 $('.post__content').removeAttr('style'); 5009 } 5010 } 5011 5012 var Gema = new pixelgradeTheme(), 5013 log = Gema.log, 5014 resizeEvent = 'ontouchstart' in window && 'onorientationchange' in window ? 'pxg:orientationchange' : 'pxg:resize', 5015 $html = $('html'), 5016 $body = $('body'); 5017 5018 window.bricklayer = null; 5019 5020 Gema.init = function() { 5021 $body.toggleClass('is--webkit', isWekbit); 5022 $body.toggleClass('is--ie', isIE); 5023 $body.toggleClass('is--ie-le10', isiele10); 5024 5025 Gema.Grid = new Grid(); 5026 Gema.Logo = new Logo(); 5027 5028 checkForSmallImageOnSingle(); 5029 5030 HandleSubmenusOnTouch.init(); 5031 5032 $('ul.nav-menu').ariaNavigation(); 5033 5034 $('.grid, .site-header').css('opacity', 1); 5035 5036 if ($body.is('.singular')) { 5037 prepareSingle(); 5038 } else { 5039 prepareArchive(); 5040 } 5041 5042 Gema.adjustLayout(); 5043 }; 5044 5045 Gema.onPostLoad = function(event, data) { 5046 var $elements = $(data.html).filter('.grid__item').addClass('ajax-loaded').each(function(i, obj) { 5047 if ($.fn.mediaelementplayer) { 5048 $(obj).find('audio, video').mediaelementplayer(); 5049 } 5050 }); 5051 5052 $('.infinite-loader').remove(); 5053 5054 if ($('.grid').length && bricklayer !== null) { 5055 bricklayer.append($.makeArray($elements)); 5056 } 5057 5058 // Clean up the duplicate posts that are appended 5059 // by default by Jetpack's Infinite Scroll to div#main 5060 // (and also the corresponding HTML comments) 5061 $('#main').contents().each(function() { 5062 if ($(this).is('article') || this.nodeType === Node.COMMENT_NODE) { 5063 $(this).remove(); 5064 } 5065 }); 5066 5067 Gema.adjustLayout(); 5068 5069 Gema.Grid.showCards($elements); 5070 }; 5071 5072 Gema.bindEvents = function() { 5073
5074 var $nav = $('ul.nav-menu'), 5075 $document = $(document), 5076 $window = $(window); 5077 5078 $window.on(resizeEvent, Gema.adjustLayout); 5079 $window.on('load', Gema.adjustLayout); 5080 $body.on('post-load', Gema.onPostLoad); 5081 5082 Gema.ev.on('render', Gema.update); 5083 5084 $('.overlay-toggle').on('touchstart click', toggleOverlay); 5085 $('.menu-toggle').on('touchstart click', toggleNav); 5086 $('.sidebar-toggle').on('touchstart click', toggleSidebar); 5087 5088 $document.keydown(function(e) { 5089 // ESC key 5090 if (e.keyCode === 27) { 5091 hidePanels(); 5092 } 5093 }); 5094 5095 // close all menus on click outside 5096 $('.overlay-shadow').on('touchstart', function(e) { 5097 e.preventDefault(); 5098 e.stopPropagation(); 5099 5100 hidePanels(); 5101 }); 5102 } 5103 5104 function hidePanels() { 5105 5106 if ($body.hasClass('nav-is-open')) { 5107 toggleNav(); 5108 } 5109 5110 if ($body.hasClass('overlay-is-open')) { 5111 toggleOverlay(); 5112 } 5113 5114 if ($body.hasClass('sidebar-is-open')) { 5115 toggleSidebar(); 5116 } 5117 } 5118 5119 function toggleOverlay(e) { 5120 5121 if (typeof e !== "undefined") { 5122 e.preventDefault(); 5123 e.stopPropagation(); 5124 } 5125 5126 if (!$body.hasClass('overlay-is-open')) { 5127 $body.addClass('overlay-is-open'); 5128 $body.css({ 5129 overflow: 'hidden', 5130 width: $body.width(), 5131 }); 5132 } else { 5133 $body.removeClass('overlay-is-open'); 5134 $body.css({ 5135 overflow: '', 5136 width: '', 5137 }); 5138 } 5139 5140 } 5141 5142 function toggleNav(e) { 5143 5144 if (typeof e !== "undefined") { 5145 e.preventDefault(); 5146 e.stopPropagation(); 5147 } 5148 5149 if ($body.hasClass('nav-is-open')) { 5150 // closing the menu 5151 $('.menu-toggle').attr('aria-expanded', 'false'); 5152 } else { 5153 // opening the menu 5154 $('.menu-toggle').attr('aria-expanded', 'true'); 5155 } 5156 5157 $body.toggleClass('nav-is-open'); 5158 } 5159 5160 function toggleSidebar(e) { 5161 5162 if (typeof e !== "undefined") { 5163 e.preventDefault(); 5164 e.stopPropagation(); 5165 } 5166 5167 if ($body.hasClass('sidebar-is-open')) { 5168 //closing the sidebar 5169 $('.sidebar-toggle').attr('aria-expanded', 'false'); 5170 } else { 5171 // opening the sidebar 5172 $('.sidebar-toggle').attr('aria-expanded', 'true'); 5173 } 5174 5175 $body.toggleClass('sidebar-is-open'); 5176 } 5177 5178 function prepareArchive() { 5179 5180 var $cards = $('.card'); 5181 5182 if ($('.grid').length && bricklayer === null) { 5183 bricklayer = new Bricklayer(document.querySelector('.grid')); 5184 bricklayer.redraw(); 5185 } 5186 5187 Gema.Grid.showCards($cards); 5188 5189 $body.css('opacity', 1); 5190 } 5191 5192 function prepareSingle() { 5193 5194 var $mobileHeader = $('.mobile-header-wrapper'), 5195 scrollTo; 5196 5197 if ($mobileHeader.is(':visible')) { 5198 scrollTo = $('.content-area').offset().top - $mobileHeader.outerHeight(); 5199 setTimeout(function() { 5200 window.scrollTo(0, scrollTo); 5201 $body.css('opacity', 1); 5202 }); 5203 } else { 5204 $body.css('opacity', 1); 5205 } 5206 } 5207 5208 Gema.hidePanels = function() { 5209 $body.removeClass('nav-is-open') 5210 .removeClass('sidebar-is-open') 5211 .removeClass('overlay-is-open') 5212 .css('width', '') 5213 .css('overflow', ''); 5214 }; 5215 5216 Gema.update = function() { 5217 if (typeof Gema.Logo !== "undefined") { 5218 Gema.Logo.update(Gema.getScroll()); 5219 } 5220 }; 5221 5222 Gema.alignWideBlocks = function() { 5223 var $header = $('#masthead'); 5224 var headerOffset = $header.offset(); 5225 var headerBottom = $header.length ? headerOffset.top + $header.outerHeight() : 0; 5226 var headerLeft = $header.length ? headerOffset.left : 0; 5227 5228 var $sidebar = $('#secondary'); 5229 var sidebarOffset = $sidebar.offset(); 5230 var sidebarPosition = $sidebar.css('position'); 5231 var sidebarBottom = ($sidebar.length && 'fixed' !== sidebarPosition) ? sidebarOffset.top + $
5231sidebar.outerHeight() : 0; 5232 5233 var $main = $('#main'); 5234 var mainOffset = $main.offset(); 5235 var mainLeft = mainOffset.left; 5236 var mainRight = $main.length ? mainOffset.left + $main.outerWidth() : 0; 5237 5238 var bodyWidth = $body.outerWidth(); 5239 5240 var $blocks = $('.entry-content').children('.alignfull, .alignwide'); 5241 5242 $blocks.removeClass('fill-left fill-right'); 5243 $blocks.css({ 5244 marginLeft: '', 5245 marginRight: '', 5246 }); 5247 5248 $blocks.each(function(i, obj) { 5249 var $block = $(obj); 5250 var blockOffset = $block.offset(); 5251 var blockTop = blockOffset.top; 5252 var blockLeft = blockOffset.left; 5253 var blockRight = blockLeft + $block.outerWidth(); 5254 var fillLeft = blockTop > headerBottom; 5255 var fillRight = blockTop > sidebarBottom; 5256 var wideToLeft = fillLeft ? headerLeft : mainLeft; 5257 var fullToLeft = fillLeft ? 0 : mainLeft; 5258 5259 $block.toggleClass('fill-left', fillLeft || fillRight); 5260 $block.toggleClass('fill-right', fillRight); 5261 5262 if ($block.is('.alignwide')) { 5263 5264 if (fillLeft || fillRight) { 5265 $block.css('marginLeft', wideToLeft - blockLeft); 5266 } 5267 5268 if (fillRight) { 5269 $block.css('marginRight', blockRight - mainRight); 5270 } 5271 } 5272 5273 if ($block.is('.alignfull')) { 5274 5275 if (fillLeft || fillRight) { 5276 $block.css('marginLeft', fullToLeft - blockLeft); 5277 } 5278 5279 if (fillRight) { 5280 $block.css('marginRight', blockRight - bodyWidth); 5281 } 5282 } 5283 5284 }); 5285 }; 5286 5287 Gema.adjustLayout = function() { 5288 5289 if (Gema.getWindowWidth() > 900) { 5290 Gema.hidePanels(); 5291 } 5292 5293 // Single 5294 checkForSmallImageOnSingle(); 5295 Gema.placeSidebar(); 5296 5297 if ($body.is('.singular')) { 5298 Gema.alignWideBlocks(); 5299 } 5300 5301 // Archive 5302 Gema.Logo.adjustSiteTitle(); 5303 Gema.Logo.adjustArchiveTitle(); 5304 Gema.Logo.prepare(); 5305 5306 Gema.Grid.offsetFirstColumn(); 5307 Gema.Grid.adjustCardMeta(); 5308 Gema.Grid.alignTitles(); 5309 Gema.Grid.addMargins(); 5310 }; 5311 5312 Gema.placeSidebar = function() { 5313 5314 if (!$body.hasClass('has-active-sidebar')) { 5315 return; 5316 } 5317 5318 var $sidebar = $('.widget-area'), 5319 $content = $('.content-area'); 5320 5321 if (Gema.getWindowWidth() > 900) { 5322 5323 if (!$sidebar.length) { 5324 return; 5325 } 5326 5327 if ($body.hasClass('no-featured-image')) { 5328 $sidebar.addClass('is--placed').css('top', $('.site-main').css('padding-top')); 5329 } else { 5330 // If we are past the breakpoint 5331 var $featuredImage = $('.entry-featured'), 5332 featuredImageBottom = $featuredImage.height() + parseInt($('.post__content').css('margin-top'), 10); 5333 5334 // position: absolute; on the sidebar(via ".is--placed") 5335 // below the featured image; 5336 if (!$body.hasClass('has--small-featured-image')) { 5337 $sidebar.addClass('is--placed').css('top', featuredImageBottom); 5338 } else { 5339 $sidebar.addClass('is--placed').css('top', 108); 5340 } 5341 } 5342 5343 // and set a height on the content so that everything seems in place. 5344 $content.css("minHeight", $sidebar.offset().top + $sidebar.height()); 5345 } else { 5346 // Remove the height (possibly) set above. 5347 $content.removeAttr('style'); 5348 $sidebar.css('top', ''); 5349 } 5350 }; 5351 5352 $.Gema = Gema; 5353 $.Gema.init(); 5354 Gema.bindEvents(); 5355 5356})(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.