vendor: 10,922 bytes, lines 1-290
1/*! 2 * modernizr v3.2.0 3 * Build http://modernizr.com/download?-applicationcache-audio-backgroundsize-bgsizecover-borderimage-borderradius-boxshadow-canvas-canvastext-cssanimations-csscolumns-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-flexbox-fontface-generatedcontent-geolocation-hashchange-history-hsla-indexeddb-inlinesvg-input-inputtypes-localstorage-multiplebgs-opacity-postmessage-preserve3d-requestanimationframe-rgba-sessionstorage-smil-svg-svgclippaths-svgfilters-textshadow-touchevents-video-webgl-webglextensions-websockets-websqldatabase-webworkers-addtest-domprefixes-hasevent-mq-prefixed-prefixes-shiv-testallprops-testprop-teststyles-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 14 * MIT License 15 */ 16 17/* 18 * Modernizr tests which native CSS3 and HTML5 features are available in the 19 * current UA and makes the results available to you in two ways: as properties on 20 * a global `Modernizr` object, and as classes on the `<html>` element. This 21 * information allows you to progressively enhance your pages with a granular level 22 * of control over the experience. 23*/ 24 25;(function(window, document, undefined){ 26 var classes = []; 27 28 29 /** 30 * docElement is a convenience wrapper to grab the root element of the document 31 * 32 * @access private 33 * @returns {HTMLElement|SVGElement} The root element of the document 34 */ 35 36 var docElement = document.documentElement; 37 38 39 /** 40 * A convenience helper to check if the document we are running in is an SVG document 41 * 42 * @access private 43 * @returns {boolean} 44 */ 45 46 var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 47 48 49/** 50 * @optionName html5shiv 51 * @optionProp html5shiv 52 */ 53 54 // Take the html5 variable out of the html5shiv scope so we can return it. 55 var html5; 56 if (!isSVG) { 57 /** 58 * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 59 */ 60 ;(function(window, document) { 61 /*jshint evil:true */ 62 /** version */ 63 var version = '3.7.3'; 64 65 /** Preset options */ 66 var options = window.html5 || {}; 67 68 /** Used to skip problem elements */ 69 var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 70 71 /** Not all elements can be cloned in IE **/ 72 var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; 73 74 /** Detect whether the browser supports default html5 styles */ 75 var supportsHtml5Styles; 76 77 /** Name of the expando, to work with multiple documents or to re-shiv one document */ 78 var expando = '_html5shiv'; 79 80 /** The id for the the documents expando */ 81 var expanID = 0; 82 83 /** Cached data for each document */ 84 var expandoData = {}; 85 86 /** Detect whether the browser supports unknown elements */ 87 var supportsUnknownElements; 88 89 (function() { 90 try { 91 var a = document.createElement('a'); 92 a.innerHTML = '<xyz></xyz>'; 93 //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles 94 supportsHtml5Styles = ('hidden' in a); 95 96 supportsUnknownElements = a.childNodes.length == 1 || (function() { 97 // assign a false positive if unable to shiv 98 (document.createElement)('a'); 99 var frag = document.createDocumentFragment(); 100 return ( 101 typeof frag.cloneNode == 'undefined' || 102 typeof frag.createDocumentFragment == 'undefined' || 103 typeof frag.createElement == 'undefined' 104 ); 105 }()); 106 } catch(e) { 107 // assign a false positive if detection fails => unable to shiv 108 supportsHtml5Styles = true; 109 supportsUnknownElements = true; 110 } 111 112 }()); 113 114 /*--------------------------------------------------------------------------*/ 115 116 /** 117 * Creates a style sheet with the given CSS text and adds it to the document. 118 * @private 119 * @param {Document} ownerDocument The document. 120 * @param {String} cssText The CSS text. 121 * @returns {StyleSheet} The style element. 122 */ 123 function addStyleSheet(ownerDocument, cssText) { 124 var p = ownerDocument.createElement('p'), 125 parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 126 127 p.innerHTML = 'x<style>' + cssText + '</style>'; 128 return parent.insertBefore(p.lastChild, parent.firstChild); 129 } 130 131 /** 132 * Returns the value of `html5.elements` as an array. 133 * @private 134 * @returns {Array} An array of shived element node names. 135 */ 136 function getElements() { 137 var elements = html5.elements; 138 return typeof elements == 'string' ? elements.split(' ') : elements; 139 } 140 141 /** 142 * Extends the built-in list of html5 elements 143 * @memberOf html5 144 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv 145 * @param {Document} ownerDocument The context document. 146 */ 147 function addElements(newElements, ownerDocument) { 148 var elements = html5.elements; 149 if(typeof elements != 'string'){ 150 elements = elements.join(' '); 151 } 152 if(typeof newElements != 'string'){ 153 newElements = newElements.join(' '); 154 } 155 html5.elements = elements +' '+ newElements; 156 shivDocument(ownerDocument); 157 } 158 159 /** 160 * Returns the data associated to the given document 161 * @private 162 * @param {Document} ownerDocument The document. 163 * @returns {Object} An object of data. 164 */ 165 function getExpandoData(ownerDocument) { 166 var data = expandoData[ownerDocument[expando]]; 167 if (!data) { 168 data = {}; 169 expanID++; 170 ownerDocument[expando] = expanID; 171 expandoData[expanID] = data; 172 } 173 return data; 174 } 175 176 /** 177 * returns a shived element for the given nodeName and document 178 * @memberOf html5 179 * @param {String} nodeName name of the element 180 * @param {Document|DocumentFragment} ownerDocument The context document. 181 * @returns {Object} The shived element. 182 */ 183 function createElement(nodeName, ownerDocument, data){ 184 if (!ownerDocument) { 185 ownerDocument = document; 186 } 187 if(supportsUnknownElements){ 188 return ownerDocument.createElement(nodeName); 189 } 190 if (!data) { 191 data = getExpandoData(ownerDocument); 192 } 193 var node; 194 195 if (data.cache[nodeName]) { 196 node = data.cache[nodeName].cloneNode(); 197 } else if (saveClones.test(nodeName)) { 198 node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 199 } else { 200 node = data.createElem(nodeName); 201 } 202 203 // Avoid adding some elements to fragments in IE < 9 because 204 // * Attributes like `name` or `type` cannot be set/changed once an element 205 // is inserted into a document/fragment 206 // * Link elements with `src` attributes that are inaccessible, as with 207 // a 403 response, will cause the tab/window to crash 208 // * Script elements appended to fragments will execute when their `src` 209 // or `text` property is set 210 return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; 211 } 212 213 /** 214 * returns a shived DocumentFragment for the given document 215 * @memberOf html5 216 * @param {Document} ownerDocument The context document. 217 * @returns {Object} The shived DocumentFragment. 218 */ 219 function createDocumentFragment(ownerDocument, data){ 220 if (!ownerDocument) { 221 ownerDocument = document; 222 } 223 if(supportsUnknownElements){ 224 return ownerDocument.createDocumentFragment(); 225 } 226 data = data || getExpandoData(ownerDocument); 227 var clone = data.frag.cloneNode(), 228 i = 0, 229 elems = getElements(), 230 l = elems.length; 231 for(;i<l;i++){ 232 clone.createElement(elems[i]); 233 } 234 return clone; 235 } 236 237 /** 238 * Shivs the `createElement` and `createDocumentFragment` methods of the document. 239 * @private 240 * @param {Document|DocumentFragment} ownerDocument The document. 241 * @param {Object} data of the document. 242 */ 243 function shivMethods(ownerDocument, data) { 244 if (!data.cache) { 245 data.cache = {}; 246 data.createElem = ownerDocument.createElement; 247 data.createFrag = ownerDocument.createDocumentFragment; 248 data.frag = data.createFrag(); 249 } 250 251 252 ownerDocument.createElement = function(nodeName) { 253 //abort shiv 254 if (!html5.shivMethods) { 255 return data.createElem(nodeName); 256 } 257 return createElement(nodeName, ownerDocument, data); 258 }; 259 260 ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' + 261 'var n=f.cloneNode(),c=n.createElement;' + 262 'h.shivMethods&&(' + 263 // unroll the `createElement` calls 264 getElements().join().replace(/[\w\-:]+/g, function(nodeName) { 265 data.createElem(nodeName); 266 data.frag.createElement(nodeName); 267 return 'c("' + nodeName + '")'; 268 }) + 269 ');return n}' 270 )(html5, data.frag); 271 } 272 273 /*--------------------------------------------------------------------------*/ 274 275 /** 276 * Shivs the given document. 277 * @memberOf html5 278 * @param {Document} ownerDocument The document to shiv. 279 * @returns {Document} The shived document. 280 */ 281 function shivDocument(ownerDocument) { 282 if (!ownerDocument) { 283 ownerDocument = document; 284 } 285 var data = getExpandoData(ownerDocument); 286 287 if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) { 288 data.hasCSS = !!addStyleSheet(ownerDocument, 289 // corrects block display not defined in IE6/7/8/9 290 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{dis
290play:block}' + 291 // adds styling not present in IE6/7/8/9 292 'mark{background:#FF0;color:#000}' + 293 // hides non-rendered elements 294 'template{display:none}' 295 ); 296 } 297 if (!supportsUnknownElements) { 298 shivMethods(ownerDocument, data); 299 } 300 return ownerDocument; 301 } 302 303 /*--------------------------------------------------------------------------*/ 304 305 /** 306 * The `html5` object is exposed so that more elements can be shived and 307 * existing shiving can be detected on iframes. 308 * @type Object 309 * @example 310 * 311 * // options can be changed before the script is included 312 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false }; 313 */ 314 var html5 = { 315 316 /** 317 * An array or space separated string of node names of the elements to shiv. 318 * @memberOf html5 319 * @type Array|String 320 */ 321 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video', 322 323 /** 324 * current version of html5shiv 325 */ 326 'version': version, 327 328 /** 329 * A flag to indicate that the HTML5 style sheet should be inserted. 330 * @memberOf html5 331 * @type Boolean 332 */ 333 'shivCSS': (options.shivCSS !== false), 334 335 /** 336 * Is equal to true if a browser supports creating unknown/HTML5 elements 337 * @memberOf html5 338 * @type boolean 339 */ 340 'supportsUnknownElements': supportsUnknownElements, 341 342 /** 343 * A flag to indicate that the document's `createElement` and `createDocumentFragment` 344 * methods should be overwritten. 345 * @memberOf html5 346 * @type Boolean 347 */ 348 'shivMethods': (options.shivMethods !== false), 349 350 /** 351 * A string to describe the type of `html5` object ("default" or "default print"). 352 * @memberOf html5
353 * @type String 354 */ 355 'type': 'default', 356 357 // shivs the document according to the specified `html5` object options 358 'shivDocument': shivDocument, 359 360 //creates a shived element 361 createElement: createElement, 362 363 //creates a shived documentFragment 364 createDocumentFragment: createDocumentFragment, 365 366 //extends list of elements 367 addElements: addElements 368 }; 369 370 /*--------------------------------------------------------------------------*/ 371 372 // expose html5 373 window.html5 = html5; 374 375 // shiv the document 376 shivDocument(document); 377 378 if(typeof module == 'object' && module.exports){ 379 module.exports = html5; 380 } 381 382 }(typeof window !== "undefined" ? window : this, document)); 383 } 384; 385 386 var tests = []; 387 388 389 /** 390 * 391 * ModernizrProto is the constructor for Modernizr 392 * 393 * @class 394 * @access public 395 */ 396 397 var ModernizrProto = { 398 // The current version, dummy 399 _version: '3.2.0', 400 401 // Any settings that don't work as separate modules 402 // can go in here as configuration. 403 _config: { 404 'classPrefix' : '', 405 'enableClasses' : true, 406 'enableJSClass' : true, 407 'usePrefixes' : true 408 }, 409 410 // Queue of tests 411 _q: [], 412 413 // Stub these for people who are listening 414 on: function(test, cb) { 415 // I don't really think people should do this, but we can 416 // safe guard it a bit. 417 // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 418 // This is in case people listen to synchronous tests. I would leave it out, 419 // but the code to *disallow* sync tests in the real version of this 420 // function is actually larger than this. 421 var self = this; 422 setTimeout(function() { 423 cb(self[test]); 424 }, 0); 425 }, 426 427 addTest: function(name, fn, options) { 428 tests.push({name : name, fn : fn, options : options}); 429 }, 430 431 addAsyncTest: function(fn) { 432 tests.push({name : null, fn : fn}); 433 } 434 }; 435 436 437 438 // Fake some of Object.create so we can force non test results to be non "own" properties. 439 var Modernizr = function() {}; 440 Modernizr.prototype = ModernizrProto; 441 442 // Leak modernizr globally when you `require` it rather than force it here. 443 // Overwrite name so constructor name is nicer :D 444 Modernizr = new Modernizr(); 445 446 447 448 /** 449 * setClasses takes an array of class names and adds them to the root element 450 * 451 * @access private 452 * @function setClasses 453 * @param {string[]} classes - Array of class names 454 */ 455 456 // Pass in an and array of class names, e.g.:
457 // ['no-webp', 'borderradius', ...] 458 function setClasses(classes) { 459 var className = docElement.className; 460 var classPrefix = Modernizr._config.classPrefix || ''; 461 462 if (isSVG) { 463 className = className.baseVal; 464 } 465 466 // Change `no-js` to `js` (independently of the `enableClasses` option) 467 // Handle classPrefix on this too 468 if (Modernizr._config.enableJSClass) { 469 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 470 className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 471 } 472 473 if (Modernizr._config.enableClasses) { 474 // Add the new classes 475 className += ' ' + classPrefix + classes.join(' ' + classPrefix); 476 isSVG ? docElement.className.baseVal = className : docElement.className = className; 477 } 478 479 } 480 481 ; 482/*! 483{ 484 "name": "Application Cache", 485 "property": "applicationcache", 486 "caniuse": "offline-apps", 487 "tags": ["storage", "offline"], 488 "notes": [{ 489 "name": "MDN documentation", 490 "href": "https://developer.mozilla.org/en/docs/HTML/Using_the_application_cache" 491 }], 492 "polyfills": ["html5gears"] 493} 494!*/ 495/* DOC 496Detects support for the Application Cache, for storing data to enable web-based applications run offline. 497 498The API has been [heavily criticized](http://alistapart.com/article/application-cache-is-a-douchebag) and discussions are underway to address this. 499*/ 500 501 Modernizr.addTest('applicationcache', 'applicationCache' in window); 502 503/*! 504{ 505 "name": "Geolocation API", 506 "property": "geolocation", 507 "caniuse": "geolocation", 508 "tags": ["media"], 509 "notes": [{ 510 "name": "MDN documentation", 511 "href": "https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation" 512 }], 513 "polyfills": [ 514 "joshuabell-polyfill", 515 "webshims", 516 "geo-location-javascript", 517 "geolocation-api-polyfill" 518 ] 519} 520!*/ 521/* DOC 522Detects support for the Geolocation API for users to provide their location to web applications. 523*/ 524 525 // geolocation is often considered a trivial feature detect... 526 // Turns out, it's quite tricky to get right: 527 // 528 // Using !!navigator.geolocation does two things we don't want. It: 529 // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513 530 // 2. Disables page caching in WebKit: webk.it/43956 531 // 532 // Meanwhile, in Firefox < 8, an about:config setting could expose 533 // a false positive that would throw an exception: bugzil.la/688158 534 535 Modernizr.addTest('geolocation', 'geolocation' in navigator); 536 537/*! 538{ 539 "name": "History API", 540 "property": "history", 541 "caniuse": "history", 542 "tags": ["history"], 543 "authors": ["Hay Kranen", "Alexander Farkas"], 544 "notes": [{ 545 "name": "W3C Spec", 546 "href": "http://www.w3.org/TR/html51/browsers.html#the-history-interface" 547 }, { 548 "name": "MDN documentation", 549 "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.history" 550 }], 551 "polyfills": ["historyjs", "html5historyapi"] 552} 553!*/ 554/* DOC 555Detects support for the History API for manipulating the browser session history. 556*/ 557 558 Modernizr.addTest('history', function() { 559 // Issue #733 560 // The stock browser on Android 2.2 & 2.3, and 4.0.x returns positive on history support 561 // Unfortunately support is really buggy and there is no clean way to detect 562 // these bugs, so we fall back to a user agent sniff :( 563 var ua = navigator.userAgent; 564 565 // We only want Android 2 and 4.0, stock browser, and not Chrome which identifies 566 // itself as 'Mobile Safari' as well, nor Windows Phone (issue #1471). 567 if ((ua.indexOf('Android 2.') !== -1 || 568 (ua.indexOf('Android 4.0') !== -1)) && 569 ua.indexOf('Mobile Safari') !== -1 && 570 ua.indexOf('Chrome') === -1 && 571 ua.indexOf('Windows Phone') === -1) { 572 return false; 573 } 574 575 // Return the regular check 576 return (window.history && 'pushState' in window.history); 577 }); 578 579/*! 580{ 581 "name": "postMessage", 582 "property": "postmessage", 583 "caniuse": "x-doc-messaging", 584 "notes": [{ 585 "name": "W3C Spec", 586 "href": "http://www.w3.org/TR/html5/comms.html#posting-messages" 587 }], 588 "polyfills": ["easyxdm", "postmessage-jquery"] 589} 590!*/ 591/* DOC 592Detects support for the `window.postMessage` protocol for cross-document messaging. 593*/ 594 595 Modernizr.addTest('postmessage', 'postMessage' in window); 596 597/*! 598{ 599 "name": "SVG", 600 "property": "svg", 601 "caniuse": "svg", 602 "tags": ["svg"], 603 "authors": ["Erik Dahlstrom"], 604 "polyfills": [ 605 "svgweb", 606 "raphael", 607 "amplesdk", 608 "canvg", 609 "svg-boilerplate", 610 "sie", 611 "dojogfx", 612 "fabricjs" 613 ] 614} 615!*/ 616/* DOC 617Detects support for SVG in `<embed>` or `<object>` elements. 618*/ 619 620 Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect); 621 622/*! 623{ 624 "name": "WebSockets Support", 625 "property": "websockets", 626 "authors": ["Phread [fearphage]", "Mike Sherov [mikesherov]", "Burak Yigit Kaya [BYK]"], 627 "caniuse": "websockets", 628 "tags": ["html5"], 629 "warnings": [ 630 "This test will reject any old version of WebSockets even if it is not prefixed such as in Safari 5.1" 631 ], 632 "notes": [{ 633 "name": "CLOSING State and Spec", 634 "href": "http://www.w3.org/TR/websockets/#the-websocket-interface" 635 }], 636 "polyfills": [ 637 "sockjs", 638 "socketio", 639 "kaazing-websocket-gateway", 640 "websocketjs", 641 "atmosphere",
642 "graceful-websocket", 643 "portal", 644 "datachannel" 645 ] 646} 647!*/ 648 649 Modernizr.addTest('websockets', 'WebSocket' in window && window.WebSocket.CLOSING === 2); 650 651/*! 652{ 653 "name": "Local Storage", 654 "property": "localstorage", 655 "caniuse": "namevalue-storage", 656 "tags": ["storage"], 657 "knownBugs": [], 658 "notes": [], 659 "warnings": [], 660 "polyfills": [ 661 "joshuabell-polyfill", 662 "cupcake", 663 "storagepolyfill", 664 "amplifyjs", 665 "yui-cacheoffline" 666 ] 667} 668!*/ 669 670 // In FF4, if disabled, window.localStorage should === null. 671 672 // Normally, we could not test that directly and need to do a 673 // `('localStorage' in window) && ` test first because otherwise Firefox will 674 // throw bugzil.la/365772 if cookies are disabled 675 676 // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem 677 // will throw the exception: 678 // QUOTA_EXCEEDED_ERROR DOM Exception 22. 679 // Peculiarly, getItem and removeItem calls do not throw. 680 681 // Because we are forced to try/catch this, we'll go aggressive. 682 683 // Just FWIW: IE8 Compat mode supports these features completely: 684 // www.quirksmode.org/dom/html5.html 685 // But IE8 doesn't support either with local files 686 687 Modernizr.addTest('localstorage', function() { 688 var mod = 'modernizr'; 689 try { 690 localStorage.setItem(mod, mod); 691 localStorage.removeItem(mod); 692 return true; 693 } catch (e) { 694 return false; 695 } 696 }); 697 698/*! 699{ 700 "name": "Session Storage", 701 "property": "sessionstorage", 702 "tags": ["storage"], 703 "polyfills": ["joshuabell-polyfill", "cupcake", "sessionstorage"] 704} 705!*/ 706 707 // Because we are forced to try/catch this, we'll go aggressive. 708 709 // Just FWIW: IE8 Compat mode supports these features completely: 710 // www.quirksmode.org/dom/html5.html 711 // But IE8 doesn't support either with local files 712 Modernizr.addTest('sessionstorage', function() { 713 var mod = 'modernizr'; 714 try { 715 sessionStorage.setItem(mod, mod); 716 sessionStorage.removeItem(mod); 717 return true; 718 } catch (e) { 719 return false; 720 } 721 }); 722 723/*! 724{ 725 "name": "Web SQL Database", 726 "property": "websqldatabase", 727 "caniuse": "sql-storage", 728 "tags": ["storage"] 729} 730!*/ 731 732 // Chrome incognito mode used to throw an exception when using openDatabase 733 // It doesn't anymore. 734 Modernizr.addTest('websqldatabase', 'openDatabase' in window); 735 736/*! 737{ 738 "name": "SVG filters", 739 "property": "svgfilters", 740 "caniuse": "svg-filters", 741 "tags": ["svg"], 742 "builderAliases": ["svg_filters"], 743 "authors": ["Erik Dahlstrom"], 744 "notes": [{ 745 "name": "W3C Spec", 746 "href": "http://www.w3.org/TR/SVG11/filters.html"
747 }] 748} 749!*/ 750 751 // Should fail in Safari: http://stackoverflow.com/questions/9739955/feature-detecting-support-for-svg-filters. 752 Modernizr.addTest('svgfilters', function() { 753 var result = false; 754 try { 755 result = 'SVGFEColorMatrixElement' in window && 756 SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE == 2; 757 } 758 catch (e) {} 759 return result; 760 }); 761 762/*! 763{ 764 "name": "Web Workers", 765 "property": "webworkers", 766 "caniuse" : "webworkers", 767 "tags": ["performance", "workers"], 768 "notes": [{ 769 "name": "W3C Reference", 770 "href": "http://www.w3.org/TR/workers/" 771 }, { 772 "name": "HTML5 Rocks article", 773 "href": "http://www.html5rocks.com/en/tutorials/workers/basics/" 774 }, { 775 "name": "MDN documentation", 776 "href": "https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers" 777 }], 778 "polyfills": ["fakeworker", "html5shims"] 779} 780!*/ 781/* DOC 782Detects support for the basic `Worker` API from the Web Workers spec. Web Workers provide a simple means for web content to run scripts in background threads. 783*/ 784 785 Modernizr.addTest('webworkers', 'Worker' in window); 786 787 788 /** 789 * List of property values to set for css tests. See ticket #21 790 * http://git.io/vUGl4 791 * 792 * @memberof Modernizr 793 * @name Modernizr._prefixes 794 * @optionName Modernizr._prefixes 795 * @optionProp prefixes 796 * @access public 797 * @example 798 * 799 * Modernizr._prefixes is the internal list of prefixes that we test against 800 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply 801 * an array of kebab-case vendor prefixes you can use within your code. 802 * 803 * Some common use cases include 804 * 805 * Generating all possible prefixed version of a CSS property 806 * ```js 807 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); 808 * 809 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' 810 * ``` 811 * 812 * Generating all possible prefixed version of a CSS value 813 * ```js 814 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; 815 * 816 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' 817 * ``` 818 */ 819 820 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : []); 821 822 // expose these for the plugin API. Look in the source for how to join() them against your input 823 ModernizrProto._prefixes = prefixes; 824 825 826 827 /** 828 * is returns a boolean if the typeof an obj is exactly type. 829 * 830 * @access private 831 * @function is 832 * @param {*} obj - A thing we want to check the type of 833 * @param {string} type - A string to compare the typeof against 834 * @returns {boolean} 835 */ 836 837 function is(obj, type) { 838 return typeof obj === type; 839 } 840 ; 841 842 /** 843 * Run through all tests and detect their support in the current UA. 844 * 845 * @access private 846 */ 847 848 function testRunner() { 849 var featureNames; 850 var feature; 851 var aliasIdx; 852 var result; 853 var nameIdx; 854 var featureName; 855 var featureNameSplit; 856 857 for (var featureIdx in tests) { 858 if (tests.hasOwnProperty(featureIdx)) { 859 featureNames = []; 860 feature = tests[featureIdx]; 861 // run the test, throw the return value into the Modernizr, 862 // then based on that boolean, define an appropriate className 863 // and push it into an array of classes we'll join later. 864 // 865 // If there is no name, it's an 'async' test that is run, 866 // but not directly added to the object. That should 867 // be done with a post-run addTest call. 868 if (feature.name) { 869 featureNames.push(feature.name.toLowerCase()); 870 871 if (feature.options && feature.options.aliases && feature.options.aliases.length) { 872 // Add all the aliases into the names list 873 for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 874 featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 875 } 876 } 877 } 878 879 // Run the test, or use the raw value if it's not a function 880 result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 881 882 883 // Set each of the names on the Modernizr object 884 for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 885 featureName = featureNames[nameIdx];
886 // Support dot properties as sub tests. We don't do checking to make sure 887 // that the implied parent tests have been added. You must call them in 888 // order (either in the test, or make the parent test a dependency). 889 // 890 // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 891 // hashtag famous last words 892 featureNameSplit = featureName.split('.'); 893 894 if (featureNameSplit.length === 1) { 895 Modernizr[featureNameSplit[0]] = result; 896 } else { 897 // cast to a Boolean, if not one already 898 /* jshint -W053 */ 899 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 900 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 901 } 902 903 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 904 } 905 906 classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 907 } 908 } 909 } 910 } 911 ; 912 913 /** 914 * If the browsers follow the spec, then they would expose vendor-specific style as: 915 * elem.style.WebkitBorderRadius 916 * instead of something like the following, which would be technically incorrect: 917 * elem.style.webkitBorderRadius 918 919 * Webkit ghosts their properties in lowercase but Opera & Moz do not. 920 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ 921 * erik.eae.net/archives/2008/03/10/21.48.10/ 922 923 * More here: github.com/Modernizr/Modernizr/issues/issue/21 924 * 925 * @access private 926 * @returns {string} The string representing the vendor-specific style properties 927 */ 928 929 var omPrefixes = 'Moz O ms Webkit'; 930 931 932 /** 933 * List of JavaScript DOM values used for tests 934 * 935 * @memberof Modernizr 936 * @name Modernizr._domPrefixes 937 * @optionName Modernizr._domPrefixes 938 * @optionProp domPrefixes 939 * @access public 940 * @example 941 * 942 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather 943 * than kebab-case properties, all properties are their Capitalized variant 944 * 945 * ```js 946 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; 947 * ``` 948 */ 949 950 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); 951 ModernizrProto._domPrefixes = domPrefixes; 952 953 954 /** 955 * createElement is a convenience wrapper around document.createElement. Since we 956 * use createElement all over the place, this allows for (slightly) smaller code 957 * as well as abstracting away issues with creating elements in contexts other than 958 * HTML documents (e.g. SVG documents). 959 * 960 * @access private 961 * @function createElement 962 * @returns {HTMLElement|SVGElement} An HTML or SVG element 963 */ 964 965 function createElement() { 966 if (typeof document.createElement !== 'function') { 967 // This is the case in IE7, where the type of createElement is "object". 968 // For this reason, we cannot call apply() as Object is not a Function. 969 return document.createElement(arguments[0]); 970 } else if (isSVG) { 971 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 972 } else { 973 return document.createElement.apply(document, arguments); 974 } 975 } 976 977 ; 978 979 /** 980 * Modernizr.hasEvent() detects support for a given event 981 * 982 * @memberof Modernizr 983 * @name Modernizr.hasEvent 984 * @optionName Modernizr.hasEvent() 985 * @optionProp hasEvent 986 * @access public 987 * @function hasEvent 988 * @param {string|*} eventName is the name of an event to test for (e.g. "resize") 989 * @param {Element|string} [element=HTMLDivElement] is the element|document|window|tagName to test on 990 * @returns {boolean} 991 * @example 992 * `Modernizr.hasEvent` lets you determine if the browser supports a supplied event. 993 * By default, it does this detection on a div element 994 * 995 * ```js 996 * hasEvent('blur') // true; 997 * ``` 998 * 999 * However, you are able to give an object as a second argument to hasEvent to 1000 * detect an event on something other than a div. 1001 * 1002 * ```js 1003 * hasEvent('devicelight', window) // true; 1004 * ``` 1005 * 1006 */ 1007 1008 var hasEvent = (function(undefined) { 1009 1010 // Detect whether event support can be detected via `in`. Test on a DOM element 1011 // using the "blur" event b/c it should always exist. bit.ly/event-detection 1012 var needsFallback = !('onblur' in document.documentElement); 1013 1014 function inner(eventName, element) { 1015 1016 var isSupported; 1017 if (!eventName) { return false; } 1018 if (!element || typeof element === 'string') { 1019 element = createElement(element || 'div'); 1020 } 1021 1022 // Testing via the `in` operator is sufficient for modern browsers and IE. 1023 // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and 1024 // "resize", whereas `in` "catches" those. 1025 eventName = 'on' + eventName; 1026 isSupported = eventName in element; 1027 1028 // Fallback technique for old Firefox - bit.ly/event-detection 1029 if (!isSupported && needsFallback) { 1030 if (!element.setAttribute) { 1031 // Switch to generic element if it lacks `setAttribute`. 1032 // It could be the `document`, `window`, or something else. 1033 element = createElement('div'); 1034 } 1035 1036 element.setAttribute(eventName, ''); 1037 isSupported = typeof element[eventName] === 'function'; 1038 1039 if (element[eventName] !== undefined) { 1040 // If property was created, "remove it" by setting value to `undefined`. 1041 element[eventName] = undefined; 1042 } 1043 element.removeAttribute(eventName); 1044 } 1045 1046 return isSupported; 1047 } 1048 return inner; 1049 })();
1050 1051 1052 ModernizrProto.hasEvent = hasEvent; 1053 1054/*! 1055{ 1056 "name": "Hashchange event", 1057 "property": "hashchange", 1058 "caniuse": "hashchange", 1059 "tags": ["history"], 1060 "notes": [{ 1061 "name": "MDN documentation", 1062 "href": "https://developer.mozilla.org/en-US/docs/Web/API/window.onhashchange" 1063 }], 1064 "polyfills": [ 1065 "jquery-hashchange", 1066 "moo-historymanager", 1067 "jquery-ajaxy", 1068 "hasher", 1069 "shistory" 1070 ] 1071} 1072!*/ 1073/* DOC 1074Detects support for the `hashchange` event, fired when the current location fragment changes. 1075*/ 1076 1077 Modernizr.addTest('hashchange', function() { 1078 if (hasEvent('hashchange', window) === false) { 1079 return false; 1080 } 1081 1082 // documentMode logic from YUI to filter out IE8 Compat Mode 1083 // which false positives. 1084 return (document.documentMode === undefined || document.documentMode > 7); 1085 }); 1086 1087/*! 1088{ 1089 "name" : "HTML5 Audio Element", 1090 "property": "audio", 1091 "tags" : ["html5", "audio", "media"] 1092} 1093!*/ 1094/* DOC 1095Detects the audio element 1096*/ 1097 1098 // This tests evaluates support of the audio element, as well as 1099 // testing what types of content it supports. 1100 // 1101 // We're using the Boolean constructor here, so that we can extend the value 1102 // e.g. Modernizr.audio // true 1103 // Modernizr.audio.ogg // 'probably' 1104 // 1105 // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 1106 // thx to NielsLeenheer and zcorpan 1107 1108 // Note: in some older browsers, "no" was a return value instead of empty string. 1109 // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 1110 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 1111 Modernizr.addTest('audio', function() { 1112 /* jshint -W053 */ 1113 var elem = createElement('audio'); 1114 var bool = false; 1115 1116 try { 1117 if (bool = !!elem.canPlayType) { 1118 bool = new Boolean(bool); 1119 bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''); 1120 bool.mp3 = elem.canPlayType('audio/mpeg; codecs="mp3"') .replace(/^no$/, ''); 1121 bool.opus = elem.canPlayType('audio/ogg; codecs="opus"') .replace(/^no$/, ''); 1122 1123 // Mimetypes accepted: 1124 // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements 1125 // bit.ly/iphoneoscodecs 1126 bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/, ''); 1127 bool.m4a = (elem.canPlayType('audio/x-m4a;') || 1128 elem.canPlayType('audio/aac;')) .replace(/^no$/, ''); 1129 } 1130 } catch (e) { } 1131 1132 return bool; 1133 }); 1134 1135/*! 1136{ 1137 "name": "Canvas", 1138 "property": "canvas", 1139 "caniuse": "canvas", 1140 "tags": ["canvas", "graphics"], 1141 "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"] 1142} 1143!*/ 1144/* DOC 1145Detects support for the `<canvas>` element for 2D drawing. 1146*/ 1147 1148 // On the S60 and BB Storm, getContext exists, but always returns undefined 1149 // so we actually have to call getContext() to verify 1150 // github.com/Modernizr/Modernizr/issues/issue/97/ 1151 Modernizr.addTest('canvas', function() { 1152 var elem = createElement('canvas'); 1153 return !!(elem.getContext && elem.getContext('2d')); 1154 }); 1155 1156/*! 1157{ 1158 "name": "Canvas text", 1159 "property": "canvastext", 1160 "caniuse": "canvas-text", 1161 "tags": ["canvas", "graphics"], 1162 "polyfills": ["canvastext"] 1163} 1164!*/ 1165/* DOC 1166Detects support for the text APIs for `<canvas>` elements. 1167*/ 1168 1169 Modernizr.addTest('canvastext', function() { 1170 if (Modernizr.canvas === false) { 1171 return false; 1172 } 1173 return typeof createElement('canvas').getContext('2d').fillText == 'function'; 1174 }); 1175 1176/*! 1177{ 1178 "name": "HTML5 Video", 1179 "property": "video", 1180 "caniuse": "video", 1181 "tags": ["html5"], 1182 "knownBugs": [ 1183 "Without QuickTime, `Modernizr.video.h264` will be `undefined`; http://github.com/Modernizr/Modernizr/issues/546" 1184 ], 1185 "polyfills": [ 1186 "html5media", 1187 "mediaelementjs", 1188 "sublimevideo", 1189 "videojs", 1190 "leanbackplayer", 1191 "videoforeverybody" 1192 ] 1193} 1194!*/ 1195/* DOC 1196Detects support for the video element, as well as testing what types of content it supports. 1197 1198Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.: 1199 1200```javascript 1201Modernizr.video // true 1202Modernizr.video.ogg // 'probably' 1203``` 1204*/ 1205 1206 // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 1207 // thx to NielsLeenheer and zcorpan 1208 1209 // Note: in some older browsers, "no" was a return value instead of empty string. 1210 // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 1211 // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 1212 1213 Modernizr.addTest('video', function() { 1214 /* jshint -W053 */ 1215 var elem = createElement('video'); 1216 var bool = false; 1217 1218 // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 1219 try { 1220 if (bool = !!elem.canPlayType) { 1221 bool = new Boolean(bool); 1222 bool.ogg = elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, ''); 1223
1224 // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546 1225 bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, ''); 1226 1227 bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, ''); 1228 1229 bool.vp9 = elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/, ''); 1230 1231 bool.hls = elem.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/, ''); 1232 } 1233 } catch (e) {} 1234 1235 return bool; 1236 }); 1237 1238/*! 1239{ 1240 "name": "WebGL", 1241 "property": "webgl", 1242 "caniuse": "webgl", 1243 "tags": ["webgl", "graphics"], 1244 "polyfills": ["jebgl", "cwebgl", "iewebgl"] 1245} 1246!*/ 1247 1248 Modernizr.addTest('webgl', function() { 1249 var canvas = createElement('canvas'); 1250 var supports = 'probablySupportsContext' in canvas ? 'probablySupportsContext' : 'supportsContext'; 1251 if (supports in canvas) { 1252 return canvas[supports]('webgl') || canvas[supports]('experimental-webgl'); 1253 } 1254 return 'WebGLRenderingContext' in window; 1255 }); 1256 1257/*! 1258{ 1259 "name": "CSS Gradients", 1260 "caniuse": "css-gradients", 1261 "property": "cssgradients", 1262 "tags": ["css"], 1263 "knownBugs": ["False-positives on webOS (https://github.com/Modernizr/Modernizr/issues/202)"], 1264 "notes": [{ 1265 "name": "Webkit Gradient Syntax", 1266 "href": "http://webkit.org/blog/175/introducing-css-gradients/" 1267 },{ 1268 "name": "Mozilla Linear Gradient Syntax", 1269 "href": "http://developer.mozilla.org/en/CSS/-moz-linear-gradient" 1270 },{ 1271 "name": "Mozilla Radial Gradient Syntax", 1272 "href": "http://developer.mozilla.org/en/CSS/-moz-radial-gradient" 1273 },{ 1274 "name": "W3C Gradient Spec", 1275 "href": "dev.w3.org/csswg/css3-images/#gradients-" 1276 }] 1277} 1278!*/ 1279 1280 1281 Modernizr.addTest('cssgradients', function() { 1282 1283 var str1 = 'background-image:'; 1284 var str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));'; 1285 var css = ''; 1286 var angle; 1287 1288 for (var i = 0, len = prefixes.length - 1; i < len; i++) { 1289 angle = (i === 0 ? 'to ' : ''); 1290 css += str1 + prefixes[i] + 'linear-gradient(' + angle + 'left top, #9f9, white);'; 1291 } 1292 1293 if (Modernizr._config.usePrefixes) { 1294 // legacy webkit syntax (FIXME: remove when syntax not in use anymore) 1295 css += str1 + '-webkit-' + str2; 1296 } 1297 1298 var elem = createElement('a'); 1299 var style = elem.style; 1300 style.cssText = css; 1301 1302 // IE6 returns undefined so cast to string 1303 return ('' + style.backgroundImage).indexOf('gradient') > -1; 1304 }); 1305 1306/*! 1307{
1308 "name": "CSS Multiple Backgrounds", 1309 "caniuse": "multibackgrounds", 1310 "property": "multiplebgs", 1311 "tags": ["css"] 1312} 1313!*/ 1314 1315 // Setting multiple images AND a color on the background shorthand property 1316 // and then querying the style.background property value for the number of 1317 // occurrences of "url(" is a reliable method for detecting ACTUAL support for this! 1318 1319 Modernizr.addTest('multiplebgs', function() { 1320 var style = createElement('a').style; 1321 style.cssText = 'background:url(https://),url(https://),red url(https://)'; 1322 1323 // If the UA supports multiple backgrounds, there should be three occurrences 1324 // of the string "url(" in the return value for elemStyle.background 1325 return (/(url\s*\(.*?){3}/).test(style.background); 1326 }); 1327 1328/*! 1329{ 1330 "name": "CSS Opacity", 1331 "caniuse": "css-opacity", 1332 "property": "opacity", 1333 "tags": ["css"] 1334} 1335!*/ 1336 1337 // Browsers that actually have CSS Opacity implemented have done so 1338 // according to spec, which means their return values are within the 1339 // range of [0.0,1.0] - including the leading zero. 1340 1341 Modernizr.addTest('opacity', function() { 1342 var style = createElement('a').style; 1343 style.cssText = prefixes.join('opacity:.55;'); 1344 1345 // The non-literal . in this regex is intentional: 1346 // German Chrome returns this value as 0,55 1347 // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632 1348 return (/^0.55$/).test(style.opacity); 1349 }); 1350 1351/*! 1352{ 1353 "name": "CSS rgba", 1354 "caniuse": "css3-colors", 1355 "property": "rgba", 1356 "tags": ["css"], 1357 "notes": [{ 1358 "name": "CSSTricks Tutorial", 1359 "href": "http://css-tricks.com/rgba-browser-support/" 1360 }] 1361} 1362!*/ 1363 1364 Modernizr.addTest('rgba', function() { 1365 var style = createElement('a').style; 1366 style.cssText = 'background-color:rgba(150,255,150,.5)'; 1367 1368 return ('' + style.backgroundColor).indexOf('rgba') > -1; 1369 }); 1370 1371/*! 1372{ 1373 "name": "Inline SVG", 1374 "property": "inlinesvg", 1375 "caniuse": "svg-html5", 1376 "tags": ["svg"], 1377 "notes": [{ 1378 "name": "Test page", 1379 "href": "http://paulirish.com/demo/inline-svg" 1380 }, { 1381 "name": "Test page and results", 1382 "href": "http://codepen.io/eltonmesquita/full/GgXbvo/" 1383 }], 1384 "polyfills": ["inline-svg-polyfill"], 1385 "knownBugs": ["False negative on some Chromia browsers."] 1386} 1387!*/ 1388/* DOC 1389Detects support for inline SVG in HTML (not within XHTML). 1390*/ 1391 1392 Modernizr.addTest('inlinesvg', function() { 1393 var div = createElement('div'); 1394 div.innerHTML = '<svg/>'; 1395 return (typeof SVGRect != 'undefined' && div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg'; 1396 }); 1397 1398/*! 1399{ 1400 "name": "WebGL Extensions", 1401 "property": "webglextensions", 1402 "tags": ["webgl", "graphics"], 1403 "builderAliases": ["webgl_extensions"], 1404 "async" : true, 1405 "authors": ["Ilmari Heikkinen"], 1406 "knownBugs": [], 1407 "notes": [{ 1408 "name": "Kronos extensions registry", 1409 "href": "http://www.khronos.org/registry/webgl/extensions/" 1410 }] 1411} 1412!*/ 1413/* DOC 1414Detects support for OpenGL extensions in WebGL. It's `true` if the [WebGL extensions API](https://developer.mozilla.org/en-US/docs/Web/WebGL/Using_Extensions) is supported, then exposes the supported extensions as subproperties, e.g.: 1415 1416```javascript 1417if (Modernizr.webglextensions) { 1418 // WebGL extensions API supported 1419} 1420if ('OES_vertex_array_object' in Modernizr.webglextensions) { 1421 // Vertex Array Objects extension supported 1422} 1423``` 1424*/ 1425 1426 // based on code from ilmari heikkinen 1427 // code.google.com/p/graphics-detect/source/browse/js/detect.js 1428 1429 // Not Async but handles it's own self 1430 Modernizr.addAsyncTest(function() { 1431 /* jshint -W053 */ 1432 1433 // Not a good candidate for css classes, so we avoid addTest stuff 1434 Modernizr.webglextensions = new Boolean(false); 1435 1436 if (!Modernizr.webgl) { 1437 return; 1438 } 1439 1440 var canvas; 1441 var ctx; 1442 var exts; 1443 1444 try { 1445 canvas = createElement('canvas'); 1446 ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 1447 exts = ctx.getSupportedExtensions(); 1448 } 1449 catch (e) { 1450 return; 1451 } 1452 1453 if (ctx !== undefined) { 1454 Modernizr.webglextensions = new Boolean(true); 1455 } 1456
1457 for (var i = -1, len = exts.length; ++i < len;) { 1458 Modernizr.webglextensions[exts[i]] = true; 1459 } 1460 1461 canvas = undefined; 1462 }); 1463 1464 1465 /** 1466 * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support 1467 * 1468 * @author kangax 1469 * @access private 1470 * @function hasOwnProp 1471 * @param {object} object - The object to check for a property 1472 * @param {string} property - The property to check for 1473 * @returns {boolean} 1474 */ 1475 1476 // hasOwnProperty shim by kangax needed for Safari 2.0 support 1477 var hasOwnProp; 1478 1479 (function() { 1480 var _hasOwnProperty = ({}).hasOwnProperty; 1481 /* istanbul ignore else */ 1482 /* we have no way of testing IE 5.5 or safari 2, 1483 * so just assume the else gets hit */ 1484 if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) { 1485 hasOwnProp = function(object, property) { 1486 return _hasOwnProperty.call(object, property); 1487 }; 1488 } 1489 else { 1490 hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ 1491 return ((property in object) && is(object.constructor.prototype[property], 'undefined')); 1492 }; 1493 } 1494 })(); 1495 1496 1497 1498 1499 // _l tracks listeners for async tests, as well as tests that execute after the initial run 1500 ModernizrProto._l = {}; 1501 1502 /** 1503 * Modernizr.on is a way to listen for the completion of async tests. Being 1504 * asynchronous, they may not finish before your scripts run. As a result you 1505 * will get a possibly false negative `undefined` value. 1506 * 1507 * @memberof Modernizr 1508 * @name Modernizr.on 1509 * @access public 1510 * @function on 1511 * @param {string} feature - String name of the feature detect 1512 * @param {function} cb - Callback function returning a Boolean - true if feature is supported, false if not 1513 * @example 1514 * 1515 * ```js 1516 * Modernizr.on('flash', function( result ) { 1517 * if (result) { 1518 * // the browser has flash 1519 * } else { 1520 * // the browser does not have flash 1521 * } 1522 * }); 1523 * ``` 1524 */ 1525 1526 ModernizrProto.on = function(feature, cb) { 1527 // Create the list of listeners if it doesn't exist 1528 if (!this._l[feature]) { 1529 this._l[feature] = []; 1530 } 1531 1532 // Push this test on to the listener list 1533 this._l[feature].push(cb); 1534 1535 // If it's already been resolved, trigger it on next tick 1536 if (Modernizr.hasOwnProperty(feature)) { 1537 // Next Tick 1538 setTimeout(function() { 1539 Modernizr._trigger(feature, Modernizr[feature]); 1540 }, 0); 1541 } 1542 }; 1543 1544 /** 1545 * _trigger is the private function used to signal test completion and run any 1546 * callbacks registered through [Modernizr.on](#modernizr-on) 1547 * 1548 * @memberof Modernizr 1549 * @name Modernizr._trigger 1550 * @access private 1551 * @function _trigger 1552 * @param {string} feature - string name of the feature detect 1553 * @param {function|boolean} [res] - A feature detection function, or the boolean = 1554 * result of a feature detection function 1555 */ 1556 1557 ModernizrProto._trigger = function(feature, res) { 1558 if (!this._l[feature]) { 1559 return; 1560 } 1561 1562 var cbs = this._l[feature]; 1563 1564 // Force async 1565 setTimeout(function() { 1566 var i, cb; 1567 for (i = 0; i < cbs.length; i++) { 1568 cb = cbs[i]; 1569 cb(res); 1570 } 1571 }, 0); 1572 1573 // Don't trigger these again 1574 delete this._l[feature]; 1575 }; 1576 1577 /** 1578 * addTest allows you to define your own feature detects that are not currently 1579 * included in Modernizr (under the covers it's the exact same code Modernizr 1580 * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result 1581 * will be added onto the Modernizr object, as well as an appropriate className set on 1582 * the html element when configured to do so 1583 * 1584 * @memberof Modernizr 1585 * @name Modernizr.addTest 1586 * @optionName Modernizr.addTest() 1587 * @optionProp addTest 1588 * @access public 1589 * @function addTest 1590 * @param {string|object} feature - The string name of the feature detect, or an 1591 * object of feature detect names and test 1592 * @param {function|boolean} test - Function returning true if feature is supported, 1593 * false if not. Otherwise a boolean representing the results of a feature detection 1594 * @example 1595 * 1596 * The most common way of creating your own feature detects is by calling 1597 * `Modernizr.addTest` with a string (preferably just lowercase, without any 1598 * punctuation), and a function you want executed that will return a boolean result 1599 * 1600 * ```js 1601 * Modernizr.addTest('itsTuesday', function() { 1602 * var d = new Date(); 1603 * return d.getDay() === 2; 1604 * }); 1605 * ``` 1606 * 1607 * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday, 1608 * and to `false` every other day of the week. One thing to notice is that the names of 1609 * feature detect functions are always lowercased when added to the Modernizr object. That 1610 * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will. 1611 * 1612 * 1613 * Since we only look at the returned value from any feature detection function, 1614 * you do not need to actually use a function. For simple detections, just passing 1615 * in a statement that will return a boolean value works just fine. 1616 * 1617 * ```js 1618 * Modernizr.addTest('hasJquery', 'jQuery' in window); 1619 * ``` 1620 * 1621 * Just like before, when the above runs `Modernizr.hasjquery` will be true if 1622 * jQuery has been included on the page. Not using a function saves a small amount
1623 * of overhead for the browser, as well as making your code much more readable. 1624 * 1625 * Finally, you also have the ability to pass in an object of feature names and 1626 * their tests. This is handy if you want to add multiple detections in one go. 1627 * The keys should always be a string, and the value can be either a boolean or 1628 * function that returns a boolean. 1629 * 1630 * ```js 1631 * var detects = { 1632 * 'hasjquery': 'jQuery' in window, 1633 * 'itstuesday': function() { 1634 * var d = new Date(); 1635 * return d.getDay() === 2; 1636 * } 1637 * } 1638 * 1639 * Modernizr.addTest(detects); 1640 * ``` 1641 * 1642 * There is really no difference between the first methods and this one, it is 1643 * just a convenience to let you write more readable code. 1644 */ 1645 1646 function addTest(feature, test) { 1647 1648 if (typeof feature == 'object') { 1649 for (var key in feature) { 1650 if (hasOwnProp(feature, key)) { 1651 addTest(key, feature[ key ]); 1652 } 1653 } 1654 } else { 1655 1656 feature = feature.toLowerCase(); 1657 var featureNameSplit = feature.split('.'); 1658 var last = Modernizr[featureNameSplit[0]]; 1659 1660 // Again, we don't check for parent test existence. Get that right, though. 1661 if (featureNameSplit.length == 2) { 1662 last = last[featureNameSplit[1]]; 1663 } 1664 1665 if (typeof last != 'undefined') { 1666 // we're going to quit if you're trying to overwrite an existing test 1667 // if we were to allow it, we'd do this: 1668 // var re = new RegExp("\\b(no-)?" + feature + "\\b"); 1669 // docElement.className = docElement.className.replace( re, '' ); 1670 // but, no rly, stuff 'em. 1671 return Modernizr; 1672 } 1673 1674 test = typeof test == 'function' ? test() : test; 1675 1676 // Set the value (this is the magic, right here). 1677 if (featureNameSplit.length == 1) { 1678 Modernizr[featureNameSplit[0]] = test; 1679 } else { 1680 // cast to a Boolean, if not one already 1681 /* jshint -W053 */ 1682 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 1683 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 1684 } 1685 1686 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test; 1687 } 1688 1689 // Set a single class (either `feature` or `no-feature`) 1690 /* jshint -W041 */ 1691 setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); 1692 /* jshint +W041 */ 1693 1694 // Trigger the event 1695 Modernizr._trigger(feature, test); 1696 } 1697 1698 return Modernizr; // allow chaining. 1699 } 1700 1701 // After all the tests are run, add self to the Modernizr prototype 1702 Modernizr._q.push(function() { 1703 ModernizrProto.addTest = addTest; 1704 }); 1705 1706 1707 1708 1709 /** 1710 * cssToDOM takes a kebab-case string and converts it to camelCase 1711 * e.g. box-sizing -> boxSizing 1712 * 1713 * @access private 1714 * @function cssToDOM 1715 * @param {string} name - String name of kebab-case prop we want to convert 1716 * @returns {string} The camelCase version of the supplied name 1717 */ 1718 1719 function cssToDOM(name) { 1720 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 1721 return m1 + m2.toUpperCase(); 1722 }).replace(/^-/, ''); 1723 } 1724 ; 1725 1726 1727 /** 1728 * contains checks to see if a string contains another string 1729 * 1730 * @access private 1731 * @function contains 1732 * @param {string} str - The string we want to check for substrings 1733 * @param {string} substr - The substring we want to search the first string for 1734 * @returns {boolean} 1735 */ 1736 1737 function contains(str, substr) { 1738 return !!~('' + str).indexOf(substr); 1739 } 1740 1741 ; 1742/*! 1743{ 1744 "name": "CSS HSLA Colors", 1745 "caniuse": "css3-colors", 1746 "property": "hsla", 1747 "tags": ["css"] 1748} 1749!*/ 1750 1751 Modernizr.addTest('hsla', function() { 1752 var style = createElement('a').style; 1753 style.cssText = 'background-color:hsla(120,40%,100%,.5)'; 1754 return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla'); 1755 }); 1756 1757 1758 /** 1759 * since we have a fairly large number of input tests that don't mutate the input 1760 * we create a single element that can be shared with all of those tests for a 1761 * minor perf boost 1762 * 1763 * @access private 1764 * @returns {HTMLInputElement} 1765 */ 1766 var inputElem = createElement('input'); 1767 1768/*! 1769{ 1770 "name": "Input attributes", 1771 "property": "input", 1772 "tags": ["forms"], 1773 "authors": ["Mike Taylor"], 1774 "notes": [{ 1775 "name": "WHATWG spec", 1776 "href": "http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary" 1777 }], 1778 "knownBugs": ["Some blackberry devices report false positive for input.multiple"] 1779} 1780!*/ 1781/* DOC 1782Detects support for HTML5 `<input>` element attributes and exposes Boolean subproperties with the results: 1783 1784```javascript 1785Modernizr.input.autocomplete 1786Modernizr.input.autofocus 1787Modernizr.input.list 1788Modernizr.input.max 1789Modernizr.input.min 1790Modernizr.input.multiple 1791Modernizr.input.pattern 1792Modernizr.input.placeholder 1793Modernizr.input.required 1794Modernizr.input.step 1795``` 1796*/ 1797 1798 // Run through HTML5's new input attributes to see if the UA understands any. 1799 // Mike Taylr has created a comprehensive resource for testing these attributes 1800 // when applied to all input types: 1801 // miketaylr.com/code/input-type-attr.html 1802 1803 // Only input placeholder is tested while textarea's placeholder is not. 1804 // Currently Safari 4 and Opera 11 have support only for the input placeholder 1805 // Both tests are available in feature-detects/forms-placeholder.js 1806 1807 var inputattrs = 'autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '); 1808 var attrs = {}; 1809 1810 Modernizr['input'] = (function(props) { 1811 for (var i = 0, len = props.length; i < len; i++) { 1812 attrs[ props[i] ] = !!(props[i] in inputElem); 1813 } 1814 if (attrs.list) { 1815 // safari false positive's on datalist: webk.it/74252 1816 // see also github.com/Modernizr/Modernizr/issues/146 1817 attrs.list = !!(createElement('datalist') && window.HTMLDataListElement); 1818 } 1819 return attrs; 1820 })(inputattrs); 1821 1822/*! 1823{ 1824 "name": "Form input types", 1825 "property": "inputtypes", 1826 "caniuse": "forms", 1827 "tags": ["forms"], 1828 "authors": ["Mike Taylor"], 1829 "polyfills": [ 1830 "jquerytools", 1831 "webshims", 1832 "h5f", 1833 "webforms2", 1834 "nwxforms", 1835 "fdslider", 1836 "html5slider", 1837 "galleryhtml5forms", 1838 "jscolor", 1839 "html5formshim", 1840 "selectedoptionsjs", 1841 "formvalidationjs" 1842 ] 1843} 1844!*/ 1845/* DOC 1846Detects support for HTML5 form input types and exposes Boolean subproperties with the results: 1847 1848```javascript 1849Modernizr.inputtypes.color 1850Modernizr.inputtypes.date 1851Modernizr.inputtypes.datetime 1852Modernizr.inputtypes['datetime-local'] 1853Modernizr.inputtypes.email 1854Modernizr.inputtypes.month
1855Modernizr.inputtypes.number 1856Modernizr.inputtypes.range 1857Modernizr.inputtypes.search 1858Modernizr.inputtypes.tel 1859Modernizr.inputtypes.time 1860Modernizr.inputtypes.url 1861Modernizr.inputtypes.week 1862``` 1863*/ 1864 1865 // Run through HTML5's new input types to see if the UA understands any. 1866 // This is put behind the tests runloop because it doesn't return a 1867 // true/false like all the other tests; instead, it returns an object 1868 // containing each input type with its corresponding true/false value 1869 1870 // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/ 1871 var inputtypes = 'search tel url email datetime date month week time datetime-local number range color'.split(' '); 1872 var inputs = {}; 1873 1874 Modernizr['inputtypes'] = (function(props) { 1875 var len = props.length; 1876 var smile = ':)'; 1877 var inputElemType; 1878 var defaultView; 1879 var bool; 1880 1881 for (var i = 0; i < len; i++) { 1882 1883 inputElem.setAttribute('type', inputElemType = props[i]); 1884 bool = inputElem.type !== 'text' && 'style' in inputElem; 1885 1886 // We first check to see if the type we give it sticks.. 1887 // If the type does, we feed it a textual value, which shouldn't be valid. 1888 // If the value doesn't stick, we know there's input sanitization which infers a custom UI 1889 if (bool) { 1890 1891 inputElem.value = smile; 1892 inputElem.style.cssText = 'position:absolute;visibility:hidden;'; 1893 1894 if (/^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined) { 1895 1896 docElement.appendChild(inputElem); 1897 defaultView = document.defaultView; 1898 1899 // Safari 2-4 allows the smiley as a value, despite making a slider 1900 bool = defaultView.getComputedStyle && 1901 defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && 1902 // Mobile android web browser has false positive, so must 1903 // check the height to see if the widget is actually there. 1904 (inputElem.offsetHeight !== 0); 1905 1906 docElement.removeChild(inputElem); 1907 1908 } else if (/^(search|tel)$/.test(inputElemType)) { 1909 // Spec doesn't define any special parsing or detectable UI 1910 // behaviors so we pass these through as true 1911 1912 // Interestingly, opera fails the earlier test, so it doesn't 1913 // even make it here. 1914 1915 } else if (/^(url|email|number)$/.test(inputElemType)) { 1916 // Real url and email support comes with prebaked validation. 1917 bool = inputElem.checkValidity && inputElem.checkValidity() === false; 1918 1919 } else { 1920 // If the upgraded input compontent rejects the :) text, we got a winner 1921 bool = inputElem.value != smile; 1922 } 1923 } 1924 1925 inputs[ props[i] ] = !!bool; 1926 } 1927 return inputs; 1928 })(inputtypes); 1929 1930/*! 1931{ 1932 "name": "CSS Supports", 1933 "property": "supports", 1934 "caniuse": "css-featurequeries", 1935 "tags": ["css"], 1936 "builderAliases": ["css_supports"], 1937 "notes": [{ 1938 "name": "W3 Spec", 1939 "href": "http://dev.w3.org/csswg/css3-conditional/#at-supports" 1940 },{ 1941 "name": "Related Github Issue", 1942 "href": "github.com/Modernizr/Modernizr/issues/648" 1943 },{ 1944 "name": "W3 Info", 1945 "href": "http://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface" 1946 }] 1947} 1948!*/ 1949 1950 var newSyntax = 'CSS' in window && 'supports' in window.CSS; 1951 var oldSyntax = 'supportsCSS' in window; 1952 Modernizr.addTest('supports', newSyntax || oldSyntax); 1953 1954 1955 /** 1956 * Object.prototype.toString can be used with every object and allows you to 1957 * get its class easily. Abstracting it off of an object prevents situations 1958 * where the toString property has been overridden 1959 * 1960 * @access private 1961 * @function toStringFn 1962 * @returns {function} An abstracted toString function 1963 */ 1964 1965 var toStringFn = ({}).toString; 1966 1967/*! 1968{ 1969 "name": "SVG clip paths", 1970 "property": "svgclippaths", 1971 "tags": ["svg"], 1972 "notes": [{ 1973 "name": "Demo", 1974 "href": "http://srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg" 1975 }] 1976} 1977!*/ 1978/* DOC 1979Detects support for clip paths in SVG (only, not on HTML content). 1980
1981See [this discussion](http://github.com/Modernizr/Modernizr/issues/213) regarding applying SVG clip paths to HTML content. 1982*/ 1983 1984 Modernizr.addTest('svgclippaths', function() { 1985 return !!document.createElementNS && 1986 /SVGClipPath/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'clipPath'))); 1987 }); 1988 1989/*! 1990{ 1991 "name": "SVG SMIL animation", 1992 "property": "smil", 1993 "caniuse": "svg-smil", 1994 "tags": ["svg"], 1995 "notes": [{ 1996 "name": "W3C Synchronised Multimedia spec", 1997 "href": "http://www.w3.org/AudioVideo/" 1998 }] 1999} 2000!*/ 2001 2002 // SVG SMIL animation 2003 Modernizr.addTest('smil', function() { 2004 return !!document.createElementNS && 2005 /SVGAnimate/.test(toStringFn.call(document.createElementNS('http://www.w3.org/2000/svg', 'animate'))); 2006 }); 2007 2008 2009 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); 2010 ModernizrProto._cssomPrefixes = cssomPrefixes; 2011 2012 2013 /** 2014 * atRule returns a given CSS property at-rule (eg @keyframes), possibly in 2015 * some prefixed form, or false, in the case of an unsupported rule 2016 * 2017 * @memberof Modernizr 2018 * @name Modernizr.atRule 2019 * @optionName Modernizr.atRule() 2020 * @optionProp atRule 2021 * @access public 2022 * @function atRule 2023 * @param {string} prop - String name of the @-rule to test for 2024 * @returns {string|false} The string representing the (possibly prefixed) 2025 * valid version of the @-rule, or `false` when it is unsupported. 2026 * @example 2027 * ```js 2028 * var keyframes = Modernizr.atRule('@keyframes'); 2029 * 2030 * if (keyframes) { 2031 * // keyframes are supported 2032 * // could be `@-webkit-keyframes` or `@keyframes` 2033 * } else { 2034 * // keyframes === `false` 2035 * } 2036 * ``` 2037 * 2038 */ 2039 2040 var atRule = function(prop) { 2041 var length = prefixes.length; 2042 var cssrule = window.CSSRule; 2043 var rule; 2044 2045 if (typeof cssrule === 'undefined') { 2046 return undefined; 2047 } 2048 2049 if (!prop) { 2050 return false; 2051 } 2052 2053 // remove literal @ from beginning of provided property 2054 prop = prop.replace(/^@/, ''); 2055 2056 // CSSRules use underscores instead of dashes 2057 rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE'; 2058 2059 if (rule in cssrule) { 2060 return '@' + prop; 2061 } 2062 2063 for (var i = 0; i < length; i++) { 2064 // prefixes gives us something like -o-, and we want O_ 2065 var prefix = prefixes[i]; 2066 var thisRule = prefix.toUpperCase() + '_' + rule; 2067 2068 if (thisRule in cssrule) { 2069 return '@-' + prefix.toLowerCase() + '-' + prop; 2070 } 2071 } 2072 2073 return false; 2074 }; 2075 2076 ModernizrProto.atRule = atRule; 2077 2078 2079 2080 /** 2081 * getBody returns the body of a document, or an element that can stand in for 2082 * the body if a real body does not exist 2083 * 2084 * @access private 2085 * @function getBody 2086 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 2087 * artificially created element that stands in for the body 2088 */ 2089 2090 function getBody() { 2091 // After page load injecting a fake body doesn't work so check if body exists 2092 var body = document.body; 2093 2094 if (!body) { 2095 // Can't use the real body create a fake one. 2096 body = createElement(isSVG ? 'svg' : 'body'); 2097 body.fake = true; 2098 } 2099 2100 return body; 2101 } 2102 2103 ; 2104 2105 /** 2106 * injectElementWithStyles injects an element with style element and some CSS rules 2107 * 2108 * @access private 2109 * @function injectElementWithStyles 2110 * @param {string} rule - String representing a css rule 2111 * @param {function} callback - A function that is used to test the injected element 2112 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 2113 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 2114 * @returns {boolean} 2115 */ 2116 2117 function injectElementWithStyles(rule, callback, nodes, testnames) { 2118 var mod = 'modernizr'; 2119 var style; 2120 var ret; 2121 var node; 2122 var docOverflow; 2123 var div = createElement('div'); 2124 var body = getBody(); 2125 2126 if (parseInt(nodes, 10)) { 2127 // In order not to give false positives we create a node for each test
2128 // This also allows the method to scale for unspecified uses 2129 while (nodes--) { 2130 node = createElement('div'); 2131 node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 2132 div.appendChild(node); 2133 } 2134 } 2135 2136 style = createElement('style'); 2137 style.type = 'text/css'; 2138 style.id = 's' + mod; 2139 2140 // 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. 2141 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 2142 (!body.fake ? div : body).appendChild(style); 2143 body.appendChild(div); 2144 2145 if (style.styleSheet) { 2146 style.styleSheet.cssText = rule; 2147 } else { 2148 style.appendChild(document.createTextNode(rule)); 2149 } 2150 div.id = mod; 2151 2152 if (body.fake) { 2153 //avoid crashing IE8, if background image is used 2154 body.style.background = ''; 2155 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 2156 body.style.overflow = 'hidden'; 2157 docOverflow = docElement.style.overflow; 2158 docElement.style.overflow = 'hidden'; 2159 docElement.appendChild(body); 2160 } 2161 2162 ret = callback(div, rule); 2163 // If this is done after page load we don't want to remove the body so check if body exists 2164 if (body.fake) { 2165 body.parentNode.removeChild(body); 2166 docElement.style.overflow = docOverflow; 2167 // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 2168 docElement.offsetHeight; 2169 } else { 2170 div.parentNode.removeChild(div); 2171 } 2172 2173 return !!ret; 2174 2175 } 2176 2177 ; 2178 2179 /** 2180 * Modernizr.mq tests a given media query, live against the current state of the window 2181 * adapted from matchMedia polyfill by Scott Jehl and Paul Irish 2182 * gist.github.com/786768 2183 * 2184 * @memberof Modernizr 2185 * @name Modernizr.mq 2186 * @optionName Modernizr.mq() 2187 * @optionProp mq 2188 * @access public 2189 * @function mq 2190 * @param {string} mq - String of the media query we want to test 2191 * @returns {boolean} 2192 * @example 2193 * Modernizr.mq allows for you to programmatically check if the current browser 2194 * window state matches a media query. 2195 * 2196 * ```js 2197 * var query = Modernizr.mq('(min-width: 900px)'); 2198 * 2199 * if (query) { 2200 * // the browser window is larger than 900px 2201 * } 2202 * ``` 2203 * 2204 * Only valid media queries are supported, therefore you must always include values 2205 * with your media query 2206 * 2207 * ```js 2208 * // good 2209 * Modernizr.mq('(min-width: 900px)'); 2210 * 2211 * // bad 2212 * Modernizr.mq('min-width'); 2213 * ``` 2214 * 2215 * If you would just like to test that media queries are supported in general, use 2216 * 2217 * ```js 2218 * Modernizr.mq('only all'); // true if MQ are supported, false if not 2219 * ``` 2220 * 2221 * 2222 * Note that if the browser does not support media queries (e.g. old IE) mq will 2223 * always return false. 2224 */ 2225 2226 var mq = (function() { 2227 var matchMedia = window.matchMedia || window.msMatchMedia; 2228 if (matchMedia) { 2229 return function(mq) { 2230 var mql = matchMedia(mq); 2231 return mql && mql.matches || false; 2232 }; 2233 } 2234 2235 return function(mq) { 2236 var bool = false; 2237 2238 injectElementWithStyles('@media ' + mq + ' { #modernizr { position: absolute; } }', function(node) { 2239 bool = (window.getComputedStyle ? 2240 window.getComputedStyle(node, null) : 2241 node.currentStyle)['position'] == 'absolute'; 2242 }); 2243 2244 return bool; 2245 }; 2246 })(); 2247 2248 2249 ModernizrProto.mq = mq; 2250 2251 2252 2253 /** 2254 * testStyles injects an element with style element and some CSS rules 2255 * 2256 * @memberof Modernizr 2257 * @name Modernizr.testStyles 2258 * @optionName Modernizr.testStyles() 2259 * @optionProp testStyles 2260 * @access public 2261 * @function testStyles 2262 * @param {string} rule - String representing a css rule 2263 * @param {function} callback - A function that is used to test the injected element 2264 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 2265 * @param {string[]}
2265 [testnames] - An array of strings that are used as ids for the additional nodes 2266 * @returns {boolean} 2267 * @example 2268 * 2269 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page 2270 * along with (possibly multiple) DOM elements. This lets you check for features 2271 * 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). 2272 * 2273 * ```js 2274 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { 2275 * // elem is the first DOM node in the page (by default #modernizr) 2276 * // rule is the first argument you supplied - the CSS rule in string form 2277 * 2278 * addTest('widthworks', elem.style.width === '9px') 2279 * }); 2280 * ``` 2281 * 2282 * If your test requires multiple nodes, you can include a third argument 2283 * indicating how many additional div elements to include on the page. The 2284 * additional nodes are injected as children of the `elem` that is returned as 2285 * the first argument to the callback. 2286 * 2287 * ```js 2288 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { 2289 * document.getElementById('modernizr').style.width === '1px'; // true 2290 * document.getElementById('modernizr2').style.width === '2px'; // true 2291 * elem.firstChild === document.getElementById('modernizr2'); // true 2292 * }, 1); 2293 * ``` 2294 * 2295 * By default, all of the additional elements have an ID of `modernizr[n]`, where 2296 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, 2297 * the second additional is `#modernizr3`, etc.). 2298 * If you want to have more meaningful IDs for your function, you can provide 2299 * them as the fourth argument, as an array of strings 2300 * 2301 * ```js 2302 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { 2303 * elem.firstChild === document.getElementById('foo'); // true 2304 * elem.lastChild === document.getElementById('bar'); // true 2305 * }, 2, ['foo', 'bar']); 2306 * ``` 2307 * 2308 */ 2309 2310 var testStyles = ModernizrProto.testStyles = injectElementWithStyles; 2311 2312/*! 2313{ 2314 "name": "Touch Events", 2315 "property": "touchevents", 2316 "caniuse" : "touch", 2317 "tags": ["media", "attribute"], 2318 "notes": [{ 2319 "name": "Touch Events spec", 2320 "href": "http://www.w3.org/TR/2013/WD-touch-events-20130124/" 2321 }], 2322 "warnings": [ 2323 "Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device" 2324 ], 2325 "knownBugs": [ 2326 "False-positive on some configurations of Nokia N900", 2327 "False-positive on some BlackBerry 6.0 builds – https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695" 2328 ] 2329} 2330!*/ 2331/* DOC 2332Indicates if the browser supports the W3C Touch Events API. 2333 2334This *does not* necessarily reflect a touchscreen device: 2335 2336* Older touchscreen devices only emulate mouse events 2337* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that 2338* Some browsers & OS setups may enable touch APIs when no touchscreen is connected 2339* Future browsers may implement other event models for touch interactions 2340 2341See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/). 2342 2343It's recommended to bind both mouse and touch/pointer events simultaneously – see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/). 2344 2345This test will also return `true` for Firefox 4 Multitouch support. 2346*/ 2347 2348 // Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415 2349 Modernizr.addTest('touchevents', function() { 2350 var bool; 2351 if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { 2352 bool = true; 2353 } else { 2354 var query = ['@media (', prefixes.join('touch-enabled),('), 'heartz', ')', '{#modernizr{top:9px;position:absolute}}'].join(''); 2355 testStyles(query, function(node) { 2356 bool = node.offsetTop === 9; 2357 }); 2358 } 2359 return bool; 2360 }); 2361 2362/*! 2363{ 2364 "name": "@font-face", 2365 "property": "fontface", 2366 "authors": ["Diego Perini", "Mat Marquis"], 2367 "tags": ["css"], 2368 "knownBugs": [ 2369 "False Positive: WebOS http://github.com/Modernizr/Modernizr/issues/342", 2370 "False Postive: WP7 http://github.com/Modernizr/Modernizr/issues/538" 2371 ], 2372 "notes": [{ 2373 "name": "@font-face detection routine by Diego Perini", 2374 "href": "http://javascript.nwbox.com/CSSSupport/" 2375 },{ 2376 "name": "Filament Group @font-face compatibility research", 2377 "href": "https://docs.google.com/presentation/d/1n4NyG4uPRjAA8zn_pSQ_
2377Ket0RhcWC6QlZ6LMjKeECo0/edit#slide=id.p" 2378 },{ 2379 "name": "Filament Grunticon/@font-face device testing results", 2380 "href": "https://docs.google.com/spreadsheet/ccc?key=0Ag5_yGvxpINRdHFYeUJPNnZMWUZKR2ItMEpRTXZPdUE#gid=0" 2381 },{ 2382 "name": "CSS fonts on Android", 2383 "href": "http://stackoverflow.com/questions/3200069/css-fonts-on-android" 2384 },{ 2385 "name": "@font-face and Android", 2386 "href": "http://archivist.incutio.com/viewlist/css-discuss/115960" 2387 }] 2388} 2389!*/ 2390 2391 var blacklist = (function() { 2392 var ua = navigator.userAgent; 2393 var wkvers = ua.match(/applewebkit\/([0-9]+)/gi) && parseFloat(RegExp.$1); 2394 var webos = ua.match(/w(eb)?osbrowser/gi); 2395 var wppre8 = ua.match(/windows phone/gi) && ua.match(/iemobile\/([0-9])+/gi) && parseFloat(RegExp.$1) >= 9; 2396 var oldandroid = wkvers < 533 && ua.match(/android/gi); 2397 return webos || oldandroid || wppre8; 2398 }()); 2399 if (blacklist) { 2400 Modernizr.addTest('fontface', false); 2401 } else { 2402 testStyles('@font-face {font-family:"font";src:url("https://")}', function(node, rule) { 2403 var style = document.getElementById('smodernizr'); 2404 var sheet = style.sheet || style.styleSheet; 2405 var cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : ''; 2406 var bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0; 2407 Modernizr.addTest('fontface', bool); 2408 }); 2409 } 2410; 2411/*! 2412{ 2413 "name": "CSS Generated Content", 2414 "property": "generatedcontent", 2415 "tags": ["css"], 2416 "warnings": ["Android won't return correct height for anything below 7px #738"], 2417 "notes": [{ 2418 "name": "W3C CSS Selectors Level 3 spec", 2419 "href": "http://www.w3.org/TR/css3-selectors/#gen-content" 2420 },{ 2421 "name": "MDN article on :before", 2422 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/::before" 2423 },{ 2424 "name": "MDN article on :after", 2425 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/::before" 2426 }] 2427} 2428!*/ 2429 2430 testStyles('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}', function(node) { 2431 Modernizr.addTest('generatedcontent', node.offsetHeight >= 7); 2432 }); 2433 2434 2435 /** 2436 * Create our "modernizr" element that we do most feature tests on. 2437 * 2438 * @access private 2439 */ 2440 2441 var modElem = { 2442 elem : createElement('modernizr') 2443 }; 2444 2445 // Clean up this element 2446 Modernizr._q.push(function() { 2447 delete modElem.elem; 2448 }); 2449 2450 2451 2452 var mStyle = { 2453 style : modElem.elem.style 2454 }; 2455 2456 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 2457 // the front of the queue. 2458 Modernizr._q.unshift(function() { 2459 delete mStyle.style; 2460 }); 2461 2462 2463 2464 /** 2465 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. 2466 * 2467 * @access private 2468 * @function fnBind 2469 * @param {function} fn - a function you want to change `this` reference to 2470 * @param {object} that - the `this` you want to call the function with 2471 * @returns {function} The wrapped version of the supplied function 2472 */ 2473 2474 function fnBind(fn, that) { 2475 return function() { 2476 return fn.apply(that, arguments); 2477 }; 2478 } 2479 2480 ; 2481 2482 /** 2483 * testDOMProps is a generic DOM property test; if a browser supports 2484 * a certain property, it won't return undefined for it. 2485 */ 2486 function testDOMProps(props, obj, elem) { 2487 var item; 2488 2489 for (var i in props) { 2490 if (props[i] in obj) { 2491 2492 // return the property name as a string 2493 if (elem === false) { 2494 return props[i]; 2495 } 2496 2497 item = obj[props[i]]; 2498 2499 // let's bind a function 2500 if (is(item, 'function')) { 2501 // bind to obj unless overriden 2502 return fnBind(item, elem || obj); 2503 } 2504 2505 // return the unbound function or obj or value 2506 return item; 2507 } 2508 } 2509 return false; 2510 } 2511 2512 ; 2513 2514 /** 2515 * domToCSS takes a camelCase string and converts it to kebab-case 2516 * e.g. boxSizing -> box-sizing 2517 * 2518 * @access private 2519 * @function domToCSS 2520 * @param {string} name - String name of camelCase prop we want to convert 2521 * @returns {string} The kebab-case version of the supplied name 2522 */ 2523 2524 function domToCSS(name) { 2525 return name.replace(/([A-Z])/g, function(str, m1) { 2526 return '-' + m1.toLowerCase(); 2527 }).replace(/^ms-/, '-ms-'); 2528 } 2529 ; 2530 2531 /** 2532 * nativeTestProps allows for us to use native feature detection functionality if available. 2533 * some prefixed form, or false, in the case of an unsupported rule 2534 * 2535 * @access private 2536 * @function nativeTestProps 2537 * @param {array} props - An array of property names 2538 * @param {string} value - A string representing the value we want to check via @supports 2539 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 2540 */ 2541 2542 // Accepts a list of property names and a single value 2543 // Returns `undefined` if native detection not available 2544 function nativeTestProps (props, value) { 2545 var i = props.length; 2546 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface 2547 if ('CSS' in window && 'supports' in window.CSS) { 2548 // Try every prefixed variant of the property 2549 while (i--) { 2550 if (window.CSS.supports(domToCSS(props[i]), value)) { 2551 return true; 2552 } 2553 } 2554 return false; 2555 } 2556 // Otherwise fall back to at-rule (for Opera 12.x) 2557 else if ('CSSSupportsRule' in window) { 2558 // Build a condition string for every prefixed variant 2559 var conditionText = []; 2560 while (i--) { 2561 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 2562 } 2563 conditionText = conditionText.join(' or '); 2564 return injectElementWithStyles('@supports (' + conditionText
2564+ ') { #modernizr { position: absolute; } }', function(node) { 2565 return getComputedStyle(node, null).position == 'absolute'; 2566 }); 2567 } 2568 return undefined; 2569 } 2570 ; 2571 2572 // testProps is a generic CSS / DOM property test. 2573 2574 // In testing support for a given CSS property, it's legit to test: 2575 // `elem.style[styleName] !== undefined` 2576 // If the property is supported it will return an empty string, 2577 // if unsupported it will return undefined. 2578 2579 // We'll take advantage of this quick test and skip setting a style 2580 // on our modernizr element, but instead just testing undefined vs 2581 // empty string. 2582 2583 // Property names can be provided in either camelCase or kebab-case. 2584 2585 function testProps(props, prefixed, value, skipValueTest) { 2586 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 2587 2588 // Try native detect first 2589 if (!is(value, 'undefined')) { 2590 var result = nativeTestProps(props, value); 2591 if (!is(result, 'undefined')) { 2592 return result; 2593 } 2594 } 2595 2596 // Otherwise do it properly 2597 var afterInit, i, propsLength, prop, before; 2598 2599 // If we don't have a style element, that means we're running async or after 2600 // the core tests, so we'll need to create our own elements to use 2601 2602 // inside of an SVG element, in certain browsers, the `style` element is only 2603 // defined for valid tags. Therefore, if `modernizr` does not have one, we 2604 // fall back to a less used element and hope for the best. 2605 var elems = ['modernizr', 'tspan']; 2606 while (!mStyle.style) { 2607 afterInit = true; 2608 mStyle.modElem = createElement(elems.shift()); 2609 mStyle.style = mStyle.modElem.style; 2610 } 2611 2612 // Delete the objects if we created them. 2613 function cleanElems() { 2614 if (afterInit) { 2615 delete mStyle.style; 2616 delete mStyle.modElem; 2617 } 2618 } 2619 2620 propsLength = props.length; 2621 for (i = 0; i < propsLength; i++) { 2622 prop = props[i]; 2623 before = mStyle.style[prop]; 2624 2625 if (contains(prop, '-')) { 2626 prop = cssToDOM(prop); 2627 } 2628 2629 if (mStyle.style[prop] !== undefined) { 2630 2631 // If value to test has been passed in, do a set-and-check test. 2632 // 0 (integer) is a valid property value, so check that `value` isn't 2633 // undefined, rather than just checking it's truthy. 2634 if (!skipValueTest && !is(value, 'undefined')) { 2635 2636 // Needs a try catch block because of old IE. This is slow, but will 2637 // be avoided in most cases because `skipValueTest` will be used. 2638 try { 2639 mStyle.style[prop] = value; 2640 } catch (e) {} 2641 2642 // If the property value has changed, we assume the value used is 2643 // supported. If `value` is empty string, it'll fail here (because 2644 // it hasn't changed), which matches how browsers have implemented 2645 // CSS.supports() 2646 if (mStyle.style[prop] != before) { 2647 cleanElems(); 2648 return prefixed == 'pfx' ? prop : true; 2649 } 2650 } 2651 // Otherwise just return true, or the property name if this is a 2652 // `prefixed()` call 2653 else { 2654 cleanElems(); 2655 return prefixed == 'pfx' ? prop : true; 2656 } 2657 } 2658 } 2659 cleanElems(); 2660 return false; 2661 } 2662 2663 ; 2664 2665 /** 2666 * testProp() investigates whether a given style property is recognized 2667 * Property names can be provided in either camelCase or kebab-case. 2668 * 2669 * @memberof Modernizr 2670 * @name Modernizr.testProp 2671 * @access public 2672 * @optionName Modernizr.testProp() 2673 * @optionProp testProp 2674 * @function testProp 2675 * @param {string} prop - Name of the CSS property to check 2676 * @param {string} [value] - Name of the CSS value to check 2677 * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported 2678 * @returns {boolean} 2679 * @example 2680 * 2681 * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed 2682 * version of the string. 2683 * 2684 * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) 2685 * 2686 * ```js 2687 * Modernizr.testProp('pointerEvents') // true 2688 * ``` 2689 * 2690 * You can also provide a value as an optional second argument to check if a 2691 * specific value is supported 2692 * 2693 * ```js
2694 * Modernizr.testProp('pointerEvents', 'none') // true 2695 * Modernizr.testProp('pointerEvents', 'penguin') // false 2696 * ``` 2697 */ 2698 2699 var testProp = ModernizrProto.testProp = function(prop, value, useValue) { 2700 return testProps([prop], undefined, value, useValue); 2701 }; 2702 2703/*! 2704{ 2705 "name": "CSS textshadow", 2706 "property": "textshadow", 2707 "caniuse": "css-textshadow", 2708 "tags": ["css"], 2709 "knownBugs": ["FF3.0 will false positive on this test"] 2710} 2711!*/ 2712 2713 Modernizr.addTest('textshadow', testProp('textShadow', '1px 1px')); 2714 2715 2716 /** 2717 * testPropsAll tests a list of DOM properties we want to check against. 2718 * We specify literally ALL possible (known and/or likely) properties on 2719 * the element including the non-vendor prefixed one, for forward- 2720 * compatibility. 2721 */ 2722 function testPropsAll(prop, prefixed, elem, value, skipValueTest) { 2723 2724 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 2725 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 2726 2727 // did they call .prefixed('boxSizing') or are we just testing a prop? 2728 if (is(prefixed, 'string') || is(prefixed, 'undefined')) { 2729 return testProps(props, prefixed, value, skipValueTest); 2730 2731 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) 2732 } else { 2733 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 2734 return testDOMProps(props, prefixed, elem); 2735 } 2736 } 2737 2738 // Modernizr.testAllProps() investigates whether a given style property, 2739 // or any of its vendor-prefixed variants, is recognized 2740 // 2741 // Note that the property names must be provided in the camelCase variant. 2742 // Modernizr.testAllProps('boxSizing') 2743 ModernizrProto.testAllProps = testPropsAll; 2744 2745 2746 2747 /** 2748 * testAllProps determines whether a given CSS property is supported in the browser 2749 * 2750 * @memberof Modernizr 2751 * @name Modernizr.testAllProps 2752 * @optionName Modernizr.testAllProps() 2753 * @optionProp testAllProps 2754 * @access public 2755 * @function testAllProps 2756 * @param {string} prop - String naming the property to test (either camelCase or kebab-case) 2757 * @param {string} [value] - String of the value to test 2758 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection 2759 * @example 2760 * 2761 * testAllProps determines whether a given CSS property, in some prefixed form, 2762 * is supported by the browser. 2763 * 2764 * ```js 2765 * testAllProps('boxSizing') // true 2766 * ``` 2767 * 2768 * It can optionally be given a CSS value in string form to test if a property 2769 * value is valid 2770 * 2771 * ```js 2772 * testAllProps('display', 'block') // true 2773 * testAllProps('display', 'penguin') // false 2774 * ``` 2775 * 2776 * A boolean can be passed as a third parameter to skip the value check when 2777 * native detection (@supports) isn't available. 2778 * 2779 * ```js 2780 * testAllProps('shapeOutside', 'content-box', true); 2781 * ``` 2782 */ 2783 2784 function testAllProps (prop, value, skipValueTest) { 2785 return testPropsAll(prop, undefined, undefined, value, skipValueTest); 2786 } 2787 ModernizrProto.testAllProps = testAllProps; 2788 2789/*! 2790{ 2791 "name": "CSS Animations", 2792 "property": "cssanimations", 2793 "caniuse": "css-animation", 2794 "polyfills": ["transformie", "csssandpaper"], 2795 "tags": ["css"], 2796 "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], 2797 "notes": [{ 2798 "name" : "Article: 'Dispelling the Android CSS animation myths'", 2799 "href": "http://goo.gl/OGw5Gm" 2800 }] 2801} 2802!*/ 2803/* DOC 2804Detects whether or not elements can be animated using CSS 2805*/ 2806 2807 Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); 2808 2809/*! 2810{ 2811 "name": "Background Size", 2812 "property": "backgroundsize", 2813 "tags": ["css"], 2814 "knownBugs": ["This will false positive in Opera Mini - http://github.com/Modernizr/Modernizr/issues/396"], 2815 "notes": [{ 2816 "name": "Related Issue", 2817 "href": "http://github.com/Modernizr/Modernizr/issues/396" 2818 }] 2819} 2820!*/ 2821 2822 Modernizr.addTest('backgroundsize', testAllProps('backgroundSize', '100%', true)); 2823 2824/*! 2825{
2826 "name": "Background Size Cover", 2827 "property": "bgsizecover", 2828 "tags": ["css"], 2829 "builderAliases": ["css_backgroundsizecover"], 2830 "notes": [{ 2831 "name" : "MDN Docs", 2832 "href": "http://developer.mozilla.org/en/CSS/background-size" 2833 }] 2834} 2835!*/ 2836 2837 // Must test value, as this specifically tests the `cover` value 2838 Modernizr.addTest('bgsizecover', testAllProps('backgroundSize', 'cover')); 2839 2840/*! 2841{ 2842 "name": "Border Image", 2843 "property": "borderimage", 2844 "caniuse": "border-image", 2845 "polyfills": ["css3pie"], 2846 "knownBugs": ["Android < 2.0 is true, but has a broken implementation"], 2847 "tags": ["css"] 2848} 2849!*/ 2850 2851 Modernizr.addTest('borderimage', testAllProps('borderImage', 'url() 1', true)); 2852 2853/*! 2854{ 2855 "name": "Border Radius", 2856 "property": "borderradius", 2857 "caniuse": "border-radius", 2858 "polyfills": ["css3pie"], 2859 "tags": ["css"], 2860 "notes": [{ 2861 "name": "Comprehensive Compat Chart", 2862 "href": "http://muddledramblings.com/table-of-css3-border-radius-compliance" 2863 }] 2864} 2865!*/ 2866 2867 Modernizr.addTest('borderradius', testAllProps('borderRadius', '0px', true)); 2868 2869/*! 2870{ 2871 "name": "Box Shadow", 2872 "property": "boxshadow", 2873 "caniuse": "css-boxshadow", 2874 "tags": ["css"], 2875 "knownBugs": [ 2876 "WebOS false positives on this test.", 2877 "The Kindle Silk browser false positives" 2878 ] 2879} 2880!*/ 2881 2882 Modernizr.addTest('boxshadow', testAllProps('boxShadow', '1px 1px', true)); 2883 2884/*! 2885{ 2886 "name": "CSS Columns", 2887 "property": "csscolumns", 2888 "caniuse": "multicolumn", 2889 "polyfills": ["css3multicolumnjs"], 2890 "tags": ["css"] 2891} 2892!*/ 2893 2894 2895 (function() { 2896 2897 /* jshint -W053 */ 2898 Modernizr.addTest('csscolumns', function() { 2899 var bool = false; 2900 var test = testAllProps('columnCount'); 2901 try { 2902 if (bool = !!test) { 2903 bool = new Boolean(bool); 2904 } 2905 } catch (e) {} 2906 2907 return bool; 2908 }); 2909 2910 var props = ['Width', 'Span', 'Fill', 'Gap', 'Rule', 'RuleColor', 'RuleStyle', 'RuleWidth', 'BreakBefore', 'BreakAfter', 'BreakInside']; 2911 var name, test; 2912 2913 for (var i = 0; i < props.length; i++) { 2914 name = props[i].toLowerCase(); 2915 test = testAllProps('column' + props[i]); 2916 2917 // break-before, break-after & break-inside are not "column"-prefixed in spec 2918 if (name === 'breakbefore' || name === 'breakafter' || name == 'breakinside') { 2919 test = test || testAllProps(props[i]); 2920 } 2921 2922 Modernizr.addTest('csscolumns.' + name, test); 2923 } 2924 2925 2926 })(); 2927 2928 2929/*! 2930{ 2931 "name": "Flexbox", 2932 "property": "flexbox", 2933 "caniuse": "flexbox", 2934 "tags": ["css"], 2935 "notes": [{ 2936 "name": "The _new_ flexbox", 2937 "href": "http://dev.w3.org/csswg/css3-flexbox" 2938 }], 2939 "warnings": [ 2940 "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect." 2941 ] 2942} 2943!*/ 2944/* DOC 2945Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container. 2946*/ 2947 2948 Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', true)); 2949 2950/*! 2951{ 2952 "name": "CSS Reflections", 2953 "caniuse": "css-reflections", 2954 "property": "cssreflections", 2955 "tags": ["css"] 2956} 2957!*/ 2958 2959 Modernizr.addTest('cssreflections', testAllProps('boxReflect', 'above', true)); 2960 2961/*! 2962{ 2963 "name": "CSS Transforms", 2964 "property": "csstransforms", 2965 "caniuse": "transforms2d", 2966 "tags": ["css"] 2967} 2968!*/ 2969 2970 Modernizr.addTest('csstransforms', function() { 2971 // Android < 3.0 is buggy, so we sniff and blacklist 2972 // http://git.io/hHzL7w 2973 return navigator.userAgent.indexOf('Android 2.') === -1 && 2974 testAllProps('transform', 'scale(1)', true); 2975 }); 2976 2977/*! 2978{ 2979 "name": "CSS Transforms 3D", 2980 "property": "csstransforms3d", 2981 "caniuse": "transforms3d", 2982 "tags": ["css"], 2983 "warnings": [ 2984 "Chrome may occassionally fail this test on some systems; more info: https://code.google.com/p/chromium/issues/detail?id=129004" 2985 ] 2986} 2987!*/ 2988 2989 Modernizr.addTest('csstransforms3d', function() { 2990 var ret = !!testAllProps('perspective', '1px', true); 2991 var usePrefix = Modernizr._config.usePrefixes; 2992 2993 // Webkit's 3D transforms are passed off to the browser's own graphics renderer. 2994 // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in 2995 // some conditions. As a result, Webkit typically recognizes the syntax but 2996 // will sometimes throw a false positive, thus we must do a more thorough check: 2997 if (ret && (!usePrefix || 'webkitPerspective' in docElement.style)) { 2998 var mq; 2999 var defaultStyle = '#modernizr{width:0;height:0}'; 3000 // Use CSS Conditional Rules if available 3001 if (Modernizr.supports) { 3002 mq = '@supports (perspective: 1px)'; 3003 } else { 3004 // Otherwise, Webkit allows this media query to succeed only if the feature is enabled. 3005 // `@media (transform-3d),(-webkit-transform-3d){ ... }` 3006 mq = '@media (transform-3d)'; 3007 if (usePrefix) { 3008 mq += ',(-webkit-transform-3d)'; 3009 } 3010 } 3011 3012 mq += '{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}'; 3013 3014 testStyles(defaultStyle + mq, function(elem) { 3015 ret = elem.offsetWidth === 7 && elem.offsetHeight === 18; 3016 }); 3017 } 3018 3019 return ret; 3020 }); 3021 3022/*! 3023{ 3024 "name": "CSS Transform Style preserve-3d", 3025 "property": "preserve3d", 3026 "authors": ["edmellum"], 3027 "tags": ["css"], 3028 "notes": [{ 3029 "name": "MDN Docs", 3030 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/transform-style" 3031 },{ 3032 "name": "Related Github Issue", 3033 "href": "https://github.com/Modernizr/Modernizr/issues/762" 3034 }] 3035} 3036!*/ 3037/* DOC 3038Detects support for `transform-style: preserve-3d`, for getting a proper 3D perspective on elements. 3039*/ 3040 3041 Modernizr.addTest('preserve3d', testAllProps('transformStyle', 'preserve-3d')); 3042 3043/*! 3044{ 3045 "name": "CSS Transitions", 3046 "property": "csstransitions", 3047 "caniuse": "css-transitions", 3048 "tags": ["css"] 3049} 3050!*/ 3051 3052 Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); 3053 3054 3055 /** 3056 * prefixed returns the prefixed or nonprefixed property name variant of your input 3057 * 3058 * @memberof Modernizr 3059 * @name Modernizr.prefixed 3060 * @optionName Modernizr.prefixed() 3061 * @optionProp prefixed 3062 * @access public 3063 * @function prefixed 3064 * @param {string} prop - String name of the property to test for 3065 * @param {object} [obj]- An object to test for the prefixed properties on 3066 * @returns {string|false} The string representing the (possibly prefixed) valid 3067 * version of the property, or `false` when it is unsupported. 3068 * @example 3069 * 3070 * Modernizr.prefixed takes a string css value in the DOM style camel
3070Case (as 3071 * opposed to the css style kebab-case) form and returns the (possibly prefixed) 3072 * version of that property that the browser actually supports. 3073 * 3074 * For example, in older Firefox... 3075 * ```js 3076 * prefixed('boxSizing') 3077 * ``` 3078 * returns 'MozBoxSizing' 3079 * 3080 * In newer Firefox, as well as any other browser that support the unprefixed 3081 * version would simply return `boxSizing`. Any browser that does not support 3082 * the property at all, it will return `false`. 3083 * 3084 * By default, prefixed is checked against a DOM element. If you want to check 3085 * for a property on another object, just pass it as a second argument 3086 * 3087 * ```js 3088 * var rAF = prefixed('requestAnimationFrame', window); 3089 * 3090 * raf(function() { 3091 * renderFunction(); 3092 * }) 3093 * ``` 3094 * 3095 * Note that this will return _the actual function_ - not the name of the function. 3096 * If you need the actual name of the property, pass in `false` as a third argument 3097 * 3098 * ```js 3099 * var rAFProp = prefixed('requestAnimationFrame', window, false); 3100 * 3101 * rafProp === 'WebkitRequestAnimationFrame' // in older webkit 3102 * ``` 3103 * 3104 * One common use case for prefixed is if you're trying to determine which transition 3105 * end event to bind to, you might do something like... 3106 * ```js 3107 * var transEndEventNames = { 3108 * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser 3109 * 'MozTransition' : 'transitionend', * only for FF < 15 3110 * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ 3111 * }; 3112 * 3113 * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; 3114 * ``` 3115 * 3116 * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). 3117 */ 3118 3119 var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) { 3120 if (prop.indexOf('@') === 0) { 3121 return atRule(prop); 3122 } 3123 3124 if (prop.indexOf('-') != -1) { 3125 // Convert kebab-case to camelCase 3126 prop = cssToDOM(prop); 3127 } 3128 if (!obj) { 3129 return testPropsAll(prop, 'pfx'); 3130 } else { 3131 // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' 3132 return testPropsAll(prop, obj, elem); 3133 } 3134 }; 3135 3136 3137/*! 3138{ 3139 "name": "IndexedDB", 3140 "property": "indexeddb", 3141 "caniuse": "indexeddb", 3142 "tags": ["storage"], 3143 "polyfills": ["indexeddb"] 3144} 3145!*/ 3146/* DOC 3147Detects support for the IndexedDB client-side storage API (final spec). 3148*/ 3149 3150 // Vendors had inconsistent prefixing with the experimental Indexed DB: 3151 // - Webkit's implementation is accessible through webkitIndexedDB 3152 // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB 3153 // For speed, we don't test the legacy (and beta-only) indexedDB 3154 3155 var indexeddb = prefixed('indexedDB', window); 3156 Modernizr.addTest('indexeddb', !!indexeddb); 3157 3158 if (!!indexeddb) { 3159 Modernizr.addTest('indexeddb.deletedatabase', 'deleteDatabase' in indexeddb); 3160 } 3161; 3162/*! 3163{ 3164 "name": "requestAnimationFrame", 3165 "property": "requestanimationframe", 3166 "aliases": ["raf"], 3167 "caniuse": "requestanimationframe", 3168 "tags": ["animation"], 3169 "authors": ["Addy Osmani"], 3170 "notes": [{ 3171 "name": "W3C spec", 3172 "href": "http://www.w3.org/TR/animation-timing/" 3173 }], 3174 "polyfills": ["raf"] 3175} 3176!*/ 3177/* DOC 3178Detects support for the `window.requestAnimationFrame` API, for offloading animation repainting to the browser for optimized performance. 3179*/ 3180 3181 Modernizr.addTest('requestanimationframe', !!prefixed('requestAnimationFrame', window), {aliases: ['raf']}); 3182 3183 3184 // Run each test 3185 testRunner(); 3186 3187 // Remove the "no-js" class if it exists 3188 setClasses(classes); 3189 3190 delete ModernizrProto.addTest; 3191 delete ModernizrProto.addAsyncTest; 3192 3193 // Run the things that are supposed to run after the tests 3194 for (var i = 0; i < Modernizr._q.length; i++) { 3195 Modernizr._q[i](); 3196 } 3197 3198 // Leak Modernizr namespace 3199 window.Modernizr = Modernizr; 3200 3201 3202; 3203 3204})(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.