|
|
|
|
@ -0,0 +1,533 @@
|
|
|
|
|
/*!
|
|
|
|
|
* modernizr v3.5.0
|
|
|
|
|
* Build https://modernizr.com/download?-touchevents-setclasses-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 classes = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var tests = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* ModernizrProto is the constructor for Modernizr
|
|
|
|
|
*
|
|
|
|
|
* @class
|
|
|
|
|
* @access public
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
var ModernizrProto = {
|
|
|
|
|
// The current version, dummy
|
|
|
|
|
_version: '3.5.0',
|
|
|
|
|
|
|
|
|
|
// Any settings that don't work as separate modules
|
|
|
|
|
// can go in here as configuration.
|
|
|
|
|
_config: {
|
|
|
|
|
'classPrefix': '',
|
|
|
|
|
'enableClasses': true,
|
|
|
|
|
'enableJSClass': true,
|
|
|
|
|
'usePrefixes': true
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Queue of tests
|
|
|
|
|
_q: [],
|
|
|
|
|
|
|
|
|
|
// Stub these for people who are listening
|
|
|
|
|
on: function(test, cb) {
|
|
|
|
|
// I don't really think people should do this, but we can
|
|
|
|
|
// safe guard it a bit.
|
|
|
|
|
// -- NOTE:: this gets WAY overridden in src/addTest for actual async tests.
|
|
|
|
|
// This is in case people listen to synchronous tests. I would leave it out,
|
|
|
|
|
// but the code to *disallow* sync tests in the real version of this
|
|
|
|
|
// function is actually larger than this.
|
|
|
|
|
var self = this;
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
cb(self[test]);
|
|
|
|
|
}, 0);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addTest: function(name, fn, options) {
|
|
|
|
|
tests.push({name: name, fn: fn, options: options});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addAsyncTest: function(fn) {
|
|
|
|
|
tests.push({name: null, fn: fn});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fake some of Object.create so we can force non test results to be non "own" properties.
|
|
|
|
|
var Modernizr = function() {};
|
|
|
|
|
Modernizr.prototype = ModernizrProto;
|
|
|
|
|
|
|
|
|
|
// Leak modernizr globally when you `require` it rather than force it here.
|
|
|
|
|
// Overwrite name so constructor name is nicer :D
|
|
|
|
|
Modernizr = new Modernizr();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* is returns a boolean if the typeof an obj is exactly type.
|
|
|
|
|
*
|
|
|
|
|
* @access private
|
|
|
|
|
* @function is
|
|
|
|
|
* @param {*} obj - A thing we want to check the type of
|
|
|
|
|
* @param {string} type - A string to compare the typeof against
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
function is(obj, type) {
|
|
|
|
|
return typeof obj === type;
|
|
|
|
|
}
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Run through all tests and detect their support in the current UA.
|
|
|
|
|
*
|
|
|
|
|
* @access private
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
function testRunner() {
|
|
|
|
|
var featureNames;
|
|
|
|
|
var feature;
|
|
|
|
|
var aliasIdx;
|
|
|
|
|
var result;
|
|
|
|
|
var nameIdx;
|
|
|
|
|
var featureName;
|
|
|
|
|
var featureNameSplit;
|
|
|
|
|
|
|
|
|
|
for (var featureIdx in tests) {
|
|
|
|
|
if (tests.hasOwnProperty(featureIdx)) {
|
|
|
|
|
featureNames = [];
|
|
|
|
|
feature = tests[featureIdx];
|
|
|
|
|
// run the test, throw the return value into the Modernizr,
|
|
|
|
|
// then based on that boolean, define an appropriate className
|
|
|
|
|
// and push it into an array of classes we'll join later.
|
|
|
|
|
//
|
|
|
|
|
// If there is no name, it's an 'async' test that is run,
|
|
|
|
|
// but not directly added to the object. That should
|
|
|
|
|
// be done with a post-run addTest call.
|
|
|
|
|
if (feature.name) {
|
|
|
|
|
featureNames.push(feature.name.toLowerCase());
|
|
|
|
|
|
|
|
|
|
if (feature.options && feature.options.aliases && feature.options.aliases.length) {
|
|
|
|
|
// Add all the aliases into the names list
|
|
|
|
|
for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) {
|
|
|
|
|
featureNames.push(feature.options.aliases[aliasIdx].toLowerCase());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run the test, or use the raw value if it's not a function
|
|
|
|
|
result = is(feature.fn, 'function') ? feature.fn() : feature.fn;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set each of the names on the Modernizr object
|
|
|
|
|
for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) {
|
|
|
|
|
featureName = featureNames[nameIdx];
|
|
|
|
|
// Support dot properties as sub tests. We don't do checking to make sure
|
|
|
|
|
// that the implied parent tests have been added. You must call them in
|
|
|
|
|
// order (either in the test, or make the parent test a dependency).
|
|
|
|
|
//
|
|
|
|
|
// Cap it to TWO to make the logic simple and because who needs that kind of subtesting
|
|
|
|
|
// hashtag famous last words
|
|
|
|
|
featureNameSplit = featureName.split('.');
|
|
|
|
|
|
|
|
|
|
if (featureNameSplit.length === 1) {
|
|
|
|
|
Modernizr[featureNameSplit[0]] = result;
|
|
|
|
|
} else {
|
|
|
|
|
// cast to a Boolean, if not one already
|
|
|
|
|
if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
|
|
|
|
|
Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
classes.push((result ? '' : 'no-') + featureNameSplit.join('-'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* docElement is a convenience wrapper to grab the root element of the document
|
|
|
|
|
*
|
|
|
|
|
* @access private
|
|
|
|
|
* @returns {HTMLElement|SVGElement} The root element of the document
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
var docElement = document.documentElement;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
className += ' ' + classPrefix + classes.join(' ' + classPrefix);
|
|
|
|
|
if (isSVG) {
|
|
|
|
|
docElement.className.baseVal = className;
|
|
|
|
|
} else {
|
|
|
|
|
docElement.className = className;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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);
|