Repository: aFarkas/lazysizes
Branch: gh-pages
Commit: 1523a4ff4579
Files: 80
Total size: 581.7 KB
Directory structure:
gitextract__71k464b/
├── BingSiteAuth.xml
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── animate.html
├── assets/
│ ├── css/
│ │ ├── bootstrap-theme.css
│ │ ├── bootstrap.css
│ │ ├── carousel.css
│ │ └── tidy.css
│ └── js/
│ └── bootstrap.js
├── bower.json
├── component.json
├── index.html
├── lazysizes-umd.js
├── lazysizes.d.ts
├── lazysizes.js
├── no-src.html
├── optimumx/
│ ├── child.html
│ ├── index.html
│ └── js/
│ └── parent.js
├── package.json
├── plugins/
│ ├── README.md
│ ├── artdirect/
│ │ ├── README.md
│ │ └── ls.artdirect.js
│ ├── aspectratio/
│ │ ├── README.md
│ │ └── ls.aspectratio.js
│ ├── attrchange/
│ │ ├── README.md
│ │ └── ls.attrchange.js
│ ├── bgset/
│ │ ├── README.md
│ │ └── ls.bgset.js
│ ├── blur-up/
│ │ ├── README.md
│ │ └── ls.blur-up.js
│ ├── custommedia/
│ │ ├── README.md
│ │ └── ls.custommedia.js
│ ├── fix-edge-h-descriptor/
│ │ ├── README.md
│ │ └── ls.fix-edge-h-descriptor.js
│ ├── fix-ios-sizes/
│ │ └── fix-ios-sizes.js
│ ├── include/
│ │ ├── README.md
│ │ └── ls.include.js
│ ├── native-loading/
│ │ ├── README.md
│ │ └── ls.native-loading.js
│ ├── noscript/
│ │ ├── README.md
│ │ └── ls.noscript.js
│ ├── object-fit/
│ │ ├── README.md
│ │ └── ls.object-fit.js
│ ├── optimumx/
│ │ ├── README.md
│ │ └── ls.optimumx.js
│ ├── parent-fit/
│ │ ├── README.md
│ │ └── ls.parent-fit.js
│ ├── print/
│ │ ├── README.md
│ │ └── ls.print.js
│ ├── progressive/
│ │ ├── README.md
│ │ └── ls.progressive.js
│ ├── respimg/
│ │ ├── README.md
│ │ └── ls.respimg.js
│ ├── rias/
│ │ ├── README.md
│ │ └── ls.rias.js
│ ├── static-gecko-picture/
│ │ └── ls.static-gecko-picture.js
│ ├── twitter/
│ │ └── ls.twitter.js
│ ├── unload/
│ │ ├── README.md
│ │ └── ls.unload.js
│ ├── unveilhooks/
│ │ ├── README.md
│ │ └── ls.unveilhooks.js
│ └── video-embed/
│ ├── README.md
│ └── ls.video-embed.js
├── rias/
│ └── index.html
├── src/
│ ├── common.wrapper
│ ├── lazysizes-core.js
│ ├── lazysizes-intersection.js
│ └── umd.wrapper
├── tests/
│ ├── functional-tests-plugins.js
│ ├── functional-tests.js
│ ├── index.html
│ ├── test-files/
│ │ ├── content-file.html
│ │ └── matchMedia.js
│ └── test-helper.js
├── tsconfig.json
└── types/
├── global.d.ts
└── lazysizes-config.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: BingSiteAuth.xml
================================================
D45AA4276907D8A86808F0D7C84BC7BC
================================================
FILE: CHANGELOG.md
================================================
# Changelog
### 5.3.1
- Added basic Typescript support
### 5.3.0
- BGSet: Support adding multiple images/backgrounds through `event.detail.fullSrc` ([#827](https://github.com/aFarkas/lazysizes/issues/827))
- RiaS plugin: Use `source` based config in case of `picture` element usage ([#764](https://github.com/aFarkas/lazysizes/issues/831))
- BlurUp plugin: Added possibility to change classNames ([#814](https://github.com/aFarkas/lazysizes/pull/814))
- Core: Added `iframeLoadMode` option (see [#810](https://github.com/aFarkas/lazysizes/pull/810))
## 5.2.2
- Add correct AMD module pattern for plugins.
## 5.2.1
- BlurUp-Plugin: Copy style attribute ([#764](https://github.com/aFarkas/lazysizes/pull/764))
- Fixes minor security issue with video-embed plugin ([#764](https://github.com/aFarkas/lazysizes/pull/764))
- Built: Update dependencies ([#774](https://github.com/aFarkas/lazysizes/pull/774), [#756](https://github.com/aFarkas/lazysizes/pull/756))
- Fixes diverse issues with old AMD module pattern ([#780](https://github.com/aFarkas/lazysizes/pull/780), [#779](https://github.com/aFarkas/lazysizes/pull/779))
## 5.2.0
* Fix wrong window context under very rare SSR (fixes [#717](https://github.com/aFarkas/lazysizes/pull/717))
* Fix Safari Back-Forward Cache issue with lazyloading image elements (fixes [#711](https://github.com/aFarkas/lazysizes/issues/711))
* Add lazyload of autoplay videos to unveilhooks (fixes [#697](https://github.com/aFarkas/lazysizes/issues/697))
## 5.1.2
* Fix visibility check (fixes [#709](https://github.com/aFarkas/lazysizes/issues/709))
## 5.1.1
* Fix ratio calculation in rias plugin (fixed in [#685](https://github.com/aFarkas/lazysizes/pull/685) by [tikotzky](https://github.com/tikotzky))
* Make thumb size for youtube poster image in video-embed plugin configurable (see [#681](https://github.com/aFarkas/lazysizes/pull/681) thx to [@nikitasol](https://github.com/nikitasol))
## 5.1.0
* Allow import/execution in node environment
* Use "hqdefault" for youtube poster image in video-embed plugin fixes [#666](https://github.com/aFarkas/lazysizes/issues/666)
## 5.0.0
* Use `width`/`height` content attributes to detect physical aspect ratio of image candidates, if `data-aspectratio` and `h`/`w` descriptors are missing. [#642](https://github.com/aFarkas/lazysizes/issues/642)
* Do not leak global `lazySizesConfig` anymore fixes [#647](https://github.com/aFarkas/lazysizes/issues/647)
* Improve handling of cloned object-fit images fixes [#648](https://github.com/aFarkas/lazysizes/issues/648)
* Improve blur-up/effect plugin.
* Add support for native `loading="lazy"` feature as a [native loading plugin](https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/native-loading).
## 4.1.8
* Added the class `ls-is-cached` to already cached images.
* Added h descriptor parsing fix plugin for MS edge (was already included in respimg polyfill.)
* Effects-Plugin/Blur Up plugin: Remove [].find because IE..., fixes [#631](https://github.com/aFarkas/lazysizes/issues/631)
* Documentation stuff
* Bring back *.min.js files to npm package, but don't use them in your `import`/`require`. These are mostly for CDNs. Not for Common JS bundlers.
## 4.1.7
* Blur Up plugin: make blur up mode configurable by script
* Unload Plugin: Fix unload plugin not knowing current expand, fixes [#608](https://github.com/aFarkas/lazysizes/issues/608)
* simplify resetPreloading and switchLoadingClass, fixes [#614](https://github.com/aFarkas/lazysizes/issues/614)
## 4.1.6
* Several Readme fixes
* Allow expand, hFax and expFactor to be changed after initialization, see [#581](https://github.com/aFarkas/lazysizes/issues/581)
## 4.1.5
* Blur Up plugin: Add an empty alt attribute to the blur image to satisfy a11y [c3256d6](https://github.com/aFarkas/lazysizes/commit/c3256d61c002a984ab3e644e922b0fdc052519d8)
* Blur Up plugin: added aria-hidden attribute [1d62efb](https://github.com/aFarkas/lazysizes/commit/1d62efb352f579d4505bd3d76d8166db2db9481f)
* RiaS plugin: fix wrong ratio calculation, fixes [#550](https://github.com/aFarkas/lazysizes/issues/550)
* Rias Plugin: add aspect-ratio to rias for calculating height, fixes [#557](https://github.com/aFarkas/lazysizes/issues/557)
## 4.1.4
* Resolve race condition with blurImg [dffa93b](https://github.com/aFarkas/lazysizes/commit/dffa93b804302363aceb7dc814b01629014ed03b)
* make intersectionobserver version compatible with plugins [2f1a025](https://github.com/aFarkas/lazysizes/commit/2f1a02531eb96e828d42fb7877e776b810d7f346)
## 4.1.3
* change from custom to basic event interface (maybe fixes [#520](https://github.com/aFarkas/lazysizes/issues/527))
* Clarify data-aspectratio attribute [d868605](https://github.com/aFarkas/lazysizes/commit/d8686050adeb68aae14e522bed12d68ab00b7595)
## 4.1.2
* fixes race condition with blurupimg [#527](https://github.com/aFarkas/lazysizes/issues/527)
* add proxy change event to extend bgset [#532](https://github.com/aFarkas/lazysizes/issues/532)
## 4.1.1
* See [3ace9f3](https://github.com/aFarkas/lazysizes/commit/3ace9f359617409fe2824311032439fcf76a7c99)
## 4.1.0
* improve effect plugin
## 4.0.4
* fixes issue in bgset introduced with version 4.0.3
## 4.0.3
* add [blur up plugin](https://jsfiddle.net/trixta/v0oq0412/embedded/result/)
## 4.0.0
* make all plugins CommonJS compatible (thx to @claudiobmgrtnr and @jantimon)
* added `loadHidden` option(thx to @justinvoelker)
* added artdirection plugin (no documentation yet, but great)
* iOS 8.1 fixes has to be loaded explicitly in non CommonJS environments (not included in respimg plugin anymore)
* removed `picture` support for old FF 38-
## 2.0.0
* lazysizes core:
* heavily improved performance (`requestIdleCallback`, better debouncing and a lot more).
* plugins:
* new plugin: [**object fit polyfill**](plugins/object-fit).
* improved new options for [parent-fit plugin](plugins/parent-fit).
## 1.5.0
Breaking change:
* the lazysizes.js and lazysizes.min.js files do not register as AMD modules anymore, if you need an AMD module use the new lazysizes-umd.js or lazysizes-umd.min.js file.
* lazysizes core:
* improved lazyloading in background tabs.
* fixed set lazyloaded class to early in FF.
* bgset/parentFit plugin:
* improved avoiding layout thrashing.
* respimg/bgset/parentFit plugin:
* fixed bug in Edge 14 to parse height descriptors correctly.
* unload plugin:
unload plugin was broken since version 1.4.0 (thanks to @hokamoto)
## 1.4.0
* lazysizes core:
* improved lazyloading in background tabs.
* improved avoiding layout thrashing
* support of SVG elements (`svg`/`image`/`use`...)
* bgset/parentFit plugin:
* improved avoiding layout thrashing
* rias (and bgset):
* added support for height calculation (thx to @LRancez, [#213](https://github.com/aFarkas/lazysizes/pull/213))
## 1.3.2
* lazysizes core:
* add `hFactor` option (see #181).
* unload plugin:
* simplified `unloadPixelThreshold` calculation.
* bgset plugin:
* add an empty alt to image (see #200).
## 1.3.1 version
* lazysizes core:
* replace `setImmediate` with `setTimeout` (improved performance for IE/EDGE).
* plugins:
* fixed conflict with respimg plugin in combination with bgset plugin, in case of art direction and resize to smaller.
* fixed conflict with RIaS plugin in combination with respimg plugin/picturefill in Safari 8/9.
================================================
FILE: Gruntfile.js
================================================
/*global module:true*/
(function () {
"use strict";
var pkg;
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: pkg = grunt.file.readJSON("package.json"),
banner: "/*! <%= pkg.name %> - v<%= pkg.version %> */\n",
// Task configuration.
uglify: {
options: {
banner: "<%= banner %>",
compress: {
dead_code: true
}
},
plugins: {
files: [
{
expand: true,
cwd: 'plugins/',
src: ['**/*.js', '!*.min.js', '!**/*.min.js'],
dest: 'plugins/',
ext: '.min.js',
extDot: 'last'
},
{
expand: true,
cwd: '',
src: ['lazysizes*.js', '!*.min.js'],
dest: '',
ext: '.min.js',
extDot: 'last'
}
]
}
},
jshint: {
all: {
options: {
jshintrc: true
},
src: [ "lazysizes.js", "plugins/**/*.js", "!*.min.js", "!plugins/**/*.min.js" ] //, "Gruntfile.js", "tests/*.js"
}
},
qunit: {
all: ['tests/*.html']
},
watch: {
gruntfile: {
files: [ "Gruntfile.js", "lazysizes.js" ],
tasks: [ "default" ]
}
},
bytesize: {
all: {
src: [ "lazysizes.min.js" ]
}
},
uncss: {
dist: {
files: {
'assets/css/tidy.css': ['index.html', 'maxdpr/*.html']
}
}
},
maxFilesize: {
options: {
// Task-specific options go here.
},
minified: {
options: {
maxBytes: (1024 * 7.8)
},
src: ["lazysizes.min.js"]
}
}
});
// These plugins provide necessary tasks.
grunt.loadNpmTasks("grunt-contrib-jshint");
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks('grunt-uncss');
grunt.loadNpmTasks('grunt-bytesize');
grunt.loadNpmTasks('grunt-max-filesize');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.registerTask('wrapcore', 'wraps lazysizes into umd and common wrapper.', function() {
var ls = grunt.file.read('src/lazysizes-core.js');
var common = grunt.file.read('src/common.wrapper');
var umd = grunt.file.read('src/umd.wrapper');
grunt.file.write('lazysizes.js', common.replace('{{ls}}', ls));
grunt.file.write('lazysizes-umd.js', umd.replace('{{ls}}', ls));
});
grunt.registerTask('importTs', 'import global typescript.', function() {
const fileName = './lazysizes.d.ts';
const importStr = `import './types/global';\n\n`;
const tsContent = grunt.file.read(fileName);
grunt.file.write(fileName, importStr + tsContent);
});
// Default task.
grunt.registerTask("default", [ "wrapcore", "test", "uglify", "bytesize", "maxFilesize" ]);
grunt.registerTask("test", [ "jshint" ]);
};
})();
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Alexander Farkas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# lazysizes
**lazysizes** is a fast (jank-free), SEO-friendly and self-initializing lazyloader for images (including responsive images ``picture``/``srcset``), iframes, scripts/widgets and much more. It also prioritizes resources by differentiating between crucial in view and near view elements to make perceived performance even faster.
It may become also your number one tool to integrate responsive images. It can automatically calculate the ``sizes`` attribute for your responsive images, it allows you to share media queries for your ``media`` attributes with your CSS, helping to separate layout (CSS) from content/structure (HTML) and it makes integrating responsive images into any environment really simple. It also includes a set of optional plugins to further extend its functionality.
## How to
1. Download the [lazysizes.min.js script](http://afarkas.github.io/lazysizes/lazysizes.min.js) and include **lazysizes** in your webpage. (Or install via npm: ``npm install lazysizes --save`` or bower ``bower install lazysizes --save``)
```html
```
Or:
```js
import 'lazysizes';
// import a plugin
import 'lazysizes/plugins/parent-fit/ls.parent-fit';
// Note: Never import/require the *.min.js files from the npm package.
```
Note: For more information see [here](#include-early).
2. lazysizes does not need any JS configuration: Add the ``class`` ``"lazyload"`` to your images/iframes in conjunction with a ``data-src`` and/or ``data-srcset`` attribute. Optionally you can also add a ``src`` attribute with a low quality image:
```html
```
```html
```
```html
```
## [Demo with code examples](http://afarkas.github.io/lazysizes/#examples)
Can be seen [here](http://afarkas.github.io/lazysizes/#examples)
## Responsive image support (picture and/or srcset)
Lazysizes is built upon the Responsive image standard and extends it with additional functionality. For full cross browser responsive image support you must use either a full polyfill like [picturefill](https://github.com/scottjehl/picturefill) or use the extreme lightweight partial [respimg polyfill plugin](plugins/respimg) or the [responsive image on demand plugin](plugins/rias). Alternatively, you can simply define a fallback src via the ``data-src`` attribute. If you want to learn more about the responsive image syntax read "[The anatomy of responsive images](https://jakearchibald.com/2015/anatomy-of-responsive-images/)".
## What makes lazysizes so awesome:
**lazysizes** is different than other lazy image loaders.
1. **Detects any visibility changes on current and future lazyload elements in any web environment automatically**: The script works as an universal, self-initializing, self-configuring and self-destroying component and detects any changes to the visibility of any current and future image/iframe elements automatically no matter whether it becomes visible through a user scroll, a CSS animation triggered through ``:hover`` or through any kind of JS behavior (carousel, slider, infinite scroll, masonry, isotope/filtering/sorting, AJAX, SPAs...). It also works automatically in conjunction with any kind of JS-/CSS-/Frontend-Framework (jQuery mobile, Bootstrap, Backbone, Angular, React, Ember (see also the [attrchange/re-initialization extension](plugins/attrchange))).
2. **Future-proof**: It directly includes standard responsive image support (``picture`` and ``srcset``)
3. **Separation of concerns**: For responsive image support it adds an automatic ``sizes`` calculation as also alias names for media queries feature. There is also no JS change needed if you add a scrollable container with CSS (overflow: auto) or create a mega menu containing images.
4. **Performance**: It's based on highly efficient, best practice code (runtime **and** network) to work jank-free at 60fps and can be used with hundreds of images/iframes on CSS and JS-heavy pages or webapps.
5. **Extendable**: It provides JS and CSS hooks to extend lazysizes with any kind of lazy loading, lazy instantiation, in view callbacks or effects (see also the [available plugins/snippets](#plugins)).
6. **Intelligent prefetch/Intelligent resource prioritization**: lazysizes prefetches/preloads near the view assets to improve user experience, but only while the browser network is idling (see also ``expand``, ``expFactor`` and ``loadMode`` options). This way in view elements are loaded faster and near of view images are preloaded lazily before they come into view.
7. **Lightweight, but mature solution**: lazysizes has the right balance between a lightweight and a fast, reliable solution
8. **SEO improved**: lazysizes does not hide images/assets from Google. No matter what markup pattern you use. Google doesn't scroll/interact with your website. lazysizes detects, whether the user agent is capable to scroll and if not, reveals all images instantly.
## More about the API
**lazysizes** comes with a simple markup and JS API. Normally you will only need to use the markup API.
### Markup API
Add the ``class`` ``lazyload`` to all ``img`` and ``iframe`` elements, which should be loaded lazy. *Instead* of a ``src`` or ``srcset`` attribute use a ``data-src`` or ``data-srcset`` attribute:
```html
```
#### Automatically setting the `sizes` attribute
**lazysizes** supports setting the ``sizes`` attribute automatically, corresponding to the current size of your image - just set the value of ``data-sizes`` to ``auto``.
```html
```
**Important: How ``sizes`` is calculated**: The automatic sizes calculation uses the display width of the image. This means that the width of the image has to be calculable at least approximately before the image itself is loaded (This means you can not use `width: auto`). Often the following general CSS rule might help: ``img[data-sizes="auto"] { display: block; width: 100%; }`` (see also [specifying image/iframe dimensions with the recommended aspect ratio definition](#specify-dimensions)). If it is below ``40`` (can be configured through the ``minSize`` option), lazysizes traverses up the DOM tree until it finds a parent which is over ``40`` and uses this number.
The width auto-calculated by lazysizes can be modified using the ``lazybeforesizes`` event ([lazybeforesizes documentation](#lazybeforesizes-documentation)). Alternatively, the [parent fit plugin](plugins/parent-fit) can be used for sizing images to fit a parent / container, and is the only solution when an image's height needs to be taken into account when fitting it to its container (This also includes the use of `object-fit`).
The ``data-sizes="auto"`` feature only makes sense if you use the ``data-srcset`` attribute with *width* descriptors which allows the most appropriate image can be selected (It does not make sense if you use the x descriptor or only ``src``.).
## Recommended/possible markup patterns
lazysizes allows you to write an endless variety of different markup patterns. Find your own/best pattern or choose one of the following. (All of the following patterns can be also used for art direction using the ``picture`` element.)
### Simple pattern
Add the class ``lazyload`` and simply omit the ``src`` attribute or add a data uri as fallback ``src``.
```html
```
Note: In case you are using either ``srcset``/``data-srcset`` or ``picture``, we recommend to extend this pattern with either a ``data-src`` (see next pattern: "Combine ``data-srcset`` with ``data-src``") or with a suitable ``src`` attribute (see: "modern pattern" or "LQIP").
### Combine ``data-srcset`` with ``data-src``
In case you want to use responsive images for supporting browsers, but don't want to include a polyfill, simply combine your ``data-srcset`` with a ``data-src`` attribute.
```html
```
Note: Due to the fact that the ``data-src`` will also be picked up by "Read-Later" Apps and other tools (for example Pin it button), this pattern also makes sense if you use a polyfill. In case you don't use a polyfill it is recommended that the first image candidate matches the fallback `src`.
### LQIP/blurry image placeholder/Blur up image technique
If you are using the LQIP (Low Quality Image Placeholder) pattern, simply add a low quality image as the ``src``:
```html
```
The LQIP technique can be enhanced by combining it with CSS transitions/animation to sharpen/unblur or overfade the LQIP image.
Please also have a look at our [lazysizes Blur Up plugin](https://jsfiddle.net/trixta/v0oq0412/embedded/result/) (recommended).
```html
```
### modern transparent ``srcset`` pattern
Combine a normal ``src`` attribute with a transparent or low quality image as ``srcset`` value and a ``data-srcset`` attribute. This way modern browsers will lazy load without loading the ``src`` attribute and all others will simply fallback to the initial ``src`` attribute (without lazyload). (This nice pattern originated from @ivopetkov.)
```html
```
### The noscript pattern
In case disabled JavaScript is a concern you can combine this simple pattern with an image inside a ``noscript`` element.
```html
```
Note: As an alternative to the noscript pattern also checkout the [noscript extension](plugins/noscript).
### [data-expand] attribute
Normally lazysizes will expand the viewport area to lazy preload images/iframes which might become visible soon. This value can be adjusted using the ``expand`` option.
Additionally, this general option can be overridden with the ``data-expand`` attribute for each element. Different than the general ``expand`` option the ``data-expand`` attribute also accepts negative values (All numbers but ``0`` are accepted!).
This becomes especially handy to add unveiling effects for teasers or other elements:
```html
Teaser Title
...
```
### CSS API
lazysizes adds the class ``lazyloading`` while the images are loading and the class ``lazyloaded`` as soon as the image is loaded. This can be used to add unveil effects:
```css
/* fade image in after load */
.lazyload,
.lazyloading {
opacity: 0;
}
.lazyloaded {
opacity: 1;
transition: opacity 300ms;
}
```
```css
/* fade image in while loading and show a spinner as background image (good for progressive images) */
.lazyload {
opacity: 0;
}
.lazyloading {
opacity: 1;
transition: opacity 300ms;
background: #f7f7f7 url(loader.gif) no-repeat center;
}
```
### Broken image symbol
In case you are using an `alt` attribute but do not declare a `src`/`srcset` attribute you will end up with a broken image symbol.
There are two easy ways to deal with it.
Either define a `src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="` or add the following CSS.
```css
img.lazyload:not([src]) {
visibility: hidden;
}
```
### JS API
**lazysizes** automatically detects new elements with the class ``lazyload`` so you won't need to call or configure anything in most situations.
#### JS API - options
Options can be set by declaring a global configuration option object named ``lazySizesConfig``. This object must be defined before the lazysizes script. A basic example:
```js
window.lazySizesConfig = window.lazySizesConfig || {};
// use .lazy instead of .lazyload
window.lazySizesConfig.lazyClass = 'lazy';
// use data-original instead of data-src
lazySizesConfig.srcAttr = 'data-original';
//page is optimized for fast onload event
lazySizesConfig.loadMode = 1;
```
In case you are using a module bundler it is recommended to change the options directly after importing the `lazysizes` module:
```js
import lazySizes from 'lazysizes';
// other imports ...
lazySizes.cfg.lazyClass = 'lazy';
```
Here the list of options:
* ``lazySizesConfig.lazyClass`` (default: ``"lazyload"``): Marker class for all elements which should be lazy loaded (There can be only one ``class``. In case you need to add some other element, without the defined class, simply add it per JS: ``$('.lazy-others').addClass('lazyload');``)
* ``lazySizesConfig.preloadAfterLoad`` (default: ``false``): Whether lazysizes should load all elements after the window onload event. Note: lazySizes will then still download those not-in-view images inside of a lazy queue, so that other downloads after onload are not blocked.)
* ``lazySizesConfig.preloadClass`` (default: ``"lazypreload"``): Marker class for elements which should be lazy pre-loaded after onload. Those elements will be even preloaded, if the ``preloadAfterLoad`` option is set to ``false``. Note: This *class* can be also dynamically set (``$currentSlide.next().find('.lazyload').addClass('lazypreload');``).
* ``lazySizesConfig.loadingClass`` (default: ``"lazyloading"``): This ``class`` will be added to ``img`` element as soon as image loading starts. Can be used to add unveil effects.
* ``lazySizesConfig.loadedClass`` (default: ``"lazyloaded"``): This ``class`` will be added to any element as soon as the image is loaded or the image comes into view. Can be used to add unveil effects or to apply styles.
* ``lazySizesConfig.expand`` (default: ``370-500``): The ``expand`` option expands the calculated visual viewport area in all directions, so that elements can be loaded before they become visible. The default value is calculated depending on the viewport size of the device. (Note: Reasonable values are between ``300`` and ``1000`` (depending on the ``expFactor`` option.) In case you have a lot of small images or you are using the LQIP pattern you can lower the value, in case you have larger images set it to a higher value. Also note, that lazySizes will dynamically shrink this value to ``0`` if the browser is currently downloading and expand it if the browser network is currently idling and the user not scrolling (by multiplying the ``expand`` option with ``1.5`` (``expFactor``)). This option can also be overridden with the ``[data-expand]`` attribute.
* ``lazySizesConfig.minSize`` (default: ``40``): For ``data-sizes="auto"`` feature. The minimum size of an image that is used to calculate the ``sizes`` attribute. In case it is under ``minSize`` the script traverses up the DOM tree until it finds a parent that is over ``minSize``.
* ``lazySizesConfig.srcAttr`` (default: ``"data-src"``): The attribute, which should be transformed to ``src``.
* ``lazySizesConfig.srcsetAttr`` (default: ``"data-srcset"``): The attribute, which should be transformed to ``srcset``.
* ``lazySizesConfig.sizesAttr`` (default: ``"data-sizes"``): The attribute, which should be transformed to ``sizes``. Makes almost only makes sense with the value ``"auto"``. Otherwise, the ``sizes`` attribute should be used directly.
* ``lazySizesConfig.customMedia`` (default: ``{}``): The ``customMedia`` option object is an alias map for different media queries. It can be used to separate/centralize your multiple specific media queries implementation (layout) from the ``source[media]`` attribute (content/structure) by creating labeled media queries. (See also the [custommedia extension](plugins/custommedia)).
* ``lazySizesConfig.loadHidden`` (default: ``true``): Whether to load `visibility: hidden` elements. Important: lazySizes will load hidden images always delayed. If you want them to be loaded as fast as possible you can use `opacity: 0.001` but never `visibility: hidden` or `opacity: 0`.
* ``lazySizesConfig.ricTimeout`` (default: ``0``): The timeout option used for the `requestIdleCallback`. Reasonable values between: 0, 100 - 1000. (Values below 50 disable the `requestIdleCallback` feature.)
* ``lazySizesConfig.throttleDelay`` (default: ``125``): The timeout option used to throttle all listeners. Reasonable values between: 66 - 200.
```html
```
* ``lazySizesConfig.expFactor`` (default: ``1.5``): The ``expFactor`` is used to calculate the "preload expand", by multiplying the normal ``expand`` with the ``expFactor`` which is used to preload assets while the browser is idling (no important network traffic and no scrolling). (Reasonable values are between ``1.5`` and ``4`` depending on the ``expand`` option).
* ``lazySizesConfig.hFac`` (default: ``0.8``): The ``hFac`` (horizontal factor) modifies the horizontal expand by multiplying the ``expand`` value with the ``hFac`` value. Use case: In case of carousels there is often the wish to make the horizontal expand narrower than the normal vertical expand option. Reasonable values are between 0.4 - 1. In the unlikely case of a horizontal scrolling website also 1 - 1.5.
* ``lazySizesConfig.loadMode`` (default: ``2``): The ``loadMode`` can be used to constrain the allowed loading mode. Possible values are 0 = don't load anything, 1 = only load visible elements, 2 = load also very near view elements (``expand`` option) and 3 = load also not so near view elements (``expand`` * ``expFactor`` option). This value is automatically set to ``3`` after onload. Change this value to ``1`` if you (also) optimize for the onload event or change it to ``3`` if your onload event is already heavily delayed.
* ``lazySizesConfig.init`` (default: ``true``): By default lazysizes initializes itself, to load in view assets as soon as possible. In the unlikely case you need to setup/configure something with a later script you can set this option to ``false`` and call ``lazySizes.init();`` later explicitly.
#### JS API - events
**lazysizes** provides three events to modify or extend the behavior of **lazysizes**.
* ``lazybeforeunveil``: This event will be fired on each lazyload element right before of the "unveil" transformation. This event can be used to extend the unveil functionality. In case the event is ``defaultPrevented`` the default transformation action will be prevented (see also the [ls.unveilhooks.js plugin](plugins/unveilhooks/ls.unveilhooks.js)):
```js
//add simple support for background images:
document.addEventListener('lazybeforeunveil', function(e){
var bg = e.target.getAttribute('data-bg');
if(bg){
e.target.style.backgroundImage = 'url(' + bg + ')';
}
});
//or add AJAX loading
//
$(document).on('lazybeforeunveil', function(){
var ajax = $(e.target).data('ajax');
if(ajax){
$(e.target).load(ajax);
}
});
```
The ``lazybeforeunveil`` can also be used for lazy initialization and due to the fact that lazysizes also detects new elements in the DOM automatically also for auto- and self-initialization of UI widgets:
```html
```
* `lazyloaded`: After the image is fully loaded lazysizes dispatches a `lazyloaded` event. While this often duplicates the native `load` event it is often more convenient to use.
* ``lazybeforesizes``: This event will be fired on each element with the ``data-sizes="auto"`` attribute right before the calculated ``sizes`` attribute will be set. The ``event.detail.width`` property is set to the calculated width of the element and can be changed to any number. In case the event is ``defaultPrevented`` the ``sizes`` attribute won't be set. See also the [parent-fit extension](plugins/parent-fit).
```js
$(document).on('lazybeforesizes', function(e){
//use width of parent node instead of the image width itself
e.detail.width = $(e.target).parents(':not(picture)').innerWidth() || e.detail.width;
});
```
#### JS API - methods
##### ``lazySizes.loader.unveil(DOMNode)``
In case a developer wants to show an image even if it is not inside the viewport the ``lazySizes.loader.unveil(DOMNode)`` can be called:
```js
lazySizes.loader.unveil(imgElem);
```
Note: As a more lazy alternative the ``lazypreload`` class can be set: ``$(imgElem).addClass('lazypreload');``.
##### ``lazySizes.autoSizer.checkElems()``
In case one or more image elements with the attribute ``data-sizes="auto"`` have changed in size ``lazySizes.autoSizer.updateElems`` can be called (For example to implement element queries):
```js
lazySizes.autoSizer.checkElems();
```
##### ``lazySizes.loader.checkElems()``
Tests whether new elements has came into view. Normally this method only needs to be called, if ``lazySizesConfig.loadMode`` was set to ``0``.
##### ``lazySizes.init()``
LazySizes initializes itself automatically. In case you set ``lazySizesConfig.init`` to ``false`` you need to explicitly call ``lazySizes.init()``. Note: You can speed up initial loading of in view images if you call `lazySizesConfig.init()` explicitly after lazysizes and all plugins are loaded.
```html
```
## Browser Support
**lazysizes** supports all browsers, that support [``document.getElementsByClassName``](http://caniuse.com/#feat=getelementsbyclassname) (== all browsers but not IE8-). In case you need to support IE8, see also the [noscript extension](plugins/noscript/README.md#ie8) (or use a modified noscript pattern or the LQIP pattern).
## Contributing
Fixes, PRs and issues are always welcome, make sure to create a new branch from the **master** (not the gh-pages 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.
### Build
Run `npx grunt` to validate JSHint and uglify/minify all files.
### Tests
Run `npx serverino -p 3333` and navigate to [http://localhost:3333/tests/](http://localhost:3333/tests/)
## Available plugins in this repo
It is recommended to concat all plugins together with lazySizes. In case you don't concat it is recommended to include the plugin scripts *before* the lazySizes main script.
### [respimg polyfill plugin](plugins/respimg)
The respimg polyfill plugin is an extremely lightweight alternate polyfill for the most important subsets of responsive images (srcset and picture).
### [OPTIMUMX plugin](plugins/optimumx)
The ``srcset`` attribute with the *w* descriptor and ``sizes`` attribute automatically also includes high DPI images. But each image has a different optimal pixel density, which might be lower (for example 1.5x) than the pixel density of your device (2x or 3x). This information is unknown to the browser and therefore can't be optimized for. The [lazySizes optimumx extension](plugins/optimumx) gives you more control to trade between perceived quality vs. perceived performance.
### [parent-fit extension](plugins/parent-fit)
The [parent fit plugin](plugins/parent-fit) extends the ``data-sizes="auto"`` feature to also calculate the right ``sizes`` for ``object-fit: contain|cover`` image elements and other **height** ( and width) constrained image elements in general.
### [object-fit polyfill extension](plugins/object-fit)
The [object fit polyfill plugin](plugins/object-fit) polyfills the `object-fit` and the `object-position` property in non supporting browsers.
### [blur up / effect plugin](plugins/blur-up)
The [blur up / effect plugin](plugins/blur-up) allows you to create [great over fade / blur up effects](https://jsfiddle.net/trixta/v0oq0412/embedded/result/) with low quality image placeholder, which improves the user experience and perceived performance in case you are using a low quality image approach.
### [attrchange / re-initialization extension](plugins/attrchange) (strongly recommended if you use React, Angular etc.)
In case you are changing the ``data-src``/``data-srcset`` attributes of already transformed lazyload elements, you must normally also re-add the ``lazyload`` class to the element.
This [attrchange / re-initialization extension](plugins/attrchange) automatically detects changes to your ``data-*`` attributes and adds the class for you.
### [artdirect plugin](plugins/artdirect)
The [artdirect plugin](plugins/artdirect) allows you to fully control art direction via CSS.
### Other [plugins/extensions](plugins)
There are also other plugins/extension in the [plugins folder](plugins). As always you are open to create new ones for your project.
## Tip: Specifying image dimensions (minimizing reflows and avoiding page jumps)
To minimize reflows, content jumping or unpredictable behavior with some other JS widgets (isotope, masonry, some sliders/carousels...) the width **and** the height of an image should be calculable by the browser before the image source itself is loaded:
```html
```
For flexible responsive images the [CSS intrinsic ratio scaling technique](http://www.mademyday.de/css-height-equals-width-with-pure-css.html) should be used:
```html
```
In case you want to dynamically calculate your intrinsic ratios for many different formats you can vary the pattern to something like this:
```html
```
In case the exact ratio of your image is unknown you can also vary the intrinsic ratio like this:
```html
```
or at least add a ``min-height`` (and ``min-width``) to minimize content jumps:
```css
.lazyload,
.lazyloading {
min-height: 200px;
}
```
**Note**:
* If you use the "unknown intrinsic ratio pattern" and the width of the loaded image will not (approximately) match the width of its container, the ``data-sizes="auto"`` feature will not be effective when used on its own. In this situation, the most appropriate size for the image to fit in the available space can be calculated automatically using the [parent fit plugin](plugins/parent-fit).
### Updating layout of JS widgets
In case you can't specify the image dimensions using CSS or one of the above suggested methods and your JS widgets have problems to calculate the right dimensions. You can use the following pattern to update your JS widgets (sliders/masonry):
```js
$('.my-widget').each(function(){
var $module = $(this);
var update = function(){
$module.myWidget('updateLayout');
};
// Note: Instead of waiting for all images until we initialize the widget
// we use event capturing to update the widget's layout progressively.
this.addEventListener('load', update, true);
$module.myWidget();
});
```
For this update pattern you may want to combine this at least with the ``min-height`` pattern explained above.
## Tip: Where/How to include lazySizes
While lazy loading is a great feature, it is important for users that crucial in view images are loaded as fast as possible. (Most users start to interact with a page after in view images are loaded.)
In case you normally combine all your scripts into one large script and add this to the bottom of your page, it can be better for perceived performance to generate two or sometimes more script packages: One small package, which includes all scripts which have heavy influence on the content or the UI and another larger one which includes the normal behavior of the page.
This smaller script, which should include lazySizes (and all its plugins), should then be placed **before** any other blocking elements (i.e.: script(s)) at the end of the body or after any blocking elements (i.e.: scripts, stylesheets) in the head to load the crucial content as fast possible. (Note: It might make also sense to call `lazySizes.init();` explicitly right after lazySizes and all its plugins are added.)
## Why lazysizes
In the past, I often struggled using lazy image loaders, because the "main check function" is called repeatedly and with a high frequency. Which makes it hard to fulfill two purposes runtime and memory efficiency. And looking into the source code of most so called lazy loaders often also unveils lazy developers...
But in a world of responsive retina optimized images on the one hand and JS widgets like carousels or tabs (a lot of initially hidden images) on the other hand lazy loading images becomes more and more important, so I created this project.
**lazysizes** is different:
Due to the fact, that it is designed to be invoked with a high frequency and therefore works highly efficient, it was possible to hook into all kinds of events as a mutationobserver meaning this lazyloader works as a simple drop in solution - you simply write/render your markup and no matter whether the ``.lazyload`` element was added by AJAX or revealed by a JS or CSS animation it will be picked up by **lazysizes**.
```html
```
================================================
FILE: animate.html
================================================
lazysizes - the umltimate lazyloader for responsive images, iframes and widget
lazySizes
lazySizes is the ultimate and lightweight lazyLoader which lazy loads images (including responsive images (picture/srcset)), iframes and scripts. It is written in VanillaJS and with high performance in mind.
Simply add the JS to your website and put the classlazyload to all elements, which should be lazy loaded. For a short API description go to the readme.md.
Image with LQIP technique
The low quality image placeholder pattern is a performance technique. While it might increase measuered time until the onload event, it dramatically increases percieved performance.
This pattern is recommend for above the fold/crictical images.
automatic sizes feature: In case of lazy loading images the sizes attribute of the img/source elements can be calculated with JS.
This automatic sizes feature is directly included in lazySizes. Simply use the keyword auto inside of the data-sizes attributes (data-sizes="auto").
Important: How sizes is calculated: The automatic sizes calculation takes the width of the image if it is over 40 (see also minSize option). In case it's below the minSize threshold, it traverses up the DOM tree until it finds a parent which is over 40 and uses this number. Often the following general CSS rule might help: img[data-sizes="auto"] { display: block; }.
responsive image with srcset and automatic sizes attribute
The autosizes feature makes using responsive images with the right sizes value easy as hell.
Nulla vitae elit libero, a pharetra augue mollis interdum.
Third slide label
Praesent commodo cursus magna, vel scelerisque nisl consectetur.
Widgets/Javascript/Script
LazySizes can be extended to load nearly everything lazily. The ls.unveilhooks.js plugin can be used to lazy load scripts, background images, and videos. Simply add a data-script to your widget and you are done:
While other lazy loaders or responsive images solution need to be called, if new elements are added to the DOM or do become visible through a special user interaction or a JavaScript behavior.
LazySizes does automatically detect any changes to the DOM and the visibility of .lazyload elements.
lazySizes is the ultimate and lightweight lazyLoader which lazy loads images (including responsive images (picture/srcset)), iframes and scripts. It is written in VanillaJS and with high performance in mind.
Simply add the JS to your website and put the classlazyload to all elements, which should be lazy loaded. For a short API description go to the readme.md.
Image with LQIP technique
The low quality image placeholder pattern uses a low quality image for the first impression.
automatic sizes feature: In case of lazy loading images the sizes attribute of the img/source elements can be calculated with JS.
This automatic sizes feature is directly included in lazySizes. Simply use the keyword auto inside of the data-sizes attributes (data-sizes="auto").
Important: How sizes is calculated: The automatic sizes calculation takes the width of the image if it is over 40 (see also minSize option). In case it's below the minSize threshold, it traverses up the DOM tree until it finds a parent which is over 40 and uses this number. Often the following general CSS rule might help: img[data-sizes="auto"] { display: block; }.
responsive image with srcset and automatic sizes attribute
The autosizes feature makes using responsive images with the right sizes value easy as hell.
<img
alt="house by the lake"
data-sizes="auto"
data-srcset="small.jpg 500w,
medium.jpg 640w,
big.jpg 1024w"
data-src="medium"
class="lazyload" />
LazySizes normally loads in view elements as fast as possible and near the view elements are lazily pre-loaded. This can be modified in general using the expand option. With the data-expand attribute this can be changed element specific to expand or shrink the viewport.
This can be used to create unveiling effects for elements with or even without loading anything.
Nulla vitae elit libero, a pharetra augue mollis interdum.
Third slide label
Praesent commodo cursus magna, vel scelerisque nisl consectetur.
Widgets/Javascript/Script
LazySizes can be extended to load nearly everything lazily. The ls.unveilhooks.js plugin can be used to lazy load scripts, background images, and videos. Simply add a data-script to your widget and you are done:
While other lazy loaders or responsive images solution need to be called, if new elements are added to the DOM or do become visible through a special user interaction or a JavaScript behavior.
LazySizes does automatically detect any changes to the DOM and the visibility of .lazyload elements.
lazySizes is the ultimate and lightweight lazyLoader which lazy loads images (including responsive images (picture/srcset)), iframes and scripts. It is written in VanillaJS and with high performance in mind.
Simply add the JS to your website and put the classlazyload to all elements, which should be lazy loaded. For a short API description go to the readme.md.
Image with LQIP technique
The low quality image placeholder pattern is a performance technique. While it might increase measured time until the onload event, it dramatically increases perceived performance.
This pattern is recommended for above the fold/critical images.
automatic sizes feature: In case of lazy loading images the sizes attribute of the img/source elements can be calculated with JS.
This automatic sizes feature is directly included in lazySizes. Simply use the keyword auto inside of the data-sizes attributes (data-sizes="auto").
Important: How sizes is calculated: The automatic sizes calculation takes the width of the image if it is over 40 (see also minSize option). In case it's below the minSize threshold, it traverses up the DOM tree until it finds a parent which is over 40 and uses this number. Often the following general CSS rule might help: img[data-sizes="auto"] { display: block; }.
responsive image with srcset and automatic sizes attribute
The autosizes feature makes using responsive images with the right sizes value easy as hell.
LazySizes normally loads in view elements as fast as possible and near the view elements are lazily pre-loaded. This can be modified in general using the expand option. With the data-expand attribute this can be changed element specific to expand or shrink the viewport.
This can be used to create unveiling effects for elements with or even without loading anything.
Nulla vitae elit libero, a pharetra augue mollis interdum.
Third slide label
Praesent commodo cursus magna, vel scelerisque nisl consectetur.
Widgets/Javascript/Script
LazySizes can be extended to load nearly everything lazily. The ls.unveilhooks.js plugin can be used to lazy load scripts, background images, and videos. Simply add a data-script to your widget and you are done:
While other lazy loaders or responsive images solution need to be called, if new elements are added to the DOM or do become visible through a special user interaction or a JavaScript behavior.
LazySizes does automatically detect any changes to the DOM and the visibility of .lazyload elements.
================================================
FILE: optimumx/child.html
================================================
respimage performance test
The numbers on the top are the calculated resolutions for the image:
number in green is the image selected. While number in gray is the next lower and number in red is the next higher candidate.
The image on the left side has the data-optimumx attribute attached, while the same image on the right side is always loaded with the full density.
As you will see with this demo. Some images do gain significant quality with retina, but some others don't. The standard HTML responsive image markup has no way to tell the browser about does differences, therefore the browser often can't make educated decisions. The optimumx plugin solves this by limiting the source canidates to reasonable ones.
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
with data-optimumx
without data-optimumx
================================================
FILE: optimumx/index.html
================================================
controlling retina/density of responsive images with lazysizes and the optimumx plugin
The srcset with the sizes attribute is definitely the way to go to create responsive, flexibel, adaptive images.
Unfortunately there is no way to tell the browser, that a specific image doesn't gain much perceived quality with a higher density or that image quality of a specific image isn't important for the user experience.
Due to the fact, that a 2x retina means 4x data and 3x retina means 9x data, performance can suffer even on good connections badly.
lazySizes data-optimumx feature (build on top of data-sizes="auto") gives the developer more control to use adaptive images markup, but opt-out from to high retina at some point by defining the optimum density.
Unfortunately this demo makes only sense, if you are using a retina device. Maybe come back with one (tablet / smartphone)? Sorry!
Configure the data-optimumx attribute
================================================
FILE: optimumx/js/parent.js
================================================
(function(window, document){
if ( window.HTMLPictureElement ) {
$('html').addClass('resp-supported');
}
if ( (window.devicePixelRatio || 1) < 1.4 ) {
$('html').addClass('no-retina');
}
webshim.setOptions('forms-ext', {
replaceUI: 'auto'
});
webshim.polyfill('forms forms-ext');
$(function(){
var oninput;
$('#vw-input')
.on('change.smooth-vwchange', function(){
oninput = $.prop(this, 'checked');
})
.trigger('change.smooth-vwchange')
;
$('#viewport').each(function(){
var onChange = function(e){
if (!e || (oninput && e.type == 'input') || (e.type == 'change' && !oninput)){
var val = $(this).val();
$('#arena').width(val+'%');
}
};
$(this).on('input change', onChange).each(onChange);
});
$('#arena').removeAttr('src').prop('src', 'javascript:false');
$('.arena-config')
.on('submit', function(){
var data = $(this).serialize();
$('#arena').prop('src', 'child.html?' + data);
return false;
})
;
$('.btn-optimum').on('click', function(){
$('#arena').prop('src', 'child.html?'+ (new Date().getTime()));
return false;
});
$('.btn-auto').on('click', function(){
$('#arena').prop('src', 'child.html?optimumx=auto');
return false;
});
$('#arena').prop('src', 'child.html');
})
})(window, document);
================================================
FILE: package.json
================================================
{
"name": "lazysizes",
"version": "5.3.2",
"filename": "lazysizes.min.js",
"license": "MIT",
"author": "Alexander Farkas ",
"scripts": {
"build": "grunt && tsc && grunt importTs",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
"url": "git://github.com/aFarkas/lazysizes.git"
},
"namespace": "lazySizes",
"main": "lazysizes.js",
"browser": "lazysizes.js",
"types": "lazysizes.d.ts",
"devDependencies": {
"grunt": "~1.1.0",
"grunt-bytesize": "~0.2.0",
"grunt-cli": "~1.3",
"grunt-contrib-jshint": "^2.1.0",
"grunt-contrib-qunit": "^3.1.0",
"grunt-contrib-uglify": "^4.0.1",
"grunt-contrib-watch": "~1.1.0",
"grunt-max-filesize": "^0.1.1",
"grunt-uncss": "~0.9.1",
"jquery": "^3.5.1",
"jquery-deparam": "^0.5.3",
"picturefill": "^3.0.3",
"qunitjs": "^2.4.1",
"respimage": "^1.4.2",
"typescript": "^4.1.5"
},
"npmName": "lazysizes",
"npmFileMap": [
{
"basePath": "",
"files": [
"lazysizes.min.js",
"plugins/**/*.min.js"
]
}
],
"description": "High performance (jankfree) lazy loader for images (including responsive images), iframes and scripts (widgets).",
"keywords": [
"lazy",
"loader",
"lazyloader",
"lazyload",
"lazySizes",
"performance",
"responsive",
"image",
"images",
"responsive images",
"picture",
"srcset",
"respimg",
"respimage",
"include",
"ajax",
"img",
"imager",
"imager.js",
"picturefill",
"component"
]
}
================================================
FILE: plugins/README.md
================================================
# lazysizes plugins/extensions/snippets
**lazysizes** works out of the box with standard and responsive images (``src``, ``srcset`` and ``picture``) and iframes.
The scripts in this folder can be used as extensions or as boilerplate snippets to extend and adjust lazysizes to your needs.
================================================
FILE: plugins/artdirect/README.md
================================================
# lazysizes artdirect extension
The artdirect extension allows you to fully control art direction through your CSS. This is accomplished by two techniques which you can be used separately or combined. The extension hooks into the `data-sizes="auto"` feature.
The first feature is by tagging and the second feature uses the information of the displayed aspect ratio of the `img` elements and the physical aspect ratio of your images.
## Enabling artdirect extension for a ``picture > img``
You can either enable the artdirect extension for all images using JavaScript:
```js
// never try to import *.min.js files
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/artdiect/ls.artdirect';
lazySizes.cfg.autoArtDirect = true;
```
Or for a specific `img` element using CSS:
```css
picture > img.is-autoartdirect {
font-family: "artdirect";
}
```
## Tagging `source` elements and controlling it via CSS
You can use a whitespace separated list of tags on the `source` elements `data-tag` attribute as also a whitespace separated list of tags inside of the CSS `font-family`:
```html
```
## Providing aspect ratio information of physical images
By providing the specific layout height and width (no `auto` values) through CSS and providing the physical aspect ratio of the images through either a `data-aspectratio` attribute or through `w` **and**`h` descriptors or through `width` and `height` content attributes the plugin can choose the best image source.
```html
```
The aspect ratio feature can be perfectly combined with the tagging feature.
```html
```
================================================
FILE: plugins/artdirect/ls.artdirect.js
================================================
(function(window, factory) {
if (!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
'use strict';
if(!window.addEventListener){return;}
var lazySizesConfig = lazySizes.cfg;
var ElementPrototype = (window.Element || Node || window.HTMLElement).prototype;
var regArtDirect = /artdirect/;
var regDescriptors = /\s+(\d+)(w|h)\s+(\d+)(w|h)/;
var regArtDirectTags = /artdirect["']*\s*:\s*["']*(.+?)(?=($|'|"|;))/;
var regPicture = /^picture$/i;
var regSplit = /[\s,]+/g;
var slice = [].slice;
function getCandidatesAspectRatio(element){
var match, width, height;
var ratio = parseFloat(element.getAttribute('data-aspectratio'));
var srcset = element.getAttribute(lazySizesConfig.srcsetAttr) || element.getAttribute('srcset');
if(!ratio){
match = srcset.match(regDescriptors);
if (match) {
if(match[2] == 'w'){
width = match[1];
height = match[3];
} else {
width = match[3];
height = match[1];
}
} else {
width = element.getAttribute('width');
height = element.getAttribute('height');
}
ratio = width / height;
}
return ratio;
}
function getLayoutAspectRatio(element){
return element.offsetWidth / element.offsetHeight;
}
function toTagSelector(tag){
return 'source[data-tag~="' + tag + '"]';
}
function getArtDirectConfig(img){
var picture = img.parentNode;
var isPicture = regPicture.test(picture.nodeName || '');
var content = (window.getComputedStyle(img) || {}).fontFamily;
var config = null;
if(isPicture && (lazySizesConfig.autoArtDirect || regArtDirect.test(content || ''))){
config = {
picture: picture,
img: img,
tags: content.match(regArtDirectTags)
};
if(config.tags){
config.selector = config.tags[1].split(regSplit).map(toTagSelector).join(',');
}
}
return config;
}
function toSourceObj(source){
var media = source.getAttribute('media');
return {
source: source,
aspectRatio: getCandidatesAspectRatio(source),
isSelected: !media || window.matchMedia(media).matches,
};
}
function sortAspectRatio(source1, source2){
return source1.aspectRatio < source2.aspectRatio;
}
function getClosestSource(sources, aspecRatio){
var i, len;
var closest = sources[0];
for(i = 1, len = sources.length; i < len; i++){
if(Math.abs(closest.aspectRatio - aspecRatio) > Math.abs(sources[i].aspectRatio - aspecRatio)){
closest = sources[i];
}
}
return closest;
}
function setMedia(source, media){
source._lsMedia = media;
lazySizes.rAF(function(){
if(source._lsMedia){
delete source._lsMedia;
}
source.setAttribute('media', media);
});
}
function selectSource(imgCfg){
var imgAspectRatio = getLayoutAspectRatio(imgCfg.img);
var sources = slice.call(imgCfg.picture.getElementsByTagName('source'))
.map(toSourceObj)
.sort(sortAspectRatio)
;
var matchedSources = imgCfg.selector ?
sources.filter(function(source){
return source.source.matches(imgCfg.selector);
}) :
sources
;
var closestSource = getClosestSource(matchedSources, imgAspectRatio);
if(!closestSource.isSelected){
setMedia(closestSource.source, '(min-width: 1px)');
}
sources
.filter(function(source){
return (source != closestSource && source.isSelected);
})
.forEach(function (source) {
setMedia(source.source, '(x)');
})
;
}
function addAutoArtDirection(e){
if(e.detail.instance != lazySizes){return;}
var img = e.target;
var imgCfg = getArtDirectConfig(img);
if(imgCfg){
selectSource(imgCfg);
}
}
if(!ElementPrototype.matches){
ElementPrototype.matches = ElementPrototype.matchesSelector ||
ElementPrototype.webkitMatchesSelector ||
ElementPrototype.msMatchesSelector ||
ElementPrototype.oMatchesSelector;
}
window.addEventListener('lazybeforesizes', addAutoArtDirection, true);
}));
================================================
FILE: plugins/aspectratio/README.md
================================================
# lazysizes aspectratio extension
This plugin helps to pre-occupy the space needed for an image by calculating the height from the image width or the width from the height (This means the width or height has to be calculable before the image is loaded). This can serve as an alternative to the different CSS intrinsic ratio patterns.
Note: The CSS patterns are recommended, but especially in case of different ratio's for art directed images not so convenient. This plugin removes the ``data-aspectratio`` attribute after processing each image and my not play well with other plugins that rely on this attribute.
## Markup API:
The value of the ``data-aspectratio`` has to be defined as the *width* divided by the *height* of the image and can be represented as a ratio or a floating point number.
Example values for an image with a width of 400 and a height of 200 (all mean the same): ``"400/200"``, ``"4/2"``, ``"2/1"``, ``"2"``, ``"2.0"``
```html
```
## JS API
In case new elements are added to the DOM the global ``imageRatio.processImages`` method can be used. The method takes either an element representing the container/wrapper of the new elements or a list of image elements:
```js
imageRatio.processImages(document.querySelector('#dynaimc-wrapper');
imageRatio.processImages(document.querySelectorAll('#dynaimc-wrapper img[data-aspectratio]');
```
In case jQuery, ZEPTO, shoestring or another jQuery-like library is used the ``imageRatio`` plugin is added also:
```js
$('.dynamic-wrapper').imageRatio();
$('.dynamic-wrapper img[data-aspectratio]').imageRatio();
```
Note: This plugin can also be used without lazySizes core script.
================================================
FILE: plugins/aspectratio/ls.aspectratio.js
================================================
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
'use strict';
if(!window.addEventListener){return;}
var forEach = Array.prototype.forEach;
var imageRatio, extend$, $;
var regPicture = /^picture$/i;
var aspectRatioAttr = 'data-aspectratio';
var aspectRatioSel = 'img[' + aspectRatioAttr + ']';
var matchesMedia = function(media){
if(window.matchMedia){
matchesMedia = function(media){
return !media || (matchMedia(media) || {}).matches;
};
} else if(window.Modernizr && Modernizr.mq){
return !media || Modernizr.mq(media);
} else {
return !media;
}
return matchesMedia(media);
};
var addClass = lazySizes.aC;
var removeClass = lazySizes.rC;
var lazySizesConfig = lazySizes.cfg;
function AspectRatio(){
this.ratioElems = document.getElementsByClassName('lazyaspectratio');
this._setupEvents();
this.processImages();
}
AspectRatio.prototype = {
_setupEvents: function(){
var module = this;
var addRemoveAspectRatio = function(elem){
if(elem.naturalWidth < 36){
module.addAspectRatio(elem, true);
} else {
module.removeAspectRatio(elem, true);
}
};
var onload = function(){
module.processImages();
};
document.addEventListener('load', function(e){
if(e.target.getAttribute && e.target.getAttribute(aspectRatioAttr)){
addRemoveAspectRatio(e.target);
}
}, true);
addEventListener('resize', (function(){
var timer;
var resize = function(){
forEach.call(module.ratioElems, addRemoveAspectRatio);
};
return function(){
clearTimeout(timer);
timer = setTimeout(resize, 99);
};
})());
document.addEventListener('DOMContentLoaded', onload);
addEventListener('load', onload);
},
processImages: function(context){
var elements, i;
if(!context){
context = document;
}
if('length' in context && !context.nodeName){
elements = context;
} else {
elements = context.querySelectorAll(aspectRatioSel);
}
for(i = 0; i < elements.length; i++){
if(elements[i].naturalWidth > 36){
this.removeAspectRatio(elements[i]);
continue;
}
this.addAspectRatio(elements[i]);
}
},
getSelectedRatio: function(img){
var i, len, sources, customMedia, ratio;
var parent = img.parentNode;
if(parent && regPicture.test(parent.nodeName || '')){
sources = parent.getElementsByTagName('source');
for(i = 0, len = sources.length; i < len; i++){
customMedia = sources[i].getAttribute('data-media') || sources[i].getAttribute('media');
if(lazySizesConfig.customMedia[customMedia]){
customMedia = lazySizesConfig.customMedia[customMedia];
}
if(matchesMedia(customMedia)){
ratio = sources[i].getAttribute(aspectRatioAttr);
break;
}
}
}
return ratio || img.getAttribute(aspectRatioAttr) || '';
},
parseRatio: (function(){
var regRatio = /^\s*([+\d\.]+)(\s*[\/x]\s*([+\d\.]+))?\s*$/;
var ratioCache = {};
return function(ratio){
var match;
if(!ratioCache[ratio] && (match = ratio.match(regRatio))){
if(match[3]){
ratioCache[ratio] = match[1] / match[3];
} else {
ratioCache[ratio] = match[1] * 1;
}
}
return ratioCache[ratio];
};
})(),
addAspectRatio: function(img, notNew){
var ratio;
var width = img.offsetWidth;
var height = img.offsetHeight;
if(!notNew){
addClass(img, 'lazyaspectratio');
}
if(width < 36 && height <= 0){
if(width || height && window.console){
console.log('Define width or height of image, so we can calculate the other dimension');
}
return;
}
ratio = this.getSelectedRatio(img);
ratio = this.parseRatio(ratio);
if(ratio){
if(width){
img.style.height = (width / ratio) + 'px';
} else {
img.style.width = (height * ratio) + 'px';
}
}
},
removeAspectRatio: function(img){
removeClass(img, 'lazyaspectratio');
img.style.height = '';
img.style.width = '';
img.removeAttribute(aspectRatioAttr);
}
};
extend$ = function(){
$ = window.jQuery || window.Zepto || window.shoestring || window.$;
if($ && $.fn && !$.fn.imageRatio && $.fn.filter && $.fn.add && $.fn.find){
$.fn.imageRatio = function(){
imageRatio.processImages(this.find(aspectRatioSel).add(this.filter(aspectRatioSel)));
return this;
};
} else {
$ = false;
}
};
extend$();
setTimeout(extend$);
imageRatio = new AspectRatio();
window.imageRatio = imageRatio;
if(typeof module == 'object' && module.exports){
module.exports = imageRatio;
} else if (typeof define == 'function' && define.amd) {
define(imageRatio);
}
}));
================================================
FILE: plugins/attrchange/README.md
================================================
# lazysizes attribute change / re-initialization extension
In case you are changing the ``data-src``/``data-srcset`` attributes of already transformed lazyload elements dynamically, you normally also must re-add the ``lazyload`` class to the element.
This extension automatically detects changes to your ``data-*`` attributes and adds the class for you. This is very comfortable in case you are using highly dynamic or reactive View libraries like React, Angular, Ember etc..
In case you are using React you can also try the following [react-lazysizes](https://www.npmjs.com/package/react-lazysizes) module as another possible alternative.
```js
// never try to import *.min.js files
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/attrchange/ls.attrchange';
```
================================================
FILE: plugins/attrchange/ls.attrchange.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
'use strict';
var addObserver = function(){
var connect, disconnect, observer, connected;
var lsCfg = lazySizes.cfg;
var attributes = {'data-bgset': 1, 'data-include': 1, 'data-poster': 1, 'data-bg': 1, 'data-script': 1};
var regClassTest = '(\\s|^)(' + lsCfg.loadedClass;
var docElem = document.documentElement;
var setClass = function(target){
lazySizes.rAF(function(){
lazySizes.rC(target, lsCfg.loadedClass);
if(lsCfg.unloadedClass){
lazySizes.rC(target, lsCfg.unloadedClass);
}
lazySizes.aC(target, lsCfg.lazyClass);
if(target.style.display == 'none' || (target.parentNode && target.parentNode.style.display == 'none')){
setTimeout(function () {
lazySizes.loader.unveil(target);
}, 0);
}
});
};
var onMutation = function(mutations){
var i, len, mutation, target;
for(i = 0, len = mutations.length; i < len; i++){
mutation = mutations[i];
target = mutation.target;
if(!target.getAttribute(mutation.attributeName)){continue;}
if(target.localName == 'source' && target.parentNode){
target = target.parentNode.querySelector('img');
}
if(target && regClassTest.test(target.className)){
setClass(target);
}
}
};
if(lsCfg.unloadedClass){
regClassTest += '|' + lsCfg.unloadedClass;
}
regClassTest += '|' + lsCfg.loadingClass + ')(\\s|$)';
regClassTest = new RegExp(regClassTest);
attributes[lsCfg.srcAttr] = 1;
attributes[lsCfg.srcsetAttr] = 1;
if(window.MutationObserver){
observer = new MutationObserver(onMutation);
connect = function(){
if(!connected){
connected = true;
observer.observe( docElem, { subtree: true, attributes: true, attributeFilter: Object.keys(attributes)} );
}
};
disconnect = function(){
if(connected){
connected = false;
observer.disconnect();
}
};
} else {
docElem.addEventListener('DOMAttrModified', (function(){
var runs;
var modifications = [];
var callMutations = function(){
onMutation(modifications);
modifications = [];
runs = false;
};
return function(e){
if(connected && attributes[e.attrName] && e.newValue){
modifications.push({target: e.target, attributeName: e.attrName});
if(!runs){
setTimeout(callMutations);
runs = true;
}
}
};
})(), true);
connect = function(){
connected = true;
};
disconnect = function(){
connected = false;
};
}
addEventListener('lazybeforeunveil', disconnect, true);
addEventListener('lazybeforeunveil', connect);
addEventListener('lazybeforesizes', disconnect, true);
addEventListener('lazybeforesizes', connect);
connect();
removeEventListener('lazybeforeunveil', addObserver);
};
addEventListener('lazybeforeunveil', addObserver);
}));
================================================
FILE: plugins/bgset/README.md
================================================
# lazysizes bgset extension - responsive background images
This simple and small plugin allows you to define multiple background images with a width descriptor, similar to how ``img[srcset]`` works as also art directed images using media queries, similar to how ``picture`` works.
The extension will then load the best image size for the current viewport and device. In case the browser does not support responsive images natively either picturefill or the [respimg polyfill plugin](../respimg) has to be used:
Note: This plugin is deprecated in most cases. Check wether the object-fit CSS property in combination with the [object-fit polyfill](../object-fit) is better for your needs.
```html
```
The bgset also supports art direction through multiple media query sets. To use this feature each set has to be separated using the ``" | "`` signs and the media query has to be added at the end of the set inside of square brackets.
Also the ``customMedia`` option can be used to separate the media queries implementation from the markup.
```html
```
Of course also resolution switching and art direction can be combined:
```html
```
Here you find a [small bgset demo](http://jsfiddle.net/trixta/bfqqnosp/embedded/result/).
**Note: In case you use this plugin with ``background-size: cover|contain`` and the ``data-sizes="auto"`` feature, we recommend to also use the [parent-fit extension](../parent-fit/) to calculate the right ``sizes`` attribute for you. See also the following [demo](http://jsfiddle.net/trixta/w96o9xm5/). In these cases the [object-fit polyfill plugin](../object-fit) should be a better option than bgset.**
================================================
FILE: plugins/bgset/ls.bgset.js
================================================
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
'use strict';
if(!window.addEventListener){return;}
var lazySizesCfg = lazySizes.cfg;
var regWhite = /\s+/g;
var regSplitSet = /\s*\|\s+|\s+\|\s*/g;
var regSource = /^(.+?)(?:\s+\[\s*(.+?)\s*\])(?:\s+\[\s*(.+?)\s*\])?$/;
var regType = /^\s*\(*\s*type\s*:\s*(.+?)\s*\)*\s*$/;
var regBgUrlEscape = /\(|\)|'/;
var allowedBackgroundSize = {contain: 1, cover: 1};
var proxyWidth = function(elem){
var width = lazySizes.gW(elem, elem.parentNode);
if(!elem._lazysizesWidth || width > elem._lazysizesWidth){
elem._lazysizesWidth = width;
}
return elem._lazysizesWidth;
};
var getBgSize = function(elem){
var bgSize;
bgSize = (getComputedStyle(elem) || {getPropertyValue: function(){}}).getPropertyValue('background-size');
if(!allowedBackgroundSize[bgSize] && allowedBackgroundSize[elem.style.backgroundSize]){
bgSize = elem.style.backgroundSize;
}
return bgSize;
};
var setTypeOrMedia = function(source, match){
if(match){
var typeMatch = match.match(regType);
if(typeMatch && typeMatch[1]){
source.setAttribute('type', typeMatch[1]);
} else {
source.setAttribute('media', lazySizesCfg.customMedia[match] || match);
}
}
};
var createPicture = function(sets, elem, img){
var picture = document.createElement('picture');
var sizes = elem.getAttribute(lazySizesCfg.sizesAttr);
var ratio = elem.getAttribute('data-ratio');
var optimumx = elem.getAttribute('data-optimumx');
if(elem._lazybgset && elem._lazybgset.parentNode == elem){
elem.removeChild(elem._lazybgset);
}
Object.defineProperty(img, '_lazybgset', {
value: elem,
writable: true
});
Object.defineProperty(elem, '_lazybgset', {
value: picture,
writable: true
});
sets = sets.replace(regWhite, ' ').split(regSplitSet);
picture.style.display = 'none';
img.className = lazySizesCfg.lazyClass;
if(sets.length == 1 && !sizes){
sizes = 'auto';
}
sets.forEach(function(set){
var match;
var source = document.createElement('source');
if(sizes && sizes != 'auto'){
source.setAttribute('sizes', sizes);
}
if((match = set.match(regSource))){
source.setAttribute(lazySizesCfg.srcsetAttr, match[1]);
setTypeOrMedia(source, match[2]);
setTypeOrMedia(source, match[3]);
} else {
source.setAttribute(lazySizesCfg.srcsetAttr, set);
}
picture.appendChild(source);
});
if(sizes){
img.setAttribute(lazySizesCfg.sizesAttr, sizes);
elem.removeAttribute(lazySizesCfg.sizesAttr);
elem.removeAttribute('sizes');
}
if(optimumx){
img.setAttribute('data-optimumx', optimumx);
}
if(ratio) {
img.setAttribute('data-ratio', ratio);
}
picture.appendChild(img);
elem.appendChild(picture);
};
var proxyLoad = function(e){
if(!e.target._lazybgset){return;}
var image = e.target;
var elem = image._lazybgset;
var bg = image.currentSrc || image.src;
if(bg){
var useSrc = regBgUrlEscape.test(bg) ? JSON.stringify(bg) : bg;
var event = lazySizes.fire(elem, 'bgsetproxy', {
src: bg,
useSrc: useSrc,
fullSrc: null,
});
if(!event.defaultPrevented){
elem.style.backgroundImage = event.detail.fullSrc || 'url(' + event.detail.useSrc + ')';
}
}
if(image._lazybgsetLoading){
lazySizes.fire(elem, '_lazyloaded', {}, false, true);
delete image._lazybgsetLoading;
}
};
addEventListener('lazybeforeunveil', function(e){
var set, image, elem;
if(e.defaultPrevented || !(set = e.target.getAttribute('data-bgset'))){return;}
elem = e.target;
image = document.createElement('img');
image.alt = '';
image._lazybgsetLoading = true;
e.detail.firesLoad = true;
createPicture(set, elem, image);
setTimeout(function(){
lazySizes.loader.unveil(image);
lazySizes.rAF(function(){
lazySizes.fire(image, '_lazyloaded', {}, true, true);
if(image.complete) {
proxyLoad({target: image});
}
});
});
});
document.addEventListener('load', proxyLoad, true);
window.addEventListener('lazybeforesizes', function(e){
if(e.detail.instance != lazySizes){return;}
if(e.target._lazybgset && e.detail.dataAttr){
var elem = e.target._lazybgset;
var bgSize = getBgSize(elem);
if(allowedBackgroundSize[bgSize]){
e.target._lazysizesParentFit = bgSize;
lazySizes.rAF(function(){
e.target.setAttribute('data-parent-fit', bgSize);
if(e.target._lazysizesParentFit){
delete e.target._lazysizesParentFit;
}
});
}
}
}, true);
document.documentElement.addEventListener('lazybeforesizes', function(e){
if(e.defaultPrevented || !e.target._lazybgset || e.detail.instance != lazySizes){return;}
e.detail.width = proxyWidth(e.target._lazybgset);
});
}));
================================================
FILE: plugins/blur-up/README.md
================================================
# The lazysizes Blur Up/effect plugin
The lazysizes Blur Up plugin ([demo](https://jsfiddle.net/trixta/v0oq0412/embedded/result/)) gives you the possibility to also lazyload the low quality placeholder and enables you to create a blur up/fade over effect.
This way the low quality image placeholder technique is more appealing to the user.
```js
// never try to import *.min.js files
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/blur-up/ls.blur-up';
```
## How to
Simply add a `data-lowsrc` attribute with the low quality image placeholder image to your `img` and in case of `picture` to your `source` elements.
Lazysizes will then create a new image right after your original image with the following class `ls-blur-up-img`.
The new image (`.ls-blur-up-img`) will get the following state classes to enable you to write a custom CSS animation/transition as soon as the image is in view and loaded: `ls-inview`/`ls-original-loaded`, while your original `img` gets the class `.ls-blur-up-is-loading` until the `.ls-blur-up-img` is loaded.
```html
```
### Blur-up options
#### BlurUp Mode
The effect mode has two possible value: `"always"` (default: The effect is generated always) and `"auto"` (The effect is only used with non cached images).
The blur up mode can be configured using JS or CSS:
```js
import lazysizes from 'lazysizes';
import 'lazysizes/plugins/blur-up/ls.blur-up';
lazysizes.cfg.blurupMode = 'auto';
```
```css
.mediabox-img {
font-family: "blur-up: auto", "object-fit: cover";
}
```
#### You can override blur-up classes
```js
import lazysizes from 'lazysizes';
import 'lazysizes/plugins/blur-up/ls.blur-up';
lazySizes.cfg.blurUpClass = 'blurred-image'
```
Here the list of override classes:
* ``lazySizes.cfg.blurUpLoadingClass`` ls-blur-up-is-loading
* ``lazySizes.cfg.blurUpInviewClass`` ls-inview
* ``lazySizes.cfg.blurUpLoadedClass`` ls-blur-up-loaded
* ``lazySizes.cfg.blurUpLoadedOriginalClass`` ls-original-loaded
================================================
FILE: plugins/blur-up/ls.blur-up.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
'use strict';
var lazySizesCfg;
(function(){
var prop;
var blurUpDefaults = {
blurUpClass: 'ls-blur-up-img',
blurUpLoadingClass: 'ls-blur-up-is-loading',
blurUpInviewClass: 'ls-inview',
blurUpLoadedClass: 'ls-blur-up-loaded',
blurUpLoadedOriginalClass: 'ls-original-loaded'
};
lazySizesCfg = lazySizes.cfg || {};
for(prop in blurUpDefaults){
if(!(prop in lazySizesCfg)){
lazySizesCfg[prop] = blurUpDefaults[prop];
}
}
})();
var slice = [].slice;
var regBlurUp = /blur-up["']*\s*:\s*["']*(always|auto)/;
var regType = /image\/(jpeg|png|gif|svg\+xml)/;
var transSrc = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
var matchesMedia = function (source) {
var media = source.getAttribute('data-media') || source.getAttribute('media');
var type = source.getAttribute('type');
return (!type || regType.test(type)) && (!media || window.matchMedia(lazySizes.cfg.customMedia[media] || media).matches);
};
var getLowSrc = function (picture, img) {
var matchingLowSrc;
var sources = picture ? slice.call(picture.querySelectorAll('source, img')) : [img];
sources.forEach(function (src) {
if (matchingLowSrc) {return;}
var lowSrc = src.getAttribute('data-lowsrc');
if (lowSrc && matchesMedia(src)) {
matchingLowSrc = lowSrc;
}
});
return matchingLowSrc;
};
var createBlurup = function(picture, img, src, blurUp){
var blurImg;
var isBlurUpLoaded = false;
var isForced = false;
var start = blurUp == 'always' ? 0 : Date.now();
var isState = 0;
var parent = (picture || img).parentNode;
var createBlurUpImg = function () {
if(!src){return;}
var onloadBlurUp = function(e){
isBlurUpLoaded = true;
if (!blurImg) {
blurImg = e.target;
}
lazySizes.rAF(function () {
lazySizes.rC(img, lazySizes.cfg.blurUpLoadingClass);
if(blurImg) {
lazySizes.aC(blurImg, lazySizes.cfg.blurUpLoadedClass);
}
});
if(blurImg){
blurImg.removeEventListener('load', onloadBlurUp);
blurImg.removeEventListener('error', onloadBlurUp);
}
};
blurImg = document.createElement('img');
blurImg.addEventListener('load', onloadBlurUp);
blurImg.addEventListener('error', onloadBlurUp);
blurImg.className = lazySizes.cfg.blurUpClass;
blurImg.cssText = img.cssText;
blurImg.src = src;
blurImg.alt = '';
blurImg.setAttribute('aria-hidden', 'true');
parent.insertBefore(blurImg, (picture || img).nextSibling);
if(blurUp != 'always'){
blurImg.style.visibility = 'hidden';
lazySizes.rAF(function () {
if (blurImg) {
setTimeout(function(){
if (blurImg) {
lazySizes.rAF(function () {
if(!isForced && blurImg){
blurImg.style.visibility = '';
}
});
}
}, lazySizes.cfg.blurupCacheDelay || 33);
}
});
}
};
var remove = function () {
if(blurImg){
lazySizes.rAF(function() {
lazySizes.rC(img, lazySizes.cfg.blurUpLoadingClass);
try {
blurImg.parentNode.removeChild(blurImg);
} catch(er){}
blurImg = null;
});
}
};
var setStateUp = function(force){
isState++;
isForced = force || isForced;
if(force){
remove();
} else if(isState > 1) {
setTimeout(remove, 5000);
}
};
var onload = function() {
img.removeEventListener('load', onload);
img.removeEventListener('error', onload);
if(blurImg){
lazySizes.rAF(function(){
if(blurImg) {
lazySizes.aC(blurImg, lazySizes.cfg.blurUpLoadedOriginalClass);
}
});
}
lazySizes.fire(img, 'blurUpLoaded');
if(blurUp != 'always' && (!isBlurUpLoaded || Date.now() - start < 66)){
setStateUp(true);
} else {
setStateUp();
}
};
createBlurUpImg();
img.addEventListener('load', onload);
img.addEventListener('error', onload);
lazySizes.aC(img, lazySizes.cfg.blurUpLoadingClass);
var parentUnveil = function (e) {
if(parent != e.target){
return;
}
lazySizes.aC(blurImg || img, lazySizes.cfg.blurUpInviewClass);
setStateUp();
parent.removeEventListener('lazybeforeunveil', parentUnveil);
};
if(!parent.getAttribute('data-expand')){
parent.setAttribute('data-expand', -1);
}
parent.addEventListener('lazybeforeunveil', parentUnveil);
lazySizes.aC(parent, lazySizes.cfg.lazyClass);
};
window.addEventListener('lazybeforeunveil', function (e) {
var detail = e.detail;
if(detail.instance != lazySizes || !detail.blurUp){return;}
var img = e.target;
var picture = img.parentNode;
if(picture.nodeName != 'PICTURE'){
picture = null;
}
createBlurup(picture, img, getLowSrc(picture, img) || transSrc, detail.blurUp);
});
window.addEventListener('lazyunveilread', function (e) {
var detail = e.detail;
if(detail.instance != lazySizes){return;}
var img = e.target;
var match = (getComputedStyle(img, null) || {fontFamily: ''}).fontFamily.match(regBlurUp);
if(!match && !img.getAttribute('data-lowsrc')){return;}
detail.blurUp = match && match[1] || lazySizes.cfg.blurupMode || 'always';
});
}));
================================================
FILE: plugins/custommedia/README.md
================================================
# lazysizes custommedia extension
lazySizes custommedia extension allows you to automatically sync and manage your breakpoints between your CSS and the ``media`` attributes of your ``"picture > source"`` elements using the ``customMedia`` option of lazySizes.
## Configuration via CSS
The following CSS...
```css
html:after {
display: none;
content: '--small: (max-width: 500px) | --medium: (max-width: 1100px) | --large: (max-width: 1500px)';
}
```
... allows you to write the following markup:
```html
```
The parsed custom media query object can be accessed through the ``lazySizesConfig.customMedia`` option object:
```js
window.lazySizesConfig.customMedia; // returns:
/*
{
'--small': '(max-width: 500px)',
'--medium': (min-width: 1100px)',
'--large': '(max-width: 1100px)'
}
*/
```
```scss
/*
Simple Sass mixin to share a map of breakpoints between CSS and JS
Usage:
$breakpoints: (
--small: (max-width: 480px),
--medium: (max-width: 1024px),
--large: (min-width: 1280px)
);
html:after {
@include shareBreakpoints($breakpoints);
}
*/
@mixin shareBreakpoints($map , $cssprop: content){
$description: '';
@each $property, $value in $map
{
@if $description != '' {
$description: $description + ' | ';
}
$description: $description + $property +': '+ inspect($value);
}
display: none;
#{$cssprop}: $description;
}
```
================================================
FILE: plugins/custommedia/ls.custommedia.js
================================================
/*
html:after {
display: none;
content: '--small: (max-width: 500px) | --medium: (max-width: 1100px) | --large: (min-width: 1100px)';
}
*/
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var docElem = document.documentElement;
lazySizes.getCustomMedias = (function(){
var regCleanPseudos = /['"]/g;
var regSplit = /\s*\|\s*/g;
var regNamedQueries = /^([a-z0-9_-]+)\s*:\s*(.+)$/i;
var getStyle = function(elem, pseudo){
return ((getComputedStyle(elem, pseudo) || {getPropertyValue: function(){}}).getPropertyValue('content') || 'none').replace(regCleanPseudos, '').trim();
};
var parse = function(string, object){
string.split(regSplit).forEach(function(query){
var match = query.match(regNamedQueries);
if(match){
object[match[1]] = match[2];
}
});
};
return function(object, element){
object = object || lazySizes.cfg.customMedia;
element = element || document.querySelector('html');
parse(getStyle(element, ':before'), object);
parse(getStyle(element, ':after'), object);
return object;
};
})();
lazySizes.updateCustomMedia = function(){
var i, len, customMedia;
var elems = docElem.querySelectorAll('source[media][data-media][srcset]');
lazySizes.getCustomMedias();
for(i = 0, len = elems.length; i < len; i++){
if( (customMedia = lazySizes.cfg.customMedia[elems[i].getAttribute('data-media') || elems[i].getAttribute('media')]) ){
elems[i].setAttribute('media', customMedia);
}
}
elems = docElem.querySelector('source[media][data-media][srcset] ~ img');
for(i = 0, len = elems.length; i < len; i++){
lazySizes.uP(elems[i]);
}
lazySizes.autoSizer.checkElems();
};
lazySizes.getCustomMedias();
}));
================================================
FILE: plugins/fix-edge-h-descriptor/README.md
================================================
# MS Edge h descriptor fix
This plugin/polyfill fixes the missing `h` descriptor parsing in MS Edge by removing the `h` from all candidates of `source` and `img` elements and storing the physical aspect ratio of the candidates into a `data-aspectratio` attribute.
Note: This polyfill is already included in the [respimg polyfill plugin](../respimg) and must **not** be included along site with it. But it is a good script in case you either use picturefill or do not use a polyfill at all.
================================================
FILE: plugins/fix-edge-h-descriptor/ls.fix-edge-h-descriptor.js
================================================
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var img = document.createElement('img');
var supportSrcset = ('sizes' in img) && ('srcset' in img);
var regHDesc = /\s+\d+h/g;
var fixEdgeHDescriptor = (function(){
var regDescriptors = /\s+(\d+)(w|h)\s+(\d+)(w|h)/;
var forEach = Array.prototype.forEach;
return function(){
var img = document.createElement('img');
var removeHDescriptors = function(source){
var ratio, match;
var srcset = source.getAttribute(lazySizesConfig.srcsetAttr);
if(srcset){
if((match = srcset.match(regDescriptors))){
if(match[2] == 'w'){
ratio = match[1] / match[3];
} else {
ratio = match[3] / match[1];
}
if(ratio){
source.setAttribute('data-aspectratio', ratio);
}
source.setAttribute(lazySizesConfig.srcsetAttr, srcset.replace(regHDesc, ''));
}
}
};
var handler = function(e){
if(e.detail.instance != lazySizes){return;}
var picture = e.target.parentNode;
if(picture && picture.nodeName == 'PICTURE'){
forEach.call(picture.getElementsByTagName('source'), removeHDescriptors);
}
removeHDescriptors(e.target);
};
var test = function(){
if(!!img.currentSrc){
document.removeEventListener('lazybeforeunveil', handler);
}
};
document.addEventListener('lazybeforeunveil', handler);
img.onload = test;
img.onerror = test;
img.srcset = 'data:,a 1w 1h';
if(img.complete){
test();
}
};
})();
if(!lazySizes.hasHDescriptorFix && window.HTMLPictureElement && supportSrcset && document.msElementsFromPoint){
lazySizes.hasHDescriptorFix = true;
fixEdgeHDescriptor();
}
}));
================================================
FILE: plugins/fix-ios-sizes/fix-ios-sizes.js
================================================
/**
* Some versions of iOS (8.1-) do load the first candidate of a srcset candidate list, if width descriptors with the sizes attribute is used.
* This tiny extension prevents this wasted download by creating a picture structure around the image.
* Note: This extension is already included in the ls.respimg.js file.
*
* Usage:
*
*
*/
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
'use strict';
var regPicture;
var lazySizesCfg = lazySizes.cfg;
var img = document.createElement('img');
if(('srcset' in img) && !('sizes' in img) && !window.HTMLPictureElement){
regPicture = /^picture$/i;
document.addEventListener('lazybeforeunveil', function(e){
if(e.detail.instance != lazySizes){return;}
var elem, parent, srcset, sizes, isPicture;
var picture, source;
if(e.defaultPrevented ||
lazySizesCfg.noIOSFix ||
!(elem = e.target) ||
!(srcset = elem.getAttribute(lazySizesCfg.srcsetAttr)) ||
!(parent = elem.parentNode) ||
(
!(isPicture = regPicture.test(parent.nodeName || '')) &&
!(sizes = elem.getAttribute('sizes') || elem.getAttribute(lazySizesCfg.sizesAttr))
)
){return;}
picture = isPicture ? parent : document.createElement('picture');
if(!elem._lazyImgSrc){
Object.defineProperty(elem, '_lazyImgSrc', {
value: document.createElement('source'),
writable: true
});
}
source = elem._lazyImgSrc;
if(sizes){
source.setAttribute('sizes', sizes);
}
source.setAttribute(lazySizesCfg.srcsetAttr, srcset);
elem.setAttribute('data-pfsrcset', srcset);
elem.removeAttribute(lazySizesCfg.srcsetAttr);
if(!isPicture){
parent.insertBefore(picture, elem);
picture.appendChild(elem);
}
picture.insertBefore(source, elem);
});
}
}));
================================================
FILE: plugins/include/README.md
================================================
# lazysizes include plugin
**lazysizes** include extension/plugin asynchronously include non crucial content, styles or JS modules. Due to lazyloading, prioritized queuing and preload after load techniques lazySizes include extension scales much better than similar other solutions.
Typical use cases are:
* lazy loading different content, styles or JS modules depending on certain conditions (responsive content, responsive behavior, media queries, existence of a DOM element, browser features, user preferences, element queries...)
* deferring heavy to render or uncacheable content (client or server side)
* progressively enhancing the document with new JS enabled content
* splitting or deferred loading of large JS/CSS modules in larger projects
* clean and simple architecture for initialization/loading and/or destroying/unloading of (conditional) JS behaviors
* deferred self-initializing of DOM behaviors
## Basic usage
Put a ``data-include`` attribute on your ``.lazyload`` element and reference the URL to load:
```html
```
The ``data-include`` can also consume a list of candidates represented by a URL in combination with an optional parenthesised condition. The lazySizes include extension will then take the first URL, that matches the condition:
```html
```
The condition is either a media query or the name of a configured condition rule. The condition rule can be configured through CSS or through JS via the ``include.conditions`` option.
### Condition configuration through CSS
The Lazysizes include extension checks wether the condition name matches the CSS ``content`` value the of ``:after`` or ``:before`` pseudo elements from the ``html`` element (can be configured).
```html
```
### Condition configuration through JS
A condition can also be configured through the ``lazySizesConfig.include.conditions`` option map. Each key is either a string representing a media query or a function:
```html
```
If the last include candidate has a condition, the ``innerHTML`` of the initial content is used as unconditioned fallback content.
```html
```
As soon as the content is changed a ``lazyincluded`` event is fired at the element:
```html
```
### Loading Styles or (AMD) Modules
The include feature can also load CSS, AMD or ES6 modules. To mark an URL as CSS put a ``css:``, to load an AMD module put a ``amd:`` or to load an ES6 module put a ``module:`` identifier in front of the URL:
```html
```
Content, Style and AMD includes can also be mixed and used with or without conditions:
```html
```
In case content and a behavior include is used together lazySizes will load them in parallel but makes sure to first include the content and then initialize the behavior.
#### AMD/ES6 module features
While you can write your AMD/ES6 module how you want lazysizes include extension will check wether your module provides the following methods:
* ``yourmodule.lazytransform``: Will be invoked before the content is inserted. Especially to transform the AJAX response. (For example JSON to HTML)
* ``yourmodule.lazyload``: Callback function to initialize the module. Will be invoked after the content was inserted.
* ``yourmodule.lazyunload``: Callback function to destroy your widget. Will be invoked before old content is removed
Each of those methods are optional static methods of a module. Here is a simple example:
```js
define(function(){
// constructor
var Slider = function(element,options) {
};
Slider.prototype = {
destroy: function(){}
};
// lazysizes include features:
// called with the DOM element (data.target) and some other useful data (data.detail)
// useful to initialize with the DOM element
Slider.lazyload = function(data){
var Slider = new Slider(data.target);
// save instance for destroy / lazyunload
// data.element._slider = Slider;
};
// in case of a conditioned include and
// the need to destroy the instance (i.e.: unbind global events)
Slider.lazyunload = function(data){
// data.target._slider.destroy();
};
// gets invoked with the a simplified XHR object (data.detail)
// and the dom element (data.target)
Slider.lazytransform = function(data){
// var json = JSON.parse(data.detail.responseText);
// data.detail.response = template(json);
};
return Slider;
});
```
In case a candidate includes new markup while another candidate only includes an AMD behavior. The initial content will be automatically resetted in case of a condition switch:
```html
```
In case both candidates has an amd module but not a content include the markup won't be resetted automatically. But the unloading module can request this behavior inside of it's unload method or the to initialized module can request this inside the ``lazytransform`` method using the ``resetHTML`` propety:
```js
Slider.lazyunload = function(data){
// data.element._slider.destroy();
data.resetHTML = true;
};
//or
NewSlider.lazytransform = function(data){
// data.element._slider.destroy();
data.resetHTML = true;
};
```
In case the content doesn't contain any mutable states, that need to be transferred to the new behavior (i.e. input fields etc.), this option makes it extremley simple to cleanup the HTML for the next module.
The data object is shared between the ``lazyunload``, ``lazytransform`` and ``lazyload`` so that a possible state can be transferred.
#### Loading multiple styles and modules
Multiple styles or AMD modules for one candidate can be configured by separating them with ``|,|`` signs:
```html
```
### Scalability and queue priority
The include feature will always use a download queue to make sure, that multiple includes do not jam the browsers own request queue. In case of many non crucial includes mixed with some crucial includes on one page the ``data-lazyqueue`` attribute can be used to add a queue priority for the include extension:
```html
```
### Events
* ``lazyincludeload`` is a cancelable event fired at the element before the request is started. The ``event.detail`` object can be used to modify the XHR request.
* ``lazyincludeloaded`` is a cancelable event fired at the element after the request is complete, but before the content is added. The ``event.detail.content`` property can be used to modify the content (for example to transform JSON to HTML).
* ``lazyincluded`` is an event fired at the element right after the HTML was injected.
Here are some examples:
```js
const component = document.querySelector('.lazy-component');
// Modify the request headers when the targeted component kicks off an XHR
component.addEventListener('lazyincludeload', function(event) {
if (event.detail) {
event.detail.xhrModifier = function(request, candidate) {
request.setRequestHeader('Accept', 'application/json');
}
}
});
// Transform a JSON response into HTML before it is injected
component.addEventListener('lazyincludeloaded', function(event) {
if (event.detail.content) {
var json = JSON.parse(event.detail.content);
var html = '
' + json.title + '
';
html += '
' + json.body + '
';
event.detail.content = html;
}
});
```
### Options
All include options are configurable through the ``lazySizesConfig.include`` option object:
#### ``contentElement`` (default: ``"html"``):
The selector of the element, which should be used to check for the CSS content value:
```js
window.lazySizesConfig = {
include: {
contentElement: '#mediaqueries' // 'html'
}
};
```
#### ``conditions`` option (default: ``{}``):
The conditions option can be used to create new custom conditions.
```js
window.lazySizesConfig = {
addClasses: true, // good to add loading styles
include: {
conditions: {
small: '(max-width: 480px)',
custom: function(elem, data){
return true || false;
}
}
}
};
```
#### ``map`` option (default: ``{}``):
The ``map`` option allows to map the value of the ``data-include`` attribute to another string. This does not only work for the hole value, but also for parsed parts.
```html
```
This option becomes useful to separate content from behavior.
The include feature works together with all normal lazySizes options (i.e.: ``addClasses`` for load indicators), events and methods.
## Reacting to user interaction
Of course it is also possible to react to a user interaction.
```html
```
It's also possible to change the ``data-include`` value and reevaluate it:
```html
```
## Sharing States between two modules
```html
```
```js
define(function(){
// constructor
var Nav = function(element,options) {
};
Nav.prototype = {
};
Nav.lazyunload = function(data){
//Reset HTML for mobileNav
data.resetHTML = true;
//share the value of the input field with mobileNav
data.shareState = {
searchValue: $('input.search', data.target).val();
};
};
Nav.lazyload = function(data){
//check wether mobileNav has shared some data
if(data.shareState){
$('input.search', data.target).val(data.shareState.searchValue || '');
}
};
return Nav;
});
```
Note: For amd require JS or SystemJS to work the `requireJs` and/or the `systemJs` option has to be provided:
```js
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.requireJs = function(modules, cb){
window.require(modules, cb);
};
window.lazySizesConfig.systemJs = function(module, cb){
window.System.import(module).then(cb);
};
```
================================================
FILE: plugins/include/ls.include.js
================================================
/*
This plugin extends lazySizes to lazyLoad and/or conditionally load content
*/
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
if(!document.getElementsByClassName) {
return;
}
var config, includeConfig, baseContentElement, basePseudos;
var regSplitCan = /\s*,+\s+/;
var cssComplete = {complete: 1, loaded: 1};
var uniqueUrls = {};
var regWhite = /\s+/;
var regTypes = /^(amd|css|module)\:(.+)/i;
var regUrlCan = /(.+)\s+(\(\s*(.+)\s*\))/;
var regCleanPseudos = /['"]/g;
var docElem = document.documentElement;
var conditionalIncludes = document.getElementsByClassName('lazyconditionalinclude');
var getStyles = function (element, pseudo) {
var view = element.ownerDocument.defaultView;
if (!view.opener) {
view = window;
}
return view.getComputedStyle(element, pseudo || null) || {getPropertyValue: function(){}, isNull: true};
};
var queue = (function(){
var lowTreshold = 2;
var highTreshold = 3;
var queueThreshold = lowTreshold;
var inProgress = 0;
var priosInProgress = 0;
var queue = [];
var resetQueue = (function(){
var timer;
var reset = function(){
if(queue.length){
inProgress = 0;
queue.d();
}
};
return function(){
clearTimeout(timer);
timer = setTimeout(reset, 999);
};
})();
return {
q: function(element){
var isPrio = element.getAttribute('data-lazyqueue') == null;
if(isPrio){
priosInProgress++;
queueThreshold = highTreshold;
}
if(inProgress > queueThreshold){
queue[isPrio ? 'unshift' : 'push'](element);
} else if(findLoadCandidate(element)) {
inProgress++;
resetQueue();
}
},
d: function(){
if(inProgress){
inProgress--;
}
if(priosInProgress > 0){
priosInProgress--;
if(!priosInProgress){
queueThreshold = lowTreshold;
}
}
if(inProgress > queueThreshold){
return;
}
while(queue.length){
if(findLoadCandidate(queue.shift())){
inProgress++;
break;
}
}
resetQueue();
}
};
})();
var refreshIncludes = (function(){
var timer;
var run = function(){
var i = 0;
var len = conditionalIncludes.length;
for(; i < len; i++){
if(!lazySizes.hC(conditionalIncludes[i], config.lazyClass) && findCandidate(conditionalIncludes[i])){
lazySizes.aC(conditionalIncludes[i], config.lazyClass);
}
}
};
return function(e){
clearTimeout(timer);
basePseudos = null;
timer = setTimeout(run, e.type == 'resize' ? 31 : 0);
};
})();
config = lazySizes && lazySizes.cfg;
if(!config.include){
config.include = {};
}
includeConfig = config.include;
if(!includeConfig.contentElement){
includeConfig.contentElement = 'html';
}
if(!includeConfig.conditions){
includeConfig.conditions = {};
}
if(!includeConfig.map){
includeConfig.map = {};
}
function addUrl(url){
/*jshint validthis:true */
var match;
if((match = url.match(regTypes))){
this.urls[match[1]] = includeConfig.map[match[2]] || match[2];
} else {
this.urls.include = includeConfig.map[url] || url;
}
}
function parseCandidate(input){
var output, map, url;
input = input.trim();
input = includeConfig.map[input] || input;
map = input.match(regUrlCan);
if(map){
url = map[1];
output = {
condition: config.include.conditions[map[3]] || config.customMedia[map[3]] || map[2] || null,
name: map[3]
};
} else {
url = input;
output = {
condition: null,
name: ''
};
}
output.urls = {};
(includeConfig.map[url] || url).split(regWhite).forEach(addUrl, output);
if(!output.urls.include && output.urls.amd){
/*jshint validthis:true */
this.saved = true;
output.initial = this;
}
return output;
}
function getIncludeData(elem){
var len;
var includeStr = (elem.getAttribute('data-include') || '');
var includeData = elem.lazyInclude;
var initialContent;
if(!includeData || includeData.str != includeStr || includeConfig.allowReload){
initialContent = {saved: false, content: null};
includeData = {
str: includeStr,
candidates: (includeConfig.map[includeStr] || includeStr).split(regSplitCan).map(parseCandidate, initialContent)
};
if(!(len = includeData.candidates.length) || includeData.candidates[len - 1].condition){
initialContent.saved = true;
includeData.candidates.push({
urls: {},
condition: null,
name: 'initial',
content: initialContent
});
} else if(initialContent.saved && includeData.candidates.length == 1){
initialContent.saved = false;
}
includeData.initialContent = initialContent;
if(initialContent.saved){
initialContent.content = elem.innerHTML;
}
elem.lazyInclude = includeData;
if(includeData.candidates.length > 1){
lazySizes.aC(elem, 'lazyconditionalinclude');
} else {
lazySizes.rC(elem, 'lazyconditionalinclude');
}
}
return includeData;
}
function matchesCondition(elem, candidate){
var matches = !candidate.condition;
if(candidate.condition){
createPseudoCondition();
if(basePseudos[candidate.name]){
matches = true;
} else if(window.matchMedia && typeof candidate.condition == 'string'){
matches = (matchMedia(candidate.condition) || {}).matches;
} else if(typeof candidate.condition == 'function'){
matches = candidate.condition(elem, candidate);
}
}
return matches;
}
function createPseudoCondition(){
var cStyle;
if(!basePseudos){
if(!baseContentElement){
baseContentElement = document.querySelector(includeConfig.contentElement);
}
if(baseContentElement){
cStyle = (getStyles(baseContentElement, ':after').getPropertyValue('content') || 'none').replace(regCleanPseudos, '');
basePseudos = {};
if(cStyle){
basePseudos[cStyle] = 1;
}
cStyle = (getStyles(baseContentElement, ':before').getPropertyValue('content') || 'none').replace(regCleanPseudos, '');
if(cStyle){
basePseudos[cStyle] = 1;
}
} else {
basePseudos = {};
}
}
}
function findCandidate(elem){
var i, candidate;
var includeData = elem.lazyInclude;
if(includeData && includeData.candidates){
for(i = 0; i < includeData.candidates.length; i++){
candidate = includeData.candidates[i];
if(matchesCondition(elem, candidate)){
break;
}
}
}
if(!candidate || candidate == includeData.current){
candidate = null;
}
return candidate;
}
function loadInclude(detail, includeCallback){
var request = new XMLHttpRequest();
request.addEventListener('readystatechange', function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE){
includeCallback(request);
request = null;
}
}, false);
request.open.apply(request, detail.openArgs);
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if(detail.xhrModifier){
detail.xhrModifier(request, detail.candidate);
}
request.send(detail.sendData);
}
function loadRequire(urls, callback){
urls = urls.split('|,|');
var last = urls.length - 1;
if(lazySizes.cfg.requireJs){
lazySizes.cfg.requireJs(urls, callback);
} else {
urls.forEach(function(url, index){
loadStyleScript(url, index == last ? callback : null);
});
}
}
function loadSystemJs(url, callback){
if(lazySizes.cfg.systemJs){
lazySizes.cfg.systemJs(url, callback);
} else {
loadStyleScript(url, callback);
}
}
function isStyleReady(link){
var ready = false;
var sheets = document.styleSheets;
var href = link.href;
for(var i = 0, length = sheets.length; i < length; i++){
if(sheets[i].href == href){
ready = true;
break;
}
}
return ready;
}
function loadStyleScript(url, isScript, cb){
if(!uniqueUrls[url]){
var elem = document.createElement(isScript === true ? 'script' : 'link');
var insertElem = document.getElementsByTagName('script')[0];
if(isScript){
elem.src = url;
elem.async = false;
} else {
elem.rel = 'stylesheet';
elem.href = url;
}
uniqueUrls[url] = [];
uniqueUrls[elem.href] = uniqueUrls[url];
if(cb){
var timer;
var load = function(e){
if(e.type == 'readystatechange' && !cssComplete[e.target.readyState]){return;}
var cbs = uniqueUrls[url];
elem.removeEventListener('load', load);
elem.removeEventListener('error', load);
elem.removeEventListener('readystatechange', load);
elem.removeEventListener('loadcssdefined', load);
if(timer){
clearInterval(timer);
}
uniqueUrls[url] = true;
uniqueUrls[elem.href] = true;
while(cbs.length){
cbs.shift()();
}
};
uniqueUrls[elem.href][0] = cb;
if(!isScript){
timer = setInterval(function(){
if(isStyleReady(elem)){
load({});
}
}, 60);
}
elem.addEventListener('load', load);
elem.addEventListener('error', load);
elem.addEventListener('readystatechange', load);
elem.addEventListener('loadcssdefined', load);
}
insertElem.parentNode.insertBefore(elem, insertElem);
} else if(cb){
if(uniqueUrls[url] === true){
setTimeout(cb);
} else {
uniqueUrls[url].push(cb);
}
}
}
function loadStyles(urls, cb){
urls = urls.split('|,|');
var last = urls.length - 1;
urls.forEach(function(url, index){
loadStyleScript(url, false, index == last ? cb : null);
});
}
function transformInclude(module){
if(module && typeof module.lazytransform == 'function'){
/*jshint validthis:true */
module.lazytransform(this);
}
}
function unloadModule(module){
if(module && typeof module.lazyunload == 'function'){
/*jshint validthis:true */
module.lazyunload(this);
}
}
function loadModule(module){
if(module && typeof module.lazyload == 'function'){
/*jshint validthis:true */
module.lazyload(this);
}
}
function loadCandidate(elem, candidate){
var include, xhrObj, modules, waitCss, runInclude;
var old = elem.lazyInclude.current || null;
var detail = {
candidate: candidate,
openArgs: ['GET', candidate.urls.include, true],
sendData: null,
xhrModifier: null,
content: candidate.content && candidate.content.content || candidate.content,
oldCandidate: old
};
var event = lazySizes.fire(elem, 'lazyincludeload', detail);
if(event.defaultPrevented){
queue.d();
return;
}
runInclude = function(){
if(xhrObj && modules && !waitCss){
include();
}
};
include = function(){
var event;
var status = xhrObj.status;
var content = xhrObj.content || xhrObj.responseText;
var reset = !!(content == null && old && old.urls.include);
var detail = {
candidate: candidate,
content: content,
text: xhrObj.responseText || xhrObj.content,
response: xhrObj.response,
xml: xhrObj.responseXML,
isSuccess: ('status' in xhrObj) ? status >= 200 && status < 300 || status === 304 : true,
oldCandidate: old,
insert: true,
resetHTML: reset
};
var moduleObj = {target: elem, details: detail, detail: detail};
candidate.modules = modules;
if(old && old.modules){
old.modules.forEach(unloadModule, moduleObj);
old.modules = null;
if(detail.resetHTML && detail.content == null && candidate.initial && candidate.initial.saved){
detail.content = candidate.initial.content;
}
}
modules.forEach(transformInclude, moduleObj);
event = lazySizes.fire(elem, 'lazyincludeloaded', detail);
if(detail.insert && detail.isSuccess && !event.defaultPrevented && detail.content != null && detail.content != elem.innerHTML){
elem.innerHTML = detail.content;
}
queue.d();
modules.forEach(loadModule, moduleObj);
setTimeout(function(){
lazySizes.fire(elem, 'lazyincluded', detail);
});
xhrObj = null;
modules = null;
};
elem.lazyInclude.current = candidate;
elem.setAttribute('data-currentinclude', candidate.name);
if(candidate.urls.css){
waitCss = true;
loadStyles(candidate.urls.css, function () {
waitCss = false;
runInclude();
});
}
if(detail.content == null && candidate.urls.include){
loadInclude(detail, function(data){
xhrObj = data;
runInclude();
});
} else {
xhrObj = detail;
}
if(candidate.urls.amd || candidate.urls.module){
var loadRequireImportCB = function(){
modules = Array.prototype.slice.call(arguments);
runInclude();
};
if(candidate.urls.amd){
loadRequire(candidate.urls.amd, loadRequireImportCB);
} else {
loadSystemJs(candidate.urls.module, loadRequireImportCB);
}
} else {
modules = [];
}
runInclude();
}
function findLoadCandidate(elem){
var candidate;
var includeData = getIncludeData(elem);
if(!includeData.candidates.length || !docElem.contains(elem) ){return;}
candidate = findCandidate(elem);
if(candidate){
loadCandidate(elem, candidate);
}
return true;
}
function beforeUnveil(e){
if(e.detail.instance != lazySizes || e.defaultPrevented || !e.target.getAttribute('data-include')){return;}
queue.q(e.target);
e.detail.firesLoad = true;
}
addEventListener('lazybeforeunveil', beforeUnveil, false);
addEventListener('resize', refreshIncludes, false);
addEventListener('lazyrefreshincludes', refreshIncludes, false);
}));
================================================
FILE: plugins/native-loading/README.md
================================================
# lazysizes native loading extension
This extension automatically transforms `img.lazyload`/`iframe.lazyload` elements in browsers that support native lazy loading.
## Usage
Simply include the plugin:
```js
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/native-loading/ls.native-loading';
```
And use normal lazySizes markup in combination with the `loading` attribute:
```html
```
## `nativeLoading` Options
Options are changed at the `lazySizes.cfg.nativeLoading` options object:
```js
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/native-loading/ls.native-loading';
lazySizes.cfg.nativeLoading = {
setLoadingAttribute: true,
disableListeners: {
scroll: true
},
};
```
### `setLoadingAttribute` `boolean` option
By setting `setLoadingAttribute` to `true`. LazySizes will automatically set the `loading="lazy"` attribute for you. `
This way all `img`/`iframe` elements will natively lazyloaded without any changes to your normal lazySizes markup.
### `disableListeners` `boolean`/`eventMap`
Due to the fact that you can use lazySizes for many things. Native lazy loading does not remove any event listeners automatically.
By setting `disableListeners` to `true` all events listeners are removed. Often it makes sense to only remove specific events like the scroll event for example.
The possible full event map looks like this:
```html
{
focus: true,
mouseover: true,
click: true,
load: true,
transitionend: true,
animationend: true,
scroll: true,
resize: true,
}
```
================================================
FILE: plugins/native-loading/ls.native-loading.js
================================================
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
'use strict';
var imgSupport = 'loading' in HTMLImageElement.prototype;
var iframeSupport = 'loading' in HTMLIFrameElement.prototype;
var isConfigSet = false;
var oldPrematureUnveil = lazySizes.prematureUnveil;
var cfg = lazySizes.cfg;
var listenerMap = {
focus: 1,
mouseover: 1,
click: 1,
load: 1,
transitionend: 1,
animationend: 1,
scroll: 1,
resize: 1,
};
if (!cfg.nativeLoading) {
cfg.nativeLoading = {};
}
if (!window.addEventListener || !window.MutationObserver || (!imgSupport && !iframeSupport)) {
return;
}
function disableEvents() {
var loader = lazySizes.loader;
var throttledCheckElements = loader.checkElems;
var removeALSL = function(){
setTimeout(function(){
window.removeEventListener('scroll', loader._aLSL, true);
}, 1000);
};
var currentListenerMap = typeof cfg.nativeLoading.disableListeners == 'object' ?
cfg.nativeLoading.disableListeners :
listenerMap;
if (currentListenerMap.scroll) {
window.addEventListener('load', removeALSL);
removeALSL();
window.removeEventListener('scroll', throttledCheckElements, true);
}
if (currentListenerMap.resize) {
window.removeEventListener('resize', throttledCheckElements, true);
}
Object.keys(currentListenerMap).forEach(function(name) {
if (currentListenerMap[name]) {
document.removeEventListener(name, throttledCheckElements, true);
}
});
}
function runConfig() {
if (isConfigSet) {return;}
isConfigSet = true;
if (imgSupport && iframeSupport && cfg.nativeLoading.disableListeners) {
if (cfg.nativeLoading.disableListeners === true) {
cfg.nativeLoading.setLoadingAttribute = true;
}
disableEvents();
}
if (cfg.nativeLoading.setLoadingAttribute) {
window.addEventListener('lazybeforeunveil', function(e){
var element = e.target;
if ('loading' in element && !element.getAttribute('loading')) {
element.setAttribute('loading', 'lazy');
}
}, true);
}
}
lazySizes.prematureUnveil = function prematureUnveil(element) {
if (!isConfigSet) {
runConfig();
}
if ('loading' in element &&
(cfg.nativeLoading.setLoadingAttribute || element.getAttribute('loading')) &&
(element.getAttribute('data-sizes') != 'auto' || element.offsetWidth)) {
return true;
}
if (oldPrematureUnveil) {
return oldPrematureUnveil(element);
}
};
}));
================================================
FILE: plugins/noscript/README.md
================================================
# lazysizes noscript/progressive enhancement extension
The noscript extension is the true ultimate progressive enhancement extension for lazySizes. It allows you to transform any HTML inside a ``noscript`` element as soon as it becomes visible.
## Markup
The ``lazyload`` class has to be added to the parent element of the ``noscript`` element and this element has to also have a ``data-noscript`` attribute. As soon as it is near the viewport the content of the ``noscript`` element will replace the content of the ``.lazyload` element.
```html
```
**Important note**: While you also can transform responsive images with this plugin, neither the ``data-sizes`` nor the ``customMedia`` features do work with the ``noscript`` extension. Also note: Android 2.x is not supported with this plugin.
## Troubleshooting: Escaped HTML entities
Normally the content of a ``noscript`` must be retrieved as text. But in some cases for example, if the ``noscript`` element was created in a XML documented/context, it must be retrieved as HTML. This can't be automatically detected.
In case this happens, you can fix this either by making sure that ``noscript`` elements are always created in a *text/html* context or by overriding the ``getNoscriptContent`` option callback:
```js
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.getNoscriptContent = function(noScript){
return (noScript.isXML) ? noScript.innerHTML : (noScript.textContent || noScript.innerText);
};
```
## Add IE8- support with conditional comments
The noscript extension can also be used in conjunction with conditional comments to add progressive enhancement support for IE8-:
```html
any kind of content you want to be unveiled
```
================================================
FILE: plugins/noscript/ls.noscript.js
================================================
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var dummyParent = {nodeName: ''};
var supportPicture = !!window.HTMLPictureElement && ('sizes' in document.createElement('img'));
var config = window.lazySizes && lazySizes.cfg;
var handleLoadingElements = function(e){
var i, isResponsive, hasTriggered, onload, loading;
var loadElements = e.target.querySelectorAll('img, iframe');
for(i = 0; i < loadElements.length; i++){
isResponsive = loadElements[i].getAttribute('srcset') || (loadElements[i].parentNode || dummyParent).nodeName.toLowerCase() == 'picture';
if(!supportPicture && isResponsive){
lazySizes.uP(loadElements[i]);
}
if(!loadElements[i].complete && (isResponsive || loadElements[i].src)){
e.detail.firesLoad = true;
if(!onload || !loading){
loading = 0;
/*jshint loopfunc:true */
onload = function(evt){
loading--;
if((!evt || loading < 1) && !hasTriggered){
hasTriggered = true;
e.detail.firesLoad = false;
lazySizes.fire(e.target, '_lazyloaded', {}, false, true);
}
if(evt && evt.target){
evt.target.removeEventListener('load', onload);
evt.target.removeEventListener('error', onload);
}
};
setTimeout(onload, 3500);
}
loading++;
loadElements[i].addEventListener('load', onload);
loadElements[i].addEventListener('error', onload);
}
}
};
config.getNoscriptContent = function(noScript){
return noScript.textContent || noScript.innerText;
};
window.addEventListener('lazybeforeunveil', function(e){
if(e.detail.instance != lazySizes || e.defaultPrevented || e.target.getAttribute('data-noscript') == null){return;}
var noScript = e.target.querySelector('noscript, script[type*="html"]') || {};
var content = config.getNoscriptContent(noScript);
if(content){
e.target.innerHTML = content;
handleLoadingElements(e);
}
});
}));
================================================
FILE: plugins/object-fit/README.md
================================================
# lazySizes object fit extension
This extension polyfills `object-fit`: `cover` and `contain` properties as also the `object-position` in non supporting browsers. Here you find a [simple demo](https://jsfiddle.net/trixta/x2p17f31/).
## Usage
### Include JS files:
Include lazysizes and lazysizes object fit and optionally lazysizes [parent-fit](../parent-fit) and lazysizes respimg plugin. Lazysizes object-fit and respimg plugin are only needed in browser that don't support object fit or responsive images. Lazysizes parent-fit is recommended if you use object fit responsive images in combination with `data-sizes="auto"`.
```html
```
```js
// never try to import *.min.js files
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/parent-fit/ls.parent-fit';
// polyfills
import 'lazysizes/plugins/respimg/ls.respimg';
if (!('object-fit' in document.createElement('a').style)) {
require('lazysizes/plugins/object-fit/ls.object-fit');
}
```
### Add markup
The object-fit plugin is not a full polyfill.
```html
/>
/>
```
### CSS
To init the plugin on an image simply use the `font-family` property directly on your image.
```css
.imagecontainer {
position: relative;
border: 3px solid #ccc;
}
.imagecontainer:before {
display: block;
width: 100%;
content: "";
padding-bottom: 100%;
height: 0;
}
.imagecontainer-img {
position: absolute;
display: block;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: 400ms transform;
object-fit: contain;
font-family: "object-fit: contain";
}
.imagecontainer-img:hover {
transform: scale(1.1);
}
```
================================================
FILE: plugins/object-fit/ls.object-fit.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(initialEvent){
factory(window.lazySizes, initialEvent);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes, initialEvent) {
'use strict';
var cloneElementClass;
var style = document.createElement('a').style;
var fitSupport = 'objectFit' in style;
var positionSupport = fitSupport && 'objectPosition' in style;
var regCssFit = /object-fit["']*\s*:\s*["']*(contain|cover)/;
var regCssPosition = /object-position["']*\s*:\s*["']*(.+?)(?=($|,|'|"|;))/;
var blankSrc = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
var regBgUrlEscape = /\(|\)|'/;
var positionDefaults = {
center: 'center',
'50% 50%': 'center',
};
function getObject(element){
var css = (getComputedStyle(element, null) || {});
var content = css.fontFamily || '';
var objectFit = content.match(regCssFit) || '';
var objectPosition = objectFit && content.match(regCssPosition) || '';
if(objectPosition){
objectPosition = objectPosition[1];
}
return {
fit: objectFit && objectFit[1] || '',
position: positionDefaults[objectPosition] || objectPosition || 'center',
};
}
function generateStyleClass() {
if (cloneElementClass) {
return;
}
var styleElement = document.createElement('style');
cloneElementClass = lazySizes.cfg.objectFitClass || 'lazysizes-display-clone';
document.querySelector('head').appendChild(styleElement);
}
function removePrevClone(element) {
var prev = element.previousElementSibling;
if (prev && lazySizes.hC(prev, cloneElementClass)) {
prev.parentNode.removeChild(prev);
element.style.position = prev.getAttribute('data-position') || '';
element.style.visibility = prev.getAttribute('data-visibility') || '';
}
}
function initFix(element, config){
var switchClassesAdded, addedSrc, styleElement, styleElementStyle;
var lazysizesCfg = lazySizes.cfg;
var onChange = function(){
var src = element.currentSrc || element.src;
if(src && addedSrc !== src){
addedSrc = src;
styleElementStyle.backgroundImage = 'url(' + (regBgUrlEscape.test(src) ? JSON.stringify(src) : src ) + ')';
if(!switchClassesAdded){
switchClassesAdded = true;
lazySizes.rC(styleElement, lazysizesCfg.loadingClass);
lazySizes.aC(styleElement, lazysizesCfg.loadedClass);
}
}
};
var rafedOnChange = function(){
lazySizes.rAF(onChange);
};
element._lazysizesParentFit = config.fit;
element.addEventListener('lazyloaded', rafedOnChange, true);
element.addEventListener('load', rafedOnChange, true);
lazySizes.rAF(function(){
var hideElement = element;
var container = element.parentNode;
if(container.nodeName.toUpperCase() == 'PICTURE'){
hideElement = container;
container = container.parentNode;
}
removePrevClone(hideElement);
if (!cloneElementClass) {
generateStyleClass();
}
styleElement = element.cloneNode(false);
styleElementStyle = styleElement.style;
styleElement.addEventListener('load', function(){
var curSrc = styleElement.currentSrc || styleElement.src;
if(curSrc && curSrc != blankSrc){
styleElement.src = blankSrc;
styleElement.srcset = '';
}
});
lazySizes.rC(styleElement, lazysizesCfg.loadedClass);
lazySizes.rC(styleElement, lazysizesCfg.lazyClass);
lazySizes.rC(styleElement, lazysizesCfg.autosizesClass);
lazySizes.aC(styleElement, lazysizesCfg.loadingClass);
lazySizes.aC(styleElement, cloneElementClass);
['data-parent-fit', 'data-parent-container', 'data-object-fit-polyfilled',
lazysizesCfg.srcsetAttr, lazysizesCfg.srcAttr].forEach(function(attr) {
styleElement.removeAttribute(attr);
});
styleElement.src = blankSrc;
styleElement.srcset = '';
styleElementStyle.backgroundRepeat = 'no-repeat';
styleElementStyle.backgroundPosition = config.position;
styleElementStyle.backgroundSize = config.fit;
styleElement.setAttribute('data-position', hideElement.style.position);
styleElement.setAttribute('data-visibility', hideElement.style.visibility);
hideElement.style.visibility = 'hidden';
hideElement.style.position = 'absolute';
element.setAttribute('data-parent-fit', config.fit);
element.setAttribute('data-parent-container', 'prev');
element.setAttribute('data-object-fit-polyfilled', '');
element._objectFitPolyfilledDisplay = styleElement;
container.insertBefore(styleElement, hideElement);
if(element._lazysizesParentFit){
delete element._lazysizesParentFit;
}
if(element.complete){
onChange();
}
});
}
if(!fitSupport || !positionSupport){
var onRead = function(e){
if(e.detail.instance != lazySizes){return;}
var element = e.target;
var obj = getObject(element);
if(obj.fit && (!fitSupport || (obj.position != 'center'))){
initFix(element, obj);
return true;
}
return false;
};
window.addEventListener('lazybeforesizes', function(e) {
if(e.detail.instance != lazySizes){return;}
var element = e.target;
if (element.getAttribute('data-object-fit-polyfilled') != null && !element._objectFitPolyfilledDisplay) {
if(!onRead(e)){
lazySizes.rAF(function () {
element.removeAttribute('data-object-fit-polyfilled');
});
}
}
});
window.addEventListener('lazyunveilread', onRead, true);
if(initialEvent && initialEvent.detail){
onRead(initialEvent);
}
}
}));
================================================
FILE: plugins/optimumx/README.md
================================================
# lazysizes optimumx plugin
**lazysizes** optimumx plugin helps you to limit/constrain the maximum resolution in case the **w descriptor** is used. Simply add the attribute ``data-optimumx="1.6"`` to constrain the max resolution to 1.6.
It gives you therefore more control to trade perceived quality against perceived performance on HD retina devices, than the HTML responsive image standard gives you.
This plugin depends on the ``data-sizes="auto"`` feature of **lazysizes**.
```html
```
A **simple [demo can be seen here](http://afarkas.github.io/lazysizes/optimumx/)**. This extension also supports art-directed responsive images using the ``picture`` element.
## Usage
```html
```
Or include via import:
```js
// never try to import *.min.js files
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/optimumx/ls.optimumx';
```
In case you want to use a CDN you can use the combohandler service provided by jsDelivr:
```html
```
**Note**: For full cross-browser support either a [responsive images polyfill like respimage or picturefill](https://github.com/aFarkas/respimage) or the [neat Responsive Images as a Service extension (RIaS)](../rias) or the [extreme lightweight alternate mini respimg polyfill extension](../respimg) needs to be used.
```html
```
```html
```
### The ``getOptimumX`` option callback
Normally the image specific optimal pixel density should be added as a floating point number using the ``data-optimumx`` attribute. Additionally it is also possible to add the ``"auto"`` keyword as a value of the ``data-optimumx`` attribute. In that case the ``getOptimumX`` option callback is invoked with the element as the first argument.
```html
```
The predefined (ugly) ``getOptimumX`` callback looks like this:
```js
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.config.getOptimumX = function(/*element*/){
var dpr = window.devicePixelRatio || 1;
if(dpr > 2.5){
dpr *= 0.6; // returns 1.8 for 3
} else if(dpr > 1.9){
dpr *= 0.8; // returns 1.6 for 2
} else {
dpr -= 0.01;
}
return Math.min(Math.round(dpr * 100) / 100, 2);
};
```
### The `constrainPixelDensity` option
In case the `constrainPixelDensity` is set to `true`. All images without a `data-optimumx` attribute are treated as they would have a `data-optimumx="auto"` attribute.
```html
```
## Background information: Compressive picture pattern
From a perceived performance vs. perceived quality standpoint the best way to deal with High DPI images is to serve higher compressed candidates to clients with high resolution displays.
This is due to the fact, that on higher DPI displays small details can be compressed more aggressively.
For native images support the ``picture`` element can be used to achieve the result:
```html
```
Or in case you are using the [Responsive Images as a Service extension (RIaS)](../rias):
```html
```
Unfortunately these techniques also double or even triple (think 1x: 65-85q, 2x: 30-50q, 3x/4x 15-40q) the amount of generated image candidates. In case you don't have so much resources the optimumx extension in conjunction with proper image compression is the best thing you can do.
But be aware each image has different characteristics: While some images look great on a HIGH DPI device even with a ``data-optimumx="1.2"`` other will need a much higher density for a good perceived quality.
## Background information: Lying sizes attribute
There is also another much more lightweight way to get a similar effect. Instead of parsing and constraining the ``srcset`` to meet the ``data-optimumx`` constraint, there is also the possibility to modify the ``sizes`` attribute instead.
A ``data-optimumx`` implementation with the ``lazybeforesizes`` event could then look something like this:
```html
```
Compared to the size of this plugin this is a very neat, simple and lightweight technique.
But this technique should be used with caution because the browsers algorithm is tricked and operates with wrong values, which can result in unpredictable and bad results.
In case the ``sizes`` attribute is faked to a lower value and the browser already wants to select a lower candidate, (because the device has a low or metered bandwidth) the browser might choose an unfeasible image candidate instead.
In case the ``sizes`` attribute is faked to a higher value and the browser already wants to select a higher candidate, (because the user has zoomed into this particular image) the browser might be tricked to download a much heavier image candidate than the device actually needs.
But still this technique can be sometimes used to tell the browser **small** lies.
================================================
FILE: plugins/optimumx/ls.optimumx.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
if(!window.addEventListener){return;}
var config;
var regPicture = /^picture$/i;
var docElem = document.documentElement;
var parseWsrcset = (function(){
var candidates;
var reg = /(([^,\s].[^\s]+)\s+(\d+)(w|h)(\s+(\d+)(w|h))?)/g;
var addCandidate = function(match, candidate, url, descNumber1, descType1, fullDesc, descNumber2, descType2){
candidates.push({
c: candidate,
u: url,
w: (descType2 == 'w' ? descNumber2 : descNumber1) * 1
});
};
return function(input){
candidates = [];
input.replace(reg, addCandidate);
return candidates;
};
})();
var parseImg = (function(){
var ascendingSort = function ( a, b ) {
return a.w - b.w;
};
var parseSets = function (elem, dataName){
var lazyData = {srcset: elem.getAttribute(lazySizes.cfg.srcsetAttr) || ''};
var cands = parseWsrcset(lazyData.srcset);
Object.defineProperty(elem, dataName, {
value: lazyData,
writable: true
});
lazyData.cands = cands;
lazyData.index = 0;
lazyData.dirty = false;
if(cands[0] && cands[0].w){
cands.sort( ascendingSort );
lazyData.cSrcset = [cands[ lazyData.index ].c];
} else {
lazyData.cSrcset = lazyData.srcset ? [lazyData.srcset] : [];
lazyData.cands = [];
}
return lazyData;
};
return function parseImg(elem, dataName){
var sources, i, len, parent;
if(!elem[dataName]){
parent = elem.parentNode || {};
elem[dataName] = parseSets(elem, dataName);
elem[dataName].isImg = true;
if(regPicture.test(parent.nodeName || '')){
elem[dataName].picture = true;
sources = parent.getElementsByTagName('source');
for(i = 0, len = sources.length; i < len; i++){
parseSets(sources[i], dataName).isImg = false;
}
}
}
return elem[dataName];
};
})();
var constraintFns = {
_lazyOptimumx: (function(){
var takeHighRes = function (lowerCandidate, higherCandidateResolution, optimumx){
var low, bonusFactor, substract;
if(!lowerCandidate || !lowerCandidate.d){
return true;
}
substract = optimumx > 0.7 ? 0.6 : 0.4;
if(lowerCandidate.d >= optimumx){return false;}
bonusFactor = Math.pow(lowerCandidate.d - substract, 1.6) || 0.1;
if(bonusFactor < 0.1){
bonusFactor = 0.1;
} else if(bonusFactor > 3){
bonusFactor = 3;
}
low = lowerCandidate.d + ((higherCandidateResolution - optimumx) * bonusFactor);
return low < optimumx;
};
return function (data, width, optimumx){
var i, can;
for(i = 0; i < data.cands.length; i++){
can = data.cands[i];
can.d = (can.w || 1) / width;
if(data.index >= i){continue;}
if(can.d <= optimumx || takeHighRes(data.cands[i - 1], can.d, optimumx)){
data.cSrcset.push(can.c);
data.index = i;
} else {
break;
}
}
};
})()
};
var constrainSets = (function(){
var constrainSet = function(elem, displayWidth, optimumx, attr, dataName){
var curIndex;
var lazyData = elem[dataName];
if(!lazyData){return;}
curIndex = lazyData.index;
constraintFns[dataName](lazyData, displayWidth, optimumx);
if(!lazyData.dirty || curIndex != lazyData.index){
lazyData.cSrcset.join(', ');
elem.setAttribute(attr, lazyData.cSrcset.join(', '));
lazyData.dirty = true;
}
};
return function(image, displayWidth, optimumx, attr, dataName){
var sources, parent, len, i;
var lazyData = image[dataName];
lazyData.width = displayWidth;
if(lazyData.picture && (parent = image.parentNode)){
sources = parent.getElementsByTagName('source');
for(i = 0, len = sources.length; i < len; i++){
constrainSet(sources[i], displayWidth, optimumx, attr, dataName);
}
}
constrainSet(image, displayWidth, optimumx, attr, dataName);
};
})();
var getOptimumX = function(element){
var optimumx = element.getAttribute('data-optimumx') || element.getAttribute('data-maxdpr');
if(!optimumx && config.constrainPixelDensity){
optimumx = 'auto';
}
if(optimumx){
if(optimumx == 'auto'){
optimumx = config.getOptimumX(element);
} else {
optimumx = parseFloat(optimumx, 10);
}
}
return optimumx;
};
var extentLazySizes = function(){
if(lazySizes && !lazySizes.getOptimumX){
lazySizes.getX = getOptimumX;
lazySizes.pWS = parseWsrcset;
docElem.removeEventListener('lazybeforeunveil', extentLazySizes);
}
};
docElem.addEventListener('lazybeforeunveil', extentLazySizes);
setTimeout(extentLazySizes);
config = lazySizes && lazySizes.cfg;
if(typeof config.getOptimumX != 'function'){
config.getOptimumX = function(/*element*/){
var dpr = window.devicePixelRatio || 1;
if(dpr > 2.6){
dpr *= 0.6; // returns 1.8 for 3
} else if(dpr > 1.9){
dpr *= 0.8; // returns 1.6 for 2
} else {
dpr -= 0.01; // returns 0.99 for 1
}
return Math.min(Math.round(dpr * 100) / 100, 2);
};
}
if(!window.devicePixelRatio){return;}
addEventListener('lazybeforesizes', function(e){
if(e.detail.instance != lazySizes){return;}
var optimumx, lazyData, width, attr;
var elem = e.target;
var detail = e.detail;
var dataAttr = detail.dataAttr;
if(e.defaultPrevented ||
!(optimumx = getOptimumX(elem)) ||
optimumx >= devicePixelRatio){return;}
if(dataAttr && elem._lazyOptimumx && !detail.reloaded && (!config.unloadedClass || !lazySizes.hC(elem, config.unloadedClass))){
elem._lazyOptimumx = null;
}
lazyData = parseImg(elem, '_lazyOptimumx');
width = detail.width;
if(width && (lazyData.width || 0) < width){
attr = dataAttr ? lazySizes.cfg.srcsetAttr : 'srcset';
lazySizes.rAF(function(){
constrainSets(elem, width, optimumx, attr, '_lazyOptimumx');
});
}
});
}));
================================================
FILE: plugins/parent-fit/README.md
================================================
# lazySizes parent fit extension
The parent fit plugin extends the ``data-sizes="auto"`` feature to also calculate the right ``sizes`` for ``object-fit: contain|cover`` image elements as also **height** ( and width) constrained image elements in general.
## Usage
```js
// never try to import *.min.js files
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/parent-fit/ls.parent-fit';
```
For this to work properly the physical aspect-ratio of the image candidates need to be calculable. To do so either a `data-aspectratio` attribute has to be provided on the `source`/`img` element(s) or through `width` and `height` content attributes or at least one of the image candidates inside the ``srcset`` attribute also need to include a **h** (height) descriptor. (MS Edge has problems to read image candidates using the h descriptor, which is fixed by the [respimg polyfill](../respimg))
### object-fit: contain|cover usage
Simply include this plugin, combine your width descriptors with height descriptors and use ``object-fit``. (To get object-fit support into IE9-11 use the [object-fit polyfill](../object-fit).)
```html
```
### [data-parent-fit="contain|cover|width"] usage
This plugin also supports calculating height and width constrained images based on a parent element.
To do so include this plugin, combine your width descriptors with height descriptors and add the attribute ``data-parent-fit`` with either ``"contain"`` or ``"cover"`` as the keyword.
```html
```
In case the *width* keyword is used, lazySizes simply takes the width of the parent container instead of the ``img`` element itself. In this case a **h** descriptor isn't necessary.
### [data-parent-container="html|.my-image-container"]
Normally the next closest parent that is not a `picture` element is used as the parent (i.e.: `:not(picture)`). This can be changed using the `data-parent-container` option. It takes any simple selector. If you want to use the viewport as the parent simply add `html`.
As a special keyword the value `self` can be used to signalize, that image itself should be taken.
### Controlling `data-parent-fit` and `data-parent-container` with CSS
These option can also be set via CSS by abusing the `font-family` property.
The `data-parent-fit` option is called here `parent-fit` and `data-parent-container` is called `parent-container`:
```css
img.my-image {
font-family: parent-container: html; parent-fit: contain;
}
```
**Note: This plugin should be also added, if you use the [bgset plugin](../bgset/) in combination with ``data-sizes="auto"`` and ``background-size: cover|contain`` and it is also the base of the [object-fit polyfill plugin](../object-fit).**
================================================
FILE: plugins/parent-fit/ls.parent-fit.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
'use strict';
if(!window.addEventListener){return;}
var regDescriptors = /\s+(\d+)(w|h)\s+(\d+)(w|h)/;
var regCssFit = /parent-fit["']*\s*:\s*["']*(contain|cover|width)/;
var regCssObject = /parent-container["']*\s*:\s*["']*(.+?)(?=(\s|$|,|'|"|;))/;
var regPicture = /^picture$/i;
var cfg = lazySizes.cfg;
var getCSS = function (elem){
return (getComputedStyle(elem, null) || {});
};
var parentFit = {
getParent: function(element, parentSel){
var parent = element;
var parentNode = element.parentNode;
if((!parentSel || parentSel == 'prev') && parentNode && regPicture.test(parentNode.nodeName || '')){
parentNode = parentNode.parentNode;
}
if(parentSel != 'self'){
if(parentSel == 'prev'){
parent = element.previousElementSibling;
} else if(parentSel && (parentNode.closest || window.jQuery)){
parent = (parentNode.closest ?
parentNode.closest(parentSel) :
jQuery(parentNode).closest(parentSel)[0]) ||
parentNode
;
} else {
parent = parentNode;
}
}
return parent;
},
getFit: function(element){
var tmpMatch, parentObj;
var css = getCSS(element);
var content = css.content || css.fontFamily;
var obj = {
fit: element._lazysizesParentFit || element.getAttribute('data-parent-fit')
};
if(!obj.fit && content && (tmpMatch = content.match(regCssFit))){
obj.fit = tmpMatch[1];
}
if(obj.fit){
parentObj = element._lazysizesParentContainer || element.getAttribute('data-parent-container');
if(!parentObj && content && (tmpMatch = content.match(regCssObject))){
parentObj = tmpMatch[1];
}
obj.parent = parentFit.getParent(element, parentObj);
} else {
obj.fit = css.objectFit;
}
return obj;
},
getImageRatio: function(element){
var i, srcset, media, ratio, match, width, height;
var parent = element.parentNode;
var elements = parent && regPicture.test(parent.nodeName || '') ?
parent.querySelectorAll('source, img') :
[element]
;
for(i = 0; i < elements.length; i++){
element = elements[i];
srcset = element.getAttribute(cfg.srcsetAttr) || element.getAttribute('srcset') || element.getAttribute('data-pfsrcset') || element.getAttribute('data-risrcset') || '';
media = element._lsMedia || element.getAttribute('media');
media = cfg.customMedia[element.getAttribute('data-media') || media] || media;
if(srcset && (!media || (window.matchMedia && matchMedia(media) || {}).matches )){
ratio = parseFloat(element.getAttribute('data-aspectratio'));
if (!ratio) {
match = srcset.match(regDescriptors);
if (match) {
if(match[2] == 'w'){
width = match[1];
height = match[3];
} else {
width = match[3];
height = match[1];
}
} else {
width = element.getAttribute('width');
height = element.getAttribute('height');
}
ratio = width / height;
}
break;
}
}
return ratio;
},
calculateSize: function(element, width){
var displayRatio, height, imageRatio, retWidth;
var fitObj = this.getFit(element);
var fit = fitObj.fit;
var fitElem = fitObj.parent;
if(fit != 'width' && ((fit != 'contain' && fit != 'cover') || !(imageRatio = this.getImageRatio(element)))){
return width;
}
if(fitElem){
width = fitElem.clientWidth;
} else {
fitElem = element;
}
retWidth = width;
if(fit == 'width'){
retWidth = width;
} else {
height = fitElem.clientHeight;
if((displayRatio = width / height) && ((fit == 'cover' && displayRatio < imageRatio) || (fit == 'contain' && displayRatio > imageRatio))){
retWidth = width * (imageRatio / displayRatio);
}
}
return retWidth;
}
};
lazySizes.parentFit = parentFit;
document.addEventListener('lazybeforesizes', function(e){
if(e.defaultPrevented || e.detail.instance != lazySizes){return;}
var element = e.target;
e.detail.width = parentFit.calculateSize(element, e.detail.width);
});
}));
================================================
FILE: plugins/print/README.md
================================================
# lazysizes print extension
This simple print plugin for lazysizes will automatically unveil all element as soon as a print is detected even if the given lazyload image isn't in the viewport.
================================================
FILE: plugins/print/ls.print.js
================================================
/*
This lazySizes extension adds better support for print.
In case the user starts to print lazysizes will load all images.
*/
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var config, elements, onprint, printMedia;
// see also: http://tjvantoll.com/2012/06/15/detecting-print-requests-with-javascript/
if(window.addEventListener){
config = lazySizes && lazySizes.cfg;
elements = config.lazyClass || 'lazyload';
onprint = function(){
var i, len;
if(typeof elements == 'string'){
elements = document.getElementsByClassName(elements);
}
if(lazySizes){
for(i = 0, len = elements.length; i < len; i++){
lazySizes.loader.unveil(elements[i]);
}
}
};
addEventListener('beforeprint', onprint, false);
if(!('onbeforeprint' in window) && window.matchMedia && (printMedia = matchMedia('print')) && printMedia.addListener){
printMedia.addListener(function(){
if(printMedia.matches){
onprint();
}
});
}
}
}));
================================================
FILE: plugins/progressive/README.md
================================================
# lazysizes progressive extension
This plugin optimizes perceived performance by adding better support for rendering progressive JPGs/PNGs in conjunction with the LQIP pattern.
## Demo
- [Watch video](http://www.webpagetest.org/video/view.php?id=150207_0d904cee5186ebf124d4e3014aa5df39895618f0)
- Or try yourself: [lazysizes + progressive plugin](http://codepen.io/jschroeter/full/NPwXNv) vs. [lazysizes](http://codepen.io/jschroeter/full/MYOrjB)
## How it works
By default, [browsers don't render images progressively when switching from one image to another](http://w3facility.org/question/progressive-jpeg-isnt-progressive-when-changing-image-src-dynamically/) (e.g. changing the `src` or adding `srcset`).
When lazysizes detects the image gets visible, this plugin will remove the `src` attribute and insert it as a background image until the image from `srcset` is completely loaded.
So when using the LQIP pattern, the low quality placeholder will stay visible and the high quality image will render progressively on top of it.
This looks especially nice for large images on slow connections.
## Requirements
- Use [LQIP pattern](https://github.com/aFarkas/lazysizes#lqip)
- Make sure your JPGs/PNGs are saved with the progressive/interlaced option: [Online Progressive JPEG checker](http://highloadtools.com/progressivejpeg)
## Browser support
All browsers with native `srcset` support. Successfully tested on
- Chrome
- Chrome for Android
- Native Android Browser 4.4
Actually it works great in Firefox too, but currently Firefox shows an annoying broken image icon for a few seconds after removing the `src` attribute. To prevent that, the plugin is disabled for browsers without native `srcset` support. Hopefully this issue will be gone when Firefox gets native `srcset` support.
================================================
FILE: plugins/progressive/ls.progressive.js
================================================
/*
This lazysizes plugin optimizes perceived performance by adding better support for rendering progressive JPGs/PNGs in conjunction with the LQIP pattern.
*/
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var regImg, onLoad;
if('srcset' in document.createElement('img')){
regImg = /^img$/i;
onLoad = function(e){
e.target.style.backgroundSize = '';
e.target.style.backgroundImage = '';
e.target.removeEventListener(e.type, onLoad);
};
document.addEventListener('lazybeforeunveil', function(e){
if(e.detail.instance != lazySizes){return;}
var img = e.target;
if(!regImg.test(img.nodeName)){
return;
}
var src = img.getAttribute('src');
if(src) {
img.style.backgroundSize = '100% 100%';
img.style.backgroundImage = 'url(' + src + ')';
img.removeAttribute('src');
img.addEventListener('load', onLoad);
}
}, false);
}
}));
================================================
FILE: plugins/respimg/README.md
================================================
# lazysizes respimg polyfill extension
While [picturefill](https://github.com/scottjehl/picturefill) are full functional responsive images polyfills, the lazySizes respimg polyfill extension is only a partial polyfill, which supports only the most important subset of the native responsive images standard and only in conjunction with the lazySizes core script.
As a result it is an extreme fast and lightweight plugin.
```js
// never try to import *.min.js files
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/respimg/ls.respimg';
```
## constrained Markup support
This plugin supports both art directed responsive images using the ``picture`` element as also resolution switching based on ``data-srcset`` using the width descriptor (and of course the combination of both).
### What is *not* supported:
- The use of explicit density descriptors (**x** descriptor) are not supported (This should not be a problem, because all use cases of the density descriptor can always also be substituted with a width descriptor).
- If ``data-srcset`` with width descriptors (**w** descriptor) are used either the ``data-sizes="auto"`` feature has to be used or the ``sizes`` value has to consist of just one source size value with the CSS *px* unit.
- If picture is used the ``img`` element should not have a ``srcset``/``data-srcset`` attribute, instead the last ``source`` element should/can be used without a ``media`` and ``type`` attribute.
- The use of the ``source[type]`` attribute is not automatically supported, but can be manually added by overriding the ``lazySizesConfig.supportsType`` option callback function.
- The use of the ``source[media]`` is supported for all browsers, which [do support ``matchMedia``](http://caniuse.com/#search=matchMedia). To add full support for IE9 and other legacy browsers a [``window.matchMedia`` polyfill](https://github.com/paulirish/matchMedia.js/) or ``Modernizr.mq`` (Modernizr Media Queries) can be used.
### What is *fully* supported
Aside from above mentioned constraints everything else is fully supported. Here are some practical examples of fully supported responsive images:
```html
```
### Tip: Using/Generating more complex dynamic ``sizes``
As explained above this partial polyfill only accepts one value for ``sizes`` using only the *px* length. Due to the fact, that also ``data-sizes="auto"`` is supported the ``lazybeforesizes`` event can be used to dynamically change/add different ``sizes``:
```js
document.addEventListener('lazybeforesizes', function(e){
//calculate the size as a number
e.detail.width = yourCalculation(e.target) || e.detail.width;
});
```
================================================
FILE: plugins/respimg/ls.respimg.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var polyfill;
var lazySizesCfg = lazySizes.cfg;
var img = document.createElement('img');
var supportSrcset = ('sizes' in img) && ('srcset' in img);
var regHDesc = /\s+\d+h/g;
var fixEdgeHDescriptor = (function(){
var regDescriptors = /\s+(\d+)(w|h)\s+(\d+)(w|h)/;
var forEach = Array.prototype.forEach;
return function(){
var img = document.createElement('img');
var removeHDescriptors = function(source){
var ratio, match;
var srcset = source.getAttribute(lazySizesCfg.srcsetAttr);
if(srcset){
if((match = srcset.match(regDescriptors))){
if(match[2] == 'w'){
ratio = match[1] / match[3];
} else {
ratio = match[3] / match[1];
}
if(ratio){
source.setAttribute('data-aspectratio', ratio);
}
source.setAttribute(lazySizesCfg.srcsetAttr, srcset.replace(regHDesc, ''));
}
}
};
var handler = function(e){
if(e.detail.instance != lazySizes){return;}
var picture = e.target.parentNode;
if(picture && picture.nodeName == 'PICTURE'){
forEach.call(picture.getElementsByTagName('source'), removeHDescriptors);
}
removeHDescriptors(e.target);
};
var test = function(){
if(!!img.currentSrc){
document.removeEventListener('lazybeforeunveil', handler);
}
};
document.addEventListener('lazybeforeunveil', handler);
img.onload = test;
img.onerror = test;
img.srcset = 'data:,a 1w 1h';
if(img.complete){
test();
}
};
})();
if(!lazySizesCfg.supportsType){
lazySizesCfg.supportsType = function(type/*, elem*/){
return !type;
};
}
if (window.HTMLPictureElement && supportSrcset) {
if(!lazySizes.hasHDescriptorFix && document.msElementsFromPoint){
lazySizes.hasHDescriptorFix = true;
fixEdgeHDescriptor();
}
return;
}
if(window.picturefill || lazySizesCfg.pf){return;}
lazySizesCfg.pf = function(options){
var i, len;
if(window.picturefill){return;}
for(i = 0, len = options.elements.length; i < len; i++){
polyfill(options.elements[i]);
}
};
// partial polyfill
polyfill = (function(){
var ascendingSort = function( a, b ) {
return a.w - b.w;
};
var regPxLength = /^\s*\d+\.*\d*px\s*$/;
var reduceCandidate = function (srces) {
var lowerCandidate, bonusFactor;
var len = srces.length;
var candidate = srces[len -1];
var i = 0;
for(i; i < len;i++){
candidate = srces[i];
candidate.d = candidate.w / srces.w;
if(candidate.d >= srces.d){
if(!candidate.cached && (lowerCandidate = srces[i - 1]) &&
lowerCandidate.d > srces.d - (0.13 * Math.pow(srces.d, 2.2))){
bonusFactor = Math.pow(lowerCandidate.d - 0.6, 1.6);
if(lowerCandidate.cached) {
lowerCandidate.d += 0.15 * bonusFactor;
}
if(lowerCandidate.d + ((candidate.d - srces.d) * bonusFactor) > srces.d){
candidate = lowerCandidate;
}
}
break;
}
}
return candidate;
};
var parseWsrcset = (function(){
var candidates;
var regWCandidates = /(([^,\s].[^\s]+)\s+(\d+)w)/g;
var regMultiple = /\s/;
var addCandidate = function(match, candidate, url, wDescriptor){
candidates.push({
c: candidate,
u: url,
w: wDescriptor * 1
});
};
return function(input){
candidates = [];
input = input.trim();
input
.replace(regHDesc, '')
.replace(regWCandidates, addCandidate)
;
if(!candidates.length && input && !regMultiple.test(input)){
candidates.push({
c: input,
u: input,
w: 99
});
}
return candidates;
};
})();
var runMatchMedia = function(){
if(runMatchMedia.init){return;}
runMatchMedia.init = true;
addEventListener('resize', (function(){
var timer;
var matchMediaElems = document.getElementsByClassName('lazymatchmedia');
var run = function(){
var i, len;
for(i = 0, len = matchMediaElems.length; i < len; i++){
polyfill(matchMediaElems[i]);
}
};
return function(){
clearTimeout(timer);
timer = setTimeout(run, 66);
};
})());
};
var createSrcset = function(elem, isImage){
var parsedSet;
var srcSet = elem.getAttribute('srcset') || elem.getAttribute(lazySizesCfg.srcsetAttr);
if(!srcSet && isImage){
srcSet = !elem._lazypolyfill ?
(elem.getAttribute(lazySizesCfg.srcAttr) || elem.getAttribute('src')) :
elem._lazypolyfill._set
;
}
if(!elem._lazypolyfill || elem._lazypolyfill._set != srcSet){
parsedSet = parseWsrcset( srcSet || '' );
if(isImage && elem.parentNode){
parsedSet.isPicture = elem.parentNode.nodeName.toUpperCase() == 'PICTURE';
if(parsedSet.isPicture){
if(window.matchMedia){
lazySizes.aC(elem, 'lazymatchmedia');
runMatchMedia();
}
}
}
parsedSet._set = srcSet;
Object.defineProperty(elem, '_lazypolyfill', {
value: parsedSet,
writable: true
});
}
};
var getX = function(elem){
var dpr = window.devicePixelRatio || 1;
var optimum = lazySizes.getX && lazySizes.getX(elem);
return Math.min(optimum || dpr, 2.5, dpr);
};
var matchesMedia = function(media){
if(window.matchMedia){
matchesMedia = function(media){
return !media || (matchMedia(media) || {}).matches;
};
} else {
return !media;
}
return matchesMedia(media);
};
var getCandidate = function(elem){
var sources, i, len, media, source, srces, src, width;
source = elem;
createSrcset(source, true);
srces = source._lazypolyfill;
if(srces.isPicture){
for(i = 0, sources = elem.parentNode.getElementsByTagName('source'), len = sources.length; i < len; i++){
if( lazySizesCfg.supportsType(sources[i].getAttribute('type'), elem) && matchesMedia( sources[i].getAttribute('media')) ){
source = sources[i];
createSrcset(source);
srces = source._lazypolyfill;
break;
}
}
}
if(srces.length > 1){
width = source.getAttribute('sizes') || '';
width = regPxLength.test(width) && parseInt(width, 10) || lazySizes.gW(elem, elem.parentNode);
srces.d = getX(elem);
if(!srces.src || !srces.w || srces.w < width){
srces.w = width;
src = reduceCandidate(srces.sort(ascendingSort));
srces.src = src;
} else {
src = srces.src;
}
} else {
src = srces[0];
}
return src;
};
var p = function(elem){
if(supportSrcset && elem.parentNode && elem.parentNode.nodeName.toUpperCase() != 'PICTURE'){return;}
var candidate = getCandidate(elem);
if(candidate && candidate.u && elem._lazypolyfill.cur != candidate.u){
elem._lazypolyfill.cur = candidate.u;
candidate.cached = true;
elem.setAttribute(lazySizesCfg.srcAttr, candidate.u);
elem.setAttribute('src', candidate.u);
}
};
p.parse = parseWsrcset;
return p;
})();
if(lazySizesCfg.loadedClass && lazySizesCfg.loadingClass){
(function(){
var sels = [];
['img[sizes$="px"][srcset].', 'picture > img:not([srcset]).'].forEach(function(sel){
sels.push(sel + lazySizesCfg.loadedClass);
sels.push(sel + lazySizesCfg.loadingClass);
});
lazySizesCfg.pf({
elements: document.querySelectorAll(sels.join(', '))
});
})();
}
}));
================================================
FILE: plugins/rias/README.md
================================================
# lazySizes RIaS extension (Responsive image as a service / Responsive image on demand)
The RiaS plugin enables lazySizes to generate the best suitable image source based on an URL pattern. It works with pre-build images (i.e. grunt-responsive-images) as also with any third party (ReSrc, Pixtulate, mobify, WURFL's Image Tailor ...) or self hosted restful responsive image services (responsive image on demand).
In general the RIaS plugin combines the simplicity of the famous Imager.js solution with the future power of native responsive images implementations and the webcomponent-like working of lazySizes' ``.lazyload`` elements (self-initialization, self-configuration and self-destroying).
In case the browser does support ``srcset`` the RIaS plugin will also produce a list of source candidates so that any current and future improvements (low bandwidth, metered bandwidth, user preferences, browser zoom etc.) to the native responsive image support is automatically exploited.
The RiaS plugin also allows art direction by combining placeholder URLs with a ``picture`` element.
## Basic/quick usage
* Add the RiaS plugin right before the lazySizes script or concat those scripts into your script bundle:
```html
```
* Add the ``lazyload`` class and the ``data-sizes="auto"`` attribute to your image and include the placeholder ``{width}`` at the point, where your image service expects the requested width of the image.
```html
```
## [Demo](http://afarkas.github.io/lazysizes/rias/)
A [demo with markup and code examples can be seen here](http://afarkas.github.io/lazysizes/rias/).
## Configuration/Options
The RIaS plugin can be configured through the ``lazySizesConfig.rias`` option object, which should be configured before the lazySizes script.
```js
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.rias = window.lazySizesConfig.rias || {};
// configure available widths to replace with the {width} placeholder
window.lazySizesConfig.rias.widths = [320, 480, 640, 960];
window.lazySizesConfig.rias.absUrl = true;
```
or element specific and declarative with corresponding ``data-*`` attributes:
```html
```
or element specific and functional using the ``lazyriasmodifyoptions`` event.
```html
```
All ``rias`` options can also be used as ``data-*`` attributes.
### URL generation and {placeholder}s
The url is dynamically generated by replacing placeholder values, which are enclosed by curly brackets.
All RiAS options can also be used as a {placeholder} inside the url.
### List of Options
* ``lazySizesConfig.rias.srcAttr`` (default: ``"data-src"``): The attribute, which should be transformed to ``src``/``srcset``. (The extension will also automatically check the ``lazySizesConfig.srcsetAttr`` and ``lazySizesConfig.srcAttr``)
* ``lazySizesConfig.rias.widths`` (``array of numbers``): The widths option reduces the calculated ``width`` to the allowed widths. The numeric width can also be simply mapped to a string (i.e.: small, medium, large) using the ``widthmap`` option. The default value is the following array: ``[180, 360, 540, 720, 900, 1080, 1296, 1512, 1728, 1944, 2160, 2376, 2592, 2808, 3024]``.
* ``lazySizesConfig.rias.widthmap`` (``{}``): The widthmap option allows you to simply transform a numeric width to a string.
```js
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.rias = window.lazySizesConfig.rias || {};
window.lazySizesConfig.rias.widths = [320, 640, 940];
window.lazySizesConfig.rias.widthmap = {
320: 'small',
640: 'medium',
940: 'large'
};
```
* ``lazySizesConfig.rias.modifyOptions`` (default: ``function`` noop ): A ``function`` that gets an data object passed with the options as the ``details`` and the corresponding ``img`` element as the ``target``. Can be used to modify existing options/placeholder values or to add new placeholder values. An event with the name ``lazyriasmodifyoptions`` is also fired at the element.
```html
```
* ``lazySizesConfig.rias.absUrl`` (default: ``false``): Wether the value of the ``data-src`` attribute should be resolved to an absolute url. The value must not contain any placeholders in this case. Use in conjunction with ``prefix`` and/or ``postfix`` option.
```html
```
* ``lazySizesConfig.rias.prefix`` (default: ``""``): A string, which is prepended to the generated src.
```html
```
* ``lazySizesConfig.rias.postfix`` (default: ``""`` ): A string, which is appended to the generated src.
With lazySizes + RIaS extension you have a script to rule them all. You won't need to include a script provided by a third party image on demand service.
### Dynamically calculating the height of image elements
You can provide a `apsectratio` option which then will be used to dynamically replace `{height}` placeholders.
```html
```
In case your image has CSS defined width and height dimensions you can provide this dynamically:
```js
document.addEventListener('lazyriasmodifyoptions', function(e) {
e.detail.aspectratio = e.target.offsetWidth / e.target.offsetHeight;
});
```
## Advanced Examples
### Embedding via CDN and combohandler
In case you want to use a CDN you can use jsDelivr's combohandler service:
```html
```
### Using art direction
In case you want to use art direction simply also use placeholder urls inside of your ``source[data-srcset]`` or ``source[data-src]`` attributes.
```html
```
### Using different ``widths`` options for different images
Often you will have different image formats with different allowed available ``widths``. This can be configured in two ways:
#### Descriptive way the ``data-widths`` attribute
```html
```
#### Scripted way using the ``lazyriasmodifyoptions`` event
```html
```
### Overriding existing placeholders or Extending new placeholders
The RIaS plugin is highly flexible in extending possible {placeholder} values. Each {placeholder} will be tried to be replaced by searching it in the ``lazySizesConfig.rias`` option object or by searching for a corresponding ``data-*`` attribute.
Additionally the ``modifyOptions`` callback or the equivalent ``lazyriasmodifyoptions`` event can be used to generate new or modify existing placeholders:
**Using ``data-*`` to override an existing placeholder:**
```html
```
**Using ``data-*`` to define a new placeholder:**
```html
```
**Using the ``lazySizesConfig.rias`` object to define a new placeholder:**
```html
```
**Using the ``lazyriasmodifyoptions`` event to define or change a placeholder object:**
```html
```
### Tip: Constraining the pixel density for a generated ``srcset`` attribute.
In case you don't want to generate additional compressive images for high resolution displays you can combine the RIaS extension with the [optimumx extension](../optimumx) to constrain the maximum pixel density for the generated ``srcset`` list.
```html
```
================================================
FILE: plugins/rias/ls.rias.js
================================================
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var config, riasCfg;
var lazySizesCfg = lazySizes.cfg;
var replaceTypes = {string: 1, number: 1};
var regNumber = /^\-*\+*\d+\.*\d*$/;
var regPicture = /^picture$/i;
var regWidth = /\s*\{\s*width\s*\}\s*/i;
var regHeight = /\s*\{\s*height\s*\}\s*/i;
var regPlaceholder = /\s*\{\s*([a-z0-9]+)\s*\}\s*/ig;
var regObj = /^\[.*\]|\{.*\}$/;
var regAllowedSizes = /^(?:auto|\d+(px)?)$/;
var anchor = document.createElement('a');
var img = document.createElement('img');
var buggySizes = ('srcset' in img) && !('sizes' in img);
var supportPicture = !!window.HTMLPictureElement && !buggySizes;
(function(){
var prop;
var noop = function(){};
var riasDefaults = {
prefix: '',
postfix: '',
srcAttr: 'data-src',
absUrl: false,
modifyOptions: noop,
widthmap: {},
ratio: false,
traditionalRatio: false,
aspectratio: false,
};
config = lazySizes && lazySizes.cfg;
if(!config.supportsType){
config.supportsType = function(type/*, elem*/){
return !type;
};
}
if(!config.rias){
config.rias = {};
}
riasCfg = config.rias;
if(!('widths' in riasCfg)){
riasCfg.widths = [];
(function (widths){
var width;
var i = 0;
while(!width || width < 3000){
i += 5;
if(i > 30){
i += 1;
}
width = (36 * i);
widths.push(width);
}
})(riasCfg.widths);
}
for(prop in riasDefaults){
if(!(prop in riasCfg)){
riasCfg[prop] = riasDefaults[prop];
}
}
})();
function getElementOptions(elem, src, options){
var attr, parent, setOption, prop, opts;
var elemStyles = window.getComputedStyle(elem);
if (!options) {
parent = elem.parentNode;
options = {
isPicture: !!(parent && regPicture.test(parent.nodeName || ''))
};
} else {
opts = {};
for (prop in options) {
opts[prop] = options[prop];
}
options = opts;
}
setOption = function(attr, run){
var attrVal = elem.getAttribute('data-'+ attr);
if (!attrVal) {
// no data- attr, get value from the CSS
var styles = elemStyles.getPropertyValue('--ls-' + attr);
// at least Safari 9 returns null rather than
// an empty string for getPropertyValue causing
// .trim() to fail
if (styles) {
attrVal = styles.trim();
}
}
if (attrVal) {
if(attrVal == 'true'){
attrVal = true;
} else if(attrVal == 'false'){
attrVal = false;
} else if(regNumber.test(attrVal)){
attrVal = parseFloat(attrVal);
} else if(typeof riasCfg[attr] == 'function'){
attrVal = riasCfg[attr](elem, attrVal);
} else if(regObj.test(attrVal)){
try {
attrVal = JSON.parse(attrVal);
} catch(e){}
}
options[attr] = attrVal;
} else if((attr in riasCfg) && typeof riasCfg[attr] != 'function' && !options[attr]){
options[attr] = riasCfg[attr];
} else if(run && typeof riasCfg[attr] == 'function'){
options[attr] = riasCfg[attr](elem, attrVal);
}
};
for(attr in riasCfg){
setOption(attr);
}
src.replace(regPlaceholder, function(full, match){
if(!(match in options)){
setOption(match, true);
}
});
return options;
}
function replaceUrlProps(url, options){
var candidates = [];
var replaceFn = function(full, match){
return (replaceTypes[typeof options[match]]) ? options[match] : full;
};
candidates.srcset = [];
if(options.absUrl){
anchor.setAttribute('href', url);
url = anchor.href;
}
url = ((options.prefix || '') + url + (options.postfix || '')).replace(regPlaceholder, replaceFn);
options.widths.forEach(function(width){
var widthAlias = options.widthmap[width] || width;
var ratio = options.aspectratio || options.ratio;
var traditionalRatio = !options.aspectratio && riasCfg.traditionalRatio;
var candidate = {
u: url.replace(regWidth, widthAlias)
.replace(regHeight, ratio ?
traditionalRatio ?
Math.round(width * ratio) :
Math.round(width / ratio)
: ''),
w: width
};
candidates.push(candidate);
candidates.srcset.push( (candidate.c = candidate.u + ' ' + width + 'w') );
});
return candidates;
}
function setSrc(src, opts, elem){
var elemW = 0;
var elemH = 0;
var sizeElement = elem;
if(!src){return;}
if (opts.ratio === 'container') {
// calculate image or parent ratio
elemW = sizeElement.scrollWidth;
elemH = sizeElement.scrollHeight;
while ((!elemW || !elemH) && sizeElement !== document) {
sizeElement = sizeElement.parentNode;
elemW = sizeElement.scrollWidth;
elemH = sizeElement.scrollHeight;
}
if (elemW && elemH) {
opts.ratio = opts.traditionalRatio ? elemH / elemW : elemW / elemH;
}
}
src = replaceUrlProps(src, opts);
src.isPicture = opts.isPicture;
if(buggySizes && elem.nodeName.toUpperCase() == 'IMG'){
elem.removeAttribute(config.srcsetAttr);
} else {
elem.setAttribute(config.srcsetAttr, src.srcset.join(', '));
}
Object.defineProperty(elem, '_lazyrias', {
value: src,
writable: true
});
}
function createAttrObject(elem, src){
var opts = getElementOptions(elem, src);
riasCfg.modifyOptions.call(elem, {target: elem, details: opts, detail: opts});
lazySizes.fire(elem, 'lazyriasmodifyoptions', opts);
return opts;
}
function getSrc(elem){
return elem.getAttribute( elem.getAttribute('data-srcattr') || riasCfg.srcAttr ) || elem.getAttribute(config.srcsetAttr) || elem.getAttribute(config.srcAttr) || elem.getAttribute('data-pfsrcset') || '';
}
addEventListener('lazybeforesizes', function(e){
if(e.detail.instance != lazySizes){return;}
var elem, src, elemOpts, sourceOpts, parent, sources, i, len, sourceSrc, sizes, detail, hasPlaceholder, modified, emptyList;
elem = e.target;
if(!e.detail.dataAttr || e.defaultPrevented || riasCfg.disabled || !((sizes = elem.getAttribute(config.sizesAttr) || elem.getAttribute('sizes')) && regAllowedSizes.test(sizes))){return;}
src = getSrc(elem);
elemOpts = createAttrObject(elem, src);
hasPlaceholder = regWidth.test(elemOpts.prefix) || regWidth.test(elemOpts.postfix);
if(elemOpts.isPicture && (parent = elem.parentNode)){
sources = parent.getElementsByTagName('source');
for(i = 0, len = sources.length; i < len; i++){
if ( hasPlaceholder || regWidth.test(sourceSrc = getSrc(sources[i])) ){
sourceOpts = getElementOptions(sources[i], sourceSrc, elemOpts);
setSrc(sourceSrc, sourceOpts, sources[i]);
modified = true;
}
}
}
if ( hasPlaceholder || regWidth.test(src) ){
setSrc(src, elemOpts, elem);
modified = true;
} else if (modified) {
emptyList = [];
emptyList.srcset = [];
emptyList.isPicture = true;
Object.defineProperty(elem, '_lazyrias', {
value: emptyList,
writable: true
});
}
if(modified){
if(supportPicture){
elem.removeAttribute(config.srcAttr);
} else if(sizes != 'auto') {
detail = {
width: parseInt(sizes, 10)
};
polyfill({
target: elem,
detail: detail
});
}
}
}, true);
// partial polyfill
var polyfill = (function(){
var ascendingSort = function( a, b ) {
return a.w - b.w;
};
var reduceCandidate = function (srces) {
var lowerCandidate, bonusFactor;
var len = srces.length;
var candidate = srces[len -1];
var i = 0;
for(i; i < len;i++){
candidate = srces[i];
candidate.d = candidate.w / srces.w;
if(candidate.d >= srces.d){
if(!candidate.cached && (lowerCandidate = srces[i - 1]) &&
lowerCandidate.d > srces.d - (0.13 * Math.pow(srces.d, 2.2))){
bonusFactor = Math.pow(lowerCandidate.d - 0.6, 1.6);
if(lowerCandidate.cached) {
lowerCandidate.d += 0.15 * bonusFactor;
}
if(lowerCandidate.d + ((candidate.d - srces.d) * bonusFactor) > srces.d){
candidate = lowerCandidate;
}
}
break;
}
}
return candidate;
};
var getWSet = function(elem, testPicture){
var src;
if(!elem._lazyrias && lazySizes.pWS && (src = lazySizes.pWS(elem.getAttribute(config.srcsetAttr || ''))).length){
Object.defineProperty(elem, '_lazyrias', {
value: src,
writable: true
});
if(testPicture && elem.parentNode){
src.isPicture = elem.parentNode.nodeName.toUpperCase() == 'PICTURE';
}
}
return elem._lazyrias;
};
var getX = function(elem){
var dpr = window.devicePixelRatio || 1;
var optimum = lazySizes.getX && lazySizes.getX(elem);
return Math.min(optimum || dpr, 2.4, dpr);
};
var getCandidate = function(elem, width){
var sources, i, len, media, srces, src;
srces = elem._lazyrias;
if(srces.isPicture && window.matchMedia){
for(i = 0, sources = elem.parentNode.getElementsByTagName('source'), len = sources.length; i < len; i++){
if(getWSet(sources[i]) && !sources[i].getAttribute('type') && ( !(media = sources[i].getAttribute('media')) || ((matchMedia(media) || {}).matches))){
srces = sources[i]._lazyrias;
break;
}
}
}
if(!srces.w || srces.w < width){
srces.w = width;
srces.d = getX(elem);
src = reduceCandidate(srces.sort(ascendingSort));
}
return src;
};
var polyfill = function(e){
if(e.detail.instance != lazySizes){return;}
var candidate;
var elem = e.target;
if(!buggySizes && (window.respimage || window.picturefill || lazySizesCfg.pf)){
document.removeEventListener('lazybeforesizes', polyfill);
return;
}
if(!('_lazyrias' in elem) && (!e.detail.dataAttr || !getWSet(elem, true))){
return;
}
candidate = getCandidate(elem, e.detail.width);
if(candidate && candidate.u && elem._lazyrias.cur != candidate.u){
elem._lazyrias.cur = candidate.u;
candidate.cached = true;
lazySizes.rAF(function(){
elem.setAttribute(config.srcAttr, candidate.u);
elem.setAttribute('src', candidate.u);
});
}
};
if(!supportPicture){
addEventListener('lazybeforesizes', polyfill);
} else {
polyfill = function(){};
}
return polyfill;
})();
}));
================================================
FILE: plugins/static-gecko-picture/ls.static-gecko-picture.js
================================================
/**
* FF's first picture implementation is static and does not react to viewport changes, this tiny script fixes this.
*/
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
var match;
var ua = navigator.userAgent;
if ( window.HTMLPictureElement && ((/ecko/).test(ua) && (match = ua.match(/rv\:(\d+)/)) && match[1] < 41) ) {
addEventListener("resize", (function() {
var timer;
var dummySrc = document.createElement("source");
var fixRespimg = function(img) {
var source, sizes;
var picture = img.parentNode;
if (picture.nodeName.toUpperCase() === "PICTURE") {
source = dummySrc.cloneNode();
picture.insertBefore(source, picture.firstElementChild);
setTimeout(function() {
picture.removeChild(source);
});
} else if (!img._pfLastSize || img.offsetWidth > img._pfLastSize) {
img._pfLastSize = img.offsetWidth;
sizes = img.sizes;
img.sizes += ",100vw";
setTimeout(function() {
img.sizes = sizes;
});
}
};
var findPictureImgs = function() {
var i;
var imgs = document.querySelectorAll("picture > img, img[srcset][sizes]");
for (i = 0; i < imgs.length; i++) {
fixRespimg(imgs[i]);
}
};
var onResize = function() {
clearTimeout(timer);
timer = setTimeout(findPictureImgs, 99);
};
var mq = window.matchMedia && matchMedia("(orientation: landscape)");
var init = function() {
onResize();
if (mq && mq.addListener) {
mq.addListener(onResize);
}
};
dummySrc.srcset = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
if (/^[c|i]|d$/.test(document.readyState || "")) {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
return onResize;
})());
}
}));
================================================
FILE: plugins/twitter/ls.twitter.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
/*
@example
Tweets by @TwitterDev
*/
'use strict';
var scriptadded;
function loadExecuteTwitter(){
if(window.twttr && twttr.widgets){
twttr.widgets.load();
return;
}
if(scriptadded){
return;
}
var elem = document.createElement('script');
var insertElem = document.getElementsByTagName('script')[0];
elem.src = '//platform.twitter.com/widgets.js';
scriptadded = true;
insertElem.parentNode.insertBefore(elem, insertElem);
}
document.addEventListener('lazybeforeunveil', function(e){
if(e.detail.instance != lazySizes){return;}
var twttrWidget = e.target.getAttribute('data-twitter');
if(twttrWidget){
lazySizes.aC(e.target, twttrWidget);
loadExecuteTwitter();
}
});
}));
================================================
FILE: plugins/unload/README.md
================================================
# lazysizes unload
Unloads ``img.lazyload`` (including ``picture``) elements if they consume a lot of memory and they are out of view. To improve memory consumption as also resize/orientationchange performance.
## Usage
Simply add the lazysizes unload extension to your site.
## Options
* ``lazySizesConfig.unloadClass`` (default: ``"lazyunload"``): Elements with this class will be unloaded even if they only consume less memory/pixels than defined in ``unloadPixelThreshold``.
* ``lazySizesConfig.unloadedClass`` (default: ``"lazyunloaded"``): If an element was unloaded it becomes the class ``lazyunloaded``.
* ``lazySizesConfig.unloadPixelThreshold`` (default: Number is dynamically calculated): If the amount of image pixels exceeds this threshold this image will be unloaded.
* ``lazySizesConfig.autoUnload`` (default: ``true``): Whether unloading should happen automatically with all lazyload images. If set to false, only elements with the class ``lazyunload`` will be unloaded.
* ``lazySizesConfig.emptySrc`` (default: transparent data uri): The src to be used as unload image.
* ``lazySizesConfig.unloadHidden`` (default: ``true``): Whether hidden images (``display: none;``) also should be unloaded.
**Note**: In case you dynamically change the ``data-src``/``data-srcset`` of an already unloaded element, you have to remove the ``lazyunloaded`` class.
## Events
* ``lazyafterunload``: This event will be fired on the unloaded lazyload elements. This event can be used to extend the unload functionality.
```js
//div ajax example which returns DOM string:
document.addEventListener('lazybeforeunveil', function (e) {
var containerId = e.target.getAttribute('data-id');
//load ajax content with containerId
//append content to e.target.innerHTML
});
//clean DOM nodes inside container that where previously loaded by ajax:
//lazyafterunload gives possibility to take care of the cleanup in this case
document.addEventListener('lazyafterunload', function (e) {
var container = e.target;
while (container.firstElementChild) {
container.removeChild(container.firstElementChild);
}
});
```
================================================
FILE: plugins/unload/ls.unload.js
================================================
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
'use strict';
if(!document.addEventListener){return;}
var config, checkElements;
var lazySizesCfg = lazySizes.cfg;
var unloadElements = [];
var requestAnimationFrame = window.requestAnimationFrame || setTimeout;
var unloader = {
checkElements: function(){
var i, len, box;
var expand = (lazySizes._defEx + 99) * 1.1;
var vTop = expand * -1;
var vLeft = vTop;
var vBottom = innerHeight + expand;
var vRight = innerWidth + expand;
for(i = 0, len = checkElements.length; i < len; i++){
box = checkElements[i].getBoundingClientRect();
if((box.top > vBottom || box.bottom < vTop || box.left > vRight || box.right < vLeft) ||
(config.unloadHidden && !box.top && !box.bottom && !box.left && !box.right)){
unloadElements.push(checkElements[i]);
}
}
requestAnimationFrame(unloader.unloadElements);
},
unload: function(element){
var sources, isResponsive, i, len;
var picture = element.parentNode;
lazySizes.rC(element, config.loadedClass);
if(element.getAttribute(config.srcsetAttr)){
element.setAttribute('srcset', config.emptySrc);
isResponsive = true;
}
if(picture && picture.nodeName.toUpperCase() == 'PICTURE'){
sources = picture.getElementsByTagName('source');
for(i = 0, len = sources.length; i < len; i++){
sources[i].setAttribute('srcset', config.emptySrc);
}
isResponsive = true;
}
if(lazySizes.hC(element, config.autosizesClass)){
lazySizes.rC(element, config.autosizesClass);
element.setAttribute(config.sizesAttr, 'auto');
}
if(isResponsive || element.getAttribute(config.srcAttr)){
element.src = config.emptySrc;
}
lazySizes.aC(element, config.unloadedClass);
lazySizes.aC(element, config.lazyClass);
lazySizes.fire(element, 'lazyafterunload');
},
unloadElements: function(elements){
elements = Array.isArray(elements) ? elements : unloadElements;
while(elements.length){
unloader.unload(elements.shift());
}
},
_reload: function(e) {
if(lazySizes.hC(e.target, config.unloadedClass) && e.detail){
e.detail.reloaded = true;
lazySizes.rC(e.target, config.unloadedClass);
}
}
};
function init(){
if(!window.lazySizes || checkElements){return;}
var docElem = document.documentElement;
var throttleRun = (function(){
var running;
var run = function(){
unloader.checkElements();
running = false;
};
return function(){
if(!running){
running = true;
setTimeout(run, 999);
}
};
})();
config = lazySizes.cfg;
removeEventListener('lazybeforeunveil', init);
if(!('unloadClass' in config)){
config.unloadClass = 'lazyunload';
}
if(!('unloadedClass' in config)){
config.unloadedClass = 'lazyunloaded';
}
if(!('unloadHidden' in config)){
config.unloadHidden = true;
}
if(!('emptySrc' in config)){
config.emptySrc = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
}
if(!('autoUnload' in config)){
config.autoUnload = true;
}
if(!('unloadPixelThreshold' in config)){
config.unloadPixelThreshold = 60000;
}
if(config.autoUnload){
docElem.addEventListener('load', function(e){
if(e.target.naturalWidth * e.target.naturalHeight > config.unloadPixelThreshold && e.target.className &&
e.target.className.indexOf && e.target.className.indexOf(lazySizesCfg.loadingClass) != -1 &&
e.target.className.indexOf(lazySizesCfg.preloadClass) == -1){
lazySizes.aC(e.target, lazySizesCfg.unloadClass);
}
}, true);
}
lazySizes.unloader = unloader;
checkElements = document.getElementsByClassName([config.unloadClass, config.loadedClass].join(' '));
setInterval(throttleRun, 9999);
addEventListener('lazybeforeunveil', throttleRun);
addEventListener('lazybeforeunveil', unloader._reload, true);
}
addEventListener('lazybeforeunveil', init);
}));
================================================
FILE: plugins/unveilhooks/README.md
================================================
# lazysizes unveilhooks extension
The unveilhooks plugin extends lazySizes to also unveil / lazyload scripts/widgets, background images, styles and video/audio elements:
```html
```
Note: In case you want to lazyload a background image via a ``class`` you can do so by using the ``addClasses`` option:
```html
```
For support responsive background images see the [bgset extension](../bgset).
For more complex loading of styles and AMD modules please see the [include extension](../include).
Note: To support the require example you need to the requireJs option:
```js
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.requireJs = function(modules, cb){
window.require(modules, cb);
};
```
================================================
FILE: plugins/unveilhooks/ls.unveilhooks.js
================================================
/*
This plugin extends lazySizes to lazyLoad:
background images, videos/posters and scripts
Background-Image:
For background images, use data-bg attribute:
Video:
For video/audio use data-poster and preload="none":
For video that plays automatically if in view:
Scripts:
For scripts use data-script:
Script modules using require:
For modules using require use data-require:
*/
(function(window, factory) {
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(window, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
var bgLoad, regBgUrlEscape;
var uniqueUrls = {};
if(document.addEventListener){
regBgUrlEscape = /\(|\)|\s|'/;
bgLoad = function (url, cb){
var img = document.createElement('img');
img.onload = function(){
img.onload = null;
img.onerror = null;
img = null;
cb();
};
img.onerror = img.onload;
img.src = url;
if(img && img.complete && img.onload){
img.onload();
}
};
addEventListener('lazybeforeunveil', function(e){
if(e.detail.instance != lazySizes){return;}
var tmp, load, bg, poster;
if(!e.defaultPrevented) {
var target = e.target;
if(target.preload == 'none'){
target.preload = target.getAttribute('data-preload') || 'auto';
}
if (target.getAttribute('data-autoplay') != null) {
if (target.getAttribute('data-expand') && !target.autoplay) {
try {
target.play();
} catch (er) {}
} else {
requestAnimationFrame(function () {
target.setAttribute('data-expand', '-10');
lazySizes.aC(target, lazySizes.cfg.lazyClass);
});
}
}
tmp = target.getAttribute('data-link');
if(tmp){
addStyleScript(tmp, true);
}
// handle data-script
tmp = target.getAttribute('data-script');
if(tmp){
e.detail.firesLoad = true;
load = function(){
e.detail.firesLoad = false;
lazySizes.fire(target, '_lazyloaded', {}, true, true);
};
addStyleScript(tmp, null, load);
}
// handle data-require
tmp = target.getAttribute('data-require');
if(tmp){
if(lazySizes.cfg.requireJs){
lazySizes.cfg.requireJs([tmp]);
} else {
addStyleScript(tmp);
}
}
// handle data-bg
bg = target.getAttribute('data-bg');
if (bg) {
e.detail.firesLoad = true;
load = function(){
target.style.backgroundImage = 'url(' + (regBgUrlEscape.test(bg) ? JSON.stringify(bg) : bg ) + ')';
e.detail.firesLoad = false;
lazySizes.fire(target, '_lazyloaded', {}, true, true);
};
bgLoad(bg, load);
}
// handle data-poster
poster = target.getAttribute('data-poster');
if(poster){
e.detail.firesLoad = true;
load = function(){
target.poster = poster;
e.detail.firesLoad = false;
lazySizes.fire(target, '_lazyloaded', {}, true, true);
};
bgLoad(poster, load);
}
}
}, false);
}
function addStyleScript(src, style, cb){
if(uniqueUrls[src]){
return;
}
var elem = document.createElement(style ? 'link' : 'script');
var insertElem = document.getElementsByTagName('script')[0];
if(style){
elem.rel = 'stylesheet';
elem.href = src;
} else {
elem.onload = function(){
elem.onerror = null;
elem.onload = null;
cb();
};
elem.onerror = elem.onload;
elem.src = src;
}
uniqueUrls[src] = true;
uniqueUrls[elem.src || elem.href] = true;
insertElem.parentNode.insertBefore(elem, insertElem);
}
}));
================================================
FILE: plugins/video-embed/README.md
================================================
TBD
```html
```
================================================
FILE: plugins/video-embed/ls.video-embed.js
================================================
(function(window, factory) {
if(!window) {return;}
var globalInstall = function(){
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if(typeof module == 'object' && module.exports){
factory(require('lazysizes'));
} else if (typeof define == 'function' && define.amd) {
define(['lazysizes'], factory);
} else if(window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
}(typeof window != 'undefined' ?
window : 0, function(window, document, lazySizes) {
/*jshint eqnull:true */
'use strict';
if(!document.getElementsByClassName){return;}
var protocol = location.protocol == 'https:' ?
'https:' :
'http:'
;
var idIndex = Date.now();
var regId = /\{\{id}}/;
var regYtImg = /\{\{hqdefault}}/;
var regAmp = /^&/;
var regValidParam = /^[a-z0-9-_&=]+$/i;
var youtubeImg = protocol + '//img.youtube.com/vi/{{id}}/{{hqdefault}}.jpg';
var youtubeIframe = protocol + '//www.youtube.com/embed/{{id}}?autoplay=1';
var vimeoApi = protocol + '//vimeo.com/api/oembed.json?url=https%3A//vimeo.com/{{id}}';
var vimeoIframe = protocol + '//player.vimeo.com/video/{{id}}?autoplay=1';
function getJSON(url, callback){
var id = 'vimeoCallback' + idIndex;
var script = document.createElement('script');
url += '&callback='+id;
idIndex++;
window[id] = function(data){
script.parentNode.removeChild(script);
delete window[id];
callback(data);
};
script.src = url;
document.head.appendChild(script);
}
function embedVimeoImg(id, elem){
getJSON(vimeoApi.replace(regId, id), function(data){
if(data && data.thumbnail_url){
elem.style.backgroundImage = 'url('+ data.thumbnail_url +')';
}
});
elem.addEventListener('click', embedVimeoIframe);
}
function embedVimeoIframe(e){
var elem = e.currentTarget;
var id = elem.getAttribute('data-vimeo');
var vimeoParams = elem.getAttribute('data-vimeoparams') || '';
elem.removeEventListener('click', embedVimeoIframe);
if (!id || !regValidParam.test(id) || (vimeoParams && !regValidParam.test(vimeoParams))) {
return;
}
if(vimeoParams && !regAmp.test(vimeoParams)){
vimeoParams = '&'+ vimeoParams;
}
e.preventDefault();
elem.innerHTML = ''
;
}
function embedYoutubeImg(id, elem){
var ytImg = elem.getAttribute('data-thumb-size') || lazySizes.cfg.ytThumb || 'hqdefault';
elem.style.backgroundImage = 'url('+ youtubeImg.replace(regId, id).replace(regYtImg, ytImg) +')';
elem.addEventListener('click', embedYoutubeIframe);
}
function embedYoutubeIframe(e){
var elem = e.currentTarget;
var id = elem.getAttribute('data-youtube');
var youtubeParams = elem.getAttribute('data-ytparams') || '';
elem.removeEventListener('click', embedYoutubeIframe);
if (!id || !regValidParam.test(id) || (youtubeParams && !regValidParam.test(youtubeParams))) {
return;
}
if(youtubeParams && !regAmp.test(youtubeParams)){
youtubeParams = '&'+ youtubeParams;
}
e.preventDefault();
elem.innerHTML = ''
;
}
document.addEventListener('lazybeforeunveil', function(e){
if(e.detail.instance != lazySizes){return;}
var elem = e.target;
var youtube = elem.getAttribute('data-youtube');
var vimeo = elem.getAttribute('data-vimeo');
if(youtube && elem){
embedYoutubeImg(youtube, elem);
}
if(vimeo && elem){
embedVimeoImg(vimeo, elem);
}
});
}));
================================================
FILE: rias/index.html
================================================
lazysizes - the restful responsive image service extension/plugin
lazySizes - RIaS plugin
The Responsive Image as Service plugin enables lazySizes to generate the best suitable image source based on an URL pattern. It works with pre-build images (i.e. grunt-responsive-images) as also with any third party or self hosted REST like image API / image on demand service.
In addition, you can set the ascpect ratio of the image from CSS. You only need to set the custom CSS variable --ls-aspectratio on the img and add a {height} placeholder
All options can be controlled globally through the lazySizesConfig.rias object or by using data-* attributes.
The rias extension can also be combined with the optimumx extension. The optimumx feature helps you to constrain the maximum pixel density for an image.