modernizr.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. /*!
  2. * modernizr v3.6.0
  3. * Build https://modernizr.com/download?-mq-setclasses-dontmin
  4. *
  5. * Copyright (c)
  6. * Faruk Ates
  7. * Paul Irish
  8. * Alex Sexton
  9. * Ryan Seddon
  10. * Patrick Kettner
  11. * Stu Cox
  12. * Richard Herrera
  13. * MIT License
  14. */
  15. /*
  16. * Modernizr tests which native CSS3 and HTML5 features are available in the
  17. * current UA and makes the results available to you in two ways: as properties on
  18. * a global `Modernizr` object, and as classes on the `<html>` element. This
  19. * information allows you to progressively enhance your pages with a granular level
  20. * of control over the experience.
  21. */
  22. ;(function(window, document, undefined){
  23. var classes = [];
  24. var tests = [];
  25. /**
  26. *
  27. * ModernizrProto is the constructor for Modernizr
  28. *
  29. * @class
  30. * @access public
  31. */
  32. var ModernizrProto = {
  33. // The current version, dummy
  34. _version: '3.6.0',
  35. // Any settings that don't work as separate modules
  36. // can go in here as configuration.
  37. _config: {
  38. 'classPrefix': '',
  39. 'enableClasses': true,
  40. 'enableJSClass': true,
  41. 'usePrefixes': true
  42. },
  43. // Queue of tests
  44. _q: [],
  45. // Stub these for people who are listening
  46. on: function(test, cb) {
  47. // I don't really think people should do this, but we can
  48. // safe guard it a bit.
  49. // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests.
  50. // This is in case people listen to synchronous tests. I would leave it out,
  51. // but the code to *disallow* sync tests in the real version of this
  52. // function is actually larger than this.
  53. var self = this;
  54. setTimeout(function() {
  55. cb(self[test]);
  56. }, 0);
  57. },
  58. addTest: function(name, fn, options) {
  59. tests.push({name: name, fn: fn, options: options});
  60. },
  61. addAsyncTest: function(fn) {
  62. tests.push({name: null, fn: fn});
  63. }
  64. };
  65. // Fake some of Object.create so we can force non test results to be non "own" properties.
  66. var Modernizr = function() {};
  67. Modernizr.prototype = ModernizrProto;
  68. // Leak modernizr globally when you `require` it rather than force it here.
  69. // Overwrite name so constructor name is nicer :D
  70. Modernizr = new Modernizr();
  71. /**
  72. * is returns a boolean if the typeof an obj is exactly type.
  73. *
  74. * @access private
  75. * @function is
  76. * @param {*} obj - A thing we want to check the type of
  77. * @param {string} type - A string to compare the typeof against
  78. * @returns {boolean}
  79. */
  80. function is(obj, type) {
  81. return typeof obj === type;
  82. }
  83. ;
  84. /**
  85. * Run through all tests and detect their support in the current UA.
  86. *
  87. * @access private
  88. */
  89. function testRunner() {
  90. var featureNames;
  91. var feature;
  92. var aliasIdx;
  93. var result;
  94. var nameIdx;
  95. var featureName;
  96. var featureNameSplit;
  97. for (var featureIdx in tests) {
  98. if (tests.hasOwnProperty(featureIdx)) {
  99. featureNames = [];
  100. feature = tests[featureIdx];
  101. // run the test, throw the return value into the Modernizr,
  102. // then based on that boolean, define an appropriate className
  103. // and push it into an array of classes we'll join later.
  104. //
  105. // If there is no name, it's an 'async' test that is run,
  106. // but not directly added to the object. That should
  107. // be done with a post-run addTest call.
  108. if (feature.name) {
  109. featureNames.push(feature.name.toLowerCase());
  110. if (feature.options && feature.options.aliases && feature.options.aliases.length) {
  111. // Add all the aliases into the names list
  112. for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) {
  113. featureNames.push(feature.options.aliases[aliasIdx].toLowerCase());
  114. }
  115. }
  116. }
  117. // Run the test, or use the raw value if it's not a function
  118. result = is(feature.fn, 'function') ? feature.fn() : feature.fn;
  119. // Set each of the names on the Modernizr object
  120. for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) {
  121. featureName = featureNames[nameIdx];
  122. // Support dot properties as sub tests. We don't do checking to make sure
  123. // that the implied parent tests have been added. You must call them in
  124. // order (either in the test, or make the parent test a dependency).
  125. //
  126. // Cap it to TWO to make the logic simple and because who needs that kind of subtesting
  127. // hashtag famous last words
  128. featureNameSplit = featureName.split('.');
  129. if (featureNameSplit.length === 1) {
  130. Modernizr[featureNameSplit[0]] = result;
  131. } else {
  132. // cast to a Boolean, if not one already
  133. if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
  134. Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
  135. }
  136. Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result;
  137. }
  138. classes.push((result ? '' : 'no-') + featureNameSplit.join('-'));
  139. }
  140. }
  141. }
  142. }
  143. ;
  144. /**
  145. * docElement is a convenience wrapper to grab the root element of the document
  146. *
  147. * @access private
  148. * @returns {HTMLElement|SVGElement} The root element of the document
  149. */
  150. var docElement = document.documentElement;
  151. /**
  152. * A convenience helper to check if the document we are running in is an SVG document
  153. *
  154. * @access private
  155. * @returns {boolean}
  156. */
  157. var isSVG = docElement.nodeName.toLowerCase() === 'svg';
  158. /**
  159. * setClasses takes an array of class names and adds them to the root element
  160. *
  161. * @access private
  162. * @function setClasses
  163. * @param {string[]} classes - Array of class names
  164. */
  165. // Pass in an and array of class names, e.g.:
  166. // ['no-webp', 'borderradius', ...]
  167. function setClasses(classes) {
  168. var className = docElement.className;
  169. var classPrefix = Modernizr._config.classPrefix || '';
  170. if (isSVG) {
  171. className = className.baseVal;
  172. }
  173. // Change `no-js` to `js` (independently of the `enableClasses` option)
  174. // Handle classPrefix on this too
  175. if (Modernizr._config.enableJSClass) {
  176. var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)');
  177. className = className.replace(reJS, '$1' + classPrefix + 'js$2');
  178. }
  179. if (Modernizr._config.enableClasses) {
  180. // Add the new classes
  181. className += ' ' + classPrefix + classes.join(' ' + classPrefix);
  182. if (isSVG) {
  183. docElement.className.baseVal = className;
  184. } else {
  185. docElement.className = className;
  186. }
  187. }
  188. }
  189. ;
  190. /**
  191. * createElement is a convenience wrapper around document.createElement. Since we
  192. * use createElement all over the place, this allows for (slightly) smaller code
  193. * as well as abstracting away issues with creating elements in contexts other than
  194. * HTML documents (e.g. SVG documents).
  195. *
  196. * @access private
  197. * @function createElement
  198. * @returns {HTMLElement|SVGElement} An HTML or SVG element
  199. */
  200. function createElement() {
  201. if (typeof document.createElement !== 'function') {
  202. // This is the case in IE7, where the type of createElement is "object".
  203. // For this reason, we cannot call apply() as Object is not a Function.
  204. return document.createElement(arguments[0]);
  205. } else if (isSVG) {
  206. return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]);
  207. } else {
  208. return document.createElement.apply(document, arguments);
  209. }
  210. }
  211. ;
  212. /**
  213. * getBody returns the body of a document, or an element that can stand in for
  214. * the body if a real body does not exist
  215. *
  216. * @access private
  217. * @function getBody
  218. * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an
  219. * artificially created element that stands in for the body
  220. */
  221. function getBody() {
  222. // After page load injecting a fake body doesn't work so check if body exists
  223. var body = document.body;
  224. if (!body) {
  225. // Can't use the real body create a fake one.
  226. body = createElement(isSVG ? 'svg' : 'body');
  227. body.fake = true;
  228. }
  229. return body;
  230. }
  231. ;
  232. /**
  233. * injectElementWithStyles injects an element with style element and some CSS rules
  234. *
  235. * @access private
  236. * @function injectElementWithStyles
  237. * @param {string} rule - String representing a css rule
  238. * @param {function} callback - A function that is used to test the injected element
  239. * @param {number} [nodes] - An integer representing the number of additional nodes you want injected
  240. * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
  241. * @returns {boolean}
  242. */
  243. function injectElementWithStyles(rule, callback, nodes, testnames) {
  244. var mod = 'modernizr';
  245. var style;
  246. var ret;
  247. var node;
  248. var docOverflow;
  249. var div = createElement('div');
  250. var body = getBody();
  251. if (parseInt(nodes, 10)) {
  252. // In order not to give false positives we create a node for each test
  253. // This also allows the method to scale for unspecified uses
  254. while (nodes--) {
  255. node = createElement('div');
  256. node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
  257. div.appendChild(node);
  258. }
  259. }
  260. style = createElement('style');
  261. style.type = 'text/css';
  262. style.id = 's' + mod;
  263. // 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.
  264. // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
  265. (!body.fake ? div : body).appendChild(style);
  266. body.appendChild(div);
  267. if (style.styleSheet) {
  268. style.styleSheet.cssText = rule;
  269. } else {
  270. style.appendChild(document.createTextNode(rule));
  271. }
  272. div.id = mod;
  273. if (body.fake) {
  274. //avoid crashing IE8, if background image is used
  275. body.style.background = '';
  276. //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
  277. body.style.overflow = 'hidden';
  278. docOverflow = docElement.style.overflow;
  279. docElement.style.overflow = 'hidden';
  280. docElement.appendChild(body);
  281. }
  282. ret = callback(div, rule);
  283. // If this is done after page load we don't want to remove the body so check if body exists
  284. if (body.fake) {
  285. body.parentNode.removeChild(body);
  286. docElement.style.overflow = docOverflow;
  287. // Trigger layout so kinetic scrolling isn't disabled in iOS6+
  288. // eslint-disable-next-line
  289. docElement.offsetHeight;
  290. } else {
  291. div.parentNode.removeChild(div);
  292. }
  293. return !!ret;
  294. }
  295. ;
  296. /**
  297. * Modernizr.mq tests a given media query, live against the current state of the window
  298. * adapted from matchMedia polyfill by Scott Jehl and Paul Irish
  299. * gist.github.com/786768
  300. *
  301. * @memberof Modernizr
  302. * @name Modernizr.mq
  303. * @optionName Modernizr.mq()
  304. * @optionProp mq
  305. * @access public
  306. * @function mq
  307. * @param {string} mq - String of the media query we want to test
  308. * @returns {boolean}
  309. * @example
  310. * Modernizr.mq allows for you to programmatically check if the current browser
  311. * window state matches a media query.
  312. *
  313. * ```js
  314. * var query = Modernizr.mq('(min-width: 900px)');
  315. *
  316. * if (query) {
  317. * // the browser window is larger than 900px
  318. * }
  319. * ```
  320. *
  321. * Only valid media queries are supported, therefore you must always include values
  322. * with your media query
  323. *
  324. * ```js
  325. * // good
  326. * Modernizr.mq('(min-width: 900px)');
  327. *
  328. * // bad
  329. * Modernizr.mq('min-width');
  330. * ```
  331. *
  332. * If you would just like to test that media queries are supported in general, use
  333. *
  334. * ```js
  335. * Modernizr.mq('only all'); // true if MQ are supported, false if not
  336. * ```
  337. *
  338. *
  339. * Note that if the browser does not support media queries (e.g. old IE) mq will
  340. * always return false.
  341. */
  342. var mq = (function() {
  343. var matchMedia = window.matchMedia || window.msMatchMedia;
  344. if (matchMedia) {
  345. return function(mq) {
  346. var mql = matchMedia(mq);
  347. return mql && mql.matches || false;
  348. };
  349. }
  350. return function(mq) {
  351. var bool = false;
  352. injectElementWithStyles('@media ' + mq + ' { #modernizr { position: absolute; } }', function(node) {
  353. bool = (window.getComputedStyle ?
  354. window.getComputedStyle(node, null) :
  355. node.currentStyle).position == 'absolute';
  356. });
  357. return bool;
  358. };
  359. })();
  360. ModernizrProto.mq = mq;
  361. // Run each test
  362. testRunner();
  363. // Remove the "no-js" class if it exists
  364. setClasses(classes);
  365. delete ModernizrProto.addTest;
  366. delete ModernizrProto.addAsyncTest;
  367. // Run the things that are supposed to run after the tests
  368. for (var i = 0; i < Modernizr._q.length; i++) {
  369. Modernizr._q[i]();
  370. }
  371. // Leak Modernizr namespace
  372. window.Modernizr = Modernizr;
  373. ;
  374. })(window, document);