modernizr.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  1. /*!
  2. * modernizr v3.5.0
  3. * Build https://modernizr.com/download?-adownload-canvas-cssanimations-csstransforms-documentfragment-fullscreen-localstorage-svg-texttrackapi_track-todataurljpeg_todataurlpng_todataurlwebp-video-websockets-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.5.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. {
  73. "name": "SVG",
  74. "property": "svg",
  75. "caniuse": "svg",
  76. "tags": ["svg"],
  77. "authors": ["Erik Dahlstrom"],
  78. "polyfills": [
  79. "svgweb",
  80. "raphael",
  81. "amplesdk",
  82. "canvg",
  83. "svg-boilerplate",
  84. "sie",
  85. "dojogfx",
  86. "fabricjs"
  87. ]
  88. }
  89. !*/
  90. /* DOC
  91. Detects support for SVG in `<embed>` or `<object>` elements.
  92. */
  93. Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);
  94. /*!
  95. {
  96. "name": "WebSockets Support",
  97. "property": "websockets",
  98. "authors": ["Phread [fearphage]", "Mike Sherov [mikesherov]", "Burak Yigit Kaya [BYK]"],
  99. "caniuse": "websockets",
  100. "tags": ["html5"],
  101. "warnings": [
  102. "This test will reject any old version of WebSockets even if it is not prefixed such as in Safari 5.1"
  103. ],
  104. "notes": [{
  105. "name": "CLOSING State and Spec",
  106. "href": "https://www.w3.org/TR/websockets/#the-websocket-interface"
  107. }],
  108. "polyfills": [
  109. "sockjs",
  110. "socketio",
  111. "kaazing-websocket-gateway",
  112. "websocketjs",
  113. "atmosphere",
  114. "graceful-websocket",
  115. "portal",
  116. "datachannel"
  117. ]
  118. }
  119. !*/
  120. var supports = false;
  121. try {
  122. supports = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
  123. } catch (e) {}
  124. Modernizr.addTest('websockets', supports);
  125. /*!
  126. {
  127. "name": "Local Storage",
  128. "property": "localstorage",
  129. "caniuse": "namevalue-storage",
  130. "tags": ["storage"],
  131. "knownBugs": [],
  132. "notes": [],
  133. "warnings": [],
  134. "polyfills": [
  135. "joshuabell-polyfill",
  136. "cupcake",
  137. "storagepolyfill",
  138. "amplifyjs",
  139. "yui-cacheoffline"
  140. ]
  141. }
  142. !*/
  143. // In FF4, if disabled, window.localStorage should === null.
  144. // Normally, we could not test that directly and need to do a
  145. // `('localStorage' in window)` test first because otherwise Firefox will
  146. // throw bugzil.la/365772 if cookies are disabled
  147. // Similarly, in Chrome with "Block third-party cookies and site data" enabled,
  148. // attempting to access `window.sessionStorage` will throw an exception. crbug.com/357625
  149. // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
  150. // will throw the exception:
  151. // QUOTA_EXCEEDED_ERROR DOM Exception 22.
  152. // Peculiarly, getItem and removeItem calls do not throw.
  153. // Because we are forced to try/catch this, we'll go aggressive.
  154. // Just FWIW: IE8 Compat mode supports these features completely:
  155. // www.quirksmode.org/dom/html5.html
  156. // But IE8 doesn't support either with local files
  157. Modernizr.addTest('localstorage', function() {
  158. var mod = 'modernizr';
  159. try {
  160. localStorage.setItem(mod, mod);
  161. localStorage.removeItem(mod);
  162. return true;
  163. } catch (e) {
  164. return false;
  165. }
  166. });
  167. /**
  168. * is returns a boolean if the typeof an obj is exactly type.
  169. *
  170. * @access private
  171. * @function is
  172. * @param {*} obj - A thing we want to check the type of
  173. * @param {string} type - A string to compare the typeof against
  174. * @returns {boolean}
  175. */
  176. function is(obj, type) {
  177. return typeof obj === type;
  178. }
  179. ;
  180. /**
  181. * Run through all tests and detect their support in the current UA.
  182. *
  183. * @access private
  184. */
  185. function testRunner() {
  186. var featureNames;
  187. var feature;
  188. var aliasIdx;
  189. var result;
  190. var nameIdx;
  191. var featureName;
  192. var featureNameSplit;
  193. for (var featureIdx in tests) {
  194. if (tests.hasOwnProperty(featureIdx)) {
  195. featureNames = [];
  196. feature = tests[featureIdx];
  197. // run the test, throw the return value into the Modernizr,
  198. // then based on that boolean, define an appropriate className
  199. // and push it into an array of classes we'll join later.
  200. //
  201. // If there is no name, it's an 'async' test that is run,
  202. // but not directly added to the object. That should
  203. // be done with a post-run addTest call.
  204. if (feature.name) {
  205. featureNames.push(feature.name.toLowerCase());
  206. if (feature.options && feature.options.aliases && feature.options.aliases.length) {
  207. // Add all the aliases into the names list
  208. for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) {
  209. featureNames.push(feature.options.aliases[aliasIdx].toLowerCase());
  210. }
  211. }
  212. }
  213. // Run the test, or use the raw value if it's not a function
  214. result = is(feature.fn, 'function') ? feature.fn() : feature.fn;
  215. // Set each of the names on the Modernizr object
  216. for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) {
  217. featureName = featureNames[nameIdx];
  218. // Support dot properties as sub tests. We don't do checking to make sure
  219. // that the implied parent tests have been added. You must call them in
  220. // order (either in the test, or make the parent test a dependency).
  221. //
  222. // Cap it to TWO to make the logic simple and because who needs that kind of subtesting
  223. // hashtag famous last words
  224. featureNameSplit = featureName.split('.');
  225. if (featureNameSplit.length === 1) {
  226. Modernizr[featureNameSplit[0]] = result;
  227. } else {
  228. // cast to a Boolean, if not one already
  229. if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
  230. Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
  231. }
  232. Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result;
  233. }
  234. classes.push((result ? '' : 'no-') + featureNameSplit.join('-'));
  235. }
  236. }
  237. }
  238. }
  239. ;
  240. /**
  241. * docElement is a convenience wrapper to grab the root element of the document
  242. *
  243. * @access private
  244. * @returns {HTMLElement|SVGElement} The root element of the document
  245. */
  246. var docElement = document.documentElement;
  247. /*!
  248. {
  249. "name": "Document Fragment",
  250. "property": "documentfragment",
  251. "notes": [{
  252. "name": "W3C DOM Level 1 Reference",
  253. "href": "https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3"
  254. }, {
  255. "name": "SitePoint Reference",
  256. "href": "http://reference.sitepoint.com/javascript/DocumentFragment"
  257. }, {
  258. "name": "QuirksMode Compatibility Tables",
  259. "href": "http://www.quirksmode.org/m/w3c_core.html#t112"
  260. }],
  261. "authors": ["Ron Waldon (@jokeyrhyme)"],
  262. "knownBugs": ["false-positive on Blackberry 9500, see QuirksMode note"],
  263. "tags": []
  264. }
  265. !*/
  266. /* DOC
  267. Append multiple elements to the DOM within a single insertion.
  268. */
  269. Modernizr.addTest('documentfragment', function() {
  270. return 'createDocumentFragment' in document &&
  271. 'appendChild' in docElement;
  272. });
  273. /**
  274. * A convenience helper to check if the document we are running in is an SVG document
  275. *
  276. * @access private
  277. * @returns {boolean}
  278. */
  279. var isSVG = docElement.nodeName.toLowerCase() === 'svg';
  280. /**
  281. * setClasses takes an array of class names and adds them to the root element
  282. *
  283. * @access private
  284. * @function setClasses
  285. * @param {string[]} classes - Array of class names
  286. */
  287. // Pass in an and array of class names, e.g.:
  288. // ['no-webp', 'borderradius', ...]
  289. function setClasses(classes) {
  290. var className = docElement.className;
  291. var classPrefix = Modernizr._config.classPrefix || '';
  292. if (isSVG) {
  293. className = className.baseVal;
  294. }
  295. // Change `no-js` to `js` (independently of the `enableClasses` option)
  296. // Handle classPrefix on this too
  297. if (Modernizr._config.enableJSClass) {
  298. var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)');
  299. className = className.replace(reJS, '$1' + classPrefix + 'js$2');
  300. }
  301. if (Modernizr._config.enableClasses) {
  302. // Add the new classes
  303. className += ' ' + classPrefix + classes.join(' ' + classPrefix);
  304. if (isSVG) {
  305. docElement.className.baseVal = className;
  306. } else {
  307. docElement.className = className;
  308. }
  309. }
  310. }
  311. ;
  312. /**
  313. * createElement is a convenience wrapper around document.createElement. Since we
  314. * use createElement all over the place, this allows for (slightly) smaller code
  315. * as well as abstracting away issues with creating elements in contexts other than
  316. * HTML documents (e.g. SVG documents).
  317. *
  318. * @access private
  319. * @function createElement
  320. * @returns {HTMLElement|SVGElement} An HTML or SVG element
  321. */
  322. function createElement() {
  323. if (typeof document.createElement !== 'function') {
  324. // This is the case in IE7, where the type of createElement is "object".
  325. // For this reason, we cannot call apply() as Object is not a Function.
  326. return document.createElement(arguments[0]);
  327. } else if (isSVG) {
  328. return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]);
  329. } else {
  330. return document.createElement.apply(document, arguments);
  331. }
  332. }
  333. ;
  334. /*!
  335. {
  336. "name": "Canvas",
  337. "property": "canvas",
  338. "caniuse": "canvas",
  339. "tags": ["canvas", "graphics"],
  340. "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"]
  341. }
  342. !*/
  343. /* DOC
  344. Detects support for the `<canvas>` element for 2D drawing.
  345. */
  346. // On the S60 and BB Storm, getContext exists, but always returns undefined
  347. // so we actually have to call getContext() to verify
  348. // github.com/Modernizr/Modernizr/issues/issue/97/
  349. Modernizr.addTest('canvas', function() {
  350. var elem = createElement('canvas');
  351. return !!(elem.getContext && elem.getContext('2d'));
  352. });
  353. /*!
  354. {
  355. "name": "HTML5 Video",
  356. "property": "video",
  357. "caniuse": "video",
  358. "tags": ["html5"],
  359. "knownBugs": [
  360. "Without QuickTime, `Modernizr.video.h264` will be `undefined`; https://github.com/Modernizr/Modernizr/issues/546"
  361. ],
  362. "polyfills": [
  363. "html5media",
  364. "mediaelementjs",
  365. "sublimevideo",
  366. "videojs",
  367. "leanbackplayer",
  368. "videoforeverybody"
  369. ]
  370. }
  371. !*/
  372. /* DOC
  373. Detects support for the video element, as well as testing what types of content it supports.
  374. Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.:
  375. ```javascript
  376. Modernizr.video // true
  377. Modernizr.video.ogg // 'probably'
  378. ```
  379. */
  380. // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
  381. // thx to NielsLeenheer and zcorpan
  382. // Note: in some older browsers, "no" was a return value instead of empty string.
  383. // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
  384. // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
  385. Modernizr.addTest('video', function() {
  386. var elem = createElement('video');
  387. var bool = false;
  388. // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
  389. try {
  390. bool = !!elem.canPlayType
  391. if (bool) {
  392. bool = new Boolean(bool);
  393. bool.ogg = elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, '');
  394. // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
  395. bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, '');
  396. bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, '');
  397. bool.vp9 = elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/, '');
  398. bool.hls = elem.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/, '');
  399. }
  400. } catch (e) {}
  401. return bool;
  402. });
  403. /*!
  404. {
  405. "name": "a[download] Attribute",
  406. "property": "adownload",
  407. "caniuse" : "download",
  408. "tags": ["media", "attribute"],
  409. "builderAliases": ["a_download"],
  410. "notes": [{
  411. "name": "WhatWG Reference",
  412. "href": "https://developers.whatwg.org/links.html#downloading-resources"
  413. }]
  414. }
  415. !*/
  416. /* DOC
  417. When used on an `<a>`, this attribute signifies that the resource it points to should be downloaded by the browser rather than navigating to it.
  418. */
  419. Modernizr.addTest('adownload', !window.externalHost && 'download' in createElement('a'));
  420. /*!
  421. {
  422. "name": "canvas.toDataURL type support",
  423. "property": ["todataurljpeg", "todataurlpng", "todataurlwebp"],
  424. "tags": ["canvas"],
  425. "builderAliases": ["canvas_todataurl_type"],
  426. "async" : false,
  427. "notes": [{
  428. "name": "MDN article",
  429. "href": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement.toDataURL"
  430. }]
  431. }
  432. !*/
  433. var canvas = createElement('canvas');
  434. Modernizr.addTest('todataurljpeg', function() {
  435. return !!Modernizr.canvas && canvas.toDataURL('image/jpeg').indexOf('data:image/jpeg') === 0;
  436. });
  437. Modernizr.addTest('todataurlpng', function() {
  438. return !!Modernizr.canvas && canvas.toDataURL('image/png').indexOf('data:image/png') === 0;
  439. });
  440. Modernizr.addTest('todataurlwebp', function() {
  441. var supports = false;
  442. // firefox 3 throws an error when you use an "invalid" toDataUrl
  443. try {
  444. supports = !!Modernizr.canvas && canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
  445. } catch (e) {}
  446. return supports;
  447. });
  448. /*!
  449. {
  450. "name": "Track element and Timed Text Track",
  451. "property": ["texttrackapi", "track"],
  452. "tags": ["elem"],
  453. "builderAliases": ["elem_track"],
  454. "authors": ["Addy Osmani"],
  455. "notes": [{
  456. "name": "W3 track Element Spec",
  457. "href": "http://www.w3.org/TR/html5/video.html#the-track-element"
  458. },{
  459. "name": "W3 track API Spec",
  460. "href": "http://www.w3.org/TR/html5/media-elements.html#text-track-api"
  461. }],
  462. "warnings": ["While IE10 has implemented the track element, IE10 does not expose the underlying APIs to create timed text tracks by JS (really sad)"]
  463. }
  464. !*/
  465. Modernizr.addTest('texttrackapi', typeof (createElement('video').addTextTrack) === 'function');
  466. // a more strict test for track including UI support: document.createElement('track').kind === 'subtitles'
  467. Modernizr.addTest('track', 'kind' in createElement('track'));
  468. /**
  469. * cssToDOM takes a kebab-case string and converts it to camelCase
  470. * e.g. box-sizing -> boxSizing
  471. *
  472. * @access private
  473. * @function cssToDOM
  474. * @param {string} name - String name of kebab-case prop we want to convert
  475. * @returns {string} The camelCase version of the supplied name
  476. */
  477. function cssToDOM(name) {
  478. return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) {
  479. return m1 + m2.toUpperCase();
  480. }).replace(/^-/, '');
  481. }
  482. ;
  483. /**
  484. * If the browsers follow the spec, then they would expose vendor-specific styles as:
  485. * elem.style.WebkitBorderRadius
  486. * instead of something like the following (which is technically incorrect):
  487. * elem.style.webkitBorderRadius
  488. * WebKit ghosts their properties in lowercase but Opera & Moz do not.
  489. * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
  490. * erik.eae.net/archives/2008/03/10/21.48.10/
  491. * More here: github.com/Modernizr/Modernizr/issues/issue/21
  492. *
  493. * @access private
  494. * @returns {string} The string representing the vendor-specific style properties
  495. */
  496. var omPrefixes = 'Moz O ms Webkit';
  497. var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []);
  498. ModernizrProto._cssomPrefixes = cssomPrefixes;
  499. /**
  500. * atRule returns a given CSS property at-rule (eg @keyframes), possibly in
  501. * some prefixed form, or false, in the case of an unsupported rule
  502. *
  503. * @memberof Modernizr
  504. * @name Modernizr.atRule
  505. * @optionName Modernizr.atRule()
  506. * @optionProp atRule
  507. * @access public
  508. * @function atRule
  509. * @param {string} prop - String name of the @-rule to test for
  510. * @returns {string|boolean} The string representing the (possibly prefixed)
  511. * valid version of the @-rule, or `false` when it is unsupported.
  512. * @example
  513. * ```js
  514. * var keyframes = Modernizr.atRule('@keyframes');
  515. *
  516. * if (keyframes) {
  517. * // keyframes are supported
  518. * // could be `@-webkit-keyframes` or `@keyframes`
  519. * } else {
  520. * // keyframes === `false`
  521. * }
  522. * ```
  523. *
  524. */
  525. var atRule = function(prop) {
  526. var length = prefixes.length;
  527. var cssrule = window.CSSRule;
  528. var rule;
  529. if (typeof cssrule === 'undefined') {
  530. return undefined;
  531. }
  532. if (!prop) {
  533. return false;
  534. }
  535. // remove literal @ from beginning of provided property
  536. prop = prop.replace(/^@/, '');
  537. // CSSRules use underscores instead of dashes
  538. rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE';
  539. if (rule in cssrule) {
  540. return '@' + prop;
  541. }
  542. for (var i = 0; i < length; i++) {
  543. // prefixes gives us something like -o-, and we want O_
  544. var prefix = prefixes[i];
  545. var thisRule = prefix.toUpperCase() + '_' + rule;
  546. if (thisRule in cssrule) {
  547. return '@-' + prefix.toLowerCase() + '-' + prop;
  548. }
  549. }
  550. return false;
  551. };
  552. ModernizrProto.atRule = atRule;
  553. /**
  554. * List of JavaScript DOM values used for tests
  555. *
  556. * @memberof Modernizr
  557. * @name Modernizr._domPrefixes
  558. * @optionName Modernizr._domPrefixes
  559. * @optionProp domPrefixes
  560. * @access public
  561. * @example
  562. *
  563. * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather
  564. * than kebab-case properties, all properties are their Capitalized variant
  565. *
  566. * ```js
  567. * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ];
  568. * ```
  569. */
  570. var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []);
  571. ModernizrProto._domPrefixes = domPrefixes;
  572. /**
  573. * contains checks to see if a string contains another string
  574. *
  575. * @access private
  576. * @function contains
  577. * @param {string} str - The string we want to check for substrings
  578. * @param {string} substr - The substring we want to search the first string for
  579. * @returns {boolean}
  580. */
  581. function contains(str, substr) {
  582. return !!~('' + str).indexOf(substr);
  583. }
  584. ;
  585. /**
  586. * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill.
  587. *
  588. * @access private
  589. * @function fnBind
  590. * @param {function} fn - a function you want to change `this` reference to
  591. * @param {object} that - the `this` you want to call the function with
  592. * @returns {function} The wrapped version of the supplied function
  593. */
  594. function fnBind(fn, that) {
  595. return function() {
  596. return fn.apply(that, arguments);
  597. };
  598. }
  599. ;
  600. /**
  601. * testDOMProps is a generic DOM property test; if a browser supports
  602. * a certain property, it won't return undefined for it.
  603. *
  604. * @access private
  605. * @function testDOMProps
  606. * @param {array.<string>} props - An array of properties to test for
  607. * @param {object} obj - An object or Element you want to use to test the parameters again
  608. * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check
  609. * @returns {false|*} returns false if the prop is unsupported, otherwise the value that is supported
  610. */
  611. function testDOMProps(props, obj, elem) {
  612. var item;
  613. for (var i in props) {
  614. if (props[i] in obj) {
  615. // return the property name as a string
  616. if (elem === false) {
  617. return props[i];
  618. }
  619. item = obj[props[i]];
  620. // let's bind a function
  621. if (is(item, 'function')) {
  622. // bind to obj unless overriden
  623. return fnBind(item, elem || obj);
  624. }
  625. // return the unbound function or obj or value
  626. return item;
  627. }
  628. }
  629. return false;
  630. }
  631. ;
  632. /**
  633. * Create our "modernizr" element that we do most feature tests on.
  634. *
  635. * @access private
  636. */
  637. var modElem = {
  638. elem: createElement('modernizr')
  639. };
  640. // Clean up this element
  641. Modernizr._q.push(function() {
  642. delete modElem.elem;
  643. });
  644. var mStyle = {
  645. style: modElem.elem.style
  646. };
  647. // kill ref for gc, must happen before mod.elem is removed, so we unshift on to
  648. // the front of the queue.
  649. Modernizr._q.unshift(function() {
  650. delete mStyle.style;
  651. });
  652. /**
  653. * domToCSS takes a camelCase string and converts it to kebab-case
  654. * e.g. boxSizing -> box-sizing
  655. *
  656. * @access private
  657. * @function domToCSS
  658. * @param {string} name - String name of camelCase prop we want to convert
  659. * @returns {string} The kebab-case version of the supplied name
  660. */
  661. function domToCSS(name) {
  662. return name.replace(/([A-Z])/g, function(str, m1) {
  663. return '-' + m1.toLowerCase();
  664. }).replace(/^ms-/, '-ms-');
  665. }
  666. ;
  667. /**
  668. * wrapper around getComputedStyle, to fix issues with Firefox returning null when
  669. * called inside of a hidden iframe
  670. *
  671. * @access private
  672. * @function computedStyle
  673. * @param {HTMLElement|SVGElement} - The element we want to find the computed styles of
  674. * @param {string|null} [pseudoSelector]- An optional pseudo element selector (e.g. :before), of null if none
  675. * @returns {CSSStyleDeclaration}
  676. */
  677. function computedStyle(elem, pseudo, prop) {
  678. var result;
  679. if ('getComputedStyle' in window) {
  680. result = getComputedStyle.call(window, elem, pseudo);
  681. var console = window.console;
  682. if (result !== null) {
  683. if (prop) {
  684. result = result.getPropertyValue(prop);
  685. }
  686. } else {
  687. if (console) {
  688. var method = console.error ? 'error' : 'log';
  689. console[method].call(console, 'getComputedStyle returning null, its possible modernizr test results are inaccurate');
  690. }
  691. }
  692. } else {
  693. result = !pseudo && elem.currentStyle && elem.currentStyle[prop];
  694. }
  695. return result;
  696. }
  697. ;
  698. /**
  699. * getBody returns the body of a document, or an element that can stand in for
  700. * the body if a real body does not exist
  701. *
  702. * @access private
  703. * @function getBody
  704. * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an
  705. * artificially created element that stands in for the body
  706. */
  707. function getBody() {
  708. // After page load injecting a fake body doesn't work so check if body exists
  709. var body = document.body;
  710. if (!body) {
  711. // Can't use the real body create a fake one.
  712. body = createElement(isSVG ? 'svg' : 'body');
  713. body.fake = true;
  714. }
  715. return body;
  716. }
  717. ;
  718. /**
  719. * injectElementWithStyles injects an element with style element and some CSS rules
  720. *
  721. * @access private
  722. * @function injectElementWithStyles
  723. * @param {string} rule - String representing a css rule
  724. * @param {function} callback - A function that is used to test the injected element
  725. * @param {number} [nodes] - An integer representing the number of additional nodes you want injected
  726. * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
  727. * @returns {boolean}
  728. */
  729. function injectElementWithStyles(rule, callback, nodes, testnames) {
  730. var mod = 'modernizr';
  731. var style;
  732. var ret;
  733. var node;
  734. var docOverflow;
  735. var div = createElement('div');
  736. var body = getBody();
  737. if (parseInt(nodes, 10)) {
  738. // In order not to give false positives we create a node for each test
  739. // This also allows the method to scale for unspecified uses
  740. while (nodes--) {
  741. node = createElement('div');
  742. node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
  743. div.appendChild(node);
  744. }
  745. }
  746. style = createElement('style');
  747. style.type = 'text/css';
  748. style.id = 's' + mod;
  749. // 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.
  750. // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
  751. (!body.fake ? div : body).appendChild(style);
  752. body.appendChild(div);
  753. if (style.styleSheet) {
  754. style.styleSheet.cssText = rule;
  755. } else {
  756. style.appendChild(document.createTextNode(rule));
  757. }
  758. div.id = mod;
  759. if (body.fake) {
  760. //avoid crashing IE8, if background image is used
  761. body.style.background = '';
  762. //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
  763. body.style.overflow = 'hidden';
  764. docOverflow = docElement.style.overflow;
  765. docElement.style.overflow = 'hidden';
  766. docElement.appendChild(body);
  767. }
  768. ret = callback(div, rule);
  769. // If this is done after page load we don't want to remove the body so check if body exists
  770. if (body.fake) {
  771. body.parentNode.removeChild(body);
  772. docElement.style.overflow = docOverflow;
  773. // Trigger layout so kinetic scrolling isn't disabled in iOS6+
  774. // eslint-disable-next-line
  775. docElement.offsetHeight;
  776. } else {
  777. div.parentNode.removeChild(div);
  778. }
  779. return !!ret;
  780. }
  781. ;
  782. /**
  783. * nativeTestProps allows for us to use native feature detection functionality if available.
  784. * some prefixed form, or false, in the case of an unsupported rule
  785. *
  786. * @access private
  787. * @function nativeTestProps
  788. * @param {array} props - An array of property names
  789. * @param {string} value - A string representing the value we want to check via @supports
  790. * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise
  791. */
  792. // Accepts a list of property names and a single value
  793. // Returns `undefined` if native detection not available
  794. function nativeTestProps(props, value) {
  795. var i = props.length;
  796. // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface
  797. if ('CSS' in window && 'supports' in window.CSS) {
  798. // Try every prefixed variant of the property
  799. while (i--) {
  800. if (window.CSS.supports(domToCSS(props[i]), value)) {
  801. return true;
  802. }
  803. }
  804. return false;
  805. }
  806. // Otherwise fall back to at-rule (for Opera 12.x)
  807. else if ('CSSSupportsRule' in window) {
  808. // Build a condition string for every prefixed variant
  809. var conditionText = [];
  810. while (i--) {
  811. conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')');
  812. }
  813. conditionText = conditionText.join(' or ');
  814. return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) {
  815. return computedStyle(node, null, 'position') == 'absolute';
  816. });
  817. }
  818. return undefined;
  819. }
  820. ;
  821. // testProps is a generic CSS / DOM property test.
  822. // In testing support for a given CSS property, it's legit to test:
  823. // `elem.style[styleName] !== undefined`
  824. // If the property is supported it will return an empty string,
  825. // if unsupported it will return undefined.
  826. // We'll take advantage of this quick test and skip setting a style
  827. // on our modernizr element, but instead just testing undefined vs
  828. // empty string.
  829. // Property names can be provided in either camelCase or kebab-case.
  830. function testProps(props, prefixed, value, skipValueTest) {
  831. skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest;
  832. // Try native detect first
  833. if (!is(value, 'undefined')) {
  834. var result = nativeTestProps(props, value);
  835. if (!is(result, 'undefined')) {
  836. return result;
  837. }
  838. }
  839. // Otherwise do it properly
  840. var afterInit, i, propsLength, prop, before;
  841. // If we don't have a style element, that means we're running async or after
  842. // the core tests, so we'll need to create our own elements to use
  843. // inside of an SVG element, in certain browsers, the `style` element is only
  844. // defined for valid tags. Therefore, if `modernizr` does not have one, we
  845. // fall back to a less used element and hope for the best.
  846. // for strict XHTML browsers the hardly used samp element is used
  847. var elems = ['modernizr', 'tspan', 'samp'];
  848. while (!mStyle.style && elems.length) {
  849. afterInit = true;
  850. mStyle.modElem = createElement(elems.shift());
  851. mStyle.style = mStyle.modElem.style;
  852. }
  853. // Delete the objects if we created them.
  854. function cleanElems() {
  855. if (afterInit) {
  856. delete mStyle.style;
  857. delete mStyle.modElem;
  858. }
  859. }
  860. propsLength = props.length;
  861. for (i = 0; i < propsLength; i++) {
  862. prop = props[i];
  863. before = mStyle.style[prop];
  864. if (contains(prop, '-')) {
  865. prop = cssToDOM(prop);
  866. }
  867. if (mStyle.style[prop] !== undefined) {
  868. // If value to test has been passed in, do a set-and-check test.
  869. // 0 (integer) is a valid property value, so check that `value` isn't
  870. // undefined, rather than just checking it's truthy.
  871. if (!skipValueTest && !is(value, 'undefined')) {
  872. // Needs a try catch block because of old IE. This is slow, but will
  873. // be avoided in most cases because `skipValueTest` will be used.
  874. try {
  875. mStyle.style[prop] = value;
  876. } catch (e) {}
  877. // If the property value has changed, we assume the value used is
  878. // supported. If `value` is empty string, it'll fail here (because
  879. // it hasn't changed), which matches how browsers have implemented
  880. // CSS.supports()
  881. if (mStyle.style[prop] != before) {
  882. cleanElems();
  883. return prefixed == 'pfx' ? prop : true;
  884. }
  885. }
  886. // Otherwise just return true, or the property name if this is a
  887. // `prefixed()` call
  888. else {
  889. cleanElems();
  890. return prefixed == 'pfx' ? prop : true;
  891. }
  892. }
  893. }
  894. cleanElems();
  895. return false;
  896. }
  897. ;
  898. /**
  899. * testPropsAll tests a list of DOM properties we want to check against.
  900. * We specify literally ALL possible (known and/or likely) properties on
  901. * the element including the non-vendor prefixed one, for forward-
  902. * compatibility.
  903. *
  904. * @access private
  905. * @function testPropsAll
  906. * @param {string} prop - A string of the property to test for
  907. * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip
  908. * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against
  909. * @param {string} [value] - A string of a css value
  910. * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set
  911. * @returns {false|string} returns the string version of the property, or false if it is unsupported
  912. */
  913. function testPropsAll(prop, prefixed, elem, value, skipValueTest) {
  914. var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
  915. props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
  916. // did they call .prefixed('boxSizing') or are we just testing a prop?
  917. if (is(prefixed, 'string') || is(prefixed, 'undefined')) {
  918. return testProps(props, prefixed, value, skipValueTest);
  919. // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
  920. } else {
  921. props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
  922. return testDOMProps(props, prefixed, elem);
  923. }
  924. }
  925. // Modernizr.testAllProps() investigates whether a given style property,
  926. // or any of its vendor-prefixed variants, is recognized
  927. //
  928. // Note that the property names must be provided in the camelCase variant.
  929. // Modernizr.testAllProps('boxSizing')
  930. ModernizrProto.testAllProps = testPropsAll;
  931. /**
  932. * prefixed returns the prefixed or nonprefixed property name variant of your input
  933. *
  934. * @memberof Modernizr
  935. * @name Modernizr.prefixed
  936. * @optionName Modernizr.prefixed()
  937. * @optionProp prefixed
  938. * @access public
  939. * @function prefixed
  940. * @param {string} prop - String name of the property to test for
  941. * @param {object} [obj] - An object to test for the prefixed properties on
  942. * @param {HTMLElement} [elem] - An element used to test specific properties against
  943. * @returns {string|false} The string representing the (possibly prefixed) valid
  944. * version of the property, or `false` when it is unsupported.
  945. * @example
  946. *
  947. * Modernizr.prefixed takes a string css value in the DOM style camelCase (as
  948. * opposed to the css style kebab-case) form and returns the (possibly prefixed)
  949. * version of that property that the browser actually supports.
  950. *
  951. * For example, in older Firefox...
  952. * ```js
  953. * prefixed('boxSizing')
  954. * ```
  955. * returns 'MozBoxSizing'
  956. *
  957. * In newer Firefox, as well as any other browser that support the unprefixed
  958. * version would simply return `boxSizing`. Any browser that does not support
  959. * the property at all, it will return `false`.
  960. *
  961. * By default, prefixed is checked against a DOM element. If you want to check
  962. * for a property on another object, just pass it as a second argument
  963. *
  964. * ```js
  965. * var rAF = prefixed('requestAnimationFrame', window);
  966. *
  967. * raf(function() {
  968. * renderFunction();
  969. * })
  970. * ```
  971. *
  972. * Note that this will return _the actual function_ - not the name of the function.
  973. * If you need the actual name of the property, pass in `false` as a third argument
  974. *
  975. * ```js
  976. * var rAFProp = prefixed('requestAnimationFrame', window, false);
  977. *
  978. * rafProp === 'WebkitRequestAnimationFrame' // in older webkit
  979. * ```
  980. *
  981. * One common use case for prefixed is if you're trying to determine which transition
  982. * end event to bind to, you might do something like...
  983. * ```js
  984. * var transEndEventNames = {
  985. * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser
  986. * 'MozTransition' : 'transitionend', * only for FF < 15
  987. * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+
  988. * };
  989. *
  990. * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
  991. * ```
  992. *
  993. * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss).
  994. */
  995. var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) {
  996. if (prop.indexOf('@') === 0) {
  997. return atRule(prop);
  998. }
  999. if (prop.indexOf('-') != -1) {
  1000. // Convert kebab-case to camelCase
  1001. prop = cssToDOM(prop);
  1002. }
  1003. if (!obj) {
  1004. return testPropsAll(prop, 'pfx');
  1005. } else {
  1006. // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
  1007. return testPropsAll(prop, obj, elem);
  1008. }
  1009. };
  1010. /*!
  1011. {
  1012. "name": "Fullscreen API",
  1013. "property": "fullscreen",
  1014. "caniuse": "fullscreen",
  1015. "notes": [{
  1016. "name": "MDN documentation",
  1017. "href": "https://developer.mozilla.org/en/API/Fullscreen"
  1018. }],
  1019. "polyfills": ["screenfulljs"],
  1020. "builderAliases": ["fullscreen_api"]
  1021. }
  1022. !*/
  1023. /* DOC
  1024. Detects support for the ability to make the current website take over the user's entire screen
  1025. */
  1026. // github.com/Modernizr/Modernizr/issues/739
  1027. Modernizr.addTest('fullscreen', !!(prefixed('exitFullscreen', document, false) || prefixed('cancelFullScreen', document, false)));
  1028. /**
  1029. * testAllProps determines whether a given CSS property is supported in the browser
  1030. *
  1031. * @memberof Modernizr
  1032. * @name Modernizr.testAllProps
  1033. * @optionName Modernizr.testAllProps()
  1034. * @optionProp testAllProps
  1035. * @access public
  1036. * @function testAllProps
  1037. * @param {string} prop - String naming the property to test (either camelCase or kebab-case)
  1038. * @param {string} [value] - String of the value to test
  1039. * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection
  1040. * @example
  1041. *
  1042. * testAllProps determines whether a given CSS property, in some prefixed form,
  1043. * is supported by the browser.
  1044. *
  1045. * ```js
  1046. * testAllProps('boxSizing') // true
  1047. * ```
  1048. *
  1049. * It can optionally be given a CSS value in string form to test if a property
  1050. * value is valid
  1051. *
  1052. * ```js
  1053. * testAllProps('display', 'block') // true
  1054. * testAllProps('display', 'penguin') // false
  1055. * ```
  1056. *
  1057. * A boolean can be passed as a third parameter to skip the value check when
  1058. * native detection (@supports) isn't available.
  1059. *
  1060. * ```js
  1061. * testAllProps('shapeOutside', 'content-box', true);
  1062. * ```
  1063. */
  1064. function testAllProps(prop, value, skipValueTest) {
  1065. return testPropsAll(prop, undefined, undefined, value, skipValueTest);
  1066. }
  1067. ModernizrProto.testAllProps = testAllProps;
  1068. /*!
  1069. {
  1070. "name": "CSS Animations",
  1071. "property": "cssanimations",
  1072. "caniuse": "css-animation",
  1073. "polyfills": ["transformie", "csssandpaper"],
  1074. "tags": ["css"],
  1075. "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"],
  1076. "notes": [{
  1077. "name" : "Article: 'Dispelling the Android CSS animation myths'",
  1078. "href": "https://goo.gl/OGw5Gm"
  1079. }]
  1080. }
  1081. !*/
  1082. /* DOC
  1083. Detects whether or not elements can be animated using CSS
  1084. */
  1085. Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true));
  1086. /*!
  1087. {
  1088. "name": "CSS Transforms",
  1089. "property": "csstransforms",
  1090. "caniuse": "transforms2d",
  1091. "tags": ["css"]
  1092. }
  1093. !*/
  1094. Modernizr.addTest('csstransforms', function() {
  1095. // Android < 3.0 is buggy, so we sniff and blacklist
  1096. // http://git.io/hHzL7w
  1097. return navigator.userAgent.indexOf('Android 2.') === -1 &&
  1098. testAllProps('transform', 'scale(1)', true);
  1099. });
  1100. // Run each test
  1101. testRunner();
  1102. // Remove the "no-js" class if it exists
  1103. setClasses(classes);
  1104. delete ModernizrProto.addTest;
  1105. delete ModernizrProto.addAsyncTest;
  1106. // Run the things that are supposed to run after the tests
  1107. for (var i = 0; i < Modernizr._q.length; i++) {
  1108. Modernizr._q[i]();
  1109. }
  1110. // Leak Modernizr namespace
  1111. window.Modernizr = Modernizr;
  1112. ;
  1113. })(window, document);