diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 86f7b40..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - env: { - browser: true, - es6: true, - commonjs: true - }, - extends: 'eslint:recommended', - rules: { - quotes: ['error', 'single'] - }, - parserOptions: { - sourceType: 'module' - } -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..a278c2f --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "env": { + "browser": true, + "es6": true, + "amd": true, + "commonjs": true + }, + "extends": "eslint:recommended", + "rules": { + "indent": ["error", 2], + "quotes": ["error", "single"], + "no-extra-semi": "off" + } +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..df64aa5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +ko_fi: surunzi +open_collective: eruda \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..d47b285 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,27 @@ +name: CI + +on: + workflow_dispatch: + push: + branches: + - 'master' + paths: + - 'src/**/*' + +jobs: + ci: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [16.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + - run: npm i + - run: npm run ci diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..2712ef8 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,28 @@ +name: Publish to NPM + +on: + workflow_dispatch: + release: + types: [created] + +jobs: + publish: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '18.x' + registry-url: 'https://registry.npmjs.org' + - name: Build eruda-features + run: | + npm i + npm run build + - name: Publish package on NPM + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index f55876c..b0ec138 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules/ eruda-features.js -eruda-features.js.map \ No newline at end of file +eruda-features.js.map +package-lock.json \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..99955c6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +/src/modernizr.js \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f83a29..f161751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v2.1.0 (5 Aug 2024) + +* feat: remove html5test link + ## v2.0.0 (5 Jan 2020) * feat: theme support \ No newline at end of file diff --git a/README.md b/README.md index b94c6ac..832b448 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # eruda-features [![NPM version][npm-image]][npm-url] -[![Build status][travis-image]][travis-url] +[![Build status][ci-image]][ci-url] [![License][license-image]][npm-url] [npm-image]: https://img.shields.io/npm/v/eruda-features.svg [npm-url]: https://npmjs.org/package/eruda-features -[travis-image]: https://img.shields.io/travis/liriliri/eruda-features.svg -[travis-url]: https://travis-ci.org/liriliri/eruda-features +[ci-image]: https://img.shields.io/github/actions/workflow/status/liriliri/eruda-features/main.yml?branch=master&style=flat-square +[ci-url]: https://github.com/liriliri/eruda-features/actions/workflows/main.yml [license-image]: https://img.shields.io/npm/l/eruda-features.svg Eruda plugin for browser feature detections, thanks to [modernizr](https://github.com/Modernizr/Modernizr) project. @@ -17,7 +17,7 @@ Red means unsupported, otherwise ok. All buttons is linked directly to related m ## Demo Browse it on your phone: -[http://eruda.liriliri.io/](http://eruda.liriliri.io/) +[https://eruda.liriliri.io/?plugin=features](https://eruda.liriliri.io/?plugin=features) ## Install diff --git a/package.json b/package.json index 1ba5409..3b80a10 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,24 @@ { "name": "eruda-features", - "version": "2.0.0", - "description": "Eruda plugin for browser feature detections", + "version": "2.1.0", "main": "eruda-features.js", + "description": "Eruda plugin for browser feature detections", + "browserslist": [ + "since 2015", + "not dead" + ], + "files": [ + "eruda-features.js", + "eruda-features.js.map" + ], "scripts": { - "dev": "webpack-dev-server --host 0.0.0.0", - "build": "webpack && webpack -p", - "ci": "npm run lint && npm run build", + "dev": "webpack-dev-server --host 0.0.0.0 --mode development", + "build": "webpack --mode production", + "ci": "npm run lint && npm run build && npm run es5", "lint": "eslint src/**/*.js", - "format": "prettier src/index.js src/style.scss *.js .*.js --write", - "buildModernizr": "node script/buildModernizr" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/liriliri/eruda-features.git" + "format": "lsla prettier \"src/*.{js,scss}\" \"*.json\" --write", + "buildModernizr": "node script/buildModernizr", + "es5": "es-check es5 eruda-features.js" }, "keywords": [ "eruda", @@ -27,24 +32,24 @@ }, "homepage": "https://github.com/liriliri/eruda-features#readme", "devDependencies": { - "autoprefixer": "^7.2.2", - "babel-core": "^6.26.0", - "babel-loader": "^7.1.2", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.7", - "eruda": "^2.0.0", - "eslint": "^6.8.0", - "handlebars": "^4.0.11", - "handlebars-loader": "^1.6.0", - "modernizr": "^3.5.0", - "node-sass": "^4.7.2", - "postcss": "^6.0.14", + "@babel/core": "^7.21.3", + "@babel/plugin-transform-runtime": "^7.21.0", + "@babel/preset-env": "^7.20.2", + "autoprefixer": "^10.4.14", + "babel-loader": "^9.1.2", + "css-loader": "^3.4.2", + "eruda": "^3.2.0", + "es-check": "^7.2.1", + "eslint": "^8.57.0", + "licia": "^1.41.1", + "modernizr": "^3.13.0", + "postcss": "^8.4.21", "postcss-class-prefix": "^0.3.0", - "postcss-loader": "^2.0.9", - "prettier": "^1.19.1", - "sass-loader": "^6.0.6", - "webpack": "^3.10.0", - "webpack-dev-server": "^2.9.7" + "postcss-loader": "^7.0.2", + "sass": "^1.77.8", + "sass-loader": "^14.2.1", + "webpack": "^5.93.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.12.0" } } diff --git a/src/index.js b/src/index.js index 90b3cef..c390d57 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,12 @@ -import modernizr from './modernizr' +const map = require('licia/map') +const modernizr = require('./modernizr') let featureList = require('../script/featureList.json') let featureNames = featureList['feature-detects'], specialNames = featureList['special-names'] -module.exports = function(eruda) { +module.exports = function (eruda) { let { evalCss } = eruda.util class Features extends eruda.Tool { @@ -14,14 +15,9 @@ module.exports = function(eruda) { this.name = 'features' this._style = evalCss(require('./style.scss')) - this._tpl = require('./template.hbs') this._features = {} this._isInit = false } - init($el, container) { - super.init($el, container) - $el.html(require('./template.hbs')()) - } show() { super.show() @@ -42,11 +38,11 @@ module.exports = function(eruda) { let i = 0, featureNum = featureNames.length - featureNames.forEach(feature => { + featureNames.forEach((feature) => { if (specialNames[feature]) feature = specialNames[feature] feature = feature.replace(/\//g, '') - modernizr.on(feature, result => { + modernizr.on(feature, (result) => { this._features[feature] = result i++ if (i === featureNum) this._render() @@ -54,7 +50,17 @@ module.exports = function(eruda) { }) } _render() { - this._$el.html(this._tpl({ features: this._features })) + const features = map(this._features, (feature, key) => { + const ok = feature ? 'eruda-ok' : '' + + return `
  • + + ${key} + +
  • ` + }).join('') + const html = `` + this._$el.html(html) } } diff --git a/src/modernizr.js b/src/modernizr.js index 1061cc5..641cea0 100644 --- a/src/modernizr.js +++ b/src/modernizr.js @@ -1,5 +1,5 @@ /*! - * modernizr v3.5.0 + * modernizr v3.13.0 * Build https://modernizr.com/download?-audio-bloburls-boxshadow-boxsizing-canvas-cookies-cssanimations-csscalc-csstransforms-csstransforms3d-csstransitions-datauri-fetch-filereader-filesystem-flexbox-fullscreen-geolocation-hashchange-history-indexeddb-json-localstorage-notification-performance-placeholder-pointerevents-promises-queryselector-scriptasync-scriptdefer-serviceworker-sessionstorage-stylescoped-svg-templatestrings-touchevents-typedarrays-video-webgl-webp-webpalpha-websockets-websqldatabase-xhr2-dontmin * * Copyright (c) @@ -10,6 +10,7 @@ * Patrick Kettner * Stu Cox * Richard Herrera + * Veeck * MIT License */ @@ -22,21 +23,19 @@ * of control over the experience. */ +;(function(scriptGlobalObject, window, document, undefined){ var tests = []; /** - * * ModernizrProto is the constructor for Modernizr * * @class * @access public */ - var ModernizrProto = { - // The current version, dummy - _version: '3.5.0', + _version: '3.13.0', // Any settings that don't work as separate modules // can go in here as configuration. @@ -95,20 +94,20 @@ * @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} + * @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; @@ -143,7 +142,6 @@ // 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]; @@ -158,8 +156,8 @@ if (featureNameSplit.length === 1) { Modernizr[featureNameSplit[0]] = result; } else { - // cast to a Boolean, if not one already - if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { + // 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]]); } @@ -179,7 +177,6 @@ * @access private * @returns {HTMLElement|SVGElement} The root element of the document */ - var docElement = document.documentElement; @@ -189,8 +186,8 @@ * @access private * @returns {boolean} */ - var isSVG = docElement.nodeName.toLowerCase() === 'svg'; + /** @@ -203,7 +200,6 @@ * @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". @@ -219,52 +215,61 @@ ; /*! { - "name" : "HTML5 Audio Element", + "name": "HTML5 Audio Element", "property": "audio", - "tags" : ["html5", "audio", "media"] + "caniuse": "audio", + "tags": ["html5", "audio", "media"], + "notes": [{ + "name": "MDN Docs", + "href": "https://developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements" + }] } !*/ /* DOC -Detects the audio element +Detects support of the audio element, as well as testing what types of content it supports. + +Subproperties are provided to describe support for `ogg`, `mp3`,`opus`, `wav` and `m4a` formats, e.g.: + +```javascript +Modernizr.audio // true +Modernizr.audio.ogg // 'probably' +``` */ - // This tests evaluates support of the audio element, as well as - // testing what types of content it supports. - // - // We're using the Boolean constructor here, so that we can extend the value - // e.g. Modernizr.audio // true - // Modernizr.audio.ogg // 'probably' - // // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 // thx to NielsLeenheer and zcorpan // Note: in some older browsers, "no" was a return value instead of empty string. // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 - Modernizr.addTest('audio', function() { + (function() { var elem = createElement('audio'); - var bool = false; + Modernizr.addTest('audio', function() { + var bool = false; + try { + bool = !!elem.canPlayType; + if (bool) { + bool = new Boolean(bool); + } + } catch (e) {} + + return bool; + }); + + // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 try { - bool = !!elem.canPlayType - if (bool) { - bool = new Boolean(bool); - bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"') .replace(/^no$/, ''); - bool.mp3 = elem.canPlayType('audio/mpeg; codecs="mp3"') .replace(/^no$/, ''); - bool.opus = elem.canPlayType('audio/ogg; codecs="opus"') || - elem.canPlayType('audio/webm; codecs="opus"') .replace(/^no$/, ''); - - // Mimetypes accepted: - // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements - // bit.ly/iphoneoscodecs - bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/, ''); - bool.m4a = (elem.canPlayType('audio/x-m4a;') || - elem.canPlayType('audio/aac;')) .replace(/^no$/, ''); + if (!!elem.canPlayType) { + Modernizr.addTest('audio.ogg', elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '')); + Modernizr.addTest('audio.mp3', elem.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/, '')); + Modernizr.addTest('audio.opus', elem.canPlayType('audio/ogg; codecs="opus"') || + elem.canPlayType('audio/webm; codecs="opus"').replace(/^no$/, '')); + Modernizr.addTest('audio.wav', elem.canPlayType('audio/wav; codecs="1"').replace(/^no$/, '')); + Modernizr.addTest('audio.m4a', (elem.canPlayType('audio/x-m4a;') || + elem.canPlayType('audio/aac;')).replace(/^no$/, '')); } - } catch (e) { } - - return bool; - }); + } catch (e) {} + })(); /*! { @@ -314,7 +319,7 @@ Detects whether cookie support is enabled. try { // Create cookie document.cookie = 'cookietest=1'; - var ret = document.cookie.indexOf('cookietest=') != -1; + var ret = document.cookie.indexOf('cookietest=') !== -1; // Delete cookie document.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; return ret; @@ -330,17 +335,16 @@ Detects whether cookie support is enabled. * 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'; @@ -348,7 +352,6 @@ Detects whether cookie support is enabled. ModernizrProto._cssomPrefixes = cssomPrefixes; - /** * contains checks to see if a string contains another string * @@ -356,9 +359,8 @@ Detects whether cookie support is enabled. * @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} + * @returns {boolean} true if and only if the first string 'str' contains the second string 'substr' */ - function contains(str, substr) { return !!~('' + str).indexOf(substr); } @@ -370,7 +372,6 @@ Detects whether cookie support is enabled. * * @access private */ - var modElem = { elem: createElement('modernizr') }; @@ -403,7 +404,6 @@ Detects whether cookie support is enabled. * @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; @@ -425,12 +425,11 @@ Detects whether cookie support is enabled. * @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 {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} + * @returns {boolean} the result of the specified callback test */ - function injectElementWithStyles(rule, callback, nodes, testnames) { var mod = 'modernizr'; var style; @@ -478,7 +477,7 @@ Detects whether cookie support is enabled. 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) { + if (body.fake && body.parentNode) { body.parentNode.removeChild(body); docElement.style.overflow = docOverflow; // Trigger layout so kinetic scrolling isn't disabled in iOS6+ @@ -489,26 +488,25 @@ Detects whether cookie support is enabled. } return !!ret; - } ; /** - * domToCSS takes a camelCase string and converts it to kebab-case + * 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 kebab-case version of the supplied name + * @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-'); } + ; @@ -518,11 +516,11 @@ Detects whether cookie support is enabled. * * @access private * @function computedStyle - * @param {HTMLElement|SVGElement} - The element we want to find the computed styles of - * @param {string|null} [pseudoSelector]- An optional pseudo element selector (e.g. :before), of null if none - * @returns {CSSStyleDeclaration} + * @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; @@ -555,16 +553,15 @@ Detects whether cookie support is enabled. * * @access private * @function nativeTestProps - * @param {array} props - An array of property names + * @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: http://www.w3.org/TR/css3-conditional/#the-css-interface + // 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--) { @@ -583,7 +580,7 @@ Detects whether cookie support is enabled. } conditionText = conditionText.join(' or '); return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { - return computedStyle(node, null, 'position') == 'absolute'; + return computedStyle(node, null, 'position') === 'absolute'; }); } return undefined; @@ -591,20 +588,20 @@ Detects whether cookie support is enabled. ; /** - * cssToDOM takes a kebab-case string and converts it to camelCase + * 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 kebab-case prop we want to convert + * @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. @@ -618,7 +615,7 @@ Detects whether cookie support is enabled. // on our modernizr element, but instead just testing undefined vs // empty string. - // Property names can be provided in either camelCase or kebab-case. + // Property names can be provided in either camelCase or hyphen-case. function testProps(props, prefixed, value, skipValueTest) { skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; @@ -635,12 +632,12 @@ Detects whether cookie support is enabled. 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 + // 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 + // 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 + // For strict XHTML browsers the hardly used samp element is used. var elems = ['modernizr', 'tspan', 'samp']; while (!mStyle.style && elems.length) { afterInit = true; @@ -682,16 +679,16 @@ Detects whether cookie support is enabled. // 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) { + if (mStyle.style[prop] !== before) { cleanElems(); - return prefixed == 'pfx' ? prop : true; + 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; + return prefixed === 'pfx' ? prop : true; } } } @@ -704,7 +701,7 @@ Detects whether cookie support is enabled. /** * List of JavaScript DOM values used for tests * - * @memberof Modernizr + * @memberOf Modernizr * @name Modernizr._domPrefixes * @optionName Modernizr._domPrefixes * @optionProp domPrefixes @@ -712,13 +709,12 @@ Detects whether cookie support is enabled. * @example * * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather - * than kebab-case properties, all properties are their Capitalized variant + * 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; @@ -728,11 +724,10 @@ Detects whether cookie support is enabled. * * @access private * @function fnBind - * @param {function} fn - a function you want to change `this` reference to + * @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 + * @returns {Function} The wrapped version of the supplied function */ - function fnBind(fn, that) { return function() { return fn.apply(that, arguments); @@ -747,10 +742,10 @@ Detects whether cookie support is enabled. * * @access private * @function testDOMProps - * @param {array.} props - An array of properties to test for + * @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 {false|*} returns false if the prop is unsupported, otherwise the value that is supported + * @returns {boolean|*} returns `false` if the prop is unsupported, otherwise the value that is supported */ function testDOMProps(props, obj, elem) { var item; @@ -767,7 +762,7 @@ Detects whether cookie support is enabled. // let's bind a function if (is(item, 'function')) { - // bind to obj unless overriden + // bind to obj unless overridden return fnBind(item, elem || obj); } @@ -793,7 +788,7 @@ Detects whether cookie support is enabled. * @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 {false|string} returns the string version of the property, or false if it is unsupported + * @returns {string|boolean} returns the string version of the property, or `false` if it is unsupported */ function testPropsAll(prop, prefixed, elem, value, skipValueTest) { @@ -823,15 +818,16 @@ Detects whether cookie support is enabled. /** * testAllProps determines whether a given CSS property is supported in the browser * - * @memberof Modernizr + * @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 kebab-case) + * @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, @@ -856,11 +852,12 @@ Detects whether cookie support is enabled. * testAllProps('shapeOutside', 'content-box', true); * ``` */ - function testAllProps(prop, value, skipValueTest) { return testPropsAll(prop, undefined, undefined, value, skipValueTest); } + ModernizrProto.testAllProps = testAllProps; + /*! { @@ -871,8 +868,8 @@ Detects whether cookie support is enabled. "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://goo.gl/OGw5Gm" + "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/" }] } !*/ @@ -908,7 +905,7 @@ Detects whether or not elements can be animated using CSS "notes": [{ "name": "MDN Docs", "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing" - },{ + }, { "name": "Related Github Issue", "href": "https://github.com/Modernizr/Modernizr/issues/248" }] @@ -920,9 +917,9 @@ Detects whether or not elements can be animated using CSS /** * List of property values to set for css tests. See ticket #21 - * http://git.io/vUGl4 + * https://github.com/modernizr/modernizr/issues/21 * - * @memberof Modernizr + * @memberOf Modernizr * @name Modernizr._prefixes * @optionName Modernizr._prefixes * @optionProp prefixes @@ -931,7 +928,7 @@ Detects whether or not elements can be animated using CSS * * 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 kebab-case vendor prefixes you can use within your code. + * an array of hyphen-case vendor prefixes you can use within your code. * * Some common use cases include * @@ -949,7 +946,6 @@ Detects whether or not elements can be animated using CSS * 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(' ') : ['','']); @@ -996,7 +992,7 @@ Method of allowing calculated values for length units. For example: "tags": ["css"], "notes": [{ "name": "The _new_ flexbox", - "href": "http://dev.w3.org/csswg/css3-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." @@ -1019,72 +1015,12 @@ Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows !*/ Modernizr.addTest('csstransforms', function() { - // Android < 3.0 is buggy, so we sniff and blacklist - // http://git.io/hHzL7w + // Android < 3.0 is buggy, so we sniff and reject it + // https://github.com/Modernizr/Modernizr/issues/903 return navigator.userAgent.indexOf('Android 2.') === -1 && testAllProps('transform', 'scale(1)', 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 Supports", @@ -1093,14 +1029,14 @@ Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows "tags": ["css"], "builderAliases": ["css_supports"], "notes": [{ - "name": "W3 Spec", - "href": "http://dev.w3.org/csswg/css3-conditional/#at-supports" - },{ + "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": "W3 Info", - "href": "http://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface" + }, { + "name": "W3C Spec (The CSSSupportsRule interface)", + "href": "https://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface" }] } !*/ @@ -1116,42 +1052,13 @@ Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows "caniuse": "transforms3d", "tags": ["css"], "warnings": [ - "Chrome may occassionally fail this test on some systems; more info: https://code.google.com/p/chromium/issues/detail?id=129004" + "Chrome may occasionally fail this test on some systems; more info: https://bugs.chromium.org/p/chromium/issues/detail?id=129004" ] } !*/ Modernizr.addTest('csstransforms3d', function() { - var ret = !!testAllProps('perspective', '1px', true); - var usePrefix = Modernizr._config.usePrefixes; - - // Webkit's 3D transforms are passed off to the browser's own graphics renderer. - // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in - // some conditions. As a result, Webkit typically recognizes the syntax but - // will sometimes throw a false positive, thus we must do a more thorough check: - if (ret && (!usePrefix || 'webkitPerspective' in docElement.style)) { - var mq; - var defaultStyle = '#modernizr{width:0;height:0}'; - // Use CSS Conditional Rules if available - if (Modernizr.supports) { - mq = '@supports (perspective: 1px)'; - } else { - // Otherwise, Webkit allows this media query to succeed only if the feature is enabled. - // `@media (transform-3d),(-webkit-transform-3d){ ... }` - mq = '@media (transform-3d)'; - if (usePrefix) { - mq += ',(-webkit-transform-3d)'; - } - } - - mq += '{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}'; - - testStyles(defaultStyle + mq, function(elem) { - ret = elem.offsetWidth === 7 && elem.offsetHeight === 18; - }); - } - - return ret; + return !!testAllProps('perspective', '1px', true); }); /*! @@ -1176,12 +1083,12 @@ Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows "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: There and back again - HTML5 Rocks", - "href": "http://www.html5rocks.com/en/tutorials/es6/promises/" + }, { + "name": "JavaScript Promises: an Introduction", + "href": "https://developers.google.com/web/fundamentals/primers/promises/" }] } !*/ @@ -1212,7 +1119,7 @@ Check if browser implements ECMAScript 6 Promises per specification. "property": "filereader", "caniuse": "fileapi", "notes": [{ - "name": "W3C Working Draft", + "name": "W3C Working Draft Spec", "href": "https://www.w3.org/TR/FileAPI/" }], "tags": ["file"], @@ -1235,7 +1142,7 @@ to be the File object's prototype.) * 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 + * @memberOf Modernizr * @name Modernizr.atRule * @optionName Modernizr.atRule() * @optionProp atRule @@ -1255,9 +1162,7 @@ to be the File object's prototype.) * // keyframes === `false` * } * ``` - * */ - var atRule = function(prop) { var length = prefixes.length; var cssrule = window.CSSRule; @@ -1301,7 +1206,7 @@ to be the File object's prototype.) /** * prefixed returns the prefixed or nonprefixed property name variant of your input * - * @memberof Modernizr + * @memberOf Modernizr * @name Modernizr.prefixed * @optionName Modernizr.prefixed() * @optionProp prefixed @@ -1310,12 +1215,12 @@ to be the File object's prototype.) * @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|false} The string representing the (possibly prefixed) valid + * @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 kebab-case) form and returns the (possibly prefixed) + * 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... @@ -1360,16 +1265,15 @@ to be the File object's prototype.) * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; * ``` * - * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). + * 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 kebab-case to camelCase + if (prop.indexOf('-') !== -1) { + // Convert hyphen-case to camelCase prop = cssToDOM(prop); } if (!obj) { @@ -1387,8 +1291,8 @@ to be the File object's prototype.) "property": "filesystem", "caniuse": "filesystem", "notes": [{ - "name": "W3 Draft", - "href": "http://dev.w3.org/2009/dap/file-system/file-dir-sys.html" + "name": "W3C Spec", + "href": "https://www.w3.org/TR/file-system-api/" }], "authors": ["Eric Bidelman (@ebidel)"], "tags": ["file"], @@ -1419,7 +1323,7 @@ Tests for placeholder attribute in inputs and textareas "property": "fullscreen", "caniuse": "fullscreen", "notes": [{ - "name": "MDN documentation", + "name": "MDN Docs", "href": "https://developer.mozilla.org/en/API/Fullscreen" }], "polyfills": ["screenfulljs"], @@ -1440,7 +1344,7 @@ Detects support for the ability to make the current website take over the user's "caniuse": "geolocation", "tags": ["media"], "notes": [{ - "name": "MDN documentation", + "name": "MDN Docs", "href": "https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation" }], "polyfills": [ @@ -1471,14 +1375,14 @@ Detects support for the Geolocation API for users to provide their location to w /** * Modernizr.hasEvent() detects support for a given event * - * @memberof Modernizr + * @memberOf Modernizr * @name Modernizr.hasEvent * @optionName Modernizr.hasEvent() * @optionProp hasEvent * @access public * @function hasEvent - * @param {string|*} eventName - the name of an event to test for (e.g. "resize") - * @param {Element|string} [element=HTMLDivElement] - is the element|document|window|tagName to test on + * @param {string|*} eventName - the name of an event to test for (e.g. "resize") + * @param {Element|string} [element=HTMLDivElement] - is the element|document|window|tagName to test on * @returns {boolean} * @example * `Modernizr.hasEvent` lets you determine if the browser supports a supplied event. @@ -1494,14 +1398,12 @@ Detects support for the Geolocation API for users to provide their location to w * ```js * hasEvent('devicelight', window) // true; * ``` - * */ - var hasEvent = (function() { // Detect whether event support can be detected via `in`. Test on a DOM element // using the "blur" event b/c it should always exist. bit.ly/event-detection - var needsFallback = !('onblur' in document.documentElement); + var needsFallback = !('onblur' in docElement); function inner(eventName, element) { @@ -1540,7 +1442,6 @@ Detects support for the Geolocation API for users to provide their location to w return inner; })(); - ModernizrProto.hasEvent = hasEvent; /*! @@ -1550,8 +1451,8 @@ Detects support for the Geolocation API for users to provide their location to w "caniuse": "hashchange", "tags": ["history"], "notes": [{ - "name": "MDN documentation", - "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.onhashchange" + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onhashchange" }], "polyfills": [ "jquery-hashchange", @@ -1587,7 +1488,7 @@ Detects support for the `hashchange` event, fired when the current location frag "name": "W3C Spec", "href": "https://www.w3.org/TR/html51/browsers.html#the-history-interface" }, { - "name": "MDN documentation", + "name": "MDN Docs", "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.history" }], "polyfills": ["historyjs", "html5historyapi"] @@ -1603,6 +1504,12 @@ Detects support for the History API for manipulating the browser session history // Unfortunately support is really buggy and there is no clean way to detect // these bugs, so we fall back to a user agent sniff :( var ua = navigator.userAgent; + + // Some browsers allow to have empty userAgent. + // Therefore, we need to check ua before using "indexOf" on it. + if(!ua) { + return false; + } // We only want Android 2 and 4.0, stock browser, and not Chrome which identifies // itself as 'Mobile Safari' as well, nor Windows Phone (issue #1471). @@ -1663,7 +1570,6 @@ Detects support for the History API for manipulating the browser session history * @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) { @@ -1683,20 +1589,21 @@ Detects support for the History API for manipulating the browser session history if (Modernizr._config.enableClasses) { // Add the new classes - className += ' ' + classPrefix + classes.join(' ' + classPrefix); + 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 + // _l tracks listeners for async tests, as well as tests that execute after the initial run ModernizrProto._l = {}; /** @@ -1704,12 +1611,13 @@ Detects support for the History API for manipulating the browser session history * asynchronous, they may not finish before your scripts run. As a result you * will get a possibly false negative `undefined` value. * - * @memberof Modernizr + * @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 + * @param {Function} cb - Callback function returning a Boolean - true if feature is supported, false if not + * @returns {void} * @example * * ```js @@ -1722,7 +1630,6 @@ Detects support for the History API for manipulating the browser session history * }); * ``` */ - ModernizrProto.on = function(feature, cb) { // Create the list of listeners if it doesn't exist if (!this._l[feature]) { @@ -1745,15 +1652,15 @@ Detects support for the History API for manipulating the browser session history * _trigger is the private function used to signal test completion and run any * callbacks registered through [Modernizr.on](#modernizr-on) * - * @memberof Modernizr + * @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 = + * @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; @@ -1777,11 +1684,12 @@ Detects support for the History API for manipulating the browser session history /** * 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 offical detects, the result + * 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 + * @memberOf Modernizr * @name Modernizr.addTest * @optionName Modernizr.addTest() * @optionProp addTest @@ -1789,8 +1697,9 @@ Detects support for the History API for manipulating the browser session history * @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, + * @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 @@ -1815,7 +1724,7 @@ Detects support for the History API for manipulating the browser session history * in a statement that will return a boolean value works just fine. * * ```js - * Modernizr.addTest('hasJquery', 'jQuery' in window); + * Modernizr.addTest('hasjquery', 'jQuery' in window); * ``` * * Just like before, when the above runs `Modernizr.hasjquery` will be true if @@ -1842,10 +1751,9 @@ Detects support for the History API for manipulating the browser session history * 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') { + if (typeof feature === 'object') { for (var key in feature) { if (hasOwnProp(feature, key)) { addTest(key, feature[ key ]); @@ -1858,11 +1766,11 @@ Detects support for the History API for manipulating the browser session history var last = Modernizr[featureNameSplit[0]]; // Again, we don't check for parent test existence. Get that right, though. - if (featureNameSplit.length == 2) { + if (featureNameSplit.length === 2) { last = last[featureNameSplit[1]]; } - if (typeof last != 'undefined') { + 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"); @@ -1871,10 +1779,10 @@ Detects support for the History API for manipulating the browser session history return Modernizr; } - test = typeof test == 'function' ? test() : test; + test = typeof test === 'function' ? test() : test; // Set the value (this is the magic, right here). - if (featureNameSplit.length == 1) { + if (featureNameSplit.length === 1) { Modernizr[featureNameSplit[0]] = test; } else { // cast to a Boolean, if not one already @@ -1886,7 +1794,7 @@ Detects support for the History API for manipulating the browser session history } // Set a single class (either `feature` or `no-feature`) - setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); + setClasses([(!!test && test !== false ? '' : 'no-') + featureNameSplit.join('-')]); // Trigger the event Modernizr._trigger(feature, test); @@ -1907,6 +1815,7 @@ Detects support for the History API for manipulating the browser session history "name": "Webp", "async": true, "property": "webp", + "caniuse": "webp", "tags": ["image"], "builderAliases": ["img_webp"], "authors": ["Krister Kari", "@amandeep", "Rich Bradshaw", "Ryan Seddon", "Paul Irish"], @@ -1914,16 +1823,16 @@ Detects support for the History API for manipulating the browser session history "name": "Webp Info", "href": "https://developers.google.com/speed/webp/" }, { - "name": "Chormium blog - Chrome 32 Beta: Animated WebP images and faster Chrome for Android touch input", + "name": "Chromium blog - Chrome 32 Beta: Animated WebP images and faster Chrome for Android touch input", "href": "https://blog.chromium.org/2013/11/chrome-32-beta-animated-webp-images-and.html" }, { "name": "Webp Lossless Spec", "href": "https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification" }, { - "name": "Article about WebP support on Android browsers", - "href": "http://www.wope-framework.com/en/2013/06/24/webp-support-on-android-browsers/" + "name": "Article about WebP support", + "href": "https://optimus.keycdn.com/support/webp-support/" }, { - "name": "Chormium WebP announcement", + "name": "Chromium WebP announcement", "href": "https://blog.chromium.org/2011/11/lossless-and-transparency-encoding-in.html?m=1" }] } @@ -1964,13 +1873,13 @@ Tests for all forms of webp support (lossless, lossy, alpha, and animated).. function addResult(event) { // if the event is from 'onload', check the see if the image's width is - // 1 pixel (which indiciates support). otherwise, it fails + // 1 pixel (which indicates support). otherwise, it fails - var result = event && event.type === 'load' ? image.width == 1 : false; + var result = event && event.type === 'load' ? image.width === 1 : false; var baseTest = name === 'webp'; // if it is the base test, and the result is false, just set a literal false - // rather than use the Boolean contrsuctor + // rather than use the Boolean constructor addTest(name, (baseTest && result) ? new Boolean(result) : result); if (cb) { @@ -2008,10 +1917,10 @@ Tests for all forms of webp support (lossless, lossy, alpha, and animated).. "notes": [{ "name": "WebP Info", "href": "https://developers.google.com/speed/webp/" - },{ - "name": "Article about WebP support on Android browsers", - "href": "http://www.wope-framework.com/en/2013/06/24/webp-support-on-android-browsers/" - },{ + }, { + "name": "Article about WebP support", + "href": "https://optimus.keycdn.com/support/webp-support/" + }, { "name": "Chromium WebP announcement", "href": "https://blog.chromium.org/2011/11/lossless-and-transparency-encoding-in.html?m=1" }] @@ -2029,7 +1938,7 @@ Tests for transparent webp support. }; image.onload = function() { - addTest('webpalpha', image.width == 1, {aliases: ['webp-alpha']}); + addTest('webpalpha', image.width === 1, {aliases: ['webp-alpha']}); }; image.src = 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA=='; @@ -2064,13 +1973,20 @@ Detects support for the IndexedDB client-side storage API (final spec). } catch (e) { } - if (!!indexeddb) { + if (indexeddb) { var testDBName = 'modernizr-' + Math.random(); - var req = indexeddb.open(testDBName); + var req; + try { + req = indexeddb.open(testDBName); + } catch (e) { + addTest('indexeddb', false); + return; + } - req.onerror = function() { - if (req.error && req.error.name === 'InvalidStateError') { + req.onerror = function(event) { + if (req.error && (req.error.name === 'InvalidStateError' || req.error.name === 'UnknownError')) { addTest('indexeddb', false); + event.preventDefault(); } else { addTest('indexeddb', true); detectDeleteDatabase(indexeddb, testDBName); @@ -2103,7 +2019,7 @@ Detects support for the IndexedDB client-side storage API (final spec). "property": "json", "caniuse": "json", "notes": [{ - "name": "MDN documentation", + "name": "MDN Docs", "href": "https://developer.mozilla.org/en-US/docs/Glossary/JSON" }], "polyfills": ["json2"] @@ -2125,7 +2041,7 @@ Detects native support for JSON handling functions. "tags": ["network"], "caniuse": "fetch", "notes": [{ - "name": "Fetch Living Standard", + "name": "WHATWG Spec", "href": "https://fetch.spec.whatwg.org/" }], "polyfills": ["fetch"] @@ -2141,12 +2057,13 @@ Detects support for the fetch API, a modern replacement for XMLHttpRequest. { "name": "XML HTTP Request Level 2 XHR2", "property": "xhr2", + "caniuse": "xhr2", "tags": ["network"], "builderAliases": ["network_xhr2"], "notes": [{ - "name": "W3 Spec", + "name": "W3C Spec", "href": "https://www.w3.org/TR/XMLHttpRequest2/" - },{ + }, { "name": "Details on Related Github Issue", "href": "https://github.com/Modernizr/Modernizr/issues/385" }] @@ -2168,18 +2085,16 @@ Tests for XHR2. "caniuse": "notifications", "authors": ["Theodoor van Donge", "Hendrik Beskow"], "notes": [{ - "name": "HTML5 Rocks tutorial", - "href": "http://www.html5rocks.com/en/tutorials/notifications/quick/" - },{ - "name": "W3C spec", + "name": "HTML5 Rocks Tutorial", + "href": "https://www.html5rocks.com/en/tutorials/notifications/quick/" + }, { + "name": "W3C Spec", "href": "https://www.w3.org/TR/notifications/" }, { "name": "Changes in Chrome to Notifications API due to Service Worker Push Notifications", "href": "https://developers.google.com/web/updates/2015/05/Notifying-you-of-notificiation-changes" }], - "knownBugs": [ - "Possibility of false-positive on Chrome for Android if permissions we're granted for a website prior to Chrome 44." - ], + "knownBugs": ["Possibility of false-positive on Chrome for Android if permissions we're granted for a website prior to Chrome 44."], "polyfills": ["desktop-notify", "html5-notifications"] } !*/ @@ -2217,9 +2132,9 @@ Detects support for the Notifications API "notes": [{ "name": "W3C Spec", "href": "https://www.w3.org/TR/navigation-timing/" - },{ - "name": "HTML5 Rocks article", - "href": "http://www.html5rocks.com/en/tutorials/webperformance/basics/" + }, { + "name": "HTML5 Rocks Tutorial", + "href": "https://www.html5rocks.com/en/tutorials/webperformance/basics/" }], "polyfills": ["perfnow"] } @@ -2230,42 +2145,62 @@ Detects support for the Navigation Timing API, for measuring browser and connect Modernizr.addTest('performance', !!prefixed('performance', window)); + + /** + * List of JavaScript DOM values used for tests including a NON-prefix + * + * @memberOf Modernizr + * @name Modernizr._domPrefixesAll + * @optionName Modernizr._domPrefixesAll + * @optionProp domPrefixesAll + * @access public + * @example + * + * Modernizr._domPrefixesAll is exactly the same as [_domPrefixes](#modernizr-_domPrefixes), but also + * adds an empty string in the array to test for a non-prefixed value + * + * ```js + * Modernizr._domPrefixesAll === [ "", "Moz", "O", "ms", "Webkit" ]; + * ``` + */ + var domPrefixesAll = [''].concat(domPrefixes); + ModernizrProto._domPrefixesAll = domPrefixesAll; + /*! { "name": "DOM Pointer Events API", "property": "pointerevents", + "caniuse": "pointer", "tags": ["input"], "authors": ["Stu Cox"], - "notes": [ - { - "name": "W3C spec", - "href": "https://www.w3.org/TR/pointerevents/" - } - ], + "notes": [{ + "name": "W3C Spec (Pointer Events)", + "href": "https://www.w3.org/TR/pointerevents/" + }, { + "name": "W3C Spec (Pointer Events Level 2)", + "href": "https://www.w3.org/TR/pointerevents2/" + }, { + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent" + }], "warnings": ["This property name now refers to W3C DOM PointerEvents: https://github.com/Modernizr/Modernizr/issues/548#issuecomment-12812099"], - "polyfills": ["handjs","pep"] + "polyfills": ["pep"] } !*/ /* DOC -Detects support for the DOM Pointer Events API, which provides a unified event interface for pointing input devices, as implemented in IE10+. +Detects support for the DOM Pointer Events API, which provides a unified event interface for pointing input devices, as implemented in IE10+, Edge and Blink. */ // **Test name hijacked!** // Now refers to W3C DOM PointerEvents spec rather than the CSS pointer-events property. Modernizr.addTest('pointerevents', function() { // Cannot use `.prefixed()` for events, so test each prefix - var bool = false, - i = domPrefixes.length; - - // Don't forget un-prefixed... - bool = Modernizr.hasEvent('pointerdown'); - - while (i-- && !bool) { - if (hasEvent(domPrefixes[i] + 'pointerdown')) { - bool = true; + for (var i = 0, len = domPrefixesAll.length; i < len; i++) { + if (hasEvent(domPrefixesAll[i] + 'pointerdown')) { + return true; } } - return bool; + return false; }); /*! @@ -2276,7 +2211,7 @@ Detects support for the DOM Pointer Events API, which provides a unified event i "tags": ["queryselector"], "authors": ["Andrew Betts (@triblondon)"], "notes": [{ - "name" : "W3C Selectors reference", + "name": "W3C Spec", "href": "https://www.w3.org/TR/selectors-api/#queryselectorall" }], "polyfills": ["css-selector-engine"] @@ -2326,6 +2261,7 @@ Detects support for the `defer` attribute on the `