1/*! 2 3 * modernizr v3.3.1 4 5 * Build https://modernizr.com/download?-blobconstructor-canvas-cookies-csshairline-csspositionsticky-cssvhunit-cssvmaxunit-cssvminunit-cssvwunit-flexbox-flexboxlegacy-flexboxtweener-flexwrap-notification-svg-touchevents-video-videoautoplay-hasevent-setclasses-dontmin 6 7 * 8 9 * Copyright (c) 10 11 * Faruk Ates 12 13 * Paul Irish 14 15 * Alex Sexton 16 17 * Ryan Seddon 18 19 * Patrick Kettner 20 21 * Stu Cox 22 23 * Richard Herrera 24 25 26 27 * MIT License 28 29 */ 30 31 32 33/* 34 35 * Modernizr tests which native CSS3 and HTML5 features are available in the 36 37 * current UA and makes the results available to you in two ways: as properties on 38 39 * a global `Modernizr` object, and as classes on the `<html>` element. This 40 41 * information allows you to progressively enhance your pages with a granular level 42 43 * of control over the experience. 44 45*/ 46 47 48 49;(function(window, document, undefined){ 50 51 var classes = []; 52 53 54 55 56 57 var tests = []; 58 59 60 61 62 63 /** 64 65 * 66 67 * ModernizrProto is the constructor for Modernizr 68 69 * 70 71 * @class 72 73 * @access public 74 75 */ 76 77 78 79 var ModernizrProto = { 80 81 // The current version, dummy 82 83 _version: '3.3.1', 84 85 86 87 // Any settings that don't work as separate modules 88 89 // can go in here as configuration. 90 91 _config: { 92 93 'classPrefix': '', 94 95 'enableClasses': true, 96 97 'enableJSClass': true, 98 99 'usePrefixes': true 100 101 }, 102 103 104 105 // Queue of tests 106 107 _q: [], 108 109 110 111 // Stub these for people who are listening 112 113 on: function(test, cb) { 114 115 // I don't really think people should do this, but we can 116 117 // safe guard it a bit. 118 119 // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 120 121 // This is in case people listen to synchronous tests. I would leave it out, 122 123 // but the code to *disallow* sync tests in the real version of this 124 125 // function is actually larger than this. 126 127 var self = this; 128 129 setTimeout(function() { 130 131 cb(self[test]); 132 133 }, 0); 134 135 }, 136 137 138 139 addTest: function(name, fn, options) { 140 141 tests.push({name: name, fn: fn, options: options}); 142 143 }, 144 145 146 147 addAsyncTest: function(fn) { 148 149 tests.push({name: null, fn: fn}); 150 151 } 152 153 }; 154 155 156 157 158 159 160 161 // Fake some of Object.create so we can force non test results to be non "own" properties. 162 163 var Modernizr = function() {}; 164 165 Modernizr.prototype = ModernizrProto; 166 167 168 169 // Leak modernizr globally when you `require` it rather than force it here. 170 171 // Overwrite name so constructor name is nicer :D 172 173 Modernizr = new Modernizr(); 174 175 176 177 178 179/*! 180 181{ 182 183 "name": "Blob constructor", 184 185 "property": "blobconstructor", 186 187 "aliases": ["blob-constructor"], 188 189 "builderAliases": ["blob_constructor"], 190 191 "caniuse": "blobbuilder", 192 193 "notes": [{ 194 195 "name": "W3C spec", 196 197 "href": "https://w3c.github.io/FileAPI/#constructorBlob" 198 199 }], 200 201 "polyfills": ["blobjs"] 202 203} 204 205!*/ 206 207/* DOC 208 209Detects support for the Blob constructor, for creating file-like objects of immutable, raw data. 210 211*/ 212 213 214 215 Modernizr.addTest('blobconstructor', function() { 216 217 try { 218 219 return !!new Blob(); 220 221 } catch (e) { 222 223 return false; 224 225 } 226 227 }, { 228 229 aliases: ['blob-constructor'] 230 231 }); 232 233 234 235/*! 236 237{ 238 239 "name": "Cookies", 240 241 "property": "cookies", 242 243 "tags": ["storage"], 244 245 "authors": ["tauren"] 246 247} 248 249!*/ 250 251/* DOC 252 253Detects whether cookie support is enabled. 254 255*/ 256 257 258 259 // https://github.com/Modernizr/Modernizr/issues/191 260 261 262 263 Modernizr.addTest('cookies', function() { 264 265 // navigator.cookieEnabled cannot detect custom or nuanced cookie blocking 266 267 // configurations. For example, when blocking cookies via the Advanced 268 269 // Privacy Settings in IE9, it always returns true. And there have been 270 271 // issues in the past with site-specific exceptions. 272 273 // Don't rely on it. 274 275 276 277 // try..catch because some in situations `document.cookie` is exposed but throws a 278 279 // SecurityError if you try to access it; e.g. documents created from data URIs 280 281 // or in sandboxed iframes (depending on flags/context) 282 283 try { 284 285 // Create cookie 286 287 document.cookie = 'cookietest=1'; 288 289 var ret = document.cookie.indexOf('cookietest=') != -1; 290 291 // Delete cookie 292 293 document.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'; 294 295 return ret; 296 297 } 298 299 catch (e) { 300 301 return false;
302 303 } 304 305 }); 306 307 308 309/*! 310 311{ 312 313 "name": "Notification", 314 315 "property": "notification", 316 317 "caniuse": "notifications", 318 319 "authors": ["Theodoor van Donge", "Hendrik Beskow"], 320 321 "notes": [{ 322 323 "name": "HTML5 Rocks tutorial", 324 325 "href": "http://www.html5rocks.com/en/tutorials/notifications/quick/" 326 327 },{ 328 329 "name": "W3C spec", 330 331 "href": "https://www.w3.org/TR/notifications/" 332 333 }, { 334 335 "name": "Changes in Chrome to Notifications API due to Service Worker Push Notifications", 336 337 "href": "https://developers.google.com/web/updates/2015/05/Notifying-you-of-notificiation-changes" 338 339 }], 340 341 "knownBugs": [ 342 343 "Possibility of false-positive on Chrome for Android if permissions we're granted for a website prior to Chrome 44." 344 345 ], 346 347 "polyfills": ["desktop-notify", "html5-notifications"] 348 349} 350 351!*/ 352 353/* DOC 354 355Detects support for the Notifications API 356 357*/ 358 359 360 361 Modernizr.addTest('notification', function() { 362 363 if (!window.Notification || !window.Notification.requestPermission) { 364 365 return false; 366 367 } 368 369 // if permission is already granted, assume support 370 371 if (window.Notification.permission === 'granted') { 372 373 return true; 374 375 } 376 377 378 379 try { 380 381 new window.Notification(''); 382 383 } catch (e) { 384 385 if (e.name === 'TypeError') { 386 387 return false; 388 389 } 390 391 } 392 393 394 395 return true; 396 397 }); 398 399 400 401/*! 402 403{ 404 405 "name": "SVG", 406 407 "property": "svg", 408 409 "caniuse": "svg", 410 411 "tags": ["svg"], 412 413 "authors": ["Erik Dahlstrom"], 414 415 "polyfills": [ 416 417 "svgweb", 418 419 "raphael", 420 421 "amplesdk", 422 423 "canvg", 424 425 "svg-boilerplate", 426 427 "sie", 428 429 "dojogfx", 430 431 "fabricjs" 432 433 ] 434 435} 436 437!*/ 438 439/* DOC 440 441Detects support for SVG in `<embed>` or `<object>` elements. 442 443*/ 444 445 446 447 Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect); 448 449 450 451 452 453 /** 454 455 * is returns a boolean if the typeof an obj is exactly type. 456 457 * 458 459 * @access private 460 461 * @function is 462 463 * @param {*} obj - A thing we want to check the type of 464 465 * @param {string} type - A string to compare the typeof against 466 467 * @returns {boolean} 468 469 */ 470 471 472 473 function is(obj, type) { 474 475 return typeof obj === type; 476 477 } 478 479 ; 480 481 482 483 /** 484 485 * Run through all tests and detect their support in the current UA. 486 487 * 488 489 * @access private 490 491 */ 492 493 494 495 function testRunner() { 496 497 var featureNames; 498 499 var feature; 500 501 var aliasIdx; 502 503 var result; 504 505 var nameIdx; 506 507 var featureName; 508 509 var featureNameSplit; 510 511 512 513 for (var featureIdx in tests) { 514 515 if (tests.hasOwnProperty(featureIdx)) { 516 517 featureNames = []; 518 519 feature = tests[featureIdx]; 520 521 // run the test, throw the return value into the Modernizr, 522 523 // then based on that boolean, define an appropriate className 524 525 // and push it into an array of classes we'll join later. 526 527 // 528 529 // If there is no name, it's an 'async' test that is run, 530 531 // but not directly added to the object. That should 532 533 // be done with a post-run addTest call. 534 535 if (feature.name) { 536 537 featureNames.push(feature.name.toLowerCase()); 538 539 540 541 if (feature.options && feature.options.aliases && feature.options.aliases.length) { 542 543 // Add all the aliases into the names list 544 545 for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 546 547 featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 548 549 } 550 551 } 552 553 } 554 555 556 557 // Run the test, or use the raw value if it's not a function 558 559 result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 560 561 562 563 564 565 // Set each of the names on the Modernizr object 566 567 for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 568 569 featureName = featureNames[nameIdx]; 570 571 // Support dot properties as sub tests. We don't do checking to make sure 572 573 // that the implied parent tests have been added. You must call them in 574 575 // order (either in the test, or make the parent test a dependency). 576 577 // 578 579 // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 580 581 // hashtag famous last words 582 583 featureNameSplit = featureName.split('.'); 584 585 586 587 if (featureNameSplit.length === 1) { 588 589 Modernizr[featureNameSplit[0]] = result; 590 591 } else { 592 593 // cast to a Boolean, if not one already 594 595 /* jshint -W053 */ 596 597 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 598 599 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 600 601 } 602 603 604 605 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 606 607 } 608 609 610 611 classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 612 613 } 614 615 } 616 617 } 618 619 } 620 621 ; 622 623 624 625 /** 626 627 * docElement is a convenience wrapper to grab the root element of the document 628 629 * 630 631 * @access private 632 633 * @returns {HTMLElement|SVGElement} The root element of the document 634 635 */ 636 637 638 639 var docElement = document.documentElement; 640 641 642 643 644 645 /** 646 647 * A convenience helper to check if the document we are running in is an SVG document 648 649 * 650 651 * @access private 652 653 * @returns {boolean} 654 655 */ 656 657 658 659 var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 660 661 662 663 664 665 /** 666 667 * setClasses takes an array of class names and adds them to the root element 668 669 * 670 671 * @access private 672 673 * @function setClasses 674 675 * @param {string[]} classes - Array of class names 676 677 */ 678 679 680 681 // Pass in an and array of class names, e.g.: 682
683 // ['no-webp', 'borderradius', ...] 684 685 function setClasses(classes) { 686 687 var className = docElement.className; 688 689 var classPrefix = Modernizr._config.classPrefix || ''; 690 691 692 693 if (isSVG) { 694 695 className = className.baseVal; 696 697 } 698 699 700 701 // Change `no-js` to `js` (independently of the `enableClasses` option) 702 703 // Handle classPrefix on this too 704 705 if (Modernizr._config.enableJSClass) { 706 707 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 708 709 className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 710 711 } 712 713 714 715 if (Modernizr._config.enableClasses) { 716 717 // Add the new classes 718 719 className += ' ' + classPrefix + classes.join(' ' + classPrefix); 720 721 isSVG ? docElement.className.baseVal = className : docElement.className = className; 722 723 } 724 725 726 727 } 728 729 730 731 ; 732 733 734 735 /** 736 737 * createElement is a convenience wrapper around document.createElement. Since we 738 739 * use createElement all over the place, this allows for (slightly) smaller code 740 741 * as well as abstracting away issues with creating elements in contexts other than 742 743 * HTML documents (e.g. SVG documents). 744 745 * 746 747 * @access private 748 749 * @function createElement 750 751 * @returns {HTMLElement|SVGElement} An HTML or SVG element 752 753 */ 754 755 756 757 function createElement() { 758 759 if (typeof document.createElement !== 'function') { 760 761 // This is the case in IE7, where the type of createElement is "object". 762 763 // For this reason, we cannot call apply() as Object is not a Function. 764 765 return document.createElement(arguments[0]); 766 767 } else if (isSVG) { 768 769 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 770 771 } else { 772 773 return document.createElement.apply(document, arguments); 774 775 } 776 777 } 778 779 780 781 ; 782 783 784 785 /** 786 787 * Modernizr.hasEvent() detects support for a given event 788 789 * 790 791 * @memberof Modernizr 792 793 * @name Modernizr.hasEvent 794 795 * @optionName Modernizr.hasEvent() 796 797 * @optionProp hasEvent 798 799 * @access public 800 801 * @function hasEvent 802 803 * @param {string|*} eventName - the name of an event to test for (e.g. "resize") 804 805 * @param {Element|string} [element=HTMLDivElement] - is the element|document|window|tagName to test on 806 807 * @returns {boolean} 808 809 * @example 810 811 * `Modernizr.hasEvent` lets you determine if the browser supports a supplied event. 812 813 * By default, it does this detection on a div element 814 815 * 816 817 * ```js 818 819 * hasEvent('blur') // true; 820 821 * ``` 822 823 * 824 825 * However, you are able to give an object as a second argument to hasEvent to 826 827 * detect an event on something other than a div. 828 829 * 830 831 * ```js 832 833 * hasEvent('devicelight', window) // true; 834 835 * ``` 836 837 * 838 839 */ 840 841 842 843 var hasEvent = (function() { 844 845 846 847 // Detect whether event support can be detected via `in`. Test on a DOM element 848 849 // using the "blur" event b/c it should always exist. bit.ly/event-detection 850 851 var needsFallback = !('onblur' in document.documentElement); 852 853 854 855 function inner(eventName, element) { 856 857 858 859 var isSupported; 860 861 if (!eventName) { return false; } 862 863 if (!element || typeof element === 'string') { 864 865 element = createElement(element || 'div'); 866 867 } 868 869 870 871 // Testing via the `in` operator is sufficient for modern browsers and IE. 872 873 // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and 874 875 // "resize", whereas `in` "catches" those. 876 877 eventName = 'on' + eventName; 878 879 isSupported = eventName in element; 880 881 882 883 // Fallback technique for old Firefox - bit.ly/event-detection 884 885 if (!isSupported && needsFallback) { 886 887 if (!element.setAttribute) { 888 889 // Switch to generic element if it lacks `setAttribute`. 890 891 // It could be the `document`, `window`, or something else. 892 893 element = createElement('div'); 894 895 } 896 897 898 899 element.setAttribute(eventName, ''); 900 901 isSupported = typeof element[eventName] === 'function'; 902 903 904 905 if (element[eventName] !== undefined) { 906 907 // If property was created, "remove it" by setting value to `undefined`. 908 909 element[eventName] = undefined; 910 911 } 912 913 element.removeAttribute(eventName); 914 915 } 916 917 918 919 return isSupported; 920 921 } 922 923 return inner; 924 925 })(); 926 927 928 929 930 931 ModernizrProto.hasEvent = hasEvent; 932 933 934 935/*! 936 937{ 938 939 "name": "Canvas", 940 941 "property": "canvas", 942 943 "caniuse": "canvas", 944 945 "tags": ["canvas", "graphics"], 946 947 "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"] 948 949} 950 951!*/ 952 953/* DOC 954 955Detects support for the `<canvas>` element for 2D drawing. 956 957*/ 958 959 960 961 // On the S60 and BB Storm, getContext exists, but always returns undefined 962 963 // so we actually have to call getContext() to verify 964 965 // github.com/Modernizr/Modernizr/issues/issue/97/ 966 967 Modernizr.addTest('canvas', function() { 968 969 var elem = createElement('canvas'); 970 971 return !!(elem.getContext && elem.getContext('2d')); 972 973 }); 974 975 976 977/*! 978 979{ 980 981 "name": "HTML5 Video", 982 983 "property": "video", 984 985 "caniuse": "video", 986 987 "tags": ["html5"], 988 989 "knownBugs": [ 990 991 "Without QuickTime, `Modernizr.video.h264` will be `undefined`; https://github.com/Modernizr/Modernizr/issues/546" 992 993 ], 994 995 "polyfills": [ 996 997 "html5media", 998 999 "mediaelementjs", 1000 1001 "sublimevideo", 1002 1003 "videojs", 1004 1005 "leanbackplayer", 1006 1007 "videoforeverybody" 1008 1009 ] 1010 1011} 1012 1013!*/ 1014 1015/* DOC 1016 1017Detects support for the video element, as well as testing what types of content it supports. 1018 1019 1020 1021Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.: 1022 1023 1024 1025```javascript 1026 1027Modernizr.video // true 1028 1029Modernizr.video.ogg // 'probably' 1030 1031``` 1032 1033*/ 1034 1035 1036 1037 // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 1038 1039 // thx to NielsLeenheer and zcorpan 1040 1041 1042 1043 // Note: in some older browsers, "no" was a return value instead of empty string. 1044 1045 // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 1046 1047 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 1048 1049 1050 1051 Modernizr.addTest('video', function() { 1052 1053 /* jshint -W053 */ 1054 1055 var elem = createElement('video'); 1056 1057 var bool = false;
1058 1059 1060 1061 // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 1062 1063 try { 1064 1065 if (bool = !!elem.canPlayType) { 1066 1067 bool = new Boolean(bool); 1068 1069 bool.ogg = elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, ''); 1070 1071 1072 1073 // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546 1074 1075 bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, ''); 1076 1077 1078 1079 bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, ''); 1080 1081 1082 1083 bool.vp9 = elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/, ''); 1084 1085 1086 1087 bool.hls = elem.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/, ''); 1088 1089 } 1090 1091 } catch (e) {} 1092 1093 1094 1095 return bool; 1096 1097 }); 1098 1099 1100 1101 1102 1103 /** 1104 1105 * List of property values to set for css tests. See ticket #21 1106 1107 * http://git.io/vUGl4 1108 1109 * 1110 1111 * @memberof Modernizr 1112 1113 * @name Modernizr._prefixes 1114 1115 * @optionName Modernizr._prefixes 1116 1117 * @optionProp prefixes 1118 1119 * @access public 1120 1121 * @example 1122 1123 * 1124 1125 * Modernizr._prefixes is the internal list of prefixes that we test against 1126 1127 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply 1128 1129 * an array of kebab-case vendor prefixes you can use within your code. 1130 1131 * 1132 1133 * Some common use cases include 1134 1135 * 1136 1137 * Generating all possible prefixed version of a CSS property 1138 1139 * ```js 1140 1141 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); 1142 1143 * 1144 1145 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' 1146 1147 * ``` 1148 1149 * 1150 1151 * Generating all possible prefixed version of a CSS value 1152 1153 * ```js 1154 1155 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; 1156 1157 * 1158 1159 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' 1160 1161 * ``` 1162 1163 */ 1164 1165 1166 1167 // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test 1168 1169 // values in feature detects to continue to work 1170 1171 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']); 1172 1173 1174 1175 // expose these for the plugin API. Look in the source for how to join() them against your input 1176 1177 ModernizrProto._prefixes = prefixes; 1178 1179 1180 1181 1182 1183 1184 1185 /** 1186 1187 * roundedEquals takes two integers and checks if the first is
1187within 1 of the second 1188 1189 * 1190 1191 * @access private 1192 1193 * @function roundedEquals 1194 1195 * @param {number} a 1196 1197 * @param {number} b 1198 1199 * @returns {boolean} 1200 1201 */ 1202 1203 1204 1205 function roundedEquals(a, b) { 1206 1207 return a - 1 === b || a === b || a + 1 === b; 1208 1209 } 1210 1211 1212 1213 ; 1214 1215 1216 1217 /** 1218 1219 * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support 1220 1221 * 1222 1223 * @author kangax 1224 1225 * @access private 1226 1227 * @function hasOwnProp 1228 1229 * @param {object} object - The object to check for a property 1230 1231 * @param {string} property - The property to check for 1232 1233 * @returns {boolean} 1234 1235 */ 1236 1237 1238 1239 // hasOwnProperty shim by kangax needed for Safari 2.0 support 1240 1241 var hasOwnProp; 1242 1243 1244 1245 (function() { 1246 1247 var _hasOwnProperty = ({}).hasOwnProperty; 1248 1249 /* istanbul ignore else */ 1250 1251 /* we have no way of testing IE 5.5 or safari 2, 1252 1253 * so just assume the else gets hit */ 1254 1255 if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) { 1256 1257 hasOwnProp = function(object, property) { 1258 1259 return _hasOwnProperty.call(object, property); 1260 1261 }; 1262 1263 } 1264 1265 else { 1266 1267 hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ 1268 1269 return ((property in object) && is(object.constructor.prototype[property], 'undefined')); 1270 1271 }; 1272 1273 } 1274 1275 })(); 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 // _l tracks listeners for async tests, as well as tests that execute after the initial run 1286 1287 ModernizrProto._l = {}; 1288 1289 1290 1291 /** 1292 1293 * Modernizr.on is a way to listen for the completion of async tests. Being 1294 1295 * asynchronous, they may not finish before your scripts run. As a result you 1296 1297 * will get a possibly false negative `undefined` value. 1298 1299 * 1300 1301 * @memberof Modernizr 1302 1303 * @name Modernizr.on 1304 1305 * @access public 1306 1307 * @function on 1308 1309 * @param {string} feature - String name of the feature detect 1310 1311 * @param {function} cb - Callback function returning a Boolean - true if feature is supported, false if not 1312 1313 * @example 1314 1315 * 1316 1317 * ```js 1318 1319 * Modernizr.on('flash', function( result ) { 1320 1321 * if (result) { 1322 1323 * // the browser has flash 1324 1325 * } else { 1326 1327 * // the browser does not have flash 1328 1329 * } 1330 1331 * }); 1332 1333 * ``` 1334 1335 */ 1336 1337 1338 1339 ModernizrProto.on = function(feature, cb) { 1340 1341 // Create the list of listeners if it doesn't exist 1342 1343 if (!this._l[feature]) { 1344 1345 this._l[feature] = []; 1346 1347 } 1348 1349 1350 1351 // Push this test on to the listener list 1352 1353 this._l[feature].push(cb); 1354 1355 1356 1357 // If it's already been resolved, trigger it on next tick 1358 1359 if (Modernizr.hasOwnProperty(feature)) { 1360 1361 // Next Tick 1362 1363 setTimeout(function() { 1364 1365 Modernizr._trigger(feature, Modernizr[feature]); 1366 1367 }, 0); 1368 1369 } 1370 1371 }; 1372 1373 1374 1375 /** 1376 1377 * _trigger is the private function used to signal test completion and run any 1378 1379 * callbacks registered through [Modernizr.on](#modernizr-on) 1380 1381 * 1382 1383 * @memberof Modernizr 1384 1385 * @name Modernizr._trigger 1386 1387 * @access private 1388 1389 * @function _trigger 1390 1391 * @param {string} feature - string name of the feature detect 1392 1393 * @param {function|boolean} [res] - A feature detection function, or the boolean = 1394 1395 * result of a feature detection function 1396 1397 */ 1398 1399 1400 1401 ModernizrProto._trigger = function(feature, res) { 1402 1403 if (!this._l[feature]) { 1404 1405 return; 1406 1407 } 1408 1409 1410 1411 var cbs = this._l[feature]; 1412 1413 1414 1415 // Force async 1416 1417 setTimeout(function() { 1418 1419 var i, cb; 1420 1421 for (i = 0; i < cbs.length; i++) { 1422 1423 cb = cbs[i]; 1424 1425 cb(res); 1426 1427 } 1428 1429 }, 0); 1430 1431 1432 1433 // Don't trigger these again 1434 1435 delete this._l[feature]; 1436 1437 }; 1438 1439 1440 1441 /** 1442 1443 * addTest allows you to define your own feature detects that are not currently 1444 1445 * included in Modernizr (under the covers it's the exact same code Modernizr 1446 1447 * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result 1448 1449 * will be added onto the Modernizr object, as well as an appropriate className set on 1450 1451 * the html element when configured to do so 1452 1453 * 1454 1455 * @memberof Modernizr 1456 1457 * @name Modernizr.addTest 1458 1459 * @optionName Modernizr.addTest() 1460 1461 * @optionProp addTest 1462 1463 * @access public 1464 1465 * @function addTest 1466 1467 * @param {string|object} feature - The string name of the feature detect, or an 1468 1469 * object of feature detect names and test 1470 1471 * @param {function|boolean} test - Function returning true if feature is supported, 1472 1473 * false if not. Otherwise a boolean representing the results of a feature detection 1474 1475 * @example 1476 1477 * 1478 1479 * The most common way of creating your own feature detects is by calling 1480 1481 * `Modernizr.addTest` with a string (preferably just lowercase, without any 1482 1483 * punctuation), and a function you want executed that will return a boolean result 1484 1485 * 1486 1487 * ```js 1488 1489 * Modernizr.addTest('itsTuesday', function() { 1490 1491 * var d = new Date(); 1492 1493 * return d.getDay() === 2; 1494 1495 * }); 1496 1497 * ``` 1498 1499 * 1500 1501 * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday, 1502 1503 * and to `false` every other day of the week. One thing to notice is that the names of 1504 1505 * feature detect functions are always lowercased when added to the Modernizr object. That 1506 1507 * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will. 1508 1509 * 1510 1511 * 1512 1513 * Since we only look at the returned value from any feature detection function, 1514 1515 * you do not need to actually use a function. For simple detections, just passing 1516 1517 * in a statement that will return a boolean value works just fine. 1518 1519 * 1520 1521 * ```js 1522 1523 * Modernizr.addTest('hasJquery', 'jQuery' in window); 1524 1525 * ``` 1526 1527 * 1528 1529 * Just like before, when the above runs `Modernizr.hasjquery` will be true if 1530 1531 * jQuery has been included on the page. Not using a function saves a small amount 1532
1533 * of overhead for the browser, as well as making your code much more readable. 1534 1535 * 1536 1537 * Finally, you also have the ability to pass in an object of feature names and 1538 1539 * their tests. This is handy if you want to add multiple detections in one go. 1540 1541 * The keys should always be a string, and the value can be either a boolean or 1542 1543 * function that returns a boolean. 1544 1545 * 1546 1547 * ```js 1548 1549 * var detects = { 1550 1551 * 'hasjquery': 'jQuery' in window, 1552 1553 * 'itstuesday': function() { 1554 1555 * var d = new Date(); 1556 1557 * return d.getDay() === 2; 1558 1559 * } 1560 1561 * } 1562 1563 * 1564 1565 * Modernizr.addTest(detects); 1566 1567 * ``` 1568 1569 * 1570 1571 * There is really no difference between the first methods and this one, it is 1572 1573 * just a convenience to let you write more readable code. 1574 1575 */ 1576 1577 1578 1579 function addTest(feature, test) { 1580 1581 1582 1583 if (typeof feature == 'object') { 1584 1585 for (var key in feature) { 1586 1587 if (hasOwnProp(feature, key)) { 1588 1589 addTest(key, feature[ key ]); 1590 1591 } 1592 1593 } 1594 1595 } else { 1596 1597 1598 1599 feature = feature.toLowerCase(); 1600 1601 var featureNameSplit = feature.split('.'); 1602 1603 var last = Modernizr[featureNameSplit[0]]; 1604 1605 1606 1607 // Again, we don't check for parent test existence. Get that right, though. 1608 1609 if (featureNameSplit.length == 2) { 1610 1611 last = last[featureNameSplit[1]]; 1612 1613 } 1614 1615 1616 1617 if (typeof last != 'undefined') { 1618 1619 // we're going to quit if you're trying to overwrite an existing test 1620 1621 // if we were to allow it, we'd do this: 1622 1623 // var re = new RegExp("\\b(no-)?" + feature + "\\b"); 1624 1625 // docElement.className = docElement.className.replace( re, '' ); 1626 1627 // but, no rly, stuff 'em. 1628 1629 return Modernizr; 1630 1631 } 1632 1633 1634 1635 test = typeof test == 'function' ? test() : test; 1636 1637 1638 1639 // Set the value (this is the magic, right here). 1640 1641 if (featureNameSplit.length == 1) { 1642 1643 Modernizr[featureNameSplit[0]] = test; 1644 1645 } else { 1646 1647 // cast to a Boolean, if not one already 1648 1649 /* jshint -W053 */ 1650 1651 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 1652 1653 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 1654 1655 } 1656 1657 1658 1659 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test; 1660 1661 } 1662 1663 1664 1665 // Set a single class (either `feature` or `no-feature`) 1666 1667 /* jshint -W041 */ 1668 1669 setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); 1670 1671 /* jshint +W041 */ 1672 1673 1674 1675 // Trigger the event 1676 1677 Modernizr._trigger(feature, test); 1678 1679 } 1680 1681 1682 1683 return Modernizr; // allow chaining. 1684 1685 } 1686 1687 1688 1689 // After all the tests are run, add self to the Modernizr prototype 1690 1691 Modernizr._q.push(function() { 1692 1693 ModernizrProto.addTest = addTest; 1694 1695 }); 1696 1697 1698 1699 1700 1701 1702 1703/*! 1704 1705{ 1706 1707 "name": "Video Autoplay", 1708 1709 "property": "videoautoplay", 1710 1711 "tags": ["video"], 1712 1713 "async" : true, 1714 1715 "warnings": ["This test is very large ? only include it if you absolutely need it"], 1716 1717 "knownBugs": ["crashes with an alert on iOS7 when added to homescreen"] 1718 1719} 1720 1721!*/ 1722 1723/* DOC 1724 1725Checks for support of the autoplay attribute of the video element. 1726 1727*/ 1728 1729 1730 1731 1732 1733 Modernizr.addAsyncTest(function() { 1734 1735 var timeout; 1736 1737 var waitTime = 200; 1738 1739 var retries = 5; 1740 1741 var currentTry = 0; 1742 1743 var elem = createElement('video'); 1744 1745 var elemStyle = elem.style; 1746 1747 1748 1749 function testAutoplay(arg) { 1750 1751 currentTry++; 1752 1753 clearTimeout(timeout); 1754 1755 1756 1757 var result = arg && arg.type === 'playing' || elem.currentTime !== 0; 1758 1759 1760 1761 if (!result && currentTry < retries) { 1762 1763 //Detection can be flaky if the browser is slow, so lets retry in a little bit 1764 1765 timeout = setTimeout(testAutoplay, waitTime); 1766 1767 return; 1768 1769 } 1770 1771 1772 1773 elem.removeEventListener('playing', testAutoplay, false); 1774 1775 addTest('videoautoplay', result); 1776 1777 elem.parentNode.removeChild(elem); 1778 1779 } 1780 1781 1782 1783 //skip the test if video itself, or the autoplay 1784 1785 //element on it isn't supported 1786 1787 if (!Modernizr.video || !('autoplay' in elem)) { 1788 1789 addTest('videoautoplay', false); 1790 1791 return; 1792 1793 } 1794 1795 1796 1797 elemStyle.position = 'absolute'; 1798 1799 elemStyle.height = 0; 1800 1801 elemStyle.width = 0; 1802 1803 1804 1805 try { 1806 1807 if (Modernizr.video.ogg) { 1808 1809 elem.src = 'data:video/ogg;base64,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'; 1810 1811 } 1812 1813 else if (Modernizr.video.h264) { 1814 1815 elem.src = 'data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAAs1tZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0OCByMjYwMSBhMGNkN2QzIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTEgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEwIHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAD2WIhAA3//728P4FNjuZQQAAAu5tb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAAZAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACGHRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAgAAAAIAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAGQAAAAAAAEAAAAAAZBtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAACgAAAAEAFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAE7bWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAA+3N0YmwAAACXc3RzZAAAAAAAAAABAAAAh2F2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAgACAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAxYXZjQwFkAAr/4QAYZ2QACqzZX4iIhAAAAwAEAAADAFA8SJZYAQAGaOvjyyLAAAAAGHN0dHMAAAAAAAAAAQAAAAEAAAQAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAABRzdHN6AAAAAAAAAsUAAAABAAAAFHN0Y28AAAAAAAAAAQAAADAAAABidWR0YQAAAFptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAAC1pbHN0AAAAJal0b28AAAAdZGF0YQAAAAEAAAAATGF2ZjU2LjQwLjEwMQ=='; 1816 1817 } 1818 1819 else { 1820 1821 addTest('videoautoplay', false); 1822 1823 return; 1824 1825 } 1826 1827 } 1828 1829 1830 1831 catch (e) { 1832 1833 addTest('videoautoplay', false); 1834 1835 return; 1836 1837 } 1838 1839 1840 1841 elem.setAttribute('autoplay', ''); 1842 1843 elem.style.cssText = 'display:none'; 1844 1845 docElement.appendChild(elem); 1846 1847 // wait for the next tick to add the listener, otherwise the element may 1848 1849 // not have time to play in high load situations (e.g. the test suite) 1850 1851 setTimeout(function() { 1852 1853 elem.addEventListener('playing', testAutoplay, false); 1854 1855 timeout = setTimeout(testAutoplay, waitTime); 1856 1857 }, 0); 1858 1859 }); 1860 1861 1862 1863 1864 1865 /** 1866 1867 * getBody returns the body of a document, or an element that can stand in for 1868 1869 * the body if a real body does not exist 1870 1871 * 1872 1873 * @access private 1874 1875 * @function getBody 1876 1877 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 1878 1879 * artificially created element that stands in for the body 1880 1881 */ 1882 1883 1884 1885 function getBody() { 1886 1887 // After page load injecting a fake body doesn't work so check if body exists 1888 1889 var body = document.body; 1890 1891 1892 1893 if (!body) { 1894 1895 // Can't use the real body create a fake one. 1896 1897 body = createElement(isSVG ? 'svg' : 'body'); 1898 1899 body.fake = true; 1900 1901 } 1902 1903 1904 1905 return body; 1906 1907 } 1908 1909 1910 1911 ; 1912 1913 1914 1915 /** 1916 1917 * injectElementWithStyles injects an element with style element and some CSS rules 1918 1919 * 1920 1921 * @access private 1922 1923 * @function injectElementWithStyles 1924 1925 * @param {string} rule - String representing a css rule 1926 1927 * @param {function} callback - A function that is used to test the injected element 1928 1929 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 1930 1931 * @param {string[]}
1931 [testnames] - An array of strings that are used as ids for the additional nodes 1932 1933 * @returns {boolean} 1934 1935 */ 1936 1937 1938 1939 function injectElementWithStyles(rule, callback, nodes, testnames) { 1940 1941 var mod = 'modernizr'; 1942 1943 var style; 1944 1945 var ret; 1946 1947 var node; 1948 1949 var docOverflow; 1950 1951 var div = createElement('div'); 1952 1953 var body = getBody(); 1954 1955 1956 1957 if (parseInt(nodes, 10)) { 1958 1959 // In order not to give false positives we create a node for each test 1960 1961 // This also allows the method to scale for unspecified uses 1962 1963 while (nodes--) { 1964 1965 node = createElement('div'); 1966 1967 node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 1968 1969 div.appendChild(node); 1970 1971 } 1972 1973 } 1974 1975 1976 1977 style = createElement('style'); 1978 1979 style.type = 'text/css'; 1980 1981 style.id = 's' + mod; 1982 1983 1984 1985 // 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. 1986 1987 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 1988 1989 (!body.fake ? div : body).appendChild(style); 1990 1991 body.appendChild(div); 1992 1993 1994 1995 if (style.styleSheet) { 1996 1997 style.styleSheet.cssText = rule; 1998 1999 } else { 2000 2001 style.appendChild(document.createTextNode(rule)); 2002 2003 } 2004 2005 div.id = mod; 2006 2007 2008 2009 if (body.fake) { 2010 2011 //avoid crashing IE8, if background image is used 2012 2013 body.style.background = ''; 2014 2015 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 2016 2017 body.style.overflow = 'hidden'; 2018 2019 docOverflow = docElement.style.overflow; 2020 2021 docElement.style.overflow = 'hidden'; 2022 2023 docElement.appendChild(body); 2024 2025 } 2026 2027 2028 2029 ret = callback(div, rule); 2030 2031 // If this is done after page load we don't want to remove the body so check if body exists 2032 2033 if (body.fake) { 2034 2035 body.parentNode.removeChild(body); 2036 2037 docElement.style.overflow = docOverflow; 2038 2039 // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 2040 2041 docElement.offsetHeight; 2042 2043 } else { 2044 2045 div.parentNode.removeChild(div); 2046 2047 } 2048 2049 2050 2051 return !!ret; 2052 2053 2054 2055 } 2056 2057 2058 2059 ; 2060 2061 2062 2063 /** 2064 2065 * testStyles injects an element with style element and some CSS rules 2066 2067 * 2068 2069 * @memberof Modernizr 2070 2071 * @name Modernizr.testStyles 2072 2073 * @optionName Modernizr.testStyles() 2074 2075 * @optionProp testStyles 2076 2077 * @access public 2078 2079 * @function testStyles 2080 2081 * @param {string} rule - String representing a css rule 2082 2083 * @param {function} callback - A function that is used to test the injected element 2084 2085 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 2086 2087 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 2088 2089 * @returns {boolean} 2090 2091 * @example 2092 2093 * 2094 2095 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page 2096 2097 * along with (possibly multiple) DOM elements. This lets you check for features 2098 2099 * 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). 2100 2101 * 2102 2103 * ```js 2104 2105 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { 2106 2107 * // elem is the first DOM node in the page (by default #modernizr) 2108 2109 * // rule is the first argument you supplied - the CSS rule in string form 2110 2111 * 2112 2113 * addTest('widthworks', elem.style.width === '9px') 2114 2115 * }); 2116 2117 * ``` 2118 2119 * 2120 2121 * If your test requires multiple nodes, you can include a third argument 2122 2123 * indicating how many additional div elements to include on the page. The 2124 2125 * additional nodes are injected as children of the `elem` that is returned as 2126 2127 * the first argument to the callback. 2128 2129 * 2130 2131 * ```js 2132 2133 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { 2134 2135 * document.getElementById('modernizr').style.width === '1px'; // true 2136 2137 * document.getElementById('modernizr2').style.width === '2px'; // true 2138 2139 * elem.firstChild === document.getElementById('modernizr2'); // true 2140 2141 * }, 1); 2142 2143 * ``` 2144 2145 * 2146 2147 * By default, all of the additional elements have an ID of `modernizr[n]`, where 2148
2149 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, 2150 2151 * the second additional is `#modernizr3`, etc.). 2152 2153 * If you want to have more meaningful IDs for your function, you can provide 2154 2155 * them as the fourth argument, as an array of strings 2156 2157 * 2158 2159 * ```js 2160 2161 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { 2162 2163 * elem.firstChild === document.getElementById('foo'); // true 2164 2165 * elem.lastChild === document.getElementById('bar'); // true 2166 2167 * }, 2, ['foo', 'bar']); 2168 2169 * ``` 2170 2171 * 2172 2173 */ 2174 2175 2176 2177 var testStyles = ModernizrProto.testStyles = injectElementWithStyles; 2178 2179 2180 2181/*! 2182 2183{ 2184 2185 "name": "Touch Events", 2186 2187 "property": "touchevents", 2188 2189 "caniuse" : "touch", 2190 2191 "tags": ["media", "attribute"], 2192 2193 "notes": [{ 2194 2195 "name": "Touch Events spec", 2196 2197 "href": "https://www.w3.org/TR/2013/WD-touch-events-20130124/" 2198 2199 }], 2200 2201 "warnings": [ 2202 2203 "Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device" 2204 2205 ], 2206 2207 "knownBugs": [ 2208 2209 "False-positive on some configurations of Nokia N900", 2210 2211 "False-positive on some BlackBerry 6.0 builds ? https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695" 2212 2213 ] 2214 2215} 2216 2217!*/ 2218 2219/* DOC 2220 2221Indicates if the browser supports the W3C Touch Events API. 2222 2223 2224 2225This *does not* necessarily reflect a touchscreen device: 2226 2227 2228 2229* Older touchscreen devices only emulate mouse events 2230 2231* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that 2232 2233* Some browsers & OS setups may enable touch APIs when no touchscreen is connected 2234 2235* Future browsers may implement other event models for touch interactions 2236 2237 2238 2239See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/). 2240 2241 2242 2243It's recommended to bind both mouse and touch/pointer events simultaneously ? see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/). 2244 2245 2246 2247This test will also return `true` for Firefox 4 Multitouch support. 2248 2249*/ 2250 2251 2252 2253 // Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415 2254 2255 Modernizr.addTest('touchevents', function() { 2256 2257 var bool; 2258 2259 if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { 2260 2261 bool = true; 2262 2263 } else { 2264 2265 // include the 'heartz' as a way to have a non matching MQ to help terminate the join 2266 2267 // https://git.io/vznFH 2268 2269 var query = ['@media (', prefixes.join('touch-enabled),('), 'heartz', ')', '{#modernizr{top:9px;position:absolute}}'].join(''); 2270 2271 testStyles(query, function(node) { 2272 2273 bool = node.offsetTop === 9; 2274 2275 }); 2276 2277 } 2278 2279 return bool; 2280 2281 }); 2282 2283 2284 2285/*! { 2286 2287 "name": "hairline", 2288 2289 "property": "csshairline", 2290 2291 "tags": ["css"], 2292 2293 "authors": ["strarsis"], 2294 2295 "notes": [{ 2296 2297 "name": "Blog post about CSS retina hairlines", 2298 2299 "href": "http://dieulot.net/css-retina-hairline" 2300 2301 },{ 2302 2303 "name": "Derived from", 2304 2305 "href": "https://gist.github.com/dieulot/520a49463f6058fbc8d1" 2306 2307 }] 2308 2309} 2310 2311!*/ 2312 2313/* DOC 2314 2315Detects support for hidpi/retina hairlines, which are CSS borders with less than 1px in width, for being physically 1px on hidpi screens. 2316 2317*/ 2318 2319 2320 2321 2322 2323 Modernizr.addTest('hairline', function() { 2324 2325 return testStyles('#modernizr {border:.5px solid transparent}', function(elem) { 2326 2327 return elem.offsetHeight === 1; 2328 2329 }); 2330 2331 }); 2332 2333 2334 2335/*! 2336 2337{ 2338 2339 "name": "CSS vh unit", 2340 2341 "property": "cssvhunit", 2342 2343 "caniuse": "viewport-units", 2344 2345 "tags": ["css"], 2346 2347 "builderAliases": ["css_vhunit"], 2348 2349 "notes": [{ 2350 2351 "name": "Related Modernizr Issue", 2352 2353 "href": "https://github.com/Modernizr/Modernizr/issues/572" 2354 2355 },{ 2356 2357 "name": "Similar JSFiddle", 2358 2359 "href": "https://jsfiddle.net/FWeinb/etnYC/" 2360 2361 }] 2362 2363} 2364 2365!*/ 2366 2367 2368 2369 testStyles('#modernizr { height: 50vh; }', function(elem) { 2370 2371 var height = parseInt(window.innerHeight / 2, 10); 2372 2373 var compStyle = parseInt((window.getComputedStyle ? 2374 2375 getComputedStyle(elem, null) : 2376 2377 elem.currentStyle).height, 10); 2378 2379 Modernizr.addTest('cssvhunit', compStyle == height); 2380 2381 }); 2382 2383 2384 2385/*! 2386 2387{ 2388 2389 "name": "CSS vmax unit", 2390 2391 "property": "cssvmaxunit", 2392 2393 "caniuse": "viewport-units", 2394 2395 "tags": ["css"], 2396 2397 "builderAliases": ["css_vmaxunit"], 2398 2399 "notes": [{ 2400 2401 "name": "Related Modernizr Issue", 2402 2403 "href": "https://github.com/Modernizr/Modernizr/issues/572" 2404 2405 },{ 2406 2407 "name": "JSFiddle Example", 2408 2409 "href": "https://jsfiddle.net/glsee/JDsWQ/4/" 2410 2411 }] 2412 2413} 2414 2415!*/ 2416 2417 2418 2419 testStyles('#modernizr1{width: 50vmax}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}', function(node) { 2420 2421 var elem = node.childNodes[2]; 2422 2423 var scroller = node.childNodes[1]; 2424 2425 var fullSizeElem = node.childNodes[0]; 2426 2427 var scrollbarWidth = parseInt((scroller.offsetWidth - scroller.clientWidth) / 2, 10); 2428 2429 2430 2431 var one_vw = fullSizeElem.clientWidth / 100; 2432 2433 var one_vh = fullSizeElem.clientHeight / 100; 2434 2435 var expectedWidth = parseInt(Math.max(one_vw, one_vh) * 50, 10); 2436 2437 var compWidth = parseInt((window.getComputedStyle ? 2438 2439 getComputedStyle(elem, null) : 2440 2441 elem.currentStyle).width, 10); 2442 2443 2444 2445 Modernizr.addTest('cssvmaxunit', roundedEquals(expectedWidth, compWidth) || roundedEquals(expectedWidth, compWidth - scrollbarWidth)); 2446 2447 }, 3); 2448 2449 2450 2451/*! 2452 2453{ 2454 2455 "name": "CSS vmin unit", 2456 2457 "property": "cssvminunit", 2458 2459 "caniuse": "viewport-units", 2460 2461 "tags": ["css"], 2462 2463 "builderAliases": ["css_vminunit"], 2464 2465 "notes": [{ 2466 2467 "name": "Related Modernizr Issue", 2468 2469 "href": "https://github.com/Modernizr/Modernizr/issues/572" 2470 2471 },{ 2472 2473 "name": "JSFiddle Example", 2474 2475 "href": "https://jsfiddle.net/glsee/JRmdq/8/" 2476 2477 }] 2478 2479} 2480 2481!*/ 2482 2483 2484 2485 testStyles('#modernizr1{width: 50vm;width:50vmin}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}', function(node) { 2486 2487 var elem = node.childNodes[2]; 2488 2489 var scroller = node.childNodes[1]; 2490 2491 var fullSizeElem = node.childNodes[0]; 2492 2493 var scrollbarWidth = parseInt((scroller.offsetWidth - scroller.clientWidth) / 2, 10); 2494 2495 2496 2497 var one_vw = fullSizeElem.clientWidth / 100; 2498 2499 var one_vh = fullSizeElem.clientHeight / 100; 2500 2501 var expectedWidth = parseInt(Math.min(one_vw, one_vh) * 50, 10); 2502 2503 var compWidth = parseInt((window.getComputedStyle ? 2504 2505 getComputedStyle(elem, null) : 2506 2507 elem.currentStyle).width, 10); 2508 2509 2510 2511 Modernizr.addTest('cssvminunit', roundedEquals(expectedWidth, compWidth) || roundedEquals(expectedWidth, compWidth - scrollbarWidth)); 2512 2513 }, 3); 2514 2515 2516 2517/*! 2518 2519{ 2520 2521 "name": "CSS vw unit", 2522 2523 "property": "cssvwunit", 2524 2525 "caniuse": "viewport-units", 2526 2527 "tags": ["css"], 2528 2529 "builderAliases": ["css_vwunit"], 2530 2531 "notes": [{ 2532 2533 "name": "Related Modernizr Issue", 2534 2535 "href": "https://github.com/Modernizr/Modernizr/issues/572" 2536 2537 },{ 2538 2539 "name": "JSFiddle Example", 2540 2541 "href": "https://jsfiddle.net/FWeinb/etnYC/" 2542 2543 }] 2544 2545} 2546 2547!*/ 2548 2549 2550 2551 testStyles('#modernizr { width: 50vw; }', function(elem) { 2552 2553 var width = parseInt(window.innerWidth / 2, 10); 2554 2555 var compStyle = parseInt((window.getComputedStyle ? 2556 2557 getComputedStyle(elem, null) : 2558 2559 elem.currentStyle).width, 10); 2560 2561 2562 2563 Modernizr.addTest('cssvwunit', compStyle == width); 2564 2565 }); 2566 2567 2568 2569 2570 2571 /** 2572 2573 * If the browsers follow the spec, then they would expose vendor-specific style as: 2574 2575 * elem.style.WebkitBorderRadius 2576 2577 * instead of something like the following, which would be technically incorrect: 2578 2579 * elem.style.webkitBorderRadius 2580 2581 2582 2583 * Webkit ghosts their properties in lowercase but Opera & Moz do not. 2584 2585 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ 2586 2587 * erik.eae.net/archives/2008/03/10/21.48.10/ 2588 2589 2590 2591 * More here: github.com/Modernizr/Modernizr/issues/issue/21 2592 2593 * 2594 2595 * @access private 2596 2597 * @returns {string} The string representing the vendor-specific style properties 2598 2599 */ 2600 2601 2602 2603 var omPrefixes = 'Moz O ms Webkit'; 2604 2605 2606 2607 2608 2609 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); 2610 2611 ModernizrProto._cssomPrefixes = cssomPrefixes; 2612 2613 2614 2615 2616 2617 /** 2618 2619 * List of JavaScript DOM values used for tests 2620 2621 * 2622 2623 * @memberof Modernizr 2624 2625 * @name Modernizr._domPrefixes 2626 2627 * @optionName Modernizr._domPrefixes 2628 2629 * @optionProp domPrefixes 2630 2631 * @access public 2632 2633 * @example 2634 2635 * 2636 2637 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather 2638 2639 * than kebab-case properties, all properties are their Capitalized variant 2640 2641 * 2642 2643 * ```js 2644 2645 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; 2646 2647 * ``` 2648 2649 */ 2650 2651 2652 2653 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); 2654 2655 ModernizrProto._domPrefixes = domPrefixes; 2656 2657 2658 2659 2660 2661 2662 2663 /** 2664 2665 * contains checks to see if a string contains another string 2666 2667 * 2668 2669 * @access private 2670 2671 * @function contains 2672 2673 * @param {string} str - The string we want to check for substrings 2674 2675 * @param {string} substr - The substring we want to search the first string for 2676 2677 * @returns {boolean} 2678 2679 */ 2680 2681 2682 2683 function contains(str, substr) { 2684
2685 return !!~('' + str).indexOf(substr); 2686 2687 } 2688 2689 2690 2691 ; 2692 2693 2694 2695 /** 2696 2697 * cssToDOM takes a kebab-case string and converts it to camelCase 2698 2699 * e.g. box-sizing -> boxSizing 2700 2701 * 2702 2703 * @access private 2704 2705 * @function cssToDOM 2706 2707 * @param {string} name - String name of kebab-case prop we want to convert 2708 2709 * @returns {string} The camelCase version of the supplied name 2710 2711 */ 2712 2713 2714 2715 function cssToDOM(name) { 2716 2717 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 2718 2719 return m1 + m2.toUpperCase(); 2720 2721 }).replace(/^-/, ''); 2722 2723 } 2724 2725 ; 2726 2727 2728 2729 /** 2730 2731 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. 2732 2733 * 2734 2735 * @access private 2736 2737 * @function fnBind 2738 2739 * @param {function} fn - a function you want to change `this` reference to 2740 2741 * @param {object} that - the `this` you want to call the function with 2742 2743 * @returns {function} The wrapped version of the supplied function 2744 2745 */ 2746 2747 2748 2749 function fnBind(fn, that) { 2750 2751 return function() { 2752 2753 return fn.apply(that, arguments); 2754 2755 }; 2756 2757 } 2758 2759 2760 2761 ; 2762 2763 2764 2765 /** 2766 2767 * testDOMProps is a generic DOM property test; if a browser supports 2768 2769 * a certain property, it won't return undefined for it. 2770 2771 * 2772 2773 * @access private 2774 2775 * @function testDOMProps 2776 2777 * @param {array.<string>} props - An array of properties to test for 2778 2779 * @param {object} obj - An object or Element you want to use to test the parameters again 2780 2781 * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check 2782 2783 */ 2784 2785 function testDOMProps(props, obj, elem) { 2786 2787 var item; 2788 2789 2790 2791 for (var i in props) { 2792 2793 if (props[i] in obj) { 2794 2795 2796 2797 // return the property name as a string 2798 2799 if (elem === false) { 2800 2801 return props[i]; 2802 2803 } 2804 2805 2806 2807 item = obj[props[i]]; 2808 2809 2810 2811 // let's bind a function 2812 2813 if (is(item, 'function')) { 2814 2815 // bind to obj unless overriden 2816 2817 return fnBind(item, elem || obj); 2818 2819 } 2820 2821 2822 2823 // return the unbound function or obj or value 2824 2825 return item; 2826 2827 } 2828 2829 } 2830 2831 return false; 2832 2833 } 2834 2835 2836 2837 ; 2838 2839 2840 2841 /** 2842 2843 * Create our "modernizr" element that we do most feature tests on. 2844 2845 * 2846 2847 * @access private 2848 2849 */ 2850 2851 2852 2853 var modElem = { 2854 2855 elem: createElement('modernizr') 2856 2857 }; 2858 2859 2860 2861 // Clean up this element 2862 2863 Modernizr._q.push(function() { 2864 2865 delete modElem.elem; 2866 2867 }); 2868 2869 2870 2871 2872 2873 2874 2875 var mStyle = { 2876 2877 style: modElem.elem.style 2878 2879 }; 2880 2881 2882 2883 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 2884 2885 // the front of the queue. 2886 2887 Modernizr._q.unshift(function() { 2888 2889 delete mStyle.style; 2890 2891 }); 2892 2893 2894 2895 2896 2897 2898 2899 /** 2900 2901 * domToCSS takes a camelCase string and converts it to kebab-case 2902 2903 * e.g. boxSizing -> box-sizing 2904 2905 * 2906 2907 * @access private 2908 2909 * @function domToCSS 2910 2911 * @param {string} name - String name of camelCase prop we want to convert 2912 2913 * @returns {string} The kebab-case version of the supplied name 2914 2915 */ 2916 2917 2918 2919 function domToCSS(name) { 2920 2921 return name.replace(/([A-Z])/g, function(str, m1) { 2922 2923 return '-' + m1.toLowerCase(); 2924 2925 }).replace(/^ms-/, '-ms-'); 2926 2927 } 2928 2929 ; 2930 2931 2932 2933 /** 2934 2935 * nativeTestProps allows for us to use native feature detection functionality if available. 2936 2937 * some prefixed form, or false, in the case of an unsupported rule 2938 2939 * 2940 2941 * @access private 2942 2943 * @function nativeTestProps 2944 2945 * @param {array} props - An array of property names 2946 2947 * @param {string} value - A string representing the value we want to check via @supports 2948 2949 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 2950 2951 */ 2952 2953 2954 2955 // Accepts a list of property names and a single value 2956 2957 // Returns `undefined` if native detection not available 2958 2959 function nativeTestProps(props, value) { 2960 2961 var i = props.length; 2962 2963 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface 2964 2965 if ('CSS' in window && 'supports' in window.CSS) { 2966 2967 // Try every prefixed variant of the property 2968 2969 while (i--) { 2970 2971 if (window.CSS.supports(domToCSS(props[i]), value)) { 2972 2973 return true; 2974 2975 } 2976 2977 } 2978 2979 return false;
2980 2981 } 2982 2983 // Otherwise fall back to at-rule (for Opera 12.x) 2984 2985 else if ('CSSSupportsRule' in window) { 2986 2987 // Build a condition string for every prefixed variant 2988 2989 var conditionText = []; 2990 2991 while (i--) { 2992 2993 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 2994 2995 } 2996 2997 conditionText = conditionText.join(' or '); 2998 2999 return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { 3000 3001 return getComputedStyle(node, null).position == 'absolute'; 3002 3003 }); 3004 3005 } 3006 3007 return undefined; 3008 3009 } 3010 3011 ; 3012 3013 3014 3015 // testProps is a generic CSS / DOM property test. 3016 3017 3018 3019 // In testing support for a given CSS property, it's legit to test: 3020 3021 // `elem.style[styleName] !== undefined` 3022 3023 // If the property is supported it will return an empty string, 3024 3025 // if unsupported it will return undefined. 3026 3027 3028 3029 // We'll take advantage of this quick test and skip setting a style 3030 3031 // on our modernizr element, but instead just testing undefined vs 3032 3033 // empty string. 3034 3035 3036 3037 // Property names can be provided in either camelCase or kebab-case. 3038 3039 3040 3041 function testProps(props, prefixed, value, skipValueTest) { 3042 3043 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 3044 3045 3046 3047 // Try native detect first 3048 3049 if (!is(value, 'undefined')) { 3050 3051 var result = nativeTestProps(props, value); 3052 3053 if (!is(result, 'undefined')) { 3054 3055 return result; 3056 3057 } 3058 3059 } 3060 3061 3062 3063 // Otherwise do it properly 3064 3065 var afterInit, i, propsLength, prop, before; 3066 3067 3068 3069 // If we don't have a style element, that means we're running async or after 3070 3071 // the core tests, so we'll need to create our own elements to use 3072 3073 3074 3075 // inside of an SVG element, in certain browsers, the `style` element is only 3076 3077 // defined for valid tags. Therefore, if `modernizr` does not have one, we 3078 3079 // fall back to a less used element and hope for the best. 3080 3081 // for strict XHTML browsers the hardly used samp element is used 3082 3083 var elems = ['modernizr', 'tspan', 'samp']; 3084 3085 while (!mStyle.style && elems.length) { 3086 3087 afterInit = true; 3088 3089 mStyle.modElem = createElement(elems.shift()); 3090 3091 mStyle.style = mStyle.modElem.style; 3092 3093 } 3094 3095 3096 3097 // Delete the objects if we created them. 3098 3099 function cleanElems() { 3100 3101 if (afterInit) { 3102 3103 delete mStyle.style; 3104 3105 delete mStyle.modElem; 3106 3107 } 3108 3109 } 3110 3111 3112 3113 propsLength = props.length; 3114 3115 for (i = 0; i < propsLength; i++) { 3116 3117 prop = props[i]; 3118 3119 before = mStyle.style[prop]; 3120 3121 3122 3123 if (contains(prop, '-')) { 3124 3125 prop = cssToDOM(prop); 3126 3127 } 3128 3129 3130 3131 if (mStyle.style[prop] !== undefined) { 3132 3133 3134 3135 // If value to test has been passed in, do a set-and-check test. 3136 3137 // 0 (integer) is a valid property value, so check that `value` isn't 3138 3139 // undefined, rather than just checking it's truthy. 3140 3141 if (!skipValueTest && !is(value, 'undefined')) { 3142 3143 3144 3145 // Needs a try catch block because of old IE. This is slow, but will 3146 3147 // be avoided in most cases because `skipValueTest` will be used. 3148 3149 try { 3150 3151 mStyle.style[prop] = value; 3152 3153 } catch (e) {} 3154 3155 3156 3157 // If the property value has changed, we assume the value used is 3158 3159 // supported. If `value` is empty string, it'll fail here (because 3160 3161 // it hasn't changed), which matches how browsers have implemented 3162 3163 // CSS.supports() 3164 3165 if (mStyle.style[prop] != before) { 3166 3167 cleanElems(); 3168 3169 return prefixed == 'pfx' ? prop : true; 3170 3171 } 3172 3173 } 3174 3175 // Otherwise just return true, or the property name if this is a 3176 3177 // `prefixed()` call 3178 3179 else { 3180 3181 cleanElems(); 3182 3183 return prefixed == 'pfx' ? prop : true; 3184 3185 } 3186 3187 } 3188 3189 } 3190 3191 cleanElems(); 3192 3193 return false; 3194 3195 } 3196 3197 3198 3199 ; 3200 3201 3202 3203 /** 3204 3205 * testPropsAll tests a list of DOM properties we want to check against. 3206 3207 * We specify literally ALL possible (known and/or likely) properties on 3208 3209 * the element including the non-vendor prefixed one, for forward- 3210 3211 * compatibility. 3212 3213 * 3214 3215 * @access private 3216 3217 * @function testPropsAll 3218 3219 * @param {string} prop - A string of the property to test for 3220 3221 * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip 3222 3223 * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against 3224 3225 * @param {string} [value] - A string of a css value 3226 3227 * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set 3228 3229 */ 3230 3231 function testPropsAll(prop, prefixed, elem, value, skipValueTest) { 3232 3233 3234 3235 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 3236
3237 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 3238 3239 3240 3241 // did they call .prefixed('boxSizing') or are we just testing a prop? 3242 3243 if (is(prefixed, 'string') || is(prefixed, 'undefined')) { 3244 3245 return testProps(props, prefixed, value, skipValueTest); 3246 3247 3248 3249 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) 3250 3251 } else { 3252 3253 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 3254 3255 return testDOMProps(props, prefixed, elem); 3256 3257 } 3258 3259 } 3260 3261 3262 3263 // Modernizr.testAllProps() investigates whether a given style property, 3264 3265 // or any of its vendor-prefixed variants, is recognized 3266 3267 // 3268 3269 // Note that the property names must be provided in the camelCase variant. 3270 3271 // Modernizr.testAllProps('boxSizing') 3272 3273 ModernizrProto.testAllProps = testPropsAll; 3274 3275 3276 3277 3278 3279 3280 3281 /** 3282 3283 * testAllProps determines whether a given CSS property is supported in the browser 3284 3285 * 3286 3287 * @memberof Modernizr 3288 3289 * @name Modernizr.testAllProps 3290 3291 * @optionName Modernizr.testAllProps() 3292 3293 * @optionProp testAllProps 3294 3295 * @access public 3296 3297 * @function testAllProps 3298 3299 * @param {string} prop - String naming the property to test (either camelCase or kebab-case) 3300 3301 * @param {string} [value] - String of the value to test 3302 3303 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection 3304 3305 * @example 3306 3307 * 3308 3309 * testAllProps determines whether a given CSS property, in some prefixed form, 3310 3311 * is supported by the browser. 3312 3313 * 3314 3315 * ```js 3316 3317 * testAllProps('boxSizing') // true 3318 3319 * ``` 3320 3321 * 3322 3323 * It can optionally be given a CSS value in string form to test if a property 3324 3325 * value is valid 3326 3327 * 3328 3329 * ```js 3330 3331 * testAllProps('display', 'block') // true 3332 3333 * testAllProps('display', 'penguin') // false 3334 3335 * ``` 3336 3337 * 3338 3339 * A boolean can be passed as a third parameter to skip the value check when 3340 3341 * native detection (@supports) isn't available. 3342 3343 * 3344 3345 * ```js 3346 3347 * testAllProps('shapeOutside', 'content-box', true); 3348 3349 * ``` 3350 3351 */ 3352 3353 3354 3355 function testAllProps(prop, value, skipValueTest) { 3356 3357 return testPropsAll(prop, undefined, undefined, value, skipValueTest); 3358 3359 } 3360 3361 ModernizrProto.testAllProps = testAllProps; 3362 3363 3364 3365/*! 3366 3367{ 3368 3369 "name": "Flexbox", 3370 3371 "property": "flexbox", 3372 3373 "caniuse": "flexbox", 3374 3375 "tags": ["css"], 3376 3377 "notes": [{ 3378 3379 "name": "The _new_ flexbox", 3380 3381 "href": "http://dev.w3.org/csswg/css3-flexbox" 3382 3383 }], 3384 3385 "warnings": [ 3386 3387 "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect." 3388 3389 ] 3390 3391} 3392 3393!*/ 3394 3395/* DOC 3396 3397Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container. 3398 3399*/ 3400 3401 3402 3403 Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', true)); 3404 3405 3406 3407/*! 3408 3409{ 3410 3411 "name": "Flexbox (legacy)", 3412 3413 "property": "flexboxlegacy", 3414 3415 "tags": ["css"], 3416 3417 "polyfills": ["flexie"], 3418 3419 "notes": [{ 3420 3421 "name": "The _old_ flexbox", 3422 3423 "href": "https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/" 3424 3425 }] 3426 3427} 3428 3429!*/ 3430 3431 3432 3433 Modernizr.addTest('flexboxlegacy', testAllProps('boxDirection', 'reverse', true)); 3434 3435 3436 3437/*! 3438 3439{ 3440 3441 "name": "Flexbox (tweener)", 3442 3443 "property": "flexboxtweener", 3444 3445 "tags": ["css"], 3446 3447 "polyfills": ["flexie"], 3448 3449 "notes": [{ 3450 3451 "name": "The _inbetween_ flexbox", 3452 3453 "href": "https://www.w3.org/TR/2011/WD-css3-flexbox-20111129/" 3454 3455 }], 3456 3457 "warnings": ["This represents an old syntax, not the latest standard syntax."] 3458 3459} 3460 3461!*/ 3462 3463 3464 3465 Modernizr.addTest('flexboxtweener', testAllProps('flexAlign', 'end', true)); 3466 3467 3468 3469/*! 3470 3471{ 3472 3473 "name": "Flex Line Wrapping", 3474 3475 "property": "flexwrap", 3476 3477 "tags": ["css", "flexbox"], 3478 3479 "notes": [{ 3480 3481 "name": "W3C Flexible Box Layout spec", 3482 3483 "href": "http://dev.w3.org/csswg/css3-flexbox" 3484 3485 }], 3486 3487 "warnings": [ 3488 3489 "Does not imply a modern implementation ? see documentation." 3490 3491 ] 3492 3493} 3494 3495!*/ 3496 3497/* DOC 3498 3499Detects support for the `flex-wrap` CSS property, part of Flexbox, which isnÂft present in all Flexbox implementations (notably Firefox). 3500 3501 3502 3503This featured in both the 'tweener' syntax (implemented by IE10) and the 'modern' syntax (implemented by others). This detect will return `true` for either of these implementations, as long as the `flex-wrap` property is supported. So to ensure the modern syntax is supported, use together with `Modernizr.flexbox`: 3504 3505 3506 3507```javascript 3508 3509if (Modernizr.flexbox && Modernizr.flexwrap) { 3510 3511 // Modern Flexbox with `flex-wrap` supported 3512 3513} 3514 3515else { 3516 3517 // Either old Flexbox syntax, or `flex-wrap` not supported 3518 3519} 3520 3521``` 3522 3523*/ 3524 3525 3526 3527 Modernizr.addTest('flexwrap', testAllProps('flexWrap', 'wrap', true)); 3528 3529 3530 3531/*! 3532 3533{ 3534 3535 "name": "CSS position: sticky", 3536 3537 "property": "csspositionsticky", 3538 3539 "tags": ["css"], 3540 3541 "builderAliases": ["css_positionsticky"], 3542 3543 "notes": [{ 3544 3545 "name": "Chrome bug report", 3546 3547 "href":"https://code.google.com/p/chromium/issues/detail?id=322972" 3548 3549 }], 3550 3551 "warnings": [ "using position:sticky on anything but top aligned elements is buggy in Chrome < 37 and iOS <=7+" ] 3552 3553} 3554 3555!*/ 3556 3557 3558 3559 // Sticky positioning - constrains an element to be positioned inside the 3560 3561 // intersection of its container box, and the viewport. 3562 3563 Modernizr.addTest('csspositionsticky', function() { 3564 3565 var prop = 'position:'; 3566 3567 var value = 'sticky'; 3568 3569 var el = createElement('a'); 3570 3571 var mStyle = el.style; 3572 3573 3574 3575 mStyle.cssText = prop + prefixes.join(value + ';' + prop).slice(0, -prop.length); 3576 3577 3578 3579 return mStyle.position.indexOf(value) !== -1; 3580 3581 }); 3582 3583 3584 3585 3586 3587 // Run each test 3588 3589 testRunner(); 3590 3591 3592 3593 // Remove the "no-js" class if it exists 3594 3595 setClasses(classes); 3596 3597 3598 3599 delete ModernizrProto.addTest; 3600 3601 delete ModernizrProto.addAsyncTest; 3602 3603 3604 3605 // Run the things that are supposed to run after the tests 3606 3607 for (var i = 0; i < Modernizr._q.length; i++) { 3608 3609 Modernizr._q[i](); 3610 3611 } 3612 3613 3614 3615 // Leak Modernizr namespace 3616 3617 window.Modernizr = Modernizr; 3618 3619 3620 3621 3622 3623; 3624 3625 3626 3627})(window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.