From 770578058b51c925a73ca27d00428e78be014d3f Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 17 Jul 2024 14:54:01 +0200 Subject: [PATCH] Add the Intl API to the modermizr checks Also change to to not minify, because it will be minified by webpack anyway so we may as wel have a halfway sensible diff next time. --- .modernizr.json | 3 +- src/vector/modernizr.js | 1992 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 1991 insertions(+), 4 deletions(-) diff --git a/.modernizr.json b/.modernizr.json index c5e97f90c5..fd040cc8a2 100644 --- a/.modernizr.json +++ b/.modernizr.json @@ -1,5 +1,5 @@ { - "minify": true, + "minify": false, "enableClasses": false, "feature-detects": [ "test/css/animations", @@ -29,6 +29,7 @@ "test/crypto", "test/iframe/sandbox", "test/json", + "test/intl", "test/network/fetch", "test/storage/localstorage", "test/window/resizeobserver" diff --git a/src/vector/modernizr.js b/src/vector/modernizr.js index 0790020f34..ff7059c08c 100644 --- a/src/vector/modernizr.js +++ b/src/vector/modernizr.js @@ -1,3 +1,1989 @@ -/*! modernizr 3.12.0 (Custom Build) | MIT * - * https://modernizr.com/download/?-cors-cryptography-cssanimations-cssfilters-displaytable-es5date-es5function-es5object-es5undefined-es6array-es6collections-es6string-fetch-flexbox-json-localstorage-objectfit-promises-resizeobserver-sandbox-svg-svgasimg-svgfilters-urlparser-urlsearchparams !*/ -!function(e,t,n,r){function o(e,t){return typeof e===t}function i(e,t){return!!~(""+e).indexOf(t)}function s(){return"function"!=typeof n.createElement?n.createElement(arguments[0]):O?n.createElementNS.call(n,"http://www.w3.org/2000/svg",arguments[0]):n.createElement.apply(n,arguments)}function a(){var e=n.body;return e||(e=s(O?"svg":"body"),e.fake=!0),e}function l(e,t,r,o){var i,l,f,d,u="modernizr",c=s("div"),p=a();if(parseInt(r,10))for(;r--;)f=s("div"),f.id=o?o[r]:u+(r+1),c.appendChild(f);return i=s("style"),i.type="text/css",i.id="s"+u,(p.fake?p:c).appendChild(i),p.appendChild(c),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(n.createTextNode(e)),c.id=u,p.fake&&(p.style.background="",p.style.overflow="hidden",d=x.style.overflow,x.style.overflow="hidden",x.appendChild(p)),l=t(c,e),p.fake&&p.parentNode?(p.parentNode.removeChild(p),x.style.overflow=d,x.offsetHeight):c.parentNode.removeChild(c),!!l}function f(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function d(e,n,r){var o;if("getComputedStyle"in t){o=getComputedStyle.call(t,e,n);var i=t.console;if(null!==o)r&&(o=o.getPropertyValue(r));else if(i){var s=i.error?"error":"log";i[s].call(i,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}}else o=!n&&e.currentStyle&&e.currentStyle[r];return o}function u(e,n){var o=e.length;if("CSS"in t&&"supports"in t.CSS){for(;o--;)if(t.CSS.supports(f(e[o]),n))return!0;return!1}if("CSSSupportsRule"in t){for(var i=[];o--;)i.push("("+f(e[o])+":"+n+")");return i=i.join(" or "),l("@supports ("+i+") { #modernizr { position: absolute; } }",function(e){return"absolute"===d(e,null,"position")})}return r}function c(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function p(e,t,n,a){function l(){d&&(delete P.style,delete P.modElem)}if(a=!o(a,"undefined")&&a,!o(n,"undefined")){var f=u(e,n);if(!o(f,"undefined"))return f}for(var d,p,y,m,v,g=["modernizr","tspan","samp"];!P.style&&g.length;)d=!0,P.modElem=s(g.shift()),P.style=P.modElem.style;for(y=e.length,p=0;p0&&(t+=" "+n+e.join(" "+n)),O?x.className.baseVal=t:x.className=t)}function S(e,t){if("object"==typeof e)for(var n in e)k(e,n)&&S(n,e[n]);else{e=e.toLowerCase();var r=e.split("."),o=Modernizr[r[0]];if(2===r.length&&(o=o[r[1]]),void 0!==o)return Modernizr;t="function"==typeof t?t():t,1===r.length?Modernizr[r[0]]=t:(!Modernizr[r[0]]||Modernizr[r[0]]instanceof Boolean||(Modernizr[r[0]]=new Boolean(Modernizr[r[0]])),Modernizr[r[0]][r[1]]=t),h([(t&&!1!==t?"":"no-")+r.join("-")]),Modernizr._trigger(e,t)}return Modernizr}var b=[],w={_version:"3.12.0",_config:{classPrefix:"",enableClasses:!1,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){b.push({name:e,fn:t,options:n})},addAsyncTest:function(e){b.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=w,Modernizr=new Modernizr;var T=[],C="Moz O ms Webkit",_=w._config.usePrefixes?C.split(" "):[];w._cssomPrefixes=_;var x=n.documentElement,O="svg"===x.nodeName.toLowerCase(),j={elem:s("modernizr")};Modernizr._q.push(function(){delete j.elem});var P={style:j.elem.style};Modernizr._q.unshift(function(){delete P.style});var E=w._config.usePrefixes?C.toLowerCase().split(" "):[];w._domPrefixes=E,w.testAllProps=v,w.testAllProps=g,Modernizr.addTest("cssanimations",g("animationName","a",!0)),(w.testStyles=l)("#modernizr{display: table; direction: ltr}#modernizr div{display: table-cell; padding: 10px}",function(e){var t,n=e.childNodes;t=n[0].offsetLeft9)}),Modernizr.addTest("flexbox",g("flexBasis","1px",!0));var R=function(e){var n,o=z.length,i=t.CSSRule;if(void 0===i)return r;if(!e)return!1;if(e=e.replace(/^@/,""),(n=e.replace(/-/g,"_").toUpperCase()+"_RULE")in i)return"@"+e;for(var s=0;s` element. This + * information allows you to progressively enhance your pages with a granular level + * of control over the experience. +*/ + +;(function(scriptGlobalObject, window, document, undefined){ + + var tests = []; + + + /** + * ModernizrProto is the constructor for Modernizr + * + * @class + * @access public + */ + var ModernizrProto = { + _version: '3.13.0', + + // Any settings that don't work as separate modules + // can go in here as configuration. + _config: { + 'classPrefix': '', + 'enableClasses': false, + 'enableJSClass': true, + 'usePrefixes': true + }, + + // Queue of tests + _q: [], + + // Stub these for people who are listening + on: function(test, cb) { + // I don't really think people should do this, but we can + // safe guard it a bit. + // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. + // This is in case people listen to synchronous tests. I would leave it out, + // but the code to *disallow* sync tests in the real version of this + // function is actually larger than this. + var self = this; + setTimeout(function() { + cb(self[test]); + }, 0); + }, + + addTest: function(name, fn, options) { + tests.push({name: name, fn: fn, options: options}); + }, + + addAsyncTest: function(fn) { + tests.push({name: null, fn: fn}); + } + }; + + + + // Fake some of Object.create so we can force non test results to be non "own" properties. + var Modernizr = function() {}; + Modernizr.prototype = ModernizrProto; + + // Leak modernizr globally when you `require` it rather than force it here. + // Overwrite name so constructor name is nicer :D + Modernizr = new Modernizr(); + + + + var classes = []; + + + /** + * is returns a boolean if the typeof an obj is exactly type. + * + * @access private + * @function is + * @param {*} obj - A thing we want to check the type of + * @param {string} type - A string to compare the typeof against + * @returns {boolean} true if the typeof the first parameter is exactly the specified type, false otherwise + */ + function is(obj, type) { + return typeof obj === type; + } + + ; + + /** + * Run through all tests and detect their support in the current UA. + * + * @access private + * @returns {void} + */ + function testRunner() { + var featureNames; + var feature; + var aliasIdx; + var result; + var nameIdx; + var featureName; + var featureNameSplit; + + for (var featureIdx in tests) { + if (tests.hasOwnProperty(featureIdx)) { + featureNames = []; + feature = tests[featureIdx]; + // run the test, throw the return value into the Modernizr, + // then based on that boolean, define an appropriate className + // and push it into an array of classes we'll join later. + // + // If there is no name, it's an 'async' test that is run, + // but not directly added to the object. That should + // be done with a post-run addTest call. + if (feature.name) { + featureNames.push(feature.name.toLowerCase()); + + if (feature.options && feature.options.aliases && feature.options.aliases.length) { + // Add all the aliases into the names list + for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { + featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); + } + } + } + + // Run the test, or use the raw value if it's not a function + result = is(feature.fn, 'function') ? feature.fn() : feature.fn; + + // Set each of the names on the Modernizr object + for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { + featureName = featureNames[nameIdx]; + // Support dot properties as sub tests. We don't do checking to make sure + // that the implied parent tests have been added. You must call them in + // order (either in the test, or make the parent test a dependency). + // + // Cap it to TWO to make the logic simple and because who needs that kind of subtesting + // hashtag famous last words + featureNameSplit = featureName.split('.'); + + if (featureNameSplit.length === 1) { + Modernizr[featureNameSplit[0]] = result; + } else { + // cast to a Boolean, if not one already or if it doesnt exist yet (like inputtypes) + if (!Modernizr[featureNameSplit[0]] || Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { + Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); + } + + Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; + } + + classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); + } + } + } + } + ; + + /** + * If the browsers follow the spec, then they would expose vendor-specific styles as: + * elem.style.WebkitBorderRadius + * instead of something like the following (which is technically incorrect): + * elem.style.webkitBorderRadius + * + * WebKit ghosts their properties in lowercase but Opera & Moz do not. + * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ + * erik.eae.net/archives/2008/03/10/21.48.10/ + * + * More here: github.com/Modernizr/Modernizr/issues/issue/21 + * + * @access private + * @returns {string} The string representing the vendor-specific style properties + */ + var omPrefixes = 'Moz O ms Webkit'; + + + var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); + ModernizrProto._cssomPrefixes = cssomPrefixes; + + + /** + * contains checks to see if a string contains another string + * + * @access private + * @function contains + * @param {string} str - The string we want to check for substrings + * @param {string} substr - The substring we want to search the first string for + * @returns {boolean} true if and only if the first string 'str' contains the second string 'substr' + */ + function contains(str, substr) { + return !!~('' + str).indexOf(substr); + } + + ; + + /** + * docElement is a convenience wrapper to grab the root element of the document + * + * @access private + * @returns {HTMLElement|SVGElement} The root element of the document + */ + var docElement = document.documentElement; + + + /** + * A convenience helper to check if the document we are running in is an SVG document + * + * @access private + * @returns {boolean} + */ + var isSVG = docElement.nodeName.toLowerCase() === 'svg'; + + + + /** + * createElement is a convenience wrapper around document.createElement. Since we + * use createElement all over the place, this allows for (slightly) smaller code + * as well as abstracting away issues with creating elements in contexts other than + * HTML documents (e.g. SVG documents). + * + * @access private + * @function createElement + * @returns {HTMLElement|SVGElement} An HTML or SVG element + */ + function createElement() { + if (typeof document.createElement !== 'function') { + // This is the case in IE7, where the type of createElement is "object". + // For this reason, we cannot call apply() as Object is not a Function. + return document.createElement(arguments[0]); + } else if (isSVG) { + return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); + } else { + return document.createElement.apply(document, arguments); + } + } + + ; + + /** + * Create our "modernizr" element that we do most feature tests on. + * + * @access private + */ + var modElem = { + elem: createElement('modernizr') + }; + + // Clean up this element + Modernizr._q.push(function() { + delete modElem.elem; + }); + + + + var mStyle = { + style: modElem.elem.style + }; + + // kill ref for gc, must happen before mod.elem is removed, so we unshift on to + // the front of the queue. + Modernizr._q.unshift(function() { + delete mStyle.style; + }); + + + + /** + * getBody returns the body of a document, or an element that can stand in for + * the body if a real body does not exist + * + * @access private + * @function getBody + * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an + * artificially created element that stands in for the body + */ + function getBody() { + // After page load injecting a fake body doesn't work so check if body exists + var body = document.body; + + if (!body) { + // Can't use the real body create a fake one. + body = createElement(isSVG ? 'svg' : 'body'); + body.fake = true; + } + + return body; + } + + ; + + /** + * injectElementWithStyles injects an element with style element and some CSS rules + * + * @access private + * @function injectElementWithStyles + * @param {string} rule - String representing a css rule + * @param {Function} callback - A function that is used to test the injected element + * @param {number} [nodes] - An integer representing the number of additional nodes you want injected + * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes + * @returns {boolean} the result of the specified callback test + */ + function injectElementWithStyles(rule, callback, nodes, testnames) { + var mod = 'modernizr'; + var style; + var ret; + var node; + var docOverflow; + var div = createElement('div'); + var body = getBody(); + + if (parseInt(nodes, 10)) { + // In order not to give false positives we create a node for each test + // This also allows the method to scale for unspecified uses + while (nodes--) { + node = createElement('div'); + node.id = testnames ? testnames[nodes] : mod + (nodes + 1); + div.appendChild(node); + } + } + + style = createElement('style'); + style.type = 'text/css'; + style.id = 's' + mod; + + // 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. + // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 + (!body.fake ? div : body).appendChild(style); + body.appendChild(div); + + if (style.styleSheet) { + style.styleSheet.cssText = rule; + } else { + style.appendChild(document.createTextNode(rule)); + } + div.id = mod; + + if (body.fake) { + //avoid crashing IE8, if background image is used + body.style.background = ''; + //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible + body.style.overflow = 'hidden'; + docOverflow = docElement.style.overflow; + docElement.style.overflow = 'hidden'; + docElement.appendChild(body); + } + + ret = callback(div, rule); + // If this is done after page load we don't want to remove the body so check if body exists + if (body.fake && body.parentNode) { + body.parentNode.removeChild(body); + docElement.style.overflow = docOverflow; + // Trigger layout so kinetic scrolling isn't disabled in iOS6+ + // eslint-disable-next-line + docElement.offsetHeight; + } else { + div.parentNode.removeChild(div); + } + + return !!ret; + } + + ; + + /** + * domToCSS takes a camelCase string and converts it to hyphen-case + * e.g. boxSizing -> box-sizing + * + * @access private + * @function domToCSS + * @param {string} name - String name of camelCase prop we want to convert + * @returns {string} The hyphen-case version of the supplied name + */ + function domToCSS(name) { + return name.replace(/([A-Z])/g, function(str, m1) { + return '-' + m1.toLowerCase(); + }).replace(/^ms-/, '-ms-'); + } + + ; + + + /** + * wrapper around getComputedStyle, to fix issues with Firefox returning null when + * called inside of a hidden iframe + * + * @access private + * @function computedStyle + * @param {HTMLElement|SVGElement} elem - The element we want to find the computed styles of + * @param {string|null} [pseudo] - An optional pseudo element selector (e.g. :before), of null if none + * @param {string} prop - A CSS property + * @returns {CSSStyleDeclaration} the value of the specified CSS property + */ + function computedStyle(elem, pseudo, prop) { + var result; + + if ('getComputedStyle' in window) { + result = getComputedStyle.call(window, elem, pseudo); + var console = window.console; + + if (result !== null) { + if (prop) { + result = result.getPropertyValue(prop); + } + } else { + if (console) { + var method = console.error ? 'error' : 'log'; + console[method].call(console, 'getComputedStyle returning null, its possible modernizr test results are inaccurate'); + } + } + } else { + result = !pseudo && elem.currentStyle && elem.currentStyle[prop]; + } + + return result; + } + + ; + + /** + * nativeTestProps allows for us to use native feature detection functionality if available. + * some prefixed form, or false, in the case of an unsupported rule + * + * @access private + * @function nativeTestProps + * @param {Array} props - An array of property names + * @param {string} value - A string representing the value we want to check via @supports + * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise + */ + // Accepts a list of property names and a single value + // Returns `undefined` if native detection not available + function nativeTestProps(props, value) { + var i = props.length; + // Start with the JS API: https://www.w3.org/TR/css3-conditional/#the-css-interface + if ('CSS' in window && 'supports' in window.CSS) { + // Try every prefixed variant of the property + while (i--) { + if (window.CSS.supports(domToCSS(props[i]), value)) { + return true; + } + } + return false; + } + // Otherwise fall back to at-rule (for Opera 12.x) + else if ('CSSSupportsRule' in window) { + // Build a condition string for every prefixed variant + var conditionText = []; + while (i--) { + conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); + } + conditionText = conditionText.join(' or '); + return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { + return computedStyle(node, null, 'position') === 'absolute'; + }); + } + return undefined; + } + ; + + /** + * cssToDOM takes a hyphen-case string and converts it to camelCase + * e.g. box-sizing -> boxSizing + * + * @access private + * @function cssToDOM + * @param {string} name - String name of hyphen-case prop we want to convert + * @returns {string} The camelCase version of the supplied name + */ + function cssToDOM(name) { + return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { + return m1 + m2.toUpperCase(); + }).replace(/^-/, ''); + } + + ; + + // testProps is a generic CSS / DOM property test. + + // In testing support for a given CSS property, it's legit to test: + // `elem.style[styleName] !== undefined` + // If the property is supported it will return an empty string, + // if unsupported it will return undefined. + + // We'll take advantage of this quick test and skip setting a style + // on our modernizr element, but instead just testing undefined vs + // empty string. + + // Property names can be provided in either camelCase or hyphen-case. + + function testProps(props, prefixed, value, skipValueTest) { + skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; + + // Try native detect first + if (!is(value, 'undefined')) { + var result = nativeTestProps(props, value); + if (!is(result, 'undefined')) { + return result; + } + } + + // Otherwise do it properly + var afterInit, i, propsLength, prop, before; + + // If we don't have a style element, that means we're running async or after + // the core tests, so we'll need to create our own elements to use. + + // Inside of an SVG element, in certain browsers, the `style` element is only + // defined for valid tags. Therefore, if `modernizr` does not have one, we + // fall back to a less used element and hope for the best. + // For strict XHTML browsers the hardly used samp element is used. + var elems = ['modernizr', 'tspan', 'samp']; + while (!mStyle.style && elems.length) { + afterInit = true; + mStyle.modElem = createElement(elems.shift()); + mStyle.style = mStyle.modElem.style; + } + + // Delete the objects if we created them. + function cleanElems() { + if (afterInit) { + delete mStyle.style; + delete mStyle.modElem; + } + } + + propsLength = props.length; + for (i = 0; i < propsLength; i++) { + prop = props[i]; + before = mStyle.style[prop]; + + if (contains(prop, '-')) { + prop = cssToDOM(prop); + } + + if (mStyle.style[prop] !== undefined) { + + // If value to test has been passed in, do a set-and-check test. + // 0 (integer) is a valid property value, so check that `value` isn't + // undefined, rather than just checking it's truthy. + if (!skipValueTest && !is(value, 'undefined')) { + + // Needs a try catch block because of old IE. This is slow, but will + // be avoided in most cases because `skipValueTest` will be used. + try { + mStyle.style[prop] = value; + } catch (e) {} + + // If the property value has changed, we assume the value used is + // supported. If `value` is empty string, it'll fail here (because + // it hasn't changed), which matches how browsers have implemented + // CSS.supports() + if (mStyle.style[prop] !== before) { + cleanElems(); + return prefixed === 'pfx' ? prop : true; + } + } + // Otherwise just return true, or the property name if this is a + // `prefixed()` call + else { + cleanElems(); + return prefixed === 'pfx' ? prop : true; + } + } + } + cleanElems(); + return false; + } + + ; + + /** + * List of JavaScript DOM values used for tests + * + * @memberOf Modernizr + * @name Modernizr._domPrefixes + * @optionName Modernizr._domPrefixes + * @optionProp domPrefixes + * @access public + * @example + * + * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather + * than hyphen-case properties, all properties are their Capitalized variant + * + * ```js + * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; + * ``` + */ + var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); + ModernizrProto._domPrefixes = domPrefixes; + + + /** + * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. + * + * @access private + * @function fnBind + * @param {Function} fn - a function you want to change `this` reference to + * @param {object} that - the `this` you want to call the function with + * @returns {Function} The wrapped version of the supplied function + */ + function fnBind(fn, that) { + return function() { + return fn.apply(that, arguments); + }; + } + + ; + + /** + * testDOMProps is a generic DOM property test; if a browser supports + * a certain property, it won't return undefined for it. + * + * @access private + * @function testDOMProps + * @param {Array} props - An array of properties to test for + * @param {object} obj - An object or Element you want to use to test the parameters again + * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check + * @returns {boolean|*} returns `false` if the prop is unsupported, otherwise the value that is supported + */ + function testDOMProps(props, obj, elem) { + var item; + + for (var i in props) { + if (props[i] in obj) { + + // return the property name as a string + if (elem === false) { + return props[i]; + } + + item = obj[props[i]]; + + // let's bind a function + if (is(item, 'function')) { + // bind to obj unless overridden + return fnBind(item, elem || obj); + } + + // return the unbound function or obj or value + return item; + } + } + return false; + } + + ; + + /** + * testPropsAll tests a list of DOM properties we want to check against. + * We specify literally ALL possible (known and/or likely) properties on + * the element including the non-vendor prefixed one, for forward- + * compatibility. + * + * @access private + * @function testPropsAll + * @param {string} prop - A string of the property to test for + * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip + * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against + * @param {string} [value] - A string of a css value + * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set + * @returns {string|boolean} returns the string version of the property, or `false` if it is unsupported + */ + function testPropsAll(prop, prefixed, elem, value, skipValueTest) { + + var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), + props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); + + // did they call .prefixed('boxSizing') or are we just testing a prop? + if (is(prefixed, 'string') || is(prefixed, 'undefined')) { + return testProps(props, prefixed, value, skipValueTest); + + // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) + } else { + props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); + return testDOMProps(props, prefixed, elem); + } + } + + // Modernizr.testAllProps() investigates whether a given style property, + // or any of its vendor-prefixed variants, is recognized + // + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testAllProps('boxSizing') + ModernizrProto.testAllProps = testPropsAll; + + + + /** + * testAllProps determines whether a given CSS property is supported in the browser + * + * @memberOf Modernizr + * @name Modernizr.testAllProps + * @optionName Modernizr.testAllProps() + * @optionProp testAllProps + * @access public + * @function testAllProps + * @param {string} prop - String naming the property to test (either camelCase or hyphen-case) + * @param {string} [value] - String of the value to test + * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection + * @returns {string|boolean} returns the string version of the property, or `false` if it is unsupported + * @example + * + * testAllProps determines whether a given CSS property, in some prefixed form, + * is supported by the browser. + * + * ```js + * testAllProps('boxSizing') // true + * ``` + * + * It can optionally be given a CSS value in string form to test if a property + * value is valid + * + * ```js + * testAllProps('display', 'block') // true + * testAllProps('display', 'penguin') // false + * ``` + * + * A boolean can be passed as a third parameter to skip the value check when + * native detection (@supports) isn't available. + * + * ```js + * testAllProps('shapeOutside', 'content-box', true); + * ``` + */ + function testAllProps(prop, value, skipValueTest) { + return testPropsAll(prop, undefined, undefined, value, skipValueTest); + } + + ModernizrProto.testAllProps = testAllProps; + + +/*! +{ + "name": "CSS Animations", + "property": "cssanimations", + "caniuse": "css-animation", + "polyfills": ["transformie", "csssandpaper"], + "tags": ["css"], + "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], + "notes": [{ + "name": "Article: 'Dispelling the Android CSS animation myths'", + "href": "https://web.archive.org/web/20180602074607/https://daneden.me/2011/12/14/putting-up-with-androids-bullshit/" + }] +} +!*/ +/* DOC +Detects whether or not elements can be animated using CSS +*/ + + Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); + + + /** + * testStyles injects an element with style element and some CSS rules + * + * @memberOf Modernizr + * @name Modernizr.testStyles + * @optionName Modernizr.testStyles() + * @optionProp testStyles + * @access public + * @function testStyles + * @param {string} rule - String representing a css rule + * @param {Function} callback - A function that is used to test the injected element + * @param {number} [nodes] - An integer representing the number of additional nodes you want injected + * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes + * @returns {boolean} + * @example + * + * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page + * along with (possibly multiple) DOM elements. This lets you check for features + * 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). + * + * ```js + * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { + * // elem is the first DOM node in the page (by default #modernizr) + * // rule is the first argument you supplied - the CSS rule in string form + * + * addTest('widthworks', elem.style.width === '9px') + * }); + * ``` + * + * If your test requires multiple nodes, you can include a third argument + * indicating how many additional div elements to include on the page. The + * additional nodes are injected as children of the `elem` that is returned as + * the first argument to the callback. + * + * ```js + * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { + * document.getElementById('modernizr').style.width === '1px'; // true + * document.getElementById('modernizr2').style.width === '2px'; // true + * elem.firstChild === document.getElementById('modernizr2'); // true + * }, 1); + * ``` + * + * By default, all of the additional elements have an ID of `modernizr[n]`, where + * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, + * the second additional is `#modernizr3`, etc.). + * If you want to have more meaningful IDs for your function, you can provide + * them as the fourth argument, as an array of strings + * + * ```js + * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { + * elem.firstChild === document.getElementById('foo'); // true + * elem.lastChild === document.getElementById('bar'); // true + * }, 2, ['foo', 'bar']); + * ``` + */ + var testStyles = ModernizrProto.testStyles = injectElementWithStyles; + +/*! +{ + "name": "CSS Display table", + "property": "displaytable", + "caniuse": "css-table", + "authors": ["scottjehl"], + "tags": ["css"], + "builderAliases": ["css_displaytable"], + "notes": [{ + "name": "Detects for all additional table display values", + "href": "https://pastebin.com/Gk9PeVaQ" + }] +} +!*/ +/* DOC +`display: table` and `table-cell` test. (both are tested under one name `table-cell` ) +*/ + + // If a document is in rtl mode this test will fail so we force ltr mode on the injected + // element https://github.com/Modernizr/Modernizr/issues/716 + testStyles('#modernizr{display: table; direction: ltr}#modernizr div{display: table-cell; padding: 10px}', function(elem) { + var ret; + var child = elem.childNodes; + ret = child[0].offsetLeft < child[1].offsetLeft; + Modernizr.addTest('displaytable', ret, {aliases: ['display-table']}); + }, 2); + + + /** + * List of property values to set for css tests. See ticket #21 + * https://github.com/modernizr/modernizr/issues/21 + * + * @memberOf Modernizr + * @name Modernizr._prefixes + * @optionName Modernizr._prefixes + * @optionProp prefixes + * @access public + * @example + * + * Modernizr._prefixes is the internal list of prefixes that we test against + * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply + * an array of hyphen-case vendor prefixes you can use within your code. + * + * Some common use cases include + * + * Generating all possible prefixed version of a CSS property + * ```js + * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); + * + * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' + * ``` + * + * Generating all possible prefixed version of a CSS value + * ```js + * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; + * + * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' + * ``` + */ + // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test + // values in feature detects to continue to work + var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']); + + // expose these for the plugin API. Look in the source for how to join() them against your input + ModernizrProto._prefixes = prefixes; + + +/*! +{ + "name": "CSS Supports", + "property": "supports", + "caniuse": "css-featurequeries", + "tags": ["css"], + "builderAliases": ["css_supports"], + "notes": [{ + "name": "W3C Spec (The @supports rule)", + "href": "https://dev.w3.org/csswg/css3-conditional/#at-supports" + }, { + "name": "Related Github Issue", + "href": "https://github.com/Modernizr/Modernizr/issues/648" + }, { + "name": "W3C Spec (The CSSSupportsRule interface)", + "href": "https://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface" + }] +} +!*/ + + var newSyntax = 'CSS' in window && 'supports' in window.CSS; + var oldSyntax = 'supportsCSS' in window; + Modernizr.addTest('supports', newSyntax || oldSyntax); + +/*! +{ + "name": "CSS Filters", + "property": "cssfilters", + "caniuse": "css-filters", + "polyfills": ["polyfilter"], + "tags": ["css"], + "builderAliases": ["css_filters"], + "notes": [{ + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/filter" + }] +} +!*/ + + Modernizr.addTest('cssfilters', function() { + if (Modernizr.supports) { + return testAllProps('filter', 'blur(2px)'); + } else { + var el = createElement('a'); + el.style.cssText = prefixes.join('filter:blur(2px); '); + // https://github.com/Modernizr/Modernizr/issues/615 + // documentMode is needed for false positives in oldIE, please see issue above + return !!el.style.length && ((document.documentMode === undefined || document.documentMode > 9)); + } + }); + + +/*! +{ + "name": "Flexbox", + "property": "flexbox", + "caniuse": "flexbox", + "tags": ["css"], + "notes": [{ + "name": "The _new_ flexbox", + "href": "https://www.w3.org/TR/css-flexbox-1/" + }], + "warnings": [ + "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect." + ] +} +!*/ +/* DOC +Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container. +*/ + + Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', true)); + + + /** + * atRule returns a given CSS property at-rule (eg @keyframes), possibly in + * some prefixed form, or false, in the case of an unsupported rule + * + * @memberOf Modernizr + * @name Modernizr.atRule + * @optionName Modernizr.atRule() + * @optionProp atRule + * @access public + * @function atRule + * @param {string} prop - String name of the @-rule to test for + * @returns {string|boolean} The string representing the (possibly prefixed) + * valid version of the @-rule, or `false` when it is unsupported. + * @example + * ```js + * var keyframes = Modernizr.atRule('@keyframes'); + * + * if (keyframes) { + * // keyframes are supported + * // could be `@-webkit-keyframes` or `@keyframes` + * } else { + * // keyframes === `false` + * } + * ``` + */ + var atRule = function(prop) { + var length = prefixes.length; + var cssrule = window.CSSRule; + var rule; + + if (typeof cssrule === 'undefined') { + return undefined; + } + + if (!prop) { + return false; + } + + // remove literal @ from beginning of provided property + prop = prop.replace(/^@/, ''); + + // CSSRules use underscores instead of dashes + rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE'; + + if (rule in cssrule) { + return '@' + prop; + } + + for (var i = 0; i < length; i++) { + // prefixes gives us something like -o-, and we want O_ + var prefix = prefixes[i]; + var thisRule = prefix.toUpperCase() + '_' + rule; + + if (thisRule in cssrule) { + return '@-' + prefix.toLowerCase() + '-' + prop; + } + } + + return false; + }; + + ModernizrProto.atRule = atRule; + + + + /** + * prefixed returns the prefixed or nonprefixed property name variant of your input + * + * @memberOf Modernizr + * @name Modernizr.prefixed + * @optionName Modernizr.prefixed() + * @optionProp prefixed + * @access public + * @function prefixed + * @param {string} prop - String name of the property to test for + * @param {object} [obj] - An object to test for the prefixed properties on + * @param {HTMLElement} [elem] - An element used to test specific properties against + * @returns {string|boolean} The string representing the (possibly prefixed) valid + * version of the property, or `false` when it is unsupported. + * @example + * + * Modernizr.prefixed takes a string css value in the DOM style camelCase (as + * opposed to the css style hyphen-case) form and returns the (possibly prefixed) + * version of that property that the browser actually supports. + * + * For example, in older Firefox... + * ```js + * prefixed('boxSizing') + * ``` + * returns 'MozBoxSizing' + * + * In newer Firefox, as well as any other browser that support the unprefixed + * version would simply return `boxSizing`. Any browser that does not support + * the property at all, it will return `false`. + * + * By default, prefixed is checked against a DOM element. If you want to check + * for a property on another object, just pass it as a second argument + * + * ```js + * var rAF = prefixed('requestAnimationFrame', window); + * + * raf(function() { + * renderFunction(); + * }) + * ``` + * + * Note that this will return _the actual function_ - not the name of the function. + * If you need the actual name of the property, pass in `false` as a third argument + * + * ```js + * var rAFProp = prefixed('requestAnimationFrame', window, false); + * + * rafProp === 'WebkitRequestAnimationFrame' // in older webkit + * ``` + * + * One common use case for prefixed is if you're trying to determine which transition + * end event to bind to, you might do something like... + * ```js + * var transEndEventNames = { + * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser + * 'MozTransition' : 'transitionend', * only for FF < 15 + * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ + * }; + * + * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; + * ``` + * + * If you want a similar lookup, but in hyphen-case, you can use [prefixedCSS](#modernizr-prefixedcss). + */ + var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) { + if (prop.indexOf('@') === 0) { + return atRule(prop); + } + + if (prop.indexOf('-') !== -1) { + // Convert hyphen-case to camelCase + prop = cssToDOM(prop); + } + if (!obj) { + return testPropsAll(prop, 'pfx'); + } else { + // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' + return testPropsAll(prop, obj, elem); + } + }; + + +/*! +{ + "name": "CSS Object Fit", + "caniuse": "object-fit", + "property": "objectfit", + "tags": ["css"], + "builderAliases": ["css_objectfit"], + "notes": [{ + "name": "Opera Article on Object Fit", + "href": "https://dev.opera.com/articles/css3-object-fit-object-position/" + }] +} +!*/ + + Modernizr.addTest('objectfit', !!prefixed('objectFit'), {aliases: ['object-fit']}); + +/*! +{ + "name": "ES5 Date", + "property": "es5date", + "notes": [{ + "name": "ECMAScript 5.1 Language Specification", + "href": "https://www.ecma-international.org/ecma-262/5.1/" + }], + "polyfills": ["es5shim"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "tags": ["es5"] +} +!*/ +/* DOC +Check if browser implements ECMAScript 5 Date per specification. +*/ + + Modernizr.addTest('es5date', function() { + var isoDate = '2013-04-12T06:06:37.307Z', + canParseISODate = false; + try { + canParseISODate = !!Date.parse(isoDate); + } catch (e) { + // no ISO date parsing yet + } + return !!(Date.now && + Date.prototype && + Date.prototype.toISOString && + Date.prototype.toJSON && + canParseISODate); + }); + +/*! +{ + "name": "ES5 Function", + "property": "es5function", + "notes": [{ + "name": "ECMAScript 5.1 Language Specification", + "href": "https://www.ecma-international.org/ecma-262/5.1/" + }], + "polyfills": ["es5shim"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "tags": ["es5"] +} +!*/ +/* DOC +Check if browser implements ECMAScript 5 Function per specification. +*/ + + Modernizr.addTest('es5function', function() { + return !!(Function.prototype && Function.prototype.bind); + }); + +/*! +{ + "name": "ES5 Object", + "property": "es5object", + "notes": [{ + "name": "ECMAScript 5.1 Language Specification", + "href": "https://www.ecma-international.org/ecma-262/5.1/" + }], + "polyfills": ["es5shim", "es5sham"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "tags": ["es5"] +} +!*/ +/* DOC +Check if browser implements ECMAScript 5 Object per specification. +*/ + + Modernizr.addTest('es5object', function() { + return !!(Object.keys && + Object.create && + Object.getPrototypeOf && + Object.getOwnPropertyNames && + Object.isSealed && + Object.isFrozen && + Object.isExtensible && + Object.getOwnPropertyDescriptor && + Object.defineProperty && + Object.defineProperties && + Object.seal && + Object.freeze && + Object.preventExtensions); + }); + +/*! +{ + "name": "ES5 Immutable Undefined", + "property": "es5undefined", + "notes": [{ + "name": "ECMAScript 5.1 Language Specification", + "href": "https://www.ecma-international.org/ecma-262/5.1/" + }, { + "name": "original implementation of detect code", + "href": "https://kangax.github.io/compat-table/es5/" + }], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "tags": ["es5"] +} +!*/ +/* DOC +Check if browser prevents assignment to global `undefined` per ECMAScript 5. +*/ + + Modernizr.addTest('es5undefined', function() { + var result, originalUndefined; + try { + originalUndefined = window.undefined; + window.undefined = 12345; + result = typeof window.undefined === 'undefined'; + window.undefined = originalUndefined; + } catch (e) { + return false; + } + return result; + }); + +/*! +{ + "name": "ES6 Array", + "property": "es6array", + "notes": [{ + "name": "unofficial ECMAScript 6 draft specification", + "href": "https://web.archive.org/web/20180825202128/https://tc39.github.io/ecma262/" + }], + "polyfills": ["es6shim"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."], + "tags": ["es6"] +} +!*/ +/* DOC +Check if browser implements ECMAScript 6 Array per specification. +*/ + + Modernizr.addTest('es6array', !!(Array.prototype && + Array.prototype.copyWithin && + Array.prototype.fill && + Array.prototype.find && + Array.prototype.findIndex && + Array.prototype.keys && + Array.prototype.entries && + Array.prototype.values && + Array.from && + Array.of)); + +/*! +{ + "name": "ES6 Collections", + "property": "es6collections", + "notes": [{ + "name": "unofficial ECMAScript 6 draft specification", + "href": "https://web.archive.org/web/20180825202128/https://tc39.github.io/ecma262/" + }], + "polyfills": ["es6shim", "weakmap"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."], + "tags": ["es6"] +} +!*/ +/* DOC +Check if browser implements ECMAScript 6 Map, Set, WeakMap and WeakSet +*/ + + Modernizr.addTest('es6collections', !!( + window.Map && window.Set && window.WeakMap && window.WeakSet + )); + +/*! +{ + "name": "ES6 Promises", + "property": "promises", + "caniuse": "promises", + "polyfills": ["es6promises"], + "authors": ["Krister Kari", "Jake Archibald"], + "tags": ["es6"], + "notes": [{ + "name": "The ES6 promises spec", + "href": "https://github.com/domenic/promises-unwrapping" + }, { + "name": "Chromium dashboard - ES6 Promises", + "href": "https://www.chromestatus.com/features/5681726336532480" + }, { + "name": "JavaScript Promises: an Introduction", + "href": "https://developers.google.com/web/fundamentals/primers/promises/" + }] +} +!*/ +/* DOC +Check if browser implements ECMAScript 6 Promises per specification. +*/ + + Modernizr.addTest('promises', function() { + return 'Promise' in window && + // Some of these methods are missing from + // Firefox/Chrome experimental implementations + 'resolve' in window.Promise && + 'reject' in window.Promise && + 'all' in window.Promise && + 'race' in window.Promise && + // Older version of the spec had a resolver object + // as the arg rather than a function + (function() { + var resolve; + new window.Promise(function(r) { resolve = r; }); + return typeof resolve === 'function'; + }()); + }); + +/*! +{ + "name": "ES6 String", + "property": "es6string", + "notes": [{ + "name": "unofficial ECMAScript 6 draft specification", + "href": "https://web.archive.org/web/20180825202128/https://tc39.github.io/ecma262/" + }], + "polyfills": ["es6shim"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."], + "tags": ["es6"] +} +!*/ +/* DOC +Check if browser implements ECMAScript 6 String per specification. +*/ + + Modernizr.addTest('es6string', !!(String.fromCodePoint && + String.raw && + String.prototype.codePointAt && + String.prototype.repeat && + String.prototype.startsWith && + String.prototype.endsWith && + String.prototype.includes)); + +/*! +{ + "name": "SVG", + "property": "svg", + "caniuse": "svg", + "tags": ["svg"], + "authors": ["Erik Dahlstrom"], + "polyfills": [ + "svgweb", + "raphael", + "amplesdk", + "canvg", + "svg-boilerplate", + "sie", + "dojogfx", + "fabricjs" + ] +} +!*/ +/* DOC +Detects support for SVG in `` or `` elements. +*/ + + Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect); + + + /** + * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support + * + * @author kangax + * @access private + * @function hasOwnProp + * @param {object} object - The object to check for a property + * @param {string} property - The property to check for + * @returns {boolean} + */ + + // hasOwnProperty shim by kangax needed for Safari 2.0 support + var hasOwnProp; + + (function() { + var _hasOwnProperty = ({}).hasOwnProperty; + /* istanbul ignore else */ + /* we have no way of testing IE 5.5 or safari 2, + * so just assume the else gets hit */ + if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) { + hasOwnProp = function(object, property) { + return _hasOwnProperty.call(object, property); + }; + } + else { + hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ + return ((property in object) && is(object.constructor.prototype[property], 'undefined')); + }; + } + })(); + + + + /** + * setClasses takes an array of class names and adds them to the root element + * + * @access private + * @function setClasses + * @param {string[]} classes - Array of class names + */ + // Pass in an and array of class names, e.g.: + // ['no-webp', 'borderradius', ...] + function setClasses(classes) { + var className = docElement.className; + var classPrefix = Modernizr._config.classPrefix || ''; + + if (isSVG) { + className = className.baseVal; + } + + // Change `no-js` to `js` (independently of the `enableClasses` option) + // Handle classPrefix on this too + if (Modernizr._config.enableJSClass) { + var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); + className = className.replace(reJS, '$1' + classPrefix + 'js$2'); + } + + if (Modernizr._config.enableClasses) { + // Add the new classes + if (classes.length > 0) { + className += ' ' + classPrefix + classes.join(' ' + classPrefix); + } + if (isSVG) { + docElement.className.baseVal = className; + } else { + docElement.className = className; + } + } + } + + ; + + + // _l tracks listeners for async tests, as well as tests that execute after the initial run + ModernizrProto._l = {}; + + /** + * Modernizr.on is a way to listen for the completion of async tests. Being + * asynchronous, they may not finish before your scripts run. As a result you + * will get a possibly false negative `undefined` value. + * + * @memberOf Modernizr + * @name Modernizr.on + * @access public + * @function on + * @param {string} feature - String name of the feature detect + * @param {Function} cb - Callback function returning a Boolean - true if feature is supported, false if not + * @returns {void} + * @example + * + * ```js + * Modernizr.on('flash', function( result ) { + * if (result) { + * // the browser has flash + * } else { + * // the browser does not have flash + * } + * }); + * ``` + */ + ModernizrProto.on = function(feature, cb) { + // Create the list of listeners if it doesn't exist + if (!this._l[feature]) { + this._l[feature] = []; + } + + // Push this test on to the listener list + this._l[feature].push(cb); + + // If it's already been resolved, trigger it on next tick + if (Modernizr.hasOwnProperty(feature)) { + // Next Tick + setTimeout(function() { + Modernizr._trigger(feature, Modernizr[feature]); + }, 0); + } + }; + + /** + * _trigger is the private function used to signal test completion and run any + * callbacks registered through [Modernizr.on](#modernizr-on) + * + * @memberOf Modernizr + * @name Modernizr._trigger + * @access private + * @function _trigger + * @param {string} feature - string name of the feature detect + * @param {Function|boolean} [res] - A feature detection function, or the boolean = + * result of a feature detection function + * @returns {void} + */ + ModernizrProto._trigger = function(feature, res) { + if (!this._l[feature]) { + return; + } + + var cbs = this._l[feature]; + + // Force async + setTimeout(function() { + var i, cb; + for (i = 0; i < cbs.length; i++) { + cb = cbs[i]; + cb(res); + } + }, 0); + + // Don't trigger these again + delete this._l[feature]; + }; + + /** + * addTest allows you to define your own feature detects that are not currently + * included in Modernizr (under the covers it's the exact same code Modernizr + * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). + * Just like the official detects, the result + * will be added onto the Modernizr object, as well as an appropriate className set on + * the html element when configured to do so + * + * @memberOf Modernizr + * @name Modernizr.addTest + * @optionName Modernizr.addTest() + * @optionProp addTest + * @access public + * @function addTest + * @param {string|object} feature - The string name of the feature detect, or an + * object of feature detect names and test + * @param {Function|boolean} test - Function returning true if feature is supported, + * false if not. Otherwise a boolean representing the results of a feature detection + * @returns {object} the Modernizr object to allow chaining + * @example + * + * The most common way of creating your own feature detects is by calling + * `Modernizr.addTest` with a string (preferably just lowercase, without any + * punctuation), and a function you want executed that will return a boolean result + * + * ```js + * Modernizr.addTest('itsTuesday', function() { + * var d = new Date(); + * return d.getDay() === 2; + * }); + * ``` + * + * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday, + * and to `false` every other day of the week. One thing to notice is that the names of + * feature detect functions are always lowercased when added to the Modernizr object. That + * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will. + * + * + * Since we only look at the returned value from any feature detection function, + * you do not need to actually use a function. For simple detections, just passing + * in a statement that will return a boolean value works just fine. + * + * ```js + * Modernizr.addTest('hasjquery', 'jQuery' in window); + * ``` + * + * Just like before, when the above runs `Modernizr.hasjquery` will be true if + * jQuery has been included on the page. Not using a function saves a small amount + * of overhead for the browser, as well as making your code much more readable. + * + * Finally, you also have the ability to pass in an object of feature names and + * their tests. This is handy if you want to add multiple detections in one go. + * The keys should always be a string, and the value can be either a boolean or + * function that returns a boolean. + * + * ```js + * var detects = { + * 'hasjquery': 'jQuery' in window, + * 'itstuesday': function() { + * var d = new Date(); + * return d.getDay() === 2; + * } + * } + * + * Modernizr.addTest(detects); + * ``` + * + * There is really no difference between the first methods and this one, it is + * just a convenience to let you write more readable code. + */ + function addTest(feature, test) { + + if (typeof feature === 'object') { + for (var key in feature) { + if (hasOwnProp(feature, key)) { + addTest(key, feature[ key ]); + } + } + } else { + + feature = feature.toLowerCase(); + var featureNameSplit = feature.split('.'); + var last = Modernizr[featureNameSplit[0]]; + + // Again, we don't check for parent test existence. Get that right, though. + if (featureNameSplit.length === 2) { + last = last[featureNameSplit[1]]; + } + + if (typeof last !== 'undefined') { + // we're going to quit if you're trying to overwrite an existing test + // if we were to allow it, we'd do this: + // var re = new RegExp("\\b(no-)?" + feature + "\\b"); + // docElement.className = docElement.className.replace( re, '' ); + // but, no rly, stuff 'em. + return Modernizr; + } + + test = typeof test === 'function' ? test() : test; + + // Set the value (this is the magic, right here). + if (featureNameSplit.length === 1) { + Modernizr[featureNameSplit[0]] = test; + } else { + // cast to a Boolean, if not one already + if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { + Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); + } + + Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test; + } + + // Set a single class (either `feature` or `no-feature`) + setClasses([(!!test && test !== false ? '' : 'no-') + featureNameSplit.join('-')]); + + // Trigger the event + Modernizr._trigger(feature, test); + } + + return Modernizr; // allow chaining. + } + + // After all the tests are run, add self to the Modernizr prototype + Modernizr._q.push(function() { + ModernizrProto.addTest = addTest; + }); + + + +/*! +{ + "name": "SVG as an tag source", + "property": "svgasimg", + "caniuse": "svg-img", + "tags": ["svg"], + "aliases": ["svgincss"], + "authors": ["Chris Coyier"], + "notes": [{ + "name": "HTML5 Spec", + "href": "https://www.w3.org/TR/html5/embedded-content-0.html#the-img-element" + }] +} +!*/ + + + // Original Async test by Stu Cox + // https://gist.github.com/chriscoyier/8774501 + + // Now a Sync test based on good results here + // https://codepen.io/chriscoyier/pen/bADFx + + // Note http://www.w3.org/TR/SVG11/feature#Image is *supposed* to represent + // support for the `` tag in SVG, not an SVG file linked from an `` + // tag in HTML – but it’s a heuristic which works + Modernizr.addTest('svgasimg', document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1')); + +/*! +{ + "name": "SVG filters", + "property": "svgfilters", + "caniuse": "svg-filters", + "tags": ["svg"], + "builderAliases": ["svg_filters"], + "authors": ["Erik Dahlstrom"], + "notes": [{ + "name": "W3C Spec", + "href": "https://www.w3.org/TR/SVG11/filters.html" + }] +} +!*/ + + // Should fail in Safari: https://stackoverflow.com/questions/9739955/feature-detecting-support-for-svg-filters. + Modernizr.addTest('svgfilters', function() { + var result = false; + try { + result = 'SVGFEColorMatrixElement' in window && + SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE === 2; + } + catch (e) {} + return result; + }); + +/*! +{ + "name": "URL parser", + "property": "urlparser", + "notes": [{ + "name": "WHATWG Spec", + "href": "https://url.spec.whatwg.org/" + }], + "polyfills": ["urlparser"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "tags": ["url"] +} +!*/ +/* DOC +Check if browser implements the URL constructor for parsing URLs. +*/ + + Modernizr.addTest('urlparser', function() { + var url; + try { + // have to actually try use it, because Safari defines a dud constructor + url = new URL('http://modernizr.com/'); + return url.href === 'http://modernizr.com/'; + } catch (e) { + return false; + } + }); + +/*! +{ + "property": "urlsearchparams", + "caniuse": "urlsearchparams", + "tags": ["querystring", "url"], + "authors": ["Cătălin Mariș"], + "name": "URLSearchParams API", + "notes": [{ + "name": "WHATWG Spec", + "href": "https://url.spec.whatwg.org/#interface-urlsearchparams" + }, { + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams" + }] +} +!*/ +/* DOC +Detects support for an API that provides utility methods for working with the query string of a URL. +*/ + + Modernizr.addTest('urlsearchparams', 'URLSearchParams' in window); + +/*! +{ + "name": "Cross-Origin Resource Sharing", + "property": "cors", + "caniuse": "cors", + "authors": ["Theodoor van Donge"], + "notes": [{ + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS" + }], + "polyfills": ["pmxdr", "ppx", "flxhr"] +} +!*/ +/* DOC +Detects support for Cross-Origin Resource Sharing: method of performing XMLHttpRequests across domains. +*/ + + Modernizr.addTest('cors', 'XMLHttpRequest' in window && 'withCredentials' in new XMLHttpRequest()); + +/*! +{ + "name": "Web Cryptography", + "property": "cryptography", + "caniuse": "cryptography", + "tags": ["crypto"], + "authors": ["roblarsen"], + "notes": [{ + "name": "W3C Editor's Draft Spec", + "href": "https://www.w3.org/TR/WebCryptoAPI/" + }], + "polyfills": ["polycrypt"] +} +!*/ +/* DOC +Detects support for the cryptographic functionality available under window.crypto.subtle +*/ + + var crypto = prefixed('crypto', window); + Modernizr.addTest('crypto', !!prefixed('subtle', crypto)); + +/*! +{ + "name": "iframe[sandbox] Attribute", + "property": "sandbox", + "caniuse": "iframe-sandbox", + "tags": ["iframe"], + "builderAliases": ["iframe_sandbox"], + "notes": [ + { + "name": "WHATWG Spec", + "href": "https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-sandbox" + }], + "knownBugs": ["False-positive on Firefox < 29"] +} +!*/ +/* DOC +Test for `sandbox` attribute in iframes. +*/ + + Modernizr.addTest('sandbox', 'sandbox' in createElement('iframe')); + +/*! +{ + "name": "JSON", + "property": "json", + "caniuse": "json", + "notes": [{ + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Glossary/JSON" + }], + "polyfills": ["json2"] +} +!*/ +/* DOC +Detects native support for JSON handling functions. +*/ + + // this will also succeed if you've loaded the JSON2.js polyfill ahead of time + // ... but that should be obvious. :) + + Modernizr.addTest('json', 'JSON' in window && 'parse' in JSON && 'stringify' in JSON); + +/*! +{ + "name": "Internationalization API", + "property": "intl", + "caniuse": "internationalization", + "notes": [{ + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl" + }, { + "name": "ECMAScript spec", + "href": "https://www.ecma-international.org/ecma-402/1.0/" + }] +} + !*/ +/* DOC +Detects support for the Internationalization API which allow easy formatting of number and dates and sorting string +based on a locale +*/ + + Modernizr.addTest('intl', !!prefixed('Intl', window)); + +/*! +{ + "name": "Fetch API", + "property": "fetch", + "tags": ["network"], + "caniuse": "fetch", + "notes": [{ + "name": "WHATWG Spec", + "href": "https://fetch.spec.whatwg.org/" + }], + "polyfills": ["fetch"] +} +!*/ +/* DOC +Detects support for the fetch API, a modern replacement for XMLHttpRequest. +*/ + + Modernizr.addTest('fetch', 'fetch' in window); + +/*! +{ + "name": "Local Storage", + "property": "localstorage", + "caniuse": "namevalue-storage", + "tags": ["storage"], + "polyfills": [ + "joshuabell-polyfill", + "cupcake", + "storagepolyfill", + "amplifyjs", + "yui-cacheoffline" + ] +} +!*/ + + // In FF4, if disabled, window.localStorage should === null. + + // Normally, we could not test that directly and need to do a + // `('localStorage' in window)` test first because otherwise Firefox will + // throw bugzil.la/365772 if cookies are disabled + + // Similarly, in Chrome with "Block third-party cookies and site data" enabled, + // attempting to access `window.sessionStorage` will throw an exception. crbug.com/357625 + + // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem + // will throw the exception: + // QUOTA_EXCEEDED_ERROR DOM Exception 22. + // Peculiarly, getItem and removeItem calls do not throw. + + // Because we are forced to try/catch this, we'll go aggressive. + + // Just FWIW: IE8 Compat mode supports these features completely: + // www.quirksmode.org/dom/html5.html + // But IE8 doesn't support either with local files + + Modernizr.addTest('localstorage', function() { + var mod = 'modernizr'; + try { + localStorage.setItem(mod, mod); + localStorage.removeItem(mod); + return true; + } catch (e) { + return false; + } + }); + +/*! +{ + "name": "ResizeObserver", + "property": "resizeobserver", + "caniuse": "resizeobserver", + "tags": ["ResizeObserver"], + "authors": ["Christian Andersson"], + "notes": [{ + "name": "W3C Spec", + "href": "https://www.w3.org/TR/resize-observer/" + }, { + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver" + }, { + "name": "Web.dev Article", + "href": "https://web.dev/resize-observer/" + }, { + "name": "Digital Ocean tutorial", + "href": "https://www.digitalocean.com/community/tutorials/js-resize-observer" + }] +} +!*/ + +/* DOC +Detects support for ResizeObserver. +*/ + + Modernizr.addTest('resizeobserver', 'ResizeObserver' in window); + + + // Run each test + testRunner(); + + delete ModernizrProto.addTest; + delete ModernizrProto.addAsyncTest; + + // Run the things that are supposed to run after the tests + for (var i = 0; i < Modernizr._q.length; i++) { + Modernizr._q[i](); + } + + // Leak Modernizr namespace + scriptGlobalObject.Modernizr = Modernizr; + + +; + +})(window, window, document);