...
 
Commits (24)
......@@ -4,144 +4,85 @@ This is a simple Drupal module to lazy-load all inline images and/or iframes
defined by content authors in entity content, usually the text-format-enabled
version of textarea fields. i.e. Node and Block body content.
The module currently depends on the [bLazy][1] image script.
This module depends on the [lazysizes](https://github.com/aFarkas/lazysizes) library.
> High performance and SEO friendly lazy loader for images (responsive and normal), iframes and more, that detects any visibility changes triggered through user interaction, CSS or JavaScript without configuration.
There is another contributed module utilizing its namesake, [Blazy][2].
Make sure to check it out, especially if you need more advanced features and
support for many features out of the box.
## 3.x Features
This module focuses on the only area Blazy module lacks of; **inline-images**
and **inline-iframes**.
* Now uses [lazysizes](https://github.com/aFarkas/lazysizes) library for rich features
* Supports **inline-images** and **inline-iframes** (Enabled per text-format)
* Supports **image fields** with following field formatters:
- Colorbox `colorbox`
- Image `image`
- Responsive image `responsive_image`
- Media Thumbnail `media_thumbnail`
* Supports adding field formatters via `hook_lazy_field_formatters_alter(&$formatters)` hook
* Supports native `loading="auto"` attribute for Chrome browsers.
You can still use this module tandem with it, though that is not a requirement.
## Breaking changes since 2.x
> If you're looking for 8.x-2.x documentation go to https://git.drupalcode.org/project/lazy/blob/8.x-2.x/README.md
**[bLazy](http://dinbror.dk/blazy/) library replaced with [lazysizes](https://github.com/aFarkas/lazysizes).**
If you customized bLazy configuration, you should checkout the [documentation](https://github.com/aFarkas/lazysizes/blob/gh-pages/README.md) for the lazysizes way.
## Requirements
* **bLazy v1.8.2** script as a library item
## Installing Manually
- [Download bLazy][3] from https://github.com/dinbror/blazy
- Extract the downloaded file,
- rename *blazy-master* directory to *blazy*,
- copy the folder into the `/libraries` folder: i.e.: `libraries/blazy/blazy.min.js`
## Installing via Composer
- Run `composer require --prefer-dist composer/installers` to ensure that you have the `composer/installers` package installed. This package facilitates the installation of packages into directories other than `/vendor` (e.g. `/libraries`) using Composer.
- If your `composer.json` doesn't already have a definition for the `libraries` path, define one similar to the one below, depending on your setup:
``` json
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
},
{
"type": "composer",
"url": "https://asset-packagist.org"
},
],
"require": {
"composer/installers": "^1.0",
"drupal/lazy": "^3.0",
"oomphinc/composer-installers-extender": "^1.0",
"bower-asset/lazysizes": "^5.1"
},
"extra": {
"installer-types": ["bower-asset", "npm-asset"],
"installer-paths": {
"web/libraries/{$name}": ["type:drupal-library"]
"web/core": ["type:drupal-core"],
"web/modules/contrib/{$name}": ["type:drupal-module"],
"web/modules/{$name}": ["type:unity-module"],
"web/profiles/contrib/{$name}": ["type:drupal-profile"],
"web/themes/contrib/{$name}": ["type:drupal-theme"],
"web/themes/{$name}": ["type:unity-theme"],
"web/libraries/{$name}": ["type:drupal-library", "type:bower-asset", "type:npm-asset"],
"drush/contrib/{$name}": ["type:drupal-drush"]
}
}
```
- Add following to the "repositories" section of `composer.json`:
``` json
"repositories": [
{
"type": "package",
"package": {
"name": "dinbror/blazy",
"version": "1.8.2",
"type": "drupal-library",
"extra": {
"installer-name": "blazy"
},
"source": {
"type": "git",
"url": "https://github.com/dinbror/blazy",
"reference": "1.8.2"
}
}
}
]
```
- Install the required _Blazy_ library:
``` sh
composer require 'dinbror/blazy:1.8.2'
```
- Install this module:
``` sh
composer require 'drupal/lazy:^2.0'
```
## Adding other image-based field formatters
## Usage
There are two options to set up for your site to lazy-load images. And both options share the same [settings](/admin/config/content/lazy).
``` php
<?php
1. Image fields
2. Inline images and Iframes managed in rich-text fields (ckeditor)
/**
* Alter enabled field formatters for lazy-loading.
*
* @param $formatters
* Array of field formatters.
*
* @return array
*/
function hook_lazy_field_formatters_alter(&$formatters) {
$formatters[] = 'hook_formatter';
### Image fields
Repeat these steps for each image field you want to enable lazy-loading:
1. Go to **Manage display** page of the entity type you want to enable lazy-loading. *i.e. Article*
2. Select the **display** you want the change. *i.e. Teaser*
3. Click on the little cog icon to edit image display settings
4. Check **Enable lazy-loading** option, update the field settings, and save.
### Inline images and Iframes
1. **Configure** the [text format](/admin/config/content/formats) you want to enable lazy-loading. *i.e. Full HTML*
2. Check **Lazy-load images and IFRAMEs via bLazy** option to enable the filter, and save configuration.
3. Go to [Lazy-load settings](/admin/config/content/lazy).
4. Check the boxes for the inline elements (`<img>`, `<iframe>`) to be lazy-loaded via filter.
5. Save configuration
*Repeat steps 1-2 for each text-format you want to enable lazy-loading.*
To disable lazy-loading for specific image or iframes, add **skip-class** to the
class attribute. Default value (no-b-lazy) can be changed in the configuration.
``` html
<img class="no-b-lazy" src="this-image-will-load-normal" alt="">
```
### Blazy plugin can be manipulated in your theme/module javascript:
The options in **Blazy configuration** section of the settings form are the default options of the Blazy library. Refer to [Blazy plugin documentation][5] for each setting.
#### Get Blazy plugin options.
```js
let opt = drupalSettings.lazy;
return $formatters;
}
```
#### Access Blazy's public functions:
| Function | Description |
|:--|:--|
| `Drupal.lazy.revalidate();` | Revalidates document for visible images. Useful if you add images with scripting or ajax |
| `Drupal.lazy.load(element(s), force);` | Forces the given element(s) to load if not collapsed. If you also want to load a collapsed/hidden elements you can add true as the second parameter. You can pass a single element or a list of elements. Tested with getElementById, getElementsByClassName, querySelectorAll, querySelector and jQuery selector. |
| `Drupal.lazy.destroy();` | Unbind events and resets image array |
## Use Case
If you have numerous images and/or iframes in your content, it could become
a challenge to update that content to make compatible for lazy-loading. In
most cases those updates needs to be handled manually, because most of the time
if not all, the body content (HTML) doesn't follow a pattern to update
them programmatically.
This is the main reason I created this module, to avoid a need for altering body
content manually while making them easy to lazy-load.
**The *Lazy-load* filter doesn't make any changes to existing content.** It only
rewrites the `<img>` and/or `<iframe>` tags in already rendered output to have
them compatible for bLazy script to lazy-load. Since the filtered output is
cached, there should not be any changes in performance.
[1]: http://dinbror.dk/blazy/
[2]: https://www.drupal.org/project/blazy
[3]: https://github.com/dinbror/blazy/archive/master.zip
[4]: https://www.drupal.org/project/libraries
[5]: http://dinbror.dk/blog/blazy/
......@@ -15,5 +15,8 @@
"support": {
"issues": "https://www.drupal.org/project/issues/lazy",
"source": "http://cgit.drupalcode.org/lazy"
},
"suggest": {
"bower-asset/lazysizes": "Lazysizes is a required library for the Lazy module. Must be installed to '/libraries/lazysizes' folder."
}
}
errorClass: "b-error"
loadInvisible: false
offset: 100
saveViewportOffsetDelay: 50
selector: "b-lazy"
image_fields: { }
alter_tag:
img: 0
img: img
iframe: 0
skipClass: "no-b-lazy"
src: "data-src"
successClass: "b-loaded"
validateDelay: 25
placeholderSrc: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
image_fields: false
disabled_paths: "/rss.xml"
\ No newline at end of file
formatters:
lazy_image: 0
lazy_responsive_image: 0
skipClass: no-lazy
disabled_paths: /rss.xml
placeholderSrc: ''
lazysizes:
lazyClass: lazyload
loadedClass: lazyloaded
loadingClass: lazyloading
preloadClass: lazypreload
errorClass: lazyerror
autosizesClass: lazyautosizes
srcAttr: data-src
srcsetAttr: data-srcset
sizesAttr: data-sizes
minSize: 40
customMedia: { }
init: true
expFactor: 1.5
hFac: 0.8
loadMode: 2
loadHidden: true
ricTimeout: 0
throttleDelay: 125
plugins: { }
lazy.settings:
type: config_object
mapping:
image_fields:
type: sequence
label: 'Image fields'
sequence:
type: string
label: 'Image field'
alter_tag:
type: mapping
label: 'Inline elements to be lazy-loaded via filter'
mapping:
img:
type: string
label: 'Enable for images'
iframe:
type: string
label: 'Enable for iframes'
formatters:
type: mapping
label: 'Additional image formatters'
mapping:
lazy_image:
type: string
label: 'Image (Lazy-load)'
lazy_responsive_image:
type: string
label: 'Responsive image (Lazy-load)'
skipClass:
type: string
label: skipClass
disabled_paths:
type: string
label: disabled_paths
placeholderSrc:
type: string
label: placeholderSrc
lazysizes:
type: mapping
mapping:
lazyClass:
type: string
label: lazyClass
loadedClass:
type: string
label: loadedClass
loadingClass:
type: string
label: loadingClass
preloadClass:
type: string
label: preloadClass
errorClass:
type: string
label: errorClass
autosizesClass:
type: string
label: autosizesClass
srcAttr:
type: string
label: srcAttr
srcsetAttr:
type: string
label: srcsetAttr
sizesAttr:
type: string
label: sizesAttr
minSize:
type: integer
label: minSize
customMedia:
type: sequence
label: customMedia
sequence:
type: string
label: 'Custom Media'
init:
type: boolean
label: init
expFactor:
type: float
label: expFactor
hFac:
type: float
label: hFac
loadMode:
type: integer
label: loadMode
loadHidden:
type: boolean
label: loadHidden
ricTimeout:
type: integer
label: ricTimeout
throttleDelay:
type: integer
label: throttleDelay
plugins:
type: sequence
label: plugins
sequence:
type: string
label: Plugin
field.formatter.third_party.lazy:
type: mapping
label: 'Per field lazy-loading setting'
mapping:
lazy_image:
type: string
label: 'Lazy-loading'
.js .lazyload,
.js .lazyloading {
opacity: 0;
}
.js .lazyloaded {
opacity: 1;
transition: opacity 2000ms;
}
(function () {
(function (Drupal, drupalSettings) {
'use strict';
Drupal.behaviors.lazy = {
attach: function (context, settings) {
var options = settings.lazy ? settings.lazy : {};
Drupal.lazy = new Blazy(options);
}
};
document.addEventListener('DOMContentLoaded', function () {
var utils = {
extend: function (obj, src) {
Object.keys(src).forEach(function (key) {
obj[key] = src[key];
});
return obj;
},
hasClass: function (el, className) {
return el.classList ? el.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(el.className);
},
addClass: function (el, className) {
if (el.classList) el.classList.add(className);
else if (!hasClass(el, className)) el.className += ' ' + className;
},
removeClass: function (el, className) {
if (el.classList) el.classList.remove(className);
else el.className = el.className.replace(new RegExp('\\b' + className + '\\b', 'g'), '');
},
loadScript: function (url) {
var script = document.createElement('script'),
scripts = document.getElementsByTagName('script')[0];
script.src = url;
scripts.parentNode.insertBefore(script, scripts);
}
};
})();
var lazysizes = drupalSettings.lazy.lazysizes || {};
if ('loading' in HTMLImageElement.prototype) {
var element = document.querySelectorAll('[loading="lazy"]');
element.forEach(function (el) {
function loaded() {
el.src = el.getAttribute(lazysizes.srcAttr);
utils.removeClass(el, lazysizes.lazyClass);
utils.addClass(el, lazysizes.loadedClass);
}
if (el.complete) {
loaded()
} else {
el.addEventListener('load', loaded);
}
});
} else {
// 1. Lazysizes configuration.
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig = utils.extend(window.lazySizesConfig, lazysizes);
// 2. Load all selected lazysizes plugins.
if (!Object.entries) {
Object.entries = function(obj) {
var ownProps = Object.keys(obj),
i = ownProps.length,
resArray = new Array(i);
while (i--) {
resArray[i] = [ownProps[i], obj[ownProps[i]]];
}
return resArray;
};
}
Object.entries(lazysizes.plugins).forEach(function (path, key) {
utils.loadScript(drupalSettings.path.baseUrl + 'libraries/lazysizes/plugins/' + path[1] + '.min.js');
});
// 3. Load the lazysizes library.
utils.loadScript(drupalSettings.path.baseUrl + 'libraries/lazysizes/lazysizes.min.js');
}
});
})(Drupal, drupalSettings);
<?php
/**
* Lazy-load API.
*
* - How to add other image-based field formatters?
* - Services.
*/
/**
* Alter enabled field formatters for lazy-loading.
*
* @param $formatters
* Array of field formatters.
*
* @return array
*/
function hook_lazy_field_formatters_alter(&$formatters) {
$formatters[] = 'hook_formatter_name';
return $formatters;
}
$disabled_paths = '/about/logo';
\Drupal::service('lazy')->isPathAllowed($disabled_paths);
......@@ -9,33 +9,34 @@
* Implements hook_requirements().
*/
function lazy_requirements($phase) {
$requirements = [];
if ($phase == 'runtime') {
$library = Drupal::service('library.discovery')
->getLibraryByName('lazy', 'lazy-blazy');
$has_blazy = FALSE;
if ($library['js']) {
foreach ($library['js'] as $js) {
if (($js['type'] == 'file') && file_exists(DRUPAL_ROOT . '/' . $js['data'])) {
$has_blazy = TRUE;
}
}
}
if ($phase != 'runtime') {
return [];
}
$requirements['blazy'] = [
'title' => t('bLazy library'),
];
$requirements['blazy']['value'] = $has_blazy ? t('Enabled') : t('Not found');
$requirements['blazy']['severity'] = $has_blazy ? REQUIREMENT_OK : REQUIREMENT_WARNING;
$library = Drupal::service('library.discovery')->getLibraryByName('lazy', 'lazysizes');
$library_exists = FALSE;
if (!$has_blazy) {
$requirements['blazy']['description'] = t('Lazy-load modules requires the bLazy library. See README.md file for instructions.');
foreach ($library['js'] as $js) {
if (strpos($js['data'], 'lazysizes.min.js') !== FALSE) {
$library_exists = file_exists(DRUPAL_ROOT . '/' . $js['data']);
}
}
return $requirements;
return [
'lazy_lazysizes' => [
'title' => t('Lazysizes library'),
'value' => $library_exists ? $library['version'] : t('Not installed'),
'description' => $library_exists ? '' : t('The Lazysizes library needs to be <a href="@url">downloaded</a> and extracted into the /libraries/lazysizes folder in your Drupal installation directory.', ['@url' => 'https://github.com/aFarkas/lazysizes/archive/5.1.1.zip']),
'severity' => $library_exists ? REQUIREMENT_OK : REQUIREMENT_ERROR,
],
];
}
/**
* Implmenets hook_install().
*/
function lazy_install() {
lazy__update_migrate_config();
}
/**
......@@ -64,3 +65,79 @@ function lazy_update_8202() {
return NULL;
}
/**
* Migrate lazy configuration.
*/
function lazy_update_8301() {
lazy__update_migrate_config();
}
/**
* Upgrade and migrate lazy configuration.
*/
function lazy__update_migrate_config() {
$config_factory = \Drupal::configFactory();
$config = $config_factory->getEditable('lazy.settings');
// Add defaults for the new lazysizes library.
if (empty($config->get('lazysizes'))) {
$lazysizes = [
'lazyClass' => 'lazyload',
'loadedClass' => 'lazyloaded',
'loadingClass' => 'lazyloading',
'preloadClass' => 'lazypreload',
'errorClass' => 'lazyerror',
'autosizesClass' => 'lazyautosizes',
'srcAttr' => 'data-src',
'srcsetAttr' => 'data-srcset',
'sizesAttr' => 'data-sizes',
'minSize' => 40,
'customMedia' => [],
'init' => TRUE,
'expFactor' => 1.5,
'hFac' => 0.8,
'loadMode' => 2,
'loadHidden' => TRUE,
'ricTimeout' => 0,
'throttleDelay' => 125,
'plugins' => [],
];
$config->set('lazysizes', $lazysizes);
}
// Migrate legacy configuration.
$selector = $config->get('selector') ? $config->get('selector') : $config->get('lazysizes.lazyClass');
$errorClass = $config->get('errorClass') ? $config->get('errorClass') : $config->get('lazysizes.errorClass');
$successClass = $config->get('successClass') ? $config->get('successClass') : $config->get('lazysizes.loadedClass');
$src = $config->get('src') ? $config->get('src') : $config->get('lazysizes.srcAttr');
$image_fields = $config->get('image_fields');
$image_fields_updated = [];
foreach ($image_fields as $image_field => $bool) {
$parts = explode('--', $image_field);
$key = implode('--', [$parts[0], $parts[1], $parts[3], $parts[2]]);
$image_fields_updated[$key] = TRUE;
}
$alter_tag = $config->get('alter_tag');
foreach ($alter_tag as $key => $value) {
$alter_tag[$key] = (string) $value;
}
$config
->set('lazysizes.lazyClass', $selector)
->set('lazysizes.errorClass', $errorClass)
->set('lazysizes.srcAttr', $src)
->set('image_fields', $image_fields_updated)
->set('alter_tag', $alter_tag)
->clear('errorClass')
->clear('loadInvisible')
->clear('offset')
->clear('saveViewportOffsetDelay')
->clear('selector')
->clear('src')
->clear('successClass')
->clear('validateDelay')
->save();
}
# Lazy-load `lazy/lazy`
lazy:
version: VERSION
js:
js/lazy.js: { }
css:
theme:
css/lazysizes.css: { }
dependencies:
- core/drupal
- core/drupalSettings
- lazy/lazy-blazy
lazy-blazy:
# Lazysizes library and its plugins `lazy/lazysizes`
#
# This library is not used within the Lazy-load module.
# They are loaded conditionally and asynchronously from `js/lazy.js`.
# It's provided here only to support customized solutions.
lazysizes:
header: true
remote: https://github.com/dinbror/blazy
version: 1.8.2
remote: https://github.com/aFarkas/lazysizes
version: 5.1.1
license:
name: MIT
url: https://github.com/dinbror/blazy/blob/master/LICENSE
gpl-compatible: true
url: https://github.com/aFarkas/lazysizes/blob/master/LICENSE
js:
# //cdn.jsdelivr.net/blazy/latest/blazy.min.js: { type: external, minified: true }
/libraries/blazy/blazy.min.js: { minified: true }
/libraries/lazysizes/plugins/artdirect/ls.artdirect.min.js: { minified: true }
/libraries/lazysizes/plugins/aspectratio/ls.aspectratio.min.js: { minified: true }
/libraries/lazysizes/plugins/attrchange/ls.attrchange.min.js: { minified: true }
/libraries/lazysizes/plugins/bgset/ls.bgset.min.js: { minified: true }
/libraries/lazysizes/plugins/blur-up/ls.blur-up.min.js: { minified: true }
/libraries/lazysizes/plugins/custommedia/ls.custommedia.min.js: { minified: true }
/libraries/lazysizes/plugins/fix-edge-h-descriptor/ls.fix-edge-h-descriptor.min.js: { minified: true }
/libraries/lazysizes/plugins/fix-ios-sizes/fix-ios-sizes.min.js: { minified: true }
/libraries/lazysizes/plugins/include/ls.include.min.js: { minified: true }
/libraries/lazysizes/plugins/native-loading/ls.native-loading.min.js: { minified: true }
/libraries/lazysizes/plugins/noscript/ls.noscript.min.js: { minified: true }
/libraries/lazysizes/plugins/object-fit/ls.object-fit.min.js: { minified: true }
/libraries/lazysizes/plugins/optimumx/ls.optimumx.min.js: { minified: true }
/libraries/lazysizes/plugins/parent-fit/ls.parent-fit.min.js: { minified: true }
/libraries/lazysizes/plugins/print/ls.print.min.js: { minified: true }
/libraries/lazysizes/plugins/progressive/ls.progressive.min.js: { minified: true }
/libraries/lazysizes/plugins/respimg/ls.respimg.min.js: { minified: true }
/libraries/lazysizes/plugins/rias/ls.rias.min.js: { minified: true }
/libraries/lazysizes/plugins/static-gecko-picture/ls.static-gecko-picture.min.js: { minified: true }
/libraries/lazysizes/plugins/twitter/ls.twitter.min.js: { minified: true }
/libraries/lazysizes/plugins/unload/ls.unload.min.js: { minified: true }
/libraries/lazysizes/plugins/unveilhooks/ls.unveilhooks.min.js: { minified: true }
/libraries/lazysizes/plugins/video-embed/ls.video-embed.min.js: { minified: true }
/libraries/lazysizes/lazysizes.min.js: { minified: true }
This diff is collapsed.
services:
lazy:
class: Drupal\lazy\Lazy
arguments: ['@config.factory', '@path.current', '@path.alias_manager', '@path.matcher']
This diff is collapsed.
<?php
namespace Drupal\lazy;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Path\PathMatcherInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Class Lazy.
*/
class Lazy implements LazyInterface {
/**
* Defines the interface for a configuration object factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Represents the current path for the current request.
*
* @var \Drupal\Core\Path\CurrentPathStack
*/
protected $pathCurrent;
/**
* Find an alias for a path and vice versa.
*
* @var \Drupal\Core\Path\AliasManagerInterface
*/
protected $pathAliasManager;
/**
* Provides an interface for URL path matchers.
*
* @var \Drupal\Core\Path\PathMatcherInterface
*/
protected $pathMatcher;
/**
* Lazy constructor.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* @param \Drupal\Core\Path\CurrentPathStack $path_current
* @param \Drupal\Core\Path\AliasManagerInterface $path_alias_manager
* @param \Drupal\Core\Path\PathMatcherInterface $path_matcher
*/
public function __construct(ConfigFactoryInterface $config_factory, CurrentPathStack $path_current, AliasManagerInterface $path_alias_manager, PathMatcherInterface $path_matcher) {
$this->configFactory = $config_factory;
$this->pathCurrent = $path_current;
$this->pathAliasManager = $path_alias_manager;
$this->pathMatcher = $path_matcher;
}
/**
* {@inheritdoc}
*/
public function getPlugins(): array {
return [
'artdirect' => 'artdirect/ls.artdirect',
'aspectratio' => 'aspectratio/ls.aspectratio',
'attrchange' => 'attrchange/ls.attrchange',
'bgset' => 'bgset/ls.bgset',
'blur-up' => 'blur-up/ls.blur-up',
'custommedia' => 'custommedia/ls.custommedia',
'fix-edge-h-descriptor' => 'fix-edge-h-descriptor/ls.fix-edge-h-descriptor',
'fix-ios-sizes' => 'fix-ios-sizes/fix-ios-sizes',
'include' => 'include/ls.include',
'native-loading' => 'native-loading/ls.native-loading',
'noscript' => 'noscript/ls.noscript',
'object-fit' => 'object-fit/ls.object-fit',
'optimumx' => 'optimumx/ls.optimumx',
'parent-fit' => 'parent-fit/ls.parent-fit',
'print' => 'print/ls.print',
'progressive' => 'progressive/ls.progressive',
'respimg' => 'respimg/ls.respimg',
'rias' => 'rias/ls.rias',
'static-gecko-picture' => 'static-gecko-picture/ls.static-gecko-picture',
'twitter' => 'twitter/ls.twitter',
'unload' => 'unload/ls.unload',
'unveilhooks' => 'unveilhooks/ls.unveilhooks',
'video-embed' => 'video-embed/ls.video-embed',
];
}
/**
* {@inheritdoc}
*/
public function isEnabled() {
$config = $this->configFactory->get('lazy.settings')->get();
$status = [];
if ($filters = $this->isFiltersEnabled()) {
$status = array_merge($status, $filters);
}
if ($fields = $this->isFieldsEnabled($config['image_fields'])) {
$status = array_merge($status, $fields);
}
$config['status'] = $status;
return count($status) ? $config : FALSE;
}
/**
* {@inheritdoc}
*/
public function isFieldsEnabled($image_fields) {
$status = [];
$image_fields = is_array($image_fields) ? $image_fields : [];
foreach ($image_fields as $tag => $option) {
if ($image_fields[$tag]) {
$status[$tag] = (bool) $option;
}
}
return count($status) ? $status : FALSE;
}
/**
* {@inheritdoc}
*/
public function isFiltersEnabled() {
$status = [];
foreach (filter_formats() as $key => $filter) {
if (
$filter->status()
&& isset($filter->getDependencies()['module'])
&& in_array('lazy', $filter->getDependencies()['module'], TRUE)
) {
$status[$filter->id()] = TRUE;
}
}
return count($status) ? $status : FALSE;
}
/**
* {@inheritdoc}
*/
public function isPathAllowed($disabled_paths): bool {
$current_path = $this->pathCurrent->getPath();
$current_path_matcher = $this->pathMatcher->matchPath($current_path, $disabled_paths);
$path_alias = $this->pathAliasManager->getAliasByPath($current_path);
$path_alias_matcher = $this->pathMatcher->matchPath($path_alias, $disabled_paths);
return !($current_path_matcher || $path_alias_matcher);
}
}
<?php
namespace Drupal\lazy;
/**
* Interface LazyInterface.
*/
interface LazyInterface {
/**
* List of available plugins.
*
* @return array
*/
public function getPlugins():array ;
/**
* Is Lazy enabled.
*
* @return array|false
*/
public function isEnabled();
/**
* Check whether any fields enabled.
*
* @param $image_fields
*
* @return array|false
*/
public function isFieldsEnabled($image_fields);
/**
* Check whether any text-format filters enabled.
*
* @return array|false
*/
public function isFiltersEnabled();
/**
* Checks whether lazy-load is disabled for the current path.
*
* @param string $disabled_paths
* List of paths Lazy should be disabled.
*
* @return bool
* Whether Lazy is disabled for the requested path.
*/
public function isPathAllowed($disabled_paths): bool;
}
<?php
namespace Drupal\lazy\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatter;
/**
* Plugin implementation of the 'lazy_image' formatter.
*
* @FieldFormatter(
* id = "lazy_image",
* label = @Translation("Image (Lazy-load)"),
* field_types = {
* "image"
* },
* quickedit = {
* "editor" = "image"
* }
* )
*/
class LazyImageFormatter extends ImageFormatter {
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
$summary[] = $this->t('Lazy-loading enabled');
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = parent::viewElements($items, $langcode);
foreach ($elements as $delta => $element) {
$elements[$delta]['#item_attributes']['data-lazy'] = TRUE;
}
return $elements;
}
}
<?php
namespace Drupal\lazy\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\responsive_image\Plugin\Field\FieldFormatter\ResponsiveImageFormatter;
/**
* Plugin implementation of the 'lazy_responsive_image' formatter.
*
* @FieldFormatter(
* id = "lazy_responsive_image",
* label = @Translation("Responsive image (Lazy-load)"),
* field_types = {
* "image"
* }
* )
*/
class LazyResponsiveImageFormatter extends ResponsiveImageFormatter {
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
$summary[] = $this->t('Lazy-loading enabled');
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = parent::viewElements($items, $langcode);
foreach ($elements as $delta => $element) {
$elements[$delta]['#item_attributes']['data-lazy'] = TRUE;
}
return $elements;
}
}
......@@ -11,7 +11,7 @@ use Drupal\filter\Plugin\FilterBase;
*
* @Filter(
* id = "lazy_filter",
* title = @Translation("Lazy-load images and IFRAMEs via bLazy"),
* title = @Translation("Lazy-load images and iframes"),
* description = @Translation("<a href=':url'>Configure options</a>", arguments = {":url" = "/admin/config/content/lazy"}),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
* weight = 20
......@@ -25,18 +25,16 @@ class LazyFilter extends FilterBase {
public function process($text, $langcode) {
$config = \Drupal::config('lazy.settings')->get();
$opt_skipClass = $config['skipClass'];
$opt_selector = ltrim($config['selector'], '.');
$opt_selector = $config['lazysizes']['lazyClass'];
$opt_tags = $config['alter_tag'];
$opt_src = ($config['src'] !== 'src') ? $config['src'] : 'data-filterlazy-src';
$opt_placeholderSrc = $config['placeholderSrc'];
$opt_src = ($config['lazysizes']['srcAttr'] !== 'src') ? $config['lazysizes']['srcAttr'] : 'data-filterlazy-src';
$result = new FilterProcessResult($text);
$html_dom = Html::load($text);
$pages = $config['disabled_paths'];
$path_matches = lazy_disabled_by_path($pages);
$path_allowed = \Drupal::service('lazy')->isPathAllowed($config['disabled_paths']);
if (!$path_matches) {
if ($path_allowed) {
foreach ($opt_tags as $tag => $status) {
$matches = $html_dom->getElementsByTagName($tag);
foreach ($matches as $element) {
......@@ -62,11 +60,12 @@ class LazyFilter extends FilterBase {
$classes = array_unique($classes);
$element->setAttribute('class', implode(' ', $classes));
$element->setAttribute('loading', 'lazy');
$src = $element->getAttribute('src');
$element->removeAttribute('src');
$element->setAttribute($opt_src, $src);
$element->setAttribute('src', $opt_placeholderSrc);
}
}
}
......