1492 lines
44 KiB
JavaScript
1492 lines
44 KiB
JavaScript
|
|
// NOTE: This file has been modified to set `usePrefixes: false`!
|
|||
|
|
// Find-replace: %s:/\*!:/*:g
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* modernizr v3.6.0
|
|||
|
|
* Build https://modernizr.com/download?-classlist-cssanimations-csspointerevents-cssremunit-csstransforms-customelements-customevent-documentfragment-eventlistener-history-matchmedia-opacity-promises-queryselector-requestanimationframe-template-touchevents-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 `<html>` 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.6.0",
|
|||
|
|
|
|||
|
|
// Any settings that don't work as separate modules
|
|||
|
|
// can go in here as configuration.
|
|||
|
|
_config: {
|
|||
|
|
classPrefix: "",
|
|||
|
|
enableClasses: true,
|
|||
|
|
enableJSClass: true,
|
|||
|
|
usePrefixes: false,
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 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();
|
|||
|
|
|
|||
|
|
// HACK: Manually add `customproperties`...
|
|||
|
|
var supportsFn =
|
|||
|
|
(window.CSS && window.CSS.supports.bind(window.CSS)) || window.supportsCSS;
|
|||
|
|
Modernizr.addTest(
|
|||
|
|
"customproperties",
|
|||
|
|
!!supportsFn && (supportsFn("--f:0") || supportsFn("--f", 0)),
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"name": "Custom Elements API",
|
|||
|
|
"property": "customelements",
|
|||
|
|
"tags": ["customelements"],
|
|||
|
|
"polyfills": ["customelements"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "Specs for Custom Elements",
|
|||
|
|
"href": "https://www.w3.org/TR/custom-elements/"
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
/* DOC
|
|||
|
|
Detects support for the Custom Elements API, to create custom html elements via js
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest("customelements", "customElements" in window);
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"name": "CustomEvent",
|
|||
|
|
"property": "customevent",
|
|||
|
|
"tags": ["customevent"],
|
|||
|
|
"authors": ["Alberto Elias"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "W3C DOM reference",
|
|||
|
|
"href": "https://www.w3.org/TR/DOM-Level-3-Events/#interface-CustomEvent"
|
|||
|
|
}, {
|
|||
|
|
"name": "MDN documentation",
|
|||
|
|
"href": "https://developer.mozilla.org/en/docs/Web/API/CustomEvent"
|
|||
|
|
}],
|
|||
|
|
"polyfills": ["eventlistener"]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
/* DOC
|
|||
|
|
|
|||
|
|
Detects support for CustomEvent.
|
|||
|
|
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest(
|
|||
|
|
"customevent",
|
|||
|
|
"CustomEvent" in window && typeof window.CustomEvent === "function",
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"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": "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": "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": "ES6 Promises",
|
|||
|
|
"property": "promises",
|
|||
|
|
"caniuse": "promises",
|
|||
|
|
"polyfills": ["es6promises"],
|
|||
|
|
"authors": ["Krister Kari", "Jake Archibald"],
|
|||
|
|
"tags": ["es6"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "The ES6 promises spec",
|
|||
|
|
"href": "https://github.com/domenic/promises-unwrapping"
|
|||
|
|
},{
|
|||
|
|
"name": "Chromium dashboard - ES6 Promises",
|
|||
|
|
"href": "https://www.chromestatus.com/features/5681726336532480"
|
|||
|
|
},{
|
|||
|
|
"name": "JavaScript Promises: There and back again - HTML5 Rocks",
|
|||
|
|
"href": "http://www.html5rocks.com/en/tutorials/es6/promises/"
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
/* DOC
|
|||
|
|
Check if browser implements ECMAScript 6 Promises per specification.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest("promises", function () {
|
|||
|
|
return (
|
|||
|
|
"Promise" in window &&
|
|||
|
|
// Some of these methods are missing from
|
|||
|
|
// Firefox/Chrome experimental implementations
|
|||
|
|
"resolve" in window.Promise &&
|
|||
|
|
"reject" in window.Promise &&
|
|||
|
|
"all" in window.Promise &&
|
|||
|
|
"race" in window.Promise &&
|
|||
|
|
// Older version of the spec had a resolver object
|
|||
|
|
// as the arg rather than a function
|
|||
|
|
(function () {
|
|||
|
|
var resolve;
|
|||
|
|
new window.Promise(function (r) {
|
|||
|
|
resolve = r;
|
|||
|
|
});
|
|||
|
|
return typeof resolve === "function";
|
|||
|
|
})()
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var classes = [];
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 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("-"));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* List of property values to set for css tests. See ticket #21
|
|||
|
|
* http://git.io/vUGl4
|
|||
|
|
*
|
|||
|
|
* @memberof Modernizr
|
|||
|
|
* @name Modernizr._prefixes
|
|||
|
|
* @optionName Modernizr._prefixes
|
|||
|
|
* @optionProp prefixes
|
|||
|
|
* @access public
|
|||
|
|
* @example
|
|||
|
|
*
|
|||
|
|
* Modernizr._prefixes is the internal list of prefixes that we test against
|
|||
|
|
* inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply
|
|||
|
|
* an array of kebab-case vendor prefixes you can use within your code.
|
|||
|
|
*
|
|||
|
|
* Some common use cases include
|
|||
|
|
*
|
|||
|
|
* Generating all possible prefixed version of a CSS property
|
|||
|
|
* ```js
|
|||
|
|
* var rule = Modernizr._prefixes.join('transform: rotate(20deg); ');
|
|||
|
|
*
|
|||
|
|
* rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);'
|
|||
|
|
* ```
|
|||
|
|
*
|
|||
|
|
* Generating all possible prefixed version of a CSS value
|
|||
|
|
* ```js
|
|||
|
|
* rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex';
|
|||
|
|
*
|
|||
|
|
* rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex'
|
|||
|
|
* ```
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test
|
|||
|
|
// values in feature detects to continue to work
|
|||
|
|
var prefixes = ModernizrProto._config.usePrefixes
|
|||
|
|
? " -webkit- -moz- -o- -ms- ".split(" ")
|
|||
|
|
: ["", ""];
|
|||
|
|
|
|||
|
|
// expose these for the plugin API. Look in the source for how to join() them against your input
|
|||
|
|
ModernizrProto._prefixes = prefixes;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 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);
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"name": "Document Fragment",
|
|||
|
|
"property": "documentfragment",
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "W3C DOM Level 1 Reference",
|
|||
|
|
"href": "https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3"
|
|||
|
|
}, {
|
|||
|
|
"name": "SitePoint Reference",
|
|||
|
|
"href": "http://reference.sitepoint.com/javascript/DocumentFragment"
|
|||
|
|
}, {
|
|||
|
|
"name": "QuirksMode Compatibility Tables",
|
|||
|
|
"href": "http://www.quirksmode.org/m/w3c_core.html#t112"
|
|||
|
|
}],
|
|||
|
|
"authors": ["Ron Waldon (@jokeyrhyme)"],
|
|||
|
|
"knownBugs": ["false-positive on Blackberry 9500, see QuirksMode note"],
|
|||
|
|
"tags": []
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
/* DOC
|
|||
|
|
Append multiple elements to the DOM within a single insertion.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest("documentfragment", function () {
|
|||
|
|
return "createDocumentFragment" in document && "appendChild" 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": "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 style = createElement("a").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 Pointer Events",
|
|||
|
|
"caniuse": "pointer-events",
|
|||
|
|
"property": "csspointerevents",
|
|||
|
|
"authors": ["ausi"],
|
|||
|
|
"tags": ["css"],
|
|||
|
|
"builderAliases": ["css_pointerevents"],
|
|||
|
|
"notes": [
|
|||
|
|
{
|
|||
|
|
"name": "MDN Docs",
|
|||
|
|
"href": "https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events"
|
|||
|
|
},{
|
|||
|
|
"name": "Test Project Page",
|
|||
|
|
"href": "https://ausi.github.com/Feature-detection-technique-for-pointer-events/"
|
|||
|
|
},{
|
|||
|
|
"name": "Test Project Wiki",
|
|||
|
|
"href": "https://github.com/ausi/Feature-detection-technique-for-pointer-events/wiki"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "Related Github Issue",
|
|||
|
|
"href": "https://github.com/Modernizr/Modernizr/issues/80"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest("csspointerevents", function () {
|
|||
|
|
var style = createElement("a").style;
|
|||
|
|
style.cssText = "pointer-events:auto";
|
|||
|
|
return style.pointerEvents === "auto";
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"name": "CSS Font rem Units",
|
|||
|
|
"caniuse": "rem",
|
|||
|
|
"authors": ["nsfmc"],
|
|||
|
|
"property": "cssremunit",
|
|||
|
|
"tags": ["css"],
|
|||
|
|
"builderAliases": ["css_remunit"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "W3C Spec",
|
|||
|
|
"href": "https://www.w3.org/TR/css3-values/#relative0"
|
|||
|
|
},{
|
|||
|
|
"name": "Font Size with rem by Jonathan Snook",
|
|||
|
|
"href": "http://snook.ca/archives/html_and_css/font-size-with-rem"
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
|
|||
|
|
// "The 'rem' unit ('root em') is relative to the computed
|
|||
|
|
// value of the 'font-size' value of the root element."
|
|||
|
|
// you can test by checking if the prop was ditched
|
|||
|
|
|
|||
|
|
Modernizr.addTest("cssremunit", function () {
|
|||
|
|
var style = createElement("a").style;
|
|||
|
|
try {
|
|||
|
|
style.fontSize = "3rem";
|
|||
|
|
} catch (e) {}
|
|||
|
|
return /rem/.test(style.fontSize);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"name": "Template Tag",
|
|||
|
|
"property": "template",
|
|||
|
|
"tags": ["elem"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "HTML5Rocks Article",
|
|||
|
|
"href": "http://www.html5rocks.com/en/tutorials/webcomponents/template/"
|
|||
|
|
},{
|
|||
|
|
"name": "W3 Spec",
|
|||
|
|
"href": "https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html"
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest("template", "content" in createElement("template"));
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* cssToDOM takes a kebab-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
|
|||
|
|
* @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(/^-/, "");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* getBody returns the body of a document, or an element that can stand in for
|
|||
|
|
* the body if a real body does not exist
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function getBody
|
|||
|
|
* @returns {HTMLElement|SVGElement} Returns the real body of a document, or an
|
|||
|
|
* artificially created element that stands in for the body
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
function getBody() {
|
|||
|
|
// After page load injecting a fake body doesn't work so check if body exists
|
|||
|
|
var body = document.body;
|
|||
|
|
|
|||
|
|
if (!body) {
|
|||
|
|
// Can't use the real body create a fake one.
|
|||
|
|
body = createElement(isSVG ? "svg" : "body");
|
|||
|
|
body.fake = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return body;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* injectElementWithStyles injects an element with style element and some CSS rules
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function injectElementWithStyles
|
|||
|
|
* @param {string} rule - String representing a css rule
|
|||
|
|
* @param {function} callback - A function that is used to test the injected element
|
|||
|
|
* @param {number} [nodes] - An integer representing the number of additional nodes you want injected
|
|||
|
|
* @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
function injectElementWithStyles(rule, callback, nodes, testnames) {
|
|||
|
|
var mod = "modernizr";
|
|||
|
|
var style;
|
|||
|
|
var ret;
|
|||
|
|
var node;
|
|||
|
|
var docOverflow;
|
|||
|
|
var div = createElement("div");
|
|||
|
|
var body = getBody();
|
|||
|
|
|
|||
|
|
if (parseInt(nodes, 10)) {
|
|||
|
|
// In order not to give false positives we create a node for each test
|
|||
|
|
// This also allows the method to scale for unspecified uses
|
|||
|
|
while (nodes--) {
|
|||
|
|
node = createElement("div");
|
|||
|
|
node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
|
|||
|
|
div.appendChild(node);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
style = createElement("style");
|
|||
|
|
style.type = "text/css";
|
|||
|
|
style.id = "s" + mod;
|
|||
|
|
|
|||
|
|
// IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.
|
|||
|
|
// Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
|
|||
|
|
(!body.fake ? div : body).appendChild(style);
|
|||
|
|
body.appendChild(div);
|
|||
|
|
|
|||
|
|
if (style.styleSheet) {
|
|||
|
|
style.styleSheet.cssText = rule;
|
|||
|
|
} else {
|
|||
|
|
style.appendChild(document.createTextNode(rule));
|
|||
|
|
}
|
|||
|
|
div.id = mod;
|
|||
|
|
|
|||
|
|
if (body.fake) {
|
|||
|
|
//avoid crashing IE8, if background image is used
|
|||
|
|
body.style.background = "";
|
|||
|
|
//Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
|
|||
|
|
body.style.overflow = "hidden";
|
|||
|
|
docOverflow = docElement.style.overflow;
|
|||
|
|
docElement.style.overflow = "hidden";
|
|||
|
|
docElement.appendChild(body);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ret = callback(div, rule);
|
|||
|
|
// If this is done after page load we don't want to remove the body so check if body exists
|
|||
|
|
if (body.fake) {
|
|||
|
|
body.parentNode.removeChild(body);
|
|||
|
|
docElement.style.overflow = docOverflow;
|
|||
|
|
// Trigger layout so kinetic scrolling isn't disabled in iOS6+
|
|||
|
|
// eslint-disable-next-line
|
|||
|
|
docElement.offsetHeight;
|
|||
|
|
} else {
|
|||
|
|
div.parentNode.removeChild(div);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return !!ret;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 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": "Touch Events",
|
|||
|
|
"property": "touchevents",
|
|||
|
|
"caniuse" : "touch",
|
|||
|
|
"tags": ["media", "attribute"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "Touch Events spec",
|
|||
|
|
"href": "https://www.w3.org/TR/2013/WD-touch-events-20130124/"
|
|||
|
|
}],
|
|||
|
|
"warnings": [
|
|||
|
|
"Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device"
|
|||
|
|
],
|
|||
|
|
"knownBugs": [
|
|||
|
|
"False-positive on some configurations of Nokia N900",
|
|||
|
|
"False-positive on some BlackBerry 6.0 builds – https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
/* DOC
|
|||
|
|
Indicates if the browser supports the W3C Touch Events API.
|
|||
|
|
|
|||
|
|
This *does not* necessarily reflect a touchscreen device:
|
|||
|
|
|
|||
|
|
* Older touchscreen devices only emulate mouse events
|
|||
|
|
* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that
|
|||
|
|
* Some browsers & OS setups may enable touch APIs when no touchscreen is connected
|
|||
|
|
* Future browsers may implement other event models for touch interactions
|
|||
|
|
|
|||
|
|
See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/).
|
|||
|
|
|
|||
|
|
It's recommended to bind both mouse and touch/pointer events simultaneously – see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/).
|
|||
|
|
|
|||
|
|
This test will also return `true` for Firefox 4 Multitouch support.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415
|
|||
|
|
Modernizr.addTest("touchevents", function () {
|
|||
|
|
var bool;
|
|||
|
|
if (
|
|||
|
|
"ontouchstart" in window ||
|
|||
|
|
(window.DocumentTouch && document instanceof DocumentTouch)
|
|||
|
|
) {
|
|||
|
|
bool = true;
|
|||
|
|
} else {
|
|||
|
|
// include the 'heartz' as a way to have a non matching MQ to help terminate the join
|
|||
|
|
// https://git.io/vznFH
|
|||
|
|
var query = [
|
|||
|
|
"@media (",
|
|||
|
|
prefixes.join("touch-enabled),("),
|
|||
|
|
"heartz",
|
|||
|
|
")",
|
|||
|
|
"{#modernizr{top:9px;position:absolute}}",
|
|||
|
|
].join("");
|
|||
|
|
testStyles(query, function (node) {
|
|||
|
|
bool = node.offsetTop === 9;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
return bool;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* contains checks to see if a string contains another string
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function contains
|
|||
|
|
* @param {string} str - The string we want to check for substrings
|
|||
|
|
* @param {string} substr - The substring we want to search the first string for
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
function contains(str, substr) {
|
|||
|
|
return !!~("" + str).indexOf(substr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* If the browsers follow the spec, then they would expose vendor-specific styles as:
|
|||
|
|
* elem.style.WebkitBorderRadius
|
|||
|
|
* instead of something like the following (which is technically incorrect):
|
|||
|
|
* elem.style.webkitBorderRadius
|
|||
|
|
|
|||
|
|
* WebKit ghosts their properties in lowercase but Opera & Moz do not.
|
|||
|
|
* Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
|
|||
|
|
* erik.eae.net/archives/2008/03/10/21.48.10/
|
|||
|
|
|
|||
|
|
* More here: github.com/Modernizr/Modernizr/issues/issue/21
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @returns {string} The string representing the vendor-specific style properties
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
var omPrefixes = "Moz O ms Webkit";
|
|||
|
|
|
|||
|
|
var cssomPrefixes = ModernizrProto._config.usePrefixes
|
|||
|
|
? omPrefixes.split(" ")
|
|||
|
|
: [];
|
|||
|
|
ModernizrProto._cssomPrefixes = cssomPrefixes;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* atRule returns a given CSS property at-rule (eg @keyframes), possibly in
|
|||
|
|
* some prefixed form, or false, in the case of an unsupported rule
|
|||
|
|
*
|
|||
|
|
* @memberof Modernizr
|
|||
|
|
* @name Modernizr.atRule
|
|||
|
|
* @optionName Modernizr.atRule()
|
|||
|
|
* @optionProp atRule
|
|||
|
|
* @access public
|
|||
|
|
* @function atRule
|
|||
|
|
* @param {string} prop - String name of the @-rule to test for
|
|||
|
|
* @returns {string|boolean} The string representing the (possibly prefixed)
|
|||
|
|
* valid version of the @-rule, or `false` when it is unsupported.
|
|||
|
|
* @example
|
|||
|
|
* ```js
|
|||
|
|
* var keyframes = Modernizr.atRule('@keyframes');
|
|||
|
|
*
|
|||
|
|
* if (keyframes) {
|
|||
|
|
* // keyframes are supported
|
|||
|
|
* // could be `@-webkit-keyframes` or `@keyframes`
|
|||
|
|
* } else {
|
|||
|
|
* // keyframes === `false`
|
|||
|
|
* }
|
|||
|
|
* ```
|
|||
|
|
*
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
var atRule = function (prop) {
|
|||
|
|
var length = prefixes.length;
|
|||
|
|
var cssrule = window.CSSRule;
|
|||
|
|
var rule;
|
|||
|
|
|
|||
|
|
if (typeof cssrule === "undefined") {
|
|||
|
|
return undefined;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!prop) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// remove literal @ from beginning of provided property
|
|||
|
|
prop = prop.replace(/^@/, "");
|
|||
|
|
|
|||
|
|
// CSSRules use underscores instead of dashes
|
|||
|
|
rule = prop.replace(/-/g, "_").toUpperCase() + "_RULE";
|
|||
|
|
|
|||
|
|
if (rule in cssrule) {
|
|||
|
|
return "@" + prop;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (var i = 0; i < length; i++) {
|
|||
|
|
// prefixes gives us something like -o-, and we want O_
|
|||
|
|
var prefix = prefixes[i];
|
|||
|
|
var thisRule = prefix.toUpperCase() + "_" + rule;
|
|||
|
|
|
|||
|
|
if (thisRule in cssrule) {
|
|||
|
|
return "@-" + prefix.toLowerCase() + "-" + prop;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
ModernizrProto.atRule = atRule;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* List of JavaScript DOM values used for tests
|
|||
|
|
*
|
|||
|
|
* @memberof Modernizr
|
|||
|
|
* @name Modernizr._domPrefixes
|
|||
|
|
* @optionName Modernizr._domPrefixes
|
|||
|
|
* @optionProp domPrefixes
|
|||
|
|
* @access public
|
|||
|
|
* @example
|
|||
|
|
*
|
|||
|
|
* Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather
|
|||
|
|
* than kebab-case properties, all properties are their Capitalized variant
|
|||
|
|
*
|
|||
|
|
* ```js
|
|||
|
|
* Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ];
|
|||
|
|
* ```
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
var domPrefixes = ModernizrProto._config.usePrefixes
|
|||
|
|
? omPrefixes.toLowerCase().split(" ")
|
|||
|
|
: [];
|
|||
|
|
ModernizrProto._domPrefixes = domPrefixes;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill.
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function fnBind
|
|||
|
|
* @param {function} fn - a function you want to change `this` reference to
|
|||
|
|
* @param {object} that - the `this` you want to call the function with
|
|||
|
|
* @returns {function} The wrapped version of the supplied function
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
function fnBind(fn, that) {
|
|||
|
|
return function () {
|
|||
|
|
return fn.apply(that, arguments);
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* testDOMProps is a generic DOM property test; if a browser supports
|
|||
|
|
* a certain property, it won't return undefined for it.
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function testDOMProps
|
|||
|
|
* @param {array.<string>} 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
|
|||
|
|
*/
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* domToCSS takes a camelCase string and converts it to kebab-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
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
function domToCSS(name) {
|
|||
|
|
return name
|
|||
|
|
.replace(/([A-Z])/g, function (str, m1) {
|
|||
|
|
return "-" + m1.toLowerCase();
|
|||
|
|
})
|
|||
|
|
.replace(/^ms-/, "-ms-");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* wrapper around getComputedStyle, to fix issues with Firefox returning null when
|
|||
|
|
* called inside of a hidden iframe
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function computedStyle
|
|||
|
|
* @param {HTMLElement|SVGElement} - 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}
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
function computedStyle(elem, pseudo, prop) {
|
|||
|
|
var result;
|
|||
|
|
|
|||
|
|
if ("getComputedStyle" in window) {
|
|||
|
|
result = getComputedStyle.call(window, elem, pseudo);
|
|||
|
|
var console = window.console;
|
|||
|
|
|
|||
|
|
if (result !== null) {
|
|||
|
|
if (prop) {
|
|||
|
|
result = result.getPropertyValue(prop);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
if (console) {
|
|||
|
|
var method = console.error ? "error" : "log";
|
|||
|
|
console[method].call(
|
|||
|
|
console,
|
|||
|
|
"getComputedStyle returning null, its possible modernizr test results are inaccurate",
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
result = !pseudo && elem.currentStyle && elem.currentStyle[prop];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* nativeTestProps allows for us to use native feature detection functionality if available.
|
|||
|
|
* some prefixed form, or false, in the case of an unsupported rule
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function nativeTestProps
|
|||
|
|
* @param {array} props - An array of property names
|
|||
|
|
* @param {string} value - A string representing the value we want to check via @supports
|
|||
|
|
* @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// Accepts a list of property names and a single value
|
|||
|
|
// Returns `undefined` if native detection not available
|
|||
|
|
function nativeTestProps(props, value) {
|
|||
|
|
var i = props.length;
|
|||
|
|
// Start with the JS API: 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 computedStyle(node, null, "position") == "absolute";
|
|||
|
|
},
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
return undefined;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Create our "modernizr" element that we do most feature tests on.
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
var modElem = {
|
|||
|
|
elem: createElement("modernizr"),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Clean up this element
|
|||
|
|
Modernizr._q.push(function () {
|
|||
|
|
delete modElem.elem;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
var mStyle = {
|
|||
|
|
style: modElem.elem.style,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// kill ref for gc, must happen before mod.elem is removed, so we unshift on to
|
|||
|
|
// the front of the queue.
|
|||
|
|
Modernizr._q.unshift(function () {
|
|||
|
|
delete mStyle.style;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 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
|
|||
|
|
|
|||
|
|
// inside of an SVG element, in certain browsers, the `style` element is only
|
|||
|
|
// defined for valid tags. Therefore, if `modernizr` does not have one, we
|
|||
|
|
// fall back to a less used element and hope for the best.
|
|||
|
|
// for strict XHTML browsers the hardly used samp element is used
|
|||
|
|
var elems = ["modernizr", "tspan", "samp"];
|
|||
|
|
while (!mStyle.style && elems.length) {
|
|||
|
|
afterInit = true;
|
|||
|
|
mStyle.modElem = createElement(elems.shift());
|
|||
|
|
mStyle.style = mStyle.modElem.style;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Delete the objects if we created them.
|
|||
|
|
function cleanElems() {
|
|||
|
|
if (afterInit) {
|
|||
|
|
delete mStyle.style;
|
|||
|
|
delete mStyle.modElem;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
propsLength = props.length;
|
|||
|
|
for (i = 0; i < propsLength; i++) {
|
|||
|
|
prop = props[i];
|
|||
|
|
before = mStyle.style[prop];
|
|||
|
|
|
|||
|
|
if (contains(prop, "-")) {
|
|||
|
|
prop = cssToDOM(prop);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (mStyle.style[prop] !== undefined) {
|
|||
|
|
// If value to test has been passed in, do a set-and-check test.
|
|||
|
|
// 0 (integer) is a valid property value, so check that `value` isn't
|
|||
|
|
// undefined, rather than just checking it's truthy.
|
|||
|
|
if (!skipValueTest && !is(value, "undefined")) {
|
|||
|
|
// Needs a try catch block because of old IE. This is slow, but will
|
|||
|
|
// be avoided in most cases because `skipValueTest` will be used.
|
|||
|
|
try {
|
|||
|
|
mStyle.style[prop] = value;
|
|||
|
|
} catch (e) {}
|
|||
|
|
|
|||
|
|
// If the property value has changed, we assume the value used is
|
|||
|
|
// supported. If `value` is empty string, it'll fail here (because
|
|||
|
|
// it hasn't changed), which matches how browsers have implemented
|
|||
|
|
// CSS.supports()
|
|||
|
|
if (mStyle.style[prop] != before) {
|
|||
|
|
cleanElems();
|
|||
|
|
return prefixed == "pfx" ? prop : true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Otherwise just return true, or the property name if this is a
|
|||
|
|
// `prefixed()` call
|
|||
|
|
else {
|
|||
|
|
cleanElems();
|
|||
|
|
return prefixed == "pfx" ? prop : true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
cleanElems();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* testPropsAll tests a list of DOM properties we want to check against.
|
|||
|
|
* We specify literally ALL possible (known and/or likely) properties on
|
|||
|
|
* the element including the non-vendor prefixed one, for forward-
|
|||
|
|
* compatibility.
|
|||
|
|
*
|
|||
|
|
* @access private
|
|||
|
|
* @function testPropsAll
|
|||
|
|
* @param {string} prop - A string of the property to test for
|
|||
|
|
* @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip
|
|||
|
|
* @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against
|
|||
|
|
* @param {string} [value] - A string of a css value
|
|||
|
|
* @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set
|
|||
|
|
* @returns {false|string} returns the string version of the property, or false if it is unsupported
|
|||
|
|
*/
|
|||
|
|
function testPropsAll(prop, prefixed, elem, value, skipValueTest) {
|
|||
|
|
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
|
|||
|
|
props = (prop + " " + cssomPrefixes.join(ucProp + " ") + ucProp).split(
|
|||
|
|
" ",
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// did they call .prefixed('boxSizing') or are we just testing a prop?
|
|||
|
|
if (is(prefixed, "string") || is(prefixed, "undefined")) {
|
|||
|
|
return testProps(props, prefixed, value, skipValueTest);
|
|||
|
|
|
|||
|
|
// otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
|
|||
|
|
} else {
|
|||
|
|
props = (prop + " " + domPrefixes.join(ucProp + " ") + ucProp).split(" ");
|
|||
|
|
return testDOMProps(props, prefixed, elem);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Modernizr.testAllProps() investigates whether a given style property,
|
|||
|
|
// or any of its vendor-prefixed variants, is recognized
|
|||
|
|
//
|
|||
|
|
// Note that the property names must be provided in the camelCase variant.
|
|||
|
|
// Modernizr.testAllProps('boxSizing')
|
|||
|
|
ModernizrProto.testAllProps = testPropsAll;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* testAllProps determines whether a given CSS property is supported in the browser
|
|||
|
|
*
|
|||
|
|
* @memberof Modernizr
|
|||
|
|
* @name Modernizr.testAllProps
|
|||
|
|
* @optionName Modernizr.testAllProps()
|
|||
|
|
* @optionProp testAllProps
|
|||
|
|
* @access public
|
|||
|
|
* @function testAllProps
|
|||
|
|
* @param {string} prop - String naming the property to test (either camelCase or kebab-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
|
|||
|
|
* @example
|
|||
|
|
*
|
|||
|
|
* testAllProps determines whether a given CSS property, in some prefixed form,
|
|||
|
|
* is supported by the browser.
|
|||
|
|
*
|
|||
|
|
* ```js
|
|||
|
|
* testAllProps('boxSizing') // true
|
|||
|
|
* ```
|
|||
|
|
*
|
|||
|
|
* It can optionally be given a CSS value in string form to test if a property
|
|||
|
|
* value is valid
|
|||
|
|
*
|
|||
|
|
* ```js
|
|||
|
|
* testAllProps('display', 'block') // true
|
|||
|
|
* testAllProps('display', 'penguin') // false
|
|||
|
|
* ```
|
|||
|
|
*
|
|||
|
|
* A boolean can be passed as a third parameter to skip the value check when
|
|||
|
|
* native detection (@supports) isn't available.
|
|||
|
|
*
|
|||
|
|
* ```js
|
|||
|
|
* testAllProps('shapeOutside', 'content-box', true);
|
|||
|
|
* ```
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
function testAllProps(prop, value, skipValueTest) {
|
|||
|
|
return testPropsAll(prop, undefined, undefined, value, skipValueTest);
|
|||
|
|
}
|
|||
|
|
ModernizrProto.testAllProps = testAllProps;
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"name": "CSS Animations",
|
|||
|
|
"property": "cssanimations",
|
|||
|
|
"caniuse": "css-animation",
|
|||
|
|
"polyfills": ["transformie", "csssandpaper"],
|
|||
|
|
"tags": ["css"],
|
|||
|
|
"warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name" : "Article: 'Dispelling the Android CSS animation myths'",
|
|||
|
|
"href": "https://goo.gl/OGw5Gm"
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
/* DOC
|
|||
|
|
Detects whether or not elements can be animated using CSS
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest("cssanimations", testAllProps("animationName", "a", true));
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"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)
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* prefixed returns the prefixed or nonprefixed property name variant of your input
|
|||
|
|
*
|
|||
|
|
* @memberof Modernizr
|
|||
|
|
* @name Modernizr.prefixed
|
|||
|
|
* @optionName Modernizr.prefixed()
|
|||
|
|
* @optionProp prefixed
|
|||
|
|
* @access public
|
|||
|
|
* @function prefixed
|
|||
|
|
* @param {string} prop - String name of the property to test for
|
|||
|
|
* @param {object} [obj] - An object to test for the prefixed properties on
|
|||
|
|
* @param {HTMLElement} [elem] - An element used to test specific properties against
|
|||
|
|
* @returns {string|false} 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)
|
|||
|
|
* version of that property that the browser actually supports.
|
|||
|
|
*
|
|||
|
|
* For example, in older Firefox...
|
|||
|
|
* ```js
|
|||
|
|
* prefixed('boxSizing')
|
|||
|
|
* ```
|
|||
|
|
* returns 'MozBoxSizing'
|
|||
|
|
*
|
|||
|
|
* In newer Firefox, as well as any other browser that support the unprefixed
|
|||
|
|
* version would simply return `boxSizing`. Any browser that does not support
|
|||
|
|
* the property at all, it will return `false`.
|
|||
|
|
*
|
|||
|
|
* By default, prefixed is checked against a DOM element. If you want to check
|
|||
|
|
* for a property on another object, just pass it as a second argument
|
|||
|
|
*
|
|||
|
|
* ```js
|
|||
|
|
* var rAF = prefixed('requestAnimationFrame', window);
|
|||
|
|
*
|
|||
|
|
* raf(function() {
|
|||
|
|
* renderFunction();
|
|||
|
|
* })
|
|||
|
|
* ```
|
|||
|
|
*
|
|||
|
|
* Note that this will return _the actual function_ - not the name of the function.
|
|||
|
|
* If you need the actual name of the property, pass in `false` as a third argument
|
|||
|
|
*
|
|||
|
|
* ```js
|
|||
|
|
* var rAFProp = prefixed('requestAnimationFrame', window, false);
|
|||
|
|
*
|
|||
|
|
* rafProp === 'WebkitRequestAnimationFrame' // in older webkit
|
|||
|
|
* ```
|
|||
|
|
*
|
|||
|
|
* One common use case for prefixed is if you're trying to determine which transition
|
|||
|
|
* end event to bind to, you might do something like...
|
|||
|
|
* ```js
|
|||
|
|
* var transEndEventNames = {
|
|||
|
|
* 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser
|
|||
|
|
* 'MozTransition' : 'transitionend', * only for FF < 15
|
|||
|
|
* 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+
|
|||
|
|
* };
|
|||
|
|
*
|
|||
|
|
* var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
|
|||
|
|
* ```
|
|||
|
|
*
|
|||
|
|
* If you want a similar lookup, but in kebab-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
|
|||
|
|
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": "https://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"] },
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
{
|
|||
|
|
"name": "matchMedia",
|
|||
|
|
"property": "matchmedia",
|
|||
|
|
"caniuse" : "matchmedia",
|
|||
|
|
"tags": ["matchmedia"],
|
|||
|
|
"authors": ["Alberto Elias"],
|
|||
|
|
"notes": [{
|
|||
|
|
"name": "W3C CSSOM View Module",
|
|||
|
|
"href": "https://drafts.csswg.org/cssom-view/#the-mediaquerylist-interface"
|
|||
|
|
}, {
|
|||
|
|
"name": "MDN documentation",
|
|||
|
|
"href": "https://developer.mozilla.org/en-US/docs/Web/API/Window.matchMedia"
|
|||
|
|
}],
|
|||
|
|
"polyfills": ["matchmediajs"]
|
|||
|
|
}
|
|||
|
|
!*/
|
|||
|
|
/* DOC
|
|||
|
|
|
|||
|
|
Detects support for matchMedia.
|
|||
|
|
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
Modernizr.addTest("matchmedia", !!prefixed("matchMedia", window));
|
|||
|
|
|
|||
|
|
// Run each test
|
|||
|
|
testRunner();
|
|||
|
|
|
|||
|
|
delete ModernizrProto.addTest;
|
|||
|
|
delete ModernizrProto.addAsyncTest;
|
|||
|
|
|
|||
|
|
// Run the things that are supposed to run after the tests
|
|||
|
|
for (var i = 0; i < Modernizr._q.length; i++) {
|
|||
|
|
Modernizr._q[i]();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Leak Modernizr namespace
|
|||
|
|
window.Modernizr = Modernizr;
|
|||
|
|
})(window, document);
|