1555 lines
49 KiB
JavaScript
Executable File
1555 lines
49 KiB
JavaScript
Executable File
/*!
|
|
* modernizr v3.7.0
|
|
* Build https://modernizr.com/download?-cors-input-smil-addtest-printshiv-setclasses-testprop-dontmin-cssclassprefix:prefixed-
|
|
*
|
|
* 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.7.0',
|
|
|
|
// Any settings that don't work as separate modules
|
|
// can go in here as configuration.
|
|
_config: {
|
|
'classPrefix': 'prefixed-',
|
|
'enableClasses': true,
|
|
'enableJSClass': true,
|
|
'usePrefixes': true
|
|
},
|
|
|
|
// Queue of tests
|
|
_q: [],
|
|
|
|
// Stub these for people who are listening
|
|
on: function(test, cb) {
|
|
// I don't really think people should do this, but we can
|
|
// safe guard it a bit.
|
|
// -- NOTE:: this gets WAY overridden in src/addTest for actual async tests.
|
|
// This is in case people listen to synchronous tests. I would leave it out,
|
|
// but the code to *disallow* sync tests in the real version of this
|
|
// function is actually larger than this.
|
|
var self = this;
|
|
setTimeout(function() {
|
|
cb(self[test]);
|
|
}, 0);
|
|
},
|
|
|
|
addTest: function(name, fn, options) {
|
|
tests.push({name: name, fn: fn, options: options});
|
|
},
|
|
|
|
addAsyncTest: function(fn) {
|
|
tests.push({name: null, fn: fn});
|
|
}
|
|
};
|
|
|
|
|
|
|
|
// Fake some of Object.create so we can force non test results to be non "own" properties.
|
|
var Modernizr = function() {};
|
|
Modernizr.prototype = ModernizrProto;
|
|
|
|
// Leak modernizr globally when you `require` it rather than force it here.
|
|
// Overwrite name so constructor name is nicer :D
|
|
Modernizr = new Modernizr();
|
|
|
|
|
|
|
|
var classes = [];
|
|
|
|
|
|
/**
|
|
* is returns a boolean if the typeof an obj is exactly type.
|
|
*
|
|
* @access private
|
|
* @function is
|
|
* @param {*} obj - A thing we want to check the type of
|
|
* @param {string} type - A string to compare the typeof against
|
|
* @returns {boolean} true if the typeof the first parameter is exactly the specified type, false otherwise
|
|
*/
|
|
function is(obj, type) {
|
|
return typeof obj === type;
|
|
}
|
|
|
|
;
|
|
|
|
/**
|
|
* Run through all tests and detect their support in the current UA.
|
|
*
|
|
* @access private
|
|
* @returns {void}
|
|
*/
|
|
function testRunner() {
|
|
var featureNames;
|
|
var feature;
|
|
var aliasIdx;
|
|
var result;
|
|
var nameIdx;
|
|
var featureName;
|
|
var featureNameSplit;
|
|
|
|
for (var featureIdx in tests) {
|
|
if (tests.hasOwnProperty(featureIdx)) {
|
|
featureNames = [];
|
|
feature = tests[featureIdx];
|
|
// run the test, throw the return value into the Modernizr,
|
|
// then based on that boolean, define an appropriate className
|
|
// and push it into an array of classes we'll join later.
|
|
//
|
|
// If there is no name, it's an 'async' test that is run,
|
|
// but not directly added to the object. That should
|
|
// be done with a post-run addTest call.
|
|
if (feature.name) {
|
|
featureNames.push(feature.name.toLowerCase());
|
|
|
|
if (feature.options && feature.options.aliases && feature.options.aliases.length) {
|
|
// Add all the aliases into the names list
|
|
for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) {
|
|
featureNames.push(feature.options.aliases[aliasIdx].toLowerCase());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run the test, or use the raw value if it's not a function
|
|
result = is(feature.fn, 'function') ? feature.fn() : feature.fn;
|
|
|
|
// Set each of the names on the Modernizr object
|
|
for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) {
|
|
featureName = featureNames[nameIdx];
|
|
// Support dot properties as sub tests. We don't do checking to make sure
|
|
// that the implied parent tests have been added. You must call them in
|
|
// order (either in the test, or make the parent test a dependency).
|
|
//
|
|
// Cap it to TWO to make the logic simple and because who needs that kind of subtesting
|
|
// hashtag famous last words
|
|
featureNameSplit = featureName.split('.');
|
|
|
|
if (featureNameSplit.length === 1) {
|
|
Modernizr[featureNameSplit[0]] = result;
|
|
} else {
|
|
// cast to a Boolean, if not one already
|
|
if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
|
|
Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
|
|
}
|
|
|
|
Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result;
|
|
}
|
|
|
|
classes.push((result ? '' : 'no-') + featureNameSplit.join('-'));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
;
|
|
|
|
/**
|
|
* docElement is a convenience wrapper to grab the root element of the document
|
|
*
|
|
* @access private
|
|
* @returns {HTMLElement|SVGElement} The root element of the document
|
|
*/
|
|
var docElement = document.documentElement;
|
|
|
|
|
|
/**
|
|
* 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';
|
|
|
|
|
|
|
|
/**
|
|
* setClasses takes an array of class names and adds them to the root element
|
|
*
|
|
* @access private
|
|
* @function setClasses
|
|
* @param {string[]} classes - Array of class names
|
|
*/
|
|
// Pass in an and array of class names, e.g.:
|
|
// ['no-webp', 'borderradius', ...]
|
|
function setClasses(classes) {
|
|
var className = docElement.className;
|
|
var classPrefix = Modernizr._config.classPrefix || '';
|
|
|
|
if (isSVG) {
|
|
className = className.baseVal;
|
|
}
|
|
|
|
// Change `no-js` to `js` (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
|
|
if (classes.length > 0) {
|
|
className += ' ' + classPrefix + classes.join(' ' + classPrefix);
|
|
}
|
|
if (isSVG) {
|
|
docElement.className.baseVal = className;
|
|
} else {
|
|
docElement.className = className;
|
|
}
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
/**
|
|
* hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support
|
|
*
|
|
* @author kangax
|
|
* @access private
|
|
* @function hasOwnProp
|
|
* @param {object} object - The object to check for a property
|
|
* @param {string} property - The property to check for
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
// 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'));
|
|
};
|
|
}
|
|
})();
|
|
|
|
|
|
|
|
|
|
// _l tracks listeners for async tests, as well as tests that execute after the initial run
|
|
ModernizrProto._l = {};
|
|
|
|
/**
|
|
* Modernizr.on is a way to listen for the completion of async tests. Being
|
|
* asynchronous, they may not finish before your scripts run. As a result you
|
|
* will get a possibly false negative `undefined` value.
|
|
*
|
|
* @memberOf Modernizr
|
|
* @name Modernizr.on
|
|
* @access public
|
|
* @function on
|
|
* @param {string} feature - String name of the feature detect
|
|
* @param {Function} cb - Callback function returning a Boolean - true if feature is supported, false if not
|
|
* @returns {void}
|
|
* @example
|
|
*
|
|
* ```js
|
|
* Modernizr.on('flash', function( result ) {
|
|
* if (result) {
|
|
* // the browser has flash
|
|
* } else {
|
|
* // the browser does not have flash
|
|
* }
|
|
* });
|
|
* ```
|
|
*/
|
|
ModernizrProto.on = function(feature, cb) {
|
|
// Create the list of listeners if it doesn't exist
|
|
if (!this._l[feature]) {
|
|
this._l[feature] = [];
|
|
}
|
|
|
|
// Push this test on to the listener list
|
|
this._l[feature].push(cb);
|
|
|
|
// If it's already been resolved, trigger it on next tick
|
|
if (Modernizr.hasOwnProperty(feature)) {
|
|
// Next Tick
|
|
setTimeout(function() {
|
|
Modernizr._trigger(feature, Modernizr[feature]);
|
|
}, 0);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* _trigger is the private function used to signal test completion and run any
|
|
* callbacks registered through [Modernizr.on](#modernizr-on)
|
|
*
|
|
* @memberOf Modernizr
|
|
* @name Modernizr._trigger
|
|
* @access private
|
|
* @function _trigger
|
|
* @param {string} feature - string name of the feature detect
|
|
* @param {Function|boolean} [res] - A feature detection function, or the boolean =
|
|
* result of a feature detection function
|
|
* @returns {void}
|
|
*/
|
|
ModernizrProto._trigger = function(feature, res) {
|
|
if (!this._l[feature]) {
|
|
return;
|
|
}
|
|
|
|
var cbs = this._l[feature];
|
|
|
|
// 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[feature];
|
|
};
|
|
|
|
/**
|
|
* addTest allows you to define your own feature detects that are not currently
|
|
* included in Modernizr (under the covers it's the exact same code Modernizr
|
|
* uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)).
|
|
* Just like the official detects, the result
|
|
* will be added onto the Modernizr object, as well as an appropriate className set on
|
|
* the html element when configured to do so
|
|
*
|
|
* @memberOf Modernizr
|
|
* @name Modernizr.addTest
|
|
* @optionName Modernizr.addTest()
|
|
* @optionProp addTest
|
|
* @access public
|
|
* @function addTest
|
|
* @param {string|Object} feature - The string name of the feature detect, or an
|
|
* object of feature detect names and test
|
|
* @param {Function|boolean} test - Function returning true if feature is supported,
|
|
* false if not. Otherwise a boolean representing the results of a feature detection
|
|
* @returns {Object} the Modernizr object to allow chaining
|
|
* @example
|
|
*
|
|
* The most common way of creating your own feature detects is by calling
|
|
* `Modernizr.addTest` with a string (preferably just lowercase, without any
|
|
* punctuation), and a function you want executed that will return a boolean result
|
|
*
|
|
* ```js
|
|
* Modernizr.addTest('itsTuesday', function() {
|
|
* var d = new Date();
|
|
* return d.getDay() === 2;
|
|
* });
|
|
* ```
|
|
*
|
|
* When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday,
|
|
* and to `false` every other day of the week. One thing to notice is that the names of
|
|
* feature detect functions are always lowercased when added to the Modernizr object. That
|
|
* means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will.
|
|
*
|
|
*
|
|
* Since we only look at the returned value from any feature detection function,
|
|
* you do not need to actually use a function. For simple detections, just passing
|
|
* in a statement that will return a boolean value works just fine.
|
|
*
|
|
* ```js
|
|
* Modernizr.addTest('hasjquery', 'jQuery' in window);
|
|
* ```
|
|
*
|
|
* Just like before, when the above runs `Modernizr.hasjquery` will be true if
|
|
* jQuery has been included on the page. Not using a function saves a small amount
|
|
* of overhead for the browser, as well as making your code much more readable.
|
|
*
|
|
* Finally, you also have the ability to pass in an object of feature names and
|
|
* their tests. This is handy if you want to add multiple detections in one go.
|
|
* The keys should always be a string, and the value can be either a boolean or
|
|
* function that returns a boolean.
|
|
*
|
|
* ```js
|
|
* var detects = {
|
|
* 'hasjquery': 'jQuery' in window,
|
|
* 'itstuesday': function() {
|
|
* var d = new Date();
|
|
* return d.getDay() === 2;
|
|
* }
|
|
* }
|
|
*
|
|
* Modernizr.addTest(detects);
|
|
* ```
|
|
*
|
|
* There is really no difference between the first methods and this one, it is
|
|
* just a convenience to let you write more readable code.
|
|
*/
|
|
function addTest(feature, test) {
|
|
|
|
if (typeof feature === 'object') {
|
|
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
|
|
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`)
|
|
setClasses([(!!test && test !== false ? '' : 'no-') + featureNameSplit.join('-')]);
|
|
|
|
// 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;
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
* @optionName html5printshiv
|
|
* @optionProp html5printshiv
|
|
*/
|
|
|
|
// Take the html5 variable out of the html5shiv scope so we can return it.
|
|
var html5;
|
|
if (!isSVG) {
|
|
/**
|
|
* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
|
*/
|
|
;(function(window, document) {
|
|
/*jshint evil:true */
|
|
/** version */
|
|
var version = '3.7.3';
|
|
|
|
/** 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 = '<xyz></xyz>';
|
|
//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<style>' + cssText + '</style>';
|
|
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<l;i++){
|
|
clone.createElement(elems[i]);
|
|
}
|
|
return clone;
|
|
}
|
|
|
|
/**
|
|
* Shivs the `createElement` and `createDocumentFragment` methods of the document.
|
|
* @private
|
|
* @param {Document|DocumentFragment} ownerDocument The document.
|
|
* @param {Object} data of the document.
|
|
*/
|
|
function shivMethods(ownerDocument, data) {
|
|
if (!data.cache) {
|
|
data.cache = {};
|
|
data.createElem = ownerDocument.createElement;
|
|
data.createFrag = ownerDocument.createDocumentFragment;
|
|
data.frag = data.createFrag();
|
|
}
|
|
|
|
|
|
ownerDocument.createElement = function(nodeName) {
|
|
//abort shiv
|
|
if (!html5.shivMethods) {
|
|
return data.createElem(nodeName);
|
|
}
|
|
return createElement(nodeName, ownerDocument, data);
|
|
};
|
|
|
|
ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
|
|
'var n=f.cloneNode(),c=n.createElement;' +
|
|
'h.shivMethods&&(' +
|
|
// unroll the `createElement` calls
|
|
getElements().join().replace(/[\w\-:]+/g, function(nodeName) {
|
|
data.createElem(nodeName);
|
|
data.frag.createElement(nodeName);
|
|
return 'c("' + nodeName + '")';
|
|
}) +
|
|
');return n}'
|
|
)(html5, data.frag);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Shivs the given document.
|
|
* @memberOf html5
|
|
* @param {Document} ownerDocument The document to shiv.
|
|
* @returns {Document} The shived document.
|
|
*/
|
|
function shivDocument(ownerDocument) {
|
|
if (!ownerDocument) {
|
|
ownerDocument = document;
|
|
}
|
|
var data = getExpandoData(ownerDocument);
|
|
|
|
if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
|
|
data.hasCSS = !!addStyleSheet(ownerDocument,
|
|
// corrects block display not defined in IE6/7/8/9
|
|
'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
|
|
// adds styling not present in IE6/7/8/9
|
|
'mark{background:#FF0;color:#000}' +
|
|
// hides non-rendered elements
|
|
'template{display:none}'
|
|
);
|
|
}
|
|
if (!supportsUnknownElements) {
|
|
shivMethods(ownerDocument, data);
|
|
}
|
|
return ownerDocument;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* The `html5` object is exposed so that more elements can be shived and
|
|
* existing shiving can be detected on iframes.
|
|
* @type Object
|
|
* @example
|
|
*
|
|
* // options can be changed before the script is included
|
|
* html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
|
|
*/
|
|
var html5 = {
|
|
|
|
/**
|
|
* An array or space separated string of node names of the elements to shiv.
|
|
* @memberOf html5
|
|
* @type Array|String
|
|
*/
|
|
'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video',
|
|
|
|
/**
|
|
* current version of html5shiv
|
|
*/
|
|
'version': version,
|
|
|
|
/**
|
|
* A flag to indicate that the HTML5 style sheet should be inserted.
|
|
* @memberOf html5
|
|
* @type Boolean
|
|
*/
|
|
'shivCSS': (options.shivCSS !== false),
|
|
|
|
/**
|
|
* Is equal to true if a browser supports creating unknown/HTML5 elements
|
|
* @memberOf html5
|
|
* @type boolean
|
|
*/
|
|
'supportsUnknownElements': supportsUnknownElements,
|
|
|
|
/**
|
|
* A flag to indicate that the document's `createElement` and `createDocumentFragment`
|
|
* methods should be overwritten.
|
|
* @memberOf html5
|
|
* @type Boolean
|
|
*/
|
|
'shivMethods': (options.shivMethods !== false),
|
|
|
|
/**
|
|
* A string to describe the type of `html5` object ("default" or "default print").
|
|
* @memberOf html5
|
|
* @type String
|
|
*/
|
|
'type': 'default',
|
|
|
|
// shivs the document according to the specified `html5` object options
|
|
'shivDocument': shivDocument,
|
|
|
|
//creates a shived element
|
|
createElement: createElement,
|
|
|
|
//creates a shived documentFragment
|
|
createDocumentFragment: createDocumentFragment,
|
|
|
|
//extends list of elements
|
|
addElements: addElements
|
|
};
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
// expose html5
|
|
window.html5 = html5;
|
|
|
|
// shiv the document
|
|
shivDocument(document);
|
|
|
|
/*------------------------------- Print Shiv -------------------------------*/
|
|
|
|
/** Used to filter media types */
|
|
var reMedia = /^$|\b(?:all|print)\b/;
|
|
|
|
/** Used to namespace printable elements */
|
|
var shivNamespace = 'html5shiv';
|
|
|
|
/** Detect whether the browser supports shivable style sheets */
|
|
var supportsShivableSheets = !supportsUnknownElements && (function() {
|
|
// assign a false negative if unable to shiv
|
|
var docEl = document.documentElement;
|
|
return !(
|
|
typeof document.namespaces == 'undefined' ||
|
|
typeof document.parentWindow == 'undefined' ||
|
|
typeof docEl.applyElement == 'undefined' ||
|
|
typeof docEl.removeNode == 'undefined' ||
|
|
typeof window.attachEvent == 'undefined'
|
|
);
|
|
}());
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Wraps all HTML5 elements in the given document with printable elements.
|
|
* (eg. the "header" element is wrapped with the "html5shiv:header" element)
|
|
* @private
|
|
* @param {Document} ownerDocument The document.
|
|
* @returns {Array} An array wrappers added.
|
|
*/
|
|
function addWrappers(ownerDocument) {
|
|
var node,
|
|
nodes = ownerDocument.getElementsByTagName('*'),
|
|
index = nodes.length,
|
|
reElements = RegExp('^(?:' + getElements().join('|') + ')$', 'i'),
|
|
result = [];
|
|
|
|
while (index--) {
|
|
node = nodes[index];
|
|
if (reElements.test(node.nodeName)) {
|
|
result.push(node.applyElement(createWrapper(node)));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates a printable wrapper for the given element.
|
|
* @private
|
|
* @param {Element} element The element.
|
|
* @returns {Element} The wrapper.
|
|
*/
|
|
function createWrapper(element) {
|
|
var node,
|
|
nodes = element.attributes,
|
|
index = nodes.length,
|
|
wrapper = element.ownerDocument.createElement(shivNamespace + ':' + element.nodeName);
|
|
|
|
// copy element attributes to the wrapper
|
|
while (index--) {
|
|
node = nodes[index];
|
|
node.specified && wrapper.setAttribute(node.nodeName, node.nodeValue);
|
|
}
|
|
// copy element styles to the wrapper
|
|
wrapper.style.cssText = element.style.cssText;
|
|
return wrapper;
|
|
}
|
|
|
|
/**
|
|
* Shivs the given CSS text.
|
|
* (eg. header{} becomes html5shiv\:header{})
|
|
* @private
|
|
* @param {String} cssText The CSS text to shiv.
|
|
* @returns {String} The shived CSS text.
|
|
*/
|
|
function shivCssText(cssText) {
|
|
var pair,
|
|
parts = cssText.split('{'),
|
|
index = parts.length,
|
|
reElements = RegExp('(^|[\\s,>+~])(' + getElements().join('|') + ')(?=[[\\s,>+~#.:]|$)', 'gi'),
|
|
replacement = '$1' + shivNamespace + '\\:$2';
|
|
|
|
while (index--) {
|
|
pair = parts[index] = parts[index].split('}');
|
|
pair[pair.length - 1] = pair[pair.length - 1].replace(reElements, replacement);
|
|
parts[index] = pair.join('}');
|
|
}
|
|
return parts.join('{');
|
|
}
|
|
|
|
/**
|
|
* Removes the given wrappers, leaving the original elements.
|
|
* @private
|
|
* @params {Array} wrappers An array of printable wrappers.
|
|
*/
|
|
function removeWrappers(wrappers) {
|
|
var index = wrappers.length;
|
|
while (index--) {
|
|
wrappers[index].removeNode();
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Shivs the given document for print.
|
|
* @memberOf html5
|
|
* @param {Document} ownerDocument The document to shiv.
|
|
* @returns {Document} The shived document.
|
|
*/
|
|
function shivPrint(ownerDocument) {
|
|
var shivedSheet,
|
|
wrappers,
|
|
data = getExpandoData(ownerDocument),
|
|
namespaces = ownerDocument.namespaces,
|
|
ownerWindow = ownerDocument.parentWindow;
|
|
|
|
if (!supportsShivableSheets || ownerDocument.printShived) {
|
|
return ownerDocument;
|
|
}
|
|
if (typeof namespaces[shivNamespace] == 'undefined') {
|
|
namespaces.add(shivNamespace);
|
|
}
|
|
|
|
function removeSheet() {
|
|
clearTimeout(data._removeSheetTimer);
|
|
if (shivedSheet) {
|
|
shivedSheet.removeNode(true);
|
|
}
|
|
shivedSheet= null;
|
|
}
|
|
|
|
ownerWindow.attachEvent('onbeforeprint', function() {
|
|
|
|
removeSheet();
|
|
|
|
var imports,
|
|
length,
|
|
sheet,
|
|
collection = ownerDocument.styleSheets,
|
|
cssText = [],
|
|
index = collection.length,
|
|
sheets = Array(index);
|
|
|
|
// convert styleSheets collection to an array
|
|
while (index--) {
|
|
sheets[index] = collection[index];
|
|
}
|
|
// concat all style sheet CSS text
|
|
while ((sheet = sheets.pop())) {
|
|
// IE does not enforce a same origin policy for external style sheets...
|
|
// but has trouble with some dynamically created stylesheets
|
|
if (!sheet.disabled && reMedia.test(sheet.media)) {
|
|
|
|
try {
|
|
imports = sheet.imports;
|
|
length = imports.length;
|
|
} catch(er){
|
|
length = 0;
|
|
}
|
|
|
|
for (index = 0; index < length; index++) {
|
|
sheets.push(imports[index]);
|
|
}
|
|
|
|
try {
|
|
cssText.push(sheet.cssText);
|
|
} catch(er){}
|
|
}
|
|
}
|
|
|
|
// wrap all HTML5 elements with printable elements and add the shived style sheet
|
|
cssText = shivCssText(cssText.reverse().join(''));
|
|
wrappers = addWrappers(ownerDocument);
|
|
shivedSheet = addStyleSheet(ownerDocument, cssText);
|
|
|
|
});
|
|
|
|
ownerWindow.attachEvent('onafterprint', function() {
|
|
// remove wrappers, leaving the original elements, and remove the shived style sheet
|
|
removeWrappers(wrappers);
|
|
clearTimeout(data._removeSheetTimer);
|
|
data._removeSheetTimer = setTimeout(removeSheet, 500);
|
|
});
|
|
|
|
ownerDocument.printShived = true;
|
|
return ownerDocument;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
// expose API
|
|
html5.type += ' print';
|
|
html5.shivPrint = shivPrint;
|
|
|
|
// shiv for print
|
|
shivPrint(document);
|
|
|
|
if(typeof module == 'object' && module.exports){
|
|
module.exports = html5;
|
|
}
|
|
|
|
}(typeof window !== "undefined" ? window : this, document));
|
|
}
|
|
|
|
;
|
|
|
|
/**
|
|
* 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} true if and only if the first string 'str' contains the second string 'substr'
|
|
*/
|
|
function contains(str, substr) {
|
|
return !!~('' + str).indexOf(substr);
|
|
}
|
|
|
|
;
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
/**
|
|
* 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;
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
* 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} the result of the specified callback test
|
|
*/
|
|
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;
|
|
}
|
|
|
|
;
|
|
|
|
/**
|
|
* 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} elem - The element we want to find the computed styles of
|
|
* @param {string|null} [pseudo] - An optional pseudo element selector (e.g. :before), of null if none
|
|
* @param {string} prop - A CSS property
|
|
* @returns {CSSStyleDeclaration} the value of the specified CSS property
|
|
*/
|
|
function computedStyle(elem, pseudo, prop) {
|
|
var result;
|
|
|
|
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: https://www.w3.org/TR/css3-conditional/#the-css-interface
|
|
if ('CSS' in window && 'supports' in window.CSS) {
|
|
// Try every prefixed variant of the property
|
|
while (i--) {
|
|
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;
|
|
}
|
|
;
|
|
|
|
/**
|
|
* 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(/^-/, '');
|
|
}
|
|
|
|
;
|
|
|
|
// 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;
|
|
}
|
|
|
|
;
|
|
|
|
/**
|
|
* testProp() investigates whether a given style property is recognized
|
|
* Property names can be provided in either camelCase or kebab-case.
|
|
*
|
|
* @memberOf Modernizr
|
|
* @name Modernizr.testProp
|
|
* @access public
|
|
* @optionName Modernizr.testProp()
|
|
* @optionProp testProp
|
|
* @function testProp
|
|
* @param {string} prop - Name of the CSS property to check
|
|
* @param {string} [value] - Name of the CSS value to check
|
|
* @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported
|
|
* @returns {boolean} an empty string if the property is supported, undefined if its unsupported
|
|
* @example
|
|
*
|
|
* Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed
|
|
* version of the string.
|
|
*
|
|
* Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing)
|
|
*
|
|
* ```js
|
|
* Modernizr.testProp('pointerEvents') // true
|
|
* ```
|
|
*
|
|
* You can also provide a value as an optional second argument to check if a
|
|
* specific value is supported
|
|
*
|
|
* ```js
|
|
* Modernizr.testProp('pointerEvents', 'none') // true
|
|
* Modernizr.testProp('pointerEvents', 'penguin') // false
|
|
* ```
|
|
*/
|
|
var testProp = ModernizrProto.testProp = function(prop, value, useValue) {
|
|
return testProps([prop], undefined, value, useValue);
|
|
};
|
|
|
|
|
|
/*!
|
|
{
|
|
"name": "Cross-Origin Resource Sharing",
|
|
"property": "cors",
|
|
"caniuse": "cors",
|
|
"authors": ["Theodoor van Donge"],
|
|
"notes": [{
|
|
"name": "MDN Docs",
|
|
"href": "https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS"
|
|
}],
|
|
"polyfills": ["pmxdr", "ppx", "flxhr"]
|
|
}
|
|
!*/
|
|
/* DOC
|
|
Detects support for Cross-Origin Resource Sharing: method of performing XMLHttpRequests across domains.
|
|
*/
|
|
|
|
Modernizr.addTest('cors', 'XMLHttpRequest' in window && 'withCredentials' in new XMLHttpRequest());
|
|
|
|
|
|
/**
|
|
* since we have a fairly large number of input tests that don't mutate the input
|
|
* we create a single element that can be shared with all of those tests for a
|
|
* minor perf boost
|
|
*
|
|
* @access private
|
|
* @returns {HTMLInputElement}
|
|
*/
|
|
var inputElem = createElement('input');
|
|
|
|
/*!
|
|
{
|
|
"name": "Input attributes",
|
|
"property": "input",
|
|
"tags": ["forms"],
|
|
"authors": ["Mike Taylor"],
|
|
"notes": [{
|
|
"name": "WHATWG Spec",
|
|
"href": "https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary"
|
|
}],
|
|
"knownBugs": ["Some blackberry devices report false positive for input.multiple"]
|
|
}
|
|
!*/
|
|
/* DOC
|
|
Detects support for HTML5 `<input>` element attributes and exposes Boolean subproperties with the results:
|
|
|
|
```javascript
|
|
Modernizr.input.autocomplete
|
|
Modernizr.input.autofocus
|
|
Modernizr.input.list
|
|
Modernizr.input.max
|
|
Modernizr.input.min
|
|
Modernizr.input.multiple
|
|
Modernizr.input.pattern
|
|
Modernizr.input.placeholder
|
|
Modernizr.input.required
|
|
Modernizr.input.step
|
|
```
|
|
*/
|
|
|
|
// Run through HTML5's new input attributes to see if the UA understands any.
|
|
// Mike Taylr has created a comprehensive resource for testing these attributes
|
|
// when applied to all input types:
|
|
// miketaylr.com/code/input-type-attr.html
|
|
|
|
// Only input placeholder is tested while textarea's placeholder is not.
|
|
// Currently Safari 4 and Opera 11 have support only for the input placeholder
|
|
// Both tests are available in feature-detects/forms-placeholder.js
|
|
|
|
var inputattrs = 'autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ');
|
|
var attrs = {};
|
|
|
|
Modernizr.input = (function(props) {
|
|
for (var i = 0, len = props.length; i < len; i++) {
|
|
attrs[ props[i] ] = !!(props[i] in inputElem);
|
|
}
|
|
if (attrs.list) {
|
|
// safari false positive's on datalist: webk.it/74252
|
|
// see also github.com/Modernizr/Modernizr/issues/146
|
|
attrs.list = !!(createElement('datalist') && window.HTMLDataListElement);
|
|
}
|
|
return attrs;
|
|
})(inputattrs);
|
|
|
|
|
|
/**
|
|
* Object.prototype.toString can be used with every object and allows you to
|
|
* get its class easily. Abstracting it off of an object prevents situations
|
|
* where the toString property has been overridden
|
|
*
|
|
* @access private
|
|
* @function toStringFn
|
|
* @returns {function} An abstracted toString function
|
|
*/
|
|
var toStringFn = ({}).toString;
|
|
|
|
/*!
|
|
{
|
|
"name": "SVG SMIL animation",
|
|
"property": "smil",
|
|
"caniuse": "svg-smil",
|
|
"tags": ["svg"],
|
|
"notes": [{
|
|
"name": "W3C Spec",
|
|
"href": "https://www.w3.org/AudioVideo/"
|
|
}]
|
|
}
|
|
!*/
|
|
|
|
// SVG SMIL animation
|
|
Modernizr.addTest('smil', function() {
|
|
return !!document.createElementNS &&
|
|
/SVGAnimate/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'animate')));
|
|
});
|
|
|
|
|
|
// 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);
|