1/*! 2 * modernizr v3.6.0 3 * Build https://modernizr.com/download?-cssanimations-csscalc-csspositionsticky-cssremunit-csstransforms-csstransforms3d-csstransitions-flexbox-fontface-hidden-hsla-mediaqueries-objectfit-sizes-srcset-svg-touchevents-addtest-domprefixes-hasevent-mq-prefixed-prefixes-setclasses-shiv-testallprops-testprop-teststyles-dontmin 4 * 5 * Copyright (c) 6 * Faruk Ates 7 * Paul Irish 8 * Alex Sexton 9 * Ryan Seddon 10 * Patrick Kettner 11 * Stu Cox 12 * Richard Herrera 13 14 * MIT License 15 */ 16 17/* 18 * Modernizr tests which native CSS3 and HTML5 features are available in the 19 * current UA and makes the results available to you in two ways: as properties on 20 * a global `Modernizr` object, and as classes on the `<html>` element. This 21 * information allows you to progressively enhance your pages with a granular level 22 * of control over the experience. 23*/ 24 25;(function(window, document, undefined){ 26 var classes = []; 27 28 29 var tests = []; 30 31 32 /** 33 * 34 * ModernizrProto is the constructor for Modernizr 35 * 36 * @class 37 * @access public 38 */ 39 40 var ModernizrProto = { 41 // The current version, dummy 42 _version: '3.6.0', 43 44 // Any settings that don't work as separate modules 45 // can go in here as configuration. 46 _config: { 47 'classPrefix': '', 48 'enableClasses': true, 49 'enableJSClass': true, 50 'usePrefixes': true 51 }, 52 53 // Queue of tests 54 _q: [], 55 56 // Stub these for people who are listening 57 on: function(test, cb) { 58 // I don't really think people should do this, but we can 59 // safe guard it a bit. 60 // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 61 // This is in case people listen to synchronous tests. I would leave it out, 62 // but the code to *disallow* sync tests in the real version of this 63 // function is actually larger than this. 64 var self = this; 65 setTimeout(function() { 66 cb(self[test]); 67 }, 0); 68 }, 69 70 addTest: function(name, fn, options) { 71 tests.push({name: name, fn: fn, options: options}); 72 }, 73 74 addAsyncTest: function(fn) { 75 tests.push({name: null, fn: fn}); 76 } 77 }; 78 79 80 81 // Fake some of Object.create so we can force non test results to be non "own" properties. 82 var Modernizr = function() {}; 83 Modernizr.prototype = ModernizrProto; 84 85 // Leak modernizr globally when you `require` it rather than force it here. 86 // Overwrite name so constructor name is nicer :D 87 Modernizr = new Modernizr(); 88 89 90/*! 91{ 92 "name": "SVG", 93 "property": "svg", 94 "caniuse": "svg", 95 "tags": ["svg"], 96 "authors": ["Erik Dahlstrom"], 97 "polyfills": [ 98 "svgweb", 99 "raphael", 100 "amplesdk", 101 "canvg", 102 "svg-boilerplate", 103 "sie", 104 "dojogfx", 105 "fabricjs" 106 ] 107} 108!*/ 109/* DOC 110Detects support for SVG in `<embed>` or `<object>` elements. 111*/ 112 113 Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect); 114 115 116 /** 117 * List of property values to set for css tests. See ticket #21 118 * http://git.io/vUGl4 119 * 120 * @memberof Modernizr 121 * @name Modernizr._prefixes 122 * @optionName Modernizr._prefixes 123 * @optionProp prefixes 124 * @access public 125 * @example 126 * 127 * Modernizr._prefixes is the internal list of prefixes that we test against 128 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply 129 * an array of kebab-case vendor prefixes you can use within your code. 130 * 131 * Some common use cases include 132 * 133 * Generating all possible prefixed version of a CSS property 134 * ```js 135 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); 136 * 137 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' 138 * ``` 139 * 140 * Generating all possible prefixed version of a CSS value 141 * ```js 142 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; 143 * 144 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' 145 * ``` 146 */ 147 148 // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test 149 // values in feature detects to continue to work 150 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']); 151 152 // expose these for the plugin API. Look in the source for how to join() them against your input 153 ModernizrProto._prefixes = prefixes; 154 155 156 157 /** 158 * is returns a boolean if the typeof an obj is exactly type. 159 * 160 * @access private 161 * @function is 162 * @param {*} obj - A thing we want to check the type of 163 * @param {string} type - A string to compare the typeof against 164 * @returns {boolean} 165 */ 166 167 function is(obj, type) { 168 return typeof obj === type; 169 } 170 ; 171 172 /** 173 * Run through all tests and detect their support in the current UA. 174 * 175 * @access private 176 */ 177 178 function testRunner() { 179 var featureNames; 180 var feature; 181 var aliasIdx; 182 var result; 183 var nameIdx; 184 var featureName; 185 var featureNameSplit; 186 187 for (var featureIdx in tests) { 188 if (tests.hasOwnProperty(featureIdx)) { 189 featureNames = []; 190 feature = tests[featureIdx]; 191 // run the test, throw the return value into the Modernizr, 192 // then based on that boolean, define an appropriate className 193 // and push it into an array of classes we'll join later. 194 // 195 // If there is no name, it's an 'async' test that is run, 196 // but not directly added to the object. That should 197 // be done with a post-run addTest call. 198 if (feature.name) { 199 featureNames.push(feature.name.toLowerCase()); 200 201 if (feature.options && feature.options.aliases && feature.options.aliases.length) { 202 // Add all the aliases into the names list 203 for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 204 featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 205 } 206 } 207 } 208 209 // Run the test, or use the raw value if it's not a function 210 result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 211 212 213 // Set each of the names on the Modernizr object 214 for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 215 featureName = featureNames[nameIdx];
216 // Support dot properties as sub tests. We don't do checking to make sure 217 // that the implied parent tests have been added. You must call them in 218 // order (either in the test, or make the parent test a dependency). 219 // 220 // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 221 // hashtag famous last words 222 featureNameSplit = featureName.split('.'); 223 224 if (featureNameSplit.length === 1) { 225 Modernizr[featureNameSplit[0]] = result; 226 } else { 227 // cast to a Boolean, if not one already 228 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 229 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 230 } 231 232 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 233 } 234 235 classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 236 } 237 } 238 } 239 } 240 ; 241 242 /** 243 * docElement is a convenience wrapper to grab the root element of the document 244 * 245 * @access private 246 * @returns {HTMLElement|SVGElement} The root element of the document 247 */ 248 249 var docElement = document.documentElement; 250 251 252 /** 253 * A convenience helper to check if the document we are running in is an SVG document 254 * 255 * @access private 256 * @returns {boolean} 257 */ 258 259 var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 260 261 262 /** 263 * setClasses takes an array of class names and adds them to the root element 264 * 265 * @access private 266 * @function setClasses 267 * @param {string[]} classes - Array of class names 268 */ 269 270 // Pass in an and array of class names, e.g.:
271 // ['no-webp', 'borderradius', ...] 272 function setClasses(classes) { 273 var className = docElement.className; 274 var classPrefix = Modernizr._config.classPrefix || ''; 275 276 if (isSVG) { 277 className = className.baseVal; 278 } 279 280 // Change `no-js` to `js` (independently of the `enableClasses` option) 281 // Handle classPrefix on this too 282 if (Modernizr._config.enableJSClass) { 283 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 284 className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 285 } 286 287 if (Modernizr._config.enableClasses) { 288 // Add the new classes 289 className += ' ' + classPrefix + classes.join(' ' + classPrefix); 290 if (isSVG) { 291 docElement.className.baseVal = className; 292 } else { 293 docElement.className = className; 294 } 295 } 296 297 } 298 299 ; 300 301/** 302 * @optionName html5shiv 303 * @optionProp html5shiv 304 */ 305 306 // Take the html5 variable out of the html5shiv scope so we can return it. 307 var html5; 308 if (!isSVG) { 309 /** 310 * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 311 */ 312 ;(function(window, document) { 313 /** version */ 314 var version = '3.7.3'; 315 316 /** Preset options */ 317 var options = window.html5 || {}; 318 319 /** Used to skip problem elements */ 320 var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 321 322 /** Not all elements can be cloned in IE **/ 323 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; 324 325 /** Detect whether the browser supports default html5 styles */ 326 var supportsHtml5Styles; 327 328 /** Name of the expando, to work with multiple documents or to re-shiv one document */ 329 var expando = '_html5shiv'; 330 331 /** The id for the the documents expando */ 332 var expanID = 0; 333 334 /** Cached data for each document */ 335 var expandoData = {}; 336 337 /** Detect whether the browser supports unknown elements */ 338 var supportsUnknownElements; 339 340 (function() { 341 try { 342 var a = document.createElement('a'); 343 a.innerHTML = '<xyz></xyz>'; 344 //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles 345 supportsHtml5Styles = ('hidden' in a); 346 347 supportsUnknownElements = a.childNodes.length == 1 || (function() { 348 // assign a false positive if unable to shiv 349 (document.createElement)('a'); 350 var frag = document.createDocumentFragment(); 351 return ( 352 typeof frag.cloneNode == 'undefined' || 353 typeof frag.createDocumentFragment == 'undefined' || 354 typeof frag.createElement == 'undefined' 355 ); 356 }()); 357 } catch(e) { 358 // assign a false positive if detection fails => unable to shiv 359 supportsHtml5Styles = true; 360 supportsUnknownElements = true; 361 } 362 363 }()); 364 365 /*--------------------------------------------------------------------------*/ 366 367 /** 368 * Creates a style sheet with the given CSS text and adds it to the document. 369 * @private 370 * @param {Document} ownerDocument The document. 371 * @param {String} cssText The CSS text. 372 * @returns {StyleSheet} The style element. 373 */ 374 function addStyleSheet(ownerDocument, cssText) { 375 var p = ownerDocument.createElement('p'), 376 parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 377 378 p.innerHTML = 'x<style>' + cssText + '</style>'; 379 return parent.insertBefore(p.lastChild, parent.firstChild); 380 } 381 382 /** 383 * Returns the value of `html5.elements` as an array. 384 * @private 385 * @returns {Array} An array of shived element node names. 386 */ 387 function getElements() { 388 var elements = html5.elements; 389 return typeof elements == 'string' ? elements.split(' ') : elements; 390 } 391 392 /** 393 * Extends the built-in list of html5 elements 394 * @memberOf html5 395 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv 396 * @param {Document} ownerDocument The context document. 397 */ 398 function addElements(newElements, ownerDocument) { 399 var elements = html5.elements; 400 if(typeof elements != 'string'){ 401 elements = elements.join(' '); 402 } 403 if(typeof newElements != 'string'){ 404 newElements = newElements.join(' '); 405 } 406 html5.elements = elements +' '+ newElements; 407 shivDocument(ownerDocument); 408 } 409 410 /** 411 * Returns the data associated to the given document 412 * @private 413 * @param {Document} ownerDocument The document. 414 * @returns {Object} An object of data. 415 */ 416 function getExpandoData(ownerDocument) { 417 var data = expandoData[ownerDocument[expando]]; 418 if (!data) { 419 data = {}; 420 expanID++; 421 ownerDocument[expando] = expanID; 422 expandoData[expanID] = data; 423 } 424 return data; 425 } 426 427 /** 428 * returns a shived element for the given nodeName and document 429 * @memberOf html5 430 * @param {String}
vendor: 4,610 bytes, lines 430-541
430 nodeName name of the element 431 * @param {Document|DocumentFragment} ownerDocument The context document. 432 * @returns {Object} The shived element. 433 */ 434 function createElement(nodeName, ownerDocument, data){ 435 if (!ownerDocument) { 436 ownerDocument = document; 437 } 438 if(supportsUnknownElements){ 439 return ownerDocument.createElement(nodeName); 440 } 441 if (!data) { 442 data = getExpandoData(ownerDocument); 443 } 444 var node; 445 446 if (data.cache[nodeName]) { 447 node = data.cache[nodeName].cloneNode(); 448 } else if (saveClones.test(nodeName)) { 449 node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 450 } else { 451 node = data.createElem(nodeName); 452 } 453 454 // Avoid adding some elements to fragments in IE < 9 because 455 // * Attributes like `name` or `type` cannot be set/changed once an element 456 // is inserted into a document/fragment 457 // * Link elements with `src` attributes that are inaccessible, as with 458 // a 403 response, will cause the tab/window to crash 459 // * Script elements appended to fragments will execute when their `src` 460 // or `text` property is set 461 return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; 462 } 463 464 /** 465 * returns a shived DocumentFragment for the given document 466 * @memberOf html5 467 * @param {Document} ownerDocument The context document. 468 * @returns {Object} The shived DocumentFragment. 469 */ 470 function createDocumentFragment(ownerDocument, data){ 471 if (!ownerDocument) { 472 ownerDocument = document; 473 } 474 if(supportsUnknownElements){ 475 return ownerDocument.createDocumentFragment(); 476 } 477 data = data || getExpandoData(ownerDocument); 478 var clone = data.frag.cloneNode(), 479 i = 0, 480 elems = getElements(), 481 l = elems.length; 482 for(;i<l;i++){ 483 clone.createElement(elems[i]); 484 } 485 return clone; 486 } 487 488 /** 489 * Shivs the `createElement` and `createDocumentFragment` methods of the document. 490 * @private 491 * @param {Document|DocumentFragment} ownerDocument The document. 492 * @param {Object} data of the document. 493 */ 494 function shivMethods(ownerDocument, data) { 495 if (!data.cache) { 496 data.cache = {}; 497 data.createElem = ownerDocument.createElement; 498 data.createFrag = ownerDocument.createDocumentFragment; 499 data.frag = data.createFrag(); 500 } 501 502 503 ownerDocument.createElement = function(nodeName) { 504 //abort shiv 505 if (!html5.shivMethods) { 506 return data.createElem(nodeName); 507 } 508 return createElement(nodeName, ownerDocument, data); 509 }; 510 511 ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' + 512 'var n=f.cloneNode(),c=n.createElement;' + 513 'h.shivMethods&&(' + 514 // unroll the `createElement` calls 515 getElements().join().replace(/[\w\-:]+/g, function(nodeName) { 516 data.createElem(nodeName); 517 data.frag.createElement(nodeName); 518 return 'c("' + nodeName + '")'; 519 }) + 520 ');return n}' 521 )(html5, data.frag); 522 } 523 524 /*--------------------------------------------------------------------------*/ 525 526 /** 527 * Shivs the given document. 528 * @memberOf html5 529 * @param {Document} ownerDocument The document to shiv. 530 * @returns {Document} The shived document. 531 */ 532 function shivDocument(ownerDocument) { 533 if (!ownerDocument) { 534 ownerDocument = document; 535 } 536 var data = getExpandoData(ownerDocument); 537 538 if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) { 539 data.hasCSS = !!addStyleSheet(ownerDocument, 540 // corrects block display not defined in IE6/7/8/9 541 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{dis
541play:block}' + 542 // adds styling not present in IE6/7/8/9 543 'mark{background:#FF0;color:#000}' + 544 // hides non-rendered elements 545 'template{display:none}' 546 ); 547 } 548 if (!supportsUnknownElements) { 549 shivMethods(ownerDocument, data); 550 } 551 return ownerDocument; 552 } 553 554 /*--------------------------------------------------------------------------*/ 555 556 /** 557 * The `html5` object is exposed so that more elements can be shived and 558 * existing shiving can be detected on iframes. 559 * @type Object 560 * @example 561 * 562 * // options can be changed before the script is included 563 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false }; 564 */ 565 var html5 = { 566 567 /** 568 * An array or space separated string of node names of the elements to shiv. 569 * @memberOf html5 570 * @type Array|String 571 */ 572 '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', 573 574 /** 575 * current version of html5shiv 576 */ 577 'version': version, 578 579 /** 580 * A flag to indicate that the HTML5 style sheet should be inserted. 581 * @memberOf html5 582 * @type Boolean 583 */ 584 'shivCSS': (options.shivCSS !== false), 585 586 /** 587 * Is equal to true if a browser supports creating unknown/HTML5 elements 588 * @memberOf html5 589 * @type boolean 590 */ 591 'supportsUnknownElements': supportsUnknownElements, 592 593 /** 594 * A flag to indicate that the document's `createElement` and `createDocumentFragment` 595 * methods should be overwritten. 596 * @memberOf html5 597 * @type Boolean 598 */ 599 'shivMethods': (options.shivMethods !== false), 600 601 /** 602 * A string to describe the type of `html5` object ("default" or "default print"). 603 * @memberOf html5
604 * @type String 605 */ 606 'type': 'default', 607 608 // shivs the document according to the specified `html5` object options 609 'shivDocument': shivDocument, 610 611 //creates a shived element 612 createElement: createElement, 613 614 //creates a shived documentFragment 615 createDocumentFragment: createDocumentFragment, 616 617 //extends list of elements 618 addElements: addElements 619 }; 620 621 /*--------------------------------------------------------------------------*/ 622 623 // expose html5 624 window.html5 = html5; 625 626 // shiv the document 627 shivDocument(document); 628 629 if(typeof module == 'object' && module.exports){ 630 module.exports = html5; 631 } 632 633 }(typeof window !== 'undefined' ? window : this, document)); 634 } 635 ; 636 637 /** 638 * If the browsers follow the spec, then they would expose vendor-specific styles as: 639 * elem.style.WebkitBorderRadius 640 * instead of something like the following (which is technically incorrect): 641 * elem.style.webkitBorderRadius 642 643 * WebKit ghosts their properties in lowercase but Opera & Moz do not. 644 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ 645 * erik.eae.net/archives/2008/03/10/21.48.10/ 646 647 * More here: github.com/Modernizr/Modernizr/issues/issue/21 648 * 649 * @access private 650 * @returns {string} The string representing the vendor-specific style properties 651 */ 652 653 var omPrefixes = 'Moz O ms Webkit'; 654 655 656 /** 657 * List of JavaScript DOM values used for tests 658 * 659 * @memberof Modernizr 660 * @name Modernizr._domPrefixes 661 * @optionName Modernizr._domPrefixes 662 * @optionProp domPrefixes 663 * @access public 664 * @example 665 * 666 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather 667 * than kebab-case properties, all properties are their Capitalized variant 668 * 669 * ```js 670 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; 671 * ``` 672 */ 673 674 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); 675 ModernizrProto._domPrefixes = domPrefixes; 676 677 678 /** 679 * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support 680 * 681 * @author kangax 682 * @access private 683 * @function hasOwnProp 684 * @param {object} object - The object to check for a property 685 * @param {string} property - The property to check for 686 * @returns {boolean} 687 */ 688 689 // hasOwnProperty shim by kangax needed for Safari 2.0 support 690 var hasOwnProp; 691 692 (function() { 693 var _hasOwnProperty = ({}).hasOwnProperty; 694 /* istanbul ignore else */ 695 /* we have no way of testing IE 5.5 or safari 2, 696 * so just assume the else gets hit */ 697 if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) { 698 hasOwnProp = function(object, property) { 699 return _hasOwnProperty.call(object, property); 700 }; 701 } 702 else { 703 hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ 704 return ((property in object) && is(object.constructor.prototype[property], 'undefined')); 705 }; 706 } 707 })(); 708 709 710 711 712 // _l tracks listeners for async tests, as well as tests that execute after the initial run 713 ModernizrProto._l = {}; 714 715 /** 716 * Modernizr.on is a way to listen for the completion of async tests. Being 717 * asynchronous, they may not finish before your scripts run. As a result you 718 * will get a possibly false negative `undefined` value. 719 * 720 * @memberof Modernizr 721 * @name Modernizr.on 722 * @access public 723 * @function on 724 * @param {string} feature - String name of the feature detect 725 * @param {function} cb - Callback function returning a Boolean - true if feature is supported, false if not 726 * @example 727 * 728 * ```js 729 * Modernizr.on('flash', function( result ) { 730 * if (result) { 731 * // the browser has flash 732 * } else { 733 * // the browser does not have flash 734 * } 735 * }); 736 * ``` 737 */ 738 739 ModernizrProto.on = function(feature, cb) { 740 // Create the list of listeners if it doesn't exist 741 if (!this._l[feature]) { 742 this._l[feature] = []; 743 } 744 745 // Push this test on to the listener list 746 this._l[feature].push(cb); 747 748 // If it's already been resolved, trigger it on next tick 749 if (Modernizr.hasOwnProperty(feature)) { 750 // Next Tick 751 setTimeout(function() { 752 Modernizr._trigger(feature, Modernizr[feature]); 753 }, 0); 754 } 755 }; 756 757 /** 758 * _trigger is the private function used to signal test completion and run any 759 * callbacks registered through [Modernizr.on](#modernizr-on) 760 * 761 * @memberof Modernizr 762 * @name Modernizr._trigger 763 * @access private 764 * @function _trigger 765 * @param {string} feature - string name of the feature detect
766 * @param {function|boolean} [res] - A feature detection function, or the boolean = 767 * result of a feature detection function 768 */ 769 770 ModernizrProto._trigger = function(feature, res) { 771 if (!this._l[feature]) { 772 return; 773 } 774 775 var cbs = this._l[feature]; 776 777 // Force async 778 setTimeout(function() { 779 var i, cb; 780 for (i = 0; i < cbs.length; i++) { 781 cb = cbs[i]; 782 cb(res); 783 } 784 }, 0); 785 786 // Don't trigger these again 787 delete this._l[feature]; 788 }; 789 790 /** 791 * addTest allows you to define your own feature detects that are not currently 792 * included in Modernizr (under the covers it's the exact same code Modernizr 793 * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result 794 * will be added onto the Modernizr object, as well as an appropriate className set on 795 * the html element when configured to do so 796 * 797 * @memberof Modernizr 798 * @name Modernizr.addTest 799 * @optionName Modernizr.addTest() 800 * @optionProp addTest 801 * @access public 802 * @function addTest 803 * @param {string|object} feature - The string name of the feature detect, or an 804 * object of feature detect names and test 805 * @param {function|boolean} test - Function returning true if feature is supported, 806 * false if not. Otherwise a boolean representing the results of a feature detection 807 * @example 808 * 809 * The most common way of creating your own feature detects is by calling 810 * `Modernizr.addTest` with a string (preferably just lowercase, without any 811 * punctuation), and a function you want executed that will return a boolean result 812 * 813 * ```js 814 * Modernizr.addTest('itsTuesday', function() { 815 * var d = new Date(); 816 * return d.getDay() === 2; 817 * }); 818 * ``` 819 * 820 * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday, 821 * and to `false` every other day of the week. One thing to notice is that the names of 822 * feature detect functions are always lowercased when added to the Modernizr object. That 823 * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will. 824 * 825 * 826 * Since we only look at the returned value from any feature detection function, 827 * you do not need to actually use a function. For simple detections, just passing 828 * in a statement that will return a boolean value works just fine. 829 * 830 * ```js 831 * Modernizr.addTest('hasJquery', 'jQuery' in window); 832 * ``` 833 * 834 * Just like before, when the above runs `Modernizr.hasjquery` will be true if 835 * jQuery has been included on the page. Not using a function saves a small amount 836 * of overhead for the browser, as well as making your code much more readable. 837 * 838 * Finally, you also have the ability to pass in an object of feature names and 839 * their tests. This is handy if you want to add multiple detections in one go. 840 * The keys should always be a string, and the value can be either a boolean or 841 * function that returns a boolean. 842 * 843 * ```js 844 * var detects = { 845 * 'hasjquery': 'jQuery' in window, 846 * 'itstuesday': function() { 847 * var d = new Date(); 848 * return d.getDay() === 2; 849 * } 850 * } 851 * 852 * Modernizr.addTest(detects); 853 * ``` 854 * 855 * There is really no difference between the first methods and this one, it is 856 * just a convenience to let you write more readable code. 857 */ 858 859 function addTest(feature, test) { 860 861 if (typeof feature == 'object') { 862 for (var key in feature) { 863 if (hasOwnProp(feature, key)) { 864 addTest(key, feature[ key ]); 865 } 866 } 867 } else { 868 869 feature = feature.toLowerCase(); 870 var featureNameSplit = feature.split('.'); 871 var last = Modernizr[featureNameSplit[0]]; 872 873 // Again, we don't check for parent test existence. Get that right, though. 874 if (featureNameSplit.length == 2) { 875 last = last[featureNameSplit[1]]; 876 } 877 878 if (typeof last != 'undefined') { 879 // we're going to quit if you're trying to overwrite an existing test 880 // if we were to allow it, we'd do this: 881 // var re = new RegExp("\\b(no-)?" + feature + "\\b"); 882 // docElement.className = docElement.className.replace( re, '' ); 883 // but, no rly, stuff 'em. 884 return Modernizr; 885 } 886 887 test = typeof test == 'function' ? test() : test; 888 889 // Set the value (this is the magic, right here). 890 if (featureNameSplit.length == 1) { 891 Modernizr[featureNameSplit[0]] = test; 892 } else { 893 // cast to a Boolean, if not one already 894 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 895 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 896 } 897 898 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test; 899 } 900 901 // Set a single class (either `feature` or `no-feature`) 902 setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); 903 904 // Trigger the event 905 Modernizr._trigger(feature, test); 906 } 907 908 return Modernizr; // allow chaining. 909 } 910 911 // After all the tests are run, add self to the Modernizr prototype 912 Modernizr._q.push(function() { 913 ModernizrProto.addTest = addTest; 914 }); 915 916 917 918 919 /** 920 * cssToDOM takes a kebab-case string and converts it to camelCase 921 * e.g. box-sizing -> boxSizing 922 * 923 * @access private 924 * @function cssToDOM 925 * @param {string} name - String name of kebab-case prop we want to convert 926 * @returns {string} The camelCase version of the supplied name 927 */ 928 929 function cssToDOM(name) { 930 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 931 return m1 + m2.toUpperCase(); 932 }).replace(/^-/, ''); 933 } 934 ; 935 936 /** 937 * createElement is a convenience wrapper around document.createElement. Since we 938 * use createElement all over the place, this allows for (slightly) smaller code 939 * as well as abstracting away issues with creating elements in contexts other than 940 * HTML documents (e.g. SVG documents). 941 * 942 * @access private 943 * @function createElement 944 * @returns {HTMLElement|SVGElement} An HTML or SVG element 945 */ 946 947 function createElement() { 948 if (typeof document.createElement !== 'function') { 949 // This is the case in IE7, where the type of createElement is "object". 950 // For this reason, we cannot call apply() as Object is not a Function. 951 return document.createElement(arguments[0]); 952 } else if (isSVG) {
953 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 954 } else { 955 return document.createElement.apply(document, arguments); 956 } 957 } 958 959 ; 960 961 /** 962 * Modernizr.hasEvent() detects support for a given event 963 * 964 * @memberof Modernizr 965 * @name Modernizr.hasEvent 966 * @optionName Modernizr.hasEvent() 967 * @optionProp hasEvent 968 * @access public 969 * @function hasEvent 970 * @param {string|*} eventName - the name of an event to test for (e.g. "resize") 971 * @param {Element|string} [element=HTMLDivElement] - is the element|document|window|tagName to test on 972 * @returns {boolean} 973 * @example 974 * `Modernizr.hasEvent` lets you determine if the browser supports a supplied event. 975 * By default, it does this detection on a div element 976 * 977 * ```js 978 * hasEvent('blur') // true; 979 * ``` 980 * 981 * However, you are able to give an object as a second argument to hasEvent to 982 * detect an event on something other than a div. 983 * 984 * ```js 985 * hasEvent('devicelight', window) // true; 986 * ``` 987 * 988 */ 989 990 var hasEvent = (function() { 991 992 // Detect whether event support can be detected via `in`. Test on a DOM element 993 // using the "blur" event b/c it should always exist. bit.ly/event-detection 994 var needsFallback = !('onblur' in document.documentElement); 995 996 function inner(eventName, element) { 997 998 var isSupported; 999 if (!eventName) { return false; } 1000 if (!element || typeof element === 'string') { 1001 element = createElement(element || 'div'); 1002 } 1003 1004 // Testing via the `in` operator is sufficient for modern browsers and IE. 1005 // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and 1006 // "resize", whereas `in` "catches" those. 1007 eventName = 'on' + eventName; 1008 isSupported = eventName in element; 1009 1010 // Fallback technique for old Firefox - bit.ly/event-detection 1011 if (!isSupported && needsFallback) { 1012 if (!element.setAttribute) { 1013 // Switch to generic element if it lacks `setAttribute`. 1014 // It could be the `document`, `window`, or something else. 1015 element = createElement('div'); 1016 } 1017 1018 element.setAttribute(eventName, ''); 1019 isSupported = typeof element[eventName] === 'function'; 1020 1021 if (element[eventName] !== undefined) { 1022 // If property was created, "remove it" by setting value to `undefined`. 1023 element[eventName] = undefined; 1024 } 1025 element.removeAttribute(eventName); 1026 } 1027 1028 return isSupported; 1029 } 1030 return inner; 1031 })(); 1032 1033 1034 ModernizrProto.hasEvent = hasEvent; 1035 1036/*! 1037{ 1038 "name": "CSS Calc", 1039 "property": "csscalc", 1040 "caniuse": "calc", 1041 "tags": ["css"], 1042 "builderAliases": ["css_calc"], 1043 "authors": ["@calvein"] 1044} 1045!*/ 1046/* DOC 1047Method of allowing calculated values for length units. For example: 1048 1049```css 1050//lem { 1051 width: calc(100% - 3em); 1052} 1053``` 1054*/ 1055 1056 Modernizr.addTest('csscalc', function() { 1057 var prop = 'width:'; 1058 var value = 'calc(10px);'; 1059 var el = createElement('a'); 1060 1061 el.style.cssText = prop + prefixes.join(value + prop); 1062 1063 return !!el.style.length; 1064 }); 1065 1066/*! 1067{ 1068 "name": "CSS position: sticky", 1069 "property": "csspositionsticky", 1070 "tags": ["css"], 1071 "builderAliases": ["css_positionsticky"], 1072 "notes": [{ 1073 "name": "Chrome bug report", 1074 "href":"https://code.google.com/p/chromium/issues/detail?id=322972" 1075 }], 1076 "warnings": [ "using position:sticky on anything but top aligned elements is buggy in Chrome < 37 and iOS <=7+" ] 1077} 1078!*/ 1079 1080 // Sticky positioning - constrains an element to be positioned inside the 1081 // intersection of its container box, and the viewport. 1082 Modernizr.addTest('csspositionsticky', function() { 1083 var prop = 'position:'; 1084 var value = 'sticky'; 1085 var el = createElement('a'); 1086 var mStyle = el.style; 1087 1088 mStyle.cssText = prop + prefixes.join(value + ';' + prop).slice(0, -prop.length); 1089 1090 return mStyle.position.indexOf(value) !== -1; 1091 }); 1092 1093/*! 1094{ 1095 "name": "CSS Font rem Units", 1096 "caniuse": "rem", 1097 "authors": ["nsfmc"], 1098 "property": "cssremunit", 1099 "tags": ["css"], 1100 "builderAliases": ["css_remunit"], 1101 "notes": [{ 1102 "name": "W3C Spec", 1103 "href": "https://www.w3.org/TR/css3-values/#relative0" 1104 },{ 1105 "name": "Font Size with rem by Jonathan Snook", 1106 "href": "http://snook.ca/archives/html_and_css/font-size-with-rem" 1107 }] 1108} 1109!*/ 1110 1111 // "The 'rem' unit ('root em') is relative to the computed 1112 // value of the 'font-size' value of the root element." 1113 // you can test by checking if the prop was ditched 1114 1115 Modernizr.addTest('cssremunit', function() { 1116 var style = createElement('a').style; 1117 try { 1118 style.fontSize = '3rem'; 1119 } 1120 catch (e) {} 1121 return (/rem/).test(style.fontSize); 1122 }); 1123 1124/*! 1125{ 1126 "name": "sizes attribute", 1127 "async": true, 1128 "property": "sizes", 1129 "tags": ["image"], 1130 "authors": ["Mat Marquis"], 1131 "notes": [{ 1132 "name": "Spec", 1133 "href": "http://picture.responsiveimages.org/#parse-sizes-attr" 1134 },{ 1135 "name": "Usage Details", 1136 "href": "http://ericportis.com/posts/2014/srcset-sizes/" 1137 }] 1138} 1139!*/ 1140/* DOC 1141Test for the `sizes` attribute on images 1142*/ 1143 1144 Modernizr.addAsyncTest(function() { 1145 var width1, width2, test; 1146 var image = createElement('img'); 1147 // in a perfect world this would be the test... 1148 var isSizes = 'sizes' in image; 1149 1150 // ... but we need to deal with Safari 9... 1151 if (!isSizes && ('srcset' in image)) { 1152 width2 = 'data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw=='; 1153 width1 = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; 1154 1155 test = function() { 1156 addTest('sizes', image.width == 2); 1157 }; 1158 1159 image.onload = test; 1160 image.onerror = test; 1161 image.setAttribute('sizes', '9px'); 1162 1163 image.srcset = width1 + ' 1w,' + width2 + ' 8w'; 1164 image.src = width1; 1165 } else { 1166 addTest('sizes', isSizes); 1167 } 1168 }); 1169 1170/*! 1171{ 1172 "name": "srcset attribute", 1173 "property": "srcset", 1174 "tags": ["image"], 1175 "notes": [{ 1176 "name": "Smashing Magazine Article", 1177 "href": "https://en.wikipedia.org/wiki/APNG" 1178 },{ 1179 "name": "Generate multi-resolution images for srcset with Grunt", 1180 "href": "https://addyosmani.com/blog/generate-multi-resolution-images-for-srcset-with-grunt/" 1181 }] 1182} 1183!*/ 1184/* DOC 1185Test for the srcset attribute of images 1186*/ 1187 1188 Modernizr.addTest('srcset', 'srcset' in createElement('img')); 1189 1190/*! 1191{ 1192 "name": "[hidden] Attribute", 1193 "property": "hidden", 1194 "tags": ["dom"], 1195 "notes": [{ 1196 "name": "WHATWG: The hidden attribute", 1197 "href": "https://developers.whatwg.org/editing.html#the-hidden-attribute" 1198 }, { 1199 "name": "original implementation of detect code", 1200 "href": "https://github.com/aFarkas/html5shiv/blob/bf4fcc4/src/html5shiv.js#L38" 1201 }], 1202 "polyfills": ["html5shiv"], 1203 "authors": ["Ron Waldon (@jokeyrhyme)"] 1204} 1205!*/ 1206/* DOC 1207Does the browser support the HTML5 [hidden] attribute? 1208*/ 1209 1210 Modernizr.addTest('hidden', 'hidden' in createElement('a')); 1211 1212 1213 1214 /** 1215 * contains checks to see if a string contains another string 1216 * 1217 * @access private 1218 * @function contains 1219 * @param {string} str - The string we want to check for substrings 1220 * @param {string} substr - The substring we want to search the first string for 1221 * @returns {boolean} 1222 */ 1223 1224 function contains(str, substr) {
1225 return !!~('' + str).indexOf(substr); 1226 } 1227 1228 ; 1229/*! 1230{ 1231 "name": "CSS HSLA Colors", 1232 "caniuse": "css3-colors", 1233 "property": "hsla", 1234 "tags": ["css"] 1235} 1236!*/ 1237 1238 Modernizr.addTest('hsla', function() { 1239 var style = createElement('a').style; 1240 style.cssText = 'background-color:hsla(120,40%,100%,.5)'; 1241 return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla'); 1242 }); 1243 1244/*! 1245{ 1246 "name": "CSS Supports", 1247 "property": "supports", 1248 "caniuse": "css-featurequeries", 1249 "tags": ["css"], 1250 "builderAliases": ["css_supports"], 1251 "notes": [{ 1252 "name": "W3 Spec", 1253 "href": "http://dev.w3.org/csswg/css3-conditional/#at-supports" 1254 },{ 1255 "name": "Related Github Issue", 1256 "href": "https://github.com/Modernizr/Modernizr/issues/648" 1257 },{ 1258 "name": "W3 Info", 1259 "href": "http://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface" 1260 }] 1261} 1262!*/ 1263 1264 var newSyntax = 'CSS' in window && 'supports' in window.CSS; 1265 var oldSyntax = 'supportsCSS' in window; 1266 Modernizr.addTest('supports', newSyntax || oldSyntax); 1267 1268 1269 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); 1270 ModernizrProto._cssomPrefixes = cssomPrefixes; 1271 1272 1273 /** 1274 * atRule returns a given CSS property at-rule (eg @keyframes), possibly in 1275 * some prefixed form, or false, in the case of an unsupported rule 1276 * 1277 * @memberof Modernizr 1278 * @name Modernizr.atRule 1279 * @optionName Modernizr.atRule() 1280 * @optionProp atRule 1281 * @access public 1282 * @function atRule 1283 * @param {string} prop - String name of the @-rule to test for 1284 * @returns {string|boolean} The string representing the (possibly prefixed) 1285 * valid version of the @-rule, or `false` when it is unsupported. 1286 * @example 1287 * ```js 1288 * var keyframes = Modernizr.atRule('@keyframes'); 1289 * 1290 * if (keyframes) { 1291 * // keyframes are supported 1292 * // could be `@-webkit-keyframes` or `@keyframes` 1293 * } else { 1294 * // keyframes === `false` 1295 * } 1296 * ``` 1297 * 1298 */ 1299 1300 var atRule = function(prop) { 1301 var length = prefixes.length; 1302 var cssrule = window.CSSRule; 1303 var rule; 1304 1305 if (typeof cssrule === 'undefined') { 1306 return undefined; 1307 } 1308 1309 if (!prop) { 1310 return false; 1311 } 1312 1313 // remove literal @ from beginning of provided property 1314 prop = prop.replace(/^@/, ''); 1315 1316 // CSSRules use underscores instead of dashes 1317 rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE'; 1318 1319 if (rule in cssrule) { 1320 return '@' + prop; 1321 } 1322 1323 for (var i = 0; i < length; i++) { 1324 // prefixes gives us something like -o-, and we want O_ 1325 var prefix = prefixes[i]; 1326 var thisRule = prefix.toUpperCase() + '_' + rule; 1327 1328 if (thisRule in cssrule) { 1329 return '@-' + prefix.toLowerCase() + '-' + prop; 1330 } 1331 } 1332 1333 return false; 1334 }; 1335 1336 ModernizrProto.atRule = atRule; 1337 1338 1339 1340 /** 1341 * getBody returns the body of a document, or an element that can stand in for 1342 * the body if a real body does not exist 1343 * 1344 * @access private 1345 * @function getBody 1346 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 1347 * artificially created element that stands in for the body 1348 */ 1349 1350 function getBody() { 1351 // After page load injecting a fake body doesn't work so check if body exists 1352 var body = document.body; 1353 1354 if (!body) { 1355 // Can't use the real body create a fake one. 1356 body = createElement(isSVG ? 'svg' : 'body'); 1357 body.fake = true; 1358 } 1359 1360 return body; 1361 } 1362 1363 ; 1364 1365 /** 1366 * injectElementWithStyles injects an element with style element and some CSS rules 1367 * 1368 * @access private 1369 * @function injectElementWithStyles 1370 * @param {string} rule - String representing a css rule 1371 * @param {function} callback - A function that is used to test the injected element 1372 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 1373 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 1374 * @returns {boolean} 1375 */ 1376 1377 function injectElementWithStyles(rule, callback, nodes, testnames) { 1378 var mod = 'modernizr'; 1379 var style; 1380 var ret; 1381 var node; 1382 var docOverflow; 1383 var div = createElement('div'); 1384 var body = getBody(); 1385 1386 if (parseInt(nodes, 10)) { 1387 // In order not to give false positives we create a node for each test
1388 // This also allows the method to scale for unspecified uses 1389 while (nodes--) { 1390 node = createElement('div'); 1391 node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 1392 div.appendChild(node); 1393 } 1394 } 1395 1396 style = createElement('style'); 1397 style.type = 'text/css'; 1398 style.id = 's' + mod; 1399 1400 // 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. 1401 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 1402 (!body.fake ? div : body).appendChild(style); 1403 body.appendChild(div); 1404 1405 if (style.styleSheet) { 1406 style.styleSheet.cssText = rule; 1407 } else { 1408 style.appendChild(document.createTextNode(rule)); 1409 } 1410 div.id = mod; 1411 1412 if (body.fake) { 1413 //avoid crashing IE8, if background image is used 1414 body.style.background = ''; 1415 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 1416 body.style.overflow = 'hidden'; 1417 docOverflow = docElement.style.overflow; 1418 docElement.style.overflow = 'hidden'; 1419 docElement.appendChild(body); 1420 } 1421 1422 ret = callback(div, rule); 1423 // If this is done after page load we don't want to remove the body so check if body exists 1424 if (body.fake) { 1425 body.parentNode.removeChild(body); 1426 docElement.style.overflow = docOverflow; 1427 // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 1428 // eslint-disable-next-line 1429 docElement.offsetHeight; 1430 } else { 1431 div.parentNode.removeChild(div); 1432 } 1433 1434 return !!ret; 1435 1436 } 1437 1438 ; 1439 1440 /** 1441 * Modernizr.mq tests a given media query, live against the current state of the window 1442 * adapted from matchMedia polyfill by Scott Jehl and Paul Irish 1443 * gist.github.com/786768 1444 * 1445 * @memberof Modernizr 1446 * @name Modernizr.mq 1447 * @optionName Modernizr.mq() 1448 * @optionProp mq 1449 * @access public 1450 * @function mq 1451 * @param {string} mq - String of the media query we want to test 1452 * @returns {boolean} 1453 * @example 1454 * Modernizr.mq allows for you to programmatically check if the current browser 1455 * window state matches a media query. 1456 * 1457 * ```js 1458 * var query = Modernizr.mq('(min-width: 900px)'); 1459 * 1460 * if (query) { 1461 * // the browser window is larger than 900px 1462 * } 1463 * ``` 1464 * 1465 * Only valid media queries are supported, therefore you must always include values 1466 * with your media query 1467 * 1468 * ```js 1469 * // good 1470 * Modernizr.mq('(min-width: 900px)'); 1471 * 1472 * // bad 1473 * Modernizr.mq('min-width'); 1474 * ``` 1475 * 1476 * If you would just like to test that media queries are supported in general, use 1477 * 1478 * ```js 1479 * Modernizr.mq('only all'); // true if MQ are supported, false if not 1480 * ``` 1481 * 1482 * 1483 * Note that if the browser does not support media queries (e.g. old IE) mq will 1484 * always return false. 1485 */ 1486 1487 var mq = (function() { 1488 var matchMedia = window.matchMedia || window.msMatchMedia; 1489 if (matchMedia) { 1490 return function(mq) { 1491 var mql = matchMedia(mq); 1492 return mql && mql.matches || false; 1493 }; 1494 } 1495 1496 return function(mq) { 1497 var bool = false;
1498 1499 injectElementWithStyles('@media ' + mq + ' { #modernizr { position: absolute; } }', function(node) { 1500 bool = (window.getComputedStyle ? 1501 window.getComputedStyle(node, null) : 1502 node.currentStyle).position == 'absolute'; 1503 }); 1504 1505 return bool; 1506 }; 1507 })(); 1508 1509 1510 ModernizrProto.mq = mq; 1511 1512 1513/*! 1514{ 1515 "name": "CSS Media Queries", 1516 "caniuse": "css-mediaqueries", 1517 "property": "mediaqueries", 1518 "tags": ["css"], 1519 "builderAliases": ["css_mediaqueries"] 1520} 1521!*/ 1522 1523 Modernizr.addTest('mediaqueries', mq('only all')); 1524 1525 1526 /** 1527 * testStyles injects an element with style element and some CSS rules 1528 * 1529 * @memberof Modernizr 1530 * @name Modernizr.testStyles 1531 * @optionName Modernizr.testStyles() 1532 * @optionProp testStyles 1533 * @access public 1534 * @function testStyles 1535 * @param {string} rule - String representing a css rule 1536 * @param {function} callback - A function that is used to test the injected element 1537 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 1538 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 1539 * @returns {boolean} 1540 * @example 1541 * 1542 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page 1543 * along with (possibly multiple) DOM elements. This lets you check for features 1544 * 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). 1545 * 1546 * ```js 1547 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { 1548 * // elem is the first DOM node in the page (by default #modernizr) 1549 * // rule is the first argument you supplied - the CSS rule in string form 1550 * 1551 * addTest('widthworks', elem.style.width === '9px') 1552 * }); 1553 * ``` 1554 * 1555 * If your test requires multiple nodes, you can include a third argument 1556 * indicating how many additional div elements to include on the page. The 1557 * additional nodes are injected as children of the `elem` that is returned as 1558 * the first argument to the callback. 1559 * 1560 * ```js 1561 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { 1562 * document.getElementById('modernizr').style.width === '1px'; // true 1563 * document.getElementById('modernizr2').style.width === '2px'; // true 1564 * elem.firstChild === document.getElementById('modernizr2'); // true 1565 * }, 1); 1566 * ``` 1567 * 1568 * By default, all of the additional elements have an ID of `modernizr[n]`, where 1569 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, 1570 * the second additional is `#modernizr3`, etc.). 1571 * If you want to have more meaningful IDs for your function, you can provide 1572 * them as the fourth argument, as an array of strings 1573 * 1574 * ```js 1575 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { 1576 * elem.firstChild === document.getElementById('foo'); // true 1577 * elem.lastChild === document.getElementById('bar'); // true 1578 * }, 2, ['foo', 'bar']); 1579 * ``` 1580 * 1581 */ 1582 1583 var testStyles = ModernizrProto.testStyles = injectElementWithStyles; 1584 1585/*! 1586{ 1587 "name": "Touch Events", 1588 "property": "touchevents", 1589 "caniuse" : "touch", 1590 "tags": ["media", "attribute"], 1591 "notes": [{ 1592 "name": "Touch Events spec", 1593 "href": "https://www.w3.org/TR/2013/WD-touch-events-20130124/" 1594 }], 1595 "warnings": [ 1596 "Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device" 1597 ], 1598 "knownBugs": [ 1599 "False-positive on some configurations of Nokia N900", 1600 "False-positive on some BlackBerry 6.0 builds – https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695" 1601 ] 1602} 1603!*/ 1604/* DOC 1605Indicates if the browser supports the W3C Touch Events API. 1606 1607This *does not* necessarily reflect a touchscreen device: 1608 1609* Older touchscreen devices only emulate mouse events 1610* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that 1611* Some browsers & OS setups may enable touch APIs when no touchscreen is connected 1612* Future browsers may implement other event models for touch interactions 1613 1614See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/). 1615 1616It's recommended to bind both mouse and touch/pointer events simultaneously – see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/). 1617 1618This test will also return `true` for Firefox 4 Multitouch support. 1619*/ 1620 1621 // Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415 1622 Modernizr.addTest('touchevents', function() { 1623 var bool; 1624 if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { 1625 bool = true; 1626 } else { 1627 // include the 'heartz' as a way to have a non matching MQ to help terminate the join 1628 // https://git.io/vznFH 1629 var query = ['@media (', prefixes.join('touch-enabled),('), 'heartz', ')', '{#modernizr{top:9px;position:absolute}}'].join(''); 1630 testStyles(query, function(node) { 1631 bool = node.offsetTop === 9; 1632 }); 1633 } 1634 return bool; 1635 }); 1636 1637/*! 1638{ 1639 "name": "@font-face", 1640 "property": "fontface", 1641 "authors": ["Diego Perini", "Mat Marquis"], 1642 "tags": ["css"], 1643 "knownBugs": [ 1644 "False Positive: WebOS https://github.com/Modernizr/Modernizr/issues/342", 1645 "False Postive: WP7 https://github.com/Modernizr/Modernizr/issues/538" 1646 ], 1647 "notes": [{ 1648 "name": "@font-face detection routine by Diego Perini", 1649 "href": "http://javascript.nwbox.com/CSSSupport/" 1650 },{ 1651 "name": "Filament Group @font-face compatibility research", 1652 "href": "https://docs.google.com/presentation/d/1n4NyG4uPRjAA8zn_pSQ_
1652Ket0RhcWC6QlZ6LMjKeECo0/edit#slide=id.p" 1653 },{ 1654 "name": "Filament Grunticon/@font-face device testing results", 1655 "href": "https://docs.google.com/spreadsheet/ccc?key=0Ag5_yGvxpINRdHFYeUJPNnZMWUZKR2ItMEpRTXZPdUE#gid=0" 1656 },{ 1657 "name": "CSS fonts on Android", 1658 "href": "https://stackoverflow.com/questions/3200069/css-fonts-on-android" 1659 },{ 1660 "name": "@font-face and Android", 1661 "href": "http://archivist.incutio.com/viewlist/css-discuss/115960" 1662 }] 1663} 1664!*/ 1665 1666 var blacklist = (function() { 1667 var ua = navigator.userAgent; 1668 var webos = ua.match(/w(eb)?osbrowser/gi); 1669 var wppre8 = ua.match(/windows phone/gi) && ua.match(/iemobile\/([0-9])+/gi) && parseFloat(RegExp.$1) >= 9; 1670 return webos || wppre8; 1671 }()); 1672 if (blacklist) { 1673 Modernizr.addTest('fontface', false); 1674 } else { 1675 testStyles('@font-face {font-family:"font";src:url("https://")}', function(node, rule) { 1676 var style = document.getElementById('smodernizr'); 1677 var sheet = style.sheet || style.styleSheet; 1678 var cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : ''; 1679 var bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0; 1680 Modernizr.addTest('fontface', bool); 1681 }); 1682 } 1683; 1684 1685 /** 1686 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. 1687 * 1688 * @access private 1689 * @function fnBind 1690 * @param {function} fn - a function you want to change `this` reference to 1691 * @param {object} that - the `this` you want to call the function with 1692 * @returns {function} The wrapped version of the supplied function 1693 */ 1694 1695 function fnBind(fn, that) { 1696 return function() { 1697 return fn.apply(that, arguments); 1698 }; 1699 } 1700 1701 ; 1702 1703 /** 1704 * testDOMProps is a generic DOM property test; if a browser supports 1705 * a certain property, it won't return undefined for it. 1706 * 1707 * @access private 1708 * @function testDOMProps 1709 * @param {array.<string>} props - An array of properties to test for 1710 * @param {object} obj - An object or Element you want to use to test the parameters again 1711 * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check 1712 * @returns {false|*} returns false if the prop is unsupported, otherwise the value that is supported 1713 */ 1714 function testDOMProps(props, obj, elem) { 1715 var item; 1716 1717 for (var i in props) { 1718 if (props[i] in obj) { 1719 1720 // return the property name as a string 1721 if (elem === false) { 1722 return props[i]; 1723 } 1724 1725 item = obj[props[i]]; 1726 1727 // let's bind a function 1728 if (is(item, 'function')) { 1729 // bind to obj unless overriden 1730 return fnBind(item, elem || obj); 1731 } 1732 1733 // return the unbound function or obj or value 1734 return item; 1735 } 1736 } 1737 return false; 1738 } 1739 1740 ; 1741 1742 /** 1743 * Create our "modernizr" element that we do most feature tests on. 1744 * 1745 * @access private 1746 */ 1747 1748 var modElem = { 1749 elem: createElement('modernizr') 1750 }; 1751 1752 // Clean up this element 1753 Modernizr._q.push(function() { 1754 delete modElem.elem; 1755 }); 1756 1757 1758 1759 var mStyle = { 1760 style: modElem.elem.style 1761 }; 1762 1763 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 1764 // the front of the queue. 1765 Modernizr._q.unshift(function() { 1766 delete mStyle.style; 1767 }); 1768 1769 1770 1771 /** 1772 * domToCSS takes a camelCase string and converts it to kebab-case 1773 * e.g. boxSizing -> box-sizing 1774 * 1775 * @access private 1776 * @function domToCSS 1777 * @param {string} name - String name of camelCase prop we want to convert 1778 * @returns {string} The kebab-case version of the supplied name 1779 */ 1780 1781 function domToCSS(name) { 1782 return name.replace(/([A-Z])/g, function(str, m1) { 1783 return '-' + m1.toLowerCase(); 1784 }).replace(/^ms-/, '-ms-'); 1785 } 1786 ; 1787 1788 1789 /** 1790 * wrapper around getComputedStyle, to fix issues with Firefox returning null when 1791 * called inside of a hidden iframe 1792 * 1793 * @access private 1794 * @function computedStyle 1795 * @param {HTMLElement|SVGElement} - The element we want to find the computed styles of 1796 * @param {string|null} [pseudoSelector]- An optional pseudo element selector (e.g. :before), of null if none 1797 * @returns {CSSStyleDeclaration} 1798 */ 1799 1800 function computedStyle(elem, pseudo, prop) { 1801 var result; 1802 1803 if ('getComputedStyle' in window) { 1804 result = getComputedStyle.call(window, elem, pseudo); 1805 var console = window.console; 1806 1807 if (result !== null) { 1808 if (prop) { 1809 result = result.getPropertyValue(prop); 1810 } 1811 } else { 1812 if (console) { 1813 var method = console.error ? 'error' : 'log'; 1814 console[method].call(console, 'getComputedStyle returning null, its possible modernizr test results are inaccurate'); 1815 } 1816 } 1817 } else { 1818 result = !pseudo && elem.currentStyle && elem.currentStyle[prop]; 1819 } 1820 1821 return result; 1822 } 1823 1824 ; 1825 1826 /** 1827 * nativeTestProps allows for us to use native feature detection functionality if available. 1828 * some prefixed form, or false, in the case of an unsupported rule 1829 * 1830 * @access private 1831 * @function nativeTestProps 1832 * @param {array} props - An array of property names 1833 * @param {string} value - A string representing the value we want to check via @supports 1834 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 1835 */ 1836 1837 // Accepts a list of property names and a single value 1838 // Returns `undefined` if native detection not available 1839 function nativeTestProps(props, value) { 1840 var i = props.length; 1841 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface 1842 if ('CSS' in window && 'supports' in window.CSS) { 1843 // Try every prefixed variant of the property 1844 while (i--) { 1845 if (window.CSS.supports(domToCSS(props[i]), value)) { 1846 return true; 1847 } 1848 } 1849 return false;
1850 } 1851 // Otherwise fall back to at-rule (for Opera 12.x) 1852 else if ('CSSSupportsRule' in window) { 1853 // Build a condition string for every prefixed variant 1854 var conditionText = []; 1855 while (i--) { 1856 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 1857 } 1858 conditionText = conditionText.join(' or '); 1859 return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { 1860 return computedStyle(node, null, 'position') == 'absolute'; 1861 }); 1862 } 1863 return undefined; 1864 } 1865 ; 1866 1867 // testProps is a generic CSS / DOM property test. 1868 1869 // In testing support for a given CSS property, it's legit to test: 1870 // `elem.style[styleName] !== undefined` 1871 // If the property is supported it will return an empty string, 1872 // if unsupported it will return undefined. 1873 1874 // We'll take advantage of this quick test and skip setting a style 1875 // on our modernizr element, but instead just testing undefined vs 1876 // empty string. 1877 1878 // Property names can be provided in either camelCase or kebab-case. 1879 1880 function testProps(props, prefixed, value, skipValueTest) { 1881 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 1882 1883 // Try native detect first 1884 if (!is(value, 'undefined')) { 1885 var result = nativeTestProps(props, value); 1886 if (!is(result, 'undefined')) { 1887 return result; 1888 } 1889 } 1890 1891 // Otherwise do it properly 1892 var afterInit, i, propsLength, prop, before; 1893 1894 // If we don't have a style element, that means we're running async or after 1895 // the core tests, so we'll need to create our own elements to use 1896 1897 // inside of an SVG element, in certain browsers, the `style` element is only 1898 // defined for valid tags. Therefore, if `modernizr` does not have one, we 1899 // fall back to a less used element and hope for the best. 1900 // for strict XHTML browsers the hardly used samp element is used 1901 var elems = ['modernizr', 'tspan', 'samp']; 1902 while (!mStyle.style && elems.length) { 1903 afterInit = true; 1904 mStyle.modElem = createElement(elems.shift()); 1905 mStyle.style = mStyle.modElem.style; 1906 } 1907 1908 // Delete the objects if we created them. 1909 function cleanElems() { 1910 if (afterInit) { 1911 delete mStyle.style; 1912 delete mStyle.modElem; 1913 } 1914 } 1915 1916 propsLength = props.length; 1917 for (i = 0; i < propsLength; i++) { 1918 prop = props[i]; 1919 before = mStyle.style[prop]; 1920 1921 if (contains(prop, '-')) { 1922 prop = cssToDOM(prop); 1923 } 1924 1925 if (mStyle.style[prop] !== undefined) { 1926 1927 // If value to test has been passed in, do a set-and-check test. 1928 // 0 (integer) is a valid property value, so check that `value` isn't 1929 // undefined, rather than just checking it's truthy. 1930 if (!skipValueTest && !is(value, 'undefined')) { 1931 1932 // Needs a try catch block because of old IE. This is slow, but will 1933 // be avoided in most cases because `skipValueTest` will be used. 1934 try { 1935 mStyle.style[prop] = value; 1936 } catch (e) {} 1937 1938 // If the property value has changed, we assume the value used is 1939 // supported. If `value` is empty string, it'll fail here (because 1940 // it hasn't changed), which matches how browsers have implemented 1941 // CSS.supports() 1942 if (mStyle.style[prop] != before) { 1943 cleanElems(); 1944 return prefixed == 'pfx' ? prop : true; 1945 } 1946 } 1947 // Otherwise just return true, or the property name if this is a 1948 // `prefixed()` call 1949 else { 1950 cleanElems(); 1951 return prefixed == 'pfx' ? prop : true; 1952 } 1953 } 1954 } 1955 cleanElems(); 1956 return false; 1957 } 1958 1959 ; 1960 1961 /** 1962 * testProp() investigates whether a given style property is recognized 1963 * Property names can be provided in either camelCase or kebab-case. 1964 * 1965 * @memberof Modernizr 1966 * @name Modernizr.testProp 1967 * @access public 1968 * @optionName Modernizr.testProp() 1969 * @optionProp testProp 1970 * @function testProp 1971 * @param {string} prop - Name of the CSS property to check 1972 * @param {string} [value] - Name of the CSS value to check 1973 * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported 1974 * @returns {boolean} 1975 * @example 1976 * 1977 * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed 1978 * version of the string. 1979 * 1980 * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) 1981 * 1982 * ```js 1983 * Modernizr.testProp('pointerEvents') // true 1984 * ``` 1985 * 1986 * You can also provide a value as an optional second argument to check if a 1987 * specific value is supported 1988 * 1989 * ```js
1990 * Modernizr.testProp('pointerEvents', 'none') // true 1991 * Modernizr.testProp('pointerEvents', 'penguin') // false 1992 * ``` 1993 */ 1994 1995 var testProp = ModernizrProto.testProp = function(prop, value, useValue) { 1996 return testProps([prop], undefined, value, useValue); 1997 }; 1998 1999 2000 /** 2001 * testPropsAll tests a list of DOM properties we want to check against. 2002 * We specify literally ALL possible (known and/or likely) properties on 2003 * the element including the non-vendor prefixed one, for forward- 2004 * compatibility. 2005 * 2006 * @access private 2007 * @function testPropsAll 2008 * @param {string} prop - A string of the property to test for 2009 * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip 2010 * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against 2011 * @param {string} [value] - A string of a css value 2012 * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set 2013 * @returns {false|string} returns the string version of the property, or false if it is unsupported 2014 */ 2015 function testPropsAll(prop, prefixed, elem, value, skipValueTest) { 2016 2017 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 2018 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 2019 2020 // did they call .prefixed('boxSizing') or are we just testing a prop? 2021 if (is(prefixed, 'string') || is(prefixed, 'undefined')) { 2022 return testProps(props, prefixed, value, skipValueTest); 2023 2024 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) 2025 } else { 2026 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 2027 return testDOMProps(props, prefixed, elem); 2028 } 2029 } 2030 2031 // Modernizr.testAllProps() investigates whether a given style property, 2032 // or any of its vendor-prefixed variants, is recognized 2033 // 2034 // Note that the property names must be provided in the camelCase variant. 2035 // Modernizr.testAllProps('boxSizing') 2036 ModernizrProto.testAllProps = testPropsAll; 2037 2038 2039 2040 /** 2041 * prefixed returns the prefixed or nonprefixed property name variant of your input 2042 * 2043 * @memberof Modernizr 2044 * @name Modernizr.prefixed 2045 * @optionName Modernizr.prefixed() 2046 * @optionProp prefixed 2047 * @access public 2048 * @function prefixed 2049 * @param {string} prop - String name of the property to test for 2050 * @param {object} [obj] - An object to test for the prefixed properties on 2051 * @param {HTMLElement} [elem] - An element used to test specific properties against 2052 * @returns {string|false} The string representing the (possibly prefixed) valid 2053 * version of the property, or `false` when it is unsupported. 2054 * @example 2055 * 2056 * Modernizr.prefixed takes a string css value in the DOM style camelCase (as 2057 * opposed to the css style kebab-case) form and returns the (possibly prefixed) 2058 * version of that property that the browser actually supports. 2059 * 2060 * For example, in older Firefox... 2061 * ```js 2062 * prefixed('boxSizing') 2063 * ``` 2064 * returns 'MozBoxSizing' 2065 * 2066 * In newer Firefox, as well as any other browser that support the unprefixed 2067 * version would simply return `boxSizing`. Any browser that does not support 2068 * the property at all, it will return `false`. 2069 * 2070 * By default, prefixed is checked against a DOM element. If you want to check 2071 * for a property on another object, just pass it as a second argument 2072 * 2073 * ```js 2074 * var rAF = prefixed('requestAnimationFrame', window); 2075 * 2076 * raf(function() { 2077 * renderFunction(); 2078 * }) 2079 * ``` 2080 * 2081 * Note that this will return _the actual function_ - not the name of the function. 2082 * If you need the actual name of the property, pass in `false` as a third argument 2083 * 2084 * ```js 2085 * var rAFProp = prefixed('requestAnimationFrame', window, false); 2086 * 2087 * rafProp === 'WebkitRequestAnimationFrame' // in older webkit 2088 * ``` 2089 * 2090 * One common use case for prefixed is if you're trying to determine which transition 2091 * end event to bind to, you might do something like... 2092 * ```js 2093 * var transEndEventNames = { 2094 * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser 2095 * 'MozTransition' : 'transitionend', * only for FF < 15 2096 * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ 2097 * }; 2098 * 2099 * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; 2100 * ``` 2101 * 2102 * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). 2103 */ 2104 2105 var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) { 2106 if (prop.indexOf('@') === 0) { 2107 return atRule(prop); 2108 } 2109 2110 if (prop.indexOf('-') != -1) { 2111 // Convert kebab-case to camelCase 2112 prop = cssToDOM(prop); 2113 } 2114 if (!obj) { 2115 return testPropsAll(prop, 'pfx'); 2116 } else { 2117 // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' 2118 return testPropsAll(prop, obj, elem); 2119 } 2120 }; 2121 2122
2123/*! 2124{ 2125 "name": "CSS Object Fit", 2126 "caniuse": "object-fit", 2127 "property": "objectfit", 2128 "tags": ["css"], 2129 "builderAliases": ["css_objectfit"], 2130 "notes": [{ 2131 "name": "Opera Article on Object Fit", 2132 "href": "https://dev.opera.com/articles/css3-object-fit-object-position/" 2133 }] 2134} 2135!*/ 2136 2137 Modernizr.addTest('objectfit', !!prefixed('objectFit'), {aliases: ['object-fit']}); 2138 2139 2140 /** 2141 * testAllProps determines whether a given CSS property is supported in the browser 2142 * 2143 * @memberof Modernizr 2144 * @name Modernizr.testAllProps 2145 * @optionName Modernizr.testAllProps() 2146 * @optionProp testAllProps 2147 * @access public 2148 * @function testAllProps 2149 * @param {string} prop - String naming the property to test (either camelCase or kebab-case) 2150 * @param {string} [value] - String of the value to test 2151 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection 2152 * @example 2153 * 2154 * testAllProps determines whether a given CSS property, in some prefixed form, 2155 * is supported by the browser. 2156 * 2157 * ```js 2158 * testAllProps('boxSizing') // true 2159 * ``` 2160 * 2161 * It can optionally be given a CSS value in string form to test if a property 2162 * value is valid 2163 * 2164 * ```js 2165 * testAllProps('display', 'block') // true 2166 * testAllProps('display', 'penguin') // false 2167 * ``` 2168 * 2169 * A boolean can be passed as a third parameter to skip the value check when 2170 * native detection (@supports) isn't available. 2171 * 2172 * ```js 2173 * testAllProps('shapeOutside', 'content-box', true); 2174 * ``` 2175 */ 2176 2177 function testAllProps(prop, value, skipValueTest) { 2178 return testPropsAll(prop, undefined, undefined, value, skipValueTest); 2179 } 2180 ModernizrProto.testAllProps = testAllProps; 2181 2182/*! 2183{ 2184 "name": "CSS Animations", 2185 "property": "cssanimations", 2186 "caniuse": "css-animation", 2187 "polyfills": ["transformie", "csssandpaper"], 2188 "tags": ["css"], 2189 "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], 2190 "notes": [{ 2191 "name" : "Article: 'Dispelling the Android CSS animation myths'", 2192 "href": "https://goo.gl/OGw5Gm" 2193 }] 2194} 2195!*/ 2196/* DOC 2197Detects whether or not elements can be animated using CSS 2198*/ 2199 2200 Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); 2201 2202/*! 2203{ 2204 "name": "Flexbox", 2205 "property": "flexbox", 2206 "caniuse": "flexbox", 2207 "tags": ["css"], 2208 "notes": [{ 2209 "name": "The _new_ flexbox", 2210 "href": "http://dev.w3.org/csswg/css3-flexbox" 2211 }], 2212 "warnings": [ 2213 "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect." 2214 ] 2215} 2216!*/ 2217/* DOC 2218Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container. 2219*/ 2220 2221 Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', true)); 2222 2223/*! 2224{ 2225 "name": "CSS Transforms", 2226 "property": "csstransforms", 2227 "caniuse": "transforms2d", 2228 "tags": ["css"] 2229} 2230!*/ 2231 2232 Modernizr.addTest('csstransforms', function() { 2233 // Android < 3.0 is buggy, so we sniff and blacklist 2234 // http://git.io/hHzL7w 2235 return navigator.userAgent.indexOf('Android 2.') === -1 && 2236 testAllProps('transform', 'scale(1)', true); 2237 }); 2238 2239/*! 2240{ 2241 "name": "CSS Transforms 3D", 2242 "property": "csstransforms3d", 2243 "caniuse": "transforms3d", 2244 "tags": ["css"], 2245 "warnings": [ 2246 "Chrome may occassionally fail this test on some systems; more info: https://code.google.com/p/chromium/issues/detail?id=129004" 2247 ] 2248} 2249!*/ 2250 2251 Modernizr.addTest('csstransforms3d', function() { 2252 return !!testAllProps('perspective', '1px', true); 2253 }); 2254 2255/*! 2256{ 2257 "name": "CSS Transitions", 2258 "property": "csstransitions", 2259 "caniuse": "css-transitions", 2260 "tags": ["css"] 2261} 2262!*/ 2263 2264 Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); 2265 2266 2267 // Run each test 2268 testRunner(); 2269 2270 // Remove the "no-js" class if it exists 2271 setClasses(classes); 2272 2273 delete ModernizrProto.addTest; 2274 delete ModernizrProto.addAsyncTest; 2275 2276 // Run the things that are supposed to run after the tests 2277 for (var i = 0; i < Modernizr._q.length; i++) { 2278 Modernizr._q[i](); 2279 } 2280 2281 // Leak Modernizr namespace 2282 window.Modernizr = Modernizr; 2283 2284 2285; 2286 2287})(window, document);
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.