├── 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 |
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 |
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 |
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 |
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 |
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 |
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 |
261 |
262 |
263 |
264 |
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 |
101 |
102 |
Abandoned Boat
103 |
by William Warby
104 |
105 |
106 |
107 |
108 |
122 |
123 |
Desert Road
124 |
by William Warby
125 |
126 |
127 |
128 |
139 |
140 |
141 |
by Micah Sheldon
142 |
143 |
144 |
145 |
146 |
159 |
160 |
@ The Desert Tortoise Natural Area
161 |
by
162 |
163 |
164 |
165 |
174 |
175 |
Woman in water
176 |
by Ton Haex
177 |
178 |
179 |
180 |
181 |
189 |
190 |
Borobudur
191 |
by Scott Anderson
192 |
193 |
194 |
195 |
196 |
197 |
207 |
208 |
A tree in the blue
209 |
by Sergio
210 |
211 |
212 |
213 |
214 |
224 |
225 |
Windows on Istanbul
226 |
by robin robokow
227 |
228 |
229 |
230 |
231 |
232 |
243 |
244 |
Goldie Dawn
245 |
by
246 |
247 |
248 |
249 |
250 |
259 |
260 |
Sant Miquel del Fai
261 |
by Jaume Meneses
262 |
263 |
264 |
265 |
266 |
276 |
277 |
Avebury Stone Circle
278 |
by Erinc Salor
279 |
280 |
281 |
282 |
283 |
294 |
295 |
el castil de tierra
296 |
by Mario Antonio Pena Zapatería
297 |
298 |
299 |
300 |
301 |
315 |
316 |
sunset
317 |
by Angela Marie
318 |
319 |
320 |
321 |
322 |
336 |
337 |
Sky and earth
338 |
by Angela Marie
339 |
340 |
341 |
342 |
343 |
353 |
354 |
Missing Ulsoor lake (Explore)
355 |
by Swaminathan
356 |
357 |
358 |
359 |
360 |
361 |
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 |
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 |
39 |
40 | ```
41 | ####Serve higher compressions to retina devices
42 | ```html
43 |
44 |
45 |
56 |
57 |
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 |
62 |
63 |
64 |
65 |
66 |
67 |
74 |
81 |
82 |
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