/*! * modernizr v3.5.0 * Build https://modernizr.com/download?-classlist-datalistelem-es5-eventlistener-framed-hidden-history-postmessage-queryselector-dontmin * * Copyright (c) * Faruk Ates * Paul Irish * Alex Sexton * Ryan Seddon * Patrick Kettner * Stu Cox * Richard Herrera * MIT License */ /* * Modernizr tests which native CSS3 and HTML5 features are available in the * current UA and makes the results available to you in two ways: as properties on * a global `Modernizr` object, and as classes on the `` element. This * information allows you to progressively enhance your pages with a granular level * of control over the experience. */ ;(function(window, document, undefined){ var tests = []; /** * * ModernizrProto is the constructor for Modernizr * * @class * @access public */ var ModernizrProto = { // The current version, dummy _version: '3.5.0', // Any settings that don't work as separate modules // can go in here as configuration. _config: { 'classPrefix': '', 'enableClasses': true, '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} */ function is(obj, type) { return typeof obj === type; } ; /** * Run through all tests and detect their support in the current UA. * * @access private */ 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 if (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('-')); } } } } ; /** * 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; /*! { "name": "classList", "caniuse": "classlist", "property": "classlist", "tags": ["dom"], "builderAliases": ["dataview_api"], "notes": [{ "name": "MDN Docs", "href": "https://developer.mozilla.org/en/DOM/element.classList" }] } !*/ Modernizr.addTest('classlist', 'classList' in docElement); /** * 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); } } ; /*! { "name": "[hidden] Attribute", "property": "hidden", "tags": ["dom"], "notes": [{ "name": "WHATWG: The hidden attribute", "href": "https://developers.whatwg.org/editing.html#the-hidden-attribute" }, { "name": "original implementation of detect code", "href": "https://github.com/aFarkas/html5shiv/blob/bf4fcc4/src/html5shiv.js#L38" }], "polyfills": ["html5shiv"], "authors": ["Ron Waldon (@jokeyrhyme)"] } !*/ /* DOC Does the browser support the HTML5 [hidden] attribute? */ Modernizr.addTest('hidden', 'hidden' in createElement('a')); /** * since we have a fairly large number of input tests that don't mutate the input * we create a single element that can be shared with all of those tests for a * minor perf boost * * @access private * @returns {HTMLInputElement} */ var inputElem = createElement('input'); /*! { "name": "Input attributes", "property": "input", "tags": ["forms"], "authors": ["Mike Taylor"], "notes": [{ "name": "WHATWG spec", "href": "https://html.spec.whatwg.org/multipage/forms.html#input-type-attr-summary" }], "knownBugs": ["Some blackberry devices report false positive for input.multiple"] } !*/ /* DOC Detects support for HTML5 `` element attributes and exposes Boolean subproperties with the results: ```javascript Modernizr.input.autocomplete Modernizr.input.autofocus Modernizr.input.list Modernizr.input.max Modernizr.input.min Modernizr.input.multiple Modernizr.input.pattern Modernizr.input.placeholder Modernizr.input.required Modernizr.input.step ``` */ // Run through HTML5's new input attributes to see if the UA understands any. // Mike Taylr has created a comprehensive resource for testing these attributes // when applied to all input types: // miketaylr.com/code/input-type-attr.html // Only input placeholder is tested while textarea's placeholder is not. // Currently Safari 4 and Opera 11 have support only for the input placeholder // Both tests are available in feature-detects/forms-placeholder.js var inputattrs = 'autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '); var attrs = {}; Modernizr.input = (function(props) { for (var i = 0, len = props.length; i < len; i++) { attrs[ props[i] ] = !!(props[i] in inputElem); } if (attrs.list) { // safari false positive's on datalist: webk.it/74252 // see also github.com/Modernizr/Modernizr/issues/146 attrs.list = !!(createElement('datalist') && window.HTMLDataListElement); } return attrs; })(inputattrs); /*! { "name": "datalist Element", "caniuse": "datalist", "property": "datalistelem", "tags": ["elem"], "builderAliases": ["elem_datalist"], "warnings": ["This test is a dupe of Modernizr.input.list. Only around for legacy reasons."], "notes": [{ "name": "CSS Tricks Article", "href": "https://css-tricks.com/15346-relevant-dropdowns-polyfill-for-datalist/" },{ "name": "Mike Taylor Code", "href": "https://miketaylr.com/code/datalist.html" }] } !*/ // lol. we already have a test for datalist built in! silly you. // Leaving it around in case anyone's using it Modernizr.addTest('datalistelem', Modernizr.input.list); /*! { "name": "ES5 Array", "property": "es5array", "notes": [{ "name": "ECMAScript 5.1 Language Specification", "href": "http://www.ecma-international.org/ecma-262/5.1/" }], "polyfills": ["es5shim"], "authors": ["Ron Waldon (@jokeyrhyme)"], "tags": ["es5"] } !*/ /* DOC Check if browser implements ECMAScript 5 Array per specification. */ Modernizr.addTest('es5array', function() { return !!(Array.prototype && Array.prototype.every && Array.prototype.filter && Array.prototype.forEach && Array.prototype.indexOf && Array.prototype.lastIndexOf && Array.prototype.map && Array.prototype.some && Array.prototype.reduce && Array.prototype.reduceRight && Array.isArray); }); /*! { "name": "ES5 Date", "property": "es5date", "notes": [{ "name": "ECMAScript 5.1 Language Specification", "href": "http://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": "http://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": "http://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 Strict Mode", "property": "strictmode", "caniuse": "sctrict-mode", "notes": [{ "name": "ECMAScript 5.1 Language Specification", "href": "http://www.ecma-international.org/ecma-262/5.1/" }], "authors": ["@kangax"], "tags": ["es5"], "builderAliases": ["es5_strictmode"] } !*/ /* DOC Check if browser implements ECMAScript 5 Object strict mode. */ Modernizr.addTest('strictmode', (function() {'use strict'; return !this; })()); /*! { "name": "ES5 String", "property": "es5string", "notes": [{ "name": "ECMAScript 5.1 Language Specification", "href": "http://www.ecma-international.org/ecma-262/5.1/" }], "polyfills": ["es5shim"], "authors": ["Ron Waldon (@jokeyrhyme)"], "tags": ["es5"] } !*/ /* DOC Check if browser implements ECMAScript 5 String per specification. */ Modernizr.addTest('es5string', function() { return !!(String.prototype && String.prototype.trim); }); /*! { "name": "JSON", "property": "json", "caniuse": "json", "notes": [{ "name": "MDN documentation", "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": "ES5 Syntax", "property": "es5syntax", "notes": [{ "name": "ECMAScript 5.1 Language Specification", "href": "http://www.ecma-international.org/ecma-262/5.1/" }, { "name": "original implementation of detect code", "href": "http://kangax.github.io/es5-compat-table/" }], "authors": ["Ron Waldon (@jokeyrhyme)"], "warnings": ["This detect uses `eval()`, so CSP may be a problem."], "tags": ["es5"] } !*/ /* DOC Check if browser accepts ECMAScript 5 syntax. */ Modernizr.addTest('es5syntax', function() { var value, obj, stringAccess, getter, setter, reservedWords, zeroWidthChars; try { /* eslint no-eval: "off" */ // Property access on strings stringAccess = eval('"foobar"[3] === "b"'); // Getter in property initializer getter = eval('({ get x(){ return 1 } }).x === 1'); eval('({ set x(v){ value = v; } }).x = 1'); // Setter in property initializer setter = value === 1; // Reserved words as property names eval('obj = ({ if: 1 })'); reservedWords = obj['if'] === 1; // Zero-width characters in identifiers zeroWidthChars = eval('_\u200c\u200d = true'); return stringAccess && getter && setter && reservedWords && zeroWidthChars; } catch (ignore) { return false; } }); /*! { "name": "ES5 Immutable Undefined", "property": "es5undefined", "notes": [{ "name": "ECMAScript 5.1 Language Specification", "href": "http://www.ecma-international.org/ecma-262/5.1/" }, { "name": "original implementation of detect code", "href": "http://kangax.github.io/es5-compat-table/" }], "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": "ES5", "property": "es5", "notes": [{ "name": "ECMAScript 5.1 Language Specification", "href": "http://www.ecma-international.org/ecma-262/5.1/" }], "polyfills": ["es5shim", "es5sham"], "authors": ["Ron Waldon (@jokeyrhyme)"], "tags": ["es5"] } !*/ /* DOC Check if browser implements everything as specified in ECMAScript 5. */ Modernizr.addTest('es5', function() { return !!( Modernizr.es5array && Modernizr.es5date && Modernizr.es5function && Modernizr.es5object && Modernizr.strictmode && Modernizr.es5string && Modernizr.json && Modernizr.es5syntax && Modernizr.es5undefined ); }); /*! { "name": "Event Listener", "property": "eventlistener", "authors": ["Andrew Betts (@triblondon)"], "notes": [{ "name": "W3C Spec", "href": "https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-Registration-interfaces" }], "polyfills": ["eventlistener"] } !*/ /* DOC Detects native support for addEventListener */ Modernizr.addTest('eventlistener', 'addEventListener' in window); /*! { "name": "History API", "property": "history", "caniuse": "history", "tags": ["history"], "authors": ["Hay Kranen", "Alexander Farkas"], "notes": [{ "name": "W3C Spec", "href": "https://www.w3.org/TR/html51/browsers.html#the-history-interface" }, { "name": "MDN documentation", "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.history" }], "polyfills": ["historyjs", "html5historyapi"] } !*/ /* DOC Detects support for the History API for manipulating the browser session history. */ Modernizr.addTest('history', function() { // Issue #733 // The stock browser on Android 2.2 & 2.3, and 4.0.x returns positive on history support // 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; // 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). if ((ua.indexOf('Android 2.') !== -1 || (ua.indexOf('Android 4.0') !== -1)) && ua.indexOf('Mobile Safari') !== -1 && ua.indexOf('Chrome') === -1 && ua.indexOf('Windows Phone') === -1 && // Since all documents on file:// share an origin, the History apis are // blocked there as well location.protocol !== 'file:' ) { return false; } // Return the regular check return (window.history && 'pushState' in window.history); }); /*! { "name": "postMessage", "property": "postmessage", "caniuse": "x-doc-messaging", "notes": [{ "name": "W3C Spec", "href": "http://www.w3.org/TR/html5/comms.html#posting-messages" }], "polyfills": ["easyxdm", "postmessage-jquery"] } !*/ /* DOC Detects support for the `window.postMessage` protocol for cross-document messaging. */ Modernizr.addTest('postmessage', 'postMessage' in window); /*! { "name": "QuerySelector", "property": "queryselector", "caniuse": "queryselector", "tags": ["queryselector"], "authors": ["Andrew Betts (@triblondon)"], "notes": [{ "name" : "W3C Selectors reference", "href": "https://www.w3.org/TR/selectors-api/#queryselectorall" }], "polyfills": ["css-selector-engine"] } !*/ /* DOC Detects support for querySelector. */ Modernizr.addTest('queryselector', 'querySelector' in document && 'querySelectorAll' in document); /*! { "name": "Framed window", "property": "framed", "tags": ["window"], "builderAliases": ["window_framed"] } !*/ /* DOC Tests if page is iframed. */ // github.com/Modernizr/Modernizr/issues/242 Modernizr.addTest('framed', window.location != top.location); // 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 window.Modernizr = Modernizr; ; })(window, document);