├── CHANGELOG.md ├── LICENSE ├── README.md ├── cfg ├── child.html ├── css │ ├── bootstrap-theme.min.css │ └── bootstrap.min.css ├── index.html └── js │ └── parent.js ├── component.json ├── how-respimg-works.md ├── plugins ├── intrinsic-dimension │ ├── README.md │ ├── ri.intrinsic.js │ └── ri.intrinsic.min.js ├── mutation │ ├── README.md │ ├── ri.mutation.js │ └── ri.mutation.min.js ├── oldie │ ├── README.md │ ├── ri.oldie.js │ └── ri.oldie.min.js ├── perfselection │ └── README.md ├── print │ ├── README.md │ ├── ri.print.js │ └── ri.print.min.js └── typesupport │ ├── README.md │ ├── ri.type.js │ └── ri.type.min.js ├── respimage.dev.js ├── respimage.js └── respimage.min.js /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ###1.4.0/1.4.1 4 | 5 | * renamed ``reparse`` option to ``reevaluate`` (better compatibility with picturefill) 6 | * Changed candidate selection for more quality especially on lower resolution devices 7 | * added video/vnd.mozilla.apng and video/x-apng types for apng in typesupport plugin (only 1.4.1) 8 | 9 | ###1.3.1 10 | 11 | * fix intrinsic dimension plugin in IE11 in conjunction with SVG images 12 | * allow complex combined media conditions in ``sizes`` attribute 13 | 14 | ###1.2.1 15 | 16 | * fixes intrinsic sizes plugin sometimes disconnects MutationObserver 17 | * improve media attribute order in conjunction with type attribute 18 | * use lqip pattern also in browsers, which support image abortion (improves speedindex) 19 | * improve mutation performance for browsers without MuationObserver support 20 | * be more memory efficient 21 | 22 | ###1.2.0 23 | 24 | * improved performance for upcoming FF 36+ 25 | * decoupled intrinsic sizing from resource selection (makes [intrinsic size plugin smoother](http://jsfiddle.net/trixta/gs3p14pr/embedded/result/)) 26 | * added SEO pattern 27 | * improved documentation 28 | * changed config options 29 | * simplified and improved smart source selection 30 | * modification to the lqip pattern. The time to the onload event is now more often increased, but perceived performance is much better. 31 | 32 | ###1.1.6 33 | 34 | * changed viewport calculation for IE8 to correspond to respondjs 35 | * added experimental print extension plugin 36 | 37 | ###1.1.5 38 | 39 | * improved viewport width/height detection 40 | 41 | ###1.1.4 42 | 43 | * improved intrinsic sizes plugin 44 | * improved ``currentSrc`` property and moved it to the [mutation plugin](plugins/mutation). 45 | * refinements to the source selection algorithm. compared to previous version: 46 | * smart source selection runs less aggressive on 1x devices (better for quality). 47 | * smart source selection runs more aggressive on 2x+ devices (better for performance). 48 | * smart source selection runs less aggressive in portrait mode than in landscape mode (good for orientation change). 49 | * implemented orientation media query polyfill (mainly for IE8 but also IE9). 50 | * implemented ``srcset`` and ``src`` getter/setters into mutation plugin. 51 | * removed perfselection plugin. (Non-biased part is now directly included into the main script. For the "biased" part [use some other x-browser techniques](plugins/perfselection).) 52 | 53 | ###1.1.1-1.1.3 54 | 55 | * no script changes, only adjustments to the package.json (sorry for the release noise) 56 | 57 | ###1.1.0 58 | 59 | * improve [intrinsic scaling and move it to a plugin](plugins/intrinsic-dimension) 60 | * allow use of h descriptor (allow futureproof markup) 61 | * make respimage lazier on resize 62 | * improve image abortion for trident 63 | * tests tests tests ;-) 64 | 65 | ###1.0.0 66 | 67 | * CSS calc function in the ``sizes`` attribute in conjunction with em/vw is now 100% precise and works even in IE8 68 | * massive network performance improvement for the Trident engine (IE) if a ``src`` fallback attribute is specified 69 | 70 | ###0.9.6 71 | 72 | * improve addSize option 73 | 74 | ###0.9.5 75 | 76 | * improve memory usage 77 | * add type support plugin 78 | * fixed debug message (for source and size order) + added new debug messages 79 | 80 | ### 0.9.4 81 | 82 | * handle changes to devicePixelRatio 83 | * improve debugging information in *.dev.js (Test wether calculated sizes matches rendered size of image + Test order of source elements and sizes attribute) 84 | * Stick to editorconfig in all files 85 | * removed UMD (simply not so good for polyfills, sorry) 86 | * Add `.editorconfig` file to ensure coding conventions 87 | * Update npm dependencies 88 | * add ``lazyload`` check 89 | 90 | ### v0.9.3-RC1 91 | ### v0.9.3 92 | ### v0.9.2 93 | ### v0.9.1 94 | ### v0.9.0-RC2 95 | ### v0.9.0 96 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Original work Copyright (c) 2014 Filament Group 4 | Modified work Copyright (c) 2014 Alexander Farkas 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #respimage 2 | **respimage** is a fast, lightweight and robust [responsive images](http://picture.responsiveimages.org/) polyfill, that saves the users bandwidth by [utilizing smart resource selection algorithm](how-respimg-works.md). It implements the ``srcset``/``sizes`` attributes as also the ``picture`` element. Unlike other responsive images polyfills ``respimage`` plays nicely with your graceful degradation / progressive enhancement and image SEO strategy. 3 | 4 | ##Download and Embed 5 | Simply [download the respimage.min.js](respimage.min.js) script and add it to your website or bundle it in your normal JS. 6 | 7 | ```html 8 | 9 | ``` 10 | **respimage** will automatically run and polyfill all images. So you can simply start writing responsive images. 11 | 12 | In case you want to include **respimage** only if the browser doesn't support responsive images you can add the following inline script at the top of your head (before any stylesheets or any blocking JS, This should be added as inline script and not inside of an external script.): 13 | 14 | ```html 15 | 23 | ``` 24 | 25 | In case you need to support IE8 and want to include the script at the bottom you need to use either the [html5shiv](https://github.com/aFarkas/html5shiv) or add at least the following script inside your ``head`` element: 26 | 27 | ```html 28 | document.createElement('picture'); 29 | ``` 30 | 31 | Also note, that only IE8 in strict mode is supported. In case you need to support IE8 compatibility view or IE7, please use the [oldie plugin](plugins/oldie). 32 | 33 | ###Mobile support 34 | 35 | For mobile support it is crucial to set the viewport ``meta`` tag to ``device-width`` 36 | 37 | ```html 38 | 39 | ``` 40 | 41 | ####Install via bower 42 | 43 | ``` 44 | $ bower install respimage --save 45 | ``` 46 | 47 | ####Install via npm 48 | 49 | ``` 50 | $ npm install respimage --save 51 | ``` 52 | 53 | ##Markup Examples 54 | Responsive images can be technically differentiated between 2 types. 55 | 56 | * ``srcset`` with source descriptors (let the browser choose the right image based on screen size/resolution, bandwidth…): 57 | * density descriptor (``x``) (for static image sizes, Retina vs. normal resolution) 58 | * width descriptor (``w``) and the corresponding ``sizes`` attribute (for flexible, responsive / adaptive images (automatically also includes Retina optimization) 59 | * and the ``picture`` element with its ``source[media]`` children (gives the author control about what ``srcset`` should be chosen by the browser depending on specific media queries) 60 | 61 | 62 | ###``srcset`` with the density ``x`` descriptor 63 | The ``x`` descriptor is natively supported in [Firefox 38, Chrome 34+ and Safari 7.1+](http://caniuse.com/#feat=srcset). All other browsers will be polyfilled.
Note: You must not mix the ``w`` and the ``x`` descriptor in one ``srcset`` attribute! 64 | 65 | ```html 66 | Static content image 71 | ``` 72 | [load example](http://codepen.io/aFarkas/pen/qEBOEq) 73 | 74 | 75 | ###``srcset`` with the width ``w`` descriptor and the ``sizes`` attribute 76 | The ``w`` descriptor is in [Firefox 38 and Chrome 38+](http://caniuse.com/#feat=srcset). All other browsers will be polyfilled.
Note: You must not mix the ``w`` and the ``x`` descriptor in one ``srcset`` attribute! 77 | 78 | ```html 79 | flexible image 87 | ``` 88 | [load example](http://codepen.io/aFarkas/pen/KwKdpY) 89 | 90 | ###The ``picture`` element 91 | The ``picture`` element is currently only supported in [Firefox 38 and Chrome 38+](http://caniuse.com/#search=picture). All other browsers will be polyfilled. To support IE9 all source elements have to be wrapped inside of an ``audio`` or hidden ``video`` element: 92 | 93 | ```html 94 | 95 | 96 | 99 | 102 | 105 | 107 | 108 | image with artdirection 111 | 112 | ``` 113 | [load example](http://codepen.io/aFarkas/pen/yyLJWO) 114 | 115 | The art direction approach of the picture element and the descriptor approach can also be mixed: 116 | 117 | ```html 118 | 119 | 120 | 124 | 128 | 131 | 132 | image with artdirection 135 | 136 | ``` 137 | [load example](http://codepen.io/aFarkas/pen/RNwRzq) 138 | 139 | ##API 140 | ###``respimage`` function 141 | In case new responsive images are created and dynamically added to the DOM simply invoke the ``respimage`` method. 142 | 143 | ```js 144 | window.respimage(); 145 | ``` 146 | 147 | Here an extended example how this could look like in a jQuery environment. 148 | 149 | ```js 150 | $("div.dynamic-context").load("path-to-content.html", function(){ 151 | if( window.respimage ) { 152 | respimage(); 153 | } 154 | }); 155 | ``` 156 | 157 | In case you are dynamically changing relevant attributes (``srcset``, ``sizes``, ``media``) on responsive images or their associated ``source`` elements, you need to call ``respimage`` with an additional argument: 158 | 159 | ```js 160 | respimage({elements: [imageElement], reevaluate: true}); 161 | ``` 162 | 163 | In the unlikely case you want either **remove** the ``srcset`` of an ``img`` (not of an ``source``) or want to directly change the ``src`` of an responsive image the additional ``src`` or ``srcset`` option has to be set (Note: In most cases, you don't want to do that!). 164 | 165 | 166 | ```js 167 | var $imgs = $('img').removeAttr('srcset'); 168 | 169 | respimage({elements: $imgs, reevaluate: true, srcset: true}); 170 | 171 | //or for src 172 | 173 | var $imgs = $('img').attr('src', 'some-img.jpg'); 174 | respimage({elements: $imgs, reevaluate: true, src: true}); 175 | ``` 176 | 177 | Note: The ``reparse`` option was renamed with version 1.4.0 to ``reevaluate`` to better match the option used by picturefill. 178 | 179 | In case you are not supporting IE8 we recommend to use the [Mutation plugin](plugins/mutation) instead of using this API (It fully polyfills also the DOM APIs and makes additional calls to ``respimage`` automatically for you). 180 | 181 | ## Browser Support 182 | **respimage** supports a broad range of browsers and devices. It is actively tested in the following browsers and devices IE8+, Firefox (ESR and current), Safari 7.0+, Chrome, Opera, Android 4.1+ and IOS 7+, but should work in a lot more browsers/devices. IE6 and IE7 are only supported with the [oldIE plugin](plugins/oldie). 183 | 184 | ##Troubleshooting and bug reporting 185 | In case of any problems include the **respimage.dev.js** into your project and open your JS console. In case you think you have found a bug, please create a testcase and then report your issue. Note: You should not use the dev build inside your production environment, because it is a lot slower. 186 | 187 | **Note: It is highly recommended to test with the *.dev.js file, especially if you are using responsive images the first time or you start a new project setup.** The **respimage.dev.js** file can give you some useful hints in the console. About 80% of all tutorials suggest wrong markup examples! Also note: That our respimg debugger can't check every possible error. 188 | 189 | ##Plugins 190 | 191 | respimage has some really nice extensions/plugins to improve standards support even more. In case you want to use a CDN, you can use the combohandler service provided by jsDelivr: 192 | 193 | 194 | ```html 195 | 203 | ``` 204 | 205 | ###The [intrinsic sizes / dimensions - Plugin](plugins/intrinsic-dimension) 206 | The intrinsic dimension plugin extends ``respimage`` to add the intrinsic dimension based on the descriptor (and the sizes attribute) and the density of the source candidate to the width content attribute of the image element. 207 | 208 | ###The [Mutation - Plugin](plugins/mutation) 209 | This plugin automatically detects new responsive images and also any changes to ``srcset``/``media`` and ``sizes`` attributes. It also implements the corresponding DOM properties for those attributes. 210 | 211 | ###The [typesupport - Plugin](plugins/typesupport) 212 | The type support plugin adds type support detection for the following image file types: apng, JPEG 2000, JPEG XR, WEBP 213 | 214 | ###The [oldie - Plugin](plugins/oldie) 215 | Respimage supports IE8+ (including) out of the box. In case you need to support IE6/7 or any IE in compatibility view or quirksmode use the oldie plugin. 216 | 217 | ##Known issues/caveats/ 218 | * Browsers without picture and srcset support and disabled JS will either show the image specified with the ``src`` attribute or - if omitted - show only the ``alt`` text. In case a ``src`` attribute is used non-supporting browser might download a wasted addtional image. For workarounds and markup patterns to improve this problem see below. 219 | * **respimage** implements [different JS techniques](how-respimg-works.md) to automatically adapt to your ``src`` strategy. This yields among other things to the fact, that using an inital ``src`` attribute in conjunction with respimage can improve perceived performance (although an additional request is generated: 220 | 221 | ###low quality image source 222 | 223 | In case JS off is a concern. Use a low quality source as the fallback ``src``. As soon as an image has already a source respimage will not simply switch the image ``src`` but will implement the low quality image placeholder pattern. While the lquip technique can often increase the time until the onload event and the transferred image data, it **improves perceived performance**: 224 | 225 | ```html 226 | flexible image 234 | ``` 235 | 236 | Due to the fact, that the lqip ``src`` attribute can be optimized by the browser's preload parser this technique yields to a very fast first impression, while the best image candidate can be loaded in the background. 237 | 238 | This technique can be combined with [lazyLoading](https://github.com/aFarkas/lazysizes), which will also additionally decrease the time until onload event and gives you the possibility to implement the improved perceived performance also for native supporting browsers. 239 | 240 | ###Omit the ``src`` 241 | 242 | In case JS disabled legacy browsers are no concern and you can't provide an lquip source or you are using client side rendering (No preload parser optimization advantage), use a one pixel ``src`` or better a data URI. 243 | 244 | ```html 245 | my image 248 | ``` 249 | 250 | ###Simply live with it and use either the most often used or the smallest source candidate as the fallback ``src`` 251 | 252 | ```html 253 | flexible image 261 | 262 | 263 | 264 | flexible image 272 | ``` 273 | 274 | In this case respimage will guard your chosen ``src`` strategy and will only kick in as a progressive enhancement script in the following situations: 275 | * the inital image candidate is too heavy and the browser supports image request abortion 276 | * the inital image candidate is too fuzzy (see LQIP pattern above) 277 | * or art direction is envolved and the fallback ``src`` candidate is not part of the chosen ``srcset`` 278 | In case the currently set source candidate is not perfect (or perfect of course), but good enough the ``src`` won't be changed by respimage. (See also the ``lazyFactor`` option below.) 279 | 280 | * Media queries support in old IEs (IE8/IE9) are limited to ``min-width``, ``max-width``, ``max-height`` and ``min-height``. For IE9 it is possible to extend support by including a [``matchMedia`` polyfill](https://github.com/paulirish/matchMedia.js). 281 | 282 | ##Responsive images and lazy loading 283 | Beside the fact, that lazy loading improves performance, there is an interesting side effect. Due to delayed image loading the sizes attribute can be dynamically calculated with JS and makes integrating responsive images in any environment therefore easy. We recommend [lazysizes](https://github.com/aFarkas/lazysizes). 284 | 285 | ##Setting options 286 | 287 | respimage uses the asynchronous push syntax for configuration. Simply create a global ``respimgCFG`` array if it doesn't exist already and push your options: 288 | 289 | ```js 290 | window.respimgCFG = window.respimgCFG || []; 291 | 292 | respimgCFG.push(['lazyFactor', 0.6]); 293 | ``` 294 | 295 | Also Note: respimage is a drop-in polyfill solution and you normally shouldn't need to configure anything. But in case you want to play around with respimage's options here is a [small testing zone for you](https://afarkas.github.io/respimage/cfg/index.html). 296 | 297 | ###The ``maxX`` option (default: ``2``) 298 | Due to the fact that reliable bandwidth detection is nearly impossible and 3x image density means 9x image data respimage constraints the maximum considered ``devicePixelRatio`` to ``2``. In case you want to serve 3x images to 3x devices even with possibly lower bandwidth set this option to 3: 299 | 300 | ```js 301 | window.respimgCFG = window.respimgCFG || []; 302 | 303 | respimgCFG.push(['maxX', 2]); 304 | ``` 305 | 306 | Note: This only affects polyfilled browsers. In case you want to constrain the maximum dpi for all browser you can try [lazySizes - optimumx extension](https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/optimumx). 307 | 308 | ###The ``lazyFactor`` option (default: ``0.4``) 309 | In case an image already has a source candidate (either initially set as ``src`` attribute or on resize) respimage becomes lazy changing the source. The higher the ``lazyFactor`` the more respimage honors your fallback ``src``. Reasonable values are between 0.1 and 1.5. 310 | 311 | ```js 312 | window.respimgCFG = window.respimgCFG || []; 313 | 314 | //make respimage more lazy 315 | respimgCFG.push(['lazyFactor', 0.8]); 316 | 317 | //make respimage less lazy 318 | //respimgCFG.push(['lazyFactor', 0.2]); 319 | ``` 320 | 321 | Wether this option has an impact depends also heavily on your fallback ``src`` strategy. 322 | 323 | ##Building a production ready respimage.js version from the *.dev.js file 324 | 325 | The respimage.js or the respimage.min.js files are production ready versions of respimage while the respimage.dev.js file includes some informativ extra checks (For example, it checks wether your markup or the content of your ``sizes`` is reasonable.). Therefore the dev version is not only bigger but also a lot slower. In case you want to use the dev version inside your dev enviroment and want to automatically build a production ready version, you can do so by using the dead code removal feature of uglify. Here is a simple grunt config example: 326 | 327 | ```js 328 | /* 329 | // simply add the following option to your uglify option task 330 | // to remove respimage's debug code: 331 | compress: { 332 | global_defs: { 333 | "RIDEBUG": false 334 | }, 335 | dead_code: true 336 | } 337 | */ 338 | grunt.initConfig({ 339 | //uglify task 340 | uglify: { 341 | options: { 342 | compress: { 343 | global_defs: { 344 | "RIDEBUG": false 345 | }, 346 | dead_code: true 347 | } 348 | }, 349 | //your task: 350 | my_target: { 351 | files: [{ 352 | expand: true, 353 | cwd: 'src/js', 354 | src: '**/*.js', 355 | dest: 'dest/js' 356 | }] 357 | } 358 | } 359 | }); 360 | ``` 361 | 362 | ##Authors 363 | * Authors of the original work: Scott Jehl, Mat Marquis, Shawn Jansepar (2.0 refactor lead) 364 | * Authors of the improved **respimage** script: Alexander Farkas 365 | * and many more: see [Authors.txt](Authors.txt) 366 | 367 | ##Contributing 368 | Fixes, PRs and issues are always welcome, make sure to create a new branch from the **dev** (not the stable branch), validate against JShint and test in all browsers. In case of an API/documentation change make sure to also document it here in the readme.md. 369 | -------------------------------------------------------------------------------- /cfg/child.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | respimage performance test - responsive images with smallest candidate as fallback src 8 | 9 | 10 | 48 | 49 | 50 | 51 | 73 | 74 | 75 |
76 |

Your browser does supports respimg natively. Please use a browser without picture/srcset support.

77 |
78 |
79 |

The number on the top are the calculated resolutions for the image:

80 |

number in green is the image selected by respimg. While number in gray is the next lower and number in red is the next higher candidate.

81 |

You might see depending on the viewport size, that respimage will sometimes take a lower resolution candidate, if the next higher candidate means to much to download.

82 |

Due to the fact that the resource algorithm takes quality vs. performance into account. Most savings occur on higher devicePixelRatio's (i.e.: retina, 1.5+x).

83 |
84 |
85 |

You have either a devicePixelRatio below 1.5 or do simulate such a devicePixelRatio. The smart selection algorithm has it's strength on higher density's (i.e.: 1.5x, 2x or 3x). You can simulate a different pixelRatio by using the xQuant option.

86 |
87 | 88 | 89 |
90 | Abandoned Boat 101 |
102 |

Abandoned Boat

103 |

by William Warby

104 |
105 |
106 | 107 |
108 | Desert Road 122 |
123 |

Desert Road

124 |

by William Warby

125 |
126 |
127 |
128 | 139 |
140 |

141 |

by Micah Sheldon

142 |
143 |
144 | 145 |
146 | @ The Desert Tortoise Natural Area 159 |
160 |

@ The Desert Tortoise Natural Area

161 |

by

162 |
163 |
164 |
165 | Woman in water 174 |
175 |

Woman in water

176 |

by Ton Haex

177 |
178 |
179 | 180 |
181 | Borobudur 189 |
190 |

Borobudur

191 |

by Scott Anderson

192 |
193 |
194 | 195 | 196 |
197 | A tree in the blue 207 |
208 |

A tree in the blue

209 |

by Sergio

210 |
211 |
212 | 213 |
214 | Windows on Istanbul 224 |
225 |

Windows on Istanbul

226 |

by robin robokow

227 |
228 |
229 | 230 | 231 |
232 | Goldie Dawn 243 |
244 |

Goldie Dawn

245 |

by

246 |
247 |
248 | 249 |
250 | Sant Miquel del Fai 259 |
260 |

Sant Miquel del Fai

261 |

by Jaume Meneses

262 |
263 |
264 | 265 |
266 | Avebury Stone Circle 276 |
277 |

Avebury Stone Circle

278 |

by Erinc Salor

279 |
280 |
281 | 282 |
283 | el castil de tierra 294 |
295 |

el castil de tierra

296 |

by Mario Antonio Pena Zapatería

297 |
298 |
299 | 300 |
301 | sunset 315 |
316 |

sunset

317 |

by Angela Marie

318 |
319 |
320 | 321 |
322 | Sky and earth 336 |
337 |

Sky and earth

338 |

by Angela Marie

339 |
340 |
341 | 342 |
343 | Missing Ulsoor lake (Explore) 353 |
354 |

Missing Ulsoor lake (Explore)

355 |

by Swaminathan

356 |
357 |
358 | 359 | 360 |
361 | Oxford Path 2 371 |
372 |

Oxford Path 2

373 |

by Danny Chapman

374 |
375 |
376 | 377 | 389 | 390 | 391 | 392 | 464 | 465 | 466 | -------------------------------------------------------------------------------- /cfg/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | /*! 8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=e83a281787cec24357bc) 9 | * Config saved to config.json and https://gist.github.com/e83a281787cec24357bc 10 | */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-o-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:linear-gradient(to bottom, #fff 0, #e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top, #428bca 0, #2d6ca2 100%);background-image:-o-linear-gradient(top, #428bca 0, #2d6ca2 100%);background-image:linear-gradient(to bottom, #428bca 0, #2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary:disabled,.btn-primary[disabled]{background-color:#2d6ca2;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:linear-gradient(to bottom, #5cb85c 0, #419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:linear-gradient(to bottom, #5bc0de 0, #2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:linear-gradient(to bottom, #f0ad4e 0, #eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-o-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-o-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:linear-gradient(to bottom, #fff 0, #f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f3f3f3 100%);background-image:-o-linear-gradient(top, #ebebeb 0, #f3f3f3 100%);background-image:linear-gradient(to bottom, #ebebeb 0, #f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-o-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:linear-gradient(to bottom, #3c3c3c 0, #222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #222 0, #282828 100%);background-image:-o-linear-gradient(top, #222 0, #282828 100%);background-image:linear-gradient(to bottom, #222 0, #282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-o-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top, #428bca 0, #3071a9 100%);background-image:-o-linear-gradient(top, #428bca 0, #3071a9 100%);background-image:linear-gradient(to bottom, #428bca 0, #3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:linear-gradient(to bottom, #5cb85c 0, #449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:linear-gradient(to bottom, #d9534f 0, #c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top, #428bca 0, #3278b3 100%);background-image:-o-linear-gradient(top, #428bca 0, #3278b3 100%);background-image:linear-gradient(to bottom, #428bca 0, #3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-o-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-o-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /cfg/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | smart candidate selection for responsive images 8 | 9 | 10 | 11 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 |
49 |
smart candidate selection for responsive images
50 |
51 |
52 |
53 | configure devicePixelRatio and src strategy 54 |
55 |
56 |
57 | 58 | 59 |

xQuant will be multiplied with your devicePixelRatio. Hint: If you are using a 2dpr device you can set 0.5 to simulate 1dpr device. In case you are using 1dpr you can set 2 to test a 2dpr device.

60 |
61 |
62 |
63 |
64 |
65 |
66 | 69 | 72 | 75 |
76 |
77 |
78 |
79 |
80 | configure candidate selection 81 | 82 |
83 |
84 |
85 |
86 | 87 | 88 |

The maximum DPR to be considered in source candidate selection

89 |
90 |
91 |
92 |
93 | 94 | 95 |

How lazily should respimage change src on resize or more important initially (see src strategy above). (try this in conjunction with the or with resize)

96 |
97 |
98 |
99 |
100 |
101 | 106 |
107 | 108 |
109 |
110 |
111 |
112 |
113 |
114 | 115 |
116 |
117 |
118 |
119 |
120 |
121 | 122 | 123 |
124 |
125 |
126 | 129 |
130 |

Wether viewport should be changed while dragging (input/checked) or on drag release (change/unchecked).

131 |
132 |
133 |
134 |
135 |
136 |
137 | 138 | 139 |
140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /cfg/js/parent.js: -------------------------------------------------------------------------------- 1 | (function(window, document){ 2 | 3 | if ( window.HTMLPictureElement ) { 4 | $('html').addClass('resp-supported'); 5 | } 6 | 7 | webshim.setOptions('forms-ext', { 8 | replaceUI: 'auto' 9 | }); 10 | 11 | 12 | webshim.polyfill('forms forms-ext details'); 13 | 14 | 15 | $(function(){ 16 | var oninput; 17 | var o = window.respimage._.cfg; 18 | 19 | $('#xQuant').each(function(){ 20 | var dpr = window.devicePixelRatio || 1; 21 | $(this).val(0.5); 22 | $(this).prop({ 23 | min: 1 / dpr, 24 | max: Math.max(2 / dpr, 1), 25 | value: 1 26 | }); 27 | }); 28 | 29 | $.each(o, function(name, value){ 30 | $('#'+name).val(value); 31 | }); 32 | 33 | $('#vw-input') 34 | .on('change.smooth-vwchange', function(){ 35 | oninput = $.prop(this, 'checked'); 36 | }) 37 | .trigger('change.smooth-vwchange') 38 | ; 39 | $('#viewport').each(function(){ 40 | var onChange = function(e){ 41 | if (!e || (oninput && e.type == 'input') || (e.type == 'change' && !oninput)){ 42 | var val = $(this).val(); 43 | $('#arena').width(val); 44 | } 45 | }; 46 | $(this).on('input change', onChange).each(onChange); 47 | }); 48 | $('#arena').removeAttr('src').prop('src', 'javascript:false'); 49 | 50 | 51 | 52 | $('.arena-config') 53 | .on('submit', function(){ 54 | var data = $(this).serialize(); 55 | $('#arena').prop('src', 'child.html?' + data); 56 | return false; 57 | }) 58 | .triggerHandler('submit') 59 | ; 60 | }) 61 | 62 | })(window, document); 63 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "respimage", 3 | "version": "1.4.2", 4 | "repo": "afarkas/respimage", 5 | "main": "respimage.min.js", 6 | "scripts": ["respimage.min.js"], 7 | "description": "The fast, lightweight and reliable polyfill for responsive images (i.e. picture element and the srcset, sizes and media attributes).", 8 | "keywords": ["responsive", "image", "responsive images", "picture", "srcset", "polyfill"], 9 | "license": "MIT" 10 | } 11 | -------------------------------------------------------------------------------- /how-respimg-works.md: -------------------------------------------------------------------------------- 1 | #How ``respimage`` works internally 2 | In case you want to know how to use ``respimage``, simply go to the [readme](README.md). This document describes some internal core concepts of ``respimage``. 3 | 4 | ``respimage`` uses several techniques to increase perceived performance or reduce bandwidth: 5 | 6 | ##Polyfill vs. graceful degradation / progressive enhancement and "image data trashing" 7 | Polyfilling responsive images with a fallback ``src`` can lead to a wasted / trashed double request in non-supporting browsers and therefore some polyfills recommend to fully omit the src attribute, which antagonizes the natively and [specified](https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element:attr-img-src-2) build-in graceful degradation support in responsive images. As it turns out it's also [not the](http://lists.w3.org/Archives/Public/public-respimage/2014Sep/0028.html) [best thing to do](https://twitter.com/grigs/status/327429827726561280) [performancewise](http://www.stevesouders.com/blog/2013/04/26/i/). As also a problem for search engine/bot visibility and the general validity of the document. 8 | 9 | While ``respimage`` also supports omitting the ``src`` attribute, ``respimage`` plays nicely with your progressive enhancement strategy (your valid markup) and does not waste an already started image download. respimage automatically adapts to your own ``src`` strategy by implementing various techniques: 10 | 11 | if the initially set image ``src``... 12 | 13 | * ... is to heavy and the browser supports image request abortion (all IEs and FF 36+) respimage will abort the request and load a smaller image 14 | * ... isn't perfect, but still has a good quality respimage won't change the ``src`` 15 | * ... is detected as too fuzzy respimage will not simply change the ``src`` but implements a variation of the [low quality image placeholder pattern (LQIP)](http://www.guypo.com/feo/introducing-lqip-low-quality-image-placeholders/) to increase perceived performance.[^] 16 | 17 | As it turns out, the LQIP pattern works so nice, that it could also be used as an enhancement by browser vendors especially in case of a low bandwidth situation and I developed a [lazylaoder, which brings the same perceived performance improvements to supporting browsers](https://github.com/aFarkas/lazysizes). 18 | 19 | It's worth noting, that you must not use the smallest image in your fallback ``src`` to take advantage of this technique you can also use a medium sized image. 20 | 21 | ##The ~~smart~~ or the "not-so-stupid" resource selection 22 | Finding the best source for an image is simple math. In case a browser finds a ``srcset`` attribute with ``w`` descriptors. The browser needs to calculate the pixel density for each source candidate. Here is an example of the calculation: 23 | 24 | ```html 25 | 30 | 31 | ``` 32 | 33 | The calculation is pretty simple. First the browser calculates the sizes attribute in CSS pixels, in our case it's simple (500px are 500px ;-)) and then divides the width descriptor by the calculated current size: 34 | 35 | ``` 36 | small.jpg: 500w / 500px = 1x 37 | medium.jpg: 1000w / 500px = 2x 38 | ``` 39 | 40 | Then the browser simply takes the source candidate, which can satisfy the pixel density of the user's device (devicePixelRatio). 41 | 42 | The specification gives the implementers (mainly browser vendors, but also polyfills) room for improvements based on bandwidth, battery status, CPU/GPU performance, user preferences and so worth. 43 | 44 | And this is where ``respimage``'s not so stupid resource selection comes into play. Because there are possible improvements to the resource selection, that should be always optimized, especially if exact bandwidth, user preferences etc. are unknown. 45 | 46 | While there are some "main breakpoints" you can never account for all breakpoints especially not, if you do multiply each of them, with the diversity of each devicePixelRatio's (1x, 1.5x, 2x and 2.25x). 47 | 48 | Here is a simple example of a calculation, where the image sizes doesn't fit: 49 | 50 | ```html 51 | 56 | ``` 57 | 58 | The calculated pixel density are as follows: 59 | 60 | ``` 61 | small.jpg: 500w / 505px = 0.99x 62 | medium.jpg: 1000w / 505px = 1.98x 63 | big.jpg: 2000w / 505px = 3.96x 64 | ``` 65 | 66 | What ``respimage``'s resource selection is doing is quite simple. It searches for the best quality candidate. In case of a 2x device, the example above returns the big.jpg, then it compares the useless extra pixels (i.e.: 3.96x - 2x : 1.96x) with the missing density of the next lower candidate "medium.jpg" (i.e.: only 0.02) and balances the quality loss vs. the performance decrease. This means the more useless data has to be downloaded, the greedier the algorithm trys to fetch the next lower candidate. 67 | 68 | Here is a simple [demo](http://codepen.io/aFarkas/full/tplJE/). 69 | 70 | The algorithm used for this is based on the following math. 71 | 72 | Let's start to include a simple "get the nearest candidate algorithm" and then refine it. We assume a 2x device and two candidates one with a resolution of 1.8x and one with a resolution with 2.6x. 73 | 74 | ```js 75 | // constants 76 | var devicePixel = 2; 77 | var lowRes = 1.8; 78 | var highRes = 2.6; 79 | 80 | 81 | var uselessDensity = highRes - devicePixel; // 0.6 82 | var lowBonus = uselessDensity; // 0.6 83 | 84 | var newLowRes = lowRes + lowBonus; // 2.4 85 | 86 | return (newLowRes > devicePixel); // true 87 | 88 | ``` 89 | 90 | While this algorithm is good, it doesn't take into account the actual perceived quality of the lower resolution image candidate and the squared image data of higher densities. For example, if you treat a lower resolution candidate with 0.7x against a higher resolution candidate with 1.4x on a 1x device, the algorithm would choose the extreme fuzzy 0.7x image. On the other hand on a 2x device the algorithm would prefer a 2.2x image over a 1.7x image, although the 1.7x image looks almost identicall to the 2.2x on this device, but is only half the size. This considerations yields to the following simple refinement. 91 | 92 | ```js 93 | // example 1. 94 | // constants 95 | var devicePixel = 1; 96 | var lowRes = 0.7; 97 | var highRes = 1.4; 98 | 99 | var uselessDensity = highRes - devicePixel; // 0.4 100 | var lowBonus = uselessDensity * lowRes; // 0.28 101 | 102 | var newLowRes = lowRes + lowBonus; // 0.98 103 | 104 | return (newLowRes > devicePixel); // false 105 | 106 | // example 2. (same algorithm) 107 | // constants 108 | var devicePixel = 2; 109 | var lowRes = 1.7; 110 | var highRes = 2.2; 111 | 112 | 113 | var uselessDensity = highRes - devicePixel; // 0.2 114 | var lowBonus = uselessDensity * lowRes; // 0.34 115 | 116 | var newLowRes = lowRes + lowBonus; // 2.04 117 | 118 | return (newLowRes > devicePixel); // true 119 | ``` 120 | 121 | Additionally to this algorithm, respimage's source selection algorithm also takes into account the device orientation (i.e. the performance algorithm runs more aggressive in landscape than in portrait mode) and also gives the current already loaded image source some additional advantage (i.e: in case of a resize/orientationchange or if there was an initial ``src`` applyed) to minimize re-downloads or double downloads. 122 | 123 | This simple and basic technique can save a lot of bandwidth with real images and realistic sizes: [smart selection demo](https://afarkas.github.io/respimage/cfg/index.html). 124 | 125 | Note: That ``respimage`` does only work in browsers, which do not support the srcset attribute natively. This means you should not use Chrome for the examples above. 126 | 127 | **``respimage`` is built to not only use responsive images today, but also to use it responsible without wasting any data or writing invalid markup.** 128 | 129 | 130 | 131 | 132 | 133 | [^]: The way how LIQP is implemented by ``respimage`` is not the implementation described by [Guypo](http://www.guypo.com/feo/introducing-lqip-low-quality-image-placeholders/) but variation suggested by [Steve Souders](http://www.guypo.com/feo/introducing-lqip-low-quality-image-placeholders/#post-850994943). As a result it can often decrease the duration until the ``onload`` event is triggered, but it will always improve perceived performance dramatically. In case you want to use the LQIP pattern for all browsers, not only Safari and FF35-, we suggest using [lazySizes](). 134 | -------------------------------------------------------------------------------- /plugins/intrinsic-dimension/README.md: -------------------------------------------------------------------------------- 1 | #respimage - Intrinsic dimension / size /scaling plugin 2 | 3 | The very lightweight intrinsic dimension plugin extends ``respimage`` to add the intrinsic dimension based on the descriptor (and the sizes attribute) and the density of the source candidate to the width content attribute of the image element. This scaling can be of course simply overwritten by any CSS selector. 4 | 5 | ##Download and embed 6 | Simply download the ``ri.intrinsic.min.js`` and include it after the respimage script: 7 | 8 | ```html 9 | 10 | 11 | ``` 12 | 13 | Here is a [demo of the intrinsic sizes extension](http://jsfiddle.net/trixta/gs3p14pr/embedded/result/). 14 | 15 | In case you want to include **respimage** only if the browser doesn't support responsive images you can use a script loader or write the following at the top of your head: 16 | 17 | ```html 18 | 27 | ``` 28 | 29 | Of course it is recommend to combine your scripts. 30 | 31 | In case you want to use a CDN you can use the combohandler service by jsDelivr: 32 | 33 | ```html 34 | 42 | ``` 43 | 44 | Note: To get this fully work in IE8 the ``img`` elements need a ``height: auto``. Simply add the following line to your CSS normalization: 45 | 46 | ```html 47 | img { 48 | height: auto; 49 | } 50 | 51 | /* 52 | or only target specific img elements: 53 | */ 54 | 55 | .content img { 56 | height: auto; 57 | } 58 | ``` 59 | 60 | -------------------------------------------------------------------------------- /plugins/intrinsic-dimension/ri.intrinsic.js: -------------------------------------------------------------------------------- 1 | (function( factory ) { 2 | "use strict"; 3 | var interValId; 4 | var intervalIndex = 0; 5 | var run = function(){ 6 | if ( window.respimage ) { 7 | factory( window.respimage ); 8 | } 9 | if(window.respimage || intervalIndex > 9999){ 10 | clearInterval(interValId); 11 | } 12 | intervalIndex++; 13 | }; 14 | interValId = setInterval(run, 8); 15 | 16 | run(); 17 | 18 | }( function( respimage, undefined ) { 19 | "use strict"; 20 | 21 | var document = window.document; 22 | var ri = respimage._; 23 | var knownWidths = {}; 24 | var cfg = ri.cfg; 25 | var setSize = function(width, img, data){ 26 | var curCandidate = data.curCan; 27 | 28 | if ( width ) { 29 | img.setAttribute( "width", Math.round(width / curCandidate.res) ); 30 | } 31 | }; 32 | var loadBg = function(url, img, data){ 33 | var bgImg, curCandidate, clear; 34 | 35 | 36 | if(url in knownWidths){ 37 | setSize(knownWidths[url], img, data); 38 | } else { 39 | clear = function(){ 40 | data.pendingURLSize = null; 41 | bgImg.onload = null; 42 | bgImg.onerror = null; 43 | img = null; 44 | bgImg = null; 45 | }; 46 | 47 | data.pendingURLSize = url; 48 | curCandidate = data.curCan; 49 | 50 | if(curCandidate.w){ 51 | setSize(curCandidate.w, img, data); 52 | } 53 | 54 | bgImg = document.createElement("img"); 55 | 56 | bgImg.onload = function(){ 57 | knownWidths[url] = bgImg.naturalWidth || bgImg.width; 58 | if (!knownWidths[url]) { 59 | try { 60 | document.body.appendChild(bgImg); 61 | knownWidths[url] = bgImg.offsetWidth || bgImg.naturalWidth || bgImg.width; 62 | document.body.removeChild(bgImg); 63 | } catch (e) {} 64 | } 65 | if(url == img.src){ 66 | setSize(knownWidths[url], img, data); 67 | } 68 | clear(); 69 | }; 70 | bgImg.onerror = clear; 71 | 72 | bgImg.src = url; 73 | 74 | if(bgImg && bgImg.complete){ 75 | bgImg.onload(); 76 | } 77 | } 78 | 79 | }; 80 | var reeval = (function(){ 81 | var running, timer; 82 | 83 | var run = function(){ 84 | var i, len, imgData; 85 | var imgs = document.getElementsByTagName("img"); 86 | var options = {elements: []}; 87 | 88 | ri.setupRun(options); 89 | 90 | running = false; 91 | clearTimeout(timer); 92 | 93 | for(i = 0, len = imgs.length; i < len; i++){ 94 | imgData = imgs[i][ri.ns]; 95 | 96 | if(imgData && imgData.curCan){ 97 | ri.setRes.res(imgData.curCan, imgData.curCan.set.sizes); 98 | ri.setSize(imgs[i]); 99 | } 100 | } 101 | 102 | ri.teardownRun( options ); 103 | }; 104 | 105 | return function(){ 106 | if(!running && cfg.addSize){ 107 | running = true; 108 | clearTimeout(timer); 109 | timer = setTimeout(run); 110 | } 111 | }; 112 | 113 | })(); 114 | 115 | ri.setSize = function( img ) { 116 | var url; 117 | var data = img[ ri.ns ]; 118 | var curCandidate = data.curCan; 119 | 120 | if ( data.dims === undefined ) { 121 | data.dims = img.getAttribute( "height" ) && img.getAttribute( "width" ); 122 | } 123 | 124 | if ( !cfg.addSize || !curCandidate || data.dims ) {return;} 125 | url = ri.makeUrl(curCandidate.url); 126 | 127 | if(url == img.src && url !== data.pendingURLSize){ 128 | loadBg(url, img, data); 129 | } 130 | }; 131 | 132 | 133 | if(window.addEventListener && !ri.supPicture){ 134 | addEventListener("resize", reeval, false); 135 | } 136 | 137 | if(!('addSize' in cfg)){ 138 | cfg.addSize = true; 139 | } else { 140 | cfg.addSize = !!cfg.addSize; 141 | } 142 | 143 | reeval(); 144 | })); 145 | -------------------------------------------------------------------------------- /plugins/intrinsic-dimension/ri.intrinsic.min.js: -------------------------------------------------------------------------------- 1 | /*! respimage - v1.4.2 - 2015-08-22 2 | Licensed MIT */ 3 | !function(a){"use strict";var b,c=0,d=function(){window.respimage&&a(window.respimage),(window.respimage||c>9999)&&clearInterval(b),c++};b=setInterval(d,8),d()}(function(a,b){"use strict";var c=window.document,d=a._,e={},f=d.cfg,g=function(a,b,c){var d=c.curCan;a&&b.setAttribute("width",Math.round(a/d.res))},h=function(a,b,d){var f,h,i;a in e?g(e[a],b,d):(i=function(){d.pendingURLSize=null,f.onload=null,f.onerror=null,b=null,f=null},d.pendingURLSize=a,h=d.curCan,h.w&&g(h.w,b,d),f=c.createElement("img"),f.onload=function(){if(e[a]=f.naturalWidth||f.width,!e[a])try{c.body.appendChild(f),e[a]=f.offsetWidth||f.naturalWidth||f.width,c.body.removeChild(f)}catch(h){}a==b.src&&g(e[a],b,d),i()},f.onerror=i,f.src=a,f&&f.complete&&f.onload())},i=function(){var a,b,e=function(){var e,f,g,h=c.getElementsByTagName("img"),i={elements:[]};for(d.setupRun(i),a=!1,clearTimeout(b),e=0,f=h.length;f>e;e++)g=h[e][d.ns],g&&g.curCan&&(d.setRes.res(g.curCan,g.curCan.set.sizes),d.setSize(h[e]));d.teardownRun(i)};return function(){!a&&f.addSize&&(a=!0,clearTimeout(b),b=setTimeout(e))}}();d.setSize=function(a){var c,e=a[d.ns],g=e.curCan;e.dims===b&&(e.dims=a.getAttribute("height")&&a.getAttribute("width")),f.addSize&&g&&!e.dims&&(c=d.makeUrl(g.url),c==a.src&&c!==e.pendingURLSize&&h(c,a,e))},window.addEventListener&&!d.supPicture&&addEventListener("resize",i,!1),f.addSize="addSize"in f?!!f.addSize:!0,i()}); -------------------------------------------------------------------------------- /plugins/mutation/README.md: -------------------------------------------------------------------------------- 1 | #respimage - Mutation plugin 2 | 3 | The mutation plugin extends ``respimage`` to automatically detect new responsive images in the document and additionally detects relevant attribute changes / mutations for responsive images. It also re-normalizes the ``getAttribute`` method in conjunction with the ``src`` and the ``srcset`` attribute. And adds getter and setter support for ``sizes`` and ``srcset`` as also getter support for the ``currentSrc`` property. 4 | 5 | ##Download and embed 6 | Simply download the ``ri.mutation.min.js`` and include it after the respimage script: 7 | 8 | ```html 9 | 10 | 11 | ``` 12 | 13 | In case you want to include **respimage** only if the browser doesn't support responsive images you can use a script loader or write the following at the top of your head (as inline script before any blocking resource): 14 | 15 | ```html 16 | 25 | ``` 26 | 27 | Of course it is recommend to combine your scripts. 28 | 29 | In case you want to use a CDN you can use the combohandler service by jsDelivr: 30 | 31 | ```html 32 | 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /plugins/mutation/ri.mutation.js: -------------------------------------------------------------------------------- 1 | (function( factory ) { 2 | "use strict"; 3 | var interValId; 4 | var intervalIndex = 0; 5 | var run = function(){ 6 | if ( window.respimage ) { 7 | factory( window.respimage ); 8 | } 9 | if(window.respimage || intervalIndex > 9999){ 10 | clearInterval(interValId); 11 | } 12 | intervalIndex++; 13 | }; 14 | interValId = setInterval(run, 8); 15 | 16 | run(); 17 | 18 | }( function( respimage ) { 19 | "use strict"; 20 | 21 | var document = window.document; 22 | var Element = window.Element; 23 | var MutationObserver = window.MutationObserver; 24 | var noop = function() {}; 25 | var riobserver = { 26 | disconnect: noop, 27 | take: noop, 28 | observe: noop, 29 | start: noop, 30 | stop: noop, 31 | connected: false 32 | }; 33 | var isReady = /^loade|^c|^i/.test(document.readyState || ""); 34 | var ri = respimage._; 35 | ri.mutationSupport = false; 36 | ri.observer = riobserver; 37 | if ( !Object.keys || !window.HTMLSourceElement || !document.addEventListener) { 38 | return; 39 | } 40 | var matches, observer, allowConnect, addMutation; 41 | 42 | var observeProps = { src: 1, srcset: 1, sizes: 1, media: 1 }; 43 | var attrFilter = Object.keys( observeProps ); 44 | var config = { attributes: true, childList: true, subtree: true, attributeFilter: attrFilter }; 45 | var elemProto = Element && Element.prototype; 46 | var sup = {}; 47 | var monkeyPatch = function( name, fn ) { 48 | sup[ name ] = ri[ name ]; 49 | ri[ name ] = fn; 50 | }; 51 | 52 | if ( elemProto && !elemProto.matches ) { 53 | elemProto.matches = elemProto.matchesSelector || elemProto.mozMatchesSelector || elemProto.webkitMatchesSelector || elemProto.msMatchesSelector; 54 | } 55 | 56 | if ( elemProto && elemProto.matches ) { 57 | matches = function( elem, sel ) { 58 | return elem.matches( sel ); 59 | }; 60 | ri.mutationSupport = !!( Object.create && Object.defineProperties ); 61 | } 62 | 63 | if ( !ri.mutationSupport ) { 64 | return; 65 | } 66 | 67 | riobserver.observe = function() { 68 | if ( allowConnect ) { 69 | riobserver.connected = true; 70 | if ( observer ) { 71 | observer.observe( document.documentElement, config ); 72 | } 73 | } 74 | }; 75 | 76 | riobserver.disconnect = function() { 77 | riobserver.connected = false; 78 | if ( observer ) { 79 | observer.disconnect(); 80 | } 81 | }; 82 | 83 | riobserver.take = function() { 84 | if ( observer ) { 85 | ri.onMutations( observer.takeRecords() ); 86 | } else if ( addMutation ) { 87 | addMutation.take(); 88 | } 89 | }; 90 | 91 | riobserver.start = function() { 92 | allowConnect = true; 93 | riobserver.observe(); 94 | }; 95 | 96 | riobserver.stop = function() { 97 | allowConnect = false; 98 | riobserver.disconnect(); 99 | }; 100 | 101 | monkeyPatch( "setupRun", function() { 102 | riobserver.disconnect(); 103 | return sup.setupRun.apply( this, arguments ); 104 | }); 105 | 106 | monkeyPatch( "teardownRun", function() { 107 | var ret = sup.setupRun.apply( this, arguments ); 108 | riobserver.observe(); 109 | return ret; 110 | }); 111 | 112 | monkeyPatch( "setSrc", function() { 113 | var ret; 114 | var wasConnected = riobserver.connected; 115 | riobserver.disconnect(); 116 | ret = sup.setSrc.apply( this, arguments ); 117 | if ( wasConnected ) { 118 | riobserver.observe(); 119 | } 120 | return ret; 121 | }); 122 | 123 | ri.onMutations = function( mutations ) { 124 | var i, len; 125 | var modifiedImgs = []; 126 | 127 | for (i = 0, len = mutations.length; i < len; i++) { 128 | if ( isReady && mutations[i].type === "childList" ) { 129 | ri.onSubtreeChange( mutations[i], modifiedImgs ); 130 | } else if ( mutations[i].type === "attributes" ) { 131 | ri.onAttrChange( mutations[i], modifiedImgs ); 132 | } 133 | } 134 | 135 | if ( modifiedImgs.length ) { 136 | 137 | ri.fillImgs({ 138 | elements: modifiedImgs, 139 | reevaluate: true 140 | }); 141 | } 142 | }; 143 | 144 | ri.onSubtreeChange = function( mutations, imgs ) { 145 | ri.findAddedMutations( mutations.addedNodes, imgs ); 146 | ri.findRemovedMutations( mutations.removedNodes, mutations.target, imgs ); 147 | }; 148 | 149 | ri.findAddedMutations = function( nodes, imgs ) { 150 | var i, len, node, nodeName; 151 | for ( i = 0, len = nodes.length; i < len; i++ ){ 152 | node = nodes[i]; 153 | if ( node.nodeType !== 1 ) {continue;} 154 | 155 | nodeName = node.nodeName.toUpperCase(); 156 | 157 | if ( nodeName === "PICTURE" ) { 158 | ri.addToElements( node.getElementsByTagName( "img" )[0], imgs ); 159 | } else if ( nodeName === "IMG" && matches( node, ri.selShort ) ){ 160 | ri.addToElements( node, imgs ); 161 | } else if ( nodeName === "SOURCE" ) { 162 | ri.addImgForSource( node, node.parentNode, imgs ); 163 | } else { 164 | ri.addToElements( ri.qsa( node, ri.selShort ), imgs ); 165 | } 166 | } 167 | }; 168 | 169 | ri.findRemovedMutations = function( nodes, target, imgs ) { 170 | var i, len, node; 171 | for ( i = 0, len = nodes.length; i < len; i++ ) { 172 | node = nodes[i]; 173 | if ( node.nodeType !== 1 ) {continue;} 174 | if ( node.nodeName.toUpperCase() === "SOURCE" ) { 175 | ri.addImgForSource( node, target, imgs ); 176 | } 177 | } 178 | }; 179 | 180 | ri.addImgForSource = function( node, parent, imgs ) { 181 | if ( parent && ( parent.nodeName || "" ).toUpperCase() !== "PICTURE" ) { 182 | parent = parent.parentNode; 183 | 184 | if(!parent || ( parent.nodeName || "" ).toUpperCase() !== "PICTURE" ) { 185 | parent = null; 186 | } 187 | } 188 | 189 | if(parent){ 190 | ri.addToElements( parent.getElementsByTagName( "img" )[0], imgs ); 191 | } 192 | }; 193 | 194 | ri.addToElements = function( img, imgs ) { 195 | var i, len; 196 | if ( img ) { 197 | if ( ("length" in img) && !img.nodeType ){ 198 | for ( i = 0, len = img.length; i < len; i++ ) { 199 | ri.addToElements( img[i], imgs ); 200 | } 201 | } else if ( img.parentNode && imgs.indexOf(img) === -1 ) { 202 | imgs.push( img ); 203 | } 204 | } 205 | }; 206 | 207 | ri.onAttrChange = function( mutation, modifiedImgs ) { 208 | var nodeName; 209 | var riData = mutation.target[ ri.ns ]; 210 | 211 | if ( !riData && 212 | mutation.attributeName === "srcset" && 213 | (nodeName = mutation.target.nodeName.toUpperCase()) === "IMG" ) { 214 | ri.addToElements( mutation.target, modifiedImgs ); 215 | } else if ( riData ) { 216 | if(!nodeName){ 217 | nodeName = mutation.target.nodeName.toUpperCase(); 218 | } 219 | 220 | if ( nodeName === "IMG" ) { 221 | if ( mutation.attributeName in riData ) { 222 | riData[ mutation.attributeName ] = undefined; 223 | } 224 | ri.addToElements( mutation.target, modifiedImgs ); 225 | } else if ( nodeName === "SOURCE" ) { 226 | ri.addImgForSource( mutation.target, mutation.target.parentNode, modifiedImgs ); 227 | } 228 | } 229 | }; 230 | 231 | if ( !ri.supPicture ) { 232 | 233 | if ( MutationObserver && !ri.testMutationEvents ) { 234 | observer = new MutationObserver( ri.onMutations ); 235 | } else { 236 | 237 | addMutation = (function() { 238 | var running = false; 239 | var mutations = []; 240 | var setImmediate = window.setImmediate || window.setTimeout; 241 | return function(mutation) { 242 | if ( !running ) { 243 | running = true; 244 | if ( !addMutation.take ) { 245 | addMutation.take = function() { 246 | if ( mutations.length ) { 247 | ri.onMutations( mutations ); 248 | mutations = []; 249 | } 250 | running = false; 251 | }; 252 | } 253 | setImmediate( addMutation.take ); 254 | } 255 | mutations.push( mutation ); 256 | }; 257 | })(); 258 | 259 | document.documentElement.addEventListener( "DOMNodeInserted", function( e ) { 260 | if ( riobserver.connected && isReady ) { 261 | addMutation( { type: "childList", addedNodes: [ e.target ], removedNodes: [] } ); 262 | } 263 | }, true); 264 | 265 | document.documentElement.addEventListener( "DOMNodeRemoved", function( e ) { 266 | 267 | if ( riobserver.connected && isReady && (e.target || {}).nodeName == 'SOURCE') { 268 | addMutation( { type: "childList", addedNodes: [], removedNodes: [ e.target ], target: e.target.parentNode } ); 269 | } 270 | }, true); 271 | 272 | document.documentElement.addEventListener( "DOMAttrModified", function( e ) { 273 | if ( riobserver.connected && observeProps[e.attrName] ) { 274 | addMutation( { type: "attributes", target: e.target, attributeName: e.attrName } ); 275 | } 276 | }, true); 277 | } 278 | 279 | if ( window.HTMLImageElement && Object.defineProperties ) { 280 | 281 | (function() { 282 | 283 | var image = document.createElement( "img" ); 284 | var imgIdls = []; 285 | var getImgAttr = image.getAttribute; 286 | var setImgAttr = image.setAttribute; 287 | var GETIMGATTRS = { 288 | src: 1 289 | }; 290 | 291 | if ( ri.supSrcset && !ri.supSizes ) { 292 | GETIMGATTRS.srcset = 1; 293 | } 294 | 295 | Object.defineProperties(HTMLImageElement.prototype, { 296 | getAttribute: { 297 | value: function( attr ) { 298 | var internal; 299 | if ( GETIMGATTRS[ attr ] && (internal = this[ ri.ns ]) && ( internal[attr] !== undefined ) ) { 300 | return internal[ attr ]; 301 | } 302 | return getImgAttr.apply( this, arguments ); 303 | }, 304 | writeable: true, 305 | enumerable: true, 306 | configurable: true 307 | } 308 | }); 309 | 310 | if(!ri.supSrcset){ 311 | imgIdls.push('srcset'); 312 | } 313 | 314 | if(!ri.supSizes){ 315 | imgIdls.push('sizes'); 316 | } 317 | 318 | imgIdls.forEach(function(idl){ 319 | Object.defineProperty(HTMLImageElement.prototype, idl, { 320 | set: function( value ) { 321 | setImgAttr.call( this, idl, value ); 322 | }, 323 | get: function() { 324 | return getImgAttr.call( this, idl ) || ''; 325 | }, 326 | enumerable: true, 327 | configurable: true 328 | }); 329 | }); 330 | 331 | if(!('currentSrc' in image)){ 332 | (function(){ 333 | var ascendingSort; 334 | var updateCurSrc = function(elem, src) { 335 | if(src == null){ 336 | src = elem.src || ''; 337 | } 338 | 339 | Object.defineProperty(elem, 'riCurrentSrc', { 340 | value: src, 341 | writable: true 342 | }); 343 | }; 344 | var baseUpdateCurSrc = updateCurSrc; 345 | 346 | if(ri.supSrcset && window.devicePixelRatio){ 347 | ascendingSort = function( a, b ) { 348 | var aRes = a.res || a.d || a.x || a.w; 349 | var bRes = b.res || b.d || b.x || a.w; 350 | return aRes - bRes; 351 | }; 352 | 353 | updateCurSrc = function(elem) { 354 | var i, cands, length, ret; 355 | var imageData = elem[ ri.ns ]; 356 | 357 | if ( imageData && imageData.supported && imageData.srcset && imageData.sets && (cands = ri.parseSet(imageData.sets[0])) && cands.sort) { 358 | 359 | cands.sort( ascendingSort ); 360 | length = cands.length; 361 | ret = cands[ length - 1 ]; 362 | 363 | for(i = 0; i < length; i++){ 364 | if(cands[i].x >= window.devicePixelRatio){ 365 | ret = cands[i]; 366 | break; 367 | } 368 | } 369 | 370 | if(ret){ 371 | ret = ri.makeUrl(ret.url); 372 | } 373 | } 374 | baseUpdateCurSrc(elem, ret); 375 | }; 376 | } 377 | 378 | document.addEventListener('load', function(e){ 379 | if(e.target.nodeName.toUpperCase() == 'IMG'){ 380 | updateCurSrc(e.target); 381 | } 382 | }, true); 383 | 384 | Object.defineProperty(HTMLImageElement.prototype, 'currentSrc', { 385 | set: function() { 386 | if(window.console && console.warn){ 387 | console.warn('currentSrc can\'t be set on img element'); 388 | } 389 | }, 390 | get: function(){ 391 | if(this.complete){ 392 | updateCurSrc(this); 393 | } 394 | return (!this.src && !this.srcset) ? '' : this.riCurrentSrc || ''; 395 | }, 396 | enumerable: true, 397 | configurable: true 398 | }); 399 | })(); 400 | } 401 | 402 | if(window.HTMLSourceElement && !('srcset' in document.createElement('source'))){ 403 | 404 | ['srcset', 'sizes'].forEach(function(idl){ 405 | Object.defineProperty(HTMLSourceElement.prototype, idl, { 406 | set: function( value ) { 407 | this.setAttribute( idl, value ); 408 | }, 409 | get: function() { 410 | return this.getAttribute( idl ) || ''; 411 | }, 412 | enumerable: true, 413 | configurable: true 414 | }); 415 | }); 416 | } 417 | 418 | })(); 419 | } 420 | 421 | riobserver.start(); 422 | } 423 | if ( !isReady ) { 424 | document.addEventListener("DOMContentLoaded", function(event) { 425 | isReady = true; 426 | }); 427 | } 428 | })); 429 | -------------------------------------------------------------------------------- /plugins/mutation/ri.mutation.min.js: -------------------------------------------------------------------------------- 1 | /*! respimage - v1.4.2 - 2015-08-22 2 | Licensed MIT */ 3 | !function(a){"use strict";var b,c=0,d=function(){window.respimage&&a(window.respimage),(window.respimage||c>9999)&&clearInterval(b),c++};b=setInterval(d,8),d()}(function(a){"use strict";var b=window.document,c=window.Element,d=window.MutationObserver,e=function(){},f={disconnect:e,take:e,observe:e,start:e,stop:e,connected:!1},g=/^loade|^c|^i/.test(b.readyState||""),h=a._;if(h.mutationSupport=!1,h.observer=f,Object.keys&&window.HTMLSourceElement&&b.addEventListener){var i,j,k,l,m={src:1,srcset:1,sizes:1,media:1},n=Object.keys(m),o={attributes:!0,childList:!0,subtree:!0,attributeFilter:n},p=c&&c.prototype,q={},r=function(a,b){q[a]=h[a],h[a]=b};p&&!p.matches&&(p.matches=p.matchesSelector||p.mozMatchesSelector||p.webkitMatchesSelector||p.msMatchesSelector),p&&p.matches&&(i=function(a,b){return a.matches(b)},h.mutationSupport=!(!Object.create||!Object.defineProperties)),h.mutationSupport&&(f.observe=function(){k&&(f.connected=!0,j&&j.observe(b.documentElement,o))},f.disconnect=function(){f.connected=!1,j&&j.disconnect()},f.take=function(){j?h.onMutations(j.takeRecords()):l&&l.take()},f.start=function(){k=!0,f.observe()},f.stop=function(){k=!1,f.disconnect()},r("setupRun",function(){return f.disconnect(),q.setupRun.apply(this,arguments)}),r("teardownRun",function(){var a=q.setupRun.apply(this,arguments);return f.observe(),a}),r("setSrc",function(){var a,b=f.connected;return f.disconnect(),a=q.setSrc.apply(this,arguments),b&&f.observe(),a}),h.onMutations=function(a){var b,c,d=[];for(b=0,c=a.length;c>b;b++)g&&"childList"===a[b].type?h.onSubtreeChange(a[b],d):"attributes"===a[b].type&&h.onAttrChange(a[b],d);d.length&&h.fillImgs({elements:d,reevaluate:!0})},h.onSubtreeChange=function(a,b){h.findAddedMutations(a.addedNodes,b),h.findRemovedMutations(a.removedNodes,a.target,b)},h.findAddedMutations=function(a,b){var c,d,e,f;for(c=0,d=a.length;d>c;c++)e=a[c],1===e.nodeType&&(f=e.nodeName.toUpperCase(),"PICTURE"===f?h.addToElements(e.getElementsByTagName("img")[0],b):"IMG"===f&&i(e,h.selShort)?h.addToElements(e,b):"SOURCE"===f?h.addImgForSource(e,e.parentNode,b):h.addToElements(h.qsa(e,h.selShort),b))},h.findRemovedMutations=function(a,b,c){var d,e,f;for(d=0,e=a.length;e>d;d++)f=a[d],1===f.nodeType&&"SOURCE"===f.nodeName.toUpperCase()&&h.addImgForSource(f,b,c)},h.addImgForSource=function(a,b,c){b&&"PICTURE"!==(b.nodeName||"").toUpperCase()&&(b=b.parentNode,b&&"PICTURE"===(b.nodeName||"").toUpperCase()||(b=null)),b&&h.addToElements(b.getElementsByTagName("img")[0],c)},h.addToElements=function(a,b){var c,d;if(a)if("length"in a&&!a.nodeType)for(c=0,d=a.length;d>c;c++)h.addToElements(a[c],b);else a.parentNode&&-1===b.indexOf(a)&&b.push(a)},h.onAttrChange=function(a,b){var c,d=a.target[h.ns];d||"srcset"!==a.attributeName||"IMG"!==(c=a.target.nodeName.toUpperCase())?d&&(c||(c=a.target.nodeName.toUpperCase()),"IMG"===c?(a.attributeName in d&&(d[a.attributeName]=void 0),h.addToElements(a.target,b)):"SOURCE"===c&&h.addImgForSource(a.target,a.target.parentNode,b)):h.addToElements(a.target,b)},h.supPicture||(d&&!h.testMutationEvents?j=new d(h.onMutations):(l=function(){var a=!1,b=[],c=window.setImmediate||window.setTimeout;return function(d){a||(a=!0,l.take||(l.take=function(){b.length&&(h.onMutations(b),b=[]),a=!1}),c(l.take)),b.push(d)}}(),b.documentElement.addEventListener("DOMNodeInserted",function(a){f.connected&&g&&l({type:"childList",addedNodes:[a.target],removedNodes:[]})},!0),b.documentElement.addEventListener("DOMNodeRemoved",function(a){f.connected&&g&&"SOURCE"==(a.target||{}).nodeName&&l({type:"childList",addedNodes:[],removedNodes:[a.target],target:a.target.parentNode})},!0),b.documentElement.addEventListener("DOMAttrModified",function(a){f.connected&&m[a.attrName]&&l({type:"attributes",target:a.target,attributeName:a.attrName})},!0)),window.HTMLImageElement&&Object.defineProperties&&!function(){var a=b.createElement("img"),c=[],d=a.getAttribute,e=a.setAttribute,f={src:1};h.supSrcset&&!h.supSizes&&(f.srcset=1),Object.defineProperties(HTMLImageElement.prototype,{getAttribute:{value:function(a){var b;return f[a]&&(b=this[h.ns])&&void 0!==b[a]?b[a]:d.apply(this,arguments)},writeable:!0,enumerable:!0,configurable:!0}}),h.supSrcset||c.push("srcset"),h.supSizes||c.push("sizes"),c.forEach(function(a){Object.defineProperty(HTMLImageElement.prototype,a,{set:function(b){e.call(this,a,b)},get:function(){return d.call(this,a)||""},enumerable:!0,configurable:!0})}),"currentSrc"in a||!function(){var a,c=function(a,b){null==b&&(b=a.src||""),Object.defineProperty(a,"riCurrentSrc",{value:b,writable:!0})},d=c;h.supSrcset&&window.devicePixelRatio&&(a=function(a,b){var c=a.res||a.d||a.x||a.w,d=b.res||b.d||b.x||a.w;return c-d},c=function(b){var c,e,f,g,i=b[h.ns];if(i&&i.supported&&i.srcset&&i.sets&&(e=h.parseSet(i.sets[0]))&&e.sort){for(e.sort(a),f=e.length,g=e[f-1],c=0;f>c;c++)if(e[c].x>=window.devicePixelRatio){g=e[c];break}g&&(g=h.makeUrl(g.url))}d(b,g)}),b.addEventListener("load",function(a){"IMG"==a.target.nodeName.toUpperCase()&&c(a.target)},!0),Object.defineProperty(HTMLImageElement.prototype,"currentSrc",{set:function(){window.console&&console.warn&&console.warn("currentSrc can't be set on img element")},get:function(){return this.complete&&c(this),this.src||this.srcset?this.riCurrentSrc||"":""},enumerable:!0,configurable:!0})}(),!window.HTMLSourceElement||"srcset"in b.createElement("source")||["srcset","sizes"].forEach(function(a){Object.defineProperty(HTMLSourceElement.prototype,a,{set:function(b){this.setAttribute(a,b)},get:function(){return this.getAttribute(a)||""},enumerable:!0,configurable:!0})})}(),f.start()),g||b.addEventListener("DOMContentLoaded",function(){g=!0}))}}); -------------------------------------------------------------------------------- /plugins/oldie/README.md: -------------------------------------------------------------------------------- 1 | #respimage - oldie plugin 2 | 3 | This simple plugin adds support for IE6 and IE7. While **respimage** supports IE8 in strict/normal mode. This plugin is also needed in case IE8 is rendering a page in compatibility view or quirks mode. It depends on jQuery. 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /plugins/oldie/ri.oldie.js: -------------------------------------------------------------------------------- 1 | (function( factory ) { 2 | "use strict"; 3 | var interValId; 4 | var intervalIndex = 0; 5 | var run = function(){ 6 | if ( window.respimage ) { 7 | factory( window.respimage ); 8 | } 9 | if(window.respimage || intervalIndex > 9999){ 10 | clearInterval(interValId); 11 | } 12 | intervalIndex++; 13 | }; 14 | interValId = setInterval(run, 8); 15 | 16 | run(); 17 | 18 | }( function( respimage ) { 19 | "use strict"; 20 | 21 | var ri = respimage._; 22 | 23 | if(!document.querySelector){ 24 | ri.qsa = function(context, sel) { 25 | return jQuery(sel, context); 26 | }; 27 | 28 | var anchor = document.createElement('a'); 29 | 30 | ri.makeUrl = function(src) { 31 | jQuery.attr(anchor, 'href', src+'' ); 32 | return jQuery.prop(anchor, 'href'); 33 | }; 34 | } 35 | 36 | })); 37 | -------------------------------------------------------------------------------- /plugins/oldie/ri.oldie.min.js: -------------------------------------------------------------------------------- 1 | /*! respimage - v1.4.2 - 2015-08-22 2 | Licensed MIT */ 3 | !function(a){"use strict";var b,c=0,d=function(){window.respimage&&a(window.respimage),(window.respimage||c>9999)&&clearInterval(b),c++};b=setInterval(d,8),d()}(function(a){"use strict";var b=a._;if(!document.querySelector){b.qsa=function(a,b){return jQuery(b,a)};var c=document.createElement("a");b.makeUrl=function(a){return jQuery.attr(c,"href",a+""),jQuery.prop(c,"href")}}}); -------------------------------------------------------------------------------- /plugins/perfselection/README.md: -------------------------------------------------------------------------------- 1 | #respimage - perfselection plugin 2 | 3 | This plugin was removed. 4 | 5 | ##perceived performance vs. perceived quality on retina devices 6 | 7 | Use one or the combination of the following techniques: 8 | 9 | ###constraining markup patterns via the ``picture`` element 10 | 11 | ####Limitting high resolution candidates due to (max-width) fragmenting 12 | ```html 13 | 14 | 15 | 22 | 29 | 30 | picture but without artdirection 39 | 40 | ``` 41 | ####Serve higher compressions to retina devices 42 | ```html 43 | 44 | 45 | 56 | 57 | picture but without artdirection 66 | 67 | ``` 68 | 69 | ###Use [lazyloading via lazysizes](https://github.com/aFarkas/lazysizes) 70 | 71 | ####Use the [``data-optimumx`` extension for lazysizes](https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/optimumx) 72 | 73 | ####Use [lazysizes responsive image service extension](https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/rias) with a third party or self hosted responsive image service 74 | -------------------------------------------------------------------------------- /plugins/print/README.md: -------------------------------------------------------------------------------- 1 | #respimage - print plugin 2 | 3 | respimage also includes a simple print plugin, which boosts the image quality as also allows re-runs the source selection for ``source`` elements with the ``media="print"``. 4 | 5 | 6 | ##Download and embed 7 | Download the ``ri.print.min.js`` and include it after the respimage script: 8 | 9 | ```html 10 | 11 | 12 | ``` 13 | 14 | In case you want to include **respimage** only if the browser doesn't support responsive images you can use a script loader or write the following at the top of your head: 15 | 16 | ```html 17 | 25 | ``` 26 | 27 | Of course it is recommended to combine your scripts. 28 | 29 | ##Browser support 30 | This extension only works in IE9+ and Firefox. Also note, that current versions of the native implementations do not yet support this feature. 31 | -------------------------------------------------------------------------------- /plugins/print/ri.print.js: -------------------------------------------------------------------------------- 1 | (function( factory ) { 2 | "use strict"; 3 | var interValId; 4 | var intervalIndex = 0; 5 | var run = function(){ 6 | if ( window.respimage ) { 7 | factory( window.respimage ); 8 | } 9 | if(window.respimage || intervalIndex > 9999){ 10 | clearInterval(interValId); 11 | } 12 | intervalIndex++; 13 | }; 14 | interValId = setInterval(run, 8); 15 | 16 | run(); 17 | 18 | }( function( respimage ) { 19 | "use strict"; 20 | if(!window.addEventListener){return;} 21 | var oldMatches, oldXQant, oldDPR; 22 | var printMedia = window.matchMedia && matchMedia('print') || {matches: false}; 23 | var ri = respimage._; 24 | var resetMedia = function(media){ 25 | if(!media){return true;} 26 | if(media.indexOf('print') != -1){return true;} 27 | if(oldMatches){return oldMatches.apply(this, arguments);} 28 | }; 29 | var beforeprint = function(){ 30 | if(!printMedia.matches && !oldMatches){ 31 | oldMatches = ri.matchesMedia; 32 | ri.matchesMedia = resetMedia; 33 | } 34 | 35 | if(!oldXQant && !oldDPR && ri.DPR < 1.5 && ri.cfg.xQuant < 1.5){ 36 | oldXQant = ri.cfg.xQuant; 37 | oldDPR = ri.DPR; 38 | ri.DPR = 1.5; 39 | ri.cfg.xQuant = 1.5; 40 | } 41 | respimage({reevaluate: true}); 42 | }; 43 | var afterprint = function(){ 44 | if(oldMatches){ 45 | ri.matchesMedia = oldMatches; 46 | oldMatches = false; 47 | } 48 | if(oldXQant){ 49 | ri.cfg.xQuant = oldXQant; 50 | oldXQant = false; 51 | } 52 | if(oldDPR){ 53 | ri.DPR = oldDPR; 54 | oldDPR = false; 55 | } 56 | respimage({reselect: true}); 57 | }; 58 | 59 | 60 | 61 | if('onbeforeprint' in window){ 62 | addEventListener('beforeprint', beforeprint, false); 63 | addEventListener('afterprint', afterprint, false); 64 | } 65 | })); 66 | -------------------------------------------------------------------------------- /plugins/print/ri.print.min.js: -------------------------------------------------------------------------------- 1 | /*! respimage - v1.4.2 - 2015-08-22 2 | Licensed MIT */ 3 | !function(a){"use strict";var b,c=0,d=function(){window.respimage&&a(window.respimage),(window.respimage||c>9999)&&clearInterval(b),c++};b=setInterval(d,8),d()}(function(a){"use strict";if(window.addEventListener){var b,c,d,e=window.matchMedia&&matchMedia("print")||{matches:!1},f=a._,g=function(a){return a?-1!=a.indexOf("print")?!0:b?b.apply(this,arguments):void 0:!0},h=function(){e.matches||b||(b=f.matchesMedia,f.matchesMedia=g),!c&&!d&&f.DPR<1.5&&f.cfg.xQuant<1.5&&(c=f.cfg.xQuant,d=f.DPR,f.DPR=1.5,f.cfg.xQuant=1.5),a({reevaluate:!0})},i=function(){b&&(f.matchesMedia=b,b=!1),c&&(f.cfg.xQuant=c,c=!1),d&&(f.DPR=d,d=!1),a({reselect:!0})};"onbeforeprint"in window&&(addEventListener("beforeprint",h,!1),addEventListener("afterprint",i,!1))}}); -------------------------------------------------------------------------------- /plugins/typesupport/README.md: -------------------------------------------------------------------------------- 1 | #respimage - type[support] plugin 2 | 3 | While respimage includes type detection for the most used images (png, jpeg, gif and svg), this tiny plugin extends the type detection to the following formats: 4 | 5 | * **webp** (image/webp) 6 | * **JPEG XR** (image/vnd.ms-photo) 7 | * **JPEG 2000** (image/jp2, image/jpx, image/jpm) 8 | * **APNG** (video/vnd.mozilla.apng, video/x-apng, video/png, video/apng, video/x-mng, video/x-png) (There is no official type for apng) 9 | 10 | Note: This is not a polyfill, this is simply a type detection. 11 | 12 | ##Download and embed 13 | Download the ``ri.type.min.js`` and include it after the respimage script: 14 | 15 | ```html 16 | 17 | 18 | ``` 19 | 20 | In case you want to include **respimage** only if the browser doesn't support responsive images you can use a script loader or write the following at the top of your head (as inline script before any blocking resource): 21 | 22 | ```html 23 | 31 | ``` 32 | 33 | Of course it is recommended to combine your scripts. 34 | 35 | In case you want to use a CDN you can use the combohandler service by jsDelivr: 36 | 37 | ```html 38 | 46 | ``` 47 | 48 | ```html 49 | 50 | 51 | 52 | 55 | 58 | 59 | image with typedirection 62 | 63 | 64 | 65 | 66 | 67 | 74 | 81 | 82 | image with typedirection 90 | 91 | ``` 92 | -------------------------------------------------------------------------------- /plugins/typesupport/ri.type.js: -------------------------------------------------------------------------------- 1 | (function( factory ) { 2 | "use strict"; 3 | var interValId; 4 | var intervalIndex = 0; 5 | var run = function(){ 6 | if ( window.respimage ) { 7 | factory( window.respimage ); 8 | } 9 | if(window.respimage || intervalIndex > 9999){ 10 | clearInterval(interValId); 11 | } 12 | intervalIndex++; 13 | }; 14 | interValId = setInterval(run, 8); 15 | 16 | run(); 17 | 18 | }( function( respimage ) { 19 | "use strict"; 20 | 21 | var ri = respimage._; 22 | var runningTests = 0; 23 | var setTypeValue = function(types, value){ 24 | var i; 25 | for(i = 0; i < types.length; i++){ 26 | ri.types[types[i]] = value; 27 | } 28 | }; 29 | 30 | if(ri.supPicture && !ri.cfg.uT){ 31 | respimage.testTypeSupport = function(){}; 32 | return; 33 | } 34 | 35 | ri.types["image/bmp"] = true; 36 | ri.types["image/x-bmp"] = true; 37 | 38 | respimage.testTypeSupport = function(types, url, width, useCanvas){ 39 | if(typeof types == "string"){ 40 | types = types.split(/\s*\,*\s+/g); 41 | } 42 | var canvas; 43 | var supports = "pending"; 44 | var img = document.createElement('img'); 45 | var onComplete = function(){ 46 | runningTests--; 47 | setTypeValue(types, supports); 48 | if(runningTests < 1){ 49 | respimage({reevaluate: true}); 50 | } 51 | }; 52 | 53 | if(useCanvas){ 54 | canvas = document.createElement('canvas'); 55 | if(!canvas.getContext){ 56 | setTypeValue(types, false); 57 | return; 58 | } 59 | } 60 | 61 | img.onload = function(){ 62 | var ctx; 63 | supports = true; 64 | if(width){ 65 | supports = img.width == width; 66 | } 67 | 68 | if(useCanvas){ 69 | ctx = canvas.getContext('2d'); 70 | ctx.drawImage(img, 0, 0); 71 | supports = ctx.getImageData(0, 0, 1, 1).data[3] === 0; 72 | } 73 | onComplete(); 74 | }; 75 | 76 | img.onerror = function(){ 77 | supports = false; 78 | onComplete(); 79 | }; 80 | runningTests++; 81 | setTypeValue(types, "pending"); 82 | img.src = url; 83 | }; 84 | 85 | 86 | respimage.testTypeSupport("image/webp", "data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==", 1); 87 | respimage.testTypeSupport("image/jp2 image/jpx image/jpm", "data:image/jp2;base64,/0//UQAyAAAAAAABAAAAAgAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAEBwEBBwEBBwEBBwEB/1IADAAAAAEAAAQEAAH/XAAEQED/ZAAlAAFDcmVhdGVkIGJ5IE9wZW5KUEVHIHZlcnNpb24gMi4wLjD/kAAKAAAAAABYAAH/UwAJAQAABAQAAf9dAAUBQED/UwAJAgAABAQAAf9dAAUCQED/UwAJAwAABAQAAf9dAAUDQED/k8+kEAGvz6QQAa/PpBABr994EAk//9k=", 1); 88 | respimage.testTypeSupport("image/vnd.ms-photo", "data:image/vnd.ms-photo;base64,SUm8AQgAAAAFAAG8AQAQAAAASgAAAIC8BAABAAAAAQAAAIG8BAABAAAAAQAAAMC8BAABAAAAWgAAAMG8BAABAAAAHwAAAAAAAAAkw91vA07+S7GFPXd2jckNV01QSE9UTwAZAYBxAAAAABP/gAAEb/8AAQAAAQAAAA==", 1); 89 | respimage.testTypeSupport("video/vnd.mozilla.apng video/x-apng video/png video/apng video/x-mng video/x-png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACGFjVEwAAAABAAAAAcMq2TYAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAGmZjVEwAAAAAAAAAAQAAAAEAAAAAAAAAAAD6A+gBAbNU+2sAAAARZmRBVAAAAAEImWNgYGBgAAAABQAB6MzFdgAAAABJRU5ErkJggg==", false, true); 90 | 91 | })); 92 | -------------------------------------------------------------------------------- /plugins/typesupport/ri.type.min.js: -------------------------------------------------------------------------------- 1 | /*! respimage - v1.4.2 - 2015-08-22 2 | Licensed MIT */ 3 | !function(a){"use strict";var b,c=0,d=function(){window.respimage&&a(window.respimage),(window.respimage||c>9999)&&clearInterval(b),c++};b=setInterval(d,8),d()}(function(a){"use strict";var b=a._,c=0,d=function(a,c){var d;for(d=0;dc&&a({reevaluate:!0})};return g&&(h=document.createElement("canvas"),!h.getContext)?void d(b,!1):(j.onload=function(){var a;i=!0,f&&(i=j.width==f),g&&(a=h.getContext("2d"),a.drawImage(j,0,0),i=0===a.getImageData(0,0,1,1).data[3]),k()},j.onerror=function(){i=!1,k()},c++,d(b,"pending"),void(j.src=e))},a.testTypeSupport("image/webp","data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==",1),a.testTypeSupport("image/jp2 image/jpx image/jpm","data:image/jp2;base64,/0//UQAyAAAAAAABAAAAAgAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAEBwEBBwEBBwEBBwEB/1IADAAAAAEAAAQEAAH/XAAEQED/ZAAlAAFDcmVhdGVkIGJ5IE9wZW5KUEVHIHZlcnNpb24gMi4wLjD/kAAKAAAAAABYAAH/UwAJAQAABAQAAf9dAAUBQED/UwAJAgAABAQAAf9dAAUCQED/UwAJAwAABAQAAf9dAAUDQED/k8+kEAGvz6QQAa/PpBABr994EAk//9k=",1),a.testTypeSupport("image/vnd.ms-photo","data:image/vnd.ms-photo;base64,SUm8AQgAAAAFAAG8AQAQAAAASgAAAIC8BAABAAAAAQAAAIG8BAABAAAAAQAAAMC8BAABAAAAWgAAAMG8BAABAAAAHwAAAAAAAAAkw91vA07+S7GFPXd2jckNV01QSE9UTwAZAYBxAAAAABP/gAAEb/8AAQAAAQAAAA==",1),void a.testTypeSupport("video/vnd.mozilla.apng video/x-apng video/png video/apng video/x-mng video/x-png","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACGFjVEwAAAABAAAAAcMq2TYAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAGmZjVEwAAAAAAAAAAQAAAAEAAAAAAAAAAAD6A+gBAbNU+2sAAAARZmRBVAAAAAEImWNgYGBgAAAABQAB6MzFdgAAAABJRU5ErkJggg==",!1,!0))}); -------------------------------------------------------------------------------- /respimage.dev.js: -------------------------------------------------------------------------------- 1 | /*! Respimg - Responsive Images that work today. 2 | * Author: Alexander Farkas, 2014 3 | * Author: Scott Jehl, Filament Group, 2012 ( new proposal implemented by Shawn Jansepar ) 4 | * License: MIT 5 | * Spec: http://picture.responsiveimages.org/ 6 | */ 7 | (function( window, document, undefined ) { 8 | // Enable strict mode 9 | "use strict"; 10 | if ( typeof RIDEBUG == "undefined" ) { 11 | window.RIDEBUG = true; 12 | } 13 | 14 | var lowTreshHold, partialLowTreshHold, isLandscape, lazyFactor, tMemory, substractCurRes, warn, eminpx, 15 | alwaysCheckWDescriptor, resizeThrottle, evalID; 16 | // local object for method references and testing exposure 17 | var ri = {}; 18 | var noop = function() {}; 19 | var image = document.createElement( "img" ); 20 | var getImgAttr = image.getAttribute; 21 | var setImgAttr = image.setAttribute; 22 | var removeImgAttr = image.removeAttribute; 23 | var docElem = document.documentElement; 24 | var types = {}; 25 | var cfg = { 26 | //resource selection: 27 | xQuant: 1, 28 | lazyFactor: 0.4, 29 | maxX: 2 30 | }; 31 | var srcAttr = "data-pfsrc"; 32 | var srcsetAttr = srcAttr + "set"; 33 | var reflowBug = "webkitBackfaceVisibility" in docElem.style; 34 | var ua = navigator.userAgent; 35 | var supportAbort = (/rident/).test(ua) || ((/ecko/).test(ua) && ua.match(/rv\:(\d+)/) && RegExp.$1 > 35 ); 36 | var curSrcProp = "currentSrc"; 37 | var regWDesc = /\s+\+?\d+(e\d+)?w/; 38 | var regSize = /((?:\([^)]+\)(?:\s*and\s*|\s*or\s*|\s*not\s*)?)+)?\s*(.+)/; 39 | var regDescriptor = /^([\+eE\d\.]+)(w|x)$/; // currently no h 40 | var regHDesc = /\s*\d+h\s*/; 41 | var setOptions = window.respimgCFG; 42 | /** 43 | * Shortcut property for https://w3c.github.io/webappsec/specs/mixedcontent/#restricts-mixed-content ( for easy overriding in tests ) 44 | */ 45 | var isSSL = location.protocol == "https:"; 46 | // baseStyle also used by getEmValue (i.e.: width: 1em is important) 47 | 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)"; 48 | var fsCss = "font-size:100%!important;"; 49 | var isVwDirty = true; 50 | 51 | var cssCache = {}; 52 | var sizeLengthCache = {}; 53 | var DPR = window.devicePixelRatio; 54 | var units = { 55 | px: 1, 56 | 'in': 96 57 | }; 58 | var anchor = document.createElement( "a" ); 59 | /** 60 | * alreadyRun flag used for setOptions. is it true setOptions will reevaluate 61 | * @type {boolean} 62 | */ 63 | var alreadyRun = false; 64 | 65 | var on = function(obj, evt, fn, capture) { 66 | if ( obj.addEventListener ) { 67 | obj.addEventListener(evt, fn, capture || false); 68 | } else if ( obj.attachEvent ) { 69 | obj.attachEvent( "on"+ evt, fn); 70 | } 71 | }; 72 | 73 | var off = function(obj, evt, fn, capture) { 74 | if ( obj.removeEventListener ) { 75 | obj.removeEventListener(evt, fn, capture || false); 76 | } else if ( obj.detachEvent ) { 77 | obj.detachEvent( "on"+ evt, fn); 78 | } 79 | }; 80 | 81 | /** 82 | * simple memoize function: 83 | */ 84 | 85 | var memoize = function(fn) { 86 | var cache = {}; 87 | return function(input) { 88 | if ( !(input in cache) ) { 89 | cache[ input ] = fn(input); 90 | } 91 | return cache[ input ]; 92 | }; 93 | }; 94 | 95 | /** 96 | * gets a mediaquery and returns a boolean or gets a css length and returns a number 97 | * @param css mediaqueries or css length 98 | * @returns {boolean|number} 99 | * 100 | * based on: https://gist.github.com/jonathantneal/db4f77009b155f083738 101 | */ 102 | var evalCSS = (function(){ 103 | var regLength = /^([\d\.]+)(em|vw|px)$/; 104 | var replace = function() { 105 | var args = arguments, index = 0, string = args[0]; 106 | while (++index in args) { 107 | string = string.replace(args[index], args[++index]); 108 | } 109 | return string; 110 | }; 111 | 112 | var buidlStr = memoize(function(css) { 113 | return "return " + replace((css || "").toLowerCase(), 114 | // interpret `and` 115 | /\band\b/g, "&&", 116 | 117 | // interpret `,` 118 | /,/g, "||", 119 | 120 | // interpret `min-` as >= 121 | /min-([a-z-\s]+):/g, "e.$1>=", 122 | 123 | // interpret `min-` as <= 124 | /max-([a-z-\s]+):/g, "e.$1<=", 125 | 126 | //calc value 127 | /calc([^)]+)/g, "($1)", 128 | 129 | // interpret css values 130 | /(\d+[\.]*[\d]*)([a-z]+)/g, "($1 * e.$2)", 131 | //make eval less evil 132 | /^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/ig, "" 133 | ); 134 | }); 135 | 136 | return function(css, length) { 137 | var parsedLength; 138 | if (!(css in cssCache)) { 139 | cssCache[css] = false; 140 | if(length && (parsedLength = css.match( regLength ))){ 141 | cssCache[css] = parsedLength[ 1 ] * units[parsedLength[ 2 ]]; 142 | } else { 143 | /*jshint evil:true */ 144 | try{ 145 | cssCache[css] = new Function("e", buidlStr(css))(units); 146 | } catch(e){} 147 | /*jshint evil:false */ 148 | } 149 | } 150 | return cssCache[css]; 151 | }; 152 | })(); 153 | 154 | var setResolution = function ( candidate, sizesattr ) { 155 | if ( candidate.w ) { // h = means height: || descriptor.type == 'h' do not handle yet... 156 | candidate.cWidth = ri.calcListLength( sizesattr || "100vw" ); 157 | candidate.res = candidate.w / candidate.cWidth ; 158 | } else { 159 | candidate.res = candidate.x; 160 | } 161 | return candidate; 162 | }; 163 | 164 | /** 165 | * 166 | * @param opt 167 | */ 168 | var respimage = function( opt ) { 169 | var elements, i, plen; 170 | 171 | var options = opt || {}; 172 | 173 | if ( options.elements && options.elements.nodeType == 1 ) { 174 | if ( options.elements.nodeName.toUpperCase() == "IMG" ) { 175 | options.elements = [ options.elements ]; 176 | } else { 177 | options.context = options.elements; 178 | options.elements = null; 179 | } 180 | } 181 | 182 | if(options.reparse){ 183 | options.reevaluate = true; 184 | if(window.console && console.warn){ 185 | console.warn('reparse was renamed to reevaluate!'); 186 | } 187 | } 188 | 189 | elements = options.elements || ri.qsa( (options.context || document), ( options.reevaluate || options.reselect ) ? ri.sel : ri.selShort ); 190 | 191 | if ( (plen = elements.length) ) { 192 | 193 | ri.setupRun( options ); 194 | alreadyRun = true; 195 | 196 | // Loop through all elements 197 | 198 | for ( i = 0; i < plen; i++ ) { 199 | ri.fillImg(elements[ i ], options); 200 | } 201 | ri.teardownRun( options ); 202 | } 203 | }; 204 | 205 | var parseDescriptor = memoize(function ( descriptor ) { 206 | 207 | var descriptorObj = [1, 'x']; 208 | var parsedDescriptor = trim( descriptor || "" ); 209 | 210 | if ( parsedDescriptor ) { 211 | parsedDescriptor = parsedDescriptor.replace(regHDesc, ""); 212 | if ( ( parsedDescriptor ).match( regDescriptor ) ) { 213 | 214 | descriptorObj = [RegExp.$1 * 1, RegExp.$2]; 215 | 216 | if ( RIDEBUG && ( 217 | descriptorObj[0] < 0 || 218 | isNaN( descriptorObj[0] ) || 219 | (descriptorObj[1] == "w" && /\./.test(''+descriptorObj[0])) 220 | ) ) { 221 | warn( "bad descriptor: " + descriptor ); 222 | } 223 | } else { 224 | descriptorObj = false; 225 | 226 | if ( RIDEBUG ) { 227 | warn( "unknown descriptor: " + descriptor ); 228 | } 229 | } 230 | } 231 | 232 | return descriptorObj; 233 | }); 234 | 235 | /** 236 | * outputs a warning for the developer 237 | * @param {message} 238 | * @type {Function} 239 | */ 240 | if(RIDEBUG){ 241 | warn = ( window.console && console.warn ) ? 242 | function( message ) { 243 | console.warn( message ); 244 | } : 245 | noop 246 | ; 247 | } 248 | 249 | if( !(curSrcProp in image) ){ 250 | curSrcProp = "src"; 251 | } 252 | 253 | // Add support for standard mime types. 254 | types["image/jpeg"] = true; 255 | types["image/gif"] = true; 256 | types["image/png"] = true; 257 | 258 | // test svg support 259 | types[ "image/svg+xml" ] = document.implementation.hasFeature( "http://wwwindow.w3.org/TR/SVG11/feature#Image", "1.1" ); 260 | 261 | /** 262 | * a trim workaroung mainly for IE8 263 | * @param str 264 | * @returns {string} 265 | */ 266 | function trim( str ) { 267 | return str.trim ? str.trim() : str.replace( /^\s+|\s+$/g, "" ); 268 | } 269 | 270 | /** 271 | * updates the internal vW property with the current viewport width in px 272 | */ 273 | function updateMetrics() { 274 | var dprM; 275 | 276 | isVwDirty = false; 277 | DPR = window.devicePixelRatio; 278 | cssCache = {}; 279 | sizeLengthCache = {}; 280 | 281 | dprM = (DPR || 1) * cfg.xQuant; 282 | 283 | if(!cfg.uT){ 284 | cfg.maxX = Math.max(1.3, cfg.maxX); 285 | dprM = Math.min( dprM, cfg.maxX ); 286 | 287 | ri.DPR = dprM; 288 | } 289 | 290 | units.width = Math.max(window.innerWidth || 0, docElem.clientWidth); 291 | units.height = Math.max(window.innerHeight || 0, docElem.clientHeight); 292 | 293 | units.vw = units.width / 100; 294 | units.vh = units.height / 100; 295 | 296 | units.em = ri.getEmValue(); 297 | units.rem = units.em; 298 | 299 | lazyFactor = cfg.lazyFactor / 2; 300 | 301 | lazyFactor = (lazyFactor * dprM) + lazyFactor; 302 | 303 | substractCurRes = 0.4 + (0.1 * dprM); 304 | 305 | lowTreshHold = 0.5 + (0.2 * dprM); 306 | 307 | partialLowTreshHold = 0.5 + (0.25 * dprM); 308 | 309 | tMemory = dprM + 1.3; 310 | 311 | if(!(isLandscape = units.width > units.height)){ 312 | lazyFactor *= 0.9; 313 | } 314 | if(supportAbort){ 315 | lazyFactor *= 0.9; 316 | } 317 | 318 | evalID = [units.width, units.height, dprM].join('-'); 319 | } 320 | 321 | function chooseLowRes( lowRes, diff, dpr ) { 322 | var add = diff * Math.pow(lowRes - 0.4, 1.9); 323 | if(!isLandscape){ 324 | add /= 1.3; 325 | } 326 | 327 | lowRes += add; 328 | return lowRes > dpr; 329 | } 330 | 331 | function applyBestCandidate( img ) { 332 | var srcSetCandidates; 333 | var matchingSet = ri.getSet( img ); 334 | var evaluated = false; 335 | if ( matchingSet != "pending" ) { 336 | evaluated = evalID; 337 | if ( matchingSet ) { 338 | srcSetCandidates = ri.setRes( matchingSet ); 339 | evaluated = ri.applySetCandidate( srcSetCandidates, img ); 340 | } 341 | 342 | } 343 | img[ ri.ns ].evaled = evaluated; 344 | } 345 | 346 | function ascendingSort( a, b ) { 347 | return a.res - b.res; 348 | } 349 | 350 | function setSrcToCur( img, src, set ) { 351 | var candidate; 352 | if ( !set && src ) { 353 | set = img[ ri.ns ].sets; 354 | set = set && set[set.length - 1]; 355 | } 356 | 357 | candidate = getCandidateForSrc(src, set); 358 | 359 | if ( candidate ) { 360 | src = ri.makeUrl(src); 361 | img[ ri.ns ].curSrc = src; 362 | img[ ri.ns ].curCan = candidate; 363 | 364 | if ( !candidate.res ) { 365 | setResolution( candidate, candidate.set.sizes ); 366 | } 367 | } 368 | return candidate; 369 | } 370 | 371 | function getCandidateForSrc( src, set ) { 372 | var i, candidate, candidates; 373 | if ( src && set ) { 374 | candidates = ri.parseSet( set ); 375 | src = ri.makeUrl(src); 376 | for ( i = 0; i < candidates.length; i++ ) { 377 | if ( src == ri.makeUrl(candidates[ i ].url) ) { 378 | candidate = candidates[ i ]; 379 | break; 380 | } 381 | } 382 | } 383 | return candidate; 384 | } 385 | 386 | 387 | function getAllSourceElements( picture, candidates ) { 388 | var i, len, source, srcset; 389 | 390 | // SPEC mismatch intended for size and perf: 391 | // actually only source elements preceding the img should be used 392 | // also note: don't use qsa here, because IE8 sometimes doesn't like source as the key part in a selector 393 | var sources = picture.getElementsByTagName( "source" ); 394 | 395 | for ( i = 0, len = sources.length; i < len; i++ ) { 396 | source = sources[ i ]; 397 | source[ ri.ns ] = true; 398 | srcset = source.getAttribute( "srcset" ); 399 | 400 | if ( RIDEBUG && document.documentMode != 9 && source.parentNode != picture ) { 401 | warn( "all source elements have to be a child of the picture element. For IE9 support wrap them in an audio/video element, BUT with conditional comments" ); 402 | } 403 | // if source does not have a srcset attribute, skip 404 | if ( srcset ) { 405 | candidates.push( { 406 | srcset: srcset, 407 | media: source.getAttribute( "media" ), 408 | type: source.getAttribute( "type" ), 409 | sizes: source.getAttribute( "sizes" ) 410 | } ); 411 | } 412 | if ( RIDEBUG && source.getAttribute( "src" ) ) { 413 | warn( "`src` on `source` invalid, use `srcset`." ); 414 | } 415 | } 416 | 417 | if ( RIDEBUG ) { 418 | var srcTest = ri.qsa( picture, "source, img"); 419 | if ( srcTest[ srcTest.length - 1].nodeName.toUpperCase() == "SOURCE" ) { 420 | warn( "all sources inside picture have to precede the img element" ); 421 | } 422 | } 423 | } 424 | 425 | // namespace 426 | ri.ns = ("ri" + new Date().getTime()).substr(0, 9); 427 | 428 | // srcset support test 429 | ri.supSrcset = "srcset" in image; 430 | ri.supSizes = "sizes" in image; 431 | ri.supPicture = !!window.HTMLPictureElement; 432 | 433 | if (ri.supSrcset && ri.supPicture && !ri.supSizes) { 434 | (function(image2) { 435 | image.srcset = "data:,a"; 436 | image2.src = "data:,a"; 437 | ri.supSrcset = image.complete === image2.complete; 438 | ri.supPicture = ri.supSrcset && ri.supPicture; 439 | })(document.createElement("img")); 440 | } 441 | 442 | // using ri.qsa instead of dom traversing does scale much better, 443 | // especially on sites mixing responsive and non-responsive images 444 | ri.selShort = "picture>img,img[srcset]"; 445 | ri.sel = ri.selShort; 446 | ri.cfg = cfg; 447 | 448 | if ( ri.supSrcset ) { 449 | ri.sel += ",img[" + srcsetAttr + "]"; 450 | } 451 | 452 | /** 453 | * Shortcut property for `devicePixelRatio` ( for easy overriding in tests ) 454 | */ 455 | ri.DPR = (DPR || 1 ); 456 | ri.u = units; 457 | 458 | // container of supported mime types that one might need to qualify before using 459 | ri.types = types; 460 | 461 | alwaysCheckWDescriptor = ri.supSrcset && !ri.supSizes; 462 | 463 | ri.setSize = noop; 464 | 465 | /** 466 | * Gets a string and returns the absolute URL 467 | * @param src 468 | * @returns {String} absolute URL 469 | */ 470 | ri.makeUrl = memoize(function(src) { 471 | anchor.href = src; 472 | return anchor.href; 473 | }); 474 | 475 | /** 476 | * Gets a DOM element or document and a selctor and returns the found matches 477 | * Can be extended with jQuery/Sizzle for IE7 support 478 | * @param context 479 | * @param sel 480 | * @returns {NodeList} 481 | */ 482 | ri.qsa = function(context, sel) { 483 | return context.querySelectorAll(sel); 484 | }; 485 | 486 | /** 487 | * Shortcut method for matchMedia ( for easy overriding in tests ) 488 | * wether native or ri.mMQ is used will be decided lazy on first call 489 | * @returns {boolean} 490 | */ 491 | ri.matchesMedia = function() { 492 | if ( window.matchMedia && (matchMedia( "(min-width: 0.1em)" ) || {}).matches ) { 493 | ri.matchesMedia = function( media ) { 494 | return !media || ( matchMedia( media ).matches ); 495 | }; 496 | } else { 497 | ri.matchesMedia = ri.mMQ; 498 | } 499 | 500 | return ri.matchesMedia.apply( this, arguments ); 501 | }; 502 | 503 | /** 504 | * A simplified matchMedia implementation for IE8 and IE9 505 | * handles only min-width/max-width with px or em values 506 | * @param media 507 | * @returns {boolean} 508 | */ 509 | ri.mMQ = function( media ) { 510 | return media ? evalCSS(media) : true; 511 | }; 512 | 513 | 514 | /** 515 | * Returns the calculated length in css pixel from the given sourceSizeValue 516 | * http://dev.w3.org/csswg/css-values-3/#length-value 517 | * intended Spec mismatches: 518 | * * Does not check for invalid use of CSS functions 519 | * * Does handle a computed length of 0 the same as a negative and therefore invalid value 520 | * @param sourceSizeValue 521 | * @returns {Number} 522 | */ 523 | ri.calcLength = function( sourceSizeValue ) { 524 | 525 | var value = evalCSS(sourceSizeValue, true) || false; 526 | if(value < 0){ 527 | value = false; 528 | } 529 | 530 | if ( RIDEBUG && (value === false || value < 0) ) { 531 | warn( "invalid source size: " + sourceSizeValue ); 532 | } 533 | return value; 534 | }; 535 | 536 | /** 537 | * Takes a type string and checks if its supported 538 | */ 539 | 540 | ri.supportsType = function( type ) { 541 | return ( type ) ? types[ type ] : true; 542 | }; 543 | 544 | /** 545 | * Parses a sourceSize into mediaCondition (media) and sourceSizeValue (length) 546 | * @param sourceSizeStr 547 | * @returns {*} 548 | */ 549 | ri.parseSize = memoize(function( sourceSizeStr ) { 550 | var match = ( sourceSizeStr || "" ).match(regSize); 551 | return { 552 | media: match && match[1], 553 | length: match && match[2] 554 | }; 555 | }); 556 | 557 | ri.parseSet = function( set ) { 558 | /* 559 | * A lot of this was pulled from Boris Smus’ parser for the now-defunct WHATWG `srcset` 560 | * https://github.com/borismus/srcset-polyfill/blob/master/js/srcset-info.js 561 | * 562 | * 1. Let input (`srcset`) be the value passed to this algorithm. 563 | * 2. Let position be a pointer into input, initially pointing at the start of the string. 564 | * 3. Let raw candidates be an initially empty ordered list of URLs with associated 565 | * unparsed descriptors. The order of entries in the list is the order in which entries 566 | * are added to the list. 567 | */ 568 | 569 | if ( !set.cands ) { 570 | 571 | var pos, url, descriptor, last, descpos, can, firstDescriptorType; 572 | var srcset = set.srcset; 573 | 574 | set.cands = []; 575 | 576 | while ( srcset ) { 577 | srcset = srcset.replace(/^\s+/g,""); 578 | // 5. Collect a sequence of characters that are not space characters, and let that be url. 579 | pos = srcset.search(/\s/g); 580 | descriptor = null; 581 | if ( pos != -1 ) { 582 | url = srcset.slice( 0, pos ); 583 | last = url.charAt( url.length - 1 ); 584 | // 6. If url ends with a U+002C COMMA character (,), remove that character from url 585 | // and let descriptors be the empty string. Otherwise, follow these substeps 586 | // 6.1. If url is empty, then jump to the step labeled descriptor parser. 587 | if ( last == "," || !url ) { 588 | url = url.replace(/,+$/, ""); 589 | descriptor = ""; 590 | } 591 | srcset = srcset.slice( pos + 1 ); 592 | // 6.2. Collect a sequence of characters that are not U+002C COMMA characters (,), and 593 | // let that be descriptors. 594 | if ( descriptor == null ) { 595 | descpos = srcset.indexOf( "," ); 596 | if ( descpos != -1 ) { 597 | descriptor = srcset.slice( 0, descpos ); 598 | srcset = srcset.slice( descpos + 1 ); 599 | } else { 600 | descriptor = srcset; 601 | srcset = ""; 602 | } 603 | } 604 | } else { 605 | url = srcset; 606 | srcset = ""; 607 | } 608 | 609 | // 7. Add url to raw candidates, associated with descriptors. 610 | if ( url && (descriptor = parseDescriptor( descriptor )) ) { 611 | 612 | if ( RIDEBUG ) { 613 | if ( !firstDescriptorType ) { 614 | firstDescriptorType = set.sizes ? "w" : descriptor[1]; 615 | } 616 | if ( firstDescriptorType != descriptor[1] ) { 617 | warn("mixing x with a w descriptor/sizes attribute in one srcset doesn't make sense in most cases and is invalid."); 618 | } 619 | } 620 | can = { 621 | url: url.replace(/^,+/, ""), 622 | set: set 623 | }; 624 | can[descriptor[1]] = descriptor[0]; 625 | 626 | if(descriptor[1] == 'x' && descriptor[0] == 1){ 627 | set.has1x = true; 628 | } 629 | 630 | set.cands.push(can); 631 | } 632 | } 633 | } 634 | 635 | return set.cands; 636 | }; 637 | 638 | /** 639 | * returns 1em in css px for html/body default size 640 | * function taken from respondjs 641 | * @returns {*|number} 642 | */ 643 | ri.getEmValue = function() { 644 | var body; 645 | if ( !eminpx && (body = document.body) ) { 646 | var div = document.createElement( "div" ), 647 | originalHTMLCSS = docElem.style.cssText, 648 | originalBodyCSS = body.style.cssText; 649 | 650 | div.style.cssText = baseStyle; 651 | 652 | // 1em in a media query is the value of the default font size of the browser 653 | // reset docElem and body to ensure the correct value is returned 654 | docElem.style.cssText = fsCss; 655 | body.style.cssText = fsCss; 656 | 657 | body.appendChild( div ); 658 | eminpx = div.offsetWidth; 659 | body.removeChild( div ); 660 | 661 | //also update eminpx before returning 662 | eminpx = parseFloat( eminpx, 10 ); 663 | 664 | // restore the original values 665 | docElem.style.cssText = originalHTMLCSS; 666 | body.style.cssText = originalBodyCSS; 667 | 668 | } 669 | return eminpx || 16; 670 | }; 671 | 672 | 673 | /** 674 | * Takes a string of sizes and returns the width in pixels as a number 675 | */ 676 | ri.calcListLength = function( sourceSizeListStr ) { 677 | // Split up source size list, ie ( max-width: 30em ) 100%, ( max-width: 50em ) 50%, 33% 678 | // 679 | // or (min-width:30em) calc(30% - 15px) 680 | if ( !(sourceSizeListStr in sizeLengthCache) || cfg.uT ) { 681 | var sourceSize, parsedSize, length, media, i, len; 682 | var sourceSizeList = trim( sourceSizeListStr ).split( /\s*,\s*/ ); 683 | var winningLength = false; 684 | for ( i = 0, len = sourceSizeList.length; i < len; i++ ) { 685 | // Match ? length, ie ( min-width: 50em ) 100% 686 | sourceSize = sourceSizeList[ i ]; 687 | // Split "( min-width: 50em ) 100%" into separate strings 688 | parsedSize = ri.parseSize( sourceSize ); 689 | length = parsedSize.length; 690 | media = parsedSize.media; 691 | 692 | if ( !length ) { 693 | continue; 694 | } 695 | // if there is no media query or it matches, choose this as our winning length 696 | // and end algorithm 697 | if ( ri.matchesMedia( media ) && (winningLength = ri.calcLength( length )) !== false ) { 698 | break; 699 | } 700 | } 701 | // pass the length to a method that can properly determine length 702 | // in pixels based on these formats: http://dev.w3.org/csswg/css-values-3/#length-value 703 | sizeLengthCache[ sourceSizeListStr ] = !winningLength ? units.width : winningLength; 704 | } 705 | 706 | return sizeLengthCache[ sourceSizeListStr ]; 707 | }; 708 | 709 | /** 710 | * Takes a candidate object with a srcset property in the form of url/ 711 | * ex. "images/pic-medium.png 1x, images/pic-medium-2x.png 2x" or 712 | * "images/pic-medium.png 400w, images/pic-medium-2x.png 800w" or 713 | * "images/pic-small.png" 714 | * Get an array of image candidates in the form of 715 | * {url: "/foo/bar.png", resolution: 1} 716 | * where resolution is http://dev.w3.org/csswg/css-values-3/#resolution-value 717 | * If sizes is specified, res is calculated 718 | */ 719 | ri.setRes = function( set ) { 720 | var candidates; 721 | if ( set ) { 722 | 723 | candidates = ri.parseSet( set ); 724 | 725 | for ( var i = 0, len = candidates.length; i < len; i++ ) { 726 | setResolution( candidates[ i ], set.sizes ); 727 | } 728 | } 729 | return candidates; 730 | }; 731 | 732 | ri.setRes.res = setResolution; 733 | 734 | ri.applySetCandidate = function( candidates, img ) { 735 | if ( !candidates.length ) {return;} 736 | var candidate, 737 | dpr, 738 | i, 739 | j, 740 | diff, 741 | length, 742 | bestCandidate, 743 | curSrc, 744 | curCan, 745 | isSameSet, 746 | candidateSrc, 747 | abortCurSrc, 748 | oldRes; 749 | 750 | var imageData = img[ ri.ns ]; 751 | var evaled = evalID; 752 | var lazyF = lazyFactor; 753 | var sub = substractCurRes; 754 | 755 | curSrc = imageData.curSrc || img[curSrcProp]; 756 | 757 | curCan = imageData.curCan || setSrcToCur(img, curSrc, candidates[0].set); 758 | 759 | dpr = ri.DPR; 760 | 761 | oldRes = curCan && curCan.res; 762 | 763 | //if we have a current source, we might either become lazy or give this source some advantage 764 | if ( !bestCandidate && curSrc ) { 765 | 766 | abortCurSrc = (supportAbort && !img.complete && curCan && oldRes - 0.2 > dpr); 767 | 768 | if( !abortCurSrc && (!curCan || tMemory > oldRes) ){ 769 | 770 | //add some lazy padding to the src 771 | if ( curCan && oldRes < dpr && oldRes > lowTreshHold ) { 772 | 773 | 774 | if(oldRes < partialLowTreshHold){ 775 | lazyF *= 0.8; 776 | sub += (0.04 * dpr); 777 | } 778 | 779 | curCan.res += lazyF * Math.pow(oldRes - sub, 2); 780 | } 781 | 782 | isSameSet = !imageData.pic || (curCan && curCan.set == candidates[ 0 ].set); 783 | 784 | if ( curCan && isSameSet && curCan.res >= dpr ) { 785 | bestCandidate = curCan; 786 | } 787 | } 788 | } 789 | 790 | if ( !bestCandidate ) { 791 | if ( oldRes ) { 792 | curCan.res = curCan.res - ((curCan.res - oldRes) / 2); 793 | } 794 | 795 | candidates.sort( ascendingSort ); 796 | 797 | length = candidates.length; 798 | bestCandidate = candidates[ length - 1 ]; 799 | 800 | for ( i = 0; i < length; i++ ) { 801 | candidate = candidates[ i ]; 802 | if ( candidate.res >= dpr ) { 803 | j = i - 1; 804 | 805 | // we have found the perfect candidate, 806 | // but let's improve this a little bit with some assumptions ;-) 807 | if (candidates[ j ] && 808 | (diff = (candidate.res - dpr)) && 809 | (abortCurSrc || curSrc != ri.makeUrl( candidate.url )) && 810 | chooseLowRes(candidates[ j ].res, diff, dpr)) { 811 | bestCandidate = candidates[ j ]; 812 | 813 | } else { 814 | bestCandidate = candidate; 815 | } 816 | break; 817 | } 818 | } 819 | } 820 | 821 | if ( oldRes ) { 822 | curCan.res = oldRes; 823 | } 824 | 825 | if ( bestCandidate ) { 826 | 827 | candidateSrc = ri.makeUrl( bestCandidate.url ); 828 | 829 | 830 | imageData.curSrc = candidateSrc; 831 | imageData.curCan = bestCandidate; 832 | 833 | if ( candidateSrc != curSrc ) { 834 | ri.setSrc( img, bestCandidate ); 835 | if ( RIDEBUG ) { 836 | testImgDimensions(img, bestCandidate); 837 | if(isSSL && !bestCandidate.url.indexOf( "http:" )){ 838 | warn( "insecure: " + candidateSrc ); 839 | } 840 | } 841 | } 842 | ri.setSize( img ); 843 | } 844 | 845 | return evaled; 846 | }; 847 | 848 | ri.setSrc = function( img, bestCandidate ) { 849 | var origStyle; 850 | img.src = bestCandidate.url; 851 | 852 | 853 | if ( reflowBug ) { 854 | origStyle = img.style.zoom; 855 | img.style.zoom = "0.999"; 856 | 857 | // next line only should trigger a repaint 858 | // if... is only done to trick dead code removal 859 | if ( img.offsetWidth || 1 ) { 860 | img.style.zoom = origStyle; 861 | } 862 | } 863 | }; 864 | 865 | ri.getSet = function( img ) { 866 | var i, set, supportsType; 867 | var match = false; 868 | var sets = img [ ri.ns ].sets; 869 | 870 | for ( i = 0; i < sets.length && !match; i++ ) { 871 | set = sets[i]; 872 | 873 | if ( !set.srcset || !ri.matchesMedia( set.media ) || !(supportsType = ri.supportsType( set.type )) ) { 874 | continue; 875 | } 876 | 877 | if ( supportsType == "pending" ) { 878 | set = supportsType; 879 | } 880 | 881 | match = set; 882 | break; 883 | } 884 | 885 | return match; 886 | }; 887 | 888 | ri.parseSets = function( element, parent, options ) { 889 | var srcsetAttribute, imageSet, isWDescripor, srcsetParsed; 890 | 891 | var hasPicture = parent.nodeName.toUpperCase() == "PICTURE"; 892 | var imageData = element[ ri.ns ]; 893 | 894 | if ( imageData.src === undefined || options.src ) { 895 | imageData.src = getImgAttr.call( element, "src" ); 896 | if ( imageData.src ) { 897 | setImgAttr.call( element, srcAttr, imageData.src ); 898 | } else { 899 | removeImgAttr.call( element, srcAttr ); 900 | } 901 | } 902 | 903 | if ( imageData.srcset === undefined || !ri.supSrcset || element.srcset || options.srcset ) { 904 | srcsetAttribute = getImgAttr.call( element, "srcset" ); 905 | imageData.srcset = srcsetAttribute; 906 | srcsetParsed = true; 907 | } 908 | 909 | imageData.sets = []; 910 | 911 | if ( hasPicture ) { 912 | imageData.pic = true; 913 | getAllSourceElements( parent, imageData.sets ); 914 | } 915 | 916 | if ( imageData.srcset ) { 917 | imageSet = { 918 | srcset: imageData.srcset, 919 | sizes: getImgAttr.call( element, "sizes" ) 920 | }; 921 | 922 | imageData.sets.push( imageSet ); 923 | 924 | isWDescripor = (alwaysCheckWDescriptor || imageData.src) && regWDesc.test(imageData.srcset || ''); 925 | 926 | // add normal src as candidate, if source has no w descriptor 927 | if ( !isWDescripor && imageData.src && !getCandidateForSrc(imageData.src, imageSet) && !imageSet.has1x ) { 928 | imageSet.srcset += ", " + imageData.src; 929 | imageSet.cands.push({ 930 | url: imageData.src, 931 | x: 1, 932 | set: imageSet 933 | }); 934 | } 935 | 936 | if ( RIDEBUG && !hasPicture && isWDescripor && imageData.src && imageSet.srcset.indexOf(element[ ri.ns ].src) == -1 ) { 937 | warn("The fallback candidate (`src`) isn't described inside the srcset attribute. Normally you want to describe all available candidates."); 938 | } 939 | 940 | } else if ( imageData.src ) { 941 | imageData.sets.push( { 942 | srcset: imageData.src, 943 | sizes: null 944 | } ); 945 | } 946 | 947 | imageData.curCan = null; 948 | imageData.curSrc = undefined; 949 | 950 | // if img has picture or the srcset was removed or has a srcset and does not support srcset at all 951 | // or has a w descriptor (and does not support sizes) set support to false to evaluate 952 | imageData.supported = !( hasPicture || ( imageSet && !ri.supSrcset ) || isWDescripor ); 953 | 954 | if ( srcsetParsed && ri.supSrcset && !imageData.supported ) { 955 | if ( srcsetAttribute ) { 956 | setImgAttr.call( element, srcsetAttr, srcsetAttribute ); 957 | element.srcset = ""; 958 | } else { 959 | removeImgAttr.call( element, srcsetAttr ); 960 | } 961 | } 962 | 963 | if(imageData.supported && !imageData.srcset && ((!imageData.src && element.src) || element.src != ri.makeUrl(imageData.src))){ 964 | if(imageData.src == null){ 965 | element.removeAttribute('src'); 966 | } else { 967 | element.src = imageData.src; 968 | } 969 | } 970 | 971 | if ( RIDEBUG ) { 972 | testMediaOrder(imageData.sets, 'source'); 973 | } 974 | imageData.parsed = true; 975 | }; 976 | 977 | 978 | ri.fillImg = function(element, options) { 979 | var parent, imageData; 980 | var extreme = options.reselect || options.reevaluate; 981 | 982 | // expando for caching data on the img 983 | if ( !element[ ri.ns ] ) { 984 | element[ ri.ns ] = {}; 985 | } 986 | 987 | imageData = element[ ri.ns ]; 988 | 989 | // if the element has already been evaluated, skip it 990 | // unless `options.reevaluate` is set to true ( this, for example, 991 | // is set to true when running `respimage` on `resize` ). 992 | if ( !extreme && imageData.evaled == evalID ) { 993 | return; 994 | } 995 | 996 | if ( !imageData.parsed || options.reevaluate ) { 997 | parent = element.parentNode; 998 | if ( !parent ) { 999 | return; 1000 | } 1001 | ri.parseSets( element, parent, options ); 1002 | } 1003 | 1004 | if ( !imageData.supported ) { 1005 | applyBestCandidate( element ); 1006 | } else { 1007 | imageData.evaled = evalID; 1008 | } 1009 | }; 1010 | 1011 | ri.setupRun = function( options ) { 1012 | if ( !alreadyRun || isVwDirty || DPR != window.devicePixelRatio ) { 1013 | updateMetrics(); 1014 | 1015 | // if all images are reevaluated clear the resizetimer 1016 | if ( !options.elements && !options.context ) { 1017 | clearTimeout( resizeThrottle ); 1018 | } 1019 | } 1020 | }; 1021 | 1022 | // If picture is supported, well, that's awesome. 1023 | if ( ri.supPicture ) { 1024 | respimage = noop; 1025 | ri.fillImg = noop; 1026 | } else { 1027 | // HTML shim|v it for old IE (IE9 will still need the HTML video tag workaround) 1028 | document.createElement( "picture" ); 1029 | 1030 | /** 1031 | * Sets up picture polyfill by polling the document 1032 | * Also attaches respimage on resize and readystatechange 1033 | */ 1034 | (function() { 1035 | var isDomReady; 1036 | var regReady = window.attachEvent ? /d$|^c/ : /d$|^c|^i/; 1037 | var run = function() { 1038 | var readyState = document.readyState || ""; 1039 | 1040 | timerId = setTimeout(run, readyState == "loading" ? 200 : 999); 1041 | if ( document.body ) { 1042 | isDomReady = isDomReady || regReady.test( readyState ); 1043 | ri.fillImgs(); 1044 | if ( isDomReady ) { 1045 | clearTimeout( timerId ); 1046 | } 1047 | } 1048 | }; 1049 | 1050 | var resizeEval = function() { 1051 | ri.fillImgs(); 1052 | }; 1053 | 1054 | var onResize = function() { 1055 | clearTimeout( resizeThrottle ); 1056 | isVwDirty = true; 1057 | resizeThrottle = setTimeout( resizeEval, 99 ); 1058 | }; 1059 | 1060 | var timerId = setTimeout(run, document.body ? 0 : 20); 1061 | 1062 | on( window, "resize", onResize ); 1063 | on( document, "readystatechange", run ); 1064 | 1065 | })(); 1066 | } 1067 | 1068 | ri.respimage = respimage; 1069 | //use this internally for easy monkey patching/performance testing 1070 | ri.fillImgs = respimage; 1071 | ri.teardownRun = noop; 1072 | 1073 | /* expose methods for testing */ 1074 | respimage._ = ri; 1075 | 1076 | /* expose respimage */ 1077 | window.respimage = window.picturefill || respimage; 1078 | 1079 | if(!window.picturefill){ 1080 | window.respimgCFG = { 1081 | ri: ri, 1082 | push: function(args){ 1083 | var name = args.shift(); 1084 | if(typeof ri[name] == "function"){ 1085 | ri[name].apply(ri, args); 1086 | } else { 1087 | cfg[name] = args[0]; 1088 | if(alreadyRun){ 1089 | ri.fillImgs({reselect: true}); 1090 | } 1091 | } 1092 | } 1093 | }; 1094 | 1095 | while(setOptions && setOptions.length){ 1096 | window.respimgCFG.push(setOptions.shift()); 1097 | } 1098 | } 1099 | 1100 | if(!window.picturefill){ 1101 | window.picturefill = window.respimage; 1102 | if(!window.picturefillCFG){ 1103 | window.picturefillCFG = window.respimgCFG; 1104 | } 1105 | } 1106 | 1107 | if ( RIDEBUG ) { 1108 | warn( "Responsive image debugger active. Do not use in production, because it slows things down! extremly" ); 1109 | 1110 | if(!document.querySelector || (document.documentMode || 9) < 8){ 1111 | warn("querySelector is needed. IE8 needs to be in strict, standard or edge mode: http://bit.ly/1yGgYU0 or try the ri.oldie.js plugin."); 1112 | } 1113 | if( (document.getElementsByTagName("picture")[0] ||{} ).outerHTML == "" ){ 1114 | warn("IE8 needs to picture shived. Either include respimage.js in or use html5shiv."); 1115 | } 1116 | 1117 | if(document.compatMode == "BackCompat"){ 1118 | warn("Browser is in quirksmode. Please make sure to be in strict mode."); 1119 | } 1120 | 1121 | var testImgDimensions = function (img, candidate) { 1122 | var onload = function () { 1123 | var dif, xtreshhold; 1124 | var imgWidth = img.offsetWidth; 1125 | var naturalWidth = img.naturalWidth; 1126 | var canWidth = candidate.cWidth; 1127 | var res = ri.DPR * cfg.xQuant; 1128 | var hTresh = 0.84; 1129 | var lTresh = res > 1 ? 0.5 : 0.75; 1130 | 1131 | 1132 | if(!canWidth && naturalWidth && candidate.x){ 1133 | canWidth = naturalWidth / res; 1134 | } 1135 | 1136 | if (imgWidth && canWidth) { 1137 | if (imgWidth > canWidth) { 1138 | dif = canWidth / imgWidth; 1139 | xtreshhold = lTresh; 1140 | } else { 1141 | dif = imgWidth / canWidth; 1142 | xtreshhold = hTresh; 1143 | } 1144 | 1145 | if(Math.abs(imgWidth - canWidth) > 50){ 1146 | if (candidate.w && dif < 0.86) { 1147 | warn("Check your sizes attribute: " + candidate.set.sizes + " was calculated to: " + canWidth + "px. But your image is shown with a size of " + imgWidth + "px. img: "+ candidate.url); 1148 | } else if(candidate.x && dif < xtreshhold){ 1149 | //warn("Image too much resized. Image was shown with "+ imgWidth +" but has a normalized width of "+ canWidth +". Maybe you should use a w descriptor instead of an x descriptor. img: "+ candidate.url); 1150 | } 1151 | } 1152 | } 1153 | 1154 | off(img, "load", onload); 1155 | }; 1156 | 1157 | on(img, "load", onload); 1158 | }; 1159 | var testMediaOrder = (function(){ 1160 | var regex = { 1161 | minw: /^\s*\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)\s*$/, 1162 | maxw: /^\s*\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)\s*$/ 1163 | }; 1164 | 1165 | var checkSetOrder = function(set, sets, index, type){ 1166 | var i, curSet; 1167 | for(i = 0; i < index && i < sets.length; i++){ 1168 | curSet = sets[i]; 1169 | if((set._min && curSet._min && set._min >= curSet._min) || (set._max && curSet._max && set._max <= curSet._max)){ 1170 | if(type == 'source'){ 1171 | warn("Order of your source elements matters. Defining "+ set.media + " after "+ curSet.media +" doesn't make sense."); 1172 | } else { 1173 | warn("Order inside your sizes attribute does matter. Defining "+ set.media + " after "+ curSet.media +" doesn't make sense."); 1174 | } 1175 | } 1176 | } 1177 | }; 1178 | var mediaTest = function(sets, type){ 1179 | var i, len, set, lastSet; 1180 | 1181 | lastSet = sets[sets.length - 1]; 1182 | if(lastSet && (lastSet.media || lastSet.type)){ 1183 | if(type == 'source'){ 1184 | warn("The last src/srcset shouldn't have any type or media conditions. Use img[src] or img[srcset]."); 1185 | } else { 1186 | warn("Last sizes attribute shouldn't have any condition otherwise 100vw is used."); 1187 | } 1188 | } 1189 | for(i = 0, len = sets.length; i < len; i++){ 1190 | set = sets[i]; 1191 | if(!set.media || set.type){ 1192 | if(!set.type && i != len - 1){ 1193 | if(type == 'source'){ 1194 | warn("A source element without [media] and [type] doesn't make any sense. Last srcset can be used at the img element. Order is important!"); 1195 | } else { 1196 | warn("The order of your sizes attribute does matter! The sizes length without a media condition has to be defined as last entry."); 1197 | } 1198 | } 1199 | continue; 1200 | } 1201 | set._min = set.media.match( regex.minw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ); 1202 | set._max = set.media.match( regex.maxw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ); 1203 | 1204 | 1205 | if ( set._min ) { 1206 | set._min = parseFloat( set._min, 10 ) * (set._min.indexOf( "em" ) > 0 ? ri.getEmValue() : 1); 1207 | } 1208 | 1209 | if ( set._max ) { 1210 | set._max = parseFloat( set._max, 10 ) * (set._max.indexOf( "em" ) > 0 ? ri.getEmValue() : 1); 1211 | } 1212 | if(set._min || set._max){ 1213 | checkSetOrder(set, sets, i, type); 1214 | } 1215 | } 1216 | }; 1217 | 1218 | return function(sets){ 1219 | var i, len, sizes, j, sizesSet; 1220 | 1221 | mediaTest(sets, 'source'); 1222 | 1223 | for(i = 0, len = sets.length; i < len; i++){ 1224 | sizes = trim(sets[i].sizes || ''); 1225 | if(sizes){ 1226 | sizesSet = []; 1227 | sizes = sizes.split( /\s*,\s*/ ); 1228 | for(j = 0; j < sizes.length; j++){ 1229 | if(sizes[j]){ 1230 | sizesSet.push(ri.parseSize( sizes[j] )); 1231 | } 1232 | } 1233 | 1234 | if(sizesSet.length){ 1235 | mediaTest(sizesSet, 'sizes'); 1236 | } 1237 | } 1238 | } 1239 | }; 1240 | })(); 1241 | } 1242 | 1243 | } )( window, document ); 1244 | -------------------------------------------------------------------------------- /respimage.js: -------------------------------------------------------------------------------- 1 | /*! respimage - v1.4.2 - 2015-08-22 2 | Licensed MIT */ 3 | !function(window, document, undefined) { 4 | "use strict"; 5 | function trim(str) { 6 | return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ""); 7 | } 8 | function updateMetrics() { 9 | var dprM; 10 | isVwDirty = !1, DPR = window.devicePixelRatio, cssCache = {}, sizeLengthCache = {}, 11 | dprM = (DPR || 1) * cfg.xQuant, cfg.uT || (cfg.maxX = Math.max(1.3, cfg.maxX), dprM = Math.min(dprM, cfg.maxX), 12 | ri.DPR = dprM), units.width = Math.max(window.innerWidth || 0, docElem.clientWidth), 13 | units.height = Math.max(window.innerHeight || 0, docElem.clientHeight), units.vw = units.width / 100, 14 | units.vh = units.height / 100, units.em = ri.getEmValue(), units.rem = units.em, 15 | lazyFactor = cfg.lazyFactor / 2, lazyFactor = lazyFactor * dprM + lazyFactor, substractCurRes = .4 + .1 * dprM, 16 | lowTreshHold = .5 + .2 * dprM, partialLowTreshHold = .5 + .25 * dprM, tMemory = dprM + 1.3, 17 | (isLandscape = units.width > units.height) || (lazyFactor *= .9), supportAbort && (lazyFactor *= .9), 18 | evalID = [ units.width, units.height, dprM ].join("-"); 19 | } 20 | function chooseLowRes(lowRes, diff, dpr) { 21 | var add = diff * Math.pow(lowRes - .4, 1.9); 22 | return isLandscape || (add /= 1.3), lowRes += add, lowRes > dpr; 23 | } 24 | function applyBestCandidate(img) { 25 | var srcSetCandidates, matchingSet = ri.getSet(img), evaluated = !1; 26 | "pending" != matchingSet && (evaluated = evalID, matchingSet && (srcSetCandidates = ri.setRes(matchingSet), 27 | evaluated = ri.applySetCandidate(srcSetCandidates, img))), img[ri.ns].evaled = evaluated; 28 | } 29 | function ascendingSort(a, b) { 30 | return a.res - b.res; 31 | } 32 | function setSrcToCur(img, src, set) { 33 | var candidate; 34 | return !set && src && (set = img[ri.ns].sets, set = set && set[set.length - 1]), 35 | candidate = getCandidateForSrc(src, set), candidate && (src = ri.makeUrl(src), img[ri.ns].curSrc = src, 36 | img[ri.ns].curCan = candidate, candidate.res || setResolution(candidate, candidate.set.sizes)), 37 | candidate; 38 | } 39 | function getCandidateForSrc(src, set) { 40 | var i, candidate, candidates; 41 | if (src && set) for (candidates = ri.parseSet(set), src = ri.makeUrl(src), i = 0; i < candidates.length; i++) if (src == ri.makeUrl(candidates[i].url)) { 42 | candidate = candidates[i]; 43 | break; 44 | } 45 | return candidate; 46 | } 47 | function getAllSourceElements(picture, candidates) { 48 | var i, len, source, srcset, sources = picture.getElementsByTagName("source"); 49 | for (i = 0, len = sources.length; len > i; i++) source = sources[i], source[ri.ns] = !0, 50 | srcset = source.getAttribute("srcset"), srcset && candidates.push({ 51 | srcset: srcset, 52 | media: source.getAttribute("media"), 53 | type: source.getAttribute("type"), 54 | sizes: source.getAttribute("sizes") 55 | }); 56 | } 57 | var lowTreshHold, partialLowTreshHold, isLandscape, lazyFactor, tMemory, substractCurRes, eminpx, alwaysCheckWDescriptor, resizeThrottle, evalID, ri = {}, noop = function() {}, image = document.createElement("img"), getImgAttr = image.getAttribute, setImgAttr = image.setAttribute, removeImgAttr = image.removeAttribute, docElem = document.documentElement, types = {}, cfg = { 58 | xQuant: 1, 59 | lazyFactor: .4, 60 | maxX: 2 61 | }, srcAttr = "data-pfsrc", srcsetAttr = srcAttr + "set", reflowBug = "webkitBackfaceVisibility" in docElem.style, ua = navigator.userAgent, supportAbort = /rident/.test(ua) || /ecko/.test(ua) && ua.match(/rv\:(\d+)/) && RegExp.$1 > 35, curSrcProp = "currentSrc", regWDesc = /\s+\+?\d+(e\d+)?w/, regSize = /((?:\([^)]+\)(?:\s*and\s*|\s*or\s*|\s*not\s*)?)+)?\s*(.+)/, regDescriptor = /^([\+eE\d\.]+)(w|x)$/, regHDesc = /\s*\d+h\s*/, setOptions = window.respimgCFG, baseStyle = ("https:" == location.protocol, 62 | "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)"), fsCss = "font-size:100%!important;", isVwDirty = !0, cssCache = {}, sizeLengthCache = {}, DPR = window.devicePixelRatio, units = { 63 | px: 1, 64 | "in": 96 65 | }, anchor = document.createElement("a"), alreadyRun = !1, on = function(obj, evt, fn, capture) { 66 | obj.addEventListener ? obj.addEventListener(evt, fn, capture || !1) : obj.attachEvent && obj.attachEvent("on" + evt, fn); 67 | }, memoize = function(fn) { 68 | var cache = {}; 69 | return function(input) { 70 | return input in cache || (cache[input] = fn(input)), cache[input]; 71 | }; 72 | }, evalCSS = function() { 73 | var regLength = /^([\d\.]+)(em|vw|px)$/, replace = function() { 74 | for (var args = arguments, index = 0, string = args[0]; ++index in args; ) string = string.replace(args[index], args[++index]); 75 | return string; 76 | }, buidlStr = memoize(function(css) { 77 | return "return " + replace((css || "").toLowerCase(), /\band\b/g, "&&", /,/g, "||", /min-([a-z-\s]+):/g, "e.$1>=", /max-([a-z-\s]+):/g, "e.$1<=", /calc([^)]+)/g, "($1)", /(\d+[\.]*[\d]*)([a-z]+)/g, "($1 * e.$2)", /^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/gi, ""); 78 | }); 79 | return function(css, length) { 80 | var parsedLength; 81 | if (!(css in cssCache)) if (cssCache[css] = !1, length && (parsedLength = css.match(regLength))) cssCache[css] = parsedLength[1] * units[parsedLength[2]]; else try { 82 | cssCache[css] = new Function("e", buidlStr(css))(units); 83 | } catch (e) {} 84 | return cssCache[css]; 85 | }; 86 | }(), setResolution = function(candidate, sizesattr) { 87 | return candidate.w ? (candidate.cWidth = ri.calcListLength(sizesattr || "100vw"), 88 | candidate.res = candidate.w / candidate.cWidth) : candidate.res = candidate.x, candidate; 89 | }, respimage = function(opt) { 90 | var elements, i, plen, options = opt || {}; 91 | if (options.elements && 1 == options.elements.nodeType && ("IMG" == options.elements.nodeName.toUpperCase() ? options.elements = [ options.elements ] : (options.context = options.elements, 92 | options.elements = null)), options.reparse && (options.reevaluate = !0, window.console && console.warn && console.warn("reparse was renamed to reevaluate!")), 93 | elements = options.elements || ri.qsa(options.context || document, options.reevaluate || options.reselect ? ri.sel : ri.selShort), 94 | plen = elements.length) { 95 | for (ri.setupRun(options), alreadyRun = !0, i = 0; plen > i; i++) ri.fillImg(elements[i], options); 96 | ri.teardownRun(options); 97 | } 98 | }, parseDescriptor = memoize(function(descriptor) { 99 | var descriptorObj = [ 1, "x" ], parsedDescriptor = trim(descriptor || ""); 100 | return parsedDescriptor && (parsedDescriptor = parsedDescriptor.replace(regHDesc, ""), 101 | descriptorObj = parsedDescriptor.match(regDescriptor) ? [ 1 * RegExp.$1, RegExp.$2 ] : !1), 102 | descriptorObj; 103 | }); 104 | if (curSrcProp in image || (curSrcProp = "src"), types["image/jpeg"] = !0, types["image/gif"] = !0, 105 | types["image/png"] = !0, types["image/svg+xml"] = document.implementation.hasFeature("http://wwwindow.w3.org/TR/SVG11/feature#Image", "1.1"), 106 | ri.ns = ("ri" + new Date().getTime()).substr(0, 9), ri.supSrcset = "srcset" in image, 107 | ri.supSizes = "sizes" in image, ri.supPicture = !!window.HTMLPictureElement, ri.supSrcset && ri.supPicture && !ri.supSizes && !function(image2) { 108 | image.srcset = "data:,a", image2.src = "data:,a", ri.supSrcset = image.complete === image2.complete, 109 | ri.supPicture = ri.supSrcset && ri.supPicture; 110 | }(document.createElement("img")), ri.selShort = "picture>img,img[srcset]", ri.sel = ri.selShort, 111 | ri.cfg = cfg, ri.supSrcset && (ri.sel += ",img[" + srcsetAttr + "]"), ri.DPR = DPR || 1, 112 | ri.u = units, ri.types = types, alwaysCheckWDescriptor = ri.supSrcset && !ri.supSizes, 113 | ri.setSize = noop, ri.makeUrl = memoize(function(src) { 114 | return anchor.href = src, anchor.href; 115 | }), ri.qsa = function(context, sel) { 116 | return context.querySelectorAll(sel); 117 | }, ri.matchesMedia = function() { 118 | return ri.matchesMedia = window.matchMedia && (matchMedia("(min-width: 0.1em)") || {}).matches ? function(media) { 119 | return !media || matchMedia(media).matches; 120 | } : ri.mMQ, ri.matchesMedia.apply(this, arguments); 121 | }, ri.mMQ = function(media) { 122 | return media ? evalCSS(media) : !0; 123 | }, ri.calcLength = function(sourceSizeValue) { 124 | var value = evalCSS(sourceSizeValue, !0) || !1; 125 | return 0 > value && (value = !1), value; 126 | }, ri.supportsType = function(type) { 127 | return type ? types[type] : !0; 128 | }, ri.parseSize = memoize(function(sourceSizeStr) { 129 | var match = (sourceSizeStr || "").match(regSize); 130 | return { 131 | media: match && match[1], 132 | length: match && match[2] 133 | }; 134 | }), ri.parseSet = function(set) { 135 | if (!set.cands) { 136 | var pos, url, descriptor, last, descpos, can, srcset = set.srcset; 137 | for (set.cands = []; srcset; ) srcset = srcset.replace(/^\s+/g, ""), pos = srcset.search(/\s/g), 138 | descriptor = null, -1 != pos ? (url = srcset.slice(0, pos), last = url.charAt(url.length - 1), 139 | "," != last && url || (url = url.replace(/,+$/, ""), descriptor = ""), srcset = srcset.slice(pos + 1), 140 | null == descriptor && (descpos = srcset.indexOf(","), -1 != descpos ? (descriptor = srcset.slice(0, descpos), 141 | srcset = srcset.slice(descpos + 1)) : (descriptor = srcset, srcset = ""))) : (url = srcset, 142 | srcset = ""), url && (descriptor = parseDescriptor(descriptor)) && (can = { 143 | url: url.replace(/^,+/, ""), 144 | set: set 145 | }, can[descriptor[1]] = descriptor[0], "x" == descriptor[1] && 1 == descriptor[0] && (set.has1x = !0), 146 | set.cands.push(can)); 147 | } 148 | return set.cands; 149 | }, ri.getEmValue = function() { 150 | var body; 151 | if (!eminpx && (body = document.body)) { 152 | var div = document.createElement("div"), originalHTMLCSS = docElem.style.cssText, originalBodyCSS = body.style.cssText; 153 | div.style.cssText = baseStyle, docElem.style.cssText = fsCss, body.style.cssText = fsCss, 154 | body.appendChild(div), eminpx = div.offsetWidth, body.removeChild(div), eminpx = parseFloat(eminpx, 10), 155 | docElem.style.cssText = originalHTMLCSS, body.style.cssText = originalBodyCSS; 156 | } 157 | return eminpx || 16; 158 | }, ri.calcListLength = function(sourceSizeListStr) { 159 | if (!(sourceSizeListStr in sizeLengthCache) || cfg.uT) { 160 | var sourceSize, parsedSize, length, media, i, len, sourceSizeList = trim(sourceSizeListStr).split(/\s*,\s*/), winningLength = !1; 161 | for (i = 0, len = sourceSizeList.length; len > i && (sourceSize = sourceSizeList[i], 162 | parsedSize = ri.parseSize(sourceSize), length = parsedSize.length, media = parsedSize.media, 163 | !length || !ri.matchesMedia(media) || (winningLength = ri.calcLength(length)) === !1); i++) ; 164 | sizeLengthCache[sourceSizeListStr] = winningLength ? winningLength : units.width; 165 | } 166 | return sizeLengthCache[sourceSizeListStr]; 167 | }, ri.setRes = function(set) { 168 | var candidates; 169 | if (set) { 170 | candidates = ri.parseSet(set); 171 | for (var i = 0, len = candidates.length; len > i; i++) setResolution(candidates[i], set.sizes); 172 | } 173 | return candidates; 174 | }, ri.setRes.res = setResolution, ri.applySetCandidate = function(candidates, img) { 175 | if (candidates.length) { 176 | var candidate, dpr, i, j, diff, length, bestCandidate, curSrc, curCan, isSameSet, candidateSrc, abortCurSrc, oldRes, imageData = img[ri.ns], evaled = evalID, lazyF = lazyFactor, sub = substractCurRes; 177 | if (curSrc = imageData.curSrc || img[curSrcProp], curCan = imageData.curCan || setSrcToCur(img, curSrc, candidates[0].set), 178 | dpr = ri.DPR, oldRes = curCan && curCan.res, !bestCandidate && curSrc && (abortCurSrc = supportAbort && !img.complete && curCan && oldRes - .2 > dpr, 179 | abortCurSrc || curCan && !(tMemory > oldRes) || (curCan && dpr > oldRes && oldRes > lowTreshHold && (partialLowTreshHold > oldRes && (lazyF *= .8, 180 | sub += .04 * dpr), curCan.res += lazyF * Math.pow(oldRes - sub, 2)), isSameSet = !imageData.pic || curCan && curCan.set == candidates[0].set, 181 | curCan && isSameSet && curCan.res >= dpr && (bestCandidate = curCan))), !bestCandidate) for (oldRes && (curCan.res = curCan.res - (curCan.res - oldRes) / 2), 182 | candidates.sort(ascendingSort), length = candidates.length, bestCandidate = candidates[length - 1], 183 | i = 0; length > i; i++) if (candidate = candidates[i], candidate.res >= dpr) { 184 | j = i - 1, bestCandidate = candidates[j] && (diff = candidate.res - dpr) && (abortCurSrc || curSrc != ri.makeUrl(candidate.url)) && chooseLowRes(candidates[j].res, diff, dpr) ? candidates[j] : candidate; 185 | break; 186 | } 187 | return oldRes && (curCan.res = oldRes), bestCandidate && (candidateSrc = ri.makeUrl(bestCandidate.url), 188 | imageData.curSrc = candidateSrc, imageData.curCan = bestCandidate, candidateSrc != curSrc && ri.setSrc(img, bestCandidate), 189 | ri.setSize(img)), evaled; 190 | } 191 | }, ri.setSrc = function(img, bestCandidate) { 192 | var origStyle; 193 | img.src = bestCandidate.url, reflowBug && (origStyle = img.style.zoom, img.style.zoom = "0.999", 194 | img.style.zoom = origStyle); 195 | }, ri.getSet = function(img) { 196 | var i, set, supportsType, match = !1, sets = img[ri.ns].sets; 197 | for (i = 0; i < sets.length && !match; i++) if (set = sets[i], set.srcset && ri.matchesMedia(set.media) && (supportsType = ri.supportsType(set.type))) { 198 | "pending" == supportsType && (set = supportsType), match = set; 199 | break; 200 | } 201 | return match; 202 | }, ri.parseSets = function(element, parent, options) { 203 | var srcsetAttribute, imageSet, isWDescripor, srcsetParsed, hasPicture = "PICTURE" == parent.nodeName.toUpperCase(), imageData = element[ri.ns]; 204 | (imageData.src === undefined || options.src) && (imageData.src = getImgAttr.call(element, "src"), 205 | imageData.src ? setImgAttr.call(element, srcAttr, imageData.src) : removeImgAttr.call(element, srcAttr)), 206 | (imageData.srcset === undefined || !ri.supSrcset || element.srcset || options.srcset) && (srcsetAttribute = getImgAttr.call(element, "srcset"), 207 | imageData.srcset = srcsetAttribute, srcsetParsed = !0), imageData.sets = [], hasPicture && (imageData.pic = !0, 208 | getAllSourceElements(parent, imageData.sets)), imageData.srcset ? (imageSet = { 209 | srcset: imageData.srcset, 210 | sizes: getImgAttr.call(element, "sizes") 211 | }, imageData.sets.push(imageSet), isWDescripor = (alwaysCheckWDescriptor || imageData.src) && regWDesc.test(imageData.srcset || ""), 212 | isWDescripor || !imageData.src || getCandidateForSrc(imageData.src, imageSet) || imageSet.has1x || (imageSet.srcset += ", " + imageData.src, 213 | imageSet.cands.push({ 214 | url: imageData.src, 215 | x: 1, 216 | set: imageSet 217 | }))) : imageData.src && imageData.sets.push({ 218 | srcset: imageData.src, 219 | sizes: null 220 | }), imageData.curCan = null, imageData.curSrc = undefined, imageData.supported = !(hasPicture || imageSet && !ri.supSrcset || isWDescripor), 221 | srcsetParsed && ri.supSrcset && !imageData.supported && (srcsetAttribute ? (setImgAttr.call(element, srcsetAttr, srcsetAttribute), 222 | element.srcset = "") : removeImgAttr.call(element, srcsetAttr)), imageData.supported && !imageData.srcset && (!imageData.src && element.src || element.src != ri.makeUrl(imageData.src)) && (null == imageData.src ? element.removeAttribute("src") : element.src = imageData.src), 223 | imageData.parsed = !0; 224 | }, ri.fillImg = function(element, options) { 225 | var parent, imageData, extreme = options.reselect || options.reevaluate; 226 | if (element[ri.ns] || (element[ri.ns] = {}), imageData = element[ri.ns], extreme || imageData.evaled != evalID) { 227 | if (!imageData.parsed || options.reevaluate) { 228 | if (parent = element.parentNode, !parent) return; 229 | ri.parseSets(element, parent, options); 230 | } 231 | imageData.supported ? imageData.evaled = evalID : applyBestCandidate(element); 232 | } 233 | }, ri.setupRun = function(options) { 234 | (!alreadyRun || isVwDirty || DPR != window.devicePixelRatio) && (updateMetrics(), 235 | options.elements || options.context || clearTimeout(resizeThrottle)); 236 | }, ri.supPicture ? (respimage = noop, ri.fillImg = noop) : (document.createElement("picture"), 237 | function() { 238 | var isDomReady, regReady = window.attachEvent ? /d$|^c/ : /d$|^c|^i/, run = function() { 239 | var readyState = document.readyState || ""; 240 | timerId = setTimeout(run, "loading" == readyState ? 200 : 999), document.body && (isDomReady = isDomReady || regReady.test(readyState), 241 | ri.fillImgs(), isDomReady && clearTimeout(timerId)); 242 | }, resizeEval = function() { 243 | ri.fillImgs(); 244 | }, onResize = function() { 245 | clearTimeout(resizeThrottle), isVwDirty = !0, resizeThrottle = setTimeout(resizeEval, 99); 246 | }, timerId = setTimeout(run, document.body ? 0 : 20); 247 | on(window, "resize", onResize), on(document, "readystatechange", run); 248 | }()), ri.respimage = respimage, ri.fillImgs = respimage, ri.teardownRun = noop, 249 | respimage._ = ri, window.respimage = window.picturefill || respimage, !window.picturefill) for (window.respimgCFG = { 250 | ri: ri, 251 | push: function(args) { 252 | var name = args.shift(); 253 | "function" == typeof ri[name] ? ri[name].apply(ri, args) : (cfg[name] = args[0], 254 | alreadyRun && ri.fillImgs({ 255 | reselect: !0 256 | })); 257 | } 258 | }; setOptions && setOptions.length; ) window.respimgCFG.push(setOptions.shift()); 259 | window.picturefill || (window.picturefill = window.respimage, window.picturefillCFG || (window.picturefillCFG = window.respimgCFG)); 260 | }(window, document); -------------------------------------------------------------------------------- /respimage.min.js: -------------------------------------------------------------------------------- 1 | /*! respimage - v1.4.2 - 2015-08-22 2 | Licensed MIT */ 3 | !function(a,b,c){"use strict";function d(a){return a.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}function e(){var b;R=!1,U=a.devicePixelRatio,S={},T={},b=(U||1)*D.xQuant,D.uT||(D.maxX=Math.max(1.3,D.maxX),b=Math.min(b,D.maxX),v.DPR=b),V.width=Math.max(a.innerWidth||0,B.clientWidth),V.height=Math.max(a.innerHeight||0,B.clientHeight),V.vw=V.width/100,V.vh=V.height/100,V.em=v.getEmValue(),V.rem=V.em,o=D.lazyFactor/2,o=o*b+o,q=.4+.1*b,l=.5+.2*b,m=.5+.25*b,p=b+1.3,(n=V.width>V.height)||(o*=.9),I&&(o*=.9),u=[V.width,V.height,b].join("-")}function f(a,b,c){var d=b*Math.pow(a-.4,1.9);return n||(d/=1.3),a+=d,a>c}function g(a){var b,c=v.getSet(a),d=!1;"pending"!=c&&(d=u,c&&(b=v.setRes(c),d=v.applySetCandidate(b,a))),a[v.ns].evaled=d}function h(a,b){return a.res-b.res}function i(a,b,c){var d;return!c&&b&&(c=a[v.ns].sets,c=c&&c[c.length-1]),d=j(b,c),d&&(b=v.makeUrl(b),a[v.ns].curSrc=b,a[v.ns].curCan=d,d.res||_(d,d.set.sizes)),d}function j(a,b){var c,d,e;if(a&&b)for(e=v.parseSet(b),a=v.makeUrl(a),c=0;cc;c++)e=g[c],e[v.ns]=!0,f=e.getAttribute("srcset"),f&&b.push({srcset:f,media:e.getAttribute("media"),type:e.getAttribute("type"),sizes:e.getAttribute("sizes")})}var l,m,n,o,p,q,r,s,t,u,v={},w=function(){},x=b.createElement("img"),y=x.getAttribute,z=x.setAttribute,A=x.removeAttribute,B=b.documentElement,C={},D={xQuant:1,lazyFactor:.4,maxX:2},E="data-pfsrc",F=E+"set",G="webkitBackfaceVisibility"in B.style,H=navigator.userAgent,I=/rident/.test(H)||/ecko/.test(H)&&H.match(/rv\:(\d+)/)&&RegExp.$1>35,J="currentSrc",K=/\s+\+?\d+(e\d+)?w/,L=/((?:\([^)]+\)(?:\s*and\s*|\s*or\s*|\s*not\s*)?)+)?\s*(.+)/,M=/^([\+eE\d\.]+)(w|x)$/,N=/\s*\d+h\s*/,O=a.respimgCFG,P=("https:"==location.protocol,"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)"),Q="font-size:100%!important;",R=!0,S={},T={},U=a.devicePixelRatio,V={px:1,"in":96},W=b.createElement("a"),X=!1,Y=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d||!1):a.attachEvent&&a.attachEvent("on"+b,c)},Z=function(a){var b={};return function(c){return c in b||(b[c]=a(c)),b[c]}},$=function(){var a=/^([\d\.]+)(em|vw|px)$/,b=function(){for(var a=arguments,b=0,c=a[0];++b in a;)c=c.replace(a[b],a[++b]);return c},c=Z(function(a){return"return "+b((a||"").toLowerCase(),/\band\b/g,"&&",/,/g,"||",/min-([a-z-\s]+):/g,"e.$1>=",/max-([a-z-\s]+):/g,"e.$1<=",/calc([^)]+)/g,"($1)",/(\d+[\.]*[\d]*)([a-z]+)/g,"($1 * e.$2)",/^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/gi,"")});return function(b,d){var e;if(!(b in S))if(S[b]=!1,d&&(e=b.match(a)))S[b]=e[1]*V[e[2]];else try{S[b]=new Function("e",c(b))(V)}catch(f){}return S[b]}}(),_=function(a,b){return a.w?(a.cWidth=v.calcListLength(b||"100vw"),a.res=a.w/a.cWidth):a.res=a.x,a},ab=function(c){var d,e,f,g=c||{};if(g.elements&&1==g.elements.nodeType&&("IMG"==g.elements.nodeName.toUpperCase()?g.elements=[g.elements]:(g.context=g.elements,g.elements=null)),g.reparse&&(g.reevaluate=!0,a.console&&console.warn&&console.warn("reparse was renamed to reevaluate!")),d=g.elements||v.qsa(g.context||b,g.reevaluate||g.reselect?v.sel:v.selShort),f=d.length){for(v.setupRun(g),X=!0,e=0;f>e;e++)v.fillImg(d[e],g);v.teardownRun(g)}},bb=Z(function(a){var b=[1,"x"],c=d(a||"");return c&&(c=c.replace(N,""),b=c.match(M)?[1*RegExp.$1,RegExp.$2]:!1),b});if(J in x||(J="src"),C["image/jpeg"]=!0,C["image/gif"]=!0,C["image/png"]=!0,C["image/svg+xml"]=b.implementation.hasFeature("http://wwwindow.w3.org/TR/SVG11/feature#Image","1.1"),v.ns=("ri"+(new Date).getTime()).substr(0,9),v.supSrcset="srcset"in x,v.supSizes="sizes"in x,v.supPicture=!!a.HTMLPictureElement,v.supSrcset&&v.supPicture&&!v.supSizes&&!function(a){x.srcset="data:,a",a.src="data:,a",v.supSrcset=x.complete===a.complete,v.supPicture=v.supSrcset&&v.supPicture}(b.createElement("img")),v.selShort="picture>img,img[srcset]",v.sel=v.selShort,v.cfg=D,v.supSrcset&&(v.sel+=",img["+F+"]"),v.DPR=U||1,v.u=V,v.types=C,s=v.supSrcset&&!v.supSizes,v.setSize=w,v.makeUrl=Z(function(a){return W.href=a,W.href}),v.qsa=function(a,b){return a.querySelectorAll(b)},v.matchesMedia=function(){return v.matchesMedia=a.matchMedia&&(matchMedia("(min-width: 0.1em)")||{}).matches?function(a){return!a||matchMedia(a).matches}:v.mMQ,v.matchesMedia.apply(this,arguments)},v.mMQ=function(a){return a?$(a):!0},v.calcLength=function(a){var b=$(a,!0)||!1;return 0>b&&(b=!1),b},v.supportsType=function(a){return a?C[a]:!0},v.parseSize=Z(function(a){var b=(a||"").match(L);return{media:b&&b[1],length:b&&b[2]}}),v.parseSet=function(a){if(!a.cands){var b,c,d,e,f,g,h=a.srcset;for(a.cands=[];h;)h=h.replace(/^\s+/g,""),b=h.search(/\s/g),d=null,-1!=b?(c=h.slice(0,b),e=c.charAt(c.length-1),","!=e&&c||(c=c.replace(/,+$/,""),d=""),h=h.slice(b+1),null==d&&(f=h.indexOf(","),-1!=f?(d=h.slice(0,f),h=h.slice(f+1)):(d=h,h=""))):(c=h,h=""),c&&(d=bb(d))&&(g={url:c.replace(/^,+/,""),set:a},g[d[1]]=d[0],"x"==d[1]&&1==d[0]&&(a.has1x=!0),a.cands.push(g))}return a.cands},v.getEmValue=function(){var a;if(!r&&(a=b.body)){var c=b.createElement("div"),d=B.style.cssText,e=a.style.cssText;c.style.cssText=P,B.style.cssText=Q,a.style.cssText=Q,a.appendChild(c),r=c.offsetWidth,a.removeChild(c),r=parseFloat(r,10),B.style.cssText=d,a.style.cssText=e}return r||16},v.calcListLength=function(a){if(!(a in T)||D.uT){var b,c,e,f,g,h,i=d(a).split(/\s*,\s*/),j=!1;for(g=0,h=i.length;h>g&&(b=i[g],c=v.parseSize(b),e=c.length,f=c.media,!e||!v.matchesMedia(f)||(j=v.calcLength(e))===!1);g++);T[a]=j?j:V.width}return T[a]},v.setRes=function(a){var b;if(a){b=v.parseSet(a);for(var c=0,d=b.length;d>c;c++)_(b[c],a.sizes)}return b},v.setRes.res=_,v.applySetCandidate=function(a,b){if(a.length){var c,d,e,g,j,k,n,r,s,t,w,x,y,z=b[v.ns],A=u,B=o,C=q;if(r=z.curSrc||b[J],s=z.curCan||i(b,r,a[0].set),d=v.DPR,y=s&&s.res,!n&&r&&(x=I&&!b.complete&&s&&y-.2>d,x||s&&!(p>y)||(s&&d>y&&y>l&&(m>y&&(B*=.8,C+=.04*d),s.res+=B*Math.pow(y-C,2)),t=!z.pic||s&&s.set==a[0].set,s&&t&&s.res>=d&&(n=s))),!n)for(y&&(s.res=s.res-(s.res-y)/2),a.sort(h),k=a.length,n=a[k-1],e=0;k>e;e++)if(c=a[e],c.res>=d){g=e-1,n=a[g]&&(j=c.res-d)&&(x||r!=v.makeUrl(c.url))&&f(a[g].res,j,d)?a[g]:c;break}return y&&(s.res=y),n&&(w=v.makeUrl(n.url),z.curSrc=w,z.curCan=n,w!=r&&v.setSrc(b,n),v.setSize(b)),A}},v.setSrc=function(a,b){var c;a.src=b.url,G&&(c=a.style.zoom,a.style.zoom="0.999",a.style.zoom=c)},v.getSet=function(a){var b,c,d,e=!1,f=a[v.ns].sets;for(b=0;b