/*! * modernizr v3.0.0-alpha.3 * Build http://v3.modernizr.com/download/#-backgroundsize-bgsizecover-borderimage-borderradius-boxshadow-boxsizing-canvas-checked-cookies-cssanimations-csscalc-csscolumns-cssgradients-csspointerevents-csspositionsticky-csstransforms-csstransitions-draganddrop-flexbox-fontface-geolocation-hashchange-history-inlinesvg-json-lastchild-multiplebgs-nthchild-opacity-picture-requestanimationframe-rgba-svg-svgasimg-textshadow-video-willchange-shiv-dontmin * * Copyright (c) * Faruk Ates * Paul Irish * Alex Sexton * Ryan Seddon * Alexander Farkas * 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 = []; var ModernizrProto = { // The current version, dummy _version: '3.0.0-alpha.3', // 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 = []; // Take the html5 variable out of the // html5shiv scope so we can return it. var html5; /** * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed */ ;(function(window, document) { /*jshint evil:true */ /** version */ var version = '3.7.2'; /** Preset options */ var options = window.html5 || {}; /** Used to skip problem elements */ var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; /** Not all elements can be cloned in IE **/ var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; /** Detect whether the browser supports default html5 styles */ var supportsHtml5Styles; /** Name of the expando, to work with multiple documents or to re-shiv one document */ var expando = '_html5shiv'; /** The id for the the documents expando */ var expanID = 0; /** Cached data for each document */ var expandoData = {}; /** Detect whether the browser supports unknown elements */ var supportsUnknownElements; (function() { try { var a = document.createElement('a'); a.innerHTML = ''; //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles supportsHtml5Styles = ('hidden' in a); supportsUnknownElements = a.childNodes.length == 1 || (function() { // assign a false positive if unable to shiv (document.createElement)('a'); var frag = document.createDocumentFragment(); return ( typeof frag.cloneNode == 'undefined' || typeof frag.createDocumentFragment == 'undefined' || typeof frag.createElement == 'undefined' ); }()); } catch(e) { // assign a false positive if detection fails => unable to shiv supportsHtml5Styles = true; supportsUnknownElements = true; } }()); /*--------------------------------------------------------------------------*/ /** * Creates a style sheet with the given CSS text and adds it to the document. * @private * @param {Document} ownerDocument The document. * @param {String} cssText The CSS text. * @returns {StyleSheet} The style element. */ function addStyleSheet(ownerDocument, cssText) { var p = ownerDocument.createElement('p'), parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; p.innerHTML = 'x'; return parent.insertBefore(p.lastChild, parent.firstChild); } /** * Returns the value of `html5.elements` as an array. * @private * @returns {Array} An array of shived element node names. */ function getElements() { var elements = html5.elements; return typeof elements == 'string' ? elements.split(' ') : elements; } /** * Extends the built-in list of html5 elements * @memberOf html5 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv * @param {Document} ownerDocument The context document. */ function addElements(newElements, ownerDocument) { var elements = html5.elements; if(typeof elements != 'string'){ elements = elements.join(' '); } if(typeof newElements != 'string'){ newElements = newElements.join(' '); } html5.elements = elements +' '+ newElements; shivDocument(ownerDocument); } /** * Returns the data associated to the given document * @private * @param {Document} ownerDocument The document. * @returns {Object} An object of data. */ function getExpandoData(ownerDocument) { var data = expandoData[ownerDocument[expando]]; if (!data) { data = {}; expanID++; ownerDocument[expando] = expanID; expandoData[expanID] = data; } return data; } /** * returns a shived element for the given nodeName and document * @memberOf html5 * @param {String} nodeName name of the element * @param {Document} ownerDocument The context document. * @returns {Object} The shived element. */ function createElement(nodeName, ownerDocument, data){ if (!ownerDocument) { ownerDocument = document; } if(supportsUnknownElements){ return ownerDocument.createElement(nodeName); } if (!data) { data = getExpandoData(ownerDocument); } var node; if (data.cache[nodeName]) { node = data.cache[nodeName].cloneNode(); } else if (saveClones.test(nodeName)) { node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); } else { node = data.createElem(nodeName); } // Avoid adding some elements to fragments in IE < 9 because // * Attributes like `name` or `type` cannot be set/changed once an element // is inserted into a document/fragment // * Link elements with `src` attributes that are inaccessible, as with // a 403 response, will cause the tab/window to crash // * Script elements appended to fragments will execute when their `src` // or `text` property is set return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; } /** * returns a shived DocumentFragment for the given document * @memberOf html5 * @param {Document} ownerDocument The context document. * @returns {Object} The shived DocumentFragment. */ function createDocumentFragment(ownerDocument, data){ if (!ownerDocument) { ownerDocument = document; } if(supportsUnknownElements){ return ownerDocument.createDocumentFragment(); } data = data || getExpandoData(ownerDocument); var clone = data.frag.cloneNode(), i = 0, elems = getElements(), l = elems.length; for(;i` or `` elements. */ Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect); /** * is returns a boolean for if typeof obj is exactly type. */ function is( obj, type ) { return typeof obj === type; } ; // Run through all tests and detect their support in the current UA. function testRunner() { var featureNames; var feature; var aliasIdx; var result; var nameIdx; var featureName; var featureNameSplit; for ( var featureIdx in tests ) { 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 /* jshint -W053 */ 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('-')); } } } ; var docElement = document.documentElement; // 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 || ''; // Change `no-js` to `js` (we do this 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 className += ' ' + classPrefix + classes.join(' ' + classPrefix); docElement.className = className; } } ; /*! { "name": "will-change", "property": "willchange", "notes": [{ "name": "Spec", "href": "http://tabatkins.github.io/specs/css-will-change/" }] } !*/ /* DOC Detects support for the `will-change` css property, which formally signals to the browser that an element will be animating. */ Modernizr.addTest('willchange', 'willChange' in docElement.style); var createElement = function() { 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 { return document.createElement.apply(document, arguments); } }; /*! { "name": "Canvas", "property": "canvas", "caniuse": "canvas", "tags": ["canvas", "graphics"], "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"] } !*/ /* DOC Detects support for the `` element for 2D drawing. */ // On the S60 and BB Storm, getContext exists, but always returns undefined // so we actually have to call getContext() to verify // github.com/Modernizr/Modernizr/issues/issue/97/ Modernizr.addTest('canvas', function() { var elem = createElement('canvas'); return !!(elem.getContext && elem.getContext('2d')); }); /*! { "name": "CSS Multiple Backgrounds", "caniuse": "multibackgrounds", "property": "multiplebgs", "tags": ["css"] } !*/ // Setting multiple images AND a color on the background shorthand property // and then querying the style.background property value for the number of // occurrences of "url(" is a reliable method for detecting ACTUAL support for this! Modernizr.addTest('multiplebgs', function() { var elem = createElement('div'); var style = elem.style; style.cssText = 'background:url(https://),url(https://),red url(https://)'; // If the UA supports multiple backgrounds, there should be three occurrences // of the string "url(" in the return value for elemStyle.background return (/(url\s*\(.*?){3}/).test(style.background); }); /*! { "name": "CSS Pointer Events", "caniuse": "pointer-events", "property": "csspointerevents", "authors": ["ausi"], "tags": ["css"], "builderAliases": ["css_pointerevents"], "notes": [ { "name": "MDN Docs", "href": "http://developer.mozilla.org/en/CSS/pointer-events" },{ "name": "Test Project Page", "href": "http://ausi.github.com/Feature-detection-technique-for-pointer-events/" },{ "name": "Test Project Wiki", "href": "http://github.com/ausi/Feature-detection-technique-for-pointer-events/wiki" }, { "name": "Related Github Issue", "href": "http://github.com/Modernizr/Modernizr/issues/80" } ] } !*/ Modernizr.addTest('csspointerevents', function() { var element = createElement('x'); element.style.cssText = 'pointer-events:auto'; return element.style.pointerEvents === 'auto'; }); /*! { "name": "CSS rgba", "caniuse": "css3-colors", "property": "rgba", "tags": ["css"], "notes": [{ "name": "CSSTricks Tutorial", "href": "http://css-tricks.com/rgba-browser-support/" }] } !*/ Modernizr.addTest('rgba', function() { var elem = createElement('div'); var style = elem.style; style.cssText = 'background-color:rgba(150,255,150,.5)'; return ('' + style.backgroundColor).indexOf('rgba') > -1; }); /*! { "name": "Drag & Drop", "property": "draganddrop", "caniuse": "dragndrop", "knownBugs": ["Mobile browsers like Android, iOS < 6, and Firefox OS technically support the APIs, but don't expose it to the end user, resulting in a false positive."], "notes": [{ "name": "W3C spec", "href": "http://www.w3.org/TR/2010/WD-html5-20101019/dnd.html" }], "polyfills": ["dropfile", "moxie", "fileapi"] } !*/ /* DOC Detects support for native drag & drop of elements. */ Modernizr.addTest('draganddrop', function() { var div = createElement('div'); return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div); }); /*! { "name": "HTML5 Video", "property": "video", "caniuse": "video", "tags": ["html5"], "knownBugs": [ "Without QuickTime, `Modernizr.video.h264` will be `undefined`; http://github.com/Modernizr/Modernizr/issues/546" ], "polyfills": [ "html5media", "mediaelementjs", "sublimevideo", "videojs", "leanbackplayer", "videoforeverybody" ] } !*/ /* DOC Detects support for the video element, as well as testing what types of content it supports. Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.: ```javascript Modernizr.video // true Modernizr.video.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('video', function() { /* jshint -W053 */ var elem = createElement('video'); var bool = false; // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 try { if ( bool = !!elem.canPlayType ) { bool = new Boolean(bool); bool.ogg = elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,''); // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546 bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,''); bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,''); bool.vp9 = elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/,''); bool.hls = elem.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/,''); } } catch(e){} return bool; }); /*! { "name": "Inline SVG", "property": "inlinesvg", "caniuse": "svg-html5", "tags": ["svg"], "notes": [{ "name": "Test page", "href": "http://paulirish.com/demo/inline-svg" }], "polyfills": ["inline-svg-polyfill"] } !*/ /* DOC Detects support for inline SVG in HTML (not within XHTML). */ Modernizr.addTest('inlinesvg', function() { var div = createElement('div'); div.innerHTML = ''; return (div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg'; }); // List of property values to set for css tests. See ticket #21 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 Calc", "property": "csscalc", "caniuse": "calc", "tags": ["css"], "builderAliases": ["css_calc"], "authors": ["@calvein"] } !*/ /* DOC Method of allowing calculated values for length units. For example: ```css #elem { width: calc(100% - 3em); } ``` */ Modernizr.addTest('csscalc', function() { var prop = 'width:'; var value = 'calc(10px);'; var el = createElement('div'); el.style.cssText = prop + prefixes.join(value + prop); return !!el.style.length; }); /*! { "name": "CSS Gradients", "caniuse": "css-gradients", "property": "cssgradients", "tags": ["css"], "knownBugs": ["False-positives on webOS (https://github.com/Modernizr/Modernizr/issues/202)"], "notes": [{ "name": "Webkit Gradient Syntax", "href": "http://webkit.org/blog/175/introducing-css-gradients/" },{ "name": "Mozilla Linear Gradient Syntax", "href": "http://developer.mozilla.org/en/CSS/-moz-linear-gradient" },{ "name": "Mozilla Radial Gradient Syntax", "href": "http://developer.mozilla.org/en/CSS/-moz-radial-gradient" },{ "name": "W3C Gradient Spec", "href": "dev.w3.org/csswg/css3-images/#gradients-" }] } !*/ Modernizr.addTest('cssgradients', function() { var str1 = 'background-image:'; var str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));'; var str3 = 'linear-gradient(left top,#9f9, white);'; // standard syntax // trailing 'background-image:' var css = str1 + prefixes.join(str3 + str1).slice(0, -str1.length); if (Modernizr._config.usePrefixes) { // legacy webkit syntax (FIXME: remove when syntax not in use anymore) css += str1 + '-webkit-' + str2; } var elem = createElement('div'); var style = elem.style; style.cssText = css; // IE6 returns undefined so cast to string return ('' + style.backgroundImage).indexOf('gradient') > -1; }); /*! { "name": "CSS Opacity", "caniuse": "css-opacity", "property": "opacity", "tags": ["css"] } !*/ // Browsers that actually have CSS Opacity implemented have done so // according to spec, which means their return values are within the // range of [0.0,1.0] - including the leading zero. Modernizr.addTest('opacity', function() { var elem = createElement('div'); var style = elem.style; style.cssText = prefixes.join('opacity:.55;'); // The non-literal . in this regex is intentional: // German Chrome returns this value as 0,55 // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632 return (/^0.55$/).test(style.opacity); }); /*! { "name": "CSS position: sticky", "property": "csspositionsticky", "tags": ["css"], "builderAliases": ["css_positionsticky"], "notes": [{ "name": "Chrome bug report", "href":"https://code.google.com/p/chromium/issues/detail?id=322972" }], "warnings": [ "using position:sticky on anything but top aligned elements is buggy in Chrome < 37 and iOS <=7+" ] } !*/ // Sticky positioning - constrains an element to be positioned inside the // intersection of its container box, and the viewport. Modernizr.addTest('csspositionsticky', function() { var prop = 'position:'; var value = 'sticky'; var el = createElement('modernizr'); var mStyle = el.style; mStyle.cssText = prop + prefixes.join(value + ';' + prop).slice(0, -prop.length); return mStyle.position.indexOf(value) !== -1; }); // isEventSupported determines if the given element supports the given event // kangax.github.com/iseventsupported/ // github.com/Modernizr/Modernizr/pull/636 // // Known incorrects: // Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative // Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333 var isEventSupported = (function (undefined) { // 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); /** * @param {string|*} eventName is the name of an event to test for (e.g. "resize") * @param {(Object|string|*)=} element is the element|document|window|tagName to test on * @return {boolean} */ function isEventSupportedInner( eventName, element ) { var isSupported; if ( !eventName ) { return false; } if ( !element || typeof element === 'string' ) { element = createElement(element || 'div'); } // Testing via the `in` operator is sufficient for modern browsers and IE. // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and // "resize", whereas `in` "catches" those. eventName = 'on' + eventName; isSupported = eventName in element; // Fallback technique for old Firefox - bit.ly/event-detection if ( !isSupported && needsFallback ) { if ( !element.setAttribute ) { // Switch to generic element if it lacks `setAttribute`. // It could be the `document`, `window`, or something else. element = createElement('div'); } element.setAttribute(eventName, ''); isSupported = typeof element[eventName] === 'function'; if ( element[eventName] !== undefined ) { // If property was created, "remove it" by setting value to `undefined`. element[eventName] = undefined; } element.removeAttribute(eventName); } return isSupported; } return isEventSupportedInner; })(); // Modernizr.hasEvent() detects support for a given event, with an optional element to test on // Modernizr.hasEvent('gesturestart', elem) var hasEvent = ModernizrProto.hasEvent = isEventSupported; /*! { "name": "Hashchange event", "property": "hashchange", "caniuse": "hashchange", "tags": ["history"], "notes": [{ "name": "MDN documentation", "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.onhashchange" }], "polyfills": [ "jquery-hashchange", "moo-historymanager", "jquery-ajaxy", "hasher", "shistory" ] } !*/ /* DOC Detects support for the `hashchange` event, fired when the current location fragment changes. */ Modernizr.addTest('hashchange', function() { if (hasEvent('hashchange', window) === false) { return false; } // documentMode logic from YUI to filter out IE8 Compat Mode // which false positives. return (document.documentMode === undefined || document.documentMode > 7); }); // Helper function for converting kebab-case to camelCase, // e.g. box-sizing -> boxSizing function cssToDOM( name ) { return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { return m1 + m2.toUpperCase(); }).replace(/^-/, ''); } ; // 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')); }; } })(); // As far as I can think of, we shouldn't need or // allow 'on' for non-async tests, and you can't do // async tests without this 'addTest' module. // Listeners for async or post-run tests ModernizrProto._l = {}; // 'addTest' implies a test after the core runloop, // So we'll add in the events ModernizrProto.on = function( test, cb ) { // Create the list of listeners if it doesn't exist if (!this._l[test]) { this._l[test] = []; } // Push this test on to the listener list this._l[test].push(cb); // If it's already been resolved, trigger it on next tick if (Modernizr.hasOwnProperty(test)) { // Next Tick setTimeout(function() { Modernizr._trigger(test, Modernizr[test]); }, 0); } }; ModernizrProto._trigger = function( test, res ) { if (!this._l[test]) { return; } var cbs = this._l[test]; // 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[test]; }; /** * addTest allows the user to define their own feature tests * the result will be added onto the Modernizr object, * as well as an appropriate className set on the html element * * @param feature - String naming the feature * @param test - Function returning true if feature is supported, false if not */ 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 /* jshint -W053 */ 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`) /* jshint -W041 */ setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); /* jshint +W041 */ // 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"], "authors": ["Chris Coyier"], "notes": [{ "name": "HTML5 Spec", "href": "http://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 // http://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')); 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('body'); body.fake = true; } return body; } ; // Inject element with style element and some CSS rules 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); } } // '].join(''); div.id = 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).innerHTML += style; body.appendChild(div); 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.removeChild(body); docElement.style.overflow = docOverflow; // Trigger layout so kinetic scrolling isn't disabled in iOS6+ docElement.offsetHeight; } else { div.parentNode.removeChild(div); } return !!ret; } ; var testStyles = ModernizrProto.testStyles = injectElementWithStyles; /*! { "name": "@font-face", "property": "fontface", "authors": ["Diego Perini", "Mat Marquis"], "tags": ["css"], "knownBugs": [ "False Positive: WebOS http://github.com/Modernizr/Modernizr/issues/342", "False Postive: WP7 http://github.com/Modernizr/Modernizr/issues/538" ], "notes": [{ "name": "@font-face detection routine by Diego Perini", "href": "http://javascript.nwbox.com/CSSSupport/" },{ "name": "Filament Group @font-face compatibility research", "href": "https://docs.google.com/presentation/d/1n4NyG4uPRjAA8zn_pSQ_Ket0RhcWC6QlZ6LMjKeECo0/edit#slide=id.p" },{ "name": "Filament Grunticon/@font-face device testing results", "href": "https://docs.google.com/spreadsheet/ccc?key=0Ag5_yGvxpINRdHFYeUJPNnZMWUZKR2ItMEpRTXZPdUE#gid=0" },{ "name": "CSS fonts on Android", "href": "http://stackoverflow.com/questions/3200069/css-fonts-on-android" },{ "name": "@font-face and Android", "href": "http://archivist.incutio.com/viewlist/css-discuss/115960" }] } !*/ var blacklist = (function() { var ua = navigator.userAgent; var wkvers = ua.match( /applewebkit\/([0-9]+)/gi ) && parseFloat( RegExp.$1 ); var webos = ua.match( /w(eb)?osbrowser/gi ); var wppre8 = ua.match( /windows phone/gi ) && ua.match( /iemobile\/([0-9])+/gi ) && parseFloat( RegExp.$1 ) >= 9; var oldandroid = wkvers < 533 && ua.match( /android/gi ); return webos || oldandroid || wppre8; }()); if( blacklist ) { Modernizr.addTest('fontface', false); } else { testStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) { var style = document.getElementById('smodernizr'); var sheet = style.sheet || style.styleSheet; var cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : ''; var bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0; Modernizr.addTest('fontface', bool); }); } ; /*! { "name": "CSS :checked pseudo-selector", "caniuse": "css-sel3", "property": "checked", "tags": ["css"], "notes": [{ "name": "Related Github Issue", "href": "https://github.com/Modernizr/Modernizr/pull/879" }] } !*/ Modernizr.addTest('checked', function(){ return testStyles('#modernizr {position:absolute} #modernizr input {margin-left:10px} #modernizr :checked {margin-left:20px;display:block}', function( elem ){ var cb = createElement('input'); cb.setAttribute('type', 'checkbox'); cb.setAttribute('checked', 'checked'); elem.appendChild(cb); return cb.offsetLeft === 20; }); }); /*! { "name": "CSS :last-child pseudo-selector", "caniuse": "css-sel3", "property": "lastchild", "tags": ["css"], "builderAliases": ["css_lastchild"], "notes": [{ "name": "Related Github Issue", "href": "https://github.com/Modernizr/Modernizr/pull/304" }] } !*/ testStyles('#modernizr div {width:100px} #modernizr :last-child{width:200px;display:block}', function( elem ) { Modernizr.addTest('lastchild', elem.lastChild.offsetWidth > elem.firstChild.offsetWidth); }, 2); /*! { "name": "CSS :nth-child pseudo-selector", "caniuse": "css-sel3", "property": "nthchild", "tags": ["css"], "notes": [ { "name": "Related Github Issue", "href": "https://github.com/Modernizr/Modernizr/pull/685" }, { "name": "Sitepoint :nth-child documentation", "href": "http://reference.sitepoint.com/css/pseudoclass-nthchild" } ], "authors": ["@emilchristensen"], "warnings": ["Known false negative in Safari 3.1 and Safari 3.2.2"] } !*/ /* DOC Detects support for the ':nth-child()' CSS pseudo-selector. */ // 5 `
` elements with `1px` width are created. // Then every other element has its `width` set to `2px`. // A Javascript loop then tests if the `
`s have the expected width // using the modulus operator. testStyles('#modernizr div {width:1px} #modernizr div:nth-child(2n) {width:2px;}', function( elem ) { Modernizr.addTest('nthchild', function() { var elems = elem.getElementsByTagName('div'), test = true; for (var i = 0; i < 5; i++) { test = test && elems[i].offsetWidth === i % 2 + 1; } return test; }); }, 5); /** * contains returns a boolean for if substr is found within str. */ function contains( str, substr ) { return !!~('' + str).indexOf(substr); } ; // Following spec is to expose vendor-specific style properties as: // elem.style.WebkitBorderRadius // and the following would be 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 var omPrefixes = 'Webkit Moz O ms'; var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); ModernizrProto._cssomPrefixes = cssomPrefixes; /** * atRule returns a given CSS property at-rule (eg @keyframes), possibly in * some prefixed form, or false, in the case of an unsupported rule * * @param prop - String naming the property to test */ var atRule = function(prop) { var length = prefixes.length; var cssrule = window.CSSRule; var rule; if (typeof cssrule === 'undefined' || !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; }; var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); ModernizrProto._domPrefixes = domPrefixes; // Change the function's scope. 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. */ 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 overriden return fnBind(item, elem || obj); } // return the unbound function or obj or value return item; } } return false; } ; /** * Create our "modernizr" element that we do most feature tests on. */ 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; }); // Helper function for converting camelCase to kebab-case, // e.g. boxSizing -> box-sizing function domToCSS( name ) { return name.replace(/([A-Z])/g, function(str, m1) { return '-' + m1.toLowerCase(); }).replace(/^ms-/, '-ms-'); } ; // Function to allow us to use native feature detection functionality if available. // 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 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 getComputedStyle(node, null).position == 'absolute'; }); } return undefined; } ; // 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 kebab-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 if ( !mStyle.style ) { afterInit = true; mStyle.modElem = createElement('modernizr'); mStyle.style = mStyle.modElem.style; } // Delete the objects if we // 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; } ; // Modernizr.testProp() investigates whether a given style property is recognized // Property names can be provided in either camelCase or kebab-case. // Modernizr.testProp('pointerEvents') // Also accepts optional 2nd arg, of a value to use for native feature detection, e.g.: // Modernizr.testProp('pointerEvents', 'none') var testProp = ModernizrProto.testProp = function( prop, value, useValue ) { return testProps([prop], undefined, value, useValue); }; /*! { "name": "CSS textshadow", "property": "textshadow", "caniuse": "css-textshadow", "tags": ["css"], "knownBugs": ["FF3.0 will false positive on this test"] } !*/ Modernizr.addTest('textshadow', testProp('textShadow', '1px 1px')); /** * 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. */ 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, in some prefixed * form, is supported by the browser. It can optionally be given a value; in * which case testAllProps will only return true if the browser supports that * value for the named property; this latter case will use native detection * (via window.CSS.supports) if available. A boolean can be passed as a 3rd * parameter to skip the value check when native detection isn't available, * to improve performance when simply testing for support of a property. * * @param prop - String naming the property to test (either camelCase or * kebab-case) * @param value - [optional] String of the value to test * @param skipValueTest - [optional] Whether to skip testing that the value * is supported when using non-native detection * (default: false) */ function testAllProps (prop, value, skipValueTest) { return testPropsAll(prop, undefined, undefined, value, skipValueTest); } ModernizrProto.testAllProps = testAllProps; /*! { "name": "Background Size", "property": "backgroundsize", "tags": ["css"], "knownBugs": ["This will false positive in Opera Mini - http://github.com/Modernizr/Modernizr/issues/396"], "notes": [{ "name": "Related Issue", "href": "http://github.com/Modernizr/Modernizr/issues/396" }] } !*/ Modernizr.addTest('backgroundsize', testAllProps('backgroundSize', '100%', true)); /*! { "name": "Background Size Cover", "property": "bgsizecover", "tags": ["css"], "builderAliases": ["css_backgroundsizecover"], "notes": [{ "name" : "MDN Docs", "href": "http://developer.mozilla.org/en/CSS/background-size" }] } !*/ // Must test value, as this specifically tests the `cover` value Modernizr.addTest('bgsizecover', testAllProps('backgroundSize', 'cover')); /*! { "name": "Border Image", "property": "borderimage", "caniuse": "border-image", "polyfills": ["css3pie"], "knownBugs": ["Android < 2.0 is true, but has a broken implementation"], "tags": ["css"] } !*/ Modernizr.addTest('borderimage', testAllProps('borderImage', 'url() 1', true)); /*! { "name": "Border Radius", "property": "borderradius", "caniuse": "border-radius", "polyfills": ["css3pie"], "tags": ["css"], "notes": [{ "name": "Comprehensive Compat Chart", "href": "http://muddledramblings.com/table-of-css3-border-radius-compliance" }] } !*/ Modernizr.addTest('borderradius', testAllProps('borderRadius', '0px', true)); /*! { "name": "Box Shadow", "property": "boxshadow", "caniuse": "css-boxshadow", "tags": ["css"], "knownBugs": [ "WebOS false positives on this test.", "The Kindle Silk browser false positives" ] } !*/ Modernizr.addTest('boxshadow', testAllProps('boxShadow', '1px 1px', true)); /*! { "name": "Box Sizing", "property": "boxsizing", "caniuse": "css3-boxsizing", "polyfills": ["borderboxmodel", "boxsizingpolyfill", "borderbox"], "tags": ["css"], "builderAliases": ["css_boxsizing"], "notes": [{ "name": "MDN Docs", "href": "http://developer.mozilla.org/en/CSS/box-sizing" },{ "name": "Related Github Issue", "href": "http://github.com/Modernizr/Modernizr/issues/248" }] } !*/ Modernizr.addTest('boxsizing', testAllProps('boxSizing', 'border-box', true) && (document.documentMode === undefined || document.documentMode > 7)); /*! { "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": "http://goo.gl/OGw5Gm" }] } !*/ /* DOC Detects whether or not elements can be animated using CSS */ Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); /*! { "name": "CSS Columns", "property": "csscolumns", "caniuse": "multicolumn", "polyfills": ["css3multicolumnjs"], "tags": ["css"] } !*/ (function() { /* jshint -W053 */ Modernizr.addTest('csscolumns', function(){ var bool = false; var test = testAllProps('columnCount'); try { if ( bool = !!test ) { bool = new Boolean(bool); } } catch(e){} return bool; }); var props = ['Width', 'Span', 'Fill', 'Gap', 'Rule', 'RuleColor', 'RuleStyle', 'RuleWidth', 'BreakBefore', 'BreakAfter', 'BreakInside']; var name, test; for (var i = 0; i < props.length; i++) { name = props[i].toLowerCase(); test = testAllProps('column' + props[i]); // break-before, break-after & break-inside are not "column"-prefixed in spec if (name === 'breakbefore' || name === 'breakafter' || name == 'breakinside') { test = test || testAllProps(props[i]); } Modernizr.addTest('csscolumns.' + name, test); } })(); /*! { "name": "CSS Transforms", "property": "csstransforms", "caniuse": "transforms2d", "tags": ["css"] } !*/ Modernizr.addTest('csstransforms', function() { // Android < 3.0 is buggy, so we sniff and blacklist // http://git.io/hHzL7w return navigator.userAgent.indexOf('Android 2.') === -1 && testAllProps('transform', 'scale(1)', true); }); /*! { "name": "CSS Transitions", "property": "csstransitions", "caniuse": "css-transitions", "tags": ["css"] } !*/ Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); /*! { "name": "Flexbox", "property": "flexbox", "caniuse": "flexbox", "tags": ["css"], "notes": [{ "name": "The _new_ flexbox", "href": "http://dev.w3.org/csswg/css3-flexbox" }], "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)); // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input // Modernizr.prefixed('boxSizing') // 'MozBoxSizing' // Properties can be passed as DOM-style camelCase or CSS-style kebab-case. // Return values will always be in camelCase; if you want kebab-case, use Modernizr.prefixedCSS(). // If you're trying to ascertain which transition end event to bind to, you might do something like... // // var transEndEventNames = { // 'WebkitTransition' : 'webkitTransitionEnd',// Saf 6, Android Browser // 'MozTransition' : 'transitionend', // only for FF < 15 // 'transition' : 'transitionend' // IE10, Opera, Chrome, FF 15+, Saf 7+ // }, // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; var prefixed = ModernizrProto.prefixed = function( prop, obj, elem ) { if (prop.indexOf('@') === 0) { return atRule(prop); } if (prop.indexOf('-') != -1) { // Convert kebab-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": "requestAnimationFrame", "property": "requestanimationframe", "aliases": ["raf"], "caniuse": "requestanimationframe", "tags": ["animation"], "authors": ["Addy Osmani"], "notes": [{ "name": "W3C spec", "href": "http://www.w3.org/TR/animation-timing/" }], "polyfills": ["raf"] } !*/ /* DOC Detects support for the `window.requestAnimationFrame` API, for offloading animation repainting to the browser for optimized performance. */ Modernizr.addTest('requestanimationframe', !!prefixed('requestAnimationFrame', window), { aliases: ['raf'] }); // Run each test testRunner(); // Remove the "no-js" class if it exists setClasses(classes); 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);