bhq@iemsoft.cn
2018-11-07 e6640b4e2661c209cb2b50adee813c8e72893b4d
commit | author | age
2f5b9b 1 /*!
B 2  * modernizr v3.3.1
3  * Build http://modernizr.com/download?-bgpositionshorthand-bgpositionxy-bgrepeatspace_bgrepeatround-bgsizecover-borderradius-cssanimations-csscalc-csstransforms-csstransforms3d-csstransitions-flexboxtweener-fontface-inlinesvg-localstorage-multiplebgs-preserve3d-sessionstorage-smil-svg-svgasimg-svgclippaths-svgfilters-svgforeignobject-websqldatabase-setclasses-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 tests = [];
27   
28
29   /**
30    *
31    * ModernizrProto is the constructor for Modernizr
32    *
33    * @class
34    * @access public
35    */
36
37   var ModernizrProto = {
38     // The current version, dummy
39     _version: '3.3.1',
40
41     // Any settings that don't work as separate modules
42     // can go in here as configuration.
43     _config: {
44       'classPrefix': '',
45       'enableClasses': true,
46       'enableJSClass': true,
47       'usePrefixes': true
48     },
49
50     // Queue of tests
51     _q: [],
52
53     // Stub these for people who are listening
54     on: function(test, cb) {
55       // I don't really think people should do this, but we can
56       // safe guard it a bit.
57       // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests.
58       // This is in case people listen to synchronous tests. I would leave it out,
59       // but the code to *disallow* sync tests in the real version of this
60       // function is actually larger than this.
61       var self = this;
62       setTimeout(function() {
63         cb(self[test]);
64       }, 0);
65     },
66
67     addTest: function(name, fn, options) {
68       tests.push({name: name, fn: fn, options: options});
69     },
70
71     addAsyncTest: function(fn) {
72       tests.push({name: null, fn: fn});
73     }
74   };
75
76   
77
78   // Fake some of Object.create so we can force non test results to be non "own" properties.
79   var Modernizr = function() {};
80   Modernizr.prototype = ModernizrProto;
81
82   // Leak modernizr globally when you `require` it rather than force it here.
83   // Overwrite name so constructor name is nicer :D
84   Modernizr = new Modernizr();
85
86   
87
88   var classes = [];
89   
90
91   /**
92    * is returns a boolean if the typeof an obj is exactly type.
93    *
94    * @access private
95    * @function is
96    * @param {*} obj - A thing we want to check the type of
97    * @param {string} type - A string to compare the typeof against
98    * @returns {boolean}
99    */
100
101   function is(obj, type) {
102     return typeof obj === type;
103   }
104   ;
105
106   /**
107    * Run through all tests and detect their support in the current UA.
108    *
109    * @access private
110    */
111
112   function testRunner() {
113     var featureNames;
114     var feature;
115     var aliasIdx;
116     var result;
117     var nameIdx;
118     var featureName;
119     var featureNameSplit;
120
121     for (var featureIdx in tests) {
122       if (tests.hasOwnProperty(featureIdx)) {
123         featureNames = [];
124         feature = tests[featureIdx];
125         // run the test, throw the return value into the Modernizr,
126         // then based on that boolean, define an appropriate className
127         // and push it into an array of classes we'll join later.
128         //
129         // If there is no name, it's an 'async' test that is run,
130         // but not directly added to the object. That should
131         // be done with a post-run addTest call.
132         if (feature.name) {
133           featureNames.push(feature.name.toLowerCase());
134
135           if (feature.options && feature.options.aliases && feature.options.aliases.length) {
136             // Add all the aliases into the names list
137             for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) {
138               featureNames.push(feature.options.aliases[aliasIdx].toLowerCase());
139             }
140           }
141         }
142
143         // Run the test, or use the raw value if it's not a function
144         result = is(feature.fn, 'function') ? feature.fn() : feature.fn;
145
146
147         // Set each of the names on the Modernizr object
148         for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) {
149           featureName = featureNames[nameIdx];
150           // Support dot properties as sub tests. We don't do checking to make sure
151           // that the implied parent tests have been added. You must call them in
152           // order (either in the test, or make the parent test a dependency).
153           //
154           // Cap it to TWO to make the logic simple and because who needs that kind of subtesting
155           // hashtag famous last words
156           featureNameSplit = featureName.split('.');
157
158           if (featureNameSplit.length === 1) {
159             Modernizr[featureNameSplit[0]] = result;
160           } else {
161             // cast to a Boolean, if not one already
162             /* jshint -W053 */
163             if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
164               Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
165             }
166
167             Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result;
168           }
169
170           classes.push((result ? '' : 'no-') + featureNameSplit.join('-'));
171         }
172       }
173     }
174   }
175   ;
176
177   /**
178    * docElement is a convenience wrapper to grab the root element of the document
179    *
180    * @access private
181    * @returns {HTMLElement|SVGElement} The root element of the document
182    */
183
184   var docElement = document.documentElement;
185   
186
187   /**
188    * A convenience helper to check if the document we are running in is an SVG document
189    *
190    * @access private
191    * @returns {boolean}
192    */
193
194   var isSVG = docElement.nodeName.toLowerCase() === 'svg';
195   
196
197   /**
198    * setClasses takes an array of class names and adds them to the root element
199    *
200    * @access private
201    * @function setClasses
202    * @param {string[]} classes - Array of class names
203    */
204
205   // Pass in an and array of class names, e.g.:
206   //  ['no-webp', 'borderradius', ...]
207   function setClasses(classes) {
208     var className = docElement.className;
209     var classPrefix = Modernizr._config.classPrefix || '';
210
211     if (isSVG) {
212       className = className.baseVal;
213     }
214
215     // Change `no-js` to `js` (independently of the `enableClasses` option)
216     // Handle classPrefix on this too
217     if (Modernizr._config.enableJSClass) {
218       var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)');
219       className = className.replace(reJS, '$1' + classPrefix + 'js$2');
220     }
221
222     if (Modernizr._config.enableClasses) {
223       // Add the new classes
224       className += ' ' + classPrefix + classes.join(' ' + classPrefix);
225       isSVG ? docElement.className.baseVal = className : docElement.className = className;
226     }
227
228   }
229
230   ;
231
232   /**
233    * createElement is a convenience wrapper around document.createElement. Since we
234    * use createElement all over the place, this allows for (slightly) smaller code
235    * as well as abstracting away issues with creating elements in contexts other than
236    * HTML documents (e.g. SVG documents).
237    *
238    * @access private
239    * @function createElement
240    * @returns {HTMLElement|SVGElement} An HTML or SVG element
241    */
242
243   function createElement() {
244     if (typeof document.createElement !== 'function') {
245       // This is the case in IE7, where the type of createElement is "object".
246       // For this reason, we cannot call apply() as Object is not a Function.
247       return document.createElement(arguments[0]);
248     } else if (isSVG) {
249       return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]);
250     } else {
251       return document.createElement.apply(document, arguments);
252     }
253   }
254
255   ;
256 /*!
257 {
258   "name": "Background Position Shorthand",
259   "property": "bgpositionshorthand",
260   "tags": ["css"],
261   "builderAliases": ["css_backgroundposition_shorthand"],
262   "notes": [{
263     "name": "MDN Docs",
264     "href": "https://developer.mozilla.org/en/CSS/background-position"
265   }, {
266     "name": "W3 Spec",
267     "href": "https://www.w3.org/TR/css3-background/#background-position"
268   }, {
269     "name": "Demo",
270     "href": "https://jsfiddle.net/Blink/bBXvt/"
271   }]
272 }
273 !*/
274 /* DOC
275 Detects if you can use the shorthand method to define multiple parts of an
276 element's background-position simultaniously.
277
278 eg `background-position: right 10px bottom 10px`
279 */
280
281   Modernizr.addTest('bgpositionshorthand', function() {
282     var elem = createElement('a');
283     var eStyle = elem.style;
284     var val = 'right 10px bottom 10px';
285     eStyle.cssText = 'background-position: ' + val + ';';
286     return (eStyle.backgroundPosition === val);
287   });
288
289
290   /**
291    * If the browsers follow the spec, then they would expose vendor-specific style as:
292    *   elem.style.WebkitBorderRadius
293    * instead of something like the following, which would be technically incorrect:
294    *   elem.style.webkitBorderRadius
295
296    * Webkit ghosts their properties in lowercase but Opera & Moz do not.
297    * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
298    *   erik.eae.net/archives/2008/03/10/21.48.10/
299
300    * More here: github.com/Modernizr/Modernizr/issues/issue/21
301    *
302    * @access private
303    * @returns {string} The string representing the vendor-specific style properties
304    */
305
306   var omPrefixes = 'Moz O ms Webkit';
307   
308
309   var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []);
310   ModernizrProto._cssomPrefixes = cssomPrefixes;
311   
312
313
314   /**
315    * contains checks to see if a string contains another string
316    *
317    * @access private
318    * @function contains
319    * @param {string} str - The string we want to check for substrings
320    * @param {string} substr - The substring we want to search the first string for
321    * @returns {boolean}
322    */
323
324   function contains(str, substr) {
325     return !!~('' + str).indexOf(substr);
326   }
327
328   ;
329
330   /**
331    * Create our "modernizr" element that we do most feature tests on.
332    *
333    * @access private
334    */
335
336   var modElem = {
337     elem: createElement('modernizr')
338   };
339
340   // Clean up this element
341   Modernizr._q.push(function() {
342     delete modElem.elem;
343   });
344
345   
346
347   var mStyle = {
348     style: modElem.elem.style
349   };
350
351   // kill ref for gc, must happen before mod.elem is removed, so we unshift on to
352   // the front of the queue.
353   Modernizr._q.unshift(function() {
354     delete mStyle.style;
355   });
356
357   
358
359   /**
360    * getBody returns the body of a document, or an element that can stand in for
361    * the body if a real body does not exist
362    *
363    * @access private
364    * @function getBody
365    * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an
366    * artificially created element that stands in for the body
367    */
368
369   function getBody() {
370     // After page load injecting a fake body doesn't work so check if body exists
371     var body = document.body;
372
373     if (!body) {
374       // Can't use the real body create a fake one.
375       body = createElement(isSVG ? 'svg' : 'body');
376       body.fake = true;
377     }
378
379     return body;
380   }
381
382   ;
383
384   /**
385    * injectElementWithStyles injects an element with style element and some CSS rules
386    *
387    * @access private
388    * @function injectElementWithStyles
389    * @param {string} rule - String representing a css rule
390    * @param {function} callback - A function that is used to test the injected element
391    * @param {number} [nodes] - An integer representing the number of additional nodes you want injected
392    * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
393    * @returns {boolean}
394    */
395
396   function injectElementWithStyles(rule, callback, nodes, testnames) {
397     var mod = 'modernizr';
398     var style;
399     var ret;
400     var node;
401     var docOverflow;
402     var div = createElement('div');
403     var body = getBody();
404
405     if (parseInt(nodes, 10)) {
406       // In order not to give false positives we create a node for each test
407       // This also allows the method to scale for unspecified uses
408       while (nodes--) {
409         node = createElement('div');
410         node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
411         div.appendChild(node);
412       }
413     }
414
415     style = createElement('style');
416     style.type = 'text/css';
417     style.id = 's' + mod;
418
419     // 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.
420     // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
421     (!body.fake ? div : body).appendChild(style);
422     body.appendChild(div);
423
424     if (style.styleSheet) {
425       style.styleSheet.cssText = rule;
426     } else {
427       style.appendChild(document.createTextNode(rule));
428     }
429     div.id = mod;
430
431     if (body.fake) {
432       //avoid crashing IE8, if background image is used
433       body.style.background = '';
434       //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
435       body.style.overflow = 'hidden';
436       docOverflow = docElement.style.overflow;
437       docElement.style.overflow = 'hidden';
438       docElement.appendChild(body);
439     }
440
441     ret = callback(div, rule);
442     // If this is done after page load we don't want to remove the body so check if body exists
443     if (body.fake) {
444       body.parentNode.removeChild(body);
445       docElement.style.overflow = docOverflow;
446       // Trigger layout so kinetic scrolling isn't disabled in iOS6+
447       docElement.offsetHeight;
448     } else {
449       div.parentNode.removeChild(div);
450     }
451
452     return !!ret;
453
454   }
455
456   ;
457
458   /**
459    * domToCSS takes a camelCase string and converts it to kebab-case
460    * e.g. boxSizing -> box-sizing
461    *
462    * @access private
463    * @function domToCSS
464    * @param {string} name - String name of camelCase prop we want to convert
465    * @returns {string} The kebab-case version of the supplied name
466    */
467
468   function domToCSS(name) {
469     return name.replace(/([A-Z])/g, function(str, m1) {
470       return '-' + m1.toLowerCase();
471     }).replace(/^ms-/, '-ms-');
472   }
473   ;
474
475   /**
476    * nativeTestProps allows for us to use native feature detection functionality if available.
477    * some prefixed form, or false, in the case of an unsupported rule
478    *
479    * @access private
480    * @function nativeTestProps
481    * @param {array} props - An array of property names
482    * @param {string} value - A string representing the value we want to check via @supports
483    * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise
484    */
485
486   // Accepts a list of property names and a single value
487   // Returns `undefined` if native detection not available
488   function nativeTestProps(props, value) {
489     var i = props.length;
490     // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface
491     if ('CSS' in window && 'supports' in window.CSS) {
492       // Try every prefixed variant of the property
493       while (i--) {
494         if (window.CSS.supports(domToCSS(props[i]), value)) {
495           return true;
496         }
497       }
498       return false;
499     }
500     // Otherwise fall back to at-rule (for Opera 12.x)
501     else if ('CSSSupportsRule' in window) {
502       // Build a condition string for every prefixed variant
503       var conditionText = [];
504       while (i--) {
505         conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')');
506       }
507       conditionText = conditionText.join(' or ');
508       return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) {
509         return getComputedStyle(node, null).position == 'absolute';
510       });
511     }
512     return undefined;
513   }
514   ;
515
516   /**
517    * cssToDOM takes a kebab-case string and converts it to camelCase
518    * e.g. box-sizing -> boxSizing
519    *
520    * @access private
521    * @function cssToDOM
522    * @param {string} name - String name of kebab-case prop we want to convert
523    * @returns {string} The camelCase version of the supplied name
524    */
525
526   function cssToDOM(name) {
527     return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) {
528       return m1 + m2.toUpperCase();
529     }).replace(/^-/, '');
530   }
531   ;
532
533   // testProps is a generic CSS / DOM property test.
534
535   // In testing support for a given CSS property, it's legit to test:
536   //    `elem.style[styleName] !== undefined`
537   // If the property is supported it will return an empty string,
538   // if unsupported it will return undefined.
539
540   // We'll take advantage of this quick test and skip setting a style
541   // on our modernizr element, but instead just testing undefined vs
542   // empty string.
543
544   // Property names can be provided in either camelCase or kebab-case.
545
546   function testProps(props, prefixed, value, skipValueTest) {
547     skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest;
548
549     // Try native detect first
550     if (!is(value, 'undefined')) {
551       var result = nativeTestProps(props, value);
552       if (!is(result, 'undefined')) {
553         return result;
554       }
555     }
556
557     // Otherwise do it properly
558     var afterInit, i, propsLength, prop, before;
559
560     // If we don't have a style element, that means we're running async or after
561     // the core tests, so we'll need to create our own elements to use
562
563     // inside of an SVG element, in certain browsers, the `style` element is only
564     // defined for valid tags. Therefore, if `modernizr` does not have one, we
565     // fall back to a less used element and hope for the best.
566     var elems = ['modernizr', 'tspan'];
567     while (!mStyle.style) {
568       afterInit = true;
569       mStyle.modElem = createElement(elems.shift());
570       mStyle.style = mStyle.modElem.style;
571     }
572
573     // Delete the objects if we created them.
574     function cleanElems() {
575       if (afterInit) {
576         delete mStyle.style;
577         delete mStyle.modElem;
578       }
579     }
580
581     propsLength = props.length;
582     for (i = 0; i < propsLength; i++) {
583       prop = props[i];
584       before = mStyle.style[prop];
585
586       if (contains(prop, '-')) {
587         prop = cssToDOM(prop);
588       }
589
590       if (mStyle.style[prop] !== undefined) {
591
592         // If value to test has been passed in, do a set-and-check test.
593         // 0 (integer) is a valid property value, so check that `value` isn't
594         // undefined, rather than just checking it's truthy.
595         if (!skipValueTest && !is(value, 'undefined')) {
596
597           // Needs a try catch block because of old IE. This is slow, but will
598           // be avoided in most cases because `skipValueTest` will be used.
599           try {
600             mStyle.style[prop] = value;
601           } catch (e) {}
602
603           // If the property value has changed, we assume the value used is
604           // supported. If `value` is empty string, it'll fail here (because
605           // it hasn't changed), which matches how browsers have implemented
606           // CSS.supports()
607           if (mStyle.style[prop] != before) {
608             cleanElems();
609             return prefixed == 'pfx' ? prop : true;
610           }
611         }
612         // Otherwise just return true, or the property name if this is a
613         // `prefixed()` call
614         else {
615           cleanElems();
616           return prefixed == 'pfx' ? prop : true;
617         }
618       }
619     }
620     cleanElems();
621     return false;
622   }
623
624   ;
625
626   /**
627    * List of JavaScript DOM values used for tests
628    *
629    * @memberof Modernizr
630    * @name Modernizr._domPrefixes
631    * @optionName Modernizr._domPrefixes
632    * @optionProp domPrefixes
633    * @access public
634    * @example
635    *
636    * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather
637    * than kebab-case properties, all properties are their Capitalized variant
638    *
639    * ```js
640    * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ];
641    * ```
642    */
643
644   var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []);
645   ModernizrProto._domPrefixes = domPrefixes;
646   
647
648   /**
649    * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill.
650    *
651    * @access private
652    * @function fnBind
653    * @param {function} fn - a function you want to change `this` reference to
654    * @param {object} that - the `this` you want to call the function with
655    * @returns {function} The wrapped version of the supplied function
656    */
657
658   function fnBind(fn, that) {
659     return function() {
660       return fn.apply(that, arguments);
661     };
662   }
663
664   ;
665
666   /**
667    * testDOMProps is a generic DOM property test; if a browser supports
668    *   a certain property, it won't return undefined for it.
669    *
670    * @access private
671    * @function testDOMProps
672    * @param {array.<string>} props - An array of properties to test for
673    * @param {object} obj - An object or Element you want to use to test the parameters again
674    * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check
675    */
676   function testDOMProps(props, obj, elem) {
677     var item;
678
679     for (var i in props) {
680       if (props[i] in obj) {
681
682         // return the property name as a string
683         if (elem === false) {
684           return props[i];
685         }
686
687         item = obj[props[i]];
688
689         // let's bind a function
690         if (is(item, 'function')) {
691           // bind to obj unless overriden
692           return fnBind(item, elem || obj);
693         }
694
695         // return the unbound function or obj or value
696         return item;
697       }
698     }
699     return false;
700   }
701
702   ;
703
704   /**
705    * testPropsAll tests a list of DOM properties we want to check against.
706    * We specify literally ALL possible (known and/or likely) properties on
707    * the element including the non-vendor prefixed one, for forward-
708    * compatibility.
709    *
710    * @access private
711    * @function testPropsAll
712    * @param {string} prop - A string of the property to test for
713    * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip
714    * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against
715    * @param {string} [value] - A string of a css value
716    * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set
717    */
718   function testPropsAll(prop, prefixed, elem, value, skipValueTest) {
719
720     var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
721     props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
722
723     // did they call .prefixed('boxSizing') or are we just testing a prop?
724     if (is(prefixed, 'string') || is(prefixed, 'undefined')) {
725       return testProps(props, prefixed, value, skipValueTest);
726
727       // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
728     } else {
729       props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
730       return testDOMProps(props, prefixed, elem);
731     }
732   }
733
734   // Modernizr.testAllProps() investigates whether a given style property,
735   // or any of its vendor-prefixed variants, is recognized
736   //
737   // Note that the property names must be provided in the camelCase variant.
738   // Modernizr.testAllProps('boxSizing')
739   ModernizrProto.testAllProps = testPropsAll;
740
741   
742
743   /**
744    * testAllProps determines whether a given CSS property is supported in the browser
745    *
746    * @memberof Modernizr
747    * @name Modernizr.testAllProps
748    * @optionName Modernizr.testAllProps()
749    * @optionProp testAllProps
750    * @access public
751    * @function testAllProps
752    * @param {string} prop - String naming the property to test (either camelCase or kebab-case)
753    * @param {string} [value] - String of the value to test
754    * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection
755    * @example
756    *
757    * testAllProps determines whether a given CSS property, in some prefixed form,
758    * is supported by the browser.
759    *
760    * ```js
761    * testAllProps('boxSizing')  // true
762    * ```
763    *
764    * It can optionally be given a CSS value in string form to test if a property
765    * value is valid
766    *
767    * ```js
768    * testAllProps('display', 'block') // true
769    * testAllProps('display', 'penguin') // false
770    * ```
771    *
772    * A boolean can be passed as a third parameter to skip the value check when
773    * native detection (@supports) isn't available.
774    *
775    * ```js
776    * testAllProps('shapeOutside', 'content-box', true);
777    * ```
778    */
779
780   function testAllProps(prop, value, skipValueTest) {
781     return testPropsAll(prop, undefined, undefined, value, skipValueTest);
782   }
783   ModernizrProto.testAllProps = testAllProps;
784   
785 /*!
786 {
787   "name": "Background Position XY",
788   "property": "bgpositionxy",
789   "tags": ["css"],
790   "builderAliases": ["css_backgroundposition_xy"],
791   "authors": ["Allan Lei", "Brandom Aaron"],
792   "notes": [{
793     "name": "Demo",
794     "href": "https://jsfiddle.net/allanlei/R8AYS/"
795   }, {
796     "name": "Adapted From",
797     "href": "https://github.com/brandonaaron/jquery-cssHooks/blob/master/bgpos.js"
798   }]
799 }
800 !*/
801 /* DOC
802 Detects the ability to control an element's background position using css
803 */
804
805   Modernizr.addTest('bgpositionxy', function() {
806     return testAllProps('backgroundPositionX', '3px', true) && testAllProps('backgroundPositionY', '5px', true);
807   });
808
809 /*!
810 {
811   "name": "Background Repeat",
812   "property": ["bgrepeatspace", "bgrepeatround"],
813   "tags": ["css"],
814   "builderAliases": ["css_backgroundrepeat"],
815   "authors": ["Ryan Seddon"],
816   "notes": [{
817     "name": "MDN Docs",
818     "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat"
819   }, {
820     "name": "Test Page",
821     "href": "https://jsbin.com/uzesun/"
822   }, {
823     "name": "Demo",
824     "href": "https://jsfiddle.net/ryanseddon/yMLTQ/6/"
825   }]
826 }
827 !*/
828 /* DOC
829 Detects the ability to use round and space as properties for background-repeat
830 */
831
832   // Must value-test these
833   Modernizr.addTest('bgrepeatround', testAllProps('backgroundRepeat', 'round'));
834   Modernizr.addTest('bgrepeatspace', testAllProps('backgroundRepeat', 'space'));
835
836 /*!
837 {
838   "name": "Background Size Cover",
839   "property": "bgsizecover",
840   "tags": ["css"],
841   "builderAliases": ["css_backgroundsizecover"],
842   "notes": [{
843     "name" : "MDN Docs",
844     "href": "https://developer.mozilla.org/en/CSS/background-size"
845   }]
846 }
847 !*/
848
849   // Must test value, as this specifically tests the `cover` value
850   Modernizr.addTest('bgsizecover', testAllProps('backgroundSize', 'cover'));
851
852 /*!
853 {
854   "name": "Border Radius",
855   "property": "borderradius",
856   "caniuse": "border-radius",
857   "polyfills": ["css3pie"],
858   "tags": ["css"],
859   "notes": [{
860     "name": "Comprehensive Compat Chart",
861     "href": "https://muddledramblings.com/table-of-css3-border-radius-compliance"
862   }]
863 }
864 !*/
865
866   Modernizr.addTest('borderradius', testAllProps('borderRadius', '0px', true));
867
868 /*!
869 {
870   "name": "CSS Animations",
871   "property": "cssanimations",
872   "caniuse": "css-animation",
873   "polyfills": ["transformie", "csssandpaper"],
874   "tags": ["css"],
875   "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"],
876   "notes": [{
877     "name" : "Article: 'Dispelling the Android CSS animation myths'",
878     "href": "https://goo.gl/OGw5Gm"
879   }]
880 }
881 !*/
882 /* DOC
883 Detects whether or not elements can be animated using CSS
884 */
885
886   Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true));
887
888
889   /**
890    * List of property values to set for css tests. See ticket #21
891    * http://git.io/vUGl4
892    *
893    * @memberof Modernizr
894    * @name Modernizr._prefixes
895    * @optionName Modernizr._prefixes
896    * @optionProp prefixes
897    * @access public
898    * @example
899    *
900    * Modernizr._prefixes is the internal list of prefixes that we test against
901    * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply
902    * an array of kebab-case vendor prefixes you can use within your code.
903    *
904    * Some common use cases include
905    *
906    * Generating all possible prefixed version of a CSS property
907    * ```js
908    * var rule = Modernizr._prefixes.join('transform: rotate(20deg); ');
909    *
910    * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);'
911    * ```
912    *
913    * Generating all possible prefixed version of a CSS value
914    * ```js
915    * rule = 'display:' +  Modernizr._prefixes.join('flex; display:') + 'flex';
916    *
917    * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex'
918    * ```
919    */
920
921   var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : []);
922
923   // expose these for the plugin API. Look in the source for how to join() them against your input
924   ModernizrProto._prefixes = prefixes;
925
926   
927 /*!
928 {
929   "name": "CSS Calc",
930   "property": "csscalc",
931   "caniuse": "calc",
932   "tags": ["css"],
933   "builderAliases": ["css_calc"],
934   "authors": ["@calvein"]
935 }
936 !*/
937 /* DOC
938 Method of allowing calculated values for length units. For example:
939
940 ```css
941 //lem {
942   width: calc(100% - 3em);
943 }
944 ```
945 */
946
947   Modernizr.addTest('csscalc', function() {
948     var prop = 'width:';
949     var value = 'calc(10px);';
950     var el = createElement('a');
951
952     el.style.cssText = prop + prefixes.join(value + prop);
953
954     return !!el.style.length;
955   });
956
957 /*!
958 {
959   "name": "CSS Transforms",
960   "property": "csstransforms",
961   "caniuse": "transforms2d",
962   "tags": ["css"]
963 }
964 !*/
965
966   Modernizr.addTest('csstransforms', function() {
967     // Android < 3.0 is buggy, so we sniff and blacklist
968     // http://git.io/hHzL7w
969     return navigator.userAgent.indexOf('Android 2.') === -1 &&
970            testAllProps('transform', 'scale(1)', true);
971   });
972
973
974   /**
975    * testStyles injects an element with style element and some CSS rules
976    *
977    * @memberof Modernizr
978    * @name Modernizr.testStyles
979    * @optionName Modernizr.testStyles()
980    * @optionProp testStyles
981    * @access public
982    * @function testStyles
983    * @param {string} rule - String representing a css rule
984    * @param {function} callback - A function that is used to test the injected element
985    * @param {number} [nodes] - An integer representing the number of additional nodes you want injected
986    * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
987    * @returns {boolean}
988    * @example
989    *
990    * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page
991    * along with (possibly multiple) DOM elements. This lets you check for features
992    * 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).
993    *
994    * ```js
995    * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) {
996    *   // elem is the first DOM node in the page (by default #modernizr)
997    *   // rule is the first argument you supplied - the CSS rule in string form
998    *
999    *   addTest('widthworks', elem.style.width === '9px')
1000    * });
1001    * ```
1002    *
1003    * If your test requires multiple nodes, you can include a third argument
1004    * indicating how many additional div elements to include on the page. The
1005    * additional nodes are injected as children of the `elem` that is returned as
1006    * the first argument to the callback.
1007    *
1008    * ```js
1009    * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) {
1010    *   document.getElementById('modernizr').style.width === '1px'; // true
1011    *   document.getElementById('modernizr2').style.width === '2px'; // true
1012    *   elem.firstChild === document.getElementById('modernizr2'); // true
1013    * }, 1);
1014    * ```
1015    *
1016    * By default, all of the additional elements have an ID of `modernizr[n]`, where
1017    * `n` is its index (e.g. the first additional, second overall is `#modernizr2`,
1018    * the second additional is `#modernizr3`, etc.).
1019    * If you want to have more meaningful IDs for your function, you can provide
1020    * them as the fourth argument, as an array of strings
1021    *
1022    * ```js
1023    * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) {
1024    *   elem.firstChild === document.getElementById('foo'); // true
1025    *   elem.lastChild === document.getElementById('bar'); // true
1026    * }, 2, ['foo', 'bar']);
1027    * ```
1028    *
1029    */
1030
1031   var testStyles = ModernizrProto.testStyles = injectElementWithStyles;
1032   
1033 /*!
1034 {
1035   "name": "CSS Supports",
1036   "property": "supports",
1037   "caniuse": "css-featurequeries",
1038   "tags": ["css"],
1039   "builderAliases": ["css_supports"],
1040   "notes": [{
1041     "name": "W3 Spec",
1042     "href": "http://dev.w3.org/csswg/css3-conditional/#at-supports"
1043   },{
1044     "name": "Related Github Issue",
1045     "href": "github.com/Modernizr/Modernizr/issues/648"
1046   },{
1047     "name": "W3 Info",
1048     "href": "http://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface"
1049   }]
1050 }
1051 !*/
1052
1053   var newSyntax = 'CSS' in window && 'supports' in window.CSS;
1054   var oldSyntax = 'supportsCSS' in window;
1055   Modernizr.addTest('supports', newSyntax || oldSyntax);
1056
1057 /*!
1058 {
1059   "name": "CSS Transforms 3D",
1060   "property": "csstransforms3d",
1061   "caniuse": "transforms3d",
1062   "tags": ["css"],
1063   "warnings": [
1064     "Chrome may occassionally fail this test on some systems; more info: https://code.google.com/p/chromium/issues/detail?id=129004"
1065   ]
1066 }
1067 !*/
1068
1069   Modernizr.addTest('csstransforms3d', function() {
1070     var ret = !!testAllProps('perspective', '1px', true);
1071     var usePrefix = Modernizr._config.usePrefixes;
1072
1073     // Webkit's 3D transforms are passed off to the browser's own graphics renderer.
1074     //   It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
1075     //   some conditions. As a result, Webkit typically recognizes the syntax but
1076     //   will sometimes throw a false positive, thus we must do a more thorough check:
1077     if (ret && (!usePrefix || 'webkitPerspective' in docElement.style)) {
1078       var mq;
1079       var defaultStyle = '#modernizr{width:0;height:0}';
1080       // Use CSS Conditional Rules if available
1081       if (Modernizr.supports) {
1082         mq = '@supports (perspective: 1px)';
1083       } else {
1084         // Otherwise, Webkit allows this media query to succeed only if the feature is enabled.
1085         // `@media (transform-3d),(-webkit-transform-3d){ ... }`
1086         mq = '@media (transform-3d)';
1087         if (usePrefix) {
1088           mq += ',(-webkit-transform-3d)';
1089         }
1090       }
1091
1092       mq += '{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}';
1093
1094       testStyles(defaultStyle + mq, function(elem) {
1095         ret = elem.offsetWidth === 7 && elem.offsetHeight === 18;
1096       });
1097     }
1098
1099     return ret;
1100   });
1101
1102 /*!
1103 {
1104   "name": "CSS Transform Style preserve-3d",
1105   "property": "preserve3d",
1106   "authors": ["edmellum"],
1107   "tags": ["css"],
1108   "notes": [{
1109     "name": "MDN Docs",
1110     "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/transform-style"
1111   },{
1112     "name": "Related Github Issue",
1113     "href": "https://github.com/Modernizr/Modernizr/issues/762"
1114   }]
1115 }
1116 !*/
1117 /* DOC
1118 Detects support for `transform-style: preserve-3d`, for getting a proper 3D perspective on elements.
1119 */
1120
1121   Modernizr.addTest('preserve3d', testAllProps('transformStyle', 'preserve-3d'));
1122
1123 /*!
1124 {
1125   "name": "CSS Transitions",
1126   "property": "csstransitions",
1127   "caniuse": "css-transitions",
1128   "tags": ["css"]
1129 }
1130 !*/
1131
1132   Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true));
1133
1134 /*!
1135 {
1136   "name": "Flexbox (tweener)",
1137   "property": "flexboxtweener",
1138   "tags": ["css"],
1139   "polyfills": ["flexie"],
1140   "notes": [{
1141     "name": "The _inbetween_ flexbox",
1142     "href": "https://www.w3.org/TR/2011/WD-css3-flexbox-20111129/"
1143   }],
1144   "warnings": ["This represents an old syntax, not the latest standard syntax."]
1145 }
1146 !*/
1147
1148   Modernizr.addTest('flexboxtweener', testAllProps('flexAlign', 'end', true));
1149
1150 /*!
1151 {
1152   "name": "@font-face",
1153   "property": "fontface",
1154   "authors": ["Diego Perini", "Mat Marquis"],
1155   "tags": ["css"],
1156   "knownBugs": [
1157     "False Positive: WebOS https://github.com/Modernizr/Modernizr/issues/342",
1158     "False Postive: WP7 https://github.com/Modernizr/Modernizr/issues/538"
1159   ],
1160   "notes": [{
1161     "name": "@font-face detection routine by Diego Perini",
1162     "href": "http://javascript.nwbox.com/CSSSupport/"
1163   },{
1164     "name": "Filament Group @font-face compatibility research",
1165     "href": "https://docs.google.com/presentation/d/1n4NyG4uPRjAA8zn_pSQ_Ket0RhcWC6QlZ6LMjKeECo0/edit#slide=id.p"
1166   },{
1167     "name": "Filament Grunticon/@font-face device testing results",
1168     "href": "https://docs.google.com/spreadsheet/ccc?key=0Ag5_yGvxpINRdHFYeUJPNnZMWUZKR2ItMEpRTXZPdUE#gid=0"
1169   },{
1170     "name": "CSS fonts on Android",
1171     "href": "https://stackoverflow.com/questions/3200069/css-fonts-on-android"
1172   },{
1173     "name": "@font-face and Android",
1174     "href": "http://archivist.incutio.com/viewlist/css-discuss/115960"
1175   }]
1176 }
1177 !*/
1178
1179   var blacklist = (function() {
1180     var ua = navigator.userAgent;
1181     var wkvers = ua.match(/applewebkit\/([0-9]+)/gi) && parseFloat(RegExp.$1);
1182     var webos = ua.match(/w(eb)?osbrowser/gi);
1183     var wppre8 = ua.match(/windows phone/gi) && ua.match(/iemobile\/([0-9])+/gi) && parseFloat(RegExp.$1) >= 9;
1184     var oldandroid = wkvers < 533 && ua.match(/android/gi);
1185     return webos || oldandroid || wppre8;
1186   }());
1187   if (blacklist) {
1188     Modernizr.addTest('fontface', false);
1189   } else {
1190     testStyles('@font-face {font-family:"font";src:url("https://")}', function(node, rule) {
1191       var style = document.getElementById('smodernizr');
1192       var sheet = style.sheet || style.styleSheet;
1193       var cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
1194       var bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
1195       Modernizr.addTest('fontface', bool);
1196     });
1197   }
1198 ;
1199 /*!
1200 {
1201   "name": "SVG",
1202   "property": "svg",
1203   "caniuse": "svg",
1204   "tags": ["svg"],
1205   "authors": ["Erik Dahlstrom"],
1206   "polyfills": [
1207     "svgweb",
1208     "raphael",
1209     "amplesdk",
1210     "canvg",
1211     "svg-boilerplate",
1212     "sie",
1213     "dojogfx",
1214     "fabricjs"
1215   ]
1216 }
1217 !*/
1218 /* DOC
1219 Detects support for SVG in `<embed>` or `<object>` elements.
1220 */
1221
1222   Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);
1223
1224
1225   /**
1226    * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support
1227    *
1228    * @author kangax
1229    * @access private
1230    * @function hasOwnProp
1231    * @param {object} object - The object to check for a property
1232    * @param {string} property - The property to check for
1233    * @returns {boolean}
1234    */
1235
1236   // hasOwnProperty shim by kangax needed for Safari 2.0 support
1237   var hasOwnProp;
1238
1239   (function() {
1240     var _hasOwnProperty = ({}).hasOwnProperty;
1241     /* istanbul ignore else */
1242     /* we have no way of testing IE 5.5 or safari 2,
1243      * so just assume the else gets hit */
1244     if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) {
1245       hasOwnProp = function(object, property) {
1246         return _hasOwnProperty.call(object, property);
1247       };
1248     }
1249     else {
1250       hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
1251         return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
1252       };
1253     }
1254   })();
1255
1256   
1257
1258
1259    // _l tracks listeners for async tests, as well as tests that execute after the initial run
1260   ModernizrProto._l = {};
1261
1262   /**
1263    * Modernizr.on is a way to listen for the completion of async tests. Being
1264    * asynchronous, they may not finish before your scripts run. As a result you
1265    * will get a possibly false negative `undefined` value.
1266    *
1267    * @memberof Modernizr
1268    * @name Modernizr.on
1269    * @access public
1270    * @function on
1271    * @param {string} feature - String name of the feature detect
1272    * @param {function} cb - Callback function returning a Boolean - true if feature is supported, false if not
1273    * @example
1274    *
1275    * ```js
1276    * Modernizr.on('flash', function( result ) {
1277    *   if (result) {
1278    *    // the browser has flash
1279    *   } else {
1280    *     // the browser does not have flash
1281    *   }
1282    * });
1283    * ```
1284    */
1285
1286   ModernizrProto.on = function(feature, cb) {
1287     // Create the list of listeners if it doesn't exist
1288     if (!this._l[feature]) {
1289       this._l[feature] = [];
1290     }
1291
1292     // Push this test on to the listener list
1293     this._l[feature].push(cb);
1294
1295     // If it's already been resolved, trigger it on next tick
1296     if (Modernizr.hasOwnProperty(feature)) {
1297       // Next Tick
1298       setTimeout(function() {
1299         Modernizr._trigger(feature, Modernizr[feature]);
1300       }, 0);
1301     }
1302   };
1303
1304   /**
1305    * _trigger is the private function used to signal test completion and run any
1306    * callbacks registered through [Modernizr.on](#modernizr-on)
1307    *
1308    * @memberof Modernizr
1309    * @name Modernizr._trigger
1310    * @access private
1311    * @function _trigger
1312    * @param {string} feature - string name of the feature detect
1313    * @param {function|boolean} [res] - A feature detection function, or the boolean =
1314    * result of a feature detection function
1315    */
1316
1317   ModernizrProto._trigger = function(feature, res) {
1318     if (!this._l[feature]) {
1319       return;
1320     }
1321
1322     var cbs = this._l[feature];
1323
1324     // Force async
1325     setTimeout(function() {
1326       var i, cb;
1327       for (i = 0; i < cbs.length; i++) {
1328         cb = cbs[i];
1329         cb(res);
1330       }
1331     }, 0);
1332
1333     // Don't trigger these again
1334     delete this._l[feature];
1335   };
1336
1337   /**
1338    * addTest allows you to define your own feature detects that are not currently
1339    * included in Modernizr (under the covers it's the exact same code Modernizr
1340    * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result
1341    * will be added onto the Modernizr object, as well as an appropriate className set on
1342    * the html element when configured to do so
1343    *
1344    * @memberof Modernizr
1345    * @name Modernizr.addTest
1346    * @optionName Modernizr.addTest()
1347    * @optionProp addTest
1348    * @access public
1349    * @function addTest
1350    * @param {string|object} feature - The string name of the feature detect, or an
1351    * object of feature detect names and test
1352    * @param {function|boolean} test - Function returning true if feature is supported,
1353    * false if not. Otherwise a boolean representing the results of a feature detection
1354    * @example
1355    *
1356    * The most common way of creating your own feature detects is by calling
1357    * `Modernizr.addTest` with a string (preferably just lowercase, without any
1358    * punctuation), and a function you want executed that will return a boolean result
1359    *
1360    * ```js
1361    * Modernizr.addTest('itsTuesday', function() {
1362    *  var d = new Date();
1363    *  return d.getDay() === 2;
1364    * });
1365    * ```
1366    *
1367    * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday,
1368    * and to `false` every other day of the week. One thing to notice is that the names of
1369    * feature detect functions are always lowercased when added to the Modernizr object. That
1370    * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will.
1371    *
1372    *
1373    *  Since we only look at the returned value from any feature detection function,
1374    *  you do not need to actually use a function. For simple detections, just passing
1375    *  in a statement that will return a boolean value works just fine.
1376    *
1377    * ```js
1378    * Modernizr.addTest('hasJquery', 'jQuery' in window);
1379    * ```
1380    *
1381    * Just like before, when the above runs `Modernizr.hasjquery` will be true if
1382    * jQuery has been included on the page. Not using a function saves a small amount
1383    * of overhead for the browser, as well as making your code much more readable.
1384    *
1385    * Finally, you also have the ability to pass in an object of feature names and
1386    * their tests. This is handy if you want to add multiple detections in one go.
1387    * The keys should always be a string, and the value can be either a boolean or
1388    * function that returns a boolean.
1389    *
1390    * ```js
1391    * var detects = {
1392    *  'hasjquery': 'jQuery' in window,
1393    *  'itstuesday': function() {
1394    *    var d = new Date();
1395    *    return d.getDay() === 2;
1396    *  }
1397    * }
1398    *
1399    * Modernizr.addTest(detects);
1400    * ```
1401    *
1402    * There is really no difference between the first methods and this one, it is
1403    * just a convenience to let you write more readable code.
1404    */
1405
1406   function addTest(feature, test) {
1407
1408     if (typeof feature == 'object') {
1409       for (var key in feature) {
1410         if (hasOwnProp(feature, key)) {
1411           addTest(key, feature[ key ]);
1412         }
1413       }
1414     } else {
1415
1416       feature = feature.toLowerCase();
1417       var featureNameSplit = feature.split('.');
1418       var last = Modernizr[featureNameSplit[0]];
1419
1420       // Again, we don't check for parent test existence. Get that right, though.
1421       if (featureNameSplit.length == 2) {
1422         last = last[featureNameSplit[1]];
1423       }
1424
1425       if (typeof last != 'undefined') {
1426         // we're going to quit if you're trying to overwrite an existing test
1427         // if we were to allow it, we'd do this:
1428         //   var re = new RegExp("\\b(no-)?" + feature + "\\b");
1429         //   docElement.className = docElement.className.replace( re, '' );
1430         // but, no rly, stuff 'em.
1431         return Modernizr;
1432       }
1433
1434       test = typeof test == 'function' ? test() : test;
1435
1436       // Set the value (this is the magic, right here).
1437       if (featureNameSplit.length == 1) {
1438         Modernizr[featureNameSplit[0]] = test;
1439       } else {
1440         // cast to a Boolean, if not one already
1441         /* jshint -W053 */
1442         if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
1443           Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
1444         }
1445
1446         Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test;
1447       }
1448
1449       // Set a single class (either `feature` or `no-feature`)
1450       /* jshint -W041 */
1451       setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]);
1452       /* jshint +W041 */
1453
1454       // Trigger the event
1455       Modernizr._trigger(feature, test);
1456     }
1457
1458     return Modernizr; // allow chaining.
1459   }
1460
1461   // After all the tests are run, add self to the Modernizr prototype
1462   Modernizr._q.push(function() {
1463     ModernizrProto.addTest = addTest;
1464   });
1465
1466   
1467
1468 /*!
1469 {
1470   "name": "SVG as an <img> tag source",
1471   "property": "svgasimg",
1472   "caniuse" : "svg-img",
1473   "tags": ["svg"],
1474   "authors": ["Chris Coyier"],
1475   "notes": [{
1476     "name": "HTML5 Spec",
1477     "href": "http://www.w3.org/TR/html5/embedded-content-0.html#the-img-element"
1478   }]
1479 }
1480 !*/
1481
1482
1483   // Original Async test by Stu Cox
1484   // https://gist.github.com/chriscoyier/8774501
1485
1486   // Now a Sync test based on good results here
1487   // http://codepen.io/chriscoyier/pen/bADFx
1488
1489   // Note http://www.w3.org/TR/SVG11/feature#Image is *supposed* to represent
1490   // support for the `<image>` tag in SVG, not an SVG file linked from an `<img>`
1491   // tag in HTML – but it’s a heuristic which works
1492   Modernizr.addTest('svgasimg', document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1'));
1493
1494
1495   /**
1496    * Object.prototype.toString can be used with every object and allows you to
1497    * get its class easily. Abstracting it off of an object prevents situations
1498    * where the toString property has been overridden
1499    *
1500    * @access private
1501    * @function toStringFn
1502    * @returns {function} An abstracted toString function
1503    */
1504
1505   var toStringFn = ({}).toString;
1506   
1507 /*!
1508 {
1509   "name": "SVG clip paths",
1510   "property": "svgclippaths",
1511   "tags": ["svg"],
1512   "notes": [{
1513     "name": "Demo",
1514     "href": "http://srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg"
1515   }]
1516 }
1517 !*/
1518 /* DOC
1519 Detects support for clip paths in SVG (only, not on HTML content).
1520
1521 See [this discussion](https://github.com/Modernizr/Modernizr/issues/213) regarding applying SVG clip paths to HTML content.
1522 */
1523
1524   Modernizr.addTest('svgclippaths', function() {
1525     return !!document.createElementNS &&
1526       /SVGClipPath/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')));
1527   });
1528
1529 /*!
1530 {
1531   "name": "SVG filters",
1532   "property": "svgfilters",
1533   "caniuse": "svg-filters",
1534   "tags": ["svg"],
1535   "builderAliases": ["svg_filters"],
1536   "authors": ["Erik Dahlstrom"],
1537   "notes": [{
1538     "name": "W3C Spec",
1539     "href": "https://www.w3.org/TR/SVG11/filters.html"
1540   }]
1541 }
1542 !*/
1543
1544   // Should fail in Safari: https://stackoverflow.com/questions/9739955/feature-detecting-support-for-svg-filters.
1545   Modernizr.addTest('svgfilters', function() {
1546     var result = false;
1547     try {
1548       result = 'SVGFEColorMatrixElement' in window &&
1549         SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE == 2;
1550     }
1551     catch (e) {}
1552     return result;
1553   });
1554
1555 /*!
1556 {
1557   "name": "SVG foreignObject",
1558   "property": "svgforeignobject",
1559   "tags": ["svg"],
1560   "notes": [{
1561     "name": "W3C Spec",
1562     "href": "https://www.w3.org/TR/SVG11/extend.html"
1563   }]
1564 }
1565 !*/
1566 /* DOC
1567 Detects support for foreignObject tag in SVG.
1568 */
1569
1570   Modernizr.addTest('svgforeignobject', function() {
1571     return !!document.createElementNS &&
1572       /SVGForeignObject/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')));
1573   });
1574
1575 /*!
1576 {
1577   "name": "Inline SVG",
1578   "property": "inlinesvg",
1579   "caniuse": "svg-html5",
1580   "tags": ["svg"],
1581   "notes": [{
1582     "name": "Test page",
1583     "href": "https://paulirish.com/demo/inline-svg"
1584   }, {
1585     "name": "Test page and results",
1586     "href": "https://codepen.io/eltonmesquita/full/GgXbvo/"
1587   }],
1588   "polyfills": ["inline-svg-polyfill"],
1589   "knownBugs": ["False negative on some Chromia browsers."]
1590 }
1591 !*/
1592 /* DOC
1593 Detects support for inline SVG in HTML (not within XHTML).
1594 */
1595
1596   Modernizr.addTest('inlinesvg', function() {
1597     var div = createElement('div');
1598     div.innerHTML = '<svg/>';
1599     return (typeof SVGRect != 'undefined' && div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg';
1600   });
1601
1602 /*!
1603 {
1604   "name": "SVG SMIL animation",
1605   "property": "smil",
1606   "caniuse": "svg-smil",
1607   "tags": ["svg"],
1608   "notes": [{
1609   "name": "W3C Synchronised Multimedia spec",
1610   "href": "https://www.w3.org/AudioVideo/"
1611   }]
1612 }
1613 !*/
1614
1615   // SVG SMIL animation
1616   Modernizr.addTest('smil', function() {
1617     return !!document.createElementNS &&
1618       /SVGAnimate/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'animate')));
1619   });
1620
1621 /*!
1622 {
1623   "name": "Local Storage",
1624   "property": "localstorage",
1625   "caniuse": "namevalue-storage",
1626   "tags": ["storage"],
1627   "knownBugs": [],
1628   "notes": [],
1629   "warnings": [],
1630   "polyfills": [
1631     "joshuabell-polyfill",
1632     "cupcake",
1633     "storagepolyfill",
1634     "amplifyjs",
1635     "yui-cacheoffline"
1636   ]
1637 }
1638 !*/
1639
1640   // In FF4, if disabled, window.localStorage should === null.
1641
1642   // Normally, we could not test that directly and need to do a
1643   //   `('localStorage' in window) && ` test first because otherwise Firefox will
1644   //   throw bugzil.la/365772 if cookies are disabled
1645
1646   // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
1647   // will throw the exception:
1648   //   QUOTA_EXCEEDED_ERROR DOM Exception 22.
1649   // Peculiarly, getItem and removeItem calls do not throw.
1650
1651   // Because we are forced to try/catch this, we'll go aggressive.
1652
1653   // Just FWIW: IE8 Compat mode supports these features completely:
1654   //   www.quirksmode.org/dom/html5.html
1655   // But IE8 doesn't support either with local files
1656
1657   Modernizr.addTest('localstorage', function() {
1658     var mod = 'modernizr';
1659     try {
1660       localStorage.setItem(mod, mod);
1661       localStorage.removeItem(mod);
1662       return true;
1663     } catch (e) {
1664       return false;
1665     }
1666   });
1667
1668 /*!
1669 {
1670   "name": "Session Storage",
1671   "property": "sessionstorage",
1672   "tags": ["storage"],
1673   "polyfills": ["joshuabell-polyfill", "cupcake", "sessionstorage"]
1674 }
1675 !*/
1676
1677   // Because we are forced to try/catch this, we'll go aggressive.
1678
1679   // Just FWIW: IE8 Compat mode supports these features completely:
1680   //   www.quirksmode.org/dom/html5.html
1681   // But IE8 doesn't support either with local files
1682   Modernizr.addTest('sessionstorage', function() {
1683     var mod = 'modernizr';
1684     try {
1685       sessionStorage.setItem(mod, mod);
1686       sessionStorage.removeItem(mod);
1687       return true;
1688     } catch (e) {
1689       return false;
1690     }
1691   });
1692
1693 /*!
1694 {
1695   "name": "Web SQL Database",
1696   "property": "websqldatabase",
1697   "caniuse": "sql-storage",
1698   "tags": ["storage"]
1699 }
1700 !*/
1701
1702   // Chrome incognito mode used to throw an exception when using openDatabase
1703   // It doesn't anymore.
1704   Modernizr.addTest('websqldatabase', 'openDatabase' in window);
1705
1706 /*!
1707 {
1708   "name": "CSS Multiple Backgrounds",
1709   "caniuse": "multibackgrounds",
1710   "property": "multiplebgs",
1711   "tags": ["css"]
1712 }
1713 !*/
1714
1715   // Setting multiple images AND a color on the background shorthand property
1716   // and then querying the style.background property value for the number of
1717   // occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
1718
1719   Modernizr.addTest('multiplebgs', function() {
1720     var style = createElement('a').style;
1721     style.cssText = 'background:url(https://),url(https://),red url(https://)';
1722
1723     // If the UA supports multiple backgrounds, there should be three occurrences
1724     // of the string "url(" in the return value for elemStyle.background
1725     return (/(url\s*\(.*?){3}/).test(style.background);
1726   });
1727
1728
1729   // Run each test
1730   testRunner();
1731
1732   // Remove the "no-js" class if it exists
1733   setClasses(classes);
1734
1735   delete ModernizrProto.addTest;
1736   delete ModernizrProto.addAsyncTest;
1737
1738   // Run the things that are supposed to run after the tests
1739   for (var i = 0; i < Modernizr._q.length; i++) {
1740     Modernizr._q[i]();
1741   }
1742
1743   // Leak Modernizr namespace
1744   window.Modernizr = Modernizr;
1745
1746
1747 ;
1748
1749 })(window, document);