· 8 years ago · Jan 19, 2018, 04:12 PM
1/*!
2 * modernizr v3.5.0
3 * Build https://modernizr.com/download?-MessageChannel-adownload-ambientlight-animation-apng-appearance-applicationcache-audio-audioloop-audiopreload-backdropfilter-backgroundblendmode-backgroundcliptext-backgroundsize-batteryapi-bdi-beacon-bgpositionshorthand-bgpositionxy-bgrepeatspace_bgrepeatround-bgsizecover-blobconstructor-bloburls-blobworkers-borderimage-borderradius-boxshadow-boxsizing-canvas-canvasblending-canvastext-canvaswinding-capture-checked-classlist-contains-contenteditable-contextmenu-cookies-cors-createelementattrs_createelement_attrs-cryptography-cssall-cssanimations-csscalc-csschunit-csscolumns-cssescape-cssexunit-cssfilters-cssgradients-cssgrid_cssgridlegacy-csshyphens_softhyphens_softhyphensfind-cssinvalid-cssmask-csspointerevents-csspositionsticky-csspseudoanimations-csspseudotransitions-cssreflections-cssremunit-cssresize-cssscrollbar-csstransforms-csstransforms3d-csstransformslevel2-csstransitions-cssvalid-cssvhunit-cssvmaxunit-cssvminunit-cssvwunit-cubicbezierrange-customelements-customevent-customprotocolhandler-dart-datachannel-datalistelem-dataset-datauri-dataview-dataworkers-details-devicemotion_deviceorientation-directory-display_runin-displaytable-documentfragment-ellipsis-emoji-es5-es5array-es5date-es5function-es5object-es5string-es5syntax-es5undefined-es6array-es6collections-es6math-es6number-es6object-es6string-eventlistener-eventsource-exiforientation-fetch-fileinput-filereader-filesystem-flash-flexbox-flexboxlegacy-flexboxtweener-flexwrap-fontface-formattribute-formvalidation-framed-fullscreen-gamepads-generatedcontent-generators-geolocation-getrandomvalues-getusermedia-hairline-hashchange-hidden-hiddenscroll-history-hovermq-hsla-htmlimports-ie8compat-imgcrossorigin-indexeddb-indexeddbblob-inlinesvg-input-inputformaction-inputformenctype-inputformmethod-inputformtarget-inputtypes-intl-jpeg2000-jpegxr-json-lastchild-ligatures-localizednumber-localstorage-lowbandwidth-lowbattery-mathml-mediaqueries-microdata-multiplebgs-mutationobserver-notification-nthchild-objectfit-olreversed-oninput-opacity-outputelem-overflowscrolling-pagevisibility-passiveeventlisteners-peerconnection-performance-picture-placeholder-pointerevents-pointerlock-pointermq-postmessage-preserve3d-progressbar_meter-promises-proximity-queryselector-quotamanagement-regions-requestanimationframe-requestautocomplete-rgba-ruby-sandbox-scriptasync-scriptdefer-scrollsnappoints-seamless-search-serviceworker-sessionstorage-shapes-sharedworkers-siblinggeneral-sizes-smil-speechrecognition-speechsynthesis-srcdoc-srcset-strictmode-stylescoped-subpixelfont-supports-svg-svgasimg-svgclippaths-svgfilters-svgforeignobject-target-template-templatestrings-textalignlast-textareamaxlength-textshadow-texttrackapi_track-time-todataurljpeg_todataurlpng_todataurlwebp-touchevents-transferables-typedarrays-unicode-unicoderange-unknownelements-urlparser-urlsearchparams-userdata-userselect-variablefonts-vibrate-video-videoautoplay-videocrossorigin-videoloop-videopreload-vml-webaudio-webgl-webglextensions-webintents-webp-webpalpha-webpanimation-webplossless_webp_lossless-websockets-websocketsbinary-websqldatabase-webworkers-willchange-wrapflow-xdomainrequest-xhr2-xhrresponsetype-xhrresponsetypearraybuffer-xhrresponsetypeblob-xhrresponsetypedocument-xhrresponsetypejson-xhrresponsetypetext-addtest-atrule-domprefixes-hasevent-load-mq-prefixed-prefixedcss-prefixes-printshiv-setclasses-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 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.5.0',
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 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
163 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
164 }
165
166 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result;
167 }
168
169 classes.push((result ? '' : 'no-') + featureNameSplit.join('-'));
170 }
171 }
172 }
173 }
174 ;
175
176 /**
177 * docElement is a convenience wrapper to grab the root element of the document
178 *
179 * @access private
180 * @returns {HTMLElement|SVGElement} The root element of the document
181 */
182
183 var docElement = document.documentElement;
184
185
186 /**
187 * A convenience helper to check if the document we are running in is an SVG document
188 *
189 * @access private
190 * @returns {boolean}
191 */
192
193 var isSVG = docElement.nodeName.toLowerCase() === 'svg';
194
195
196 /**
197 * setClasses takes an array of class names and adds them to the root element
198 *
199 * @access private
200 * @function setClasses
201 * @param {string[]} classes - Array of class names
202 */
203
204 // Pass in an and array of class names, e.g.:
205 // ['no-webp', 'borderradius', ...]
206 function setClasses(classes) {
207 var className = docElement.className;
208 var classPrefix = Modernizr._config.classPrefix || '';
209
210 if (isSVG) {
211 className = className.baseVal;
212 }
213
214 // Change `no-js` to `js` (independently of the `enableClasses` option)
215 // Handle classPrefix on this too
216 if (Modernizr._config.enableJSClass) {
217 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)');
218 className = className.replace(reJS, '$1' + classPrefix + 'js$2');
219 }
220
221 if (Modernizr._config.enableClasses) {
222 // Add the new classes
223 className += ' ' + classPrefix + classes.join(' ' + classPrefix);
224 if (isSVG) {
225 docElement.className.baseVal = className;
226 } else {
227 docElement.className = className;
228 }
229 }
230
231 }
232
233 ;
234
235 /**
236 * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support
237 *
238 * @author kangax
239 * @access private
240 * @function hasOwnProp
241 * @param {object} object - The object to check for a property
242 * @param {string} property - The property to check for
243 * @returns {boolean}
244 */
245
246 // hasOwnProperty shim by kangax needed for Safari 2.0 support
247 var hasOwnProp;
248
249 (function() {
250 var _hasOwnProperty = ({}).hasOwnProperty;
251 /* istanbul ignore else */
252 /* we have no way of testing IE 5.5 or safari 2,
253 * so just assume the else gets hit */
254 if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) {
255 hasOwnProp = function(object, property) {
256 return _hasOwnProperty.call(object, property);
257 };
258 }
259 else {
260 hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
261 return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
262 };
263 }
264 })();
265
266
267
268
269 // _l tracks listeners for async tests, as well as tests that execute after the initial run
270 ModernizrProto._l = {};
271
272 /**
273 * Modernizr.on is a way to listen for the completion of async tests. Being
274 * asynchronous, they may not finish before your scripts run. As a result you
275 * will get a possibly false negative `undefined` value.
276 *
277 * @memberof Modernizr
278 * @name Modernizr.on
279 * @access public
280 * @function on
281 * @param {string} feature - String name of the feature detect
282 * @param {function} cb - Callback function returning a Boolean - true if feature is supported, false if not
283 * @example
284 *
285 * ```js
286 * Modernizr.on('flash', function( result ) {
287 * if (result) {
288 * // the browser has flash
289 * } else {
290 * // the browser does not have flash
291 * }
292 * });
293 * ```
294 */
295
296 ModernizrProto.on = function(feature, cb) {
297 // Create the list of listeners if it doesn't exist
298 if (!this._l[feature]) {
299 this._l[feature] = [];
300 }
301
302 // Push this test on to the listener list
303 this._l[feature].push(cb);
304
305 // If it's already been resolved, trigger it on next tick
306 if (Modernizr.hasOwnProperty(feature)) {
307 // Next Tick
308 setTimeout(function() {
309 Modernizr._trigger(feature, Modernizr[feature]);
310 }, 0);
311 }
312 };
313
314 /**
315 * _trigger is the private function used to signal test completion and run any
316 * callbacks registered through [Modernizr.on](#modernizr-on)
317 *
318 * @memberof Modernizr
319 * @name Modernizr._trigger
320 * @access private
321 * @function _trigger
322 * @param {string} feature - string name of the feature detect
323 * @param {function|boolean} [res] - A feature detection function, or the boolean =
324 * result of a feature detection function
325 */
326
327 ModernizrProto._trigger = function(feature, res) {
328 if (!this._l[feature]) {
329 return;
330 }
331
332 var cbs = this._l[feature];
333
334 // Force async
335 setTimeout(function() {
336 var i, cb;
337 for (i = 0; i < cbs.length; i++) {
338 cb = cbs[i];
339 cb(res);
340 }
341 }, 0);
342
343 // Don't trigger these again
344 delete this._l[feature];
345 };
346
347 /**
348 * addTest allows you to define your own feature detects that are not currently
349 * included in Modernizr (under the covers it's the exact same code Modernizr
350 * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result
351 * will be added onto the Modernizr object, as well as an appropriate className set on
352 * the html element when configured to do so
353 *
354 * @memberof Modernizr
355 * @name Modernizr.addTest
356 * @optionName Modernizr.addTest()
357 * @optionProp addTest
358 * @access public
359 * @function addTest
360 * @param {string|object} feature - The string name of the feature detect, or an
361 * object of feature detect names and test
362 * @param {function|boolean} test - Function returning true if feature is supported,
363 * false if not. Otherwise a boolean representing the results of a feature detection
364 * @example
365 *
366 * The most common way of creating your own feature detects is by calling
367 * `Modernizr.addTest` with a string (preferably just lowercase, without any
368 * punctuation), and a function you want executed that will return a boolean result
369 *
370 * ```js
371 * Modernizr.addTest('itsTuesday', function() {
372 * var d = new Date();
373 * return d.getDay() === 2;
374 * });
375 * ```
376 *
377 * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday,
378 * and to `false` every other day of the week. One thing to notice is that the names of
379 * feature detect functions are always lowercased when added to the Modernizr object. That
380 * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will.
381 *
382 *
383 * Since we only look at the returned value from any feature detection function,
384 * you do not need to actually use a function. For simple detections, just passing
385 * in a statement that will return a boolean value works just fine.
386 *
387 * ```js
388 * Modernizr.addTest('hasJquery', 'jQuery' in window);
389 * ```
390 *
391 * Just like before, when the above runs `Modernizr.hasjquery` will be true if
392 * jQuery has been included on the page. Not using a function saves a small amount
393 * of overhead for the browser, as well as making your code much more readable.
394 *
395 * Finally, you also have the ability to pass in an object of feature names and
396 * their tests. This is handy if you want to add multiple detections in one go.
397 * The keys should always be a string, and the value can be either a boolean or
398 * function that returns a boolean.
399 *
400 * ```js
401 * var detects = {
402 * 'hasjquery': 'jQuery' in window,
403 * 'itstuesday': function() {
404 * var d = new Date();
405 * return d.getDay() === 2;
406 * }
407 * }
408 *
409 * Modernizr.addTest(detects);
410 * ```
411 *
412 * There is really no difference between the first methods and this one, it is
413 * just a convenience to let you write more readable code.
414 */
415
416 function addTest(feature, test) {
417
418 if (typeof feature == 'object') {
419 for (var key in feature) {
420 if (hasOwnProp(feature, key)) {
421 addTest(key, feature[ key ]);
422 }
423 }
424 } else {
425
426 feature = feature.toLowerCase();
427 var featureNameSplit = feature.split('.');
428 var last = Modernizr[featureNameSplit[0]];
429
430 // Again, we don't check for parent test existence. Get that right, though.
431 if (featureNameSplit.length == 2) {
432 last = last[featureNameSplit[1]];
433 }
434
435 if (typeof last != 'undefined') {
436 // we're going to quit if you're trying to overwrite an existing test
437 // if we were to allow it, we'd do this:
438 // var re = new RegExp("\\b(no-)?" + feature + "\\b");
439 // docElement.className = docElement.className.replace( re, '' );
440 // but, no rly, stuff 'em.
441 return Modernizr;
442 }
443
444 test = typeof test == 'function' ? test() : test;
445
446 // Set the value (this is the magic, right here).
447 if (featureNameSplit.length == 1) {
448 Modernizr[featureNameSplit[0]] = test;
449 } else {
450 // cast to a Boolean, if not one already
451 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
452 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
453 }
454
455 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test;
456 }
457
458 // Set a single class (either `feature` or `no-feature`)
459 setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]);
460
461 // Trigger the event
462 Modernizr._trigger(feature, test);
463 }
464
465 return Modernizr; // allow chaining.
466 }
467
468 // After all the tests are run, add self to the Modernizr prototype
469 Modernizr._q.push(function() {
470 ModernizrProto.addTest = addTest;
471 });
472
473
474
475
476 /**
477 * If the browsers follow the spec, then they would expose vendor-specific styles as:
478 * elem.style.WebkitBorderRadius
479 * instead of something like the following (which is technically incorrect):
480 * elem.style.webkitBorderRadius
481
482 * WebKit ghosts their properties in lowercase but Opera & Moz do not.
483 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
484 * erik.eae.net/archives/2008/03/10/21.48.10/
485
486 * More here: github.com/Modernizr/Modernizr/issues/issue/21
487 *
488 * @access private
489 * @returns {string} The string representing the vendor-specific style properties
490 */
491
492 var omPrefixes = 'Moz O ms Webkit';
493
494
495 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []);
496 ModernizrProto._cssomPrefixes = cssomPrefixes;
497
498
499 /**
500 * atRule returns a given CSS property at-rule (eg @keyframes), possibly in
501 * some prefixed form, or false, in the case of an unsupported rule
502 *
503 * @memberof Modernizr
504 * @name Modernizr.atRule
505 * @optionName Modernizr.atRule()
506 * @optionProp atRule
507 * @access public
508 * @function atRule
509 * @param {string} prop - String name of the @-rule to test for
510 * @returns {string|boolean} The string representing the (possibly prefixed)
511 * valid version of the @-rule, or `false` when it is unsupported.
512 * @example
513 * ```js
514 * var keyframes = Modernizr.atRule('@keyframes');
515 *
516 * if (keyframes) {
517 * // keyframes are supported
518 * // could be `@-webkit-keyframes` or `@keyframes`
519 * } else {
520 * // keyframes === `false`
521 * }
522 * ```
523 *
524 */
525
526 var atRule = function(prop) {
527 var length = prefixes.length;
528 var cssrule = window.CSSRule;
529 var rule;
530
531 if (typeof cssrule === 'undefined') {
532 return undefined;
533 }
534
535 if (!prop) {
536 return false;
537 }
538
539 // remove literal @ from beginning of provided property
540 prop = prop.replace(/^@/, '');
541
542 // CSSRules use underscores instead of dashes
543 rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE';
544
545 if (rule in cssrule) {
546 return '@' + prop;
547 }
548
549 for (var i = 0; i < length; i++) {
550 // prefixes gives us something like -o-, and we want O_
551 var prefix = prefixes[i];
552 var thisRule = prefix.toUpperCase() + '_' + rule;
553
554 if (thisRule in cssrule) {
555 return '@-' + prefix.toLowerCase() + '-' + prop;
556 }
557 }
558
559 return false;
560 };
561
562 ModernizrProto.atRule = atRule;
563
564
565
566 /**
567 * List of JavaScript DOM values used for tests
568 *
569 * @memberof Modernizr
570 * @name Modernizr._domPrefixes
571 * @optionName Modernizr._domPrefixes
572 * @optionProp domPrefixes
573 * @access public
574 * @example
575 *
576 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather
577 * than kebab-case properties, all properties are their Capitalized variant
578 *
579 * ```js
580 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ];
581 * ```
582 */
583
584 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []);
585 ModernizrProto._domPrefixes = domPrefixes;
586
587
588 /**
589 * createElement is a convenience wrapper around document.createElement. Since we
590 * use createElement all over the place, this allows for (slightly) smaller code
591 * as well as abstracting away issues with creating elements in contexts other than
592 * HTML documents (e.g. SVG documents).
593 *
594 * @access private
595 * @function createElement
596 * @returns {HTMLElement|SVGElement} An HTML or SVG element
597 */
598
599 function createElement() {
600 if (typeof document.createElement !== 'function') {
601 // This is the case in IE7, where the type of createElement is "object".
602 // For this reason, we cannot call apply() as Object is not a Function.
603 return document.createElement(arguments[0]);
604 } else if (isSVG) {
605 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]);
606 } else {
607 return document.createElement.apply(document, arguments);
608 }
609 }
610
611 ;
612
613 /**
614 * Modernizr.hasEvent() detects support for a given event
615 *
616 * @memberof Modernizr
617 * @name Modernizr.hasEvent
618 * @optionName Modernizr.hasEvent()
619 * @optionProp hasEvent
620 * @access public
621 * @function hasEvent
622 * @param {string|*} eventName - the name of an event to test for (e.g. "resize")
623 * @param {Element|string} [element=HTMLDivElement] - is the element|document|window|tagName to test on
624 * @returns {boolean}
625 * @example
626 * `Modernizr.hasEvent` lets you determine if the browser supports a supplied event.
627 * By default, it does this detection on a div element
628 *
629 * ```js
630 * hasEvent('blur') // true;
631 * ```
632 *
633 * However, you are able to give an object as a second argument to hasEvent to
634 * detect an event on something other than a div.
635 *
636 * ```js
637 * hasEvent('devicelight', window) // true;
638 * ```
639 *
640 */
641
642 var hasEvent = (function() {
643
644 // Detect whether event support can be detected via `in`. Test on a DOM element
645 // using the "blur" event b/c it should always exist. bit.ly/event-detection
646 var needsFallback = !('onblur' in document.documentElement);
647
648 function inner(eventName, element) {
649
650 var isSupported;
651 if (!eventName) { return false; }
652 if (!element || typeof element === 'string') {
653 element = createElement(element || 'div');
654 }
655
656 // Testing via the `in` operator is sufficient for modern browsers and IE.
657 // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and
658 // "resize", whereas `in` "catches" those.
659 eventName = 'on' + eventName;
660 isSupported = eventName in element;
661
662 // Fallback technique for old Firefox - bit.ly/event-detection
663 if (!isSupported && needsFallback) {
664 if (!element.setAttribute) {
665 // Switch to generic element if it lacks `setAttribute`.
666 // It could be the `document`, `window`, or something else.
667 element = createElement('div');
668 }
669
670 element.setAttribute(eventName, '');
671 isSupported = typeof element[eventName] === 'function';
672
673 if (element[eventName] !== undefined) {
674 // If property was created, "remove it" by setting value to `undefined`.
675 element[eventName] = undefined;
676 }
677 element.removeAttribute(eventName);
678 }
679
680 return isSupported;
681 }
682 return inner;
683 })();
684
685
686 ModernizrProto.hasEvent = hasEvent;
687
688
689/**
690 * @optionName html5printshiv
691 * @optionProp html5printshiv
692 */
693
694 // Take the html5 variable out of the html5shiv scope so we can return it.
695 var html5;
696 if (!isSVG) {
697
698 /**
699 * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
700 */
701 ;(function(window, document) {
702 /** version */
703 var version = '3.7.3';
704
705 /** Preset options */
706 var options = window.html5 || {};
707
708 /** Used to skip problem elements */
709 var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
710
711 /** Not all elements can be cloned in IE **/
712 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;
713
714 /** Detect whether the browser supports default html5 styles */
715 var supportsHtml5Styles;
716
717 /** Name of the expando, to work with multiple documents or to re-shiv one document */
718 var expando = '_html5shiv';
719
720 /** The id for the the documents expando */
721 var expanID = 0;
722
723 /** Cached data for each document */
724 var expandoData = {};
725
726 /** Detect whether the browser supports unknown elements */
727 var supportsUnknownElements;
728
729 (function() {
730 try {
731 var a = document.createElement('a');
732 a.innerHTML = '<xyz></xyz>';
733 //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
734 supportsHtml5Styles = ('hidden' in a);
735
736 supportsUnknownElements = a.childNodes.length == 1 || (function() {
737 // assign a false positive if unable to shiv
738 (document.createElement)('a');
739 var frag = document.createDocumentFragment();
740 return (
741 typeof frag.cloneNode == 'undefined' ||
742 typeof frag.createDocumentFragment == 'undefined' ||
743 typeof frag.createElement == 'undefined'
744 );
745 }());
746 } catch(e) {
747 // assign a false positive if detection fails => unable to shiv
748 supportsHtml5Styles = true;
749 supportsUnknownElements = true;
750 }
751
752 }());
753
754 /*--------------------------------------------------------------------------*/
755
756 /**
757 * Creates a style sheet with the given CSS text and adds it to the document.
758 * @private
759 * @param {Document} ownerDocument The document.
760 * @param {String} cssText The CSS text.
761 * @returns {StyleSheet} The style element.
762 */
763 function addStyleSheet(ownerDocument, cssText) {
764 var p = ownerDocument.createElement('p'),
765 parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
766
767 p.innerHTML = 'x<style>' + cssText + '</style>';
768 return parent.insertBefore(p.lastChild, parent.firstChild);
769 }
770
771 /**
772 * Returns the value of `html5.elements` as an array.
773 * @private
774 * @returns {Array} An array of shived element node names.
775 */
776 function getElements() {
777 var elements = html5.elements;
778 return typeof elements == 'string' ? elements.split(' ') : elements;
779 }
780
781 /**
782 * Extends the built-in list of html5 elements
783 * @memberOf html5
784 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv
785 * @param {Document} ownerDocument The context document.
786 */
787 function addElements(newElements, ownerDocument) {
788 var elements = html5.elements;
789 if(typeof elements != 'string'){
790 elements = elements.join(' ');
791 }
792 if(typeof newElements != 'string'){
793 newElements = newElements.join(' ');
794 }
795 html5.elements = elements +' '+ newElements;
796 shivDocument(ownerDocument);
797 }
798
799 /**
800 * Returns the data associated to the given document
801 * @private
802 * @param {Document} ownerDocument The document.
803 * @returns {Object} An object of data.
804 */
805 function getExpandoData(ownerDocument) {
806 var data = expandoData[ownerDocument[expando]];
807 if (!data) {
808 data = {};
809 expanID++;
810 ownerDocument[expando] = expanID;
811 expandoData[expanID] = data;
812 }
813 return data;
814 }
815
816 /**
817 * returns a shived element for the given nodeName and document
818 * @memberOf html5
819 * @param {String} nodeName name of the element
820 * @param {Document} ownerDocument The context document.
821 * @returns {Object} The shived element.
822 */
823 function createElement(nodeName, ownerDocument, data){
824 if (!ownerDocument) {
825 ownerDocument = document;
826 }
827 if(supportsUnknownElements){
828 return ownerDocument.createElement(nodeName);
829 }
830 if (!data) {
831 data = getExpandoData(ownerDocument);
832 }
833 var node;
834
835 if (data.cache[nodeName]) {
836 node = data.cache[nodeName].cloneNode();
837 } else if (saveClones.test(nodeName)) {
838 node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
839 } else {
840 node = data.createElem(nodeName);
841 }
842
843 // Avoid adding some elements to fragments in IE < 9 because
844 // * Attributes like `name` or `type` cannot be set/changed once an element
845 // is inserted into a document/fragment
846 // * Link elements with `src` attributes that are inaccessible, as with
847 // a 403 response, will cause the tab/window to crash
848 // * Script elements appended to fragments will execute when their `src`
849 // or `text` property is set
850 return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
851 }
852
853 /**
854 * returns a shived DocumentFragment for the given document
855 * @memberOf html5
856 * @param {Document} ownerDocument The context document.
857 * @returns {Object} The shived DocumentFragment.
858 */
859 function createDocumentFragment(ownerDocument, data){
860 if (!ownerDocument) {
861 ownerDocument = document;
862 }
863 if(supportsUnknownElements){
864 return ownerDocument.createDocumentFragment();
865 }
866 data = data || getExpandoData(ownerDocument);
867 var clone = data.frag.cloneNode(),
868 i = 0,
869 elems = getElements(),
870 l = elems.length;
871 for(;i<l;i++){
872 clone.createElement(elems[i]);
873 }
874 return clone;
875 }
876
877 /**
878 * Shivs the `createElement` and `createDocumentFragment` methods of the document.
879 * @private
880 * @param {Document|DocumentFragment} ownerDocument The document.
881 * @param {Object} data of the document.
882 */
883 function shivMethods(ownerDocument, data) {
884 if (!data.cache) {
885 data.cache = {};
886 data.createElem = ownerDocument.createElement;
887 data.createFrag = ownerDocument.createDocumentFragment;
888 data.frag = data.createFrag();
889 }
890
891
892 ownerDocument.createElement = function(nodeName) {
893 //abort shiv
894 if (!html5.shivMethods) {
895 return data.createElem(nodeName);
896 }
897 return createElement(nodeName, ownerDocument, data);
898 };
899
900 ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
901 'var n=f.cloneNode(),c=n.createElement;' +
902 'h.shivMethods&&(' +
903 // unroll the `createElement` calls
904 getElements().join().replace(/[\w\-:]+/g, function(nodeName) {
905 data.createElem(nodeName);
906 data.frag.createElement(nodeName);
907 return 'c("' + nodeName + '")';
908 }) +
909 ');return n}'
910 )(html5, data.frag);
911 }
912
913 /*--------------------------------------------------------------------------*/
914
915 /**
916 * Shivs the given document.
917 * @memberOf html5
918 * @param {Document} ownerDocument The document to shiv.
919 * @returns {Document} The shived document.
920 */
921 function shivDocument(ownerDocument) {
922 if (!ownerDocument) {
923 ownerDocument = document;
924 }
925 var data = getExpandoData(ownerDocument);
926
927 if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
928 data.hasCSS = !!addStyleSheet(ownerDocument,
929 // corrects block display not defined in IE6/7/8/9
930 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
931 // adds styling not present in IE6/7/8/9
932 'mark{background:#FF0;color:#000}' +
933 // hides non-rendered elements
934 'template{display:none}'
935 );
936 }
937 if (!supportsUnknownElements) {
938 shivMethods(ownerDocument, data);
939 }
940 return ownerDocument;
941 }
942
943 /*--------------------------------------------------------------------------*/
944
945 /**
946 * The `html5` object is exposed so that more elements can be shived and
947 * existing shiving can be detected on iframes.
948 * @type Object
949 * @example
950 *
951 * // options can be changed before the script is included
952 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
953 */
954 var html5 = {
955
956 /**
957 * An array or space separated string of node names of the elements to shiv.
958 * @memberOf html5
959 * @type Array|String
960 */
961 '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',
962
963 /**
964 * current version of html5shiv
965 */
966 'version': version,
967
968 /**
969 * A flag to indicate that the HTML5 style sheet should be inserted.
970 * @memberOf html5
971 * @type Boolean
972 */
973 'shivCSS': (options.shivCSS !== false),
974
975 /**
976 * Is equal to true if a browser supports creating unknown/HTML5 elements
977 * @memberOf html5
978 * @type boolean
979 */
980 'supportsUnknownElements': supportsUnknownElements,
981
982 /**
983 * A flag to indicate that the document's `createElement` and `createDocumentFragment`
984 * methods should be overwritten.
985 * @memberOf html5
986 * @type Boolean
987 */
988 'shivMethods': (options.shivMethods !== false),
989
990 /**
991 * A string to describe the type of `html5` object ("default" or "default print").
992 * @memberOf html5
993 * @type String
994 */
995 'type': 'default',
996
997 // shivs the document according to the specified `html5` object options
998 'shivDocument': shivDocument,
999
1000 //creates a shived element
1001 createElement: createElement,
1002
1003 //creates a shived documentFragment
1004 createDocumentFragment: createDocumentFragment,
1005
1006 //extends list of elements
1007 addElements: addElements
1008 };
1009
1010 /*--------------------------------------------------------------------------*/
1011
1012 // expose html5
1013 window.html5 = html5;
1014
1015 // shiv the document
1016 shivDocument(document);
1017
1018 /*------------------------------- Print Shiv -------------------------------*/
1019
1020 /** Used to filter media types */
1021 var reMedia = /^$|\b(?:all|print)\b/;
1022
1023 /** Used to namespace printable elements */
1024 var shivNamespace = 'html5shiv';
1025
1026 /** Detect whether the browser supports shivable style sheets */
1027 var supportsShivableSheets = !supportsUnknownElements && (function() {
1028 // assign a false negative if unable to shiv
1029 var docEl = document.documentElement;
1030 return !(
1031 typeof document.namespaces == 'undefined' ||
1032 typeof document.parentWindow == 'undefined' ||
1033 typeof docEl.applyElement == 'undefined' ||
1034 typeof docEl.removeNode == 'undefined' ||
1035 typeof window.attachEvent == 'undefined'
1036 );
1037 }());
1038
1039 /*--------------------------------------------------------------------------*/
1040
1041 /**
1042 * Wraps all HTML5 elements in the given document with printable elements.
1043 * (eg. the "header" element is wrapped with the "html5shiv:header" element)
1044 * @private
1045 * @param {Document} ownerDocument The document.
1046 * @returns {Array} An array wrappers added.
1047 */
1048 function addWrappers(ownerDocument) {
1049 var node,
1050 nodes = ownerDocument.getElementsByTagName('*'),
1051 index = nodes.length,
1052 reElements = RegExp('^(?:' + getElements().join('|') + ')$', 'i'),
1053 result = [];
1054
1055 while (index--) {
1056 node = nodes[index];
1057 if (reElements.test(node.nodeName)) {
1058 result.push(node.applyElement(createWrapper(node)));
1059 }
1060 }
1061 return result;
1062 }
1063
1064 /**
1065 * Creates a printable wrapper for the given element.
1066 * @private
1067 * @param {Element} element The element.
1068 * @returns {Element} The wrapper.
1069 */
1070 function createWrapper(element) {
1071 var node,
1072 nodes = element.attributes,
1073 index = nodes.length,
1074 wrapper = element.ownerDocument.createElement(shivNamespace + ':' + element.nodeName);
1075
1076 // copy element attributes to the wrapper
1077 while (index--) {
1078 node = nodes[index];
1079 node.specified && wrapper.setAttribute(node.nodeName, node.nodeValue);
1080 }
1081 // copy element styles to the wrapper
1082 wrapper.style.cssText = element.style.cssText;
1083 return wrapper;
1084 }
1085
1086 /**
1087 * Shivs the given CSS text.
1088 * (eg. header{} becomes html5shiv\:header{})
1089 * @private
1090 * @param {String} cssText The CSS text to shiv.
1091 * @returns {String} The shived CSS text.
1092 */
1093 function shivCssText(cssText) {
1094 var pair,
1095 parts = cssText.split('{'),
1096 index = parts.length,
1097 reElements = RegExp('(^|[\\s,>+~])(' + getElements().join('|') + ')(?=[[\\s,>+~#.:]|$)', 'gi'),
1098 replacement = '$1' + shivNamespace + '\\:$2';
1099
1100 while (index--) {
1101 pair = parts[index] = parts[index].split('}');
1102 pair[pair.length - 1] = pair[pair.length - 1].replace(reElements, replacement);
1103 parts[index] = pair.join('}');
1104 }
1105 return parts.join('{');
1106 }
1107
1108 /**
1109 * Removes the given wrappers, leaving the original elements.
1110 * @private
1111 * @params {Array} wrappers An array of printable wrappers.
1112 */
1113 function removeWrappers(wrappers) {
1114 var index = wrappers.length;
1115 while (index--) {
1116 wrappers[index].removeNode();
1117 }
1118 }
1119
1120 /*--------------------------------------------------------------------------*/
1121
1122 /**
1123 * Shivs the given document for print.
1124 * @memberOf html5
1125 * @param {Document} ownerDocument The document to shiv.
1126 * @returns {Document} The shived document.
1127 */
1128 function shivPrint(ownerDocument) {
1129 var shivedSheet,
1130 wrappers,
1131 data = getExpandoData(ownerDocument),
1132 namespaces = ownerDocument.namespaces,
1133 ownerWindow = ownerDocument.parentWindow;
1134
1135 if (!supportsShivableSheets || ownerDocument.printShived) {
1136 return ownerDocument;
1137 }
1138 if (typeof namespaces[shivNamespace] == 'undefined') {
1139 namespaces.add(shivNamespace);
1140 }
1141
1142 function removeSheet() {
1143 clearTimeout(data._removeSheetTimer);
1144 if (shivedSheet) {
1145 shivedSheet.removeNode(true);
1146 }
1147 shivedSheet= null;
1148 }
1149
1150 ownerWindow.attachEvent('onbeforeprint', function() {
1151
1152 removeSheet();
1153
1154 var imports,
1155 length,
1156 sheet,
1157 collection = ownerDocument.styleSheets,
1158 cssText = [],
1159 index = collection.length,
1160 sheets = Array(index);
1161
1162 // convert styleSheets collection to an array
1163 while (index--) {
1164 sheets[index] = collection[index];
1165 }
1166 // concat all style sheet CSS text
1167 while ((sheet = sheets.pop())) {
1168 // IE does not enforce a same origin policy for external style sheets...
1169 // but has trouble with some dynamically created stylesheets
1170 if (!sheet.disabled && reMedia.test(sheet.media)) {
1171
1172 try {
1173 imports = sheet.imports;
1174 length = imports.length;
1175 } catch(er){
1176 length = 0;
1177 }
1178
1179 for (index = 0; index < length; index++) {
1180 sheets.push(imports[index]);
1181 }
1182
1183 try {
1184 cssText.push(sheet.cssText);
1185 } catch(er){}
1186 }
1187 }
1188
1189 // wrap all HTML5 elements with printable elements and add the shived style sheet
1190 cssText = shivCssText(cssText.reverse().join(''));
1191 wrappers = addWrappers(ownerDocument);
1192 shivedSheet = addStyleSheet(ownerDocument, cssText);
1193
1194 });
1195
1196 ownerWindow.attachEvent('onafterprint', function() {
1197 // remove wrappers, leaving the original elements, and remove the shived style sheet
1198 removeWrappers(wrappers);
1199 clearTimeout(data._removeSheetTimer);
1200 data._removeSheetTimer = setTimeout(removeSheet, 500);
1201 });
1202
1203 ownerDocument.printShived = true;
1204 return ownerDocument;
1205 }
1206
1207 /*--------------------------------------------------------------------------*/
1208
1209 // expose API
1210 html5.type += ' print';
1211 html5.shivPrint = shivPrint;
1212
1213 // shiv for print
1214 shivPrint(document);
1215
1216 if(typeof module == 'object' && module.exports){
1217 module.exports = html5;
1218 }
1219
1220 }(typeof window !== 'undefined' ? window : this, document));
1221 }
1222
1223 ;
1224
1225 /**
1226 * Previously, Modernizr.load was an alias for yepnope. Since yepnope was
1227 * deprecated, we removed it as well. It is not available on the website builder,
1228 * this is only included as an improved warning to those who build a custom
1229 * version locally.
1230 *
1231 * @memberof Modernizr
1232 * @name Modernizr.load
1233 * @access private
1234 * @function load
1235 *
1236 */
1237
1238 var err = function() {};
1239 var warn = function() {};
1240
1241 if (window.console) {
1242 err = function() {
1243 var method = console.error ? 'error' : 'log';
1244 window.console[method].apply(window.console, Array.prototype.slice.call(arguments));
1245 };
1246
1247 warn = function() {
1248 var method = console.warn ? 'warn' : 'log';
1249 window.console[method].apply(window.console, Array.prototype.slice.call(arguments));
1250 };
1251 }
1252
1253 ModernizrProto.load = function() {
1254 if ('yepnope' in window) {
1255 warn('yepnope.js (aka Modernizr.load) is no longer included as part of Modernizr. yepnope appears to be available on the page, so we?ll use it to handle this call to Modernizr.load, but please update your code to use yepnope directly.\n See http://github.com/Modernizr/Modernizr/issues/1182 for more information.');
1256 window.yepnope.apply(window, [].slice.call(arguments, 0));
1257 } else {
1258 err('yepnope.js (aka Modernizr.load) is no longer included as part of Modernizr. Get it from http://yepnopejs.com. See http://github.com/Modernizr/Modernizr/issues/1182 for more information.');
1259 }
1260 };
1261
1262
1263
1264 /**
1265 * getBody returns the body of a document, or an element that can stand in for
1266 * the body if a real body does not exist
1267 *
1268 * @access private
1269 * @function getBody
1270 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an
1271 * artificially created element that stands in for the body
1272 */
1273
1274 function getBody() {
1275 // After page load injecting a fake body doesn't work so check if body exists
1276 var body = document.body;
1277
1278 if (!body) {
1279 // Can't use the real body create a fake one.
1280 body = createElement(isSVG ? 'svg' : 'body');
1281 body.fake = true;
1282 }
1283
1284 return body;
1285 }
1286
1287 ;
1288
1289 /**
1290 * injectElementWithStyles injects an element with style element and some CSS rules
1291 *
1292 * @access private
1293 * @function injectElementWithStyles
1294 * @param {string} rule - String representing a css rule
1295 * @param {function} callback - A function that is used to test the injected element
1296 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected
1297 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
1298 * @returns {boolean}
1299 */
1300
1301 function injectElementWithStyles(rule, callback, nodes, testnames) {
1302 var mod = 'modernizr';
1303 var style;
1304 var ret;
1305 var node;
1306 var docOverflow;
1307 var div = createElement('div');
1308 var body = getBody();
1309
1310 if (parseInt(nodes, 10)) {
1311 // In order not to give false positives we create a node for each test
1312 // This also allows the method to scale for unspecified uses
1313 while (nodes--) {
1314 node = createElement('div');
1315 node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
1316 div.appendChild(node);
1317 }
1318 }
1319
1320 style = createElement('style');
1321 style.type = 'text/css';
1322 style.id = 's' + mod;
1323
1324 // 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.
1325 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
1326 (!body.fake ? div : body).appendChild(style);
1327 body.appendChild(div);
1328
1329 if (style.styleSheet) {
1330 style.styleSheet.cssText = rule;
1331 } else {
1332 style.appendChild(document.createTextNode(rule));
1333 }
1334 div.id = mod;
1335
1336 if (body.fake) {
1337 //avoid crashing IE8, if background image is used
1338 body.style.background = '';
1339 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
1340 body.style.overflow = 'hidden';
1341 docOverflow = docElement.style.overflow;
1342 docElement.style.overflow = 'hidden';
1343 docElement.appendChild(body);
1344 }
1345
1346 ret = callback(div, rule);
1347 // If this is done after page load we don't want to remove the body so check if body exists
1348 if (body.fake) {
1349 body.parentNode.removeChild(body);
1350 docElement.style.overflow = docOverflow;
1351 // Trigger layout so kinetic scrolling isn't disabled in iOS6+
1352 // eslint-disable-next-line
1353 docElement.offsetHeight;
1354 } else {
1355 div.parentNode.removeChild(div);
1356 }
1357
1358 return !!ret;
1359
1360 }
1361
1362 ;
1363
1364 /**
1365 * Modernizr.mq tests a given media query, live against the current state of the window
1366 * adapted from matchMedia polyfill by Scott Jehl and Paul Irish
1367 * gist.github.com/786768
1368 *
1369 * @memberof Modernizr
1370 * @name Modernizr.mq
1371 * @optionName Modernizr.mq()
1372 * @optionProp mq
1373 * @access public
1374 * @function mq
1375 * @param {string} mq - String of the media query we want to test
1376 * @returns {boolean}
1377 * @example
1378 * Modernizr.mq allows for you to programmatically check if the current browser
1379 * window state matches a media query.
1380 *
1381 * ```js
1382 * var query = Modernizr.mq('(min-width: 900px)');
1383 *
1384 * if (query) {
1385 * // the browser window is larger than 900px
1386 * }
1387 * ```
1388 *
1389 * Only valid media queries are supported, therefore you must always include values
1390 * with your media query
1391 *
1392 * ```js
1393 * // good
1394 * Modernizr.mq('(min-width: 900px)');
1395 *
1396 * // bad
1397 * Modernizr.mq('min-width');
1398 * ```
1399 *
1400 * If you would just like to test that media queries are supported in general, use
1401 *
1402 * ```js
1403 * Modernizr.mq('only all'); // true if MQ are supported, false if not
1404 * ```
1405 *
1406 *
1407 * Note that if the browser does not support media queries (e.g. old IE) mq will
1408 * always return false.
1409 */
1410
1411 var mq = (function() {
1412 var matchMedia = window.matchMedia || window.msMatchMedia;
1413 if (matchMedia) {
1414 return function(mq) {
1415 var mql = matchMedia(mq);
1416 return mql && mql.matches || false;
1417 };
1418 }
1419
1420 return function(mq) {
1421 var bool = false;
1422
1423 injectElementWithStyles('@media ' + mq + ' { #modernizr { position: absolute; } }', function(node) {
1424 bool = (window.getComputedStyle ?
1425 window.getComputedStyle(node, null) :
1426 node.currentStyle).position == 'absolute';
1427 });
1428
1429 return bool;
1430 };
1431 })();
1432
1433
1434 ModernizrProto.mq = mq;
1435
1436
1437
1438
1439 /**
1440 * contains checks to see if a string contains another string
1441 *
1442 * @access private
1443 * @function contains
1444 * @param {string} str - The string we want to check for substrings
1445 * @param {string} substr - The substring we want to search the first string for
1446 * @returns {boolean}
1447 */
1448
1449 function contains(str, substr) {
1450 return !!~('' + str).indexOf(substr);
1451 }
1452
1453 ;
1454
1455 /**
1456 * Create our "modernizr" element that we do most feature tests on.
1457 *
1458 * @access private
1459 */
1460
1461 var modElem = {
1462 elem: createElement('modernizr')
1463 };
1464
1465 // Clean up this element
1466 Modernizr._q.push(function() {
1467 delete modElem.elem;
1468 });
1469
1470
1471
1472 var mStyle = {
1473 style: modElem.elem.style
1474 };
1475
1476 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to
1477 // the front of the queue.
1478 Modernizr._q.unshift(function() {
1479 delete mStyle.style;
1480 });
1481
1482
1483
1484 /**
1485 * domToCSS takes a camelCase string and converts it to kebab-case
1486 * e.g. boxSizing -> box-sizing
1487 *
1488 * @access private
1489 * @function domToCSS
1490 * @param {string} name - String name of camelCase prop we want to convert
1491 * @returns {string} The kebab-case version of the supplied name
1492 */
1493
1494 function domToCSS(name) {
1495 return name.replace(/([A-Z])/g, function(str, m1) {
1496 return '-' + m1.toLowerCase();
1497 }).replace(/^ms-/, '-ms-');
1498 }
1499 ;
1500
1501
1502 /**
1503 * wrapper around getComputedStyle, to fix issues with Firefox returning null when
1504 * called inside of a hidden iframe
1505 *
1506 * @access private
1507 * @function computedStyle
1508 * @param {HTMLElement|SVGElement} - The element we want to find the computed styles of
1509 * @param {string|null} [pseudoSelector]- An optional pseudo element selector (e.g. :before), of null if none
1510 * @returns {CSSStyleDeclaration}
1511 */
1512
1513 function computedStyle(elem, pseudo, prop) {
1514 var result;
1515
1516 if ('getComputedStyle' in window) {
1517 result = getComputedStyle.call(window, elem, pseudo);
1518 var console = window.console;
1519
1520 if (result !== null) {
1521 if (prop) {
1522 result = result.getPropertyValue(prop);
1523 }
1524 } else {
1525 if (console) {
1526 var method = console.error ? 'error' : 'log';
1527 console[method].call(console, 'getComputedStyle returning null, its possible modernizr test results are inaccurate');
1528 }
1529 }
1530 } else {
1531 result = !pseudo && elem.currentStyle && elem.currentStyle[prop];
1532 }
1533
1534 return result;
1535 }
1536
1537 ;
1538
1539 /**
1540 * nativeTestProps allows for us to use native feature detection functionality if available.
1541 * some prefixed form, or false, in the case of an unsupported rule
1542 *
1543 * @access private
1544 * @function nativeTestProps
1545 * @param {array} props - An array of property names
1546 * @param {string} value - A string representing the value we want to check via @supports
1547 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise
1548 */
1549
1550 // Accepts a list of property names and a single value
1551 // Returns `undefined` if native detection not available
1552 function nativeTestProps(props, value) {
1553 var i = props.length;
1554 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface
1555 if ('CSS' in window && 'supports' in window.CSS) {
1556 // Try every prefixed variant of the property
1557 while (i--) {
1558 if (window.CSS.supports(domToCSS(props[i]), value)) {
1559 return true;
1560 }
1561 }
1562 return false;
1563 }
1564 // Otherwise fall back to at-rule (for Opera 12.x)
1565 else if ('CSSSupportsRule' in window) {
1566 // Build a condition string for every prefixed variant
1567 var conditionText = [];
1568 while (i--) {
1569 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')');
1570 }
1571 conditionText = conditionText.join(' or ');
1572 return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) {
1573 return computedStyle(node, null, 'position') == 'absolute';
1574 });
1575 }
1576 return undefined;
1577 }
1578 ;
1579
1580 /**
1581 * cssToDOM takes a kebab-case string and converts it to camelCase
1582 * e.g. box-sizing -> boxSizing
1583 *
1584 * @access private
1585 * @function cssToDOM
1586 * @param {string} name - String name of kebab-case prop we want to convert
1587 * @returns {string} The camelCase version of the supplied name
1588 */
1589
1590 function cssToDOM(name) {
1591 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) {
1592 return m1 + m2.toUpperCase();
1593 }).replace(/^-/, '');
1594 }
1595 ;
1596
1597 // testProps is a generic CSS / DOM property test.
1598
1599 // In testing support for a given CSS property, it's legit to test:
1600 // `elem.style[styleName] !== undefined`
1601 // If the property is supported it will return an empty string,
1602 // if unsupported it will return undefined.
1603
1604 // We'll take advantage of this quick test and skip setting a style
1605 // on our modernizr element, but instead just testing undefined vs
1606 // empty string.
1607
1608 // Property names can be provided in either camelCase or kebab-case.
1609
1610 function testProps(props, prefixed, value, skipValueTest) {
1611 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest;
1612
1613 // Try native detect first
1614 if (!is(value, 'undefined')) {
1615 var result = nativeTestProps(props, value);
1616 if (!is(result, 'undefined')) {
1617 return result;
1618 }
1619 }
1620
1621 // Otherwise do it properly
1622 var afterInit, i, propsLength, prop, before;
1623
1624 // If we don't have a style element, that means we're running async or after
1625 // the core tests, so we'll need to create our own elements to use
1626
1627 // inside of an SVG element, in certain browsers, the `style` element is only
1628 // defined for valid tags. Therefore, if `modernizr` does not have one, we
1629 // fall back to a less used element and hope for the best.
1630 // for strict XHTML browsers the hardly used samp element is used
1631 var elems = ['modernizr', 'tspan', 'samp'];
1632 while (!mStyle.style && elems.length) {
1633 afterInit = true;
1634 mStyle.modElem = createElement(elems.shift());
1635 mStyle.style = mStyle.modElem.style;
1636 }
1637
1638 // Delete the objects if we created them.
1639 function cleanElems() {
1640 if (afterInit) {
1641 delete mStyle.style;
1642 delete mStyle.modElem;
1643 }
1644 }
1645
1646 propsLength = props.length;
1647 for (i = 0; i < propsLength; i++) {
1648 prop = props[i];
1649 before = mStyle.style[prop];
1650
1651 if (contains(prop, '-')) {
1652 prop = cssToDOM(prop);
1653 }
1654
1655 if (mStyle.style[prop] !== undefined) {
1656
1657 // If value to test has been passed in, do a set-and-check test.
1658 // 0 (integer) is a valid property value, so check that `value` isn't
1659 // undefined, rather than just checking it's truthy.
1660 if (!skipValueTest && !is(value, 'undefined')) {
1661
1662 // Needs a try catch block because of old IE. This is slow, but will
1663 // be avoided in most cases because `skipValueTest` will be used.
1664 try {
1665 mStyle.style[prop] = value;
1666 } catch (e) {}
1667
1668 // If the property value has changed, we assume the value used is
1669 // supported. If `value` is empty string, it'll fail here (because
1670 // it hasn't changed), which matches how browsers have implemented
1671 // CSS.supports()
1672 if (mStyle.style[prop] != before) {
1673 cleanElems();
1674 return prefixed == 'pfx' ? prop : true;
1675 }
1676 }
1677 // Otherwise just return true, or the property name if this is a
1678 // `prefixed()` call
1679 else {
1680 cleanElems();
1681 return prefixed == 'pfx' ? prop : true;
1682 }
1683 }
1684 }
1685 cleanElems();
1686 return false;
1687 }
1688
1689 ;
1690
1691 /**
1692 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill.
1693 *
1694 * @access private
1695 * @function fnBind
1696 * @param {function} fn - a function you want to change `this` reference to
1697 * @param {object} that - the `this` you want to call the function with
1698 * @returns {function} The wrapped version of the supplied function
1699 */
1700
1701 function fnBind(fn, that) {
1702 return function() {
1703 return fn.apply(that, arguments);
1704 };
1705 }
1706
1707 ;
1708
1709 /**
1710 * testDOMProps is a generic DOM property test; if a browser supports
1711 * a certain property, it won't return undefined for it.
1712 *
1713 * @access private
1714 * @function testDOMProps
1715 * @param {array.<string>} props - An array of properties to test for
1716 * @param {object} obj - An object or Element you want to use to test the parameters again
1717 * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check
1718 * @returns {false|*} returns false if the prop is unsupported, otherwise the value that is supported
1719 */
1720 function testDOMProps(props, obj, elem) {
1721 var item;
1722
1723 for (var i in props) {
1724 if (props[i] in obj) {
1725
1726 // return the property name as a string
1727 if (elem === false) {
1728 return props[i];
1729 }
1730
1731 item = obj[props[i]];
1732
1733 // let's bind a function
1734 if (is(item, 'function')) {
1735 // bind to obj unless overriden
1736 return fnBind(item, elem || obj);
1737 }
1738
1739 // return the unbound function or obj or value
1740 return item;
1741 }
1742 }
1743 return false;
1744 }
1745
1746 ;
1747
1748 /**
1749 * testPropsAll tests a list of DOM properties we want to check against.
1750 * We specify literally ALL possible (known and/or likely) properties on
1751 * the element including the non-vendor prefixed one, for forward-
1752 * compatibility.
1753 *
1754 * @access private
1755 * @function testPropsAll
1756 * @param {string} prop - A string of the property to test for
1757 * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip
1758 * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against
1759 * @param {string} [value] - A string of a css value
1760 * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set
1761 * @returns {false|string} returns the string version of the property, or false if it is unsupported
1762 */
1763 function testPropsAll(prop, prefixed, elem, value, skipValueTest) {
1764
1765 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
1766 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
1767
1768 // did they call .prefixed('boxSizing') or are we just testing a prop?
1769 if (is(prefixed, 'string') || is(prefixed, 'undefined')) {
1770 return testProps(props, prefixed, value, skipValueTest);
1771
1772 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
1773 } else {
1774 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
1775 return testDOMProps(props, prefixed, elem);
1776 }
1777 }
1778
1779 // Modernizr.testAllProps() investigates whether a given style property,
1780 // or any of its vendor-prefixed variants, is recognized
1781 //
1782 // Note that the property names must be provided in the camelCase variant.
1783 // Modernizr.testAllProps('boxSizing')
1784 ModernizrProto.testAllProps = testPropsAll;
1785
1786
1787
1788 /**
1789 * prefixed returns the prefixed or nonprefixed property name variant of your input
1790 *
1791 * @memberof Modernizr
1792 * @name Modernizr.prefixed
1793 * @optionName Modernizr.prefixed()
1794 * @optionProp prefixed
1795 * @access public
1796 * @function prefixed
1797 * @param {string} prop - String name of the property to test for
1798 * @param {object} [obj] - An object to test for the prefixed properties on
1799 * @param {HTMLElement} [elem] - An element used to test specific properties against
1800 * @returns {string|false} The string representing the (possibly prefixed) valid
1801 * version of the property, or `false` when it is unsupported.
1802 * @example
1803 *
1804 * Modernizr.prefixed takes a string css value in the DOM style camelCase (as
1805 * opposed to the css style kebab-case) form and returns the (possibly prefixed)
1806 * version of that property that the browser actually supports.
1807 *
1808 * For example, in older Firefox...
1809 * ```js
1810 * prefixed('boxSizing')
1811 * ```
1812 * returns 'MozBoxSizing'
1813 *
1814 * In newer Firefox, as well as any other browser that support the unprefixed
1815 * version would simply return `boxSizing`. Any browser that does not support
1816 * the property at all, it will return `false`.
1817 *
1818 * By default, prefixed is checked against a DOM element. If you want to check
1819 * for a property on another object, just pass it as a second argument
1820 *
1821 * ```js
1822 * var rAF = prefixed('requestAnimationFrame', window);
1823 *
1824 * raf(function() {
1825 * renderFunction();
1826 * })
1827 * ```
1828 *
1829 * Note that this will return _the actual function_ - not the name of the function.
1830 * If you need the actual name of the property, pass in `false` as a third argument
1831 *
1832 * ```js
1833 * var rAFProp = prefixed('requestAnimationFrame', window, false);
1834 *
1835 * rafProp === 'WebkitRequestAnimationFrame' // in older webkit
1836 * ```
1837 *
1838 * One common use case for prefixed is if you're trying to determine which transition
1839 * end event to bind to, you might do something like...
1840 * ```js
1841 * var transEndEventNames = {
1842 * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser
1843 * 'MozTransition' : 'transitionend', * only for FF < 15
1844 * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+
1845 * };
1846 *
1847 * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
1848 * ```
1849 *
1850 * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss).
1851 */
1852
1853 var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) {
1854 if (prop.indexOf('@') === 0) {
1855 return atRule(prop);
1856 }
1857
1858 if (prop.indexOf('-') != -1) {
1859 // Convert kebab-case to camelCase
1860 prop = cssToDOM(prop);
1861 }
1862 if (!obj) {
1863 return testPropsAll(prop, 'pfx');
1864 } else {
1865 // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
1866 return testPropsAll(prop, obj, elem);
1867 }
1868 };
1869
1870
1871
1872 /**
1873 * List of property values to set for css tests. See ticket #21
1874 * http://git.io/vUGl4
1875 *
1876 * @memberof Modernizr
1877 * @name Modernizr._prefixes
1878 * @optionName Modernizr._prefixes
1879 * @optionProp prefixes
1880 * @access public
1881 * @example
1882 *
1883 * Modernizr._prefixes is the internal list of prefixes that we test against
1884 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply
1885 * an array of kebab-case vendor prefixes you can use within your code.
1886 *
1887 * Some common use cases include
1888 *
1889 * Generating all possible prefixed version of a CSS property
1890 * ```js
1891 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); ');
1892 *
1893 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);'
1894 * ```
1895 *
1896 * Generating all possible prefixed version of a CSS value
1897 * ```js
1898 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex';
1899 *
1900 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex'
1901 * ```
1902 */
1903
1904 // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test
1905 // values in feature detects to continue to work
1906 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']);
1907
1908 // expose these for the plugin API. Look in the source for how to join() them against your input
1909 ModernizrProto._prefixes = prefixes;
1910
1911
1912
1913 /**
1914 * prefixedCSS is just like [prefixed](#modernizr-prefixed), but the returned values are in
1915 * kebab-case (e.g. `box-sizing`) rather than camelCase (boxSizing).
1916 *
1917 * @memberof Modernizr
1918 * @name Modernizr.prefixedCSS
1919 * @optionName Modernizr.prefixedCSS()
1920 * @optionProp prefixedCSS
1921 * @access public
1922 * @function prefixedCSS
1923 * @param {string} prop - String name of the property to test for
1924 * @returns {string|false} The string representing the (possibly prefixed)
1925 * valid version of the property, or `false` when it is unsupported.
1926 * @example
1927 *
1928 * `Modernizr.prefixedCSS` is like `Modernizr.prefixed`, but returns the result
1929 * in hyphenated form
1930 *
1931 * ```js
1932 * Modernizr.prefixedCSS('transition') // '-moz-transition' in old Firefox
1933 * ```
1934 *
1935 * Since it is only useful for CSS style properties, it can only be tested against
1936 * an HTMLElement.
1937 *
1938 * Properties can be passed as both the DOM style camelCase or CSS style kebab-case.
1939 */
1940
1941 var prefixedCSS = ModernizrProto.prefixedCSS = function(prop) {
1942 var prefixedProp = prefixed(prop);
1943 return prefixedProp && domToCSS(prefixedProp);
1944 };
1945
1946
1947 /**
1948 * testAllProps determines whether a given CSS property is supported in the browser
1949 *
1950 * @memberof Modernizr
1951 * @name Modernizr.testAllProps
1952 * @optionName Modernizr.testAllProps()
1953 * @optionProp testAllProps
1954 * @access public
1955 * @function testAllProps
1956 * @param {string} prop - String naming the property to test (either camelCase or kebab-case)
1957 * @param {string} [value] - String of the value to test
1958 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection
1959 * @example
1960 *
1961 * testAllProps determines whether a given CSS property, in some prefixed form,
1962 * is supported by the browser.
1963 *
1964 * ```js
1965 * testAllProps('boxSizing') // true
1966 * ```
1967 *
1968 * It can optionally be given a CSS value in string form to test if a property
1969 * value is valid
1970 *
1971 * ```js
1972 * testAllProps('display', 'block') // true
1973 * testAllProps('display', 'penguin') // false
1974 * ```
1975 *
1976 * A boolean can be passed as a third parameter to skip the value check when
1977 * native detection (@supports) isn't available.
1978 *
1979 * ```js
1980 * testAllProps('shapeOutside', 'content-box', true);
1981 * ```
1982 */
1983
1984 function testAllProps(prop, value, skipValueTest) {
1985 return testPropsAll(prop, undefined, undefined, value, skipValueTest);
1986 }
1987 ModernizrProto.testAllProps = testAllProps;
1988
1989
1990 /**
1991 * testProp() investigates whether a given style property is recognized
1992 * Property names can be provided in either camelCase or kebab-case.
1993 *
1994 * @memberof Modernizr
1995 * @name Modernizr.testProp
1996 * @access public
1997 * @optionName Modernizr.testProp()
1998 * @optionProp testProp
1999 * @function testProp
2000 * @param {string} prop - Name of the CSS property to check
2001 * @param {string} [value] - Name of the CSS value to check
2002 * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported
2003 * @returns {boolean}
2004 * @example
2005 *
2006 * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed
2007 * version of the string.
2008 *
2009 * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing)
2010 *
2011 * ```js
2012 * Modernizr.testProp('pointerEvents') // true
2013 * ```
2014 *
2015 * You can also provide a value as an optional second argument to check if a
2016 * specific value is supported
2017 *
2018 * ```js
2019 * Modernizr.testProp('pointerEvents', 'none') // true
2020 * Modernizr.testProp('pointerEvents', 'penguin') // false
2021 * ```
2022 */
2023
2024 var testProp = ModernizrProto.testProp = function(prop, value, useValue) {
2025 return testProps([prop], undefined, value, useValue);
2026 };
2027
2028
2029 /**
2030 * testStyles injects an element with style element and some CSS rules
2031 *
2032 * @memberof Modernizr
2033 * @name Modernizr.testStyles
2034 * @optionName Modernizr.testStyles()
2035 * @optionProp testStyles
2036 * @access public
2037 * @function testStyles
2038 * @param {string} rule - String representing a css rule
2039 * @param {function} callback - A function that is used to test the injected element
2040 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected
2041 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
2042 * @returns {boolean}
2043 * @example
2044 *
2045 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page
2046 * along with (possibly multiple) DOM elements. This lets you check for features
2047 * 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).
2048 *
2049 * ```js
2050 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) {
2051 * // elem is the first DOM node in the page (by default #modernizr)
2052 * // rule is the first argument you supplied - the CSS rule in string form
2053 *
2054 * addTest('widthworks', elem.style.width === '9px')
2055 * });
2056 * ```
2057 *
2058 * If your test requires multiple nodes, you can include a third argument
2059 * indicating how many additional div elements to include on the page. The
2060 * additional nodes are injected as children of the `elem` that is returned as
2061 * the first argument to the callback.
2062 *
2063 * ```js
2064 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) {
2065 * document.getElementById('modernizr').style.width === '1px'; // true
2066 * document.getElementById('modernizr2').style.width === '2px'; // true
2067 * elem.firstChild === document.getElementById('modernizr2'); // true
2068 * }, 1);
2069 * ```
2070 *
2071 * By default, all of the additional elements have an ID of `modernizr[n]`, where
2072 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`,
2073 * the second additional is `#modernizr3`, etc.).
2074 * If you want to have more meaningful IDs for your function, you can provide
2075 * them as the fourth argument, as an array of strings
2076 *
2077 * ```js
2078 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) {
2079 * elem.firstChild === document.getElementById('foo'); // true
2080 * elem.lastChild === document.getElementById('bar'); // true
2081 * }, 2, ['foo', 'bar']);
2082 * ```
2083 *
2084 */
2085
2086 var testStyles = ModernizrProto.testStyles = injectElementWithStyles;
2087
2088/*!
2089{
2090 "name": "a[download] Attribute",
2091 "property": "adownload",
2092 "caniuse" : "download",
2093 "tags": ["media", "attribute"],
2094 "builderAliases": ["a_download"],
2095 "notes": [{
2096 "name": "WhatWG Reference",
2097 "href": "https://developers.whatwg.org/links.html#downloading-resources"
2098 }]
2099}
2100!*/
2101/* DOC
2102When used on an `<a>`, this attribute signifies that the resource it points to should be downloaded by the browser rather than navigating to it.
2103*/
2104
2105 Modernizr.addTest('adownload', !window.externalHost && 'download' in createElement('a'));
2106
2107/*!
2108{
2109 "name": "Ambient Light Events",
2110 "property": "ambientlight",
2111 "notes": [{
2112 "name": "W3C Ambient Light Events",
2113 "href": "https://www.w3.org/TR/ambient-light/"
2114 }]
2115}
2116!*/
2117/* DOC
2118Detects support for the API that provides information about the ambient light levels, as detected by the device's light detector, in terms of lux units.
2119*/
2120
2121 Modernizr.addTest('ambientlight', hasEvent('devicelight', window));
2122
2123/*!
2124{
2125 "name": "Application Cache",
2126 "property": "applicationcache",
2127 "caniuse": "offline-apps",
2128 "tags": ["storage", "offline"],
2129 "notes": [{
2130 "name": "MDN documentation",
2131 "href": "https://developer.mozilla.org/en/docs/HTML/Using_the_application_cache"
2132 }],
2133 "polyfills": ["html5gears"]
2134}
2135!*/
2136/* DOC
2137Detects support for the Application Cache, for storing data to enable web-based applications run offline.
2138
2139The API has been [heavily criticized](http://alistapart.com/article/application-cache-is-a-douchebag) and discussions are underway to address this.
2140*/
2141
2142 Modernizr.addTest('applicationcache', 'applicationCache' in window);
2143
2144/*!
2145{
2146 "name" : "HTML5 Audio Element",
2147 "property": "audio",
2148 "tags" : ["html5", "audio", "media"]
2149}
2150!*/
2151/* DOC
2152Detects the audio element
2153*/
2154
2155 // This tests evaluates support of the audio element, as well as
2156 // testing what types of content it supports.
2157 //
2158 // We're using the Boolean constructor here, so that we can extend the value
2159 // e.g. Modernizr.audio // true
2160 // Modernizr.audio.ogg // 'probably'
2161 //
2162 // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
2163 // thx to NielsLeenheer and zcorpan
2164
2165 // Note: in some older browsers, "no" was a return value instead of empty string.
2166 // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
2167 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
2168 Modernizr.addTest('audio', function() {
2169 var elem = createElement('audio');
2170 var bool = false;
2171
2172 try {
2173 bool = !!elem.canPlayType
2174 if (bool) {
2175 bool = new Boolean(bool);
2176 bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"') .replace(/^no$/, '');
2177 bool.mp3 = elem.canPlayType('audio/mpeg; codecs="mp3"') .replace(/^no$/, '');
2178 bool.opus = elem.canPlayType('audio/ogg; codecs="opus"') ||
2179 elem.canPlayType('audio/webm; codecs="opus"') .replace(/^no$/, '');
2180
2181 // Mimetypes accepted:
2182 // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
2183 // bit.ly/iphoneoscodecs
2184 bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/, '');
2185 bool.m4a = (elem.canPlayType('audio/x-m4a;') ||
2186 elem.canPlayType('audio/aac;')) .replace(/^no$/, '');
2187 }
2188 } catch (e) { }
2189
2190 return bool;
2191 });
2192
2193/*!
2194{
2195 "name": "Audio Loop Attribute",
2196 "property": "audioloop",
2197 "tags": ["audio", "media"]
2198}
2199!*/
2200/* DOC
2201Detects if an audio element can automatically restart, once it has finished
2202*/
2203
2204 Modernizr.addTest('audioloop', 'loop' in createElement('audio'));
2205
2206/*!
2207{
2208 "name": "Audio Preload",
2209 "property": "audiopreload",
2210 "tags": ["audio", "media"],
2211 "async" : true,
2212 "warnings": ["This test is very large ? only include it if you absolutely need it"]
2213}
2214!*/
2215/* DOC
2216Detects if audio can be downloaded in the background before it starts playing in the `<audio>` element
2217*/
2218
2219
2220 Modernizr.addAsyncTest(function() {
2221 var timeout;
2222 var waitTime = 300;
2223 var elem = createElement('audio');
2224 var elemStyle = elem.style;
2225
2226 function testpreload(event) {
2227 clearTimeout(timeout);
2228 var result = event !== undefined && event.type === 'loadeddata' ? true : false; //need to check if event is not undefined here in case function is evoked from timeout (no parameters)
2229 elem.removeEventListener('loadeddata', testpreload, false);
2230 addTest('audiopreload', result);
2231 // Cleanup, but don't assume elem is still in the page -
2232 // an extension (eg Flashblock) may already have removed it.
2233 if (elem.parentNode) {
2234 elem.parentNode.removeChild(elem);
2235 }
2236 }
2237
2238 //skip the test if audio itself, or the preload
2239 //element on it isn't supported
2240 if (!Modernizr.audio || !('preload' in elem)) {
2241 addTest('audiopreload', false);
2242 return;
2243 }
2244
2245 elemStyle.position = 'absolute';
2246 elemStyle.height = 0;
2247 elemStyle.width = 0;
2248
2249 try {
2250 if (Modernizr.audio.mp3) {
2251 //75ms of silence (minumum Mp3 duration loaded by Safari, not tested other formats thoroughly: may be possible to shrink base64 URI)
2252 elem.src = 'data:audio/mpeg;base64,//MUxAAB6AXgAAAAAPP+c6nf//yi/6f3//MUxAMAAAIAAAjEcH//0fTX6C9Lf//0//MUxA4BeAIAAAAAAKX2/6zv//+IlR4f//MUxBMCMAH8AAAAABYWalVMQU1FMy45//MUxBUB0AH0AAAAADkuM1VVVVVVVVVV//MUxBgBUATowAAAAFVVVVVVVVVVVVVV';
2253 }
2254 else if (Modernizr.audio.m4a) {
2255 elem.src = 'data:audio/x-m4a;base64,AAAAGGZ0eXBNNEEgAAACAGlzb21pc28yAAAACGZyZWUAAAAfbWRhdN4EAABsaWJmYWFjIDEuMjgAAAFoAQBHAAACiG1vb3YAAABsbXZoZAAAAAB8JbCAfCWwgAAAA+gAAAAYAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAG0dHJhawAAAFx0a2hkAAAAD3wlsIB8JbCAAAAAAQAAAAAAAAAYAAAAAAAAAAAAAAAAAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAABUG1kaWEAAAAgbWRoZAAAAAB8JbCAfCWwgAAArEQAAAQAVcQAAAAAAC1oZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU291bmRIYW5kbGVyAAAAAPttaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAL9zdGJsAAAAW3N0c2QAAAAAAAAAAQAAAEttcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAACdlc2RzAAAAAAMZAAEABBFAFQAAAAABftAAAAAABQISCAYBAgAAABhzdHRzAAAAAAAAAAEAAAABAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAXAAAAAQAAABRzdGNvAAAAAAAAAAEAAAAoAAAAYHVkdGEAAABYbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAraWxzdAAAACOpdG9vAAAAG2RhdGEAAAABAAAAAExhdmY1Mi42NC4y';
2256 }
2257 else if (Modernizr.audio.ogg) {
2258 elem.src = 'data:audio/ogg;base64,T2dnUwACAAAAAAAAAAD/QwAAAAAAAM2LVKsBHgF2b3JiaXMAAAAAAUSsAAAAAAAAgLsAAAAAAAC4AU9nZ1MAAAAAAAAAAAAA/0MAAAEAAADmvOe6Dy3/////////////////MgN2b3JiaXMdAAAAWGlwaC5PcmcgbGliVm9yYmlzIEkgMjAwNzA2MjIAAAAAAQV2b3JiaXMfQkNWAQAAAQAYY1QpRplS0kqJGXOUMUaZYpJKiaWEFkJInXMUU6k515xrrLm1IIQQGlNQKQWZUo5SaRljkCkFmVIQS0kldBI6J51jEFtJwdaYa4tBthyEDZpSTCnElFKKQggZU4wpxZRSSkIHJXQOOuYcU45KKEG4nHOrtZaWY4updJJK5yRkTEJIKYWSSgelU05CSDWW1lIpHXNSUmpB6CCEEEK2IIQNgtCQVQAAAQDAQBAasgoAUAAAEIqhGIoChIasAgAyAAAEoCiO4iiOIzmSY0kWEBqyCgAAAgAQAADAcBRJkRTJsSRL0ixL00RRVX3VNlVV9nVd13Vd13UgNGQVAAABAEBIp5mlGiDCDGQYCA1ZBQAgAAAARijCEANCQ1YBAAABAABiKDmIJrTmfHOOg2Y5aCrF5nRwItXmSW4q5uacc845J5tzxjjnnHOKcmYxaCa05pxzEoNmKWgmtOacc57E5kFrqrTmnHPGOaeDcUYY55xzmrTmQWo21uaccxa0pjlqLsXmnHMi5eZJbS7V5pxzzjnnnHPOOeecc6oXp3NwTjjnnHOi9uZabkIX55xzPhmne3NCOOecc84555xzzjnnnHOC0JBVAAAQAABBGDaGcacgSJ+jgRhFiGnIpAfdo8MkaAxyCqlHo6ORUuoglFTGSSmdIDRkFQAACAAAIYQUUkghhRRSSCGFFFKIIYYYYsgpp5yCCiqppKKKMsoss8wyyyyzzDLrsLPOOuwwxBBDDK20EktNtdVYY62555xrDtJaaa211koppZRSSikIDVkFAIAAABAIGWSQQUYhhRRSiCGmnHLKKaigAkJDVgEAgAAAAgAAADzJc0RHdERHdERHdERHdETHczxHlERJlERJtEzL1ExPFVXVlV1b1mXd9m1hF3bd93Xf93Xj14VhWZZlWZZlWZZlWZZlWZZlWYLQkFUAAAgAAIAQQgghhRRSSCGlGGPMMeegk1BCIDRkFQAACAAgAAAAwFEcxXEkR3IkyZIsSZM0S7M8zdM8TfREURRN01RFV3RF3bRF2ZRN13RN2XRVWbVdWbZt2dZtX5Zt3/d93/d93/d93/d93/d1HQgNWQUASAAA6EiOpEiKpEiO4ziSJAGhIasAABkAAAEAKIqjOI7jSJIkSZakSZ7lWaJmaqZneqqoAqEhqwAAQAAAAQAAAAAAKJriKabiKaLiOaIjSqJlWqKmaq4om7Lruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7rui4QGrIKAJAAANCRHMmRHEmRFEmRHMkBQkNWAQAyAAACAHAMx5AUybEsS9M8zdM8TfRET/RMTxVd0QVCQ1YBAIAAAAIAAAAAADAkw1IsR3M0SZRUS7VUTbVUSxVVT1VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTVN0zRNIDRkJQAABADAYo3B5SAhJSXl3hDCEJOeMSYhtV4hBJGS3jEGFYOeMqIMct5C4xCDHggNWREARAEAAMYgxxBzyDlHqZMSOeeodJQa5xyljlJnKcWYYs0oldhSrI1zjlJHraOUYiwtdpRSjanGAgAAAhwAAAIshEJDVgQAUQAAhDFIKaQUYow5p5xDjCnnmHOGMeYcc44556B0UirnnHROSsQYc445p5xzUjonlXNOSiehAACAAAcAgAALodCQFQFAnACAQZI8T/I0UZQ0TxRFU3RdUTRd1/I81fRMU1U90VRVU1Vt2VRVWZY8zzQ901RVzzRV1VRVWTZVVZZFVdVt03V123RV3ZZt2/ddWxZ2UVVt3VRd2zdV1/Zd2fZ9WdZ1Y/I8VfVM03U903Rl1XVtW3VdXfdMU5ZN15Vl03Vt25VlXXdl2fc103Rd01Vl2XRd2XZlV7ddWfZ903WF35VlX1dlWRh2XfeFW9eV5XRd3VdlVzdWWfZ9W9eF4dZ1YZk8T1U903RdzzRdV3VdX1dd19Y105Rl03Vt2VRdWXZl2fddV9Z1zzRl2XRd2zZdV5ZdWfZ9V5Z13XRdX1dlWfhVV/Z1WdeV4dZt4Tdd1/dVWfaFV5Z14dZ1Ybl1XRg+VfV9U3aF4XRl39eF31luXTiW0XV9YZVt4VhlWTl+4ViW3feVZXRdX1ht2RhWWRaGX/id5fZ943h1XRlu3efMuu8Mx++k+8rT1W1jmX3dWWZfd47hGDq/8OOpqq+brisMpywLv+3rxrP7vrKMruv7qiwLvyrbwrHrvvP8vrAso+z6wmrLwrDatjHcvm4sv3Acy2vryjHrvlG2dXxfeArD83R1XXlmXcf2dXTjRzh+ygAAgAEHAIAAE8pAoSErAoA4AQCPJImiZFmiKFmWKIqm6LqiaLqupGmmqWmeaVqaZ5qmaaqyKZquLGmaaVqeZpqap5mmaJqua5qmrIqmKcumasqyaZqy7LqybbuubNuiacqyaZqybJqmLLuyq9uu7Oq6pFmmqXmeaWqeZ5qmasqyaZquq3meanqeaKqeKKqqaqqqraqqLFueZ5qa6KmmJ4qqaqqmrZqqKsumqtqyaaq2bKqqbbuq7Pqybeu6aaqybaqmLZuqatuu7OqyLNu6L2maaWqeZ5qa55mmaZqybJqqK1uep5qeKKqq5ommaqqqLJumqsqW55mqJ4qq6omea5qqKsumatqqaZq2bKqqLZumKsuubfu+68qybqqqbJuqauumasqybMu+78qq7oqmKcumqtqyaaqyLduy78uyrPuiacqyaaqybaqqLsuybRuzbPu6aJqybaqmLZuqKtuyLfu6LNu678qub6uqrOuyLfu67vqucOu6MLyybPuqrPq6K9u6b+sy2/Z9RNOUZVM1bdtUVVl2Zdn2Zdv2fdE0bVtVVVs2TdW2ZVn2fVm2bWE0Tdk2VVXWTdW0bVmWbWG2ZeF2Zdm3ZVv2ddeVdV/XfePXZd3murLty7Kt+6qr+rbu+8Jw667wCgAAGHAAAAgwoQwUGrISAIgCAACMYYwxCI1SzjkHoVHKOecgZM5BCCGVzDkIIZSSOQehlJQy5yCUklIIoZSUWgshlJRSawUAABQ4AAAE2KApsThAoSErAYBUAACD41iW55miatqyY0meJ4qqqaq27UiW54miaaqqbVueJ4qmqaqu6+ua54miaaqq6+q6aJqmqaqu67q6Lpqiqaqq67qyrpumqqquK7uy7Oumqqqq68quLPvCqrquK8uybevCsKqu68qybNu2b9y6ruu+7/vCka3rui78wjEMRwEA4AkOAEAFNqyOcFI0FlhoyEoAIAMAgDAGIYMQQgYhhJBSSiGllBIAADDgAAAQYEIZKDRkRQAQJwAAGEMppJRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkgppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkqppJRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSCgCQinAAkHowoQwUGrISAEgFAACMUUopxpyDEDHmGGPQSSgpYsw5xhyUklLlHIQQUmktt8o5CCGk1FJtmXNSWosx5hgz56SkFFvNOYdSUoux5ppr7qS0VmuuNedaWqs115xzzbm0FmuuOdecc8sx15xzzjnnGHPOOeecc84FAOA0OACAHtiwOsJJ0VhgoSErAYBUAAACGaUYc8456BBSjDnnHIQQIoUYc845CCFUjDnnHHQQQqgYc8w5CCGEkDnnHIQQQgghcw466CCEEEIHHYQQQgihlM5BCCGEEEooIYQQQgghhBA6CCGEEEIIIYQQQgghhFJKCCGEEEIJoZRQAABggQMAQIANqyOcFI0FFhqyEgAAAgCAHJagUs6EQY5Bjw1BylEzDUJMOdGZYk5qMxVTkDkQnXQSGWpB2V4yCwAAgCAAIMAEEBggKPhCCIgxAABBiMwQCYVVsMCgDBoc5gHAA0SERACQmKBIu7iALgNc0MVdB0IIQhCCWBxAAQk4OOGGJ97whBucoFNU6iAAAAAAAAwA4AEA4KAAIiKaq7C4wMjQ2ODo8AgAAAAAABYA+AAAOD6AiIjmKiwuMDI0Njg6PAIAAAAAAAAAAICAgAAAAAAAQAAAAICAT2dnUwAE7AwAAAAAAAD/QwAAAgAAADuydfsFAQEBAQEACg4ODg==';
2259 }
2260 else if (Modernizr.audio.wav) {
2261 elem.src = 'data:audio/wav;base64,UklGRvwZAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YdgZAAAAAAEA/v8CAP//AAABAP////8DAPz/BAD9/wEAAAAAAAAAAAABAP7/AgD//wAAAQD//wAAAQD//wAAAQD+/wIA//8AAAAAAAD//wIA/v8BAAAA//8BAAAA//8BAP//AQAAAP//AQD//wEAAAD//wEA//8BAP//AQD//wEA//8BAP//AQD+/wMA/f8DAP3/AgD+/wIA/////wMA/f8CAP7/AgD+/wMA/f8CAP7/AgD//wAAAAAAAAAAAQD+/wIA/v8CAP7/AwD9/wIA/v8BAAEA/v8CAP7/AQAAAAAAAAD//wEAAAD//wIA/f8DAP7/AQD//wEAAAD//wEA//8CAP7/AQD//wIA/v8CAP7/AQAAAAAAAAD//wEAAAAAAAAA//8BAP//AgD9/wQA+/8FAPz/AgAAAP//AgD+/wEAAAD//wIA/v8CAP3/BAD8/wQA/P8DAP7/AwD8/wQA/P8DAP7/AQAAAAAA//8BAP//AgD+/wEAAAD//wIA/v8BAP//AQD//wEAAAD//wEA//8BAAAAAAAAAP//AgD+/wEAAAAAAAAAAAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AgD+/wIA/v8BAP//AQABAP7/AQD//wIA/v8CAP3/AwD/////AgD9/wMA/v8BAP//AQAAAP//AQD//wEA//8BAP//AAABAP//AAABAP//AQD//wAAAAACAP3/AwD9/wIA//8BAP//AQD//wEA//8BAP//AgD9/wMA/v8AAAIA/f8CAAAA/v8EAPv/BAD9/wIAAAD+/wQA+v8HAPr/BAD+/wEAAAD//wIA/f8EAPz/BAD7/wUA/P8EAPz/AwD+/wEAAAD//wEAAAAAAP//AgD8/wUA+/8FAPz/AwD9/wIA//8AAAEA/v8CAP//AQD//wAAAAABAP//AgD9/wMA/f8EAPz/AwD+/wAAAwD7/wUA/P8DAP7/AQAAAP//AgD+/wEAAQD+/wIA/v8BAAEA/v8CAP7/AQAAAP//AgD9/wMA/f8DAP7/AgD+/wEAAAAAAAEA//8AAAEA/v8DAP3/AgD//wEA//8BAP7/AwD9/wMA/v8BAP//AQAAAP//AgD9/wMA/v8BAP//AQAAAP//AgD+/wEAAQD+/wIA/////wIA//8AAAEA/f8DAP//AAABAP////8DAP3/AwD+/wEA//8BAP//AQAAAAAA//8BAP//AQD//wEA//8BAP//AAAAAAEA//8BAP7/AgD//wEA//8AAAAAAAAAAAAAAAD//wIA/v8BAAAA//8BAAEA/v8BAAAA//8DAPz/AwD+/wIA/v8CAP3/AwD+/wEAAAD//wEA//8BAAAA//8BAAAA/v8EAPv/BAD+/wAAAAABAP7/AgD//wAAAAABAP7/AgD//wAAAAAAAAAAAAABAP3/BAD8/wQA/f8BAAAAAAABAP7/AgD+/wIA/v8CAP7/AgD+/wIA/v8BAAAAAAD//wIA/f8DAP7/AAABAP//AAACAPz/BAD9/wIA//8AAP//AwD9/wMA/P8EAP3/AwD9/wIA//8BAP//AQD+/wMA/f8DAP7/AAABAP//AQAAAP//AQD//wIA/f8DAP7/AQAAAP//AQAAAAAA//8CAP7/AQABAP7/AgD+/wEAAQD+/wIA/v8CAP////8CAP7/AgD//wAAAAABAP7/AwD9/wIAAAD+/wMA/f8CAP//AQD+/wMA/f8CAP//AAACAPz/BQD6/wUA/v///wIA/v8CAP3/BAD7/wYA+v8FAPz/AwD/////AgD+/wEAAAD//wEAAAD//wIA/f8DAP7/AQAAAP//AgD//wAA//8BAAAAAAAAAP//AQD//wEA//8AAAIA/f8DAP3/AgAAAP//AQD//wEA//8AAAEA//8BAP////8CAP//AAABAP3/BAD9/wIA/v8BAAEA//8BAP7/AgD//wEA//8AAAEA//8BAP//AAAAAAEA//8BAP7/AgD//wEA//8AAAAAAQD+/wIA/v8BAAAAAAD//wIA/v8BAAAAAAAAAAAAAQD+/wMA/f8CAP//AQD//wIA/f8DAP7/AQD//wEA//8CAP7/AAABAP7/AwD9/wMA/v8AAAEA//8BAAAAAAD//wIA/v8BAAAA//8CAP7/AgD+/wEA//8CAP7/AgD//wAAAAAAAAAAAQD//wEA/v8DAPz/BQD8/wIA//8AAAEAAAD//wEA//8BAP//AQAAAAAA//8BAP//AgD+/wEAAAAAAP//AQD+/wMA/////wEA/v8CAP//AQD//wEA//8AAAEA//8BAAAA/v8EAPz/AwD+/wEAAAAAAAAA//8CAP7/AQD//wEA//8BAP//AAABAP7/AwD9/wIA//8BAP//AQD//wEA//8AAAEA/v8EAPv/BAD9/wIA//8BAP7/AwD9/wIA//8AAAEA//8BAP//AQD//wAAAQD//wEAAAD+/wMA/v8AAAIA/f8DAP7/AQD//wAAAQD+/wMA/f8CAP//AAABAP7/AgD+/wMA/f8CAP7/AQABAP7/AgD+/wIA/v8CAP7/AwD8/wMA//8AAAEA//8AAAAAAAABAP//AQD//wAAAQD//wIA/f8DAP3/AwD+/wAAAgD9/wIA//8AAAEAAAD+/wMA/P8FAPv/BAD9/wIA//8AAP//AgD+/wIA/v8BAAAAAAD//wEAAAAAAP//AQD//wEA//8BAP//AAABAP7/AwD9/wIA//8BAP//AAABAP//AQD//wAAAQD//wEA//8BAP//AAABAAAA//8BAP7/AwD9/wMA/f8DAP3/AgD//wEA//8BAP7/AgD//wAAAgD8/wQA/f8CAP//AQD+/wMA/f8CAP7/AgD//wAAAAAAAAAAAAABAP7/AwD9/wIA/v8DAP3/AwD9/wIA/v8DAPz/BQD7/wQA/f8CAP7/AwD9/wMA/f8CAP//AQAAAP7/AwD+/wEA//8AAAEAAAAAAP//AAABAP//AQAAAP7/AwD9/wMA/f8CAP//AQD//wEA//8AAAIA/f8CAAAA//8BAAAA//8BAAAA/v8EAPv/BAD9/wIA//8AAAEA/v8CAP//AAABAP//AAABAP//AAABAP7/AwD8/wQA/f8CAAAA/v8DAP3/AwD9/wMA/v8BAAAA//8BAAAA//8CAP7/AQAAAAAAAAAAAAAA//8CAP7/AgD+/wIA/v8CAP7/AgD//wAAAQD//wAAAQD//wAAAQD//wAAAQD+/wIA//8AAAAAAQD+/wMA/f8CAP//AQD//wEA//8AAAEA/v8DAP3/AgD//wAAAAABAP7/AwD9/wIA//8AAAEA/v8DAP3/AgD//wAAAAABAP7/AwD8/wMA/v8CAP//AAD//wIA/v8CAP7/AQABAP7/AQAAAP//AgD/////AQD//wEAAAD//wEA/v8EAPv/BAD9/wMA/v8BAAAA//8BAAEA/P8GAPr/BQD8/wMA/v8BAAAA//8CAP7/AQABAP3/BAD7/wYA+/8EAPz/AwD//wEA//8BAP7/BAD8/wMA/v8AAAIA/v8BAAAA//8BAAAA//8BAAAA//8CAP3/AwD+/wAAAgD8/wUA/P8DAP7/AAABAAAAAAD//wEAAAD//wIA/f8DAP7/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA/f8EAPz/AwD/////AgD+/wIA/f8DAP7/AgD+/wEA//8CAP7/AQD//wEAAAAAAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAP//AQAAAP//AAACAP3/BAD7/wQA/v8BAAAA//8BAP//AQAAAP//AQAAAP7/BAD7/wUA+/8EAP3/AgD//wAAAQD+/wIA//8AAAEA/v8CAP//AQD+/wEAAAAAAAAAAAD//wEA//8CAP3/AwD9/wIA//8AAAAAAAAAAAAA//8BAP//AgD+/wEA//8CAP7/AQAAAP//AgD/////AgD/////AgD+/wIA//8AAP//AQABAP7/AgD9/wMA/v8CAP////8BAAAAAAAAAAAA//8CAP////8DAPz/AwD+/wEAAAAAAP//AQD//wEAAAD//wEAAAD+/wQA+/8FAPz/AgAAAP//AgD9/wMA/v8BAAAAAAD//wEAAAD//wIA/v8BAAAAAAD//wIA/v8BAAAA//8BAAAA//8CAP7/AQD//wEA//8BAAAA//8BAP//AAABAP//AQAAAP7/AgD//wEA//8AAAAAAQD+/wMA/P8EAP7///8DAPz/BQD8/wEAAQD+/wMA/v8AAAEA//8BAP//AQD//wEA/v8CAP//AQD//wAAAAABAAAA//8BAP//AQAAAAAA//8BAP//AgD+/wAAAQD//wIA/f8CAP//AQAAAP7/AwD9/wMA/v8BAP//AAABAP//AgD9/wIA//8BAAAA//8BAAAA//8CAP3/AwD+/wEAAAD+/wQA/P8DAP7/AAACAP7/AQAAAP//AQAAAP//AQAAAP//AgD9/wIAAAD//wIA/f8DAP7/AQD//wEA//8CAP7/AQD//wAAAQD//wEA//8AAAAAAQD//wEAAAD9/wUA+/8FAPz/AgD//wAAAQD//wAAAQD+/wMA/f8BAAEA/v8CAP7/AgD+/wIA/v8BAAAAAAAAAAAAAAD//wIA/v8CAP////8CAP7/AgD+/wIA/v8CAP7/AQAAAP//AQAAAP//AQD//wAAAQD//wAAAQD+/wMA/f8CAAAA/v8DAP3/AgAAAP//AQAAAP7/AwD9/wMA/v8BAP//AQD//wEAAAD+/wMA/f8CAAAA/v8CAP//AAAAAAEA//8AAAEA/v8DAP3/AwD9/wIA//8BAP//AgD8/wQA/v8BAAAA/v8CAP//AQD//wAAAAAAAAEA/f8EAPz/BAD9/wIA//8AAAAAAAABAP//AAAAAAAAAAABAP3/BAD9/wIA/v8BAAEA//8AAAAA//8CAP7/AgD9/wQA+/8FAPv/BQD8/wMA/f8DAP3/AwD+/wAAAgD9/wMA/f8CAAAA/v8EAPv/BQD7/wUA/P8DAP///v8DAP3/BAD8/wMA/f8DAP7/AQD//wEAAAD//wEA/v8CAAAA/v8CAP7/AgD//wAAAAAAAAAAAQD+/wIA//8AAAEA/v8DAPz/BAD9/wIA//8AAP//AgD//wEA/v8BAAAAAQD//wAAAAAAAAEA//8AAAEA//8BAP//AAABAP//AQD+/wIA/v8DAPz/BAD8/wQA/f8BAAAAAQD+/wMA/P8DAP//AAAAAAAAAAD//wMA+/8FAP3/AQABAP3/BAD8/wMA/v8BAAAA//8CAP3/AwD+/wEAAQD9/wMA/f8EAPz/BAD7/wQA/v8BAAEA/f8DAP7/AQAAAP//AgD+/wEAAAD//wIA/v8CAP7/AgD+/wEAAQD//wEA/v8CAP7/BAD7/wQA/f8CAAAA//8AAAAAAAABAP//AQD+/wEAAQD+/wMA/f8BAAEA/v8DAPz/AwD/////AwD8/wQA/P8DAP7/AgD//wAA//8BAAAAAAAAAP//AgD+/wEAAAD//wIA/v8BAAAA//8CAP3/AgD//wAAAQD+/wIA/v8BAAAA//8CAP7/AgD+/wEA//8CAP3/BAD7/wQA/v8BAAAA//8AAAEAAAD//wIA/f8DAP7/AgD+/wIA/v8CAP7/AgD+/wEAAAAAAP//AgD9/wMA/v8BAP//AgD9/wMA/v8AAAEA//8BAP//AQD//wEA//8AAAEA/v8EAPz/AgD//wAAAQAAAP//AAABAP//AQD//wEAAAD//wEA//8BAAEA/f8DAP7/AQABAP3/AwD+/wIA/////wEAAAAAAAAAAAD//wIA/v8CAP////8CAP7/AgD//wAA//8CAP3/BAD9/wAAAgD9/wMA/v8BAP//AQAAAP//AQAAAP//AgD9/wMA/f8EAPz/AwD+/wEAAAAAAAAAAAD//wIA/f8EAP3/AAABAAAA//8CAP7/AQAAAP//AQAAAAAA//8BAP//AQAAAP//AQAAAP//AQAAAP//AgD9/wMA/v8BAP//AQAAAP//AQD//wIA/v8CAP3/BAD9/wEAAAD//wEAAQD9/wMA/f8CAAAA/v8DAP3/AgD//wAAAQD+/wIA/v8CAP7/AQAAAP//AgD+/wEAAAAAAP//AwD7/wUA/f8BAAEA/v8BAAEA/v8DAP3/AgD//wEA//8BAP//AQD//wEA//8CAP3/BAD7/wQA/////wIA/v8AAAIA/v8CAP3/BAD7/wUA/P8DAP3/AwD9/wMA/v8AAAIA/v8CAP7/AgD+/wIA//8AAAEA/v8CAP7/AgD//wAAAAD//wEAAAAAAAAA//8BAP7/BAD7/wUA/P8CAAAA//8BAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAAAA//8CAP3/AwD+/wEA//8CAP3/AwD+/wAAAwD8/wIAAAD//wIA/////wIA/v8CAP7/AgD+/wEAAAAAAAAAAAAAAP//AgD+/wIA//8AAAAA//8CAP7/AgD+/wEA//8CAP3/AwD9/wMA/v8BAP7/AwD9/wMA/f8CAP//AQD+/wIA//8BAP//AQD+/wMA/v8BAAAA//8BAAAA//8CAP7/AQAAAP//AgD+/wIA/v8CAP//AAAAAAEA//8BAP//AAABAAAA//8BAP//AQD//wEA//8BAP//AQAAAP//AQD//wEAAAD//wIA/f8CAAAA//8BAAAA//8BAP//AAABAP//AQD//wAAAAAAAAEA/v8CAP//AQD//wAAAAABAP7/AwD9/wIAAAD+/wIA//8BAP//AgD9/wMA/f8DAP7/AgD+/wEAAAAAAAEA/v8CAP7/AgD//wAAAAAAAAAAAAAAAP//AgD/////AgD9/wQA/f8BAAAAAAAAAAEA/f8DAP////8DAP3/AQABAP7/AgD//wAAAQD+/wMA/f8CAP7/AQABAP7/AwD7/wYA+v8FAP3/AQABAP7/AgD+/wMA/f8CAP7/AwD+/wEA//8BAP//AQAAAP7/BQD5/wcA+v8FAPz/AwD+/wIA/v8BAAAA//8DAPv/BQD8/wMA/////wEAAAAAAAAAAAD//wIA/f8DAP7/AQAAAP//AQAAAP//AgD+/wIA/v8BAAEA/f8EAPz/AwD+/wEA//8CAP7/AQD//wEA//8CAP7/AQAAAP//AgD+/wEAAAAAAAAAAAAAAAAAAAD//wIA/f8EAPz/AwD+/wEA//8CAP7/AgD+/wEAAQD+/wEAAQD+/wIA/////wIA//8AAAAAAAAAAAAAAAD//wEAAAAAAP//AgD9/wMA/v8BAP//AQAAAP//AQD//wEA//8BAP//AQD//wEA//8BAP//AQAAAP7/AwD9/wMA/v8BAP7/AwD9/wMA/v8BAP//AAABAP//AQD//wAAAAABAP//AAAAAAAAAQD//wEA/v8CAAAA/v8EAPv/BAD9/wIAAAD+/wMA/P8DAP//AAAAAP//AQD//wIA/f8DAP3/AwD9/wMA/v8BAAAA//8BAAAA//8CAP3/AwD9/wQA+/8FAPv/BQD8/wMA/v8BAAAA//8BAP//AgD+/wEAAAD//wIA/v8BAAEA/f8DAP3/AgAAAP//AQD//wAAAQD//wEA//8BAP//AQD//wEA/v8DAP3/AgAAAP7/AwD9/wIAAAD//wEAAAD//wIA/f8DAP7/AgD9/wQA+/8FAPz/AgAAAP//AgD9/wIA//8BAP//AQD//wEA//8BAP//AQD//wIA/f8DAP3/AgD//wAAAQD+/wIA/v8BAAEA/v8CAP7/AgD+/wMA/P8DAP//AAABAP7/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA/v8CAP3/BAD8/wMA/v8BAAAAAAD//wEAAAAAAAAAAAD//wEAAAAAAAAA//8BAP//AgD+/wEA//8CAP3/AwD9/wMA/f8EAPv/BAD+/wAAAQD//wEA//8BAP//AAABAP//AQD//wEAAAD//wEA//8BAP//AgD9/wMA/v8AAAIA/f8DAP7/AAACAP3/AwD+/wEA//8BAP//AQAAAP//AQAAAP7/AwD9/wMA/v8AAAEA//8BAP//AAAAAAEA//8AAAEA/v8CAP//AAAAAAEA/v8DAPz/BAD9/wEAAQD+/wEAAQD9/wQA/P8DAP7/AQAAAAAAAAAAAAAAAAAAAAAAAQD+/wIA/////wIA/v8BAAAA//8BAP//AQD//wEA//8BAAAA/v8EAPz/AwD///7/BAD8/wMA/////wIA/v8CAP////8CAP7/AgD+/wIA/v8CAP////8CAP7/AwD9/wIA/v8CAP//AAABAP7/AwD9/wEAAQD+/wMA/f8CAP//AAAAAAEA/v8DAPz/BAD9/wIA/v8CAP7/AgD//wAAAAD//wIA/v8CAP7/AQAAAAAA//8CAP7/AgD+/wIA/v8CAP7/AwD8/wUA+v8GAPv/AwD//wAAAAAAAAAA//8DAPv/BQD9/wAAAgD9/wMA/v8BAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAAAAAAAAAP//AQAAAAAAAAD//wEA//8CAP3/AwD+/wAAAgD+/wEAAAD//wIA/v8CAP7/AgD/////AwD8/wUA/P8CAP//AQD//wIA/f8DAP3/AwD+/wAAAQD+/wMA/f8DAP3/AgD//wAAAQD//wEA//8BAP7/AwD+/wEA//8AAAEA//8CAPz/BAD9/wIA//8AAAEA/v8DAPz/BAD9/wIA//8AAAEA/v8CAP7/AgD//wEA/f8EAPz/BAD+////AgD//wAAAQD//wAAAQD//wEA//8BAP7/AwD+/wEA';
2262 }
2263 else {
2264 addTest('audiopreload', false);
2265 return;
2266 }
2267 }
2268
2269 catch (e) {
2270 addTest('audiopreload', false);
2271 return;
2272 }
2273
2274 elem.setAttribute('preload', 'auto');
2275 elem.style.cssText = 'display:none';
2276 docElement.appendChild(elem);
2277 // wait for the next tick to add the listener, otherwise the element may
2278 // not have time to play in high load situations (e.g. the test suite)
2279 setTimeout(function() {
2280 elem.addEventListener('loadeddata', testpreload, false);
2281 timeout = setTimeout(testpreload, waitTime);
2282 }, 0);
2283 });
2284
2285/*!
2286{
2287 "name": "Web Audio API",
2288 "property": "webaudio",
2289 "caniuse": "audio-api",
2290 "polyfills": ["xaudiojs", "dynamicaudiojs", "audiolibjs"],
2291 "tags": ["audio", "media"],
2292 "builderAliases": ["audio_webaudio_api"],
2293 "authors": ["Addy Osmani"],
2294 "notes": [{
2295 "name": "W3 Specification",
2296 "href": "https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html"
2297 }]
2298}
2299!*/
2300/* DOC
2301Detects the older non standard webaudio API, (as opposed to the standards based AudioContext API)
2302*/
2303
2304 Modernizr.addTest('webaudio', function() {
2305 var prefixed = 'webkitAudioContext' in window;
2306 var unprefixed = 'AudioContext' in window;
2307
2308 if (Modernizr._config.usePrefixes) {
2309 return prefixed || unprefixed;
2310 }
2311 return unprefixed;
2312 });
2313
2314/*!
2315{
2316 "name": "Battery API",
2317 "property": "batteryapi",
2318 "aliases": ["battery-api"],
2319 "builderAliases": ["battery_api"],
2320 "tags": ["device", "media"],
2321 "authors": ["Paul Sayre"],
2322 "notes": [{
2323 "name": "MDN documentation",
2324 "href": "https://developer.mozilla.org/en/DOM/window.navigator.mozBattery"
2325 }]
2326}
2327!*/
2328/* DOC
2329Detect support for the Battery API, for accessing information about the system's battery charge level.
2330*/
2331
2332 Modernizr.addTest('batteryapi', !!prefixed('battery', navigator), {aliases: ['battery-api']});
2333
2334/*!
2335{
2336 "name": "Low Battery Level",
2337 "property": "lowbattery",
2338 "tags": ["hardware", "mobile"],
2339 "builderAliases": ["battery_level"],
2340 "authors": ["Paul Sayre"],
2341 "notes": [{
2342 "name": "MDN Docs",
2343 "href": "https://developer.mozilla.org/en-US/docs/Web/API/Navigator/battery"
2344 }]
2345}
2346!*/
2347/* DOC
2348Enable a developer to remove CPU intensive CSS/JS when battery is low
2349*/
2350
2351 Modernizr.addTest('lowbattery', function() {
2352 var minLevel = 0.20;
2353 var battery = prefixed('battery', navigator);
2354 return !!(battery && !battery.charging && battery.level <= minLevel);
2355 });
2356
2357/*!
2358{
2359 "name": "Blob constructor",
2360 "property": "blobconstructor",
2361 "aliases": ["blob-constructor"],
2362 "builderAliases": ["blob_constructor"],
2363 "caniuse": "blobbuilder",
2364 "notes": [{
2365 "name": "W3C spec",
2366 "href": "https://w3c.github.io/FileAPI/#constructorBlob"
2367 }],
2368 "polyfills": ["blobjs"]
2369}
2370!*/
2371/* DOC
2372Detects support for the Blob constructor, for creating file-like objects of immutable, raw data.
2373*/
2374
2375 Modernizr.addTest('blobconstructor', function() {
2376 try {
2377 return !!new Blob();
2378 } catch (e) {
2379 return false;
2380 }
2381 }, {
2382 aliases: ['blob-constructor']
2383 });
2384
2385/*!
2386{
2387 "name": "Canvas",
2388 "property": "canvas",
2389 "caniuse": "canvas",
2390 "tags": ["canvas", "graphics"],
2391 "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"]
2392}
2393!*/
2394/* DOC
2395Detects support for the `<canvas>` element for 2D drawing.
2396*/
2397
2398 // On the S60 and BB Storm, getContext exists, but always returns undefined
2399 // so we actually have to call getContext() to verify
2400 // github.com/Modernizr/Modernizr/issues/issue/97/
2401 Modernizr.addTest('canvas', function() {
2402 var elem = createElement('canvas');
2403 return !!(elem.getContext && elem.getContext('2d'));
2404 });
2405
2406/*!
2407{
2408 "name": "canvas blending support",
2409 "property": "canvasblending",
2410 "tags": ["canvas"],
2411 "async" : false,
2412 "notes": [{
2413 "name": "HTML5 Spec",
2414 "href": "https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blending"
2415 },
2416 {
2417 "name": "Article",
2418 "href": "https://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas"
2419 }]
2420}
2421!*/
2422/* DOC
2423Detects if Photoshop style blending modes are available in canvas.
2424*/
2425
2426
2427 Modernizr.addTest('canvasblending', function() {
2428 if (Modernizr.canvas === false) {
2429 return false;
2430 }
2431 var ctx = createElement('canvas').getContext('2d');
2432 // firefox 3 throws an error when setting an invalid `globalCompositeOperation`
2433 try {
2434 ctx.globalCompositeOperation = 'screen';
2435 } catch (e) {}
2436
2437 return ctx.globalCompositeOperation === 'screen';
2438 });
2439
2440
2441/*!
2442{
2443 "name": "canvas.toDataURL type support",
2444 "property": ["todataurljpeg", "todataurlpng", "todataurlwebp"],
2445 "tags": ["canvas"],
2446 "builderAliases": ["canvas_todataurl_type"],
2447 "async" : false,
2448 "notes": [{
2449 "name": "MDN article",
2450 "href": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement.toDataURL"
2451 }]
2452}
2453!*/
2454
2455
2456 var canvas = createElement('canvas');
2457
2458 Modernizr.addTest('todataurljpeg', function() {
2459 return !!Modernizr.canvas && canvas.toDataURL('image/jpeg').indexOf('data:image/jpeg') === 0;
2460 });
2461 Modernizr.addTest('todataurlpng', function() {
2462 return !!Modernizr.canvas && canvas.toDataURL('image/png').indexOf('data:image/png') === 0;
2463 });
2464 Modernizr.addTest('todataurlwebp', function() {
2465 var supports = false;
2466
2467 // firefox 3 throws an error when you use an "invalid" toDataUrl
2468 try {
2469 supports = !!Modernizr.canvas && canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
2470 } catch (e) {}
2471
2472 return supports;
2473 });
2474
2475
2476/*!
2477{
2478 "name": "canvas winding support",
2479 "property": ["canvaswinding"],
2480 "tags": ["canvas"],
2481 "async" : false,
2482 "notes": [{
2483 "name": "Article",
2484 "href": "https://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/"
2485 }]
2486}
2487!*/
2488/* DOC
2489Determines if winding rules, which controls if a path can go clockwise or counterclockwise
2490*/
2491
2492
2493 Modernizr.addTest('canvaswinding', function() {
2494 if (Modernizr.canvas === false) {
2495 return false;
2496 }
2497 var ctx = createElement('canvas').getContext('2d');
2498
2499 ctx.rect(0, 0, 10, 10);
2500 ctx.rect(2, 2, 6, 6);
2501 return ctx.isPointInPath(5, 5, 'evenodd') === false;
2502 });
2503
2504
2505/*!
2506{
2507 "name": "Canvas text",
2508 "property": "canvastext",
2509 "caniuse": "canvas-text",
2510 "tags": ["canvas", "graphics"],
2511 "polyfills": ["canvastext"]
2512}
2513!*/
2514/* DOC
2515Detects support for the text APIs for `<canvas>` elements.
2516*/
2517
2518 Modernizr.addTest('canvastext', function() {
2519 if (Modernizr.canvas === false) {
2520 return false;
2521 }
2522 return typeof createElement('canvas').getContext('2d').fillText == 'function';
2523 });
2524
2525/*!
2526{
2527 "name": "Content Editable",
2528 "property": "contenteditable",
2529 "caniuse": "contenteditable",
2530 "notes": [{
2531 "name": "WHATWG spec",
2532 "href": "https://html.spec.whatwg.org/multipage/interaction.html#contenteditable"
2533 }]
2534}
2535!*/
2536/* DOC
2537Detects support for the `contenteditable` attribute of elements, allowing their DOM text contents to be edited directly by the user.
2538*/
2539
2540 Modernizr.addTest('contenteditable', function() {
2541 // early bail out
2542 if (!('contentEditable' in docElement)) {
2543 return;
2544 }
2545
2546 // some mobile browsers (android < 3.0, iOS < 5) claim to support
2547 // contentEditable, but but don't really. This test checks to see
2548 // confirms whether or not it actually supports it.
2549
2550 var div = createElement('div');
2551 div.contentEditable = true;
2552 return div.contentEditable === 'true';
2553 });
2554
2555/*!
2556{
2557 "name": "Context menus",
2558 "property": "contextmenu",
2559 "caniuse": "menu",
2560 "notes": [{
2561 "name": "W3C spec",
2562 "href": "http://www.w3.org/TR/html5/interactive-elements.html#context-menus"
2563 },{
2564 "name": "thewebrocks.com Demo",
2565 "href": "http://thewebrocks.com/demos/context-menu/"
2566 }],
2567 "polyfills": ["jquery-contextmenu"]
2568}
2569!*/
2570/* DOC
2571Detects support for custom context menus.
2572*/
2573
2574 Modernizr.addTest(
2575 'contextmenu',
2576 ('contextMenu' in docElement && 'HTMLMenuItemElement' in window)
2577 );
2578
2579/*!
2580{
2581 "name": "Cookies",
2582 "property": "cookies",
2583 "tags": ["storage"],
2584 "authors": ["tauren"]
2585}
2586!*/
2587/* DOC
2588Detects whether cookie support is enabled.
2589*/
2590
2591 // https://github.com/Modernizr/Modernizr/issues/191
2592
2593 Modernizr.addTest('cookies', function() {
2594 // navigator.cookieEnabled cannot detect custom or nuanced cookie blocking
2595 // configurations. For example, when blocking cookies via the Advanced
2596 // Privacy Settings in IE9, it always returns true. And there have been
2597 // issues in the past with site-specific exceptions.
2598 // Don't rely on it.
2599
2600 // try..catch because some in situations `document.cookie` is exposed but throws a
2601 // SecurityError if you try to access it; e.g. documents created from data URIs
2602 // or in sandboxed iframes (depending on flags/context)
2603 try {
2604 // Create cookie
2605 document.cookie = 'cookietest=1';
2606 var ret = document.cookie.indexOf('cookietest=') != -1;
2607 // Delete cookie
2608 document.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT';
2609 return ret;
2610 }
2611 catch (e) {
2612 return false;
2613 }
2614 });
2615
2616/*!
2617{
2618 "name": "Cross-Origin Resource Sharing",
2619 "property": "cors",
2620 "caniuse": "cors",
2621 "authors": ["Theodoor van Donge"],
2622 "notes": [{
2623 "name": "MDN documentation",
2624 "href": "https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS"
2625 }],
2626 "polyfills": ["pmxdr", "ppx", "flxhr"]
2627}
2628!*/
2629/* DOC
2630Detects support for Cross-Origin Resource Sharing: method of performing XMLHttpRequests across domains.
2631*/
2632
2633 Modernizr.addTest('cors', 'XMLHttpRequest' in window && 'withCredentials' in new XMLHttpRequest());
2634
2635/*!
2636{
2637 "name": "Custom Elements API",
2638 "property": "customelements",
2639 "tags": ["customelements"],
2640 "polyfills": ["customelements"],
2641 "notes": [{
2642 "name": "Specs for Custom Elements",
2643 "href": "https://www.w3.org/TR/custom-elements/"
2644 }]
2645}
2646!*/
2647/* DOC
2648Detects support for the Custom Elements API, to create custom html elements via js
2649*/
2650
2651 Modernizr.addTest('customelements', 'customElements' in window);
2652
2653/*!
2654{
2655 "name": "Web Cryptography",
2656 "property": "cryptography",
2657 "caniuse": "cryptography",
2658 "tags": ["crypto"],
2659 "authors": ["roblarsen"],
2660 "notes": [{
2661 "name": "W3C Editor's Draft",
2662 "href": "https://www.w3.org/TR/WebCryptoAPI/"
2663 }],
2664 "polyfills": [
2665 "polycrypt"
2666 ]
2667}
2668!*/
2669/* DOC
2670Detects support for the cryptographic functionality available under window.crypto.subtle
2671*/
2672
2673 var crypto = prefixed('crypto', window);
2674 Modernizr.addTest('crypto', !!prefixed('subtle', crypto));
2675
2676/*!
2677{
2678 "name": "getRandomValues",
2679 "property": "getrandomvalues",
2680 "caniuse": "window.crypto.getRandomValues",
2681 "tags": ["crypto"],
2682 "authors": ["komachi"],
2683 "notes": [{
2684 "name": "W3C Editor?s Draft",
2685 "href": "https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#RandomSource-method-getRandomValues"
2686 }],
2687 "polyfills": [
2688 "polycrypt"
2689 ]
2690}
2691!*/
2692/* DOC
2693Detects support for the window.crypto.getRandomValues method for generating cryptographically secure random numbers
2694*/
2695
2696 // In Safari <=5.0 `window.crypto` exists (for some reason) but is `undefined`, so we have to check
2697 // it?s truthy before checking for existence of `getRandomValues`
2698 var crypto = prefixed('crypto', window);
2699 var supportsGetRandomValues;
2700
2701 // Safari 6.0 supports crypto.getRandomValues, but does not return the array,
2702 // which is required by the spec, so we need to actually check.
2703 if (crypto && 'getRandomValues' in crypto && 'Uint32Array' in window) {
2704 var array = new Uint32Array(10);
2705 var values = crypto.getRandomValues(array);
2706 supportsGetRandomValues = values && is(values[0], 'number');
2707 }
2708
2709 Modernizr.addTest('getrandomvalues', !!supportsGetRandomValues);
2710
2711/*!
2712{
2713 "name": "cssall",
2714 "property": "cssall",
2715 "notes": [{
2716 "name": "Spec",
2717 "href": "https://drafts.csswg.org/css-cascade/#all-shorthand"
2718 }]
2719}
2720!*/
2721/* DOC
2722Detects support for the `all` css property, which is a shorthand to reset all css properties (except direction and unicode-bidi) to their original value
2723*/
2724
2725
2726 Modernizr.addTest('cssall', 'all' in docElement.style);
2727
2728/*!
2729{
2730 "name": "CSS Animations",
2731 "property": "cssanimations",
2732 "caniuse": "css-animation",
2733 "polyfills": ["transformie", "csssandpaper"],
2734 "tags": ["css"],
2735 "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"],
2736 "notes": [{
2737 "name" : "Article: 'Dispelling the Android CSS animation myths'",
2738 "href": "https://goo.gl/OGw5Gm"
2739 }]
2740}
2741!*/
2742/* DOC
2743Detects whether or not elements can be animated using CSS
2744*/
2745
2746 Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true));
2747
2748/*!
2749{
2750 "name": "Appearance",
2751 "property": "appearance",
2752 "caniuse": "css-appearance",
2753 "tags": ["css"],
2754 "notes": [{
2755 "name": "MDN documentation",
2756 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance"
2757 },{
2758 "name": "CSS-Tricks CSS Almanac: appearance",
2759 "href": "https://css-tricks.com/almanac/properties/a/appearance/"
2760 }]
2761}
2762!*/
2763/* DOC
2764Detects support for the `appearance` css property, which is used to make an
2765element inherit the style of a standard user interface element. It can also be
2766used to remove the default styles of an element, such as input and buttons.
2767*/
2768
2769 Modernizr.addTest('appearance', testAllProps('appearance'));
2770
2771/*!
2772{
2773 "name": "Backdrop Filter",
2774 "property": "backdropfilter",
2775 "authors": ["Brian Seward"],
2776 "tags": ["css"],
2777 "notes": [
2778 {
2779 "name": "W3C Editor?s Draft specification",
2780 "href": "https://drafts.fxtf.org/filters-2/#BackdropFilterProperty"
2781 },
2782 {
2783 "name": "Caniuse for CSS Backdrop Filter",
2784 "href": "http://caniuse.com/#feat=css-backdrop-filter"
2785 },
2786 {
2787 "name": "WebKit Blog introduction + Demo",
2788 "href": "https://www.webkit.org/blog/3632/introducing-backdrop-filters/"
2789 }
2790 ]
2791}
2792!*/
2793/* DOC
2794Detects support for CSS Backdrop Filters, allowing for background blur effects like those introduced in iOS 7. Support for this was added to iOS Safari/WebKit in iOS 9.
2795*/
2796
2797 Modernizr.addTest('backdropfilter', testAllProps('backdropFilter'));
2798
2799/*!
2800{
2801 "name": "CSS Background Blend Mode",
2802 "property": "backgroundblendmode",
2803 "caniuse": "css-backgroundblendmode",
2804 "tags": ["css"],
2805 "notes": [
2806 {
2807 "name": "CSS Blend Modes could be the next big thing in Web Design",
2808 "href": " https://medium.com/@bennettfeely/css-blend-modes-could-be-the-next-big-thing-in-web-design-6b51bf53743a"
2809 }, {
2810 "name": "Demo",
2811 "href": "http://bennettfeely.com/gradients/"
2812 }
2813 ]
2814}
2815!*/
2816/* DOC
2817Detects the ability for the browser to composite backgrounds using blending modes similar to ones found in Photoshop or Illustrator.
2818*/
2819
2820 Modernizr.addTest('backgroundblendmode', prefixed('backgroundBlendMode', 'text'));
2821
2822/*!
2823{
2824 "name": "CSS Background Clip Text",
2825 "property": "backgroundcliptext",
2826 "authors": ["ausi"],
2827 "tags": ["css"],
2828 "notes": [
2829 {
2830 "name": "CSS Tricks Article",
2831 "href": "https://css-tricks.com/image-under-text/"
2832 },
2833 {
2834 "name": "MDN Docs",
2835 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/background-clip"
2836 },
2837 {
2838 "name": "Related Github Issue",
2839 "href": "https://github.com/Modernizr/Modernizr/issues/199"
2840 }
2841 ]
2842}
2843!*/
2844/* DOC
2845Detects the ability to control specifies whether or not an element's background
2846extends beyond its border in CSS
2847*/
2848
2849 Modernizr.addTest('backgroundcliptext', function() {
2850 return testAllProps('backgroundClip', 'text');
2851 });
2852
2853/*!
2854{
2855 "name": "Background Position Shorthand",
2856 "property": "bgpositionshorthand",
2857 "tags": ["css"],
2858 "builderAliases": ["css_backgroundposition_shorthand"],
2859 "notes": [{
2860 "name": "MDN Docs",
2861 "href": "https://developer.mozilla.org/en/CSS/background-position"
2862 }, {
2863 "name": "W3 Spec",
2864 "href": "https://www.w3.org/TR/css3-background/#background-position"
2865 }, {
2866 "name": "Demo",
2867 "href": "https://jsfiddle.net/Blink/bBXvt/"
2868 }]
2869}
2870!*/
2871/* DOC
2872Detects if you can use the shorthand method to define multiple parts of an
2873element's background-position simultaniously.
2874
2875eg `background-position: right 10px bottom 10px`
2876*/
2877
2878 Modernizr.addTest('bgpositionshorthand', function() {
2879 var elem = createElement('a');
2880 var eStyle = elem.style;
2881 var val = 'right 10px bottom 10px';
2882 eStyle.cssText = 'background-position: ' + val + ';';
2883 return (eStyle.backgroundPosition === val);
2884 });
2885
2886/*!
2887{
2888 "name": "Background Position XY",
2889 "property": "bgpositionxy",
2890 "tags": ["css"],
2891 "builderAliases": ["css_backgroundposition_xy"],
2892 "authors": ["Allan Lei", "Brandom Aaron"],
2893 "notes": [{
2894 "name": "Demo",
2895 "href": "https://jsfiddle.net/allanlei/R8AYS/"
2896 }, {
2897 "name": "Adapted From",
2898 "href": "https://github.com/brandonaaron/jquery-cssHooks/blob/master/bgpos.js"
2899 }]
2900}
2901!*/
2902/* DOC
2903Detects the ability to control an element's background position using css
2904*/
2905
2906 Modernizr.addTest('bgpositionxy', function() {
2907 return testAllProps('backgroundPositionX', '3px', true) && testAllProps('backgroundPositionY', '5px', true);
2908 });
2909
2910/*!
2911{
2912 "name": "Background Repeat",
2913 "property": ["bgrepeatspace", "bgrepeatround"],
2914 "tags": ["css"],
2915 "builderAliases": ["css_backgroundrepeat"],
2916 "authors": ["Ryan Seddon"],
2917 "notes": [{
2918 "name": "MDN Docs",
2919 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat"
2920 }, {
2921 "name": "Test Page",
2922 "href": "https://jsbin.com/uzesun/"
2923 }, {
2924 "name": "Demo",
2925 "href": "https://jsfiddle.net/ryanseddon/yMLTQ/6/"
2926 }]
2927}
2928!*/
2929/* DOC
2930Detects the ability to use round and space as properties for background-repeat
2931*/
2932
2933 // Must value-test these
2934 Modernizr.addTest('bgrepeatround', testAllProps('backgroundRepeat', 'round'));
2935 Modernizr.addTest('bgrepeatspace', testAllProps('backgroundRepeat', 'space'));
2936
2937/*!
2938{
2939 "name": "Background Size",
2940 "property": "backgroundsize",
2941 "tags": ["css"],
2942 "knownBugs": ["This will false positive in Opera Mini - https://github.com/Modernizr/Modernizr/issues/396"],
2943 "notes": [{
2944 "name": "Related Issue",
2945 "href": "https://github.com/Modernizr/Modernizr/issues/396"
2946 }]
2947}
2948!*/
2949
2950 Modernizr.addTest('backgroundsize', testAllProps('backgroundSize', '100%', true));
2951
2952/*!
2953{
2954 "name": "Background Size Cover",
2955 "property": "bgsizecover",
2956 "tags": ["css"],
2957 "builderAliases": ["css_backgroundsizecover"],
2958 "notes": [{
2959 "name" : "MDN Docs",
2960 "href": "https://developer.mozilla.org/en/CSS/background-size"
2961 }]
2962}
2963!*/
2964
2965 // Must test value, as this specifically tests the `cover` value
2966 Modernizr.addTest('bgsizecover', testAllProps('backgroundSize', 'cover'));
2967
2968/*!
2969{
2970 "name": "Border Image",
2971 "property": "borderimage",
2972 "caniuse": "border-image",
2973 "polyfills": ["css3pie"],
2974 "knownBugs": ["Android < 2.0 is true, but has a broken implementation"],
2975 "tags": ["css"]
2976}
2977!*/
2978
2979 Modernizr.addTest('borderimage', testAllProps('borderImage', 'url() 1', true));
2980
2981/*!
2982{
2983 "name": "Border Radius",
2984 "property": "borderradius",
2985 "caniuse": "border-radius",
2986 "polyfills": ["css3pie"],
2987 "tags": ["css"],
2988 "notes": [{
2989 "name": "Comprehensive Compat Chart",
2990 "href": "https://muddledramblings.com/table-of-css3-border-radius-compliance"
2991 }]
2992}
2993!*/
2994
2995 Modernizr.addTest('borderradius', testAllProps('borderRadius', '0px', true));
2996
2997/*!
2998{
2999 "name": "Box Shadow",
3000 "property": "boxshadow",
3001 "caniuse": "css-boxshadow",
3002 "tags": ["css"],
3003 "knownBugs": [
3004 "WebOS false positives on this test.",
3005 "The Kindle Silk browser false positives"
3006 ]
3007}
3008!*/
3009
3010 Modernizr.addTest('boxshadow', testAllProps('boxShadow', '1px 1px', true));
3011
3012/*!
3013{
3014 "name": "Box Sizing",
3015 "property": "boxsizing",
3016 "caniuse": "css3-boxsizing",
3017 "polyfills": ["borderboxmodel", "boxsizingpolyfill", "borderbox"],
3018 "tags": ["css"],
3019 "builderAliases": ["css_boxsizing"],
3020 "notes": [{
3021 "name": "MDN Docs",
3022 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing"
3023 },{
3024 "name": "Related Github Issue",
3025 "href": "https://github.com/Modernizr/Modernizr/issues/248"
3026 }]
3027}
3028!*/
3029
3030 Modernizr.addTest('boxsizing', testAllProps('boxSizing', 'border-box', true) && (document.documentMode === undefined || document.documentMode > 7));
3031
3032/*!
3033{
3034 "name": "CSS Calc",
3035 "property": "csscalc",
3036 "caniuse": "calc",
3037 "tags": ["css"],
3038 "builderAliases": ["css_calc"],
3039 "authors": ["@calvein"]
3040}
3041!*/
3042/* DOC
3043Method of allowing calculated values for length units. For example:
3044
3045```css
3046//lem {
3047 width: calc(100% - 3em);
3048}
3049```
3050*/
3051
3052 Modernizr.addTest('csscalc', function() {
3053 var prop = 'width:';
3054 var value = 'calc(10px);';
3055 var el = createElement('a');
3056
3057 el.style.cssText = prop + prefixes.join(value + prop);
3058
3059 return !!el.style.length;
3060 });
3061
3062/*!
3063{
3064 "name": "CSS :checked pseudo-selector",
3065 "caniuse": "css-sel3",
3066 "property": "checked",
3067 "tags": ["css"],
3068 "notes": [{
3069 "name": "Related Github Issue",
3070 "href": "https://github.com/Modernizr/Modernizr/pull/879"
3071 }]
3072}
3073!*/
3074
3075 Modernizr.addTest('checked', function() {
3076 return testStyles('#modernizr {position:absolute} #modernizr input {margin-left:10px} #modernizr :checked {margin-left:20px;display:block}', function(elem) {
3077 var cb = createElement('input');
3078 cb.setAttribute('type', 'checkbox');
3079 cb.setAttribute('checked', 'checked');
3080 elem.appendChild(cb);
3081 return cb.offsetLeft === 20;
3082 });
3083 });
3084
3085/*!
3086{
3087 "name": "CSS Font ch Units",
3088 "authors": ["Ron Waldon (@jokeyrhyme)"],
3089 "property": "csschunit",
3090 "tags": ["css"],
3091 "notes": [{
3092 "name": "W3C Spec",
3093 "href": "https://www.w3.org/TR/css3-values/#font-relative-lengths"
3094 }]
3095}
3096!*/
3097
3098 Modernizr.addTest('csschunit', function() {
3099 var elemStyle = modElem.elem.style;
3100 var supports;
3101 try {
3102 elemStyle.fontSize = '3ch';
3103 supports = elemStyle.fontSize.indexOf('ch') !== -1;
3104 } catch (e) {
3105 supports = false;
3106 }
3107 return supports;
3108 });
3109
3110/*!
3111{
3112 "name": "CSS Columns",
3113 "property": "csscolumns",
3114 "caniuse": "multicolumn",
3115 "polyfills": ["css3multicolumnjs"],
3116 "tags": ["css"]
3117}
3118!*/
3119
3120
3121 (function() {
3122
3123 Modernizr.addTest('csscolumns', function() {
3124 var bool = false;
3125 var test = testAllProps('columnCount');
3126 try {
3127 bool = !!test
3128 if (bool) {
3129 bool = new Boolean(bool);
3130 }
3131 } catch (e) {}
3132
3133 return bool;
3134 });
3135
3136 var props = ['Width', 'Span', 'Fill', 'Gap', 'Rule', 'RuleColor', 'RuleStyle', 'RuleWidth', 'BreakBefore', 'BreakAfter', 'BreakInside'];
3137 var name, test;
3138
3139 for (var i = 0; i < props.length; i++) {
3140 name = props[i].toLowerCase();
3141 test = testAllProps('column' + props[i]);
3142
3143 // break-before, break-after & break-inside are not "column"-prefixed in spec
3144 if (name === 'breakbefore' || name === 'breakafter' || name == 'breakinside') {
3145 test = test || testAllProps(props[i]);
3146 }
3147
3148 Modernizr.addTest('csscolumns.' + name, test);
3149 }
3150
3151
3152 })();
3153
3154
3155/*!
3156{
3157 "name": "CSS Grid (old & new)",
3158 "property": ["cssgrid", "cssgridlegacy"],
3159 "authors": ["Faruk Ates"],
3160 "tags": ["css"],
3161 "notes": [{
3162 "name": "The new, standardized CSS Grid",
3163 "href": "https://www.w3.org/TR/css3-grid-layout/"
3164 }, {
3165 "name": "The _old_ CSS Grid (legacy)",
3166 "href": "https://www.w3.org/TR/2011/WD-css3-grid-layout-20110407/"
3167 }]
3168}
3169!*/
3170
3171 // `grid-columns` is only in the old syntax, `grid-column` exists in both and so `grid-template-rows` is used for the new syntax.
3172 Modernizr.addTest('cssgridlegacy', testAllProps('grid-columns', '10px', true));
3173 Modernizr.addTest('cssgrid', testAllProps('grid-template-rows', 'none', true));
3174
3175/*!
3176{
3177 "name": "CSS Cubic Bezier Range",
3178 "property": "cubicbezierrange",
3179 "tags": ["css"],
3180 "builderAliases": ["css_cubicbezierrange"],
3181 "doc" : null,
3182 "authors": ["@calvein"],
3183 "warnings": ["cubic-bezier values can't be > 1 for Webkit until [bug #45761](https://bugs.webkit.org/show_bug.cgi?id=45761) is fixed"],
3184 "notes": [{
3185 "name": "Comprehensive Compat Chart",
3186 "href": "http://muddledramblings.com/table-of-css3-border-radius-compliance"
3187 }]
3188}
3189!*/
3190
3191 Modernizr.addTest('cubicbezierrange', function() {
3192 var el = createElement('a');
3193 el.style.cssText = prefixes.join('transition-timing-function:cubic-bezier(1,0,0,1.1); ');
3194 return !!el.style.length;
3195 });
3196
3197/*!
3198{
3199 "name": "CSS Display run-in",
3200 "property": "display-runin",
3201 "authors": ["alanhogan"],
3202 "tags": ["css"],
3203 "builderAliases": ["css_displayrunin"],
3204 "notes": [{
3205 "name": "CSS Tricks Article",
3206 "href": "https://css-tricks.com/596-run-in/"
3207 },{
3208 "name": "Related Github Issue",
3209 "href": "https://github.com/Modernizr/Modernizr/issues/198"
3210 }]
3211}
3212!*/
3213
3214 Modernizr.addTest('displayrunin', testAllProps('display', 'run-in'),
3215 {aliases: ['display-runin']});
3216
3217/*!
3218{
3219 "name": "CSS Display table",
3220 "property": "displaytable",
3221 "caniuse": "css-table",
3222 "authors": ["scottjehl"],
3223 "tags": ["css"],
3224 "builderAliases": ["css_displaytable"],
3225 "notes": [{
3226 "name": "Detects for all additional table display values",
3227 "href": "http://pastebin.com/Gk9PeVaQ"
3228 }]
3229}
3230!*/
3231/* DOC
3232`display: table` and `table-cell` test. (both are tested under one name `table-cell` )
3233*/
3234
3235 // If a document is in rtl mode this test will fail so we force ltr mode on the injeced
3236 // element https://github.com/Modernizr/Modernizr/issues/716
3237 testStyles('#modernizr{display: table; direction: ltr}#modernizr div{display: table-cell; padding: 10px}', function(elem) {
3238 var ret;
3239 var child = elem.childNodes;
3240 ret = child[0].offsetLeft < child[1].offsetLeft;
3241 Modernizr.addTest('displaytable', ret, {aliases: ['display-table']});
3242 }, 2);
3243
3244/*!
3245{
3246 "name": "CSS text-overflow ellipsis",
3247 "property": "ellipsis",
3248 "caniuse": "text-overflow",
3249 "polyfills": [
3250 "text-overflow"
3251 ],
3252 "tags": ["css"]
3253}
3254!*/
3255
3256 Modernizr.addTest('ellipsis', testAllProps('textOverflow', 'ellipsis'));
3257
3258/*!
3259{
3260 "name": "CSS.escape()",
3261 "property": "cssescape",
3262 "polyfills": [
3263 "css-escape"
3264 ],
3265 "tags": [
3266 "css",
3267 "cssom"
3268 ]
3269}
3270!*/
3271/* DOC
3272Tests for `CSS.escape()` support.
3273*/
3274
3275 var CSS = window.CSS;
3276 Modernizr.addTest('cssescape', CSS ? typeof CSS.escape == 'function' : false);
3277
3278/*!
3279{
3280 "name": "CSS Font ex Units",
3281 "authors": ["Ron Waldon (@jokeyrhyme)"],
3282 "property": "cssexunit",
3283 "tags": ["css"],
3284 "notes": [{
3285 "name": "W3C Spec",
3286 "href": "https://www.w3.org/TR/css3-values/#font-relative-lengths"
3287 }]
3288}
3289!*/
3290
3291 Modernizr.addTest('cssexunit', function() {
3292 var elemStyle = modElem.elem.style;
3293 var supports;
3294 try {
3295 elemStyle.fontSize = '3ex';
3296 supports = elemStyle.fontSize.indexOf('ex') !== -1;
3297 } catch (e) {
3298 supports = false;
3299 }
3300 return supports;
3301 });
3302
3303/*!
3304{
3305 "name": "CSS Supports",
3306 "property": "supports",
3307 "caniuse": "css-featurequeries",
3308 "tags": ["css"],
3309 "builderAliases": ["css_supports"],
3310 "notes": [{
3311 "name": "W3 Spec",
3312 "href": "http://dev.w3.org/csswg/css3-conditional/#at-supports"
3313 },{
3314 "name": "Related Github Issue",
3315 "href": "https://github.com/Modernizr/Modernizr/issues/648"
3316 },{
3317 "name": "W3 Info",
3318 "href": "http://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface"
3319 }]
3320}
3321!*/
3322
3323 var newSyntax = 'CSS' in window && 'supports' in window.CSS;
3324 var oldSyntax = 'supportsCSS' in window;
3325 Modernizr.addTest('supports', newSyntax || oldSyntax);
3326
3327/*!
3328{
3329 "name": "CSS Filters",
3330 "property": "cssfilters",
3331 "caniuse": "css-filters",
3332 "polyfills": ["polyfilter"],
3333 "tags": ["css"],
3334 "builderAliases": ["css_filters"],
3335 "notes": [{
3336 "name": "MDN article on CSS filters",
3337 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/filter"
3338 }]
3339}
3340!*/
3341
3342 Modernizr.addTest('cssfilters', function() {
3343 if (Modernizr.supports) {
3344 return testAllProps('filter', 'blur(2px)');
3345 } else {
3346 var el = createElement('a');
3347 el.style.cssText = prefixes.join('filter:blur(2px); ');
3348 // https://github.com/Modernizr/Modernizr/issues/615
3349 // documentMode is needed for false positives in oldIE, please see issue above
3350 return !!el.style.length && ((document.documentMode === undefined || document.documentMode > 9));
3351 }
3352 });
3353
3354
3355/*!
3356{
3357 "name": "Flexbox",
3358 "property": "flexbox",
3359 "caniuse": "flexbox",
3360 "tags": ["css"],
3361 "notes": [{
3362 "name": "The _new_ flexbox",
3363 "href": "http://dev.w3.org/csswg/css3-flexbox"
3364 }],
3365 "warnings": [
3366 "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect."
3367 ]
3368}
3369!*/
3370/* DOC
3371Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container.
3372*/
3373
3374 Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', true));
3375
3376/*!
3377{
3378 "name": "Flexbox (legacy)",
3379 "property": "flexboxlegacy",
3380 "tags": ["css"],
3381 "polyfills": ["flexie"],
3382 "notes": [{
3383 "name": "The _old_ flexbox",
3384 "href": "https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/"
3385 }]
3386}
3387!*/
3388
3389 Modernizr.addTest('flexboxlegacy', testAllProps('boxDirection', 'reverse', true));
3390
3391/*!
3392{
3393 "name": "Flexbox (tweener)",
3394 "property": "flexboxtweener",
3395 "tags": ["css"],
3396 "polyfills": ["flexie"],
3397 "notes": [{
3398 "name": "The _inbetween_ flexbox",
3399 "href": "https://www.w3.org/TR/2011/WD-css3-flexbox-20111129/"
3400 }],
3401 "warnings": ["This represents an old syntax, not the latest standard syntax."]
3402}
3403!*/
3404
3405 Modernizr.addTest('flexboxtweener', testAllProps('flexAlign', 'end', true));
3406
3407/*!
3408{
3409 "name": "Flex Line Wrapping",
3410 "property": "flexwrap",
3411 "tags": ["css", "flexbox"],
3412 "notes": [{
3413 "name": "W3C Flexible Box Layout spec",
3414 "href": "http://dev.w3.org/csswg/css3-flexbox"
3415 }],
3416 "warnings": [
3417 "Does not imply a modern implementation ? see documentation."
3418 ]
3419}
3420!*/
3421/* DOC
3422Detects support for the `flex-wrap` CSS property, part of Flexbox, which isn?t present in all Flexbox implementations (notably Firefox).
3423
3424This featured in both the 'tweener' syntax (implemented by IE10) and the 'modern' syntax (implemented by others). This detect will return `true` for either of these implementations, as long as the `flex-wrap` property is supported. So to ensure the modern syntax is supported, use together with `Modernizr.flexbox`:
3425
3426```javascript
3427if (Modernizr.flexbox && Modernizr.flexwrap) {
3428 // Modern Flexbox with `flex-wrap` supported
3429}
3430else {
3431 // Either old Flexbox syntax, or `flex-wrap` not supported
3432}
3433```
3434*/
3435
3436 Modernizr.addTest('flexwrap', testAllProps('flexWrap', 'wrap', true));
3437
3438/*!
3439{
3440 "name": "@font-face",
3441 "property": "fontface",
3442 "authors": ["Diego Perini", "Mat Marquis"],
3443 "tags": ["css"],
3444 "knownBugs": [
3445 "False Positive: WebOS https://github.com/Modernizr/Modernizr/issues/342",
3446 "False Postive: WP7 https://github.com/Modernizr/Modernizr/issues/538"
3447 ],
3448 "notes": [{
3449 "name": "@font-face detection routine by Diego Perini",
3450 "href": "http://javascript.nwbox.com/CSSSupport/"
3451 },{
3452 "name": "Filament Group @font-face compatibility research",
3453 "href": "https://docs.google.com/presentation/d/1n4NyG4uPRjAA8zn_pSQ_Ket0RhcWC6QlZ6LMjKeECo0/edit#slide=id.p"
3454 },{
3455 "name": "Filament Grunticon/@font-face device testing results",
3456 "href": "https://docs.google.com/spreadsheet/ccc?key=0Ag5_yGvxpINRdHFYeUJPNnZMWUZKR2ItMEpRTXZPdUE#gid=0"
3457 },{
3458 "name": "CSS fonts on Android",
3459 "href": "https://stackoverflow.com/questions/3200069/css-fonts-on-android"
3460 },{
3461 "name": "@font-face and Android",
3462 "href": "http://archivist.incutio.com/viewlist/css-discuss/115960"
3463 }]
3464}
3465!*/
3466
3467 var blacklist = (function() {
3468 var ua = navigator.userAgent;
3469 var webos = ua.match(/w(eb)?osbrowser/gi);
3470 var wppre8 = ua.match(/windows phone/gi) && ua.match(/iemobile\/([0-9])+/gi) && parseFloat(RegExp.$1) >= 9;
3471 return webos || wppre8;
3472 }());
3473 if (blacklist) {
3474 Modernizr.addTest('fontface', false);
3475 } else {
3476 testStyles('@font-face {font-family:"font";src:url("https://")}', function(node, rule) {
3477 var style = document.getElementById('smodernizr');
3478 var sheet = style.sheet || style.styleSheet;
3479 var cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
3480 var bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
3481 Modernizr.addTest('fontface', bool);
3482 });
3483 }
3484;
3485/*!
3486{
3487 "name": "CSS Generated Content",
3488 "property": "generatedcontent",
3489 "tags": ["css"],
3490 "warnings": ["Android won't return correct height for anything below 7px #738"],
3491 "notes": [{
3492 "name": "W3C CSS Selectors Level 3 spec",
3493 "href": "https://www.w3.org/TR/css3-selectors/#gen-content"
3494 },{
3495 "name": "MDN article on :before",
3496 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/::before"
3497 },{
3498 "name": "MDN article on :after",
3499 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/::before"
3500 }]
3501}
3502!*/
3503
3504 testStyles('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}', function(node) {
3505 // See bug report on why this value is 6 crbug.com/608142
3506 Modernizr.addTest('generatedcontent', node.offsetHeight >= 6);
3507 });
3508
3509/*!
3510{
3511 "name": "CSS Gradients",
3512 "caniuse": "css-gradients",
3513 "property": "cssgradients",
3514 "tags": ["css"],
3515 "knownBugs": ["False-positives on webOS (https://github.com/Modernizr/Modernizr/issues/202)"],
3516 "notes": [{
3517 "name": "Webkit Gradient Syntax",
3518 "href": "https://webkit.org/blog/175/introducing-css-gradients/"
3519 },{
3520 "name": "Linear Gradient Syntax",
3521 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient"
3522 },{
3523 "name": "W3C Gradient Spec",
3524 "href": "https://drafts.csswg.org/css-images-3/#gradients"
3525 }]
3526}
3527!*/
3528
3529
3530 Modernizr.addTest('cssgradients', function() {
3531
3532 var str1 = 'background-image:';
3533 var str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));';
3534 var css = '';
3535 var angle;
3536
3537 for (var i = 0, len = prefixes.length - 1; i < len; i++) {
3538 angle = (i === 0 ? 'to ' : '');
3539 css += str1 + prefixes[i] + 'linear-gradient(' + angle + 'left top, #9f9, white);';
3540 }
3541
3542 if (Modernizr._config.usePrefixes) {
3543 // legacy webkit syntax (FIXME: remove when syntax not in use anymore)
3544 css += str1 + '-webkit-' + str2;
3545 }
3546
3547 var elem = createElement('a');
3548 var style = elem.style;
3549 style.cssText = css;
3550
3551 // IE6 returns undefined so cast to string
3552 return ('' + style.backgroundImage).indexOf('gradient') > -1;
3553 });
3554
3555/*! {
3556 "name": "CSS Hairline",
3557 "property": "hairline",
3558 "tags": ["css"],
3559 "authors": ["strarsis"],
3560 "notes": [{
3561 "name": "Blog post about CSS retina hairlines",
3562 "href": "http://dieulot.net/css-retina-hairline"
3563 },{
3564 "name": "Derived from",
3565 "href": "https://gist.github.com/dieulot/520a49463f6058fbc8d1"
3566 }]
3567}
3568!*/
3569/* DOC
3570Detects support for hidpi/retina hairlines, which are CSS borders with less than 1px in width, for being physically 1px on hidpi screens.
3571*/
3572
3573
3574 Modernizr.addTest('hairline', function() {
3575 return testStyles('#modernizr {border:.5px solid transparent}', function(elem) {
3576 return elem.offsetHeight === 1;
3577 });
3578 });
3579
3580/*!
3581{
3582 "name": "CSS HSLA Colors",
3583 "caniuse": "css3-colors",
3584 "property": "hsla",
3585 "tags": ["css"]
3586}
3587!*/
3588
3589 Modernizr.addTest('hsla', function() {
3590 var style = createElement('a').style;
3591 style.cssText = 'background-color:hsla(120,40%,100%,.5)';
3592 return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');
3593 });
3594
3595/*!
3596{
3597 "name": "CSS Hyphens",
3598 "caniuse": "css-hyphens",
3599 "property": ["csshyphens", "softhyphens", "softhyphensfind"],
3600 "tags": ["css"],
3601 "builderAliases": ["css_hyphens"],
3602 "async" : true,
3603 "authors": ["David Newton"],
3604 "warnings": [
3605 "These tests currently require document.body to be present",
3606 "If loading Hyphenator.js via yepnope, be cautious of issue 158: http://code.google.com/p/hyphenator/issues/detail?id=158",
3607 "This is very large ? only include it if you absolutely need it"
3608 ],
3609 "notes": [{
3610 "name": "The Current State of Hyphenation on the Web.",
3611 "href": "http://davidnewton.ca/the-current-state-of-hyphenation-on-the-web"
3612 },{
3613 "name": "Hyphenation Test Page",
3614 "href": "http://davidnewton.ca/demos/hyphenation/test.html"
3615 },{
3616 "name": "Hyphenation is Language Specific",
3617 "href": " http://code.google.com/p/hyphenator/source/diff?spec=svn975&r=975&format=side&path=/trunk/Hyphenator.js#sc_svn975_313"
3618 },{
3619 "name": "Related Modernizr Issue",
3620 "href": "https://github.com/Modernizr/Modernizr/issues/312"
3621 }]
3622}
3623!*/
3624
3625
3626 Modernizr.addAsyncTest(function() {
3627 var waitTime = 300;
3628 setTimeout(runHyphenTest, waitTime);
3629 // Wait 1000ms so we can hope for document.body
3630 function runHyphenTest() {
3631 if (!document.body && !document.getElementsByTagName('body')[0]) {
3632 setTimeout(runHyphenTest, waitTime);
3633 return;
3634 }
3635
3636 // functional test of adding hyphens:auto
3637 function test_hyphens_css() {
3638 try {
3639 /* create a div container and a span within that
3640 * these have to be appended to document.body, otherwise some browsers can give false negative */
3641 var div = createElement('div');
3642 var span = createElement('span');
3643 var divStyle = div.style;
3644 var spanHeight = 0;
3645 var spanWidth = 0;
3646 var result = false;
3647 var firstChild = document.body.firstElementChild || document.body.firstChild;
3648
3649 div.appendChild(span);
3650 span.innerHTML = 'Bacon ipsum dolor sit amet jerky velit in culpa hamburger et. Laborum dolor proident, enim dolore duis commodo et strip steak. Salami anim et, veniam consectetur dolore qui tenderloin jowl velit sirloin. Et ad culpa, fatback cillum jowl ball tip ham hock nulla short ribs pariatur aute. Pig pancetta ham bresaola, ut boudin nostrud commodo flank esse cow tongue culpa. Pork belly bresaola enim pig, ea consectetur nisi. Fugiat officia turkey, ea cow jowl pariatur ullamco proident do laborum velit sausage. Magna biltong sint tri-tip commodo sed bacon, esse proident aliquip. Ullamco ham sint fugiat, velit in enim sed mollit nulla cow ut adipisicing nostrud consectetur. Proident dolore beef ribs, laborum nostrud meatball ea laboris rump cupidatat labore culpa. Shankle minim beef, velit sint cupidatat fugiat tenderloin pig et ball tip. Ut cow fatback salami, bacon ball tip et in shank strip steak bresaola. In ut pork belly sed mollit tri-tip magna culpa veniam, short ribs qui in andouille ham consequat. Dolore bacon t-bone, velit short ribs enim strip steak nulla. Voluptate labore ut, biltong swine irure jerky. Cupidatat excepteur aliquip salami dolore. Ball tip strip steak in pork dolor. Ad in esse biltong. Dolore tenderloin exercitation ad pork loin t-bone, dolore in chicken ball tip qui pig. Ut culpa tongue, sint ribeye dolore ex shank voluptate hamburger. Jowl et tempor, boudin pork chop labore ham hock drumstick consectetur tri-tip elit swine meatball chicken ground round. Proident shankle mollit dolore. Shoulder ut duis t-bone quis reprehenderit. Meatloaf dolore minim strip steak, laboris ea aute bacon beef ribs elit shank in veniam drumstick qui. Ex laboris meatball cow tongue pork belly. Ea ball tip reprehenderit pig, sed fatback boudin dolore flank aliquip laboris eu quis. Beef ribs duis beef, cow corned beef adipisicing commodo nisi deserunt exercitation. Cillum dolor t-bone spare ribs, ham hock est sirloin. Brisket irure meatloaf in, boudin pork belly sirloin ball tip. Sirloin sint irure nisi nostrud aliqua. Nostrud nulla aute, enim officia culpa ham hock. Aliqua reprehenderit dolore sunt nostrud sausage, ea boudin pork loin ut t-bone ham tempor. Tri-tip et pancetta drumstick laborum. Ham hock magna do nostrud in proident. Ex ground round fatback, venison non ribeye in.';
3651
3652 document.body.insertBefore(div, firstChild);
3653
3654 /* get size of unhyphenated text */
3655 divStyle.cssText = 'position:absolute;top:0;left:0;width:5em;text-align:justify;text-justification:newspaper;';
3656 spanHeight = span.offsetHeight;
3657 spanWidth = span.offsetWidth;
3658
3659 /* compare size with hyphenated text */
3660 divStyle.cssText = 'position:absolute;top:0;left:0;width:5em;text-align:justify;' +
3661 'text-justification:newspaper;' +
3662 prefixes.join('hyphens:auto; ');
3663
3664 result = (span.offsetHeight != spanHeight || span.offsetWidth != spanWidth);
3665
3666 /* results and cleanup */
3667 document.body.removeChild(div);
3668 div.removeChild(span);
3669
3670 return result;
3671 } catch (e) {
3672 return false;
3673 }
3674 }
3675
3676 // for the softhyphens test
3677 function test_hyphens(delimiter, testWidth) {
3678 try {
3679 /* create a div container and a span within that
3680 * these have to be appended to document.body, otherwise some browsers can give false negative */
3681 var div = createElement('div');
3682 var span = createElement('span');
3683 var divStyle = div.style;
3684 var spanSize = 0;
3685 var result = false;
3686 var result1 = false;
3687 var result2 = false;
3688 var firstChild = document.body.firstElementChild || document.body.firstChild;
3689
3690 divStyle.cssText = 'position:absolute;top:0;left:0;overflow:visible;width:1.25em;';
3691 div.appendChild(span);
3692 document.body.insertBefore(div, firstChild);
3693
3694
3695 /* get height of unwrapped text */
3696 span.innerHTML = 'mm';
3697 spanSize = span.offsetHeight;
3698
3699 /* compare height w/ delimiter, to see if it wraps to new line */
3700 span.innerHTML = 'm' + delimiter + 'm';
3701 result1 = (span.offsetHeight > spanSize);
3702
3703 /* if we're testing the width too (i.e. for soft-hyphen, not zws),
3704 * this is because tested Blackberry devices will wrap the text but not display the hyphen */
3705 if (testWidth) {
3706 /* get width of wrapped, non-hyphenated text */
3707 span.innerHTML = 'm<br />m';
3708 spanSize = span.offsetWidth;
3709
3710 /* compare width w/ wrapped w/ delimiter to see if hyphen is present */
3711 span.innerHTML = 'm' + delimiter + 'm';
3712 result2 = (span.offsetWidth > spanSize);
3713 } else {
3714 result2 = true;
3715 }
3716
3717 /* results and cleanup */
3718 if (result1 === true && result2 === true) { result = true; }
3719 document.body.removeChild(div);
3720 div.removeChild(span);
3721
3722 return result;
3723 } catch (e) {
3724 return false;
3725 }
3726 }
3727
3728 // testing if in-browser Find functionality will work on hyphenated text
3729 function test_hyphens_find(delimiter) {
3730 try {
3731 /* create a dummy input for resetting selection location, and a div container
3732 * these have to be appended to document.body, otherwise some browsers can give false negative
3733 * div container gets the doubled testword, separated by the delimiter
3734 * Note: giving a width to div gives false positive in iOS Safari */
3735 var dummy = createElement('input');
3736 var div = createElement('div');
3737 var testword = 'lebowski';
3738 var result = false;
3739 var textrange;
3740 var firstChild = document.body.firstElementChild || document.body.firstChild;
3741
3742 div.innerHTML = testword + delimiter + testword;
3743
3744 document.body.insertBefore(div, firstChild);
3745 document.body.insertBefore(dummy, div);
3746
3747
3748 /* reset the selection to the dummy input element, i.e. BEFORE the div container
3749 * stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area */
3750 if (dummy.setSelectionRange) {
3751 dummy.focus();
3752 dummy.setSelectionRange(0, 0);
3753 } else if (dummy.createTextRange) {
3754 textrange = dummy.createTextRange();
3755 textrange.collapse(true);
3756 textrange.moveEnd('character', 0);
3757 textrange.moveStart('character', 0);
3758 textrange.select();
3759 }
3760
3761 /* try to find the doubled testword, without the delimiter */
3762 try {
3763 if (window.find) {
3764 result = window.find(testword + testword);
3765 } else {
3766 textrange = window.self.document.body.createTextRange();
3767 result = textrange.findText(testword + testword);
3768 }
3769 } catch (e) {
3770 result = false;
3771 }
3772
3773 document.body.removeChild(div);
3774 document.body.removeChild(dummy);
3775
3776 return result;
3777 } catch (e) {
3778 return false;
3779 }
3780 }
3781
3782 addTest('csshyphens', function() {
3783
3784 if (!testAllProps('hyphens', 'auto', true)) {
3785 return false;
3786 }
3787
3788 /* Chrome lies about its hyphens support so we need a more robust test
3789 crbug.com/107111
3790 */
3791 try {
3792 return test_hyphens_css();
3793 } catch (e) {
3794 return false;
3795 }
3796 });
3797
3798 addTest('softhyphens', function() {
3799 try {
3800 // use numeric entity instead of ­ in case it's XHTML
3801 return test_hyphens('­', true) && test_hyphens('​', false);
3802 } catch (e) {
3803 return false;
3804 }
3805 });
3806
3807 addTest('softhyphensfind', function() {
3808 try {
3809 return test_hyphens_find('­') && test_hyphens_find('​');
3810 } catch (e) {
3811 return false;
3812 }
3813 });
3814
3815 }
3816 });
3817
3818/*!
3819{
3820 "name": "CSS :invalid pseudo-class",
3821 "property": "cssinvalid",
3822 "notes": [{
3823 "name": "MDN documentation",
3824 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid"
3825 }]
3826}
3827!*/
3828/* DOC
3829 Detects support for the ':invalid' CSS pseudo-class.
3830*/
3831
3832 Modernizr.addTest('cssinvalid', function() {
3833 return testStyles('#modernizr input{height:0;border:0;padding:0;margin:0;width:10px} #modernizr input:invalid{width:50px}', function(elem) {
3834 var input = createElement('input');
3835 input.required = true;
3836 elem.appendChild(input);
3837 return input.clientWidth > 10;
3838 });
3839 });
3840
3841/*!
3842{
3843 "name": "CSS :last-child pseudo-selector",
3844 "caniuse": "css-sel3",
3845 "property": "lastchild",
3846 "tags": ["css"],
3847 "builderAliases": ["css_lastchild"],
3848 "notes": [{
3849 "name": "Related Github Issue",
3850 "href": "https://github.com/Modernizr/Modernizr/pull/304"
3851 }]
3852}
3853!*/
3854
3855 testStyles('#modernizr div {width:100px} #modernizr :last-child{width:200px;display:block}', function(elem) {
3856 Modernizr.addTest('lastchild', elem.lastChild.offsetWidth > elem.firstChild.offsetWidth);
3857 }, 2);
3858
3859/*!
3860{
3861 "name": "CSS Mask",
3862 "caniuse": "css-masks",
3863 "property": "cssmask",
3864 "tags": ["css"],
3865 "builderAliases": ["css_mask"],
3866 "notes": [
3867 {
3868 "name": "Webkit blog on CSS Masks",
3869 "href": "https://webkit.org/blog/181/css-masks/"
3870 },
3871 {
3872 "name": "Safari Docs",
3873 "href": "https://developer.apple.com/library/safari/#documentation/InternetWeb/Conceptual/SafariVisualEffectsProgGuide/Masks/Masks.html"
3874 },
3875 {
3876 "name": "CSS SVG mask",
3877 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/mask"
3878 },
3879 {
3880 "name": "Combine with clippaths for awesomeness",
3881 "href": "https://generic.cx/for/webkit/test.html"
3882 }
3883 ]
3884}
3885!*/
3886
3887 Modernizr.addTest('cssmask', testAllProps('maskRepeat', 'repeat-x', true));
3888
3889/*!
3890{
3891 "name": "CSS Media Queries",
3892 "caniuse": "css-mediaqueries",
3893 "property": "mediaqueries",
3894 "tags": ["css"],
3895 "builderAliases": ["css_mediaqueries"]
3896}
3897!*/
3898
3899 Modernizr.addTest('mediaqueries', mq('only all'));
3900
3901/*!
3902{
3903 "name": "CSS Multiple Backgrounds",
3904 "caniuse": "multibackgrounds",
3905 "property": "multiplebgs",
3906 "tags": ["css"]
3907}
3908!*/
3909
3910 // Setting multiple images AND a color on the background shorthand property
3911 // and then querying the style.background property value for the number of
3912 // occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
3913
3914 Modernizr.addTest('multiplebgs', function() {
3915 var style = createElement('a').style;
3916 style.cssText = 'background:url(https://),url(https://),red url(https://)';
3917
3918 // If the UA supports multiple backgrounds, there should be three occurrences
3919 // of the string "url(" in the return value for elemStyle.background
3920 return (/(url\s*\(.*?){3}/).test(style.background);
3921 });
3922
3923/*!
3924{
3925 "name": "CSS :nth-child pseudo-selector",
3926 "caniuse": "css-sel3",
3927 "property": "nthchild",
3928 "tags": ["css"],
3929 "notes": [
3930 {
3931 "name": "Related Github Issue",
3932 "href": "https://github.com/Modernizr/Modernizr/pull/685"
3933 },
3934 {
3935 "name": "Sitepoint :nth-child documentation",
3936 "href": "http://reference.sitepoint.com/css/pseudoclass-nthchild"
3937 }
3938 ],
3939 "authors": ["@emilchristensen"],
3940 "warnings": ["Known false negative in Safari 3.1 and Safari 3.2.2"]
3941}
3942!*/
3943/* DOC
3944Detects support for the ':nth-child()' CSS pseudo-selector.
3945*/
3946
3947 // 5 `<div>` elements with `1px` width are created.
3948 // Then every other element has its `width` set to `2px`.
3949 // A Javascript loop then tests if the `<div>`s have the expected width
3950 // using the modulus operator.
3951 testStyles('#modernizr div {width:1px} #modernizr div:nth-child(2n) {width:2px;}', function(elem) {
3952 var elems = elem.getElementsByTagName('div');
3953 var correctWidths = true;
3954
3955 for (var i = 0; i < 5; i++) {
3956 correctWidths = correctWidths && elems[i].offsetWidth === i % 2 + 1;
3957 }
3958 Modernizr.addTest('nthchild', correctWidths);
3959 }, 5);
3960
3961/*!
3962{
3963 "name": "CSS Object Fit",
3964 "caniuse": "object-fit",
3965 "property": "objectfit",
3966 "tags": ["css"],
3967 "builderAliases": ["css_objectfit"],
3968 "notes": [{
3969 "name": "Opera Article on Object Fit",
3970 "href": "https://dev.opera.com/articles/css3-object-fit-object-position/"
3971 }]
3972}
3973!*/
3974
3975 Modernizr.addTest('objectfit', !!prefixed('objectFit'), {aliases: ['object-fit']});
3976
3977/*!
3978{
3979 "name": "CSS Opacity",
3980 "caniuse": "css-opacity",
3981 "property": "opacity",
3982 "tags": ["css"]
3983}
3984!*/
3985
3986 // Browsers that actually have CSS Opacity implemented have done so
3987 // according to spec, which means their return values are within the
3988 // range of [0.0,1.0] - including the leading zero.
3989
3990 Modernizr.addTest('opacity', function() {
3991 var style = createElement('a').style;
3992 style.cssText = prefixes.join('opacity:.55;');
3993
3994 // The non-literal . in this regex is intentional:
3995 // German Chrome returns this value as 0,55
3996 // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
3997 return (/^0.55$/).test(style.opacity);
3998 });
3999
4000/*!
4001{
4002 "name": "CSS Overflow Scrolling",
4003 "property": "overflowscrolling",
4004 "tags": ["css"],
4005 "builderAliases": ["css_overflow_scrolling"],
4006 "warnings": ["Introduced in iOS5b2. API is subject to change."],
4007 "notes": [{
4008 "name": "Article on iOS overflow scrolling",
4009 "href": "https://css-tricks.com/snippets/css/momentum-scrolling-on-ios-overflow-elements/"
4010 }]
4011}
4012!*/
4013
4014 Modernizr.addTest('overflowscrolling', testAllProps('overflowScrolling', 'touch', true));
4015
4016/*!
4017{
4018 "name": "CSS Pointer Events",
4019 "caniuse": "pointer-events",
4020 "property": "csspointerevents",
4021 "authors": ["ausi"],
4022 "tags": ["css"],
4023 "builderAliases": ["css_pointerevents"],
4024 "notes": [
4025 {
4026 "name": "MDN Docs",
4027 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events"
4028 },{
4029 "name": "Test Project Page",
4030 "href": "https://ausi.github.com/Feature-detection-technique-for-pointer-events/"
4031 },{
4032 "name": "Test Project Wiki",
4033 "href": "https://github.com/ausi/Feature-detection-technique-for-pointer-events/wiki"
4034 },
4035 {
4036 "name": "Related Github Issue",
4037 "href": "https://github.com/Modernizr/Modernizr/issues/80"
4038 }
4039 ]
4040}
4041!*/
4042
4043 Modernizr.addTest('csspointerevents', function() {
4044 var style = createElement('a').style;
4045 style.cssText = 'pointer-events:auto';
4046 return style.pointerEvents === 'auto';
4047 });
4048
4049/*!
4050{
4051 "name": "CSS position: sticky",
4052 "property": "csspositionsticky",
4053 "tags": ["css"],
4054 "builderAliases": ["css_positionsticky"],
4055 "notes": [{
4056 "name": "Chrome bug report",
4057 "href":"https://code.google.com/p/chromium/issues/detail?id=322972"
4058 }],
4059 "warnings": [ "using position:sticky on anything but top aligned elements is buggy in Chrome < 37 and iOS <=7+" ]
4060}
4061!*/
4062
4063 // Sticky positioning - constrains an element to be positioned inside the
4064 // intersection of its container box, and the viewport.
4065 Modernizr.addTest('csspositionsticky', function() {
4066 var prop = 'position:';
4067 var value = 'sticky';
4068 var el = createElement('a');
4069 var mStyle = el.style;
4070
4071 mStyle.cssText = prop + prefixes.join(value + ';' + prop).slice(0, -prop.length);
4072
4073 return mStyle.position.indexOf(value) !== -1;
4074 });
4075
4076/*!
4077{
4078 "name": "CSS Generated Content Animations",
4079 "property": "csspseudoanimations",
4080 "tags": ["css"]
4081}
4082!*/
4083
4084 Modernizr.addTest('csspseudoanimations', function() {
4085 var result = false;
4086
4087 if (!Modernizr.cssanimations || !window.getComputedStyle) {
4088 return result;
4089 }
4090
4091 var styles = [
4092 '@', Modernizr._prefixes.join('keyframes csspseudoanimations { from { font-size: 10px; } }@').replace(/\@$/, ''),
4093 '#modernizr:before { content:" "; font-size:5px;',
4094 Modernizr._prefixes.join('animation:csspseudoanimations 1ms infinite;'),
4095 '}'
4096 ].join('');
4097
4098 Modernizr.testStyles(styles, function(elem) {
4099 result = window.getComputedStyle(elem, ':before').getPropertyValue('font-size') === '10px';
4100 });
4101
4102 return result;
4103 });
4104
4105/*!
4106{
4107 "name": "CSS Transitions",
4108 "property": "csstransitions",
4109 "caniuse": "css-transitions",
4110 "tags": ["css"]
4111}
4112!*/
4113
4114 Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true));
4115
4116/*!
4117{
4118 "name": "CSS Generated Content Transitions",
4119 "property": "csspseudotransitions",
4120 "tags": ["css"]
4121}
4122!*/
4123
4124 Modernizr.addTest('csspseudotransitions', function() {
4125 var result = false;
4126
4127 if (!Modernizr.csstransitions || !window.getComputedStyle) {
4128 return result;
4129 }
4130
4131 var styles =
4132 '#modernizr:before { content:" "; font-size:5px;' + Modernizr._prefixes.join('transition:0s 100s;') + '}' +
4133 '#modernizr.trigger:before { font-size:10px; }';
4134
4135 Modernizr.testStyles(styles, function(elem) {
4136 // Force rendering of the element's styles so that the transition will trigger
4137 window.getComputedStyle(elem, ':before').getPropertyValue('font-size');
4138 elem.className += 'trigger';
4139 result = window.getComputedStyle(elem, ':before').getPropertyValue('font-size') === '5px';
4140 });
4141
4142 return result;
4143 });
4144
4145/*!
4146{
4147 "name": "CSS Reflections",
4148 "caniuse": "css-reflections",
4149 "property": "cssreflections",
4150 "tags": ["css"]
4151}
4152!*/
4153
4154 Modernizr.addTest('cssreflections', testAllProps('boxReflect', 'above', true));
4155
4156/*!
4157{
4158 "name": "CSS Regions",
4159 "caniuse": "css-regions",
4160 "authors": ["Mihai Balan"],
4161 "property": "regions",
4162 "tags": ["css"],
4163 "builderAliases": ["css_regions"],
4164 "notes": [{
4165 "name": "W3C Specification",
4166 "href": "https://www.w3.org/TR/css3-regions/"
4167 }]
4168}
4169!*/
4170
4171 // We start with a CSS parser test then we check page geometry to see if it's affected by regions
4172 // Later we might be able to retire the second part, as WebKit builds with the false positives die out
4173
4174 Modernizr.addTest('regions', function() {
4175
4176 if (isSVG) {
4177 // css regions don't work inside of SVG elements. Rather than update the
4178 // below test to work in an SVG context, just exit early to save bytes
4179 return false;
4180 }
4181
4182 /* Get the 'flowFrom' property name available in the browser. Either default or vendor prefixed.
4183 If the property name can't be found we'll get Boolean 'false' and fail quickly */
4184 var flowFromProperty = prefixed('flowFrom');
4185 var flowIntoProperty = prefixed('flowInto');
4186 var result = false;
4187
4188 if (!flowFromProperty || !flowIntoProperty) {
4189 return result;
4190 }
4191
4192 /* If CSS parsing is there, try to determine if regions actually work. */
4193 var iframeContainer = createElement('iframe');
4194 var container = createElement('div');
4195 var content = createElement('div');
4196 var region = createElement('div');
4197
4198 /* we create a random, unlikely to be generated flow number to make sure we don't
4199 clash with anything more vanilla, like 'flow', or 'article', or 'f1' */
4200 var flowName = 'modernizr_flow_for_regions_check';
4201
4202 /* First create a div with two adjacent divs inside it. The first will be the
4203 content, the second will be the region. To be able to distinguish between the two,
4204 we'll give the region a particular padding */
4205 content.innerText = 'M';
4206 container.style.cssText = 'top: 150px; left: 150px; padding: 0px;';
4207 region.style.cssText = 'width: 50px; height: 50px; padding: 42px;';
4208
4209 region.style[flowFromProperty] = flowName;
4210 container.appendChild(content);
4211 container.appendChild(region);
4212 docElement.appendChild(container);
4213
4214 /* Now compute the bounding client rect, before and after attempting to flow the
4215 content div in the region div. If regions are enabled, the after bounding rect
4216 should reflect the padding of the region div.*/
4217 var flowedRect, delta;
4218 var plainRect = content.getBoundingClientRect();
4219
4220
4221 content.style[flowIntoProperty] = flowName;
4222 flowedRect = content.getBoundingClientRect();
4223
4224 delta = parseInt(flowedRect.left - plainRect.left, 10);
4225 docElement.removeChild(container);
4226
4227 if (delta == 42) {
4228 result = true;
4229 } else {
4230 /* IE only allows for the content to come from iframes. This has the
4231 * side effect of automatic collapsing of iframes once they get the flow-into
4232 * property set. checking for a change on the height allows us to detect this
4233 * in a sync way, without having to wait for a frame to load */
4234
4235 docElement.appendChild(iframeContainer);
4236 plainRect = iframeContainer.getBoundingClientRect();
4237 iframeContainer.style[flowIntoProperty] = flowName;
4238 flowedRect = iframeContainer.getBoundingClientRect();
4239
4240 if (plainRect.height > 0 && plainRect.height !== flowedRect.height && flowedRect.height === 0) {
4241 result = true;
4242 }
4243 }
4244
4245 content = region = container = iframeContainer = undefined;
4246
4247 return result;
4248 });
4249
4250/*!
4251{
4252 "name": "CSS Font rem Units",
4253 "caniuse": "rem",
4254 "authors": ["nsfmc"],
4255 "property": "cssremunit",
4256 "tags": ["css"],
4257 "builderAliases": ["css_remunit"],
4258 "notes": [{
4259 "name": "W3C Spec",
4260 "href": "https://www.w3.org/TR/css3-values/#relative0"
4261 },{
4262 "name": "Font Size with rem by Jonathan Snook",
4263 "href": "http://snook.ca/archives/html_and_css/font-size-with-rem"
4264 }]
4265}
4266!*/
4267
4268 // "The 'rem' unit ('root em') is relative to the computed
4269 // value of the 'font-size' value of the root element."
4270 // you can test by checking if the prop was ditched
4271
4272 Modernizr.addTest('cssremunit', function() {
4273 var style = createElement('a').style;
4274 try {
4275 style.fontSize = '3rem';
4276 }
4277 catch (e) {}
4278 return (/rem/).test(style.fontSize);
4279 });
4280
4281/*!
4282{
4283 "name": "CSS UI Resize",
4284 "property": "cssresize",
4285 "caniuse": "css-resize",
4286 "tags": ["css"],
4287 "builderAliases": ["css_resize"],
4288 "notes": [{
4289 "name": "W3C Specification",
4290 "href": "https://www.w3.org/TR/css3-ui/#resize"
4291 },{
4292 "name": "MDN Docs",
4293 "href": "https://developer.mozilla.org/en/CSS/resize"
4294 }]
4295}
4296!*/
4297/* DOC
4298Test for CSS 3 UI "resize" property
4299*/
4300
4301 Modernizr.addTest('cssresize', testAllProps('resize', 'both', true));
4302
4303/*!
4304{
4305 "name": "CSS rgba",
4306 "caniuse": "css3-colors",
4307 "property": "rgba",
4308 "tags": ["css"],
4309 "notes": [{
4310 "name": "CSSTricks Tutorial",
4311 "href": "https://css-tricks.com/rgba-browser-support/"
4312 }]
4313}
4314!*/
4315
4316 Modernizr.addTest('rgba', function() {
4317 var style = createElement('a').style;
4318 style.cssText = 'background-color:rgba(150,255,150,.5)';
4319
4320 return ('' + style.backgroundColor).indexOf('rgba') > -1;
4321 });
4322
4323/*!
4324{
4325 "name": "CSS Stylable Scrollbars",
4326 "property": "cssscrollbar",
4327 "tags": ["css"],
4328 "builderAliases": ["css_scrollbars"]
4329}
4330!*/
4331
4332 testStyles('#modernizr{overflow: scroll; width: 40px; height: 40px; }#' + prefixes
4333 .join('scrollbar{width:10px}' + ' #modernizr::')
4334 .split('#')
4335 .slice(1)
4336 .join('#') + 'scrollbar{width:10px}',
4337 function(node) {
4338 Modernizr.addTest('cssscrollbar', 'scrollWidth' in node && node.scrollWidth == 30);
4339 });
4340
4341/*!
4342{
4343 "name": "Scroll Snap Points",
4344 "property": "scrollsnappoints",
4345 "notes": [{
4346 "name": "Setting native-like scrolling offsets in CSS with Scrolling Snap Points",
4347 "href": "http://generatedcontent.org/post/66817675443/setting-native-like-scrolling-offsets-in-css-with"
4348 },{
4349 "name": "MDN Article",
4350 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap_Points"
4351 }],
4352 "polyfills": ["scrollsnap"]
4353}
4354!*/
4355/* DOC
4356Detects support for CSS Snap Points
4357*/
4358
4359 Modernizr.addTest('scrollsnappoints', testAllProps('scrollSnapType'));
4360
4361/*!
4362{
4363 "name": "CSS Shapes",
4364 "property": "shapes",
4365 "tags": ["css"],
4366 "notes": [{
4367 "name": "CSS Shapes W3C specification",
4368 "href": "https://www.w3.org/TR/css-shapes"
4369 },{
4370 "name": "Examples from Adobe",
4371 "href": "http://webplatform.adobe.com/shapes/"
4372 }, {
4373 "name": "Samples showcasing uses of Shapes",
4374 "href": "http://codepen.io/collection/qFesk"
4375 }]
4376}
4377!*/
4378
4379 Modernizr.addTest('shapes', testAllProps('shapeOutside', 'content-box', true));
4380
4381/*!
4382{
4383 "name": "CSS general sibling selector",
4384 "caniuse": "css-sel3",
4385 "property": "siblinggeneral",
4386 "tags": ["css"],
4387 "notes": [{
4388 "name": "Related Github Issue",
4389 "href": "https://github.com/Modernizr/Modernizr/pull/889"
4390 }]
4391}
4392!*/
4393
4394 Modernizr.addTest('siblinggeneral', function() {
4395 return testStyles('#modernizr div {width:100px} #modernizr div ~ div {width:200px;display:block}', function(elem) {
4396 return elem.lastChild.offsetWidth == 200;
4397 }, 2);
4398 });
4399
4400/*!
4401{
4402 "name": "CSS Subpixel Fonts",
4403 "property": "subpixelfont",
4404 "tags": ["css"],
4405 "builderAliases": ["css_subpixelfont"],
4406 "authors": [
4407 "@derSchepp",
4408 "@gerritvanaaken",
4409 "@rodneyrehm",
4410 "@yatil",
4411 "@ryanseddon"
4412 ],
4413 "notes": [{
4414 "name": "Origin Test",
4415 "href": "https://github.com/gerritvanaaken/subpixeldetect"
4416 }]
4417}
4418!*/
4419
4420 /*
4421 * (to infer if GDI or DirectWrite is used on Windows)
4422 */
4423 testStyles(
4424 '#modernizr{position: absolute; top: -10em; visibility:hidden; font: normal 10px arial;}#subpixel{float: left; font-size: 33.3333%;}',
4425 function(elem) {
4426 var subpixel = elem.firstChild;
4427 subpixel.innerHTML = 'This is a text written in Arial';
4428 Modernizr.addTest('subpixelfont', window.getComputedStyle ?
4429 window.getComputedStyle(subpixel, null).getPropertyValue('width') !== '44px'
4430 : false);
4431 }, 1, ['subpixel']);
4432
4433/*!
4434{
4435 "name": "CSS :target pseudo-class",
4436 "caniuse": "css-sel3",
4437 "property": "target",
4438 "tags": ["css"],
4439 "notes": [{
4440 "name": "MDN documentation",
4441 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/:target"
4442 }],
4443 "authors": ["@zachleat"],
4444 "warnings": ["Opera Mini supports :target but doesn't update the hash for anchor links."]
4445}
4446!*/
4447/* DOC
4448Detects support for the ':target' CSS pseudo-class.
4449*/
4450
4451 // querySelector
4452 Modernizr.addTest('target', function() {
4453 var doc = window.document;
4454 if (!('querySelectorAll' in doc)) {
4455 return false;
4456 }
4457
4458 try {
4459 doc.querySelectorAll(':target');
4460 return true;
4461 } catch (e) {
4462 return false;
4463 }
4464 });
4465
4466/*!
4467{
4468 "name": "CSS text-align-last",
4469 "property": "textalignlast",
4470 "tags": ["css"],
4471 "knownBugs": ["IE does not support the 'start' or 'end' values."],
4472 "notes": [{
4473 "name": "Quicksmode",
4474 "href": "http://www.quirksmode.org/css/text/textalignlast.html"
4475 },{
4476 "name": "MDN",
4477 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/text-align-last"
4478 }]
4479}
4480!*/
4481
4482 Modernizr.addTest('textalignlast', testAllProps('textAlignLast'));
4483
4484/*!
4485{
4486 "name": "CSS textshadow",
4487 "property": "textshadow",
4488 "caniuse": "css-textshadow",
4489 "tags": ["css"],
4490 "knownBugs": ["FF3.0 will false positive on this test"]
4491}
4492!*/
4493
4494 Modernizr.addTest('textshadow', testProp('textShadow', '1px 1px'));
4495
4496/*!
4497{
4498 "name": "CSS Transforms",
4499 "property": "csstransforms",
4500 "caniuse": "transforms2d",
4501 "tags": ["css"]
4502}
4503!*/
4504
4505 Modernizr.addTest('csstransforms', function() {
4506 // Android < 3.0 is buggy, so we sniff and blacklist
4507 // http://git.io/hHzL7w
4508 return navigator.userAgent.indexOf('Android 2.') === -1 &&
4509 testAllProps('transform', 'scale(1)', true);
4510 });
4511
4512/*!
4513{
4514 "name": "CSS Transforms Level 2",
4515 "property": "csstransformslevel2",
4516 "authors": ["rupl"],
4517 "tags": ["css"],
4518 "notes": [{
4519 "name": "CSSWG Draft Spec",
4520 "href": "https://drafts.csswg.org/css-transforms-2/"
4521 }]
4522}
4523!*/
4524
4525 Modernizr.addTest('csstransformslevel2', function() {
4526 return testAllProps('translate', '45px', true);
4527 });
4528
4529/*!
4530{
4531 "name": "CSS Transforms 3D",
4532 "property": "csstransforms3d",
4533 "caniuse": "transforms3d",
4534 "tags": ["css"],
4535 "warnings": [
4536 "Chrome may occassionally fail this test on some systems; more info: https://code.google.com/p/chromium/issues/detail?id=129004"
4537 ]
4538}
4539!*/
4540
4541 Modernizr.addTest('csstransforms3d', function() {
4542 var ret = !!testAllProps('perspective', '1px', true);
4543 var usePrefix = Modernizr._config.usePrefixes;
4544
4545 // Webkit's 3D transforms are passed off to the browser's own graphics renderer.
4546 // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
4547 // some conditions. As a result, Webkit typically recognizes the syntax but
4548 // will sometimes throw a false positive, thus we must do a more thorough check:
4549 if (ret && (!usePrefix || 'webkitPerspective' in docElement.style)) {
4550 var mq;
4551 var defaultStyle = '#modernizr{width:0;height:0}';
4552 // Use CSS Conditional Rules if available
4553 if (Modernizr.supports) {
4554 mq = '@supports (perspective: 1px)';
4555 } else {
4556 // Otherwise, Webkit allows this media query to succeed only if the feature is enabled.
4557 // `@media (transform-3d),(-webkit-transform-3d){ ... }`
4558 mq = '@media (transform-3d)';
4559 if (usePrefix) {
4560 mq += ',(-webkit-transform-3d)';
4561 }
4562 }
4563
4564 mq += '{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}';
4565
4566 testStyles(defaultStyle + mq, function(elem) {
4567 ret = elem.offsetWidth === 7 && elem.offsetHeight === 18;
4568 });
4569 }
4570
4571 return ret;
4572 });
4573
4574/*!
4575{
4576 "name": "CSS Transform Style preserve-3d",
4577 "property": "preserve3d",
4578 "authors": ["denyskoch", "aFarkas"],
4579 "tags": ["css"],
4580 "notes": [{
4581 "name": "MDN Docs",
4582 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/transform-style"
4583 },{
4584 "name": "Related Github Issue",
4585 "href": "https://github.com/Modernizr/Modernizr/issues/1748"
4586 }]
4587}
4588!*/
4589/* DOC
4590Detects support for `transform-style: preserve-3d`, for getting a proper 3D perspective on elements.
4591*/
4592
4593 Modernizr.addTest('preserve3d', function() {
4594 var outerAnchor, innerAnchor;
4595 var CSS = window.CSS;
4596 var result = false;
4597
4598 if (CSS && CSS.supports && CSS.supports('(transform-style: preserve-3d)')) {
4599 return true;
4600 }
4601
4602 outerAnchor = createElement('a');
4603 innerAnchor = createElement('a');
4604
4605 outerAnchor.style.cssText = 'display: block; transform-style: preserve-3d; transform-origin: right; transform: rotateY(40deg);';
4606 innerAnchor.style.cssText = 'display: block; width: 9px; height: 1px; background: #000; transform-origin: right; transform: rotateY(40deg);';
4607
4608 outerAnchor.appendChild(innerAnchor);
4609 docElement.appendChild(outerAnchor);
4610
4611 result = innerAnchor.getBoundingClientRect();
4612 docElement.removeChild(outerAnchor);
4613
4614 result = result.width && result.width < 4;
4615 return result;
4616 });
4617
4618/*!
4619{
4620 "name": "CSS user-select",
4621 "property": "userselect",
4622 "caniuse": "user-select-none",
4623 "authors": ["ryan seddon"],
4624 "tags": ["css"],
4625 "builderAliases": ["css_userselect"],
4626 "notes": [{
4627 "name": "Related Modernizr Issue",
4628 "href": "https://github.com/Modernizr/Modernizr/issues/250"
4629 }]
4630}
4631!*/
4632
4633 //https://github.com/Modernizr/Modernizr/issues/250
4634 Modernizr.addTest('userselect', testAllProps('userSelect', 'none', true));
4635
4636/*!
4637{
4638 "name": "CSS :valid pseudo-class",
4639 "property": "cssvalid",
4640 "notes": [{
4641 "name": "MDN documentation",
4642 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/:valid"
4643 }]
4644}
4645!*/
4646/* DOC
4647 Detects support for the ':valid' CSS pseudo-class.
4648*/
4649
4650 Modernizr.addTest('cssvalid', function() {
4651 return testStyles('#modernizr input{height:0;border:0;padding:0;margin:0;width:10px} #modernizr input:valid{width:50px}', function(elem) {
4652 var input = createElement('input');
4653 elem.appendChild(input);
4654 return input.clientWidth > 10;
4655 });
4656 });
4657
4658/*!
4659{
4660 "name": "Variable Open Type Fonts",
4661 "property": ["variablefonts"],
4662 "authors": ["Patrick Kettner"],
4663 "tags": ["css"],
4664 "notes": [{
4665 "name": "Variable fonts on the web",
4666 "href": "https://webkit.org/blog/7051/variable-fonts-on-the-web/"
4667 }, {
4668 "name": "Variable fonts for responsive design",
4669 "href": "https://alistapart.com/blog/post/variable-fonts-for-responsive-design"
4670 }]
4671}
4672!*/
4673
4674 Modernizr.addTest('variablefonts', testAllProps('fontVariationSettings'));
4675
4676/*!
4677{
4678 "name": "CSS vh unit",
4679 "property": "cssvhunit",
4680 "caniuse": "viewport-units",
4681 "tags": ["css"],
4682 "builderAliases": ["css_vhunit"],
4683 "notes": [{
4684 "name": "Related Modernizr Issue",
4685 "href": "https://github.com/Modernizr/Modernizr/issues/572"
4686 },{
4687 "name": "Similar JSFiddle",
4688 "href": "https://jsfiddle.net/FWeinb/etnYC/"
4689 }]
4690}
4691!*/
4692
4693 testStyles('#modernizr { height: 50vh; }', function(elem) {
4694 var height = parseInt(window.innerHeight / 2, 10);
4695 var compStyle = parseInt(computedStyle(elem, null, 'height'), 10);
4696 Modernizr.addTest('cssvhunit', compStyle == height);
4697 });
4698
4699
4700 /**
4701 * roundedEquals takes two integers and checks if the first is within 1 of the second
4702 *
4703 * @access private
4704 * @function roundedEquals
4705 * @param {number} a
4706 * @param {number} b
4707 * @returns {boolean}
4708 */
4709
4710 function roundedEquals(a, b) {
4711 return a - 1 === b || a === b || a + 1 === b;
4712 }
4713
4714 ;
4715/*!
4716{
4717 "name": "CSS vmax unit",
4718 "property": "cssvmaxunit",
4719 "caniuse": "viewport-units",
4720 "tags": ["css"],
4721 "builderAliases": ["css_vmaxunit"],
4722 "notes": [{
4723 "name": "Related Modernizr Issue",
4724 "href": "https://github.com/Modernizr/Modernizr/issues/572"
4725 },{
4726 "name": "JSFiddle Example",
4727 "href": "https://jsfiddle.net/glsee/JDsWQ/4/"
4728 }]
4729}
4730!*/
4731
4732 testStyles('#modernizr1{width: 50vmax}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}', function(node) {
4733 var elem = node.childNodes[2];
4734 var scroller = node.childNodes[1];
4735 var fullSizeElem = node.childNodes[0];
4736 var scrollbarWidth = parseInt((scroller.offsetWidth - scroller.clientWidth) / 2, 10);
4737
4738 var one_vw = fullSizeElem.clientWidth / 100;
4739 var one_vh = fullSizeElem.clientHeight / 100;
4740 var expectedWidth = parseInt(Math.max(one_vw, one_vh) * 50, 10);
4741 var compWidth = parseInt(computedStyle(elem, null, 'width'), 10);
4742
4743 Modernizr.addTest('cssvmaxunit', roundedEquals(expectedWidth, compWidth) || roundedEquals(expectedWidth, compWidth - scrollbarWidth));
4744 }, 3);
4745
4746/*!
4747{
4748 "name": "CSS vmin unit",
4749 "property": "cssvminunit",
4750 "caniuse": "viewport-units",
4751 "tags": ["css"],
4752 "builderAliases": ["css_vminunit"],
4753 "notes": [{
4754 "name": "Related Modernizr Issue",
4755 "href": "https://github.com/Modernizr/Modernizr/issues/572"
4756 },{
4757 "name": "JSFiddle Example",
4758 "href": "https://jsfiddle.net/glsee/JRmdq/8/"
4759 }]
4760}
4761!*/
4762
4763 testStyles('#modernizr1{width: 50vm;width:50vmin}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}', function(node) {
4764 var elem = node.childNodes[2];
4765 var scroller = node.childNodes[1];
4766 var fullSizeElem = node.childNodes[0];
4767 var scrollbarWidth = parseInt((scroller.offsetWidth - scroller.clientWidth) / 2, 10);
4768
4769 var one_vw = fullSizeElem.clientWidth / 100;
4770 var one_vh = fullSizeElem.clientHeight / 100;
4771 var expectedWidth = parseInt(Math.min(one_vw, one_vh) * 50, 10);
4772 var compWidth = parseInt(computedStyle(elem, null, 'width'), 10);
4773
4774 Modernizr.addTest('cssvminunit', roundedEquals(expectedWidth, compWidth) || roundedEquals(expectedWidth, compWidth - scrollbarWidth));
4775 }, 3);
4776
4777/*!
4778{
4779 "name": "CSS vw unit",
4780 "property": "cssvwunit",
4781 "caniuse": "viewport-units",
4782 "tags": ["css"],
4783 "builderAliases": ["css_vwunit"],
4784 "notes": [{
4785 "name": "Related Modernizr Issue",
4786 "href": "https://github.com/Modernizr/Modernizr/issues/572"
4787 },{
4788 "name": "JSFiddle Example",
4789 "href": "https://jsfiddle.net/FWeinb/etnYC/"
4790 }]
4791}
4792!*/
4793
4794 testStyles('#modernizr { width: 50vw; }', function(elem) {
4795 var width = parseInt(window.innerWidth / 2, 10);
4796 var compStyle = parseInt(computedStyle(elem, null, 'width'), 10);
4797
4798 Modernizr.addTest('cssvwunit', compStyle == width);
4799 });
4800
4801/*!
4802{
4803 "name": "will-change",
4804 "property": "willchange",
4805 "notes": [{
4806 "name": "Spec",
4807 "href": "https://drafts.csswg.org/css-will-change/"
4808 }]
4809}
4810!*/
4811/* DOC
4812Detects support for the `will-change` css property, which formally signals to the
4813browser that an element will be animating.
4814*/
4815
4816 Modernizr.addTest('willchange', 'willChange' in docElement.style);
4817
4818/*!
4819{
4820 "name": "CSS wrap-flow",
4821 "property": "wrapflow",
4822 "tags": ["css"],
4823 "notes": [
4824 {
4825 "name": "W3C Exclusions spec",
4826 "href": "https://www.w3.org/TR/css3-exclusions"
4827 },
4828 {
4829 "name": "Example by Adobe",
4830 "href": "http://html.adobe.com/webstandards/cssexclusions"
4831 }
4832 ]
4833}
4834!*/
4835
4836 Modernizr.addTest('wrapflow', function() {
4837 var prefixedProperty = prefixed('wrapFlow');
4838 if (!prefixedProperty || isSVG) {
4839 return false;
4840 }
4841
4842 var wrapFlowProperty = prefixedProperty.replace(/([A-Z])/g, function(str, m1) { return '-' + m1.toLowerCase(); }).replace(/^ms-/, '-ms-');
4843
4844 /* If the CSS parsing is there we need to determine if wrap-flow actually works to avoid false positive cases, e.g. the browser parses
4845 the property, but it hasn't got the implementation for the functionality yet. */
4846 var container = createElement('div');
4847 var exclusion = createElement('div');
4848 var content = createElement('span');
4849
4850 /* First we create a div with two adjacent divs inside it. The first div will be the content, the second div will be the exclusion area.
4851 We use the "wrap-flow: end" property to test the actual behavior. (http://dev.w3.org/csswg/css3-exclusions/#wrap-flow-property)
4852 The wrap-flow property is applied to the exclusion area what has a 50px left offset and a 100px width.
4853 If the wrap-flow property is working correctly then the content should start after the exclusion area, so the content's left offset should be 150px. */
4854 exclusion.style.cssText = 'position: absolute; left: 50px; width: 100px; height: 20px;' + wrapFlowProperty + ':end;';
4855 content.innerText = 'X';
4856
4857 container.appendChild(exclusion);
4858 container.appendChild(content);
4859 docElement.appendChild(container);
4860
4861 var leftOffset = content.offsetLeft;
4862
4863 docElement.removeChild(container);
4864 exclusion = content = container = undefined;
4865
4866 return (leftOffset == 150);
4867 });
4868
4869/*!
4870{
4871 "name": "Custom protocol handler",
4872 "property": "customprotocolhandler",
4873 "authors": ["Ben Schwarz"],
4874 "builderAliases": ["custom_protocol_handler"],
4875 "notes": [{
4876 "name": "WHATWG overview",
4877 "href": "https://developers.whatwg.org/timers.html#custom-handlers"
4878 },{
4879 "name": "MDN documentation",
4880 "href": "https://developer.mozilla.org/en-US/docs/Web/API/navigator.registerProtocolHandler"
4881 }],
4882 "warnings": [],
4883 "polyfills": []
4884}
4885!*/
4886/* DOC
4887Detects support for the `window.registerProtocolHandler()` API to allow websites to register themselves as possible handlers for particular protocols.
4888*/
4889
4890 Modernizr.addTest('customprotocolhandler', function() {
4891 // early bailout where it doesn't exist at all
4892 if (!navigator.registerProtocolHandler) {
4893 return false;
4894 }
4895
4896 // registerProtocolHandler was stubbed in webkit for a while, and didn't
4897 // actually do anything. We intentionally set it improperly to test for
4898 // the proper sort of failure
4899 try {
4900 navigator.registerProtocolHandler('thisShouldFail');
4901 }
4902 catch (e) {
4903 return e instanceof TypeError;
4904 }
4905
4906 return false;
4907 });
4908
4909/*!
4910{
4911 "name": "CustomEvent",
4912 "property": "customevent",
4913 "tags": ["customevent"],
4914 "authors": ["Alberto Elias"],
4915 "notes": [{
4916 "name": "W3C DOM reference",
4917 "href": "https://www.w3.org/TR/DOM-Level-3-Events/#interface-CustomEvent"
4918 }, {
4919 "name": "MDN documentation",
4920 "href": "https://developer.mozilla.org/en/docs/Web/API/CustomEvent"
4921 }],
4922 "polyfills": ["eventlistener"]
4923}
4924!*/
4925/* DOC
4926
4927Detects support for CustomEvent.
4928
4929*/
4930
4931 Modernizr.addTest('customevent', 'CustomEvent' in window && typeof window.CustomEvent === 'function');
4932
4933/*!
4934{
4935 "name": "Dart",
4936 "property": "dart",
4937 "authors": ["Theodoor van Donge"],
4938 "notes": [{
4939 "name": "Language website",
4940 "href": "https://www.dartlang.org/"
4941 }]
4942}
4943!*/
4944/* DOC
4945Detects native support for the Dart programming language.
4946*/
4947
4948 Modernizr.addTest('dart', !!prefixed('startDart', navigator));
4949
4950/*!
4951{
4952 "name": "DataView",
4953 "property": "dataview",
4954 "authors": ["Addy Osmani"],
4955 "builderAliases": ["dataview_api"],
4956 "notes": [{
4957 "name": "MDN documentation",
4958 "href": "https://developer.mozilla.org/en/JavaScript_typed_arrays/DataView"
4959 }],
4960 "polyfills": ["jdataview"]
4961}
4962!*/
4963/* DOC
4964Detects support for the DataView interface for reading data from an ArrayBuffer as part of the Typed Array spec.
4965*/
4966
4967 Modernizr.addTest('dataview', (typeof DataView !== 'undefined' && 'getFloat64' in DataView.prototype));
4968
4969/*!
4970{
4971 "name": "classList",
4972 "caniuse": "classlist",
4973 "property": "classlist",
4974 "tags": ["dom"],
4975 "builderAliases": ["dataview_api"],
4976 "notes": [{
4977 "name": "MDN Docs",
4978 "href": "https://developer.mozilla.org/en/DOM/element.classList"
4979 }]
4980}
4981!*/
4982
4983 Modernizr.addTest('classlist', 'classList' in docElement);
4984
4985/*!
4986{
4987 "name": "createElement with Attributes",
4988 "property": ["createelementattrs", "createelement-attrs"],
4989 "tags": ["dom"],
4990 "builderAliases": ["dom_createElement_attrs"],
4991 "authors": ["James A. Rosen"],
4992 "notes": [{
4993 "name": "Related Github Issue",
4994 "href": "https://github.com/Modernizr/Modernizr/issues/258"
4995 }]
4996}
4997!*/
4998
4999 Modernizr.addTest('createelementattrs', function() {
5000 try {
5001 return createElement('<input name="test" />').getAttribute('name') == 'test';
5002 } catch (e) {
5003 return false;
5004 }
5005 }, {
5006 aliases: ['createelement-attrs']
5007 });
5008
5009/*!
5010{
5011 "name": "dataset API",
5012 "caniuse": "dataset",
5013 "property": "dataset",
5014 "tags": ["dom"],
5015 "builderAliases": ["dom_dataset"],
5016 "authors": ["@phiggins42"]
5017}
5018!*/
5019
5020 // dataset API for data-* attributes
5021 Modernizr.addTest('dataset', function() {
5022 var n = createElement('div');
5023 n.setAttribute('data-a-b', 'c');
5024 return !!(n.dataset && n.dataset.aB === 'c');
5025 });
5026
5027/*!
5028{
5029 "name": "Document Fragment",
5030 "property": "documentfragment",
5031 "notes": [{
5032 "name": "W3C DOM Level 1 Reference",
5033 "href": "https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3"
5034 }, {
5035 "name": "SitePoint Reference",
5036 "href": "http://reference.sitepoint.com/javascript/DocumentFragment"
5037 }, {
5038 "name": "QuirksMode Compatibility Tables",
5039 "href": "http://www.quirksmode.org/m/w3c_core.html#t112"
5040 }],
5041 "authors": ["Ron Waldon (@jokeyrhyme)"],
5042 "knownBugs": ["false-positive on Blackberry 9500, see QuirksMode note"],
5043 "tags": []
5044}
5045!*/
5046/* DOC
5047Append multiple elements to the DOM within a single insertion.
5048*/
5049
5050 Modernizr.addTest('documentfragment', function() {
5051 return 'createDocumentFragment' in document &&
5052 'appendChild' in docElement;
5053 });
5054
5055/*!
5056{
5057 "name": "[hidden] Attribute",
5058 "property": "hidden",
5059 "tags": ["dom"],
5060 "notes": [{
5061 "name": "WHATWG: The hidden attribute",
5062 "href": "https://developers.whatwg.org/editing.html#the-hidden-attribute"
5063 }, {
5064 "name": "original implementation of detect code",
5065 "href": "https://github.com/aFarkas/html5shiv/blob/bf4fcc4/src/html5shiv.js#L38"
5066 }],
5067 "polyfills": ["html5shiv"],
5068 "authors": ["Ron Waldon (@jokeyrhyme)"]
5069}
5070!*/
5071/* DOC
5072Does the browser support the HTML5 [hidden] attribute?
5073*/
5074
5075 Modernizr.addTest('hidden', 'hidden' in createElement('a'));
5076
5077/*!
5078{
5079 "name": "microdata",
5080 "property": "microdata",
5081 "tags": ["dom"],
5082 "builderAliases": ["dom_microdata"],
5083 "notes": [{
5084 "name": "W3 Spec",
5085 "href": "https://www.w3.org/TR/microdata/"
5086 }]
5087}
5088!*/
5089
5090 Modernizr.addTest('microdata', 'getItems' in document);
5091
5092/*!
5093{
5094 "name": "DOM4 MutationObserver",
5095 "property": "mutationobserver",
5096 "caniuse": "mutationobserver",
5097 "tags": ["dom"],
5098 "authors": ["Karel Sedlá?ek (@ksdlck)"],
5099 "polyfills": ["mutationobservers"],
5100 "notes": [{
5101 "name": "MDN documentation",
5102 "href": "https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"
5103 }]
5104}
5105!*/
5106/* DOC
5107
5108Determines if DOM4 MutationObserver support is available.
5109
5110*/
5111
5112 Modernizr.addTest('mutationobserver',
5113 !!window.MutationObserver || !!window.WebKitMutationObserver);
5114
5115/*!
5116{
5117 "authors": ["Rick Byers"],
5118 "name": "Passive event listeners",
5119 "notes": [
5120 {
5121 "name": "WHATWG specification",
5122 "href": "https://dom.spec.whatwg.org/#dom-addeventlisteneroptions-passive"
5123 },
5124 {
5125 "name": "WICG explainer",
5126 "href": "https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md"
5127 }
5128 ],
5129 "property": "passiveeventlisteners",
5130 "tags": ["dom"]
5131}
5132!*/
5133
5134/* DOC
5135Detects support for the passive option to addEventListener.
5136*/
5137
5138
5139 Modernizr.addTest('passiveeventlisteners', function() {
5140 var supportsPassiveOption = false;
5141 try {
5142 var opts = Object.defineProperty({}, 'passive', {
5143 get: function() {
5144 supportsPassiveOption = true;
5145 }
5146 });
5147 window.addEventListener('test', null, opts);
5148 } catch (e) {}
5149 return supportsPassiveOption;
5150 });
5151
5152/*!
5153{
5154 "name": "bdi Element",
5155 "property": "bdi",
5156 "notes": [{
5157 "name": "MDN Overview",
5158 "href": "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi"
5159 }]
5160}
5161!*/
5162/* DOC
5163Detect support for the bdi element, a way to have text that is isolated from its possibly bidirectional surroundings
5164*/
5165
5166 Modernizr.addTest('bdi', function() {
5167 var div = createElement('div');
5168 var bdi = createElement('bdi');
5169
5170 bdi.innerHTML = 'إ';
5171 div.appendChild(bdi);
5172
5173 docElement.appendChild(div);
5174
5175 var supports = computedStyle(bdi, null, 'direction') === 'rtl';
5176
5177 docElement.removeChild(div);
5178
5179 return supports;
5180 });
5181
5182
5183 /**
5184 * since we have a fairly large number of input tests that don't mutate the input
5185 * we create a single element that can be shared with all of those tests for a
5186 * minor perf boost
5187 *
5188 * @access private
5189 * @returns {HTMLInputElement}
5190 */
5191 var inputElem = createElement('input');
5192
5193/*!
5194{
5195 "name": "Input attributes",
5196 "property": "input",
5197 "tags": ["forms"],
5198 "authors": ["Mike Taylor"],
5199 "notes": [{
5200 "name": "WHATWG spec",
5201 "href": "https://html.spec.whatwg.org/multipage/forms.html#input-type-attr-summary"
5202 }],
5203 "knownBugs": ["Some blackberry devices report false positive for input.multiple"]
5204}
5205!*/
5206/* DOC
5207Detects support for HTML5 `<input>` element attributes and exposes Boolean subproperties with the results:
5208
5209```javascript
5210Modernizr.input.autocomplete
5211Modernizr.input.autofocus
5212Modernizr.input.list
5213Modernizr.input.max
5214Modernizr.input.min
5215Modernizr.input.multiple
5216Modernizr.input.pattern
5217Modernizr.input.placeholder
5218Modernizr.input.required
5219Modernizr.input.step
5220```
5221*/
5222
5223 // Run through HTML5's new input attributes to see if the UA understands any.
5224 // Mike Taylr has created a comprehensive resource for testing these attributes
5225 // when applied to all input types:
5226 // miketaylr.com/code/input-type-attr.html
5227
5228 // Only input placeholder is tested while textarea's placeholder is not.
5229 // Currently Safari 4 and Opera 11 have support only for the input placeholder
5230 // Both tests are available in feature-detects/forms-placeholder.js
5231
5232 var inputattrs = 'autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ');
5233 var attrs = {};
5234
5235 Modernizr.input = (function(props) {
5236 for (var i = 0, len = props.length; i < len; i++) {
5237 attrs[ props[i] ] = !!(props[i] in inputElem);
5238 }
5239 if (attrs.list) {
5240 // safari false positive's on datalist: webk.it/74252
5241 // see also github.com/Modernizr/Modernizr/issues/146
5242 attrs.list = !!(createElement('datalist') && window.HTMLDataListElement);
5243 }
5244 return attrs;
5245 })(inputattrs);
5246
5247/*!
5248{
5249 "name": "datalist Element",
5250 "caniuse": "datalist",
5251 "property": "datalistelem",
5252 "tags": ["elem"],
5253 "builderAliases": ["elem_datalist"],
5254 "warnings": ["This test is a dupe of Modernizr.input.list. Only around for legacy reasons."],
5255 "notes": [{
5256 "name": "CSS Tricks Article",
5257 "href": "https://css-tricks.com/15346-relevant-dropdowns-polyfill-for-datalist/"
5258 },{
5259 "name": "Mike Taylor Code",
5260 "href": "https://miketaylr.com/code/datalist.html"
5261 }]
5262}
5263!*/
5264
5265 // lol. we already have a test for datalist built in! silly you.
5266 // Leaving it around in case anyone's using it
5267
5268 Modernizr.addTest('datalistelem', Modernizr.input.list);
5269
5270/*!
5271{
5272 "name": "details Element",
5273 "caniuse": "details",
5274 "property": "details",
5275 "tags": ["elem"],
5276 "builderAliases": ["elem_details"],
5277 "authors": ["@mathias"],
5278 "notes": [{
5279 "name": "Mathias' Original",
5280 "href": "https://mathiasbynens.be/notes/html5-details-jquery#comment-35"
5281 }]
5282}
5283!*/
5284
5285 Modernizr.addTest('details', function() {
5286 var el = createElement('details');
5287 var diff;
5288
5289 // return early if possible; thanks @aFarkas!
5290 if (!('open' in el)) {
5291 return false;
5292 }
5293
5294 testStyles('#modernizr details{display:block}', function(node) {
5295 node.appendChild(el);
5296 el.innerHTML = '<summary>a</summary>b';
5297 diff = el.offsetHeight;
5298 el.open = true;
5299 diff = diff != el.offsetHeight;
5300 });
5301
5302
5303 return diff;
5304 });
5305
5306/*!
5307{
5308 "name": "output Element",
5309 "property": "outputelem",
5310 "tags": ["elem"],
5311 "builderAliases": ["elem_output"],
5312 "notes": [{
5313 "name": "WhatWG Spec",
5314 "href": "https://html.spec.whatwg.org/multipage/forms.html#the-output-element"
5315 }]
5316}
5317!*/
5318
5319 Modernizr.addTest('outputelem', 'value' in createElement('output'));
5320
5321/*!
5322{
5323 "name": "picture Element",
5324 "property": "picture",
5325 "tags": ["elem"],
5326 "authors": ["Scott Jehl", "Mat Marquis"],
5327 "notes": [{
5328 "name": "Specification",
5329 "href": "http://picture.responsiveimages.org"
5330 },{
5331 "name": "Relevant spec issue",
5332 "href": "https://github.com/ResponsiveImagesCG/picture-element/issues/87"
5333 }]
5334}
5335!*/
5336
5337 Modernizr.addTest('picture', 'HTMLPictureElement' in window);
5338
5339/*!
5340{
5341 "name": "progress Element",
5342 "caniuse": "progress",
5343 "property": ["progressbar", "meter"],
5344 "tags": ["elem"],
5345 "builderAliases": ["elem_progress_meter"],
5346 "authors": ["Stefan Wallin"]
5347}
5348!*/
5349
5350 // Tests for progressbar-support. All browsers that don't support progressbar returns undefined =)
5351 Modernizr.addTest('progressbar', createElement('progress').max !== undefined);
5352
5353 // Tests for meter-support. All browsers that don't support meters returns undefined =)
5354 Modernizr.addTest('meter', createElement('meter').max !== undefined);
5355
5356/*!
5357{
5358 "name": "ruby, rp, rt Elements",
5359 "caniuse": "ruby",
5360 "property": "ruby",
5361 "tags": ["elem"],
5362 "builderAliases": ["elem_ruby"],
5363 "authors": ["C?t?lin Mari?"],
5364 "notes": [{
5365 "name": "WHATWG Specification",
5366 "href": "https://html.spec.whatwg.org/multipage/semantics.html#the-ruby-element"
5367 }]
5368}
5369!*/
5370
5371 Modernizr.addTest('ruby', function() {
5372
5373 var ruby = createElement('ruby');
5374 var rt = createElement('rt');
5375 var rp = createElement('rp');
5376 var displayStyleProperty = 'display';
5377 // 'fontSize' - because it`s only used for IE6 and IE7
5378 var fontSizeStyleProperty = 'fontSize';
5379
5380 ruby.appendChild(rp);
5381 ruby.appendChild(rt);
5382 docElement.appendChild(ruby);
5383
5384 // browsers that support <ruby> hide the <rp> via "display:none"
5385 if (getStyle(rp, displayStyleProperty) == 'none' || // for non-IE browsers
5386 // but in IE browsers <rp> has "display:inline" so, the test needs other conditions:
5387 getStyle(ruby, displayStyleProperty) == 'ruby' && getStyle(rt, displayStyleProperty) == 'ruby-text' || // for IE8+
5388 getStyle(rp, fontSizeStyleProperty) == '6pt' && getStyle(rt, fontSizeStyleProperty) == '6pt') { // for IE6 & IE7
5389
5390 cleanUp();
5391 return true;
5392
5393 } else {
5394 cleanUp();
5395 return false;
5396 }
5397
5398 function getStyle(element, styleProperty) {
5399 var result;
5400
5401 if (window.getComputedStyle) { // for non-IE browsers
5402 result = document.defaultView.getComputedStyle(element, null).getPropertyValue(styleProperty);
5403 } else if (element.currentStyle) { // for IE
5404 result = element.currentStyle[styleProperty];
5405 }
5406
5407 return result;
5408 }
5409
5410 function cleanUp() {
5411 docElement.removeChild(ruby);
5412 // the removed child node still exists in memory, so ...
5413 ruby = null;
5414 rt = null;
5415 rp = null;
5416 }
5417
5418 });
5419
5420
5421/*!
5422{
5423 "name": "Template Tag",
5424 "property": "template",
5425 "tags": ["elem"],
5426 "notes": [{
5427 "name": "HTML5Rocks Article",
5428 "href": "http://www.html5rocks.com/en/tutorials/webcomponents/template/"
5429 },{
5430 "name": "W3 Spec",
5431 "href": "https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html"
5432 }]
5433}
5434!*/
5435
5436 Modernizr.addTest('template', 'content' in createElement('template'));
5437
5438/*!
5439{
5440 "name": "time Element",
5441 "property": "time",
5442 "tags": ["elem"],
5443 "builderAliases": ["elem_time"],
5444 "notes": [{
5445 "name": "WhatWG Spec",
5446 "href": "https://html.spec.whatwg.org/multipage/semantics.html#the-time-element"
5447 }]
5448}
5449!*/
5450
5451 Modernizr.addTest('time', 'valueAsDate' in createElement('time'));
5452
5453/*!
5454{
5455 "name": "Track element and Timed Text Track",
5456 "property": ["texttrackapi", "track"],
5457 "tags": ["elem"],
5458 "builderAliases": ["elem_track"],
5459 "authors": ["Addy Osmani"],
5460 "notes": [{
5461 "name": "W3 track Element Spec",
5462 "href": "http://www.w3.org/TR/html5/video.html#the-track-element"
5463 },{
5464 "name": "W3 track API Spec",
5465 "href": "http://www.w3.org/TR/html5/media-elements.html#text-track-api"
5466 }],
5467 "warnings": ["While IE10 has implemented the track element, IE10 does not expose the underlying APIs to create timed text tracks by JS (really sad)"]
5468}
5469!*/
5470
5471 Modernizr.addTest('texttrackapi', typeof (createElement('video').addTextTrack) === 'function');
5472
5473 // a more strict test for track including UI support: document.createElement('track').kind === 'subtitles'
5474 Modernizr.addTest('track', 'kind' in createElement('track'));
5475
5476/*!
5477{
5478 "name": "Unknown Elements",
5479 "property": "unknownelements",
5480 "tags": ["elem"],
5481 "notes": [{
5482 "name": "The Story of the HTML5 Shiv",
5483 "href": "https://www.paulirish.com/2011/the-history-of-the-html5-shiv/"
5484 }, {
5485 "name": "original implementation of detect code",
5486 "href": "https://github.com/aFarkas/html5shiv/blob/bf4fcc4/src/html5shiv.js#L36"
5487 }],
5488 "polyfills": ["html5shiv"],
5489 "authors": ["Ron Waldon (@jokeyrhyme)"]
5490}
5491!*/
5492/* DOC
5493Does the browser support HTML with non-standard / new elements?
5494*/
5495
5496 Modernizr.addTest('unknownelements', function() {
5497 var a = createElement('a');
5498 a.innerHTML = '<xyz></xyz>';
5499 return a.childNodes.length === 1;
5500 });
5501
5502/*!
5503{
5504 "name": "Emoji",
5505 "property": "emoji"
5506}
5507!*/
5508/* DOC
5509Detects support for emoji character sets.
5510*/
5511
5512 Modernizr.addTest('emoji', function() {
5513 if (!Modernizr.canvastext) {
5514 return false;
5515 }
5516 var pixelRatio = window.devicePixelRatio || 1;
5517 var offset = 12 * pixelRatio;
5518 var node = createElement('canvas');
5519 var ctx = node.getContext('2d');
5520 ctx.fillStyle = '#f00';
5521 ctx.textBaseline = 'top';
5522 ctx.font = '32px Arial';
5523 ctx.fillText('\ud83d\udc28', 0, 0); // U+1F428 KOALA
5524 return ctx.getImageData(offset, offset, 1, 1).data[0] !== 0;
5525 });
5526
5527/*!
5528{
5529 "name": "ES5 Array",
5530 "property": "es5array",
5531 "notes": [{
5532 "name": "ECMAScript 5.1 Language Specification",
5533 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5534 }],
5535 "polyfills": ["es5shim"],
5536 "authors": ["Ron Waldon (@jokeyrhyme)"],
5537 "tags": ["es5"]
5538}
5539!*/
5540/* DOC
5541Check if browser implements ECMAScript 5 Array per specification.
5542*/
5543
5544 Modernizr.addTest('es5array', function() {
5545 return !!(Array.prototype &&
5546 Array.prototype.every &&
5547 Array.prototype.filter &&
5548 Array.prototype.forEach &&
5549 Array.prototype.indexOf &&
5550 Array.prototype.lastIndexOf &&
5551 Array.prototype.map &&
5552 Array.prototype.some &&
5553 Array.prototype.reduce &&
5554 Array.prototype.reduceRight &&
5555 Array.isArray);
5556 });
5557
5558/*!
5559{
5560 "name": "ES5 Date",
5561 "property": "es5date",
5562 "notes": [{
5563 "name": "ECMAScript 5.1 Language Specification",
5564 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5565 }],
5566 "polyfills": ["es5shim"],
5567 "authors": ["Ron Waldon (@jokeyrhyme)"],
5568 "tags": ["es5"]
5569}
5570!*/
5571/* DOC
5572Check if browser implements ECMAScript 5 Date per specification.
5573*/
5574
5575 Modernizr.addTest('es5date', function() {
5576 var isoDate = '2013-04-12T06:06:37.307Z',
5577 canParseISODate = false;
5578 try {
5579 canParseISODate = !!Date.parse(isoDate);
5580 } catch (e) {
5581 // no ISO date parsing yet
5582 }
5583 return !!(Date.now &&
5584 Date.prototype &&
5585 Date.prototype.toISOString &&
5586 Date.prototype.toJSON &&
5587 canParseISODate);
5588 });
5589
5590/*!
5591{
5592 "name": "ES5 Function",
5593 "property": "es5function",
5594 "notes": [{
5595 "name": "ECMAScript 5.1 Language Specification",
5596 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5597 }],
5598 "polyfills": ["es5shim"],
5599 "authors": ["Ron Waldon (@jokeyrhyme)"],
5600 "tags": ["es5"]
5601}
5602!*/
5603/* DOC
5604Check if browser implements ECMAScript 5 Function per specification.
5605*/
5606
5607 Modernizr.addTest('es5function', function() {
5608 return !!(Function.prototype && Function.prototype.bind);
5609 });
5610
5611/*!
5612{
5613 "name": "ES5 Object",
5614 "property": "es5object",
5615 "notes": [{
5616 "name": "ECMAScript 5.1 Language Specification",
5617 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5618 }],
5619 "polyfills": ["es5shim", "es5sham"],
5620 "authors": ["Ron Waldon (@jokeyrhyme)"],
5621 "tags": ["es5"]
5622}
5623!*/
5624/* DOC
5625Check if browser implements ECMAScript 5 Object per specification.
5626*/
5627
5628 Modernizr.addTest('es5object', function() {
5629 return !!(Object.keys &&
5630 Object.create &&
5631 Object.getPrototypeOf &&
5632 Object.getOwnPropertyNames &&
5633 Object.isSealed &&
5634 Object.isFrozen &&
5635 Object.isExtensible &&
5636 Object.getOwnPropertyDescriptor &&
5637 Object.defineProperty &&
5638 Object.defineProperties &&
5639 Object.seal &&
5640 Object.freeze &&
5641 Object.preventExtensions);
5642 });
5643
5644/*!
5645{
5646 "name": "ES5 Strict Mode",
5647 "property": "strictmode",
5648 "caniuse": "sctrict-mode",
5649 "notes": [{
5650 "name": "ECMAScript 5.1 Language Specification",
5651 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5652 }],
5653 "authors": ["@kangax"],
5654 "tags": ["es5"],
5655 "builderAliases": ["es5_strictmode"]
5656}
5657!*/
5658/* DOC
5659Check if browser implements ECMAScript 5 Object strict mode.
5660*/
5661
5662 Modernizr.addTest('strictmode', (function() {'use strict'; return !this; })());
5663
5664/*!
5665{
5666 "name": "ES5 String",
5667 "property": "es5string",
5668 "notes": [{
5669 "name": "ECMAScript 5.1 Language Specification",
5670 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5671 }],
5672 "polyfills": ["es5shim"],
5673 "authors": ["Ron Waldon (@jokeyrhyme)"],
5674 "tags": ["es5"]
5675}
5676!*/
5677/* DOC
5678Check if browser implements ECMAScript 5 String per specification.
5679*/
5680
5681 Modernizr.addTest('es5string', function() {
5682 return !!(String.prototype && String.prototype.trim);
5683 });
5684
5685/*!
5686{
5687 "name": "JSON",
5688 "property": "json",
5689 "caniuse": "json",
5690 "notes": [{
5691 "name": "MDN documentation",
5692 "href": "https://developer.mozilla.org/en-US/docs/Glossary/JSON"
5693 }],
5694 "polyfills": ["json2"]
5695}
5696!*/
5697/* DOC
5698Detects native support for JSON handling functions.
5699*/
5700
5701 // this will also succeed if you've loaded the JSON2.js polyfill ahead of time
5702 // ... but that should be obvious. :)
5703
5704 Modernizr.addTest('json', 'JSON' in window && 'parse' in JSON && 'stringify' in JSON);
5705
5706/*!
5707{
5708 "name": "ES5 Syntax",
5709 "property": "es5syntax",
5710 "notes": [{
5711 "name": "ECMAScript 5.1 Language Specification",
5712 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5713 }, {
5714 "name": "original implementation of detect code",
5715 "href": "http://kangax.github.io/es5-compat-table/"
5716 }],
5717 "authors": ["Ron Waldon (@jokeyrhyme)"],
5718 "warnings": ["This detect uses `eval()`, so CSP may be a problem."],
5719 "tags": ["es5"]
5720}
5721!*/
5722/* DOC
5723Check if browser accepts ECMAScript 5 syntax.
5724*/
5725
5726 Modernizr.addTest('es5syntax', function() {
5727 var value, obj, stringAccess, getter, setter, reservedWords, zeroWidthChars;
5728 try {
5729 /* eslint no-eval: "off" */
5730 // Property access on strings
5731 stringAccess = eval('"foobar"[3] === "b"');
5732 // Getter in property initializer
5733 getter = eval('({ get x(){ return 1 } }).x === 1');
5734 eval('({ set x(v){ value = v; } }).x = 1');
5735 // Setter in property initializer
5736 setter = value === 1;
5737 // Reserved words as property names
5738 eval('obj = ({ if: 1 })');
5739 reservedWords = obj['if'] === 1;
5740 // Zero-width characters in identifiers
5741 zeroWidthChars = eval('_\u200c\u200d = true');
5742
5743 return stringAccess && getter && setter && reservedWords && zeroWidthChars;
5744 } catch (ignore) {
5745 return false;
5746 }
5747 });
5748
5749/*!
5750{
5751 "name": "ES5 Immutable Undefined",
5752 "property": "es5undefined",
5753 "notes": [{
5754 "name": "ECMAScript 5.1 Language Specification",
5755 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5756 }, {
5757 "name": "original implementation of detect code",
5758 "href": "http://kangax.github.io/es5-compat-table/"
5759 }],
5760 "authors": ["Ron Waldon (@jokeyrhyme)"],
5761 "tags": ["es5"]
5762}
5763!*/
5764/* DOC
5765Check if browser prevents assignment to global `undefined` per ECMAScript 5.
5766*/
5767
5768 Modernizr.addTest('es5undefined', function() {
5769 var result, originalUndefined;
5770 try {
5771 originalUndefined = window.undefined;
5772 window.undefined = 12345;
5773 result = typeof window.undefined === 'undefined';
5774 window.undefined = originalUndefined;
5775 } catch (e) {
5776 return false;
5777 }
5778 return result;
5779 });
5780
5781/*!
5782{
5783 "name": "ES5",
5784 "property": "es5",
5785 "notes": [{
5786 "name": "ECMAScript 5.1 Language Specification",
5787 "href": "http://www.ecma-international.org/ecma-262/5.1/"
5788 }],
5789 "polyfills": ["es5shim", "es5sham"],
5790 "authors": ["Ron Waldon (@jokeyrhyme)"],
5791 "tags": ["es5"]
5792}
5793!*/
5794/* DOC
5795Check if browser implements everything as specified in ECMAScript 5.
5796*/
5797
5798 Modernizr.addTest('es5', function() {
5799 return !!(
5800 Modernizr.es5array &&
5801 Modernizr.es5date &&
5802 Modernizr.es5function &&
5803 Modernizr.es5object &&
5804 Modernizr.strictmode &&
5805 Modernizr.es5string &&
5806 Modernizr.json &&
5807 Modernizr.es5syntax &&
5808 Modernizr.es5undefined
5809 );
5810 });
5811
5812/*!
5813{
5814 "name": "ES6 Array",
5815 "property": "es6array",
5816 "notes": [{
5817 "name": "unofficial ECMAScript 6 draft specification",
5818 "href": "https://people.mozilla.org/~jorendorff/es6-draft.html"
5819 }],
5820 "polyfills": ["es6shim"],
5821 "authors": ["Ron Waldon (@jokeyrhyme)"],
5822 "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."],
5823 "tags": ["es6"]
5824}
5825!*/
5826/* DOC
5827Check if browser implements ECMAScript 6 Array per specification.
5828*/
5829
5830 Modernizr.addTest('es6array', !!(Array.prototype &&
5831 Array.prototype.copyWithin &&
5832 Array.prototype.fill &&
5833 Array.prototype.find &&
5834 Array.prototype.findIndex &&
5835 Array.prototype.keys &&
5836 Array.prototype.entries &&
5837 Array.prototype.values &&
5838 Array.from &&
5839 Array.of));
5840
5841/*!
5842{
5843 "name": "ES6 Collections",
5844 "property": "es6collections",
5845 "notes": [{
5846 "name": "unofficial ECMAScript 6 draft specification",
5847 "href": "https://people.mozilla.org/~jorendorff/es6-draft.html"
5848 }],
5849 "polyfills": ["es6shim", "weakmap"],
5850 "authors": ["Ron Waldon (@jokeyrhyme)"],
5851 "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."],
5852 "tags": ["es6"]
5853}
5854!*/
5855/* DOC
5856Check if browser implements ECMAScript 6 Map, Set, WeakMap and WeakSet
5857*/
5858
5859 Modernizr.addTest('es6collections', !!(
5860 window.Map && window.Set && window.WeakMap && window.WeakSet
5861 ));
5862
5863/*!
5864{
5865 "name": "ES5 String.prototype.contains",
5866 "property": "contains",
5867 "authors": ["Robert Kowalski"],
5868 "tags": ["es6"]
5869}
5870!*/
5871/* DOC
5872Check if browser implements ECMAScript 6 `String.prototype.contains` per specification.
5873*/
5874
5875 Modernizr.addTest('contains', is(String.prototype.contains, 'function'));
5876
5877/*!
5878{
5879 "name": "ES6 Generators",
5880 "property": "generators",
5881 "authors": ["Michael Kachanovskyi"],
5882 "tags": ["es6"]
5883}
5884!*/
5885/* DOC
5886Check if browser implements ECMAScript 6 Generators per specification.
5887*/
5888
5889 Modernizr.addTest('generators', function() {
5890 try {
5891 new Function('function* test() {}')();
5892 } catch (e) {
5893 return false;
5894 }
5895 return true;
5896 });
5897
5898/*!
5899{
5900 "name": "ES6 Math",
5901 "property": "es6math",
5902 "notes": [{
5903 "name": "unofficial ECMAScript 6 draft specification",
5904 "href": "https://people.mozilla.org/~jorendorff/es6-draft.html"
5905 }],
5906 "polyfills": ["es6shim"],
5907 "authors": ["Ron Waldon (@jokeyrhyme)"],
5908 "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."],
5909 "tags": ["es6"]
5910}
5911!*/
5912/* DOC
5913Check if browser implements ECMAScript 6 Math per specification.
5914*/
5915
5916 Modernizr.addTest('es6math', !!(Math &&
5917 Math.clz32 &&
5918 Math.cbrt &&
5919 Math.imul &&
5920 Math.sign &&
5921 Math.log10 &&
5922 Math.log2 &&
5923 Math.log1p &&
5924 Math.expm1 &&
5925 Math.cosh &&
5926 Math.sinh &&
5927 Math.tanh &&
5928 Math.acosh &&
5929 Math.asinh &&
5930 Math.atanh &&
5931 Math.hypot &&
5932 Math.trunc &&
5933 Math.fround));
5934
5935/*!
5936{
5937 "name": "ES6 Number",
5938 "property": "es6number",
5939 "notes": [{
5940 "name": "unofficial ECMAScript 6 draft specification",
5941 "href": "https://people.mozilla.org/~jorendorff/es6-draft.html"
5942 }],
5943 "polyfills": ["es6shim"],
5944 "authors": ["Ron Waldon (@jokeyrhyme)"],
5945 "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."],
5946 "tags": ["es6"]
5947}
5948!*/
5949/* DOC
5950Check if browser implements ECMAScript 6 Number per specification.
5951*/
5952
5953 Modernizr.addTest('es6number', !!(Number.isFinite &&
5954 Number.isInteger &&
5955 Number.isSafeInteger &&
5956 Number.isNaN &&
5957 Number.parseInt &&
5958 Number.parseFloat &&
5959 Number.isInteger(Number.MAX_SAFE_INTEGER) &&
5960 Number.isInteger(Number.MIN_SAFE_INTEGER) &&
5961 Number.isFinite(Number.EPSILON)));
5962
5963/*!
5964{
5965 "name": "ES6 Object",
5966 "property": "es6object",
5967 "notes": [{
5968 "name": "unofficial ECMAScript 6 draft specification",
5969 "href": "https://people.mozilla.org/~jorendorff/es6-draft.html"
5970 }],
5971 "polyfills": ["es6shim"],
5972 "authors": ["Ron Waldon (@jokeyrhyme)"],
5973 "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."],
5974 "tags": ["es6"]
5975}
5976!*/
5977/* DOC
5978Check if browser implements ECMAScript 6 Object per specification.
5979*/
5980
5981 Modernizr.addTest('es6object', !!(Object.assign &&
5982 Object.is &&
5983 Object.setPrototypeOf));
5984
5985/*!
5986{
5987 "name": "ES6 Promises",
5988 "property": "promises",
5989 "caniuse": "promises",
5990 "polyfills": ["es6promises"],
5991 "authors": ["Krister Kari", "Jake Archibald"],
5992 "tags": ["es6"],
5993 "notes": [{
5994 "name": "The ES6 promises spec",
5995 "href": "https://github.com/domenic/promises-unwrapping"
5996 },{
5997 "name": "Chromium dashboard - ES6 Promises",
5998 "href": "https://www.chromestatus.com/features/5681726336532480"
5999 },{
6000 "name": "JavaScript Promises: There and back again - HTML5 Rocks",
6001 "href": "http://www.html5rocks.com/en/tutorials/es6/promises/"
6002 }]
6003}
6004!*/
6005/* DOC
6006Check if browser implements ECMAScript 6 Promises per specification.
6007*/
6008
6009 Modernizr.addTest('promises', function() {
6010 return 'Promise' in window &&
6011 // Some of these methods are missing from
6012 // Firefox/Chrome experimental implementations
6013 'resolve' in window.Promise &&
6014 'reject' in window.Promise &&
6015 'all' in window.Promise &&
6016 'race' in window.Promise &&
6017 // Older version of the spec had a resolver object
6018 // as the arg rather than a function
6019 (function() {
6020 var resolve;
6021 new window.Promise(function(r) { resolve = r; });
6022 return typeof resolve === 'function';
6023 }());
6024 });
6025
6026/*!
6027{
6028 "name": "ES6 String",
6029 "property": "es6string",
6030 "notes": [{
6031 "name": "unofficial ECMAScript 6 draft specification",
6032 "href": "https://people.mozilla.org/~jorendorff/es6-draft.html"
6033 }],
6034 "polyfills": ["es6shim"],
6035 "authors": ["Ron Waldon (@jokeyrhyme)"],
6036 "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."],
6037 "tags": ["es6"]
6038}
6039!*/
6040/* DOC
6041Check if browser implements ECMAScript 6 String per specification.
6042*/
6043
6044 Modernizr.addTest('es6string', !!(String.fromCodePoint &&
6045 String.raw &&
6046 String.prototype.codePointAt &&
6047 String.prototype.repeat &&
6048 String.prototype.startsWith &&
6049 String.prototype.endsWith &&
6050 String.prototype.includes));
6051
6052/*!
6053{
6054 "name": "Orientation and Motion Events",
6055 "property": ["devicemotion", "deviceorientation"],
6056 "caniuse": "deviceorientation",
6057 "notes": [{
6058 "name": "W3C Editor's Draft",
6059 "href": "http://w3c.github.io/deviceorientation/spec-source-orientation.html"
6060 },{
6061 "name": "Implementation by iOS Safari (Orientation)",
6062 "href": "http://goo.gl/fhce3"
6063 },{
6064 "name": "Implementation by iOS Safari (Motion)",
6065 "href": "http://goo.gl/rLKz8"
6066 }],
6067 "authors": ["Shi Chuan"],
6068 "tags": ["event"],
6069 "builderAliases": ["event_deviceorientation_motion"]
6070}
6071!*/
6072/* DOC
6073Part of Device Access aspect of HTML5, same category as geolocation.
6074
6075`devicemotion` tests for Device Motion Event support, returns boolean value true/false.
6076
6077`deviceorientation` tests for Device Orientation Event support, returns boolean value true/false
6078*/
6079
6080 Modernizr.addTest('devicemotion', 'DeviceMotionEvent' in window);
6081 Modernizr.addTest('deviceorientation', 'DeviceOrientationEvent' in window);
6082
6083/*!
6084{
6085 "name": "onInput Event",
6086 "property": "oninput",
6087 "notes": [{
6088 "name": "MDN article",
6089 "href": "https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers.oninput"
6090 },{
6091 "name": "WHATWG spec",
6092 "href": "https://html.spec.whatwg.org/multipage/forms.html#common-input-element-attributes"
6093 },{
6094 "name": "Detecting onInput support",
6095 "href": "http://danielfriesen.name/blog/2010/02/16/html5-browser-maze-oninput-support"
6096 }],
6097 "authors": ["Patrick Kettner"],
6098 "tags": ["event"]
6099}
6100!*/
6101/* DOC
6102`oninput` tests if the browser is able to detect the input event
6103*/
6104
6105
6106 Modernizr.addTest('oninput', function() {
6107 var input = createElement('input');
6108 var supportsOnInput;
6109 input.setAttribute('oninput', 'return');
6110
6111 if (hasEvent('oninput', docElement) || typeof input.oninput == 'function') {
6112 return true;
6113 }
6114
6115 // IE doesn't support onInput, so we wrap up the non IE APIs
6116 // (createEvent, addEventListener) in a try catch, rather than test for
6117 // their trident equivalent.
6118 try {
6119 // Older Firefox didn't map oninput attribute to oninput property
6120 var testEvent = document.createEvent('KeyboardEvent');
6121 supportsOnInput = false;
6122 var handler = function(e) {
6123 supportsOnInput = true;
6124 e.preventDefault();
6125 e.stopPropagation();
6126 };
6127
6128 testEvent.initKeyEvent('keypress', true, true, window, false, false, false, false, 0, 'e'.charCodeAt(0));
6129 docElement.appendChild(input);
6130 input.addEventListener('input', handler, false);
6131 input.focus();
6132 input.dispatchEvent(testEvent);
6133 input.removeEventListener('input', handler, false);
6134 docElement.removeChild(input);
6135 } catch (e) {
6136 supportsOnInput = false;
6137 }
6138 return supportsOnInput;
6139 });
6140
6141/*!
6142{
6143 "name": "Event Listener",
6144 "property": "eventlistener",
6145 "authors": ["Andrew Betts (@triblondon)"],
6146 "notes": [{
6147 "name": "W3C Spec",
6148 "href": "https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-Registration-interfaces"
6149 }],
6150 "polyfills": ["eventlistener"]
6151}
6152!*/
6153/* DOC
6154Detects native support for addEventListener
6155*/
6156
6157 Modernizr.addTest('eventlistener', 'addEventListener' in window);
6158
6159/*!
6160{
6161 "name": "EXIF Orientation",
6162 "property": "exiforientation",
6163 "tags": ["image"],
6164 "builderAliases": ["exif_orientation"],
6165 "async": true,
6166 "authors": ["Paul Sayre"],
6167 "notes": [{
6168 "name": "Article by Dave Perrett",
6169 "href": "http://recursive-design.com/blog/2012/07/28/exif-orientation-handling-is-a-ghetto/"
6170 },{
6171 "name": "Article by Calvin Hass",
6172 "href": "http://www.impulseadventure.com/photo/exif-orientation.html"
6173 }]
6174}
6175!*/
6176/* DOC
6177Detects support for EXIF Orientation in JPEG images.
6178
6179iOS looks at the EXIF Orientation flag in JPEGs and rotates the image accordingly. Most desktop browsers just ignore this data.
6180*/
6181
6182 // Bug trackers:
6183 // bugzil.la/298619 (unimplemented)
6184 // crbug.com/56845 (looks incomplete)
6185 // webk.it/19688 (available upstream but its up all ports to turn on individually)
6186 Modernizr.addAsyncTest(function() {
6187 var img = new Image();
6188
6189 img.onerror = function() {
6190 addTest('exiforientation', false, {aliases: ['exif-orientation']});
6191 };
6192
6193 img.onload = function() {
6194 addTest('exiforientation', img.width !== 2, {aliases: ['exif-orientation']});
6195 };
6196
6197 // There may be a way to shrink this more, it's a 1x2 white jpg with the orientation flag set to 6
6198 img.src = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAAABABIBAwABAAAABgASAAAAAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/iiiigD/2Q==';
6199 });
6200
6201/*!
6202{
6203 "name": "File API",
6204 "property": "filereader",
6205 "caniuse": "fileapi",
6206 "notes": [{
6207 "name": "W3C Working Draft",
6208 "href": "https://www.w3.org/TR/FileAPI/"
6209 }],
6210 "tags": ["file"],
6211 "builderAliases": ["file_api"],
6212 "knownBugs": ["Will fail in Safari 5 due to its lack of support for the standards defined FileReader object"]
6213}
6214!*/
6215/* DOC
6216`filereader` tests for the File API specification
6217
6218Tests for objects specific to the File API W3C specification without
6219being redundant (don't bother testing for Blob since it is assumed
6220to be the File object's prototype.)
6221*/
6222
6223 Modernizr.addTest('filereader', !!(window.File && window.FileList && window.FileReader));
6224
6225/*!
6226{
6227 "name": "Filesystem API",
6228 "property": "filesystem",
6229 "caniuse": "filesystem",
6230 "notes": [{
6231 "name": "W3 Draft",
6232 "href": "http://dev.w3.org/2009/dap/file-system/file-dir-sys.html"
6233 }],
6234 "authors": ["Eric Bidelman (@ebidel)"],
6235 "tags": ["file"],
6236 "builderAliases": ["file_filesystem"],
6237 "knownBugs": ["The API will be present in Chrome incognito, but will throw an exception. See crbug.com/93417"]
6238}
6239!*/
6240
6241 Modernizr.addTest('filesystem', !!prefixed('requestFileSystem', window));
6242
6243/*!
6244 {
6245 "name": "Flash",
6246 "property": "flash",
6247 "tags": ["flash"],
6248 "polyfills": ["shumway"]
6249 }
6250 !*/
6251/* DOC
6252Detects Flash support as well as Flash-blocking plugins
6253*/
6254
6255 Modernizr.addAsyncTest(function() {
6256
6257 var attachBody = function(body) {
6258 if (!docElement.contains(body)) {
6259 docElement.appendChild(body);
6260 }
6261 };
6262 var removeFakeBody = function(body) {
6263 // If we?re rockin? an attached fake body, clean it up
6264 if (body.fake && body.parentNode) {
6265 body.parentNode.removeChild(body);
6266 }
6267 };
6268 var runTest = function(result, embed) {
6269 var bool = !!result;
6270 if (bool) {
6271 bool = new Boolean(bool);
6272 bool.blocked = (result === 'blocked');
6273 }
6274 addTest('flash', function() { return bool; });
6275
6276 if (embed && body.contains(embed)) {
6277
6278 // in case embed has been wrapped, as with ClickToPlugin
6279 while (embed.parentNode !== body) {
6280 embed = embed.parentNode;
6281 }
6282
6283 body.removeChild(embed);
6284 }
6285
6286 };
6287 var easy_detect;
6288 var activex;
6289 // we wrap activex in a try/catch because when Flash is disabled through
6290 // ActiveX controls, it throws an error.
6291 try {
6292 // Pan is an API that exists for Flash objects.
6293 activex = 'ActiveXObject' in window && 'Pan' in new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash');
6294 } catch (e) {}
6295
6296 easy_detect = !(('plugins' in navigator && 'Shockwave Flash' in navigator.plugins) || activex);
6297
6298 if (easy_detect || isSVG) {
6299 runTest(false);
6300 }
6301 else {
6302 // Flash seems to be installed, but it might be blocked. We have to
6303 // actually create an element to see what happens to it.
6304 var embed = createElement('embed');
6305 var body = getBody();
6306 var blockedDetect;
6307 var inline_style;
6308
6309 embed.type = 'application/x-shockwave-flash';
6310
6311 // Need to do this in the body (fake or otherwise) otherwise IE8 complains
6312 body.appendChild(embed);
6313
6314 // Pan doesn't exist in the embed if its IE (its on the ActiveXObjeect)
6315 // so this check is for all other browsers.
6316 if (!('Pan' in embed) && !activex) {
6317 attachBody(body);
6318 runTest('blocked', embed);
6319 removeFakeBody(body);
6320 return;
6321 }
6322
6323 blockedDetect = function() {
6324 // if we used a fake body originally, we need to restart this test, since
6325 // we haven't been attached to the DOM, and therefore none of the blockers
6326 // have had time to work.
6327 attachBody(body);
6328 if (!docElement.contains(body)) {
6329 body = document.body || body;
6330 embed = createElement('embed');
6331 embed.type = 'application/x-shockwave-flash';
6332 body.appendChild(embed);
6333 return setTimeout(blockedDetect, 1000);
6334 }
6335 if (!docElement.contains(embed)) {
6336 runTest('blocked');
6337 }
6338 else {
6339 inline_style = embed.style.cssText;
6340 if (inline_style !== '') {
6341 // the style of the element has changed automatically. This is a
6342 // really poor heuristic, but for lower end Flash blocks, it the
6343 // only change they can make.
6344 runTest('blocked', embed);
6345 }
6346 else {
6347 runTest(true, embed);
6348 }
6349 }
6350 removeFakeBody(body);
6351 };
6352
6353 // If we have got this far, there is still a chance a userland plugin
6354 // is blocking us (either changing the styles, or automatically removing
6355 // the element). Both of these require us to take a step back for a moment
6356 // to allow for them to get time of the thread, hence a setTimeout.
6357 //
6358 setTimeout(blockedDetect, 10);
6359 }
6360 });
6361
6362/*!
6363{
6364 "name": "input[capture] Attribute",
6365 "property": "capture",
6366 "tags": ["video", "image", "audio", "media", "attribute"],
6367 "notes": [{
6368 "name": "W3C draft: HTML Media Capture",
6369 "href": "https://www.w3.org/TR/html-media-capture/"
6370 }]
6371}
6372!*/
6373/* DOC
6374When used on an `<input>`, this attribute signifies that the resource it takes should be generated via device's camera, camcorder, sound recorder.
6375*/
6376
6377 // testing for capture attribute in inputs
6378 Modernizr.addTest('capture', ('capture' in createElement('input')));
6379
6380/*!
6381{
6382 "name": "input[file] Attribute",
6383 "property": "fileinput",
6384 "caniuse" : "forms",
6385 "tags": ["file", "forms", "input"],
6386 "builderAliases": ["forms_fileinput"]
6387}
6388!*/
6389/* DOC
6390Detects whether input type="file" is available on the platform
6391
6392E.g. iOS < 6 and some android version don't support this
6393*/
6394
6395 Modernizr.addTest('fileinput', function() {
6396 if (navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/)) {
6397 return false;
6398 }
6399 var elem = createElement('input');
6400 elem.type = 'file';
6401 return !elem.disabled;
6402 });
6403
6404/*!
6405{
6406 "name": "input[directory] Attribute",
6407 "property": "directory",
6408 "authors": ["silverwind"],
6409 "tags": ["file", "input", "attribute"]
6410}
6411!*/
6412/* DOC
6413When used on an `<input type="file">`, the `directory` attribute instructs
6414the user agent to present a directory selection dialog instead of the usual
6415file selection dialog.
6416*/
6417
6418 Modernizr.addTest('fileinputdirectory', function() {
6419 var elem = createElement('input'), dir = 'directory';
6420 elem.type = 'file';
6421 if (dir in elem) {
6422 return true;
6423 } else {
6424 for (var i = 0, len = domPrefixes.length; i < len; i++) {
6425 if (domPrefixes[i] + dir in elem) {
6426 return true;
6427 }
6428 }
6429 }
6430 return false;
6431 });
6432
6433/*!
6434{
6435 "name": "input[form] Attribute",
6436 "property": "formattribute",
6437 "tags": ["attribute", "forms", "input"],
6438 "builderAliases": ["forms_formattribute"]
6439}
6440!*/
6441/* DOC
6442Detects whether input form="form_id" is available on the platform
6443E.g. IE 10 (and below), don't support this
6444*/
6445
6446
6447 Modernizr.addTest('formattribute', function() {
6448 var form = createElement('form');
6449 var input = createElement('input');
6450 var div = createElement('div');
6451 var id = 'formtest' + (new Date()).getTime();
6452 var attr;
6453 var bool = false;
6454
6455 form.id = id;
6456
6457 //IE6/7 confuses the form idl attribute and the form content attribute, so we use document.createAttribute
6458 try {
6459 input.setAttribute('form', id);
6460 }
6461 catch (e) {
6462 if (document.createAttribute) {
6463 attr = document.createAttribute('form');
6464 attr.nodeValue = id;
6465 input.setAttributeNode(attr);
6466 }
6467 }
6468
6469 div.appendChild(form);
6470 div.appendChild(input);
6471
6472 docElement.appendChild(div);
6473
6474 bool = form.elements && form.elements.length === 1 && input.form == form;
6475
6476 div.parentNode.removeChild(div);
6477 return bool;
6478 });
6479
6480/*!
6481{
6482 "name": "Form input types",
6483 "property": "inputtypes",
6484 "caniuse": "forms",
6485 "tags": ["forms"],
6486 "authors": ["Mike Taylor"],
6487 "polyfills": [
6488 "jquerytools",
6489 "webshims",
6490 "h5f",
6491 "webforms2",
6492 "nwxforms",
6493 "fdslider",
6494 "html5slider",
6495 "galleryhtml5forms",
6496 "jscolor",
6497 "html5formshim",
6498 "selectedoptionsjs",
6499 "formvalidationjs"
6500 ]
6501}
6502!*/
6503/* DOC
6504Detects support for HTML5 form input types and exposes Boolean subproperties with the results:
6505
6506```javascript
6507Modernizr.inputtypes.color
6508Modernizr.inputtypes.date
6509Modernizr.inputtypes.datetime
6510Modernizr.inputtypes['datetime-local']
6511Modernizr.inputtypes.email
6512Modernizr.inputtypes.month
6513Modernizr.inputtypes.number
6514Modernizr.inputtypes.range
6515Modernizr.inputtypes.search
6516Modernizr.inputtypes.tel
6517Modernizr.inputtypes.time
6518Modernizr.inputtypes.url
6519Modernizr.inputtypes.week
6520```
6521*/
6522
6523 // Run through HTML5's new input types to see if the UA understands any.
6524 // This is put behind the tests runloop because it doesn't return a
6525 // true/false like all the other tests; instead, it returns an object
6526 // containing each input type with its corresponding true/false value
6527
6528 // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/
6529 var inputtypes = 'search tel url email datetime date month week time datetime-local number range color'.split(' ');
6530 var inputs = {};
6531
6532 Modernizr.inputtypes = (function(props) {
6533 var len = props.length;
6534 var smile = '1)';
6535 var inputElemType;
6536 var defaultView;
6537 var bool;
6538
6539 for (var i = 0; i < len; i++) {
6540
6541 inputElem.setAttribute('type', inputElemType = props[i]);
6542 bool = inputElem.type !== 'text' && 'style' in inputElem;
6543
6544 // We first check to see if the type we give it sticks..
6545 // If the type does, we feed it a textual value, which shouldn't be valid.
6546 // If the value doesn't stick, we know there's input sanitization which infers a custom UI
6547 if (bool) {
6548
6549 inputElem.value = smile;
6550 inputElem.style.cssText = 'position:absolute;visibility:hidden;';
6551
6552 if (/^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined) {
6553
6554 docElement.appendChild(inputElem);
6555 defaultView = document.defaultView;
6556
6557 // Safari 2-4 allows the smiley as a value, despite making a slider
6558 bool = defaultView.getComputedStyle &&
6559 defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
6560 // Mobile android web browser has false positive, so must
6561 // check the height to see if the widget is actually there.
6562 (inputElem.offsetHeight !== 0);
6563
6564 docElement.removeChild(inputElem);
6565
6566 } else if (/^(search|tel)$/.test(inputElemType)) {
6567 // Spec doesn't define any special parsing or detectable UI
6568 // behaviors so we pass these through as true
6569
6570 // Interestingly, opera fails the earlier test, so it doesn't
6571 // even make it here.
6572
6573 } else if (/^(url|email)$/.test(inputElemType)) {
6574 // Real url and email support comes with prebaked validation.
6575 bool = inputElem.checkValidity && inputElem.checkValidity() === false;
6576
6577 } else {
6578 // If the upgraded input compontent rejects the :) text, we got a winner
6579 bool = inputElem.value != smile;
6580 }
6581 }
6582
6583 inputs[ props[i] ] = !!bool;
6584 }
6585 return inputs;
6586 })(inputtypes);
6587
6588/*!
6589{
6590 "name": "Form Validation",
6591 "property": "formvalidation",
6592 "tags": ["forms", "validation", "attribute"],
6593 "builderAliases": ["forms_validation"]
6594}
6595!*/
6596/* DOC
6597This implementation only tests support for interactive form validation.
6598To check validation for a specific type or a specific other constraint,
6599the test can be combined:
6600
6601- `Modernizr.inputtypes.number && Modernizr.formvalidation` (browser supports rangeOverflow, typeMismatch etc. for type=number)
6602- `Modernizr.input.required && Modernizr.formvalidation` (browser supports valueMissing)
6603*/
6604
6605 Modernizr.addTest('formvalidation', function() {
6606 var form = createElement('form');
6607 if (!('checkValidity' in form) || !('addEventListener' in form)) {
6608 return false;
6609 }
6610 if ('reportValidity' in form) {
6611 return true;
6612 }
6613 var invalidFired = false;
6614 var input;
6615
6616 Modernizr.formvalidationapi = true;
6617
6618 // Prevent form from being submitted
6619 form.addEventListener('submit', function(e) {
6620 // Old Presto based Opera does not validate form, if submit is prevented
6621 // although Opera Mini servers use newer Presto.
6622 if (!window.opera || window.operamini) {
6623 e.preventDefault();
6624 }
6625 e.stopPropagation();
6626 }, false);
6627
6628 // Calling form.submit() doesn't trigger interactive validation,
6629 // use a submit button instead
6630 //older opera browsers need a name attribute
6631 form.innerHTML = '<input name="modTest" required="required" /><button></button>';
6632
6633 testStyles('#modernizr form{position:absolute;top:-99999em}', function(node) {
6634 node.appendChild(form);
6635
6636 input = form.getElementsByTagName('input')[0];
6637
6638 // Record whether "invalid" event is fired
6639 input.addEventListener('invalid', function(e) {
6640 invalidFired = true;
6641 e.preventDefault();
6642 e.stopPropagation();
6643 }, false);
6644
6645 //Opera does not fully support the validationMessage property
6646 Modernizr.formvalidationmessage = !!input.validationMessage;
6647
6648 // Submit form by clicking submit button
6649 form.getElementsByTagName('button')[0].click();
6650 });
6651
6652 return invalidFired;
6653 });
6654
6655/*!
6656{
6657 "name": "input[type=\"number\"] Localization",
6658 "property": "localizednumber",
6659 "tags": ["forms", "localization", "attribute"],
6660 "authors": ["Peter Janes"],
6661 "notes": [{
6662 "name": "Webkit Bug Tracker Listing",
6663 "href": "https://bugs.webkit.org/show_bug.cgi?id=42484"
6664 },{
6665 "name": "Based on This",
6666 "href": "https://trac.webkit.org/browser/trunk/LayoutTests/fast/forms/script-tests/input-number-keyoperation.js?rev=80096#L9"
6667 }],
6668 "knownBugs": ["Only ever returns true if the browser/OS is configured to use comma as a decimal separator. This is probably fine for most use cases."]
6669}
6670!*/
6671/* DOC
6672Detects whether input type="number" is capable of receiving and displaying localized numbers, e.g. with comma separator.
6673*/
6674
6675 Modernizr.addTest('localizednumber', function() {
6676 // this extends our testing of input[type=number], so bomb out if that's missing
6677 if (!Modernizr.inputtypes.number) { return false; }
6678 // we rely on checkValidity later, so bomb out early if we don't have it
6679 if (!Modernizr.formvalidation) { return false; }
6680
6681 var el = createElement('div');
6682 var diff;
6683 var body = getBody();
6684
6685 var root = (function() {
6686 return docElement.insertBefore(body, docElement.firstElementChild || docElement.firstChild);
6687 }());
6688 el.innerHTML = '<input type="number" value="1.0" step="0.1"/>';
6689 var input = el.childNodes[0];
6690 root.appendChild(el);
6691 input.focus();
6692 try {
6693 document.execCommand('SelectAll', false); // Overwrite current input value, rather than appending text
6694 document.execCommand('InsertText', false, '1,1');
6695 } catch (e) { // prevent warnings in IE
6696 }
6697 diff = input.type === 'number' && input.valueAsNumber === 1.1 && input.checkValidity();
6698 root.removeChild(el);
6699 if (body.fake) {
6700 root.parentNode.removeChild(root);
6701 }
6702 return diff;
6703 });
6704
6705
6706/*!
6707{
6708 "name": "placeholder attribute",
6709 "property": "placeholder",
6710 "tags": ["forms", "attribute"],
6711 "builderAliases": ["forms_placeholder"]
6712}
6713!*/
6714/* DOC
6715Tests for placeholder attribute in inputs and textareas
6716*/
6717
6718 Modernizr.addTest('placeholder', ('placeholder' in createElement('input') && 'placeholder' in createElement('textarea')));
6719
6720/*!
6721{
6722 "name": "form#requestAutocomplete()",
6723 "property": "requestautocomplete",
6724 "tags": ["form", "forms", "requestAutocomplete", "payments"],
6725 "notes": [{
6726 "name": "WHATWG proposed spec",
6727 "href": "https://wiki.whatwg.org/wiki/RequestAutocomplete"
6728 }]
6729}
6730!*/
6731/* DOC
6732When used with input[autocomplete] to annotate a form, form.requestAutocomplete() shows a dialog in Chrome that speeds up
6733checkout flows (payments specific for now).
6734*/
6735
6736 Modernizr.addTest('requestautocomplete', !!prefixed('requestAutocomplete', createElement('form')));
6737
6738/*!
6739{
6740 "name": "Fullscreen API",
6741 "property": "fullscreen",
6742 "caniuse": "fullscreen",
6743 "notes": [{
6744 "name": "MDN documentation",
6745 "href": "https://developer.mozilla.org/en/API/Fullscreen"
6746 }],
6747 "polyfills": ["screenfulljs"],
6748 "builderAliases": ["fullscreen_api"]
6749}
6750!*/
6751/* DOC
6752Detects support for the ability to make the current website take over the user's entire screen
6753*/
6754
6755 // github.com/Modernizr/Modernizr/issues/739
6756 Modernizr.addTest('fullscreen', !!(prefixed('exitFullscreen', document, false) || prefixed('cancelFullScreen', document, false)));
6757
6758/*!
6759{
6760 "name": "GamePad API",
6761 "property": "gamepads",
6762 "authors": ["Eric Bidelman"],
6763 "tags": ["media"],
6764 "notes": [{
6765 "name": "W3C spec",
6766 "href": "https://www.w3.org/TR/gamepad/"
6767 },{
6768 "name": "HTML5 Rocks tutorial",
6769 "href": "http://www.html5rocks.com/en/tutorials/doodles/gamepad/#toc-featuredetect"
6770 }],
6771 "warnings": [],
6772 "polyfills": []
6773}
6774!*/
6775/* DOC
6776Detects support for the Gamepad API, for access to gamepads and controllers.
6777*/
6778
6779
6780 Modernizr.addTest('gamepads', !!prefixed('getGamepads', navigator));
6781
6782/*!
6783{
6784 "name": "Geolocation API",
6785 "property": "geolocation",
6786 "caniuse": "geolocation",
6787 "tags": ["media"],
6788 "notes": [{
6789 "name": "MDN documentation",
6790 "href": "https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation"
6791 }],
6792 "polyfills": [
6793 "joshuabell-polyfill",
6794 "webshims",
6795 "geo-location-javascript",
6796 "geolocation-api-polyfill"
6797 ]
6798}
6799!*/
6800/* DOC
6801Detects support for the Geolocation API for users to provide their location to web applications.
6802*/
6803
6804 // geolocation is often considered a trivial feature detect...
6805 // Turns out, it's quite tricky to get right:
6806 //
6807 // Using !!navigator.geolocation does two things we don't want. It:
6808 // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513
6809 // 2. Disables page caching in WebKit: webk.it/43956
6810 //
6811 // Meanwhile, in Firefox < 8, an about:config setting could expose
6812 // a false positive that would throw an exception: bugzil.la/688158
6813
6814 Modernizr.addTest('geolocation', 'geolocation' in navigator);
6815
6816/*!
6817{
6818 "name": "Hashchange event",
6819 "property": "hashchange",
6820 "caniuse": "hashchange",
6821 "tags": ["history"],
6822 "notes": [{
6823 "name": "MDN documentation",
6824 "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.onhashchange"
6825 }],
6826 "polyfills": [
6827 "jquery-hashchange",
6828 "moo-historymanager",
6829 "jquery-ajaxy",
6830 "hasher",
6831 "shistory"
6832 ]
6833}
6834!*/
6835/* DOC
6836Detects support for the `hashchange` event, fired when the current location fragment changes.
6837*/
6838
6839 Modernizr.addTest('hashchange', function() {
6840 if (hasEvent('hashchange', window) === false) {
6841 return false;
6842 }
6843
6844 // documentMode logic from YUI to filter out IE8 Compat Mode
6845 // which false positives.
6846 return (document.documentMode === undefined || document.documentMode > 7);
6847 });
6848
6849/*!
6850{
6851 "name": "Hidden Scrollbar",
6852 "property": "hiddenscroll",
6853 "authors": ["Oleg Korsunsky"],
6854 "tags": ["overlay"],
6855 "notes": [{
6856 "name": "Overlay Scrollbar description",
6857 "href": "https://developer.apple.com/library/mac/releasenotes/MacOSX/WhatsNewInOSX/Articles/MacOSX10_7.html#//apple_ref/doc/uid/TP40010355-SW39"
6858 },{
6859 "name": "Video example of overlay scrollbars",
6860 "href": "https://gfycat.com/FoolishMeaslyAtlanticsharpnosepuffer"
6861 }]
6862}
6863!*/
6864/* DOC
6865Detects overlay scrollbars (when scrollbars on overflowed blocks are visible). This is found most commonly on mobile and OS X.
6866*/
6867
6868 Modernizr.addTest('hiddenscroll', function() {
6869 return testStyles('#modernizr {width:100px;height:100px;overflow:scroll}', function(elem) {
6870 return elem.offsetWidth === elem.clientWidth;
6871 });
6872 });
6873
6874/*!
6875{
6876 "name": "History API",
6877 "property": "history",
6878 "caniuse": "history",
6879 "tags": ["history"],
6880 "authors": ["Hay Kranen", "Alexander Farkas"],
6881 "notes": [{
6882 "name": "W3C Spec",
6883 "href": "https://www.w3.org/TR/html51/browsers.html#the-history-interface"
6884 }, {
6885 "name": "MDN documentation",
6886 "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.history"
6887 }],
6888 "polyfills": ["historyjs", "html5historyapi"]
6889}
6890!*/
6891/* DOC
6892Detects support for the History API for manipulating the browser session history.
6893*/
6894
6895 Modernizr.addTest('history', function() {
6896 // Issue #733
6897 // The stock browser on Android 2.2 & 2.3, and 4.0.x returns positive on history support
6898 // Unfortunately support is really buggy and there is no clean way to detect
6899 // these bugs, so we fall back to a user agent sniff :(
6900 var ua = navigator.userAgent;
6901
6902 // We only want Android 2 and 4.0, stock browser, and not Chrome which identifies
6903 // itself as 'Mobile Safari' as well, nor Windows Phone (issue #1471).
6904 if ((ua.indexOf('Android 2.') !== -1 ||
6905 (ua.indexOf('Android 4.0') !== -1)) &&
6906 ua.indexOf('Mobile Safari') !== -1 &&
6907 ua.indexOf('Chrome') === -1 &&
6908 ua.indexOf('Windows Phone') === -1 &&
6909 // Since all documents on file:// share an origin, the History apis are
6910 // blocked there as well
6911 location.protocol !== 'file:'
6912 ) {
6913 return false;
6914 }
6915
6916 // Return the regular check
6917 return (window.history && 'pushState' in window.history);
6918 });
6919
6920/*!
6921{
6922 "name": "HTML Imports",
6923 "notes": [
6924 {
6925 "name": "W3C HTML Imports Specification",
6926 "href": "https://w3c.github.io/webcomponents/spec/imports/"
6927 },
6928 {
6929 "name": "HTML Imports - #include for the web",
6930 "href": "http://www.html5rocks.com/en/tutorials/webcomponents/imports/"
6931 }
6932 ],
6933 "polyfills": ["polymer-htmlimports"],
6934 "property": "htmlimports",
6935 "tags": ["html", "import"]
6936}
6937!*/
6938/* DOC
6939Detects support for HTML import, a feature that is used for loading in Web Components.
6940 */
6941
6942
6943 addTest('htmlimports', 'import' in createElement('link'));
6944
6945/*!
6946{
6947 "name": "IE8 compat mode",
6948 "property": "ie8compat",
6949 "authors": ["Erich Ocean"]
6950}
6951!*/
6952/* DOC
6953Detects whether or not the current browser is IE8 in compatibility mode (i.e. acting as IE7).
6954*/
6955
6956 // In this case, IE8 will be acting as IE7. You may choose to remove features in this case.
6957
6958 // related:
6959 // james.padolsey.com/javascript/detect-ie-in-js-using-conditional-comments/
6960
6961 Modernizr.addTest('ie8compat', (!window.addEventListener && !!document.documentMode && document.documentMode === 7));
6962
6963/*!
6964{
6965 "name": "iframe[sandbox] Attribute",
6966 "property": "sandbox",
6967 "tags": ["iframe"],
6968 "builderAliases": ["iframe_sandbox"],
6969 "notes": [
6970 {
6971 "name": "WhatWG Spec",
6972 "href": "https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-sandbox"
6973 }],
6974 "knownBugs": [ "False-positive on Firefox < 29" ]
6975}
6976!*/
6977/* DOC
6978Test for `sandbox` attribute in iframes.
6979*/
6980
6981 Modernizr.addTest('sandbox', 'sandbox' in createElement('iframe'));
6982
6983/*!
6984{
6985 "name": "iframe[seamless] Attribute",
6986 "property": "seamless",
6987 "tags": ["iframe"],
6988 "builderAliases": ["iframe_seamless"],
6989 "notes": [{
6990 "name": "WhatWG Spec",
6991 "href": "https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-seamless"
6992 }]
6993}
6994!*/
6995/* DOC
6996Test for `seamless` attribute in iframes.
6997*/
6998
6999 Modernizr.addTest('seamless', 'seamless' in createElement('iframe'));
7000
7001/*!
7002{
7003 "name": "iframe[srcdoc] Attribute",
7004 "property": "srcdoc",
7005 "tags": ["iframe"],
7006 "builderAliases": ["iframe_srcdoc"],
7007 "notes": [{
7008 "name": "WhatWG Spec",
7009 "href": "https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-srcdoc"
7010 }]
7011}
7012!*/
7013/* DOC
7014Test for `srcdoc` attribute in iframes.
7015*/
7016
7017 Modernizr.addTest('srcdoc', 'srcdoc' in createElement('iframe'));
7018
7019/*!
7020{
7021 "name": "Animated PNG",
7022 "async": true,
7023 "property": "apng",
7024 "tags": ["image"],
7025 "builderAliases": ["img_apng"],
7026 "notes": [{
7027 "name": "Wikipedia Article",
7028 "href": "https://en.wikipedia.org/wiki/APNG"
7029 }]
7030}
7031!*/
7032/* DOC
7033Test for animated png support.
7034*/
7035
7036 Modernizr.addAsyncTest(function() {
7037 if (!Modernizr.canvas) {
7038 return false;
7039 }
7040
7041 var image = new Image();
7042 var canvas = createElement('canvas');
7043 var ctx = canvas.getContext('2d');
7044
7045 image.onload = function() {
7046 addTest('apng', function() {
7047 if (typeof canvas.getContext == 'undefined') {
7048 return false;
7049 }
7050 else {
7051 ctx.drawImage(image, 0, 0);
7052 return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
7053 }
7054 });
7055 };
7056
7057 image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACGFjVEwAAAABAAAAAcMq2TYAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAGmZjVEwAAAAAAAAAAQAAAAEAAAAAAAAAAAD6A+gBAbNU+2sAAAARZmRBVAAAAAEImWNgYGBgAAAABQAB6MzFdgAAAABJRU5ErkJggg==';
7058 });
7059
7060/*!
7061{
7062 "name": "Image crossOrigin",
7063 "property": "imgcrossorigin",
7064 "notes": [{
7065 "name": "Cross Domain Images and the Tainted Canvas",
7066 "href": "https://blog.codepen.io/2013/10/08/cross-domain-images-tainted-canvas/"
7067 }]
7068}
7069!*/
7070/* DOC
7071Detects support for the crossOrigin attribute on images, which allow for cross domain images inside of a canvas without tainting it
7072*/
7073
7074 Modernizr.addTest('imgcrossorigin', 'crossOrigin' in createElement('img'));
7075
7076/*!
7077{
7078 "name": "JPEG 2000",
7079 "async": true,
7080 "aliases": ["jpeg-2000", "jpg2"],
7081 "property": "jpeg2000",
7082 "tags": ["image"],
7083 "authors": ["@eric_wvgg"],
7084 "notes": [{
7085 "name": "Wikipedia Article",
7086 "href": "https://en.wikipedia.org/wiki/JPEG_2000"
7087 }]
7088}
7089!*/
7090/* DOC
7091Test for JPEG 2000 support
7092*/
7093
7094
7095 Modernizr.addAsyncTest(function() {
7096 var image = new Image();
7097
7098 image.onload = image.onerror = function() {
7099 addTest('jpeg2000', image.width == 1);
7100 };
7101
7102 image.src = 'data:image/jp2;base64,/0//UQAyAAAAAAABAAAAAgAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAEBwEBBwEBBwEBBwEB/1IADAAAAAEAAAQEAAH/XAAEQED/ZAAlAAFDcmVhdGVkIGJ5IE9wZW5KUEVHIHZlcnNpb24gMi4wLjD/kAAKAAAAAABYAAH/UwAJAQAABAQAAf9dAAUBQED/UwAJAgAABAQAAf9dAAUCQED/UwAJAwAABAQAAf9dAAUDQED/k8+kEAGvz6QQAa/PpBABr994EAk//9k=';
7103 });
7104
7105/*!
7106{
7107 "name": "JPEG XR (extended range)",
7108 "async": true,
7109 "aliases": ["jpeg-xr"],
7110 "property": "jpegxr",
7111 "tags": ["image"],
7112 "notes": [{
7113 "name": "Wikipedia Article",
7114 "href": "https://en.wikipedia.org/wiki/JPEG_XR"
7115 }]
7116}
7117!*/
7118/* DOC
7119Test for JPEG XR support
7120*/
7121
7122
7123 Modernizr.addAsyncTest(function() {
7124 var image = new Image();
7125
7126 image.onload = image.onerror = function() {
7127 addTest('jpegxr', image.width == 1, {aliases: ['jpeg-xr']});
7128 };
7129
7130 image.src = 'data:image/vnd.ms-photo;base64,SUm8AQgAAAAFAAG8AQAQAAAASgAAAIC8BAABAAAAAQAAAIG8BAABAAAAAQAAAMC8BAABAAAAWgAAAMG8BAABAAAAHwAAAAAAAAAkw91vA07+S7GFPXd2jckNV01QSE9UTwAZAYBxAAAAABP/gAAEb/8AAQAAAQAAAA==';
7131 });
7132
7133/*!
7134{
7135 "name": "sizes attribute",
7136 "async": true,
7137 "property": "sizes",
7138 "tags": ["image"],
7139 "authors": ["Mat Marquis"],
7140 "notes": [{
7141 "name": "Spec",
7142 "href": "http://picture.responsiveimages.org/#parse-sizes-attr"
7143 },{
7144 "name": "Usage Details",
7145 "href": "http://ericportis.com/posts/2014/srcset-sizes/"
7146 }]
7147}
7148!*/
7149/* DOC
7150Test for the `sizes` attribute on images
7151*/
7152
7153 Modernizr.addAsyncTest(function() {
7154 var width1, width2, test;
7155 var image = createElement('img');
7156 // in a perfect world this would be the test...
7157 var isSizes = 'sizes' in image;
7158
7159 // ... but we need to deal with Safari 9...
7160 if (!isSizes && ('srcset' in image)) {
7161 width2 = 'data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw==';
7162 width1 = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
7163
7164 test = function() {
7165 addTest('sizes', image.width == 2);
7166 };
7167
7168 image.onload = test;
7169 image.onerror = test;
7170 image.setAttribute('sizes', '9px');
7171
7172 image.srcset = width1 + ' 1w,' + width2 + ' 8w';
7173 image.src = width1;
7174 } else {
7175 addTest('sizes', isSizes);
7176 }
7177 });
7178
7179/*!
7180{
7181 "name": "srcset attribute",
7182 "property": "srcset",
7183 "tags": ["image"],
7184 "notes": [{
7185 "name": "Smashing Magazine Article",
7186 "href": "https://en.wikipedia.org/wiki/APNG"
7187 },{
7188 "name": "Generate multi-resolution images for srcset with Grunt",
7189 "href": "https://addyosmani.com/blog/generate-multi-resolution-images-for-srcset-with-grunt/"
7190 }]
7191}
7192!*/
7193/* DOC
7194Test for the srcset attribute of images
7195*/
7196
7197 Modernizr.addTest('srcset', 'srcset' in createElement('img'));
7198
7199/*!
7200{
7201 "name": "Webp",
7202 "async": true,
7203 "property": "webp",
7204 "tags": ["image"],
7205 "builderAliases": ["img_webp"],
7206 "authors": ["Krister Kari", "@amandeep", "Rich Bradshaw", "Ryan Seddon", "Paul Irish"],
7207 "notes": [{
7208 "name": "Webp Info",
7209 "href": "https://developers.google.com/speed/webp/"
7210 }, {
7211 "name": "Chormium blog - Chrome 32 Beta: Animated WebP images and faster Chrome for Android touch input",
7212 "href": "https://blog.chromium.org/2013/11/chrome-32-beta-animated-webp-images-and.html"
7213 }, {
7214 "name": "Webp Lossless Spec",
7215 "href": "https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification"
7216 }, {
7217 "name": "Article about WebP support on Android browsers",
7218 "href": "http://www.wope-framework.com/en/2013/06/24/webp-support-on-android-browsers/"
7219 }, {
7220 "name": "Chormium WebP announcement",
7221 "href": "https://blog.chromium.org/2011/11/lossless-and-transparency-encoding-in.html?m=1"
7222 }]
7223}
7224!*/
7225/* DOC
7226Tests for lossy, non-alpha webp support.
7227
7228Tests for all forms of webp support (lossless, lossy, alpha, and animated)..
7229
7230 Modernizr.webp // Basic support (lossy)
7231 Modernizr.webp.lossless // Lossless
7232 Modernizr.webp.alpha // Alpha (both lossy and lossless)
7233 Modernizr.webp.animation // Animated WebP
7234
7235*/
7236
7237
7238 Modernizr.addAsyncTest(function() {
7239
7240 var webpTests = [{
7241 'uri': 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=',
7242 'name': 'webp'
7243 }, {
7244 'uri': 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==',
7245 'name': 'webp.alpha'
7246 }, {
7247 'uri': 'data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA',
7248 'name': 'webp.animation'
7249 }, {
7250 'uri': 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=',
7251 'name': 'webp.lossless'
7252 }];
7253
7254 var webp = webpTests.shift();
7255 function test(name, uri, cb) {
7256
7257 var image = new Image();
7258
7259 function addResult(event) {
7260 // if the event is from 'onload', check the see if the image's width is
7261 // 1 pixel (which indiciates support). otherwise, it fails
7262
7263 var result = event && event.type === 'load' ? image.width == 1 : false;
7264 var baseTest = name === 'webp';
7265
7266 // if it is the base test, and the result is false, just set a literal false
7267 // rather than use the Boolean contrsuctor
7268 addTest(name, (baseTest && result) ? new Boolean(result) : result);
7269
7270 if (cb) {
7271 cb(event);
7272 }
7273 }
7274
7275 image.onerror = addResult;
7276 image.onload = addResult;
7277
7278 image.src = uri;
7279 }
7280
7281 // test for webp support in general
7282 test(webp.name, webp.uri, function(e) {
7283 // if the webp test loaded, test everything else.
7284 if (e && e.type === 'load') {
7285 for (var i = 0; i < webpTests.length; i++) {
7286 test(webpTests[i].name, webpTests[i].uri);
7287 }
7288 }
7289 });
7290
7291 });
7292
7293
7294/*!
7295{
7296 "name": "Webp Alpha",
7297 "async": true,
7298 "property": "webpalpha",
7299 "aliases": ["webp-alpha"],
7300 "tags": ["image"],
7301 "authors": ["Krister Kari", "Rich Bradshaw", "Ryan Seddon", "Paul Irish"],
7302 "notes": [{
7303 "name": "WebP Info",
7304 "href": "https://developers.google.com/speed/webp/"
7305 },{
7306 "name": "Article about WebP support on Android browsers",
7307 "href": "http://www.wope-framework.com/en/2013/06/24/webp-support-on-android-browsers/"
7308 },{
7309 "name": "Chromium WebP announcement",
7310 "href": "https://blog.chromium.org/2011/11/lossless-and-transparency-encoding-in.html?m=1"
7311 }]
7312}
7313!*/
7314/* DOC
7315Tests for transparent webp support.
7316*/
7317
7318 Modernizr.addAsyncTest(function() {
7319 var image = new Image();
7320
7321 image.onerror = function() {
7322 addTest('webpalpha', false, {aliases: ['webp-alpha']});
7323 };
7324
7325 image.onload = function() {
7326 addTest('webpalpha', image.width == 1, {aliases: ['webp-alpha']});
7327 };
7328
7329 image.src = 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==';
7330 });
7331
7332/*!
7333{
7334 "name": "Webp Animation",
7335 "async": true,
7336 "property": "webpanimation",
7337 "aliases": ["webp-animation"],
7338 "tags": ["image"],
7339 "authors": ["Krister Kari", "Rich Bradshaw", "Ryan Seddon", "Paul Irish"],
7340 "notes": [{
7341 "name": "WebP Info",
7342 "href": "https://developers.google.com/speed/webp/"
7343 },{
7344 "name": "Chromium blog - Chrome 32 Beta: Animated WebP images and faster Chrome for Android touch input",
7345 "href": "https://blog.chromium.org/2013/11/chrome-32-beta-animated-webp-images-and.html"
7346 }]
7347}
7348!*/
7349/* DOC
7350Tests for animated webp support.
7351*/
7352
7353 Modernizr.addAsyncTest(function() {
7354 var image = new Image();
7355
7356 image.onerror = function() {
7357 addTest('webpanimation', false, {aliases: ['webp-animation']});
7358 };
7359
7360 image.onload = function() {
7361 addTest('webpanimation', image.width == 1, {aliases: ['webp-animation']});
7362 };
7363
7364 image.src = 'data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA';
7365 });
7366
7367/*!
7368{
7369 "name": "Webp Lossless",
7370 "async": true,
7371 "property": ["webplossless", "webp-lossless"],
7372 "tags": ["image"],
7373 "authors": ["@amandeep", "Rich Bradshaw", "Ryan Seddon", "Paul Irish"],
7374 "notes": [{
7375 "name": "Webp Info",
7376 "href": "https://developers.google.com/speed/webp/"
7377 },{
7378 "name": "Webp Lossless Spec",
7379 "href": "https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification"
7380 }]
7381}
7382!*/
7383/* DOC
7384Tests for non-alpha lossless webp support.
7385*/
7386
7387 Modernizr.addAsyncTest(function() {
7388 var image = new Image();
7389
7390 image.onerror = function() {
7391 addTest('webplossless', false, {aliases: ['webp-lossless']});
7392 };
7393
7394 image.onload = function() {
7395 addTest('webplossless', image.width == 1, {aliases: ['webp-lossless']});
7396 };
7397
7398 image.src = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=';
7399 });
7400
7401/*!
7402{
7403 "name": "IndexedDB",
7404 "property": "indexeddb",
7405 "caniuse": "indexeddb",
7406 "tags": ["storage"],
7407 "polyfills": ["indexeddb"],
7408 "async": true
7409}
7410!*/
7411/* DOC
7412Detects support for the IndexedDB client-side storage API (final spec).
7413*/
7414
7415 // Vendors had inconsistent prefixing with the experimental Indexed DB:
7416 // - Webkit's implementation is accessible through webkitIndexedDB
7417 // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
7418 // For speed, we don't test the legacy (and beta-only) indexedDB
7419
7420 Modernizr.addAsyncTest(function() {
7421
7422 var indexeddb;
7423
7424 try {
7425 // Firefox throws a Security Error when cookies are disabled
7426 indexeddb = prefixed('indexedDB', window);
7427 } catch (e) {
7428 }
7429
7430 if (!!indexeddb) {
7431 var testDBName = 'modernizr-' + Math.random();
7432 var req = indexeddb.open(testDBName);
7433
7434 req.onerror = function() {
7435 if (req.error && req.error.name === 'InvalidStateError') {
7436 addTest('indexeddb', false);
7437 } else {
7438 addTest('indexeddb', true);
7439 detectDeleteDatabase(indexeddb, testDBName);
7440 }
7441 };
7442
7443 req.onsuccess = function() {
7444 addTest('indexeddb', true);
7445 detectDeleteDatabase(indexeddb, testDBName);
7446 };
7447 } else {
7448 addTest('indexeddb', false);
7449 }
7450 });
7451
7452 function detectDeleteDatabase(indexeddb, testDBName) {
7453 var deleteReq = indexeddb.deleteDatabase(testDBName);
7454 deleteReq.onsuccess = function() {
7455 addTest('indexeddb.deletedatabase', true);
7456 };
7457 deleteReq.onerror = function() {
7458 addTest('indexeddb.deletedatabase', false);
7459 };
7460 }
7461
7462;
7463/*!
7464{
7465 "name": "IndexedDB Blob",
7466 "property": "indexeddbblob"
7467}
7468!*/
7469/* DOC
7470Detects if the browser can save File/Blob objects to IndexedDB
7471*/
7472
7473 // Vendors had inconsistent prefixing with the experimental Indexed DB:
7474 // - Webkit's implementation is accessible through webkitIndexedDB
7475 // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
7476 // For speed, we don't test the legacy (and beta-only) indexedDB
7477
7478 Modernizr.addAsyncTest(function() {
7479 var indexeddb;
7480 var dbname = 'detect-blob-support';
7481 var supportsBlob = false;
7482 var openRequest;
7483 var db;
7484 var putRequest;
7485
7486 try {
7487 indexeddb = prefixed('indexedDB', window);
7488 } catch (e) {
7489 }
7490
7491 if (!(Modernizr.indexeddb && Modernizr.indexeddb.deletedatabase)) {
7492 return false;
7493 }
7494
7495 // Calling `deleteDatabase` in a try?catch because some contexts (e.g. data URIs)
7496 // will throw a `SecurityError`
7497 try {
7498 indexeddb.deleteDatabase(dbname).onsuccess = function() {
7499 openRequest = indexeddb.open(dbname, 1);
7500 openRequest.onupgradeneeded = function() {
7501 openRequest.result.createObjectStore('store');
7502 };
7503 openRequest.onsuccess = function() {
7504 db = openRequest.result;
7505 try {
7506 putRequest = db.transaction('store', 'readwrite').objectStore('store').put(new Blob(), 'key');
7507 putRequest.onsuccess = function() {
7508 supportsBlob = true;
7509 };
7510 putRequest.onerror = function() {
7511 supportsBlob = false;
7512 };
7513 }
7514 catch (e) {
7515 supportsBlob = false;
7516 }
7517 finally {
7518 addTest('indexeddbblob', supportsBlob);
7519 db.close();
7520 indexeddb.deleteDatabase(dbname);
7521 }
7522 };
7523 };
7524 }
7525 catch (e) {
7526 addTest('indexeddbblob', false);
7527 }
7528 });
7529
7530/*!
7531{
7532 "name": "input formaction",
7533 "property": "inputformaction",
7534 "aliases": ["input-formaction"],
7535 "notes": [{
7536 "name": "WHATWG Spec",
7537 "href": "https://html.spec.whatwg.org/multipage/forms.html#attr-fs-formaction"
7538 }, {
7539 "name": "Wufoo demo",
7540 "href": "https://www.wufoo.com/html5/attributes/13-formaction.html"
7541 }],
7542 "polyfills": [
7543 "webshims"
7544 ]
7545}
7546!*/
7547/* DOC
7548Detect support for the formaction attribute on form inputs
7549*/
7550
7551 Modernizr.addTest('inputformaction', !!('formAction' in createElement('input')), {aliases: ['input-formaction']});
7552
7553/*!
7554{
7555 "name": "input formenctype",
7556 "property": "inputformenctype",
7557 "aliases": ["input-formenctype"],
7558 "notes": [{
7559 "name": "WHATWG Spec",
7560 "href": "https://html.spec.whatwg.org/multipage/forms.html#attr-fs-formenctype"
7561 }, {
7562 "name": "Wufoo demo",
7563 "href": "https://www.wufoo.com/html5/attributes/16-formenctype.html"
7564 }],
7565 "polyfills": [
7566 "html5formshim"
7567 ]
7568}
7569!*/
7570/* DOC
7571Detect support for the formenctype attribute on form inputs, which overrides the form enctype attribute
7572*/
7573
7574 Modernizr.addTest('inputformenctype', !!('formEnctype' in createElement('input')), {aliases: ['input-formenctype']});
7575
7576/*!
7577{
7578 "name": "input formmethod",
7579 "property": "inputformmethod",
7580 "notes": [{
7581 "name": "WHATWG Spec",
7582 "href": "https://html.spec.whatwg.org/multipage/forms.html#attr-fs-formmethod"
7583 }, {
7584 "name": "Wufoo demo",
7585 "href": "https://www.wufoo.com/html5/attributes/14-formmethod.html"
7586 }],
7587 "polyfills": [
7588 "webshims"
7589 ]
7590}
7591!*/
7592/* DOC
7593Detect support for the formmethod attribute on form inputs
7594*/
7595
7596 Modernizr.addTest('inputformmethod', !!('formMethod' in createElement('input')));
7597
7598/*!
7599{
7600 "name": "input formtarget",
7601 "property": "inputformtarget",
7602 "aliases": ["input-formtarget"],
7603 "notes": [{
7604 "name": "WHATWG Spec",
7605 "href": "https://html.spec.whatwg.org/multipage/forms.html#attr-fs-formtarget"
7606 }, {
7607 "name": "Wufoo demo",
7608 "href": "https://www.wufoo.com/html5/attributes/15-formtarget.html"
7609 }],
7610 "polyfills": [
7611 "html5formshim"
7612 ]
7613}
7614!*/
7615/* DOC
7616Detect support for the formtarget attribute on form inputs, which overrides the form target attribute
7617*/
7618
7619 Modernizr.addTest('inputformtarget', !!('formtarget' in createElement('input')), {aliases: ['input-formtarget']});
7620
7621/*!
7622{
7623 "name": "input[search] search event",
7624 "property": "search",
7625 "tags": ["input","search"],
7626 "authors": ["Calvin Webster"],
7627 "notes": [{
7628 "name": "Wufoo demo",
7629 "href": "https://www.wufoo.com/html5/types/5-search.html?"
7630 }, {
7631 "name": "CSS Tricks",
7632 "href": "https://css-tricks.com/webkit-html5-search-inputs/"
7633 }]
7634}
7635!*/
7636/* DOC
7637There is a custom `search` event implemented in webkit browsers when using an `input[search]` element.
7638*/
7639
7640 Modernizr.addTest('inputsearchevent', hasEvent('search'));
7641
7642/*!
7643 {
7644 "name": "Internationalization API",
7645 "property": "intl",
7646 "notes": [{
7647 "name": "MDN documentation",
7648 "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl"
7649 },{
7650 "name": "ECMAScript spec",
7651 "href": "http://www.ecma-international.org/ecma-402/1.0/"
7652 }]
7653 }
7654 !*/
7655/* DOC
7656Detects support for the Internationalization API which allow easy formatting of number and dates and sorting string
7657based on a locale
7658*/
7659
7660 Modernizr.addTest('intl', !!prefixed('Intl', window));
7661
7662/*!
7663{
7664 "name": "Font Ligatures",
7665 "property": "ligatures",
7666 "caniuse": "font-feature",
7667 "notes": [{
7668 "name": "Cross-browser Web Fonts",
7669 "href": "http://www.sitepoint.com/cross-browser-web-fonts-part-3/"
7670 }]
7671}
7672!*/
7673/* DOC
7674Detects support for OpenType ligatures
7675*/
7676
7677 Modernizr.addTest('ligatures', testAllProps('fontFeatureSettings', '"liga" 1'));
7678
7679/*!
7680{
7681 "name": "Reverse Ordered Lists",
7682 "property": "olreversed",
7683 "notes": [{
7684 "name": "Impressive Webs article",
7685 "href": "http://impressivewebs.com/reverse-ordered-lists-html5"
7686 }],
7687 "builderAliases": ["lists_reversed"]
7688}
7689!*/
7690/* DOC
7691Detects support for the `reversed` attribute on the `<ol>` element.
7692*/
7693
7694 Modernizr.addTest('olreversed', 'reversed' in createElement('ol'));
7695
7696/*!
7697{
7698 "name": "MathML",
7699 "property": "mathml",
7700 "caniuse": "mathml",
7701 "authors": ["Addy Osmani", "Davide P. Cervone", "David Carlisle"],
7702 "knownBugs": ["Firefox < 4 will likely return a false, however it does support MathML inside XHTML documents"],
7703 "notes": [{
7704 "name": "W3C spec",
7705 "href": "https://www.w3.org/Math/"
7706 }],
7707 "polyfills": ["mathjax"]
7708}
7709!*/
7710/* DOC
7711Detects support for MathML, for mathematic equations in web pages.
7712*/
7713
7714 // Based on work by Davide (@dpvc) and David (@davidcarlisle)
7715 // in https://github.com/mathjax/MathJax/issues/182
7716
7717 Modernizr.addTest('mathml', function() {
7718 var ret;
7719
7720 testStyles('#modernizr{position:absolute;display:inline-block}', function(node) {
7721 node.innerHTML += '<math><mfrac><mi>xx</mi><mi>yy</mi></mfrac></math>';
7722
7723 ret = node.offsetHeight > node.offsetWidth;
7724 });
7725
7726 return ret;
7727 });
7728
7729/*!
7730{
7731 "name": "Hover Media Query",
7732 "property": "hovermq",
7733 "notes": [{
7734 "name": "//Name of reference document",
7735 "href": "//URL of reference document"
7736 }]
7737}
7738!*/
7739/* DOC
7740Detect support for Hover based media queries
7741*/
7742
7743 Modernizr.addTest('hovermq', mq(('(hover)')));
7744
7745/*!
7746{
7747 "name": "Pointer Media Query",
7748 "property": "pointermq",
7749 "notes": [{
7750 "name": "//Name of reference document",
7751 "href": "//URL of reference document"
7752 }]
7753}
7754!*/
7755/* DOC
7756Detect support for Pointer based media queries
7757*/
7758
7759 Modernizr.addTest('pointermq', mq(('(pointer:coarse),(pointer:fine),(pointer:none)')));
7760
7761/*!
7762{
7763 "name": "Message Channel",
7764 "property": "MessageChannel",
7765 "authors": ["Raju Konga [kongaraju]"],
7766 "caniuse" : "MessageChannel",
7767 "tags": ["performance", "messagechannel"],
7768 "notes": [{
7769 "name": "W3C Reference",
7770 "href": "https://www.w3.org/TR/2011/WD-webmessaging-20110317/#message-channels"
7771 }, {
7772 "name": "MDN documentation",
7773 "href": "https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API/Using_channel_messaging"
7774 }]
7775}
7776!*/
7777/* DOC
7778Detects support for Message Channels, a way to communicate between different browsing contexts like iframes, workers, etc..
7779*/
7780
7781 Modernizr.addTest('messagechannel', 'MessageChannel' in window);
7782
7783/*!
7784{
7785 "name": "Beacon API",
7786 "notes": [{
7787 "name": "MDN documentation",
7788 "href": "https://developer.mozilla.org/en-US/docs/Web/API/navigator.sendBeacon"
7789 },{
7790 "name": "W3C specification",
7791 "href": "https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/Beacon/Overview.html"
7792 }],
7793 "property": "beacon",
7794 "tags": ["beacon", "network"],
7795 "authors": ["C?t?lin Mari?"]
7796}
7797!*/
7798/* DOC
7799Detects support for an API that allows for asynchronous transfer of small HTTP data from the client to a server.
7800*/
7801
7802 Modernizr.addTest('beacon', 'sendBeacon' in navigator);
7803
7804/*!
7805{
7806 "name": "Low Bandwidth Connection",
7807 "property": "lowbandwidth",
7808 "tags": ["network"],
7809 "builderAliases": ["network_connection"]
7810}
7811!*/
7812/* DOC
7813Tests for determining low-bandwidth via `navigator.connection`
7814
7815There are two iterations of the `navigator.connection` interface.
7816
7817The first is present in Android 2.2+ and only in the Browser (not WebView)
7818
7819- http://docs.phonegap.com/en/1.2.0/phonegap_connection_connection.md.html#connection.type
7820- http://davidbcalhoun.com/2010/using-navigator-connection-android
7821
7822The second is specced at http://dev.w3.org/2009/dap/netinfo/ and perhaps landing in WebKit
7823
7824- https://bugs.webkit.org/show_bug.cgi?id=73528
7825
7826Unknown devices are assumed as fast
7827
7828For more rigorous network testing, consider boomerang.js: https://github.com/bluesmoon/boomerang/
7829*/
7830
7831 Modernizr.addTest('lowbandwidth', function() {
7832 // polyfill
7833 var connection = navigator.connection || {type: 0};
7834
7835 return connection.type == 3 || // connection.CELL_2G
7836 connection.type == 4 || // connection.CELL_3G
7837 /^[23]g$/.test(connection.type); // string value in new spec
7838 });
7839
7840/*!
7841{
7842 "name": "Server Sent Events",
7843 "property": "eventsource",
7844 "tags": ["network"],
7845 "builderAliases": ["network_eventsource"],
7846 "notes": [{
7847 "name": "WHATWG Spec",
7848 "href": "https://html.spec.whatwg.org/multipage/comms.html#server-sent-events"
7849 }]
7850}
7851!*/
7852/* DOC
7853Tests for server sent events aka eventsource.
7854*/
7855
7856 Modernizr.addTest('eventsource', 'EventSource' in window);
7857
7858/*!
7859{
7860 "name": "Fetch API",
7861 "property": "fetch",
7862 "tags": ["network"],
7863 "caniuse": "fetch",
7864 "notes": [{
7865 "name": "Fetch Living Standard",
7866 "href": "https://fetch.spec.whatwg.org/"
7867 }],
7868 "polyfills": ["fetch"]
7869}
7870!*/
7871/* DOC
7872Detects support for the fetch API, a modern replacement for XMLHttpRequest.
7873*/
7874
7875 Modernizr.addTest('fetch', 'fetch' in window);
7876
7877/*!
7878{
7879 "name": "XHR responseType",
7880 "property": "xhrresponsetype",
7881 "tags": ["network"],
7882 "notes": [{
7883 "name": "XMLHttpRequest Living Standard",
7884 "href": "https://xhr.spec.whatwg.org/#the-responsetype-attribute"
7885 }]
7886}
7887!*/
7888/* DOC
7889Tests for XMLHttpRequest xhr.responseType.
7890*/
7891
7892 Modernizr.addTest('xhrresponsetype', (function() {
7893 if (typeof XMLHttpRequest == 'undefined') {
7894 return false;
7895 }
7896 var xhr = new XMLHttpRequest();
7897 xhr.open('get', '/', true);
7898 return 'response' in xhr;
7899 }()));
7900
7901
7902 /**
7903 * http://mathiasbynens.be/notes/xhr-responsetype-json#comment-4
7904 *
7905 * @access private
7906 * @function testXhrType
7907 * @param {string} type - String name of the XHR type you want to detect
7908 * @returns {boolean}
7909 * @author Mathias Bynens
7910 */
7911
7912 /* istanbul ignore next */
7913 var testXhrType = function(type) {
7914 if (typeof XMLHttpRequest == 'undefined') {
7915 return false;
7916 }
7917 var xhr = new XMLHttpRequest();
7918 xhr.open('get', '/', true);
7919 try {
7920 xhr.responseType = type;
7921 } catch (error) {
7922 return false;
7923 }
7924 return 'response' in xhr && xhr.responseType == type;
7925 };
7926
7927
7928/*!
7929{
7930 "name": "XHR responseType='arraybuffer'",
7931 "property": "xhrresponsetypearraybuffer",
7932 "tags": ["network"],
7933 "notes": [{
7934 "name": "XMLHttpRequest Living Standard",
7935 "href": "https://xhr.spec.whatwg.org/#the-responsetype-attribute"
7936 }]
7937}
7938!*/
7939/* DOC
7940Tests for XMLHttpRequest xhr.responseType='arraybuffer'.
7941*/
7942
7943 Modernizr.addTest('xhrresponsetypearraybuffer', testXhrType('arraybuffer'));
7944
7945/*!
7946{
7947 "name": "XHR responseType='blob'",
7948 "property": "xhrresponsetypeblob",
7949 "tags": ["network"],
7950 "notes": [{
7951 "name": "XMLHttpRequest Living Standard",
7952 "href": "https://xhr.spec.whatwg.org/#the-responsetype-attribute"
7953 }]
7954}
7955!*/
7956/* DOC
7957Tests for XMLHttpRequest xhr.responseType='blob'.
7958*/
7959
7960 Modernizr.addTest('xhrresponsetypeblob', testXhrType('blob'));
7961
7962/*!
7963{
7964 "name": "XHR responseType='document'",
7965 "property": "xhrresponsetypedocument",
7966 "tags": ["network"],
7967 "notes": [{
7968 "name": "XMLHttpRequest Living Standard",
7969 "href": "https://xhr.spec.whatwg.org/#the-responsetype-attribute"
7970 }]
7971}
7972!*/
7973/* DOC
7974Tests for XMLHttpRequest xhr.responseType='document'.
7975*/
7976
7977 Modernizr.addTest('xhrresponsetypedocument', testXhrType('document'));
7978
7979/*!
7980{
7981 "name": "XHR responseType='json'",
7982 "property": "xhrresponsetypejson",
7983 "tags": ["network"],
7984 "notes": [{
7985 "name": "XMLHttpRequest Living Standard",
7986 "href": "https://xhr.spec.whatwg.org/#the-responsetype-attribute"
7987 },{
7988 "name": "Explanation of xhr.responseType='json'",
7989 "href": "https://mathiasbynens.be/notes/xhr-responsetype-json"
7990 }]
7991}
7992!*/
7993/* DOC
7994Tests for XMLHttpRequest xhr.responseType='json'.
7995*/
7996
7997 Modernizr.addTest('xhrresponsetypejson', testXhrType('json'));
7998
7999/*!
8000{
8001 "name": "XHR responseType='text'",
8002 "property": "xhrresponsetypetext",
8003 "tags": ["network"],
8004 "notes": [{
8005 "name": "XMLHttpRequest Living Standard",
8006 "href": "https://xhr.spec.whatwg.org/#the-responsetype-attribute"
8007 }]
8008}
8009!*/
8010/* DOC
8011Tests for XMLHttpRequest xhr.responseType='text'.
8012*/
8013
8014 Modernizr.addTest('xhrresponsetypetext', testXhrType('text'));
8015
8016/*!
8017{
8018 "name": "XML HTTP Request Level 2 XHR2",
8019 "property": "xhr2",
8020 "tags": ["network"],
8021 "builderAliases": ["network_xhr2"],
8022 "notes": [{
8023 "name": "W3 Spec",
8024 "href": "https://www.w3.org/TR/XMLHttpRequest2/"
8025 },{
8026 "name": "Details on Related Github Issue",
8027 "href": "https://github.com/Modernizr/Modernizr/issues/385"
8028 }]
8029}
8030!*/
8031/* DOC
8032Tests for XHR2.
8033*/
8034
8035 // all three of these details report consistently across all target browsers:
8036 // !!(window.ProgressEvent);
8037 // 'XMLHttpRequest' in window && 'withCredentials' in new XMLHttpRequest
8038 Modernizr.addTest('xhr2', 'XMLHttpRequest' in window && 'withCredentials' in new XMLHttpRequest());
8039
8040/*!
8041{
8042 "name": "Notification",
8043 "property": "notification",
8044 "caniuse": "notifications",
8045 "authors": ["Theodoor van Donge", "Hendrik Beskow"],
8046 "notes": [{
8047 "name": "HTML5 Rocks tutorial",
8048 "href": "http://www.html5rocks.com/en/tutorials/notifications/quick/"
8049 },{
8050 "name": "W3C spec",
8051 "href": "https://www.w3.org/TR/notifications/"
8052 }, {
8053 "name": "Changes in Chrome to Notifications API due to Service Worker Push Notifications",
8054 "href": "https://developers.google.com/web/updates/2015/05/Notifying-you-of-notificiation-changes"
8055 }],
8056 "knownBugs": [
8057 "Possibility of false-positive on Chrome for Android if permissions we're granted for a website prior to Chrome 44."
8058 ],
8059 "polyfills": ["desktop-notify", "html5-notifications"]
8060}
8061!*/
8062/* DOC
8063Detects support for the Notifications API
8064*/
8065
8066 Modernizr.addTest('notification', function() {
8067 if (!window.Notification || !window.Notification.requestPermission) {
8068 return false;
8069 }
8070 // if permission is already granted, assume support
8071 if (window.Notification.permission === 'granted') {
8072 return true;
8073 }
8074
8075 try {
8076 new window.Notification('');
8077 } catch (e) {
8078 if (e.name === 'TypeError') {
8079 return false;
8080 }
8081 }
8082
8083 return true;
8084 });
8085
8086/*!
8087{
8088 "name": "Page Visibility API",
8089 "property": "pagevisibility",
8090 "caniuse": "pagevisibility",
8091 "tags": ["performance"],
8092 "notes": [{
8093 "name": "MDN documentation",
8094 "href": "https://developer.mozilla.org/en-US/docs/DOM/Using_the_Page_Visibility_API"
8095 },{
8096 "name": "W3C spec",
8097 "href": "https://www.w3.org/TR/2011/WD-page-visibility-20110602/"
8098 },{
8099 "name": "HTML5 Rocks tutorial",
8100 "href": "http://www.html5rocks.com/en/tutorials/pagevisibility/intro/"
8101 }],
8102 "polyfills": ["visibilityjs", "visiblyjs", "jquery-visibility"]
8103}
8104!*/
8105/* DOC
8106Detects support for the Page Visibility API, which can be used to disable unnecessary actions and otherwise improve user experience.
8107*/
8108
8109 Modernizr.addTest('pagevisibility', !!prefixed('hidden', document, false));
8110
8111/*!
8112{
8113 "name": "Navigation Timing API",
8114 "property": "performance",
8115 "caniuse": "nav-timing",
8116 "tags": ["performance"],
8117 "authors": ["Scott Murphy (@uxder)"],
8118 "notes": [{
8119 "name": "W3C Spec",
8120 "href": "https://www.w3.org/TR/navigation-timing/"
8121 },{
8122 "name": "HTML5 Rocks article",
8123 "href": "http://www.html5rocks.com/en/tutorials/webperformance/basics/"
8124 }],
8125 "polyfills": ["perfnow"]
8126}
8127!*/
8128/* DOC
8129Detects support for the Navigation Timing API, for measuring browser and connection performance.
8130*/
8131
8132 Modernizr.addTest('performance', !!prefixed('performance', window));
8133
8134/*!
8135{
8136 "name": "DOM Pointer Events API",
8137 "property": "pointerevents",
8138 "tags": ["input"],
8139 "authors": ["Stu Cox"],
8140 "notes": [
8141 {
8142 "name": "W3C Pointer Events",
8143 "href": "https://www.w3.org/TR/pointerevents/"
8144 },{
8145 "name": "W3C Pointer Events Level 2",
8146 "href": "https://www.w3.org/TR/pointerevents2/"
8147 },{
8148 "name": "MDN documentation",
8149 "href": "https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent"
8150 }],
8151 "warnings": ["This property name now refers to W3C DOM PointerEvents: https://github.com/Modernizr/Modernizr/issues/548#issuecomment-12812099"],
8152 "polyfills": ["pep"]
8153}
8154!*/
8155/* DOC
8156Detects support for the DOM Pointer Events API, which provides a unified event interface for pointing input devices, as implemented in IE10+, Edge and Blink.
8157*/
8158
8159 // **Test name hijacked!**
8160 // Now refers to W3C DOM PointerEvents spec rather than the CSS pointer-events property.
8161 Modernizr.addTest('pointerevents', function() {
8162 // Cannot use `.prefixed()` for events, so test each prefix
8163 var bool = false,
8164 i = domPrefixes.length;
8165
8166 // Don't forget un-prefixed...
8167 bool = Modernizr.hasEvent('pointerdown');
8168
8169 while (i-- && !bool) {
8170 if (hasEvent(domPrefixes[i] + 'pointerdown')) {
8171 bool = true;
8172 }
8173 }
8174 return bool;
8175 });
8176
8177/*!
8178{
8179 "name": "Pointer Lock API",
8180 "property": "pointerlock",
8181 "notes": [{
8182 "name": "MDN documentation",
8183 "href": "https://developer.mozilla.org/en-US/docs/API/Pointer_Lock_API"
8184 }],
8185 "builderAliases": ["pointerlock_api"]
8186}
8187!*/
8188/* DOC
8189Detects support the pointer lock API which allows you to lock the mouse cursor to the browser window.
8190*/
8191
8192 // https://developer.mozilla.org/en-US/docs/API/Pointer_Lock_API
8193 Modernizr.addTest('pointerlock', !!prefixed('exitPointerLock', document));
8194
8195/*!
8196{
8197 "name": "postMessage",
8198 "property": "postmessage",
8199 "caniuse": "x-doc-messaging",
8200 "notes": [{
8201 "name": "W3C Spec",
8202 "href": "http://www.w3.org/TR/html5/comms.html#posting-messages"
8203 }],
8204 "polyfills": ["easyxdm", "postmessage-jquery"]
8205}
8206!*/
8207/* DOC
8208Detects support for the `window.postMessage` protocol for cross-document messaging.
8209*/
8210
8211 Modernizr.addTest('postmessage', 'postMessage' in window);
8212
8213/*!
8214{
8215 "authors": ["C?t?lin Mari?"],
8216 "caniuse": "proximity",
8217 "name": "Proximity API",
8218 "notes": [{
8219 "name": "MDN documentation",
8220 "href": "https://developer.mozilla.org/en-US/docs/Web/API/Proximity_Events"
8221 },{
8222 "name": "W3C specification",
8223 "href": "https://www.w3.org/TR/proximity/"
8224 }],
8225 "property": "proximity",
8226 "tags": ["events", "proximity"]
8227}
8228!*/
8229/* DOC
8230Detects support for an API that allows users to get proximity related information from the device's proximity sensor.
8231*/
8232
8233
8234 Modernizr.addAsyncTest(function() {
8235
8236 var timeout;
8237 var timeoutTime = 300;
8238
8239 function advertiseSupport() {
8240
8241 // Clean up after ourselves
8242 clearTimeout(timeout);
8243 window.removeEventListener('deviceproximity', advertiseSupport);
8244
8245 // Advertise support as the browser supports
8246 // the API and the device has a proximity sensor
8247 addTest('proximity', true);
8248
8249 }
8250
8251 // Check if the browser has support for the API
8252 if ('ondeviceproximity' in window && 'onuserproximity' in window) {
8253
8254 // Check if the device has a proximity sensor
8255 // ( devices without such a sensor support the events but
8256 // will never fire them resulting in a false positive )
8257 window.addEventListener('deviceproximity', advertiseSupport);
8258
8259 // If the event doesn't fire in a reasonable amount of time,
8260 // it means that the device doesn't have a proximity sensor,
8261 // thus, we can advertise the "lack" of support
8262 timeout = setTimeout(function() {
8263 window.removeEventListener('deviceproximity', advertiseSupport);
8264 addTest('proximity', false);
8265 }, timeoutTime);
8266
8267 } else {
8268 addTest('proximity', false);
8269 }
8270
8271 });
8272
8273
8274/*!
8275{
8276 "name": "QuerySelector",
8277 "property": "queryselector",
8278 "caniuse": "queryselector",
8279 "tags": ["queryselector"],
8280 "authors": ["Andrew Betts (@triblondon)"],
8281 "notes": [{
8282 "name" : "W3C Selectors reference",
8283 "href": "https://www.w3.org/TR/selectors-api/#queryselectorall"
8284 }],
8285 "polyfills": ["css-selector-engine"]
8286}
8287!*/
8288/* DOC
8289Detects support for querySelector.
8290*/
8291
8292 Modernizr.addTest('queryselector', 'querySelector' in document && 'querySelectorAll' in document);
8293
8294/*!
8295{
8296 "name": "Quota Storage Management API",
8297 "property": "quotamanagement",
8298 "tags": ["storage"],
8299 "builderAliases": ["quota_management_api"],
8300 "notes": [{
8301 "name": "W3C Spec",
8302 "href": "https://www.w3.org/TR/quota-api/"
8303 }]
8304}
8305!*/
8306/* DOC
8307Detects the ability to request a specific amount of space for filesystem access
8308*/
8309
8310 Modernizr.addTest('quotamanagement', function() {
8311 var tempStorage = prefixed('temporaryStorage', navigator);
8312 var persStorage = prefixed('persistentStorage', navigator);
8313
8314 return !!(tempStorage && persStorage);
8315 });
8316
8317/*!
8318{
8319 "name": "requestAnimationFrame",
8320 "property": "requestanimationframe",
8321 "aliases": ["raf"],
8322 "caniuse": "requestanimationframe",
8323 "tags": ["animation"],
8324 "authors": ["Addy Osmani"],
8325 "notes": [{
8326 "name": "W3C spec",
8327 "href": "https://www.w3.org/TR/animation-timing/"
8328 }],
8329 "polyfills": ["raf"]
8330}
8331!*/
8332/* DOC
8333Detects support for the `window.requestAnimationFrame` API, for offloading animation repainting to the browser for optimized performance.
8334*/
8335
8336 Modernizr.addTest('requestanimationframe', !!prefixed('requestAnimationFrame', window), {aliases: ['raf']});
8337
8338/*!
8339{
8340 "name": "script[async]",
8341 "property": "scriptasync",
8342 "caniuse": "script-async",
8343 "tags": ["script"],
8344 "builderAliases": ["script_async"],
8345 "authors": ["Theodoor van Donge"]
8346}
8347!*/
8348/* DOC
8349Detects support for the `async` attribute on the `<script>` element.
8350*/
8351
8352 Modernizr.addTest('scriptasync', 'async' in createElement('script'));
8353
8354/*!
8355{
8356 "name": "script[defer]",
8357 "property": "scriptdefer",
8358 "caniuse": "script-defer",
8359 "tags": ["script"],
8360 "builderAliases": ["script_defer"],
8361 "authors": ["Theodoor van Donge"],
8362 "warnings": ["Browser implementation of the `defer` attribute vary: https://stackoverflow.com/questions/3952009/defer-attribute-chrome#answer-3982619"],
8363 "knownBugs": ["False positive in Opera 12"]
8364}
8365!*/
8366/* DOC
8367Detects support for the `defer` attribute on the `<script>` element.
8368*/
8369
8370 Modernizr.addTest('scriptdefer', 'defer' in createElement('script'));
8371
8372/*!
8373{
8374 "name": "ServiceWorker API",
8375 "property": "serviceworker",
8376 "notes": [{
8377 "name": "ServiceWorkers Explained",
8378 "href": "https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md"
8379 }]
8380}
8381!*/
8382/* DOC
8383ServiceWorkers (formerly Navigation Controllers) are a way to persistently cache resources to built apps that work better offline.
8384*/
8385
8386 Modernizr.addTest('serviceworker', 'serviceWorker' in navigator);
8387
8388/*!
8389{
8390 "authors": ["C?t?lin Mari?"],
8391 "name": "Speech Recognition API",
8392 "notes": [
8393 {
8394 "name": "W3C Web Speech API Specification - The SpeechRecognition Interface",
8395 "href": "https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html#speechreco-section"
8396 },
8397 {
8398 "name": "Introduction to the Web Speech API",
8399 "href": "http://updates.html5rocks.com/2013/01/Voice-Driven-Web-Apps-Introduction-to-the-Web-Speech-API"
8400 }
8401 ],
8402 "property": "speechrecognition",
8403 "tags": ["input", "speech"]
8404}
8405!*/
8406
8407
8408 Modernizr.addTest('speechrecognition', !!prefixed('SpeechRecognition', window));
8409
8410/*!
8411{
8412 "authors": ["C?t?lin Mari?"],
8413 "name": "Speech Synthesis API",
8414 "notes": [
8415 {
8416 "name": "W3C Web Speech API Specification - The SpeechSynthesis Interface",
8417 "href": "https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html#tts-section"
8418 }
8419 ],
8420 "property": "speechsynthesis",
8421 "tags": ["input", "speech"]
8422}
8423!*/
8424
8425
8426 Modernizr.addTest('speechsynthesis', 'SpeechSynthesisUtterance' in window);
8427
8428/*!
8429{
8430 "name": "Local Storage",
8431 "property": "localstorage",
8432 "caniuse": "namevalue-storage",
8433 "tags": ["storage"],
8434 "knownBugs": [],
8435 "notes": [],
8436 "warnings": [],
8437 "polyfills": [
8438 "joshuabell-polyfill",
8439 "cupcake",
8440 "storagepolyfill",
8441 "amplifyjs",
8442 "yui-cacheoffline"
8443 ]
8444}
8445!*/
8446
8447 // In FF4, if disabled, window.localStorage should === null.
8448
8449 // Normally, we could not test that directly and need to do a
8450 // `('localStorage' in window)` test first because otherwise Firefox will
8451 // throw bugzil.la/365772 if cookies are disabled
8452
8453 // Similarly, in Chrome with "Block third-party cookies and site data" enabled,
8454 // attempting to access `window.sessionStorage` will throw an exception. crbug.com/357625
8455
8456 // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
8457 // will throw the exception:
8458 // QUOTA_EXCEEDED_ERROR DOM Exception 22.
8459 // Peculiarly, getItem and removeItem calls do not throw.
8460
8461 // Because we are forced to try/catch this, we'll go aggressive.
8462
8463 // Just FWIW: IE8 Compat mode supports these features completely:
8464 // www.quirksmode.org/dom/html5.html
8465 // But IE8 doesn't support either with local files
8466
8467 Modernizr.addTest('localstorage', function() {
8468 var mod = 'modernizr';
8469 try {
8470 localStorage.setItem(mod, mod);
8471 localStorage.removeItem(mod);
8472 return true;
8473 } catch (e) {
8474 return false;
8475 }
8476 });
8477
8478/*!
8479{
8480 "name": "Session Storage",
8481 "property": "sessionstorage",
8482 "tags": ["storage"],
8483 "polyfills": ["joshuabell-polyfill", "cupcake", "sessionstorage"]
8484}
8485!*/
8486
8487 // Because we are forced to try/catch this, we'll go aggressive.
8488
8489 // Just FWIW: IE8 Compat mode supports these features completely:
8490 // www.quirksmode.org/dom/html5.html
8491 // But IE8 doesn't support either with local files
8492 Modernizr.addTest('sessionstorage', function() {
8493 var mod = 'modernizr';
8494 try {
8495 sessionStorage.setItem(mod, mod);
8496 sessionStorage.removeItem(mod);
8497 return true;
8498 } catch (e) {
8499 return false;
8500 }
8501 });
8502
8503/*!
8504{
8505 "name": "Web SQL Database",
8506 "property": "websqldatabase",
8507 "caniuse": "sql-storage",
8508 "tags": ["storage"]
8509}
8510!*/
8511
8512 // Chrome incognito mode used to throw an exception when using openDatabase
8513 // It doesn't anymore.
8514 Modernizr.addTest('websqldatabase', 'openDatabase' in window);
8515
8516/*!
8517{
8518 "name": "style[scoped]",
8519 "property": "stylescoped",
8520 "caniuse": "style-scoped",
8521 "tags": ["dom"],
8522 "builderAliases": ["style_scoped"],
8523 "authors": ["C?t?lin Mari?"],
8524 "notes": [{
8525 "name": "WHATWG Specification",
8526 "href": "https://html.spec.whatwg.org/multipage/semantics.html#attr-style-scoped"
8527 }],
8528 "polyfills": ["scoped-styles"]
8529}
8530!*/
8531/* DOC
8532Support for the `scoped` attribute of the `<style>` element.
8533*/
8534
8535 Modernizr.addTest('stylescoped', 'scoped' in createElement('style'));
8536
8537/*!
8538{
8539 "name": "SVG",
8540 "property": "svg",
8541 "caniuse": "svg",
8542 "tags": ["svg"],
8543 "authors": ["Erik Dahlstrom"],
8544 "polyfills": [
8545 "svgweb",
8546 "raphael",
8547 "amplesdk",
8548 "canvg",
8549 "svg-boilerplate",
8550 "sie",
8551 "dojogfx",
8552 "fabricjs"
8553 ]
8554}
8555!*/
8556/* DOC
8557Detects support for SVG in `<embed>` or `<object>` elements.
8558*/
8559
8560 Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);
8561
8562/*!
8563{
8564 "name": "SVG as an <img> tag source",
8565 "property": "svgasimg",
8566 "caniuse" : "svg-img",
8567 "tags": ["svg"],
8568 "aliases": ["svgincss"],
8569 "authors": ["Chris Coyier"],
8570 "notes": [{
8571 "name": "HTML5 Spec",
8572 "href": "http://www.w3.org/TR/html5/embedded-content-0.html#the-img-element"
8573 }]
8574}
8575!*/
8576
8577
8578 // Original Async test by Stu Cox
8579 // https://gist.github.com/chriscoyier/8774501
8580
8581 // Now a Sync test based on good results here
8582 // http://codepen.io/chriscoyier/pen/bADFx
8583
8584 // Note http://www.w3.org/TR/SVG11/feature#Image is *supposed* to represent
8585 // support for the `<image>` tag in SVG, not an SVG file linked from an `<img>`
8586 // tag in HTML ? but it?s a heuristic which works
8587 Modernizr.addTest('svgasimg', document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1'));
8588
8589
8590 /**
8591 * Object.prototype.toString can be used with every object and allows you to
8592 * get its class easily. Abstracting it off of an object prevents situations
8593 * where the toString property has been overridden
8594 *
8595 * @access private
8596 * @function toStringFn
8597 * @returns {function} An abstracted toString function
8598 */
8599
8600 var toStringFn = ({}).toString;
8601
8602/*!
8603{
8604 "name": "SVG clip paths",
8605 "property": "svgclippaths",
8606 "tags": ["svg"],
8607 "notes": [{
8608 "name": "Demo",
8609 "href": "http://srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg"
8610 }]
8611}
8612!*/
8613/* DOC
8614Detects support for clip paths in SVG (only, not on HTML content).
8615
8616See [this discussion](https://github.com/Modernizr/Modernizr/issues/213) regarding applying SVG clip paths to HTML content.
8617*/
8618
8619 Modernizr.addTest('svgclippaths', function() {
8620 return !!document.createElementNS &&
8621 /SVGClipPath/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')));
8622 });
8623
8624/*!
8625{
8626 "name": "SVG filters",
8627 "property": "svgfilters",
8628 "caniuse": "svg-filters",
8629 "tags": ["svg"],
8630 "builderAliases": ["svg_filters"],
8631 "authors": ["Erik Dahlstrom"],
8632 "notes": [{
8633 "name": "W3C Spec",
8634 "href": "https://www.w3.org/TR/SVG11/filters.html"
8635 }]
8636}
8637!*/
8638
8639 // Should fail in Safari: https://stackoverflow.com/questions/9739955/feature-detecting-support-for-svg-filters.
8640 Modernizr.addTest('svgfilters', function() {
8641 var result = false;
8642 try {
8643 result = 'SVGFEColorMatrixElement' in window &&
8644 SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE == 2;
8645 }
8646 catch (e) {}
8647 return result;
8648 });
8649
8650/*!
8651{
8652 "name": "SVG foreignObject",
8653 "property": "svgforeignobject",
8654 "tags": ["svg"],
8655 "notes": [{
8656 "name": "W3C Spec",
8657 "href": "https://www.w3.org/TR/SVG11/extend.html"
8658 }]
8659}
8660!*/
8661/* DOC
8662Detects support for foreignObject tag in SVG.
8663*/
8664
8665 Modernizr.addTest('svgforeignobject', function() {
8666 return !!document.createElementNS &&
8667 /SVGForeignObject/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')));
8668 });
8669
8670/*!
8671{
8672 "name": "Inline SVG",
8673 "property": "inlinesvg",
8674 "caniuse": "svg-html5",
8675 "tags": ["svg"],
8676 "notes": [{
8677 "name": "Test page",
8678 "href": "https://paulirish.com/demo/inline-svg"
8679 }, {
8680 "name": "Test page and results",
8681 "href": "https://codepen.io/eltonmesquita/full/GgXbvo/"
8682 }],
8683 "polyfills": ["inline-svg-polyfill"],
8684 "knownBugs": ["False negative on some Chromia browsers."]
8685}
8686!*/
8687/* DOC
8688Detects support for inline SVG in HTML (not within XHTML).
8689*/
8690
8691 Modernizr.addTest('inlinesvg', function() {
8692 var div = createElement('div');
8693 div.innerHTML = '<svg/>';
8694 return (typeof SVGRect != 'undefined' && div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg';
8695 });
8696
8697/*!
8698{
8699 "name": "SVG SMIL animation",
8700 "property": "smil",
8701 "caniuse": "svg-smil",
8702 "tags": ["svg"],
8703 "notes": [{
8704 "name": "W3C Synchronised Multimedia spec",
8705 "href": "https://www.w3.org/AudioVideo/"
8706 }]
8707}
8708!*/
8709
8710 // SVG SMIL animation
8711 Modernizr.addTest('smil', function() {
8712 return !!document.createElementNS &&
8713 /SVGAnimate/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'animate')));
8714 });
8715
8716/*!
8717{
8718 "name": "Template strings",
8719 "property": "templatestrings",
8720 "notes": [{
8721 "name": "MDN Reference",
8722 "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings#Browser_compatibility"
8723 }]
8724}
8725!*/
8726/* DOC
8727Template strings are string literals allowing embedded expressions.
8728*/
8729
8730 Modernizr.addTest('templatestrings', function() {
8731 var supports;
8732 try {
8733 // A number of tools, including uglifyjs and require, break on a raw "`", so
8734 // use an eval to get around that.
8735 // eslint-disable-next-line
8736 eval('``');
8737 supports = true;
8738 } catch (e) {}
8739 return !!supports;
8740 });
8741
8742/*!
8743{
8744 "name": "textarea maxlength",
8745 "property": "textareamaxlength",
8746 "aliases": ["textarea-maxlength"],
8747 "notes": [{
8748 "name": "MDN documentation",
8749 "href": "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea"
8750 }],
8751 "polyfills": [
8752 "maxlength"
8753 ]
8754}
8755!*/
8756/* DOC
8757Detect support for the maxlength attribute of a textarea element
8758*/
8759
8760 Modernizr.addTest('textareamaxlength', !!('maxLength' in createElement('textarea')));
8761
8762/*!
8763{
8764 "name": "Touch Events",
8765 "property": "touchevents",
8766 "caniuse" : "touch",
8767 "tags": ["media", "attribute"],
8768 "notes": [{
8769 "name": "Touch Events spec",
8770 "href": "https://www.w3.org/TR/2013/WD-touch-events-20130124/"
8771 }],
8772 "warnings": [
8773 "Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device"
8774 ],
8775 "knownBugs": [
8776 "False-positive on some configurations of Nokia N900",
8777 "False-positive on some BlackBerry 6.0 builds ? https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695"
8778 ]
8779}
8780!*/
8781/* DOC
8782Indicates if the browser supports the W3C Touch Events API.
8783
8784This *does not* necessarily reflect a touchscreen device:
8785
8786* Older touchscreen devices only emulate mouse events
8787* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that
8788* Some browsers & OS setups may enable touch APIs when no touchscreen is connected
8789* Future browsers may implement other event models for touch interactions
8790
8791See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/).
8792
8793It's recommended to bind both mouse and touch/pointer events simultaneously ? see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/).
8794
8795This test will also return `true` for Firefox 4 Multitouch support.
8796*/
8797
8798 // Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415
8799 Modernizr.addTest('touchevents', function() {
8800 var bool;
8801 if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
8802 bool = true;
8803 } else {
8804 // include the 'heartz' as a way to have a non matching MQ to help terminate the join
8805 // https://git.io/vznFH
8806 var query = ['@media (', prefixes.join('touch-enabled),('), 'heartz', ')', '{#modernizr{top:9px;position:absolute}}'].join('');
8807 testStyles(query, function(node) {
8808 bool = node.offsetTop === 9;
8809 });
8810 }
8811 return bool;
8812 });
8813
8814/*!
8815{
8816 "name": "Typed arrays",
8817 "property": "typedarrays",
8818 "caniuse": "typedarrays",
8819 "tags": ["js"],
8820 "authors": ["Stanley Stuart (@fivetanley)"],
8821 "notes": [{
8822 "name": "MDN documentation",
8823 "href": "https://developer.mozilla.org/en-US/docs/JavaScript_typed_arrays"
8824 },{
8825 "name": "Kronos spec",
8826 "href": "https://www.khronos.org/registry/typedarray/specs/latest/"
8827 }],
8828 "polyfills": ["joshuabell-polyfill"]
8829}
8830!*/
8831/* DOC
8832Detects support for native binary data manipulation via Typed Arrays in JavaScript.
8833
8834Does not check for DataView support; use `Modernizr.dataview` for that.
8835*/
8836
8837 // Should fail in:
8838 // Internet Explorer <= 9
8839 // Firefox <= 3.6
8840 // Chrome <= 6.0
8841 // iOS Safari < 4.2
8842 // Safari < 5.1
8843 // Opera < 11.6
8844 // Opera Mini, <= 7.0
8845 // Android Browser < 4.0
8846 // Blackberry Browser < 10.0
8847
8848 Modernizr.addTest('typedarrays', 'ArrayBuffer' in window);
8849
8850/*!
8851{
8852 "name": "Unicode characters",
8853 "property": "unicode",
8854 "tags": ["encoding"],
8855 "warnings": [
8856 "positive Unicode support doesn't mean you can use it inside <title>, this seems more related to OS & Language packs"
8857 ]
8858}
8859!*/
8860/* DOC
8861Detects if unicode characters are supported in the current document.
8862*/
8863
8864 /**
8865 * Unicode special character support
8866 *
8867 * Detection is made by testing missing glyph box rendering against star character
8868 * If widths are the same, this "probably" means the browser didn't support the star character and rendered a glyph box instead
8869 * Just need to ensure the font characters have different widths
8870 */
8871 Modernizr.addTest('unicode', function() {
8872 var bool;
8873 var missingGlyph = createElement('span');
8874 var star = createElement('span');
8875
8876 testStyles('#modernizr{font-family:Arial,sans;font-size:300em;}', function(node) {
8877
8878 missingGlyph.innerHTML = isSVG ? '\u5987' : 'ᝣ';
8879 star.innerHTML = isSVG ? '\u2606' : '☆';
8880
8881 node.appendChild(missingGlyph);
8882 node.appendChild(star);
8883
8884 bool = 'offsetWidth' in missingGlyph && missingGlyph.offsetWidth !== star.offsetWidth;
8885 });
8886
8887 return bool;
8888
8889 });
8890
8891/*!
8892{
8893 "name": "Unicode Range",
8894 "property": "unicoderange",
8895 "notes": [{
8896 "name" : "W3C reference",
8897 "href": "https://www.w3.org/TR/2013/CR-css-fonts-3-20131003/#descdef-unicode-range"
8898 }, {
8899 "name" : "24 Way article",
8900 "href": "https://24ways.org/2011/creating-custom-font-stacks-with-unicode-range"
8901 }]
8902}
8903!*/
8904
8905 Modernizr.addTest('unicoderange', function() {
8906
8907 return Modernizr.testStyles('@font-face{font-family:"unicodeRange";src:local("Arial");unicode-range:U+0020,U+002E}#modernizr span{font-size:20px;display:inline-block;font-family:"unicodeRange",monospace}#modernizr .mono{font-family:monospace}', function(elem) {
8908
8909 // we use specify a unicode-range of 002E (the `.` glyph,
8910 // and a monospace font as the fallback. If the first of
8911 // these test glyphs is a different width than the other
8912 // the other three (which are all monospace), then we
8913 // have a winner.
8914 var testGlyphs = ['.', '.', 'm', 'm'];
8915
8916 for (var i = 0; i < testGlyphs.length; i++) {
8917 var elm = createElement('span');
8918 elm.innerHTML = testGlyphs[i];
8919 elm.className = i % 2 ? 'mono' : '';
8920 elem.appendChild(elm);
8921 testGlyphs[i] = elm.clientWidth;
8922 }
8923
8924 return (testGlyphs[0] !== testGlyphs[1] && testGlyphs[2] === testGlyphs[3]);
8925 });
8926 });
8927
8928/*!
8929{
8930 "name": "Blob URLs",
8931 "property": "bloburls",
8932 "caniuse": "bloburls",
8933 "notes": [{
8934 "name": "W3C Working Draft",
8935 "href": "https://www.w3.org/TR/FileAPI/#creating-revoking"
8936 }],
8937 "tags": ["file", "url"],
8938 "authors": ["Ron Waldon (@jokeyrhyme)"]
8939}
8940!*/
8941/* DOC
8942Detects support for creating Blob URLs
8943*/
8944
8945 var url = prefixed('URL', window, false);
8946 url = url && window[url];
8947 Modernizr.addTest('bloburls', url && 'revokeObjectURL' in url && 'createObjectURL' in url);
8948
8949/*!
8950{
8951 "name": "Data URI",
8952 "property": "datauri",
8953 "caniuse": "datauri",
8954 "tags": ["url"],
8955 "builderAliases": ["url_data_uri"],
8956 "async": true,
8957 "notes": [{
8958 "name": "Wikipedia article",
8959 "href": "https://en.wikipedia.org/wiki/Data_URI_scheme"
8960 }],
8961 "warnings": ["Support in Internet Explorer 8 is limited to images and linked resources like CSS files, not HTML files"]
8962}
8963!*/
8964/* DOC
8965Detects support for data URIs. Provides a subproperty to report support for data URIs over 32kb in size:
8966
8967```javascript
8968Modernizr.datauri // true
8969Modernizr.datauri.over32kb // false in IE8
8970```
8971*/
8972
8973 // https://github.com/Modernizr/Modernizr/issues/14
8974 Modernizr.addAsyncTest(function() {
8975
8976 // IE7 throw a mixed content warning on HTTPS for this test, so we'll
8977 // just blacklist it (we know it doesn't support data URIs anyway)
8978 // https://github.com/Modernizr/Modernizr/issues/362
8979 if (navigator.userAgent.indexOf('MSIE 7.') !== -1) {
8980 // Keep the test async
8981 setTimeout(function() {
8982 addTest('datauri', false);
8983 }, 10);
8984 }
8985
8986 var datauri = new Image();
8987
8988 datauri.onerror = function() {
8989 addTest('datauri', false);
8990 };
8991 datauri.onload = function() {
8992 if (datauri.width == 1 && datauri.height == 1) {
8993 testOver32kb();
8994 }
8995 else {
8996 addTest('datauri', false);
8997 }
8998 };
8999
9000 datauri.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
9001
9002 // Once we have datauri, let's check to see if we can use data URIs over
9003 // 32kb (IE8 can't). https://github.com/Modernizr/Modernizr/issues/321
9004 function testOver32kb() {
9005
9006 var datauriBig = new Image();
9007
9008 datauriBig.onerror = function() {
9009 addTest('datauri', true);
9010 Modernizr.datauri = new Boolean(true);
9011 Modernizr.datauri.over32kb = false;
9012 };
9013 datauriBig.onload = function() {
9014 addTest('datauri', true);
9015 Modernizr.datauri = new Boolean(true);
9016 Modernizr.datauri.over32kb = (datauriBig.width == 1 && datauriBig.height == 1);
9017 };
9018
9019 var base64str = 'R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
9020 while (base64str.length < 33000) {
9021 base64str = '\r\n' + base64str;
9022 }
9023 datauriBig.src = 'data:image/gif;base64,' + base64str;
9024 }
9025
9026 });
9027
9028/*!
9029{
9030 "name": "URL parser",
9031 "property": "urlparser",
9032 "notes": [{
9033 "name": "URL",
9034 "href": "https://dvcs.w3.org/hg/url/raw-file/tip/Overview.html"
9035 }],
9036 "polyfills": ["urlparser"],
9037 "authors": ["Ron Waldon (@jokeyrhyme)"],
9038 "tags": ["url"]
9039}
9040!*/
9041/* DOC
9042Check if browser implements the URL constructor for parsing URLs.
9043*/
9044
9045 Modernizr.addTest('urlparser', function() {
9046 var url;
9047 try {
9048 // have to actually try use it, because Safari defines a dud constructor
9049 url = new URL('http://modernizr.com/');
9050 return url.href === 'http://modernizr.com/';
9051 } catch (e) {
9052 return false;
9053 }
9054 });
9055
9056/*!
9057{
9058 "authors": ["C?t?lin Mari?"],
9059 "name": "URLSearchParams API",
9060 "notes": [
9061 {
9062 "name": "WHATWG specification",
9063 "href": "https://url.spec.whatwg.org/#interface-urlsearchparams"
9064 },
9065 {
9066 "name": "MDN documentation",
9067 "href": "https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams"
9068 }
9069 ],
9070 "property": "urlsearchparams",
9071 "tags": ["querystring", "url"]
9072}
9073!*/
9074
9075/* DOC
9076Detects support for an API that provides utility methods for working with the query string of a URL.
9077*/
9078
9079
9080 Modernizr.addTest('urlsearchparams', 'URLSearchParams' in window);
9081
9082/*!
9083{
9084 "name": "IE User Data API",
9085 "property": "userdata",
9086 "tags": ["storage"],
9087 "authors": ["@stereobooster"],
9088 "notes": [{
9089 "name": "MSDN Documentation",
9090 "href": "https://msdn.microsoft.com/en-us/library/ms531424.aspx"
9091 }]
9092}
9093!*/
9094/* DOC
9095Detects support for IE userData for persisting data, an API similar to localStorage but supported since IE5.
9096*/
9097
9098 Modernizr.addTest('userdata', !!createElement('div').addBehavior);
9099
9100/*!
9101{
9102 "name": "Vibration API",
9103 "property": "vibrate",
9104 "notes": [{
9105 "name": "MDN documentation",
9106 "href": "https://developer.mozilla.org/en/DOM/window.navigator.mozVibrate"
9107 },{
9108 "name": "W3C spec",
9109 "href": "https://www.w3.org/TR/vibration/"
9110 }]
9111}
9112!*/
9113/* DOC
9114Detects support for the API that provides access to the vibration mechanism of the hosting device, to provide tactile feedback.
9115*/
9116
9117 Modernizr.addTest('vibrate', !!prefixed('vibrate', navigator));
9118
9119/*!
9120{
9121 "name": "HTML5 Video",
9122 "property": "video",
9123 "caniuse": "video",
9124 "tags": ["html5"],
9125 "knownBugs": [
9126 "Without QuickTime, `Modernizr.video.h264` will be `undefined`; https://github.com/Modernizr/Modernizr/issues/546"
9127 ],
9128 "polyfills": [
9129 "html5media",
9130 "mediaelementjs",
9131 "sublimevideo",
9132 "videojs",
9133 "leanbackplayer",
9134 "videoforeverybody"
9135 ]
9136}
9137!*/
9138/* DOC
9139Detects support for the video element, as well as testing what types of content it supports.
9140
9141Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.:
9142
9143```javascript
9144Modernizr.video // true
9145Modernizr.video.ogg // 'probably'
9146```
9147*/
9148
9149 // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
9150 // thx to NielsLeenheer and zcorpan
9151
9152 // Note: in some older browsers, "no" was a return value instead of empty string.
9153 // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
9154 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
9155
9156 Modernizr.addTest('video', function() {
9157 var elem = createElement('video');
9158 var bool = false;
9159
9160 // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
9161 try {
9162 bool = !!elem.canPlayType
9163 if (bool) {
9164 bool = new Boolean(bool);
9165 bool.ogg = elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, '');
9166
9167 // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
9168 bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, '');
9169
9170 bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, '');
9171
9172 bool.vp9 = elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/, '');
9173
9174 bool.hls = elem.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/, '');
9175 }
9176 } catch (e) {}
9177
9178 return bool;
9179 });
9180
9181/*!
9182{
9183 "name": "Video Autoplay",
9184 "property": "videoautoplay",
9185 "tags": ["video"],
9186 "async" : true,
9187 "warnings": ["This test is very large ? only include it if you absolutely need it"],
9188 "knownBugs": ["crashes with an alert on iOS7 when added to homescreen"]
9189}
9190!*/
9191/* DOC
9192Checks for support of the autoplay attribute of the video element.
9193*/
9194
9195
9196 Modernizr.addAsyncTest(function() {
9197 var timeout;
9198 var waitTime = 200;
9199 var retries = 5;
9200 var currentTry = 0;
9201 var elem = createElement('video');
9202 var elemStyle = elem.style;
9203
9204 function testAutoplay(arg) {
9205 currentTry++;
9206 clearTimeout(timeout);
9207
9208 var result = arg && arg.type === 'playing' || elem.currentTime !== 0;
9209
9210 if (!result && currentTry < retries) {
9211 //Detection can be flaky if the browser is slow, so lets retry in a little bit
9212 timeout = setTimeout(testAutoplay, waitTime);
9213 return;
9214 }
9215
9216 elem.removeEventListener('playing', testAutoplay, false);
9217 addTest('videoautoplay', result);
9218
9219 // Cleanup, but don't assume elem is still in the page -
9220 // an extension (eg Flashblock) may already have removed it.
9221 if (elem.parentNode) {
9222 elem.parentNode.removeChild(elem);
9223 }
9224 }
9225
9226 //skip the test if video itself, or the autoplay
9227 //element on it isn't supported
9228 if (!Modernizr.video || !('autoplay' in elem)) {
9229 addTest('videoautoplay', false);
9230 return;
9231 }
9232
9233 elemStyle.position = 'absolute';
9234 elemStyle.height = 0;
9235 elemStyle.width = 0;
9236
9237 try {
9238 if (Modernizr.video.ogg) {
9239 elem.src = 'data:video/ogg;base64,T2dnUwACAAAAAAAAAABmnCATAAAAAHDEixYBKoB0aGVvcmEDAgEAAQABAAAQAAAQAAAAAAAFAAAAAQAAAAAAAAAAAGIAYE9nZ1MAAAAAAAAAAAAAZpwgEwEAAAACrA7TDlj///////////////+QgXRoZW9yYSsAAABYaXBoLk9yZyBsaWJ0aGVvcmEgMS4xIDIwMDkwODIyIChUaHVzbmVsZGEpAQAAABoAAABFTkNPREVSPWZmbXBlZzJ0aGVvcmEtMC4yOYJ0aGVvcmG+zSj3uc1rGLWpSUoQc5zmMYxSlKQhCDGMYhCEIQhAAAAAAAAAAAAAEW2uU2eSyPxWEvx4OVts5ir1aKtUKBMpJFoQ/nk5m41mUwl4slUpk4kkghkIfDwdjgajQYC8VioUCQRiIQh8PBwMhgLBQIg4FRba5TZ5LI/FYS/Hg5W2zmKvVoq1QoEykkWhD+eTmbjWZTCXiyVSmTiSSCGQh8PB2OBqNBgLxWKhQJBGIhCHw8HAyGAsFAiDgUCw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDAwPEhQUFQ0NDhESFRUUDg4PEhQVFRUOEBETFBUVFRARFBUVFRUVEhMUFRUVFRUUFRUVFRUVFRUVFRUVFRUVEAwLEBQZGxwNDQ4SFRwcGw4NEBQZHBwcDhATFhsdHRwRExkcHB4eHRQYGxwdHh4dGxwdHR4eHh4dHR0dHh4eHRALChAYKDM9DAwOExo6PDcODRAYKDlFOA4RFh0zV1A+EhYlOkRtZ00YIzdAUWhxXDFATldneXhlSFxfYnBkZ2MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEhIVGRoaGhoSFBYaGhoaGhUWGRoaGhoaGRoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhESFh8kJCQkEhQYIiQkJCQWGCEkJCQkJB8iJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQREhgvY2NjYxIVGkJjY2NjGBo4Y2NjY2MvQmNjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRISEhUXGBkbEhIVFxgZGxwSFRcYGRscHRUXGBkbHB0dFxgZGxwdHR0YGRscHR0dHhkbHB0dHR4eGxwdHR0eHh4REREUFxocIBERFBcaHCAiERQXGhwgIiUUFxocICIlJRcaHCAiJSUlGhwgIiUlJSkcICIlJSUpKiAiJSUlKSoqEBAQFBgcICgQEBQYHCAoMBAUGBwgKDBAFBgcICgwQEAYHCAoMEBAQBwgKDBAQEBgICgwQEBAYIAoMEBAQGCAgAfF5cdH1e3Ow/L66wGmYnfIUbwdUTe3LMRbqON8B+5RJEvcGxkvrVUjTMrsXYhAnIwe0dTJfOYbWrDYyqUrz7dw/JO4hpmV2LsQQvkUeGq1BsZLx+cu5iV0e0eScJ91VIQYrmqfdVSK7GgjOU0oPaPOu5IcDK1mNvnD+K8LwS87f8Jx2mHtHnUkTGAurWZlNQa74ZLSFH9oF6FPGxzLsjQO5Qe0edcpttd7BXBSqMCL4k/4tFrHIPuEQ7m1/uIWkbDMWVoDdOSuRQ9286kvVUlQjzOE6VrNguN4oRXYGkgcnih7t13/9kxvLYKQezwLTrO44sVmMPgMqORo1E0sm1/9SludkcWHwfJwTSybR4LeAz6ugWVgRaY8mV/9SluQmtHrzsBtRF/wPY+X0JuYTs+ltgrXAmlk10xQHmTu9VSIAk1+vcvU4ml2oNzrNhEtQ3CysNP8UeR35wqpKUBdGdZMSjX4WVi8nJpdpHnbhzEIdx7mwf6W1FKAiucMXrWUWVjyRf23chNtR9mIzDoT/6ZLYailAjhFlZuvPtSeZ+2oREubDoWmT3TguY+JHPdRVSLKxfKH3vgNqJ/9emeEYikGXDFNzaLjvTeGAL61mogOoeG3y6oU4rW55ydoj0lUTSR/mmRhPmF86uwIfzp3FtiufQCmppaHDlGE0r2iTzXIw3zBq5hvaTldjG4CPb9wdxAme0SyedVKczJ9AtYbgPOzYKJvZZImsN7ecrxWZg5dR6ZLj/j4qpWsIA+vYwE+Tca9ounMIsrXMB4Stiib2SPQtZv+FVIpfEbzv8ncZoLBXc3YBqTG1HsskTTotZOYTG+oVUjLk6zhP8bg4RhMUNtfZdO7FdpBuXzhJ5Fh8IKlJG7wtD9ik8rWOJxy6iQ3NwzBpQ219mlyv+FLicYs2iJGSE0u2txzed++D61ZWCiHD/cZdQVCqkO2gJpdpNaObhnDfAPrT89RxdWFZ5hO3MseBSIlANppdZNIV/Rwe5eLTDvkfWKzFnH+QJ7m9QWV1KdwnuIwTNtZdJMoXBf74OhRnh2t+OTGL+AVUnIkyYY+QG7g9itHXyF3OIygG2s2kud679ZWKqSFa9n3IHD6MeLv1lZ0XyduRhiDRtrNnKoyiFVLcBm0ba5Yy3fQkDh4XsFE34isVpOzpa9nR8iCpS4HoxG2rJpnRhf3YboVa1PcRouh5LIJv/uQcPNd095ickTaiGBnWLKVWRc0OnYTSyex/n2FofEPnDG8y3PztHrzOLK1xo6RAml2k9owKajOC0Wr4D5x+3nA0UEhK2m198wuBHF3zlWWVKWLN1CHzLClUfuoYBcx4b1llpeBKmbayaR58njtE9onD66lUcsg0Spm2snsb+8HaJRn4dYcLbCuBuYwziB8/5U1C1DOOz2gZjSZtrLJk6vrLF3hwY4Io9xuT/ruUFRSBkNtUzTOWhjh26irLEPx4jPZL3Fo3QrReoGTTM21xYTT9oFdhTUIvjqTkfkvt0bzgVUjq/hOYY8j60IaO/0AzRBtqkTS6R5ellZd5uKdzzhb8BFlDdAcrwkE0rbXTOPB+7Y0FlZO96qFL4Ykg21StJs8qIW7h16H5hGiv8V2Cflau7QVDepTAHa6Lgt6feiEvJDM21StJsmOH/hynURrKxvUpQ8BH0JF7BiyG2qZpnL/7AOU66gt+reLEXY8pVOCQvSsBtqZTNM8bk9ohRcwD18o/WVkbvrceVKRb9I59IEKysjBeTMmmbA21xu/6iHadLRxuIzkLpi8wZYmmbbWi32RVAUjruxWlJ//iFxE38FI9hNKOoCdhwf5fDe4xZ81lgREhK2m1j78vW1CqkuMu/AjBNK210kzRUX/B+69cMMUG5bYrIeZxVSEZISmkzbXOi9yxwIfPgdsov7R71xuJ7rFcACjG/9PzApqFq7wEgzNJm2suWESPuwrQvejj7cbnQxMkxpm21lUYJL0fKmogPPqywn7e3FvB/FCNxPJ85iVUkCE9/tLKx31G4CgNtWTTPFhMvlu8G4/TrgaZttTChljfNJGgOT2X6EqpETy2tYd9cCBI4lIXJ1/3uVUllZEJz4baqGF64yxaZ+zPLYwde8Uqn1oKANtUrSaTOPHkhvuQP3bBlEJ/LFe4pqQOHUI8T8q7AXx3fLVBgSCVpMba55YxN3rv8U1Dv51bAPSOLlZWebkL8vSMGI21lJmmeVxPRwFlZF1CpqCN8uLwymaZyjbXHCRytogPN3o/n74CNykfT+qqRv5AQlHcRxYrC5KvGmbbUwmZY/29BvF6C1/93x4WVglXDLFpmbapmF89HKTogRwqqSlGbu+oiAkcWFbklC6Zhf+NtTLFpn8oWz+HsNRVSgIxZWON+yVyJlE5tq/+GWLTMutYX9ekTySEQPLVNQQ3OfycwJBM0zNtZcse7CvcKI0V/zh16Dr9OSA21MpmmcrHC+6pTAPHPwoit3LHHqs7jhFNRD6W8+EBGoSEoaZttTCZljfduH/fFisn+dRBGAZYtMzbVMwvul/T/crK1NQh8gN0SRRa9cOux6clC0/mDLFpmbarmF8/e6CopeOLCNW6S/IUUg3jJIYiAcDoMcGeRbOvuTPjXR/tyo79LK3kqqkbxkkMRAOB0GODPItnX3Jnxro/25Ud+llbyVVSN4ySGIgHA6DHBnkWzr7kz410f7cqO/Syt5KqpFVJwn6gBEvBM0zNtZcpGOEPiysW8vvRd2R0f7gtjhqUvXL+gWVwHm4XJDBiMpmmZtrLfPwd/IugP5+fKVSysH1EXreFAcEhelGmbbUmZY4Xdo1vQWVnK19P4RuEnbf0gQnR+lDCZlivNM22t1ESmopPIgfT0duOfQrsjgG4tPxli0zJmF5trdL1JDUIUT1ZXSqQDeR4B8mX3TrRro/2McGeUvLtwo6jIEKMkCUXWsLyZROd9P/rFYNtXPBli0z398iVUlVKAjFlY437JXImUTm2r/4ZYtMy61hf16RPJIU9nZ1MABAwAAAAAAAAAZpwgEwIAAABhp658BScAAAAAAADnUFBQXIDGXLhwtttNHDhw5OcpQRMETBEwRPduylKVB0HRdF0A';
9240 }
9241 else if (Modernizr.video.h264) {
9242 elem.src = 'data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAAs1tZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0OCByMjYwMSBhMGNkN2QzIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTEgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEwIHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAD2WIhAA3//728P4FNjuZQQAAAu5tb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAAZAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACGHRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAgAAAAIAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAGQAAAAAAAEAAAAAAZBtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAACgAAAAEAFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAE7bWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAA+3N0YmwAAACXc3RzZAAAAAAAAAABAAAAh2F2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAgACAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAxYXZjQwFkAAr/4QAYZ2QACqzZX4iIhAAAAwAEAAADAFA8SJZYAQAGaOvjyyLAAAAAGHN0dHMAAAAAAAAAAQAAAAEAAAQAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAABRzdHN6AAAAAAAAAsUAAAABAAAAFHN0Y28AAAAAAAAAAQAAADAAAABidWR0YQAAAFptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAAC1pbHN0AAAAJal0b28AAAAdZGF0YQAAAAEAAAAATGF2ZjU2LjQwLjEwMQ==';
9243 }
9244 else {
9245 addTest('videoautoplay', false);
9246 return;
9247 }
9248 }
9249
9250 catch (e) {
9251 addTest('videoautoplay', false);
9252 return;
9253 }
9254
9255 elem.setAttribute('autoplay', '');
9256 elemStyle.cssText = 'display:none';
9257 docElement.appendChild(elem);
9258 // wait for the next tick to add the listener, otherwise the element may
9259 // not have time to play in high load situations (e.g. the test suite)
9260 setTimeout(function() {
9261 elem.addEventListener('playing', testAutoplay, false);
9262 timeout = setTimeout(testAutoplay, waitTime);
9263 }, 0);
9264 });
9265
9266/*!
9267{
9268 "name": "Video crossOrigin",
9269 "property": "videocrossorigin",
9270 "caniuse": "cors",
9271 "authors": ["Florian Mailliet"],
9272 "notes": [{
9273 "name": "MDN documentation",
9274 "href": "https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes"
9275 }]
9276}
9277!*/
9278/* DOC
9279Detects support for the crossOrigin attribute on video tag
9280*/
9281
9282 Modernizr.addTest('videocrossorigin', 'crossOrigin' in createElement('video'));
9283
9284/*!
9285{
9286 "name": "Video Loop Attribute",
9287 "property": "videoloop",
9288 "tags": ["video", "media"]
9289}
9290!*/
9291
9292 Modernizr.addTest('videoloop', 'loop' in createElement('video'));
9293
9294/*!
9295{
9296 "name": "Video Preload Attribute",
9297 "property": "videopreload",
9298 "tags": ["video", "media"]
9299}
9300!*/
9301
9302 Modernizr.addTest('videopreload', 'preload' in createElement('video'));
9303
9304/*!
9305{
9306 "name": "VML",
9307 "property": "vml",
9308 "caniuse": "vml",
9309 "tags": ["vml"],
9310 "authors": ["Craig Andrews (@candrews)"],
9311 "notes": [{
9312 "name" : "W3C VML reference",
9313 "href": "https://www.w3.org/TR/NOTE-VML"
9314 },{
9315 "name" : "Microsoft VML reference",
9316 "href": "https://msdn.microsoft.com/en-us/library/bb263898.aspx"
9317 }]
9318}
9319!*/
9320/* DOC
9321Detects support for VML.
9322*/
9323
9324 Modernizr.addTest('vml', function() {
9325 var containerDiv = createElement('div');
9326 var supports = false;
9327 var shape;
9328
9329 if (!isSVG) {
9330 containerDiv.innerHTML = '<v:shape id="vml_flag1" adj="1" />';
9331 shape = containerDiv.firstChild;
9332 if ('style' in shape) {
9333 shape.style.behavior = 'url(#default#VML)';
9334 }
9335 supports = shape ? typeof shape.adj == 'object' : true;
9336 }
9337
9338 return supports;
9339 });
9340
9341/*!
9342{
9343 "name": "Web Intents",
9344 "property": "webintents",
9345 "authors": ["Eric Bidelman"],
9346 "notes": [{
9347 "name": "Web Intents project site",
9348 "href": "http://webintents.org/"
9349 }],
9350 "polyfills": ["webintents"],
9351 "builderAliases": ["web_intents"]
9352}
9353!*/
9354/* DOC
9355Detects native support for the Web Intents APIs for service discovery and inter-application communication.
9356
9357Chrome added support for this in v19, but [removed it again in v24](https://lists.w3.org/Archives/Public/public-web-intents/2012Nov/0000.html) because of "a number of areas for
9358development in both the API and specific user experience in Chrome". No other browsers currently support it, however a [JavaScript shim](http://webintents.org/#javascriptshim) is available.
9359*/
9360
9361 Modernizr.addTest('webintents', !!prefixed('startActivity', navigator));
9362
9363/*!
9364{
9365 "name": "Web Animation API",
9366 "property": "animation",
9367 "tags": ["webanimations"],
9368 "polyfills": ["webanimationsjs"],
9369 "notes": [{
9370 "name": "Introducing Web Animations",
9371 "href": "http://brian.sol1.net/svg/2013/06/26/introducing-web-animations/"
9372 }]
9373}
9374!*/
9375/* DOC
9376Detects support for the Web Animation API, a way to create css animations in js
9377*/
9378
9379 Modernizr.addTest('webanimations', 'animate' in createElement('div'));
9380
9381/*!
9382{
9383 "name": "WebGL",
9384 "property": "webgl",
9385 "caniuse": "webgl",
9386 "tags": ["webgl", "graphics"],
9387 "polyfills": ["jebgl", "cwebgl", "iewebgl"]
9388}
9389!*/
9390
9391 Modernizr.addTest('webgl', function() {
9392 var canvas = createElement('canvas');
9393 var supports = 'probablySupportsContext' in canvas ? 'probablySupportsContext' : 'supportsContext';
9394 if (supports in canvas) {
9395 return canvas[supports]('webgl') || canvas[supports]('experimental-webgl');
9396 }
9397 return 'WebGLRenderingContext' in window;
9398 });
9399
9400/*!
9401{
9402 "name": "WebGL Extensions",
9403 "property": "webglextensions",
9404 "tags": ["webgl", "graphics"],
9405 "builderAliases": ["webgl_extensions"],
9406 "async" : true,
9407 "authors": ["Ilmari Heikkinen"],
9408 "knownBugs": [],
9409 "notes": [{
9410 "name": "Kronos extensions registry",
9411 "href": "http://www.khronos.org/registry/webgl/extensions/"
9412 }]
9413}
9414!*/
9415/* DOC
9416Detects support for OpenGL extensions in WebGL. It's `true` if the [WebGL extensions API](https://developer.mozilla.org/en-US/docs/Web/WebGL/Using_Extensions) is supported, then exposes the supported extensions as subproperties, e.g.:
9417
9418```javascript
9419if (Modernizr.webglextensions) {
9420 // WebGL extensions API supported
9421}
9422if ('OES_vertex_array_object' in Modernizr.webglextensions) {
9423 // Vertex Array Objects extension supported
9424}
9425```
9426*/
9427
9428 // based on code from ilmari heikkinen
9429 // code.google.com/p/graphics-detect/source/browse/js/detect.js
9430
9431 // Not Async but handles it's own self
9432 Modernizr.addAsyncTest(function() {
9433
9434 // Not a good candidate for css classes, so we avoid addTest stuff
9435 Modernizr.webglextensions = false;
9436
9437 if (!Modernizr.webgl) {
9438 return;
9439 }
9440
9441 var canvas;
9442 var ctx;
9443 var exts;
9444
9445 try {
9446 canvas = createElement('canvas');
9447 ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
9448 exts = ctx.getSupportedExtensions();
9449 }
9450 catch (e) {
9451 return;
9452 }
9453
9454 if (ctx !== undefined) {
9455 Modernizr.webglextensions = new Boolean(true);
9456 }
9457
9458 for (var i = -1, len = exts.length; ++i < len;) {
9459 Modernizr.webglextensions[exts[i]] = true;
9460 }
9461
9462 canvas = undefined;
9463 });
9464
9465/*!
9466{
9467 "name": "RTC Peer Connection",
9468 "property": "peerconnection",
9469 "tags": ["webrtc"],
9470 "authors": ["Ankur Oberoi"],
9471 "notes": [{
9472 "name": "W3C Web RTC spec",
9473 "href": "https://www.w3.org/TR/webrtc/"
9474 }]
9475}
9476!*/
9477
9478 Modernizr.addTest('peerconnection', !!prefixed('RTCPeerConnection', window));
9479
9480/*!
9481{
9482 "name": "RTC Data Channel",
9483 "property": "datachannel",
9484 "notes": [{
9485 "name": "HTML5 Rocks! Article",
9486 "href": "http://www.html5rocks.com/en/tutorials/webrtc/datachannels/"
9487 }]
9488}
9489!*/
9490/* DOC
9491Detect for the RTCDataChannel API that allows for transfer data directly from one peer to another
9492*/
9493
9494
9495 Modernizr.addTest('datachannel', function() {
9496 if (!Modernizr.peerconnection) {
9497 return false;
9498 }
9499 for (var i = 0, l = domPrefixes.length; i < l; i++) {
9500 var PeerConnectionConstructor = window[domPrefixes[i] + 'RTCPeerConnection'];
9501
9502 if (PeerConnectionConstructor) {
9503 var peerConnection = new PeerConnectionConstructor(null);
9504
9505 return 'createDataChannel' in peerConnection;
9506 }
9507
9508 }
9509 return false;
9510 });
9511
9512/*!
9513{
9514 "name": "getUserMedia",
9515 "property": "getusermedia",
9516 "caniuse": "stream",
9517 "tags": ["webrtc"],
9518 "authors": ["Eric Bidelman"],
9519 "notes": [{
9520 "name": "W3C Media Capture and Streams spec",
9521 "href": "https://www.w3.org/TR/mediacapture-streams/"
9522 }],
9523 "polyfills": ["getusermedia"]
9524}
9525!*/
9526
9527 Modernizr.addTest('getusermedia', !!prefixed('getUserMedia', navigator));
9528
9529/*!
9530{
9531 "name": "WebSockets Support",
9532 "property": "websockets",
9533 "authors": ["Phread [fearphage]", "Mike Sherov [mikesherov]", "Burak Yigit Kaya [BYK]"],
9534 "caniuse": "websockets",
9535 "tags": ["html5"],
9536 "warnings": [
9537 "This test will reject any old version of WebSockets even if it is not prefixed such as in Safari 5.1"
9538 ],
9539 "notes": [{
9540 "name": "CLOSING State and Spec",
9541 "href": "https://www.w3.org/TR/websockets/#the-websocket-interface"
9542 }],
9543 "polyfills": [
9544 "sockjs",
9545 "socketio",
9546 "kaazing-websocket-gateway",
9547 "websocketjs",
9548 "atmosphere",
9549 "graceful-websocket",
9550 "portal",
9551 "datachannel"
9552 ]
9553}
9554!*/
9555
9556 var supports = false;
9557 try {
9558 supports = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
9559 } catch (e) {}
9560 Modernizr.addTest('websockets', supports);
9561
9562/*!
9563{
9564 "name": "Binary WebSockets",
9565 "property": "websocketsbinary",
9566 "tags": ["websockets"],
9567 "builderAliases": ["websockets_binary"]
9568}
9569!*/
9570
9571 // binaryType is truthy if there is support.. returns "blob" in new-ish chrome.
9572 // plus.google.com/115535723976198353696/posts/ERN6zYozENV
9573 // github.com/Modernizr/Modernizr/issues/370
9574
9575 Modernizr.addTest('websocketsbinary', function() {
9576 var protocol = 'https:' == location.protocol ? 'wss' : 'ws',
9577 protoBin;
9578
9579 if ('WebSocket' in window) {
9580 protoBin = 'binaryType' in WebSocket.prototype
9581 if (protoBin) {
9582 return protoBin;
9583 }
9584 try {
9585 return !!(new WebSocket(protocol + '://.').binaryType);
9586 } catch (e) {}
9587 }
9588
9589 return false;
9590 });
9591
9592/*!
9593{
9594 "name": "Framed window",
9595 "property": "framed",
9596 "tags": ["window"],
9597 "builderAliases": ["window_framed"]
9598}
9599!*/
9600/* DOC
9601Tests if page is iframed.
9602*/
9603
9604 // github.com/Modernizr/Modernizr/issues/242
9605
9606 Modernizr.addTest('framed', window.location != top.location);
9607
9608/*!
9609{
9610 "name": "Workers from Blob URIs",
9611 "property": "blobworkers",
9612 "tags": ["performance", "workers"],
9613 "builderAliases": ["workers_blobworkers"],
9614 "notes": [{
9615 "name": "W3C Reference",
9616 "href": "https://www.w3.org/TR/workers/"
9617 }],
9618 "knownBugs": ["This test may output garbage to console."],
9619 "authors": ["Jussi Kalliokoski"],
9620 "async": true
9621}
9622!*/
9623/* DOC
9624Detects support for creating Web Workers from Blob URIs.
9625*/
9626
9627 Modernizr.addAsyncTest(function() {
9628 try {
9629 // we're avoiding using Modernizr._domPrefixes as the prefix capitalization on
9630 // these guys are notoriously peculiar.
9631 var BlobBuilder = window.BlobBuilder;
9632 var URL = window.URL;
9633 if (Modernizr._config.usePrefix) {
9634 BlobBuilder = BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder || window.OBlobBuilder;
9635 URL = URL || window.MozURL || window.webkitURL || window.MSURL || window.OURL;
9636 }
9637 var data = 'Modernizr',
9638 blob,
9639 bb,
9640 worker,
9641 url,
9642 timeout,
9643 scriptText = 'this.onmessage=function(e){postMessage(e.data)}';
9644
9645 try {
9646 blob = new Blob([scriptText], {type: 'text/javascript'});
9647 } catch (e) {
9648 // we'll fall back to the deprecated BlobBuilder
9649 }
9650 if (!blob) {
9651 bb = new BlobBuilder();
9652 bb.append(scriptText);
9653 blob = bb.getBlob();
9654 }
9655
9656 url = URL.createObjectURL(blob);
9657 worker = new Worker(url);
9658
9659 worker.onmessage = function(e) {
9660 addTest('blobworkers', data === e.data);
9661 cleanup();
9662 };
9663
9664 // Just in case...
9665 worker.onerror = fail;
9666 timeout = setTimeout(fail, 200);
9667
9668 worker.postMessage(data);
9669 } catch (e) {
9670 fail();
9671 }
9672
9673 function fail() {
9674 addTest('blobworkers', false);
9675 cleanup();
9676 }
9677
9678 function cleanup() {
9679 if (url) {
9680 URL.revokeObjectURL(url);
9681 }
9682 if (worker) {
9683 worker.terminate();
9684 }
9685 if (timeout) {
9686 clearTimeout(timeout);
9687 }
9688 }
9689 });
9690
9691/*!
9692{
9693 "name": "Workers from Data URIs",
9694 "property": "dataworkers",
9695 "tags": ["performance", "workers"],
9696 "builderAliases": ["workers_dataworkers"],
9697 "notes": [{
9698 "name": "W3C Reference",
9699 "href": "https://www.w3.org/TR/workers/"
9700 }],
9701 "knownBugs": ["This test may output garbage to console."],
9702 "authors": ["Jussi Kalliokoski"],
9703 "async": true
9704}
9705!*/
9706/* DOC
9707Detects support for creating Web Workers from Data URIs.
9708*/
9709
9710 Modernizr.addAsyncTest(function() {
9711 try {
9712 var data = 'Modernizr',
9713 worker = new Worker('data:text/javascript;base64,dGhpcy5vbm1lc3NhZ2U9ZnVuY3Rpb24oZSl7cG9zdE1lc3NhZ2UoZS5kYXRhKX0=');
9714
9715 worker.onmessage = function(e) {
9716 worker.terminate();
9717 addTest('dataworkers', data === e.data);
9718 worker = null;
9719 };
9720
9721 // Just in case...
9722 worker.onerror = function() {
9723 addTest('dataworkers', false);
9724 worker = null;
9725 };
9726
9727 setTimeout(function() {
9728 addTest('dataworkers', false);
9729 }, 200);
9730
9731 worker.postMessage(data);
9732 } catch (e) {
9733 setTimeout(function() {
9734 addTest('dataworkers', false);
9735 }, 0);
9736 }
9737 });
9738
9739/*!
9740{
9741 "name": "Shared Workers",
9742 "property": "sharedworkers",
9743 "caniuse" : "sharedworkers",
9744 "tags": ["performance", "workers"],
9745 "builderAliases": ["workers_sharedworkers"],
9746 "notes": [{
9747 "name": "W3C Reference",
9748 "href": "https://www.w3.org/TR/workers/"
9749 }]
9750}
9751!*/
9752/* DOC
9753Detects support for the `SharedWorker` API from the Web Workers spec.
9754*/
9755
9756 Modernizr.addTest('sharedworkers', 'SharedWorker' in window);
9757
9758/*!
9759{
9760 "name": "Web Workers",
9761 "property": "webworkers",
9762 "caniuse" : "webworkers",
9763 "tags": ["performance", "workers"],
9764 "notes": [{
9765 "name": "W3C Reference",
9766 "href": "https://www.w3.org/TR/workers/"
9767 }, {
9768 "name": "HTML5 Rocks article",
9769 "href": "http://www.html5rocks.com/en/tutorials/workers/basics/"
9770 }, {
9771 "name": "MDN documentation",
9772 "href": "https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers"
9773 }],
9774 "polyfills": ["fakeworker", "html5shims"]
9775}
9776!*/
9777/* DOC
9778Detects support for the basic `Worker` API from the Web Workers spec. Web Workers provide a simple means for web content to run scripts in background threads.
9779*/
9780
9781 Modernizr.addTest('webworkers', 'Worker' in window);
9782
9783/*!
9784{
9785 "name": "Transferables Objects",
9786 "property": "transferables",
9787 "tags": ["performance", "workers"],
9788 "builderAliases": ["transferables"],
9789 "notes": [{
9790 "name": "HTML5 Rocks article",
9791 "href": "http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast"
9792 }],
9793 "async": true
9794}
9795!*/
9796/* DOC
9797Detects whether web workers can use `transferables` objects.
9798*/
9799
9800 Modernizr.addAsyncTest(function() {
9801 var prerequisites = !!(Modernizr.blobconstructor &&
9802 Modernizr.bloburls &&
9803 Modernizr.webworkers &&
9804 Modernizr.typedarrays);
9805
9806 // Early exit
9807 if (!prerequisites) {
9808 return addTest('transferables', false);
9809 }
9810
9811 // Proper test if prerequisites are met
9812 try {
9813 var buffer,
9814 scriptText = 'var hello = "world"',
9815 blob = new Blob([scriptText], {type: 'text/javascript'}),
9816 url = URL.createObjectURL(blob),
9817 worker = new Worker(url),
9818 timeout;
9819
9820 // Just in case...
9821 worker.onerror = fail;
9822 timeout = setTimeout(fail, 200);
9823
9824 // Building an minimal array buffer to send to the worker
9825 buffer = new ArrayBuffer(1);
9826
9827 // Sending the buffer to the worker
9828 worker.postMessage(buffer, [buffer]);
9829
9830 // If length of buffer is now 0, transferables are working
9831 addTest('transferables', buffer.byteLength === 0);
9832 cleanup();
9833 } catch (e) {
9834 fail();
9835 }
9836
9837 function fail() {
9838 addTest('transferables', false);
9839 cleanup();
9840 }
9841
9842 function cleanup() {
9843 if (url) {
9844 URL.revokeObjectURL(url);
9845 }
9846 if (worker) {
9847 worker.terminate();
9848 }
9849 if (timeout) {
9850 clearTimeout(timeout);
9851 }
9852 }
9853 });
9854
9855/*!
9856{
9857 "name": "XDomainRequest",
9858 "property": "xdomainrequest",
9859 "tags": ["cors", "xdomainrequest", "ie9", "ie8"],
9860 "authors": ["Ivan Pan (@hypotenuse)"],
9861 "notes": [
9862 {
9863 "name": "MDN documentation",
9864 "href": "https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest"
9865 },
9866 {
9867 "name": "MSDN documentation",
9868 "href": "https://msdn.microsoft.com/library/ie/cc288060.aspx/"
9869 }]
9870}
9871!*/
9872/* DOC
9873Detects support for XDomainRequest in IE9 & IE8
9874*/
9875
9876 Modernizr.addTest('xdomainrequest', 'XDomainRequest' in window);
9877
9878
9879 // Run each test
9880 testRunner();
9881
9882 // Remove the "no-js" class if it exists
9883 setClasses(classes);
9884
9885 delete ModernizrProto.addTest;
9886 delete ModernizrProto.addAsyncTest;
9887
9888 // Run the things that are supposed to run after the tests
9889 for (var i = 0; i < Modernizr._q.length; i++) {
9890 Modernizr._q[i]();
9891 }
9892
9893 // Leak Modernizr namespace
9894 window.Modernizr = Modernizr;
9895
9896
9897;
9898
9899})(window, document);