...
 
Commits (20)
......@@ -15,35 +15,114 @@ and **inline-iframes**.
You can still use this module tandem with it, though that is not a requirement.
## Requirements
* **[Libraries API][4]** module
* **bLazy v1.8.2** script as a library item:
[Download bLazy][3] from https://github.com/dinbror/blazy
1) Extract the downloaded file,
2) rename *blazy-master* directory to *blazy*,
3) copy the folder into one of the following places that *Libraries API*
module supports, `sites/all/libraries` (or site-specific libraries folder):
i.e.: `sites/all/libraries/blazy/blazy.min.js`
* **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
"extra": {
"installer-paths": {
"web/libraries/{$name}": ["type:drupal-library"]
}
}
```
- 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"
}
}
}
## Installation
]
```
- Install the required _Blazy_ library:
``` sh
composer require 'dinbror/blazy:1.8.2'
```
- Install this module:
``` sh
composer require 'drupal/lazy:^2.0'
```
Install the module as usual. More information can be found at
https://www.drupal.org/docs/7/extend/installing-modules
## 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).
1. Image fields
2. Inline images and Iframes managed in rich-text fields (ckeditor)
### 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:
This modules makes a new text filter available for the text-formats: *Lazy-load*
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.
Enable the *Lazy-load* filter for the desired text-formats. i.e. *Full HTML* or
*Filtered HTML*
#### Get Blazy plugin options.
```js
let opt = drupalSettings.lazy;
```
Check out the module configuration at `admin/config/content/lazy`. The default
settings should work for most developers. Incase they are not, change the
settings to suit your needs and submit the form.
#### 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 |
This configuration is used globally for all the text-formats having *Lazy-load*
filter enabled.
## Use Case
......@@ -65,3 +144,4 @@ cached, there should not be any changes in performance.
[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/
{
"name": "drupal/lazy",
"type": "drupal-module",
"description": "Lazy-loading images and iframes",
"keywords": ["Drupal", "bLazy", "Lazy-load"],
"license": "GPL-2.0+",
"homepage": "https://www.drupal.org/project/lazy",
"authors": [
{
"name": "Osman Gormus",
"homepage": "https://www.drupal.org/u/osman",
"role": "Maintainer"
}
],
"support": {
"issues": "https://www.drupal.org/project/issues/lazy",
"source": "http://cgit.drupalcode.org/lazy"
}
}
errorClass: "b-error"
loadInvisible: false
offset: 100
saveViewportOffsetDelay: 50
selector: "b-lazy"
alter_tag:
img: 0
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
(function ($) {
(function () {
'use strict';
Drupal.behaviors.lazy = {
attach: function (context, settings) {
var options = settings.lazy.bLazy ? settings.lazy.bLazy : {};
new Blazy(options);
var options = settings.lazy ? settings.lazy : {};
Drupal.lazy = new Blazy(options);
}
};
})(jQuery);
})();
<?php
/**
* @file
* Configuration form for Lazy-load.
*/
/**
* Lazy-load configuration form.
*/
function lazy_configuration_form($form, $form_state) {
$defaults = variable_get('lazy_filter_defaults');
$filter_enabled = lazy_is_filter_enabled();
$description = t('The %filter filter must be enabled for at least one <a href="!path">text-format</a>.', array(
'!path' => url('admin/config/content/formats'),
'%filter' => 'Lazy-load',
));
$form['settings'] = array(
'#type' => 'fieldset',
'#title' => t('Global settings'),
'#collapsible' => FALSE,
);
$form['settings']['lazy_filter_alter_tag'] = array(
'#type' => 'checkboxes',
'#title' => t('Select the inline elements to be lazy-loaded via filter.'),
'#options' => array(
'img' => t('Images (%img tags)', array('%img' => '<img>')),
'iframe' => t('Iframes (%iframe tags)', array('%iframe' => '<iframe>')),
),
'#default_value' => variable_get('lazy_filter_alter_tag', $defaults['lazy_filter_alter_tag']),
'#description' => $filter_enabled ? '' : $description,
'#disabled' => !$filter_enabled,
);
$form['settings']['lazy_filter_image_fields'] = array(
'#type' => 'checkbox',
'#title' => t('Enable on image fields attached to fieldable entities. For example, content-types, blocks.'),
'#description' => t('Default is %val', array('%val' => ($defaults['lazy_filter_loadInvisible']) ? 'checked' : 'unchecked')),
'#default_value' => variable_get('lazy_filter_image_fields', $defaults['lazy_filter_image_fields']),
'#return_value' => TRUE,
);
$form['settings']['lazy_filter_placeholderSrc'] = array(
'#type' => 'textfield',
'#title' => t('Placeholder image URL'),
'#description' => t('Default is %val', array('%val' => $defaults['lazy_filter_placeholderSrc'])),
'#default_value' => variable_get('lazy_filter_placeholderSrc', $defaults['lazy_filter_placeholderSrc']),
);
$form['blazy'] = array(
'#type' => 'fieldset',
'#title' => t('bLazy configuration'),
'#description' => t('<p><a href="!url">bLazy</a> is a lightweight lazy loading and multi-serving image script created by Bjoern Klinggaard. See its website for usage details and demos.</p>', array(
'!url' => 'http://dinbror.dk/blog/blazy/',
)),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['blazy']['lazy_filter_loadInvisible'] = array(
'#type' => 'checkbox',
'#title' => t('loadInvisible'),
'#description' => t('If checked loads invisible (hidden) elements. Default is %val', array('%val' => ($defaults['lazy_filter_loadInvisible']) ? 'checked' : 'unchecked')),
'#default_value' => variable_get('lazy_filter_loadInvisible', $defaults['lazy_filter_loadInvisible']),
'#return_value' => TRUE,
);
$form['blazy']['lazy_filter_offset'] = array(
'#type' => module_exists('elements') ? 'numberfield' : 'textfield',
'#title' => t('offset'),
'#description' => t('The offset controls how early you want the elements to be loaded before they’re visible. Default is %val, so %val pixel before an element is visible it’ll start loading.', array('%val' => $defaults['lazy_filter_offset'])),
'#default_value' => variable_get('lazy_filter_offset', $defaults['lazy_filter_offset']),
);
$form['blazy']['lazy_filter_saveViewportOffsetDelay'] = array(
'#type' => module_exists('elements') ? 'numberfield' : 'textfield',
'#title' => t('saveViewportOffsetDelay'),
'#description' => t('Delay for how often it should call the saveViewportOffset function on resize. Default is %val', array('%val' => $defaults['lazy_filter_saveViewportOffsetDelay'])),
'#default_value' => variable_get('lazy_filter_saveViewportOffsetDelay', $defaults['lazy_filter_saveViewportOffsetDelay']),
);
$form['blazy']['lazy_filter_validateDelay'] = array(
'#type' => module_exists('elements') ? 'numberfield' : 'textfield',
'#title' => t('validateDelay'),
'#description' => t('Delay for how often it should call the validate function on scroll/resize. Default is %val', array('%val' => $defaults['lazy_filter_validateDelay'])),
'#default_value' => variable_get('lazy_filter_validateDelay', $defaults['lazy_filter_validateDelay']),
);
$form['blazy']['lazy_filter_selector'] = array(
'#type' => 'textfield',
'#title' => t('Selector class'),
'#description' => t('Element selector for elements that should lazy load. Do not include a leading period. Default is %val', array('%val' => $defaults['lazy_filter_selector'])),
'#default_value' => variable_get('lazy_filter_selector', $defaults['lazy_filter_selector']),
);
$form['blazy']['lazy_filter_skipClass'] = array(
'#type' => 'textfield',
'#title' => t('skipClass'),
'#description' => t('Elements having this class name will be ignored. Default is %val', array('%val' => $defaults['lazy_filter_skipClass'])),
'#default_value' => variable_get('lazy_filter_skipClass', $defaults['lazy_filter_skipClass']),
);
$form['blazy']['lazy_filter_errorClass'] = array(
'#type' => 'textfield',
'#title' => t('errorClass'),
'#description' => t('The classname an element will get if something goes wrong. Default is %val', array('%val' => $defaults['lazy_filter_errorClass'])),
'#default_value' => variable_get('lazy_filter_errorClass', $defaults['lazy_filter_errorClass']),
);
$form['blazy']['lazy_filter_successClass'] = array(
'#type' => 'textfield',
'#title' => t('successClass'),
'#description' => t('The classname an element will get when loaded. Default is %val', array('%val' => $defaults['lazy_filter_successClass'])),
'#default_value' => variable_get('lazy_filter_successClass', $defaults['lazy_filter_successClass']),
);
$form['blazy']['lazy_filter_src'] = array(
'#type' => 'textfield',
'#title' => t('src'),
'#description' => t('Attribute where the original element source will be assigned. Do not change this unless attribute is used for other purposes. Default is %val', array('%val' => $defaults['lazy_filter_src'])),
'#default_value' => variable_get('lazy_filter_src', $defaults['lazy_filter_src']),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
return $form;
}
/**
* Submit handler for lazy_configuration_form().
*/
function lazy_configuration_form_submit($form, &$form_state) {
$int_value = array(
'lazy_filter_offset',
'lazy_filter_saveViewportOffsetDelay',
'lazy_filter_validateDelay',
);
$bool_value = array(
'lazy_filter_loadInvisible',
);
// Exclude unnecessary elements.
form_state_values_clean($form_state);
foreach ($form_state['values'] as $key => $value) {
if (in_array($key, $int_value)) {
$value = intval($value);
}
if (in_array($key, $bool_value)) {
$value = boolval($value);
}
variable_set($key, $value);
}
drupal_set_message(t('The configuration options have been saved.'));
}
name = Lazy-load
description = "This module integrates bLazy script to lazy load inline images and/or iframes via input-filters."
core = 7.x
dependencies[] = drupal:filter
dependencies[] = libraries
package = "Input filters"
configure = admin/config/content/lazy
name: 'Lazy-load'
type: module
description: 'This module integrates bLazy script to lazy load inline images and/or iframes via input-filters.'
core: 8.x
package: 'Input filters'
configure: lazy.config_form
dependencies:
- drupal:filter
......@@ -5,90 +5,33 @@
* Install, update, and uninstall functions for the Lazy-load module.
*/
/**
* Implements hook_install().
*/
function lazy_install() {
$defaults = array(
'lazy_filter_errorClass' => 'b-error',
'lazy_filter_loadInvisible' => FALSE,
'lazy_filter_offset' => 100,
'lazy_filter_saveViewportOffsetDelay' => 50,
'lazy_filter_selector' => 'b-lazy',
'lazy_filter_alter_tag' => array('img' => 'img', 'iframe' => 'iframe'),
'lazy_filter_skipClass' => 'no-b-lazy',
'lazy_filter_src' => 'data-src',
'lazy_filter_successClass' => 'b-loaded',
'lazy_filter_validateDelay' => 25,
'lazy_filter_placeholderSrc' => 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
'lazy_filter_image_fields' => FALSE,
);
variable_set('lazy_filter_defaults', $defaults);
foreach ($defaults as $key => $value) {
variable_set($key, $value);
}
}
/**
* Implements hook_enable().
*/
function lazy_enable() {
$options = array(
'!config' => url('admin/config/content/lazy'),
'%filter' => 'Lazy-load',
'!path' => url('admin/config/content/formats'),
);
$message = t('The <a href="!config">Lazy</a> module was installed. The new %filter filter should be enabled in desired <a href="!path">text-formats</a>.', $options);
drupal_set_message($message, 'warning');
}
/**
* Implements hook_uninstall().
*/
function lazy_uninstall() {
variable_del('lazy_filter_defaults');
variable_del('lazy_filter_errorClass');
variable_del('lazy_filter_loadInvisible');
variable_del('lazy_filter_offset');
variable_del('lazy_filter_saveViewportOffsetDelay');
variable_del('lazy_filter_selector');
variable_del('lazy_filter_alter_tag');
variable_del('lazy_filter_skipClass');
variable_del('lazy_filter_src');
variable_del('lazy_filter_successClass');
variable_del('lazy_filter_validateDelay');
variable_del('lazy_filter_placeholderSrc');
variable_del('lazy_filter_image_fields');
$module = 'lazy';
$name = 'lazy_filter';
db_delete('filter')
->condition('module', $module)
->condition('name', $name)
->execute();
}
/**
* Implements hook_requirements().
*/
function lazy_requirements($phase) {
$requirements = array();
$t = get_t();
$requirements = [];
if ($phase == 'runtime') {
$blazy = libraries_detect('blazy');
$requirements['blazy'] = array(
'title' => $t('bLazy'),
);
if ($blazy['installed']) {
$requirements['blazy']['value'] = $blazy['version'];
$requirements['blazy']['severity'] = REQUIREMENT_OK;
$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;
}
}
}
else {
$requirements['blazy']['value'] = $blazy['error'];
$requirements['blazy']['description'] = $blazy['error message'];
$requirements['blazy']['severity'] = REQUIREMENT_ERROR;
$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;
if (!$has_blazy) {
$requirements['blazy']['description'] = t('Lazy-load modules requires the bLazy library. See README.md file for instructions.');
}
}
......@@ -96,34 +39,28 @@ function lazy_requirements($phase) {
}
/**
* Update module default values.
* Reset "disabled_paths" configuration to module default.
*/
function lazy_update_7100() {
// Get current default values.
$defaults = variable_get('lazy_filter_defaults');
function lazy_update_8201() {
$config = \Drupal::service('config.factory')->getEditable('lazy.settings');
// Update it from old string value to new array value.
$defaults['lazy_filter_alter_tag'] = array('img' => 'img', 'iframe' => 'iframe');
// Fix already existing value with new format.
$lazy_filter_alter_tag = variable_get('lazy_filter_alter_tag');
switch ($lazy_filter_alter_tag) {
case 'img':
variable_set('lazy_filter_alter_tag', array('img' => 'img', 'iframe' => 0));
break;
if ($config->get('disabled_paths') === NULL) {
$config->set('disabled_paths', '/rss.xml');
return t('The new "disabled_paths" configuration is set to "/rss.xml" (default value).');
}
case 'iframe':
variable_set('lazy_filter_alter_tag', array('img' => 0, 'iframe' => 'iframe'));
break;
return NULL;
}
default:
variable_set('lazy_filter_alter_tag', $defaults['lazy_filter_alter_tag']);
break;
/**
* Reset "image_fields" configuration to module default.
*/
function lazy_update_8202() {
$config = \Drupal::service('config.factory')->getEditable('lazy.settings');
if ($config->get('image_fields') === TRUE) {
$config->set('image_fields', FALSE);
return t('<b>Action needed:</b> As of 8.x-2.x, image fields are now controlled individually. You need to manually update each image field to enable lazy-loading.');
}
// Add new setting to support image fields.
$defaults['lazy_filter_image_fields'] = FALSE;
// Update the defaults with new values.
variable_set('lazy_filter_defaults', $defaults);
variable_set('lazy_filter_image_fields', $defaults['lazy_filter_image_fields']);
return NULL;
}
lazy:
version: VERSION
js:
js/lazy.js: { }
dependencies:
- core/drupal
- core/drupalSettings
- lazy/lazy-blazy
lazy-blazy:
header: true
remote: https://github.com/dinbror/blazy
version: 1.8.2
license:
name: MIT
url: https://github.com/dinbror/blazy/blob/master/LICENSE
gpl-compatible: true
js:
# //cdn.jsdelivr.net/blazy/latest/blazy.min.js: { type: external, minified: true }
/libraries/blazy/blazy.min.js: { minified: true }
lazy.config_form:
title: 'Lazy-load configuration'
parent: system.admin_config_content
description: 'Configure how images and iframes are lazy-loaded in content.'
route_name: lazy.config_form
This diff is collapsed.
lazy.config_form:
path: 'admin/config/content/lazy'
defaults:
_title: 'Lazy-load'
_form: 'Drupal\lazy\Form\LazyForm'
requirements:
_permission: 'administer filters'
This diff is collapsed.
<?php
namespace Drupal\lazy\Plugin\Filter;
use Drupal\Component\Utility\Html;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
* Provides a filter to lazy-load images.
*
* @Filter(
* id = "lazy_filter",
* title = @Translation("Lazy-load images and IFRAMEs via bLazy"),
* description = @Translation("<a href=':url'>Configure options</a>", arguments = {":url" = "/admin/config/content/lazy"}),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
* weight = 20
* )
*/
class LazyFilter extends FilterBase {
/**
* {@inheritdoc}
*/
public function process($text, $langcode) {
$config = \Drupal::config('lazy.settings')->get();
$opt_skipClass = $config['skipClass'];
$opt_selector = ltrim($config['selector'], '.');
$opt_tags = $config['alter_tag'];
$opt_src = ($config['src'] !== 'src') ? $config['src'] : 'data-filterlazy-src';
$opt_placeholderSrc = $config['placeholderSrc'];
$result = new FilterProcessResult($text);
$html_dom = Html::load($text);
$pages = $config['disabled_paths'];
$path_matches = lazy_disabled_by_path($pages);
if (!$path_matches) {
foreach ($opt_tags as $tag => $status) {
$matches = $html_dom->getElementsByTagName($tag);
foreach ($matches as $element) {
$classes = $element->getAttribute('class');
$classes = ($classes !== '') ? explode(' ', $classes) : [];
$parent_classes = $element->parentNode->getAttribute('class');
$parent_classes = ($parent_classes !== '') ? explode(' ', $parent_classes) : [];
if (empty($opt_tags[$tag])) {
// If the `tag` is not enabled remove the bLazy selector class.
if (($key = array_search($opt_selector, $classes, FALSE)) !== FALSE) {
unset($classes[$key]);
$element->setAttribute('class', implode(' ', $classes));
if (empty($classes)) {
$element->removeAttribute('class');
}
}
}
else {
// `tag` is enabled. Make sure skipClass is not set before
// proceeding.
if (!in_array($opt_skipClass, $classes, FALSE) && !in_array($opt_skipClass, $parent_classes, FALSE)) {
$classes[] = $opt_selector;
$classes = array_unique($classes);
$element->setAttribute('class', implode(' ', $classes));
$src = $element->getAttribute('src');
$element->removeAttribute('src');
$element->setAttribute($opt_src, $src);
$element->setAttribute('src', $opt_placeholderSrc);
}
}
}
}
}
$result->setProcessedText(Html::serialize($html_dom));
return $result;
}
/**
* {@inheritdoc}
*/
public function tips($long = FALSE) {
$settings = \Drupal::config('lazy.settings');
$tags = $settings->get('alter_tag');
$skip = $settings->get('skipClass');
$options = ['%img' => '<img>', '%iframe' => '<iframe>'];
$skip_help = t('If you want certain elements skip lazy-loading, add <code>%class</code> class name.', ['%class' => $skip]);
if (!empty($tags)) {
if ($tags['img'] && $tags['iframe']) {
return t('Lazy-loading is enabled for both %img and %iframe tags.', $options) . ' ' . $skip_help;
}
elseif ($tags['img']) {
return t('Lazy-loading is enabled for %img tags.', $options) . ' ' . $skip_help;
}
elseif ($tags['iframe']) {
return t('Lazy-loading is enabled for %iframe tags.', $options) . ' ' . $skip_help;
}
}
return t('Lazy-loading is not enabled.');
}
}