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 my image my image ``` 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 image with artdirection ``` * ``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 class lazyload to all elements, which should be lazy loaded. For a short API description go to the readme.md.

Boat

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.

<img
	alt="100%x200"
	src="low-quality.jpg"
	data-src="normal-quality.jpg"
	class="lazyload" />
Desert Road

Normal lazy image

The normal image pattern is can be used for non-critical images or in case there is no low quality image available:

<img class="lazyload" data-src="image.jpg" alt="Desert Road" />

responsive image with srcset and sizes attribute

Simply use data-srcset and data-sizes and you can support responsive images.

<img
	alt=""
	src="small.jpg"
	sizes="(min-width: 1000px) 930px, 90vw"
	data-srcset="small.jpg 500w,
		medium.jpg 640w,
		big.jpg 1024w"
	class="lazyload" />
image with artdirection

responsive image with the picture element

The picture element is also supported. Simply add the lazyload class to the img and use data-srcset on your source and the img element.

<picture>
	<!--[if IE 9]><video style="display: none"><![endif]-->
	<source
		data-srcset="500.jpg"
		media="(max-width: 500px)" />
	<source
			data-srcset="1024.jpg"
			media="(max-width: 1024px)" />
	<source
			data-srcset="1200.jpg" />
<!--[if IE 9]></video><![endif]-->
<img
		src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
		class="lazyload"
		alt="image with artdirection" />
</picture>

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=""
	data-sizes="auto"
	src="small.jpg"
	data-srcset="small.jpg 500w,
		medium.jpg 640w,
		big.jpg 1024w"
	class="lazyload" />

For responsive images support you must use either use a full polyfill like picturefill or respimage or use the extreme lightweight partial respimg polyfill plugin or use the Responsive Images as a Service extension.

iframe

Iframes can be loaded too:

<iframe data-src="//www.youtube.com/embed/ZfV-aYdU4uE" class="lazyload" frameborder="0" allowfullscreen></iframe>

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:

<div data-ride="carousel" data-script="assets/js/bootstrap.min.js" class="carousel lazyload">
	<!-- widget content -->
<div>
Desert Road
@ The Desert Tortoise Natural Area
Woman in water
Borobudur
A tree in the blue
Windows on Istanbul
Goldie Dawn
Avebury Stone Circle
el castil de tierra
sunset
Sky and earth
Missing Ulsoor lake (Explore)
Oxford Path 2

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: assets/css/bootstrap-theme.css ================================================ /*! * Bootstrap v3.3.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=5e2acd9519dbb9aaf550) * Config saved to config.json and https://gist.github.com/5e2acd9519dbb9aaf550 */ .btn-default, .btn-primary, .btn-success, .btn-info, .btn-warning, .btn-danger { text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); } .btn-default:active, .btn-primary:active, .btn-success:active, .btn-info:active, .btn-warning:active, .btn-danger:active, .btn-default.active, .btn-primary.active, .btn-success.active, .btn-info.active, .btn-warning.active, .btn-danger.active { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn-default .badge, .btn-primary .badge, .btn-success .badge, .btn-info .badge, .btn-warning .badge, .btn-danger .badge { text-shadow: none; } .btn:active, .btn.active { background-image: none; } .btn-default { background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #dbdbdb; text-shadow: 0 1px 0 #fff; border-color: #ccc; } .btn-default:hover, .btn-default:focus { background-color: #e0e0e0; background-position: 0 -15px; } .btn-default:active, .btn-default.active { background-color: #e0e0e0; border-color: #dbdbdb; } .btn-default:disabled, .btn-default[disabled] { background-color: #e0e0e0; background-image: none; } .btn-primary { background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #2b669a; } .btn-primary:hover, .btn-primary:focus { background-color: #2d6ca2; background-position: 0 -15px; } .btn-primary:active, .btn-primary.active { background-color: #2d6ca2; border-color: #2b669a; } .btn-primary:disabled, .btn-primary[disabled] { background-color: #2d6ca2; background-image: none; } .btn-success { background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #3e8f3e; } .btn-success:hover, .btn-success:focus { background-color: #419641; background-position: 0 -15px; } .btn-success:active, .btn-success.active { background-color: #419641; border-color: #3e8f3e; } .btn-success:disabled, .btn-success[disabled] { background-color: #419641; background-image: none; } .btn-info { background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #28a4c9; } .btn-info:hover, .btn-info:focus { background-color: #2aabd2; background-position: 0 -15px; } .btn-info:active, .btn-info.active { background-color: #2aabd2; border-color: #28a4c9; } .btn-info:disabled, .btn-info[disabled] { background-color: #2aabd2; background-image: none; } .btn-warning { background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #e38d13; } .btn-warning:hover, .btn-warning:focus { background-color: #eb9316; background-position: 0 -15px; } .btn-warning:active, .btn-warning.active { background-color: #eb9316; border-color: #e38d13; } .btn-warning:disabled, .btn-warning[disabled] { background-color: #eb9316; background-image: none; } .btn-danger { background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #b92c28; } .btn-danger:hover, .btn-danger:focus { background-color: #c12e2a; background-position: 0 -15px; } .btn-danger:active, .btn-danger.active { background-color: #c12e2a; border-color: #b92c28; } .btn-danger:disabled, .btn-danger[disabled] { background-color: #c12e2a; background-image: none; } .thumbnail, .img-thumbnail { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); background-color: #e8e8e8; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); background-color: #357ebd; } .navbar-default { background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); } .navbar-brand, .navbar-nav > li > a { text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); } .navbar-inverse { background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%); background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%); background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); } .navbar-inverse .navbar-brand, .navbar-inverse .navbar-nav > li > a { text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } .alert { text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); } .alert-success { background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); border-color: #b2dba1; } .alert-info { background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); border-color: #9acfea; } .alert-warning { background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); border-color: #f5e79e; } .alert-danger { background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); border-color: #dca7a7; } .progress { background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); } .progress-bar { background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); } .progress-bar-success { background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); } .progress-bar-info { background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); } .progress-bar-warning { background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); } .progress-bar-danger { background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); } .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .list-group { border-radius: 4px; -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { text-shadow: 0 -1px 0 #3071a9; background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); border-color: #3278b3; } .panel { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } .panel-default > .panel-heading { background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); } .panel-primary > .panel-heading { background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); } .panel-success > .panel-heading { background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); } .panel-info > .panel-heading { background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); } .panel-warning > .panel-heading { background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); } .panel-danger > .panel-heading { background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); } .well { background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); border-color: #dcdcdc; -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); } ================================================ FILE: assets/css/bootstrap.css ================================================ /*! * Bootstrap v3.3.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=5e2acd9519dbb9aaf550) * Config saved to config.json and https://gist.github.com/5e2acd9519dbb9aaf550 */ /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ html { font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } a { background-color: transparent; } a:active, a:hover { outline: 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } h1 { font-size: 2em; margin: 0.67em 0; } mark { background: #ff0; color: #000; } small { font-size: 80%; } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 1em 40px; } hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } pre { overflow: auto; } code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } button, input, optgroup, select, textarea { color: inherit; font: inherit; margin: 0; } button { overflow: visible; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; } button[disabled], html input[disabled] { cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } input { line-height: normal; } input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } legend { border: 0; padding: 0; } textarea { overflow: auto; } optgroup { font-weight: bold; } table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 10px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.42857143; color: #333333; background-color: #ffffff; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } a { color: #428bca; text-decoration: none; } a:hover, a:focus { color: #2a6496; text-decoration: underline; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } figure { margin: 0; } img { vertical-align: middle; } .img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; max-width: 100%; height: auto; } .img-rounded { border-radius: 6px; } .img-thumbnail { padding: 4px; line-height: 1.42857143; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; display: inline-block; max-width: 100%; height: auto; } .img-circle { border-radius: 50%; } hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eeeeee; } .sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: inherit; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { font-weight: normal; line-height: 1; color: #777777; } h1, .h1, h2, .h2, h3, .h3 { margin-top: 20px; margin-bottom: 10px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h1 .small, .h1 .small, h2 .small, .h2 .small, h3 .small, .h3 .small { font-size: 65%; } h4, .h4, h5, .h5, h6, .h6 { margin-top: 10px; margin-bottom: 10px; } h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small, h4 .small, .h4 .small, h5 .small, .h5 .small, h6 .small, .h6 .small { font-size: 75%; } h1, .h1 { font-size: 36px; } h2, .h2 { font-size: 30px; } h3, .h3 { font-size: 24px; } h4, .h4 { font-size: 18px; } h5, .h5 { font-size: 14px; } h6, .h6 { font-size: 12px; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 16px; font-weight: 300; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 21px; } } small, .small { font-size: 85%; } mark, .mark { background-color: #fcf8e3; padding: .2em; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } .text-justify { text-align: justify; } .text-nowrap { white-space: nowrap; } .text-lowercase { text-transform: lowercase; } .text-uppercase { text-transform: uppercase; } .text-capitalize { text-transform: capitalize; } .text-muted { color: #777777; } .text-primary { color: #428bca; } a.text-primary:hover { color: #3071a9; } .text-success { color: #3c763d; } a.text-success:hover { color: #2b542c; } .text-info { color: #31708f; } a.text-info:hover { color: #245269; } .text-warning { color: #8a6d3b; } a.text-warning:hover { color: #66512c; } .text-danger { color: #a94442; } a.text-danger:hover { color: #843534; } .bg-primary { color: #fff; background-color: #428bca; } a.bg-primary:hover { background-color: #3071a9; } .bg-success { background-color: #dff0d8; } a.bg-success:hover { background-color: #c1e2b3; } .bg-info { background-color: #d9edf7; } a.bg-info:hover { background-color: #afd9ee; } .bg-warning { background-color: #fcf8e3; } a.bg-warning:hover { background-color: #f7ecb5; } .bg-danger { background-color: #f2dede; } a.bg-danger:hover { background-color: #e4b9b9; } .page-header { padding-bottom: 9px; margin: 40px 0 20px; border-bottom: 1px solid #eeeeee; } ul, ol { margin-top: 0; margin-bottom: 10px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; margin-left: -5px; } .list-inline > li { display: inline-block; padding-left: 5px; padding-right: 5px; } dl { margin-top: 0; margin-bottom: 20px; } dt, dd { line-height: 1.42857143; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; clear: left; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #777777; } .initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 10px 20px; margin: 0 0 20px; font-size: 17.5px; border-left: 5px solid #eeeeee; } blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { margin-bottom: 0; } blockquote footer, blockquote small, blockquote .small { display: block; font-size: 80%; line-height: 1.42857143; color: #777777; } blockquote footer:before, blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } .blockquote-reverse, blockquote.pull-right { padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; text-align: right; } .blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { content: ''; } .blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } address { margin-bottom: 20px; font-style: normal; line-height: 1.42857143; } code, kbd, pre, samp { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 4px; } kbd { padding: 2px 4px; font-size: 90%; color: #ffffff; background-color: #333333; border-radius: 3px; box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); } kbd kbd { padding: 0; font-size: 100%; font-weight: bold; box-shadow: none; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.42857143; word-break: break-all; word-wrap: break-word; color: #333333; background-color: #f5f5f5; border: 1px solid #cccccc; border-radius: 4px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } @media (min-width: 768px) { .container { width: 750px; } } @media (min-width: 992px) { .container { width: 970px; } } @media (min-width: 1200px) { .container { width: 1170px; } } .container-fluid { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } .row { margin-left: -15px; margin-right: -15px; } .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { position: relative; min-height: 1px; padding-left: 15px; padding-right: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { float: left; } .col-xs-12 { width: 100%; } .col-xs-11 { width: 91.66666667%; } .col-xs-10 { width: 83.33333333%; } .col-xs-9 { width: 75%; } .col-xs-8 { width: 66.66666667%; } .col-xs-7 { width: 58.33333333%; } .col-xs-6 { width: 50%; } .col-xs-5 { width: 41.66666667%; } .col-xs-4 { width: 33.33333333%; } .col-xs-3 { width: 25%; } .col-xs-2 { width: 16.66666667%; } .col-xs-1 { width: 8.33333333%; } .col-xs-pull-12 { right: 100%; } .col-xs-pull-11 { right: 91.66666667%; } .col-xs-pull-10 { right: 83.33333333%; } .col-xs-pull-9 { right: 75%; } .col-xs-pull-8 { right: 66.66666667%; } .col-xs-pull-7 { right: 58.33333333%; } .col-xs-pull-6 { right: 50%; } .col-xs-pull-5 { right: 41.66666667%; } .col-xs-pull-4 { right: 33.33333333%; } .col-xs-pull-3 { right: 25%; } .col-xs-pull-2 { right: 16.66666667%; } .col-xs-pull-1 { right: 8.33333333%; } .col-xs-pull-0 { right: auto; } .col-xs-push-12 { left: 100%; } .col-xs-push-11 { left: 91.66666667%; } .col-xs-push-10 { left: 83.33333333%; } .col-xs-push-9 { left: 75%; } .col-xs-push-8 { left: 66.66666667%; } .col-xs-push-7 { left: 58.33333333%; } .col-xs-push-6 { left: 50%; } .col-xs-push-5 { left: 41.66666667%; } .col-xs-push-4 { left: 33.33333333%; } .col-xs-push-3 { left: 25%; } .col-xs-push-2 { left: 16.66666667%; } .col-xs-push-1 { left: 8.33333333%; } .col-xs-push-0 { left: auto; } .col-xs-offset-12 { margin-left: 100%; } .col-xs-offset-11 { margin-left: 91.66666667%; } .col-xs-offset-10 { margin-left: 83.33333333%; } .col-xs-offset-9 { margin-left: 75%; } .col-xs-offset-8 { margin-left: 66.66666667%; } .col-xs-offset-7 { margin-left: 58.33333333%; } .col-xs-offset-6 { margin-left: 50%; } .col-xs-offset-5 { margin-left: 41.66666667%; } .col-xs-offset-4 { margin-left: 33.33333333%; } .col-xs-offset-3 { margin-left: 25%; } .col-xs-offset-2 { margin-left: 16.66666667%; } .col-xs-offset-1 { margin-left: 8.33333333%; } .col-xs-offset-0 { margin-left: 0%; } @media (min-width: 768px) { .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { float: left; } .col-sm-12 { width: 100%; } .col-sm-11 { width: 91.66666667%; } .col-sm-10 { width: 83.33333333%; } .col-sm-9 { width: 75%; } .col-sm-8 { width: 66.66666667%; } .col-sm-7 { width: 58.33333333%; } .col-sm-6 { width: 50%; } .col-sm-5 { width: 41.66666667%; } .col-sm-4 { width: 33.33333333%; } .col-sm-3 { width: 25%; } .col-sm-2 { width: 16.66666667%; } .col-sm-1 { width: 8.33333333%; } .col-sm-pull-12 { right: 100%; } .col-sm-pull-11 { right: 91.66666667%; } .col-sm-pull-10 { right: 83.33333333%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-8 { right: 66.66666667%; } .col-sm-pull-7 { right: 58.33333333%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-5 { right: 41.66666667%; } .col-sm-pull-4 { right: 33.33333333%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-2 { right: 16.66666667%; } .col-sm-pull-1 { right: 8.33333333%; } .col-sm-pull-0 { right: auto; } .col-sm-push-12 { left: 100%; } .col-sm-push-11 { left: 91.66666667%; } .col-sm-push-10 { left: 83.33333333%; } .col-sm-push-9 { left: 75%; } .col-sm-push-8 { left: 66.66666667%; } .col-sm-push-7 { left: 58.33333333%; } .col-sm-push-6 { left: 50%; } .col-sm-push-5 { left: 41.66666667%; } .col-sm-push-4 { left: 33.33333333%; } .col-sm-push-3 { left: 25%; } .col-sm-push-2 { left: 16.66666667%; } .col-sm-push-1 { left: 8.33333333%; } .col-sm-push-0 { left: auto; } .col-sm-offset-12 { margin-left: 100%; } .col-sm-offset-11 { margin-left: 91.66666667%; } .col-sm-offset-10 { margin-left: 83.33333333%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-8 { margin-left: 66.66666667%; } .col-sm-offset-7 { margin-left: 58.33333333%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-5 { margin-left: 41.66666667%; } .col-sm-offset-4 { margin-left: 33.33333333%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-2 { margin-left: 16.66666667%; } .col-sm-offset-1 { margin-left: 8.33333333%; } .col-sm-offset-0 { margin-left: 0%; } } @media (min-width: 992px) { .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { float: left; } .col-md-12 { width: 100%; } .col-md-11 { width: 91.66666667%; } .col-md-10 { width: 83.33333333%; } .col-md-9 { width: 75%; } .col-md-8 { width: 66.66666667%; } .col-md-7 { width: 58.33333333%; } .col-md-6 { width: 50%; } .col-md-5 { width: 41.66666667%; } .col-md-4 { width: 33.33333333%; } .col-md-3 { width: 25%; } .col-md-2 { width: 16.66666667%; } .col-md-1 { width: 8.33333333%; } .col-md-pull-12 { right: 100%; } .col-md-pull-11 { right: 91.66666667%; } .col-md-pull-10 { right: 83.33333333%; } .col-md-pull-9 { right: 75%; } .col-md-pull-8 { right: 66.66666667%; } .col-md-pull-7 { right: 58.33333333%; } .col-md-pull-6 { right: 50%; } .col-md-pull-5 { right: 41.66666667%; } .col-md-pull-4 { right: 33.33333333%; } .col-md-pull-3 { right: 25%; } .col-md-pull-2 { right: 16.66666667%; } .col-md-pull-1 { right: 8.33333333%; } .col-md-pull-0 { right: auto; } .col-md-push-12 { left: 100%; } .col-md-push-11 { left: 91.66666667%; } .col-md-push-10 { left: 83.33333333%; } .col-md-push-9 { left: 75%; } .col-md-push-8 { left: 66.66666667%; } .col-md-push-7 { left: 58.33333333%; } .col-md-push-6 { left: 50%; } .col-md-push-5 { left: 41.66666667%; } .col-md-push-4 { left: 33.33333333%; } .col-md-push-3 { left: 25%; } .col-md-push-2 { left: 16.66666667%; } .col-md-push-1 { left: 8.33333333%; } .col-md-push-0 { left: auto; } .col-md-offset-12 { margin-left: 100%; } .col-md-offset-11 { margin-left: 91.66666667%; } .col-md-offset-10 { margin-left: 83.33333333%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-8 { margin-left: 66.66666667%; } .col-md-offset-7 { margin-left: 58.33333333%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-5 { margin-left: 41.66666667%; } .col-md-offset-4 { margin-left: 33.33333333%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-2 { margin-left: 16.66666667%; } .col-md-offset-1 { margin-left: 8.33333333%; } .col-md-offset-0 { margin-left: 0%; } } @media (min-width: 1200px) { .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { float: left; } .col-lg-12 { width: 100%; } .col-lg-11 { width: 91.66666667%; } .col-lg-10 { width: 83.33333333%; } .col-lg-9 { width: 75%; } .col-lg-8 { width: 66.66666667%; } .col-lg-7 { width: 58.33333333%; } .col-lg-6 { width: 50%; } .col-lg-5 { width: 41.66666667%; } .col-lg-4 { width: 33.33333333%; } .col-lg-3 { width: 25%; } .col-lg-2 { width: 16.66666667%; } .col-lg-1 { width: 8.33333333%; } .col-lg-pull-12 { right: 100%; } .col-lg-pull-11 { right: 91.66666667%; } .col-lg-pull-10 { right: 83.33333333%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-8 { right: 66.66666667%; } .col-lg-pull-7 { right: 58.33333333%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-5 { right: 41.66666667%; } .col-lg-pull-4 { right: 33.33333333%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-2 { right: 16.66666667%; } .col-lg-pull-1 { right: 8.33333333%; } .col-lg-pull-0 { right: auto; } .col-lg-push-12 { left: 100%; } .col-lg-push-11 { left: 91.66666667%; } .col-lg-push-10 { left: 83.33333333%; } .col-lg-push-9 { left: 75%; } .col-lg-push-8 { left: 66.66666667%; } .col-lg-push-7 { left: 58.33333333%; } .col-lg-push-6 { left: 50%; } .col-lg-push-5 { left: 41.66666667%; } .col-lg-push-4 { left: 33.33333333%; } .col-lg-push-3 { left: 25%; } .col-lg-push-2 { left: 16.66666667%; } .col-lg-push-1 { left: 8.33333333%; } .col-lg-push-0 { left: auto; } .col-lg-offset-12 { margin-left: 100%; } .col-lg-offset-11 { margin-left: 91.66666667%; } .col-lg-offset-10 { margin-left: 83.33333333%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-8 { margin-left: 66.66666667%; } .col-lg-offset-7 { margin-left: 58.33333333%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-5 { margin-left: 41.66666667%; } .col-lg-offset-4 { margin-left: 33.33333333%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-2 { margin-left: 16.66666667%; } .col-lg-offset-1 { margin-left: 8.33333333%; } .col-lg-offset-0 { margin-left: 0%; } } table { background-color: transparent; } caption { padding-top: 8px; padding-bottom: 8px; color: #777777; text-align: left; } th { text-align: left; } .table { width: 100%; max-width: 100%; margin-bottom: 20px; } .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { padding: 8px; line-height: 1.42857143; vertical-align: top; border-top: 1px solid #dddddd; } .table > thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #dddddd; } .table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { border-top: 0; } .table > tbody + tbody { border-top: 2px solid #dddddd; } .table .table { background-color: #ffffff; } .table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { padding: 5px; } .table-bordered { border: 1px solid #dddddd; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #dddddd; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-child(odd) { background-color: #f9f9f9; } .table-hover > tbody > tr:hover { background-color: #f5f5f5; } table col[class*="col-"] { position: static; float: none; display: table-column; } table td[class*="col-"], table th[class*="col-"] { position: static; float: none; display: table-cell; } .table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { background-color: #f5f5f5; } .table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { background-color: #e8e8e8; } .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { background-color: #dff0d8; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { background-color: #d0e9c6; } .table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { background-color: #d9edf7; } .table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { background-color: #c4e3f3; } .table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { background-color: #fcf8e3; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { background-color: #faf2cc; } .table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { background-color: #f2dede; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr.danger:hover > th { background-color: #ebcccc; } .table-responsive { overflow-x: auto; min-height: 0.01%; } @media screen and (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 15px; overflow-y: hidden; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #dddddd; } .table-responsive > .table { margin-bottom: 0; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { padding: 0; margin: 0; border: 0; min-width: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; max-width: 100%; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; line-height: normal; } input[type="file"] { display: block; } input[type="range"] { display: block; width: 100%; } select[multiple], select[size] { height: auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } output { display: block; padding-top: 7px; font-size: 14px; line-height: 1.42857143; color: #555555; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555555; background-color: #ffffff; background-image: none; border: 1px solid #cccccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); } .form-control::-moz-placeholder { color: #999999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999999; } .form-control::-webkit-input-placeholder { color: #999999; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { cursor: not-allowed; background-color: #eeeeee; opacity: 1; } textarea.form-control { height: auto; } input[type="search"] { -webkit-appearance: none; } input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] { line-height: 34px; line-height: 1.42857143 \0; } input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm { line-height: 30px; line-height: 1.5 \0; } input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg { line-height: 46px; line-height: 1.33 \0; } _:-ms-fullscreen, :root input[type="date"], _:-ms-fullscreen, :root input[type="time"], _:-ms-fullscreen, :root input[type="datetime-local"], _:-ms-fullscreen, :root input[type="month"] { line-height: 1.42857143; } _:-ms-fullscreen.input-sm, :root input[type="date"].input-sm, _:-ms-fullscreen.input-sm, :root input[type="time"].input-sm, _:-ms-fullscreen.input-sm, :root input[type="datetime-local"].input-sm, _:-ms-fullscreen.input-sm, :root input[type="month"].input-sm { line-height: 1.5; } _:-ms-fullscreen.input-lg, :root input[type="date"].input-lg, _:-ms-fullscreen.input-lg, :root input[type="time"].input-lg, _:-ms-fullscreen.input-lg, :root input[type="datetime-local"].input-lg, _:-ms-fullscreen.input-lg, :root input[type="month"].input-lg { line-height: 1.33; } .form-group { margin-bottom: 15px; } .radio, .checkbox { position: relative; display: block; margin-top: 10px; margin-bottom: 10px; } .radio label, .checkbox label { min-height: 20px; padding-left: 20px; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { position: absolute; margin-left: -20px; margin-top: 4px \9; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { display: inline-block; padding-left: 20px; margin-bottom: 0; vertical-align: middle; font-weight: normal; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { cursor: not-allowed; } .radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { cursor: not-allowed; } .form-control-static { padding-top: 7px; padding-bottom: 7px; margin-bottom: 0; } .form-control-static.input-lg, .form-control-static.input-sm { padding-left: 0; padding-right: 0; } .input-sm, .form-group-sm .form-control { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-sm, select.form-group-sm .form-control { height: 30px; line-height: 30px; } textarea.input-sm, textarea.form-group-sm .form-control, select[multiple].input-sm, select[multiple].form-group-sm .form-control { height: auto; } .input-lg, .form-group-lg .form-control { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-lg, select.form-group-lg .form-control { height: 46px; line-height: 46px; } textarea.input-lg, textarea.form-group-lg .form-control, select[multiple].input-lg, select[multiple].form-group-lg .form-control { height: auto; } .has-feedback { position: relative; } .has-feedback .form-control { padding-right: 42.5px; } .form-control-feedback { position: absolute; top: 0; right: 0; z-index: 2; display: block; width: 34px; height: 34px; line-height: 34px; text-align: center; pointer-events: none; } .input-lg + .form-control-feedback { width: 46px; height: 46px; line-height: 46px; } .input-sm + .form-control-feedback { width: 30px; height: 30px; line-height: 30px; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline, .has-success.radio label, .has-success.checkbox label, .has-success.radio-inline label, .has-success.checkbox-inline label { color: #3c763d; } .has-success .form-control { border-color: #3c763d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-success .form-control:focus { border-color: #2b542c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; } .has-success .input-group-addon { color: #3c763d; border-color: #3c763d; background-color: #dff0d8; } .has-success .form-control-feedback { color: #3c763d; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline, .has-warning.radio label, .has-warning.checkbox label, .has-warning.radio-inline label, .has-warning.checkbox-inline label { color: #8a6d3b; } .has-warning .form-control { border-color: #8a6d3b; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-warning .form-control:focus { border-color: #66512c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; } .has-warning .input-group-addon { color: #8a6d3b; border-color: #8a6d3b; background-color: #fcf8e3; } .has-warning .form-control-feedback { color: #8a6d3b; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline, .has-error.radio label, .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label { color: #a94442; } .has-error .form-control { border-color: #a94442; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-error .form-control:focus { border-color: #843534; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; } .has-error .input-group-addon { color: #a94442; border-color: #a94442; background-color: #f2dede; } .has-error .form-control-feedback { color: #a94442; } .has-feedback label ~ .form-control-feedback { top: 25px; } .has-feedback label.sr-only ~ .form-control-feedback { top: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #737373; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; width: auto; vertical-align: middle; } .form-inline .form-control-static { display: inline-block; } .form-inline .input-group { display: inline-table; vertical-align: middle; } .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { width: auto; } .form-inline .input-group > .form-control { width: 100%; } .form-inline .control-label { margin-bottom: 0; vertical-align: middle; } .form-inline .radio, .form-inline .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .form-inline .radio label, .form-inline .checkbox label { padding-left: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .form-inline .has-feedback .form-control-feedback { top: 0; } } .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { margin-top: 0; margin-bottom: 0; padding-top: 7px; } .form-horizontal .radio, .form-horizontal .checkbox { min-height: 27px; } .form-horizontal .form-group { margin-left: -15px; margin-right: -15px; } @media (min-width: 768px) { .form-horizontal .control-label { text-align: right; margin-bottom: 0; padding-top: 7px; } } .form-horizontal .has-feedback .form-control-feedback { right: 15px; } @media (min-width: 768px) { .form-horizontal .form-group-lg .control-label { padding-top: 14.3px; } } @media (min-width: 768px) { .form-horizontal .form-group-sm .control-label { padding-top: 6px; } } .btn { display: inline-block; margin-bottom: 0; font-weight: normal; text-align: center; vertical-align: middle; touch-action: manipulation; cursor: pointer; background-image: none; border: 1px solid transparent; white-space: nowrap; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; border-radius: 4px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus, .btn.focus { color: #333333; text-decoration: none; } .btn:active, .btn.active { outline: 0; background-image: none; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { cursor: not-allowed; pointer-events: none; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; } .btn-default { color: #333333; background-color: #ffffff; border-color: #cccccc; } .btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { color: #333333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { background-color: #ffffff; border-color: #cccccc; } .btn-default .badge { color: #ffffff; background-color: #333333; } .btn-primary { color: #ffffff; background-color: #428bca; border-color: #357ebd; } .btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { color: #ffffff; background-color: #3071a9; border-color: #285e8e; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #428bca; border-color: #357ebd; } .btn-primary .badge { color: #428bca; background-color: #ffffff; } .btn-success { color: #ffffff; background-color: #5cb85c; border-color: #4cae4c; } .btn-success:hover, .btn-success:focus, .btn-success.focus, .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { color: #ffffff; background-color: #449d44; border-color: #398439; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active { background-color: #5cb85c; border-color: #4cae4c; } .btn-success .badge { color: #5cb85c; background-color: #ffffff; } .btn-info { color: #ffffff; background-color: #5bc0de; border-color: #46b8da; } .btn-info:hover, .btn-info:focus, .btn-info.focus, .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { color: #ffffff; background-color: #31b0d5; border-color: #269abc; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active { background-color: #5bc0de; border-color: #46b8da; } .btn-info .badge { color: #5bc0de; background-color: #ffffff; } .btn-warning { color: #ffffff; background-color: #f0ad4e; border-color: #eea236; } .btn-warning:hover, .btn-warning:focus, .btn-warning.focus, .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { color: #ffffff; background-color: #ec971f; border-color: #d58512; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active { background-color: #f0ad4e; border-color: #eea236; } .btn-warning .badge { color: #f0ad4e; background-color: #ffffff; } .btn-danger { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; } .btn-danger:hover, .btn-danger:focus, .btn-danger.focus, .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { color: #ffffff; background-color: #c9302c; border-color: #ac2925; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active { background-color: #d9534f; border-color: #d43f3a; } .btn-danger .badge { color: #d9534f; background-color: #ffffff; } .btn-link { color: #428bca; font-weight: normal; border-radius: 0; } .btn-link, .btn-link:active, .btn-link.active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #2a6496; text-decoration: underline; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #777777; text-decoration: none; } .btn-lg { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-sm { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-xs { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-block { display: block; width: 100%; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } a.label:hover, a.label:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .btn .label { position: relative; top: -1px; } .label-default { background-color: #777777; } .label-default[href]:hover, .label-default[href]:focus { background-color: #5e5e5e; } .label-primary { background-color: #428bca; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #3071a9; } .label-success { background-color: #5cb85c; } .label-success[href]:hover, .label-success[href]:focus { background-color: #449d44; } .label-info { background-color: #5bc0de; } .label-info[href]:hover, .label-info[href]:focus { background-color: #31b0d5; } .label-warning { background-color: #f0ad4e; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #ec971f; } .label-danger { background-color: #d9534f; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #c9302c; } .jumbotron { padding: 30px 15px; margin-bottom: 30px; color: inherit; background-color: #eeeeee; } .jumbotron h1, .jumbotron .h1 { color: inherit; } .jumbotron p { margin-bottom: 15px; font-size: 21px; font-weight: 200; } .jumbotron > hr { border-top-color: #d5d5d5; } .container .jumbotron, .container-fluid .jumbotron { border-radius: 6px; } .jumbotron .container { max-width: 100%; } @media screen and (min-width: 768px) { .jumbotron { padding: 48px 0; } .container .jumbotron { padding-left: 60px; padding-right: 60px; } .jumbotron h1, .jumbotron .h1 { font-size: 63px; } } .thumbnail { display: block; padding: 4px; margin-bottom: 20px; line-height: 1.42857143; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-transition: border 0.2s ease-in-out; -o-transition: border 0.2s ease-in-out; transition: border 0.2s ease-in-out; } .thumbnail > img, .thumbnail a > img { margin-left: auto; margin-right: auto; } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #428bca; } .thumbnail .caption { padding: 9px; color: #333333; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable, .alert-dismissible { padding-right: 35px; } .alert-dismissable .close, .alert-dismissible .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { background-color: #dff0d8; border-color: #d6e9c6; color: #3c763d; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #2b542c; } .alert-info { background-color: #d9edf7; border-color: #bce8f1; color: #31708f; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #245269; } .alert-warning { background-color: #fcf8e3; border-color: #faebcc; color: #8a6d3b; } .alert-warning hr { border-top-color: #f7e1b5; } .alert-warning .alert-link { color: #66512c; } .alert-danger { background-color: #f2dede; border-color: #ebccd1; color: #a94442; } .alert-danger hr { border-top-color: #e4b9c0; } .alert-danger .alert-link { color: #843534; } .panel { margin-bottom: 20px; background-color: #ffffff; border: 1px solid transparent; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); } .panel-body { padding: 15px; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-right-radius: 3px; border-top-left-radius: 3px; } .panel-heading > .dropdown .dropdown-toggle { color: inherit; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 16px; color: inherit; } .panel-title > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #dddddd; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .list-group, .panel > .panel-collapse > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item, .panel > .panel-collapse > .list-group .list-group-item { border-width: 1px 0; border-radius: 0; } .panel > .list-group:first-child .list-group-item:first-child, .panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { border-top: 0; border-top-right-radius: 3px; border-top-left-radius: 3px; } .panel > .list-group:last-child .list-group-item:last-child, .panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { border-bottom: 0; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .list-group + .panel-footer { border-top-width: 0; } .panel > .table, .panel > .table-responsive > .table, .panel > .panel-collapse > .table { margin-bottom: 0; } .panel > .table caption, .panel > .table-responsive > .table caption, .panel > .panel-collapse > .table caption { padding-left: 15px; padding-right: 15px; } .panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { border-top-right-radius: 3px; border-top-left-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { border-top-left-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { border-top-right-radius: 3px; } .panel > .table:last-child, .panel > .table-responsive:last-child > .table:last-child { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { border-bottom-right-radius: 3px; } .panel > .panel-body + .table, .panel > .panel-body + .table-responsive, .panel > .table + .panel-body, .panel > .table-responsive + .panel-body { border-top: 1px solid #dddddd; } .panel > .table > tbody:first-child > tr:first-child th, .panel > .table > tbody:first-child > tr:first-child td { border-top: 0; } .panel > .table-bordered, .panel > .table-responsive > .table-bordered { border: 0; } .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .panel > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { border-bottom: 0; } .panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { border-bottom: 0; } .panel > .table-responsive { border: 0; margin-bottom: 0; } .panel-group { margin-bottom: 20px; } .panel-group .panel { margin-bottom: 0; border-radius: 4px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse > .panel-body, .panel-group .panel-heading + .panel-collapse > .list-group { border-top: 1px solid #dddddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #dddddd; } .panel-default { border-color: #dddddd; } .panel-default > .panel-heading { color: #333333; background-color: #f5f5f5; border-color: #dddddd; } .panel-default > .panel-heading + .panel-collapse > .panel-body { border-top-color: #dddddd; } .panel-default > .panel-heading .badge { color: #f5f5f5; background-color: #333333; } .panel-default > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #dddddd; } .panel-primary { border-color: #428bca; } .panel-primary > .panel-heading { color: #ffffff; background-color: #428bca; border-color: #428bca; } .panel-primary > .panel-heading + .panel-collapse > .panel-body { border-top-color: #428bca; } .panel-primary > .panel-heading .badge { color: #428bca; background-color: #ffffff; } .panel-primary > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #428bca; } .panel-success { border-color: #d6e9c6; } .panel-success > .panel-heading { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .panel-success > .panel-heading + .panel-collapse > .panel-body { border-top-color: #d6e9c6; } .panel-success > .panel-heading .badge { color: #dff0d8; background-color: #3c763d; } .panel-success > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #d6e9c6; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse > .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-heading .badge { color: #d9edf7; background-color: #31708f; } .panel-info > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #bce8f1; } .panel-warning { border-color: #faebcc; } .panel-warning > .panel-heading { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .panel-warning > .panel-heading + .panel-collapse > .panel-body { border-top-color: #faebcc; } .panel-warning > .panel-heading .badge { color: #fcf8e3; background-color: #8a6d3b; } .panel-warning > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #faebcc; } .panel-danger { border-color: #ebccd1; } .panel-danger > .panel-heading { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .panel-danger > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ebccd1; } .panel-danger > .panel-heading .badge { color: #f2dede; background-color: #a94442; } .panel-danger > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ebccd1; } .carousel { position: relative; } .carousel-inner { position: relative; overflow: hidden; width: 100%; } .carousel-inner > .item { display: none; position: relative; -webkit-transition: 0.6s ease-in-out left; -o-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { line-height: 1; } @media all and (transform-3d), (-webkit-transform-3d) { .carousel-inner > .item { transition: transform 0.6s ease-in-out; backface-visibility: hidden; perspective: 1000; } .carousel-inner > .item.next, .carousel-inner > .item.active.right { transform: translate3d(100%, 0, 0); left: 0; } .carousel-inner > .item.prev, .carousel-inner > .item.active.left { transform: translate3d(-100%, 0, 0); left: 0; } .carousel-inner > .item.next.left, .carousel-inner > .item.prev.right, .carousel-inner > .item.active { transform: translate3d(0, 0, 0); left: 0; } } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; left: 0; bottom: 0; width: 15%; opacity: 0.5; filter: alpha(opacity=50); font-size: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-control.left { background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); } .carousel-control.right { left: auto; right: 0; background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); } .carousel-control:hover, .carousel-control:focus { outline: 0; color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; margin-top: -10px; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; margin-left: -30%; padding-left: 0; list-style: none; text-align: center; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; border: 1px solid #ffffff; border-radius: 10px; cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); } .carousel-indicators .active { margin: 0; width: 12px; height: 12px; background-color: #ffffff; } .carousel-caption { position: absolute; left: 15%; right: 15%; bottom: 20px; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -15px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -15px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -15px; } .carousel-caption { left: 20%; right: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after, .dl-horizontal dd:before, .dl-horizontal dd:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .panel-body:before, .panel-body:after { content: " "; display: table; } .clearfix:after, .dl-horizontal dd:after, .container:after, .container-fluid:after, .row:after, .form-horizontal .form-group:after, .panel-body:after { clear: both; } .center-block { display: block; margin-left: auto; margin-right: auto; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .hidden { display: none !important; visibility: hidden !important; } .affix { position: fixed; } @-ms-viewport { width: device-width; } .visible-xs, .visible-sm, .visible-md, .visible-lg { display: none !important; } .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-lg-block, .visible-lg-inline, .visible-lg-inline-block { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } table.visible-xs { display: table; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (max-width: 767px) { .visible-xs-block { display: block !important; } } @media (max-width: 767px) { .visible-xs-inline { display: inline !important; } } @media (max-width: 767px) { .visible-xs-inline-block { display: inline-block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } table.visible-sm { display: table; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-block { display: block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline { display: inline !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline-block { display: inline-block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } table.visible-md { display: table; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-block { display: block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline { display: inline !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline-block { display: inline-block !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } table.visible-lg { display: table; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg-block { display: block !important; } } @media (min-width: 1200px) { .visible-lg-inline { display: inline !important; } } @media (min-width: 1200px) { .visible-lg-inline-block { display: inline-block !important; } } @media (max-width: 767px) { .hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } } .visible-print { display: none !important; } @media print { .visible-print { display: block !important; } table.visible-print { display: table; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } } .visible-print-block { display: none !important; } @media print { .visible-print-block { display: block !important; } } .visible-print-inline { display: none !important; } @media print { .visible-print-inline { display: inline !important; } } .visible-print-inline-block { display: none !important; } @media print { .visible-print-inline-block { display: inline-block !important; } } @media print { .hidden-print { display: none !important; } } ================================================ FILE: assets/css/carousel.css ================================================ .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; width: 100% \9; max-width: 100%; height: auto; } .carousel { position: relative; } .carousel-inner { position: relative; overflow: hidden; width: 100%; } .carousel-inner > .item { display: none; position: relative; -webkit-transition: 0.6s ease-in-out left; -o-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { line-height: 1; } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; left: 0; bottom: 0; width: 15%; opacity: 0.5; filter: alpha(opacity=50); font-size: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-control.left { background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); } .carousel-control.right { left: auto; right: 0; background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); } .carousel-control:hover, .carousel-control:focus { outline: 0; color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; margin-top: -10px; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; margin-left: -30%; padding-left: 0; list-style: none; text-align: center; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; border: 1px solid #ffffff; border-radius: 10px; cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); } .carousel-indicators .active { margin: 0; width: 12px; height: 12px; background-color: #ffffff; } .carousel-caption { position: absolute; left: 15%; right: 15%; bottom: 20px; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -15px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -15px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -15px; } .carousel-caption { left: 20%; right: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } ================================================ FILE: assets/css/tidy.css ================================================ /*! * Bootstrap v3.3.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=5e2acd9519dbb9aaf550) * Config saved to config.json and https://gist.github.com/5e2acd9519dbb9aaf550 */ /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ html { font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } body { margin: 0; } a { background-color: transparent; } a:active, a:hover { outline: 0; } strong { font-weight: bold; } h1 { font-size: 2em; margin: 0.67em 0; } img { border: 0; } pre { overflow: auto; } code, pre { font-family: monospace, monospace; font-size: 1em; } button, input { color: inherit; font: inherit; margin: 0; } button { overflow: visible; } button { text-transform: none; } button { -webkit-appearance: button; cursor: pointer; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } input { line-height: normal; } input[type="checkbox"] { box-sizing: border-box; padding: 0; } fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } legend { border: 0; padding: 0; } table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 10px; -webkit-tap-highlight-color: rgba(0,0,0,0); } body { font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; font-size: 14px; line-height: 1.42857143; color: #333; background-color: #fff; } input, button { font-family: inherit; font-size: inherit; line-height: inherit; } a { color: #428bca; text-decoration: none; } a:hover, a:focus { color: #2a6496; text-decoration: underline; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } img { vertical-align: middle; } .thumbnail>img { display: block; max-width: 100%; height: auto; } h1, h3 { font-family: inherit; font-weight: 500; line-height: 1.1; color: inherit; } h1, h3 { margin-top: 20px; margin-bottom: 10px; } h1 { font-size: 36px; } h3 { font-size: 24px; } p { margin: 0 0 10px; } code, pre { font-family: Menlo,Monaco,Consolas,"Courier New",monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 4px; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.42857143; word-break: break-all; word-wrap: break-word; color: #333; background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 4px; } .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } @media (min-width:768px) { .container { width: 750px; } } @media (min-width:992px) { .container { width: 970px; } } @media (min-width:1200px) { .container { width: 1170px; } } .row { margin-left: -15px; margin-right: -15px; } .col-sm-4, .col-sm-6, .col-sm-12 { position: relative; min-height: 1px; padding-left: 15px; padding-right: 15px; } @media (min-width:768px) { .col-sm-4, .col-sm-6, .col-sm-12 { float: left; } .col-sm-12 { width: 100%; } .col-sm-4 { width: 33%; } .col-sm-6 { width: 50%; } } table { background-color: transparent; } th { text-align: left; } .table { width: 100%; max-width: 100%; margin-bottom: 20px; } .table>thead>tr>th, .table>tbody>tr>th, .table>tbody>tr>td { padding: 8px; line-height: 1.42857143; vertical-align: top; border-top: 1px solid #ddd; } .table>thead>tr>th { vertical-align: bottom; border-bottom: 2px solid #ddd; } .table>thead:first-child>tr:first-child>th { border-top: 0; } .table-condensed>thead>tr>th, .table-condensed>tbody>tr>th, .table-condensed>tbody>tr>td { padding: 5px; } .table-striped>tbody>tr:nth-child(odd) { background-color: #f9f9f9; } fieldset { padding: 0; margin: 0; border: 0; min-width: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; max-width: 100%; margin-bottom: 5px; font-weight: bold; } input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; line-height: normal; } input[type="range"] { display: block; width: 100%; } input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .form-control::-moz-placeholder { color: #999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999; } _:-ms-fullscreen, _:-ms-fullscreen, _:-ms-fullscreen, _:-ms-fullscreen { line-height: 1.42857143; } _:-ms-fullscreen.input-sm, _:-ms-fullscreen.input-sm, _:-ms-fullscreen.input-sm, _:-ms-fullscreen.input-sm { line-height: 1.5; } _:-ms-fullscreen.input-lg, _:-ms-fullscreen.input-lg, _:-ms-fullscreen.input-lg, _:-ms-fullscreen.input-lg { line-height: 1.33; } .form-group { margin-bottom: 15px; } .checkbox { position: relative; display: block; margin-top: 10px; margin-bottom: 10px; } .checkbox label { min-height: 20px; padding-left: 20px; margin-bottom: 0; font-weight: normal; cursor: pointer; } .checkbox input[type="checkbox"] { position: absolute; margin-left: -20px; margin-top: 4px \9; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #737373; } .btn { display: inline-block; margin-bottom: 0; font-weight: normal; text-align: center; vertical-align: middle; touch-action: manipulation; cursor: pointer; background-image: none; border: 1px solid transparent; white-space: nowrap; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; border-radius: 4px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .btn:focus, .btn:active:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus { color: #333; text-decoration: none; } .btn:active { outline: 0; background-image: none; -webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125); box-shadow: inset 0 3px 5px rgba(0,0,0,0.125); } .btn-primary { color: #fff; background-color: #428bca; border-color: #357ebd; } .btn-primary:hover, .btn-primary:focus, .btn-primary:active { color: #fff; background-color: #3071a9; border-color: #285e8e; } .btn-primary:active { background-image: none; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } .label-default { background-color: #777; } .label-success { background-color: #5cb85c; } .label-danger { background-color: #d9534f; } .jumbotron { padding: 30px 15px; margin-bottom: 30px; color: inherit; background-color: #eee; } .jumbotron h1 { color: inherit; } .jumbotron p { margin-bottom: 15px; font-size: 21px; font-weight: 200; } .container .jumbotron { border-radius: 6px; } @media screen and (min-width:768px) { .jumbotron { padding: 48px 0; } .container .jumbotron { padding-left: 60px; padding-right: 60px; } .jumbotron h1 { font-size: 63px; } } .thumbnail { display: block; padding: 4px; margin-bottom: 20px; line-height: 1.42857143; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; -webkit-transition: border .2s ease-in-out; -o-transition: border .2s ease-in-out; transition: border .2s ease-in-out; } .thumbnail>img { margin-left: auto; margin-right: auto; } .thumbnail .caption { padding: 9px; color: #333; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert>p { margin-bottom: 0; } .alert>p+p { margin-top: 5px; } .alert-info { background-color: #d9edf7; border-color: #bce8f1; color: #31708f; } .alert-danger { background-color: #f2dede; border-color: #ebccd1; color: #a94442; } .panel { margin-bottom: 20px; background-color: #fff; border: 1px solid transparent; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.05); box-shadow: 0 1px 1px rgba(0,0,0,0.05); } .panel-body { padding: 15px; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-right-radius: 3px; border-top-left-radius: 3px; } .panel-default { border-color: #ddd; } .panel-default>.panel-heading { color: #333; background-color: #f5f5f5; border-color: #ddd; } .container:before, .container:after, .row:before, .row:after, .panel-body:before, .panel-body:after { content: " "; display: table; } .container:after, .row:after, .panel-body:after { clear: both; } @-ms-viewport { width: device-width; } /*! * Bootstrap v3.3.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=5e2acd9519dbb9aaf550) * Config saved to config.json and https://gist.github.com/5e2acd9519dbb9aaf550 */ .btn-primary { text-shadow: 0 -1px 0 rgba(0,0,0,0.2); -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075); box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075); } .btn-primary:active { -webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125); box-shadow: inset 0 3px 5px rgba(0,0,0,0.125); } .btn:active { background-image: none; } .btn-primary { background-image: -webkit-linear-gradient(top, #428bca 0, #2d6ca2 100%); background-image: -o-linear-gradient(top, #428bca 0, #2d6ca2 100%); background-image: linear-gradient(to bottom, #428bca 0, #2d6ca2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #2b669a; } .btn-primary:hover, .btn-primary:focus { background-color: #2d6ca2; background-position: 0 -15px; } .btn-primary:active { background-color: #2d6ca2; border-color: #2b669a; } .btn-primary:disabled { background-color: #2d6ca2; background-image: none; } .thumbnail { -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.075); box-shadow: 0 1px 2px rgba(0,0,0,0.075); } .alert { text-shadow: 0 1px 0 rgba(255,255,255,0.2); -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05); box-shadow: inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05); } .alert-info { background-image: -webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%); background-image: -o-linear-gradient(top, #d9edf7 0, #b9def0 100%); background-image: linear-gradient(to bottom, #d9edf7 0, #b9def0 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); border-color: #9acfea; } .alert-danger { background-image: -webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%); background-image: -o-linear-gradient(top, #f2dede 0, #e7c3c3 100%); background-image: linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); border-color: #dca7a7; } .panel { -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.05); box-shadow: 0 1px 2px rgba(0,0,0,0.05); } .panel-default>.panel-heading { background-image: -webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%); background-image: -o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%); background-image: linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); } ================================================ FILE: assets/js/bootstrap.js ================================================ /*! * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=43210ccffbad43b37f66) * Config saved to config.json and https://gist.github.com/43210ccffbad43b37f66 */ if (typeof jQuery === "undefined") { throw new Error("Bootstrap's JavaScript requires jQuery") } /* ======================================================================== * Bootstrap: carousel.js v3.2.0 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = this.sliding = this.interval = this.$active = this.$items = null this.options.pause == 'hover' && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } Carousel.VERSION = '3.2.0' Carousel.DEFAULTS = { interval: 5000, pause: 'hover', wrap: true } Carousel.prototype.keydown = function (e) { switch (e.which) { case 37: this.prev(); break case 39: this.next(); break default: return } e.preventDefault() } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getItemIndex = function (item) { this.$items = item.parent().children('.item') return this.$items.index(item || this.$active) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || $active[type]() var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var fallback = type == 'next' ? 'first' : 'last' var that = this if (!$next.length) { if (!this.options.wrap) return $next = this.$element.find('.item')[fallback]() } if ($next.hasClass('active')) return (this.sliding = false) var relatedTarget = $next[0] var slideEvent = $.Event('slide.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) this.$element.trigger(slideEvent) if (slideEvent.isDefaultPrevented()) return this.sliding = true isCycling && this.pause() if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) $nextIndicator && $nextIndicator.addClass('active') } var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" if ($.support.transition && this.$element.hasClass('slide')) { $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one('bsTransitionEnd', function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger(slidEvent) }, 0) }) .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) } else { $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger(slidEvent) } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } var old = $.fn.carousel $.fn.carousel = Plugin $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 if (!$target.hasClass('carousel')) return var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false Plugin.call($target, options) if (slideIndex) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() }) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) Plugin.call($carousel, $carousel.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: transition.js v3.2.0 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() if (!$.support.transition) return $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) } } }) }(jQuery); ================================================ FILE: bower.json ================================================ { "name": "lazysizes", "repo": "afarkas/lazysizes", "main": "lazysizes.js", "scripts": [ "lazysizes.js", "lazysizes.min.js" ], "license": "MIT", "authors": [ "Alexander Farkas " ], "moduleType": [ "amd", "globals" ], "description": "High performance (jankfree) lazy loader for images (including responsive images), iframes and scripts (widgets).", "keywords": ["lazy", "lazyloader", "performance", "responsive", "image", "responsive images", "picture", "srcset"], "ignore": [ "**/.*", "node_modules", "/assets", "/maxdpr", "/rias", "/include", "/optimumx", "bower_components", "/test", "/tests", "index.html", "no-src.html", "animate.html", "Gruntfile.js", "component.json", "package.json", "plato-report" ] } ================================================ FILE: component.json ================================================ { "name": "lazysizes", "version": "1.4.0", "repo": "afarkas/lazysizes", "main": "lazysizes.min.js", "scripts": ["lazysizes.min.js"], "description": "High performance (jankfree) lazy loader for images (including responsive images), iframes and scripts (widgets).", "keywords": ["lazy", "lazyloader", "performance", "responsive", "image", "responsive images", "picture", "srcset"], "license": "MIT" } ================================================ FILE: index.html ================================================ lazysizes - the ultimate 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 class lazyload to all elements, which should be lazy loaded. For a short API description go to the readme.md.

bird

Image with LQIP technique

The low quality image placeholder pattern uses a low quality image for the first impression.

<img
	alt="bird"
	src="low-quality.jpg"
	data-src="normal-quality.jpg"
	class="lazyload" />
Mountain

Normal lazy image

Of course the low quality image can be simply omitted to save more image data.

<img class="lazyload" data-src="image.jpg" alt="Mountain" />
Jellyfish

responsive image with srcset and sizes attribute

Simply use data-srcset and you can support responsive images.

<img
	alt="Jellyfish"
	sizes="(min-width: 1000px) 930px, 90vw"
	data-srcset="small.jpg 500w,
		medium.jpg 640w,
		big.jpg 1024w"
	data-src="medium.jpg"
	class="lazyload" />
image with artdirection

responsive image with the picture element

The picture element is also supported. Simply add the lazyload class to the img and use data-srcset on your source and the img element.

<picture>
	<!--[if IE 9]><video style="display: none"><![endif]-->
	<source
		data-srcset="240.jpg 240w,
			320.jpg 320w,
			500.jpg 500w"
		media="(max-width: 550px)" />
	<source
			data-srcset="640.jpg 640w,
				990.jpg 990w,
				1024.jpg 1024w"
			media="(max-width: 1024px)" />
	<source
			data-srcset="1200.jpg 1200w" />
<!--[if IE 9]></video><![endif]-->
<img
		src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
		data-src="1024.jpg"
		class="lazyload"
		alt="image with artdirection" />
</picture>

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; }.

house by the lake

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" />

For responsive images support you must use either use a full polyfill like picturefill or use the extreme lightweight partial respimg polyfill plugin or use the Responsive Images as a Service extension.

iframe

Iframes can be loaded too:

<iframe data-src="//www.youtube.com/embed/ZfV-aYdU4uE" class="lazyload" frameborder="0" allowfullscreen></iframe>
Windows on Istanbul

[data-expand]: More than lazyloading

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.

<style>
.teaser.lazyload {
	opacity: 0;
	transform: scale(0.8);
}

.teaser.lazyloaded {
	opacity: 1;
	transform: scale(1);
	transition: all 700ms;
}
</style>

<script>

window.lazySizesConfig = {
	addClasses: true
};
</script>

<div class="teaser lazyload" data-expand="-110">
	<img data-src="image.jpg" class="lazyload" />
	<h1>Teaser Title</h1>
	<p>...</p>
</div>

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:

<div data-ride="carousel" data-script="assets/js/bootstrap.min.js" class="carousel lazyload">
	<!-- widget content -->
<div>
Bird
Mountains
Jellyfish
Flower
Borobudur
A tree in the blue
Windows on Istanbul
Goldie Dawn
Avebury Stone Circle
el castil de tierra
sunset
Sky and earth
Missing Ulsoor lake (Explore)
Oxford Path 2

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: lazysizes-umd.js ================================================ (function(window, factory) { var lazySizes = factory(window, window.document, Date); if(typeof module == 'object' && module.exports){ module.exports = lazySizes; } else if (typeof define == 'function' && define.amd) { define(lazySizes); } else { window.lazySizes = lazySizes; } }(window, /** * @typedef { import("./types/global").LazySizesConfigPartial } LazySizesConfigPartial */ function l(window, document, Date) { // Pass in the window Date function also for SSR because the Date class can be lost 'use strict'; /*jshint eqnull:true */ var lazysizes, /** * @type { LazySizesConfigPartial } */ lazySizesCfg; (function(){ var prop; var lazySizesDefaults = { lazyClass: 'lazyload', loadedClass: 'lazyloaded', loadingClass: 'lazyloading', preloadClass: 'lazypreload', errorClass: 'lazyerror', //strictClass: 'lazystrict', autosizesClass: 'lazyautosizes', fastLoadedClass: 'ls-is-cached', iframeLoadMode: 0, srcAttr: 'data-src', srcsetAttr: 'data-srcset', sizesAttr: 'data-sizes', //preloadAfterLoad: false, minSize: 40, customMedia: {}, init: true, expFactor: 1.5, hFac: 0.8, loadMode: 2, loadHidden: true, ricTimeout: 0, throttleDelay: 125, }; lazySizesCfg = window.lazySizesConfig || window.lazysizesConfig || {}; for(prop in lazySizesDefaults){ if(!(prop in lazySizesCfg)){ lazySizesCfg[prop] = lazySizesDefaults[prop]; } } })(); if (!document || !document.getElementsByClassName) { return { init: function () {}, /** * @type { LazySizesConfigPartial } */ cfg: lazySizesCfg, /** * @type { true } */ noSupport: true, }; } var docElem = document.documentElement; var supportPicture = window.HTMLPictureElement; var _addEventListener = 'addEventListener'; var _getAttribute = 'getAttribute'; /** * Update to bind to window because 'this' becomes null during SSR * builds. */ var addEventListener = window[_addEventListener].bind(window); var setTimeout = window.setTimeout; var requestAnimationFrame = window.requestAnimationFrame || setTimeout; var requestIdleCallback = window.requestIdleCallback; var regPicture = /^picture$/i; var loadEvents = ['load', 'error', 'lazyincluded', '_lazyloaded']; var regClassCache = {}; var forEach = Array.prototype.forEach; /** * @param ele {Element} * @param cls {string} */ var hasClass = function(ele, cls) { if(!regClassCache[cls]){ regClassCache[cls] = new RegExp('(\\s|^)'+cls+'(\\s|$)'); } return regClassCache[cls].test(ele[_getAttribute]('class') || '') && regClassCache[cls]; }; /** * @param ele {Element} * @param cls {string} */ var addClass = function(ele, cls) { if (!hasClass(ele, cls)){ ele.setAttribute('class', (ele[_getAttribute]('class') || '').trim() + ' ' + cls); } }; /** * @param ele {Element} * @param cls {string} */ var removeClass = function(ele, cls) { var reg; if ((reg = hasClass(ele,cls))) { ele.setAttribute('class', (ele[_getAttribute]('class') || '').replace(reg, ' ')); } }; var addRemoveLoadEvents = function(dom, fn, add){ var action = add ? _addEventListener : 'removeEventListener'; if(add){ addRemoveLoadEvents(dom, fn); } loadEvents.forEach(function(evt){ dom[action](evt, fn); }); }; /** * @param elem { Element } * @param name { string } * @param detail { any } * @param noBubbles { boolean } * @param noCancelable { boolean } * @returns { CustomEvent } */ var triggerEvent = function(elem, name, detail, noBubbles, noCancelable){ var event = document.createEvent('Event'); if(!detail){ detail = {}; } detail.instance = lazysizes; event.initEvent(name, !noBubbles, !noCancelable); event.detail = detail; elem.dispatchEvent(event); return event; }; var updatePolyfill = function (el, full){ var polyfill; if( !supportPicture && ( polyfill = (window.picturefill || lazySizesCfg.pf) ) ){ if(full && full.src && !el[_getAttribute]('srcset')){ el.setAttribute('srcset', full.src); } polyfill({reevaluate: true, elements: [el]}); } else if(full && full.src){ el.src = full.src; } }; var getCSS = function (elem, style){ return (getComputedStyle(elem, null) || {})[style]; }; /** * * @param elem { Element } * @param parent { Element } * @param [width] {number} * @returns {number} */ var getWidth = function(elem, parent, width){ width = width || elem.offsetWidth; while(width < lazySizesCfg.minSize && parent && !elem._lazysizesWidth){ width = parent.offsetWidth; parent = parent.parentNode; } return width; }; var rAF = (function(){ var running, waiting; var firstFns = []; var secondFns = []; var fns = firstFns; var run = function(){ var runFns = fns; fns = firstFns.length ? secondFns : firstFns; running = true; waiting = false; while(runFns.length){ runFns.shift()(); } running = false; }; var rafBatch = function(fn, queue){ if(running && !queue){ fn.apply(this, arguments); } else { fns.push(fn); if(!waiting){ waiting = true; (document.hidden ? setTimeout : requestAnimationFrame)(run); } } }; rafBatch._lsFlush = run; return rafBatch; })(); var rAFIt = function(fn, simple){ return simple ? function() { rAF(fn); } : function(){ var that = this; var args = arguments; rAF(function(){ fn.apply(that, args); }); } ; }; var throttle = function(fn){ var running; var lastTime = 0; var gDelay = lazySizesCfg.throttleDelay; var rICTimeout = lazySizesCfg.ricTimeout; var run = function(){ running = false; lastTime = Date.now(); fn(); }; var idleCallback = requestIdleCallback && rICTimeout > 49 ? function(){ requestIdleCallback(run, {timeout: rICTimeout}); if(rICTimeout !== lazySizesCfg.ricTimeout){ rICTimeout = lazySizesCfg.ricTimeout; } } : rAFIt(function(){ setTimeout(run); }, true) ; return function(isPriority){ var delay; if((isPriority = isPriority === true)){ rICTimeout = 33; } if(running){ return; } running = true; delay = gDelay - (Date.now() - lastTime); if(delay < 0){ delay = 0; } if(isPriority || delay < 9){ idleCallback(); } else { setTimeout(idleCallback, delay); } }; }; //based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html var debounce = function(func) { var timeout, timestamp; var wait = 99; var run = function(){ timeout = null; func(); }; var later = function() { var last = Date.now() - timestamp; if (last < wait) { setTimeout(later, wait - last); } else { (requestIdleCallback || run)(run); } }; return function() { timestamp = Date.now(); if (!timeout) { timeout = setTimeout(later, wait); } }; }; var loader = (function(){ var preloadElems, isCompleted, resetPreloadingTimer, loadMode, started; var eLvW, elvH, eLtop, eLleft, eLright, eLbottom, isBodyHidden; var regImg = /^img$/i; var regIframe = /^iframe$/i; var supportScroll = ('onscroll' in window) && !(/(gle|ing)bot/.test(navigator.userAgent)); var shrinkExpand = 0; var currentExpand = 0; var isLoading = 0; var lowRuns = -1; var resetPreloading = function(e){ isLoading--; if(!e || isLoading < 0 || !e.target){ isLoading = 0; } }; var isVisible = function (elem) { if (isBodyHidden == null) { isBodyHidden = getCSS(document.body, 'visibility') == 'hidden'; } return isBodyHidden || !(getCSS(elem.parentNode, 'visibility') == 'hidden' && getCSS(elem, 'visibility') == 'hidden'); }; var isNestedVisible = function(elem, elemExpand){ var outerRect; var parent = elem; var visible = isVisible(elem); eLtop -= elemExpand; eLbottom += elemExpand; eLleft -= elemExpand; eLright += elemExpand; while(visible && (parent = parent.offsetParent) && parent != document.body && parent != docElem){ visible = ((getCSS(parent, 'opacity') || 1) > 0); if(visible && getCSS(parent, 'overflow') != 'visible'){ outerRect = parent.getBoundingClientRect(); visible = eLright > outerRect.left && eLleft < outerRect.right && eLbottom > outerRect.top - 1 && eLtop < outerRect.bottom + 1 ; } } return visible; }; var checkElements = function() { var eLlen, i, rect, autoLoadElem, loadedSomething, elemExpand, elemNegativeExpand, elemExpandVal, beforeExpandVal, defaultExpand, preloadExpand, hFac; var lazyloadElems = lazysizes.elements; if((loadMode = lazySizesCfg.loadMode) && isLoading < 8 && (eLlen = lazyloadElems.length)){ i = 0; lowRuns++; for(; i < eLlen; i++){ if(!lazyloadElems[i] || lazyloadElems[i]._lazyRace){continue;} if(!supportScroll || (lazysizes.prematureUnveil && lazysizes.prematureUnveil(lazyloadElems[i]))){unveilElement(lazyloadElems[i]);continue;} if(!(elemExpandVal = lazyloadElems[i][_getAttribute]('data-expand')) || !(elemExpand = elemExpandVal * 1)){ elemExpand = currentExpand; } if (!defaultExpand) { defaultExpand = (!lazySizesCfg.expand || lazySizesCfg.expand < 1) ? docElem.clientHeight > 500 && docElem.clientWidth > 500 ? 500 : 370 : lazySizesCfg.expand; lazysizes._defEx = defaultExpand; preloadExpand = defaultExpand * lazySizesCfg.expFactor; hFac = lazySizesCfg.hFac; isBodyHidden = null; if(currentExpand < preloadExpand && isLoading < 1 && lowRuns > 2 && loadMode > 2 && !document.hidden){ currentExpand = preloadExpand; lowRuns = 0; } else if(loadMode > 1 && lowRuns > 1 && isLoading < 6){ currentExpand = defaultExpand; } else { currentExpand = shrinkExpand; } } if(beforeExpandVal !== elemExpand){ eLvW = innerWidth + (elemExpand * hFac); elvH = innerHeight + elemExpand; elemNegativeExpand = elemExpand * -1; beforeExpandVal = elemExpand; } rect = lazyloadElems[i].getBoundingClientRect(); if ((eLbottom = rect.bottom) >= elemNegativeExpand && (eLtop = rect.top) <= elvH && (eLright = rect.right) >= elemNegativeExpand * hFac && (eLleft = rect.left) <= eLvW && (eLbottom || eLright || eLleft || eLtop) && (lazySizesCfg.loadHidden || isVisible(lazyloadElems[i])) && ((isCompleted && isLoading < 3 && !elemExpandVal && (loadMode < 3 || lowRuns < 4)) || isNestedVisible(lazyloadElems[i], elemExpand))){ unveilElement(lazyloadElems[i]); loadedSomething = true; if(isLoading > 9){break;} } else if(!loadedSomething && isCompleted && !autoLoadElem && isLoading < 4 && lowRuns < 4 && loadMode > 2 && (preloadElems[0] || lazySizesCfg.preloadAfterLoad) && (preloadElems[0] || (!elemExpandVal && ((eLbottom || eLright || eLleft || eLtop) || lazyloadElems[i][_getAttribute](lazySizesCfg.sizesAttr) != 'auto')))){ autoLoadElem = preloadElems[0] || lazyloadElems[i]; } } if(autoLoadElem && !loadedSomething){ unveilElement(autoLoadElem); } } }; var throttledCheckElements = throttle(checkElements); var switchLoadingClass = function(e){ var elem = e.target; if (elem._lazyCache) { delete elem._lazyCache; return; } resetPreloading(e); addClass(elem, lazySizesCfg.loadedClass); removeClass(elem, lazySizesCfg.loadingClass); addRemoveLoadEvents(elem, rafSwitchLoadingClass); triggerEvent(elem, 'lazyloaded'); }; var rafedSwitchLoadingClass = rAFIt(switchLoadingClass); var rafSwitchLoadingClass = function(e){ rafedSwitchLoadingClass({target: e.target}); }; var changeIframeSrc = function(elem, src){ var loadMode = elem.getAttribute('data-load-mode') || lazySizesCfg.iframeLoadMode; // loadMode can be also a string! if (loadMode == 0) { elem.contentWindow.location.replace(src); } else if (loadMode == 1) { elem.src = src; } }; var handleSources = function(source){ var customMedia; var sourceSrcset = source[_getAttribute](lazySizesCfg.srcsetAttr); if( (customMedia = lazySizesCfg.customMedia[source[_getAttribute]('data-media') || source[_getAttribute]('media')]) ){ source.setAttribute('media', customMedia); } if(sourceSrcset){ source.setAttribute('srcset', sourceSrcset); } }; var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg){ var src, srcset, parent, isPicture, event, firesLoad; if(!(event = triggerEvent(elem, 'lazybeforeunveil', detail)).defaultPrevented){ if(sizes){ if(isAuto){ addClass(elem, lazySizesCfg.autosizesClass); } else { elem.setAttribute('sizes', sizes); } } srcset = elem[_getAttribute](lazySizesCfg.srcsetAttr); src = elem[_getAttribute](lazySizesCfg.srcAttr); if(isImg) { parent = elem.parentNode; isPicture = parent && regPicture.test(parent.nodeName || ''); } firesLoad = detail.firesLoad || (('src' in elem) && (srcset || src || isPicture)); event = {target: elem}; addClass(elem, lazySizesCfg.loadingClass); if(firesLoad){ clearTimeout(resetPreloadingTimer); resetPreloadingTimer = setTimeout(resetPreloading, 2500); addRemoveLoadEvents(elem, rafSwitchLoadingClass, true); } if(isPicture){ forEach.call(parent.getElementsByTagName('source'), handleSources); } if(srcset){ elem.setAttribute('srcset', srcset); } else if(src && !isPicture){ if(regIframe.test(elem.nodeName)){ changeIframeSrc(elem, src); } else { elem.src = src; } } if(isImg && (srcset || isPicture)){ updatePolyfill(elem, {src: src}); } } if(elem._lazyRace){ delete elem._lazyRace; } removeClass(elem, lazySizesCfg.lazyClass); rAF(function(){ // Part of this can be removed as soon as this fix is older: https://bugs.chromium.org/p/chromium/issues/detail?id=7731 (2015) var isLoaded = elem.complete && elem.naturalWidth > 1; if( !firesLoad || isLoaded){ if (isLoaded) { addClass(elem, lazySizesCfg.fastLoadedClass); } switchLoadingClass(event); elem._lazyCache = true; setTimeout(function(){ if ('_lazyCache' in elem) { delete elem._lazyCache; } }, 9); } if (elem.loading == 'lazy') { isLoading--; } }, true); }); /** * * @param elem { Element } */ var unveilElement = function (elem){ if (elem._lazyRace) {return;} var detail; var isImg = regImg.test(elem.nodeName); //allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw") var sizes = isImg && (elem[_getAttribute](lazySizesCfg.sizesAttr) || elem[_getAttribute]('sizes')); var isAuto = sizes == 'auto'; if( (isAuto || !isCompleted) && isImg && (elem[_getAttribute]('src') || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesCfg.errorClass) && hasClass(elem, lazySizesCfg.lazyClass)){return;} detail = triggerEvent(elem, 'lazyunveilread').detail; if(isAuto){ autoSizer.updateElem(elem, true, elem.offsetWidth); } elem._lazyRace = true; isLoading++; lazyUnveil(elem, detail, isAuto, sizes, isImg); }; var afterScroll = debounce(function(){ lazySizesCfg.loadMode = 3; throttledCheckElements(); }); var altLoadmodeScrollListner = function(){ if(lazySizesCfg.loadMode == 3){ lazySizesCfg.loadMode = 2; } afterScroll(); }; var onload = function(){ if(isCompleted){return;} if(Date.now() - started < 999){ setTimeout(onload, 999); return; } isCompleted = true; lazySizesCfg.loadMode = 3; throttledCheckElements(); addEventListener('scroll', altLoadmodeScrollListner, true); }; return { _: function(){ started = Date.now(); lazysizes.elements = document.getElementsByClassName(lazySizesCfg.lazyClass); preloadElems = document.getElementsByClassName(lazySizesCfg.lazyClass + ' ' + lazySizesCfg.preloadClass); addEventListener('scroll', throttledCheckElements, true); addEventListener('resize', throttledCheckElements, true); addEventListener('pageshow', function (e) { if (e.persisted) { var loadingElements = document.querySelectorAll('.' + lazySizesCfg.loadingClass); if (loadingElements.length && loadingElements.forEach) { requestAnimationFrame(function () { loadingElements.forEach( function (img) { if (img.complete) { unveilElement(img); } }); }); } } }); if(window.MutationObserver){ new MutationObserver( throttledCheckElements ).observe( docElem, {childList: true, subtree: true, attributes: true} ); } else { docElem[_addEventListener]('DOMNodeInserted', throttledCheckElements, true); docElem[_addEventListener]('DOMAttrModified', throttledCheckElements, true); setInterval(throttledCheckElements, 999); } addEventListener('hashchange', throttledCheckElements, true); //, 'fullscreenchange' ['focus', 'mouseover', 'click', 'load', 'transitionend', 'animationend'].forEach(function(name){ document[_addEventListener](name, throttledCheckElements, true); }); if((/d$|^c/.test(document.readyState))){ onload(); } else { addEventListener('load', onload); document[_addEventListener]('DOMContentLoaded', throttledCheckElements); setTimeout(onload, 20000); } if(lazysizes.elements.length){ checkElements(); rAF._lsFlush(); } else { throttledCheckElements(); } }, checkElems: throttledCheckElements, unveil: unveilElement, _aLSL: altLoadmodeScrollListner, }; })(); var autoSizer = (function(){ var autosizesElems; var sizeElement = rAFIt(function(elem, parent, event, width){ var sources, i, len; elem._lazysizesWidth = width; width += 'px'; elem.setAttribute('sizes', width); if(regPicture.test(parent.nodeName || '')){ sources = parent.getElementsByTagName('source'); for(i = 0, len = sources.length; i < len; i++){ sources[i].setAttribute('sizes', width); } } if(!event.detail.dataAttr){ updatePolyfill(elem, event.detail); } }); /** * * @param elem {Element} * @param dataAttr * @param [width] { number } */ var getSizeElement = function (elem, dataAttr, width){ var event; var parent = elem.parentNode; if(parent){ width = getWidth(elem, parent, width); event = triggerEvent(elem, 'lazybeforesizes', {width: width, dataAttr: !!dataAttr}); if(!event.defaultPrevented){ width = event.detail.width; if(width && width !== elem._lazysizesWidth){ sizeElement(elem, parent, event, width); } } } }; var updateElementsSizes = function(){ var i; var len = autosizesElems.length; if(len){ i = 0; for(; i < len; i++){ getSizeElement(autosizesElems[i]); } } }; var debouncedUpdateElementsSizes = debounce(updateElementsSizes); return { _: function(){ autosizesElems = document.getElementsByClassName(lazySizesCfg.autosizesClass); addEventListener('resize', debouncedUpdateElementsSizes); }, checkElems: debouncedUpdateElementsSizes, updateElem: getSizeElement }; })(); var init = function(){ if(!init.i && document.getElementsByClassName){ init.i = true; autoSizer._(); loader._(); } }; setTimeout(function(){ if(lazySizesCfg.init){ init(); } }); lazysizes = { /** * @type { LazySizesConfigPartial } */ cfg: lazySizesCfg, autoSizer: autoSizer, loader: loader, init: init, uP: updatePolyfill, aC: addClass, rC: removeClass, hC: hasClass, fire: triggerEvent, gW: getWidth, rAF: rAF, }; return lazysizes; } )); ================================================ FILE: lazysizes.d.ts ================================================ import './types/global'; export = lazySizes; declare var lazySizes: { init: () => void; /** * @type { LazySizesConfigPartial } */ cfg: LazySizesConfigPartial; /** * @type { true } */ noSupport: true; autoSizer?: undefined; loader?: undefined; uP?: undefined; aC?: undefined; rC?: undefined; hC?: undefined; fire?: undefined; gW?: undefined; rAF?: undefined; } | { /** * @type { LazySizesConfigPartial } */ cfg: LazySizesConfigPartial; autoSizer: { _: () => void; checkElems: () => void; updateElem: (elem: Element, dataAttr: any, width?: number) => void; }; loader: { _: () => void; checkElems: (isPriority: any) => void; unveil: (elem: Element) => void; _aLSL: () => void; }; init: () => void; uP: (el: any, full: any) => void; aC: (ele: Element, cls: string) => void; rC: (ele: Element, cls: string) => void; hC: (ele: Element, cls: string) => any; fire: (elem: Element, name: string, detail: any, noBubbles: boolean, noCancelable: boolean) => CustomEvent; gW: (elem: Element, parent: Element, width?: number) => number; rAF: { (fn: any, queue: any, ...args: any[]): void; _lsFlush: () => void; }; /** * @type { true } */ noSupport?: undefined; }; declare namespace lazySizes { export { LazySizesConfigPartial }; } type LazySizesConfigPartial = { [x: string]: any; lazyClass?: string; loadedClass?: string; loadingClass?: string; preloadClass?: string; errorClass?: string; autosizesClass?: string; fastLoadedClass?: string; iframeLoadMode?: 0 | 1; srcAttr?: string; srcsetAttr?: string; sizesAttr?: string; preloadAfterLoad?: boolean; minSize?: number; customMedia?: Record; init?: boolean; expFactor?: number; hFac?: number; loadMode?: 0 | 1 | 2 | 3; loadHidden?: boolean; ricTimeout?: number; throttleDelay?: number; }; ================================================ FILE: lazysizes.js ================================================ (function(window, factory) { var lazySizes = factory(window, window.document, Date); window.lazySizes = lazySizes; if(typeof module == 'object' && module.exports){ module.exports = lazySizes; } }(typeof window != 'undefined' ? window : {}, /** * @typedef { import("./types/global").LazySizesConfigPartial } LazySizesConfigPartial */ function l(window, document, Date) { // Pass in the window Date function also for SSR because the Date class can be lost 'use strict'; /*jshint eqnull:true */ var lazysizes, /** * @type { LazySizesConfigPartial } */ lazySizesCfg; (function(){ var prop; var lazySizesDefaults = { lazyClass: 'lazyload', loadedClass: 'lazyloaded', loadingClass: 'lazyloading', preloadClass: 'lazypreload', errorClass: 'lazyerror', //strictClass: 'lazystrict', autosizesClass: 'lazyautosizes', fastLoadedClass: 'ls-is-cached', iframeLoadMode: 0, srcAttr: 'data-src', srcsetAttr: 'data-srcset', sizesAttr: 'data-sizes', //preloadAfterLoad: false, minSize: 40, customMedia: {}, init: true, expFactor: 1.5, hFac: 0.8, loadMode: 2, loadHidden: true, ricTimeout: 0, throttleDelay: 125, }; lazySizesCfg = window.lazySizesConfig || window.lazysizesConfig || {}; for(prop in lazySizesDefaults){ if(!(prop in lazySizesCfg)){ lazySizesCfg[prop] = lazySizesDefaults[prop]; } } })(); if (!document || !document.getElementsByClassName) { return { init: function () {}, /** * @type { LazySizesConfigPartial } */ cfg: lazySizesCfg, /** * @type { true } */ noSupport: true, }; } var docElem = document.documentElement; var supportPicture = window.HTMLPictureElement; var _addEventListener = 'addEventListener'; var _getAttribute = 'getAttribute'; /** * Update to bind to window because 'this' becomes null during SSR * builds. */ var addEventListener = window[_addEventListener].bind(window); var setTimeout = window.setTimeout; var requestAnimationFrame = window.requestAnimationFrame || setTimeout; var requestIdleCallback = window.requestIdleCallback; var regPicture = /^picture$/i; var loadEvents = ['load', 'error', 'lazyincluded', '_lazyloaded']; var regClassCache = {}; var forEach = Array.prototype.forEach; /** * @param ele {Element} * @param cls {string} */ var hasClass = function(ele, cls) { if(!regClassCache[cls]){ regClassCache[cls] = new RegExp('(\\s|^)'+cls+'(\\s|$)'); } return regClassCache[cls].test(ele[_getAttribute]('class') || '') && regClassCache[cls]; }; /** * @param ele {Element} * @param cls {string} */ var addClass = function(ele, cls) { if (!hasClass(ele, cls)){ ele.setAttribute('class', (ele[_getAttribute]('class') || '').trim() + ' ' + cls); } }; /** * @param ele {Element} * @param cls {string} */ var removeClass = function(ele, cls) { var reg; if ((reg = hasClass(ele,cls))) { ele.setAttribute('class', (ele[_getAttribute]('class') || '').replace(reg, ' ')); } }; var addRemoveLoadEvents = function(dom, fn, add){ var action = add ? _addEventListener : 'removeEventListener'; if(add){ addRemoveLoadEvents(dom, fn); } loadEvents.forEach(function(evt){ dom[action](evt, fn); }); }; /** * @param elem { Element } * @param name { string } * @param detail { any } * @param noBubbles { boolean } * @param noCancelable { boolean } * @returns { CustomEvent } */ var triggerEvent = function(elem, name, detail, noBubbles, noCancelable){ var event = document.createEvent('Event'); if(!detail){ detail = {}; } detail.instance = lazysizes; event.initEvent(name, !noBubbles, !noCancelable); event.detail = detail; elem.dispatchEvent(event); return event; }; var updatePolyfill = function (el, full){ var polyfill; if( !supportPicture && ( polyfill = (window.picturefill || lazySizesCfg.pf) ) ){ if(full && full.src && !el[_getAttribute]('srcset')){ el.setAttribute('srcset', full.src); } polyfill({reevaluate: true, elements: [el]}); } else if(full && full.src){ el.src = full.src; } }; var getCSS = function (elem, style){ return (getComputedStyle(elem, null) || {})[style]; }; /** * * @param elem { Element } * @param parent { Element } * @param [width] {number} * @returns {number} */ var getWidth = function(elem, parent, width){ width = width || elem.offsetWidth; while(width < lazySizesCfg.minSize && parent && !elem._lazysizesWidth){ width = parent.offsetWidth; parent = parent.parentNode; } return width; }; var rAF = (function(){ var running, waiting; var firstFns = []; var secondFns = []; var fns = firstFns; var run = function(){ var runFns = fns; fns = firstFns.length ? secondFns : firstFns; running = true; waiting = false; while(runFns.length){ runFns.shift()(); } running = false; }; var rafBatch = function(fn, queue){ if(running && !queue){ fn.apply(this, arguments); } else { fns.push(fn); if(!waiting){ waiting = true; (document.hidden ? setTimeout : requestAnimationFrame)(run); } } }; rafBatch._lsFlush = run; return rafBatch; })(); var rAFIt = function(fn, simple){ return simple ? function() { rAF(fn); } : function(){ var that = this; var args = arguments; rAF(function(){ fn.apply(that, args); }); } ; }; var throttle = function(fn){ var running; var lastTime = 0; var gDelay = lazySizesCfg.throttleDelay; var rICTimeout = lazySizesCfg.ricTimeout; var run = function(){ running = false; lastTime = Date.now(); fn(); }; var idleCallback = requestIdleCallback && rICTimeout > 49 ? function(){ requestIdleCallback(run, {timeout: rICTimeout}); if(rICTimeout !== lazySizesCfg.ricTimeout){ rICTimeout = lazySizesCfg.ricTimeout; } } : rAFIt(function(){ setTimeout(run); }, true) ; return function(isPriority){ var delay; if((isPriority = isPriority === true)){ rICTimeout = 33; } if(running){ return; } running = true; delay = gDelay - (Date.now() - lastTime); if(delay < 0){ delay = 0; } if(isPriority || delay < 9){ idleCallback(); } else { setTimeout(idleCallback, delay); } }; }; //based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html var debounce = function(func) { var timeout, timestamp; var wait = 99; var run = function(){ timeout = null; func(); }; var later = function() { var last = Date.now() - timestamp; if (last < wait) { setTimeout(later, wait - last); } else { (requestIdleCallback || run)(run); } }; return function() { timestamp = Date.now(); if (!timeout) { timeout = setTimeout(later, wait); } }; }; var loader = (function(){ var preloadElems, isCompleted, resetPreloadingTimer, loadMode, started; var eLvW, elvH, eLtop, eLleft, eLright, eLbottom, isBodyHidden; var regImg = /^img$/i; var regIframe = /^iframe$/i; var supportScroll = ('onscroll' in window) && !(/(gle|ing)bot/.test(navigator.userAgent)); var shrinkExpand = 0; var currentExpand = 0; var isLoading = 0; var lowRuns = -1; var resetPreloading = function(e){ isLoading--; if(!e || isLoading < 0 || !e.target){ isLoading = 0; } }; var isVisible = function (elem) { if (isBodyHidden == null) { isBodyHidden = getCSS(document.body, 'visibility') == 'hidden'; } return isBodyHidden || !(getCSS(elem.parentNode, 'visibility') == 'hidden' && getCSS(elem, 'visibility') == 'hidden'); }; var isNestedVisible = function(elem, elemExpand){ var outerRect; var parent = elem; var visible = isVisible(elem); eLtop -= elemExpand; eLbottom += elemExpand; eLleft -= elemExpand; eLright += elemExpand; while(visible && (parent = parent.offsetParent) && parent != document.body && parent != docElem){ visible = ((getCSS(parent, 'opacity') || 1) > 0); if(visible && getCSS(parent, 'overflow') != 'visible'){ outerRect = parent.getBoundingClientRect(); visible = eLright > outerRect.left && eLleft < outerRect.right && eLbottom > outerRect.top - 1 && eLtop < outerRect.bottom + 1 ; } } return visible; }; var checkElements = function() { var eLlen, i, rect, autoLoadElem, loadedSomething, elemExpand, elemNegativeExpand, elemExpandVal, beforeExpandVal, defaultExpand, preloadExpand, hFac; var lazyloadElems = lazysizes.elements; if((loadMode = lazySizesCfg.loadMode) && isLoading < 8 && (eLlen = lazyloadElems.length)){ i = 0; lowRuns++; for(; i < eLlen; i++){ if(!lazyloadElems[i] || lazyloadElems[i]._lazyRace){continue;} if(!supportScroll || (lazysizes.prematureUnveil && lazysizes.prematureUnveil(lazyloadElems[i]))){unveilElement(lazyloadElems[i]);continue;} if(!(elemExpandVal = lazyloadElems[i][_getAttribute]('data-expand')) || !(elemExpand = elemExpandVal * 1)){ elemExpand = currentExpand; } if (!defaultExpand) { defaultExpand = (!lazySizesCfg.expand || lazySizesCfg.expand < 1) ? docElem.clientHeight > 500 && docElem.clientWidth > 500 ? 500 : 370 : lazySizesCfg.expand; lazysizes._defEx = defaultExpand; preloadExpand = defaultExpand * lazySizesCfg.expFactor; hFac = lazySizesCfg.hFac; isBodyHidden = null; if(currentExpand < preloadExpand && isLoading < 1 && lowRuns > 2 && loadMode > 2 && !document.hidden){ currentExpand = preloadExpand; lowRuns = 0; } else if(loadMode > 1 && lowRuns > 1 && isLoading < 6){ currentExpand = defaultExpand; } else { currentExpand = shrinkExpand; } } if(beforeExpandVal !== elemExpand){ eLvW = innerWidth + (elemExpand * hFac); elvH = innerHeight + elemExpand; elemNegativeExpand = elemExpand * -1; beforeExpandVal = elemExpand; } rect = lazyloadElems[i].getBoundingClientRect(); if ((eLbottom = rect.bottom) >= elemNegativeExpand && (eLtop = rect.top) <= elvH && (eLright = rect.right) >= elemNegativeExpand * hFac && (eLleft = rect.left) <= eLvW && (eLbottom || eLright || eLleft || eLtop) && (lazySizesCfg.loadHidden || isVisible(lazyloadElems[i])) && ((isCompleted && isLoading < 3 && !elemExpandVal && (loadMode < 3 || lowRuns < 4)) || isNestedVisible(lazyloadElems[i], elemExpand))){ unveilElement(lazyloadElems[i]); loadedSomething = true; if(isLoading > 9){break;} } else if(!loadedSomething && isCompleted && !autoLoadElem && isLoading < 4 && lowRuns < 4 && loadMode > 2 && (preloadElems[0] || lazySizesCfg.preloadAfterLoad) && (preloadElems[0] || (!elemExpandVal && ((eLbottom || eLright || eLleft || eLtop) || lazyloadElems[i][_getAttribute](lazySizesCfg.sizesAttr) != 'auto')))){ autoLoadElem = preloadElems[0] || lazyloadElems[i]; } } if(autoLoadElem && !loadedSomething){ unveilElement(autoLoadElem); } } }; var throttledCheckElements = throttle(checkElements); var switchLoadingClass = function(e){ var elem = e.target; if (elem._lazyCache) { delete elem._lazyCache; return; } resetPreloading(e); addClass(elem, lazySizesCfg.loadedClass); removeClass(elem, lazySizesCfg.loadingClass); addRemoveLoadEvents(elem, rafSwitchLoadingClass); triggerEvent(elem, 'lazyloaded'); }; var rafedSwitchLoadingClass = rAFIt(switchLoadingClass); var rafSwitchLoadingClass = function(e){ rafedSwitchLoadingClass({target: e.target}); }; var changeIframeSrc = function(elem, src){ var loadMode = elem.getAttribute('data-load-mode') || lazySizesCfg.iframeLoadMode; // loadMode can be also a string! if (loadMode == 0) { elem.contentWindow.location.replace(src); } else if (loadMode == 1) { elem.src = src; } }; var handleSources = function(source){ var customMedia; var sourceSrcset = source[_getAttribute](lazySizesCfg.srcsetAttr); if( (customMedia = lazySizesCfg.customMedia[source[_getAttribute]('data-media') || source[_getAttribute]('media')]) ){ source.setAttribute('media', customMedia); } if(sourceSrcset){ source.setAttribute('srcset', sourceSrcset); } }; var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg){ var src, srcset, parent, isPicture, event, firesLoad; if(!(event = triggerEvent(elem, 'lazybeforeunveil', detail)).defaultPrevented){ if(sizes){ if(isAuto){ addClass(elem, lazySizesCfg.autosizesClass); } else { elem.setAttribute('sizes', sizes); } } srcset = elem[_getAttribute](lazySizesCfg.srcsetAttr); src = elem[_getAttribute](lazySizesCfg.srcAttr); if(isImg) { parent = elem.parentNode; isPicture = parent && regPicture.test(parent.nodeName || ''); } firesLoad = detail.firesLoad || (('src' in elem) && (srcset || src || isPicture)); event = {target: elem}; addClass(elem, lazySizesCfg.loadingClass); if(firesLoad){ clearTimeout(resetPreloadingTimer); resetPreloadingTimer = setTimeout(resetPreloading, 2500); addRemoveLoadEvents(elem, rafSwitchLoadingClass, true); } if(isPicture){ forEach.call(parent.getElementsByTagName('source'), handleSources); } if(srcset){ elem.setAttribute('srcset', srcset); } else if(src && !isPicture){ if(regIframe.test(elem.nodeName)){ changeIframeSrc(elem, src); } else { elem.src = src; } } if(isImg && (srcset || isPicture)){ updatePolyfill(elem, {src: src}); } } if(elem._lazyRace){ delete elem._lazyRace; } removeClass(elem, lazySizesCfg.lazyClass); rAF(function(){ // Part of this can be removed as soon as this fix is older: https://bugs.chromium.org/p/chromium/issues/detail?id=7731 (2015) var isLoaded = elem.complete && elem.naturalWidth > 1; if( !firesLoad || isLoaded){ if (isLoaded) { addClass(elem, lazySizesCfg.fastLoadedClass); } switchLoadingClass(event); elem._lazyCache = true; setTimeout(function(){ if ('_lazyCache' in elem) { delete elem._lazyCache; } }, 9); } if (elem.loading == 'lazy') { isLoading--; } }, true); }); /** * * @param elem { Element } */ var unveilElement = function (elem){ if (elem._lazyRace) {return;} var detail; var isImg = regImg.test(elem.nodeName); //allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw") var sizes = isImg && (elem[_getAttribute](lazySizesCfg.sizesAttr) || elem[_getAttribute]('sizes')); var isAuto = sizes == 'auto'; if( (isAuto || !isCompleted) && isImg && (elem[_getAttribute]('src') || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesCfg.errorClass) && hasClass(elem, lazySizesCfg.lazyClass)){return;} detail = triggerEvent(elem, 'lazyunveilread').detail; if(isAuto){ autoSizer.updateElem(elem, true, elem.offsetWidth); } elem._lazyRace = true; isLoading++; lazyUnveil(elem, detail, isAuto, sizes, isImg); }; var afterScroll = debounce(function(){ lazySizesCfg.loadMode = 3; throttledCheckElements(); }); var altLoadmodeScrollListner = function(){ if(lazySizesCfg.loadMode == 3){ lazySizesCfg.loadMode = 2; } afterScroll(); }; var onload = function(){ if(isCompleted){return;} if(Date.now() - started < 999){ setTimeout(onload, 999); return; } isCompleted = true; lazySizesCfg.loadMode = 3; throttledCheckElements(); addEventListener('scroll', altLoadmodeScrollListner, true); }; return { _: function(){ started = Date.now(); lazysizes.elements = document.getElementsByClassName(lazySizesCfg.lazyClass); preloadElems = document.getElementsByClassName(lazySizesCfg.lazyClass + ' ' + lazySizesCfg.preloadClass); addEventListener('scroll', throttledCheckElements, true); addEventListener('resize', throttledCheckElements, true); addEventListener('pageshow', function (e) { if (e.persisted) { var loadingElements = document.querySelectorAll('.' + lazySizesCfg.loadingClass); if (loadingElements.length && loadingElements.forEach) { requestAnimationFrame(function () { loadingElements.forEach( function (img) { if (img.complete) { unveilElement(img); } }); }); } } }); if(window.MutationObserver){ new MutationObserver( throttledCheckElements ).observe( docElem, {childList: true, subtree: true, attributes: true} ); } else { docElem[_addEventListener]('DOMNodeInserted', throttledCheckElements, true); docElem[_addEventListener]('DOMAttrModified', throttledCheckElements, true); setInterval(throttledCheckElements, 999); } addEventListener('hashchange', throttledCheckElements, true); //, 'fullscreenchange' ['focus', 'mouseover', 'click', 'load', 'transitionend', 'animationend'].forEach(function(name){ document[_addEventListener](name, throttledCheckElements, true); }); if((/d$|^c/.test(document.readyState))){ onload(); } else { addEventListener('load', onload); document[_addEventListener]('DOMContentLoaded', throttledCheckElements); setTimeout(onload, 20000); } if(lazysizes.elements.length){ checkElements(); rAF._lsFlush(); } else { throttledCheckElements(); } }, checkElems: throttledCheckElements, unveil: unveilElement, _aLSL: altLoadmodeScrollListner, }; })(); var autoSizer = (function(){ var autosizesElems; var sizeElement = rAFIt(function(elem, parent, event, width){ var sources, i, len; elem._lazysizesWidth = width; width += 'px'; elem.setAttribute('sizes', width); if(regPicture.test(parent.nodeName || '')){ sources = parent.getElementsByTagName('source'); for(i = 0, len = sources.length; i < len; i++){ sources[i].setAttribute('sizes', width); } } if(!event.detail.dataAttr){ updatePolyfill(elem, event.detail); } }); /** * * @param elem {Element} * @param dataAttr * @param [width] { number } */ var getSizeElement = function (elem, dataAttr, width){ var event; var parent = elem.parentNode; if(parent){ width = getWidth(elem, parent, width); event = triggerEvent(elem, 'lazybeforesizes', {width: width, dataAttr: !!dataAttr}); if(!event.defaultPrevented){ width = event.detail.width; if(width && width !== elem._lazysizesWidth){ sizeElement(elem, parent, event, width); } } } }; var updateElementsSizes = function(){ var i; var len = autosizesElems.length; if(len){ i = 0; for(; i < len; i++){ getSizeElement(autosizesElems[i]); } } }; var debouncedUpdateElementsSizes = debounce(updateElementsSizes); return { _: function(){ autosizesElems = document.getElementsByClassName(lazySizesCfg.autosizesClass); addEventListener('resize', debouncedUpdateElementsSizes); }, checkElems: debouncedUpdateElementsSizes, updateElem: getSizeElement }; })(); var init = function(){ if(!init.i && document.getElementsByClassName){ init.i = true; autoSizer._(); loader._(); } }; setTimeout(function(){ if(lazySizesCfg.init){ init(); } }); lazysizes = { /** * @type { LazySizesConfigPartial } */ cfg: lazySizesCfg, autoSizer: autoSizer, loader: loader, init: init, uP: updatePolyfill, aC: addClass, rC: removeClass, hC: hasClass, fire: triggerEvent, gW: getWidth, rAF: rAF, }; return lazysizes; } )); ================================================ FILE: no-src.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 class lazyload to all elements, which should be lazy loaded. For a short API description go to the readme.md.

Boat

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.

<img
	alt="100%x200"
	src="low-quality.jpg"
	data-src="normal-quality.jpg"
	class="lazyload" />
Desert Road

Normal lazy image

The normal image pattern can be used for non-critical/below the fold images or in case there is no low quality image available:

<img class="lazyload" data-src="image.jpg" alt="Desert Road" />

responsive image with srcset and sizes attribute

Simply use data-srcset and data-sizes and you can support responsive images.

<img
	alt=""
	src="small.jpg"
	sizes="(min-width: 1000px) 930px, 90vw"
	data-srcset="small.jpg 500w,
		medium.jpg 640w,
		big.jpg 1024w"
	class="lazyload" />
image with artdirection

responsive image with the picture element

The picture element is also supported. Simply add the lazyload class to the img and use data-srcset on your source and the img element.

<picture>
	<!--[if IE 9]><video style="display: none"><![endif]-->
	<source
		data-srcset="500.jpg"
		media="(max-width: 500px)" />
	<source
			data-srcset="1024.jpg"
			media="(max-width: 1024px)" />
	<source
			data-srcset="1200.jpg" />
<!--[if IE 9]></video><![endif]-->
<img
		src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
		class="lazyload"
		data-src="1024.jpg"
		alt="image with artdirection" />
</picture>

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=""
	data-sizes="auto"
	data-src="medium.jpg"
	data-srcset="small.jpg 500w,
		medium.jpg 640w,
		big.jpg 1024w"
	class="lazyload" />

For responsive images support you must use either use a full polyfill like picturefill or use the extreme lightweight partial respimg polyfill plugin or use the Responsive Images as a Service extension.

iframe

Iframes can be loaded too:

<iframe data-src="//www.youtube.com/embed/ZfV-aYdU4uE" class="lazyload" frameborder="0" allowfullscreen></iframe>
Windows on Istanbul

[data-expand]: More than lazyloading

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.

<style>
.teaser.lazyload {
	opacity: 0;
	transform: scale(0.8);
}

.teaser.lazyloaded {
	opacity: 1;
	transform: scale(1);
	transition: all 700ms;
}
</style>

<script>

window.lazySizesConfig = {
	addClasses: true
};
</script>

<div class="teaser lazyload" data-expand="-80">
	<img data-src="image.jpg" class="lazyload" />
	<h1>Teaser Title</h1>
	<p>...</p>
</div>

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:

<div data-ride="carousel" data-script="assets/js/bootstrap.min.js" class="carousel lazyload">
	<!-- widget content -->
<div>
Desert Road
@ The Desert Tortoise Natural Area
Woman in water
Borobudur
A tree in the blue
Windows on Istanbul
Goldie Dawn
Avebury Stone Circle
el castil de tierra
sunset
Sky and earth
Missing Ulsoor lake (Explore)

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
Abandoned Boat
Abandoned Boat
with data-optimumx without data-optimumx
Windows on Istanbul
Windows on Istanbul
with data-optimumx without data-optimumx
Goldie Dawn
Goldie Dawn
with data-optimumx without data-optimumx
Abandoned Boat
Abandoned Boat
with data-optimumx without data-optimumx
Desert Road
Desert Road
with data-optimumx without data-optimumx
with data-optimumx without data-optimumx
@ The Desert Tortoise Natural Area
@ The Desert Tortoise Natural Area
with data-optimumx without data-optimumx
Borobudur
Borobudur
with data-optimumx without data-optimumx
A tree in the blue
A tree in the blue
with data-optimumx without data-optimumx
Sant Miquel del Fai
Sant Miquel del Fai
with data-optimumx without data-optimumx
Avebury Stone Circle
Avebury Stone Circle
with data-optimumx without data-optimumx
el castil de tierra
el castil de tierra
with data-optimumx without data-optimumx
sunset
sunset
with data-optimumx without data-optimumx
Sky and earth
Sky and earth
with data-optimumx without data-optimumx
Missing Ulsoor lake (Explore)
Missing Ulsoor lake (Explore)
with data-optimumx without data-optimumx
Oxford Path 2
Oxford Path 2
with data-optimumx without data-optimumx
Woman in water
Woman in water
================================================ 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
data-optimumx

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

================================================ 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 image with artdirection ``` ## 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 image with artdirection ``` The aspect ratio feature can be perfectly combined with the tagging feature. ```html image with artdirection ``` ================================================ 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 image with artdirection ``` ## 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 image with artdirection ``` 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
  • Category 1 (articles: )
  • Category 2 (articles: )
  • Category 3 (articles: )
  • ``` ### 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 flexible image ``` 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 flexible image ``` 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 flexible image ``` 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 flexible image ``` ## 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 picture but without artdirection ``` 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 image with artdirection image with artdirection ``` ### 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

    Nothing Twitter is doing is working https://t.co/s0FppnacwK pic.twitter.com/GK9MRfQkYO

    — The Verge (@verge) April 26, 2016
    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.

    For a short but full API description go to the readme.md.

    Basic RIaS implementation

    Include the ls.rias plugin right after your lazysizes script, add the lazyload class as usual and the data-sizes="auto" attribute.

    Inside of your data-src value include the {width} placeholder

    <script src="../lazysizes.js"></script>
    <script src="../plugins/rias/ls.rias.js"></script>
    
    <img
    	src="http://placehold.it/100"
    	data-src="http://placehold.it/{width}"
    	data-sizes="auto"
    	class="lazyload"
    	alt="" />
    

    Aspect ratio

    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

    <style>
    		img.rectangle {
    			--ls-aspectratio: 2;
    		}
    <style/>
    
    <img
    	src="http://placehold.it/200x100"
    	data-src="http://placehold.it/{width}x{height}"
    	data-sizes="auto"
    	class="lazyload rectangle"
    	alt="" />
    

    Note that you can also set the aspect ratio via a data-aspectratio value on the img tag.

    RIaS and Art direction using the picture element

    A responsive image service can be also combined with the art direction approach using the HTML5 picture element:

    <picture>
    <!--[if IE 9]><audio><![endif]-->
    <source
    		data-src="http://placehold.it/{width}/1111ee/fff"
    		media="(max-width: 500px)" />
    	<source
    			data-src="http://placehold.it/{width}/e8117f/fff"
    			media="(max-width: 1090px)" />
    	<source
    			data-src="http://placehold.it/{width}/7D26CD/fff"
    			media="(min-width: 1224px)" />
    
    <!--[if IE 9]></audio><![endif]-->
    <img
    		src="http://placehold.it/100"
    		data-src="http://placehold.it/{width}"
    		data-sizes="auto"
    		class="lazyload"
    		alt="" />
    </picture>
    

    optimumx

    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.

    <img
    	data-src="image?width={width}&amp;quality={quality}"
    	data-quality="75"
    	data-optimumx="1.5"
    	class="lazyload"
    	alt="" />
    

    RIaS and Art direction using the picture element

    A responsive image service can be also combined with the art direction approach using the HTML5 picture element.

    In this case, we simply use the cropping service

    ================================================ FILE: src/common.wrapper ================================================ (function(window, factory) { var lazySizes = factory(window, window.document, Date); window.lazySizes = lazySizes; if(typeof module == 'object' && module.exports){ module.exports = lazySizes; } }(typeof window != 'undefined' ? window : {}, {{ls}})); ================================================ FILE: src/lazysizes-core.js ================================================ /** * @typedef { import("./types/global").LazySizesConfigPartial } LazySizesConfigPartial */ function l(window, document, Date) { // Pass in the window Date function also for SSR because the Date class can be lost 'use strict'; /*jshint eqnull:true */ var lazysizes, /** * @type { LazySizesConfigPartial } */ lazySizesCfg; (function(){ var prop; var lazySizesDefaults = { lazyClass: 'lazyload', loadedClass: 'lazyloaded', loadingClass: 'lazyloading', preloadClass: 'lazypreload', errorClass: 'lazyerror', //strictClass: 'lazystrict', autosizesClass: 'lazyautosizes', fastLoadedClass: 'ls-is-cached', iframeLoadMode: 0, srcAttr: 'data-src', srcsetAttr: 'data-srcset', sizesAttr: 'data-sizes', //preloadAfterLoad: false, minSize: 40, customMedia: {}, init: true, expFactor: 1.5, hFac: 0.8, loadMode: 2, loadHidden: true, ricTimeout: 0, throttleDelay: 125, }; lazySizesCfg = window.lazySizesConfig || window.lazysizesConfig || {}; for(prop in lazySizesDefaults){ if(!(prop in lazySizesCfg)){ lazySizesCfg[prop] = lazySizesDefaults[prop]; } } })(); if (!document || !document.getElementsByClassName) { return { init: function () {}, /** * @type { LazySizesConfigPartial } */ cfg: lazySizesCfg, /** * @type { true } */ noSupport: true, }; } var docElem = document.documentElement; var supportPicture = window.HTMLPictureElement; var _addEventListener = 'addEventListener'; var _getAttribute = 'getAttribute'; /** * Update to bind to window because 'this' becomes null during SSR * builds. */ var addEventListener = window[_addEventListener].bind(window); var setTimeout = window.setTimeout; var requestAnimationFrame = window.requestAnimationFrame || setTimeout; var requestIdleCallback = window.requestIdleCallback; var regPicture = /^picture$/i; var loadEvents = ['load', 'error', 'lazyincluded', '_lazyloaded']; var regClassCache = {}; var forEach = Array.prototype.forEach; /** * @param ele {Element} * @param cls {string} */ var hasClass = function(ele, cls) { if(!regClassCache[cls]){ regClassCache[cls] = new RegExp('(\\s|^)'+cls+'(\\s|$)'); } return regClassCache[cls].test(ele[_getAttribute]('class') || '') && regClassCache[cls]; }; /** * @param ele {Element} * @param cls {string} */ var addClass = function(ele, cls) { if (!hasClass(ele, cls)){ ele.setAttribute('class', (ele[_getAttribute]('class') || '').trim() + ' ' + cls); } }; /** * @param ele {Element} * @param cls {string} */ var removeClass = function(ele, cls) { var reg; if ((reg = hasClass(ele,cls))) { ele.setAttribute('class', (ele[_getAttribute]('class') || '').replace(reg, ' ')); } }; var addRemoveLoadEvents = function(dom, fn, add){ var action = add ? _addEventListener : 'removeEventListener'; if(add){ addRemoveLoadEvents(dom, fn); } loadEvents.forEach(function(evt){ dom[action](evt, fn); }); }; /** * @param elem { Element } * @param name { string } * @param detail { any } * @param noBubbles { boolean } * @param noCancelable { boolean } * @returns { CustomEvent } */ var triggerEvent = function(elem, name, detail, noBubbles, noCancelable){ var event = document.createEvent('Event'); if(!detail){ detail = {}; } detail.instance = lazysizes; event.initEvent(name, !noBubbles, !noCancelable); event.detail = detail; elem.dispatchEvent(event); return event; }; var updatePolyfill = function (el, full){ var polyfill; if( !supportPicture && ( polyfill = (window.picturefill || lazySizesCfg.pf) ) ){ if(full && full.src && !el[_getAttribute]('srcset')){ el.setAttribute('srcset', full.src); } polyfill({reevaluate: true, elements: [el]}); } else if(full && full.src){ el.src = full.src; } }; var getCSS = function (elem, style){ return (getComputedStyle(elem, null) || {})[style]; }; /** * * @param elem { Element } * @param parent { Element } * @param [width] {number} * @returns {number} */ var getWidth = function(elem, parent, width){ width = width || elem.offsetWidth; while(width < lazySizesCfg.minSize && parent && !elem._lazysizesWidth){ width = parent.offsetWidth; parent = parent.parentNode; } return width; }; var rAF = (function(){ var running, waiting; var firstFns = []; var secondFns = []; var fns = firstFns; var run = function(){ var runFns = fns; fns = firstFns.length ? secondFns : firstFns; running = true; waiting = false; while(runFns.length){ runFns.shift()(); } running = false; }; var rafBatch = function(fn, queue){ if(running && !queue){ fn.apply(this, arguments); } else { fns.push(fn); if(!waiting){ waiting = true; (document.hidden ? setTimeout : requestAnimationFrame)(run); } } }; rafBatch._lsFlush = run; return rafBatch; })(); var rAFIt = function(fn, simple){ return simple ? function() { rAF(fn); } : function(){ var that = this; var args = arguments; rAF(function(){ fn.apply(that, args); }); } ; }; var throttle = function(fn){ var running; var lastTime = 0; var gDelay = lazySizesCfg.throttleDelay; var rICTimeout = lazySizesCfg.ricTimeout; var run = function(){ running = false; lastTime = Date.now(); fn(); }; var idleCallback = requestIdleCallback && rICTimeout > 49 ? function(){ requestIdleCallback(run, {timeout: rICTimeout}); if(rICTimeout !== lazySizesCfg.ricTimeout){ rICTimeout = lazySizesCfg.ricTimeout; } } : rAFIt(function(){ setTimeout(run); }, true) ; return function(isPriority){ var delay; if((isPriority = isPriority === true)){ rICTimeout = 33; } if(running){ return; } running = true; delay = gDelay - (Date.now() - lastTime); if(delay < 0){ delay = 0; } if(isPriority || delay < 9){ idleCallback(); } else { setTimeout(idleCallback, delay); } }; }; //based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html var debounce = function(func) { var timeout, timestamp; var wait = 99; var run = function(){ timeout = null; func(); }; var later = function() { var last = Date.now() - timestamp; if (last < wait) { setTimeout(later, wait - last); } else { (requestIdleCallback || run)(run); } }; return function() { timestamp = Date.now(); if (!timeout) { timeout = setTimeout(later, wait); } }; }; var loader = (function(){ var preloadElems, isCompleted, resetPreloadingTimer, loadMode, started; var eLvW, elvH, eLtop, eLleft, eLright, eLbottom, isBodyHidden; var regImg = /^img$/i; var regIframe = /^iframe$/i; var supportScroll = ('onscroll' in window) && !(/(gle|ing)bot/.test(navigator.userAgent)); var shrinkExpand = 0; var currentExpand = 0; var isLoading = 0; var lowRuns = -1; var resetPreloading = function(e){ isLoading--; if(!e || isLoading < 0 || !e.target){ isLoading = 0; } }; var isVisible = function (elem) { if (isBodyHidden == null) { isBodyHidden = getCSS(document.body, 'visibility') == 'hidden'; } return isBodyHidden || !(getCSS(elem.parentNode, 'visibility') == 'hidden' && getCSS(elem, 'visibility') == 'hidden'); }; var isNestedVisible = function(elem, elemExpand){ var outerRect; var parent = elem; var visible = isVisible(elem); eLtop -= elemExpand; eLbottom += elemExpand; eLleft -= elemExpand; eLright += elemExpand; while(visible && (parent = parent.offsetParent) && parent != document.body && parent != docElem){ visible = ((getCSS(parent, 'opacity') || 1) > 0); if(visible && getCSS(parent, 'overflow') != 'visible'){ outerRect = parent.getBoundingClientRect(); visible = eLright > outerRect.left && eLleft < outerRect.right && eLbottom > outerRect.top - 1 && eLtop < outerRect.bottom + 1 ; } } return visible; }; var checkElements = function() { var eLlen, i, rect, autoLoadElem, loadedSomething, elemExpand, elemNegativeExpand, elemExpandVal, beforeExpandVal, defaultExpand, preloadExpand, hFac; var lazyloadElems = lazysizes.elements; if((loadMode = lazySizesCfg.loadMode) && isLoading < 8 && (eLlen = lazyloadElems.length)){ i = 0; lowRuns++; for(; i < eLlen; i++){ if(!lazyloadElems[i] || lazyloadElems[i]._lazyRace){continue;} if(!supportScroll || (lazysizes.prematureUnveil && lazysizes.prematureUnveil(lazyloadElems[i]))){unveilElement(lazyloadElems[i]);continue;} if(!(elemExpandVal = lazyloadElems[i][_getAttribute]('data-expand')) || !(elemExpand = elemExpandVal * 1)){ elemExpand = currentExpand; } if (!defaultExpand) { defaultExpand = (!lazySizesCfg.expand || lazySizesCfg.expand < 1) ? docElem.clientHeight > 500 && docElem.clientWidth > 500 ? 500 : 370 : lazySizesCfg.expand; lazysizes._defEx = defaultExpand; preloadExpand = defaultExpand * lazySizesCfg.expFactor; hFac = lazySizesCfg.hFac; isBodyHidden = null; if(currentExpand < preloadExpand && isLoading < 1 && lowRuns > 2 && loadMode > 2 && !document.hidden){ currentExpand = preloadExpand; lowRuns = 0; } else if(loadMode > 1 && lowRuns > 1 && isLoading < 6){ currentExpand = defaultExpand; } else { currentExpand = shrinkExpand; } } if(beforeExpandVal !== elemExpand){ eLvW = innerWidth + (elemExpand * hFac); elvH = innerHeight + elemExpand; elemNegativeExpand = elemExpand * -1; beforeExpandVal = elemExpand; } rect = lazyloadElems[i].getBoundingClientRect(); if ((eLbottom = rect.bottom) >= elemNegativeExpand && (eLtop = rect.top) <= elvH && (eLright = rect.right) >= elemNegativeExpand * hFac && (eLleft = rect.left) <= eLvW && (eLbottom || eLright || eLleft || eLtop) && (lazySizesCfg.loadHidden || isVisible(lazyloadElems[i])) && ((isCompleted && isLoading < 3 && !elemExpandVal && (loadMode < 3 || lowRuns < 4)) || isNestedVisible(lazyloadElems[i], elemExpand))){ unveilElement(lazyloadElems[i]); loadedSomething = true; if(isLoading > 9){break;} } else if(!loadedSomething && isCompleted && !autoLoadElem && isLoading < 4 && lowRuns < 4 && loadMode > 2 && (preloadElems[0] || lazySizesCfg.preloadAfterLoad) && (preloadElems[0] || (!elemExpandVal && ((eLbottom || eLright || eLleft || eLtop) || lazyloadElems[i][_getAttribute](lazySizesCfg.sizesAttr) != 'auto')))){ autoLoadElem = preloadElems[0] || lazyloadElems[i]; } } if(autoLoadElem && !loadedSomething){ unveilElement(autoLoadElem); } } }; var throttledCheckElements = throttle(checkElements); var switchLoadingClass = function(e){ var elem = e.target; if (elem._lazyCache) { delete elem._lazyCache; return; } resetPreloading(e); addClass(elem, lazySizesCfg.loadedClass); removeClass(elem, lazySizesCfg.loadingClass); addRemoveLoadEvents(elem, rafSwitchLoadingClass); triggerEvent(elem, 'lazyloaded'); }; var rafedSwitchLoadingClass = rAFIt(switchLoadingClass); var rafSwitchLoadingClass = function(e){ rafedSwitchLoadingClass({target: e.target}); }; var changeIframeSrc = function(elem, src){ var loadMode = elem.getAttribute('data-load-mode') || lazySizesCfg.iframeLoadMode; // loadMode can be also a string! if (loadMode == 0) { elem.contentWindow.location.replace(src); } else if (loadMode == 1) { elem.src = src; } }; var handleSources = function(source){ var customMedia; var sourceSrcset = source[_getAttribute](lazySizesCfg.srcsetAttr); if( (customMedia = lazySizesCfg.customMedia[source[_getAttribute]('data-media') || source[_getAttribute]('media')]) ){ source.setAttribute('media', customMedia); } if(sourceSrcset){ source.setAttribute('srcset', sourceSrcset); } }; var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg){ var src, srcset, parent, isPicture, event, firesLoad; if(!(event = triggerEvent(elem, 'lazybeforeunveil', detail)).defaultPrevented){ if(sizes){ if(isAuto){ addClass(elem, lazySizesCfg.autosizesClass); } else { elem.setAttribute('sizes', sizes); } } srcset = elem[_getAttribute](lazySizesCfg.srcsetAttr); src = elem[_getAttribute](lazySizesCfg.srcAttr); if(isImg) { parent = elem.parentNode; isPicture = parent && regPicture.test(parent.nodeName || ''); } firesLoad = detail.firesLoad || (('src' in elem) && (srcset || src || isPicture)); event = {target: elem}; addClass(elem, lazySizesCfg.loadingClass); if(firesLoad){ clearTimeout(resetPreloadingTimer); resetPreloadingTimer = setTimeout(resetPreloading, 2500); addRemoveLoadEvents(elem, rafSwitchLoadingClass, true); } if(isPicture){ forEach.call(parent.getElementsByTagName('source'), handleSources); } if(srcset){ elem.setAttribute('srcset', srcset); } else if(src && !isPicture){ if(regIframe.test(elem.nodeName)){ changeIframeSrc(elem, src); } else { elem.src = src; } } if(isImg && (srcset || isPicture)){ updatePolyfill(elem, {src: src}); } } if(elem._lazyRace){ delete elem._lazyRace; } removeClass(elem, lazySizesCfg.lazyClass); rAF(function(){ // Part of this can be removed as soon as this fix is older: https://bugs.chromium.org/p/chromium/issues/detail?id=7731 (2015) var isLoaded = elem.complete && elem.naturalWidth > 1; if( !firesLoad || isLoaded){ if (isLoaded) { addClass(elem, lazySizesCfg.fastLoadedClass); } switchLoadingClass(event); elem._lazyCache = true; setTimeout(function(){ if ('_lazyCache' in elem) { delete elem._lazyCache; } }, 9); } if (elem.loading == 'lazy') { isLoading--; } }, true); }); /** * * @param elem { Element } */ var unveilElement = function (elem){ if (elem._lazyRace) {return;} var detail; var isImg = regImg.test(elem.nodeName); //allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw") var sizes = isImg && (elem[_getAttribute](lazySizesCfg.sizesAttr) || elem[_getAttribute]('sizes')); var isAuto = sizes == 'auto'; if( (isAuto || !isCompleted) && isImg && (elem[_getAttribute]('src') || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesCfg.errorClass) && hasClass(elem, lazySizesCfg.lazyClass)){return;} detail = triggerEvent(elem, 'lazyunveilread').detail; if(isAuto){ autoSizer.updateElem(elem, true, elem.offsetWidth); } elem._lazyRace = true; isLoading++; lazyUnveil(elem, detail, isAuto, sizes, isImg); }; var afterScroll = debounce(function(){ lazySizesCfg.loadMode = 3; throttledCheckElements(); }); var altLoadmodeScrollListner = function(){ if(lazySizesCfg.loadMode == 3){ lazySizesCfg.loadMode = 2; } afterScroll(); }; var onload = function(){ if(isCompleted){return;} if(Date.now() - started < 999){ setTimeout(onload, 999); return; } isCompleted = true; lazySizesCfg.loadMode = 3; throttledCheckElements(); addEventListener('scroll', altLoadmodeScrollListner, true); }; return { _: function(){ started = Date.now(); lazysizes.elements = document.getElementsByClassName(lazySizesCfg.lazyClass); preloadElems = document.getElementsByClassName(lazySizesCfg.lazyClass + ' ' + lazySizesCfg.preloadClass); addEventListener('scroll', throttledCheckElements, true); addEventListener('resize', throttledCheckElements, true); addEventListener('pageshow', function (e) { if (e.persisted) { var loadingElements = document.querySelectorAll('.' + lazySizesCfg.loadingClass); if (loadingElements.length && loadingElements.forEach) { requestAnimationFrame(function () { loadingElements.forEach( function (img) { if (img.complete) { unveilElement(img); } }); }); } } }); if(window.MutationObserver){ new MutationObserver( throttledCheckElements ).observe( docElem, {childList: true, subtree: true, attributes: true} ); } else { docElem[_addEventListener]('DOMNodeInserted', throttledCheckElements, true); docElem[_addEventListener]('DOMAttrModified', throttledCheckElements, true); setInterval(throttledCheckElements, 999); } addEventListener('hashchange', throttledCheckElements, true); //, 'fullscreenchange' ['focus', 'mouseover', 'click', 'load', 'transitionend', 'animationend'].forEach(function(name){ document[_addEventListener](name, throttledCheckElements, true); }); if((/d$|^c/.test(document.readyState))){ onload(); } else { addEventListener('load', onload); document[_addEventListener]('DOMContentLoaded', throttledCheckElements); setTimeout(onload, 20000); } if(lazysizes.elements.length){ checkElements(); rAF._lsFlush(); } else { throttledCheckElements(); } }, checkElems: throttledCheckElements, unveil: unveilElement, _aLSL: altLoadmodeScrollListner, }; })(); var autoSizer = (function(){ var autosizesElems; var sizeElement = rAFIt(function(elem, parent, event, width){ var sources, i, len; elem._lazysizesWidth = width; width += 'px'; elem.setAttribute('sizes', width); if(regPicture.test(parent.nodeName || '')){ sources = parent.getElementsByTagName('source'); for(i = 0, len = sources.length; i < len; i++){ sources[i].setAttribute('sizes', width); } } if(!event.detail.dataAttr){ updatePolyfill(elem, event.detail); } }); /** * * @param elem {Element} * @param dataAttr * @param [width] { number } */ var getSizeElement = function (elem, dataAttr, width){ var event; var parent = elem.parentNode; if(parent){ width = getWidth(elem, parent, width); event = triggerEvent(elem, 'lazybeforesizes', {width: width, dataAttr: !!dataAttr}); if(!event.defaultPrevented){ width = event.detail.width; if(width && width !== elem._lazysizesWidth){ sizeElement(elem, parent, event, width); } } } }; var updateElementsSizes = function(){ var i; var len = autosizesElems.length; if(len){ i = 0; for(; i < len; i++){ getSizeElement(autosizesElems[i]); } } }; var debouncedUpdateElementsSizes = debounce(updateElementsSizes); return { _: function(){ autosizesElems = document.getElementsByClassName(lazySizesCfg.autosizesClass); addEventListener('resize', debouncedUpdateElementsSizes); }, checkElems: debouncedUpdateElementsSizes, updateElem: getSizeElement }; })(); var init = function(){ if(!init.i && document.getElementsByClassName){ init.i = true; autoSizer._(); loader._(); } }; setTimeout(function(){ if(lazySizesCfg.init){ init(); } }); lazysizes = { /** * @type { LazySizesConfigPartial } */ cfg: lazySizesCfg, autoSizer: autoSizer, loader: loader, init: init, uP: updatePolyfill, aC: addClass, rC: removeClass, hC: hasClass, fire: triggerEvent, gW: getWidth, rAF: rAF, }; return lazysizes; } ================================================ FILE: src/lazysizes-intersection.js ================================================ (function(window, factory) { if(typeof module == 'object' && module.exports){ module.exports = lazySizes; } else { window.lazySizes = factory(window, window.document, Date); } }(window, function l(window, document, Date) { 'use strict'; /*jshint eqnull:true */ if(!window.IntersectionObserver || !document.getElementsByClassName || !window.MutationObserver){return;} var lazysizes, lazySizesCfg; var docElem = document.documentElement; var supportPicture = window.HTMLPictureElement; var _addEventListener = 'addEventListener'; var _getAttribute = 'getAttribute'; var addEventListener = window[_addEventListener].bind(window); var setTimeout = window.setTimeout; var requestAnimationFrame = window.requestAnimationFrame || setTimeout; var requestIdleCallback = window.requestIdleCallback || setTimeout; var regPicture = /^picture$/i; var loadEvents = ['load', 'error', 'lazyincluded', '_lazyloaded']; var forEach = Array.prototype.forEach; var hasClass = function(ele, cls) { return ele.classList.contains(cls); }; var addClass = function(ele, cls) { ele.classList.add(cls); }; var removeClass = function(ele, cls) { ele.classList.remove(cls); }; var addRemoveLoadEvents = function(dom, fn, add){ var action = add ? _addEventListener : 'removeEventListener'; if(add){ addRemoveLoadEvents(dom, fn); } loadEvents.forEach(function(evt){ dom[action](evt, fn); }); }; var triggerEvent = function(elem, name, detail, noBubbles, noCancelable){ var event = document.createEvent('CustomEvent'); if(!detail){ detail = {}; } detail.instance = lazysizes; event.initCustomEvent(name, !noBubbles, !noCancelable, detail); elem.dispatchEvent(event); return event; }; var updatePolyfill = function (el, full){ var polyfill; if( !supportPicture && ( polyfill = (window.picturefill || lazySizesCfg.pf) ) ){ polyfill({reevaluate: true, elements: [el]}); } else if(full && full.src){ el.src = full.src; } }; var getWidth = function(elem, parent, width){ width = width || elem.offsetWidth; while(width < lazySizesCfg.minSize && parent && !elem._lazysizesWidth){ width = parent.offsetWidth; parent = parent.parentNode; } return width; }; var rAF = (function(){ var running, waiting; var fns = []; var run = function(){ var fn; running = true; waiting = false; while(fns.length){ fn = fns.shift(); fn[0].apply(fn[1], fn[2]); } running = false; }; return function(fn){ if(running){ fn.apply(this, arguments); } else { fns.push([fn, this, arguments]); if(!waiting){ waiting = true; (document.hidden ? setTimeout : requestAnimationFrame)(run); } } }; })(); var rAFIt = function(fn, simple){ return simple ? function() { rAF(fn); } : function(){ var that = this; var args = arguments; rAF(function(){ fn.apply(that, args); }); } ; }; //based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html var debounce = function(func) { var timeout, timestamp; var wait = 99; var run = function(){ timeout = null; func(); }; var later = function() { var last = Date.now() - timestamp; if (last < wait) { setTimeout(later, wait - last); } else { (requestIdleCallback || run)(run); } }; return function() { timestamp = Date.now(); if (!timeout) { timeout = setTimeout(later, wait); } }; }; var loader = (function(){ var inviewObserver, preloadObserver; var lazyloadElems, isCompleted, resetPreloadingTimer, started; var regImg = /^img$/i; var regIframe = /^iframe$/i; var supportScroll = ('onscroll' in window) && !(/glebot/.test(navigator.userAgent)); var isLoading = 0; var isPreloadLoading = 0; var resetPreloading = function(e){ isLoading--; if(isPreloadLoading){ isPreloadLoading--; } if(e && e.target){ addRemoveLoadEvents(e.target, resetPreloading); } if(!e || isLoading < 0 || !e.target){ isLoading = 0; isPreloadLoading = 0; } if(lazyQuedElements.length && (isLoading - isPreloadLoading) < 1 && isLoading < 3){ setTimeout(function(){ while(lazyQuedElements.length && (isLoading - isPreloadLoading) < 1 && isLoading < 4){ lazyUnveilElement({target: lazyQuedElements.shift()}); } }); } }; var switchLoadingClass = function(e){ addClass(e.target, lazySizesCfg.loadedClass); removeClass(e.target, lazySizesCfg.loadingClass); addRemoveLoadEvents(e.target, rafSwitchLoadingClass); }; var rafedSwitchLoadingClass = rAFIt(switchLoadingClass); var rafSwitchLoadingClass = function(e){ rafedSwitchLoadingClass({target: e.target}); }; var changeIframeSrc = function(elem, src){ try { elem.contentWindow.location.replace(src); } catch(e){ elem.src = src; } }; var handleSources = function(source){ var customMedia; var sourceSrcset = source[_getAttribute](lazySizesCfg.srcsetAttr); if( (customMedia = lazySizesCfg.customMedia[source[_getAttribute]('data-media') || source[_getAttribute]('media')]) ){ source.setAttribute('media', customMedia); } if(sourceSrcset){ source.setAttribute('srcset', sourceSrcset); } }; var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg){ var src, srcset, parent, isPicture, event, firesLoad; if(!(event = triggerEvent(elem, 'lazybeforeunveil', detail)).defaultPrevented){ if(sizes){ if(isAuto){ addClass(elem, lazySizesCfg.autosizesClass); } else { elem.setAttribute('sizes', sizes); } } srcset = elem[_getAttribute](lazySizesCfg.srcsetAttr); src = elem[_getAttribute](lazySizesCfg.srcAttr); if(isImg) { parent = elem.parentNode; isPicture = parent && regPicture.test(parent.nodeName || ''); } firesLoad = detail.firesLoad || (('src' in elem) && (srcset || src || isPicture)); event = {target: elem}; if(firesLoad){ addRemoveLoadEvents(elem, resetPreloading, true); clearTimeout(resetPreloadingTimer); resetPreloadingTimer = setTimeout(resetPreloading, 2500); addClass(elem, lazySizesCfg.loadingClass); addRemoveLoadEvents(elem, rafSwitchLoadingClass, true); } if(isPicture){ forEach.call(parent.getElementsByTagName('source'), handleSources); } if(srcset){ elem.setAttribute('srcset', srcset); } else if(src && !isPicture){ if(regIframe.test(elem.nodeName)){ changeIframeSrc(elem, src); } else { elem.src = src; } } if(srcset || isPicture){ updatePolyfill(elem, {src: src}); } } rAF(function(){ if(elem._lazyRace){ delete elem._lazyRace; } removeClass(elem, lazySizesCfg.lazyWaitClass); if( !firesLoad || elem.complete ){ if(firesLoad){ resetPreloading(event); } else { isLoading--; } switchLoadingClass(event); } }); }); var unveilElement = function (elem){ var detail, index; var isImg = regImg.test(elem.nodeName); //allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw") var sizes = isImg && (elem[_getAttribute](lazySizesCfg.sizesAttr) || elem[_getAttribute]('sizes')); var isAuto = sizes == 'auto'; if( (isAuto || !isCompleted) && isImg && (elem.src || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesCfg.errorClass)){return;} detail = triggerEvent(elem, 'lazyunveilread').detail; if(isAuto){ autoSizer.updateElem(elem, true, elem.offsetWidth); } isLoading++; if((index = lazyQuedElements.indexOf(elem)) != -1){ lazyQuedElements.splice(index, 1); } inviewObserver.unobserve(elem); preloadObserver.unobserve(elem); lazyUnveil(elem, detail, isAuto, sizes, isImg); }; var unveilElements = function(change){ var i, len; for(i = 0, len = change.length; i < len; i++){ if (change[i].isIntersecting === false) { continue; } unveilElement(change[i].target); } }; var lazyQuedElements = []; var lazyUnveilElement = function(change){ var index, i, len, element; for(i = 0, len = change.length; i < len; i++){ element = change[i].target; if((isLoading - isPreloadLoading) < 1 && isLoading < 4){ isPreloadLoading++; unveilElement(element); } else if((index = lazyQuedElements.indexOf(element)) == -1){ lazyQuedElements.push(element); } else { lazyQuedElements.splice(index, 1); } } }; var removeLazyClassElements = []; var removeLazyClass = rAFIt(function(){ var element; while(removeLazyClassElements.length){ element = removeLazyClassElements.shift(); addClass(element, lazySizesCfg.lazyWaitClass); removeClass(element, lazySizesCfg.lazyClass); if(element._lazyAdd){ delete element._lazyAdd; } } }, true); var addElements = function(){ var i, len, runLazyRemove; for(i = 0, len = lazyloadElems.length; i < len; i++){ if(!lazyloadElems[i]._lazyAdd){ lazyloadElems[i]._lazyAdd = true; inviewObserver.observe(lazyloadElems[i]); preloadObserver.observe(lazyloadElems[i]); removeLazyClassElements.push(lazyloadElems[i]); runLazyRemove = true; if(!supportScroll){ unveilElement(lazyloadElems[i]); } } } if(runLazyRemove){ removeLazyClass(); } }; return { _: function(){ started = Date.now(); lazyloadElems = document.getElementsByClassName(lazySizesCfg.lazyClass); inviewObserver = new IntersectionObserver(unveilElements); preloadObserver = new IntersectionObserver(lazyUnveilElement, { rootMargin: lazySizesCfg.expand + 'px ' + (lazySizesCfg.expand * lazySizesCfg.hFac) + 'px', }); new MutationObserver( addElements ).observe( docElem, {childList: true, subtree: true, attributes: true} ); addElements(); }, unveil: unveilElement }; })(); var autoSizer = (function(){ var autosizesElems; var sizeElement = rAFIt(function(elem, parent, event, width){ var sources, i, len; elem._lazysizesWidth = width; width += 'px'; elem.setAttribute('sizes', width); if(regPicture.test(parent.nodeName || '')){ sources = parent.getElementsByTagName('source'); for(i = 0, len = sources.length; i < len; i++){ sources[i].setAttribute('sizes', width); } } if(!event.detail.dataAttr){ updatePolyfill(elem, event.detail); } }); var getSizeElement = function (elem, dataAttr, width){ var event; var parent = elem.parentNode; if(parent){ width = getWidth(elem, parent, width); event = triggerEvent(elem, 'lazybeforesizes', {width: width, dataAttr: !!dataAttr}); if(!event.defaultPrevented){ width = event.detail.width; if(width && width !== elem._lazysizesWidth){ sizeElement(elem, parent, event, width); } } } }; var updateElementsSizes = function(){ var i; var len = autosizesElems.length; if(len){ i = 0; for(; i < len; i++){ getSizeElement(autosizesElems[i]); } } }; var debouncedUpdateElementsSizes = debounce(updateElementsSizes); return { _: function(){ autosizesElems = document.getElementsByClassName(lazySizesCfg.autosizesClass); addEventListener('resize', debouncedUpdateElementsSizes); }, checkElems: debouncedUpdateElementsSizes, updateElem: getSizeElement }; })(); var init = function(){ if(!init.i){ init.i = true; autoSizer._(); loader._(); } }; (function(){ var prop; var lazySizesDefaults = { lazyClass: 'lazyload', lazyWaitClass: 'lazyloadwait', loadedClass: 'lazyloaded', loadingClass: 'lazyloading', preloadClass: 'lazypreload', errorClass: 'lazyerror', //strictClass: 'lazystrict', autosizesClass: 'lazyautosizes', srcAttr: 'data-src', srcsetAttr: 'data-srcset', sizesAttr: 'data-sizes', minSize: 40, customMedia: {}, init: true, hFac: 0.8, loadMode: 2, expand: 400, }; lazySizesCfg = window.lazySizesConfig || window.lazysizesConfig || {}; for(prop in lazySizesDefaults){ if(!(prop in lazySizesCfg)){ lazySizesCfg[prop] = lazySizesDefaults[prop]; } } setTimeout(function(){ if(lazySizesCfg.init){ init(); } }); })(); lazysizes = { cfg: lazySizesCfg, autoSizer: autoSizer, loader: loader, init: init, uP: updatePolyfill, aC: addClass, rC: removeClass, hC: hasClass, fire: triggerEvent, gW: getWidth, rAF: rAF, }; return lazysizes; })); ================================================ FILE: src/umd.wrapper ================================================ (function(window, factory) { var lazySizes = factory(window, window.document, Date); if(typeof module == 'object' && module.exports){ module.exports = lazySizes; } else if (typeof define == 'function' && define.amd) { define(lazySizes); } else { window.lazySizes = lazySizes; } }(window, {{ls}})); ================================================ FILE: tests/functional-tests-plugins.js ================================================ function _optimumxReinit(addClass){ return function (assert){ if(!window.devicePixelRatio){ assert.ok(true); return; } var done = assert.async(); this.promise.always(function($){ var placeholderSrc, $image; var unveiled = 0; var initTest = function(){ var success = [ { u: 'data:,img1-5000', w: 5000, c: 'data:,img1-5000 5000w' }, { u: 'data:,img1-50000', w: 50000, c: 'data:,img1-50000 50000w' }, { u: 'data:,img1-500000', w: 500000, c: 'data:,img1-500000 500000w' } ]; assert.propEqual(cleanUpDensity($image.prop('_lazyOptimumx').cands), success); assert.equal($image.prop('_lazyOptimumx').cSrcset.length, 1); assert.equal($image.prop('_lazyOptimumx').cSrcset[0], 'data:,img1-5000 5000w'); assert.equal($image.attr('srcset'), 'data:,img1-5000 5000w'); }; var reinitTest = function(){ var success = [ { u: 'data:,img2-50', w: 50, c: 'data:,img2-50 50w' }, { u: 'data:,img2-100', w: 100, c: 'data:,img2-100 100w' }, { u: 'data:,img2-500000', w: 500000, c: 'data:,img2-500000 500000w' } ]; assert.propEqual(cleanUpDensity($image.prop('_lazyOptimumx').cands), success); assert.equal($image.prop('_lazyOptimumx').cSrcset.length, 2, '1'); assert.equal($image.prop('_lazyOptimumx').cSrcset[0], 'data:,img2-50 50w', '2'); assert.equal($image.prop('_lazyOptimumx').cSrcset[1], 'data:,img2-100 100w', '3'); assert.equal($image.attr('srcset'), 'data:,img2-50 50w, data:,img2-100 100w', '4'); }; var test = [ ['\ndata:,img1-50000 50000w, \ndata:,img1-5000 5000w, data:,img1-500000 500000w', initTest], ['\ndata:,img2-100 100w, \ndata:,img2-500000 500000w, \ndata:,img2-50 50w', reinitTest] ]; var run = function(){ if(test.length){ placeholderSrc = test.shift(); $image .attr('data-srcset', placeholderSrc[0]) .attr('data-optimumx', '0.6') ; if(addClass){ $image.addClass('lazyload'); } } else { setTimeout(function(){ assert.ok(unveiled == 2); done(); }, 130); } }; $image = $('') .appendTo('body') ; $image.on('lazybeforeunveil', function(e){ unveiled++; }); $image.on('lazybeforeunveil', function(e){ afterUnveil(function(){ placeholderSrc[1](); setTimeout(run, 20); }); }); run(); }); }; } $.extend(window.lazyTests, { bgsetParentFit: ['lazyloads bgsetParentFit', function(assert){ var done = assert.async(); var $iframe = this.$iframe; this.promise.always(function($, frameWindow){ var viewport; var $div = $('
    ') .attr({ 'data-bgset': '\ndata:,jo2 400w 800h, \ndata:,jo3 800w 1600h, data:,jo4 1200w 2400h [(max-width: 300px)] | ' + 'data:,jo5 800h 500w', 'data-sizes': 'auto', 'class': 'lazyload', 'data-optimumx': '0.6' }) .css({ display: 'block', width: 400, height: 400, backgroundSize: 'contain' }) .appendTo('body') ; var initialTest = function(){ assert.equal($('source').eq(0).attr('sizes'), '200px'); }; var endTest = function(){ assert.equal($('source').eq(1).attr('sizes'), '250px'); }; var viewportTests = [ ['300', initialTest], ['500', endTest] ]; var run = function(){ if(viewportTests.length){ viewport = viewportTests.shift(); $iframe.css('width', viewport[0]); } else { done(); } }; run(); $div.on('lazybeforesizes', function(e){ afterUnveil(function(){ viewport[1](); run(); }); }); }); }], optimumxPictureResize: ['lazyloads constraints srcset on picture', function(assert){ var done = assert.async(); var $iframe = this.$iframe; this.promise.always(function($, frameWindow){ var viewport; var $picture = createPicture($, [ { 'data-srcset': '\n\ndata:,lazysource150 150w, \ndata:,lazysource100 100w, \ndata:,lazysource280 280w', media: '(min-width: 0.5em)' }, { 'data-srcset': '\ndata:,lazyimg100 100w, \n\ndata:,lazyimg200 200w, \n\ndata:,lazyimg150 150w', 'data-optimumx': '0.5', 'data-sizes': 'auto', 'class': 'lazyload' } ]); var $source = $picture.find('source'); var $image = $picture.find('img'); var initialTest = function(){ var haspolyfill = frameWindow.respimage || frameWindow.picturefill || (frameWindow.lazySizes.cfg.rias && frameWindow.lazySizes.pWS) || frameWindow.lazySizes.cfg.pf; var nowSrc = window.HTMLPictureElement || !haspolyfill ? '' : 'data:,lazysource150'; assert.equal($source.attr('srcset'), 'data:,lazysource100 100w, data:,lazysource150 150w'); if(!window.bustedSrcset || !frameWindow.lazySizes.cfg.pf){ assert.equal($image.attr('srcset') || $image.attr('data-risrcset') || $image.attr('data-pfsrcset'), 'data:,lazyimg100 100w, data:,lazyimg150 150w'); } assert.equal($image.prop('src'), nowSrc, 'jo'); }; var endTest = function(){ var haspolyfill = frameWindow.respimage || frameWindow.picturefill || (frameWindow.lazySizes.cfg.rias && frameWindow.lazySizes.pWS) || frameWindow.lazySizes.cfg.pf; var nowSrc = window.HTMLPictureElement || !haspolyfill ? '' : 'data:,lazysource280'; assert.equal($source.attr('srcset'), 'data:,lazysource100 100w, data:,lazysource150 150w, data:,lazysource280 280w'); if(!window.bustedSrcset || !frameWindow.lazySizes.cfg.pf){ assert.equal($image.attr('srcset') || $image.attr('data-risrcset') || $image.attr('data-pfsrcset'), 'data:,lazyimg100 100w, data:,lazyimg150 150w, data:,lazyimg200 200w'); } assert.equal($image.prop('src'), nowSrc); }; var viewportTests = [ ['300', initialTest], ['200', initialTest], ['500', endTest] ]; var run = function(){ if(viewportTests.length){ viewport = viewportTests.shift(); $iframe.css('width', viewport[0]); } else { done(); } }; if(!window.devicePixelRatio){ assert.ok(true); done(); return; } run(); $picture.appendTo('body'); assert.equal($source.attr('srcset'), null); assert.equal($image.attr('srcset'), null); $image.on('lazybeforesizes', function(e){ afterUnveil(function(){ assert.equal($source.attr('src'), null); assert.equal($source.attr('data-src'), null); viewport[1](); run(); }); }); }); }], optimumxRiasPictureResize: ['lazyloads constraints rias generated srcset on picture', function(assert){ if(!supportsPicture){ assert.ok(true); return; } var done = assert.async(); var $iframe = this.$iframe; this.promise.always(function($, frameWindow){ var viewport; var $picture = createPicture($, [ { 'data-srcset': 'data:,lazysource{width}', media: '(min-width: 0.5em)' }, { 'data-srcset': 'data:,lazyimg{width}', 'data-optimumx': '0.5', 'data-sizes': 'auto', 'data-widths': '[100, 150, 280]', 'class': 'lazyload' } ]); var $source = $picture.find('source'); var $image = $picture.find('img'); var initialTest = function(){ var nowSrc = window.HTMLPictureElement ? '' : 'data:,lazysource150'; assert.equal($source.attr('srcset'), 'data:,lazysource100 100w, data:,lazysource150 150w'); assert.equal($image.attr('srcset') || $image.attr('data-risrcset') || $image.attr('data-pfsrcset') || 'data:,lazyimg100 100w, data:,lazyimg150 150w', 'data:,lazyimg100 100w, data:,lazyimg150 150w'); assert.equal($image.prop('src'), nowSrc); }; var endTest = function(){ var nowSrc = window.HTMLPictureElement ? '' : 'data:,lazysource280'; assert.equal($source.attr('srcset'), 'data:,lazysource100 100w, data:,lazysource150 150w, data:,lazysource280 280w'); assert.equal($image.attr('srcset') || $image.attr('data-risrcset') || $image.attr('data-pfsrcset') || 'data:,lazyimg100 100w, data:,lazyimg150 150w, data:,lazyimg280 280w', 'data:,lazyimg100 100w, data:,lazyimg150 150w, data:,lazyimg280 280w'); assert.equal($image.prop('src'), nowSrc); }; var viewportTests = [ ['300', initialTest], ['200', initialTest], ['500', endTest] ]; var run = function(){ if(viewportTests.length){ viewport = viewportTests.shift(); $iframe.css('width', viewport[0]); } else { done(); } }; if(!window.devicePixelRatio){ assert.ok(true); done(); return; } run(); $picture.appendTo('body'); assert.equal($source.attr('srcset'), null); assert.equal($image.attr('srcset'), null); $image.on('lazybeforesizes', function(e){ afterUnveil(function(){ assert.equal($source.attr('src'), null); assert.equal($source.attr('data-src'), null); viewport[1](); run(); }); }); }); }], riasResize: ['lazysizes rias reacts on resize', function(assert){ var done = assert.async(); var $iframe = this.$iframe; this.promise.always(function($, frameWindow){ var viewport, $image; var initTest = function(){ var nowSrc = window.HTMLPictureElement ? '' : 'data:,img-small-yo'; assert.equal($image.attr('srcset') || 'data:,img-1-yo 1w, data:,img-small-yo 500w, data:,img-large-yo 1200w', 'data:,img-1-yo 1w, data:,img-small-yo 500w, data:,img-large-yo 1200w'); assert.equal($image.prop('src'), nowSrc); }; var largerTest = function(){ var nowSrc = window.HTMLPictureElement ? '' : 'data:,img-large-yo'; assert.equal($image.attr('srcset') || 'data:,img-1-yo 1w, data:,img-small-yo 500w, data:,img-large-yo 1200w', 'data:,img-1-yo 1w, data:,img-small-yo 500w, data:,img-large-yo 1200w'); assert.equal($image.prop('src'), nowSrc); }; var viewportTests = [ ['200', initTest], ['250', initTest], ['1200', largerTest], ['800', largerTest] ]; var run = function(){ if(viewportTests.length){ viewport = viewportTests.shift(); $iframe.css('width', viewport[0]); } else { done(); } }; run(); frameWindow.document.addEventListener('lazyriasmodifyoptions', function(e){ // change available widths and widthmap for .special-widths elements e.detail.widthmap = { 500: 'small', 1200: 'large' }; //add new custom property with value 'foo' e.detail.foo = 'yo'; }); $image = $('
    ' + '' + '
    ') .appendTo('body') .find('img'); $image.on('lazybeforesizes', function(e){ afterUnveil(function(){ viewport[1](); run(); }); }); }); }], riasPictureResize: ['lazysizes rias reacts on resize and allows art direction', function(assert){ var done = assert.async(); var $iframe = this.$iframe; this.promise.always(function($, frameWindow){ var viewport; var initTest = function(){ var nowSrc = window.HTMLPictureElement ? '' : 'data:,lazysource900'; assert.equal($image.attr('srcset') || 'data:,lazyimg900 900w, data:,lazyimg1500 1500w', 'data:,lazyimg900 900w, data:,lazyimg1500 1500w'); assert.equal($source.attr('srcset'), 'data:,lazysource900 900w, data:,lazysource1500 1500w'); assert.equal($image.prop('src'), nowSrc); }; var largerTest = function(){ var nowSrc = window.HTMLPictureElement ? '' : 'data:,lazyimg1500'; assert.equal($image.attr('srcset') || 'data:,lazyimg900 900w, data:,lazyimg1500 1500w', 'data:,lazyimg900 900w, data:,lazyimg1500 1500w'); assert.equal($source.attr('srcset'), 'data:,lazysource900 900w, data:,lazysource1500 1500w'); assert.equal($image.prop('src'), nowSrc); }; var viewportTests = [ ['300', initTest], ['400', initTest], ['1500', largerTest], ['1220', largerTest], ['450', initTest] ]; var run = function(){ if(viewportTests.length){ viewport = viewportTests.shift(); $iframe.css('width', viewport[0]); } else { done(); } }; var $picture = createPicture($, [ { 'data-srcset': 'data:,lazysource{width}', media: '(max-width: 500px)' }, { 'data-srcset': 'data:,lazyimg{width}', 'data-sizes': 'auto', 'data-widths': '[900, 1500]', 'class': 'lazyload' } ]); var $source = $picture.find('source'); var $image = $picture.find('img'); run(); $picture.appendTo('body'); $image.on('lazybeforesizes', function(e){ afterUnveil(function(){ viewport[1](); run(); }); }); }); }], riasReinit: ['lazysizes rias can be re-initialized', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var placeholderSrc, $image; var initTest = function(){ var success = [ { u: 'data:,img1-1', w: 1, c: 'data:,img1-1 1w' }, { u: 'data:,img1-500', w: 500, c: 'data:,img1-500 500w' }, { u: 'data:,img1-1200', w: 1200, c: 'data:,img1-1200 1200w' } ]; assert.propEqual(cleanUpDensity($image.prop('_lazyrias')), success); assert.equal($image.attr('srcset') || 'data:,img1-1 1w, data:,img1-500 500w, data:,img1-1200 1200w', 'data:,img1-1 1w, data:,img1-500 500w, data:,img1-1200 1200w'); }; var reinitTest = function(){ var success = [ { u: 'data:,img2-1', w: 1, c: 'data:,img2-1 1w' }, { u: 'data:,img2-500', w: 500, c: 'data:,img2-500 500w' }, { u: 'data:,img2-1200', w: 1200, c: 'data:,img2-1200 1200w' } ]; assert.propEqual(cleanUpDensity($image.prop('_lazyrias')), success); assert.equal($image.attr('srcset') || 'data:,img2-1 1w, data:,img2-500 500w, data:,img2-1200 1200w', 'data:,img2-1 1w, data:,img2-500 500w, data:,img2-1200 1200w'); }; var test = [ ['data:,img1-{width}', initTest], ['data:,img2-{width}', reinitTest] ]; var run = function(){ if(test.length){ placeholderSrc = test.shift(); if(window.isTrident){ $image.addClass('lazyerror'); } $image .attr('data-src', placeholderSrc[0]) .addClass('lazyload') ; } else { done(); } }; $image = $(''); $image.on('lazybeforeunveil', function(e){ afterUnveil(function(){ placeholderSrc[1](); setTimeout(run, 17); }); }); $image.appendTo('body'); run(); }); }], optimumxReinit: ['lazysizes optimumx can be re-initialized', _optimumxReinit(true)], optimumxAttrchangeReinit: ['lazysizes optimumx can be re-initialized', _optimumxReinit(false)], riasAutoSizes: ['lazysizes rias works with autosizes simple sizes', function(assert){ var done = assert.async(); this.promise.always(function($){ var $image; $image = $('') .attr('data-src', 'data:,image-{width}') .appendTo('body') ; $image.on('lazybeforeunveil', function(e){ afterUnveil(function(){ assert.equal($image.prop('currentSrc') || $image.attr('src'), 'data:,image-900'); done(); }); }); }); }], noscriptImg: ['noscript img', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var $noscript; var errors = 0; var onerror = function(){ errors++; }; $noscript = $('
    ') .appendTo('body') ; frameWindow.addEventListener('error', onerror); $noscript.on('lazybeforeunveil', function(e){ afterUnveil(function(){ assert.equal($noscript.find('img').attr('src'), 'data:,img'); assert.equal(errors, 0); frameWindow.removeEventListener('error', onerror); done(); }); }); }); }] }); QUnit.module( "optimumx", { beforeEach: createBeforeEach( { plugins: ['optimumx'] } ) }); QUnit.test.apply(QUnit, lazyTests.simplePicture); QUnit.test.apply(QUnit, lazyTests.optimumxReinit); QUnit.module( "optimumx + mini respimg polyfill", { beforeEach: createBeforeEach( { plugins: ['optimumx', 'respimg'] } ) }); QUnit.test.apply(QUnit, lazyTests.optimumxPictureResize); QUnit.module( "optimumx + respimage + respmutation", { beforeEach: createBeforeEach( { plugins: ['optimumx'], libs: ['respimage', 'respmutation'] } ) }); QUnit.test.apply(QUnit, lazyTests.optimumxPictureResize); QUnit.module( "optimumx + rias", { beforeEach: createBeforeEach( { plugins: ['optimumx', 'rias'] } ) }); QUnit.test.apply(QUnit, lazyTests.optimumxRiasPictureResize); QUnit.test.apply(QUnit, lazyTests.riasResize); QUnit.test.apply(QUnit, lazyTests.riasPictureResize); QUnit.test.apply(QUnit, lazyTests.simplePicture); QUnit.test.apply(QUnit, lazyTests.riasReinit); QUnit.test.apply(QUnit, lazyTests.riasAutoSizes); QUnit.module( "optimumx + rias + respimage", { beforeEach: createBeforeEach( { plugins: ['optimumx', 'rias'], libs: ['respimage'] } ) }); QUnit.test.apply(QUnit, lazyTests.optimumxPictureResize); QUnit.test.apply(QUnit, lazyTests.riasResize); QUnit.test.apply(QUnit, lazyTests.riasPictureResize); QUnit.test.apply(QUnit, lazyTests.simpleAutoSizesPicture); QUnit.test.apply(QUnit, lazyTests.riasReinit); QUnit.test.apply(QUnit, lazyTests.riasAutoSizes); QUnit.module( "rias", { beforeEach: createBeforeEach( { plugins: ['rias'] } ) }); QUnit.test.apply(QUnit, lazyTests.riasResize); QUnit.test.apply(QUnit, lazyTests.riasPictureResize); QUnit.test.apply(QUnit, lazyTests.simplePicture); QUnit.test.apply(QUnit, lazyTests.riasReinit); QUnit.test.apply(QUnit, lazyTests.riasAutoSizes); QUnit.module( "rias + respimage", { beforeEach: createBeforeEach( { plugins: ['optimumx', 'rias'], libs: ['respimage'] } ) }); QUnit.test.apply(QUnit, lazyTests.riasResize); QUnit.test.apply(QUnit, lazyTests.riasPictureResize); QUnit.test.apply(QUnit, lazyTests.riasReinit); QUnit.test.apply(QUnit, lazyTests.riasAutoSizes); QUnit.module( "mini respimg polyfill", { beforeEach: createBeforeEach( { plugins: ['respimg'] } ) }); QUnit.test.apply(QUnit, lazyTests.simplePicture); QUnit.test.apply(QUnit, lazyTests.simpleAutoSizesPicture); QUnit.test.apply(QUnit, lazyTests.simpleSrcset); QUnit.test.apply(QUnit, lazyTests.simpleSrcsetSrc); QUnit.module( "noscript", { beforeEach: createBeforeEach( { plugins: ['noscript'] } ) }); QUnit.test.apply(QUnit, lazyTests.noscriptImg); QUnit.module( "attrchange + rias", { beforeEach: createBeforeEach( { plugins: ['attrchange', 'rias'] } ) }); QUnit.test.apply(QUnit, lazyTests.riasResize); QUnit.test.apply(QUnit, lazyTests.riasPictureResize); QUnit.test.apply(QUnit, lazyTests.riasReinit); QUnit.module( "attrchange + optimumx mix", { beforeEach: createBeforeEach( { plugins: ['attrchange', 'optimumx'] } ) }); QUnit.test.apply(QUnit, lazyTests.optimumxReinit); QUnit.test.apply(QUnit, lazyTests.optimumxAttrchangeReinit); QUnit.module( "parentFit + bgset + optimumx", { beforeEach: createBeforeEach( { plugins: ['optimumx', 'parentFit', 'bgset'], libs: ['respimage'] } ) }); QUnit.test.apply(QUnit, lazyTests.bgsetParentFit); QUnit.module( "parentFit + bgset + respimg", { beforeEach: createBeforeEach( { plugins: ['parentFit', 'bgset', 'respimg'] } ) }); QUnit.test.apply(QUnit, lazyTests.bgsetParentFit); ================================================ FILE: tests/functional-tests.js ================================================ window.lazyTests = { simpleView: ['lazyloads simple image in view', function(assert){ var done = assert.async(); this.promise.always(function($){ var initialSrc = 'data:initial'; var lazySrc = 'data:,lazysrc'; var $topImage = $('') .attr({ src: initialSrc, 'data-src': lazySrc, 'class': 'lazyload' }).appendTo('body') ; assert.equal($topImage.attr('src'), initialSrc); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ assert.equal($topImage.attr('src'), lazySrc); done(); }, 140); }); }); }], simpleScrollView: ['lazyloads simple image after it scrolls near to view', function(assert){ if(/mobi/i.test(navigator.userAgent)){ assert.ok(true); return; } var done = assert.async(); this.promise.always(function($, frameWindow){ var initialSrc = 'data:initial'; var lazySrc = 'data:,lazysrc'; var $topImage = $('') .attr({ src: initialSrc, 'data-src': lazySrc, 'class': 'lazyload' }) .css({ position: 'absolute', top: 1999 }) .appendTo('body') ; assert.equal($topImage.attr('src'), initialSrc); setTimeout(function(){ assert.equal($topImage.attr('src'), initialSrc); }, 70); setTimeout(function(){ $(frameWindow).scrollTop(1900); }, 70); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ assert.equal($topImage.attr('src'), lazySrc); done(); }, 140); }); }); }], simpleAnimateView: ['lazyloads simple image after it animates near to view', function(assert){ if(/mobi/i.test(navigator.userAgent)){ assert.ok(true); return; } var done = assert.async(); this.promise.always(function($, frameWindow){ var initialSrc = 'data:initial'; var lazySrc = 'data:,lazysrc'; var $topImage = $('') .attr({ src: initialSrc, 'data-src': lazySrc, 'class': 'lazyload' }) .css({ position: 'absolute', top: 9999 }) .appendTo('body') ; assert.equal($topImage.attr('src'), initialSrc); setTimeout(function(){ assert.equal($topImage.attr('src'), initialSrc); }, 70); setTimeout(function(){ $topImage.animate({top: 0}, {duration: 50}); }, 70); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ assert.equal($topImage.attr('src'), lazySrc); done(); }); }); }); }], simpleAutoSizes: ['takes width of image and adds it to the sizes attribute', function(assert){ var done = assert.async(); this.promise.always(function($){ var $topImage = $('
    ' + '' + '
    ') .appendTo('body') .find('img'); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ assert.equal($topImage.attr('sizes'), '100px'); done(); }, 140); }); }); }], autoSizesEvent: ['lazybeforesizes event allows modifying sizes attribute', function(assert){ var done = assert.async(); this.promise.always(function($){ var $topImage = $('
    ' + '' + '
    ') .appendTo('body') .find('img'); $topImage.on('lazybeforesizes', function(e){ e.originalEvent.detail.width = 12; afterUnveil(function(){ assert.equal($topImage.attr('sizes'), '12px'); done(); }, 140); }); }); }], autoSizesResize: ['lazysizes reacts on resize', function(assert){ var done = assert.async(); var $iframe = this.$iframe; this.promise.always(function($, frameWindow){ var viewport; var $topImage; var respimgCalls = 0; var repimgExpectedCalls = window.supportsPicture ? 0 : 3; var viewportTests = [ ['300', 150], ['400', 200], ['200', 100], ['400', 200] ]; var run = function(){ if(viewportTests.length){ viewport = viewportTests.shift(); $iframe.css('width', viewport[0]); } else { setTimeout(function(){ $topImage.off('lazybeforesizes.test'); assert.equal(respimgCalls, repimgExpectedCalls); done(); }, 99); } }; frameWindow.picturefill = function(){ respimgCalls++; }; run(); afterUnveil(function(){ $topImage = $('
    ' + '' + '
    ') .appendTo('body') .find('img'); $topImage.on('lazybeforesizes.test', function(e){ console.log('viewport width: '+ $topImage.width()) afterUnveil(function(){ setTimeout(function(){ assert.equal($topImage.attr('sizes'), viewport[1]+'px'); run(); }, 99); }); }); }); }); }], parentAutoSizes: ['takes width of parent of image and adds it to the sizes attribute2', function(assert){ var done = assert.async(); this.promise.always(function($){ var $topImage = $('
    ' + '' + '
    ').find('img'); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ assert.equal($topImage.attr('sizes'), '200px'); done(); }); }); $topImage.parent().appendTo('body'); }); }], simplePicture: ['lazyloads srcset on picture (simplePicture)', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var $picture = createPicture($, [ { 'data-srcset': 'data:,lazysource 200w' }, { 'data-srcset': 'data:,lazyimg 200w', 'class': 'lazyload' } ]); var $source = $picture.find('source'); var $image = $picture.find('img'); $picture.appendTo('body'); assert.equal($source.attr('srcset'), null); assert.equal($image.attr('srcset'), null); $image.on('lazybeforeunveil', function(){ afterUnveil(function(){ var hasPolyfill = frameWindow.respimage || frameWindow.picturefill || frameWindow.lazySizes.cfg.pf; assert.equal($source.attr('srcset'), 'data:,lazysource 200w'); assert.equal($image.attr('srcset') || $image.attr('data-risrcset') || $image.attr('data-pfsrcset'), 'data:,lazyimg 200w'); assert.equal($image.prop('src'), window.HTMLPictureElement || !hasPolyfill ? '' : 'data:,lazysource'); done(); }); }); }); }], simpleAutoSizesPicture: ['lazyloads srcset on picture (simpleAutoSizesPicture)', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var $picture = createPicture($, [ { 'data-srcset': 'data:,lazysource 200w' }, { 'data-srcset': 'data:,lazyimg 200w', 'class': 'lazyload', 'data-sizes': 'auto' } ]); var $source = $picture.find('source'); var $image = $picture.find('img'); $picture.appendTo('body'); assert.equal($source.attr('srcset'), null); assert.equal($image.attr('srcset'), null); assert.equal($image.attr('sizes'), null); $image.on('lazybeforeunveil', function(){ afterUnveil(function(){ var haspolyfill = frameWindow.respimage || frameWindow.picturefill || (frameWindow.lazySizes.cfg.rias && frameWindow.lazySizes.pWS) || frameWindow.lazySizes.cfg.pf; assert.equal($source.attr('srcset'), 'data:,lazysource 200w'); assert.equal($source.attr('sizes'), $image.attr('sizes')); assert.equal($image.attr('srcset') || $image.attr('data-risrcset') || $image.attr('data-pfsrcset'), 'data:,lazyimg 200w'); assert.equal($image.prop('src'), window.HTMLPictureElement || !haspolyfill ? '' : 'data:,lazysource'); assert.equal($image.attr('sizes'), '300px'); done(); }); }); }); }], simpleSrcset: ['lazyloads srcset', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var initialSrc = 'data:initial'; var lazySrcset = 'data:,lazysrcset 300w'; var $topImage = $('') .attr({ src: initialSrc, 'data-srcset': lazySrcset, 'class': 'lazyload', sizes: '100vw' }).appendTo('body') ; assert.equal($topImage.attr('src'), initialSrc); assert.equal($topImage.attr('srcset'), null); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ var haspolyfill = frameWindow.respimage || frameWindow.picturefill || (frameWindow.lazySizes.cfg.rias && frameWindow.lazySizes.pWS) || frameWindow.lazySizes.cfg.pf; var nowSrc = window.HTMLPictureElement || !haspolyfill ? initialSrc : 'data:,lazysrcset'; assert.equal($topImage.attr('srcset') || $topImage.attr('data-risrcset') || $topImage.attr('data-pfsrcset'), lazySrcset); assert.equal($topImage.prop('src'), nowSrc); done(); }); }); }); }], simpleSrcsetSrc: ['lazyloads srcset or src (simpleSrcsetSrc)', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var initialSrc = 'data:,initial'; var lazySrcset = 'data:,lazysrcset 300w'; var lazySrc = 'data:,lazysrc'; var $topImage = $('') .attr({ src: initialSrc, 'data-srcset': lazySrcset, 'data-src': lazySrc, 'class': 'lazyload', sizes: '100vw' }).appendTo('body') ; assert.equal($topImage.prop('src'), initialSrc); assert.equal($topImage.attr('srcset'), null); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ var nowSrc; var haspolyfill = frameWindow.respimage || frameWindow.picturefill || (frameWindow.lazySizes.cfg.rias && frameWindow.lazySizes.pWS) || frameWindow.lazySizes.cfg.pf; if(window.supportsPicture){ nowSrc = lazySrc; } else { nowSrc = haspolyfill ? 'data:,lazysrcset' : lazySrc; } assert.equal($topImage.attr('srcset') || $topImage.attr('data-risrcset') || $topImage.attr('data-pfsrcset'), lazySrcset); assert.equal($topImage.prop('src'), nowSrc); done(); }); }); }); }], extendedSrcsetSrc: ['lazyloads srcset or src (extendedSrcsetSrc)', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var initialSrc = 'data:,initial'; var lazySrcset = 'data:,lazysrcset 300w'; var lazySrc = 'data:,lazysrc'; var $topImage = $('') .attr({ src: initialSrc, 'data-srcset': lazySrcset, 'data-src': lazySrc, 'class': 'lazyload', sizes: '100vw' }).appendTo('body') ; frameWindow.picturefill = function(){ }; assert.equal($topImage.prop('src'), initialSrc); assert.equal($topImage.attr('srcset'), null); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ var nowSrc; if(window.supportsPicture){ nowSrc = lazySrc; } else { nowSrc = initialSrc; } assert.equal($topImage.attr('srcset') || $topImage.attr('data-risrcset') || $topImage.attr('data-pfsrcset'), lazySrcset); assert.equal($topImage.prop('src'), nowSrc); done(); }); }); }); }], extendedPictureSrcsetSrc: ['lazyloads picture srcset or src', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var initialSrc = 'data:,initial'; var lazySrcset = 'data:,lazysrcset 300w'; var lazySrc = 'data:,lazysrc'; var $picture = createPicture($, [ { 'data-srcset': 'data:,lazysource 200w' }, { src: initialSrc, 'data-srcset': lazySrcset, 'data-src': lazySrc, 'class': 'lazyload', sizes: '100vw' } ]); var $topImage = $picture.find('img'); frameWindow.picturefill = function(){}; $picture.appendTo('body'); assert.equal($topImage.prop('src'), initialSrc); assert.equal($topImage.attr('srcset'), null); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ var nowSrc; if(window.supportsPicture){ nowSrc = lazySrc; } else { nowSrc = initialSrc; } assert.equal($topImage.attr('srcset') || $topImage.attr('data-risrcset') || $topImage.attr('data-pfsrcset'), lazySrcset); assert.equal($topImage.prop('src'), nowSrc); done(); }); }); }); }], extendedPictureSrc: ['lazyloads picture src', function(assert){ var done = assert.async(); this.promise.always(function($, frameWindow){ var initialSrc = 'data:,initial'; var lazySrc = 'data:,lazysrc'; var $picture = createPicture($, [ { 'data-srcset': 'data:,lazysource 200w' }, { src: initialSrc, 'data-src': lazySrc, 'class': 'lazyload', sizes: '100vw' } ]); var $topImage = $picture.find('img'); frameWindow.picturefill = function(){}; $picture.appendTo('body'); assert.equal($topImage.prop('src'), initialSrc); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ var nowSrc; if(window.supportsPicture){ nowSrc = lazySrc; } else { nowSrc = initialSrc; } assert.equal($topImage.prop('src'), nowSrc); done(); }); }); }); }], nestedOverflow: ['lazyload detects nested overflow with [data-expand]', function(assert){ var done = assert.async(); this.promise.always(function($){ var initialSrc = 'data:,initial'; var lazySrc = 'data:,lazysrc'; var $topImage = $('
    ' + '
    ') .appendTo('body') .find('img') .attr({ src: initialSrc, 'data-src': lazySrc, 'class': 'lazyload', 'data-expand': -1, style: 'position: relative; top: 100px;' }) ; assert.equal($topImage.prop('src'), initialSrc); setTimeout(function(){ assert.equal($topImage.prop('src'), initialSrc); $topImage.css({top: 50}); }, 70); setTimeout(function(){ assert.equal($topImage.prop('src'), initialSrc); $topImage.css({top: 0}); }, 140); $topImage.on('lazybeforeunveil', function(){ afterUnveil(function(){ assert.equal($topImage.prop('src'), lazySrc); done(); }); }); }); }] }; QUnit.module( "clean lazySizes", { beforeEach: createBeforeEach() }); QUnit.test.apply(QUnit, lazyTests.simpleView); QUnit.test.apply(QUnit, lazyTests.simpleScrollView); QUnit.test.apply(QUnit, lazyTests.simpleAnimateView); QUnit.test.apply(QUnit, lazyTests.simpleAutoSizes); QUnit.test.apply(QUnit, lazyTests.parentAutoSizes); QUnit.test.apply(QUnit, lazyTests.simpleSrcset); QUnit.test.apply(QUnit, lazyTests.simpleSrcsetSrc); QUnit.test.apply(QUnit, lazyTests.extendedSrcsetSrc); QUnit.test.apply(QUnit, lazyTests.extendedPictureSrc); QUnit.test.apply(QUnit, lazyTests.extendedPictureSrcsetSrc); QUnit.test.apply(QUnit, lazyTests.nestedOverflow); QUnit.test.apply(QUnit, lazyTests.autoSizesEvent); QUnit.test.apply(QUnit, lazyTests.autoSizesResize); QUnit.test.apply(QUnit, lazyTests.simplePicture); QUnit.test.apply(QUnit, lazyTests.simpleAutoSizesPicture); QUnit.module( "lazySizes with respimage", { beforeEach: createBeforeEach({libs: ['respimage']}) }); QUnit.test.apply(QUnit, lazyTests.simpleView); QUnit.test.apply(QUnit, lazyTests.simpleScrollView); QUnit.test.apply(QUnit, lazyTests.simpleSrcset); QUnit.test.apply(QUnit, lazyTests.simpleSrcsetSrc); QUnit.test.apply(QUnit, lazyTests.autoSizesResize); QUnit.test.apply(QUnit, lazyTests.simpleAutoSizesPicture); QUnit.module( "lazySizes + options + respimage + respmutation", { beforeEach: createBeforeEach({libs: ['respimage', 'respmutation']}) }); QUnit.test.apply(QUnit, lazyTests.simpleView); QUnit.test.apply(QUnit, lazyTests.simpleScrollView); QUnit.test.apply(QUnit, lazyTests.simpleSrcset); QUnit.test.apply(QUnit, lazyTests.simpleSrcsetSrc); QUnit.test.apply(QUnit, lazyTests.autoSizesEvent); QUnit.test.apply(QUnit, lazyTests.autoSizesResize); QUnit.test.apply(QUnit, lazyTests.simplePicture); QUnit.test.apply(QUnit, lazyTests.simpleAutoSizesPicture); ================================================ FILE: tests/index.html ================================================
    ================================================ FILE: tests/test-files/content-file.html ================================================ ================================================ FILE: tests/test-files/matchMedia.js ================================================ /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */ window.matchMedia || (window.matchMedia = function() { "use strict"; // For browsers that support matchMedium api such as IE 9 and webkit var styleMedia = (window.styleMedia || window.media); // For those that don't support matchMedium if (!styleMedia) { var style = document.createElement('style'), script = document.getElementsByTagName('script')[0], info = null; style.type = 'text/css'; style.id = 'matchmediajs-test'; script.parentNode.insertBefore(style, script); // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle; styleMedia = { matchMedium: function(media) { var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers if (style.styleSheet) { style.styleSheet.cssText = text; } else { style.textContent = text; } // Test if media query is true or false return info.width === '1px'; } }; } return function(media) { return { matches: styleMedia.matchMedium(media || 'all'), media: media || 'all' }; }; }()); ================================================ FILE: tests/test-helper.js ================================================ (function(){ 'use strict'; var source = document.createElement('source'); var requestAnimationFrame = window.requestAnimationFrame || setTimeout; window.createBeforeEach = function(params){ params = $.param(params || {}); return function(){ var that = this; this.promise = $.Deferred(); this.$iframe = $('#test-iframe'); this.$iframe.css({width: 300, height: 300}); this.$iframe.one('load', function(){ that.promise.resolveWith(that, [that.$iframe.prop('contentWindow').jQuery, that.$iframe.prop('contentWindow')]); }); this.$iframe.prop('src', 'test-files/content-file.html?'+params); }; }; window.createPicture = function($, srces){ var $picture = $(''); $.each(srces, function(i, attrs){ var $elem; if(i >= srces.length -1){ $elem = 'img'; } else { $elem = 'source'; } $elem = $('<' + $elem + '/>'); $picture.append($elem); $elem.attr(attrs); }); return $picture; }; window.cleanUpDensity = function(ar){ (ar || []).forEach(function(obj){ if(obj.d){ delete obj.d; } if(obj.cached){ delete obj.cached; } }); return ar; }; window.isTrident = /rident/.test(navigator.userAgent); window.bustedSrcset = (('srcset' in document.createElement('img')) && !('sizes' in document.createElement('img'))); window.supportsPicture = !window.bustedSrcset && window.HTMLPictureElement; window.afterUnveil = (function(){ return function(fn, delay){ setTimeout(function(){ requestAnimationFrame(function(){ setTimeout(fn,0); }); }, delay || 9); }; })(); })(); ================================================ FILE: tsconfig.json ================================================ { // Change this to match your project "include": ["lazysizes.js"], "compilerOptions": { // Tells TypeScript to read JS files, as // normally they are ignored as source files "allowJs": true, // Generate d.ts files "declaration": true, // This compiler run should // only output d.ts files "emitDeclarationOnly": true, } } ================================================ FILE: types/global.d.ts ================================================ import { LazySizesConfigPartial, LazySizesConfig } from './lazysizes-config'; export { LazySizesConfigPartial, LazySizesConfig }; export interface LazyUnveilReadEvent extends CustomEvent { target: Element; type: 'lazyunveilread'; detail: { instance: any; // lazySizes [key: string]: any; } } export interface LazyBeforeUnveilEvent extends CustomEvent { target: Element; type: 'lazybeforeunveil'; preventAble: true; detail: { instance: any; // lazySizes [key: string]: any; } } export interface LazyBeforeSizesEvent extends CustomEvent { type: 'lazybeforesizes'; detail: { width: number; dataAttr: boolean; instance: any; // lazySizes [key: string]: any; } } declare global { interface Window { lazySizesConfig?: LazySizesConfigPartial; } interface WindowEventMap { lazyunveilread: LazyUnveilReadEvent; lazybeforeunveil: LazyBeforeUnveilEvent; lazybeforesizes: LazyBeforeSizesEvent; } interface DocumentEventMap { lazyunveilread: LazyUnveilReadEvent; lazybeforeunveil: LazyBeforeUnveilEvent; lazybeforesizes: LazyBeforeSizesEvent; } interface ElementEventMap { lazyunveilread: LazyUnveilReadEvent; lazybeforeunveil: LazyBeforeUnveilEvent; lazybeforesizes: LazyBeforeSizesEvent; } } ================================================ FILE: types/lazysizes-config.d.ts ================================================ export interface LazySizesConfig { lazyClass: string; loadedClass: string; loadingClass: string; preloadClass: string; errorClass: string; //strictClass: 'lazystrict'; autosizesClass: string; fastLoadedClass: string; iframeLoadMode: 0 | 1; srcAttr: string; srcsetAttr: string; sizesAttr: string; preloadAfterLoad: boolean; minSize: number; customMedia: Record; init: boolean; /** * Must be over 1. */ expFactor: number; hFac: number; loadMode: 0 | 1 | 2 | 3; loadHidden: boolean; ricTimeout: number; throttleDelay: number; [key: string]: any; } export type LazySizesConfigPartial = Partial;