` 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);