1/*! 2 * modernizr v3.3.1 3 * Build http://modernizr.com/download?-canvas-cssanimations-csscolumns-cssgradients-csstransforms-csstransitions-cssvhunit-cssvwunit-flexbox-inlinesvg-localstorage-placeholder-shapes-target-domprefixes-mq-prefixed-prefixes-setclasses-testallprops-testprop-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 tests = []; 27 28 29 /** 30 * 31 * ModernizrProto is the constructor for Modernizr 32 * 33 * @class 34 * @access public 35 */ 36 37 var ModernizrProto = { 38 // The current version, dummy 39 _version: '3.3.1', 40 41 // Any settings that don't work as separate modules 42 // can go in here as configuration. 43 _config: { 44 'classPrefix': '', 45 'enableClasses': true, 46 'enableJSClass': true, 47 'usePrefixes': true 48 }, 49 50 // Queue of tests 51 _q: [], 52 53 // Stub these for people who are listening 54 on: function(test, cb) { 55 // I don't really think people should do this, but we can 56 // safe guard it a bit. 57 // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 58 // This is in case people listen to synchronous tests. I would leave it out, 59 // but the code to *disallow* sync tests in the real version of this 60 // function is actually larger than this. 61 var self = this; 62 setTimeout(function() { 63 cb(self[test]); 64 }, 0); 65 }, 66 67 addTest: function(name, fn, options) { 68 tests.push({name: name, fn: fn, options: options}); 69 }, 70 71 addAsyncTest: function(fn) { 72 tests.push({name: null, fn: fn}); 73 } 74 }; 75 76 77 78 // Fake some of Object.create so we can force non test results to be non "own" properties. 79 var Modernizr = function() {}; 80 Modernizr.prototype = ModernizrProto; 81 82 // Leak modernizr globally when you `require` it rather than force it here. 83 // Overwrite name so constructor name is nicer :D 84 Modernizr = new Modernizr(); 85 86 87 88 var classes = []; 89 90 91 /** 92 * is returns a boolean if the typeof an obj is exactly type. 93 * 94 * @access private 95 * @function is 96 * @param {*} obj - A thing we want to check the type of 97 * @param {string} type - A string to compare the typeof against 98 * @returns {boolean} 99 */ 100 101 function is(obj, type) { 102 return typeof obj === type; 103 } 104 ; 105 106 /** 107 * Run through all tests and detect their support in the current UA. 108 * 109 * @access private 110 */ 111 112 function testRunner() { 113 var featureNames; 114 var feature; 115 var aliasIdx; 116 var result; 117 var nameIdx; 118 var featureName; 119 var featureNameSplit; 120 121 for (var featureIdx in tests) { 122 if (tests.hasOwnProperty(featureIdx)) { 123 featureNames = []; 124 feature = tests[featureIdx]; 125 // run the test, throw the return value into the Modernizr, 126 // then based on that boolean, define an appropriate className 127 // and push it into an array of classes we'll join later. 128 // 129 // If there is no name, it's an 'async' test that is run, 130 // but not directly added to the object. That should 131 // be done with a post-run addTest call. 132 if (feature.name) { 133 featureNames.push(feature.name.toLowerCase()); 134 135 if (feature.options && feature.options.aliases && feature.options.aliases.length) { 136 // Add all the aliases into the names list 137 for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 138 featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 139 } 140 } 141 } 142 143 // Run the test, or use the raw value if it's not a function 144 result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 145 146 147 // Set each of the names on the Modernizr object 148 for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 149 featureName = featureNames[nameIdx];
150 // Support dot properties as sub tests. We don't do checking to make sure 151 // that the implied parent tests have been added. You must call them in 152 // order (either in the test, or make the parent test a dependency). 153 // 154 // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 155 // hashtag famous last words 156 featureNameSplit = featureName.split('.'); 157 158 if (featureNameSplit.length === 1) { 159 Modernizr[featureNameSplit[0]] = result; 160 } else { 161 // cast to a Boolean, if not one already 162 /* jshint -W053 */ 163 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 164 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 165 } 166 167 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 168 } 169 170 classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 171 } 172 } 173 } 174 } 175 ; 176 177 /** 178 * docElement is a convenience wrapper to grab the root element of the document 179 * 180 * @access private 181 * @returns {HTMLElement|SVGElement} The root element of the document 182 */ 183 184 var docElement = document.documentElement; 185 186 187 /** 188 * A convenience helper to check if the document we are running in is an SVG document 189 * 190 * @access private 191 * @returns {boolean} 192 */ 193 194 var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 195 196 197 /** 198 * setClasses takes an array of class names and adds them to the root element 199 * 200 * @access private 201 * @function setClasses 202 * @param {string[]} classes - Array of class names 203 */ 204 205 // Pass in an and array of class names, e.g.:
206 // ['no-webp', 'borderradius', ...] 207 function setClasses(classes) { 208 var className = docElement.className; 209 var classPrefix = Modernizr._config.classPrefix || ''; 210 211 if (isSVG) { 212 className = className.baseVal; 213 } 214 215 // Change `no-js` to `js` (independently of the `enableClasses` option) 216 // Handle classPrefix on this too 217 if (Modernizr._config.enableJSClass) { 218 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 219 className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 220 } 221 222 if (Modernizr._config.enableClasses) { 223 // Add the new classes 224 className += ' ' + classPrefix + classes.join(' ' + classPrefix); 225 isSVG ? docElement.className.baseVal = className : docElement.className = className; 226 } 227 228 } 229 230 ; 231 232 /** 233 * If the browsers follow the spec, then they would expose vendor-specific style as: 234 * elem.style.WebkitBorderRadius 235 * instead of something like the following, which would be technically incorrect: 236 * elem.style.webkitBorderRadius 237 238 * Webkit ghosts their properties in lowercase but Opera & Moz do not. 239 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ 240 * erik.eae.net/archives/2008/03/10/21.48.10/ 241 242 * More here: github.com/Modernizr/Modernizr/issues/issue/21 243 * 244 * @access private 245 * @returns {string} The string representing the vendor-specific style properties 246 */ 247 248 var omPrefixes = 'Moz O ms Webkit'; 249 250 251 /** 252 * List of JavaScript DOM values used for tests 253 * 254 * @memberof Modernizr 255 * @name Modernizr._domPrefixes 256 * @optionName Modernizr._domPrefixes 257 * @optionProp domPrefixes 258 * @access public 259 * @example 260 * 261 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather 262 * than kebab-case properties, all properties are their Capitalized variant 263 * 264 * ```js 265 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; 266 * ``` 267 */ 268 269 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); 270 ModernizrProto._domPrefixes = domPrefixes; 271 272 273 /** 274 * List of property values to set for css tests. See ticket #21 275 * http://git.io/vUGl4 276 * 277 * @memberof Modernizr 278 * @name Modernizr._prefixes 279 * @optionName Modernizr._prefixes 280 * @optionProp prefixes 281 * @access public 282 * @example 283 * 284 * Modernizr._prefixes is the internal list of prefixes that we test against 285 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply 286 * an array of kebab-case vendor prefixes you can use within your code. 287 * 288 * Some common use cases include 289 * 290 * Generating all possible prefixed version of a CSS property 291 * ```js 292 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); 293 * 294 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' 295 * ``` 296 * 297 * Generating all possible prefixed version of a CSS value 298 * ```js 299 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; 300 * 301 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' 302 * ``` 303 */ 304 305 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : []); 306 307 // expose these for the plugin API. Look in the source for how to join() them against your input 308 ModernizrProto._prefixes = prefixes; 309 310 311 312 /** 313 * createElement is a convenience wrapper around document.createElement. Since we 314 * use createElement all over the place, this allows for (slightly) smaller code 315 * as well as abstracting away issues with creating elements in contexts other than 316 * HTML documents (e.g. SVG documents). 317 * 318 * @access private 319 * @function createElement 320 * @returns {HTMLElement|SVGElement} An HTML or SVG element 321 */ 322 323 function createElement() { 324 if (typeof document.createElement !== 'function') { 325 // This is the case in IE7, where the type of createElement is "object". 326 // For this reason, we cannot call apply() as Object is not a Function. 327 return document.createElement(arguments[0]); 328 } else if (isSVG) {
329 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 330 } else { 331 return document.createElement.apply(document, arguments); 332 } 333 } 334 335 ; 336 337 /** 338 * getBody returns the body of a document, or an element that can stand in for 339 * the body if a real body does not exist 340 * 341 * @access private 342 * @function getBody 343 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 344 * artificially created element that stands in for the body 345 */ 346 347 function getBody() { 348 // After page load injecting a fake body doesn't work so check if body exists 349 var body = document.body; 350 351 if (!body) { 352 // Can't use the real body create a fake one. 353 body = createElement(isSVG ? 'svg' : 'body'); 354 body.fake = true; 355 } 356 357 return body; 358 } 359 360 ; 361 362 /** 363 * injectElementWithStyles injects an element with style element and some CSS rules 364 * 365 * @access private 366 * @function injectElementWithStyles 367 * @param {string} rule - String representing a css rule 368 * @param {function} callback - A function that is used to test the injected element 369 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 370 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 371 * @returns {boolean} 372 */ 373 374 function injectElementWithStyles(rule, callback, nodes, testnames) { 375 var mod = 'modernizr'; 376 var style; 377 var ret; 378 var node; 379 var docOverflow; 380 var div = createElement('div'); 381 var body = getBody(); 382 383 if (parseInt(nodes, 10)) { 384 // In order not to give false positives we create a node for each test 385 // This also allows the method to scale for unspecified uses 386 while (nodes--) { 387 node = createElement('div'); 388 node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 389 div.appendChild(node); 390 } 391 } 392 393 style = createElement('style'); 394 style.type = 'text/css'; 395 style.id = 's' + mod; 396 397 // 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. 398 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 399 (!body.fake ? div : body).appendChild(style); 400 body.appendChild(div); 401 402 if (style.styleSheet) { 403 style.styleSheet.cssText = rule; 404 } else { 405 style.appendChild(document.createTextNode(rule)); 406 } 407 div.id = mod; 408 409 if (body.fake) { 410 //avoid crashing IE8, if background image is used 411 body.style.background = ''; 412 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 413 body.style.overflow = 'hidden'; 414 docOverflow = docElement.style.overflow; 415 docElement.style.overflow = 'hidden'; 416 docElement.appendChild(body); 417 } 418 419 ret = callback(div, rule); 420 // If this is done after page load we don't want to remove the body so check if body exists 421 if (body.fake) { 422 body.parentNode.removeChild(body); 423 docElement.style.overflow = docOverflow; 424 // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 425 docElement.offsetHeight; 426 } else { 427 div.parentNode.removeChild(div); 428 } 429 430 return !!ret; 431 432 } 433 434 ; 435 436 /** 437 * Modernizr.mq tests a given media query, live against the current state of the window 438 * adapted from matchMedia polyfill by Scott Jehl and Paul Irish 439 * gist.github.com/786768 440 * 441 * @memberof Modernizr 442 * @name Modernizr.mq 443 * @optionName Modernizr.mq() 444 * @optionProp mq 445 * @access public 446 * @function mq 447 * @param {string} mq - String of the media query we want to test 448 * @returns {boolean} 449 * @example 450 * Modernizr.mq allows for you to programmatically check if the current browser 451 * window state matches a media query. 452 * 453 * ```js 454 * var query = Modernizr.mq('(min-width: 900px)'); 455 * 456 * if (query) { 457 * // the browser window is larger than 900px 458 * } 459 * ``` 460 * 461 * Only valid media queries are supported, therefore you must always include values 462 * with your media query 463 * 464 * ```js 465 * // good 466 * Modernizr.mq('(min-width: 900px)'); 467 * 468 * // bad 469 * Modernizr.mq('min-width'); 470 * ``` 471 * 472 * If you would just like to test that media queries are supported in general, use 473 * 474 * ```js 475 * Modernizr.mq('only all'); // true if MQ are supported, false if not 476 * ``` 477 * 478 * 479 * Note that if the browser does not support media queries (e.g. old IE) mq will 480 * always return false. 481 */ 482 483 var mq = (function() { 484 var matchMedia = window.matchMedia || window.msMatchMedia; 485 if (matchMedia) { 486 return function(mq) { 487 var mql = matchMedia(mq); 488 return mql && mql.matches || false; 489 }; 490 } 491 492 return function(mq) { 493 var bool = false;
494 495 injectElementWithStyles('@media ' + mq + ' { #modernizr { position: absolute; } }', function(node) { 496 bool = (window.getComputedStyle ? 497 window.getComputedStyle(node, null) : 498 node.currentStyle).position == 'absolute'; 499 }); 500 501 return bool; 502 }; 503 })(); 504 505 506 ModernizrProto.mq = mq; 507 508 509 510 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); 511 ModernizrProto._cssomPrefixes = cssomPrefixes; 512 513 514 515 /** 516 * contains checks to see if a string contains another string 517 * 518 * @access private 519 * @function contains 520 * @param {string} str - The string we want to check for substrings 521 * @param {string} substr - The substring we want to search the first string for 522 * @returns {boolean} 523 */ 524 525 function contains(str, substr) { 526 return !!~('' + str).indexOf(substr); 527 } 528 529 ; 530 531 /** 532 * Create our "modernizr" element that we do most feature tests on. 533 * 534 * @access private 535 */ 536 537 var modElem = { 538 elem: createElement('modernizr') 539 }; 540 541 // Clean up this element 542 Modernizr._q.push(function() { 543 delete modElem.elem; 544 }); 545 546 547 548 var mStyle = { 549 style: modElem.elem.style 550 }; 551 552 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 553 // the front of the queue. 554 Modernizr._q.unshift(function() { 555 delete mStyle.style; 556 }); 557 558 559 560 /** 561 * domToCSS takes a camelCase string and converts it to kebab-case 562 * e.g. boxSizing -> box-sizing 563 * 564 * @access private 565 * @function domToCSS 566 * @param {string} name - String name of camelCase prop we want to convert 567 * @returns {string} The kebab-case version of the supplied name 568 */ 569 570 function domToCSS(name) { 571 return name.replace(/([A-Z])/g, function(str, m1) { 572 return '-' + m1.toLowerCase(); 573 }).replace(/^ms-/, '-ms-'); 574 } 575 ; 576 577 /** 578 * nativeTestProps allows for us to use native feature detection functionality if available. 579 * some prefixed form, or false, in the case of an unsupported rule 580 * 581 * @access private 582 * @function nativeTestProps 583 * @param {array} props - An array of property names 584 * @param {string} value - A string representing the value we want to check via @supports 585 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 586 */ 587 588 // Accepts a list of property names and a single value 589 // Returns `undefined` if native detection not available 590 function nativeTestProps(props, value) { 591 var i = props.length; 592 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface 593 if ('CSS' in window && 'supports' in window.CSS) { 594 // Try every prefixed variant of the property 595 while (i--) { 596 if (window.CSS.supports(domToCSS(props[i]), value)) { 597 return true; 598 } 599 } 600 return false; 601 } 602 // Otherwise fall back to at-rule (for Opera 12.x) 603 else if ('CSSSupportsRule' in window) { 604 // Build a condition string for every prefixed variant 605 var conditionText = []; 606 while (i--) { 607 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 608 } 609 conditionText = conditionText.join(' or '); 610 return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { 611 return getComputedStyle(node, null).position == 'absolute'; 612 }); 613 } 614 return undefined; 615 } 616 ; 617 618 /** 619 * cssToDOM takes a kebab-case string and converts it to camelCase 620 * e.g. box-sizing -> boxSizing 621 * 622 * @access private 623 * @function cssToDOM 624 * @param {string} name - String name of kebab-case prop we want to convert 625 * @returns {string} The camelCase version of the supplied name 626 */ 627 628 function cssToDOM(name) { 629 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 630 return m1 + m2.toUpperCase(); 631 }).replace(/^-/, ''); 632 } 633 ; 634 635 // testProps is a generic CSS / DOM property test. 636 637 // In testing support for a given CSS property, it's legit to test: 638 // `elem.style[styleName] !== undefined` 639 // If the property is supported it will return an empty string, 640 // if unsupported it will return undefined. 641 642 // We'll take advantage of this quick test and skip setting a style 643 // on our modernizr element, but instead just testing undefined vs 644 // empty string. 645 646 // Property names can be provided in either camelCase or kebab-case. 647 648 function testProps(props, prefixed, value, skipValueTest) { 649 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 650 651 // Try native detect first 652 if (!is(value, 'undefined')) { 653 var result = nativeTestProps(props, value); 654 if (!is(result, 'undefined')) { 655 return result; 656 } 657 } 658 659 // Otherwise do it properly 660 var afterInit, i, propsLength, prop, before; 661 662 // If we don't have a style element, that means we're running async or after 663 // the core tests, so we'll need to create our own elements to use 664 665 // inside of an SVG element, in certain browsers, the `style` element is only 666 // defined for valid tags. Therefore, if `modernizr` does not have one, we 667 // fall back to a less used element and hope for the best. 668 var elems = ['modernizr', 'tspan']; 669 while (!mStyle.style) { 670 afterInit = true; 671 mStyle.modElem = createElement(elems.shift()); 672 mStyle.style = mStyle.modElem.style; 673 } 674 675 // Delete the objects if we created them. 676 function cleanElems() { 677 if (afterInit) { 678 delete mStyle.style; 679 delete mStyle.modElem; 680 } 681 } 682 683 propsLength = props.length; 684 for (i = 0; i < propsLength; i++) { 685 prop = props[i]; 686 before = mStyle.style[prop]; 687 688 if (contains(prop, '-')) { 689 prop = cssToDOM(prop); 690 } 691 692 if (mStyle.style[prop] !== undefined) { 693 694 // If value to test has been passed in, do a set-and-check test. 695 // 0 (integer) is a valid property value, so check that `value` isn't 696 // undefined, rather than just checking it's truthy. 697 if (!skipValueTest && !is(value, 'undefined')) { 698 699 // Needs a try catch block because of old IE. This is slow, but will 700 // be avoided in most cases because `skipValueTest` will be used. 701 try { 702 mStyle.style[prop] = value; 703 } catch (e) {} 704 705 // If the property value has changed, we assume the value used is 706 // supported. If `value` is empty string, it'll fail here (because 707 // it hasn't changed), which matches how browsers have implemented 708 // CSS.supports() 709 if (mStyle.style[prop] != before) { 710 cleanElems(); 711 return prefixed == 'pfx' ? prop : true; 712 } 713 } 714 // Otherwise just return true, or the property name if this is a 715 // `prefixed()` call 716 else { 717 cleanElems(); 718 return prefixed == 'pfx' ? prop : true; 719 } 720 } 721 } 722 cleanElems(); 723 return false;
724 } 725 726 ; 727 728 /** 729 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. 730 * 731 * @access private 732 * @function fnBind 733 * @param {function} fn - a function you want to change `this` reference to 734 * @param {object} that - the `this` you want to call the function with 735 * @returns {function} The wrapped version of the supplied function 736 */ 737 738 function fnBind(fn, that) { 739 return function() { 740 return fn.apply(that, arguments); 741 }; 742 } 743 744 ; 745 746 /** 747 * testDOMProps is a generic DOM property test; if a browser supports 748 * a certain property, it won't return undefined for it. 749 * 750 * @access private 751 * @function testDOMProps 752 * @param {array.<string>} props - An array of properties to test for 753 * @param {object} obj - An object or Element you want to use to test the parameters again 754 * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check 755 */ 756 function testDOMProps(props, obj, elem) { 757 var item; 758 759 for (var i in props) { 760 if (props[i] in obj) { 761 762 // return the property name as a string 763 if (elem === false) { 764 return props[i]; 765 } 766 767 item = obj[props[i]]; 768 769 // let's bind a function 770 if (is(item, 'function')) { 771 // bind to obj unless overriden 772 return fnBind(item, elem || obj); 773 } 774 775 // return the unbound function or obj or value 776 return item; 777 } 778 } 779 return false; 780 } 781 782 ; 783 784 /** 785 * testPropsAll tests a list of DOM properties we want to check against. 786 * We specify literally ALL possible (known and/or likely) properties on 787 * the element including the non-vendor prefixed one, for forward- 788 * compatibility. 789 * 790 * @access private 791 * @function testPropsAll 792 * @param {string} prop - A string of the property to test for 793 * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip 794 * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against 795 * @param {string} [value] - A string of a css value 796 * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set 797 */ 798 function testPropsAll(prop, prefixed, elem, value, skipValueTest) { 799 800 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 801 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 802 803 // did they call .prefixed('boxSizing') or are we just testing a prop? 804 if (is(prefixed, 'string') || is(prefixed, 'undefined')) { 805 return testProps(props, prefixed, value, skipValueTest); 806 807 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) 808 } else { 809 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 810 return testDOMProps(props, prefixed, elem); 811 } 812 } 813 814 // Modernizr.testAllProps() investigates whether a given style property, 815 // or any of its vendor-prefixed variants, is recognized 816 // 817 // Note that the property names must be provided in the camelCase variant. 818 // Modernizr.testAllProps('boxSizing') 819 ModernizrProto.testAllProps = testPropsAll; 820 821 822 823 /** 824 * atRule returns a given CSS property at-rule (eg @keyframes), possibly in 825 * some prefixed form, or false, in the case of an unsupported rule 826 * 827 * @memberof Modernizr 828 * @name Modernizr.atRule 829 * @optionName Modernizr.atRule() 830 * @optionProp atRule 831 * @access public 832 * @function atRule 833 * @param {string} prop - String name of the @-rule to test for 834 * @returns {string|boolean} The string representing the (possibly prefixed) 835 * valid version of the @-rule, or `false` when it is unsupported. 836 * @example 837 * ```js 838 * var keyframes = Modernizr.atRule('@keyframes'); 839 * 840 * if (keyframes) { 841 * // keyframes are supported 842 * // could be `@-webkit-keyframes` or `@keyframes` 843 * } else { 844 * // keyframes === `false` 845 * } 846 * ``` 847 * 848 */ 849 850 var atRule = function(prop) { 851 var length = prefixes.length; 852 var cssrule = window.CSSRule; 853 var rule; 854 855 if (typeof cssrule === 'undefined') { 856 return undefined; 857 } 858 859 if (!prop) { 860 return false;
861 } 862 863 // remove literal @ from beginning of provided property 864 prop = prop.replace(/^@/, ''); 865 866 // CSSRules use underscores instead of dashes 867 rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE'; 868 869 if (rule in cssrule) { 870 return '@' + prop; 871 } 872 873 for (var i = 0; i < length; i++) { 874 // prefixes gives us something like -o-, and we want O_ 875 var prefix = prefixes[i]; 876 var thisRule = prefix.toUpperCase() + '_' + rule; 877 878 if (thisRule in cssrule) { 879 return '@-' + prefix.toLowerCase() + '-' + prop; 880 } 881 } 882 883 return false; 884 }; 885 886 ModernizrProto.atRule = atRule; 887 888 889 890 /** 891 * prefixed returns the prefixed or nonprefixed property name variant of your input 892 * 893 * @memberof Modernizr 894 * @name Modernizr.prefixed 895 * @optionName Modernizr.prefixed() 896 * @optionProp prefixed 897 * @access public 898 * @function prefixed 899 * @param {string} prop - String name of the property to test for 900 * @param {object} [obj] - An object to test for the prefixed properties on 901 * @param {HTMLElement} [elem] - An element used to test specific properties against 902 * @returns {string|false} The string representing the (possibly prefixed) valid 903 * version of the property, or `false` when it is unsupported. 904 * @example 905 * 906 * Modernizr.prefixed takes a string css value in the DOM style camelCase (as 907 * opposed to the css style kebab-case) form and returns the (possibly prefixed) 908 * version of that property that the browser actually supports. 909 * 910 * For example, in older Firefox... 911 * ```js 912 * prefixed('boxSizing') 913 * ``` 914 * returns 'MozBoxSizing' 915 * 916 * In newer Firefox, as well as any other browser that support the unprefixed 917 * version would simply return `boxSizing`. Any browser that does not support 918 * the property at all, it will return `false`. 919 * 920 * By default, prefixed is checked against a DOM element. If you want to check 921 * for a property on another object, just pass it as a second argument 922 * 923 * ```js 924 * var rAF = prefixed('requestAnimationFrame', window); 925 * 926 * raf(function() { 927 * renderFunction(); 928 * }) 929 * ``` 930 * 931 * Note that this will return _the actual function_ - not the name of the function. 932 * If you need the actual name of the property, pass in `false` as a third argument 933 * 934 * ```js 935 * var rAFProp = prefixed('requestAnimationFrame', window, false); 936 * 937 * rafProp === 'WebkitRequestAnimationFrame' // in older webkit 938 * ``` 939 * 940 * One common use case for prefixed is if you're trying to determine which transition 941 * end event to bind to, you might do something like... 942 * ```js 943 * var transEndEventNames = { 944 * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser 945 * 'MozTransition' : 'transitionend', * only for FF < 15 946 * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ 947 * }; 948 * 949 * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; 950 * ``` 951 * 952 * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). 953 */ 954 955 var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) { 956 if (prop.indexOf('@') === 0) { 957 return atRule(prop); 958 } 959 960 if (prop.indexOf('-') != -1) { 961 // Convert kebab-case to camelCase 962 prop = cssToDOM(prop); 963 } 964 if (!obj) { 965 return testPropsAll(prop, 'pfx'); 966 } else { 967 // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' 968 return testPropsAll(prop, obj, elem); 969 } 970 }; 971 972 973 974 /** 975 * testAllProps determines whether a given CSS property is supported in the browser 976 * 977 * @memberof Modernizr 978 * @name Modernizr.testAllProps 979 * @optionName Modernizr.testAllProps() 980 * @optionProp testAllProps 981 * @access public 982 * @function testAllProps 983 * @param {string} prop - String naming the property to test (either camelCase or kebab-case) 984 * @param {string} [value] - String of the value to test 985 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection 986 * @example 987 * 988 * testAllProps determines whether a given CSS property, in some prefixed form, 989 * is supported by the browser. 990 * 991 * ```js 992 * testAllProps('boxSizing') // true 993 * ``` 994 * 995 * It can optionally be given a CSS value in string form to test if a property 996 * value is valid 997 * 998 * ```js 999 * testAllProps('display', 'block') // true 1000 * testAllProps('display', 'penguin') // false 1001 * ``` 1002 * 1003 * A boolean can be passed as a third parameter to skip the value check when 1004 * native detection (@supports) isn't available. 1005 * 1006 * ```js 1007 * testAllProps('shapeOutside', 'content-box', true); 1008 * ``` 1009 */ 1010 1011 function testAllProps(prop, value, skipValueTest) { 1012 return testPropsAll(prop, undefined, undefined, value, skipValueTest); 1013 } 1014 ModernizrProto.testAllProps = testAllProps; 1015 1016 1017 /** 1018 * testProp() investigates whether a given style property is recognized 1019 * Property names can be provided in either camelCase or kebab-case. 1020 * 1021 * @memberof Modernizr 1022 * @name Modernizr.testProp 1023 * @access public 1024 * @optionName Modernizr.testProp() 1025 * @optionProp testProp 1026 * @function testProp 1027 * @param {string} prop - Name of the CSS property to check 1028 * @param {string} [value] - Name of the CSS value to check 1029 * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported 1030 * @returns {boolean} 1031 * @example 1032 * 1033 * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed 1034 * version of the string. 1035 * 1036 * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) 1037 * 1038 * ```js 1039 * Modernizr.testProp('pointerEvents') // true 1040 * ``` 1041 * 1042 * You can also provide a value as an optional second argument to check if a 1043 * specific value is supported 1044 * 1045 * ```js
1046 * Modernizr.testProp('pointerEvents', 'none') // true 1047 * Modernizr.testProp('pointerEvents', 'penguin') // false 1048 * ``` 1049 */ 1050 1051 var testProp = ModernizrProto.testProp = function(prop, value, useValue) { 1052 return testProps([prop], undefined, value, useValue); 1053 }; 1054 1055/*! 1056{ 1057 "name": "Flexbox", 1058 "property": "flexbox", 1059 "caniuse": "flexbox", 1060 "tags": ["css"], 1061 "notes": [{ 1062 "name": "The _new_ flexbox", 1063 "href": "http://dev.w3.org/csswg/css3-flexbox" 1064 }], 1065 "warnings": [ 1066 "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect." 1067 ] 1068} 1069!*/ 1070/* DOC 1071Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container. 1072*/ 1073 1074 Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', true)); 1075 1076/*! 1077{ 1078 "name": "CSS Animations", 1079 "property": "cssanimations", 1080 "caniuse": "css-animation", 1081 "polyfills": ["transformie", "csssandpaper"], 1082 "tags": ["css"], 1083 "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], 1084 "notes": [{ 1085 "name" : "Article: 'Dispelling the Android CSS animation myths'", 1086 "href": "https://goo.gl/OGw5Gm" 1087 }] 1088} 1089!*/ 1090/* DOC 1091Detects whether or not elements can be animated using CSS 1092*/ 1093 1094 Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); 1095 1096/*! 1097{ 1098 "name": "CSS Gradients", 1099 "caniuse": "css-gradients", 1100 "property": "cssgradients", 1101 "tags": ["css"], 1102 "knownBugs": ["False-positives on webOS (https://github.com/Modernizr/Modernizr/issues/202)"], 1103 "notes": [{ 1104 "name": "Webkit Gradient Syntax", 1105 "href": "https://webkit.org/blog/175/introducing-css-gradients/" 1106 },{ 1107 "name": "Linear Gradient Syntax", 1108 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient" 1109 },{ 1110 "name": "W3C Gradient Spec", 1111 "href": "https://drafts.csswg.org/css-images-3/#gradients" 1112 }] 1113} 1114!*/ 1115 1116 1117 Modernizr.addTest('cssgradients', function() { 1118 1119 var str1 = 'background-image:'; 1120 var str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));'; 1121 var css = ''; 1122 var angle; 1123 1124 for (var i = 0, len = prefixes.length - 1; i < len; i++) { 1125 angle = (i === 0 ? 'to ' : ''); 1126 css += str1 + prefixes[i] + 'linear-gradient(' + angle + 'left top, #9f9, white);'; 1127 } 1128 1129 if (Modernizr._config.usePrefixes) { 1130 // legacy webkit syntax (FIXME: remove when syntax not in use anymore) 1131 css += str1 + '-webkit-' + str2; 1132 } 1133 1134 var elem = createElement('a'); 1135 var style = elem.style; 1136 style.cssText = css; 1137 1138 // IE6 returns undefined so cast to string 1139 return ('' + style.backgroundImage).indexOf('gradient') > -1; 1140 }); 1141 1142/*! 1143{ 1144 "name": "CSS Transforms", 1145 "property": "csstransforms", 1146 "caniuse": "transforms2d", 1147 "tags": ["css"] 1148} 1149!*/ 1150 1151 Modernizr.addTest('csstransforms', function() { 1152 // Android < 3.0 is buggy, so we sniff and blacklist 1153 // http://git.io/hHzL7w 1154 return navigator.userAgent.indexOf('Android 2.') === -1 && 1155 testAllProps('transform', 'scale(1)', true); 1156 }); 1157 1158/*! 1159{ 1160 "name": "CSS Transitions", 1161 "property": "csstransitions", 1162 "caniuse": "css-transitions", 1163 "tags": ["css"] 1164} 1165!*/ 1166 1167 Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); 1168 1169/*! 1170{ 1171 "name": "CSS Columns", 1172 "property": "csscolumns", 1173 "caniuse": "multicolumn", 1174 "polyfills": ["css3multicolumnjs"], 1175 "tags": ["css"] 1176} 1177!*/ 1178 1179 1180 (function() { 1181 1182 /* jshint -W053 */ 1183 Modernizr.addTest('csscolumns', function() { 1184 var bool = false;
1185 var test = testAllProps('columnCount'); 1186 try { 1187 if (bool = !!test) { 1188 bool = new Boolean(bool); 1189 } 1190 } catch (e) {} 1191 1192 return bool; 1193 }); 1194 1195 var props = ['Width', 'Span', 'Fill', 'Gap', 'Rule', 'RuleColor', 'RuleStyle', 'RuleWidth', 'BreakBefore', 'BreakAfter', 'BreakInside']; 1196 var name, test; 1197 1198 for (var i = 0; i < props.length; i++) { 1199 name = props[i].toLowerCase(); 1200 test = testAllProps('column' + props[i]); 1201 1202 // break-before, break-after & break-inside are not "column"-prefixed in spec 1203 if (name === 'breakbefore' || name === 'breakafter' || name == 'breakinside') { 1204 test = test || testAllProps(props[i]); 1205 } 1206 1207 Modernizr.addTest('csscolumns.' + name, test); 1208 } 1209 1210 1211 })(); 1212 1213 1214 1215 /** 1216 * testStyles injects an element with style element and some CSS rules 1217 * 1218 * @memberof Modernizr 1219 * @name Modernizr.testStyles 1220 * @optionName Modernizr.testStyles() 1221 * @optionProp testStyles 1222 * @access public 1223 * @function testStyles 1224 * @param {string} rule - String representing a css rule 1225 * @param {function} callback - A function that is used to test the injected element 1226 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 1227 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 1228 * @returns {boolean} 1229 * @example 1230 * 1231 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page 1232 * along with (possibly multiple) DOM elements. This lets you check for features 1233 * 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). 1234 * 1235 * ```js 1236 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { 1237 * // elem is the first DOM node in the page (by default #modernizr) 1238 * // rule is the first argument you supplied - the CSS rule in string form 1239 * 1240 * addTest('widthworks', elem.style.width === '9px') 1241 * }); 1242 * ``` 1243 * 1244 * If your test requires multiple nodes, you can include a third argument 1245 * indicating how many additional div elements to include on the page. The 1246 * additional nodes are injected as children of the `elem` that is returned as 1247 * the first argument to the callback. 1248 * 1249 * ```js 1250 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { 1251 * document.getElementById('modernizr').style.width === '1px'; // true 1252 * document.getElementById('modernizr2').style.width === '2px'; // true 1253 * elem.firstChild === document.getElementById('modernizr2'); // true 1254 * }, 1); 1255 * ``` 1256 * 1257 * By default, all of the additional elements have an ID of `modernizr[n]`, where 1258 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, 1259 * the second additional is `#modernizr3`, etc.). 1260 * If you want to have more meaningful IDs for your function, you can provide 1261 * them as the fourth argument, as an array of strings 1262 * 1263 * ```js 1264 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { 1265 * elem.firstChild === document.getElementById('foo'); // true 1266 * elem.lastChild === document.getElementById('bar'); // true 1267 * }, 2, ['foo', 'bar']); 1268 * ``` 1269 * 1270 */ 1271 1272 var testStyles = ModernizrProto.testStyles = injectElementWithStyles; 1273 1274/*! 1275{ 1276 "name": "CSS vh unit", 1277 "property": "cssvhunit", 1278 "caniuse": "viewport-units", 1279 "tags": ["css"], 1280 "builderAliases": ["css_vhunit"], 1281 "notes": [{ 1282 "name": "Related Modernizr Issue", 1283 "href": "https://github.com/Modernizr/Modernizr/issues/572" 1284 },{ 1285 "name": "Similar JSFiddle", 1286 "href": "https://jsfiddle.net/FWeinb/etnYC/" 1287 }] 1288} 1289!*/ 1290 1291 testStyles('#modernizr { height: 50vh; }', function(elem) { 1292 var height = parseInt(window.innerHeight / 2, 10); 1293 var compStyle = parseInt((window.getComputedStyle ? 1294 getComputedStyle(elem, null) : 1295 elem.currentStyle).height, 10); 1296 Modernizr.addTest('cssvhunit', compStyle == height); 1297 }); 1298 1299/*! 1300{ 1301 "name": "CSS vw unit", 1302 "property": "cssvwunit", 1303 "caniuse": "viewport-units", 1304 "tags": ["css"], 1305 "builderAliases": ["css_vwunit"], 1306 "notes": [{ 1307 "name": "Related Modernizr Issue", 1308 "href": "https://github.com/Modernizr/Modernizr/issues/572" 1309 },{ 1310 "name": "JSFiddle Example", 1311 "href": "https://jsfiddle.net/FWeinb/etnYC/" 1312 }] 1313} 1314!*/ 1315 1316 testStyles('#modernizr { width: 50vw; }', function(elem) { 1317 var width = parseInt(window.innerWidth / 2, 10); 1318 var compStyle = parseInt((window.getComputedStyle ? 1319 getComputedStyle(elem, null) : 1320 elem.currentStyle).width, 10); 1321 1322 Modernizr.addTest('cssvwunit', compStyle == width); 1323 }); 1324 1325/*! 1326{ 1327 "name": "placeholder attribute", 1328 "property": "placeholder", 1329 "tags": ["forms", "attribute"], 1330 "builderAliases": ["forms_placeholder"] 1331} 1332!*/ 1333/* DOC 1334Tests for placeholder attribute in inputs and textareas 1335*/ 1336 1337 Modernizr.addTest('placeholder', ('placeholder' in createElement('input') && 'placeholder' in createElement('textarea'))); 1338 1339/*! 1340{ 1341 "name": "Local Storage", 1342 "property": "localstorage", 1343 "caniuse": "namevalue-storage", 1344 "tags": ["storage"], 1345 "knownBugs": [], 1346 "notes": [], 1347 "warnings": [], 1348 "polyfills": [ 1349 "joshuabell-polyfill", 1350 "cupcake", 1351 "storagepolyfill", 1352 "amplifyjs", 1353 "yui-cacheoffline" 1354 ] 1355} 1356!*/ 1357 1358 // In FF4, if disabled, window.localStorage should === null. 1359 1360 // Normally, we could not test that directly and need to do a 1361 // `('localStorage' in window) && ` test first because otherwise Firefox will 1362 // throw bugzil.la/365772 if cookies are disabled 1363 1364 // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem 1365 // will throw the exception: 1366 // QUOTA_EXCEEDED_ERROR DOM Exception 22. 1367 // Peculiarly, getItem and removeItem calls do not throw. 1368 1369 // Because we are forced to try/catch this, we'll go aggressive. 1370 1371 // Just FWIW: IE8 Compat mode supports these features completely: 1372 // www.quirksmode.org/dom/html5.html 1373 // But IE8 doesn't support either with local files 1374 1375 Modernizr.addTest('localstorage', function() { 1376 var mod = 'modernizr'; 1377 try { 1378 localStorage.setItem(mod, mod); 1379 localStorage.removeItem(mod); 1380 return true; 1381 } catch (e) { 1382 return false;
1383 } 1384 }); 1385 1386/*! 1387{ 1388 "name": "Inline SVG", 1389 "property": "inlinesvg", 1390 "caniuse": "svg-html5", 1391 "tags": ["svg"], 1392 "notes": [{ 1393 "name": "Test page", 1394 "href": "https://paulirish.com/demo/inline-svg" 1395 }, { 1396 "name": "Test page and results", 1397 "href": "https://codepen.io/eltonmesquita/full/GgXbvo/" 1398 }], 1399 "polyfills": ["inline-svg-polyfill"], 1400 "knownBugs": ["False negative on some Chromia browsers."] 1401} 1402!*/ 1403/* DOC 1404Detects support for inline SVG in HTML (not within XHTML). 1405*/ 1406 1407 Modernizr.addTest('inlinesvg', function() { 1408 var div = createElement('div'); 1409 div.innerHTML = '<svg/>'; 1410 return (typeof SVGRect != 'undefined' && div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg'; 1411 }); 1412 1413/*! 1414{ 1415 "name": "Canvas", 1416 "property": "canvas", 1417 "caniuse": "canvas", 1418 "tags": ["canvas", "graphics"], 1419 "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"] 1420} 1421!*/ 1422/* DOC 1423Detects support for the `<canvas>` element for 2D drawing. 1424*/ 1425 1426 // On the S60 and BB Storm, getContext exists, but always returns undefined 1427 // so we actually have to call getContext() to verify 1428 // github.com/Modernizr/Modernizr/issues/issue/97/ 1429 Modernizr.addTest('canvas', function() { 1430 var elem = createElement('canvas'); 1431 return !!(elem.getContext && elem.getContext('2d')); 1432 }); 1433 1434/*! 1435{ 1436 "name": "CSS Shapes", 1437 "property": "shapes", 1438 "tags": ["css"], 1439 "notes": [{ 1440 "name": "CSS Shapes W3C specification", 1441 "href": "https://www.w3.org/TR/css-shapes" 1442 },{ 1443 "name": "Examples from Adobe", 1444 "href": "http://webplatform.adobe.com/shapes/" 1445 }, { 1446 "name": "Samples showcasing uses of Shapes", 1447 "href": "http://codepen.io/collection/qFesk" 1448 }] 1449} 1450!*/ 1451 1452 Modernizr.addTest('shapes', testAllProps('shapeOutside', 'content-box', true)); 1453 1454/*! 1455{ 1456 "name": "CSS :target pseudo-class", 1457 "caniuse": "css-sel3", 1458 "property": "target", 1459 "tags": ["css"], 1460 "notes": [{ 1461 "name": "MDN documentation", 1462 "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/:target" 1463 }], 1464 "authors": ["@zachleat"], 1465 "warnings": ["Opera Mini supports :target but doesn't update the hash for anchor links."] 1466} 1467!*/ 1468/* DOC 1469Detects support for the ':target' CSS pseudo-class. 1470*/ 1471 1472 // querySelector 1473 Modernizr.addTest('target', function() { 1474 var doc = window.document; 1475 if (!('querySelectorAll' in doc)) { 1476 return false; 1477 } 1478 1479 try { 1480 doc.querySelectorAll(':target'); 1481 return true; 1482 } catch (e) { 1483 return false; 1484 } 1485 }); 1486 1487 1488 // Run each test 1489 testRunner(); 1490 1491 // Remove the "no-js" class if it exists 1492 setClasses(classes); 1493 1494 delete ModernizrProto.addTest; 1495 delete ModernizrProto.addAsyncTest; 1496 1497 // Run the things that are supposed to run after the tests 1498 for (var i = 0; i < Modernizr._q.length; i++) { 1499 Modernizr._q[i](); 1500 } 1501 1502 // Leak Modernizr namespace 1503 window.Modernizr = Modernizr; 1504 1505 1506; 1507 1508})(window, document); 1509
vendor: 44,683 bytes, lines 1510-3053
1510/*! picturefill - v3.0.2 - 2016-02-12 1511 * https://scottjehl.github.io/picturefill/ 1512 * Copyright (c) 2016 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT 1513 */ 1514/*! Gecko-Picture - v1.0 1515 * https://github.com/scottjehl/picturefill/tree/3.0/src/plugins/gecko-picture 1516 * Firefox's early picture implementation (prior to FF41) is static and does 1517 * not react to viewport changes. This tiny module fixes this. 1518 */ 1519(function(window) { 1520 /*jshint eqnull:true */ 1521 var ua = navigator.userAgent; 1522 1523 if ( window.HTMLPictureElement && ((/ecko/).test(ua) && ua.match(/rv\:(\d+)/) && RegExp.$1 < 45) ) { 1524 addEventListener("resize", (function() { 1525 var timer; 1526 1527 var dummySrc = document.createElement("source"); 1528 1529 var fixRespimg = function(img) { 1530 var source, sizes; 1531 var picture = img.parentNode; 1532 1533 if (picture.nodeName.toUpperCase() === "PICTURE") { 1534 source = dummySrc.cloneNode(); 1535 1536 picture.insertBefore(source, picture.firstElementChild); 1537 setTimeout(function() { 1538 picture.removeChild(source); 1539 }); 1540 } else if (!img._pfLastSize || img.offsetWidth > img._pfLastSize) { 1541 img._pfLastSize = img.offsetWidth; 1542 sizes = img.sizes; 1543 img.sizes += ",100vw"; 1544 setTimeout(function() { 1545 img.sizes = sizes; 1546 }); 1547 } 1548 }; 1549 1550 var findPictureImgs = function() { 1551 var i; 1552 var imgs = document.querySelectorAll("picture > img, img[srcset][sizes]"); 1553 for (i = 0; i < imgs.length; i++) { 1554 fixRespimg(imgs[i]); 1555 } 1556 }; 1557 var onResize = function() { 1558 clearTimeout(timer); 1559 timer = setTimeout(findPictureImgs, 99); 1560 }; 1561 var mq = window.matchMedia && matchMedia("(orientation: landscape)"); 1562 var init = function() { 1563 onResize(); 1564 1565 if (mq && mq.addListener) { 1566 mq.addListener(onResize); 1567 } 1568 }; 1569 1570 dummySrc.srcset = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; 1571 1572 if (/^[c|i]|d$/.test(document.readyState || "")) { 1573 init(); 1574 } else { 1575 document.addEventListener("DOMContentLoaded", init); 1576 } 1577 1578 return onResize; 1579 })()); 1580 } 1581})(window); 1582 1583/*! Picturefill - v3.0.2 1584 * http://scottjehl.github.io/picturefill 1585 * Copyright (c) 2015 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; 1586 * License: MIT 1587 */ 1588 1589(function( window, document, undefined ) { 1590 // Enable strict mode 1591 "use strict"; 1592 1593 // HTML shim|v it for old IE (IE9 will still need the HTML video tag workaround) 1594 document.createElement( "picture" ); 1595 1596 var warn, eminpx, alwaysCheckWDescriptor, evalId; 1597 // local object for method references and testing exposure 1598 var pf = {}; 1599 var isSupportTestReady = false; 1600 var noop = function() {}; 1601 var image = document.createElement( "img" ); 1602 var getImgAttr = image.getAttribute; 1603 var setImgAttr = image.setAttribute; 1604 var removeImgAttr = image.removeAttribute; 1605 var docElem = document.documentElement; 1606 var types = {}; 1607 var cfg = { 1608 //resource selection: 1609 algorithm: "" 1610 }; 1611 var srcAttr = "data-pfsrc"; 1612 var srcsetAttr = srcAttr + "set"; 1613 // ua sniffing is done for undetectable img loading features, 1614 // to do some non crucial perf optimizations 1615 var ua = navigator.userAgent; 1616 var supportAbort = (/rident/).test(ua) || ((/ecko/).test(ua) && ua.match(/rv\:(\d+)/) && RegExp.$1 > 35 ); 1617 var curSrcProp = "currentSrc"; 1618 var regWDesc = /\s+\+?\d+(e\d+)?w/; 1619 var regSize = /(\([^)]+\))?\s*(.+)/; 1620 var setOptions = window.picturefillCFG; 1621 /** 1622 * Shortcut property for https://w3c.github.io/webappsec/specs/mixedcontent/#restricts-mixed-content ( for easy overriding in tests ) 1623 */ 1624 // baseStyle also used by getEmValue (i.e.: width: 1em is important) 1625 var baseStyle = "position:absolute;left:0;visibility:hidden;display:block;padding:0;border:none;font-size:1em;width:1em;overflow:hidden;clip:rect(0px, 0px, 0px, 0px)"; 1626 var fsCss = "font-size:100%!important;"; 1627 var isVwDirty = true; 1628 1629 var cssCache = {}; 1630 var sizeLengthCache = {}; 1631 var DPR = window.devicePixelRatio; 1632 var units = { 1633 px: 1, 1634 "in": 96 1635 }; 1636 var anchor = document.createElement( "a" ); 1637 /** 1638 * alreadyRun flag used for setOptions. is it true setOptions will reevaluate 1639 * @type {boolean} 1640 */ 1641 var alreadyRun = false; 1642 1643 // Reusable, non-"g" Regexes 1644 1645 // (Don't use \s, to avoid matching non-breaking space.) 1646 var regexLeadingSpaces = /^[ \t\n\r\u000c]+/, 1647 regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/, 1648 regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/, 1649 regexTrailingCommas = /[,]+$/, 1650 regexNonNegativeInteger = /^\d+$/, 1651 1652 // ( Positive or negative or unsigned integers or decimals, without or without exponents. 1653 // Must include at least one digit. 1654 // According to spec tests any decimal point must be followed by a digit. 1655 // No leading plus sign is allowed.) 1656 // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-floating-point-number 1657 regexFloatingPoint = /^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/; 1658 1659 var on = function(obj, evt, fn, capture) { 1660 if ( obj.addEventListener ) { 1661 obj.addEventListener(evt, fn, capture || false); 1662 } else if ( obj.attachEvent ) { 1663 obj.attachEvent( "on" + evt, fn); 1664 } 1665 }; 1666 1667 /** 1668 * simple memoize function: 1669 */ 1670 1671 var memoize = function(fn) { 1672 var cache = {}; 1673 return function(input) { 1674 if ( !(input in cache) ) { 1675 cache[ input ] = fn(input); 1676 } 1677 return cache[ input ]; 1678 }; 1679 }; 1680 1681 // UTILITY FUNCTIONS 1682 1683 // Manual is faster than RegEx 1684 // http://jsperf.com/whitespace-character/5 1685 function isSpace(c) { 1686 return (c === "\u0020" || // space 1687 c === "\u0009" || // horizontal tab 1688 c === "\u000A" || // new line 1689 c === "\u000C" || // form feed 1690 c === "\u000D"); // carriage return 1691 } 1692 1693 /** 1694 * gets a mediaquery and returns a boolean or gets a css length and returns a number 1695 * @param css mediaqueries or css length 1696 * @returns {boolean|number} 1697 * 1698 * based on: https://gist.github.com/jonathantneal/db4f77009b155f083738 1699 */ 1700 var evalCSS = (function() { 1701 1702 var regLength = /^([\d\.]+)(em|vw|px)$/; 1703 var replace = function() { 1704 var args = arguments, index = 0, string = args[0]; 1705 while (++index in args) { 1706 string = string.replace(args[index], args[++index]); 1707 } 1708 return string; 1709 }; 1710 1711 var buildStr = memoize(function(css) { 1712 1713 return "return " + replace((css || "").toLowerCase(), 1714 // interpret `and` 1715 /\band\b/g, "&&", 1716 1717 // interpret `,` 1718 /,/g, "||", 1719 1720 // interpret `min-` as >= 1721 /min-([a-z-\s]+):/g, "e.$1>=", 1722 1723 // interpret `max-` as <= 1724 /max-([a-z-\s]+):/g, "e.$1<=", 1725 1726 //calc value 1727 /calc([^)]+)/g, "($1)", 1728 1729 // interpret css values 1730 /(\d+[\.]*[\d]*)([a-z]+)/g, "($1 * e.$2)", 1731 //make eval less evil 1732 /^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/ig, "" 1733 ) + ";"; 1734 }); 1735 1736 return function(css, length) { 1737 var parsedLength; 1738 if (!(css in cssCache)) { 1739 cssCache[css] = false; 1740 if (length && (parsedLength = css.match( regLength ))) { 1741 cssCache[css] = parsedLength[ 1 ] * units[parsedLength[ 2 ]]; 1742 } else { 1743 /*jshint evil:true */ 1744 try{ 1745 cssCache[css] = new Function("e", buildStr(css))(units); 1746 } catch(e) {} 1747 /*jshint evil:false */ 1748 } 1749 } 1750 return cssCache[css]; 1751 }; 1752 })(); 1753 1754 var setResolution = function( candidate, sizesattr ) { 1755 if ( candidate.w ) { // h = means height: || descriptor.type === 'h' do not handle yet... 1756 candidate.cWidth = pf.calcListLength( sizesattr || "100vw" ); 1757 candidate.res = candidate.w / candidate.cWidth ; 1758 } else { 1759 candidate.res = candidate.d; 1760 } 1761 return candidate; 1762 }; 1763 1764 /** 1765 * 1766 * @param opt 1767 */ 1768 var picturefill = function( opt ) { 1769 1770 if (!isSupportTestReady) {return;} 1771 1772 var elements, i, plen; 1773 1774 var options = opt || {}; 1775 1776 if ( options.elements && options.elements.nodeType === 1 ) { 1777 if ( options.elements.nodeName.toUpperCase() === "IMG" ) { 1778 options.elements = [ options.elements ]; 1779 } else { 1780 options.context = options.elements; 1781 options.elements = null; 1782 } 1783 } 1784 1785 elements = options.elements || pf.qsa( (options.context || document), ( options.reevaluate || options.reselect ) ? pf.sel : pf.selShort ); 1786 1787 if ( (plen = elements.length) ) { 1788 1789 pf.setupRun( options ); 1790 alreadyRun = true; 1791 1792 // Loop through all elements 1793 for ( i = 0; i < plen; i++ ) { 1794 pf.fillImg(elements[ i ], options); 1795 } 1796 1797 pf.teardownRun( options ); 1798 } 1799 }; 1800 1801 /** 1802 * outputs a warning for the developer 1803 * @param {message} 1804 * @type {Function} 1805 */ 1806 warn = ( window.console && console.warn ) ? 1807 function( message ) { 1808 console.warn( message ); 1809 } : 1810 noop 1811 ; 1812 1813 if ( !(curSrcProp in image) ) { 1814 curSrcProp = "src"; 1815 } 1816 1817 // Add support for standard mime types. 1818 types[ "image/jpeg" ] = true; 1819 types[ "image/gif" ] = true; 1820 types[ "image/png" ] = true; 1821 1822 function detectTypeSupport( type, typeUri ) { 1823 // based on Modernizr's lossless img-webp test 1824 // note: asynchronous 1825 var image = new window.Image(); 1826 image.onerror = function() { 1827 types[ type ] = false; 1828 picturefill(); 1829 }; 1830 image.onload = function() { 1831 types[ type ] = image.width === 1; 1832 picturefill(); 1833 }; 1834 image.src = typeUri; 1835 return "pending"; 1836 } 1837 1838 // test svg support 1839 types[ "image/svg+xml" ] = document.implementation.hasFeature( "http://www.w3.org/TR/SVG11/feature#Image", "1.1" ); 1840 1841 /** 1842 * updates the internal vW property with the current viewport width in px 1843 */ 1844 function updateMetrics() { 1845 1846 isVwDirty = false; 1847 DPR = window.devicePixelRatio; 1848 cssCache = {}; 1849 sizeLengthCache = {}; 1850 1851 pf.DPR = DPR || 1; 1852 1853 units.width = Math.max(window.innerWidth || 0, docElem.clientWidth); 1854 units.height = Math.max(window.innerHeight || 0, docElem.clientHeight); 1855 1856 units.vw = units.width / 100; 1857 units.vh = units.height / 100; 1858 1859 evalId = [ units.height, units.width, DPR ].join("-"); 1860 1861 units.em = pf.getEmValue(); 1862 units.rem = units.em; 1863 } 1864 1865 function chooseLowRes( lowerValue, higherValue, dprValue, isCached ) { 1866 var bonusFactor, tooMuch, bonus, meanDensity; 1867 1868 //experimental 1869 if (cfg.algorithm === "saveData" ){ 1870 if ( lowerValue > 2.7 ) { 1871 meanDensity = dprValue + 1; 1872 } else { 1873 tooMuch = higherValue - dprValue; 1874 bonusFactor = Math.pow(lowerValue - 0.6, 1.5); 1875 1876 bonus = tooMuch * bonusFactor; 1877 1878 if (isCached) { 1879 bonus += 0.1 * bonusFactor; 1880 } 1881 1882 meanDensity = lowerValue + bonus; 1883 } 1884 } else { 1885 meanDensity = (dprValue > 1) ? 1886 Math.sqrt(lowerValue * higherValue) : 1887 lowerValue; 1888 } 1889 1890 return meanDensity > dprValue; 1891 } 1892 1893 function applyBestCandidate( img ) { 1894 var srcSetCandidates; 1895 var matchingSet = pf.getSet( img ); 1896 var evaluated = false; 1897 if ( matchingSet !== "pending" ) { 1898 evaluated = evalId; 1899 if ( matchingSet ) { 1900 srcSetCandidates = pf.setRes( matchingSet ); 1901 pf.applySetCandidate( srcSetCandidates, img ); 1902 } 1903 } 1904 img[ pf.ns ].evaled = evaluated; 1905 } 1906 1907 function ascendingSort( a, b ) { 1908 return a.res - b.res; 1909 } 1910 1911 function setSrcToCur( img, src, set ) { 1912 var candidate; 1913 if ( !set && src ) { 1914 set = img[ pf.ns ].sets; 1915 set = set && set[set.length - 1]; 1916 } 1917 1918 candidate = getCandidateForSrc(src, set); 1919 1920 if ( candidate ) { 1921 src = pf.makeUrl(src); 1922 img[ pf.ns ].curSrc = src; 1923 img[ pf.ns ].curCan = candidate; 1924 1925 if ( !candidate.res ) { 1926 setResolution( candidate, candidate.set.sizes ); 1927 } 1928 } 1929 return candidate; 1930 } 1931 1932 function getCandidateForSrc( src, set ) { 1933 var i, candidate, candidates; 1934 if ( src && set ) { 1935 candidates = pf.parseSet( set ); 1936 src = pf.makeUrl(src); 1937 for ( i = 0; i < candidates.length; i++ ) { 1938 if ( src === pf.makeUrl(candidates[ i ].url) ) { 1939 candidate = candidates[ i ]; 1940 break; 1941 } 1942 } 1943 } 1944 return candidate; 1945 } 1946 1947 function getAllSourceElements( picture, candidates ) { 1948 var i, len, source, srcset; 1949 1950 // SPEC mismatch intended for size and perf: 1951 // actually only source elements preceding the img should be used 1952 // also note: don't use qsa here, because IE8 sometimes doesn't like source as the key part in a selector 1953 var sources = picture.getElementsByTagName( "source" ); 1954 1955 for ( i = 0, len = sources.length; i < len; i++ ) { 1956 source = sources[ i ]; 1957 source[ pf.ns ] = true; 1958 srcset = source.getAttribute( "srcset" ); 1959 1960 // if source does not have a srcset attribute, skip 1961 if ( srcset ) { 1962 candidates.push( { 1963 srcset: srcset, 1964 media: source.getAttribute( "media" ), 1965 type: source.getAttribute( "type" ), 1966 sizes: source.getAttribute( "sizes" ) 1967 } ); 1968 } 1969 } 1970 } 1971 1972 /** 1973 * Srcset Parser 1974 * By Alex Bell | MIT License 1975 * 1976 * @returns Array [{url: _, d: _, w: _, h:_, set:_(????)}, ...] 1977 * 1978 * Based super duper closely on the reference algorithm at: 1979 * https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-srcset-attribute 1980 */ 1981 1982 // 1. Let input be the value passed to this algorithm. 1983 // (TO-DO : Explain what "set" argument is here. Maybe choose a more 1984 // descriptive & more searchable name. Since passing the "set" in really has 1985 // nothing to do with parsing proper, I would prefer this assignment eventually 1986 // go in an external fn.) 1987 function parseSrcset(input, set) { 1988 1989 function collectCharacters(regEx) { 1990 var chars, 1991 match = regEx.exec(input.substring(pos)); 1992 if (match) { 1993 chars = match[ 0 ]; 1994 pos += chars.length; 1995 return chars; 1996 } 1997 } 1998 1999 var inputLength = input.length, 2000 url, 2001 descriptors, 2002 currentDescriptor, 2003 state, 2004 c, 2005 2006 // 2. Let position be a pointer into input, initially pointing at the start 2007 // of the string. 2008 pos = 0, 2009 2010 // 3. Let candidates be an initially empty source set. 2011 candidates = []; 2012 2013 /** 2014 * Adds descriptor properties to a candidate, pushes to the candidates array 2015 * @return undefined 2016 */ 2017 // (Declared outside of the while loop so that it's only created once. 2018 // (This fn is defined before it is used, in order to pass JSHINT. 2019 // Unfortunately this breaks the sequencing of the spec comments. :/ ) 2020 function parseDescriptors() { 2021 2022 // 9. Descriptor parser: Let error be no. 2023 var pError = false, 2024 2025 // 10. Let width be absent. 2026 // 11. Let density be absent. 2027 // 12. Let future-compat-h be absent. (We're implementing it now as h) 2028 w, d, h, i, 2029 candidate = {}, 2030 desc, lastChar, value, intVal, floatVal; 2031 2032 // 13. For each descriptor in descriptors, run the appropriate set of steps 2033 // from the following list: 2034 for (i = 0 ; i < descriptors.length; i++) { 2035 desc = descriptors[ i ]; 2036 2037 lastChar = desc[ desc.length - 1 ]; 2038 value = desc.substring(0, desc.length - 1); 2039 intVal = parseInt(value, 10); 2040 floatVal = parseFloat(value); 2041 2042 // If the descriptor consists of a valid non-negative integer followed by 2043 // a U+0077 LATIN SMALL LETTER W character 2044 if (regexNonNegativeInteger.test(value) && (lastChar === "w")) { 2045 2046 // If width and density are not both absent, then let error be yes. 2047 if (w || d) {pError = true;} 2048 2049 // Apply the rules for parsing non-negative integers to the descriptor. 2050 // If the result is zero, let error be yes. 2051 // Otherwise, let width be the result. 2052 if (intVal === 0) {pError = true;} else {w = intVal;} 2053 2054 // If the descriptor consists of a valid floating-point number followed by 2055 // a U+0078 LATIN SMALL LETTER X character 2056 } else if (regexFloatingPoint.test(value) && (lastChar === "x")) { 2057 2058 // If width, density and future-compat-h are not all absent, then let error 2059 // be yes. 2060 if (w || d || h) {pError = true;} 2061 2062 // Apply the rules for parsing floating-point number values to the descriptor. 2063 // If the result is less than zero, let error be yes. Otherwise, let density 2064 // be the result. 2065 if (floatVal < 0) {pError = true;} else {d = floatVal;} 2066 2067 // If the descriptor consists of a valid non-negative integer followed by 2068 // a U+0068 LATIN SMALL LETTER H character 2069 } else if (regexNonNegativeInteger.test(value) && (lastChar === "h")) { 2070 2071 // If height and density are not both absent, then let error be yes. 2072 if (h || d) {pError = true;} 2073 2074 // Apply the rules for parsing non-negative integers to the descriptor. 2075 // If the result is zero, let error be yes. Otherwise, let future-compat-h 2076 // be the result. 2077 if (intVal === 0) {pError = true;} else {h = intVal;} 2078 2079 // Anything else, Let error be yes. 2080 } else {pError = true;} 2081 } // (close step 13 for loop) 2082 2083 // 15. If error is still no, then append a new image source to candidates whose 2084 // URL is url, associated with a width width if not absent and a pixel 2085 // density density if not absent. Otherwise, there is a parse error. 2086 if (!pError) { 2087 candidate.url = url; 2088 2089 if (w) { candidate.w = w;} 2090 if (d) { candidate.d = d;} 2091 if (h) { candidate.h = h;} 2092 if (!h && !d && !w) {candidate.d = 1;} 2093 if (candidate.d === 1) {set.has1x = true;} 2094 candidate.set = set; 2095 2096 candidates.push(candidate); 2097 } 2098 } // (close parseDescriptors fn) 2099 2100 /** 2101 * Tokenizes descriptor properties prior to parsing 2102 * Returns undefined. 2103 * (Again, this fn is defined before it is used, in order to pass JSHINT. 2104 * Unfortunately this breaks the logical sequencing of the spec comments. :/ ) 2105 */ 2106 function tokenize() { 2107 2108 // 8.1. Descriptor tokeniser: Skip whitespace 2109 collectCharacters(regexLeadingSpaces); 2110 2111 // 8.2. Let current descriptor be the empty string. 2112 currentDescriptor = ""; 2113 2114 // 8.3. Let state be in descriptor. 2115 state = "in descriptor"; 2116 2117 while (true) { 2118 2119 // 8.4. Let c be the character at position. 2120 c = input.charAt(pos); 2121 2122 // Do the following depending on the value of state. 2123 // For the purpose of this step, "EOF" is a special character representing 2124 // that position is past the end of input. 2125 2126 // In descriptor 2127 if (state === "in descriptor") { 2128 // Do the following, depending on the value of c: 2129 2130 // Space character 2131 // If current descriptor is not empty, append current descriptor to 2132 // descriptors and let current descriptor be the empty string. 2133 // Set state to after descriptor. 2134 if (isSpace(c)) { 2135 if (currentDescriptor) { 2136 descriptors.push(currentDescriptor); 2137 currentDescriptor = ""; 2138 state = "after descriptor"; 2139 } 2140 2141 // U+002C COMMA (,) 2142 // Advance position to the next character in input. If current descriptor 2143 // is not empty, append current descriptor to descriptors. Jump to the step 2144 // labeled descriptor parser. 2145 } else if (c === ",") { 2146 pos += 1; 2147 if (currentDescriptor) { 2148 descriptors.push(currentDescriptor); 2149 } 2150 parseDescriptors(); 2151 return; 2152 2153 // U+0028 LEFT PARENTHESIS (() 2154 // Append c to current descriptor. Set state to in parens. 2155 } else if (c === "\u0028") { 2156 currentDescriptor = currentDescriptor + c; 2157 state = "in parens"; 2158 2159 // EOF 2160 // If current descriptor is not empty, append current descriptor to 2161 // descriptors. Jump to the step labeled descriptor parser. 2162 } else if (c === "") { 2163 if (currentDescriptor) { 2164 descriptors.push(currentDescriptor); 2165 } 2166 parseDescriptors(); 2167 return; 2168 2169 // Anything else 2170 // Append c to current descriptor. 2171 } else { 2172 currentDescriptor = currentDescriptor + c; 2173 } 2174 // (end "in descriptor" 2175 2176 // In parens 2177 } else if (state === "in parens") { 2178 2179 // U+0029 RIGHT PARENTHESIS ()) 2180 // Append c to current descriptor. Set state to in descriptor. 2181 if (c === ")") { 2182 currentDescriptor = currentDescriptor + c; 2183 state = "in descriptor"; 2184 2185 // EOF 2186 // Append current descriptor to descriptors. Jump to the step labeled 2187 // descriptor parser. 2188 } else if (c === "") { 2189 descriptors.push(currentDescriptor); 2190 parseDescriptors(); 2191 return; 2192 2193 // Anything else 2194 // Append c to current descriptor. 2195 } else { 2196 currentDescriptor = currentDescriptor + c; 2197 } 2198 2199 // After descriptor 2200 } else if (state === "after descriptor") { 2201 2202 // Do the following, depending on the value of c: 2203 // Space character: Stay in this state. 2204 if (isSpace(c)) { 2205 2206 // EOF: Jump to the step labeled descriptor parser. 2207 } else if (c === "") { 2208 parseDescriptors(); 2209 return; 2210 2211 // Anything else 2212 // Set state to in descriptor. Set position to the previous character in input. 2213 } else { 2214 state = "in descriptor"; 2215 pos -= 1; 2216 2217 } 2218 } 2219 2220 // Advance position to the next character in input. 2221 pos += 1; 2222 2223 // Repeat this step. 2224 } // (close while true loop) 2225 } 2226 2227 // 4. Splitting loop: Collect a sequence of characters that are space 2228 // characters or U+002C COMMA characters. If any U+002C COMMA characters 2229 // were collected, that is a parse error. 2230 while (true) { 2231 collectCharacters(regexLeadingCommasOrSpaces); 2232 2233 // 5. If position is past the end of input, return candidates and abort these steps. 2234 if (pos >= inputLength) { 2235 return candidates; // (we're done, this is the sole return path) 2236 } 2237 2238 // 6. Collect a sequence of characters that are not space characters, 2239 // and let that be url. 2240 url = collectCharacters(regexLeadingNotSpaces); 2241 2242 // 7. Let descriptors be a new empty list. 2243 descriptors = []; 2244 2245 // 8. If url ends with a U+002C COMMA character (,), follow these substeps: 2246 // (1). Remove all trailing U+002C COMMA characters from url. If this removed 2247 // more than one character, that is a parse error. 2248 if (url.slice(-1) === ",") { 2249 url = url.replace(regexTrailingCommas, ""); 2250 // (Jump ahead to step 9 to skip tokenization and just push the candidate). 2251 parseDescriptors(); 2252 2253 // Otherwise, follow these substeps: 2254 } else { 2255 tokenize(); 2256 } // (close else of step 8) 2257 2258 // 16. Return to the step labeled splitting loop. 2259 } // (Close of big while loop.) 2260 } 2261 2262 /* 2263 * Sizes Parser 2264 * 2265 * By Alex Bell | MIT License 2266 * 2267 * Non-strict but accurate and lightweight JS Parser for the string value <img sizes="here"> 2268 * 2269 * Reference algorithm at: 2270 * https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-sizes-attribute 2271 * 2272 * Most comments are copied in directly from the spec 2273 * (except for comments in parens). 2274 * 2275 * Grammar is: 2276 * <source-size-list> = <source-size># [ , <source-size-value> ]? | <source-size-value> 2277 * <source-size> = <media-condition> <source-size-value> 2278 * <source-size-value> = <length> 2279 * http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#attr-img-sizes 2280 * 2281 * E.g. "(max-width: 30em) 100vw, (max-width: 50em) 70vw, 100vw" 2282 * or "(min-width: 30em), calc(30vw - 15px)" or just "30vw" 2283 * 2284 * Returns the first valid <css-length> with a media condition that evaluates to true, 2285 * or "100vw" if all valid media conditions evaluate to false. 2286 * 2287 */ 2288 2289 function parseSizes(strValue) { 2290 2291 // (Percentage CSS lengths are not allowed in this case, to avoid confusion: 2292 // https://html.spec.whatwg.org/multipage/embedded-content.html#valid-source-size-list 2293 // CSS allows a single optional plus or minus sign: 2294 // http://www.w3.org/TR/CSS2/syndata.html#numbers 2295 // CSS is ASCII case-insensitive: 2296 // http://www.w3.org/TR/CSS2/syndata.html#characters ) 2297 // Spec allows exponential notation for <number> type: 2298 // http://dev.w3.org/csswg/css-values/#numbers 2299 var regexCssLengthWithUnits = /^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$/i; 2300 2301 // (This is a quick and lenient test. Because of optional unlimited-depth internal 2302 // grouping parens and strict spacing rules, this could get very complicated.) 2303 var regexCssCalc = /^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i; 2304 2305 var i; 2306 var unparsedSizesList; 2307 var unparsedSizesListLength; 2308 var unparsedSize; 2309 var lastComponentValue; 2310 var size; 2311 2312 // UTILITY FUNCTIONS 2313 2314 // (Toy CSS parser. The goals here are: 2315 // 1) expansive test coverage without the weight of a full CSS parser. 2316 // 2) Avoiding regex wherever convenient. 2317 // Quick tests: http://jsfiddle.net/gtntL4gr/3/ 2318 // Returns an array of arrays.) 2319 function parseComponentValues(str) { 2320 var chrctr; 2321 var component = ""; 2322 var componentArray = []; 2323 var listArray = []; 2324 var parenDepth = 0; 2325 var pos = 0; 2326 var inComment = false; 2327 2328 function pushComponent() { 2329 if (component) { 2330 componentArray.push(component); 2331 component = ""; 2332 } 2333 } 2334 2335 function pushComponentArray() { 2336 if (componentArray[0]) { 2337 listArray.push(componentArray); 2338 componentArray = []; 2339 } 2340 } 2341 2342 // (Loop forwards from the beginning of the string.) 2343 while (true) { 2344 chrctr = str.charAt(pos); 2345 2346 if (chrctr === "") { // ( End of string reached.) 2347 pushComponent(); 2348 pushComponentArray(); 2349 return listArray; 2350 } else if (inComment) { 2351 if ((chrctr === "*") && (str[pos + 1] === "/")) { // (At end of a comment.) 2352 inComment = false; 2353 pos += 2; 2354 pushComponent(); 2355 continue; 2356 } else { 2357 pos += 1; // (Skip all characters inside comments.) 2358 continue; 2359 } 2360 } else if (isSpace(chrctr)) { 2361 // (If previous character in loop was also a space, or if 2362 // at the beginning of the string, do not add space char to 2363 // component.) 2364 if ( (str.charAt(pos - 1) && isSpace( str.charAt(pos - 1) ) ) || !component ) { 2365 pos += 1; 2366 continue; 2367 } else if (parenDepth === 0) { 2368 pushComponent(); 2369 pos +=1; 2370 continue; 2371 } else { 2372 // (Replace any space character with a plain space for legibility.) 2373 chrctr = " "; 2374 } 2375 } else if (chrctr === "(") { 2376 parenDepth += 1; 2377 } else if (chrctr === ")") { 2378 parenDepth -= 1; 2379 } else if (chrctr === ",") { 2380 pushComponent(); 2381 pushComponentArray(); 2382 pos += 1; 2383 continue; 2384 } else if ( (chrctr === "/") && (str.charAt(pos + 1) === "*") ) { 2385 inComment = true; 2386 pos += 2; 2387 continue; 2388 } 2389 2390 component = component + chrctr; 2391 pos += 1; 2392 } 2393 } 2394 2395 function isValidNonNegativeSourceSizeValue(s) { 2396 if (regexCssLengthWithUnits.test(s) && (parseFloat(s) >= 0)) {return true;} 2397 if (regexCssCalc.test(s)) {return true;} 2398 // ( http://www.w3.org/TR/CSS2/syndata.html#numbers says: 2399 // "-0 is equivalent to 0 and is not a negative number." which means that 2400 // unitless zero and unitless negative zero must be accepted as special cases.) 2401 if ((s === "0") || (s === "-0") || (s === "+0")) {return true;} 2402 return false; 2403 } 2404 2405 // When asked to parse a sizes attribute from an element, parse a 2406 // comma-separated list of component values from the value of the element's 2407 // sizes attribute (or the empty string, if the attribute is absent), and let 2408 // unparsed sizes list be the result. 2409 // http://dev.w3.org/csswg/css-syntax/#parse-comma-separated-list-of-component-values 2410 2411 unparsedSizesList = parseComponentValues(strValue); 2412 unparsedSizesListLength = unparsedSizesList.length; 2413 2414 // For each unparsed size in unparsed sizes list: 2415 for (i = 0; i < unparsedSizesListLength; i++) { 2416 unparsedSize = unparsedSizesList[i]; 2417 2418 // 1. Remove all consecutive <whitespace-token>s from the end of unparsed size. 2419 // ( parseComponentValues() already omits spaces outside of parens. ) 2420 2421 // If unparsed size is now empty, that is a parse error; continue to the next 2422 // iteration of this algorithm. 2423 // ( parseComponentValues() won't push an empty array. ) 2424 2425 // 2. If the last component value in unparsed size is a valid non-negative 2426 // <source-size-value>, let size be its value and remove the component value 2427 // from unparsed size. Any CSS function other than the calc() function is 2428 // invalid. Otherwise, there is a parse error; continue to the next iteration 2429 // of this algorithm. 2430 // http://dev.w3.org/csswg/css-syntax/#parse-component-value 2431 lastComponentValue = unparsedSize[unparsedSize.length - 1]; 2432 2433 if (isValidNonNegativeSourceSizeValue(lastComponentValue)) { 2434 size = lastComponentValue; 2435 unparsedSize.pop(); 2436 } else { 2437 continue; 2438 } 2439 2440 // 3. Remove all consecutive <whitespace-token>s from the end of unparsed 2441 // size. If unparsed size is now empty, return size and exit this algorithm. 2442 // If this was not the last item in unparsed sizes list, that is a parse error. 2443 if (unparsedSize.length === 0) { 2444 return size; 2445 } 2446 2447 // 4. Parse the remaining component values in unparsed size as a 2448 // <media-condition>. If it does not parse correctly, or it does parse 2449 // correctly but the <media-condition> evaluates to false, continue to the 2450 // next iteration of this algorithm. 2451 // (Parsing all possible compound media conditions in JS is heavy, complicated, 2452 // and the payoff is unclear. Is there ever an situation where the 2453 // media condition parses incorrectly but still somehow evaluates to true? 2454 // Can we just rely on the browser/polyfill to do it?) 2455 unparsedSize = unparsedSize.join(" "); 2456 if (!(pf.matchesMedia( unparsedSize ) ) ) { 2457 continue; 2458 } 2459 2460 // 5. Return size and exit this algorithm. 2461 return size; 2462 } 2463 2464 // If the above algorithm exhausts unparsed sizes list without returning a 2465 // size value, return 100vw. 2466 return "100vw"; 2467 } 2468 2469 // namespace 2470 pf.ns = ("pf" + new Date().getTime()).substr(0, 9); 2471 2472 // srcset support test 2473 pf.supSrcset = "srcset" in image; 2474 pf.supSizes = "sizes" in image; 2475 pf.supPicture = !!window.HTMLPictureElement; 2476 2477 // UC browser does claim to support srcset and picture, but not sizes, 2478 // this extended test reveals the browser does support nothing 2479 if (pf.supSrcset && pf.supPicture && !pf.supSizes) { 2480 (function(image2) { 2481 image.srcset = "data:,a"; 2482 image2.src = "data:,a"; 2483 pf.supSrcset = image.complete === image2.complete; 2484 pf.supPicture = pf.supSrcset && pf.supPicture; 2485 })(document.createElement("img")); 2486 } 2487 2488 // Safari9 has basic support for sizes, but does't expose the `sizes` idl attribute 2489 if (pf.supSrcset && !pf.supSizes) { 2490 2491 (function() { 2492 var width2 = "data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw=="; 2493 var width1 = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; 2494 var img = document.createElement("img"); 2495 var test = function() { 2496 var width = img.width; 2497 2498 if (width === 2) { 2499 pf.supSizes = true; 2500 } 2501 2502 alwaysCheckWDescriptor = pf.supSrcset && !pf.supSizes; 2503 2504 isSupportTestReady = true; 2505 // force async 2506 setTimeout(picturefill); 2507 }; 2508 2509 img.onload = test; 2510 img.onerror = test; 2511 img.setAttribute("sizes", "9px"); 2512 2513 img.srcset = width1 + " 1w," + width2 + " 9w"; 2514 img.src = width1; 2515 })(); 2516 2517 } else { 2518 isSupportTestReady = true; 2519 } 2520 2521 // using pf.qsa instead of dom traversing does scale much better, 2522 // especially on sites mixing responsive and non-responsive images 2523 pf.selShort = "picture>img,img[srcset]"; 2524 pf.sel = pf.selShort; 2525 pf.cfg = cfg; 2526 2527 /** 2528 * Shortcut property for `devicePixelRatio` ( for easy overriding in tests ) 2529 */ 2530 pf.DPR = (DPR || 1 ); 2531 pf.u = units; 2532 2533 // container of supported mime types that one might need to qualify before using 2534 pf.types = types; 2535 2536 pf.setSize = noop; 2537 2538 /** 2539 * Gets a string and returns the absolute URL 2540 * @param src 2541 * @returns {String} absolute URL 2542 */ 2543 2544 pf.makeUrl = memoize(function(src) { 2545 anchor.href = src; 2546 return anchor.href; 2547 }); 2548 2549 /** 2550 * Gets a DOM element or document and a selctor and returns the found matches 2551 * Can be extended with jQuery/Sizzle for IE7 support 2552 * @param context 2553 * @param sel 2554 * @returns {NodeList|Array} 2555 */ 2556 pf.qsa = function(context, sel) { 2557 return ( "querySelector" in context ) ? context.querySelectorAll(sel) : []; 2558 }; 2559 2560 /** 2561 * Shortcut method for matchMedia ( for easy overriding in tests ) 2562 * wether native or pf.mMQ is used will be decided lazy on first call 2563 * @returns {boolean} 2564 */ 2565 pf.matchesMedia = function() { 2566 if ( window.matchMedia && (matchMedia( "(min-width: 0.1em)" ) || {}).matches ) { 2567 pf.matchesMedia = function( media ) { 2568 return !media || ( matchMedia( media ).matches ); 2569 }; 2570 } else { 2571 pf.matchesMedia = pf.mMQ; 2572 } 2573 2574 return pf.matchesMedia.apply( this, arguments ); 2575 }; 2576 2577 /** 2578 * A simplified matchMedia implementation for IE8 and IE9 2579 * handles only min-width/max-width with px or em values 2580 * @param media 2581 * @returns {boolean} 2582 */ 2583 pf.mMQ = function( media ) { 2584 return media ? evalCSS(media) : true; 2585 }; 2586 2587 /** 2588 * Returns the calculated length in css pixel from the given sourceSizeValue 2589 * http://dev.w3.org/csswg/css-values-3/#length-value 2590 * intended Spec mismatches: 2591 * * Does not check for invalid use of CSS functions 2592 * * Does handle a computed length of 0 the same as a negative and therefore invalid value 2593 * @param sourceSizeValue 2594 * @returns {Number} 2595 */ 2596 pf.calcLength = function( sourceSizeValue ) { 2597 2598 var value = evalCSS(sourceSizeValue, true) || false; 2599 if (value < 0) { 2600 value = false; 2601 } 2602 2603 return value; 2604 }; 2605 2606 /** 2607 * Takes a type string and checks if its supported 2608 */ 2609 2610 pf.supportsType = function( type ) { 2611 return ( type ) ? types[ type ] : true; 2612 }; 2613 2614 /** 2615 * Parses a sourceSize into mediaCondition (media) and sourceSizeValue (length) 2616 * @param sourceSizeStr 2617 * @returns {*} 2618 */ 2619 pf.parseSize = memoize(function( sourceSizeStr ) { 2620 var match = ( sourceSizeStr || "" ).match(regSize); 2621 return { 2622 media: match && match[1], 2623 length: match && match[2] 2624 }; 2625 }); 2626 2627 pf.parseSet = function( set ) { 2628 if ( !set.cands ) { 2629 set.cands = parseSrcset(set.srcset, set); 2630 } 2631 return set.cands; 2632 }; 2633 2634 /** 2635 * returns 1em in css px for html/body default size 2636 * function taken from respondjs 2637 * @returns {*|number} 2638 */ 2639 pf.getEmValue = function() { 2640 var body; 2641 if ( !eminpx && (body = document.body) ) { 2642 var div = document.createElement( "div" ), 2643 originalHTMLCSS = docElem.style.cssText, 2644 originalBodyCSS = body.style.cssText; 2645 2646 div.style.cssText = baseStyle; 2647 2648 // 1em in a media query is the value of the default font size of the browser 2649 // reset docElem and body to ensure the correct value is returned 2650 docElem.style.cssText = fsCss; 2651 body.style.cssText = fsCss; 2652 2653 body.appendChild( div ); 2654 eminpx = div.offsetWidth; 2655 body.removeChild( div ); 2656 2657 //also update eminpx before returning 2658 eminpx = parseFloat( eminpx, 10 ); 2659 2660 // restore the original values 2661 docElem.style.cssText = originalHTMLCSS; 2662 body.style.cssText = originalBodyCSS; 2663 2664 } 2665 return eminpx || 16; 2666 }; 2667 2668 /** 2669 * Takes a string of sizes and returns the width in pixels as a number 2670 */ 2671 pf.calcListLength = function( sourceSizeListStr ) { 2672 // Split up source size list, ie ( max-width: 30em ) 100%, ( max-width: 50em ) 50%, 33% 2673 // 2674 // or (min-width:30em) calc(30% - 15px) 2675 if ( !(sourceSizeListStr in sizeLengthCache) || cfg.uT ) { 2676 var winningLength = pf.calcLength( parseSizes( sourceSizeListStr ) ); 2677 2678 sizeLengthCache[ sourceSizeListStr ] = !winningLength ? units.width : winningLength; 2679 } 2680 2681 return sizeLengthCache[ sourceSizeListStr ]; 2682 }; 2683 2684 /** 2685 * Takes a candidate object with a srcset property in the form of url/ 2686 * ex. "images/pic-medium.png 1x, images/pic-medium-2x.png 2x" or 2687 * "images/pic-medium.png 400w, images/pic-medium-2x.png 800w" or 2688 * "images/pic-small.png" 2689 * Get an array of image candidates in the form of 2690 * {url: "/foo/bar.png", resolution: 1} 2691 * where resolution is http://dev.w3.org/csswg/css-values-3/#resolution-value 2692 * If sizes is specified, res is calculated 2693 */ 2694 pf.setRes = function( set ) { 2695 var candidates; 2696 if ( set ) { 2697 2698 candidates = pf.parseSet( set ); 2699 2700 for ( var i = 0, len = candidates.length; i < len; i++ ) { 2701 setResolution( candidates[ i ], set.sizes ); 2702 } 2703 } 2704 return candidates; 2705 }; 2706 2707 pf.setRes.res = setResolution; 2708 2709 pf.applySetCandidate = function( candidates, img ) { 2710 if ( !candidates.length ) {return;} 2711 var candidate, 2712 i, 2713 j, 2714 length, 2715 bestCandidate, 2716 curSrc, 2717 curCan, 2718 candidateSrc, 2719 abortCurSrc; 2720 2721 var imageData = img[ pf.ns ]; 2722 var dpr = pf.DPR; 2723 2724 curSrc = imageData.curSrc || img[curSrcProp]; 2725 2726 curCan = imageData.curCan || setSrcToCur(img, curSrc, candidates[0].set); 2727 2728 // if we have a current source, we might either become lazy or give this source some advantage 2729 if ( curCan && curCan.set === candidates[ 0 ].set ) { 2730 2731 // if browser can abort image request and the image has a higher pixel density than needed 2732 // and this image isn't downloaded yet, we skip next part and try to save bandwidth 2733 abortCurSrc = (supportAbort && !img.complete && curCan.res - 0.1 > dpr); 2734 2735 if ( !abortCurSrc ) { 2736 curCan.cached = true; 2737 2738 // if current candidate is "best", "better" or "okay", 2739 // set it to bestCandidate 2740 if ( curCan.res >= dpr ) { 2741 bestCandidate = curCan; 2742 } 2743 } 2744 } 2745 2746 if ( !bestCandidate ) { 2747 2748 candidates.sort( ascendingSort ); 2749 2750 length = candidates.length; 2751 bestCandidate = candidates[ length - 1 ]; 2752 2753 for ( i = 0; i < length; i++ ) { 2754 candidate = candidates[ i ]; 2755 if ( candidate.res >= dpr ) { 2756 j = i - 1; 2757 2758 // we have found the perfect candidate, 2759 // but let's improve this a little bit with some assumptions ;-) 2760 if (candidates[ j ] && 2761 (abortCurSrc || curSrc !== pf.makeUrl( candidate.url )) && 2762 chooseLowRes(candidates[ j ].res, candidate.res, dpr, candidates[ j ].cached)) { 2763 2764 bestCandidate = candidates[ j ]; 2765 2766 } else { 2767 bestCandidate = candidate; 2768 } 2769 break; 2770 } 2771 } 2772 } 2773 2774 if ( bestCandidate ) { 2775 2776 candidateSrc = pf.makeUrl( bestCandidate.url ); 2777 2778 imageData.curSrc = candidateSrc; 2779 imageData.curCan = bestCandidate; 2780 2781 if ( candidateSrc !== curSrc ) { 2782 pf.setSrc( img, bestCandidate ); 2783 } 2784 pf.setSize( img ); 2785 } 2786 }; 2787 2788 pf.setSrc = function( img, bestCandidate ) { 2789 var origWidth; 2790 img.src = bestCandidate.url; 2791 2792 // although this is a specific Safari issue, we don't want to take too much different code paths 2793 if ( bestCandidate.set.type === "image/svg+xml" ) { 2794 origWidth = img.style.width; 2795 img.style.width = (img.offsetWidth + 1) + "px"; 2796 2797 // next line only should trigger a repaint 2798 // if... is only done to trick dead code removal 2799 if ( img.offsetWidth + 1 ) { 2800 img.style.width = origWidth; 2801 } 2802 } 2803 }; 2804 2805 pf.getSet = function( img ) { 2806 var i, set, supportsType; 2807 var match = false; 2808 var sets = img [ pf.ns ].sets; 2809 2810 for ( i = 0; i < sets.length && !match; i++ ) { 2811 set = sets[i]; 2812 2813 if ( !set.srcset || !pf.matchesMedia( set.media ) || !(supportsType = pf.supportsType( set.type )) ) { 2814 continue; 2815 } 2816 2817 if ( supportsType === "pending" ) { 2818 set = supportsType; 2819 } 2820 2821 match = set; 2822 break; 2823 } 2824 2825 return match; 2826 }; 2827 2828 pf.parseSets = function( element, parent, options ) { 2829 var srcsetAttribute, imageSet, isWDescripor, srcsetParsed; 2830 2831 var hasPicture = parent && parent.nodeName.toUpperCase() === "PICTURE"; 2832 var imageData = element[ pf.ns ]; 2833 2834 if ( imageData.src === undefined || options.src ) { 2835 imageData.src = getImgAttr.call( element, "src" ); 2836 if ( imageData.src ) { 2837 setImgAttr.call( element, srcAttr, imageData.src ); 2838 } else { 2839 removeImgAttr.call( element, srcAttr ); 2840 } 2841 } 2842 2843 if ( imageData.srcset === undefined || options.srcset || !pf.supSrcset || element.srcset ) { 2844 srcsetAttribute = getImgAttr.call( element, "srcset" ); 2845 imageData.srcset = srcsetAttribute; 2846 srcsetParsed = true; 2847 } 2848 2849 imageData.sets = []; 2850 2851 if ( hasPicture ) { 2852 imageData.pic = true; 2853 getAllSourceElements( parent, imageData.sets ); 2854 } 2855 2856 if ( imageData.srcset ) { 2857 imageSet = { 2858 srcset: imageData.srcset, 2859 sizes: getImgAttr.call( element, "sizes" ) 2860 }; 2861 2862 imageData.sets.push( imageSet ); 2863 2864 isWDescripor = (alwaysCheckWDescriptor || imageData.src) && regWDesc.test(imageData.srcset || ""); 2865 2866 // add normal src as candidate, if source has no w descriptor 2867 if ( !isWDescripor && imageData.src && !getCandidateForSrc(imageData.src, imageSet) && !imageSet.has1x ) { 2868 imageSet.srcset += ", " + imageData.src; 2869 imageSet.cands.push({ 2870 url: imageData.src, 2871 d: 1, 2872 set: imageSet 2873 }); 2874 } 2875 2876 } else if ( imageData.src ) { 2877 imageData.sets.push( { 2878 srcset: imageData.src, 2879 sizes: null 2880 } ); 2881 } 2882 2883 imageData.curCan = null; 2884 imageData.curSrc = undefined; 2885 2886 // if img has picture or the srcset was removed or has a srcset and does not support srcset at all 2887 // or has a w descriptor (and does not support sizes) set support to false to evaluate 2888 imageData.supported = !( hasPicture || ( imageSet && !pf.supSrcset ) || (isWDescripor && !pf.supSizes) ); 2889 2890 if ( srcsetParsed && pf.supSrcset && !imageData.supported ) { 2891 if ( srcsetAttribute ) { 2892 setImgAttr.call( element, srcsetAttr, srcsetAttribute ); 2893 element.srcset = ""; 2894 } else { 2895 removeImgAttr.call( element, srcsetAttr ); 2896 } 2897 } 2898 2899 if (imageData.supported && !imageData.srcset && ((!imageData.src && element.src) || element.src !== pf.makeUrl(imageData.src))) { 2900 if (imageData.src === null) { 2901 element.removeAttribute("src"); 2902 } else { 2903 element.src = imageData.src; 2904 } 2905 } 2906 2907 imageData.parsed = true; 2908 }; 2909 2910 pf.fillImg = function(element, options) { 2911 var imageData; 2912 var extreme = options.reselect || options.reevaluate; 2913 2914 // expando for caching data on the img 2915 if ( !element[ pf.ns ] ) { 2916 element[ pf.ns ] = {}; 2917 } 2918 2919 imageData = element[ pf.ns ]; 2920 2921 // if the element has already been evaluated, skip it 2922 // unless `options.reevaluate` is set to true ( this, for example, 2923 // is set to true when running `picturefill` on `resize` ). 2924 if ( !extreme && imageData.evaled === evalId ) { 2925 return; 2926 } 2927 2928 if ( !imageData.parsed || options.reevaluate ) { 2929 pf.parseSets( element, element.parentNode, options ); 2930 } 2931 2932 if ( !imageData.supported ) { 2933 applyBestCandidate( element ); 2934 } else { 2935 imageData.evaled = evalId; 2936 } 2937 }; 2938 2939 pf.setupRun = function() { 2940 if ( !alreadyRun || isVwDirty || (DPR !== window.devicePixelRatio) ) { 2941 updateMetrics(); 2942 } 2943 }; 2944 2945 // If picture is supported, well, that's awesome. 2946 if ( pf.supPicture ) { 2947 picturefill = noop; 2948 pf.fillImg = noop; 2949 } else { 2950 2951 // Set up picture polyfill by polling the document 2952 (function() { 2953 var isDomReady; 2954 var regReady = window.attachEvent ? /d$|^c/ : /d$|^c|^i/; 2955 2956 var run = function() { 2957 var readyState = document.readyState || ""; 2958 2959 timerId = setTimeout(run, readyState === "loading" ? 200 : 999); 2960 if ( document.body ) { 2961 pf.fillImgs(); 2962 isDomReady = isDomReady || regReady.test(readyState); 2963 if ( isDomReady ) { 2964 clearTimeout( timerId ); 2965 } 2966 2967 } 2968 }; 2969 2970 var timerId = setTimeout(run, document.body ? 9 : 99); 2971 2972 // Also attach picturefill on resize and readystatechange 2973 // http://modernjavascript.blogspot.com/2013/08/building-better-debounce.html 2974 var debounce = function(func, wait) { 2975 var timeout, timestamp; 2976 var later = function() { 2977 var last = (new Date()) - timestamp; 2978 2979 if (last < wait) { 2980 timeout = setTimeout(later, wait - last); 2981 } else { 2982 timeout = null; 2983 func(); 2984 } 2985 }; 2986 2987 return function() { 2988 timestamp = new Date(); 2989 2990 if (!timeout) { 2991 timeout = setTimeout(later, wait); 2992 } 2993 }; 2994 }; 2995 var lastClientWidth = docElem.clientHeight; 2996 var onResize = function() { 2997 isVwDirty = Math.max(window.innerWidth || 0, docElem.clientWidth) !== units.width || docElem.clientHeight !== lastClientWidth; 2998 lastClientWidth = docElem.clientHeight; 2999 if ( isVwDirty ) { 3000 pf.fillImgs(); 3001 } 3002 }; 3003 3004 on( window, "resize", debounce(onResize, 99 ) ); 3005 on( document, "readystatechange", run ); 3006 })(); 3007 } 3008 3009 pf.picturefill = picturefill; 3010 //use this internally for easy monkey patching/performance testing 3011 pf.fillImgs = picturefill; 3012 pf.teardownRun = noop; 3013 3014 /* expose methods for testing */ 3015 picturefill._ = pf; 3016 3017 window.picturefillCFG = { 3018 pf: pf, 3019 push: function(args) { 3020 var name = args.shift(); 3021 if (typeof pf[name] === "function") { 3022 pf[name].apply(pf, args); 3023 } else { 3024 cfg[name] = args[0]; 3025 if (alreadyRun) { 3026 pf.fillImgs( { reselect: true } ); 3027 } 3028 } 3029 } 3030 }; 3031 3032 while (setOptions && setOptions.length) { 3033 window.picturefillCFG.push(setOptions.shift()); 3034 } 3035 3036 /* expose picturefill */ 3037 window.picturefill = picturefill; 3038 3039 /* expose picturefill */ 3040 if ( typeof module === "object" && typeof module.exports === "object" ) { 3041 // CommonJS, just export 3042 module.exports = picturefill; 3043 } else if ( typeof define === "function" && define.amd ) { 3044 // AMD support 3045 define( "picturefill", function() { return picturefill; } ); 3046 } 3047 3048 // IE8 evals this sync, so it must be the last thing we do 3049 if ( !pf.supPicture ) { 3050 types[ "image/webp" ] = detectTypeSupport("image/webp", "data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==" ); 3051 } 3052 3053} )( window, document );
3054 3055 3056// Custom JS for the Header. 3057// ---------------------------------------------------------------------------- 3058 3059// Adds User Agent and Operating System 3060// details to the <html> tag. 3061// ------------------------------------- 3062var b = document.documentElement; 3063 b.setAttribute('data-useragent', navigator.userAgent); 3064 b.setAttribute('data-platform', navigator.platform ); 3065 b.className += ((!!('ontouchstart' in window) || !!('onmsgesturechange' in window))?' touch':'');
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.