diff --git a/.gitignore b/.gitignore index 67a35a4..4f5ba0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,12 @@ -/node_modules -.DS_Store -.idea -/.idea -dist -npm-debug.log* -yarn-debug.log* -yarn-error.log* -package-lock.json -examples/tests - - +/node_modules +.DS_Store +.idea +/.idea +dist +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +examples/tests + + diff --git a/.npmignore b/.npmignore index 739cdec..3b8812e 100644 --- a/.npmignore +++ b/.npmignore @@ -1,8 +1,8 @@ -# .npmignore -src -examples -config -build -.babelrc -.gitignore +# .npmignore +src +examples +config +build +.babelrc +.gitignore .idea \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a664c..0e065bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,86 +1,90 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -Types of changes: -- `Added` for new features. -- `Changed` for changes in existing functionality. -- `Deprecated` for soon-to-be removed features. -- `Removed` for now removed features. -- `Fixed` for any bug fixes. -- `Security` in case of vulnerabilities. - -> Date format: YYYY-MM-DD - -> If we have some "Breaking changes" we can mark it in message by `**BREAKING**` preffix, like: -> `- **BREAKING**: Some message` - -------------- - -## TODOs -> Todo list for future - -- ... - -------------- -## 4.8.2 - 2021-11-08 - -### Fixed -- possibility to remove API version in blur-hash, low-preview, wp and plain -## 4.8.1 - 2021-08-17 - -### fixed - -- Minifying build in low preview -## 4.8.0 - 2021-06-16 - -### Deprecated - -Property **ignoreNodeImgSize** is deprecated. Use **imageSizeAttributes: 'ignore'** instead - -### Added -- new property: **imageSizeAttributes** -## 4.7.0 - 2020-02-23 -### added -- add custom root element to process function -- process result coudimage URL before inserting into dom (plain version only) - -### fixed -- banners in build files - -## 4.6.8 - 2020-02-04 -### fixed -- problem with background images with no children - -## 4.6.7 - 2020-02-03 -### fixed -- problem with rendering images inside background image - -## 4.6.6 - 2020-1-20 -### updated -- highlight js - -## 4.6.5 - 2020-11-09 -### updated -- utils version to add src to each size - -## 4.6.4 - 2020-11-04 -### updated -- utils version to fix getComputed function -## 4.6.3 - 2020-11-02 -### updated -- utils version to enable params by queries - -## 4.6.2 - 2020-11-02 -### updated -- utils version - -## 4.6.1 - 2020-09-03 -### Fixed -- background images doesn't appear - -## 4.6.0 - 2020-07-27 -### Added -- possibility to change cloudimage responsive selector +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +Types of changes: +- `Added` for new features. +- `Changed` for changes in existing functionality. +- `Deprecated` for soon-to-be removed features. +- `Removed` for now removed features. +- `Fixed` for any bug fixes. +- `Security` in case of vulnerabilities. + +> Date format: YYYY-MM-DD + +> If we have some "Breaking changes" we can mark it in message by `**BREAKING**` preffix, like: +> `- **BREAKING**: Some message` + +------------- + +## TODOs +> Todo list for future + +- ... + +------------- +## 4.8.3 - 2021-12-09 +### Fixed +- img-src starts with "//" + +## 4.8.2 - 2021-11-08 + +### Fixed +- possibility to remove API version in blur-hash, low-preview, wp and plain +## 4.8.1 - 2021-08-17 + +### fixed + +- Minifying build in low preview +## 4.8.0 - 2021-06-16 + +### Deprecated + +Property **ignoreNodeImgSize** is deprecated. Use **imageSizeAttributes: 'ignore'** instead + +### Added +- new property: **imageSizeAttributes** +## 4.7.0 - 2020-02-23 +### added +- add custom root element to process function +- process result coudimage URL before inserting into dom (plain version only) + +### fixed +- banners in build files + +## 4.6.8 - 2020-02-04 +### fixed +- problem with background images with no children + +## 4.6.7 - 2020-02-03 +### fixed +- problem with rendering images inside background image + +## 4.6.6 - 2020-1-20 +### updated +- highlight js + +## 4.6.5 - 2020-11-09 +### updated +- utils version to add src to each size + +## 4.6.4 - 2020-11-04 +### updated +- utils version to fix getComputed function +## 4.6.3 - 2020-11-02 +### updated +- utils version to enable params by queries + +## 4.6.2 - 2020-11-02 +### updated +- utils version + +## 4.6.1 - 2020-09-03 +### Fixed +- background images doesn't appear + +## 4.6.0 - 2020-07-27 +### Added +- possibility to change cloudimage responsive selector diff --git a/LICENSE b/LICENSE index ee8855c..82800cb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2019 scaleflex - -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. +MIT License + +Copyright (c) 2019 scaleflex + +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. diff --git a/README-BLUR-HASH.md b/README-BLUR-HASH.md index 75010a8..97f3683 100644 --- a/README-BLUR-HASH.md +++ b/README-BLUR-HASH.md @@ -1,566 +1,566 @@ -[![Release](https://img.shields.io/badge/release-v4.8.2-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) -[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) -[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) - -## VERSIONS - -* [__Low Quality Preview__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README.md) -* __Blur Hash__ -* [__Plain (CSS free)__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-PLAIN.md) - -

- The Lounge -

- -

- JS Cloudimage Responsive | Cloudimage v7 -

- -

- (blur-hash version) -

- -[Documentation for v2 | Cloudimage v6](https://github.com/scaleflex/js-cloudimage-responsive/blob/v7/README_v6.md) - -

- - Docs - • - Demo - • - Code Sandbox - • - Why? - -

- -This plugin detects the width of any image container as well as the device pixel ratio -density to load the optimal image size needed. -Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive -automated image optimization service. - -When an image is first loaded on your website or mobile app, -Cloudimage's resizing servers will download the origin image from -the source, resize it for the client's screen size and deliver to your users through one or multiple -Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. - -**NOTE:** Your original (master) images should be stored on a server -or storage bucket (S3, Google Cloud, Azure Blob...) reachable over -HTTP or HTTPS by Cloudimage. If you want to upload your master images to -Cloudimage, contact us at -[hello@cloudimage.io](mailto:hello@cloudimage.io). - -

- The Lounge -

- -powered by [Cloudimage](https://www.cloudimage.io/) -([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) - -## Table of contents - -* [Demo](#demo) -* [Requirements](#requirements) -* [Step 1: Installation](#installation) -* [Step 2: Initialize](#initialize) -* [Step 3: Implement](#implement) -* [Step 4: Prevent seeing broken images](#prevent_styles) -* [Configuration](#configuration) -* [Image properties](#image_properties) -* [Lazy loading](#lazy_loading) -* [Process dynamically loaded images](#dynamically-loaded) -* [Examples & workarounds](#examples_workarounds) -* [Browser support](#browser_support) -* [Filerobot UI Family](#ui_family) -* [Contributing](#contributing) -* [License](#license) - - -## Demo - -To see the Cloudimage Responsive plugin in action, please check out the -[Demo page](https://cdn.scaleflex.it/plugins/js-cloudimage-responsive/demo/blur-hash/index.html?v=28.04.2020_2). -Play with your browser's window size and observe your -Inspector's Network tab to see how Cloudimage delivers the optimal -image size to your browser, hence accelerating the overall page -loading time. - -## Requirements - -### Cloudimage account - -To use the Cloudimage Responsive plugin, you will need a -Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by -registering [here](https://www.cloudimage.io/en/register_page). -Once your token is created, you can configure it as described below. -This token allows you to use 25GB of image cache and 25GB of worldwide -CDN traffic per month for free. - -### Layout/CSS - -In order to use smooth transition between preview image and good quality and size image, the plugin uses absolute positioning for images and wraps an image tag with div element with relative positioning. - -You have to pay attention on the following things: - -- the plugin sets 100% width for img tag and position absolute (You should not apply other sizes or change position property. If you need to change width of image or position, you have to set it to wrapper) - -## Step 1: Installation - -Add script tag with CDN link to js-cloudimage-responsive - -```javascript - -``` - -or using npm - -``` -$ npm install --save js-cloudimage-responsive -``` - -## Step 2: Initialize - -After adding the js-cloudimage-responsive lib, simply iniatialize it with your **token** and the **baseURL** of your image storage: - -```javascript - -``` - -or in new style with npm: - -```javascript - -import 'js-cloudimage-responsive/blur-hash'; - -const ciResponsive = new window.CIResponsive({ - token: 'demo', - baseURL: 'https://cloudimage.public.airstore.io/demo/' // optional -}); -``` - -**NOTE**: You should put the scripts below all your content in the body tag and above all other scripts. After inserting the scripts the plugin starts immediately processing all images with ci-src, ci-bg-url attributes. (If the scripts are put into the head tag, no images will be detected and processed. If the scripts are put below all other scripts on your page, the images will be not showed until all the scripts downloaded.) - -## Step 3: Implement in img tag or use it as background image - -### img tag - -Finally, just use the `ci-src` instead of the `src` attribute in image tag: - -```html - -``` - -NOTE: - -"ci-ratio" is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. - -"ci-blur-hash" is A very compact representation of a placeholder for an image. read more - -edeit in codesandbox - -### background image - -Use the `ci-bg-url` instead of CSS background-image property `background-image: url(...)`: - -```html -
-``` - -edeit in codesandbox - -## Step 4: Prevent seeing broken images - -Add the following styles in the head of your site - -```html - -``` - -## Config - -### token - -###### Type: **String** | Default: **"demo"** | _required_ - -Your Cloudimage customer token. -[Subscribe](https://www.cloudimage.io/en/register_page) for a -Cloudimage account to get one. The subscription takes less than a -minute and is totally free. - -### domain - -###### Type: **String** | Default: **"cloudimg.io"** - -Use your custom domain. - -### imgSelector - -###### Type: **String** | Default: **"ci-src"** - -Cloudimage Responsive Selector for images. - -### bgSelector - -###### Type: **String** | Default: **"ci-bg-url"** - -Cloudimage Responsive Selector for background images. - -### doNotReplaceURL - -###### Type: **bool** | Default: **false** - -If set to **true** the plugin will only add query params to the given source of image. - -### baseURL - -###### Type: **String** | _optional_ - -Your image folder on server, this alows to shorten your origin image URLs. - -### apiVersion - -###### Type: **String** |Default: **'v7'** | _optional_ -Allow to use a specific version of API. - -- set a specific version of API -```javascript - -``` -- disable API version -```javascript - -``` - -### lazyLoading - -###### Type: **Bool** | Default: **false** | _optional_ - -Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) - -### params - -###### Type: **String** | Default: **'org_if_sml=1'** | _optional_ - -Applies default Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... -Multiple params can be applied, separated by "```&```" e.g. wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1 - -```javascript -{ - ..., - params: 'org_if_sml=1' -} -``` - -#### alternative syntax: type: **Object** - -```javascript -{ - ..., - params: { - org_if_sml: 1, - grey: 1, - ... - } -} -``` - -[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) - -### exactSize - -###### Type: **Bool** | Default: **false** | _optional_ - -Forces to load exact size of images. -By default the plugin rounds container width to next possible value which can be divided by 100 without the remainder. -It’s done for cache reasons so that not all images are cached by 1px, but only 100px, 200px, 300px … - -### limitFactor - -###### Type: **Number** | Default: **100** | _optional_ - -Rounds up size of an image to nearest limitFactor value. - -For example -* for an image with width **358px** and limitFactor equals **100** the plugin will round up to 400px -* for an image with width **358px** and limitFactor equals **5** the plugin will round up to 360px - -### presets - -###### Type: **Object** - -Default: - -```javascript -const cloudimageConfig = { - token: 'demo', - baseURL: 'https://jolipage.airstore.io/', - ... - presets: { - xs: '(max-width: 575px)', // up to 575 PHONE - sm: '(min-width: 576px)', // 576 - 767 PHABLET - md: '(min-width: 768px)', // 768 - 991 TABLET - lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN - xl: '(min-width: 1200px)' // from 1200 USUALSCREEN - } -}; -``` - -### devicePixelRatioList - -###### Type: **[Number,...]** | Default: **[1, 1.5, 2, 3, 4]** | _optional_ - -List of supported device pixel ratios. If there is no need to support retina devices, you should set empty array `devicePixelRatioList: []` - -### imageSizeAttributes - -###### Type: **String** | possible values: 'use', 'ignore', 'take-ratio' | Default: **'use'** - -If width and height attributes are set: - -**use** - width & height attributes values will be used to calculate image size (according to user's DPR) and **ratio**. - -**take-ratio** - width & height attributes values will be used only to calculate **ratio**. - -**ignore** - width & height attributes will be ignored. - -If width and height attributes are NOT set, image container size will be detected to calculate result image size (according to user's DPR) - -*Note*: If only width or height attributes is set, ratio is going to be taken from ci-ratio image attribute - - -## Image properties - -Cloudimage responsive plugin will make image on your page responsive if you replace the `src` with `ci-src` attribute in the `` tag: - -### ci-src - -###### Type: **String** | Default: **undefined** | _required_ - -Original image hosted on your web server. You can use absolute path or -relative to baseURL in your config. - -**NOTES:** -* The plugin uses a special algorithm to detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. -* Images where `ci-src` is not used will be delivered in a standard, non-responsive way. -* Parameters after "?" question mark will be added at the end of result URL after processing by the plugin. - -### width - -###### Type: **String** (e.g. 300px, 20vw) | Default: **undefined** - -If it's set the plugin will use width as fixed value and change only according device pixel ratio. - -### height - -###### Type: **String** (e.g. 300px, 20vh) | Default: **undefined** - -If it's set the plugin will use height as fixed value and change only according device pixel ratio. - -### ci-blur-hash - -###### Type: **String** | Default: **undefined** | _required_ - -BlurHash is a very compact representation of a placeholder for an image. read more - -```javascript -ci-blur-hash="LNAyTi9ZVsQ,.TM{WAkW4T%2WBt7" -``` -### ci-params - -###### Type: **String** | Default: **undefined** | _optional_ - -You can apply any Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... -Multiple params can be applied, separated by "```&```" e.g. **wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1** - -```javascript -ci-params="gray=1&bright=10" -``` - -#### alternative syntax: type: **Object** - -```javascript -ci-params="{ - bright: 10, - grey: 1, - ... -}" -``` - -[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) - -### ci-sizes - -###### Type: **Object** | Default: **undefined** - -**{ preset breakpoint | 'media query': imageProps }**: - -preset breakpoints: **xs, sm, md, lg, xl** ([can be changed with](#presets)) -imageProps: **{ w, h, r, src }** where - -* **w** - width, -* **h** - height, -* **r** - ratio, -* **src** - original image hosted on your web server. You can use absolute path or relative to the baseURL in your config. - -```jsx - -``` - -You can drop some breakpoints, for example: - -```jsx - -``` - -##### new experimental syntax - -md: { w: '40vw', h: 350 } or md: { w: 250, h: '20vh' } - -adds possibility to use fixed height or width and change dynamically other dimension - -**NOTE:** if size is not set, the plugin uses a special algorithm to -detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. - -### ci-ratio (or data-ci-ratio) - -###### Type: **Number** | _optional_ - -It is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. - -To see the full cloudimage documentation [click here](https://docs.cloudimage.io/go/cloudimage-documentation) - -### ci-not-lazy (or data-ci-not-lazy) - -###### Type: **Bool** - -Switch off lazyload per image. - -## Lazy Loading - -Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. - -The example below uses [lazysizes](https://github.com/aFarkas/lazysizes) -library using Intersection Observer API. - -[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) - -add the following scripts right after js-cloudimage-responsive script - -```javascript - - - -``` - -the initialization script - -```javascript - - ``` - -## Process dynamically loaded images! - -In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. - -```javascript - -``` - -## Examples & workarounds -* [See all](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md) -* [Cropping](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md#cropping) - - -## Browser support - -Tested in all modern browsers and IE 11,10,9. - -If you want to address the use case where your visitors disable JS. You have to add noscript tag. - -```html - -``` - -NOTE: If you use lazy loading with IntersectionObserver, you must -manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) -for cross-browser support. - -## Filerobot UI Familiy - -* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) -* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) -* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) -* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) -* [Uploader](https://github.com/scaleflex/filerobot-uploader) - -## Contributing! - -All contributions are super welcome! - - -## License -JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT) - +[![Release](https://img.shields.io/badge/release-v4.8.3-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) +[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) +[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) + +## VERSIONS + +* [__Low Quality Preview__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README.md) +* __Blur Hash__ +* [__Plain (CSS free)__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-PLAIN.md) + +

+ The Lounge +

+ +

+ JS Cloudimage Responsive | Cloudimage v7 +

+ +

+ (blur-hash version) +

+ +[Documentation for v2 | Cloudimage v6](https://github.com/scaleflex/js-cloudimage-responsive/blob/v7/README_v6.md) + +

+ + Docs + • + Demo + • + Code Sandbox + • + Why? + +

+ +This plugin detects the width of any image container as well as the device pixel ratio +density to load the optimal image size needed. +Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive +automated image optimization service. + +When an image is first loaded on your website or mobile app, +Cloudimage's resizing servers will download the origin image from +the source, resize it for the client's screen size and deliver to your users through one or multiple +Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. + +**NOTE:** Your original (master) images should be stored on a server +or storage bucket (S3, Google Cloud, Azure Blob...) reachable over +HTTP or HTTPS by Cloudimage. If you want to upload your master images to +Cloudimage, contact us at +[hello@cloudimage.io](mailto:hello@cloudimage.io). + +

+ The Lounge +

+ +powered by [Cloudimage](https://www.cloudimage.io/) +([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) + +## Table of contents + +* [Demo](#demo) +* [Requirements](#requirements) +* [Step 1: Installation](#installation) +* [Step 2: Initialize](#initialize) +* [Step 3: Implement](#implement) +* [Step 4: Prevent seeing broken images](#prevent_styles) +* [Configuration](#configuration) +* [Image properties](#image_properties) +* [Lazy loading](#lazy_loading) +* [Process dynamically loaded images](#dynamically-loaded) +* [Examples & workarounds](#examples_workarounds) +* [Browser support](#browser_support) +* [Filerobot UI Family](#ui_family) +* [Contributing](#contributing) +* [License](#license) + + +## Demo + +To see the Cloudimage Responsive plugin in action, please check out the +[Demo page](https://cdn.scaleflex.it/plugins/js-cloudimage-responsive/demo/blur-hash/index.html?v=28.04.2020_2). +Play with your browser's window size and observe your +Inspector's Network tab to see how Cloudimage delivers the optimal +image size to your browser, hence accelerating the overall page +loading time. + +## Requirements + +### Cloudimage account + +To use the Cloudimage Responsive plugin, you will need a +Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by +registering [here](https://www.cloudimage.io/en/register_page). +Once your token is created, you can configure it as described below. +This token allows you to use 25GB of image cache and 25GB of worldwide +CDN traffic per month for free. + +### Layout/CSS + +In order to use smooth transition between preview image and good quality and size image, the plugin uses absolute positioning for images and wraps an image tag with div element with relative positioning. + +You have to pay attention on the following things: + +- the plugin sets 100% width for img tag and position absolute (You should not apply other sizes or change position property. If you need to change width of image or position, you have to set it to wrapper) + +## Step 1: Installation + +Add script tag with CDN link to js-cloudimage-responsive + +```javascript + +``` + +or using npm + +``` +$ npm install --save js-cloudimage-responsive +``` + +## Step 2: Initialize + +After adding the js-cloudimage-responsive lib, simply iniatialize it with your **token** and the **baseURL** of your image storage: + +```javascript + +``` + +or in new style with npm: + +```javascript + +import 'js-cloudimage-responsive/blur-hash'; + +const ciResponsive = new window.CIResponsive({ + token: 'demo', + baseURL: 'https://cloudimage.public.airstore.io/demo/' // optional +}); +``` + +**NOTE**: You should put the scripts below all your content in the body tag and above all other scripts. After inserting the scripts the plugin starts immediately processing all images with ci-src, ci-bg-url attributes. (If the scripts are put into the head tag, no images will be detected and processed. If the scripts are put below all other scripts on your page, the images will be not showed until all the scripts downloaded.) + +## Step 3: Implement in img tag or use it as background image + +### img tag + +Finally, just use the `ci-src` instead of the `src` attribute in image tag: + +```html + +``` + +NOTE: + +"ci-ratio" is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. + +"ci-blur-hash" is A very compact representation of a placeholder for an image. read more + +edeit in codesandbox + +### background image + +Use the `ci-bg-url` instead of CSS background-image property `background-image: url(...)`: + +```html +
+``` + +edeit in codesandbox + +## Step 4: Prevent seeing broken images + +Add the following styles in the head of your site + +```html + +``` + +## Config + +### token + +###### Type: **String** | Default: **"demo"** | _required_ + +Your Cloudimage customer token. +[Subscribe](https://www.cloudimage.io/en/register_page) for a +Cloudimage account to get one. The subscription takes less than a +minute and is totally free. + +### domain + +###### Type: **String** | Default: **"cloudimg.io"** + +Use your custom domain. + +### imgSelector + +###### Type: **String** | Default: **"ci-src"** + +Cloudimage Responsive Selector for images. + +### bgSelector + +###### Type: **String** | Default: **"ci-bg-url"** + +Cloudimage Responsive Selector for background images. + +### doNotReplaceURL + +###### Type: **bool** | Default: **false** + +If set to **true** the plugin will only add query params to the given source of image. + +### baseURL + +###### Type: **String** | _optional_ + +Your image folder on server, this alows to shorten your origin image URLs. + +### apiVersion + +###### Type: **String** |Default: **'v7'** | _optional_ +Allow to use a specific version of API. + +- set a specific version of API +```javascript + +``` +- disable API version +```javascript + +``` + +### lazyLoading + +###### Type: **Bool** | Default: **false** | _optional_ + +Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) + +### params + +###### Type: **String** | Default: **'org_if_sml=1'** | _optional_ + +Applies default Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... +Multiple params can be applied, separated by "```&```" e.g. wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1 + +```javascript +{ + ..., + params: 'org_if_sml=1' +} +``` + +#### alternative syntax: type: **Object** + +```javascript +{ + ..., + params: { + org_if_sml: 1, + grey: 1, + ... + } +} +``` + +[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) + +### exactSize + +###### Type: **Bool** | Default: **false** | _optional_ + +Forces to load exact size of images. +By default the plugin rounds container width to next possible value which can be divided by 100 without the remainder. +It’s done for cache reasons so that not all images are cached by 1px, but only 100px, 200px, 300px … + +### limitFactor + +###### Type: **Number** | Default: **100** | _optional_ + +Rounds up size of an image to nearest limitFactor value. + +For example +* for an image with width **358px** and limitFactor equals **100** the plugin will round up to 400px +* for an image with width **358px** and limitFactor equals **5** the plugin will round up to 360px + +### presets + +###### Type: **Object** + +Default: + +```javascript +const cloudimageConfig = { + token: 'demo', + baseURL: 'https://jolipage.airstore.io/', + ... + presets: { + xs: '(max-width: 575px)', // up to 575 PHONE + sm: '(min-width: 576px)', // 576 - 767 PHABLET + md: '(min-width: 768px)', // 768 - 991 TABLET + lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN + xl: '(min-width: 1200px)' // from 1200 USUALSCREEN + } +}; +``` + +### devicePixelRatioList + +###### Type: **[Number,...]** | Default: **[1, 1.5, 2, 3, 4]** | _optional_ + +List of supported device pixel ratios. If there is no need to support retina devices, you should set empty array `devicePixelRatioList: []` + +### imageSizeAttributes + +###### Type: **String** | possible values: 'use', 'ignore', 'take-ratio' | Default: **'use'** + +If width and height attributes are set: + +**use** - width & height attributes values will be used to calculate image size (according to user's DPR) and **ratio**. + +**take-ratio** - width & height attributes values will be used only to calculate **ratio**. + +**ignore** - width & height attributes will be ignored. + +If width and height attributes are NOT set, image container size will be detected to calculate result image size (according to user's DPR) + +*Note*: If only width or height attributes is set, ratio is going to be taken from ci-ratio image attribute + + +## Image properties + +Cloudimage responsive plugin will make image on your page responsive if you replace the `src` with `ci-src` attribute in the `` tag: + +### ci-src + +###### Type: **String** | Default: **undefined** | _required_ + +Original image hosted on your web server. You can use absolute path or +relative to baseURL in your config. + +**NOTES:** +* The plugin uses a special algorithm to detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. +* Images where `ci-src` is not used will be delivered in a standard, non-responsive way. +* Parameters after "?" question mark will be added at the end of result URL after processing by the plugin. + +### width + +###### Type: **String** (e.g. 300px, 20vw) | Default: **undefined** + +If it's set the plugin will use width as fixed value and change only according device pixel ratio. + +### height + +###### Type: **String** (e.g. 300px, 20vh) | Default: **undefined** + +If it's set the plugin will use height as fixed value and change only according device pixel ratio. + +### ci-blur-hash + +###### Type: **String** | Default: **undefined** | _required_ + +BlurHash is a very compact representation of a placeholder for an image. read more + +```javascript +ci-blur-hash="LNAyTi9ZVsQ,.TM{WAkW4T%2WBt7" +``` +### ci-params + +###### Type: **String** | Default: **undefined** | _optional_ + +You can apply any Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... +Multiple params can be applied, separated by "```&```" e.g. **wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1** + +```javascript +ci-params="gray=1&bright=10" +``` + +#### alternative syntax: type: **Object** + +```javascript +ci-params="{ + bright: 10, + grey: 1, + ... +}" +``` + +[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) + +### ci-sizes + +###### Type: **Object** | Default: **undefined** + +**{ preset breakpoint | 'media query': imageProps }**: + +preset breakpoints: **xs, sm, md, lg, xl** ([can be changed with](#presets)) +imageProps: **{ w, h, r, src }** where + +* **w** - width, +* **h** - height, +* **r** - ratio, +* **src** - original image hosted on your web server. You can use absolute path or relative to the baseURL in your config. + +```jsx + +``` + +You can drop some breakpoints, for example: + +```jsx + +``` + +##### new experimental syntax + +md: { w: '40vw', h: 350 } or md: { w: 250, h: '20vh' } + +adds possibility to use fixed height or width and change dynamically other dimension + +**NOTE:** if size is not set, the plugin uses a special algorithm to +detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. + +### ci-ratio (or data-ci-ratio) + +###### Type: **Number** | _optional_ + +It is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. + +To see the full cloudimage documentation [click here](https://docs.cloudimage.io/go/cloudimage-documentation) + +### ci-not-lazy (or data-ci-not-lazy) + +###### Type: **Bool** + +Switch off lazyload per image. + +## Lazy Loading + +Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. + +The example below uses [lazysizes](https://github.com/aFarkas/lazysizes) +library using Intersection Observer API. + +[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) + +add the following scripts right after js-cloudimage-responsive script + +```javascript + + + +``` + +the initialization script + +```javascript + + ``` + +## Process dynamically loaded images! + +In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. + +```javascript + +``` + +## Examples & workarounds +* [See all](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md) +* [Cropping](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md#cropping) + + +## Browser support + +Tested in all modern browsers and IE 11,10,9. + +If you want to address the use case where your visitors disable JS. You have to add noscript tag. + +```html + +``` + +NOTE: If you use lazy loading with IntersectionObserver, you must +manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) +for cross-browser support. + +## Filerobot UI Familiy + +* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) +* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) +* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) +* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) +* [Uploader](https://github.com/scaleflex/filerobot-uploader) + +## Contributing! + +All contributions are super welcome! + + +## License +JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT) + diff --git a/README-PLAIN.md b/README-PLAIN.md index db4e4ca..71363f6 100644 --- a/README-PLAIN.md +++ b/README-PLAIN.md @@ -1,544 +1,544 @@ -[![Release](https://img.shields.io/badge/release-v4.8.2-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) -[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) -[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) - -## VERSIONS - -* [__Low Quality Preview__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README.md) -* [__Blur Hash__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-BLUR-HASH.md) -* __Plain (CSS free)__ - -

- The Lounge -

- -

- JS Cloudimage Responsive | Cloudimage v7 -

- -

- (plain version) -

- -

- - Docs - • - Demo - • - Code Sandbox - • - Why? - -

- -This plugin detects the width of any image container as well as the device pixel ratio -density to load the optimal image size needed. -Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive -automated image optimization service. - -When an image is first loaded on your website or mobile app, -Cloudimage's resizing servers will download the origin image from -the source, resize it for the client's screen size and deliver to your users through one or multiple -Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. - -**NOTE:** Your original (master) images should be stored on a server -or storage bucket (S3, Google Cloud, Azure Blob...) reachable over -HTTP or HTTPS by Cloudimage. If you want to upload your master images to -Cloudimage, contact us at -[hello@cloudimage.io](mailto:hello@cloudimage.io). - -

- The Lounge -

- -powered by [Cloudimage](https://www.cloudimage.io/) -([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) - -## Table of contents - -* [Demo](#demo) -* [Requirements](#requirements) -* [Step 1: Installation](#installation) -* [Step 2: Initialize](#initialize) -* [Step 3: Implement](#implement) -* [Step 4: Prevent seeing broken images](#prevent_styles) -* [Configuration](#configuration) -* [Image properties](#image_properties) -* [Lazy loading](#lazy_loading) -* [Process dynamically loaded images](#dynamically-loaded) -* [Examples & workarounds](#examples_workarounds) -* [Browser support](#browser_support) -* [Filerobot UI Family](#ui_family) -* [Contributing](#contributing) -* [License](#license) - - -## Demo - -To see the Cloudimage Responsive plugin in action, please check out the -[Demo page](https://scaleflex.airstore.io/plugins/js-cloudimage-responsive/demo/plain/index.html?v=28.04.2020). -Play with your browser's window size and observe your -Inspector's Network tab to see how Cloudimage delivers the optimal -image size to your browser, hence accelerating the overall page -loading time. - -## Requirements - -### Cloudimage account - -To use the Cloudimage Responsive plugin, you will need a -Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by -registering [here](https://www.cloudimage.io/en/register_page). -Once your token is created, you can configure it as described below. -This token allows you to use 25GB of image cache and 25GB of worldwide -CDN traffic per month for free. - -### Layout/CSS - -This version of plugin doesn't add any CSS or change layout thus no effect on your layout and styling. - -## Step 1: Installation - -Add script tag with CDN link to js-cloudimage-responsive - -```javascript - -``` - -or using npm - -``` -$ npm install --save js-cloudimage-responsive -``` - -## Step 2: Initialize - -After adding the js-cloudimage-responsive lib, simply iniatialize it with your **token** and the **baseURL** of your image storage: - -```javascript - -``` - -or in new style with npm: - -```javascript - -import 'js-cloudimage-responsive/plain'; - -const ciResponsive = new window.CIResponsive({ - token: 'demo', - baseURL: 'https://cloudimage.public.airstore.io/demo/' // optional -}); -``` - -**NOTE**: You should put the scripts below all your content in the body tag and above all other scripts. After inserting the scripts the plugin starts immediately processing all images with ci-src, ci-bg-url attributes. (If the scripts are put into the head tag, no images will be detected and processed. If the scripts are put below all other scripts on your page, the images will be not showed until all the scripts downloaded.) - -## Step 3: Implement in img tag or use it as background image - -### img tag - -Finally, just use the `ci-src` instead of the `src` attribute in image tag: - -```html - -``` - -edeit in codesandbox - -### background image - -Use the `ci-bg-url` instead of CSS background-image property `background-image: url(...)`: - -```html -
-``` - -edeit in codesandbox - -## Step 4: Prevent seeing broken images - -Add the following styles in the head of your site - -```html - -``` - -## Config - -### token - -###### Type: **String** | Default: **"demo"** | _required_ - -Your Cloudimage customer token. -[Subscribe](https://www.cloudimage.io/en/register_page) for a -Cloudimage account to get one. The subscription takes less than a -minute and is totally free. - -### domain - -###### Type: **String** | Default: **"cloudimg.io"** - -Use your custom domain. - -### imgSelector - -###### Type: **String** | Default: **"ci-src"** - -Cloudimage Responsive Selector for images. - -### bgSelector - -###### Type: **String** | Default: **"ci-bg-url"** - -Cloudimage Responsive Selector for background images. - -### doNotReplaceURL - -###### Type: **bool** | Default: **false** - -If set to **true** the plugin will only add query params to the given source of image. - -### baseURL - -###### Type: **String** | _optional_ - -Your image folder on server, this alows to shorten your origin image URLs. - -### apiVersion - -###### Type: **String** |Default: **'v7'** | _optional_ -Allow to use a specific version of API. - -- set a specific version of API -```javascript - -``` -- disable API version -```javascript - -``` - -### lazyLoading - -###### Type: **Bool** | Default: **false** | _optional_ - -Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) - -### params - -###### Type: **String** | Default: **'org_if_sml=1'** | _optional_ - -Applies default Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... -Multiple params can be applied, separated by "```&```" e.g. wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1 - -```javascript -{ - ..., - params: 'org_if_sml=1' -} -``` - -#### alternative syntax: type: **Object** - -```javascript -{ - ..., - params: { - org_if_sml: 1, - grey: 1, - ... - } -} -``` - -[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) - -### exactSize - -###### Type: **Bool** | Default: **false** | _optional_ - -Forces to load exact size of images. -By default the plugin rounds container width to next possible value which can be divided by 100 without the remainder. -It’s done for cache reasons so that not all images are cached by 1px, but only 100px, 200px, 300px … - -### limitFactor - -###### Type: **Number** | Default: **100** | _optional_ - -Rounds up size of an image to nearest limitFactor value. - -For example -* for an image with width **358px** and limitFactor equals **100** the plugin will round up to 400px -* for an image with width **358px** and limitFactor equals **5** the plugin will round up to 360px - -### devicePixelRatioList - -###### Type: **[Number,...]** | Default: **[1, 1.5, 2, 3, 4]** | _optional_ - -List of supported device pixel ratios. If there is no need to support retina devices, you should set empty array `devicePixelRatioList: []` - -### presets - -###### Type: **Object** - -Default: - -```javascript - - -``` - -Breakpoints shortcuts to use in image size property, can be overwridden. - -### imageSizeAttributes - -###### Type: **String** | possible values: 'use', 'ignore', 'take-ratio' | Default: **'use'** - -If width and height attributes are set: - -**use** - width & height attributes values will be used to calculate image size (according to user's DPR) and **ratio**. - -**take-ratio** - width & height attributes values will be used only to calculate **ratio**. - -**ignore** - width & height attributes will be ignored. - -If width and height attributes are NOT set, image container size will be detected to calculate result image size (according to user's DPR) - -*Note*: If only width or height attributes is set, ratio is going to be taken from ci-ratio image attribute - -## Image properties - -Cloudimage responsive plugin will make image on your page responsive if you replace the `src` with `ci-src` attribute in the `` tag: - -### ci-src - -###### Type: **String** | Default: **undefined** | _required_ - -Original image hosted on your web server. You can use absolute path or -relative to baseURL in your config. - -**NOTES:** -* The plugin uses a special algorithm to detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. -* Images where `ci-src` is not used will be delivered in a standard, non-responsive way. -* Parameters after "?" question mark will be added at the end of result URL after processing by the plugin. - -### width - -###### Type: **String** (e.g. 300px, 20vw) | Default: **undefined** - -If it's set the plugin will use width as fixed value and change only according device pixel ratio. - -### height - -###### Type: **String** (e.g. 300px, 20vh) | Default: **undefined** - -If it's set the plugin will use height as fixed value and change only according device pixel ratio. - -### ci-params - -###### Type: **String** | Default: **undefined** | _optional_ - -You can apply any Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... -Multiple params can be applied, separated by "```&```" e.g. **wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1** - -```javascript -ci-params="gray=1&bright=10" -``` - -#### alternative syntax: type: **Object** - -```javascript -ci-params="{ - bright: 10, - grey: 1, - ... -}" -``` - -[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) - -### ci-sizes - -###### Type: **Object** | Default: **undefined** - -**{ preset breakpoint | 'media query': imageProps }**: - -preset breakpoints: **xs, sm, md, lg, xl** ([can be changed with](#presets)) -imageProps: **{ w, h, r, src }** where - -* **w** - width, -* **h** - height, -* **r** - ratio, -* **src** - original image hosted on your web server. You can use absolute path or relative to the baseURL in your config. - -```jsx - -``` - -You can drop some breakpoints, for example: - -```jsx - -``` - -##### new experimental syntax - -md: { w: '40vw', h: 350 } or md: { w: 250, h: '20vh' } - -adds possibility to use fixed height or width and change dynamically other dimension - -**NOTE:** if size is not set, the plugin uses a special algorithm to -detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. - -### ci-not-lazy (or data-ci-not-lazy) - -###### Type: **Bool** - -Switch off lazyload per image. - -## Lazy Loading - -Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. - -The example below uses [lazysizes](https://github.com/aFarkas/lazysizes) -library using Intersection Observer API. - -[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) - -add the following scripts right after js-cloudimage-responsive script - -```javascript - - - -``` - -the initialization script - -```javascript - - ``` - -## Process dynamically loaded images! - -In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. - -```javascript - -``` - -## Examples & workarounds -* [See all](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md) -* [Cropping](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md#cropping) - -## Browser support - -Tested in all modern browsers and IE 11,10,9. - -If you want to address the use case where your visitors disable JS. You have to add noscript tag. - -```html - -``` - -NOTE: If you use lazy loading with IntersectionObserver, you must -manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) -for cross-browser support. - -## Filerobot UI Familiy - -* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) -* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) -* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) -* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) -* [Uploader](https://github.com/scaleflex/filerobot-uploader) - -## Contributing! - -All contributions are super welcome! - - -## License -JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT) - +[![Release](https://img.shields.io/badge/release-v4.8.3-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) +[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) +[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) + +## VERSIONS + +* [__Low Quality Preview__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README.md) +* [__Blur Hash__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-BLUR-HASH.md) +* __Plain (CSS free)__ + +

+ The Lounge +

+ +

+ JS Cloudimage Responsive | Cloudimage v7 +

+ +

+ (plain version) +

+ +

+ + Docs + • + Demo + • + Code Sandbox + • + Why? + +

+ +This plugin detects the width of any image container as well as the device pixel ratio +density to load the optimal image size needed. +Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive +automated image optimization service. + +When an image is first loaded on your website or mobile app, +Cloudimage's resizing servers will download the origin image from +the source, resize it for the client's screen size and deliver to your users through one or multiple +Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. + +**NOTE:** Your original (master) images should be stored on a server +or storage bucket (S3, Google Cloud, Azure Blob...) reachable over +HTTP or HTTPS by Cloudimage. If you want to upload your master images to +Cloudimage, contact us at +[hello@cloudimage.io](mailto:hello@cloudimage.io). + +

+ The Lounge +

+ +powered by [Cloudimage](https://www.cloudimage.io/) +([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) + +## Table of contents + +* [Demo](#demo) +* [Requirements](#requirements) +* [Step 1: Installation](#installation) +* [Step 2: Initialize](#initialize) +* [Step 3: Implement](#implement) +* [Step 4: Prevent seeing broken images](#prevent_styles) +* [Configuration](#configuration) +* [Image properties](#image_properties) +* [Lazy loading](#lazy_loading) +* [Process dynamically loaded images](#dynamically-loaded) +* [Examples & workarounds](#examples_workarounds) +* [Browser support](#browser_support) +* [Filerobot UI Family](#ui_family) +* [Contributing](#contributing) +* [License](#license) + + +## Demo + +To see the Cloudimage Responsive plugin in action, please check out the +[Demo page](https://scaleflex.airstore.io/plugins/js-cloudimage-responsive/demo/plain/index.html?v=28.04.2020). +Play with your browser's window size and observe your +Inspector's Network tab to see how Cloudimage delivers the optimal +image size to your browser, hence accelerating the overall page +loading time. + +## Requirements + +### Cloudimage account + +To use the Cloudimage Responsive plugin, you will need a +Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by +registering [here](https://www.cloudimage.io/en/register_page). +Once your token is created, you can configure it as described below. +This token allows you to use 25GB of image cache and 25GB of worldwide +CDN traffic per month for free. + +### Layout/CSS + +This version of plugin doesn't add any CSS or change layout thus no effect on your layout and styling. + +## Step 1: Installation + +Add script tag with CDN link to js-cloudimage-responsive + +```javascript + +``` + +or using npm + +``` +$ npm install --save js-cloudimage-responsive +``` + +## Step 2: Initialize + +After adding the js-cloudimage-responsive lib, simply iniatialize it with your **token** and the **baseURL** of your image storage: + +```javascript + +``` + +or in new style with npm: + +```javascript + +import 'js-cloudimage-responsive/plain'; + +const ciResponsive = new window.CIResponsive({ + token: 'demo', + baseURL: 'https://cloudimage.public.airstore.io/demo/' // optional +}); +``` + +**NOTE**: You should put the scripts below all your content in the body tag and above all other scripts. After inserting the scripts the plugin starts immediately processing all images with ci-src, ci-bg-url attributes. (If the scripts are put into the head tag, no images will be detected and processed. If the scripts are put below all other scripts on your page, the images will be not showed until all the scripts downloaded.) + +## Step 3: Implement in img tag or use it as background image + +### img tag + +Finally, just use the `ci-src` instead of the `src` attribute in image tag: + +```html + +``` + +edeit in codesandbox + +### background image + +Use the `ci-bg-url` instead of CSS background-image property `background-image: url(...)`: + +```html +
+``` + +edeit in codesandbox + +## Step 4: Prevent seeing broken images + +Add the following styles in the head of your site + +```html + +``` + +## Config + +### token + +###### Type: **String** | Default: **"demo"** | _required_ + +Your Cloudimage customer token. +[Subscribe](https://www.cloudimage.io/en/register_page) for a +Cloudimage account to get one. The subscription takes less than a +minute and is totally free. + +### domain + +###### Type: **String** | Default: **"cloudimg.io"** + +Use your custom domain. + +### imgSelector + +###### Type: **String** | Default: **"ci-src"** + +Cloudimage Responsive Selector for images. + +### bgSelector + +###### Type: **String** | Default: **"ci-bg-url"** + +Cloudimage Responsive Selector for background images. + +### doNotReplaceURL + +###### Type: **bool** | Default: **false** + +If set to **true** the plugin will only add query params to the given source of image. + +### baseURL + +###### Type: **String** | _optional_ + +Your image folder on server, this alows to shorten your origin image URLs. + +### apiVersion + +###### Type: **String** |Default: **'v7'** | _optional_ +Allow to use a specific version of API. + +- set a specific version of API +```javascript + +``` +- disable API version +```javascript + +``` + +### lazyLoading + +###### Type: **Bool** | Default: **false** | _optional_ + +Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) + +### params + +###### Type: **String** | Default: **'org_if_sml=1'** | _optional_ + +Applies default Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... +Multiple params can be applied, separated by "```&```" e.g. wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1 + +```javascript +{ + ..., + params: 'org_if_sml=1' +} +``` + +#### alternative syntax: type: **Object** + +```javascript +{ + ..., + params: { + org_if_sml: 1, + grey: 1, + ... + } +} +``` + +[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) + +### exactSize + +###### Type: **Bool** | Default: **false** | _optional_ + +Forces to load exact size of images. +By default the plugin rounds container width to next possible value which can be divided by 100 without the remainder. +It’s done for cache reasons so that not all images are cached by 1px, but only 100px, 200px, 300px … + +### limitFactor + +###### Type: **Number** | Default: **100** | _optional_ + +Rounds up size of an image to nearest limitFactor value. + +For example +* for an image with width **358px** and limitFactor equals **100** the plugin will round up to 400px +* for an image with width **358px** and limitFactor equals **5** the plugin will round up to 360px + +### devicePixelRatioList + +###### Type: **[Number,...]** | Default: **[1, 1.5, 2, 3, 4]** | _optional_ + +List of supported device pixel ratios. If there is no need to support retina devices, you should set empty array `devicePixelRatioList: []` + +### presets + +###### Type: **Object** + +Default: + +```javascript + + +``` + +Breakpoints shortcuts to use in image size property, can be overwridden. + +### imageSizeAttributes + +###### Type: **String** | possible values: 'use', 'ignore', 'take-ratio' | Default: **'use'** + +If width and height attributes are set: + +**use** - width & height attributes values will be used to calculate image size (according to user's DPR) and **ratio**. + +**take-ratio** - width & height attributes values will be used only to calculate **ratio**. + +**ignore** - width & height attributes will be ignored. + +If width and height attributes are NOT set, image container size will be detected to calculate result image size (according to user's DPR) + +*Note*: If only width or height attributes is set, ratio is going to be taken from ci-ratio image attribute + +## Image properties + +Cloudimage responsive plugin will make image on your page responsive if you replace the `src` with `ci-src` attribute in the `` tag: + +### ci-src + +###### Type: **String** | Default: **undefined** | _required_ + +Original image hosted on your web server. You can use absolute path or +relative to baseURL in your config. + +**NOTES:** +* The plugin uses a special algorithm to detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. +* Images where `ci-src` is not used will be delivered in a standard, non-responsive way. +* Parameters after "?" question mark will be added at the end of result URL after processing by the plugin. + +### width + +###### Type: **String** (e.g. 300px, 20vw) | Default: **undefined** + +If it's set the plugin will use width as fixed value and change only according device pixel ratio. + +### height + +###### Type: **String** (e.g. 300px, 20vh) | Default: **undefined** + +If it's set the plugin will use height as fixed value and change only according device pixel ratio. + +### ci-params + +###### Type: **String** | Default: **undefined** | _optional_ + +You can apply any Cloudimage operations/ filters to your image, e.g. brightness, contrast, rotation... +Multiple params can be applied, separated by "```&```" e.g. **wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1** + +```javascript +ci-params="gray=1&bright=10" +``` + +#### alternative syntax: type: **Object** + +```javascript +ci-params="{ + bright: 10, + grey: 1, + ... +}" +``` + +[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) + +### ci-sizes + +###### Type: **Object** | Default: **undefined** + +**{ preset breakpoint | 'media query': imageProps }**: + +preset breakpoints: **xs, sm, md, lg, xl** ([can be changed with](#presets)) +imageProps: **{ w, h, r, src }** where + +* **w** - width, +* **h** - height, +* **r** - ratio, +* **src** - original image hosted on your web server. You can use absolute path or relative to the baseURL in your config. + +```jsx + +``` + +You can drop some breakpoints, for example: + +```jsx + +``` + +##### new experimental syntax + +md: { w: '40vw', h: 350 } or md: { w: 250, h: '20vh' } + +adds possibility to use fixed height or width and change dynamically other dimension + +**NOTE:** if size is not set, the plugin uses a special algorithm to +detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. + +### ci-not-lazy (or data-ci-not-lazy) + +###### Type: **Bool** + +Switch off lazyload per image. + +## Lazy Loading + +Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. + +The example below uses [lazysizes](https://github.com/aFarkas/lazysizes) +library using Intersection Observer API. + +[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) + +add the following scripts right after js-cloudimage-responsive script + +```javascript + + + +``` + +the initialization script + +```javascript + + ``` + +## Process dynamically loaded images! + +In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. + +```javascript + +``` + +## Examples & workarounds +* [See all](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md) +* [Cropping](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md#cropping) + +## Browser support + +Tested in all modern browsers and IE 11,10,9. + +If you want to address the use case where your visitors disable JS. You have to add noscript tag. + +```html + +``` + +NOTE: If you use lazy loading with IntersectionObserver, you must +manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) +for cross-browser support. + +## Filerobot UI Familiy + +* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) +* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) +* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) +* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) +* [Uploader](https://github.com/scaleflex/filerobot-uploader) + +## Contributing! + +All contributions are super welcome! + + +## License +JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT) + diff --git a/README.md b/README.md index 094570e..b11a127 100644 --- a/README.md +++ b/README.md @@ -1,574 +1,574 @@ -[![Release](https://img.shields.io/badge/release-v4.8.2-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) -[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) -[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) - -## VERSIONS - -* __Low Quality Preview__ -* [__Blur Hash__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-BLUR-HASH.md) -* [__Plain (CSS free)__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-PLAIN.md) - -

- The Lounge -

- -

- JS Cloudimage Responsive | Cloudimage v7 -

- -

- (low quality image placeholder) -

- -[Documentation for v2 | Cloudimage v6](https://github.com/scaleflex/js-cloudimage-responsive/blob/v7/README_v6.md) - -

- - Docs - • - Demo - • - Code Sandbox - • - Why? - -

- -This plugin detects the width of any image container as well as the device pixel ratio -density to load the optimal image size needed. -Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive -automated image optimization service. - -When an image is first loaded on your website or mobile app, -Cloudimage's resizing servers will download the origin image from -the source, resize it for the client's screen size and deliver to your users through one or multiple -Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. - -**NOTE:** Your original (master) images have to be stored on a server -or storage bucket (S3, Google Cloud, Azure Blob...) reachable over -HTTP or HTTPS by Cloudimage. If you want to store your master images with us, -you can check our all-in-one Digital Asset Management solution -[Filerobot](https://www.filerobot.com/). - -

- The Lounge -

- -powered by [Cloudimage](https://www.cloudimage.io/) -([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) - -## Table of contents - -* [Demo](#demo) -* [Requirements](#requirements) -* [Step 1: Installation](#installation) -* [Step 2: Initialize](#initialize) -* [Step 3: Implement](#implement) -* [Configuration](#configuration) -* [Image properties](#image_properties) -* [Lazy loading](#lazy_loading) -* [Process dynamically loaded images](#dynamically-loaded) -* [Examples & workarounds](#examples_workarounds) -* [Browser support](#browser_support) -* [Filerobot UI Family](#ui_family) -* [Contributing](#contributing) -* [License](#license) - - -## Demo - -To see the Cloudimage Responsive plugin in action, please check out the -[Demo page](https://scaleflex.github.io/js-cloudimage-responsive/). -Play with your browser's window size and observe your -Inspector's Network tab to see how Cloudimage delivers the optimal -image size to your browser, hence accelerating the overall page -loading time. - -## Requirements - -### Cloudimage account - -To use the Cloudimage Responsive plugin, you will need a -Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by -registering [here](https://www.cloudimage.io/en/register_page). -Once your token is created, you can configure it as described below. -This token allows you to use 25GB of image cache and 25GB of worldwide -CDN traffic per month for free. - -### Layout/CSS - -In order to use smooth transition between preview image and good quality and size image, the plugin uses absolute positioning for images and wraps an image tag with a div element with relative positioning. - -You have to pay attention to the following things: - -- the plugin sets 100% width for the img tag and position:absolute (You should not apply other sizes or change position property. If you need to change the width of an image or its position, you have to set it to the wrapper element) - -## Step 1: Installation - -Add a style tag with CDN link to js-cloudimage-responsive in the head of your site - -```javascript - -``` - -Add a script tag with CDN link to js-cloudimage-responsive - -```javascript - -``` - -or using npm - -``` -$ npm install --save js-cloudimage-responsive -``` - -## Step 2: Initialize - -After adding the js-cloudimage-responsive library, simply iniatialise it with your **token** and the **baseURL** of your image storage: - -```javascript - -``` - -or in new style with npm: - -```javascript - -import 'js-cloudimage-responsive'; - -const ciResponsive = new window.CIResponsive({ - token: 'demo', - baseURL: 'https://cloudimage.public.airstore.io/demo/' // optional -}); -``` - -**NOTE**: You should put the scripts below all your content in the body tag and above all other scripts. After inserting the scripts, the plugin immediately starts processing all images with ci-src and ci-bg-url attributes. (If the scripts are put into the head tag, no images will be detected and processed. If the scripts are put below all other scripts on your page, the images will be not showed until all the scripts are downloaded.) - -## Step 3: Implement in an img tag or use it as a background image - -### img tag - -Finally, just use `ci-src` instead of the `src` attribute in image tag: - -```html - -``` - -NOTE: setting "ci-ratio" is recommended to prevent page layout jumping. The parameter is used to calculate the image height to hold the image position while the image is loading. - -edit in codesandbox - -### background image - -Use `ci-bg-url` instead of the CSS background-image property `background-image: url(...)`: - -```html -
-``` - -edit in codesandbox - -## Configuration - -### token - -###### Type: **String** | Default: **"demo"** | _required_ - -Your Cloudimage customer token. -[Subscribe](https://www.cloudimage.io/en/register_page) for a -Cloudimage account to get one. The subscription takes less than a -minute and is totally free. - -### domain - -###### Type: **String** | Default: **"cloudimg.io"** - -Use your custom domain. - -### imgSelector - -###### Type: **String** | Default: **"ci-src"** - -Cloudimage Responsive Selector for images. - -### bgSelector - -###### Type: **String** | Default: **"ci-bg-url"** - -Cloudimage Responsive Selector for background images. - -### doNotReplaceURL - -###### Type: **bool** | Default: **false** - -If set to **true**, the plugin will only add query parameters to the provided image source URL. - -### baseURL - -###### Type: **String** | _optional_ - -Your image folder on server; this alows to shorten your origin image URLs. - -### apiVersion - -###### Type: **String** |Default: **'v7'** | _optional_ -Allow to use a specific version of API. - -- set a specific version of API -```javascript - -``` -- disable API version -```javascript - -``` - - -### lazyLoading - -###### Type: **Bool** | Default: **false** | _optional_ - -Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) - -### params - -###### Type: **String** | Default: **'org_if_sml=1'** | _optional_ - -Applies default Cloudimage operations/filters to your image like brightness, contrast, rotation, etc. -Multiple params can be applied, separated by "```&```" e.g. wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1 - -```javascript -{ - ..., - params: 'org_if_sml=1' -} -``` - -#### alternative syntax - -###### Type: **Object** - -```javascript -{ - ..., - params: { - org_if_sml: 1, - grey: 1, - ... - } -} -``` - -[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) - - -### placeholderBackground - -###### Type: **String** | Default: **'#f4f4f4'** | _optional_ - -Placeholder coloured background while the image is loading - - -### exactSize - -###### Type: **Bool** | Default: **false** | _optional_ - -Forces to load exact size of images. -By default, the plugin rounds the container width to next possible value which can be divided by 100 without the remainder. -This is done for caching reasons so that not all images are cached by 1px, but only 100px, 200px, 300px... - -### limitFactor - -###### Type: **Number** | Default: **100** | _optional_ - -Rounds up the size of the image to the nearest limitFactor value. - -For example: -* for an image with width **358px** and limitFactor equal to **100**, the plugin will round up to 400px; -* for an image with width **358px** and limitFactor equal to **5**, the plugin will round up to 360px. - - -### devicePixelRatioList - -###### Type: **[Number,...]** | Default: **[1, 1.5, 2, 3, 4]** | _optional_ - -List of supported device pixel ratios. If there is no need to support retina devices, you should set empty array `devicePixelRatioList: []` - - -### lowQualityPreview - -###### Type: **Object** - -* `lowQualityPreview.minImgWidth` number (default: 400) - minimal width of an image to load a low-quality preview image - -Example: - -```javascript -lowQualityPreview: { - minImgWidth = 400 -} -``` - -### presets - -###### Type: **Object** - -Default: - -```javascript -{ - ..., - presets: { - xs: '(max-width: 575px)', // up to 575 PHONE - sm: '(min-width: 576px)', // 576 - 767 PHABLET - md: '(min-width: 768px)', // 768 - 991 TABLET - lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN - xl: '(min-width: 1200px)' // from 1200 USUALSCREEN - } -} -``` - -Breakpoints shortcuts to use in image size property, can be overridden. - -### imageSizeAttributes - -###### Type: **String** | possible values: 'use', 'ignore', 'take-ratio' | Default: **'use'** - -If width and height attributes are set: - -**use** - width & height attributes values will be used to calculate image size (according to user's DPR) and **ratio**. - -**take-ratio** - width & height attributes values will be used only to calculate **ratio**. - -**ignore** - width & height attributes will be ignored. - -If width and height attributes are NOT set, image container size will be detected to calculate result image size (according to user's DPR) - -*Note*: If only width or height attributes is set, ratio is going to be taken from ci-ratio image attribute - -## Image properties - -The Cloudimage responsive plugin will make an image on your page responsive if you replace the `src` with a `ci-src` attribute in the `` tag: - -### ci-src - -###### Type: **String** | Default: **undefined** | _required_ - -Original image hosted on your web server. You can use absolute path or -relative to the baseURL in your config. - -**NOTES:** -* The plugin uses a special algorithm to detect the width of the image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. -* Images where `ci-src` is not used will be delivered in a standard, non-responsive way. -* Parameters after "?" question mark will be added at the end of result URL after processing by the plugin. - -### width - -###### Type: **String** (e.g. 300px, 20vw) | Default: **undefined** - -If it's set, the plugin will use width as a fixed value and change only according device pixel ratio. - -### height - -###### Type: **String** (e.g. 300px, 20vh) | Default: **undefined** - -If it's set, the plugin will use height as fixed value and change only according device pixel ratio. - -### ci-params - -###### Type: **String** | Default: **undefined** | _optional_ - -You can apply any Cloudimage operations/filters to your image, e.g. brightness, contrast, rotation... -Multiple parameters can be applied, separated by "```&```" e.g. **wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1** - -```javascript -ci-params="gray=1&bright=10" -``` - -#### alternative syntax: type: **Object** - -```javascript -ci-params="{ - bright: 10, - grey: 1, - ... -}" -``` - -[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) - -### ci-sizes - -###### Type: **Object** | Default: **undefined** - -**{ preset breakpoint | 'media query': imageProps }**: - -preset breakpoints: **xs, sm, md, lg, xl** ([can be changed with](#presets)) -imageProps: **{ w, h, r, src }** where - -* **w** - width, -* **h** - height, -* **r** - ratio, -* **src** - original image hosted on your web server. You can use absolute path or relative to the baseURL in your config. - -```jsx - -``` - -You can drop some breakpoints, for example: - -```jsx - -``` - -##### new experimental syntax - -md: { w: '40vw', h: 350 } or md: { w: 250, h: '20vh' } - -adds a possibility to use fixed height or width and change the other dimension dynamically - -**NOTE:** if size is not set, the plugin uses a special algorithm to -detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. - -### ci-ratio (or data-ci-ratio) - -###### Type: **Number** | _optional_ - -It is recommended to set this parameter to prevent page layout jumping. It is used to calculate the image height to hold the image position while the image is loading. - -To see the full Cloudimage documentation, [click here](https://docs.cloudimage.io/go/cloudimage-documentation). - -### ci-not-lazy (or data-ci-not-lazy) - -###### Type: **Bool** - -Switch off lazy loading on a per-image basis. - -## Lazy Loading - -Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. - -The example below uses the [lazysizes](https://github.com/aFarkas/lazysizes) -library using Intersection Observer API. - -[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) - -add the following scripts right after js-cloudimage-responsive script - -```javascript - - - -``` - -the initialization script - -```javascript - - ``` - -## Process dynamically loaded images! - -In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. - -```javascript - -``` - -The process function accepts a second argument. It expects an [HTML Element](https://developer.mozilla.org/fr/docs/Web/API/HTMLElement), if provided it will be used as the root for the images lookup. - -## Examples & workarounds -* [See all](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md) -* [Cropping](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md#cropping) - - -## Browser support - -Tested in all modern browsers and IE 11,10,9. - -If you want to address the use case where your visitors disable JS, You have to add a noscript tag: - -```html - -``` - -NOTE: If you use lazy loading with IntersectionObserver, you must -manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) -for cross-browser support. - -## Filerobot UI Familiy - -* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) -* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) -* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) -* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) -* [Uploader](https://github.com/scaleflex/filerobot-uploader) - -## Contributing! - -All contributions are super welcome! - - -## License -JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT). - +[![Release](https://img.shields.io/badge/release-v4.8.3-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) +[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) +[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) + +## VERSIONS + +* __Low Quality Preview__ +* [__Blur Hash__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-BLUR-HASH.md) +* [__Plain (CSS free)__](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/README-PLAIN.md) + +

+ The Lounge +

+ +

+ JS Cloudimage Responsive | Cloudimage v7 +

+ +

+ (low quality image placeholder) +

+ +[Documentation for v2 | Cloudimage v6](https://github.com/scaleflex/js-cloudimage-responsive/blob/v7/README_v6.md) + +

+ + Docs + • + Demo + • + Code Sandbox + • + Why? + +

+ +This plugin detects the width of any image container as well as the device pixel ratio +density to load the optimal image size needed. +Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive +automated image optimization service. + +When an image is first loaded on your website or mobile app, +Cloudimage's resizing servers will download the origin image from +the source, resize it for the client's screen size and deliver to your users through one or multiple +Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. + +**NOTE:** Your original (master) images have to be stored on a server +or storage bucket (S3, Google Cloud, Azure Blob...) reachable over +HTTP or HTTPS by Cloudimage. If you want to store your master images with us, +you can check our all-in-one Digital Asset Management solution +[Filerobot](https://www.filerobot.com/). + +

+ The Lounge +

+ +powered by [Cloudimage](https://www.cloudimage.io/) +([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) + +## Table of contents + +* [Demo](#demo) +* [Requirements](#requirements) +* [Step 1: Installation](#installation) +* [Step 2: Initialize](#initialize) +* [Step 3: Implement](#implement) +* [Configuration](#configuration) +* [Image properties](#image_properties) +* [Lazy loading](#lazy_loading) +* [Process dynamically loaded images](#dynamically-loaded) +* [Examples & workarounds](#examples_workarounds) +* [Browser support](#browser_support) +* [Filerobot UI Family](#ui_family) +* [Contributing](#contributing) +* [License](#license) + + +## Demo + +To see the Cloudimage Responsive plugin in action, please check out the +[Demo page](https://scaleflex.github.io/js-cloudimage-responsive/). +Play with your browser's window size and observe your +Inspector's Network tab to see how Cloudimage delivers the optimal +image size to your browser, hence accelerating the overall page +loading time. + +## Requirements + +### Cloudimage account + +To use the Cloudimage Responsive plugin, you will need a +Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by +registering [here](https://www.cloudimage.io/en/register_page). +Once your token is created, you can configure it as described below. +This token allows you to use 25GB of image cache and 25GB of worldwide +CDN traffic per month for free. + +### Layout/CSS + +In order to use smooth transition between preview image and good quality and size image, the plugin uses absolute positioning for images and wraps an image tag with a div element with relative positioning. + +You have to pay attention to the following things: + +- the plugin sets 100% width for the img tag and position:absolute (You should not apply other sizes or change position property. If you need to change the width of an image or its position, you have to set it to the wrapper element) + +## Step 1: Installation + +Add a style tag with CDN link to js-cloudimage-responsive in the head of your site + +```javascript + +``` + +Add a script tag with CDN link to js-cloudimage-responsive + +```javascript + +``` + +or using npm + +``` +$ npm install --save js-cloudimage-responsive +``` + +## Step 2: Initialize + +After adding the js-cloudimage-responsive library, simply iniatialise it with your **token** and the **baseURL** of your image storage: + +```javascript + +``` + +or in new style with npm: + +```javascript + +import 'js-cloudimage-responsive'; + +const ciResponsive = new window.CIResponsive({ + token: 'demo', + baseURL: 'https://cloudimage.public.airstore.io/demo/' // optional +}); +``` + +**NOTE**: You should put the scripts below all your content in the body tag and above all other scripts. After inserting the scripts, the plugin immediately starts processing all images with ci-src and ci-bg-url attributes. (If the scripts are put into the head tag, no images will be detected and processed. If the scripts are put below all other scripts on your page, the images will be not showed until all the scripts are downloaded.) + +## Step 3: Implement in an img tag or use it as a background image + +### img tag + +Finally, just use `ci-src` instead of the `src` attribute in image tag: + +```html + +``` + +NOTE: setting "ci-ratio" is recommended to prevent page layout jumping. The parameter is used to calculate the image height to hold the image position while the image is loading. + +edit in codesandbox + +### background image + +Use `ci-bg-url` instead of the CSS background-image property `background-image: url(...)`: + +```html +
+``` + +edit in codesandbox + +## Configuration + +### token + +###### Type: **String** | Default: **"demo"** | _required_ + +Your Cloudimage customer token. +[Subscribe](https://www.cloudimage.io/en/register_page) for a +Cloudimage account to get one. The subscription takes less than a +minute and is totally free. + +### domain + +###### Type: **String** | Default: **"cloudimg.io"** + +Use your custom domain. + +### imgSelector + +###### Type: **String** | Default: **"ci-src"** + +Cloudimage Responsive Selector for images. + +### bgSelector + +###### Type: **String** | Default: **"ci-bg-url"** + +Cloudimage Responsive Selector for background images. + +### doNotReplaceURL + +###### Type: **bool** | Default: **false** + +If set to **true**, the plugin will only add query parameters to the provided image source URL. + +### baseURL + +###### Type: **String** | _optional_ + +Your image folder on server; this alows to shorten your origin image URLs. + +### apiVersion + +###### Type: **String** |Default: **'v7'** | _optional_ +Allow to use a specific version of API. + +- set a specific version of API +```javascript + +``` +- disable API version +```javascript + +``` + + +### lazyLoading + +###### Type: **Bool** | Default: **false** | _optional_ + +Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) + +### params + +###### Type: **String** | Default: **'org_if_sml=1'** | _optional_ + +Applies default Cloudimage operations/filters to your image like brightness, contrast, rotation, etc. +Multiple params can be applied, separated by "```&```" e.g. wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1 + +```javascript +{ + ..., + params: 'org_if_sml=1' +} +``` + +#### alternative syntax + +###### Type: **Object** + +```javascript +{ + ..., + params: { + org_if_sml: 1, + grey: 1, + ... + } +} +``` + +[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) + + +### placeholderBackground + +###### Type: **String** | Default: **'#f4f4f4'** | _optional_ + +Placeholder coloured background while the image is loading + + +### exactSize + +###### Type: **Bool** | Default: **false** | _optional_ + +Forces to load exact size of images. +By default, the plugin rounds the container width to next possible value which can be divided by 100 without the remainder. +This is done for caching reasons so that not all images are cached by 1px, but only 100px, 200px, 300px... + +### limitFactor + +###### Type: **Number** | Default: **100** | _optional_ + +Rounds up the size of the image to the nearest limitFactor value. + +For example: +* for an image with width **358px** and limitFactor equal to **100**, the plugin will round up to 400px; +* for an image with width **358px** and limitFactor equal to **5**, the plugin will round up to 360px. + + +### devicePixelRatioList + +###### Type: **[Number,...]** | Default: **[1, 1.5, 2, 3, 4]** | _optional_ + +List of supported device pixel ratios. If there is no need to support retina devices, you should set empty array `devicePixelRatioList: []` + + +### lowQualityPreview + +###### Type: **Object** + +* `lowQualityPreview.minImgWidth` number (default: 400) - minimal width of an image to load a low-quality preview image + +Example: + +```javascript +lowQualityPreview: { + minImgWidth = 400 +} +``` + +### presets + +###### Type: **Object** + +Default: + +```javascript +{ + ..., + presets: { + xs: '(max-width: 575px)', // up to 575 PHONE + sm: '(min-width: 576px)', // 576 - 767 PHABLET + md: '(min-width: 768px)', // 768 - 991 TABLET + lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN + xl: '(min-width: 1200px)' // from 1200 USUALSCREEN + } +} +``` + +Breakpoints shortcuts to use in image size property, can be overridden. + +### imageSizeAttributes + +###### Type: **String** | possible values: 'use', 'ignore', 'take-ratio' | Default: **'use'** + +If width and height attributes are set: + +**use** - width & height attributes values will be used to calculate image size (according to user's DPR) and **ratio**. + +**take-ratio** - width & height attributes values will be used only to calculate **ratio**. + +**ignore** - width & height attributes will be ignored. + +If width and height attributes are NOT set, image container size will be detected to calculate result image size (according to user's DPR) + +*Note*: If only width or height attributes is set, ratio is going to be taken from ci-ratio image attribute + +## Image properties + +The Cloudimage responsive plugin will make an image on your page responsive if you replace the `src` with a `ci-src` attribute in the `` tag: + +### ci-src + +###### Type: **String** | Default: **undefined** | _required_ + +Original image hosted on your web server. You can use absolute path or +relative to the baseURL in your config. + +**NOTES:** +* The plugin uses a special algorithm to detect the width of the image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. +* Images where `ci-src` is not used will be delivered in a standard, non-responsive way. +* Parameters after "?" question mark will be added at the end of result URL after processing by the plugin. + +### width + +###### Type: **String** (e.g. 300px, 20vw) | Default: **undefined** + +If it's set, the plugin will use width as a fixed value and change only according device pixel ratio. + +### height + +###### Type: **String** (e.g. 300px, 20vh) | Default: **undefined** + +If it's set, the plugin will use height as fixed value and change only according device pixel ratio. + +### ci-params + +###### Type: **String** | Default: **undefined** | _optional_ + +You can apply any Cloudimage operations/filters to your image, e.g. brightness, contrast, rotation... +Multiple parameters can be applied, separated by "```&```" e.g. **wat_scale=35&wat_gravity=northeast&wat_pad=10&grey=1** + +```javascript +ci-params="gray=1&bright=10" +``` + +#### alternative syntax: type: **Object** + +```javascript +ci-params="{ + bright: 10, + grey: 1, + ... +}" +``` + +[Full cloudimage v7 documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction) + +### ci-sizes + +###### Type: **Object** | Default: **undefined** + +**{ preset breakpoint | 'media query': imageProps }**: + +preset breakpoints: **xs, sm, md, lg, xl** ([can be changed with](#presets)) +imageProps: **{ w, h, r, src }** where + +* **w** - width, +* **h** - height, +* **r** - ratio, +* **src** - original image hosted on your web server. You can use absolute path or relative to the baseURL in your config. + +```jsx + +``` + +You can drop some breakpoints, for example: + +```jsx + +``` + +##### new experimental syntax + +md: { w: '40vw', h: 350 } or md: { w: 250, h: '20vh' } + +adds a possibility to use fixed height or width and change the other dimension dynamically + +**NOTE:** if size is not set, the plugin uses a special algorithm to +detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. + +### ci-ratio (or data-ci-ratio) + +###### Type: **Number** | _optional_ + +It is recommended to set this parameter to prevent page layout jumping. It is used to calculate the image height to hold the image position while the image is loading. + +To see the full Cloudimage documentation, [click here](https://docs.cloudimage.io/go/cloudimage-documentation). + +### ci-not-lazy (or data-ci-not-lazy) + +###### Type: **Bool** + +Switch off lazy loading on a per-image basis. + +## Lazy Loading + +Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. + +The example below uses the [lazysizes](https://github.com/aFarkas/lazysizes) +library using Intersection Observer API. + +[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) + +add the following scripts right after js-cloudimage-responsive script + +```javascript + + + +``` + +the initialization script + +```javascript + + ``` + +## Process dynamically loaded images! + +In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. + +```javascript + +``` + +The process function accepts a second argument. It expects an [HTML Element](https://developer.mozilla.org/fr/docs/Web/API/HTMLElement), if provided it will be used as the root for the images lookup. + +## Examples & workarounds +* [See all](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md) +* [Cropping](https://github.com/scaleflex/js-cloudimage-responsive/blob/master/examples/EXAMPLES.md#cropping) + + +## Browser support + +Tested in all modern browsers and IE 11,10,9. + +If you want to address the use case where your visitors disable JS, You have to add a noscript tag: + +```html + +``` + +NOTE: If you use lazy loading with IntersectionObserver, you must +manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) +for cross-browser support. + +## Filerobot UI Familiy + +* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) +* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) +* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) +* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) +* [Uploader](https://github.com/scaleflex/filerobot-uploader) + +## Contributing! + +All contributions are super welcome! + + +## License +JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT). + diff --git a/README_v6.md b/README_v6.md index e432911..866b9dd 100644 --- a/README_v6.md +++ b/README_v6.md @@ -1,469 +1,469 @@ -[![Release](https://img.shields.io/badge/release-v2.3.0-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) -[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) -[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) - -

- The Lounge -

- -

- JS Cloudimage Responsive | Cloudimage v6 -

- -

- - Docs - • - Demo - • - Code Sandbox - • - Why? - -

- -This plugin detects the width of any image container as well as the device pixel ratio -density to load the optimal image size needed. -Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive -automated image optimization service. - -When an image is first loaded on your website or mobile app, -Cloudimage's resizing servers will download the origin image from -the source, resize it for the client's screen size and deliver to your users through one or multiple -Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. - -**NOTE:** Your original (master) images should be stored on a server -or storage bucket (S3, Google Cloud, Azure Blob...) reachable over -HTTP or HTTPS by Cloudimage. If you want to upload your master images to -Cloudimage, contact us at -[hello@cloudimage.io](mailto:hello@cloudimage.io). - -

- The Lounge -

- -powered by [Cloudimage](https://www.cloudimage.io/) -([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) - -## Table of contents - -* [Demo](#demo) -* [Requirements](#requirements) -* [Step 1: Installation](#installation) -* [Step 2: Initialize](#initialize) -* [Step 3: Implement](#implement) -* [Configuration](#configuration) -* [Image properties](#image_properties) -* [Lazy loading](#lazy_loading) -* [Process dynamically loaded images](#dynamically-loaded) -* [Browser support](#browser_support) -* [Filerobot UI Family](#ui_family) -* [Contributing](#contributing) -* [License](#license) - - -## Demo - -To see the Cloudimage Responsive plugin in action, please check out the -[Demo page](https://scaleflex.github.io/js-cloudimage-responsive/). -Play with your browser's window size and observe your -Inspector's Network tab to see how Cloudimage delivers the optimal -image size to your browser, hence accelerating the overall page -loading time. - -## Requirements - -To use the Cloudimage Responsive plugin, you will need a -Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by -registering [here](https://www.cloudimage.io/en/register_page). -Once your token is created, you can configure it as described below. -This token allows you to use 25GB of image cache and 25GB of worldwide -CDN traffic per month for free. - -## Step 1: Installation - -Add script tag with CDN link to js-cloudimage-responsive - -```javascript - -``` - -You may also use major version number instead of fixed version to have the latest version available. - -```javascript - -``` - -or using npm - -``` -$ npm install --save js-cloudimage-responsive -``` - -## Step 2: Initialize - -After adding the js-cloudimage-responsive lib, simply iniatialize it with your **token** and the **baseUrl** of your image storage: - -```javascript - -``` - -or in new style with npm: - -```javascript - -import 'js-cloudimage-responsive'; - -const ciResponsive = new window.CIResponsive({ - token: 'demo', - baseUrl: 'https://cloudimage.public.airstore.io/demo/' // optional -}); -``` - -## Step 3: Implement in img tag or use it as background image - -### img tag - -Finally, just use the `ci-src` instead of the `src` attribute in image tag: - -```html - -``` - -NOTE: "ratio" is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. - -edeit in codesandbox - -### background image - -Use the `ci-bg` instead of CSS background-image property `background-image: url(...)`: - -```html -
-``` - -edeit in codesandbox - -## Config - -### token - -###### Type: **String** | Default: **"demo"** | _required_ - -Your Cloudimage customer token. -[Subscribe](https://www.cloudimage.io/en/register_page) for a -Cloudimage account to get one. The subscription takes less than a -minute and is totally free. - -### domain - -###### Type: **String** | Default: **"cloudimg.io"** - -Use your custom domain. - -### baseUrl - -###### Type: **String** | Default: **"/"** | _optional_ - -Your image folder on server, this alows to shorten your origin image URLs. - -### lazyLoading - -###### Type: **Bool** | Default: **false** | _optional_ - -Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) - -### imgLoadingAnimation - -###### Type: **Bool** | Default: **true** | _optional_ - -Applies a nice interlacing effect for preview transition - -### filters - -###### Type: **String** | Default: **'foil1'** | _optional_ - -Applies default Cloudimage filters to your image, e.g. fcontrast, fpixelate, fgaussian, backtransparent, -rotation... Multiple filters can be applied, separated by "```.```" (dot). - -[Full documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation/en/filters/) - - -### placeholderBackground - -###### Type: **String** | Default: **'#f4f4f4'** | _optional_ - -Placeholder colored background while the image is loading - - -### exactSize - -###### Type: **Bool** | Default: **false** | _optional_ - -Forces to load exact size of images. -By default the plugin rounds container width to next possible value which can be divided by 100 without the remainder. -It’s done for cache reasons so that not all images are cached by 1px, but only 100px, 200px, 300px … - -### presets - -###### Type: **Object** - -Default: - -```javascript - - -``` - -Breakpoints shortcuts to use in image size property, can be overwridden. - -## Image properties - -Cloudimage responsive plugin will make image on your page responsive if you replace the `src` with `ci-src` attribute in the `` tag: - -### ci-src - -###### Type: **String** | Default: **undefined** | _required_ - -Original image hosted on your web server. You can use absolute path or -relative to baseUrl in your config. - -**NOTES:** - -The plugin uses a special algorithm to detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. - -Images where `ci-src` is not used will be delivered in a standard, non-responsive way. - -### operation (or o) - -###### Type: **String** | Default: **width** | _optional_ - -Operation allows to customize the behaviour of the plugin for specific images: - -**width** - to resize with a specific width. This is useful when you want to have a fixed width, regardless of screen size. - -**height** - to resize with a specific height. This is useful when you want to have a fixed height, regardless of screen size. - -**crop** - to crop the image around the center - -**crop_px** - to crop an image with a non-centered focal point [doc](https://docs.cloudimage.io/go/cloudimage-documentation/en/operations/crop/positionable-crop) - -[see example in Code Sandbox](https://codesandbox.io/s/l530w827lq) - -**fit** - to resize the image in a box and keeping the proportions of the source image - -**cover** - to resize the image in a box without keeping the proportions of the source image - -**NOTES:** - -When you use an operation, you must specify the size for each screen size, see below - -Full documentation of all operations available [here](https://docs.cloudimage.io/go/cloudimage-documentation/en/operations/) - -### size (or s) - -###### Type: **String** | Default: **undefined** | _optional_ but _required_ when using operation - -Size of an image which is used as a base for creating retina ready and responsive image element. - -Examples (PR - stands for your device Pixel Ratio): - -**[width]**: - -```jsx - -``` -=> width: 250 * PR (px); height: auto; - -**[width x height]**: - -```jsx - -``` - -=> width: 125 * PR (px); height: 200 * PR (px); - -**[x1, y1, x2, y2, -final_size]**: - -_final_size_ can be [width], [width x height], [x height] - -```jsx - -``` - -=> will crop the top-left 500 x 500px square and resize to 300 x 300px; - -[see example in Code Sandbox](https://codesandbox.io/s/l530w827lq) - -**[preset breakpoint (xs,sm, md,lg,xl) or media query + ' ' + image size]**: - -```jsx - -``` - -You can drop some breakpoints, for example: - -```jsx - -``` - -**NOTE:** if size is not set, the plugin uses a special algorithm to -detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. - -For example: - -```jsx - -``` - -### filters (or f) - -###### Type: **String** | Default: **none** | _optional_ - -Filters allow you to modify the image's apperance and can be added on top of the resizing features above. - -**fgrey** - apply a greyscale filter on the image - -**fgaussian[0..10]** - apply a gaussian blur filter on the image - -**fcontrast[-100..100]** - apply a contrast filter on the image - -**fbright[0..255]** - apply a brightness filter on the image - -**fpixelate[0..100]** - apply a pixelate filter on the image - -**fradius[0..500]** - create a radius on the corners - -Full documentation of all filters available [here](https://docs.cloudimage.io/go/cloudimage-documentation/en/filters/) - -### ratio (or r) - -###### Type: **Number** | _optional_ - -It is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. - -To see the full cloudimage documentation [click here](https://docs.cloudimage.io/go/cloudimage-documentation) - -## Lazy Loading - -Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. - -The example below uses [lazysizes](https://github.com/aFarkas/lazysizes) -library using Intersection Observer API. - -[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) - -add the following scripts right after js-cloudimage-responsive script - -```javascript - - - -``` - -the initialization script - -```javascript - - ``` - -## Process dynamically loaded images! - -In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. - -```javascript - -``` - -## Browser support - -Tested in all modern browsers and IE 11. - -NOTE: If you use lazy loading with IntersectionObserver, you must -manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) -for cross-browser support. - -## Filerobot UI Familiy - -* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) -* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) -* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) -* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) -* [Uploader](https://github.com/scaleflex/filerobot-uploader) - -## Contributing! - -All contributions are super welcome! - - -## License -JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT) - +[![Release](https://img.shields.io/badge/release-v2.3.0-blue.svg)](https://github.com/scaleflex/js-cloudimage-responsive/releases) +[![Free plan](https://img.shields.io/badge/price-includes%20free%20plan-green.svg)](https://www.cloudimage.io/en/home#b38181a6-b9c8-4015-9742-7b1a1ad382d5) +[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)](#contributing) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Scaleflex team](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-the%20Scaleflex%20team-6986fa.svg)](https://www.scaleflex.it/en/home) + +

+ The Lounge +

+ +

+ JS Cloudimage Responsive | Cloudimage v6 +

+ +

+ + Docs + • + Demo + • + Code Sandbox + • + Why? + +

+ +This plugin detects the width of any image container as well as the device pixel ratio +density to load the optimal image size needed. +Images are resized on-the-fly via the Cloudimage service, thus offering a comprehensive +automated image optimization service. + +When an image is first loaded on your website or mobile app, +Cloudimage's resizing servers will download the origin image from +the source, resize it for the client's screen size and deliver to your users through one or multiple +Content Delivery Networks (CDNs). The generated image formats are cached in the CDN and will be delivered rocket fast on any subsequent request. + +**NOTE:** Your original (master) images should be stored on a server +or storage bucket (S3, Google Cloud, Azure Blob...) reachable over +HTTP or HTTPS by Cloudimage. If you want to upload your master images to +Cloudimage, contact us at +[hello@cloudimage.io](mailto:hello@cloudimage.io). + +

+ The Lounge +

+ +powered by [Cloudimage](https://www.cloudimage.io/) +([Watch the video here](https://www.youtube.com/watch?time_continue=2&v=JFZSE1vYb0k)) + +## Table of contents + +* [Demo](#demo) +* [Requirements](#requirements) +* [Step 1: Installation](#installation) +* [Step 2: Initialize](#initialize) +* [Step 3: Implement](#implement) +* [Configuration](#configuration) +* [Image properties](#image_properties) +* [Lazy loading](#lazy_loading) +* [Process dynamically loaded images](#dynamically-loaded) +* [Browser support](#browser_support) +* [Filerobot UI Family](#ui_family) +* [Contributing](#contributing) +* [License](#license) + + +## Demo + +To see the Cloudimage Responsive plugin in action, please check out the +[Demo page](https://scaleflex.github.io/js-cloudimage-responsive/). +Play with your browser's window size and observe your +Inspector's Network tab to see how Cloudimage delivers the optimal +image size to your browser, hence accelerating the overall page +loading time. + +## Requirements + +To use the Cloudimage Responsive plugin, you will need a +Cloudimage token to deliver your images over CDN. Don't worry, it only takes seconds to get one by +registering [here](https://www.cloudimage.io/en/register_page). +Once your token is created, you can configure it as described below. +This token allows you to use 25GB of image cache and 25GB of worldwide +CDN traffic per month for free. + +## Step 1: Installation + +Add script tag with CDN link to js-cloudimage-responsive + +```javascript + +``` + +You may also use major version number instead of fixed version to have the latest version available. + +```javascript + +``` + +or using npm + +``` +$ npm install --save js-cloudimage-responsive +``` + +## Step 2: Initialize + +After adding the js-cloudimage-responsive lib, simply iniatialize it with your **token** and the **baseUrl** of your image storage: + +```javascript + +``` + +or in new style with npm: + +```javascript + +import 'js-cloudimage-responsive'; + +const ciResponsive = new window.CIResponsive({ + token: 'demo', + baseUrl: 'https://cloudimage.public.airstore.io/demo/' // optional +}); +``` + +## Step 3: Implement in img tag or use it as background image + +### img tag + +Finally, just use the `ci-src` instead of the `src` attribute in image tag: + +```html + +``` + +NOTE: "ratio" is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. + +edeit in codesandbox + +### background image + +Use the `ci-bg` instead of CSS background-image property `background-image: url(...)`: + +```html +
+``` + +edeit in codesandbox + +## Config + +### token + +###### Type: **String** | Default: **"demo"** | _required_ + +Your Cloudimage customer token. +[Subscribe](https://www.cloudimage.io/en/register_page) for a +Cloudimage account to get one. The subscription takes less than a +minute and is totally free. + +### domain + +###### Type: **String** | Default: **"cloudimg.io"** + +Use your custom domain. + +### baseUrl + +###### Type: **String** | Default: **"/"** | _optional_ + +Your image folder on server, this alows to shorten your origin image URLs. + +### lazyLoading + +###### Type: **Bool** | Default: **false** | _optional_ + +Only images close to the client's viewport will be loaded, hence accelerating the page loading time. If set to **true**, an additional script must be included, see [Lazy loading](#lazy_loading) + +### imgLoadingAnimation + +###### Type: **Bool** | Default: **true** | _optional_ + +Applies a nice interlacing effect for preview transition + +### filters + +###### Type: **String** | Default: **'foil1'** | _optional_ + +Applies default Cloudimage filters to your image, e.g. fcontrast, fpixelate, fgaussian, backtransparent, +rotation... Multiple filters can be applied, separated by "```.```" (dot). + +[Full documentation here.](https://docs.cloudimage.io/go/cloudimage-documentation/en/filters/) + + +### placeholderBackground + +###### Type: **String** | Default: **'#f4f4f4'** | _optional_ + +Placeholder colored background while the image is loading + + +### exactSize + +###### Type: **Bool** | Default: **false** | _optional_ + +Forces to load exact size of images. +By default the plugin rounds container width to next possible value which can be divided by 100 without the remainder. +It’s done for cache reasons so that not all images are cached by 1px, but only 100px, 200px, 300px … + +### presets + +###### Type: **Object** + +Default: + +```javascript + + +``` + +Breakpoints shortcuts to use in image size property, can be overwridden. + +## Image properties + +Cloudimage responsive plugin will make image on your page responsive if you replace the `src` with `ci-src` attribute in the `` tag: + +### ci-src + +###### Type: **String** | Default: **undefined** | _required_ + +Original image hosted on your web server. You can use absolute path or +relative to baseUrl in your config. + +**NOTES:** + +The plugin uses a special algorithm to detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. + +Images where `ci-src` is not used will be delivered in a standard, non-responsive way. + +### operation (or o) + +###### Type: **String** | Default: **width** | _optional_ + +Operation allows to customize the behaviour of the plugin for specific images: + +**width** - to resize with a specific width. This is useful when you want to have a fixed width, regardless of screen size. + +**height** - to resize with a specific height. This is useful when you want to have a fixed height, regardless of screen size. + +**crop** - to crop the image around the center + +**crop_px** - to crop an image with a non-centered focal point [doc](https://docs.cloudimage.io/go/cloudimage-documentation/en/operations/crop/positionable-crop) + +[see example in Code Sandbox](https://codesandbox.io/s/l530w827lq) + +**fit** - to resize the image in a box and keeping the proportions of the source image + +**cover** - to resize the image in a box without keeping the proportions of the source image + +**NOTES:** + +When you use an operation, you must specify the size for each screen size, see below + +Full documentation of all operations available [here](https://docs.cloudimage.io/go/cloudimage-documentation/en/operations/) + +### size (or s) + +###### Type: **String** | Default: **undefined** | _optional_ but _required_ when using operation + +Size of an image which is used as a base for creating retina ready and responsive image element. + +Examples (PR - stands for your device Pixel Ratio): + +**[width]**: + +```jsx + +``` +=> width: 250 * PR (px); height: auto; + +**[width x height]**: + +```jsx + +``` + +=> width: 125 * PR (px); height: 200 * PR (px); + +**[x1, y1, x2, y2, -final_size]**: + +_final_size_ can be [width], [width x height], [x height] + +```jsx + +``` + +=> will crop the top-left 500 x 500px square and resize to 300 x 300px; + +[see example in Code Sandbox](https://codesandbox.io/s/l530w827lq) + +**[preset breakpoint (xs,sm, md,lg,xl) or media query + ' ' + image size]**: + +```jsx + +``` + +You can drop some breakpoints, for example: + +```jsx + +``` + +**NOTE:** if size is not set, the plugin uses a special algorithm to +detect the width of image container and set the image size accordingly. This is the recommended way of using the Cloudimage Responsive plugin. + +For example: + +```jsx + +``` + +### filters (or f) + +###### Type: **String** | Default: **none** | _optional_ + +Filters allow you to modify the image's apperance and can be added on top of the resizing features above. + +**fgrey** - apply a greyscale filter on the image + +**fgaussian[0..10]** - apply a gaussian blur filter on the image + +**fcontrast[-100..100]** - apply a contrast filter on the image + +**fbright[0..255]** - apply a brightness filter on the image + +**fpixelate[0..100]** - apply a pixelate filter on the image + +**fradius[0..500]** - create a radius on the corners + +Full documentation of all filters available [here](https://docs.cloudimage.io/go/cloudimage-documentation/en/filters/) + +### ratio (or r) + +###### Type: **Number** | _optional_ + +It is recommended to prevent page layout jumping. The parameter is used to calculate image height to hold the image position while image is loading. + +To see the full cloudimage documentation [click here](https://docs.cloudimage.io/go/cloudimage-documentation) + +## Lazy Loading + +Lazy loading is not included into js-cloudimage-responsive by default. If you [enable lazy loading](#lazy_loading_config) in the configuration, you need to add an additional library. + +The example below uses [lazysizes](https://github.com/aFarkas/lazysizes) +library using Intersection Observer API. + +[Code Sandbox example](https://codesandbox.io/s/6jkovjvkxz) + +add the following scripts right after js-cloudimage-responsive script + +```javascript + + + +``` + +the initialization script + +```javascript + + ``` + +## Process dynamically loaded images! + +In case you load some images dynamically you need to trigger `ciResponsive.process()` manually. + +```javascript + +``` + +## Browser support + +Tested in all modern browsers and IE 11. + +NOTE: If you use lazy loading with IntersectionObserver, you must +manually add the [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) +for cross-browser support. + +## Filerobot UI Familiy + +* [React Cloudimage Responsive](https://github.com/scaleflex/react-cloudimage-responsive) +* [Angular Cloudimage Responsive](https://github.com/scaleflex/ng-cloudimage-responsive) +* [JS Cloudimage 360 view](https://github.com/scaleflex/js-cloudimage-360-view) +* [Image Editor](https://github.com/scaleflex/filerobot-image-editor) +* [Uploader](https://github.com/scaleflex/filerobot-uploader) + +## Contributing! + +All contributions are super welcome! + + +## License +JS Cloudimage Responsive is provided under the [MIT License](https://opensource.org/licenses/MIT) + diff --git a/build/blur-hash/js-cloudimage-responsive.min.js b/build/blur-hash/js-cloudimage-responsive.min.js index 2afd1d8..05407d1 100644 --- a/build/blur-hash/js-cloudimage-responsive.min.js +++ b/build/blur-hash/js-cloudimage-responsive.min.js @@ -1,12 +1,12 @@ /*! * - * js-cloudimage-responsive v4.8.2 with blur hash placeholder technique + * js-cloudimage-responsive v4.8.3 with blur hash placeholder technique * https://github.com/scaleflex/js-cloudimage-responsive * * Copyright (c) 2019 scaleflex * Released under the MIT license * - * Date: 2021-11-08T15:31:39.658Z + * Date: 2021-12-09T14:21:55.660Z * - */!function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=216)}([function(t,e,r){(function(e){var r=function(t){return t&&t.Math==Math&&t};t.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||function(){return this}()||Function("return this")()}).call(this,r(119))},function(t,e,r){"use strict";var n,o,i,a=r(83),u=r(13),c=r(0),f=r(4),s=r(6),p=r(8),l=r(27),d=r(34),v=r(25),h=r(17),y=r(14).f,g=r(33),m=r(38),b=r(28),w=r(5),x=r(48),O=c.Int8Array,S=O&&O.prototype,A=c.Uint8ClampedArray,j=A&&A.prototype,T=O&&m(O),P=S&&m(S),I=Object.prototype,E=c.TypeError,R=w("toStringTag"),_=x("TYPED_ARRAY_TAG"),N=x("TYPED_ARRAY_CONSTRUCTOR"),M=a&&!!b&&"Opera"!==l(c.opera),C=!1,z={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},L={BigInt64Array:8,BigUint64Array:8},U=function(t){if(!s(t))return!1;var e=l(t);return p(z,e)||p(L,e)};for(n in z)(i=(o=c[n])&&o.prototype)?v(i,N,o):M=!1;for(n in L)(i=(o=c[n])&&o.prototype)&&v(i,N,o);if((!M||!f(T)||T===Function.prototype)&&(T=function(){throw E("Incorrect invocation")},M))for(n in z)c[n]&&b(c[n],T);if((!M||!P||P===I)&&(P=T.prototype,M))for(n in z)c[n]&&b(c[n].prototype,P);if(M&&m(j)!==P&&b(j,P),u&&!p(P,R))for(n in C=!0,y(P,R,{get:function(){return s(this)?this[_]:void 0}}),z)c[n]&&v(c[n],_,n);t.exports={NATIVE_ARRAY_BUFFER_VIEWS:M,TYPED_ARRAY_CONSTRUCTOR:N,TYPED_ARRAY_TAG:C&&_,aTypedArray:function(t){if(U(t))return t;throw E("Target is not a typed array")},aTypedArrayConstructor:function(t){if(f(t)&&(!b||g(T,t)))return t;throw E(d(t)+" is not a typed array constructor")},exportTypedArrayMethod:function(t,e,r){if(u){if(r)for(var n in z){var o=c[n];if(o&&p(o.prototype,t))try{delete o.prototype[t]}catch(t){}}P[t]&&!r||h(P,t,r?e:M&&S[t]||e)}},exportTypedArrayStaticMethod:function(t,e,r){var n,o;if(u){if(b){if(r)for(n in z)if((o=c[n])&&p(o,t))try{delete o[t]}catch(t){}if(T[t]&&!r)return;try{return h(T,t,r?e:M&&T[t]||e)}catch(t){}}for(n in z)!(o=c[n])||o[t]&&!r||h(o,t,e)}},isView:function(t){if(!s(t))return!1;var e=l(t);return"DataView"===e||p(z,e)||p(L,e)},isTypedArray:U,TypedArray:T,TypedArrayPrototype:P}},function(t,e){var r=Function.prototype,n=r.bind,o=r.call,i=n&&n.bind(o);t.exports=n?function(t){return t&&i(o,t)}:function(t){return t&&function(){return o.apply(t,arguments)}}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=function(t){return"function"==typeof t}},function(t,e,r){var n=r(0),o=r(80),i=r(8),a=r(48),u=r(79),c=r(78),f=o("wks"),s=n.Symbol,p=s&&s.for,l=c?s:s&&s.withoutSetter||a;t.exports=function(t){if(!i(f,t)||!u&&"string"!=typeof f[t]){var e="Symbol."+t;u&&i(s,t)?f[t]=s[t]:f[t]=c&&p?p(e):l(e)}return f[t]}},function(t,e,r){var n=r(4);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},function(t,e){var r=Function.prototype.call;t.exports=r.bind?r.bind(r):function(){return r.apply(r,arguments)}},function(t,e,r){var n=r(2),o=r(12),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,e){return i(o(t),e)}},function(t,e,r){var n=r(0),o=r(6),i=n.String,a=n.TypeError;t.exports=function(t){if(o(t))return t;throw a(i(t)+" is not an object")}},function(t,e,r){var n=r(37);t.exports=function(t){return n(t.length)}},function(t,e,r){var n=r(0),o=r(4),i=function(t){return o(t)?t:void 0};t.exports=function(t,e){return arguments.length<2?i(n[t]):n[t]&&n[t][e]}},function(t,e,r){var n=r(0),o=r(59),i=n.Object;t.exports=function(t){return i(o(t))}},function(t,e,r){var n=r(3);t.exports=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,e,r){var n=r(0),o=r(13),i=r(81),a=r(9),u=r(46),c=n.TypeError,f=Object.defineProperty;e.f=o?f:function(t,e,r){if(a(t),e=u(e),a(r),i)try{return f(t,e,r)}catch(t){}if("get"in r||"set"in r)throw c("Accessors not supported");return"value"in r&&(t[e]=r.value),t}},function(t,e,r){var n=r(18),o=r(2),i=r(32),a=r(12),u=r(10),c=r(90),f=o([].push),s=function(t){var e=1==t,r=2==t,o=3==t,s=4==t,p=6==t,l=7==t,d=5==t||p;return function(v,h,y,g){for(var m,b,w=a(v),x=i(w),O=n(h,y),S=u(x),A=0,j=g||c,T=e?j(v,S):r||l?j(v,0):void 0;S>A;A++)if((d||A in x)&&(b=O(m=x[A],A,w),t))if(e)T[A]=b;else if(b)switch(t){case 3:return!0;case 5:return m;case 6:return A;case 2:f(T,m)}else switch(t){case 4:return!1;case 7:f(T,m)}return p?-1:o||s?s:T}};t.exports={forEach:s(0),map:s(1),filter:s(2),some:s(3),every:s(4),find:s(5),findIndex:s(6),filterReject:s(7)}},function(t,e,r){var n=r(0),o=r(4),i=r(34),a=n.TypeError;t.exports=function(t){if(o(t))return t;throw a(i(t)+" is not a function")}},function(t,e,r){var n=r(0),o=r(4),i=r(8),a=r(25),u=r(62),c=r(49),f=r(19),s=r(51).CONFIGURABLE,p=f.get,l=f.enforce,d=String(String).split("String");(t.exports=function(t,e,r,c){var f,p=!!c&&!!c.unsafe,v=!!c&&!!c.enumerable,h=!!c&&!!c.noTargetGet,y=c&&void 0!==c.name?c.name:e;o(r)&&("Symbol("===String(y).slice(0,7)&&(y="["+String(y).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),(!i(r,"name")||s&&r.name!==y)&&a(r,"name",y),(f=l(r)).source||(f.source=d.join("string"==typeof y?y:""))),t!==n?(p?!h&&t[e]&&(v=!0):delete t[e],v?t[e]=r:a(t,e,r)):v?t[e]=r:u(e,r)})(Function.prototype,"toString",(function(){return o(this)&&p(this).source||c(this)}))},function(t,e,r){var n=r(2),o=r(16),i=n(n.bind);t.exports=function(t,e){return o(t),void 0===e?t:i?i(t,e):function(){return t.apply(e,arguments)}}},function(t,e,r){var n,o,i,a=r(123),u=r(0),c=r(2),f=r(6),s=r(25),p=r(8),l=r(47),d=r(64),v=r(50),h=u.TypeError,y=u.WeakMap;if(a||l.state){var g=l.state||(l.state=new y),m=c(g.get),b=c(g.has),w=c(g.set);n=function(t,e){if(b(g,t))throw new h("Object already initialized");return e.facade=t,w(g,t,e),e},o=function(t){return m(g,t)||{}},i=function(t){return b(g,t)}}else{var x=d("state");v[x]=!0,n=function(t,e){if(p(t,x))throw new h("Object already initialized");return e.facade=t,s(t,x,e),e},o=function(t){return p(t,x)?t[x]:{}},i=function(t){return p(t,x)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(e){var r;if(!f(e)||(r=o(e)).type!==t)throw h("Incompatible receiver, "+t+" required");return r}}}},function(t,e){var r=Math.ceil,n=Math.floor;t.exports=function(t){var e=+t;return e!=e||0===e?0:(e>0?n:r)(e)}},function(t,e,r){var n,o=r(9),i=r(133),a=r(66),u=r(50),c=r(87),f=r(63),s=r(64),p=s("IE_PROTO"),l=function(){},d=function(t){return" - - - - - JS Cloudimage Responsive - - - - - -
- - - - - - - - - - + + + + + + + + + + + + + + JS Cloudimage Responsive + + + + + +
+ + + + + + + + + + diff --git a/examples/blur-hash/src/index.js b/examples/blur-hash/src/index.js index fa74429..b7de5da 100644 --- a/examples/blur-hash/src/index.js +++ b/examples/blur-hash/src/index.js @@ -1,3 +1,3 @@ -import '../../../src/blur-hash'; -import './init'; -import '../../common/script'; +import '../../../src/blur-hash'; +import './init'; +import '../../common/script'; diff --git a/examples/blur-hash/src/init.js b/examples/blur-hash/src/init.js index b69a3d0..9e38c26 100644 --- a/examples/blur-hash/src/init.js +++ b/examples/blur-hash/src/init.js @@ -1,22 +1,22 @@ -window.ciResponsive = new window.CIResponsive({ - token: 'demo', - baseURL: 'https://cloudimage.public.airstore.io/demo/', - params: 'ci_info=1&org_if_sml=1&version=16.04.2020', - lazyLoadOffset: 100, - apiVersion: 'v7', - lazyLoading: true, - exactSize: false, - limitFactor: 10 -}); - -setTimeout(() => { - window.ciResponsive.process(); -}, 1000) -setTimeout(() => { - window.ciResponsive.process(); -}, 2000) -setTimeout(() => { - window.ciResponsive.process(); -}, 4000) - -window.lazySizes.init(); +window.ciResponsive = new window.CIResponsive({ + token: 'demo', + baseURL: 'https://cloudimage.public.airstore.io/demo/', + params: 'ci_info=1&org_if_sml=1&version=16.04.2020', + lazyLoadOffset: 100, + apiVersion: 'v7', + lazyLoading: true, + exactSize: false, + limitFactor: 10 +}); + +setTimeout(() => { + window.ciResponsive.process(); +}, 1000) +setTimeout(() => { + window.ciResponsive.process(); +}, 2000) +setTimeout(() => { + window.ciResponsive.process(); +}, 4000) + +window.lazySizes.init(); diff --git a/examples/blur-hash/src/pages/different-size-image-on-screen-width/index.html b/examples/blur-hash/src/pages/different-size-image-on-screen-width/index.html index 3d850e9..8bfa714 100644 --- a/examples/blur-hash/src/pages/different-size-image-on-screen-width/index.html +++ b/examples/blur-hash/src/pages/different-size-image-on-screen-width/index.html @@ -1,80 +1,80 @@ - - - - - - - - Document - - -
-

Cloudimage Plugin

-

Cloudimage plugin - will resize, compress and accelerate images across the World - in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver - the right image size based on the client's screen size and pixel ratio (retina - vs non-retina). You can find the full extend of image operations in the Cloudimage documentation - here.

- -

- I. Responsive mode, according to image container size -

-
- -
- -

- II. Manual mode -

- -
-
- - - original: 9.2mb link
-
-
-
-

You can control your image size/ratio/crop with media query breakpoints

-

Resize your browser window to see how it works

-

-<img
-  operation="crop"
-  ci-src="dino-reichmuth-1.jpg"
-  size="{
-    xl: '1600x1000',
-    lg: '1400x1200',
-    md: '1000x1350',
-    sm: '800x400'
-  }"
-</>
-      
-
-
- - -
-

Any questions?

-

Contact us at hello@cloudimage.io, our image resizing experts will be - happy to help!

-
-
- - - - - - - + + + + + + + + Document + + +
+

Cloudimage Plugin

+

Cloudimage plugin + will resize, compress and accelerate images across the World + in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver + the right image size based on the client's screen size and pixel ratio (retina + vs non-retina). You can find the full extend of image operations in the Cloudimage documentation + here.

+ +

+ I. Responsive mode, according to image container size +

+
+ +
+ +

+ II. Manual mode +

+ +
+
+ + + original: 9.2mb link
+
+
+
+

You can control your image size/ratio/crop with media query breakpoints

+

Resize your browser window to see how it works

+

+<img
+  operation="crop"
+  ci-src="dino-reichmuth-1.jpg"
+  size="{
+    xl: '1600x1000',
+    lg: '1400x1200',
+    md: '1000x1350',
+    sm: '800x400'
+  }"
+</>
+      
+
+
+ + +
+

Any questions?

+

Contact us at hello@cloudimage.io, our image resizing experts will be + happy to help!

+
+
+ + + + + + + \ No newline at end of file diff --git a/examples/common/fonts/helvetica-neue.css b/examples/common/fonts/helvetica-neue.css index 03b7699..5a98609 100644 --- a/examples/common/fonts/helvetica-neue.css +++ b/examples/common/fonts/helvetica-neue.css @@ -1,24 +1,24 @@ -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); - font-weight: 500; - font-style: normal; -} - -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); - font-weight: 300; - font-style: normal; -} - +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); + font-weight: 300; + font-style: normal; +} + diff --git a/examples/common/script.js b/examples/common/script.js index 0277d45..010f102 100644 --- a/examples/common/script.js +++ b/examples/common/script.js @@ -1,42 +1,42 @@ -import './style.css'; -import './fonts/helvetica-neue.css'; -import { debounce } from 'throttle-debounce'; -import javascript from 'highlight.js/lib/languages/javascript'; -import 'highlight.js/styles/github.css'; - -window.hljs.registerLanguage('javascript', javascript); -window.hljs.initHighlightingOnLoad(); -/* -* logic for image containers; -*/ - -const spinner = document.getElementById('spinner'); -const wrapper = document.getElementById('main'); -const containerBox = document.querySelectorAll('.container-width-box:not(.custom)'); -const windowBox = document.querySelectorAll('.window-width-box:not(.custom)'); -const devicePixelRatio = document.querySelector('#device-pixel-ratio span'); -const setBoxSizes = () => { - [].slice.call(containerBox).forEach(box => { - box.querySelector('span').innerText = box.offsetWidth; - }); -}; -const setWindowBoxes = () => { - [].slice.call(windowBox).forEach(box => { - box.querySelector('span').innerText = window.innerWidth.toString() + 'px'; - }); -} - -setBoxSizes(); -setWindowBoxes(); - -devicePixelRatio.innerText = window.devicePixelRatio.toFixed(1); -window.onresize = debounce(400, () => { - setBoxSizes(); - setWindowBoxes(); -}); - - - - -wrapper.classList.add('active'); -spinner.style.display = 'none'; +import './style.css'; +import './fonts/helvetica-neue.css'; +import { debounce } from 'throttle-debounce'; +import javascript from 'highlight.js/lib/languages/javascript'; +import 'highlight.js/styles/github.css'; + +window.hljs.registerLanguage('javascript', javascript); +window.hljs.initHighlightingOnLoad(); +/* +* logic for image containers; +*/ + +const spinner = document.getElementById('spinner'); +const wrapper = document.getElementById('main'); +const containerBox = document.querySelectorAll('.container-width-box:not(.custom)'); +const windowBox = document.querySelectorAll('.window-width-box:not(.custom)'); +const devicePixelRatio = document.querySelector('#device-pixel-ratio span'); +const setBoxSizes = () => { + [].slice.call(containerBox).forEach(box => { + box.querySelector('span').innerText = box.offsetWidth; + }); +}; +const setWindowBoxes = () => { + [].slice.call(windowBox).forEach(box => { + box.querySelector('span').innerText = window.innerWidth.toString() + 'px'; + }); +} + +setBoxSizes(); +setWindowBoxes(); + +devicePixelRatio.innerText = window.devicePixelRatio.toFixed(1); +window.onresize = debounce(400, () => { + setBoxSizes(); + setWindowBoxes(); +}); + + + + +wrapper.classList.add('active'); +spinner.style.display = 'none'; diff --git a/examples/common/style.css b/examples/common/style.css index 230f1fc..4896954 100644 --- a/examples/common/style.css +++ b/examples/common/style.css @@ -1,571 +1,571 @@ -body, html { - font-size: 14px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - background: #f5f5f5; -} - -.home { - position: relative; - border-top: 1px solid transparent; - background: #222; - color: #fff; - text-align: center; - padding-top: 50px; - padding-bottom: 50px; -} - -.home .cloudimage-logo { - width: 200px; - position: absolute; - top: 30px; - left: 30px; -} - -.home .logo { - font-weight: normal; - font-size: 24px; - color: #fff; -} - -.home .logo:hover, .home .logo:active, .home .logo:visited { - text-decoration: none; - color: #fff; -} - -.home .fork-me-on-github { - position: absolute; - top: 0; - right: 0; - border: 0; - width: auto; -} - -.home h1 { - font-size: 48px; - margin-top: 40px; - margin-bottom: 30px; - font-weight: lighter; - font-family: Helvetica, Arial, sans-serif; -} - -.home h2 { - font-weight: lighter; - color: #d0d0d0; - line-height: 1.5em; - font-size: 18px; - max-width: 770px; - display: inline-block; -} - -.home h2 strong { - color: #fff; -} - -.home .actions-wrapper { - margin-top: 20px; -} - -.home .robot-icon { - position: absolute; - left: 50px; - width: 200px; - top: 110px; - -webkit-animation-name: example; /* Safari 4.0 - 8.0 */ - -webkit-animation-duration: 4s; /* Safari 4.0 - 8.0 */ - animation-name: example; - animation-duration: 4s; - animation-iteration-count: infinite; - animation-timing-function: ease-in-out; -} - -@media all and (max-width: 1380px) { - .home .robot-icon { - left: 45px; - width: 100px; - top: 15px; - } -} - -@media all and (max-width: 600px) { - .home { - padding-top: 150px; - } - - .home h1 { - font-size: 30px; - } - - .home .robot-icon { - left: 50%; - width: 100px; - margin-left: -50px; - top: 15px; - } -} - -@keyframes example { - 0% {transform: translate(0px, 0px);} - 50% {transform: translate(5px, 2px) scale(1.02);} - 100% {transform: translate(0px, 0px);} -} - -.ready-to-start { - padding-top: 50px; - padding-bottom: 50px; -} - -.ready-to-start h2 { - font-weight: lighter; - font-size: 40px; - max-width: 770px; - margin: 0 auto 30px auto; -} - -.ready-to-start p, .ready-to-start ul { - display: block; - max-width: 850px; - margin: 0 auto; - font-weight: lighter; - line-height: 1.5em; - font-size: 18px; -} - -.ready-to-start ul li { - margin-bottom: 10px; -} - -.uploaded-image-container { - padding: 20px 0; -} - -.filerobot-uploader-view-wrapper { - position: relative; - margin: 0 auto -20px; - top: 30px; - padding: 0; - max-width: 770px; - display: inline-block; - overflow: hidden; - border-radius: 5px; -} - -.filerobot-uploader-view-wrapper .filerobot-uploader-view { - width: 100%; -} - -* { - box-sizing: border-box; -} - -#image-box { - cursor: pointer; -} - -.image-container { - position: relative; - display: inline-block; - vertical-align: top; - width: 50%; - height: 400px; - background-color: #222222; - border-radius: 5px; - transition: 0.3s all; -} - -.image-container img { - max-width: calc(100% - 30px); - max-height: calc(100% - 30px); - width: auto; - height: auto; - position: absolute; - top: 15px; - bottom: 15px; - left: 15px; - right: 15px; - margin: auto; -} - -#image-description { - display: inline-block; - width: 45%; - vertical-align: top; - padding: 20px; - font-size: 18px; -} - -#image-description ul { - list-style-type: none; - margin: 0; - padding: 0; -} - -#image-description ul li { - margin-bottom: 10px; -} - -#image-description ul li span:first-child { - display: inline-block; - font-weight: bold; - color: #6d6d6d; -} - -.highlight { - padding: 1.5rem; - margin-bottom: 1rem; - background-color: #f8f9fa; - display: block; -} - -.highlight pre { - margin: 0; -} - -.see-doc { - font-size: 14px; - margin-left: 10px; -} - -.github-logo-wrapper { - position: absolute; - top: 10px; - right: 15px; -} - -.github-logo-wrapper .github-logo { - width: 20px; - margin-right: 10px; -} - -.title-wrapper { - position: relative; -} - -.wrapper { - transition: 0.3s all; - opacity: 0; -} - -.wrapper.active { - visibility: visible !important; - opacity: 1 !important; -} - -#edit-btn { - position: relative; -} - -.arrow { - background: url('https://cdn.scaleflex.it/filerobot/assets/arrow_1.png') 50% 50% / contain no-repeat; - width: 100px; - height: 250px; - position: absolute; - top: -42px; - left: -97px; - transform: rotate(38deg); - cursor: pointer; -} - - -.action-wrapper { - text-align: left; - position: relative; - background-color: #fff; - margin: 0 0 70px 0px; - padding: 40px 40px 40px 60px; - font-size: 18px; - border: 1px solid #ddd; - border-radius: 5px; - z-index: 1; - -webkit-transition: all 0.6s ease-in-out 0s; - -moz-transition: all 0.6s ease-in-out 0s; - -o-transition: all 0.6s ease-in-out 0s; - transition: all 0.6s ease-in-out 0s; -} - -.action-wrapper:after { - width: 58px; - height: 58px; - line-height: 58px; - background-color: #327fc7; - border-radius: 200px; - font-size: 30px; - font-weight: 300; - color: #fff; - position: absolute; - left: -15px; - top: -15px; - text-align: center; - -webkit-backface-visibility: hidden; - z-index: 100; -} - -.action-wrapper.first-action:after { - content: '1'; -} - -.action-wrapper.second-action:after { - content: '2'; -} - -.action-wrapper.third-action:after { - content: '3'; -} - -.action-wrapper.forth-action:after { - content: '4'; -} - -.action-wrapper p { - font-weight: lighter; -} - -.plugin-version-switcher { - padding-bottom: 30px; - display: inline-block; -} - -.inner-spinner { - display: none; -} - -footer { - text-align: center; - background: #fff; - padding-bottom: 60px; -} - -footer { - background: #fff; - padding-bottom: 30px; - font-weight: lighter; -} - -footer .copyright { - margin-top: 30px; -} - -footer .team-desc { - text-align: left; -} - -footer .footer-menu { - text-align: right; -} - -footer .footer-menu ul { - margin: 0; - padding: 0; - list-style-type: none; -} - -footer .footer-menu ul li { - display: inline-block; - margin-left: 15px; - margin-bottom: 5px; -} - -@media all and (max-width: 576px) { - footer .team-desc { - text-align: center; - margin-bottom: 20px; - } - - footer .footer-menu { - text-align: center; - } -} - -.twitter-share-button { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; - font-size: 11px; - font-weight: 600; - height: 20px; - padding: 0 5px; - line-height: 18px; - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFAFBFC', endColorstr='#FFE4EBF0')"; - display: inline-block; - vertical-align: bottom; - cursor: pointer; - border-radius: 0.25em; - text-decoration: none; - vertical-align: top; - background-color: #1b95e0; - color: #fff; -} - -.twitter-share-button i { - position: relative; - top: 2px; - display: inline-block; - width: 14px; - height: 14px; - background: transparent 0 0 no-repeat; - background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E); - box-sizing: border-box; - padding: 1px 8px 1px 6px; - color: #fff; - border-radius: 3px; - font-weight: 500; - cursor: pointer; -} - -.twitter-share-button span { - vertical-align: top; -} - -.container h1 { - margin-top: 40px; -} - -.container h1 + p { - color: #ababab; - font-size: 22px; -} - -p.description { - margin-top: 10px; -} - -p.numbers { - font-size: 16px; - color: #4b4b4b; -} - -@media (max-width: 767px) { - .desc-wrapper-with-media-query { - margin-top: 20px; - } -} - -div.images-in-columns div:not(.container-width-box) { - margin-bottom: 15px; - font-size: 14px; -} - -pre { - padding: 1.5rem; - margin-right: 0; - margin-left: 0; - border-width: .2rem; - margin: 1rem -15px 0; - border: solid #f8f9fa; -} - -pre p { - margin: 0; - padding: 0; -} - -img { - width: 100%; -} - -.logo { - display: inline-block; - width: auto; - margin-left: 5px; - position: relative; - margin-top: -8px; - height: 47px; -} - -.table { - margin: 30px auto 30px auto; - max-width: 850px; -} - -.table thead { - text-align: left; -} - -.table thead th { - vertical-align: middle; - font-size: 16px; -} - -.table tbody td { - font-size: 18px; -} - -.container-width-box { - background: #222; - color: #fff; - border-top: 1px solid #6d6d6d; - border-bottom: 1px solid #6d6d6d; - text-align: center; - padding: 4px 0; -} - -.device-pixel-ratio { - background: #222; - color: #fff; - border: 1px solid #6d6d6d; - border-radius: 4px; - padding: 4px 8px; - position: fixed; - bottom: 15px; - right: 15px; - z-index: 100; - max-width: 230px; -} - -.device-pixel-ratio span { - background: #fff; - display: inline-block; - border-radius: 4px; - padding: 0 4px; - min-width: 30px; - height: 30px; - line-height: 30px; - color: #222; - text-align: center; - vertical-align: middle; -} - -.device-pixel-ratio .window-width-box { - display: inline-block; - margin-left: 4px; -} - -.device-pixel-ratio hr { - margin: 0; - margin-top: 5px; -} - -.device-pixel-ratio .label { - min-width: 150px; - display: inline-block; -} - -.ready-to-start.filerobot-ui-family { - padding-bottom: 20px; - text-align: left; -} - -.ready-to-start.filerobot-ui-family ul { - list-style-type: none; - margin: 0; - padding: 0; - max-width: none; - font-size: 16px; - text-align: left; -} - -.ready-to-start.filerobot-ui-family h5 { - font-weight: lighter; -} - -.ready-to-start.filerobot-ui-family ul li { - display: inline-block; - margin-right: 30px; -} - -@media (max-width: 767px) { - .filerobot-ui-family-label, .filerobot-ui-family-libs { - min-width: 100% !important; - } - - .ready-to-start.filerobot-ui-family h5 { - margin-bottom: 20px; - } +body, html { + font-size: 14px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + background: #f5f5f5; +} + +.home { + position: relative; + border-top: 1px solid transparent; + background: #222; + color: #fff; + text-align: center; + padding-top: 50px; + padding-bottom: 50px; +} + +.home .cloudimage-logo { + width: 200px; + position: absolute; + top: 30px; + left: 30px; +} + +.home .logo { + font-weight: normal; + font-size: 24px; + color: #fff; +} + +.home .logo:hover, .home .logo:active, .home .logo:visited { + text-decoration: none; + color: #fff; +} + +.home .fork-me-on-github { + position: absolute; + top: 0; + right: 0; + border: 0; + width: auto; +} + +.home h1 { + font-size: 48px; + margin-top: 40px; + margin-bottom: 30px; + font-weight: lighter; + font-family: Helvetica, Arial, sans-serif; +} + +.home h2 { + font-weight: lighter; + color: #d0d0d0; + line-height: 1.5em; + font-size: 18px; + max-width: 770px; + display: inline-block; +} + +.home h2 strong { + color: #fff; +} + +.home .actions-wrapper { + margin-top: 20px; +} + +.home .robot-icon { + position: absolute; + left: 50px; + width: 200px; + top: 110px; + -webkit-animation-name: example; /* Safari 4.0 - 8.0 */ + -webkit-animation-duration: 4s; /* Safari 4.0 - 8.0 */ + animation-name: example; + animation-duration: 4s; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; +} + +@media all and (max-width: 1380px) { + .home .robot-icon { + left: 45px; + width: 100px; + top: 15px; + } +} + +@media all and (max-width: 600px) { + .home { + padding-top: 150px; + } + + .home h1 { + font-size: 30px; + } + + .home .robot-icon { + left: 50%; + width: 100px; + margin-left: -50px; + top: 15px; + } +} + +@keyframes example { + 0% {transform: translate(0px, 0px);} + 50% {transform: translate(5px, 2px) scale(1.02);} + 100% {transform: translate(0px, 0px);} +} + +.ready-to-start { + padding-top: 50px; + padding-bottom: 50px; +} + +.ready-to-start h2 { + font-weight: lighter; + font-size: 40px; + max-width: 770px; + margin: 0 auto 30px auto; +} + +.ready-to-start p, .ready-to-start ul { + display: block; + max-width: 850px; + margin: 0 auto; + font-weight: lighter; + line-height: 1.5em; + font-size: 18px; +} + +.ready-to-start ul li { + margin-bottom: 10px; +} + +.uploaded-image-container { + padding: 20px 0; +} + +.filerobot-uploader-view-wrapper { + position: relative; + margin: 0 auto -20px; + top: 30px; + padding: 0; + max-width: 770px; + display: inline-block; + overflow: hidden; + border-radius: 5px; +} + +.filerobot-uploader-view-wrapper .filerobot-uploader-view { + width: 100%; +} + +* { + box-sizing: border-box; +} + +#image-box { + cursor: pointer; +} + +.image-container { + position: relative; + display: inline-block; + vertical-align: top; + width: 50%; + height: 400px; + background-color: #222222; + border-radius: 5px; + transition: 0.3s all; +} + +.image-container img { + max-width: calc(100% - 30px); + max-height: calc(100% - 30px); + width: auto; + height: auto; + position: absolute; + top: 15px; + bottom: 15px; + left: 15px; + right: 15px; + margin: auto; +} + +#image-description { + display: inline-block; + width: 45%; + vertical-align: top; + padding: 20px; + font-size: 18px; +} + +#image-description ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +#image-description ul li { + margin-bottom: 10px; +} + +#image-description ul li span:first-child { + display: inline-block; + font-weight: bold; + color: #6d6d6d; +} + +.highlight { + padding: 1.5rem; + margin-bottom: 1rem; + background-color: #f8f9fa; + display: block; +} + +.highlight pre { + margin: 0; +} + +.see-doc { + font-size: 14px; + margin-left: 10px; +} + +.github-logo-wrapper { + position: absolute; + top: 10px; + right: 15px; +} + +.github-logo-wrapper .github-logo { + width: 20px; + margin-right: 10px; +} + +.title-wrapper { + position: relative; +} + +.wrapper { + transition: 0.3s all; + opacity: 0; +} + +.wrapper.active { + visibility: visible !important; + opacity: 1 !important; +} + +#edit-btn { + position: relative; +} + +.arrow { + background: url('https://cdn.scaleflex.it/filerobot/assets/arrow_1.png') 50% 50% / contain no-repeat; + width: 100px; + height: 250px; + position: absolute; + top: -42px; + left: -97px; + transform: rotate(38deg); + cursor: pointer; +} + + +.action-wrapper { + text-align: left; + position: relative; + background-color: #fff; + margin: 0 0 70px 0px; + padding: 40px 40px 40px 60px; + font-size: 18px; + border: 1px solid #ddd; + border-radius: 5px; + z-index: 1; + -webkit-transition: all 0.6s ease-in-out 0s; + -moz-transition: all 0.6s ease-in-out 0s; + -o-transition: all 0.6s ease-in-out 0s; + transition: all 0.6s ease-in-out 0s; +} + +.action-wrapper:after { + width: 58px; + height: 58px; + line-height: 58px; + background-color: #327fc7; + border-radius: 200px; + font-size: 30px; + font-weight: 300; + color: #fff; + position: absolute; + left: -15px; + top: -15px; + text-align: center; + -webkit-backface-visibility: hidden; + z-index: 100; +} + +.action-wrapper.first-action:after { + content: '1'; +} + +.action-wrapper.second-action:after { + content: '2'; +} + +.action-wrapper.third-action:after { + content: '3'; +} + +.action-wrapper.forth-action:after { + content: '4'; +} + +.action-wrapper p { + font-weight: lighter; +} + +.plugin-version-switcher { + padding-bottom: 30px; + display: inline-block; +} + +.inner-spinner { + display: none; +} + +footer { + text-align: center; + background: #fff; + padding-bottom: 60px; +} + +footer { + background: #fff; + padding-bottom: 30px; + font-weight: lighter; +} + +footer .copyright { + margin-top: 30px; +} + +footer .team-desc { + text-align: left; +} + +footer .footer-menu { + text-align: right; +} + +footer .footer-menu ul { + margin: 0; + padding: 0; + list-style-type: none; +} + +footer .footer-menu ul li { + display: inline-block; + margin-left: 15px; + margin-bottom: 5px; +} + +@media all and (max-width: 576px) { + footer .team-desc { + text-align: center; + margin-bottom: 20px; + } + + footer .footer-menu { + text-align: center; + } +} + +.twitter-share-button { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 11px; + font-weight: 600; + height: 20px; + padding: 0 5px; + line-height: 18px; + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFAFBFC', endColorstr='#FFE4EBF0')"; + display: inline-block; + vertical-align: bottom; + cursor: pointer; + border-radius: 0.25em; + text-decoration: none; + vertical-align: top; + background-color: #1b95e0; + color: #fff; +} + +.twitter-share-button i { + position: relative; + top: 2px; + display: inline-block; + width: 14px; + height: 14px; + background: transparent 0 0 no-repeat; + background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E); + box-sizing: border-box; + padding: 1px 8px 1px 6px; + color: #fff; + border-radius: 3px; + font-weight: 500; + cursor: pointer; +} + +.twitter-share-button span { + vertical-align: top; +} + +.container h1 { + margin-top: 40px; +} + +.container h1 + p { + color: #ababab; + font-size: 22px; +} + +p.description { + margin-top: 10px; +} + +p.numbers { + font-size: 16px; + color: #4b4b4b; +} + +@media (max-width: 767px) { + .desc-wrapper-with-media-query { + margin-top: 20px; + } +} + +div.images-in-columns div:not(.container-width-box) { + margin-bottom: 15px; + font-size: 14px; +} + +pre { + padding: 1.5rem; + margin-right: 0; + margin-left: 0; + border-width: .2rem; + margin: 1rem -15px 0; + border: solid #f8f9fa; +} + +pre p { + margin: 0; + padding: 0; +} + +img { + width: 100%; +} + +.logo { + display: inline-block; + width: auto; + margin-left: 5px; + position: relative; + margin-top: -8px; + height: 47px; +} + +.table { + margin: 30px auto 30px auto; + max-width: 850px; +} + +.table thead { + text-align: left; +} + +.table thead th { + vertical-align: middle; + font-size: 16px; +} + +.table tbody td { + font-size: 18px; +} + +.container-width-box { + background: #222; + color: #fff; + border-top: 1px solid #6d6d6d; + border-bottom: 1px solid #6d6d6d; + text-align: center; + padding: 4px 0; +} + +.device-pixel-ratio { + background: #222; + color: #fff; + border: 1px solid #6d6d6d; + border-radius: 4px; + padding: 4px 8px; + position: fixed; + bottom: 15px; + right: 15px; + z-index: 100; + max-width: 230px; +} + +.device-pixel-ratio span { + background: #fff; + display: inline-block; + border-radius: 4px; + padding: 0 4px; + min-width: 30px; + height: 30px; + line-height: 30px; + color: #222; + text-align: center; + vertical-align: middle; +} + +.device-pixel-ratio .window-width-box { + display: inline-block; + margin-left: 4px; +} + +.device-pixel-ratio hr { + margin: 0; + margin-top: 5px; +} + +.device-pixel-ratio .label { + min-width: 150px; + display: inline-block; +} + +.ready-to-start.filerobot-ui-family { + padding-bottom: 20px; + text-align: left; +} + +.ready-to-start.filerobot-ui-family ul { + list-style-type: none; + margin: 0; + padding: 0; + max-width: none; + font-size: 16px; + text-align: left; +} + +.ready-to-start.filerobot-ui-family h5 { + font-weight: lighter; +} + +.ready-to-start.filerobot-ui-family ul li { + display: inline-block; + margin-right: 30px; +} + +@media (max-width: 767px) { + .filerobot-ui-family-label, .filerobot-ui-family-libs { + min-width: 100% !important; + } + + .ready-to-start.filerobot-ui-family h5 { + margin-bottom: 20px; + } } \ No newline at end of file diff --git a/examples/low-preview/src/assets/fonts/helvetica-neue.css b/examples/low-preview/src/assets/fonts/helvetica-neue.css index 03b7699..5a98609 100644 --- a/examples/low-preview/src/assets/fonts/helvetica-neue.css +++ b/examples/low-preview/src/assets/fonts/helvetica-neue.css @@ -1,24 +1,24 @@ -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); - font-weight: 500; - font-style: normal; -} - -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); - font-weight: 300; - font-style: normal; -} - +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); + font-weight: 300; + font-style: normal; +} + diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index 232ebfd..7404e6b 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -1,624 +1,624 @@ - - - - - - - - - - - - - - JS Cloudimage Responsive - - - - - -
- - - - - - - - - - + + + + + + + + + + + + + + JS Cloudimage Responsive + + + + + +
+ + + + + + + + + + diff --git a/examples/low-preview/src/index.js b/examples/low-preview/src/index.js index 4612915..9d188c8 100644 --- a/examples/low-preview/src/index.js +++ b/examples/low-preview/src/index.js @@ -1,3 +1,3 @@ -import '../../../src/low-preview'; -import './init'; +import '../../../src/low-preview'; +import './init'; import '../../common/script'; \ No newline at end of file diff --git a/examples/low-preview/src/init.js b/examples/low-preview/src/init.js index aa128f1..c7c48fa 100644 --- a/examples/low-preview/src/init.js +++ b/examples/low-preview/src/init.js @@ -1,14 +1,14 @@ -window.ciResponsive = new window.CIResponsive({ - token: 'demo', - baseURL: 'https://cloudimage.public.airstore.io/demo/', - params: 'ci_info=1&org_if_sml=1&version=16.04.2020', - lazyLoading: true, - exactSize: false, - limitFactor: 10, - apiVersion: 'v7', - lowQualityPreview: { - minImgWidth: 180 - } -}); - -window.lazySizes.init(); +window.ciResponsive = new window.CIResponsive({ + token: 'demo', + baseURL: 'https://cloudimage.public.airstore.io/demo/', + params: 'ci_info=1&org_if_sml=1&version=16.04.2020', + lazyLoading: true, + exactSize: false, + limitFactor: 10, + apiVersion: 'v7', + lowQualityPreview: { + minImgWidth: 180 + } +}); + +window.lazySizes.init(); diff --git a/examples/low-preview/src/pages/different-size-image-on-screen-width/index.html b/examples/low-preview/src/pages/different-size-image-on-screen-width/index.html index 3d850e9..8bfa714 100644 --- a/examples/low-preview/src/pages/different-size-image-on-screen-width/index.html +++ b/examples/low-preview/src/pages/different-size-image-on-screen-width/index.html @@ -1,80 +1,80 @@ - - - - - - - - Document - - -
-

Cloudimage Plugin

-

Cloudimage plugin - will resize, compress and accelerate images across the World - in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver - the right image size based on the client's screen size and pixel ratio (retina - vs non-retina). You can find the full extend of image operations in the Cloudimage documentation - here.

- -

- I. Responsive mode, according to image container size -

-
- -
- -

- II. Manual mode -

- -
-
- - - original: 9.2mb link
-
-
-
-

You can control your image size/ratio/crop with media query breakpoints

-

Resize your browser window to see how it works

-

-<img
-  operation="crop"
-  ci-src="dino-reichmuth-1.jpg"
-  size="{
-    xl: '1600x1000',
-    lg: '1400x1200',
-    md: '1000x1350',
-    sm: '800x400'
-  }"
-</>
-      
-
-
- - -
-

Any questions?

-

Contact us at hello@cloudimage.io, our image resizing experts will be - happy to help!

-
-
- - - - - - - + + + + + + + + Document + + +
+

Cloudimage Plugin

+

Cloudimage plugin + will resize, compress and accelerate images across the World + in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver + the right image size based on the client's screen size and pixel ratio (retina + vs non-retina). You can find the full extend of image operations in the Cloudimage documentation + here.

+ +

+ I. Responsive mode, according to image container size +

+
+ +
+ +

+ II. Manual mode +

+ +
+
+ + + original: 9.2mb link
+
+
+
+

You can control your image size/ratio/crop with media query breakpoints

+

Resize your browser window to see how it works

+

+<img
+  operation="crop"
+  ci-src="dino-reichmuth-1.jpg"
+  size="{
+    xl: '1600x1000',
+    lg: '1400x1200',
+    md: '1000x1350',
+    sm: '800x400'
+  }"
+</>
+      
+
+
+ + +
+

Any questions?

+

Contact us at hello@cloudimage.io, our image resizing experts will be + happy to help!

+
+
+ + + + + + + \ No newline at end of file diff --git a/examples/plain/src/assets/fonts/helvetica-neue.css b/examples/plain/src/assets/fonts/helvetica-neue.css index 03b7699..5a98609 100644 --- a/examples/plain/src/assets/fonts/helvetica-neue.css +++ b/examples/plain/src/assets/fonts/helvetica-neue.css @@ -1,24 +1,24 @@ -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); - font-weight: 500; - font-style: normal; -} - -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); - font-weight: 300; - font-style: normal; -} - +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); + font-weight: 300; + font-style: normal; +} + diff --git a/examples/plain/src/index.html b/examples/plain/src/index.html index 04373ee..22b1451 100644 --- a/examples/plain/src/index.html +++ b/examples/plain/src/index.html @@ -1,630 +1,630 @@ - - - - - - - - - - - - - JS Cloudimage Responsive - - - - -
- - - - - - - - - + + + + + + + + + + + + + JS Cloudimage Responsive + + + + +
+ + + + + + + + + diff --git a/examples/plain/src/index.js b/examples/plain/src/index.js index 177823f..a5feb25 100644 --- a/examples/plain/src/index.js +++ b/examples/plain/src/index.js @@ -1,3 +1,3 @@ -import '../../../src/plain'; -import './init'; +import '../../../src/plain'; +import './init'; import '../../common/script'; \ No newline at end of file diff --git a/examples/plain/src/init.js b/examples/plain/src/init.js index 394016c..4af12a2 100644 --- a/examples/plain/src/init.js +++ b/examples/plain/src/init.js @@ -1,62 +1,62 @@ -window.ciResponsive = new window.CIResponsive({ - token: 'demo', - baseURL: 'https://cloudimage.public.airstore.io/demo/', - params: 'ci_info=1&org_if_sml=1&version=16.04.2020', - lazyLoadOffset: 100, - apiVersion: 'v7', - lazyLoading: true, - exactSize: false, - limitFactor: 10, - - // processURL: (props) => { - // console.log(props) - // - // return props.url; - // }, - // props: { - // query, - // widthQ, - // heightQ, - // restParamsQ, - // processOnlyWidth, - // devicePixelRatio, - // service: { - // methods: {}, - // props: { - // imgNode, - // imgProps, - // config - // } - // } - // } - // processQueryString: (props) => { - // var imgNode = props.service.props.imgNode; - // var maxWidth = imgNode.getAttribute('max-width'); - // var maxHeight = imgNode.getAttribute('max-height'); - // - // if (maxWidth && maxHeight) { - // return [ - // `${props.restParamsQ}`, - // `&w=${maxWidth * props.devicePixelRatio}&h=${maxHeight * props.devicePixelRatio}&func=fit` - // ].join(''); - // } else { - // return props.query; - // } - // } - - // imgSelector: 'data-src', - // bgSelector: 'data-bg-src' - - //ignoreNodeImgSize: false, - //saveNodeImgRatio: false, - //ignoreStyleImgSize: false, - //destroyNodeImgSize: false, - //detectImageNodeCSS: false, - //processOnlyWidth: false, - // - //onImageLoad: function (image) { - // console.log(image.width, image.height); - //} -}); - -window.lazySizes.init(); +window.ciResponsive = new window.CIResponsive({ + token: 'demo', + baseURL: 'https://cloudimage.public.airstore.io/demo/', + params: 'ci_info=1&org_if_sml=1&version=16.04.2020', + lazyLoadOffset: 100, + apiVersion: 'v7', + lazyLoading: true, + exactSize: false, + limitFactor: 10, + + // processURL: (props) => { + // console.log(props) + // + // return props.url; + // }, + // props: { + // query, + // widthQ, + // heightQ, + // restParamsQ, + // processOnlyWidth, + // devicePixelRatio, + // service: { + // methods: {}, + // props: { + // imgNode, + // imgProps, + // config + // } + // } + // } + // processQueryString: (props) => { + // var imgNode = props.service.props.imgNode; + // var maxWidth = imgNode.getAttribute('max-width'); + // var maxHeight = imgNode.getAttribute('max-height'); + // + // if (maxWidth && maxHeight) { + // return [ + // `${props.restParamsQ}`, + // `&w=${maxWidth * props.devicePixelRatio}&h=${maxHeight * props.devicePixelRatio}&func=fit` + // ].join(''); + // } else { + // return props.query; + // } + // } + + // imgSelector: 'data-src', + // bgSelector: 'data-bg-src' + + //ignoreNodeImgSize: false, + //saveNodeImgRatio: false, + //ignoreStyleImgSize: false, + //destroyNodeImgSize: false, + //detectImageNodeCSS: false, + //processOnlyWidth: false, + // + //onImageLoad: function (image) { + // console.log(image.width, image.height); + //} +}); + +window.lazySizes.init(); diff --git a/examples/plain/src/pages/different-size-image-on-screen-width/index.html b/examples/plain/src/pages/different-size-image-on-screen-width/index.html index 3d850e9..8bfa714 100644 --- a/examples/plain/src/pages/different-size-image-on-screen-width/index.html +++ b/examples/plain/src/pages/different-size-image-on-screen-width/index.html @@ -1,80 +1,80 @@ - - - - - - - - Document - - -
-

Cloudimage Plugin

-

Cloudimage plugin - will resize, compress and accelerate images across the World - in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver - the right image size based on the client's screen size and pixel ratio (retina - vs non-retina). You can find the full extend of image operations in the Cloudimage documentation - here.

- -

- I. Responsive mode, according to image container size -

-
- -
- -

- II. Manual mode -

- -
-
- - - original: 9.2mb link
-
-
-
-

You can control your image size/ratio/crop with media query breakpoints

-

Resize your browser window to see how it works

-

-<img
-  operation="crop"
-  ci-src="dino-reichmuth-1.jpg"
-  size="{
-    xl: '1600x1000',
-    lg: '1400x1200',
-    md: '1000x1350',
-    sm: '800x400'
-  }"
-</>
-      
-
-
- - -
-

Any questions?

-

Contact us at hello@cloudimage.io, our image resizing experts will be - happy to help!

-
-
- - - - - - - + + + + + + + + Document + + +
+

Cloudimage Plugin

+

Cloudimage plugin + will resize, compress and accelerate images across the World + in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver + the right image size based on the client's screen size and pixel ratio (retina + vs non-retina). You can find the full extend of image operations in the Cloudimage documentation + here.

+ +

+ I. Responsive mode, according to image container size +

+
+ +
+ +

+ II. Manual mode +

+ +
+
+ + + original: 9.2mb link
+
+
+
+

You can control your image size/ratio/crop with media query breakpoints

+

Resize your browser window to see how it works

+

+<img
+  operation="crop"
+  ci-src="dino-reichmuth-1.jpg"
+  size="{
+    xl: '1600x1000',
+    lg: '1400x1200',
+    md: '1000x1350',
+    sm: '800x400'
+  }"
+</>
+      
+
+
+ + +
+

Any questions?

+

Contact us at hello@cloudimage.io, our image resizing experts will be + happy to help!

+
+
+ + + + + + + \ No newline at end of file diff --git a/examples/wp/src/assets/fonts/helvetica-neue.css b/examples/wp/src/assets/fonts/helvetica-neue.css index 03b7699..5a98609 100644 --- a/examples/wp/src/assets/fonts/helvetica-neue.css +++ b/examples/wp/src/assets/fonts/helvetica-neue.css @@ -1,24 +1,24 @@ -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); - font-weight: 500; - font-style: normal; -} - -@font-face { - font-family: 'Helvetica Neue'; - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); - src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), - url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); - font-weight: 300; - font-style: normal; -} - +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-Medium.svg#HelveticaNeue-Medium') format('svg'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'Helvetica Neue'; + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot'); + src: url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.eot?#iefix') format('embedded-opentype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff2') format('woff2'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.woff') format('woff'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.ttf') format('truetype'), + url('https://cdn.scaleflex.it/filerobot/assets/fonts/helvetica-neue/HelveticaNeue-LightExt.svg#HelveticaNeue-LightExt') format('svg'); + font-weight: 300; + font-style: normal; +} + diff --git a/examples/wp/src/index.html b/examples/wp/src/index.html index 92b555d..d7503a6 100644 --- a/examples/wp/src/index.html +++ b/examples/wp/src/index.html @@ -1,630 +1,630 @@ - - - - - - - - - - - - - JS Cloudimage Responsive - - - - -
- - - - - - - - + + + + + + + + + + + + + JS Cloudimage Responsive + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/examples/wp/src/index.js b/examples/wp/src/index.js index 43b2b51..ede8d84 100644 --- a/examples/wp/src/index.js +++ b/examples/wp/src/index.js @@ -1,3 +1,3 @@ -import './init'; -import '../../../src/wp'; +import './init'; +import '../../../src/wp'; import '../../common/script'; \ No newline at end of file diff --git a/examples/wp/src/init.js b/examples/wp/src/init.js index 68ff856..5330ab7 100644 --- a/examples/wp/src/init.js +++ b/examples/wp/src/init.js @@ -1,10 +1,10 @@ -window.CIResponsiveConfig = { - token: 'demo', - baseURL: 'https://cloudimage.public.airstore.io/demo/', - params: 'ci_info=1&org_if_sml=1&version=16.04.2020', - apiVersion: 'v7', - lazyLoadOffset: 100, - lazyLoading: true, - exactSize: false, - limitFactor: 10, +window.CIResponsiveConfig = { + token: 'demo', + baseURL: 'https://cloudimage.public.airstore.io/demo/', + params: 'ci_info=1&org_if_sml=1&version=16.04.2020', + apiVersion: 'v7', + lazyLoadOffset: 100, + lazyLoading: true, + exactSize: false, + limitFactor: 10, }; \ No newline at end of file diff --git a/examples/wp/src/pages/different-size-image-on-screen-width/index.html b/examples/wp/src/pages/different-size-image-on-screen-width/index.html index 3d850e9..8bfa714 100644 --- a/examples/wp/src/pages/different-size-image-on-screen-width/index.html +++ b/examples/wp/src/pages/different-size-image-on-screen-width/index.html @@ -1,80 +1,80 @@ - - - - - - - - Document - - -
-

Cloudimage Plugin

-

Cloudimage plugin - will resize, compress and accelerate images across the World - in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver - the right image size based on the client's screen size and pixel ratio (retina - vs non-retina). You can find the full extend of image operations in the Cloudimage documentation - here.

- -

- I. Responsive mode, according to image container size -

-
- -
- -

- II. Manual mode -

- -
-
- - - original: 9.2mb link
-
-
-
-

You can control your image size/ratio/crop with media query breakpoints

-

Resize your browser window to see how it works

-

-<img
-  operation="crop"
-  ci-src="dino-reichmuth-1.jpg"
-  size="{
-    xl: '1600x1000',
-    lg: '1400x1200',
-    md: '1000x1350',
-    sm: '800x400'
-  }"
-</>
-      
-
-
- - -
-

Any questions?

-

Contact us at hello@cloudimage.io, our image resizing experts will be - happy to help!

-
-
- - - - - - - + + + + + + + + Document + + +
+

Cloudimage Plugin

+

Cloudimage plugin + will resize, compress and accelerate images across the World + in your site. It leverages the HTML5 <picture> and <srcset> elements to deliver + the right image size based on the client's screen size and pixel ratio (retina + vs non-retina). You can find the full extend of image operations in the Cloudimage documentation + here.

+ +

+ I. Responsive mode, according to image container size +

+
+ +
+ +

+ II. Manual mode +

+ +
+
+ + + original: 9.2mb link
+
+
+
+

You can control your image size/ratio/crop with media query breakpoints

+

Resize your browser window to see how it works

+

+<img
+  operation="crop"
+  ci-src="dino-reichmuth-1.jpg"
+  size="{
+    xl: '1600x1000',
+    lg: '1400x1200',
+    md: '1000x1350',
+    sm: '800x400'
+  }"
+</>
+      
+
+
+ + +
+

Any questions?

+

Contact us at hello@cloudimage.io, our image resizing experts will be + happy to help!

+
+
+ + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 035d26c..fd019b7 100644 --- a/package.json +++ b/package.json @@ -1,91 +1,91 @@ -{ - "name": "js-cloudimage-responsive", - "version": "4.8.2", - "main": "dist/low-preview/index.js", - "description": "Cloudimage Responsive will smartly resize, compress and accelerate images across the World in your site for all devices. The plugin supports lazy loading technique with fancy animation on image load.", - "author": "scaleflex", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/scaleflex/js-cloudimage-responsive" - }, - "homepage": "https://github.com/scaleflex/js-cloudimage-responsive#readme", - "keywords": [ - "image", - "images", - "cloudimage", - "responsive images", - "lazy loading", - "web acceleration", - "image management", - "image resizing", - "image compression", - "image optimization", - "image CDN", - "webp", - "jpeg xr", - "jpg optimization", - "image resizing and CDN", - "crop", - "resize" - ], - "scripts": { - "start-demo-low-preview": "webpack-dev-server --mode development --config config/low-preview/webpack-demo.config.js", - "start-demo-blur-hash": "webpack-dev-server --mode development --config config/blur-hash/webpack-demo.config.js", - "start-demo-plain": "webpack-dev-server --mode development --config config/plain/webpack-demo.config.js", - "start-demo-wp": "webpack-dev-server --mode development --config config/wp/webpack-demo.config.js", - "clean-build": "rm -rf build", - "build-low-preview": "webpack --mode production --config config/low-preview/webpack-build.config.js", - "build-blur-hash": "webpack --mode production --config config/blur-hash/webpack-build.config.js", - "build-plain": "webpack --mode production --config config/plain/webpack-build.config.js", - "build-wp": "webpack --mode production --config config/wp/webpack-build.config.js", - "build": "npm run clean-build && npm run build-low-preview && npm run build-blur-hash && npm run build-plain && npm run build-wp", - "build-lazysizes": "webpack --mode production --config config/webpack-lazysizes-build.config.js", - "clean-dist": "rm -rf dist", - "dist": "npm run clean-dist && babel src -d dist --copy-files", - "dist-minify": "export NODE_ENV=dist && npm run clean-dist && npm run build", - "clean-demo": "rm -rf examples/blur-hash/dist && rm -rf examples/low-preview/dist && rm -rf examples/plain/dist", - "build-demo-low-preview": "webpack --mode production --config config/low-preview/webpack-demo.config.js", - "build-demo-blur-hash": "webpack --mode production --config config/blur-hash/webpack-demo.config.js", - "build-demo-plain": "webpack --mode production --config config/plain/webpack-demo.config.js", - "build-demo-wp": "webpack --mode production --config config/wp/webpack-demo.config.js", - "build-demo": "npm run clean-demo && npm run build-demo-low-preview && npm run build-demo-blur-hash && npm run build-demo-plain && npm run build-demo-wp", - "deploy-demo": "gh-pages -d examples/low-preview/dist", - "publish-demo": "npm run build-demo && npm run deploy-demo" - }, - "dependencies": { - "blurhash": "^1.1.3", - "core-js": "^3.15.2", - "throttle-debounce": "^2.1.0" - }, - "devDependencies": { - "@babel/cli": "^7.0.0", - "@babel/core": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-decorators": "^7.0.0", - "@babel/plugin-proposal-export-namespace-from": "^7.0.0", - "@babel/plugin-proposal-function-sent": "^7.0.0", - "@babel/plugin-proposal-json-strings": "^7.0.0", - "@babel/plugin-proposal-numeric-separator": "^7.0.0", - "@babel/plugin-proposal-throw-expressions": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-syntax-import-meta": "^7.0.0", - "@babel/preset-env": "^7.0.0", - "babel-loader": "^8.0.0", - "babel-plugin-array-includes": "^2.0.3", - "babel-preset-minify": "^0.5.1", - "css-loader": "^2.1.1", - "css-minimizer-webpack-plugin": "^1.3.0", - "extract-text-webpack-plugin": "^1.0.1", - "gh-pages": "^2.2.0", - "highlight.js": "^11.1.0", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.5.0", - "style-loader": "^0.23.1", - "uglifyjs-webpack-plugin": "^2.2.0", - "webpack": "^4.46.0", - "webpack-cli": "^3.3.11", - "webpack-dev-server": "^3.11.2", - "webpack-stream": "^3.1.0" - } -} +{ + "name": "js-cloudimage-responsive", + "version": "4.8.3", + "main": "dist/low-preview/index.js", + "description": "Cloudimage Responsive will smartly resize, compress and accelerate images across the World in your site for all devices. The plugin supports lazy loading technique with fancy animation on image load.", + "author": "scaleflex", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/scaleflex/js-cloudimage-responsive" + }, + "homepage": "https://github.com/scaleflex/js-cloudimage-responsive#readme", + "keywords": [ + "image", + "images", + "cloudimage", + "responsive images", + "lazy loading", + "web acceleration", + "image management", + "image resizing", + "image compression", + "image optimization", + "image CDN", + "webp", + "jpeg xr", + "jpg optimization", + "image resizing and CDN", + "crop", + "resize" + ], + "scripts": { + "start-demo-low-preview": "webpack-dev-server --mode development --config config/low-preview/webpack-demo.config.js", + "start-demo-blur-hash": "webpack-dev-server --mode development --config config/blur-hash/webpack-demo.config.js", + "start-demo-plain": "webpack-dev-server --mode development --config config/plain/webpack-demo.config.js", + "start-demo-wp": "webpack-dev-server --mode development --config config/wp/webpack-demo.config.js", + "clean-build": "rm -rf build", + "build-low-preview": "webpack --mode production --config config/low-preview/webpack-build.config.js", + "build-blur-hash": "webpack --mode production --config config/blur-hash/webpack-build.config.js", + "build-plain": "webpack --mode production --config config/plain/webpack-build.config.js", + "build-wp": "webpack --mode production --config config/wp/webpack-build.config.js", + "build": "npm run clean-build && npm run build-low-preview && npm run build-blur-hash && npm run build-plain && npm run build-wp", + "build-lazysizes": "webpack --mode production --config config/webpack-lazysizes-build.config.js", + "clean-dist": "rm -rf dist", + "dist": "npm run clean-dist && babel src -d dist --copy-files", + "dist-minify": "export NODE_ENV=dist && npm run clean-dist && npm run build", + "clean-demo": "rm -rf examples/blur-hash/dist && rm -rf examples/low-preview/dist && rm -rf examples/plain/dist", + "build-demo-low-preview": "webpack --mode production --config config/low-preview/webpack-demo.config.js", + "build-demo-blur-hash": "webpack --mode production --config config/blur-hash/webpack-demo.config.js", + "build-demo-plain": "webpack --mode production --config config/plain/webpack-demo.config.js", + "build-demo-wp": "webpack --mode production --config config/wp/webpack-demo.config.js", + "build-demo": "npm run clean-demo && npm run build-demo-low-preview && npm run build-demo-blur-hash && npm run build-demo-plain && npm run build-demo-wp", + "deploy-demo": "gh-pages -d examples/low-preview/dist", + "publish-demo": "npm run build-demo && npm run deploy-demo" + }, + "dependencies": { + "blurhash": "^1.1.3", + "core-js": "^3.15.2", + "throttle-debounce": "^2.1.0" + }, + "devDependencies": { + "@babel/cli": "^7.0.0", + "@babel/core": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.0.0", + "@babel/plugin-proposal-decorators": "^7.0.0", + "@babel/plugin-proposal-export-namespace-from": "^7.0.0", + "@babel/plugin-proposal-function-sent": "^7.0.0", + "@babel/plugin-proposal-json-strings": "^7.0.0", + "@babel/plugin-proposal-numeric-separator": "^7.0.0", + "@babel/plugin-proposal-throw-expressions": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.0.0", + "@babel/plugin-syntax-import-meta": "^7.0.0", + "@babel/preset-env": "^7.0.0", + "babel-loader": "^8.0.0", + "babel-plugin-array-includes": "^2.0.3", + "babel-preset-minify": "^0.5.1", + "css-loader": "^2.1.1", + "css-minimizer-webpack-plugin": "^1.3.0", + "extract-text-webpack-plugin": "^1.0.1", + "gh-pages": "^2.2.0", + "highlight.js": "^11.1.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.5.0", + "style-loader": "^0.23.1", + "uglifyjs-webpack-plugin": "^2.2.0", + "webpack": "^4.46.0", + "webpack-cli": "^3.3.11", + "webpack-dev-server": "^3.11.2", + "webpack-stream": "^3.1.0" + } +} diff --git a/src/blur-hash/blurHash/base83.js b/src/blur-hash/blurHash/base83.js index d5de0d6..fe4867f 100644 --- a/src/blur-hash/blurHash/base83.js +++ b/src/blur-hash/blurHash/base83.js @@ -1,104 +1,104 @@ -const digitCharacters = [ - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "#", - "$", - "%", - "*", - "+", - ",", - "-", - ".", - ":", - ";", - "=", - "?", - "@", - "[", - "]", - "^", - "_", - "{", - "|", - "}", - "~" -]; - -export const decode83 = (str) => { - let value = 0; - for (let i = 0; i < str.length; i++) { - const c = str[i]; - const digit = digitCharacters.indexOf(c); - value = value * 83 + digit; - } - return value; -}; - -export const encode83 = (n, length) => { - var result = ""; - for (let i = 1; i <= length; i++) { - let digit = (Math.floor(n) / Math.pow(83, length - i)) % 83; - result += digitCharacters[Math.floor(digit)]; - } - return result; -}; +const digitCharacters = [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "#", + "$", + "%", + "*", + "+", + ",", + "-", + ".", + ":", + ";", + "=", + "?", + "@", + "[", + "]", + "^", + "_", + "{", + "|", + "}", + "~" +]; + +export const decode83 = (str) => { + let value = 0; + for (let i = 0; i < str.length; i++) { + const c = str[i]; + const digit = digitCharacters.indexOf(c); + value = value * 83 + digit; + } + return value; +}; + +export const encode83 = (n, length) => { + var result = ""; + for (let i = 1; i <= length; i++) { + let digit = (Math.floor(n) / Math.pow(83, length - i)) % 83; + result += digitCharacters[Math.floor(digit)]; + } + return result; +}; diff --git a/src/blur-hash/blurHash/decode.js b/src/blur-hash/blurHash/decode.js index 381352c..52d691b 100644 --- a/src/blur-hash/blurHash/decode.js +++ b/src/blur-hash/blurHash/decode.js @@ -1,118 +1,118 @@ -import { decode83 } from "./base83"; -import { sRGBToLinear, signPow, linearTosRGB } from "./utils"; -import { ValidationError } from "./error"; - -/** - * Returns an error message if invalid or undefined if valid - * @param blurhash - */ -const validateBlurhash = (blurhash) => { - if (!blurhash || blurhash.length < 6) { - throw new ValidationError( - "The blurhash string must be at least 6 characters" - ); - } - - const sizeFlag = decode83(blurhash[0]); - const numY = Math.floor(sizeFlag / 9) + 1; - const numX = (sizeFlag % 9) + 1; - - if (blurhash.length !== 4 + 2 * numX * numY) { - throw new ValidationError( - `blurhash length mismatch: length is ${ - blurhash.length - } but it should be ${4 + 2 * numX * numY}` - ); - } -}; - -export const isBlurhashValid = (blurhash) => { - try { - validateBlurhash(blurhash); - } catch (error) { - return { result: false, errorReason: error.message }; - } - - return { result: true }; -}; - -const decodeDC = (value) => { - const intR = value >> 16; - const intG = (value >> 8) & 255; - const intB = value & 255; - return [sRGBToLinear(intR), sRGBToLinear(intG), sRGBToLinear(intB)]; -}; - -const decodeAC = (value, maximumValue) => { - const quantR = Math.floor(value / (19 * 19)); - const quantG = Math.floor(value / 19) % 19; - const quantB = value % 19; - - const rgb = [ - signPow((quantR - 9) / 9, 2.0) * maximumValue, - signPow((quantG - 9) / 9, 2.0) * maximumValue, - signPow((quantB - 9) / 9, 2.0) * maximumValue - ]; - - return rgb; -}; - -const decode = (blurhash, width, height, punch) => { - validateBlurhash(blurhash); - - punch = punch | 1; - - const sizeFlag = decode83(blurhash[0]); - const numY = Math.floor(sizeFlag / 9) + 1; - const numX = (sizeFlag % 9) + 1; - - const quantisedMaximumValue = decode83(blurhash[1]); - const maximumValue = (quantisedMaximumValue + 1) / 166; - - const colors = new Array(numX * numY); - - for (let i = 0; i < colors.length; i++) { - if (i === 0) { - const value = decode83(blurhash.substring(2, 6)); - colors[i] = decodeDC(value); - } else { - const value = decode83(blurhash.substring(4 + i * 2, 6 + i * 2)); - colors[i] = decodeAC(value, maximumValue * punch); - } - } - - const bytesPerRow = width * 4; - const pixels = new Uint8ClampedArray(bytesPerRow * height); - - for (let y = 0; y < height; y++) { - for (let x = 0; x < width; x++) { - let r = 0; - let g = 0; - let b = 0; - - for (let j = 0; j < numY; j++) { - for (let i = 0; i < numX; i++) { - const basis = - Math.cos((Math.PI * x * i) / width) * - Math.cos((Math.PI * y * j) / height); - let color = colors[i + j * numX]; - r += color[0] * basis; - g += color[1] * basis; - b += color[2] * basis; - } - } - - let intR = linearTosRGB(r); - let intG = linearTosRGB(g); - let intB = linearTosRGB(b); - - pixels[4 * x + 0 + y * bytesPerRow] = intR; - pixels[4 * x + 1 + y * bytesPerRow] = intG; - pixels[4 * x + 2 + y * bytesPerRow] = intB; - pixels[4 * x + 3 + y * bytesPerRow] = 255; // alpha - } - } - return pixels; -}; - -export default decode; +import { decode83 } from "./base83"; +import { sRGBToLinear, signPow, linearTosRGB } from "./utils"; +import { ValidationError } from "./error"; + +/** + * Returns an error message if invalid or undefined if valid + * @param blurhash + */ +const validateBlurhash = (blurhash) => { + if (!blurhash || blurhash.length < 6) { + throw new ValidationError( + "The blurhash string must be at least 6 characters" + ); + } + + const sizeFlag = decode83(blurhash[0]); + const numY = Math.floor(sizeFlag / 9) + 1; + const numX = (sizeFlag % 9) + 1; + + if (blurhash.length !== 4 + 2 * numX * numY) { + throw new ValidationError( + `blurhash length mismatch: length is ${ + blurhash.length + } but it should be ${4 + 2 * numX * numY}` + ); + } +}; + +export const isBlurhashValid = (blurhash) => { + try { + validateBlurhash(blurhash); + } catch (error) { + return { result: false, errorReason: error.message }; + } + + return { result: true }; +}; + +const decodeDC = (value) => { + const intR = value >> 16; + const intG = (value >> 8) & 255; + const intB = value & 255; + return [sRGBToLinear(intR), sRGBToLinear(intG), sRGBToLinear(intB)]; +}; + +const decodeAC = (value, maximumValue) => { + const quantR = Math.floor(value / (19 * 19)); + const quantG = Math.floor(value / 19) % 19; + const quantB = value % 19; + + const rgb = [ + signPow((quantR - 9) / 9, 2.0) * maximumValue, + signPow((quantG - 9) / 9, 2.0) * maximumValue, + signPow((quantB - 9) / 9, 2.0) * maximumValue + ]; + + return rgb; +}; + +const decode = (blurhash, width, height, punch) => { + validateBlurhash(blurhash); + + punch = punch | 1; + + const sizeFlag = decode83(blurhash[0]); + const numY = Math.floor(sizeFlag / 9) + 1; + const numX = (sizeFlag % 9) + 1; + + const quantisedMaximumValue = decode83(blurhash[1]); + const maximumValue = (quantisedMaximumValue + 1) / 166; + + const colors = new Array(numX * numY); + + for (let i = 0; i < colors.length; i++) { + if (i === 0) { + const value = decode83(blurhash.substring(2, 6)); + colors[i] = decodeDC(value); + } else { + const value = decode83(blurhash.substring(4 + i * 2, 6 + i * 2)); + colors[i] = decodeAC(value, maximumValue * punch); + } + } + + const bytesPerRow = width * 4; + const pixels = new Uint8ClampedArray(bytesPerRow * height); + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + let r = 0; + let g = 0; + let b = 0; + + for (let j = 0; j < numY; j++) { + for (let i = 0; i < numX; i++) { + const basis = + Math.cos((Math.PI * x * i) / width) * + Math.cos((Math.PI * y * j) / height); + let color = colors[i + j * numX]; + r += color[0] * basis; + g += color[1] * basis; + b += color[2] * basis; + } + } + + let intR = linearTosRGB(r); + let intG = linearTosRGB(g); + let intB = linearTosRGB(b); + + pixels[4 * x + 0 + y * bytesPerRow] = intR; + pixels[4 * x + 1 + y * bytesPerRow] = intG; + pixels[4 * x + 2 + y * bytesPerRow] = intB; + pixels[4 * x + 3 + y * bytesPerRow] = 255; // alpha + } + } + return pixels; +}; + +export default decode; diff --git a/src/blur-hash/blurHash/error.js b/src/blur-hash/blurHash/error.js index c3246cf..523815b 100644 --- a/src/blur-hash/blurHash/error.js +++ b/src/blur-hash/blurHash/error.js @@ -1,8 +1,8 @@ -export class ValidationError extends Error { - constructor(message) { - super(message); - - this.name = "ValidationError"; - this.message = message; - } -} +export class ValidationError extends Error { + constructor(message) { + super(message); + + this.name = "ValidationError"; + this.message = message; + } +} diff --git a/src/blur-hash/blurHash/imageDataPolyfill.js b/src/blur-hash/blurHash/imageDataPolyfill.js index 8d6e88f..c0e349e 100644 --- a/src/blur-hash/blurHash/imageDataPolyfill.js +++ b/src/blur-hash/blurHash/imageDataPolyfill.js @@ -1,51 +1,51 @@ -if(window.CanvasPixelArray) { - CanvasPixelArray.prototype.set = function(arr) { - var l=this.length, i=0; - - for(;i { - try { - new window.ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1); - } catch (e) { - function ImageDataPolyfill () { - let args = [...arguments], data; - - if (args.length < 2) { - throw new TypeError(` - Failed to construct 'ImageData': 2 arguments required, but only ${args.length} present. - `); - } - - if (args.length > 2) { - data = args.shift(); - - if (!(data instanceof Uint8ClampedArray)) { - throw new TypeError(` - Failed to construct 'ImageData': parameter 1 is not of type 'Uint8ClampedArray' - `); - } - - if (data.length !== 4 * args[0] * args[1]) { - throw new Error(` - Failed to construct 'ImageData': The input data byte length is not a multiple of (4 * width * height) - `); - } - } - - const width = args[0], - height = args[1], - canvas = document.createElement('canvas'), - ctx = canvas.getContext('2d'), - imageData = ctx.createImageData(width, height); - - if (data) imageData.data.set(data); - return imageData; - }; - - window.ImageData = ImageDataPolyfill; - } +if(window.CanvasPixelArray) { + CanvasPixelArray.prototype.set = function(arr) { + var l=this.length, i=0; + + for(;i { + try { + new window.ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1); + } catch (e) { + function ImageDataPolyfill () { + let args = [...arguments], data; + + if (args.length < 2) { + throw new TypeError(` + Failed to construct 'ImageData': 2 arguments required, but only ${args.length} present. + `); + } + + if (args.length > 2) { + data = args.shift(); + + if (!(data instanceof Uint8ClampedArray)) { + throw new TypeError(` + Failed to construct 'ImageData': parameter 1 is not of type 'Uint8ClampedArray' + `); + } + + if (data.length !== 4 * args[0] * args[1]) { + throw new Error(` + Failed to construct 'ImageData': The input data byte length is not a multiple of (4 * width * height) + `); + } + } + + const width = args[0], + height = args[1], + canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'), + imageData = ctx.createImageData(width, height); + + if (data) imageData.data.set(data); + return imageData; + }; + + window.ImageData = ImageDataPolyfill; + } })(); \ No newline at end of file diff --git a/src/blur-hash/blurHash/index.js b/src/blur-hash/blurHash/index.js index cffed77..4a054b7 100644 --- a/src/blur-hash/blurHash/index.js +++ b/src/blur-hash/blurHash/index.js @@ -1,3 +1,3 @@ -import './imageDataPolyfill'; - +import './imageDataPolyfill'; + export { default as decode, isBlurhashValid } from "./decode"; \ No newline at end of file diff --git a/src/blur-hash/blurHash/unit8ClampedArrayPolyfill.js b/src/blur-hash/blurHash/unit8ClampedArrayPolyfill.js index b5e5c50..06d488e 100644 --- a/src/blur-hash/blurHash/unit8ClampedArrayPolyfill.js +++ b/src/blur-hash/blurHash/unit8ClampedArrayPolyfill.js @@ -1,45 +1,45 @@ -if (!window.Uint8ClampedArray && window.Uint8Array && window.ImageData) { - window.Uint8ClampedArray = function(input,arg1,arg2) { - var len = 0; - if (typeof input == "undefined") { } - else if (!isNaN(parseInt(input.length))) { //an array, yay - len = input.length; - } - else if (input instanceof ArrayBuffer) { - return new Uint8ClampedArray(new Uint8Array(input,arg1,arg2)); - } - else { - len = parseInt(input); - if (isNaN(len) || len < 0) { - throw new RangeError(); - } - input = undefined; - } - len = Math.ceil(len / 4); - - if (len == 0) len = 1; - - var array = document.createElement("canvas") - .getContext("2d") - .createImageData(len, 1) - .data; - - if (typeof input != "undefined") { - for (var i=0;i { - let v = value / 255; - if (v <= 0.04045) { - return v / 12.92; - } else { - return Math.pow((v + 0.055) / 1.055, 2.4); - } -}; - -export const linearTosRGB = (value) => { - let v = Math.max(0, Math.min(1, value)); - if (v <= 0.0031308) { - return Math.round(v * 12.92 * 255 + 0.5); - } else { - return Math.round((1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255 + 0.5); - } -}; - -export const sign = (n) => (n < 0 ? -1 : 1); - -export const signPow = (val, exp) => - sign(val) * Math.pow(Math.abs(val), exp); +export const sRGBToLinear = (value) => { + let v = value / 255; + if (v <= 0.04045) { + return v / 12.92; + } else { + return Math.pow((v + 0.055) / 1.055, 2.4); + } +}; + +export const linearTosRGB = (value) => { + let v = Math.max(0, Math.min(1, value)); + if (v <= 0.0031308) { + return Math.round(v * 12.92 * 255 + 0.5); + } else { + return Math.round((1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255 + 0.5); + } +}; + +export const sign = (n) => (n < 0 ? -1 : 1); + +export const signPow = (val, exp) => + sign(val) * Math.pow(Math.abs(val), exp); diff --git a/src/blur-hash/ci.config.js b/src/blur-hash/ci.config.js index 2580f4c..fea8cc7 100644 --- a/src/blur-hash/ci.config.js +++ b/src/blur-hash/ci.config.js @@ -1,69 +1,69 @@ -import { getParams } from '../common/ci.utils'; -import { DEVICE_PIXEL_RATIO_LIST } from 'cloudimage-responsive-utils/dist/constants'; - - -export const getInitialConfigBlurHash = (config) => { - const { - imgSelector = 'ci-src', - bgSelector = 'ci-bg-url', - token = '', - domain = 'cloudimg.io', - lazyLoading = false, - baseUrl, - baseURL, - presets, - ratio = 1.5, - params = 'org_if_sml=1', - apiVersion = 'v7', - init = true, - exactSize = false, - doNotReplaceURL = false, - limitFactor = 100, - devicePixelRatioList = DEVICE_PIXEL_RATIO_LIST, - ignoreNodeImgSize, - imageSizeAttributes = 'use', - ignoreStyleImgSize = false, - destroyNodeImgSize = false, - saveNodeImgRatio = false, - detectImageNodeCSS = false, - processOnlyWidth = false, - - // callbacks - onImageLoad - } = config; - - return { - imgSelector, - bgSelector, - token, - domain, - lazyLoading, - baseURL: baseUrl || baseURL, - ratio, - exactSize, - presets: presets ? presets : - { - xs: '(max-width: 575px)', // to 575 PHONE - sm: '(min-width: 576px)', // 576 - 767 PHABLET - md: '(min-width: 768px)', // 768 - 991 TABLET - lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN - xl: '(min-width: 1200px)' // from 1200 USUALSCREEN - }, - params: getParams(params), - apiVersion, - innerWidth: typeof window !== 'undefined' ? window.innerWidth : null, - init, - previewQualityFactor: 10, - doNotReplaceURL, - devicePixelRatioList, - limitFactor, - ignoreNodeImgSize, - ignoreStyleImgSize, - destroyNodeImgSize, - saveNodeImgRatio, - detectImageNodeCSS, - processOnlyWidth, - imageSizeAttributes, - onImageLoad - }; -}; +import { getParams } from '../common/ci.utils'; +import { DEVICE_PIXEL_RATIO_LIST } from 'cloudimage-responsive-utils/dist/constants'; + + +export const getInitialConfigBlurHash = (config) => { + const { + imgSelector = 'ci-src', + bgSelector = 'ci-bg-url', + token = '', + domain = 'cloudimg.io', + lazyLoading = false, + baseUrl, + baseURL, + presets, + ratio = 1.5, + params = 'org_if_sml=1', + apiVersion = 'v7', + init = true, + exactSize = false, + doNotReplaceURL = false, + limitFactor = 100, + devicePixelRatioList = DEVICE_PIXEL_RATIO_LIST, + ignoreNodeImgSize, + imageSizeAttributes = 'use', + ignoreStyleImgSize = false, + destroyNodeImgSize = false, + saveNodeImgRatio = false, + detectImageNodeCSS = false, + processOnlyWidth = false, + + // callbacks + onImageLoad + } = config; + + return { + imgSelector, + bgSelector, + token, + domain, + lazyLoading, + baseURL: baseUrl || baseURL, + ratio, + exactSize, + presets: presets ? presets : + { + xs: '(max-width: 575px)', // to 575 PHONE + sm: '(min-width: 576px)', // 576 - 767 PHABLET + md: '(min-width: 768px)', // 768 - 991 TABLET + lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN + xl: '(min-width: 1200px)' // from 1200 USUALSCREEN + }, + params: getParams(params), + apiVersion, + innerWidth: typeof window !== 'undefined' ? window.innerWidth : null, + init, + previewQualityFactor: 10, + doNotReplaceURL, + devicePixelRatioList, + limitFactor, + ignoreNodeImgSize, + ignoreStyleImgSize, + destroyNodeImgSize, + saveNodeImgRatio, + detectImageNodeCSS, + processOnlyWidth, + imageSizeAttributes, + onImageLoad + }; +}; diff --git a/src/blur-hash/ci.service.js b/src/blur-hash/ci.service.js index e8e1c27..b9b26fa 100644 --- a/src/blur-hash/ci.service.js +++ b/src/blur-hash/ci.service.js @@ -1,185 +1,185 @@ -import { - destroyNodeImgSize, - getBackgroundImageProps, - getFreshCIElements, - getImageProps, - isLazy, - setBackgroundSrc, - setSrc, - setSrcset -} from '../common/ci.utils'; -import { determineContainerProps } from 'cloudimage-responsive-utils/dist/utils/determine-container-props'; -import { getImgSRC } from 'cloudimage-responsive-utils/dist/utils/get-img-src'; -import { generateURL } from 'cloudimage-responsive-utils/dist/utils/generate-url'; -import { getBreakpoint } from 'cloudimage-responsive-utils/dist/utils/get-breakpoint'; -import { isSupportedInBrowser } from 'cloudimage-responsive-utils/dist/utils/is-supported-in-browser'; -import { getInitialConfigBlurHash } from './ci.config'; -import { - applyOrUpdateBlurHashCanvas, - applyOrUpdateWrapper, - finishAnimation, - initImageBackgroundClasses, - initImageBackgroundStyles, - initImageClasses, - initImageStyles, - loadBackgroundImage, - onImageLoad -} from './ci.utils'; -import { debounce } from 'throttle-debounce'; - - -export default class CIResponsive { - constructor(config) { - this.config = getInitialConfigBlurHash(config); - - if (this.config.init) this.init(); - - this.innerWidth = window.innerWidth; - } - - init() { - document.addEventListener('lazybeforeunveil', loadBackgroundImage); - window.addEventListener('resize', debounce(100, this.onUpdateDimensions.bind(this))); - - this.process(); - } - - onUpdateDimensions() { - this.process(true); - - if (this.innerWidth < window.innerWidth) { - this.innerWidth = window.innerWidth; - } - } - - process(isUpdate, rootElement = document) { - const { imgSelector, bgSelector } = this.config; - const windowScreenBecomesBigger = this.innerWidth < window.innerWidth; - let [images, backgroundImages] = getFreshCIElements(isUpdate, rootElement, imgSelector, bgSelector); - - if (images.length > -1) { - images.forEach(imgNode => { - this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image'); - }); - } - - if (backgroundImages.length > -1) { - backgroundImages.forEach(imgNode => { - this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'background'); - }); - } - } - - getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type) => { - const isImage = type === 'image'; - const { config } = this; - const { baseURL, lazyLoading, presets, devicePixelRatioList, imgSelector, bgSelector } = config; - const imgProps = isImage ? - getImageProps(imgNode, imgSelector) : getBackgroundImageProps(imgNode, bgSelector); - const { params, imgNodeSRC, blurHash, isLazyCanceled, sizes, isAdaptive, preserveSize, minWindowWidth } = imgProps; - - if (!imgNodeSRC) return; - - const [src, isSVG] = getImgSRC(imgNodeSRC, baseURL); - const lazy = isLazy(lazyLoading, isLazyCanceled, isUpdate); - let size; - - if (!isSupportedInBrowser(true)) { - if (isImage) { - imgNode.src = src; - } else { - imgNode.style.backgroundImage = 'url(' + src + ')'; - } - - return; - } - - if (window.innerWidth < minWindowWidth && !isImage) { - imgNode.style.backgroundImage = 'none'; - return; - } - - if (isAdaptive) { - size = getBreakpoint(sizes, presets); - } else { - if (isUpdate && !windowScreenBecomesBigger) return; - } - - const containerProps = determineContainerProps({ ...imgProps, imgNode, config, size }); - const generateURLbyDPR = devicePixelRatio => generateURL({ src, params, config, containerProps, devicePixelRatio }) - const cloudimageUrl = generateURLbyDPR(); - const cloudimageSrcset = devicePixelRatioList.map(dpr => ({ dpr: dpr.toString(), url: generateURLbyDPR(dpr) })); - const props = { - config, - isUpdate, - imgNode, - containerProps, - imgProps, - lazy, - blurHash, - cloudimageUrl, - isSVG, - src, - preserveSize, - isAdaptive - }; - - if (isImage) { - this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset }); - } else { - this.processBackgroundImage(props); - } - } - - processImage(props) { - const { config, isUpdate, imgNode, containerProps, imgProps, lazy, blurHash, cloudimageUrl, isSVG, src, preserveSize, cloudimageSrcset, isAdaptive } = props; - const { ratio } = containerProps; - const { dataSrcAttr } = config; - const wrapper = applyOrUpdateWrapper({ isUpdate, imgNode, ratio, ...imgProps }); - - if (!isUpdate) { - initImageClasses(imgNode, lazy); - initImageStyles(imgNode); - - if (config.destroyNodeImgSize) { - destroyNodeImgSize(imgNode); - } - - const canvas = applyOrUpdateBlurHashCanvas(wrapper, blurHash); - - imgNode.onload = () => { - if (config.onImageLoad && typeof config.onImageLoad === 'function') { - config.onImageLoad(imgNode); - } - onImageLoad({ wrapper, imgNode, canvas: blurHash && canvas, ratio, preserveSize, isAdaptive }) - }; - } - - setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr); - setSrc(imgNode, cloudimageUrl, null, lazy, src, isSVG, dataSrcAttr); - } - - processBackgroundImage(props) { - const { config, isUpdate, imgNode, lazy, blurHash, cloudimageUrl, isSVG, src } = props; - const { dataSrcAttr } = config; - - if (!isUpdate) { - initImageBackgroundClasses(imgNode, lazy); - initImageBackgroundStyles(imgNode); - - const canvas = applyOrUpdateBlurHashCanvas(imgNode, blurHash); - - if (!lazy) { - let tempImage = new Image(); - - tempImage.src = cloudimageUrl; - - tempImage.onload = () => { - finishAnimation(imgNode, blurHash && canvas); - }; - } - } - - setBackgroundSrc(imgNode, cloudimageUrl, lazy, src, isSVG, dataSrcAttr); - } -} +import { + destroyNodeImgSize, + getBackgroundImageProps, + getFreshCIElements, + getImageProps, + isLazy, + setBackgroundSrc, + setSrc, + setSrcset +} from '../common/ci.utils'; +import { determineContainerProps } from 'cloudimage-responsive-utils/dist/utils/determine-container-props'; +import { getImgSRC } from 'cloudimage-responsive-utils/dist/utils/get-img-src'; +import { generateURL } from 'cloudimage-responsive-utils/dist/utils/generate-url'; +import { getBreakpoint } from 'cloudimage-responsive-utils/dist/utils/get-breakpoint'; +import { isSupportedInBrowser } from 'cloudimage-responsive-utils/dist/utils/is-supported-in-browser'; +import { getInitialConfigBlurHash } from './ci.config'; +import { + applyOrUpdateBlurHashCanvas, + applyOrUpdateWrapper, + finishAnimation, + initImageBackgroundClasses, + initImageBackgroundStyles, + initImageClasses, + initImageStyles, + loadBackgroundImage, + onImageLoad +} from './ci.utils'; +import { debounce } from 'throttle-debounce'; + + +export default class CIResponsive { + constructor(config) { + this.config = getInitialConfigBlurHash(config); + + if (this.config.init) this.init(); + + this.innerWidth = window.innerWidth; + } + + init() { + document.addEventListener('lazybeforeunveil', loadBackgroundImage); + window.addEventListener('resize', debounce(100, this.onUpdateDimensions.bind(this))); + + this.process(); + } + + onUpdateDimensions() { + this.process(true); + + if (this.innerWidth < window.innerWidth) { + this.innerWidth = window.innerWidth; + } + } + + process(isUpdate, rootElement = document) { + const { imgSelector, bgSelector } = this.config; + const windowScreenBecomesBigger = this.innerWidth < window.innerWidth; + let [images, backgroundImages] = getFreshCIElements(isUpdate, rootElement, imgSelector, bgSelector); + + if (images.length > -1) { + images.forEach(imgNode => { + this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image'); + }); + } + + if (backgroundImages.length > -1) { + backgroundImages.forEach(imgNode => { + this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'background'); + }); + } + } + + getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type) => { + const isImage = type === 'image'; + const { config } = this; + const { baseURL, lazyLoading, presets, devicePixelRatioList, imgSelector, bgSelector } = config; + const imgProps = isImage ? + getImageProps(imgNode, imgSelector) : getBackgroundImageProps(imgNode, bgSelector); + const { params, imgNodeSRC, blurHash, isLazyCanceled, sizes, isAdaptive, preserveSize, minWindowWidth } = imgProps; + + if (!imgNodeSRC) return; + + const [src, isSVG] = getImgSRC(imgNodeSRC, baseURL); + const lazy = isLazy(lazyLoading, isLazyCanceled, isUpdate); + let size; + + if (!isSupportedInBrowser(true)) { + if (isImage) { + imgNode.src = src; + } else { + imgNode.style.backgroundImage = 'url(' + src + ')'; + } + + return; + } + + if (window.innerWidth < minWindowWidth && !isImage) { + imgNode.style.backgroundImage = 'none'; + return; + } + + if (isAdaptive) { + size = getBreakpoint(sizes, presets); + } else { + if (isUpdate && !windowScreenBecomesBigger) return; + } + + const containerProps = determineContainerProps({ ...imgProps, imgNode, config, size }); + const generateURLbyDPR = devicePixelRatio => generateURL({ src, params, config, containerProps, devicePixelRatio }) + const cloudimageUrl = generateURLbyDPR(); + const cloudimageSrcset = devicePixelRatioList.map(dpr => ({ dpr: dpr.toString(), url: generateURLbyDPR(dpr) })); + const props = { + config, + isUpdate, + imgNode, + containerProps, + imgProps, + lazy, + blurHash, + cloudimageUrl, + isSVG, + src, + preserveSize, + isAdaptive + }; + + if (isImage) { + this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset }); + } else { + this.processBackgroundImage(props); + } + } + + processImage(props) { + const { config, isUpdate, imgNode, containerProps, imgProps, lazy, blurHash, cloudimageUrl, isSVG, src, preserveSize, cloudimageSrcset, isAdaptive } = props; + const { ratio } = containerProps; + const { dataSrcAttr } = config; + const wrapper = applyOrUpdateWrapper({ isUpdate, imgNode, ratio, ...imgProps }); + + if (!isUpdate) { + initImageClasses(imgNode, lazy); + initImageStyles(imgNode); + + if (config.destroyNodeImgSize) { + destroyNodeImgSize(imgNode); + } + + const canvas = applyOrUpdateBlurHashCanvas(wrapper, blurHash); + + imgNode.onload = () => { + if (config.onImageLoad && typeof config.onImageLoad === 'function') { + config.onImageLoad(imgNode); + } + onImageLoad({ wrapper, imgNode, canvas: blurHash && canvas, ratio, preserveSize, isAdaptive }) + }; + } + + setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr); + setSrc(imgNode, cloudimageUrl, null, lazy, src, isSVG, dataSrcAttr); + } + + processBackgroundImage(props) { + const { config, isUpdate, imgNode, lazy, blurHash, cloudimageUrl, isSVG, src } = props; + const { dataSrcAttr } = config; + + if (!isUpdate) { + initImageBackgroundClasses(imgNode, lazy); + initImageBackgroundStyles(imgNode); + + const canvas = applyOrUpdateBlurHashCanvas(imgNode, blurHash); + + if (!lazy) { + let tempImage = new Image(); + + tempImage.src = cloudimageUrl; + + tempImage.onload = () => { + finishAnimation(imgNode, blurHash && canvas); + }; + } + } + + setBackgroundSrc(imgNode, cloudimageUrl, lazy, src, isSVG, dataSrcAttr); + } +} diff --git a/src/blur-hash/ci.utils.js b/src/blur-hash/ci.utils.js index ce82baf..51624c6 100644 --- a/src/blur-hash/ci.utils.js +++ b/src/blur-hash/ci.utils.js @@ -1,151 +1,151 @@ -import { addClass, getWrapper } from '../common/ci.utils'; -import { decode } from './blurHash'; - - -export const loadBackgroundImage = (event) => { - const bgContainer = event.target; - const bg = bgContainer.getAttribute('data-bg'); - const ciBlurHash = bgContainer.getAttribute('ci-blur-hash'); - - if (bg) { - let optimizedImage = new Image(); - - optimizedImage.onload = () => { - const bgCanvas = bgContainer.querySelector('canvas'); - - finishAnimation(bgContainer, ciBlurHash && bgCanvas); - bgContainer.removeAttribute('data-bg'); - bgContainer.removeAttribute('ci-preview'); - } - - optimizedImage.src = bg; - - bgContainer.style.backgroundImage = 'url(' + bg + ')'; - } -}; - -export const applyOrUpdateWrapper = props => { - const { isUpdate, imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize } = props; - let wrapper; - - if (!isUpdate) { - wrapper = wrapImage({ imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize }); - } else { - wrapper = getWrapper(imgNode); - - // TODO: remove in next release - // if (ratio) { - // wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; - // } - } - - return wrapper; -} - -export const wrapImage = (props) => { - const { imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize } = props; - let { wrapper } = props; - - wrapper = wrapper || document.createElement('div'); - - addClass(wrapper, 'ci-image-wrapper'); - wrapper.style.display = 'block'; - wrapper.style.width = preserveSize ? imgNodeWidth : '100%'; - wrapper.style.height = preserveSize ? imgNodeHeight : 'auto'; - wrapper.style.overflow = 'hidden'; - wrapper.style.position = 'relative'; - - if (ratio) { - wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; - } - - if (imgNode.nextSibling) { - imgNode.parentNode.insertBefore(wrapper, imgNode.nextSibling); - } else { - imgNode.parentNode.appendChild(wrapper); - } - - wrapper.appendChild(imgNode); - - return wrapper; -}; - -export const finishAnimation = (image, canvas) => { - if (canvas && canvas.style) { - canvas.style.opacity = '0'; - } - - addClass(image, 'ci-image-loaded'); -}; - -export const initImageBackgroundClasses = (image, lazy) => { - addClass(image, 'ci-bg'); - - if (lazy) { - addClass(image, 'lazyload'); - } -}; - -export const initImageBackgroundStyles = (image) => { - image.style.position = (!image.style.position || image.style.position === 'static') ? - 'relative' : image.style.position; -}; - -export const initImageClasses = (imgNode, lazy) => { - addClass(imgNode, 'ci-image'); - - if (lazy) { - addClass(imgNode, 'lazyload'); - } -}; - -export const initImageStyles = imgNode => { - imgNode.style.display = 'block'; - imgNode.style.width = '100%'; - imgNode.style.padding = '0'; - imgNode.style.position = 'absolute'; - imgNode.style.top = '0'; - imgNode.style.left = '0'; - imgNode.style.height = 'auto'; - imgNode.style.opacity = 1; -}; - -export const applyOrUpdateBlurHashCanvas = (wrapper, blurHash) => { - let canvas = wrapper.querySelector('canvas'); - - if (!canvas && blurHash) { - canvas = document.createElement("canvas"); - - const pixels = decode(blurHash, 32, 32); - canvas.width = 32; - canvas.height = 32; - const ctx = canvas.getContext("2d"); - const imageData = ctx.getImageData(0, 0, 32, 32); - imageData.data.set(pixels); - ctx.putImageData(imageData, 0, 0); - canvas.style.width = '100%'; - canvas.style.height = '100%'; - canvas.style.position = 'absolute'; - canvas.style.top = '0'; - canvas.style.bottom = '0'; - canvas.style.left = '0'; - canvas.style.right = '0'; - canvas.style.opacity = '1'; - canvas.style.zIndex = '1'; - canvas.style.transition = 'opacity 400ms ease 0ms'; - - wrapper.prepend(canvas); - } - - return canvas; -}; - -export const onImageLoad = ({ wrapper, imgNode, canvas, preserveSize, ratio, isAdaptive }) => { - wrapper.style.background = 'transparent'; - - if (!ratio || isAdaptive) { - wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ((imgNode.width / imgNode.height) || 1)) + '%'; - } - - finishAnimation(imgNode, canvas) +import { addClass, getWrapper } from '../common/ci.utils'; +import { decode } from './blurHash'; + + +export const loadBackgroundImage = (event) => { + const bgContainer = event.target; + const bg = bgContainer.getAttribute('data-bg'); + const ciBlurHash = bgContainer.getAttribute('ci-blur-hash'); + + if (bg) { + let optimizedImage = new Image(); + + optimizedImage.onload = () => { + const bgCanvas = bgContainer.querySelector('canvas'); + + finishAnimation(bgContainer, ciBlurHash && bgCanvas); + bgContainer.removeAttribute('data-bg'); + bgContainer.removeAttribute('ci-preview'); + } + + optimizedImage.src = bg; + + bgContainer.style.backgroundImage = 'url(' + bg + ')'; + } +}; + +export const applyOrUpdateWrapper = props => { + const { isUpdate, imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize } = props; + let wrapper; + + if (!isUpdate) { + wrapper = wrapImage({ imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize }); + } else { + wrapper = getWrapper(imgNode); + + // TODO: remove in next release + // if (ratio) { + // wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; + // } + } + + return wrapper; +} + +export const wrapImage = (props) => { + const { imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize } = props; + let { wrapper } = props; + + wrapper = wrapper || document.createElement('div'); + + addClass(wrapper, 'ci-image-wrapper'); + wrapper.style.display = 'block'; + wrapper.style.width = preserveSize ? imgNodeWidth : '100%'; + wrapper.style.height = preserveSize ? imgNodeHeight : 'auto'; + wrapper.style.overflow = 'hidden'; + wrapper.style.position = 'relative'; + + if (ratio) { + wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; + } + + if (imgNode.nextSibling) { + imgNode.parentNode.insertBefore(wrapper, imgNode.nextSibling); + } else { + imgNode.parentNode.appendChild(wrapper); + } + + wrapper.appendChild(imgNode); + + return wrapper; +}; + +export const finishAnimation = (image, canvas) => { + if (canvas && canvas.style) { + canvas.style.opacity = '0'; + } + + addClass(image, 'ci-image-loaded'); +}; + +export const initImageBackgroundClasses = (image, lazy) => { + addClass(image, 'ci-bg'); + + if (lazy) { + addClass(image, 'lazyload'); + } +}; + +export const initImageBackgroundStyles = (image) => { + image.style.position = (!image.style.position || image.style.position === 'static') ? + 'relative' : image.style.position; +}; + +export const initImageClasses = (imgNode, lazy) => { + addClass(imgNode, 'ci-image'); + + if (lazy) { + addClass(imgNode, 'lazyload'); + } +}; + +export const initImageStyles = imgNode => { + imgNode.style.display = 'block'; + imgNode.style.width = '100%'; + imgNode.style.padding = '0'; + imgNode.style.position = 'absolute'; + imgNode.style.top = '0'; + imgNode.style.left = '0'; + imgNode.style.height = 'auto'; + imgNode.style.opacity = 1; +}; + +export const applyOrUpdateBlurHashCanvas = (wrapper, blurHash) => { + let canvas = wrapper.querySelector('canvas'); + + if (!canvas && blurHash) { + canvas = document.createElement("canvas"); + + const pixels = decode(blurHash, 32, 32); + canvas.width = 32; + canvas.height = 32; + const ctx = canvas.getContext("2d"); + const imageData = ctx.getImageData(0, 0, 32, 32); + imageData.data.set(pixels); + ctx.putImageData(imageData, 0, 0); + canvas.style.width = '100%'; + canvas.style.height = '100%'; + canvas.style.position = 'absolute'; + canvas.style.top = '0'; + canvas.style.bottom = '0'; + canvas.style.left = '0'; + canvas.style.right = '0'; + canvas.style.opacity = '1'; + canvas.style.zIndex = '1'; + canvas.style.transition = 'opacity 400ms ease 0ms'; + + wrapper.prepend(canvas); + } + + return canvas; +}; + +export const onImageLoad = ({ wrapper, imgNode, canvas, preserveSize, ratio, isAdaptive }) => { + wrapper.style.background = 'transparent'; + + if (!ratio || isAdaptive) { + wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ((imgNode.width / imgNode.height) || 1)) + '%'; + } + + finishAnimation(imgNode, canvas) }; \ No newline at end of file diff --git a/src/blur-hash/index.js b/src/blur-hash/index.js index 1f1c7b1..5d149f1 100644 --- a/src/blur-hash/index.js +++ b/src/blur-hash/index.js @@ -1,6 +1,6 @@ -import 'core-js/features/typed-array/uint8-clamped-array'; -import './polyfills/prepend.polyfill'; -import CIResponsive from './ci.service'; - - +import 'core-js/features/typed-array/uint8-clamped-array'; +import './polyfills/prepend.polyfill'; +import CIResponsive from './ci.service'; + + window.CIResponsive = CIResponsive; \ No newline at end of file diff --git a/src/blur-hash/polyfills/prepend.polyfill.js b/src/blur-hash/polyfills/prepend.polyfill.js index 4d87e0c..4dd16c9 100644 --- a/src/blur-hash/polyfills/prepend.polyfill.js +++ b/src/blur-hash/polyfills/prepend.polyfill.js @@ -1,23 +1,23 @@ -(function (arr) { - arr.forEach(function (item) { - if (item.hasOwnProperty('prepend')) { - return; - } - Object.defineProperty(item, 'prepend', { - configurable: true, - enumerable: true, - writable: true, - value: function prepend() { - var argArr = Array.prototype.slice.call(arguments), - docFrag = document.createDocumentFragment(); - - argArr.forEach(function (argItem) { - var isNode = argItem instanceof Node; - docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem))); - }); - - this.insertBefore(docFrag, this.firstChild); - } - }); - }); +(function (arr) { + arr.forEach(function (item) { + if (item.hasOwnProperty('prepend')) { + return; + } + Object.defineProperty(item, 'prepend', { + configurable: true, + enumerable: true, + writable: true, + value: function prepend() { + var argArr = Array.prototype.slice.call(arguments), + docFrag = document.createDocumentFragment(); + + argArr.forEach(function (argItem) { + var isNode = argItem instanceof Node; + docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem))); + }); + + this.insertBefore(docFrag, this.firstChild); + } + }); + }); })([Element.prototype, Document.prototype, DocumentFragment.prototype]); \ No newline at end of file diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js index 3e86fce..076702a 100644 --- a/src/common/ci.utils.js +++ b/src/common/ci.utils.js @@ -1,190 +1,190 @@ -import { getParamsFromURL } from 'cloudimage-responsive-utils/dist/utils/get-params-from-url'; - - -export const filterImages = (images, type) => { - const filtered = []; - - for (let i = 0; i < images.length; i++) { - const image = images[i]; - const isProcessed = image.className.includes(type); - - if (!isProcessed) { - filtered.push(image); - } - } - - return filtered; -}; - -const getCommonImageProps = (image) => ({ - sizes: getSize(attr(image, 'ci-sizes') || attr(image, 'data-ci-size') || {}) || undefined, - params: getParams(attr(image, 'ci-params') || attr(image, 'data-ci-params') || {}), - imgNodeRatio: attr(image, 'ci-ratio') || attr(image, 'data-ci-ratio') || undefined, - blurHash: attr(image, 'ci-blur-hash') || attr(image, 'data-ci-blur-hash') || undefined, - isLazyCanceled: (attr(image, 'ci-not-lazy') !== null || attr(image, 'data-ci-not-lazy') !== null) || undefined, - preserveSize: (attr(image, 'ci-preserve-size') !== null || attr(image, 'data-preserve-size') !== null) || undefined, - imgNodeWidth: attr(image, 'width'), - imgNodeHeight: attr(image, 'height') -}); - -export const getParams = (params) => { - let resultParams = undefined; - - try { - let temp = params.replace(/(\w+:)|(\w+ :)/g, function (matchedStr) { - return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":'; - }); - - resultParams = JSON.parse(temp); - } catch (e) {} - - if (!resultParams) { - try { - resultParams = JSON.parse('{"' + decodeURI(params.replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}'); - } catch (e) {} - } - - return resultParams; -} - -const getSize = (sizes) => { - let resultSizes = null; - - try { - // add quotes around params - let temp = sizes.replace(/(\w+:)|(\w+ :)/g, function (matchedStr) { - if(matchedStr === 'https:' || matchedStr === 'http:'){ - return matchedStr - }else { - return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":'; - } - }); - // change single quotes to double quotes - temp = temp.replace(/'/g, '"').replace(/-"width":/g, '-width:'); - resultSizes = JSON.parse(temp); - } catch (e) {} - - if (resultSizes) { - Object.keys(resultSizes).forEach(key => { - if (typeof resultSizes[key] === 'string') { - try { - resultSizes[key] = JSON.parse('{"' + decodeURI(resultSizes[key].replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}'); - } catch (e) {} - } - }); - } - - return resultSizes; -} - -export const getImageProps = (image, imgSelector) => { - const props = { - ...getCommonImageProps(image), - imgNodeSRC: attr(image, imgSelector) || undefined - }; - const params = { - ...getParamsFromURL(props.imgNodeSRC || ''), - ...props.params - }; - - return { - ...props, - params, - isAdaptive: !!props.sizes, - imgNodeSRC: getURLWithoutQueryParams(props.imgNodeSRC) - }; -}; - -export const getBackgroundImageProps = (image, bgSelector) => { - const props = { - ...getCommonImageProps(image), - imgNodeSRC: attr(image, bgSelector)|| undefined, - minWindowWidth: attr(image, 'ci-min-window-width') || attr(image, 'data-min-window-width') || undefined - }; - const params = { - ...getParamsFromURL(props.imgNodeSRC || ''), - ...props.params - }; - - return { - ...props, - params, - isAdaptive: !!props.sizes, - imgNodeSRC: getURLWithoutQueryParams(props.imgNodeSRC) - }; -}; - -const getURLWithoutQueryParams = (url = '') => url.split('?')[0]; - -const attr = (element, attribute) => element.getAttribute(attribute); - -export const addClass = (elem, className) => { - if (!(elem.className.indexOf(className) > -1)) { - elem.className += ' ' + className; - } -}; - -export const getWrapper = (image) => { - if ((image.parentNode.className || '').indexOf('ci-image-wrapper') > -1) { - return image.parentNode; - } else if ((image.parentNode.parentNode.className || '').indexOf('ci-image-wrapper') > -1) { - return image.parentNode.parentNode; - } -}; - -export const isLazy = (lazyLoading, isLazyCanceled, isUpdate) => { - if ((isLazyCanceled && lazyLoading) || isUpdate) { - lazyLoading = false; - } - - return lazyLoading; -}; - -export const setSrc = (image, url, propertyName, lazy, imgSrc, isSVG, dataSrcAttr) => { - image.setAttribute( - lazy ? (propertyName ? propertyName : 'data-src') : (dataSrcAttr ? dataSrcAttr : 'src'), - isSVG ? imgSrc : url - ); -}; - -export const setSrcset = (image, urls, propertyName, lazy, imgSrc, isSVG, dataSrcAttr) => { - if (isSVG) return; - - image.setAttribute( - lazy ? (propertyName ? propertyName : 'data-srcset') : (dataSrcAttr ? dataSrcAttr : 'srcset'), - urls.map(({ dpr, url }) => `${url} ${dpr}x`).join(', ') - ); -}; - -export const setBackgroundSrc = (image, url, lazy, imgSrc, isSVG, dataSrcAttr) => { - const resultLink = isSVG ? imgSrc : url; - - if (lazy) { - image.setAttribute((dataSrcAttr ? dataSrcAttr : 'data-bg'), resultLink); - } else { - image.style.backgroundImage = `url('${resultLink}')` - } -}; - -export const getFreshCIElements = (isUpdate, rootElement, imgSelector, bgSelector) => { - let images, backgroundImages; - - if (rootElement !== document && !(rootElement instanceof HTMLElement)) { - throw new TypeError('rootElement should be an HTMLElement'); - } - - if (isUpdate) { - images = rootElement.querySelectorAll(`img[${imgSelector}]`); - backgroundImages = rootElement.querySelectorAll(`[${bgSelector}]`); - } else { - images = filterImages(rootElement.querySelectorAll(`img[${imgSelector}]`), 'ci-image'); - backgroundImages = filterImages(rootElement.querySelectorAll(`[${bgSelector}]`), 'ci-bg'); - } - - return [images, backgroundImages]; -}; - -export const destroyNodeImgSize = imgNode => { - imgNode.removeAttribute("height"); - imgNode.removeAttribute("width"); -}; +import { getParamsFromURL } from 'cloudimage-responsive-utils/dist/utils/get-params-from-url'; + + +export const filterImages = (images, type) => { + const filtered = []; + + for (let i = 0; i < images.length; i++) { + const image = images[i]; + const isProcessed = image.className.includes(type); + + if (!isProcessed) { + filtered.push(image); + } + } + + return filtered; +}; + +const getCommonImageProps = (image) => ({ + sizes: getSize(attr(image, 'ci-sizes') || attr(image, 'data-ci-size') || {}) || undefined, + params: getParams(attr(image, 'ci-params') || attr(image, 'data-ci-params') || {}), + imgNodeRatio: attr(image, 'ci-ratio') || attr(image, 'data-ci-ratio') || undefined, + blurHash: attr(image, 'ci-blur-hash') || attr(image, 'data-ci-blur-hash') || undefined, + isLazyCanceled: (attr(image, 'ci-not-lazy') !== null || attr(image, 'data-ci-not-lazy') !== null) || undefined, + preserveSize: (attr(image, 'ci-preserve-size') !== null || attr(image, 'data-preserve-size') !== null) || undefined, + imgNodeWidth: attr(image, 'width'), + imgNodeHeight: attr(image, 'height') +}); + +export const getParams = (params) => { + let resultParams = undefined; + + try { + let temp = params.replace(/(\w+:)|(\w+ :)/g, function (matchedStr) { + return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":'; + }); + + resultParams = JSON.parse(temp); + } catch (e) {} + + if (!resultParams) { + try { + resultParams = JSON.parse('{"' + decodeURI(params.replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}'); + } catch (e) {} + } + + return resultParams; +} + +const getSize = (sizes) => { + let resultSizes = null; + + try { + // add quotes around params + let temp = sizes.replace(/(\w+:)|(\w+ :)/g, function (matchedStr) { + if(matchedStr === 'https:' || matchedStr === 'http:'){ + return matchedStr + }else { + return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":'; + } + }); + // change single quotes to double quotes + temp = temp.replace(/'/g, '"').replace(/-"width":/g, '-width:'); + resultSizes = JSON.parse(temp); + } catch (e) {} + + if (resultSizes) { + Object.keys(resultSizes).forEach(key => { + if (typeof resultSizes[key] === 'string') { + try { + resultSizes[key] = JSON.parse('{"' + decodeURI(resultSizes[key].replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}'); + } catch (e) {} + } + }); + } + + return resultSizes; +} + +export const getImageProps = (image, imgSelector) => { + const props = { + ...getCommonImageProps(image), + imgNodeSRC: attr(image, imgSelector) || undefined + }; + const params = { + ...getParamsFromURL(props.imgNodeSRC || ''), + ...props.params + }; + + return { + ...props, + params, + isAdaptive: !!props.sizes, + imgNodeSRC: getURLWithoutQueryParams(props.imgNodeSRC) + }; +}; + +export const getBackgroundImageProps = (image, bgSelector) => { + const props = { + ...getCommonImageProps(image), + imgNodeSRC: attr(image, bgSelector)|| undefined, + minWindowWidth: attr(image, 'ci-min-window-width') || attr(image, 'data-min-window-width') || undefined + }; + const params = { + ...getParamsFromURL(props.imgNodeSRC || ''), + ...props.params + }; + + return { + ...props, + params, + isAdaptive: !!props.sizes, + imgNodeSRC: getURLWithoutQueryParams(props.imgNodeSRC) + }; +}; + +const getURLWithoutQueryParams = (url = '') => url.split('?')[0]; + +const attr = (element, attribute) => element.getAttribute(attribute); + +export const addClass = (elem, className) => { + if (!(elem.className.indexOf(className) > -1)) { + elem.className += ' ' + className; + } +}; + +export const getWrapper = (image) => { + if ((image.parentNode.className || '').indexOf('ci-image-wrapper') > -1) { + return image.parentNode; + } else if ((image.parentNode.parentNode.className || '').indexOf('ci-image-wrapper') > -1) { + return image.parentNode.parentNode; + } +}; + +export const isLazy = (lazyLoading, isLazyCanceled, isUpdate) => { + if ((isLazyCanceled && lazyLoading) || isUpdate) { + lazyLoading = false; + } + + return lazyLoading; +}; + +export const setSrc = (image, url, propertyName, lazy, imgSrc, isSVG, dataSrcAttr) => { + image.setAttribute( + lazy ? (propertyName ? propertyName : 'data-src') : (dataSrcAttr ? dataSrcAttr : 'src'), + isSVG ? imgSrc : url + ); +}; + +export const setSrcset = (image, urls, propertyName, lazy, imgSrc, isSVG, dataSrcAttr) => { + if (isSVG) return; + + image.setAttribute( + lazy ? (propertyName ? propertyName : 'data-srcset') : (dataSrcAttr ? dataSrcAttr : 'srcset'), + urls.map(({ dpr, url }) => `${url} ${dpr}x`).join(', ') + ); +}; + +export const setBackgroundSrc = (image, url, lazy, imgSrc, isSVG, dataSrcAttr) => { + const resultLink = isSVG ? imgSrc : url; + + if (lazy) { + image.setAttribute((dataSrcAttr ? dataSrcAttr : 'data-bg'), resultLink); + } else { + image.style.backgroundImage = `url('${resultLink}')` + } +}; + +export const getFreshCIElements = (isUpdate, rootElement, imgSelector, bgSelector) => { + let images, backgroundImages; + + if (rootElement !== document && !(rootElement instanceof HTMLElement)) { + throw new TypeError('rootElement should be an HTMLElement'); + } + + if (isUpdate) { + images = rootElement.querySelectorAll(`img[${imgSelector}]`); + backgroundImages = rootElement.querySelectorAll(`[${bgSelector}]`); + } else { + images = filterImages(rootElement.querySelectorAll(`img[${imgSelector}]`), 'ci-image'); + backgroundImages = filterImages(rootElement.querySelectorAll(`[${bgSelector}]`), 'ci-bg'); + } + + return [images, backgroundImages]; +}; + +export const destroyNodeImgSize = imgNode => { + imgNode.removeAttribute("height"); + imgNode.removeAttribute("width"); +}; diff --git a/src/lazysizes-intersection.js b/src/lazysizes-intersection.js index 730c4d1..706a330 100644 --- a/src/lazysizes-intersection.js +++ b/src/lazysizes-intersection.js @@ -1,544 +1,544 @@ -(function(window, factory) { - window.lazySizes = factory(window, window.document); -}(window, function l(window, document) { - 'use strict'; - - /*jshint eqnull:true */ - if(!window.IntersectionObserver || !document.getElementsByClassName || !window.MutationObserver){return;} - - var lazysizes, lazySizesConfig; - - var docElem = document.documentElement; - - var Date = window.Date; - - var supportPicture = window.HTMLPictureElement; - - var _addEventListener = 'addEventListener'; - - var _getAttribute = 'getAttribute'; - - var addEventListener = window[_addEventListener]; - - 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 || lazySizesConfig.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 < lazySizesConfig.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, lazySizesConfig.loadedClass); - removeClass(e.target, lazySizesConfig.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](lazySizesConfig.srcsetAttr); - - if( (customMedia = lazySizesConfig.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, lazySizesConfig.autosizesClass); - } else { - elem.setAttribute('sizes', sizes); - } - } - - srcset = elem[_getAttribute](lazySizesConfig.srcsetAttr); - src = elem[_getAttribute](lazySizesConfig.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, lazySizesConfig.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, lazySizesConfig.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](lazySizesConfig.sizesAttr) || elem[_getAttribute]('sizes')); - var isAuto = sizes == 'auto'; - - if( (isAuto || !isCompleted) && isImg && (elem.src || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesConfig.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, lazySizesConfig.lazyWaitClass); - removeClass(element, lazySizesConfig.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(lazySizesConfig.lazyClass); - - inviewObserver = new IntersectionObserver(unveilElements); - preloadObserver = new IntersectionObserver(lazyUnveilElement, { - rootMargin: lazySizesConfig.expand + 'px ' + (lazySizesConfig.expand * lazySizesConfig.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(lazySizesConfig.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, - }; - - lazySizesConfig = window.lazySizesConfig || window.lazysizesConfig || {}; - - for(prop in lazySizesDefaults){ - if(!(prop in lazySizesConfig)){ - lazySizesConfig[prop] = lazySizesDefaults[prop]; - } - } - - window.lazySizesConfig = lazySizesConfig; - - setTimeout(function(){ - if(lazySizesConfig.init){ - init(); - } - }); - })(); - - lazysizes = { - cfg: lazySizesConfig, - autoSizer: autoSizer, - loader: loader, - init: init, - uP: updatePolyfill, - aC: addClass, - rC: removeClass, - hC: hasClass, - fire: triggerEvent, - gW: getWidth, - rAF: rAF, - }; - - return lazysizes; +(function(window, factory) { + window.lazySizes = factory(window, window.document); +}(window, function l(window, document) { + 'use strict'; + + /*jshint eqnull:true */ + if(!window.IntersectionObserver || !document.getElementsByClassName || !window.MutationObserver){return;} + + var lazysizes, lazySizesConfig; + + var docElem = document.documentElement; + + var Date = window.Date; + + var supportPicture = window.HTMLPictureElement; + + var _addEventListener = 'addEventListener'; + + var _getAttribute = 'getAttribute'; + + var addEventListener = window[_addEventListener]; + + 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 || lazySizesConfig.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 < lazySizesConfig.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, lazySizesConfig.loadedClass); + removeClass(e.target, lazySizesConfig.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](lazySizesConfig.srcsetAttr); + + if( (customMedia = lazySizesConfig.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, lazySizesConfig.autosizesClass); + } else { + elem.setAttribute('sizes', sizes); + } + } + + srcset = elem[_getAttribute](lazySizesConfig.srcsetAttr); + src = elem[_getAttribute](lazySizesConfig.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, lazySizesConfig.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, lazySizesConfig.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](lazySizesConfig.sizesAttr) || elem[_getAttribute]('sizes')); + var isAuto = sizes == 'auto'; + + if( (isAuto || !isCompleted) && isImg && (elem.src || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesConfig.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, lazySizesConfig.lazyWaitClass); + removeClass(element, lazySizesConfig.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(lazySizesConfig.lazyClass); + + inviewObserver = new IntersectionObserver(unveilElements); + preloadObserver = new IntersectionObserver(lazyUnveilElement, { + rootMargin: lazySizesConfig.expand + 'px ' + (lazySizesConfig.expand * lazySizesConfig.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(lazySizesConfig.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, + }; + + lazySizesConfig = window.lazySizesConfig || window.lazysizesConfig || {}; + + for(prop in lazySizesDefaults){ + if(!(prop in lazySizesConfig)){ + lazySizesConfig[prop] = lazySizesDefaults[prop]; + } + } + + window.lazySizesConfig = lazySizesConfig; + + setTimeout(function(){ + if(lazySizesConfig.init){ + init(); + } + }); + })(); + + lazysizes = { + cfg: lazySizesConfig, + autoSizer: autoSizer, + loader: loader, + init: init, + uP: updatePolyfill, + aC: addClass, + rC: removeClass, + hC: hasClass, + fire: triggerEvent, + gW: getWidth, + rAF: rAF, + }; + + return lazysizes; })); \ No newline at end of file diff --git a/src/low-preview/ci.config.js b/src/low-preview/ci.config.js index 62bc062..75c4811 100644 --- a/src/low-preview/ci.config.js +++ b/src/low-preview/ci.config.js @@ -1,78 +1,78 @@ -import { getParams } from '../common/ci.utils'; -import { DEVICE_PIXEL_RATIO_LIST } from 'cloudimage-responsive-utils/dist/constants'; - - -export const getInitialConfigLowPreview = (config) => { - const { - imgSelector = 'ci-src', - bgSelector = 'ci-bg-url', - token = '', - domain = 'cloudimg.io', - lazyLoading = false, - imgLoadingAnimation = true, - placeholderBackground = '#f4f4f4', - baseUrl, // to support old name - baseURL, - ratio, - presets, - params = 'org_if_sml=1', - apiVersion = 'v7', - init = true, - exactSize = false, - doNotReplaceURL = false, - limitFactor = 100, - ignoreNodeImgSize, - imageSizeAttributes = 'use', - ignoreStyleImgSize = false, - destroyNodeImgSize = false, - saveNodeImgRatio = false, - detectImageNodeCSS = false, - processOnlyWidth = false, - devicePixelRatioList = DEVICE_PIXEL_RATIO_LIST, - lowQualityPreview: { - minImgWidth = 400 - } = {}, - - // callback - onImageLoad = null - } = config; - - return { - imgSelector, - bgSelector, - token, - domain, - lazyLoading, - imgLoadingAnimation, - placeholderBackground, - baseURL: baseUrl || baseURL, - ratio, - exactSize, - presets: presets ? presets : - { - xs: '(max-width: 575px)', // to 575 PHONE - sm: '(min-width: 576px)', // 576 - 767 PHABLET - md: '(min-width: 768px)', // 768 - 991 TABLET - lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN - xl: '(min-width: 1200px)' // from 1200 USUALSCREEN - }, - params: getParams(params), - apiVersion, - innerWidth: window.innerWidth, - init, - previewQualityFactor: 10, - doNotReplaceURL, - devicePixelRatioList, - limitFactor, - minLowQualityWidth: minImgWidth, - ignoreNodeImgSize, - ignoreStyleImgSize, - imageSizeAttributes, - destroyNodeImgSize, - saveNodeImgRatio, - detectImageNodeCSS, - processOnlyWidth, - onImageLoad - //isChrome: /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor) - }; -}; +import { getParams } from '../common/ci.utils'; +import { DEVICE_PIXEL_RATIO_LIST } from 'cloudimage-responsive-utils/dist/constants'; + + +export const getInitialConfigLowPreview = (config) => { + const { + imgSelector = 'ci-src', + bgSelector = 'ci-bg-url', + token = '', + domain = 'cloudimg.io', + lazyLoading = false, + imgLoadingAnimation = true, + placeholderBackground = '#f4f4f4', + baseUrl, // to support old name + baseURL, + ratio, + presets, + params = 'org_if_sml=1', + apiVersion = 'v7', + init = true, + exactSize = false, + doNotReplaceURL = false, + limitFactor = 100, + ignoreNodeImgSize, + imageSizeAttributes = 'use', + ignoreStyleImgSize = false, + destroyNodeImgSize = false, + saveNodeImgRatio = false, + detectImageNodeCSS = false, + processOnlyWidth = false, + devicePixelRatioList = DEVICE_PIXEL_RATIO_LIST, + lowQualityPreview: { + minImgWidth = 400 + } = {}, + + // callback + onImageLoad = null + } = config; + + return { + imgSelector, + bgSelector, + token, + domain, + lazyLoading, + imgLoadingAnimation, + placeholderBackground, + baseURL: baseUrl || baseURL, + ratio, + exactSize, + presets: presets ? presets : + { + xs: '(max-width: 575px)', // to 575 PHONE + sm: '(min-width: 576px)', // 576 - 767 PHABLET + md: '(min-width: 768px)', // 768 - 991 TABLET + lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN + xl: '(min-width: 1200px)' // from 1200 USUALSCREEN + }, + params: getParams(params), + apiVersion, + innerWidth: window.innerWidth, + init, + previewQualityFactor: 10, + doNotReplaceURL, + devicePixelRatioList, + limitFactor, + minLowQualityWidth: minImgWidth, + ignoreNodeImgSize, + ignoreStyleImgSize, + imageSizeAttributes, + destroyNodeImgSize, + saveNodeImgRatio, + detectImageNodeCSS, + processOnlyWidth, + onImageLoad + //isChrome: /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor) + }; +}; diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 33e9c4e..2d17ffd 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -1,215 +1,215 @@ -import { - destroyNodeImgSize, - getBackgroundImageProps, - getFreshCIElements, - getImageProps, - isLazy, - setBackgroundSrc, - setSrc, - setSrcset -} from '../common/ci.utils'; -import { isLowQualityPreview } from 'cloudimage-responsive-utils/dist/utils/is-low-quality-preview'; -import { determineContainerProps } from 'cloudimage-responsive-utils/dist/utils/determine-container-props'; -import { getImgSRC } from 'cloudimage-responsive-utils/dist/utils/get-img-src'; -import { generateURL } from 'cloudimage-responsive-utils/dist/utils/generate-url'; -import { getPreviewSRC } from 'cloudimage-responsive-utils/dist/utils/get-preview-src'; -import { getBreakpoint } from 'cloudimage-responsive-utils/dist/utils/get-breakpoint'; -import { isSupportedInBrowser } from 'cloudimage-responsive-utils/dist/utils/is-supported-in-browser'; -import { getInitialConfigLowPreview } from './ci.config'; -import { - applyBackgroundStyles, - applyOrUpdateWrapper, - initImageClasses, - loadBackgroundImage, - onImageLoad, - onLazyBeforeUnveil, - onPreviewImageLoad, - setAnimation, - wrapBackgroundContainer, - updateSizeWithPixelRatio -} from './ci.utis'; -import { debounce } from 'throttle-debounce'; - - -export default class CIResponsive { - constructor(config) { - this.config = getInitialConfigLowPreview(config); - - if (this.config.init) this.init(); - - this.innerWidth = window.innerWidth; - } - - init() { - document.addEventListener('lazybeforeunveil', onLazyBeforeUnveil); - window.addEventListener('resize', debounce(100, this.onUpdateDimensions.bind(this))); - - this.process(); - } - - onUpdateDimensions() { - this.process(true); - - if (this.innerWidth < window.innerWidth) { - this.innerWidth = window.innerWidth; - } - } - - process(isUpdate, rootElement = document) { - const { imgSelector, bgSelector } = this.config; - const windowScreenBecomesBigger = this.innerWidth < window.innerWidth; - let [images, backgroundImages] = getFreshCIElements(isUpdate, rootElement, imgSelector, bgSelector); - - if (images.length > -1) { - images.forEach(imgNode => { - this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image'); - }); - } - - if (backgroundImages.length > -1) { - backgroundImages.forEach(imgNode => { - this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'background'); - }); - } - } - - getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type) => { - const isImage = type === 'image'; - const { config } = this; - const { baseURL, lazyLoading, presets, devicePixelRatioList, minLowQualityWidth, imgSelector, bgSelector } = config; - const imgProps = isImage ? - getImageProps(imgNode, imgSelector) : getBackgroundImageProps(imgNode, bgSelector); - const { params, imgNodeSRC, isLazyCanceled, sizes, isAdaptive, preserveSize, minWindowWidth } = imgProps; - - if (!imgNodeSRC) return; - - let [src, isSVG] = getImgSRC(imgNodeSRC, baseURL); - const lazy = isLazy(lazyLoading, isLazyCanceled, isUpdate); - let size; - - if (!isSupportedInBrowser(true)) { - if (isImage) { - imgNode.src = src; - } else { - imgNode.style.backgroundImage = 'url(' + src + ')'; - } - - return; - } - - if (window.innerWidth < minWindowWidth && !isImage) { - imgNode.style.backgroundImage = 'none'; - return; - } - - if (isAdaptive) { - size = getBreakpoint(sizes, presets); - if(size){ - if(size.params.src){ - [src, isSVG] = getImgSRC(size.params.src, baseURL); - } - } - } else { - if (isUpdate && !windowScreenBecomesBigger) return; - } - - const containerProps = determineContainerProps({ ...imgProps, size, imgNode, config }); - const { width } = containerProps; - const isPreview = isLowQualityPreview(isAdaptive, width, isSVG, minLowQualityWidth); - const generateURLbyDPR = devicePixelRatio => generateURL({ src, params, config, containerProps, devicePixelRatio }) - const cloudimageUrl = generateURLbyDPR(); - const cloudimageSrcset = devicePixelRatioList.map(dpr => ({ dpr: dpr.toString(), url: generateURLbyDPR(dpr) })); - const props = { - imgNode, isUpdate, imgProps, lazy, isPreview, containerProps, isSVG, cloudimageUrl, src, preserveSize, isAdaptive - }; - - if (isImage) { - this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset }); - } else { - this.processBackgroundImage(props); - } - } - - processImage(props) { - const { - imgNode, - isUpdate, - imgProps, - lazy, - isPreview, - containerProps, - isSVG, - cloudimageUrl, - src, - preserveSize, - cloudimageSrcset, - isAdaptive - } = props; - const { params } = imgProps; - const { width, ratio } = containerProps; - const { config } = this; - const { dataSrcAttr, placeholderBackground } = config; - const { wrapper, previewImgNode, previewWrapper } = applyOrUpdateWrapper( - { isUpdate, imgNode, ratio, lazy, placeholderBackground, preserveSize, isPreview, ...imgProps } - ); - - if (!isUpdate) { - initImageClasses({ imgNode, lazy }); - - if (config.destroyNodeImgSize) { - destroyNodeImgSize(imgNode); - } - - if (isPreview) { - const previewImgURL = getPreviewSRC({ src, params, config, containerProps }); - - setAnimation(previewWrapper, previewImgNode, updateSizeWithPixelRatio(width)); - setSrc(previewImgNode, previewImgURL, 'data-src', lazy, src, isSVG, dataSrcAttr); - - previewImgNode.onload = () => { - onPreviewImageLoad(wrapper, previewImgNode, ratio, preserveSize); - } - } - } - - imgNode.onload = () => { - if (config.onImageLoad && typeof config.onImageLoad === 'function') { - config.onImageLoad(imgNode); - } - onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive); - }; - - setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr); - setSrc(imgNode, cloudimageUrl, 'data-src', lazy, src, isSVG, dataSrcAttr); - } - - processBackgroundImage(props) { - const { imgNode, isUpdate, imgProps, lazy, isPreview, containerProps, isSVG, cloudimageUrl, src } = props; - const { params } = imgProps; - const { width } = containerProps; - const { config } = this; - const { dataSrcAttr } = config; - - if (!isUpdate) { - if (isPreview) { - const previewImgURL = getPreviewSRC({ src, params, config, containerProps }); - const [previewBox, contentBox] = wrapBackgroundContainer(imgNode); - - applyBackgroundStyles({ imgNode, previewBox, contentBox, lazy, width }); - - if (lazy) { - imgNode.setAttribute('ci-optimized-url', cloudimageUrl); - - setBackgroundSrc(previewBox, previewImgURL, lazy, src, isSVG, dataSrcAttr); - } else { - loadBackgroundImage(previewImgURL, isPreview, previewBox, cloudimageUrl); - } - } else { - imgNode.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`; - loadBackgroundImage(cloudimageUrl, false, imgNode, null); - } - } else { - setBackgroundSrc(imgNode, cloudimageUrl, lazy, src, isSVG, dataSrcAttr); - } - } -} +import { + destroyNodeImgSize, + getBackgroundImageProps, + getFreshCIElements, + getImageProps, + isLazy, + setBackgroundSrc, + setSrc, + setSrcset +} from '../common/ci.utils'; +import { isLowQualityPreview } from 'cloudimage-responsive-utils/dist/utils/is-low-quality-preview'; +import { determineContainerProps } from 'cloudimage-responsive-utils/dist/utils/determine-container-props'; +import { getImgSRC } from 'cloudimage-responsive-utils/dist/utils/get-img-src'; +import { generateURL } from 'cloudimage-responsive-utils/dist/utils/generate-url'; +import { getPreviewSRC } from 'cloudimage-responsive-utils/dist/utils/get-preview-src'; +import { getBreakpoint } from 'cloudimage-responsive-utils/dist/utils/get-breakpoint'; +import { isSupportedInBrowser } from 'cloudimage-responsive-utils/dist/utils/is-supported-in-browser'; +import { getInitialConfigLowPreview } from './ci.config'; +import { + applyBackgroundStyles, + applyOrUpdateWrapper, + initImageClasses, + loadBackgroundImage, + onImageLoad, + onLazyBeforeUnveil, + onPreviewImageLoad, + setAnimation, + wrapBackgroundContainer, + updateSizeWithPixelRatio +} from './ci.utis'; +import { debounce } from 'throttle-debounce'; + + +export default class CIResponsive { + constructor(config) { + this.config = getInitialConfigLowPreview(config); + + if (this.config.init) this.init(); + + this.innerWidth = window.innerWidth; + } + + init() { + document.addEventListener('lazybeforeunveil', onLazyBeforeUnveil); + window.addEventListener('resize', debounce(100, this.onUpdateDimensions.bind(this))); + + this.process(); + } + + onUpdateDimensions() { + this.process(true); + + if (this.innerWidth < window.innerWidth) { + this.innerWidth = window.innerWidth; + } + } + + process(isUpdate, rootElement = document) { + const { imgSelector, bgSelector } = this.config; + const windowScreenBecomesBigger = this.innerWidth < window.innerWidth; + let [images, backgroundImages] = getFreshCIElements(isUpdate, rootElement, imgSelector, bgSelector); + + if (images.length > -1) { + images.forEach(imgNode => { + this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image'); + }); + } + + if (backgroundImages.length > -1) { + backgroundImages.forEach(imgNode => { + this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'background'); + }); + } + } + + getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type) => { + const isImage = type === 'image'; + const { config } = this; + const { baseURL, lazyLoading, presets, devicePixelRatioList, minLowQualityWidth, imgSelector, bgSelector } = config; + const imgProps = isImage ? + getImageProps(imgNode, imgSelector) : getBackgroundImageProps(imgNode, bgSelector); + const { params, imgNodeSRC, isLazyCanceled, sizes, isAdaptive, preserveSize, minWindowWidth } = imgProps; + + if (!imgNodeSRC) return; + + let [src, isSVG] = getImgSRC(imgNodeSRC, baseURL); + const lazy = isLazy(lazyLoading, isLazyCanceled, isUpdate); + let size; + + if (!isSupportedInBrowser(true)) { + if (isImage) { + imgNode.src = src; + } else { + imgNode.style.backgroundImage = 'url(' + src + ')'; + } + + return; + } + + if (window.innerWidth < minWindowWidth && !isImage) { + imgNode.style.backgroundImage = 'none'; + return; + } + + if (isAdaptive) { + size = getBreakpoint(sizes, presets); + if(size){ + if(size.params.src){ + [src, isSVG] = getImgSRC(size.params.src, baseURL); + } + } + } else { + if (isUpdate && !windowScreenBecomesBigger) return; + } + + const containerProps = determineContainerProps({ ...imgProps, size, imgNode, config }); + const { width } = containerProps; + const isPreview = isLowQualityPreview(isAdaptive, width, isSVG, minLowQualityWidth); + const generateURLbyDPR = devicePixelRatio => generateURL({ src, params, config, containerProps, devicePixelRatio }) + const cloudimageUrl = generateURLbyDPR(); + const cloudimageSrcset = devicePixelRatioList.map(dpr => ({ dpr: dpr.toString(), url: generateURLbyDPR(dpr) })); + const props = { + imgNode, isUpdate, imgProps, lazy, isPreview, containerProps, isSVG, cloudimageUrl, src, preserveSize, isAdaptive + }; + + if (isImage) { + this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset }); + } else { + this.processBackgroundImage(props); + } + } + + processImage(props) { + const { + imgNode, + isUpdate, + imgProps, + lazy, + isPreview, + containerProps, + isSVG, + cloudimageUrl, + src, + preserveSize, + cloudimageSrcset, + isAdaptive + } = props; + const { params } = imgProps; + const { width, ratio } = containerProps; + const { config } = this; + const { dataSrcAttr, placeholderBackground } = config; + const { wrapper, previewImgNode, previewWrapper } = applyOrUpdateWrapper( + { isUpdate, imgNode, ratio, lazy, placeholderBackground, preserveSize, isPreview, ...imgProps } + ); + + if (!isUpdate) { + initImageClasses({ imgNode, lazy }); + + if (config.destroyNodeImgSize) { + destroyNodeImgSize(imgNode); + } + + if (isPreview) { + const previewImgURL = getPreviewSRC({ src, params, config, containerProps }); + + setAnimation(previewWrapper, previewImgNode, updateSizeWithPixelRatio(width)); + setSrc(previewImgNode, previewImgURL, 'data-src', lazy, src, isSVG, dataSrcAttr); + + previewImgNode.onload = () => { + onPreviewImageLoad(wrapper, previewImgNode, ratio, preserveSize); + } + } + } + + imgNode.onload = () => { + if (config.onImageLoad && typeof config.onImageLoad === 'function') { + config.onImageLoad(imgNode); + } + onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive); + }; + + setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr); + setSrc(imgNode, cloudimageUrl, 'data-src', lazy, src, isSVG, dataSrcAttr); + } + + processBackgroundImage(props) { + const { imgNode, isUpdate, imgProps, lazy, isPreview, containerProps, isSVG, cloudimageUrl, src } = props; + const { params } = imgProps; + const { width } = containerProps; + const { config } = this; + const { dataSrcAttr } = config; + + if (!isUpdate) { + if (isPreview) { + const previewImgURL = getPreviewSRC({ src, params, config, containerProps }); + const [previewBox, contentBox] = wrapBackgroundContainer(imgNode); + + applyBackgroundStyles({ imgNode, previewBox, contentBox, lazy, width }); + + if (lazy) { + imgNode.setAttribute('ci-optimized-url', cloudimageUrl); + + setBackgroundSrc(previewBox, previewImgURL, lazy, src, isSVG, dataSrcAttr); + } else { + loadBackgroundImage(previewImgURL, isPreview, previewBox, cloudimageUrl); + } + } else { + imgNode.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`; + loadBackgroundImage(cloudimageUrl, false, imgNode, null); + } + } else { + setBackgroundSrc(imgNode, cloudimageUrl, lazy, src, isSVG, dataSrcAttr); + } + } +} diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index ead2a58..afbeb00 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -1,52 +1,52 @@ -img[ci-src] { - opacity: 0; -} - -div.ci-image-wrapper { - display: block; - width: 100%; - overflow: hidden; - position: relative; -} - -img.ci-image { - display: block; - width: 100%; - padding: 0 !important; - position: absolute; - top: 0; - left: 0; - height: auto; -} - -img.ci-image-loaded { - opacity: 1; -} - -img.ci-image-ratio.ci-image-preview { - height: 100%; -} - -.ci-bg { - position: relative; -} - -.ci-bg > * { - position: relative; -} - -.ci-bg:before { - content: ""; - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - background: inherit; - filter: blur(0); - transition: filter 0.4s ease-in-out; -} - -.ci-bg.ci-bg-animation:before { - filter: blur(10px); -} +img[ci-src] { + opacity: 0; +} + +div.ci-image-wrapper { + display: block; + width: 100%; + overflow: hidden; + position: relative; +} + +img.ci-image { + display: block; + width: 100%; + padding: 0 !important; + position: absolute; + top: 0; + left: 0; + height: auto; +} + +img.ci-image-loaded { + opacity: 1; +} + +img.ci-image-ratio.ci-image-preview { + height: 100%; +} + +.ci-bg { + position: relative; +} + +.ci-bg > * { + position: relative; +} + +.ci-bg:before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + background: inherit; + filter: blur(0); + transition: filter 0.4s ease-in-out; +} + +.ci-bg.ci-bg-animation:before { + filter: blur(10px); +} diff --git a/src/low-preview/ci.utis.js b/src/low-preview/ci.utis.js index 2950b90..ba38e8a 100644 --- a/src/low-preview/ci.utis.js +++ b/src/low-preview/ci.utis.js @@ -1,232 +1,232 @@ -import { addClass, getWrapper } from '../common/ci.utils'; - - -export const wrapBackgroundContainer = (imgNode) => { - let previewBox = document.createElement('div'); - let contentBox = document.createElement('div'); - - if (imgNode.children && imgNode.children.length > 0) { - wrapAll(contentBox, imgNode.children); - } - - imgNode.prepend(previewBox); - - return [previewBox, contentBox] -}; - -export const applyBackgroundStyles = ({ imgNode, previewBox, contentBox, lazy, width }) => { - imgNode.style.position = 'relative'; - - contentBox.style.position = 'relative'; - - previewBox.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`; - previewBox.setAttribute('ci-preview', true); - previewBox.style.background = 'inherit'; - previewBox.style.position = 'absolute'; - previewBox.style.left = '0'; - previewBox.style.top = '0'; - previewBox.style.width = '100%'; - previewBox.style.height = '100%'; - - imgNode.style.transform = 'translateZ(0)'; - imgNode.style.overflow = 'hidden'; - - previewBox.style.transform = 'scale(1.1)'; - previewBox.style.filter = `blur(${Math.floor(width / 100)}px)`; - previewBox.style.transition = 'opacity 400ms ease 0ms'; -}; - -export const setAnimation = (wrapper, image, parentContainerWidth, isBackground) => { - if (!isBackground) { - if (wrapper) { - wrapper.style.transition = 'opacity 400ms ease 0ms'; - } - - image.style.transform = 'scale(1.1)'; - image.style.filter = `blur(${Math.floor(parentContainerWidth / 100)}px)`; - } else { - image.style.overflow = 'hidden'; - addClass(image, 'ci-bg-animation'); - } -}; - -export const finishAnimation = (image, isBackground) => { - if (!isBackground) { - const previewImg = image.parentNode.querySelector('img.ci-image-preview'); - const previewImgWrapper = previewImg && previewImg.parentNode; - - if (previewImgWrapper) { - previewImgWrapper.style.opacity = 0; - } - } else { - image.style.opacity = '0'; - } - - addClass(image, 'ci-image-loaded'); -}; - -export const onImageLoad = (wrapper, previewImg, imgNode, ratio, preserveSize, isAdaptive) => { - const { width, height } = imgNode; - - wrapper.style.background = 'transparent'; - - if (!ratio || isAdaptive) { - wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / (width / height)) + '%'; - } - - finishAnimation(imgNode); -}; - -export const onPreviewImageLoad = (wrapper, previewImg, ratio, preserveSize) => { - const { naturalWidth, naturalHeight } = previewImg; - - wrapper.style.background = 'transparent'; - - if (!ratio) { - wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / (naturalWidth / naturalHeight)) + '%'; - } -}; - -export const onLazyBeforeUnveil = (event) => { - const bgContainer = event.target; - const bg = bgContainer.getAttribute('data-bg'); - const isPreview = bgContainer.getAttribute('ci-preview') === 'true'; - const ciOptimizedUrl = (isPreview ? bgContainer.parentNode : bgContainer).getAttribute('ci-optimized-url'); - - loadBackgroundImage(bg, isPreview, bgContainer, ciOptimizedUrl); -} - -export const loadBackgroundImage = (bg, isPreview, bgContainer, ciOptimizedUrl) => { - if (bg) { - let optimizedImage = new Image(); - - if (isPreview) { - let previewImage = new Image(); - - optimizedImage.onload = () => { - finishAnimation(bgContainer, true); - bgContainer.parentNode.removeAttribute('ci-optimized-url'); - bgContainer.removeAttribute('data-bg'); - bgContainer.removeAttribute('ci-preview'); - } - - bgContainer.parentNode.style.backgroundImage = 'url(' + ciOptimizedUrl + ')'; - optimizedImage.src = ciOptimizedUrl; - previewImage.src = bg; - } else { - optimizedImage.onload = () => { - bgContainer.removeAttribute('data-bg'); - bgContainer.removeAttribute('ci-preview'); - } - - optimizedImage.src = bg; - } - - bgContainer.style.backgroundImage = 'url(' + bg + ')'; - } -}; - -export const applyOrUpdateWrapper = props => { - const { isUpdate, imgNode, isPreview, lazy } = props; - let wrapper, previewImgNode = null, previewWrapper = null; - - if (!isUpdate) { - wrapper = wrapImage(props); - - if (isPreview) { - previewWrapper = document.createElement('div'); - previewImgNode = document.createElement('img'); - - previewImgNode.className = `ci-image-ratio ci-image-preview${lazy ? ' lazyload' : ''}`; - previewWrapper.style.transform = 'translateZ(0)'; - previewWrapper.style.zIndex = '1'; - previewWrapper.style.height = '100%'; - previewWrapper.style.width = '100%'; - previewWrapper.style.position = 'absolute'; - previewWrapper.style.top = '0'; - previewWrapper.style.left = '0'; - previewImgNode.alt = `Low quality preview for ${imgNode.alt || (imgNode.src || '').split('.')[0]}`; - previewWrapper.appendChild(previewImgNode); - wrapper.insertBefore(previewWrapper, imgNode); - addClass(wrapper, 'ci-with-preview-image'); - } - } else { - wrapper = getWrapper(imgNode); - - // TODO: remove in next release - //if (ratio) { - // wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; - //} - } - - return { wrapper, previewImgNode, previewWrapper }; -}; - -export const wrapImage = (props) => { - const { imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize, placeholderBackground } = props; - let { wrapper } = props; - - wrapper = wrapper || document.createElement('div'); - - addClass(wrapper, 'ci-image-wrapper'); - wrapper.style.background = placeholderBackground; - wrapper.style.display = 'block'; - wrapper.style.width = preserveSize ? imgNodeWidth : '100%'; - wrapper.style.height = preserveSize ? imgNodeHeight : 'auto'; - wrapper.style.overflow = 'hidden'; - wrapper.style.position = 'relative'; - - if (ratio) { - wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; - } - - if (imgNode.nextSibling) { - imgNode.parentNode.insertBefore(wrapper, imgNode.nextSibling); - } else { - imgNode.parentNode.appendChild(wrapper); - } - - wrapper.appendChild(imgNode); - - return wrapper; -}; - -export const initImageClasses = ({ imgNode, lazy }) => { - addClass(imgNode, 'ci-image'); - - if (lazy) { - addClass(imgNode, 'lazyload'); - } -}; - -/* -* possible size values: 200 | 200x400 -* */ -export const updateSizeWithPixelRatio = (size, devicePixelRatio) => { - const splittedSizes = size.toString().split('x'); - const result = []; - - [].forEach.call(splittedSizes, size => { - size ? result.push(Math.floor(size * ((devicePixelRatio || window.devicePixelRatio).toFixed(1) || 1))) : ''; - }); - - return result.join('x'); -}; - -const wrapAll = function(wrapper, elms) { - const el = elms.length ? elms[0] : elms; - const parent = el.parentNode; - const sibling = el.nextSibling; - - wrapper.appendChild(el); - - while (elms.length) { - wrapper.appendChild(elms[0]); - } - - if (sibling) { - parent.insertBefore(wrapper, sibling); - } else { - parent.appendChild(wrapper); - } +import { addClass, getWrapper } from '../common/ci.utils'; + + +export const wrapBackgroundContainer = (imgNode) => { + let previewBox = document.createElement('div'); + let contentBox = document.createElement('div'); + + if (imgNode.children && imgNode.children.length > 0) { + wrapAll(contentBox, imgNode.children); + } + + imgNode.prepend(previewBox); + + return [previewBox, contentBox] +}; + +export const applyBackgroundStyles = ({ imgNode, previewBox, contentBox, lazy, width }) => { + imgNode.style.position = 'relative'; + + contentBox.style.position = 'relative'; + + previewBox.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`; + previewBox.setAttribute('ci-preview', true); + previewBox.style.background = 'inherit'; + previewBox.style.position = 'absolute'; + previewBox.style.left = '0'; + previewBox.style.top = '0'; + previewBox.style.width = '100%'; + previewBox.style.height = '100%'; + + imgNode.style.transform = 'translateZ(0)'; + imgNode.style.overflow = 'hidden'; + + previewBox.style.transform = 'scale(1.1)'; + previewBox.style.filter = `blur(${Math.floor(width / 100)}px)`; + previewBox.style.transition = 'opacity 400ms ease 0ms'; +}; + +export const setAnimation = (wrapper, image, parentContainerWidth, isBackground) => { + if (!isBackground) { + if (wrapper) { + wrapper.style.transition = 'opacity 400ms ease 0ms'; + } + + image.style.transform = 'scale(1.1)'; + image.style.filter = `blur(${Math.floor(parentContainerWidth / 100)}px)`; + } else { + image.style.overflow = 'hidden'; + addClass(image, 'ci-bg-animation'); + } +}; + +export const finishAnimation = (image, isBackground) => { + if (!isBackground) { + const previewImg = image.parentNode.querySelector('img.ci-image-preview'); + const previewImgWrapper = previewImg && previewImg.parentNode; + + if (previewImgWrapper) { + previewImgWrapper.style.opacity = 0; + } + } else { + image.style.opacity = '0'; + } + + addClass(image, 'ci-image-loaded'); +}; + +export const onImageLoad = (wrapper, previewImg, imgNode, ratio, preserveSize, isAdaptive) => { + const { width, height } = imgNode; + + wrapper.style.background = 'transparent'; + + if (!ratio || isAdaptive) { + wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / (width / height)) + '%'; + } + + finishAnimation(imgNode); +}; + +export const onPreviewImageLoad = (wrapper, previewImg, ratio, preserveSize) => { + const { naturalWidth, naturalHeight } = previewImg; + + wrapper.style.background = 'transparent'; + + if (!ratio) { + wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / (naturalWidth / naturalHeight)) + '%'; + } +}; + +export const onLazyBeforeUnveil = (event) => { + const bgContainer = event.target; + const bg = bgContainer.getAttribute('data-bg'); + const isPreview = bgContainer.getAttribute('ci-preview') === 'true'; + const ciOptimizedUrl = (isPreview ? bgContainer.parentNode : bgContainer).getAttribute('ci-optimized-url'); + + loadBackgroundImage(bg, isPreview, bgContainer, ciOptimizedUrl); +} + +export const loadBackgroundImage = (bg, isPreview, bgContainer, ciOptimizedUrl) => { + if (bg) { + let optimizedImage = new Image(); + + if (isPreview) { + let previewImage = new Image(); + + optimizedImage.onload = () => { + finishAnimation(bgContainer, true); + bgContainer.parentNode.removeAttribute('ci-optimized-url'); + bgContainer.removeAttribute('data-bg'); + bgContainer.removeAttribute('ci-preview'); + } + + bgContainer.parentNode.style.backgroundImage = 'url(' + ciOptimizedUrl + ')'; + optimizedImage.src = ciOptimizedUrl; + previewImage.src = bg; + } else { + optimizedImage.onload = () => { + bgContainer.removeAttribute('data-bg'); + bgContainer.removeAttribute('ci-preview'); + } + + optimizedImage.src = bg; + } + + bgContainer.style.backgroundImage = 'url(' + bg + ')'; + } +}; + +export const applyOrUpdateWrapper = props => { + const { isUpdate, imgNode, isPreview, lazy } = props; + let wrapper, previewImgNode = null, previewWrapper = null; + + if (!isUpdate) { + wrapper = wrapImage(props); + + if (isPreview) { + previewWrapper = document.createElement('div'); + previewImgNode = document.createElement('img'); + + previewImgNode.className = `ci-image-ratio ci-image-preview${lazy ? ' lazyload' : ''}`; + previewWrapper.style.transform = 'translateZ(0)'; + previewWrapper.style.zIndex = '1'; + previewWrapper.style.height = '100%'; + previewWrapper.style.width = '100%'; + previewWrapper.style.position = 'absolute'; + previewWrapper.style.top = '0'; + previewWrapper.style.left = '0'; + previewImgNode.alt = `Low quality preview for ${imgNode.alt || (imgNode.src || '').split('.')[0]}`; + previewWrapper.appendChild(previewImgNode); + wrapper.insertBefore(previewWrapper, imgNode); + addClass(wrapper, 'ci-with-preview-image'); + } + } else { + wrapper = getWrapper(imgNode); + + // TODO: remove in next release + //if (ratio) { + // wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; + //} + } + + return { wrapper, previewImgNode, previewWrapper }; +}; + +export const wrapImage = (props) => { + const { imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize, placeholderBackground } = props; + let { wrapper } = props; + + wrapper = wrapper || document.createElement('div'); + + addClass(wrapper, 'ci-image-wrapper'); + wrapper.style.background = placeholderBackground; + wrapper.style.display = 'block'; + wrapper.style.width = preserveSize ? imgNodeWidth : '100%'; + wrapper.style.height = preserveSize ? imgNodeHeight : 'auto'; + wrapper.style.overflow = 'hidden'; + wrapper.style.position = 'relative'; + + if (ratio) { + wrapper.style.paddingBottom = preserveSize ? 'none' : (100 / ratio) + '%'; + } + + if (imgNode.nextSibling) { + imgNode.parentNode.insertBefore(wrapper, imgNode.nextSibling); + } else { + imgNode.parentNode.appendChild(wrapper); + } + + wrapper.appendChild(imgNode); + + return wrapper; +}; + +export const initImageClasses = ({ imgNode, lazy }) => { + addClass(imgNode, 'ci-image'); + + if (lazy) { + addClass(imgNode, 'lazyload'); + } +}; + +/* +* possible size values: 200 | 200x400 +* */ +export const updateSizeWithPixelRatio = (size, devicePixelRatio) => { + const splittedSizes = size.toString().split('x'); + const result = []; + + [].forEach.call(splittedSizes, size => { + size ? result.push(Math.floor(size * ((devicePixelRatio || window.devicePixelRatio).toFixed(1) || 1))) : ''; + }); + + return result.join('x'); +}; + +const wrapAll = function(wrapper, elms) { + const el = elms.length ? elms[0] : elms; + const parent = el.parentNode; + const sibling = el.nextSibling; + + wrapper.appendChild(el); + + while (elms.length) { + wrapper.appendChild(elms[0]); + } + + if (sibling) { + parent.insertBefore(wrapper, sibling); + } else { + parent.appendChild(wrapper); + } }; \ No newline at end of file diff --git a/src/low-preview/index.js b/src/low-preview/index.js index c06837f..d533f22 100644 --- a/src/low-preview/index.js +++ b/src/low-preview/index.js @@ -1,7 +1,7 @@ -import 'core-js/features/array/find'; -import 'core-js/es/math/trunc'; -import CIResponsive from './ci.service'; -import './ci.styles.css'; - - +import 'core-js/features/array/find'; +import 'core-js/es/math/trunc'; +import CIResponsive from './ci.service'; +import './ci.styles.css'; + + window.CIResponsive = CIResponsive; \ No newline at end of file diff --git a/src/plain/ci.config.js b/src/plain/ci.config.js index 16b141b..68ac14c 100644 --- a/src/plain/ci.config.js +++ b/src/plain/ci.config.js @@ -1,71 +1,71 @@ -import { getParams } from '../common/ci.utils'; -import { DEVICE_PIXEL_RATIO_LIST } from 'cloudimage-responsive-utils/dist/constants'; - - -export const getInitialConfigPlain = (config) => { - const { - imgSelector = 'ci-src', - bgSelector = 'ci-bg-url', - token = '', - domain = 'cloudimg.io', - lazyLoading = false, - baseUrl, // to support old name - baseURL, - presets, - params = 'org_if_sml=1', - apiVersion = 'v7', - init = true, - exactSize = false, - doNotReplaceURL = false, - limitFactor = 100, - imageSizeAttributes = 'use', - ignoreNodeImgSize, - ignoreStyleImgSize = false, - destroyNodeImgSize = false, - saveNodeImgRatio = false, - detectImageNodeCSS = false, - processOnlyWidth = false, - devicePixelRatioList = DEVICE_PIXEL_RATIO_LIST, - // callbacks - onImageLoad = null, - // methods - processURL = null, - processQueryString = null - } = config; - - return { - imgSelector, - bgSelector, - token, - domain, - lazyLoading, - baseURL: baseUrl || baseURL, - exactSize, - presets: presets ? presets : - { - xs: '(max-width: 575px)', // to 575 PHONE - sm: '(min-width: 576px)', // 576 - 767 PHABLET - md: '(min-width: 768px)', // 768 - 991 TABLET - lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN - xl: '(min-width: 1200px)' // from 1200 USUALSCREEN - }, - params: getParams(params), - apiVersion, - innerWidth: window.innerWidth, - init, - doNotReplaceURL, - devicePixelRatioList, - limitFactor, - imageSizeAttributes, - ignoreNodeImgSize, - ignoreStyleImgSize, - destroyNodeImgSize, - saveNodeImgRatio, - detectImageNodeCSS, - processOnlyWidth, - onImageLoad, - processURL, - processQueryString - //isChrome: /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor) - }; -}; +import { getParams } from '../common/ci.utils'; +import { DEVICE_PIXEL_RATIO_LIST } from 'cloudimage-responsive-utils/dist/constants'; + + +export const getInitialConfigPlain = (config) => { + const { + imgSelector = 'ci-src', + bgSelector = 'ci-bg-url', + token = '', + domain = 'cloudimg.io', + lazyLoading = false, + baseUrl, // to support old name + baseURL, + presets, + params = 'org_if_sml=1', + apiVersion = 'v7', + init = true, + exactSize = false, + doNotReplaceURL = false, + limitFactor = 100, + imageSizeAttributes = 'use', + ignoreNodeImgSize, + ignoreStyleImgSize = false, + destroyNodeImgSize = false, + saveNodeImgRatio = false, + detectImageNodeCSS = false, + processOnlyWidth = false, + devicePixelRatioList = DEVICE_PIXEL_RATIO_LIST, + // callbacks + onImageLoad = null, + // methods + processURL = null, + processQueryString = null + } = config; + + return { + imgSelector, + bgSelector, + token, + domain, + lazyLoading, + baseURL: baseUrl || baseURL, + exactSize, + presets: presets ? presets : + { + xs: '(max-width: 575px)', // to 575 PHONE + sm: '(min-width: 576px)', // 576 - 767 PHABLET + md: '(min-width: 768px)', // 768 - 991 TABLET + lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN + xl: '(min-width: 1200px)' // from 1200 USUALSCREEN + }, + params: getParams(params), + apiVersion, + innerWidth: window.innerWidth, + init, + doNotReplaceURL, + devicePixelRatioList, + limitFactor, + imageSizeAttributes, + ignoreNodeImgSize, + ignoreStyleImgSize, + destroyNodeImgSize, + saveNodeImgRatio, + detectImageNodeCSS, + processOnlyWidth, + onImageLoad, + processURL, + processQueryString + //isChrome: /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor) + }; +}; diff --git a/src/plain/ci.service.js b/src/plain/ci.service.js index 3ea8df7..e77dc40 100644 --- a/src/plain/ci.service.js +++ b/src/plain/ci.service.js @@ -1,158 +1,158 @@ -import { - addClass, - destroyNodeImgSize, - getBackgroundImageProps, - getFreshCIElements, - getImageProps, - isLazy, - setBackgroundSrc, - setSrc, - setSrcset -} from '../common/ci.utils'; -import { determineContainerProps } from 'cloudimage-responsive-utils/dist/utils/determine-container-props'; -import { getImgSRC } from 'cloudimage-responsive-utils/dist/utils/get-img-src'; -import { generateURL } from 'cloudimage-responsive-utils/dist/utils/generate-url'; -import { getBreakpoint } from 'cloudimage-responsive-utils/dist/utils/get-breakpoint'; -import { isSupportedInBrowser } from 'cloudimage-responsive-utils/dist/utils/is-supported-in-browser'; -import { getInitialConfigPlain } from './ci.config'; -import { initImageClasses, loadBackgroundImage } from './ci.utils'; -import { debounce } from 'throttle-debounce'; - - -export default class CIResponsive { - constructor(config) { - this.config = getInitialConfigPlain(config); - - if (this.config.init) this.init(); - - this.innerWidth = window.innerWidth; - } - - init() { - document.addEventListener('lazybeforeunveil', loadBackgroundImage); - window.addEventListener('resize', debounce(100, this.onUpdateDimensions.bind(this))); - - this.process(); - } - - onUpdateDimensions() { - this.process(true); - - if (this.innerWidth < window.innerWidth) { - this.innerWidth = window.innerWidth; - } - } - - process(isUpdate, rootElement = document) { - const { imgSelector, bgSelector } = this.config; - const windowScreenBecomesBigger = this.innerWidth < window.innerWidth; - let [images, backgroundImages] = getFreshCIElements(isUpdate, rootElement, imgSelector, bgSelector); - - if (images.length > -1) { - images.forEach(imgNode => { - this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image'); - }); - } - - if (backgroundImages.length > -1) { - backgroundImages.forEach(imgNode => { - this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'background'); - }); - } - } - - getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type) => { - const isImage = type === 'image'; - const { config } = this; - const { - baseURL, lazyLoading, presets, devicePixelRatioList, imgSelector, bgSelector, processURL, processQueryString - } = config; - const imgProps = isImage ? - getImageProps(imgNode, imgSelector) : getBackgroundImageProps(imgNode, bgSelector); - const { params, imgNodeSRC, isLazyCanceled, sizes, isAdaptive, preserveSize, minWindowWidth } = imgProps; - - if (!imgNodeSRC) return; - - const [src, isSVG] = getImgSRC(imgNodeSRC, baseURL); - const lazy = isLazy(lazyLoading, isLazyCanceled, isUpdate); - let size; - - if (!isSupportedInBrowser(true)) { - if (isImage) { - imgNode.src = src; - } else { - imgNode.style.backgroundImage = 'url(' + src + ')'; - } - - return; - } - - if (window.innerWidth < minWindowWidth && !isImage) { - imgNode.style.backgroundImage = 'none'; - return; - } - - if (isAdaptive) { - size = getBreakpoint(sizes, presets); - } else { - if (isUpdate && !windowScreenBecomesBigger) return; - } - - const containerProps = determineContainerProps({ ...imgProps, size, imgNode, config }); - const service = { - props: { imgNode, imgProps, config }, - methods: {} - }; - const generateURLbyDPR = devicePixelRatio => - generateURL({ src, params, config, containerProps, devicePixelRatio, processURL, processQueryString, service }); - const cloudimageUrl = generateURLbyDPR(); - const cloudimageSrcset = devicePixelRatioList.map(dpr => ({ dpr: dpr.toString(), url: generateURLbyDPR(dpr) })); - const props = { imgNode, isUpdate, imgProps, lazy, containerProps, isSVG, cloudimageUrl, src, preserveSize }; - - if (isImage) { - this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset }); - } else { - this.processBackgroundImage(props); - } - } - - processImage(props) { - const { imgNode, isUpdate, lazy, isSVG, cloudimageUrl, src, cloudimageSrcset } = props; - const { config } = this; - const { dataSrcAttr, onImageLoad } = config; - - if (!isUpdate) { - initImageClasses({ imgNode, lazy }); - } - - if (config.destroyNodeImgSize) { - destroyNodeImgSize(imgNode); - } - - if (config.processOnlyWidth) { - imgNode.removeAttribute("height"); - } - - imgNode.onload = () => { - if (onImageLoad && typeof onImageLoad === 'function') { - onImageLoad(imgNode); - } - addClass(imgNode, 'ci-image-loaded'); - }; - - setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr); - setSrc(imgNode, cloudimageUrl, 'data-src', lazy, src, isSVG, dataSrcAttr); - } - - processBackgroundImage(props) { - const { imgNode, isUpdate, lazy, isSVG, cloudimageUrl, src } = props; - const { config } = this; - const { dataSrcAttr } = config; - - if (!isUpdate) { - imgNode.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`; - } - - setBackgroundSrc(imgNode, cloudimageUrl, lazy, src, isSVG, dataSrcAttr); - } -} +import { + addClass, + destroyNodeImgSize, + getBackgroundImageProps, + getFreshCIElements, + getImageProps, + isLazy, + setBackgroundSrc, + setSrc, + setSrcset +} from '../common/ci.utils'; +import { determineContainerProps } from 'cloudimage-responsive-utils/dist/utils/determine-container-props'; +import { getImgSRC } from 'cloudimage-responsive-utils/dist/utils/get-img-src'; +import { generateURL } from 'cloudimage-responsive-utils/dist/utils/generate-url'; +import { getBreakpoint } from 'cloudimage-responsive-utils/dist/utils/get-breakpoint'; +import { isSupportedInBrowser } from 'cloudimage-responsive-utils/dist/utils/is-supported-in-browser'; +import { getInitialConfigPlain } from './ci.config'; +import { initImageClasses, loadBackgroundImage } from './ci.utils'; +import { debounce } from 'throttle-debounce'; + + +export default class CIResponsive { + constructor(config) { + this.config = getInitialConfigPlain(config); + + if (this.config.init) this.init(); + + this.innerWidth = window.innerWidth; + } + + init() { + document.addEventListener('lazybeforeunveil', loadBackgroundImage); + window.addEventListener('resize', debounce(100, this.onUpdateDimensions.bind(this))); + + this.process(); + } + + onUpdateDimensions() { + this.process(true); + + if (this.innerWidth < window.innerWidth) { + this.innerWidth = window.innerWidth; + } + } + + process(isUpdate, rootElement = document) { + const { imgSelector, bgSelector } = this.config; + const windowScreenBecomesBigger = this.innerWidth < window.innerWidth; + let [images, backgroundImages] = getFreshCIElements(isUpdate, rootElement, imgSelector, bgSelector); + + if (images.length > -1) { + images.forEach(imgNode => { + this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image'); + }); + } + + if (backgroundImages.length > -1) { + backgroundImages.forEach(imgNode => { + this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'background'); + }); + } + } + + getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type) => { + const isImage = type === 'image'; + const { config } = this; + const { + baseURL, lazyLoading, presets, devicePixelRatioList, imgSelector, bgSelector, processURL, processQueryString + } = config; + const imgProps = isImage ? + getImageProps(imgNode, imgSelector) : getBackgroundImageProps(imgNode, bgSelector); + const { params, imgNodeSRC, isLazyCanceled, sizes, isAdaptive, preserveSize, minWindowWidth } = imgProps; + + if (!imgNodeSRC) return; + + const [src, isSVG] = getImgSRC(imgNodeSRC, baseURL); + const lazy = isLazy(lazyLoading, isLazyCanceled, isUpdate); + let size; + + if (!isSupportedInBrowser(true)) { + if (isImage) { + imgNode.src = src; + } else { + imgNode.style.backgroundImage = 'url(' + src + ')'; + } + + return; + } + + if (window.innerWidth < minWindowWidth && !isImage) { + imgNode.style.backgroundImage = 'none'; + return; + } + + if (isAdaptive) { + size = getBreakpoint(sizes, presets); + } else { + if (isUpdate && !windowScreenBecomesBigger) return; + } + + const containerProps = determineContainerProps({ ...imgProps, size, imgNode, config }); + const service = { + props: { imgNode, imgProps, config }, + methods: {} + }; + const generateURLbyDPR = devicePixelRatio => + generateURL({ src, params, config, containerProps, devicePixelRatio, processURL, processQueryString, service }); + const cloudimageUrl = generateURLbyDPR(); + const cloudimageSrcset = devicePixelRatioList.map(dpr => ({ dpr: dpr.toString(), url: generateURLbyDPR(dpr) })); + const props = { imgNode, isUpdate, imgProps, lazy, containerProps, isSVG, cloudimageUrl, src, preserveSize }; + + if (isImage) { + this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset }); + } else { + this.processBackgroundImage(props); + } + } + + processImage(props) { + const { imgNode, isUpdate, lazy, isSVG, cloudimageUrl, src, cloudimageSrcset } = props; + const { config } = this; + const { dataSrcAttr, onImageLoad } = config; + + if (!isUpdate) { + initImageClasses({ imgNode, lazy }); + } + + if (config.destroyNodeImgSize) { + destroyNodeImgSize(imgNode); + } + + if (config.processOnlyWidth) { + imgNode.removeAttribute("height"); + } + + imgNode.onload = () => { + if (onImageLoad && typeof onImageLoad === 'function') { + onImageLoad(imgNode); + } + addClass(imgNode, 'ci-image-loaded'); + }; + + setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr); + setSrc(imgNode, cloudimageUrl, 'data-src', lazy, src, isSVG, dataSrcAttr); + } + + processBackgroundImage(props) { + const { imgNode, isUpdate, lazy, isSVG, cloudimageUrl, src } = props; + const { config } = this; + const { dataSrcAttr } = config; + + if (!isUpdate) { + imgNode.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`; + } + + setBackgroundSrc(imgNode, cloudimageUrl, lazy, src, isSVG, dataSrcAttr); + } +} diff --git a/src/plain/ci.utils.js b/src/plain/ci.utils.js index 0c06e91..ed3ef75 100644 --- a/src/plain/ci.utils.js +++ b/src/plain/ci.utils.js @@ -1,37 +1,37 @@ -import { addClass } from '../common/ci.utils'; - - -export const loadBackgroundImage = (event) => { - const bgContainer = event.target; - const bg = bgContainer.getAttribute('data-bg'); - - if (bg) { - let optimizedImage = new Image(); - - optimizedImage.onload = () => { - addClass(bgContainer, 'ci-image-loaded'); - bgContainer.removeAttribute('data-bg'); - bgContainer.removeAttribute('ci-preview'); - } - - optimizedImage.src = bg; - - bgContainer.style.backgroundImage = 'url(' + bg + ')'; - } -}; - -export const initImageClasses = ({ imgNode, lazy }) => { - addClass(imgNode, 'ci-image'); - - if (lazy) { - addClass(imgNode, 'lazyload'); - } -}; - -export const initImageBackgroundClasses = ({ imgNode, lazy }) => { - addClass(imgNode, 'ci-bg'); - - if (lazy) { - addClass(imgNode, 'lazyload'); - } +import { addClass } from '../common/ci.utils'; + + +export const loadBackgroundImage = (event) => { + const bgContainer = event.target; + const bg = bgContainer.getAttribute('data-bg'); + + if (bg) { + let optimizedImage = new Image(); + + optimizedImage.onload = () => { + addClass(bgContainer, 'ci-image-loaded'); + bgContainer.removeAttribute('data-bg'); + bgContainer.removeAttribute('ci-preview'); + } + + optimizedImage.src = bg; + + bgContainer.style.backgroundImage = 'url(' + bg + ')'; + } +}; + +export const initImageClasses = ({ imgNode, lazy }) => { + addClass(imgNode, 'ci-image'); + + if (lazy) { + addClass(imgNode, 'lazyload'); + } +}; + +export const initImageBackgroundClasses = ({ imgNode, lazy }) => { + addClass(imgNode, 'ci-bg'); + + if (lazy) { + addClass(imgNode, 'lazyload'); + } }; \ No newline at end of file diff --git a/src/plain/index.js b/src/plain/index.js index 71fa283..94ced64 100644 --- a/src/plain/index.js +++ b/src/plain/index.js @@ -1,6 +1,6 @@ -import 'core-js/features/array/find'; -import 'core-js/es/math/trunc'; -import CIResponsive from './ci.service'; - - +import 'core-js/features/array/find'; +import 'core-js/es/math/trunc'; +import CIResponsive from './ci.service'; + + window.CIResponsive = CIResponsive; \ No newline at end of file diff --git a/src/wp/index.js b/src/wp/index.js index 5793bd8..584b7d9 100644 --- a/src/wp/index.js +++ b/src/wp/index.js @@ -1,14 +1,14 @@ -import 'core-js/features/array/find'; -import 'core-js/es/math/trunc'; -import CIResponsive from '../plain/ci.service'; - - -window.CIResponsive = CIResponsive; - -if (window.CIResponsiveConfig) { - window.ciResponsive = new window.CIResponsive(window.CIResponsiveConfig); -} - -if (window.CIResponsiveConfig && window.lazySizes) { - window.lazySizes.init(); +import 'core-js/features/array/find'; +import 'core-js/es/math/trunc'; +import CIResponsive from '../plain/ci.service'; + + +window.CIResponsive = CIResponsive; + +if (window.CIResponsiveConfig) { + window.ciResponsive = new window.CIResponsive(window.CIResponsiveConfig); +} + +if (window.CIResponsiveConfig && window.lazySizes) { + window.lazySizes.init(); } \ No newline at end of file diff --git a/wp/cloudimage-wp-fixes.css b/wp/cloudimage-wp-fixes.css index cec48b6..105f736 100644 --- a/wp/cloudimage-wp-fixes.css +++ b/wp/cloudimage-wp-fixes.css @@ -1,3 +1,3 @@ -span.et_pb_image_wrap { - width: 100%; +span.et_pb_image_wrap { + width: 100%; } \ No newline at end of file diff --git a/wp/cloudimage-wp-fixes.js b/wp/cloudimage-wp-fixes.js index 51ffcb1..6b0d8ee 100644 --- a/wp/cloudimage-wp-fixes.js +++ b/wp/cloudimage-wp-fixes.js @@ -1,27 +1,27 @@ -window.onload = function() { - var carousels = $('.carousel'); - var number = $('.carousel').length; - - if (number > 0) { - carousels.on('slide.bs.carousel', function () { - window.dispatchEvent(new Event('resize')); - }); - } -}; - - -(function() { - window.ciResponsive = window.ciResponsive || {}; - window.cloudimgResponsive = window.cloudimgResponsive || {}; - window.ciResponsive.getImages = function () { - return [].slice.call(document.images ? document.images : document.getElementsByTagName('img'), 0); - }; - - setInterval(function() { - var imageList = window.ciResponsive.getImages(), process = false; - imageList.forEach(function(image) { - if (!image.src && image.getAttribute('ci-src') && !image.getAttribute('data-src')) { process = true; } - }); - if (process) { window.cloudimgResponsive.process && window.cloudimgResponsive.process(); } - }, 1000) +window.onload = function() { + var carousels = $('.carousel'); + var number = $('.carousel').length; + + if (number > 0) { + carousels.on('slide.bs.carousel', function () { + window.dispatchEvent(new Event('resize')); + }); + } +}; + + +(function() { + window.ciResponsive = window.ciResponsive || {}; + window.cloudimgResponsive = window.cloudimgResponsive || {}; + window.ciResponsive.getImages = function () { + return [].slice.call(document.images ? document.images : document.getElementsByTagName('img'), 0); + }; + + setInterval(function() { + var imageList = window.ciResponsive.getImages(), process = false; + imageList.forEach(function(image) { + if (!image.src && image.getAttribute('ci-src') && !image.getAttribute('data-src')) { process = true; } + }); + if (process) { window.cloudimgResponsive.process && window.cloudimgResponsive.process(); } + }, 1000) })(); \ No newline at end of file