Commit 568f942a by Manzar Hussain

add new module for slick slider

parent 4af59c0d
......@@ -17,10 +17,13 @@
"require": {
"composer/installers": "^1.9",
"drupal/admin_toolbar": "^2.4",
"drupal/blazy": "^2.1",
"drupal/core-composer-scaffold": "^9",
"drupal/core-project-message": "^9",
"drupal/core-recommended": "^9",
"drupal/core-vendor-hardening": "^9",
"drupal/slick": "^2.2",
"drupal/slick_views": "^2.3",
"drush/drush": "^10.3"
},
"conflict": {
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "fef43a42f38e0e2a9bafbff1a395d5c0",
"content-hash": "d7677eef25260d44f08c46a77379b7c3",
"packages": [
{
"name": "asm89/stack-cors",
......@@ -1373,6 +1373,70 @@
}
},
{
"name": "drupal/blazy",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://git.drupalcode.org/project/blazy.git",
"reference": "8.x-2.1"
},
"dist": {
"type": "zip",
"url": "https://ftp.drupal.org/files/projects/blazy-8.x-2.1.zip",
"reference": "8.x-2.1",
"shasum": "bbdc3d9b5a49dca8e59373fd80739db84f8fa64a"
},
"require": {
"drupal/core": "^8.8 || ^9"
},
"type": "drupal-module",
"extra": {
"drupal": {
"version": "8.x-2.1",
"datestamp": "1589120601",
"security-coverage": {
"status": "covered",
"message": "Covered by Drupal's security advisory policy"
}
}
},
"notification-url": "https://packages.drupal.org/8/downloads",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Gaus Surahman",
"homepage": "https://www.drupal.org/u/gausarts",
"role": "Maintainer"
},
{
"name": "Contributors",
"homepage": "https://www.drupal.org/node/2663268/committers",
"role": "Contributor"
},
{
"name": "geek-merlin",
"homepage": "https://www.drupal.org/user/229048"
},
{
"name": "sun",
"homepage": "https://www.drupal.org/user/54136"
}
],
"description": "Provides basic bLazy integration for lazy loading and multi-serving images.",
"homepage": "https://drupal.org/project/blazy",
"keywords": [
"Drupal",
"bLazy",
"lazyload"
],
"support": {
"source": "https://git.drupalcode.org/project/blazy",
"issues": "https://drupal.org/project/issues/blazy"
}
},
{
"name": "drupal/core",
"version": "9.0.9",
"source": {
......@@ -1820,6 +1884,118 @@
]
},
{
"name": "drupal/slick",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://git.drupalcode.org/project/slick.git",
"reference": "8.x-2.2"
},
"dist": {
"type": "zip",
"url": "https://ftp.drupal.org/files/projects/slick-8.x-2.2.zip",
"reference": "8.x-2.2",
"shasum": "789395b52697b6cee27648e478c4e579eee9540b"
},
"require": {
"drupal/blazy": "~2.0",
"drupal/core": "^8.8 || ^9"
},
"type": "drupal-module",
"extra": {
"drupal": {
"version": "8.x-2.2",
"datestamp": "1588861104",
"security-coverage": {
"status": "covered",
"message": "Covered by Drupal's security advisory policy"
}
}
},
"notification-url": "https://packages.drupal.org/8/downloads",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Contributors",
"homepage": "https://www.drupal.org/node/2232779/committers",
"role": "Contributors"
},
{
"name": "gausarts",
"homepage": "https://www.drupal.org/user/159062"
},
{
"name": "thalles",
"homepage": "https://www.drupal.org/user/3589086"
}
],
"description": "Slick carousel, the last carousel you'll ever need.",
"homepage": "https://drupal.org/project/slick",
"keywords": [
"Drupal",
"carousel",
"slideshow"
],
"support": {
"source": "https://git.drupalcode.org/project/slick",
"issues": "https://drupal.org/project/issues/slick"
}
},
{
"name": "drupal/slick_views",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://git.drupalcode.org/project/slick_views.git",
"reference": "8.x-2.3"
},
"dist": {
"type": "zip",
"url": "https://ftp.drupal.org/files/projects/slick_views-8.x-2.3.zip",
"reference": "8.x-2.3",
"shasum": "974ece2c6a0f805c5cfc0e619f330cabf714633c"
},
"require": {
"drupal/core": "^8 || ^9",
"drupal/slick": "~2.0"
},
"type": "drupal-module",
"extra": {
"drupal": {
"version": "8.x-2.3",
"datestamp": "1589111335",
"security-coverage": {
"status": "covered",
"message": "Covered by Drupal's security advisory policy"
}
}
},
"notification-url": "https://packages.drupal.org/8/downloads",
"license": [
"GPL-2.0+"
],
"authors": [
{
"name": "gausarts",
"homepage": "https://www.drupal.org/user/159062"
}
],
"description": "Provides Slick carousel integration with Views. Slick carousel, the last carousel you'll ever need.",
"homepage": "https://drupal.org/project/slick_views",
"keywords": [
"Drupal",
"carousel",
"slideshow",
"views"
],
"support": {
"source": "http://cgit.drupalcode.org/slick_views",
"issues": "https://drupal.org/project/issues/slick_views"
}
},
{
"name": "drush/drush",
"version": "10.3.6",
"source": {
......
{
// This file extends .eslintrc at drupal root.
// Located at /modules/contrib/blazy, run:
// eslint . -o ../../eslint/blazy.html -f html
"extends": "../../../core/.eslintrc.legacy.json",
"globals": {
"Promise": true,
"Blazy": true,
"dBlazy": true,
"Bio": true,
"BioMedia": true
},
"rules": {
"consistent-this": [0, "me"],
"max-params": 0,
"id-length": [1, {"min": 1}],
// Warnings.
"max-nested-callbacks": [1, 4]
}
}
css/components/blazy.colorbox.css
name: Blazy
type: module
description: "Provides basic Blazy integration for lazy loading and multi-serving images."
core_version_requirement: ^8.8 || ^9
package: Blazy
dependencies:
- drupal:filter
- drupal:media
test_dependencies:
- drupal:responsive_image
- drupal:views
# Add a fake version so local drush and Git checkouts do not fail dependencies.
# version: 8.x-2.x
# Information added by Drupal.org packaging script on 2020-05-10
version: '8.x-2.1'
project: 'blazy'
datestamp: 1589112984
<?php
/**
* @file
* Installation actions for Blazy.
*/
/**
* Implements hook_requirements().
*/
function blazy_requirements($phase) {
if ($phase != 'runtime') {
return [];
}
$path = blazy_libraries_get_path('blazy') ?: \Drupal::root() . '/libraries/blazy';
$exists = is_file($path . '/blazy.js');
return [
'blazy_library' => [
'title' => t('Blazy library'),
'description' => $exists ? '' : t('The <a href=":url">Blazy library</a> should be installed at <strong>/libraries/blazy/blazy.js</strong>, or any path supported by libraries.module if installed. Check out file or folder permissions if troubled.', [':url' => 'https://github.com/dinbror/blazy']),
'severity' => $exists ? REQUIREMENT_OK : REQUIREMENT_ERROR,
'value' => $exists ? t('Installed') : t('Not installed'),
],
];
}
/**
* Added new services blazy.oembed and blazy.entity.
*/
function blazy_update_8201() {
// Do nothing to clear cache.
}
/**
* Added a new argument date.formatter to blazy.admin.base service.
*/
function blazy_update_8202() {
// Do nothing to clear cache.
}
/**
* Added a new argument @entity.repository to blazy.manager.base service.
*/
function blazy_update_8203() {
// Do nothing to clear cache.
}
/**
* Added new classes: BlazyUtil, BlazyBreakpoint to declutter Blazy.
*/
function blazy_update_8204() {
// Do nothing to clear cache.
}
/**
* Removed deprecated or no longer in use classes, settings, etc.
*/
function blazy_update_8205() {
$config = \Drupal::configFactory()->getEditable('blazy.settings');
foreach (['native', 'unbreakpoints'] as $key) {
$config->clear($key);
}
$config->save(TRUE);
}
blazy:
header: true
remote: https://github.com/dinbror/blazy
version: 1.x
license:
name: MIT
url: https://github.com/dinbror/blazy/blob/master/LICENSE
gpl-compatible: true
js:
/libraries/blazy/blazy.js: { weight: -4 }
base:
css:
component:
css/blazy.css: {}
filter:
css:
component:
css/components/blazy.filter.css: {}
loading:
css:
component:
css/components/blazy.loading.css: {}
dependencies:
- blazy/base
fx.blur:
css:
component:
css/components/blazy.fx.blur.css: {}
dependencies:
- blazy/loading
oembed:
css:
component:
css/components/blazy.oembed.css: {}
preview:
css:
component:
css/components/blazy.preview.css: {}
ratio:
css:
component:
css/components/blazy.ratio.css: {}
grid:
css:
component:
css/components/blazy.grid.css: {}
dependencies:
- blazy/base
column:
css:
component:
css/components/blazy.column.css: {}
dependencies:
- blazy/base
dblazy:
js:
js/dblazy.js: { weight: -4 }
dependencies:
- blazy/base
# @todo adjust weights to use hundredth decimals.
bio:
js:
js/bio.js: { weight: -3 }
dependencies:
- blazy/dblazy
bio.media:
js:
js/bio.media.js: { weight: -2 }
dependencies:
- blazy/bio
load:
js:
js/blazy.load.js: { weight: -1.5 }
dependencies:
- core/drupal.debounce
- blazy/blazy
- blazy/dblazy
- blazy/loading
bio.ajax:
js:
js/bio.ajax.js: {}
dependencies:
- core/drupal.ajax
- blazy/load
media:
js:
js/blazy.media.js: {}
css:
component:
css/components/blazy.media.css: {}
dependencies:
- core/drupal
- blazy/ratio
blazybox:
js:
js/blazy.blazybox.js: {}
css:
component:
css/components/blazy.blazybox.css: {}
dependencies:
- core/drupal
- blazy/dblazy
- blazy/lightbox
lightbox:
css:
component:
css/components/blazy.lightbox.css: {}
colorbox.skin:
css:
component:
css/components/blazy.colorbox.css: {}
dependencies:
- colorbox/colorbox
colorbox:
js:
js/blazy.colorbox.js: {}
dependencies:
- core/drupal
- blazy/load
- blazy/ratio
- blazy/colorbox.skin
photobox:
remote: https://github.com/yairEO/photobox/
version: 1.x
license:
name: MIT
url: https://github.com/yairEO/photobox/issues/17
gpl-compatible: true
js:
/libraries/photobox/photobox/jquery.photobox.js: {}
js/blazy.photobox.js: {}
css:
base:
/libraries/photobox/photobox/photobox.css: {}
dependencies:
- core/jquery
- core/jquery.once
- core/drupal
- blazy/load
# use photobox when available instead.
# - photobox/photobox
admin:
js:
js/blazy.admin.js: {}
css:
theme:
css/blazy.admin.css: {}
dependencies:
- core/jquery
- core/jquery.once
- core/drupal
<?php
/**
* @file
* Provides basic Blazy integration for lazy loading and multi-serving images.
*/
use Drupal\Core\Field\FormatterInterface;
use Drupal\editor\Entity\Editor;
use Drupal\blazy\Blazy;
use Drupal\blazy\BlazyAlter;
use Drupal\blazy\BlazyDefault;
use Drupal\blazy\BlazyViews;
use Drupal\blazy\Utility\BlazyMarkdown;
/**
* Provides a convenient shortcut for procedural hooks.
*
* @return class
* The Blazy manager class instance.
*/
function blazy() {
static $manager;
if (!isset($manager)) {
$manager = \Drupal::service('blazy.manager');
}
return $manager;
}
/**
* Implements hook_theme().
*/
function blazy_theme() {
return ['blazy' => ['render element' => 'element']];
}
/**
* Prepares variables for blazy.html.twig templates.
*/
function template_preprocess_blazy(&$variables) {
Blazy::preprocessBlazy($variables);
}
/**
* Overrides variables for responsive-image.html.twig templates.
*/
function blazy_preprocess_responsive_image(&$variables) {
if (isset($variables['attributes']['data-b-lazy'])) {
Blazy::preprocessResponsiveImage($variables);
}
}
/**
* Overrides variables for file-video.html.twig templates.
*/
function blazy_preprocess_file_video(&$variables) {
if (isset($variables['attributes']['data-b-lazy'])) {
Blazy::preprocessFileVideo($variables);
}
}
/**
* Overrides template_preprocess_media_oembed_iframe().
*/
function blazy_preprocess_media_oembed_iframe(array &$variables) {
\Drupal::service('blazy.oembed')->preprocessMediaOembedIframe($variables);
}
/**
* Implements hook_preprocess_field().
*/
function blazy_preprocess_field(array &$variables) {
if (isset($variables['element']['#blazy']) || !empty($variables['element']['#third_party_settings']['blazy']['blazy'])) {
Blazy::preprocessField($variables);
}
}
/**
* Implements hook_preprocess_views_view().
*/
function blazy_preprocess_views_view(array &$variables) {
if (!empty($variables['css_class']) && strpos($variables['css_class'], 'blazy--') !== FALSE) {
if ($lightboxes = blazy()->getLightboxes()) {
BlazyViews::preprocessViewsView($variables, $lightboxes);
}
}
}
/**
* Implements hook_views_pre_render().
*/
function blazy_views_pre_render($view) {
if (isset($view)) {
BlazyViews::viewsPreRender($view);
}
}
/**
* Implements hook_config_schema_info_alter().
*/
function blazy_config_schema_info_alter(array &$definitions) {
BlazyAlter::configSchemaInfoAlter($definitions, 'blazy_base');
}
/**
* Implements hook_library_info_alter().
*/
function blazy_library_info_alter(&$libraries, $extension) {
if ($extension === 'blazy' || $extension === 'media') {
BlazyAlter::libraryInfoAlter($libraries, $extension);
}
}
/**
* Implements hook_blazy_settings_alter().
*/
function blazy_blazy_settings_alter(array &$build, $items) {
BlazyAlter::blazySettingsAlter($build, $items);
}
/**
* Implements hook_field_formatter_third_party_settings_form().
*/
function blazy_field_formatter_third_party_settings_form(FormatterInterface $plugin) {
return BlazyAlter::fieldFormatterThirdPartySettingsForm($plugin);
}
/**
* Implements hook_field_formatter_settings_summary_alter().
*/
function blazy_field_formatter_settings_summary_alter(&$summary, $context) {
BlazyAlter::fieldFormatterSettingsSummaryAlter($summary, $context);
}
/**
* Implements hook_ckeditor_css_alter().
*/
function blazy_ckeditor_css_alter(array &$css, Editor $editor) {
BlazyAlter::ckeditorCssAlter($css, $editor);
}
/**
* Alters blazy settings here due to static FormatterBase::defaultSettings().
*/
function blazy_alterable_settings(array &$settings) {
return BlazyDefault::getInstance()->alterableSettings($settings);
}
/**
* Provides a shortcut to parse the markdown string for better hook_help().
*/
function blazy_parse_markdown($string) {
return BlazyMarkdown::parse($string);
}
/**
* Provides a wrapper to replace deprecated libraries_get_path() at ease.
*/
function blazy_libraries_get_path($name, $base_path = FALSE) {
$function = 'libraries_get_path';
if (\Drupal::hasService('library.libraries_directory_file_finder')) {
return \Drupal::service('library.libraries_directory_file_finder')->find($name);
}
elseif (is_callable($function)) {
return $function($name, $base_path);
}
return FALSE;
}
/**
* Implements hook_field_formatter_info_alter().
*
* @todo remove VEF/VEM from blazy:8.x-3.0 for core Media integration.
*/
function blazy_field_formatter_info_alter(array &$info) {
if (blazy()->getModuleHandler()->moduleExists('video_embed_media')) {
Blazy::fieldFormatterInfoAlter($info);
}
}
services:
blazy.manager.base:
abstract: true
class: Drupal\blazy\BlazyManagerBase
arguments: ['@entity.repository', '@entity_type.manager', '@module_handler', '@renderer', '@config.factory', '@cache.default']
calls:
# @todo remove and use DI instead after sub-module updates, and put as the
# last argument only if to support careless users ignoring hook_update().
- [setRoot, ['@app.root']]
blazy.manager:
abstract: false
class: Drupal\blazy\BlazyManager
parent: blazy.manager.base
blazy.formatter:
class: Drupal\blazy\BlazyFormatter
parent: blazy.manager
blazy.oembed:
class: Drupal\blazy\BlazyOEmbed
arguments: ['@request_stack', '@media.oembed.resource_fetcher', '@media.oembed.url_resolver', '@media.oembed.iframe_url_helper', '@image.factory', '@blazy.manager']
blazy.entity:
class: Drupal\blazy\BlazyEntity
arguments: ['@blazy.oembed']
blazy.admin.base:
abstract: true
class: Drupal\blazy\Form\BlazyAdminBase
arguments: ['@entity_display.repository', '@config.typed', '@date.formatter', '@blazy.manager']
blazy.admin.formatter:
class: Drupal\blazy\Form\BlazyAdminFormatter
parent: blazy.admin.base
blazy.admin.extended:
class: Drupal\blazy\Dejavu\BlazyAdminExtended
parent: blazy.admin.base
blazy.admin:
class: Drupal\blazy\Form\BlazyAdmin
parent: blazy.admin.base
# @todo deprecate this if you can't make it non-static at blazy: 8.3+.
blazy.media:
class: Drupal\blazy\BlazyMedia
# @todo arguments: ['@blazy.entity']
# @todo deprecated and is removed for blazy.formatter at blazy: 8.3+.
blazy.formatter.manager:
class: Drupal\blazy\BlazyFormatterManager
parent: blazy.formatter
# @todo deprecated and is removed from blazy: 8.2.1+.
blazy:
class: Drupal\blazy\Blazy
<?php
/**
* @file
* Provides views data for blazy.module.
*/
/**
* Implements hook_views_data_alter().
*/
function blazy_views_data_alter(&$data) {
// @todo let's keep it for a while as this can be useful for EB.
$data['file_managed']['blazy_file'] = [
'title' => t('Blazy'),
'help' => t('Displays a preview of a File using Blazy, if applicable.'),
'field' => [
'id' => 'blazy_file',
'click sortable' => FALSE,
],
];
// @todo recheck to use core Media post VEF/ VEM removal.
$data['media_field_data']['blazy_media'] = [
'title' => 'Blazy',
'help' => t('Displays a preview of a Media using Blazy, if applicable.'),
'field' => [
'id' => 'blazy_media',
'click sortable' => FALSE,
],
];
}
/**
* Implements hook_views_plugins_style_alter().
*/
function blazy_views_plugins_style_alter(array &$plugins) {
$plugins['blazy'] = [
'id' => 'blazy',
'label' => 'Blazy Grid',
'description' => t('Display the results in a Blazy grid.'),
'class' => 'Drupal\blazy\Plugin\views\style\BlazyViews',
'display_types' => ['normal'],
'help' => t('Works best with Views field containing Blazy.'),
'parent' => 'parent',
'plugin_type' => 'style',
'register_theme' => FALSE,
'short_title' => 'Blazy',
'title' => 'Blazy Grid',
'provider' => 'blazy',
];
}
name: Blazy UI
type: module
description: 'Provides Blazy configuration UI.'
core_version_requirement: ^8.8 || ^9
package: Blazy
configure: blazy.settings
dependencies:
- blazy:blazy
# Information added by Drupal.org packaging script on 2020-05-10
version: '8.x-2.1'
project: 'blazy'
datestamp: 1589112984
blazy.admin.vtabs:
version: VERSION
css:
theme:
css/blazy.admin--vertical-tabs.css: {}
dependencies:
- core/drupal.vertical-tabs
- blazy/blazy.admin
blazy.settings:
title: Blazy UI
route_name: blazy.settings
description: 'Manage Blazy UI.'
parent: system.admin_config_media
<?php
/**
* @file
* Provides Blazy UI.
*/
/**
* Implements hook_help().
*/
function blazy_ui_help($route_name) {
if ($route_name == 'help.page.blazy_ui') {
$output = file_get_contents(dirname(dirname(__FILE__)) . '/docs/README.md');
$output .= file_get_contents(dirname(dirname(__FILE__)) . '/docs/UPDATING.md');
$output .= file_get_contents(dirname(dirname(__FILE__)) . '/docs/TROUBLESHOOTING.md');
$output .= file_get_contents(dirname(dirname(__FILE__)) . '/docs/ROADMAP.md');
$output .= file_get_contents(dirname(dirname(__FILE__)) . '/docs/FAQ.md');
$output .= file_get_contents(dirname(dirname(__FILE__)) . '/docs/CONTRIBUTION.md');
$output .= file_get_contents(dirname(dirname(__FILE__)) . '/docs/ASPECT_RATIO.md');
$output .= file_get_contents(dirname(dirname(__FILE__)) . '/docs/ISSUE_TEMPLATE.md');
return blazy_parse_markdown($output);
}
return '';
}
administer blazy:
title: 'Administer the Blazy module'
blazy.settings:
path: '/admin/config/media/blazy'
defaults:
_form: '\Drupal\blazy_ui\Form\BlazySettingsForm'
_title: 'Blazy UI'
requirements:
_permission: 'administer blazy'
/**
* @file
* Vertical tabs. Lets face it, VT over-specified selector is for a reason:
* not easily get broken. And that is not a breeze to override.
* And we don't want to CSS alter for a partial use case.
*/
.form--slick .form-type-vertical-tabs {
margin-bottom: 30px;
padding: 0;
width: 100%;
}
.form--slick .vertical-tabs__menu {
float: left;
margin: 0;
width: 100%;
}
.form--slick .vertical-tabs__panes {
margin: 0;
}
.form--slick .vertical-tabs__menu-item {
border: 0;
text-align: right;
}
.form--slick .vertical-tabs__menu-item a {
background: #f9f9f9;
}
.form--slick .vertical-tabs__menu-item.is-selected {
border-right: 0;
}
.form--slick .vertical-tabs__menu-item.is-selected a {
background: #fff;
}
.form--slick .vertical-tabs details {
border: 0;
}
.form--slick .vertical-tabs .vertical-tabs__panes .vertical-tabs__pane {
padding: 0;
}
.form--slick .vertical-tabs__panes {
background: #f3f3f3;
background: rgba(255, 255, 255, 0.9);
border: 1px solid #dadada;
border-radius: 1px;
padding: 0;
min-height: 240px;
}
.form--slick .vertical-tabs__panes > details,
.form--slick .vertical-tabs__panes > details:first-child {
border: 0;
margin: 0;
}
.form--slick .vertical-tabs .vertical-tabs__panes > details:first-child {
padding-top: 0;
}
@media screen and (min-width: 1280px) {
.form--slick .vertical-tabs__menu {
float: left;
margin: 0 -100% -1px 0;
width: 200px;
}
.form--slick .vertical-tabs__panes {
margin: 0 0 0 200px;
padding: 0;
}
}
{
"name": "drupal/blazy",
"description": "Provides basic bLazy integration for lazy loading and multi-serving images.",
"keywords": ["bLazy", "lazyload", "Drupal"],
"type": "drupal-module",
"license": "GPL-2.0-or-later",
"homepage": "https://drupal.org/project/blazy",
"authors": [
{
"name": "Gaus Surahman",
"homepage": "https://www.drupal.org/u/gausarts",
"role": "Maintainer"
},
{
"name": "Contributors",
"homepage": "https://www.drupal.org/node/2663268/committers",
"role": "Contributor"
}
],
"support": {
"issues": "https://drupal.org/project/issues/blazy",
"source": "https://git.drupalcode.org/project/blazy"
},
"require": { }
}
admin_css: true
fx: ''
noscript: false
responsive_image: false
one_pixel: true
placeholder: ''
extras: {}
blazy:
loadInvisible: false
offset: 100
saveViewportOffsetDelay: 50
validateDelay: 25
container: ''
io:
enabled: false
unblazy: false
rootMargin: 0px
threshold: '0'
disconnect: false
blazy.settings:
type: config_object
label: 'Blazy settings'
mapping:
admin_css:
type: boolean
label: 'Admin CSS'
fx:
type: string
label: 'Image effect'
noscript:
type: boolean
label: 'Add noscript'
one_pixel:
type: boolean
label: 'One pixel placeholder'
placeholder:
type: string
label: Placeholder
responsive_image:
type: boolean
label: 'Support Responsive image'
blazy:
type: mapping
label: Blazy
mapping:
loadInvisible:
type: boolean
label: 'Load invisible'
offset:
type: integer
label: Offset
saveViewportOffsetDelay:
type: integer
label: 'Save viewport offset delay'
validateDelay:
type: integer
label: 'Set validate delay'
container:
type: string
label: 'Scrolling container'
io:
type: mapping
label: IO
mapping:
enabled:
type: boolean
label: 'Enable IO'
unblazy:
type: boolean
label: 'Unload bLazy'
rootMargin:
type: string
label: rootMargin
threshold:
type: string
label: threshold
disconnect:
type: boolean
label: Disconnect
extras:
type: blazy_base
label: Extras
blazy_base:
type: mapping
label: 'Blazy image base display format settings'
mapping:
caption:
type: sequence
label: Captions
sequence:
- type: string
label: Caption
optionset:
type: string
label: Optionset
field.formatter.settings.blazy:
type: blazy_base
label: 'Blazy image display format settings'
field.formatter.settings.blazy_file:
type: blazy_base
label: 'Blazy image with ME display format settings'
field.formatter.settings.blazy_video:
type: blazy_base
label: 'Blazy image with VEF display format settings'
field.formatter.settings.blazy_media:
type: blazy_base
label: 'Blazy media display format settings'
field.formatter.settings.blazy_oembed:
type: blazy_base
label: 'Blazy oembed display format settings'
field.formatter.settings.blazy_text:
type: blazy_base
label: 'Blazy text display format settings'
field.formatter.third_party.blazy:
type: mapping
label: 'Blazy third party display format settings'
mapping:
blazy:
type: boolean
label: Blazy
filter_settings.blazy_filter:
type: filter
label: 'Blazy filter'
mapping:
filter_tags:
type: sequence
label: 'HTML tags'
sequence:
- type: string
label: tag
media_switch:
type: string
label: 'Media switcher'
use_data_uri:
type: string
label: 'Trust data URI'
blazy_views_field:
type: views_field
label: Blazy
mapping:
box_style:
type: string
label: 'Lightbox image style'
box_media_style:
type: string
label: 'Lightbox video style'
image_style:
type: string
label: 'Image style'
media_switch:
type: string
label: 'Media switch'
ratio:
type: string
label: 'Aspect ratio'
thumbnail_style:
type: string
label: 'Thumbnail style'
view_mode:
type: string
label: 'View mode'
views.field.blazy_file:
type: blazy_views_field
views.field.blazy_media:
type: blazy_views_field
views.style.blazy:
type: blazy_base
label: 'Blazy Grid'
/**
* @file
*/
.blazy,
.blazy *,
.blazy *::before,
.blazy *::after {
box-sizing: border-box;
}
/* The lazyloaded element: IMG, IFRAME, DIV. */
.b-lazy,
.b-responsive {
display: block;
height: auto;
min-height: 1px;
opacity: 0;
transition: opacity 500ms ease-in-out;
}
/* Needed to display preloader with CSS BG image, otherwise hidden. */
.b-loaded,
.b-error,
.b-bg.media--loading {
opacity: 1;
}
.b-bg {
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
}
/* The .blazy container is not always preset such at lightboxes. */
.litebox,
.blazy iframe,
.media iframe {
border: 0 none;
display: block;
max-width: 100%;
}
/** Fix for conflict with Bootstrap CSS if not using aspect ratio. */
.blazy .media {
display: block;
position: relative;
}
/* Be sure to add width to the container accordingly, otherwise collapsed. */
.field[data-blazy] {
min-width: 50%;
}
/**
* @file
* Provides fullscreen video view.
*/
.is-blazybox--open {
overflow: hidden;
}
.blazybox,
.blazybox__close {
background: #000;
background: rgba(0, 0, 0, .9);
position: fixed;
}
.blazybox {
bottom: 0;
left: 0;
right: 0;
top: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.is-blazybox--open .blazybox {
z-index: 1001;
}
.blazybox__close {
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, .8);
border-top: 0;
color: #fff;
display: block;
cursor: pointer;
font-size: 22px;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 42px;
height: 42px;
z-index: 1002;
}
.blazybox iframe,
.blazybox .media--fullscreen {
height: 100vh;
width: 100%;
}
/**
* @file
* Provides colorbox integration.
*
* @requires coder shutup
* phpcs:ignoreFile -- Colorbox has no classes, just IDs, with inline styles.
*/
/* csslint ignore:start */
.colorbox-on--media #colorbox,
.colorbox-on--media #colorbox * {
box-sizing: border-box;
max-width: 100%;
}
.colorbox-on--media #cboxBottomCenter,
.colorbox-on--media #cboxBottomRight,
.colorbox-on--media #cboxBottomLeft,
.colorbox-on--media #cboxTopCenter,
.colorbox-on--media #cboxTopRight,
.colorbox-on--media #cboxTopLeft,
.colorbox-on--media #cboxMiddleLeft,
.colorbox-on--media #cboxMiddleRight {
display: none !important; /* csslint allow: known-properties, important */
}
.colorbox-on--media #cboxWrapper,
.colorbox-on--media #cboxContent {
background: none;
border-radius: 0;
}
.colorbox-on--media #cboxWrapper,
.colorbox-on--media #cboxContent,
.colorbox-on--media #cboxLoadedContent {
overflow: visible !important; /* csslint allow: known-properties, important */
}
#cboxLoadedContent.media--ratio {
height: 0 !important; /* csslint allow: known-properties, important */
padding-bottom: 56%;
}
#cboxLoadedContent.media--ratio .cboxIframe {
background-clip: padding-box;
border-radius: 0;
border: 8px solid #37465b;
}
/* csslint ignore:end */
/**
* @file
* Experimental: CSS3 multi-column.
*
* column-count is the maximum number of columns and column-width is the minimum
* width for those columns.
* Gets consistent with Grid Foundation even if non-BEM classes.
* Note: The reason for making column-width somewhat flexible is to achieve
* scalable designs that can fit many screen sizes. To set an exact column
* width, the column gap and the width of the multicol element (assuming
* horizontal text) must also be specified.
*/
.block-column,
.blazy.block-column,
.item-list > .block-column {
clear: both;
display: block;
list-style: none;
margin: 0 auto;
padding: 0;
width: 100%;
}
/** https://developer.mozilla.org/en-US/docs/Web/CSS/break-inside */
.block-column > .grid {
display: block;
float: none;
height: auto;
overflow: hidden;
-webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
page-break-inside: avoid; /* Deprecated Firefox, works at 49.0.2 */
-moz-column-break-inside: avoid; /* Current Firefox, but no joy */
break-inside: avoid; /* IE 10+ */
break-inside: avoid-column;
}
.block-column > .grid ,
.item-list > .block-column > .grid {
margin: 0 0 1rem;
padding: 0;
}
/** Fix for broken break-inside with extremely tall grid for FF. */
.block-column .grid__content {
display: inline-block;
margin: 0;
max-width: 100%;
position: relative;
vertical-align: top;
width: 100%;
}
/** 480px with 16px base font. */
@media only screen and (min-width: 30em) {
.small-block-column-1 {
-webkit-columns: 30em 1;
-moz-columns: 30em 1;
columns: 30em 1;
}
.small-block-column-2 {
-webkit-columns: 15em 2;
-moz-columns: 15em 2;
columns: 15em 2;
}
}
/** 641px with 16px base font. */
@media only screen and (min-width: 40.063em) {
.medium-block-column-1 {
-webkit-columns: 40.063em 1;
-moz-columns: 40.063em 1;
columns: 40.063em 1;
}
.medium-block-column-2 {
-webkit-columns: 20.0315em 2;
-moz-columns: 20.0315em 2;
columns: 20.0315em 2;
}
.medium-block-column-3 {
-webkit-columns: 3 auto;
-moz-columns: 3 auto;
columns: 3 auto;
}
.medium-block-column-4 {
-webkit-columns: 4 auto;
-moz-columns: 4 auto;
columns: 4 auto;
}
.medium-block-column-5 {
-webkit-columns: 5 auto;
-moz-columns: 5 auto;
columns: 5 auto;
}
.medium-block-column-6 {
-webkit-columns: 6 auto;
-moz-columns: 6 auto;
columns: 6 auto;
}
.medium-block-column-7 {
-webkit-columns: 7 auto;
-moz-columns: 7 auto;
columns: 7 auto;
}
.medium-block-column-8 {
-webkit-columns: 8 auto;
-moz-columns: 8 auto;
columns: 8 auto;
}
.medium-block-column-9 {
-webkit-columns: 9 auto;
-moz-columns: 9 auto;
columns: 9 auto;
}
.medium-block-column-10 {
-webkit-columns: 10 auto;
-moz-columns: 10 auto;
columns: 10 auto;
}
.medium-block-column-11 {
-webkit-columns: 11 auto;
-moz-columns: 11 auto;
columns: 11 auto;
}
.medium-block-column-12 {
-webkit-columns: 12 auto;
-moz-columns: 12 auto;
columns: 12 auto;
}
}
/** 1025px with 16px base font. */
@media only screen and (min-width: 64.063em) {
.large-block-column-1 {
-webkit-columns: 64.063em 1;
-moz-columns: 64.063em 1;
columns: 64.063em 1;
}
.large-block-column-2 {
-webkit-columns: 2 auto;
-moz-columns: 2 auto;
columns: 2 auto;
}
.large-block-column-3.block-count-2,
.large-block-column-4.block-count-2 {
-webkit-columns: 32.0315em 2;
-moz-columns: 32.0315em 2;
columns: 32.0315em 2;
}
.large-block-column-3,
.large-block-column-4.block-count-3 {
-webkit-columns: 3 auto;
-moz-columns: 3 auto;
columns: 3 auto;
}
.large-block-column-4 {
-webkit-columns: 4 auto;
-moz-columns: 4 auto;
columns: 4 auto;
}
.large-block-column-5 {
-webkit-columns: 5 auto;
-moz-columns: 5 auto;
columns: 5 auto;
}
.large-block-column-6 {
-webkit-columns: 6 auto;
-moz-columns: 6 auto;
columns: 6 auto;
}
.large-block-column-7 {
-webkit-columns: 7 auto;
-moz-columns: 7 auto;
columns: 7 auto;
}
.large-block-column-8 {
-webkit-columns: 8 auto;
-moz-columns: 8 auto;
columns: 8 auto;
}
.large-block-column-9 {
-webkit-columns: 9 auto;
-moz-columns: 9 auto;
columns: 9 auto;
}
.large-block-column-10 {
-webkit-columns: 10 auto;
-moz-columns: 10 auto;
columns: 10 auto;
}
.large-block-column-11 {
-webkit-columns: 11 auto;
-moz-columns: 11 auto;
columns: 11 auto;
}
.large-block-column-12 {
-webkit-columns: 12 auto;
-moz-columns: 12 auto;
columns: 12 auto;
}
}
/**
* @file
* Provides integration with core filter module.
*
* This file contains fixes when Blazy put inside core Align/ Caption images
* filter which may not suit your design needs, adjust it.
*/
.media-wrapper--blazy,
.media-wrapper--blazy * {
box-sizing: border-box;
}
/** Without this, media--ratio will be collapsed. Adjust accordingly. */
.media-wrapper--blazy {
max-width: 100%;
min-width: 50%;
position: relative;
}
.grid .media-wrapper--blazy {
width: 100%;
}
/** Blazy is placed after filter Align/ Caption will be collapsed. Adjust it. */
.caption .media-wrapper--blazy,
.align-left .media-wrapper--blazy,
.align-right .media-wrapper--blazy,
.align-center .media-wrapper--blazy {
min-width: 320px;
}
.blazy--filter .media-wrapper--blazy {
min-width: 0;
overflow: hidden;
}
.media-wrapper--blazy.align-center img {
display: block;
margin-right: auto;
margin-left: auto;
}
/**
* @file
* Blur effect.
*
* While blurring animation impresses smoother transition, it is likely slow.
* You can override this file, and change animation to just transition instead.
*/
@-webkit-keyframes blazyBlur {
0% {
filter: blur(6px);
-webkit-transform: scale(1.04);
transform: scale(1.04);
opacity: 0;
}
100% {
filter: blur(0px);
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}
@keyframes blazyBlur {
0% {
filter: blur(6px);
transform: scale(1.04);
opacity: 0;
}
100% {
filter: blur(0px);
transform: scale(1);
opacity: 1;
}
}
/**
Remove overflow as needed if CSS background is enabled, or on your own.
Basically the parent container should have overflow: hidden; to avoid unwanted
scaling. Adjust this to the closest ancestor if not already: .grid, .slide, etc.
.blazy {
overflow: hidden;
position: relative;
}
*/
/* Non-js element. Supports both BG, or inline media. */
/* Prevents collapsing thumbnail image if Aspect ratio is disabled. */
[data-animation="blur"] img {
width: 100%;
}
/* Remove not-so-useful loading indicator for BG only.
.b-bg.media--loading[data-animation="blur"]::before {
display: none;
} */
/* Js dynamic classes during animation to match animate.css convention. */
.b-bg.animated.blur,
.animated.blur img {
/* opacity: 1; */
/* transition: opacity 500ms ease-in-out; */
transition: none;
/** will-change: transform, opacity; */
-webkit-animation: blazyBlur 2s;
animation: blazyBlur 2s;
}
/**
* @file
* Block grid based on Foundation 5.
*
* This is a modified version to be re-usable for divities, not only UL/OL list,
* as long as the list item has class "grid".
*/
.block-grid,
.blazy.block-grid,
.item-list > .block-grid {
clear: both;
display: block;
list-style: none;
padding: 0;
margin: 0 -0.625rem;
}
.block-grid::before,
.block-grid::after {
content: " ";
display: table;
}
.block-grid::after {
clear: both;
}
.block-grid > .grid {
display: block;
float: left;
height: auto;
list-style: none;
margin: 0;
padding: 0 0.625rem 1.25rem;
}
.block-grid.block-grid--centered,
.item-list > .block-grid.block-grid--centered {
text-align: center;
}
.block-grid.block-grid--centered > .grid,
.item-list > .block-grid.block-grid--centered > .grid {
text-align: left;
display: inline-block;
float: none;
vertical-align: top;
}
.block-grid .grid__content {
position: relative;
}
@media only screen {
.small-block-grid-1 > .grid {
width: 100%;
}
.small-block-grid-2 > .grid {
width: 50%;
}
.small-block-grid-3 > .grid {
width: 33.33333%;
}
.small-block-grid-4 > .grid {
width: 25%;
}
.small-block-grid-5 > .grid {
width: 20%;
}
.small-block-grid-6 > .grid {
width: 16.66667%;
}
.small-block-grid-7 > .grid {
width: 14.28571%;
}
.small-block-grid-8 > .grid {
width: 12.5%;
}
.small-block-grid-9 > .grid {
width: 11.11111%;
}
.small-block-grid-10 > .grid {
width: 10%;
}
.small-block-grid-11 > .grid {
width: 9.09091%;
}
.small-block-grid-12 > .grid {
width: 8.33333%;
}
.small-block-grid-1 > .grid:nth-of-type(1n),
.small-block-grid-2 > .grid:nth-of-type(1n),
.small-block-grid-3 > .grid:nth-of-type(1n),
.small-block-grid-4 > .grid:nth-of-type(1n),
.small-block-grid-5 > .grid:nth-of-type(1n),
.small-block-grid-6 > .grid:nth-of-type(1n),
.small-block-grid-8 > .grid:nth-of-type(1n),
.small-block-grid-7 > .grid:nth-of-type(1n),
.small-block-grid-9 > .grid:nth-of-type(1n),
.small-block-grid-10 > .grid:nth-of-type(1n),
.small-block-grid-11 > .grid:nth-of-type(1n),
.small-block-grid-12 > .grid:nth-of-type(1n) {
clear: none;
}
.small-block-grid-1 > .grid:nth-of-type(1n+1),
.small-block-grid-2 > .grid:nth-of-type(2n+1),
.small-block-grid-3 > .grid:nth-of-type(3n+1),
.small-block-grid-4 > .grid:nth-of-type(4n+1),
.small-block-grid-5 > .grid:nth-of-type(5n+1),
.small-block-grid-6 > .grid:nth-of-type(6n+1),
.small-block-grid-7 > .grid:nth-of-type(7n+1),
.small-block-grid-8 > .grid:nth-of-type(8n+1),
.small-block-grid-9 > .grid:nth-of-type(9n+1),
.small-block-grid-10 > .grid:nth-of-type(10n+1),
.small-block-grid-11 > .grid:nth-of-type(11n+1),
.small-block-grid-12 > .grid:nth-of-type(12n+1) {
clear: both;
}
}
/** 641px with 16px base font. */
@media only screen and (min-width: 40.063em) {
.medium-block-grid-1 > .grid {
width: 100%;
}
.medium-block-grid-2 > .grid {
width: 50%;
}
.medium-block-grid-3 > .grid {
width: 33.33333%;
}
.medium-block-grid-4 > .grid {
width: 25%;
}
.medium-block-grid-5 > .grid {
width: 20%;
}
.medium-block-grid-6 > .grid {
width: 16.66667%;
}
.medium-block-grid-7 > .grid {
width: 14.28571%;
}
.medium-block-grid-8 > .grid {
width: 12.5%;
}
.medium-block-grid-9 > .grid {
width: 11.11111%;
}
.medium-block-grid-10 > .grid {
width: 10%;
}
.medium-block-grid-11 > .grid {
width: 9.09091%;
}
.medium-block-grid-12 > .grid {
width: 8.33333%;
}
.medium-block-grid-1 > .grid:nth-of-type(1n),
.medium-block-grid-2 > .grid:nth-of-type(1n),
.medium-block-grid-3 > .grid:nth-of-type(1n),
.medium-block-grid-4 > .grid:nth-of-type(1n),
.medium-block-grid-5 > .grid:nth-of-type(1n),
.medium-block-grid-6 > .grid:nth-of-type(1n),
.medium-block-grid-7 > .grid:nth-of-type(1n),
.medium-block-grid-8 > .grid:nth-of-type(1n),
.medium-block-grid-9 > .grid:nth-of-type(1n),
.medium-block-grid-10 > .grid:nth-of-type(1n),
.medium-block-grid-11 > .grid:nth-of-type(1n),
.medium-block-grid-12 > .grid:nth-of-type(1n) {
clear: none;
}
.medium-block-grid-1 > .grid:nth-of-type(1n+1),
.medium-block-grid-2 > .grid:nth-of-type(2n+1),
.medium-block-grid-3 > .grid:nth-of-type(3n+1),
.medium-block-grid-4 > .grid:nth-of-type(4n+1),
.medium-block-grid-5 > .grid:nth-of-type(5n+1),
.medium-block-grid-6 > .grid:nth-of-type(6n+1),
.medium-block-grid-7 > .grid:nth-of-type(7n+1),
.medium-block-grid-8 > .grid:nth-of-type(8n+1),
.medium-block-grid-9 > .grid:nth-of-type(9n+1),
.medium-block-grid-10 > .grid:nth-of-type(10n+1),
.medium-block-grid-11 > .grid:nth-of-type(11n+1),
.medium-block-grid-12 > .grid:nth-of-type(12n+1) {
clear: both;
}
}
/** 1025px with 16px base font. */
@media only screen and (min-width: 64.063em) {
.large-block-grid-1 > .grid {
width: 100%;
}
.large-block-grid-2 > .grid {
width: 50%;
}
.large-block-grid-3 > .grid {
width: 33.33333%;
}
.large-block-grid-4 > .grid {
width: 25%;
}
.large-block-grid-5 > .grid {
width: 20%;
}
.large-block-grid-6 > .grid {
width: 16.66667%;
}
.large-block-grid-7 > .grid {
width: 14.28571%;
}
.large-block-grid-8 > .grid {
width: 12.5%;
}
.large-block-grid-9 > .grid {
width: 11.11111%;
}
.large-block-grid-10 > .grid {
width: 10%;
}
.large-block-grid-11 > .grid {
width: 9.09091%;
}
.large-block-grid-12 > .grid {
width: 8.33333%;
}
.large-block-grid-1 > .grid:nth-of-type(1n),
.large-block-grid-2 > .grid:nth-of-type(1n),
.large-block-grid-3 > .grid:nth-of-type(1n),
.large-block-grid-4 > .grid:nth-of-type(1n),
.large-block-grid-5 > .grid:nth-of-type(1n),
.large-block-grid-6 > .grid:nth-of-type(1n),
.large-block-grid-7 > .grid:nth-of-type(1n),
.large-block-grid-8 > .grid:nth-of-type(1n),
.large-block-grid-9 > .grid:nth-of-type(1n),
.large-block-grid-10 > .grid:nth-of-type(1n),
.large-block-grid-11 > .grid:nth-of-type(1n),
.large-block-grid-12 > .grid:nth-of-type(1n) {
clear: none;
}
.large-block-grid-1 > .grid:nth-of-type(1n+1),
.large-block-grid-2 > .grid:nth-of-type(2n+1),
.large-block-grid-3 > .grid:nth-of-type(3n+1),
.large-block-grid-4 > .grid:nth-of-type(4n+1),
.large-block-grid-5 > .grid:nth-of-type(5n+1),
.large-block-grid-6 > .grid:nth-of-type(6n+1),
.large-block-grid-7 > .grid:nth-of-type(7n+1),
.large-block-grid-8 > .grid:nth-of-type(8n+1),
.large-block-grid-9 > .grid:nth-of-type(9n+1),
.large-block-grid-10 > .grid:nth-of-type(10n+1),
.large-block-grid-11 > .grid:nth-of-type(11n+1),
.large-block-grid-12 > .grid:nth-of-type(12n+1) {
clear: both;
}
}
/**
* @file
* Provides shared lightbox stylings for both Colorbox and Photobox.
*/
.litebox {
position: relative;
}
.media__icon--litebox {
cursor: pointer;
display: block;
height: 80px;
left: 50%;
margin: 0;
opacity: 0;
pointer-events: none;
position: absolute;
top: 50%;
width: 80px;
visibility: hidden;
z-index: 9;
transition: visibility 0s linear 0.5s, opacity 0.5s linear;
-ms-transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.media__icon--litebox::before,
.media__icon--litebox::after {
content: '';
display: block;
position: absolute;
pointer-events: none;
}
.media__icon--litebox {
z-index: 9;
}
.media__icon--litebox::before,
.media__icon--litebox::after {
background: white;
border-radius: 4px;
height: 2px;
left: 50%;
margin: -1px 0 0 -40px;
top: 50%;
width: 80px;
-ms-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
.media__icon--litebox::after {
-ms-transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.media__icon--litebox:hover::before,
.media__icon--litebox:hover::after {
background-color: #ff6d2c;
}
.media:hover .media__icon--litebox {
transition-delay: 0s;
}
.media:hover .media__icon--litebox,
.media--switch .media__image {
opacity: 1;
visibility: visible;
}
/**
* @file
*/
/* Credit: https://github.com/tobiasahlin/SpinKit */
@-webkit-keyframes rotateplane {
0% {
-webkit-transform: perspective(120px);
transform: perspective(120px);
}
50% {
-webkit-transform: perspective(120px) rotateY(180deg);
transform: perspective(120px) rotateY(180deg);
}
100% {
-webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg);
transform: perspective(120px) rotateY(180deg) rotateX(180deg);
}
}
@keyframes rotateplane {
0% {
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
}
50% {
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
}
100% {
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
}
}
.media--loading {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
position: relative;
min-height: 30px;
}
.media--loading::before {
content: '';
display: none;
width: 30px;
height: 30px;
max-width: 30px;
background: #2eaae0;
position: absolute;
left: 50%;
top: 50%;
margin-left: -15px;
margin-top: -15px;
font-size: 0;
z-index: 22;
-webkit-animation: rotateplane 1.2s infinite ease-in-out;
animation: rotateplane 1.2s infinite ease-in-out;
}
/**
* With JS being disabled, the NOSCRIPT tag is replaced by SPAN.
* No worries about video iframes, they are broken without JS anyway.
*/
.blazy--on .media--loading::before {
display: block;
}
/** Hide JS stuffs when being disabled. Ensures to not mess up JS users.
.media--loading.media--player > span ~ .media__icon, */
.media--loading > span + .b-lazy,
.media--loading > span + picture,
.media--loading > span + a,
.media--loading.media--player > span ~ .b-lazy {
display: none;
}
/* Must blur to avoid artifacts from showing regardless animation name.
.b-bg.media--loading[data-animation], */
.media--loading[data-animation] img {
filter: blur(6px);
}
/**
* @file
* Provides media integration with pure CSS fluid video wrapper.
*/
/** Ensures not affecting iframe only without media switcher */
.media--switch iframe {
opacity: 0;
visibility: hidden;
}
.media__icon,
.media--ratio iframe {
transition: visibility 0s linear 0.5s, opacity 0.5s linear;
}
.media__icon {
cursor: pointer;
display: block;
opacity: 0;
position: absolute;
visibility: hidden;
z-index: 8;
}
.media__icon--play,
.media__icon--close,
.media__icon--spinner {
height: 80px;
left: 50%;
margin: -40px 0 0 -40px;
top: 50%;
width: 80px;
}
.media__icon--close::before,
.media__icon--close::after,
.media__icon--play::before {
content: '';
display: block;
position: absolute;
pointer-events: none;
}
.media__icon--close::before,
.media__icon--close::after {
background: white;
border-radius: 4px;
height: 8px;
left: 50%;
margin: -4px 0 0 -40px;
top: 50%;
width: 80px;
-ms-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.media__icon--close::after {
-ms-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.media__icon--close:hover::before,
.media__icon--close:hover::after {
background-color: #ff6d2c;
}
.media__icon--play {
border: 8px solid white;
border-radius: 50%;
}
.media__icon--play::before {
border: 16px solid transparent;
border-left: 24px solid white;
left: 50%;
line-height: 60px;
margin: -16px 0 0 -6px;
top: 50%;
}
.media__icon--play:hover {
border-color: #ff6d2c;
}
.media__icon--play:hover::before {
border-left-color: #ff6d2c;
}
.is-playing:hover .media__icon {
transition-delay: 0s;
}
.is-playing .media__icon--close {
visibility: hidden;
}
.media__icon--play,
.media:hover .media__icon--litebox,
.media--switch img,
.media--switch.is-playing iframe,
.media--switch.is-playing:hover .media__icon--close {
opacity: 1;
visibility: visible;
}
.media--switch.is-playing img,
.media--switch.is-playing .media__icon--play,
.media--switch.is-playing:hover .media__icon--play {
opacity: 0;
position: absolute;
visibility: hidden;
z-index: -1;
}
.media--switch.is-playing iframe {
z-index: 3;
}
/** @requires coder shutup, front-end complication with inline styles. */
/** Prevents Twitter iframe from breaking grid, even if unholy. */
.grid .twitter-tweet-rendered {
margin: 0 !important; /* csslint allow: known-properties, important */
min-width: 1px !important; /* csslint allow: known-properties, important */
}
/** Fix for overflowing Facebook/ Twitter iframes. */
.grid .media,
.grid .fb_iframe_widget {
overflow: hidden;
}
.grid .fb_iframe_widget span {
width: 100% !important; /* csslint allow: known-properties, important */
}
.grid .fb_iframe_widget iframe {
position: relative !important; /* csslint allow: known-properties, important */
}
/**
* @file
* Makes oembed iframe responsive, leaving the rest to media/oembed.iframe.
*/
/** This class ensures Blazy does not interfere non-blazy oembed iframes. */
.is-b-oembed {
overflow: hidden;
}
.is-b-oembed iframe {
border: 0;
height: 100vh;
max-width: 100%;
overflow: hidden;
}
/**
* @file
* Provides CKEditor preview mode overrides.
*/
/** Disable interactive elements such as file video within CKEditor preview. */
.cke_widget_element .blazy {
position: relative;
}
.cke_widget_element .blazy::after {
background: transparent;
content: '';
display: block;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
z-index: 9;
}
.cke_widget_element .blazy iframe {
pointer-events: none;
z-index: -2;
}
/** Fixed for file video. */
.cke_widget_element .blazy .media__element {
object-fit: cover;
width: 100%;
}
/** Fixed for shrink media due to parent display: flex or inline-block. */
.cke_widget_element .media {
min-width: 100%;
}
@media screen and (min-width: 768px) {
.cke_widget_element .media {
min-width: 50%;
}
.cke_widget_element .media--switch--media {
min-width: 640px;
}
}
/**
* @file
* The CSS and class names below are based on Slick media for easy migration.
*/
/**
* Aspect ratio element wrapper. So can use dynamic/fluid ratio via JS which is
* not possible using ::pseudo selector approach.
*/
.media.media--ratio {
display: block;
height: 0;
min-height: 1px;
overflow: hidden;
position: relative;
width: 100%;
}
/* Trying to be nice to minimize abrupt changes. */
.media--ratio.is-b-loaded {
transition: padding-bottom .2s;
}
/* Aspect ratio element: IMG, IFRAME, DIV. */
.media--ratio .media__element {
display: block;
height: 100%;
left: 0;
min-height: 1px;
position: absolute;
top: 0;
width: 100%;
z-index: 0;
/** Temp fix, also to fix the VIDEO element to max width, not only IMG. */
object-fit: cover;
}
/* 1:1 ratio */
.media--ratio--11 {
padding-bottom: 100%;
}
/* 3:2 ratio */
.media--ratio--32 {
padding-bottom: 66.66%;
}
/* 4:3 ratio */
.media--ratio--43 {
padding-bottom: 75%;
}
/* 8:5 ratio */
.media--ratio--85 {
padding-bottom: 62.5%;
}
/* 16:9 ratio */
.media--ratio--169 {
padding-bottom: 56.25%;
}
.
***
***
.
# <a name="aspect-ratio-template"></a>ASPECT RATIO TEMPLATE
## Tools to check aspect ratio:
http://size43.com/jqueryVideoTool.html
## Common resolutions:
http://en.wikipedia.org/wiki/List_of_common_resolutions
## Aspect ratio 4:3
* 640x480
* 800x600
* 1024x768
* 1152x864
* 1280x960
* 1400x1050
* 1600x1200
* 2048x1536
* 3200x2400
* 4000x3000
* 6400x4800
## Aspect ratio 16:9
* 640x360
* 853x480
* 960x540
* 1024x576
* 1280x720
* 1366x768
* 1600x900
* 1920x1080
* 2048x1152
* 2560x1440
* 2880x1620
* 3840x2160
* 4096x2304
* 7680x4320
## Aspect ratio 16:10
* 1440x900
* 1680x1050
* 1920x1200
* 2560x1600
* 3840x2400
* 7680x4800
.
***
***
.
# <a name="contribution"></a>SUBMITTING PATCHES OR ISSUES
Please consider the following to help you explain better, and to help us
understand better your bug reports, or patches as needed:
1. [Issue summaries](https://www.drupal.org/issue-summaries)
2. [Issue template](https://www.drupal.org/node/1326662)
* If you hate formalism, consider a crystal clear line, or two in the body text.
* Avoid explaining everything in the title.
* Use body text for explanation purposes.
* If language is a barrier, use any available/ online translation tool.
## 1. SUBMITTING ISSUES
When submitting bug reports, please:
* be kind with proper reproduction, and enough details.
* mention library version, related-module version, if any, active theme, or
anything which may help us identify issue better.
* ensure the library is loaded, not 404.
* switch to stock (Responsive) Bartik for just in case it is your custom theme.
* switch to default formatters, image to Image, text to Default, etc.
* use matching or similar branches or tags for related modules.
* check out dups at project issues, your problem may be already addressed.
* consult descriptions on each form item.
* file it a support request, if unsure. We'll mark a bug a bug even if you
file it under support requests.
## 2. SUBMITTING PATCHES
We consider a patch as help, they consider it a sale, so thank you in advanced!
In order for you to help, or buy, us successfully, please consider:
* communicating and filling out the body text with proper explanations, not in
comments (unless for comment patches, of course).
I've seen patches which broke a module, so explanation is a must.
If you have no time to write it in the body text, please hold off till later!
* providing optional links to the change records, or docs, if any.
* providing links to docs is a must for coding standards issues.
This also lets us, you and me, learn from the actual docs, not told by tools!
We can just run `drupalcs ...`, but help is welcome, too, in case a miss.
* checking out the latest dev branch in case already resolved.
* providing reproduction steps for bug reports is a must. No repro, no bugs.
You must speak like human to human, and help us respect you, and your time.
Dumping patches with empty body text will be disregarded, till the above is met.
Thank you for your kind consideration, cooperation, and contribution!
.
***
***
.
# <a name="faq"></a>FAQ
## CURRENT DEVELOPMENT STATUS
A full release should be reasonable after proper feedback from the community,
some code cleanup, and optimization where needed. Patches are very much welcome.
## PROGRAMATICALLY
[blazy.api.php](https://git.drupalcode.org/project/blazy/blob/8.x-2.x/blazy.api.php)
## BLAZY VS. B-LAZY
`blazy` is the module namespace. `b-lazy` is the default CSS class to lazy load.
* The `blazy` class is applied to the **top level container**, e,g.. `.field`,
`.view`, `.item-list`, etc., those which normally contain item collection.
In this container, you can feed any `bLazy` script options into `[data-blazy]`
attribute to override existing behaviors per particular page, only if needed.
* The `b-lazy` class is applied to the **target item** to lazy load, normally
the children of `.blazy`, but not always. This can be IMG, VIDEO, DIV, etc.
## WHAT `BLAZY` CSS CLASS IS FOR?
Aside from the fact that a module must reserve its namespace including for CSS
classes, the `blazy` is actually used to limit the scope to scan document.
Rather than scanning the entire DOM, you limit your work to a particular
`.blazy` container, and these can be many, no problem. This also allows each
`.blazy` container to have unique features, such as ones with multi-breakpoint
images, others with regular images; ones with a lightbox, others with
image to iframe; ones with CSS background, others with regular images; etc.
right on the same page. This is only possible and efficient within the `.blazy`
scope.
## WHY NOT `BLAZY__LAZY` FOR `B-LAZY`?
`b-lazy` is the default CSS class reserved by JS script. Rather than recreating
a new one, respecting the defaults is better. Following BEM standard is not
crucial for most JS generated CSS classes. Uniqueness matters.
## NATIVE LAZY LOADING
Native lazy loading is supported by Chrome 76+ as of 01/2019. Blazy or IO will
be used as fallback for other browsers instead. Currently the offset/ threshold
before loading is hard-coded to [800px at Chrome](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/frame/settings.json5?l=971-1003&rcl=e8f3cf0bbe085fee0d1b468e84395aad3ebb2cad),
so it might only be good for super tall pages for now, be aware.
[Read more](https://web.dev/native-lazy-loading/)
**UPDATE 2020-04-24**: Added a delay to only lazy load once the first found is
loaded, see [#3120696](https://drupal.org/node/3120696)
## ANIMATE.CSS INTEGRATION
Blazy container (`.media`) can be animated using
[animate.css](https://github.com/daneden/animate.css). The container is chosen
to be the animated element so to support various use cases:
CSS background, picture, image, or rich media contents.
See [GridStack](https://drupal.org/project/gridstack) 2.6+ for the `animate.css`
samples at Layout Builder pages.
To replace **Blur** effect with `animate.css` thingies, implements two things:
1. **Globally**: `hook_blazy_image_effects_alter` and add `animate.css` classes
to make them available for select options at Blazy UI.
2. **Fine grained**: `hook_blazy_settings_alter`, and replace a setting named
`fx` with one of `animate.css` CSS classes, adjust conditions based settings.
### Requirements:
* The `animate.css` library included in your theme, or via `animate_css` module.
* Data attributes: `data-animation`, with optional: `data-animation-duration`,
`data-animation-delay` and `data-animation-iteration-count`, as seen below.
```
function MYTHEME_preprocess_blazy(&$variables) {
$settings = &$variables['settings'];
$attributes = &$variables['attributes'];
// Be sure to limit the scope, only animate for particular conditions.
if ($settings['entity_id'] == 123
&& $settings['field_name'] == 'field_media_animated') {
// This was taken care of by feeding $settings['fx'], or hard-coded here.
$attributes['data-animation'] = 'wobble';
// The following can be defined manually.
$attributes['data-animation-duration'] = '3s';
$attributes['data-animation-delay'] = '.3s';
// Iteration can be any number, or infinite.
$attributes['data-animation-iteration-count'] = 'infinite';
}
}
```
.
***
***
.
# <a name="issue-template"></a>ISSUE TEMPLATE
## Please describe the issue in crystal-clear sentences:
(Steps to reproduce, if it is a bug/ support report.)
## Library versions:
(Blazy, Slick, etc.)
## Module versions, including its-submodules relevant to this issue:
(Blazy, Slick, Slick Views, etc. Ignore any irrelevant accordingly.)
## Default active theme:
(Responsive/ Bartik, custom.)
## Press F12 at any browser, and post the console output, if any error:
(Ignore if you don't see no evil.)
## Delete this line and anything below when submitting issue. This is just FYI:
This is just a generic catch-all issue template to help you explain better, and
to help us understand your issue better in a shortest possible time.
Be more specific, of course. Feel free to ignore or delete any irrelevant step,
or be more innovative. A crystal-clear line or two is also very much welcome.
It doesn't dictate, it should help us be on the same page, and narrows down.
### Before you proceed creating the issue, please:
* visit **/admin/help**, and find the relevant module documentations.
* install the samples, if relevant:
[Slick example](https://www.drupal.org/project/slick_extras).
* ensure the library is loaded, not 404, by viewing it in a browser.
* cross-check against other versions of the library.
* switch to stock (Responsive) Bartik for just in case it is your custom theme.
* switch to default formatters, image to Image, text to Default, etc.
* use matching or similar branches or tags for related modules.
* check out dups at project issues, your problem may be already addressed.
* be kind with proper reproduction, enough details, screen shots of form, etc.
* specific to Slick alike, export the optionset, if relevant to this issue.
* consult descriptions on each form item.
* consult the provided TROUBLESHOOTING section on any of the modules.
* file it a support request, if unsure. We'll mark a bug a bug even if you
file it under support requests.
Hopefully resolved, before you create one.
If you see a persistent issue after reading/ addressing the above, go ahead!
# <a name="top"> </a>CONTENTS OF THIS FILE
* [Introduction](#introduction)
* [Requirements](#requirements)
* [Recommended modules](#recommended-modules)
* [Installation](#installation)
* [Configuration](#configuration)
* [Features](#features)
* [Updating](#updating)
* [Troubleshooting](#troubleshooting)
* [Roadmap](#roadmap)
* [FAQ](#faq)
* [Aspect ratio template](#aspect-ratio-template)
* [Contribution](#contribution)
* [Maintainers](#maintainers)
.
***
***
.
# <a name="introduction"></a>INTRODUCTION
Provides integration with bLazy and or Intersection Observer API, or browser
native lazy loading to lazy load and multi-serve images to save bandwidth and
server requests. The user will have faster load times and save data usage if
they don't browse the whole page.
.
***
***
.
# <a name="requirements"> </a>REQUIREMENTS
1. bLazy library:
* [Download bLazy](https://github.com/dinbror/blazy)
* Extract it as is, rename **blazy-master** to **blazy**, so the assets are:
+ **/libraries/blazy/blazy.js**
2. Media and Filter module in core.
.
***
***
.
# <a name="recommended-modules"> </a>RECOMMENDED MODULES
* [Markdown](https://www.drupal.org/project/markdown)
To make reading this README a breeze at [Blazy help](/admin/help/blazy_ui)
## MODULES THAT INTEGRATE WITH OR REQUIRE BLAZY
* [Ajaxin](https://www.drupal.org/project/ajaxin)
* [Intersection Observer](https://www.drupal.org/project/io)
* [Blazy PhotoSwipe](https://www.drupal.org/project/blazy_photoswipe)
* [GridStack](https://www.drupal.org/project/gridstack)
* [Outlayer](https://www.drupal.org/project/outlayer)
* [Intense](https://www.drupal.org/project/intense)
* [Mason](https://www.drupal.org/project/mason)
* [Slick](https://www.drupal.org/project/slick)
* [Slick Lightbox](https://www.drupal.org/project/slick_lightbox)
* [Slick Views](https://www.drupal.org/project/slick_views)
* [Slick Paragraphs](https://www.drupal.org/project/slick_paragraphs)
* [Slick Browser](https://www.drupal.org/project/slick_browser)
* [Jumper](https://www.drupal.org/project/jumper)
* [Zooming](https://www.drupal.org/project/zooming)
* [ElevateZoom Plus](https://www.drupal.org/project/elevatezoomplus)
Most duplication efforts from the above modules will be merged into
`\Drupal\blazy\Dejavu`, or anywhere else namespace.
**What dups?**
The most obvious is the removal of formatters from Intense, Zooming,
Slick Lightbox, Blazy PhotoSwipe, and other (quasi-)lightboxes. Any lightbox
supported by Blazy can use Blazy, or Slick formatters if applicable instead.
We do not have separate formatters when its prime functionality is embedding
a lightbox, or superceded by Blazy.
Blazy provides a versatile and reusable formatter for a few known lightboxes
with extra advantages:
lazyloading, grid, multi-serving images, Responsive image,
CSS background, captioning, etc.
Including making those lightboxes available for free at Views Field for
File entity, Media and Blazy Filter for inline images.
If you are developing lightboxes and using Blazy, I would humbly invite you
to give Blazy a try, and consider joining forces with Blazy, and help improve it
for the above-mentioned advantages. We are also continuously improving and
solidifying the API to make advanced usages a lot easier, and DX friendly.
Currently, of course, not perfect, but have been proven to play nice with at
least 7 lightboxes, and likely more.
## SIMILAR MODULES
[Lazyloader](https://www.drupal.org/project/lazyloader)
.
***
***
.
# <a name="installation"> </a>INSTALLATION
1. **MANUAL:**
Install the module as usual, more info can be found on:
[Installing Drupal 8 Modules](https://drupal.org/node/1897420)
2. **COMPOSER:**
There are various ways to install third party bower/npm asset libraries.
Check out any below suitable to your workflow:
+ [#3021902](https://www.drupal.org/project/blazy/issues/3021902)
+ [#2907371](https://www.drupal.org/project/slick/issues/2907371)
+ [#2907371](https://www.drupal.org/project/slick/issues/2907371#comment-12882235)
It is up to you to decide which works best. Composer is not designed to
manage JS, CSS or HTML framework assets. It is for PHP. Then come Composer
plugins, and other workarounds to make Composer workflow easier. As many
alternatives, it is not covered here. Please find more info on the
above-mentioned issues.
.
***
***
.
# <a name="configuration"> </a>CONFIGURATION
Visit the following to configure and make use of Blazy:
1. `/admin/config/media/blazy`
Enable Blazy UI sub-module first, otherwise regular **Access denied**.
Contains few global options, including enabling support to bring core
Responsive image into blazy-related formatters.
Blazy UI can be uninstalled at production later without problems.
2. Visit any entity types:
+ `/admin/structure/types`
+ `/admin/structure/block/block-content/types`
+ `/admin/structure/paragraphs_type`
+ etc.
Use Blazy as a formatter under **Manage display** for the supported fields:
Image, Media, Entity reference, or even Text.
3. `/admin/structure/views`
Use Blazy Grid as standalone blocks, or pages.
### USAGES: BLAZY FOR MULTIMEDIA GALLERY VIA VIEWS UI
#### Using **Blazy Grid**
1. Add a Views style **Blazy Grid** for entities containing Media or Image.
2. Add a Blazy formatter for the Media or Image field.
3. Add any lightbox under **Media switcher** option.
4. Limit the values to 1 under **Multiple field settings** > **Display**.
#### Without **Blazy Grid**
If you can't use **Blazy Grid** for a reason, maybe having a table, HTML list,
etc., try the following:
1. Add a CSS class under **Advanced > CSS class** for any reasonable supported/
supportive lightbox in the format **blazy--LIGHTBOX-gallery**, e.g.:
+ **blazy--colorbox-gallery**
+ **blazy--intense-gallery**
+ **blazy--photobox-gallery**
+ **blazy--photoswipe-gallery**
+ **blazy--slick-lightbox-gallery**
+ **blazy--zooming-gallery**
Note the double dashes BEM modifier "**--**", just to make sure we are on the
same page that you are intentionally creating a blazy LIGHTBOX gallery.
The View container will then have the following attributes:
`class="blazy blazy--LIGHTBOX-gallery ..." data-blazy data-LIGHTBOX-gallery`
2. Add a Blazy formatter for the Media or Image field.
3. Add the relevant lightbox under **Media switcher** option based on the given
CSS class at #1.
**Important!**
Be sure to leave **Use field template** under **Style settings** unchecked.
If checked, the gallery is locked to a single entity, that is no Views gallery,
but gallery per field. The same applies when using Blazy formatter with VIS
pager, alike, or inside Slick Carousel, GridStack, etc. If confusing, just
toggle this option, and you'll know which works. Only checked if Blazy formatter
is a standalone output from Views so to use field template in this case.
Check out the relevant sub-module docs for details.
.
***
***
.
# <a name="features"> </a>FEATURES
* Supports core Image.
* Supports core Responsive image.
* Supports Colorbox/ Photobox/ PhotoSwipe, also multimedia lightboxes.
* Multi-serving lazyloaded images, including multi-breakpoint CSS backgrounds.
* Lazyload video iframe urls via custom coded, or core Media.
* Supports inline images and iframes with lightboxes, and grid or CSS3 Masonry
via Blazy Filter. Enable Blazy Filter at **/admin/config/content/formats**,
and check out instructions at **/filter/tips**.
* Field formatters: Blazy with Media integration.
* Blazy Grid formatter for Image, Media and Text with multi-value.
* Delay loading for below-fold images until 100px (configurable) before they are
visible at viewport.
* A simple effortless CSS loading indicator.
* It doesn't take over all images, so it can be enabled as needed via Blazy
formatter, or its supporting modules.
## OPTIONAL FEATURES
* Views fields for File Entity and Media integration, see
[Slick Browser](https://www.drupal.org/project/slick_browser).
* Views style plugin `Blazy Grid` for Grid Foundation or pure CSS3 Masonry.
.
***
***
.
# <a name="maintainers"> </a>MAINTAINERS/CREDITS
* [Gaus Surahman](https://www.drupal.org/user/159062)
* [geek-merlin](https://www.drupal.org/u/geek-merlin)
* [Contributors](https://www.drupal.org/node/2663268/committers)
* CHANGELOG.txt for helpful souls with their patches, suggestions and reports.
## READ MORE
See the project page on drupal.org:
* [Blazy module](https://www.drupal.org/project/blazy)
See the bLazy docs at:
* [Blazy library](https://github.com/dinbror/blazy)
* [Blazy website](http://dinbror.dk/blazy/)
.
***
***
.
# <a name="roadmap"></a>ROADMAP/ TODO
* [x] Adds a basic configuration to load the library, probably an image
formatter.
2/24/2016
* [x] Media entity image/video, and Video embed field lazyloading, if any.
10/25/2016
Added both simple Blazy Media formatter and Views field Media Entity.
* [x] Makes a solid lazyloading solution for IMG, DIV, IFRAME tags.
4/9/2017
Added IFRAME (Blazy Video), apart from existing IMG/ DIV (CSS background).
* [x] Core Media integration.
01/03/2019
* [x] Core Responsive Image integration.
05/02/2020
Added multi-breakpoint CSS background and Aspect ratio Fluid supports.
* [?] Optimization and solidification.
Why is it the last plan? It should make sense, if you have heard **premature
optimization** in software developments.
.
***
***
.
# <a name="troubleshooting"></a>TROUBLESHOOTING
* Blazy and its sub-modules -- Slick, GridStack, etc. are tightly coupled.
Be sure to have the latest release date or matching versions in the least.
DEV for DEV, Beta for Beta, etc. Mismatched versions may lead to errors
especially before having RCs. Mismatched branches will surely be errors.
* Resizing is not supported. Just reload the page. **The main reason**:
When being resized, the browser gave no data about pixel ratio from desktop
to mobile, not vice versa. Unless delayed for 4s+, not less, which is of
course unacceptable.
* Images are gone, only eternal blue loader is flipping like a drunk butterfly.
Solution: ensures that blazy library is loaded. And temporarily switch to
stock Bartik themes.
* Press F12 at any browser, and see the errors at the browser console. Any JS
error will prevent Blazy from working identified by eternal blue loaders.
* Images are collapsed. Solution: choose one of the Aspect ratio.
* Images or videos aren't responsive. Solution: choose one of the Aspect ratio.
* Images are distorted. Solution: choose the correct Aspect ratio. If unsure,
choose "fluid" to let the module calculate aspect ratio automatically.
[Check out few aspect ratio samples](https://cgit.drupalcode.org/blazy/tree/docs/ASPECT_RATIO.md)
## 1. VIEWS INTEGRATION
Blazy provides a simple Views field for File Entity, and Media. Also a Blazy
Grid views style plugin.
When using Blazy formatter within Views, check **Use field template** under
**Style settings**, if trouble with Blazy Formatter as a stand alone Views
output.
On the contrary, uncheck **Use field template**, when Blazy formatter
is embedded inside another module such as Slick so to pass the renderable
array to work with accordingly.
This is a Views common gotcha with field formatter, so be aware of it.
If confusing, just toggle **Use field template**, and see the output. You'll
know which works.
## 2. BLAZY GRID WITH SINGLE VALUE FIELD (D7 ONLY)
This is no issue at D8. Blazy Grid formatter is designed for multi-value fields.
Unfortunately no handy way to disable formatters for single value at D7. So
the formatter is available even for single value, but not actually
functioning. Please ignore it till we can get rid of it at D7, if possible,
without extra legs.
## 3. MIN-WIDTH
If the images appear to be shrink within a **floating** container, add
some expected width or min-width to the parent container via CSS accordingly.
Non-floating image parent containers aren't affected.
## 4. MIN-HEIGHT
Add a min-height CSS to individual element to avoid layout reflow if not using
**Aspect ratio** or when **Aspect ratio** is not supported such as with
Responsive image. Otherwise some collapsed image containers will defeat the
purpose of lazyloading. When using CSS background, the container may also be
collapsed.
### SOLUTIONS
Both layout reflow and lazyloading delay issues are actually taken care of
if **Aspect ratio** option is enabled in the first place.
Adjust, and override blazy CSS/ JS files accordingly.
## 5. BLAZY FILTER
Blazy Filter must run after **Align/ Caption filters** as otherwise the required
CSS class `b-lazy` will be moved into `<figure>` elements and make Blazy fail
with JS error due to not finding the required `SRC` and `[data-src]` attributes.
**Align/ Caption filters** output are respected and moved into Blazy markups
accordingly when Blazy Filter runs after them.
Blazy Filter is useless and broken when you enable **Media embed** or
**Display embedded entities**. You can disable Blazy Filter in favor of Blazy
formatter embedded inside **Media embed** or **Display embedded entities**
instead. However it might be useful for User Generated Contents (UGC) where
Entity/Media Embed are likely more for privileged users, editors, admins, alike.
Or when Entity/Media Embed is disabled.
## 6. INTERSECTION OBSERVER API
* **IntersectionObserver API** is not loading all images, try disabling
**Disconnect** option at Blazy UI.
* **IntersectionObserver API** is not working with Slick `slidesToShow > 1`, try
disabling Slick `centerMode`. If still failing, choose one of the 4 lazy
load options, except Blazy.
## 7. BLUR IMAGE EFFECT
`/admin/config/media/blazy`
The `Image effect` Blur will override `Placeholder` option.
Will use `Thumbnail style` option at Blazy formatters for the placeholder with
fallback to core `Thumbnail` image style.
**For best results:**
* Choose `Aspect ratio` option, non-fluid is better;
* Use similar aspect ratio for both `Thumbnail style` and `Image style`;
* Adjust `Offset` and or `threshold`;
* The smaller the better.
Use `hook_blazy_image_effects_alter()` to add more effects -- curtain, fractal,
slice, whatever.
**Limitations**:
Currently only works with a proper `Aspect ratio` as otherwise collapsed image.
Be sure to add one. If not, add regular CSS `width: 100%` to the blurred
image if doable with your design.
## 8. ASPECT RATIO
**UPDATE 05/02/2020**:
Blazy RC7+ is 99% integrated with Responsive image, including
CSS background and the notorious aspect ratio **Fluid**. The remaining 1% is
some unknown glicthes.
Aspect ratio was never supported for Responsive image till Blazy 2.rc7+, <s>not
fully though. One remaining issue is to make Aspect ratio `Fluid` work for:
CSS background + Picture element.</s>
Any **fixed** Aspect ratio (`4:3, 16:9`, etc) should immediately work as long as
you understand what it means.
Aspect ratio `Fluid` worked with
[**custom breakpoints**](https://www.drupal.org/node/3105243) (deprecated),
<s>not Responsive image, yet. If you want Aspect ratio for Responsive image,
choose anything but `Fluid`.</s>
Any **fixed** Aspect ratio (`4:3, 16:9`, etc), but `Fluid`, wants consistent
aspect ratio down to mobile, which means it won't work with art direction
technique, or Picture element. [Check out few aspect ratio samples](https://cgit.drupalcode.org/blazy/tree/docs/ASPECT_RATIO.md)
Temporary workaround is to add regular CSS `width: 100%` to the controlling
image if doable with your design. And a `min-height` per breakpoint via CSS
mediaqueries.
Aspect ratio fixes many issues with lazyloaded element -- collapsed, distorted,
excessive height, layout reflow, etc., including making iframe fully responsive.
However it doesn't fix everything. Please bear with it.
**If you have display issues, the correct Aspect ratio is your first best bet.**
Depending on your particular issue, **enable or disable**, either way, is your
potential solution. One good sample when Aspect ratio makes no sense is
GridStack gapless grids. Image sizes, hence Aspect ratio, cannot be applied
to gapless grids. Aspect ratio is based on image sizes, not grid sizes.
## 9. BLAZY WITHIN SCROLLING CONTAINER DOES NOT LOAD
`/admin/config/media/blazy`
**Note**: `IO` does not need it, old `bLazy` does.
If you put Blazy within a scrolling container, provide valid comma separated CSS
selectors, except `#drupal-modal, .is-b-scroll`, e.g.: `#my-scrolling-container,
.another-scrolling-container`.
A known scrolling container is `#drupal-modal` like seen at **Media library**.
A scrolling modal with an iframe like **Entity Browser** has no issue since the
scrolling container is the entire DOM. Must know `.blazy` parent container which
has CSS rules containing `overflow` with values anything but `hidden` such as
`auto` or `scroll`. Press `F12` at any browser to inspect elements.
Default to known `#drupal-modal, .is-b-scroll`.
The `.is-b-scroll` can be used when Blazy UI is unreachable without extra legs.
## 10. LINKED FIELD INTEGRATION
Under `Media switcher` option, only `Image to iFrame` makes sense. The rest like
`Image to lightboxes`, or `Image linked to content` will obviously be ignored
since these will output A tag just like what Linked Field does.
Alternatively leave `Media switcher` empty, if no videos are mixed with images.
With `Image to iFrame`, the good thing is video will be still playable, and the
image be linked as required. Best of Both Worlds for real.
## 11. BROKEN MODULES
Alpha, Beta, DEV releases are for developers only. Beware of possible breakage.
However if it is broken, unless an update is provided, running `drush cr` during
DEV releases should fix most issues as we add new services, or change things.
If you don't drush, before any module update, always open:
[Performance](/admin/config/development/performance)
And so you are ready to hit **Clear all caches** if any issue.
Only at worst case, know how to run
https://www.drupal.org/project/registry_rebuild safely.
Check out [Update SOP](#updating) for the non-drush users.
.
***
***
.
# <a name="updating"></a>UPDATE SOP
Visit any of the following URLs when updating Blazy, or its related modules.
Please ignore any documentation if already aware of Drupal site building. This
is for the sake of completed documentation for those who may need it.
1. [Performance](/admin/config/development/performance)
Unless an update is required, clearing cache should fix most issues.
* Hit **Clear all caches** button once the new Blazy in place.
* Regenerate CSS and JS as the latest fixes may contain changes to the assets.
Ignore below if you are aware, and found no asset changes from commits.
Normally clearing cache suffices when no asset changes are found.
* Uncheck CSS and JS aggregation options under Bandwidth optimization.
* Save.
* [Ignorable] See one of Blazy related pages if display is expected.
* [Ignorable] Only clear cache if needed.
* Check both options again.
* Save again.
* [Ignorable] Press F5, or CMD/ CTRL + R to refresh browser cache if
needed.
2. [Admin status](/admin/reports/status)
Check for any pending update, and run `/update.php` from browser address bar.
3. If Twig templates are customized, compare against the latest.
4. Always test updates at DEV or STAGING environments like a pro so nothing
breaks your PRODUCTION site till everything is thoroughly reviewed.
5. Read more the [TROUBLESHOOTING](#troubleshooting) section for common trouble
solutions.
## BROKEN MODULES
Alpha, Beta, DEV releases are for developers only. Beware of possible breakage.
However if it is broken, unless an update is provided, running `drush cr` during
DEV releases should fix most issues as we add new services, or change things.
If you don't drush, before any module update:
1. Always open a separate tab:
[Performance](/admin/config/development/performance)
2. And so you are ready to hit **Clear all caches** button if any issue. Do not
reload this page.
3. Instead view other browser tabs, and simply hit the button if any
issue.
4. Run `/update.php` as required.
5. Only at worst case, know how to run
[Registry Rebuild](https://www.drupal.org/project/registry_rebuild) safely.
/**
* @file
* Provides Intersection Observer API AJAX helper.
*
* Blazy IO works fine with AJAX, until using VIS, or alike. Adds a helper.
*/
(function (Drupal) {
'use strict';
var _blazy = Drupal.blazy || {};
var _ajax = Drupal.Ajax || {};
var _proto = _ajax.prototype;
var _revTimer;
// Overrides Drupal.Ajax.prototype.success to re-observe new AJAX contents.
_proto.success = (function (_ajax) {
return function (response, status) {
var me = _blazy.init;
if (me !== null) {
window.clearTimeout(_revTimer);
// DOM ready fix. Be sure Views "Use field template" is disabled.
_revTimer = window.setTimeout(function () {
var elms = document.querySelectorAll(me.options.selector);
if (elms !== null) {
// ::load() means forcing them to load at once, great for small
// amount of items, bad for large amount.
// ::revalidate() means re-observe newly loaded AJAX contents without
// forcing all images to load at once, great for large, bad for small.
// Unfortunately revalidate() not always work, likely layout reflow.
me.load(elms);
}
}, 100);
}
return _ajax.apply(this, arguments);
};
})(_proto.success);
})(Drupal);
/**
* @file
* Provides Intersection Observer API loader.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
* @see https://developers.google.com/web/updates/2016/04/intersectionobserver
*/
/* global define, module */
(function (root, factory) {
'use strict';
// Inspired by https://github.com/addyosmani/memoize.js/blob/master/memoize.js
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([window.dBlazy], factory);
}
else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but only CommonJS-like
// environments that support module.exports, like Node.
module.exports = factory(window.dBlazy);
}
else {
// Browser globals (root is window).
root.Bio = factory(window.dBlazy);
}
})(this, function (dBlazy) {
'use strict';
/**
* Private variables.
*/
var _doc = document;
var _db = dBlazy;
var _bioTick = 0;
var _revTick = 0;
var _disconnected = false;
var _observed = false;
/**
* Constructor for Bio, Blazy IntersectionObserver.
*
* @param {object} options
* The Bio options.
*
* @namespace
*/
function Bio(options) {
var me = this;
me.options = _db.extend({}, me.defaults, options || {});
// Initializes Blazy IntersectionObserver.
_disconnected = false;
_observed = false;
init(me);
}
// Cache our prototype.
var _proto = Bio.prototype;
_proto.constructor = Bio;
// Prepare prototype to interchange with Blazy as fallback.
_proto.count = 0;
_proto.counted = -1;
_proto.erCounted = 0;
_proto._er = -1;
_proto._ok = 1;
_proto.defaults = {
root: null,
disconnect: false,
error: false,
success: false,
intersecting: false,
observing: false,
successClass: 'b-loaded',
selector: '.b-lazy',
errorClass: 'b-error',
bgClass: 'b-bg',
rootMargin: '0px',
threshold: [0]
};
// BC for interchanging with bLazy.
_proto.load = function (elms) {
var me = this;
// Manually load elements regardless of being disconnected, or not, relevant
// for Slick slidesToShow > 1 which rebuilds clones of unloaded elements.
if (me.isValid(elms)) {
me.intersecting(elms);
}
else {
_db.forEach(elms, function (el) {
if (me.isValid(el)) {
me.intersecting(el);
}
});
}
if (!_disconnected) {
me.disconnect();
}
};
_proto.isLoaded = function (el) {
return el.classList.contains(this.options.successClass);
};
_proto.isValid = function (el) {
return typeof el === 'object' && typeof el.length === 'undefined' && !this.isLoaded(el);
};
_proto.prepare = function () {
// Do nothing, let extenders do their jobs.
};
_proto.revalidate = function (force) {
var me = this;
// Prevents from too many revalidations unless needed.
if ((force === true || me.count !== me.counted) && (_revTick < me.counted)) {
_disconnected = false;
me.elms = (me.options.root || _doc).querySelectorAll(me.options.selector);
me.observe();
_revTick++;
}
};
_proto.intersecting = function (el) {
var me = this;
// If not extending/ overriding, at least provide the option.
if (typeof me.options.intersecting === 'function') {
me.options.intersecting(el, me.options);
}
// Be sure to throttle, or debounce your method when calling this.
_db.trigger(el, 'bio.intersecting', {options: me.options});
me.lazyLoad(el);
me.counted++;
if (!_disconnected) {
me.observer.unobserve(el);
}
};
_proto.lazyLoad = function (el) {
// Do nothing, let extenders do their own lazy, can be images, AJAX, etc.
};
_proto.success = function (el, status, parent) {
var me = this;
if (typeof me.options.success === 'function') {
me.options.success(el, status, parent, me.options);
}
if (me.erCounted > 0) {
me.erCounted--;
}
};
_proto.error = function (el, status, parent) {
var me = this;
if (typeof me.options.error === 'function') {
me.options.error(el, status, parent, me.options);
}
me.erCounted++;
};
_proto.loaded = function (el, status, parent) {
var me = this;
el.classList.add(status === me._ok ? me.options.successClass : me.options.errorClass);
me[status === me._ok ? 'success' : 'error'](el, status, parent);
};
_proto.observe = function () {
var me = this;
_bioTick = me.elms.length;
_db.forEach(me.elms, function (entry) {
// Only observes if not already loaded.
if (!me.isLoaded(entry)) {
me.observer.observe(entry);
}
});
};
_proto.observing = function (entries, observer) {
var me = this;
me.entries = entries;
// Stop watching if already disconnected.
if (_disconnected) {
return;
}
// Load each on entering viewport.
_db.forEach(entries, function (entry) {
// Provides option such as to animate bg or elements regardless position.
if (typeof me.options.observing === 'function') {
me.options.observing(entry, observer, me.options);
}
// The element is being intersected.
if (entry.isIntersecting || entry.intersectionRatio > 0) {
if (!me.isLoaded(entry.target)) {
me.intersecting(entry.target);
}
_bioTick--;
}
});
// Disconnect when all is done.
me.disconnect();
};
_proto.disconnect = function (force) {
var me = this;
// Do not disconnect if any error found.
if (me.erCounted > 0 && !force) {
return;
}
// Disconnect when all entries are loaded, if so configured.
if (((_bioTick === 0 || me.count === me.counted) && me.options.disconnect) || force) {
me.observer.disconnect();
me.count = 0;
me.elms = null;
_disconnected = true;
}
};
_proto.destroy = function (force) {
var me = this;
me.disconnect(force);
me.observer = null;
};
_proto.disconnected = function () {
return _disconnected;
};
_proto.reinit = function () {
_disconnected = false;
_observed = false;
init(this);
};
function init(me) {
var config = {
rootMargin: me.options.rootMargin,
threshold: me.options.threshold
};
me.elms = (me.options.root || _doc).querySelectorAll(me.options.selector + ':not(.' + me.options.successClass + ')');
me.count = me.elms.length;
me.windowWidth = _db.windowWidth();
me.prepare();
// Initializes the IO.
me.observer = new IntersectionObserver(me.observing.bind(me), config);
// Observes once on the page load regardless multiple observer instances.
// Possible as we nullify the root option to allow querying the DOM once.
// Should you need to re-validate, or re-observe, just call ::observe().
if (!_observed) {
me.observe();
_observed = true;
}
}
return Bio;
});
/**
* @file
* Provides Intersection Observer API loader for media.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
* @see https://developers.google.com/web/updates/2016/04/intersectionobserver
*/
/* global define, module */
(function (root, factory) {
'use strict';
// Inspired by https://github.com/addyosmani/memoize.js/blob/master/memoize.js
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([window.dBlazy, window.Bio], factory);
}
else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but only CommonJS-like
// environments that support module.exports, like Node.
module.exports = factory(window.dBlazy, window.Bio);
}
else {
// Browser globals (root is window).
root.BioMedia = factory(window.dBlazy, window.Bio);
}
})(this, function (dBlazy, Bio) {
'use strict';
/**
* Private variables.
*/
var _db = dBlazy;
var _bio = Bio;
var _src = 'src';
var _srcSet = 'srcset';
var _bgSrc = 'data-src';
var _dataSrc = 'data-src';
var _dataSrcset = 'data-srcset';
var _bgSources = [_src];
var _imgSources = [_srcSet, _src];
/**
* Constructor for BioMedia, Blazy IntersectionObserver for media.
*
* @param {object} options
* The BioMedia options.
*
* @return {object}
* The BioMedia instance.
*
* @namespace
*/
function BioMedia(options) {
return _bio.apply(this, arguments);
}
// Inherits Bio prototype.
var _proto = BioMedia.prototype = Object.create(Bio.prototype);
_proto.constructor = BioMedia;
_proto.lazyLoad = (function (_bio) {
return function (el) {
// Image may take time to load after being hit, and it may be intersected
// several times till marked loaded. Ensures it is hit once regardless
// of being loaded, or not. No real issue with normal images on the page,
// until having VIS alike which may spit out new images on AJAX request.
if (el.hasAttribute('data-bio-hit')) {
return;
}
var me = this;
var parent = el.parentNode;
var isImage = _db.equal(el, 'img');
var isBg = typeof el.src === 'undefined' && el.classList.contains(me.options.bgClass);
var isPicture = parent && _db.equal(parent, 'picture');
var isVideo = _db.equal(el, 'video');
// PICTURE elements.
if (isPicture) {
_db.setAttrsWithSources(el, _srcSet, true);
// Tiny controller image inside picture element won't get preloaded.
_db.setAttr(el, _src, true);
me.loaded(el, me._ok);
}
// VIDEO elements.
else if (isVideo) {
_db.setAttrsWithSources(el, _src, true);
el.load();
me.loaded(el, me._ok);
}
else {
// IMG or DIV/ block elements got preloaded for better UX with loading.
if (isImage || isBg) {
me.setImage(el, isBg);
}
// IFRAME elements, etc.
else {
if (el.getAttribute(_dataSrc) && el.hasAttribute(_src)) {
_db.setAttr(el, _src, true);
me.loaded(el, me._ok);
}
}
}
// Marks it hit/ requested. Not necessarily loaded.
el.setAttribute('data-bio-hit', 1);
return _bio.apply(this, arguments);
};
})(_proto.lazyLoad);
_proto.promise = function (el, isBg) {
var me = this;
return new Promise(function (resolve, reject) {
var img = new Image();
// Preload `img` to have correct event handlers.
img.src = el.getAttribute(isBg ? _bgSrc : _dataSrc);
if (el.hasAttribute(_dataSrcset)) {
img.srcset = el.getAttribute(_dataSrcset);
}
// Applies attributes regardless, will re-observe if any error.
var applyAttrs = function () {
if (isBg) {
me.setBg(el);
}
else {
_db.setAttrs(el, _imgSources, false);
}
};
// Handle onload event.
img.onload = function () {
applyAttrs();
resolve(me._ok);
};
// Handle onerror event.
img.onerror = function () {
applyAttrs();
reject(me._er);
};
});
};
_proto.setImage = function (el, isBg) {
var me = this;
return me.promise(el, isBg)
.then(function (status) {
me.loaded(el, status);
_db.removeAttrs(el, isBg ? _bgSources : _imgSources);
})
.catch(function (status) {
me.loaded(el, status);
el.removeAttribute('data-bio-hit');
})
.finally(function () {
// Be sure to throttle, or debounce your method when calling this.
_db.trigger(el, 'bio.finally', {options: me.options});
});
};
_proto.setBg = function (el) {
if (el.hasAttribute(_bgSrc)) {
el.style.backgroundImage = 'url("' + el.getAttribute(_bgSrc) + '")';
el.removeAttribute(_src);
}
};
return BioMedia;
});
/**
* @file
* Provides admin utilities.
*/
(function ($, Drupal) {
'use strict';
/**
* Blazy admin utility functions.
*
* @param {int} i
* The index of the current element.
* @param {HTMLElement} form
* The Blazy form wrapper HTML element.
*/
function blazyForm(i, form) {
var t = $(form);
$('.details-legend-prefix', t).removeClass('element-invisible');
t[$('.form-checkbox--vanilla', t).prop('checked') ? 'addClass' : 'removeClass']('form--vanilla-on');
t.on('click', '.form-checkbox', function () {
var $input = $(this);
$input[$input.prop('checked') ? 'addClass' : 'removeClass']('on');
if ($input.hasClass('form-checkbox--vanilla')) {
t[$input.prop('checked') ? 'addClass' : 'removeClass']('form--vanilla-on');
}
});
$('select[name$="[style]"]', t).on('change', function () {
var $select = $(this);
t.removeClass(function (index, css) {
return (css.match(/(^|\s)form--style-\S+/g) || []).join(' ');
});
if ($select.val() === '') {
t.addClass('form--style-off');
}
else {
t.addClass('form--style-on form--style-' + $select.val());
}
}).change();
$('select[name$="[grid]"]', t).on('change', function () {
var $select = $(this);
t[$select.val() === '' ? 'removeClass' : 'addClass']('form--grid-on');
}).change();
$('select[name$="[responsive_image_style]"]', t).on('change', function () {
var $select = $(this);
t[$select.val() === '' ? 'removeClass' : 'addClass']('form--responsive-image-on');
}).change();
$('select[name$="[media_switch]"]', t).on('change', function () {
var $select = $(this);
t.removeClass(function (index, css) {
return (css.match(/(^|\s)form--media-switch-\S+/g) || []).join(' ');
});
t[$select.val() === '' ? 'removeClass' : 'addClass']('form--media-switch-' + $select.val());
}).change();
t.on('mouseenter touchstart', '.b-hint', function () {
$(this).closest('.form-item').addClass('is-hovered');
});
t.on('mouseleave touchend', '.b-hint', function () {
$(this).closest('.form-item').removeClass('is-hovered');
});
t.on('click', '.b-hint', function () {
$('.form-item.is-selected', t).removeClass('is-selected');
$(this).parent().toggleClass('is-selected');
});
t.on('click', '.description, .form-item__description', function () {
$(this).closest('.is-selected').removeClass('is-selected');
});
t.on('focus', '.js-expandable', function () {
$(this).parent().addClass('is-focused');
});
t.on('blur', '.js-expandable', function () {
$(this).parent().removeClass('is-focused');
});
}
/**
* Blazy admin tooltip function.
*
* @param {int} i
* The index of the current element.
* @param {HTMLElement} elm
* The Blazy form item description HTML element.
*/
function blazyTooltip(i, elm) {
var $tip = $(elm);
// Claro removed description for BEM form-item__description.
if (!$tip.hasClass('description')) {
$tip.addClass('description');
}
if (!$tip.siblings('.b-hint').length) {
$tip.closest('.form-item').append('<span class="b-hint">?</span>');
}
}
/**
* Blazy admin checkbox function.
*
* @param {int} i
* The index of the current element.
* @param {HTMLElement} elm
* The Blazy form item checkbox HTML element.
*/
function blazyCheckbox(i, elm) {
var $elm = $(elm);
if (!$elm.next('.field-suffix').length) {
$elm.after('<span class="field-suffix"></span>');
}
}
/**
* Attaches Blazy form behavior to HTML element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.blazyAdmin = {
attach: function (context) {
var $form = $('.form--slick', context);
$('.description, .form-item__description', context).once('blazy-tooltip').each(blazyTooltip);
$('.form-checkbox', $form).once('blazy-checkbox').each(blazyCheckbox);
$form.once('blazy-admin').each(blazyForm);
}
};
})(jQuery, Drupal);
/**
* @file
* Provides a fullscreen video view for Intense, Slick Browser, etc.
*/
(function (Drupal, _db, window, document) {
'use strict';
Drupal.blazyBox = Drupal.blazyBox || {};
Drupal.blazyBox.el = document.querySelector('.blazybox');
/**
* Theme function for a fullscreen lightbox video container.
*
* @return {HTMLElement}
* Returns a HTMLElement object.
*/
Drupal.theme.blazyBox = function () {
var html;
html = '<div id="blazybox" class="blazybox visually-hidden" tabindex="-1" role="dialog" aria-hidden="true">';
html += '<div class="blazybox__content">' + Drupal.t('Dynamic video content.') + '</div>';
html += '<button class="blazybox__close" data-role="none">&times;</button>';
html += '</div>';
return html;
};
/**
* Theme function for a standalone fullscreen video.
*
* @param {Object} settings
* An object containing the embed url.
*
* @return {HTMLElement}
* Returns a HTMLElement object.
*/
Drupal.theme.blazyBoxMedia = function (settings) {
var html;
html = '<div class="media media--fullscreen">';
html += '<iframe src="' + settings.embedUrl + '" width="100%" height="100%" allowfullscreen></iframe>';
html += '</div>';
return html;
};
/**
* Open the blazyBox.
*
* @param {string} embedUrl
* The video embed url.
*/
Drupal.blazyBox.open = function (embedUrl) {
var me = this;
var mediaEl = Drupal.theme('blazyBoxMedia', {embedUrl: embedUrl});
Drupal.attachBehaviors(me.el);
me.el.querySelector('.blazybox__content').innerHTML = mediaEl;
me.el.classList.remove('visually-hidden');
me.el.setAttribute('aria-hidden', false);
document.body.classList.add('is-blazybox--open');
};
/**
* Attach the blazyBox.
*/
Drupal.blazyBox.attach = function () {
if (document.querySelector('.blazybox') === null) {
// https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
document.body.insertAdjacentHTML('beforeend', Drupal.theme('blazyBox'));
}
};
/**
* Close the blazyBox.
*
* @param {Event} e
* The mouse event triggering the close.
*/
Drupal.blazyBox.close = function (e) {
var el = Drupal.blazyBox.el;
e.preventDefault();
el.classList.add('visually-hidden');
el.setAttribute('aria-hidden', true);
el.querySelector('.blazybox__content').innerHTML = '';
document.body.classList.remove('is-blazybox--open');
};
/**
* BlazyBox utility functions.
*
* @param {HTMLElement} box
* The blazybox HTML element.
*/
function doBlazyBox(box) {
box.classList.add('blazybox--on');
Drupal.blazyBox.el = box;
_db.on(Drupal.blazyBox.el, 'click', '.blazybox__close', Drupal.blazyBox.close);
}
/**
* Attaches Blazybox behavior to HTML element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.blazyBox = {
attach: function (context) {
var boxes = context.querySelectorAll('.blazybox:not(.blazybox--on)');
if (boxes.length > 0) {
_db.once(_db.forEach(boxes, doBlazyBox, context));
}
}
};
})(Drupal, dBlazy, this, this.document);
/**
* @file
*/
(function ($, Drupal, drupalSettings, window) {
'use strict';
var cboxTimer;
var $body = $('body');
/**
* Blazy Colorbox utility functions.
*
* @param {int} i
* The index of the current element.
* @param {HTMLElement} box
* The colorbox HTML element.
*/
function blazyColorbox(i, box) {
var $box = $(box);
var media = $box.data('media') || {};
var isMedia = media.type === 'video';
var runtimeOptions = {
rel: media.rel || null,
iframe: isMedia,
title: function () {
var $caption = $box.next('.litebox-caption');
return $caption.length ? $caption.html() : '';
},
onComplete: function () {
removeClasses();
$body.addClass('colorbox-on colorbox-on--' + media.type);
if (isMedia) {
resizeBox();
$body.addClass('colorbox-on--media');
}
},
onClosed: function () {
removeClasses();
}
};
/**
* Remove the custom colorbox classes.
*/
function removeClasses() {
$body.removeClass(function (index, css) {
return (css.match(/(^|\s)colorbox-\S+/g) || []).join(' ');
});
}
/**
* Resize the colorbox.
*/
function resizeBox() {
window.clearTimeout(cboxTimer);
var o = {
width: media.width || drupalSettings.colorbox.maxWidth,
height: media.height || drupalSettings.colorbox.maxHeight
};
cboxTimer = window.setTimeout(function () {
if ($('#cboxOverlay').is(':visible')) {
var $container = $('#cboxLoadedContent');
var $iframe = $('.cboxIframe', $container);
if ($iframe.length) {
$container.addClass('media media--ratio');
$iframe.attr('width', o.width).attr('height', o.height).addClass('media__element');
$container.css({paddingBottom: (o.height / o.width) * 100 + '%', height: 0});
$.colorbox.resize({
innerWidth: o.width,
innerHeight: o.height
});
}
else {
$container.removeClass('media media--ratio');
$container.css({paddingBottom: '', height: o.height}).removeClass('media__element');
}
}
}, 10);
}
$box.colorbox($.extend({}, drupalSettings.colorbox, runtimeOptions));
}
/**
* Attaches blazy colorbox behavior to HTML element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.blazyColorbox = {
attach: function (context) {
if (typeof drupalSettings.colorbox === 'undefined') {
return;
}
if (drupalSettings.colorbox.mobiledetect && window.matchMedia) {
// Disable Colorbox for small screens.
var mq = window.matchMedia('(max-device-width: ' + drupalSettings.colorbox.mobiledevicewidth + ')');
if (mq.matches) {
return;
}
}
$('[data-colorbox-trigger]', context).once('blazy-colorbox').each(blazyColorbox);
}
};
})(jQuery, Drupal, drupalSettings, this);
/**
* @file
* Provides Media module integration.
*
* @todo use classList anytime.
*/
(function (Drupal, _db) {
'use strict';
/**
* Blazy media utility functions.
*
* @param {HTMLElement} media
* The media player HTML element.
*/
function blazyMedia(media) {
var t = media;
var iframe = t.querySelector('iframe');
var btn = t.querySelector('.media__icon--play');
// Media player toggler is disabled, just display iframe.
if (btn === null) {
return;
}
var url = btn.getAttribute('data-url');
var newIframe;
/**
* Play the media.
*
* @param {Event} event
* The event triggered by a `click` event.
*
* @return {bool}|{mixed}
* Return false if url is not available.
*/
function play(event) {
event.preventDefault();
// oEmbed/ Soundcloud needs internet, fails on disconnected local.
if (url === '') {
return false;
}
var target = this;
var player = target.parentNode;
var playing = document.querySelector('.is-playing');
var iframe = player.querySelector('iframe');
url = target.getAttribute('data-url');
// First, reset any video to avoid multiple videos from playing.
if (playing !== null) {
var played = document.querySelector('.is-playing iframe');
// Remove the previous iframe.
if (played !== null) {
playing.removeChild(played);
}
playing.className = playing.className.replace(/(\S+)playing/, '');
}
// Appends the iframe.
player.className += ' is-playing';
// Remove the existing iframe on the current clicked iframe.
if (iframe !== null) {
player.removeChild(iframe);
}
// Cache iframe for the potential repeating clicks.
if (!newIframe) {
newIframe = document.createElement('iframe');
newIframe.className = 'media__iframe media__element';
newIframe.setAttribute('src', url);
newIframe.setAttribute('allowfullscreen', true);
}
player.appendChild(newIframe);
}
/**
* Close the media.
*
* @param {Event} event
* The event triggered by a `click` event.
*/
function stop(event) {
event.preventDefault();
var target = this;
var player = target.parentNode;
var iframe = player.querySelector('iframe');
if (player.className.match('is-playing')) {
player.className = player.className.replace(/(\S+)playing/, '');
}
if (iframe !== null) {
player.removeChild(iframe);
}
}
// Remove iframe to avoid browser requesting them till clicked.
// The iframe is there as Blazy supports non-lazyloaded/ non-JS iframes.
if (iframe !== null && iframe.parentNode != null) {
iframe.parentNode.removeChild(iframe);
}
// Plays the media player.
_db.on(t, 'click', '.media__icon--play', play);
// Closes the video.
_db.on(t, 'click', '.media__icon--close', stop);
t.className += ' media--player--on';
}
/**
* Theme function for a dynamic inline video.
*
* @param {Object} settings
* An object containing the link element which triggers the lightbox.
* This link must have [data-media] attribute containing video metadata.
*
* @return {HTMLElement}
* Returns a HTMLElement object.
*/
Drupal.theme.blazyMedia = function (settings) {
var elm = settings.el;
var media = elm.getAttribute('data-media') ? _db.parse(elm.getAttribute('data-media')) : {};
var img = elm.querySelector('img');
var alt = img !== null ? img.getAttribute('alt') : 'Video preview';
var pad = media ? Math.round(((media.height / media.width) * 100), 2) : 100;
var boxUrl = elm.getAttribute('data-box-url');
var href = elm.getAttribute('href');
var oembedUrl = elm.hasAttribute('data-oembed-url') ? elm.getAttribute('data-oembed-url') : href;
var html;
html = '<div class="media-wrapper media-wrapper--inline" style="width:' + media.width + 'px">';
html += '<div class="media media--switch media--player media--ratio media--ratio--fluid" style="padding-bottom: ' + pad + '%">';
html += '<img src="' + boxUrl + '" class="media__image media__element" alt="' + Drupal.t(alt) + '"/>';
html += '<span class="media__icon media__icon--close"></span>';
html += '<span class="media__icon media__icon--play" data-url="' + oembedUrl + '"></span>';
html += '</div></div>';
return html;
};
/**
* Attaches Blazy media behavior to HTML element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.blazyMedia = {
attach: function (context) {
var players = context.querySelectorAll('.media--player:not(.media--player--on)');
if (players.length > 0) {
_db.once(_db.forEach(players, blazyMedia));
}
}
};
})(Drupal, dBlazy);
/**
* @file
* Provides Photobox integration for Image and Media fields.
*/
(function ($, Drupal) {
'use strict';
Drupal.blazy = Drupal.blazy || {};
Drupal.behaviors.blazyPhotobox = {
attach: function (context) {
$('[data-photobox-gallery]', context).once('blazy-photobox').each(function () {
$(this).photobox('a[data-photobox-trigger]', {thumb: '> [data-thumb]', thumbAttr: 'data-thumb'}, Drupal.blazy.photobox);
});
}
};
/**
* Callback for custom captions.
*/
Drupal.blazy.photobox = function () {
var $elm = $('.litebox[href="' + $('.pbWrapper img').attr('src') + '"]');
var $caption = $elm.next('.litebox-caption');
if ($caption.length) {
$('#pbCaption .title').html($caption.html());
}
};
}(jQuery, Drupal));
<?php
namespace Drupal\blazy;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FormatterInterface;
use Drupal\editor\Entity\Editor;
/**
* Provides hook_alter() methods for Blazy.
*
* @internal
* This is an internal part of the Blazy system and should only be used by
* blazy-related code in Blazy module.
*/
class BlazyAlter {
/**
* Implements hook_config_schema_info_alter().
*/
public static function configSchemaInfoAlter(array &$definitions, $formatter = 'blazy_base', array $settings = []) {
if (isset($definitions[$formatter])) {
$mappings = &$definitions[$formatter]['mapping'];
$settings = $settings ?: BlazyDefault::extendedSettings() + BlazyDefault::gridSettings();
foreach ($settings as $key => $value) {
// Seems double is ignored, and causes a missing schema, unlike float.
$type = gettype($value);
$type = $type == 'double' ? 'float' : $type;
$mappings[$key]['type'] = $key == 'breakpoints' ? 'mapping' : (is_array($value) ? 'sequence' : $type);
if (!is_array($value)) {
$mappings[$key]['label'] = Unicode::ucfirst(str_replace('_', ' ', $key));
}
}
// @todo remove custom breakpoints anytime before 3.x as per #3105243.
if (isset($mappings['breakpoints'])) {
foreach (['xs', 'sm', 'md', 'lg', 'xl'] as $breakpoint) {
$mappings['breakpoints']['mapping'][$breakpoint]['type'] = 'mapping';
foreach (['breakpoint', 'width', 'image_style'] as $item) {
$mappings['breakpoints']['mapping'][$breakpoint]['mapping'][$item]['type'] = 'string';
$mappings['breakpoints']['mapping'][$breakpoint]['mapping'][$item]['label'] = Unicode::ucfirst(str_replace('_', ' ', $item));
}
}
}
}
}
/**
* Implements hook_library_info_alter().
*/
public static function libraryInfoAlter(&$libraries, $extension) {
if ($extension === 'blazy') {
if ($path = blazy_libraries_get_path('blazy')) {
$libraries['blazy']['js'] = ['/' . $path . '/blazy.js' => ['weight' => -4]];
}
if (blazy()->configLoad('io.enabled')) {
if (blazy()->configLoad('io.unblazy')) {
$dependencies = ['core/drupal', 'blazy/bio.media', 'blazy/loading'];
$libraries['load']['dependencies'] = $dependencies;
}
else {
$libraries['load']['dependencies'][] = 'blazy/bio.media';
}
}
}
if ($extension === 'media' && isset($libraries['oembed.frame'])) {
$libraries['oembed.frame']['dependencies'][] = 'blazy/oembed';
}
}
/**
* Implements hook_blazy_settings_alter().
*/
public static function blazySettingsAlter(array &$build, $items) {
$settings = &$build['settings'];
// Sniffs for Views to allow block__no_wrapper, views_no_wrapper, etc.
if (function_exists('views_get_current_view') && $view = views_get_current_view()) {
$settings['view_name'] = $view->storage->id();
$settings['current_view_mode'] = $view->current_display;
$settings['view_plugin_id'] = empty($settings['view_plugin_id']) ? $view->style_plugin->getPluginId() : $settings['view_plugin_id'];
}
}
/**
* Checks if Entity/Media Embed is enabled.
*/
public static function isCkeditorApplicable(Editor $editor) {
foreach (['entity_embed', 'media_embed'] as $filter) {
if (!$editor->isNew()
&& $editor->getFilterFormat()->filters()->has($filter)
&& $editor->getFilterFormat()
->filters($filter)
->getConfiguration()['status']) {
return TRUE;
}
}
return FALSE;
}
/**
* Implements hook_ckeditor_css_alter().
*/
public static function ckeditorCssAlter(array &$css, Editor $editor) {
if (self::isCkeditorApplicable($editor)) {
$path = base_path() . drupal_get_path('module', 'blazy');
$css[] = $path . '/css/components/blazy.media.css';
$css[] = $path . '/css/components/blazy.preview.css';
$css[] = $path . '/css/components/blazy.ratio.css';
}
}
/**
* Provides the third party formatters where full blown Blazy is not worthy.
*
* The module doesn't automatically convert the relevant theme to use Blazy,
* however two attributes are provided: `data-b-lazy` and `data-b-preview`
* which can be used to override a particular theme to use Blazy.
*
* The `data-b-lazy`is a flag indicating Blazy is enabled.
* The `data-b-preview` is a flag indicating Blazy in CKEditor preview mode
* via Entity/Media Embed which normally means Blazy should be disabled
* due to CKEditor not supporting JS assets.
*
* @see \Drupal\blazy\Blazy::preprocessBlazy()
* @see \Drupal\blazy\Blazy::preprocessField()
* @see \Drupal\blazy\Blazy::preprocessFileVideo()
* @see blazy_preprocess_file_video()
*/
public static function thirdPartyFormatters() {
$formatters = ['file_video'];
blazy()->getModuleHandler()->alter('blazy_third_party_formatters', $formatters);
return array_unique($formatters);
}
/**
* Overrides variables for field.html.twig templates.
*/
public static function thirdPartyPreprocessField(array &$variables) {
$element = $variables['element'];
$settings = empty($element['#blazy']) ? [] : $element['#blazy'];
$settings['third_party'] = $element['#third_party_settings'];
$is_preview = Blazy::isPreview();
foreach ($variables['items'] as &$item) {
if (empty($item['content'])) {
continue;
}
$item_attributes = &$item['content'][isset($item['content']['#attributes']) ? '#attributes' : '#item_attributes'];
$item_attributes['data-b-lazy'] = TRUE;
if ($is_preview) {
$item_attributes['data-b-preview'] = TRUE;
}
}
// Attaches Blazy libraries here since Blazy is not the formatter.
$attachments = blazy()->attach($settings);
$variables['#attached'] = empty($variables['#attached']) ? $attachments : NestedArray::mergeDeep($variables['#attached'], $attachments);
}
/**
* Implements hook_field_formatter_third_party_settings_form().
*/
public static function fieldFormatterThirdPartySettingsForm(FormatterInterface $plugin) {
if (in_array($plugin->getPluginId(), self::thirdPartyFormatters())) {
return [
'blazy' => [
'#type' => 'checkbox',
'#title' => 'Blazy',
'#default_value' => $plugin->getThirdPartySetting('blazy', 'blazy', FALSE),
],
];
}
return [];
}
/**
* Implements hook_field_formatter_settings_summary_alter().
*/
public static function fieldFormatterSettingsSummaryAlter(&$summary, $context) {
$on = $context['formatter']->getThirdPartySetting('blazy', 'blazy', FALSE);
if ($on && in_array($context['formatter']->getPluginId(), self::thirdPartyFormatters())) {
$summary[] = 'Blazy';
}
}
/**
* Attaches Colorbox if so configured.
*/
public static function attachColorbox(array &$load, $attach = []) {
if (\Drupal::hasService('colorbox.attachment')) {
$dummy = [];
\Drupal::service('colorbox.attachment')->attach($dummy);
$load = isset($dummy['#attached']) ? NestedArray::mergeDeep($load, $dummy['#attached']) : $load;
$load['library'][] = 'blazy/colorbox';
unset($dummy);
}
}
}
<?php
namespace Drupal\blazy;
/**
* Defines shared plugin default settings for field formatter and Views style.
*/
class BlazyDefault {
/**
* Defines constant for the supported text tags.
*/
const TAGS = ['a', 'em', 'strong', 'h2', 'p', 'span', 'ul', 'ol', 'li'];
/**
* The current class instance.
*
* @var self
*/
private static $instance = NULL;
/**
* Returns the static instance of this class.
*/
public static function getInstance() {
if (is_null(self::$instance)) {
self::$instance = new BlazyDefault();
}
return self::$instance;
}
/**
* Returns Blazy specific breakpoints.
*
* @todo remove custom breakpoints anytime before blazy:3.x.
*/
public static function getConstantBreakpoints() {
return ['xs', 'sm', 'md', 'lg', 'xl'];
}
/**
* Returns alterable plugin settings to pass the tests.
*/
public function alterableSettings(array &$settings = []) {
$context = ['class' => get_called_class()];
\Drupal::moduleHandler()->alter('blazy_base_settings', $settings, $context);
return $settings;
}
/**
* Returns settings provided by various UI.
*/
public static function anywhereSettings() {
return ['fx' => '', 'style' => ''];
}
/**
* Returns basic plugin settings.
*/
public static function baseSettings() {
$settings = [
'cache' => 0,
'current_view_mode' => '',
'skin' => '',
] + self::anywhereSettings();
blazy_alterable_settings($settings);
return $settings;
}
/**
* Returns cherry-picked settings for field formatters and Views fields.
*/
public static function cherrySettings() {
return [
'box_style' => '',
'image_style' => '',
'media_switch' => '',
'ratio' => '',
'thumbnail_style' => '',
'_uri' => '',
];
}
/**
* Returns image-related field formatter and Views settings.
*/
public static function baseImageSettings() {
return [
'background' => FALSE,
'box_caption' => '',
'box_caption_custom' => '',
'box_media_style' => '',
'caption' => [],
'responsive_image_style' => '',
] + self::cherrySettings();
}
/**
* Returns deprecated settings.
*
* @todo remove custom breakpoints anytime before 3.x.
*/
public static function deprecatedSettings() {
return [
'breakpoints' => [],
'sizes' => '',
'grid_header' => '',
];
}
/**
* Returns image-related field formatter and Views settings.
*/
public static function imageSettings() {
return [
'icon' => '',
'layout' => '',
'view_mode' => '',
] + self::baseSettings() + self::baseImageSettings() + self::deprecatedSettings();
}
/**
* Returns Views specific settings.
*/
public static function viewsSettings() {
return [
'class' => '',
'id' => '',
'image' => '',
'link' => '',
'overlay' => '',
'title' => '',
'vanilla' => FALSE,
];
}
/**
* Returns fieldable entity formatter and Views settings.
*/
public static function extendedSettings() {
return self::viewsSettings() + self::imageSettings();
}
/**
* Returns optional grid field formatter and Views settings.
*/
public static function gridSettings() {
return [
'grid' => 0,
'grid_header' => '',
'grid_medium' => 0,
'grid_small' => 0,
] + self::anywhereSettings();
}
/**
* Returns sensible default options common for Views lacking of UI.
*/
public static function lazySettings() {
return [
'blazy' => TRUE,
'lazy' => 'blazy',
'ratio' => 'fluid',
];
}
/**
* Returns sensible default options common for entities lacking of UI.
*/
public static function entitySettings() {
return [
'media_switch' => 'media',
'rendered' => FALSE,
'view_mode' => 'default',
'_detached' => TRUE,
] + self::lazySettings();
}
/**
* Returns default options common for rich Media entities: Facebook, etc.
*
* This basically disables few Blazy features for rendered-entity-like.
*/
public static function richSettings() {
return [
'background' => FALSE,
'lazy' => '',
'lightbox' => FALSE,
'media_switch' => '',
'placeholder' => '',
'resimage' => FALSE,
'use_loading' => FALSE,
'type' => 'rich',
] + self::anywhereSettings();
}
/**
* Returns shared global form settings which should be consumed at formatters.
*/
public static function uiSettings() {
return [
'one_pixel' => TRUE,
'noscript' => FALSE,
'placeholder' => '',
'responsive_image' => FALSE,
] + self::anywhereSettings();
}
/**
* Returns sensible default container settings to shutup notices when lacking.
*/
public static function htmlSettings() {
return [
'blazy_data' => [],
'lightbox' => FALSE,
'namespace' => 'blazy',
'id' => '',
'is_preview' => FALSE,
'route_name' => '',
'use_field' => FALSE,
'view_name' => '',
] + self::imageSettings() + self::uiSettings();
}
/**
* Returns sensible default item settings to shutup notices when lacking.
*/
public static function itemSettings() {
return [
'_api' => FALSE,
'bundle' => '',
'classes' => [],
'content_url' => '',
'delta' => 0,
'embed_url' => '',
'entity_type_id' => '',
'extension' => '',
'image_url' => '',
'item_id' => 'blazy',
'lazy_attribute' => 'src',
'lazy_class' => 'b-lazy',
'padding_bottom' => '',
'player' => FALSE,
'resimage' => FALSE,
'scheme' => '',
'type' => 'image',
'uri' => '',
'use_data_uri' => FALSE,
'use_loading' => TRUE,
'use_media' => FALSE,
'height' => NULL,
'width' => NULL,
] + self::htmlSettings();
}
/**
* Returns blazy theme properties, its image and container attributes.
*
* The reserved attributes is defined before entering Blazy as bonus variable.
* Consider other bonuses: title and content attributes at a later stage.
*/
public static function themeProperties() {
return [
'attributes',
'captions',
'content',
'iframe',
'image',
'icon',
'item',
'item_attributes',
'noscript',
'overlay',
'preface',
'postscript',
'settings',
'url',
];
}
/**
* Returns additional/ optional blazy theme attributes.
*
* The attributes mentioned here are only instantiated at theme_blazy() and
* might be an empty array, not instanceof \Drupal\Core\Template\Attribute.
*/
public static function themeAttributes() {
return ['caption', 'media', 'url', 'wrapper'];
}
}
<?php
namespace Drupal\blazy;
use Drupal\Core\StringTranslation\TranslatableMarkup;
/**
* A Trait common for deprecated methods for easy removal and declutter.
*
* @todo remove at blazy:8.x-2.1, or earlier.
* @see https://www.drupal.org/node/3103018
*/
trait BlazyDeprecatedTrait {
/**
* Implements hook_field_formatter_info_alter().
*
* @todo remove from blazy:8.x-2.1 for
* \Drupal\blazy\Plugin\Field\FieldFormatter\BlazyMediaFormatter.
* @see https://www.drupal.org/node/3103018
*/
public static function fieldFormatterInfoAlter(array &$info) {
// Supports optional Media Entity via VEM/VEF if available.
$common = [
'description' => new TranslatableMarkup('Displays lazyloaded images, or iframes, for VEF/ ME.'),
'quickedit' => ['editor' => 'disabled'],
'provider' => 'blazy',
];
$info['blazy_file'] = $common + [
'id' => 'blazy_file',
'label' => new TranslatableMarkup('Blazy Image with VEF (deprecated)'),
'class' => 'Drupal\blazy\Plugin\Field\FieldFormatter\BlazyFileFormatter',
'field_types' => ['entity_reference', 'image'],
];
$info['blazy_video'] = $common + [
'id' => 'blazy_video',
'label' => new TranslatableMarkup('Blazy Video (deprecated)'),
'class' => 'Drupal\blazy\Plugin\Field\FieldFormatter\BlazyVideoFormatter',
'field_types' => ['video_embed_field'],
];
}
}
<?php
namespace Drupal\blazy;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Render\Element;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Implements BlazyEntityInterface.
*/
class BlazyEntity implements BlazyEntityInterface {
/**
* The blazy oembed service.
*
* @var object
*/
protected $oembed;
/**
* The blazy manager service.
*
* @var object
*/
protected $blazyManager;
/**
* Constructs a BlazyFormatter instance.
*/
public function __construct(BlazyOEmbedInterface $oembed) {
$this->oembed = $oembed;
$this->blazyManager = $oembed->blazyManager();
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('blazy.oembed')
);
}
/**
* Returns the blazy oembed service.
*/
public function oembed() {
return $this->oembed;
}
/**
* Returns the blazy manager service.
*/
public function blazyManager() {
return $this->blazyManager;
}
/**
* {@inheritdoc}
*/
public function build(array &$data, $entity, $fallback = '') {
if (!$entity instanceof EntityInterface) {
return [];
}
// Supports core Media via Drupal\blazy\BlazyOEmbed::getMediaItem().
$data['settings'] = empty($data['settings']) ? [] : $data['settings'];
$this->blazyManager->getCommonSettings($data['settings']);
$this->oembed->getMediaItem($data, $entity);
$settings = &$data['settings'];
// Made Responsive image also available outside formatters here.
if (!empty($settings['resimage']) && $settings['ratio'] == 'fluid') {
$this->blazyManager->setResponsiveImageDimensions($settings, FALSE);
}
// Only pass to Blazy for known entities related to File or Media.
if (in_array($entity->getEntityTypeId(), ['file', 'media'])) {
/** @var Drupal\image\Plugin\Field\FieldType\ImageItem $item */
if (empty($data['item'])) {
$data['content'][] = $this->getEntityView($entity, $settings, $fallback);
}
// Pass it to Blazy for consistent markups.
$build = $this->blazyManager->getBlazy($data);
// Allows top level elements to load Blazy once rather than per field.
// This is still here for non-supported Views style plugins, etc.
if (empty($settings['_detached'])) {
$load = $this->blazyManager->attach($settings);
$build['#attached'] = empty($build['#attached']) ? $load : NestedArray::mergeDeep($build['#attached'], $load);
}
}
else {
$build = $this->getEntityView($entity, $settings, $fallback);
}
$this->blazyManager->getModuleHandler()->alter('blazy_build_entity', $build, $entity, $settings);
return $build;
}
/**
* {@inheritdoc}
*/
public function getEntityView($entity, array $settings = [], $fallback = '') {
if ($entity instanceof EntityInterface) {
$entity_type_id = $entity->getEntityTypeId();
$view_mode = $settings['view_mode'] = empty($settings['view_mode']) ? 'default' : $settings['view_mode'];
$langcode = $entity->language()->getId();
$fallback = $fallback && is_string($fallback) ? ['#markup' => '<div class="is-fallback">' . $fallback . '</div>'] : $fallback;
// If entity has view_builder handler.
if ($this->blazyManager->getEntityTypeManager()->hasHandler($entity_type_id, 'view_builder')) {
$build = $this->blazyManager->getEntityTypeManager()->getViewBuilder($entity_type_id)->view($entity, $view_mode, $langcode);
// @todo figure out why video_file empty, this is blatant assumption.
if ($entity_type_id == 'file') {
try {
$build = $this->getFileOrMedia($entity, $settings) ?: $build;
}
catch (\Exception $ignore) {
// Do nothing, no need to be chatty in mischievous deeds.
}
}
return $build ?: $fallback;
}
else {
// If module implements own {entity_type}_view.
// @todo remove due to being deprecated at D8.7.
// See https://www.drupal.org/node/3033656
$view_hook = $entity_type_id . '_view';
if (is_callable($view_hook)) {
return $view_hook($entity, $view_mode, $langcode);
}
}
}
return $fallback;
}
/**
* Returns file view or media due to being empty returned by view builder.
*
* @todo make it usable for other file-related entities.
*/
public function getFileOrMedia($file, array $settings, $use_file = TRUE) {
list($type,) = explode('/', $file->getMimeType(), 2);
if ($type == 'video') {
// As long as you are not being too creative by renaming or changing
// fields provided by core, this should be your good friend.
$settings['media_source'] = 'video_file';
$settings['source_field'] = 'field_media_video_file';
}
if (!empty($settings['source_field']) && isset($settings['media_source'])
&& $media = $this->blazyManager->getEntityTypeManager()->getStorage('media')->loadByProperties([$settings['source_field'] => ['fid' => $file->id()]])) {
if ($media = reset($media)) {
return $use_file ? BlazyMedia::build($media, $settings) : $media;
}
}
return [];
}
/**
* Returns the string value of the fields: link, or text.
*/
public function getFieldValue($entity, $field_name, $langcode) {
if ($entity->hasField($field_name)) {
if ($entity->hasTranslation($langcode)) {
// If the entity has translation, fetch the translated value.
return $entity->getTranslation($langcode)->get($field_name)->getValue();
}
// Entity doesn't have translation, fetch original value.
return $entity->get($field_name)->getValue();
}
return NULL;
}
/**
* Returns the string value of the fields: link, or text.
*/
public function getFieldString($entity, $field_name, $langcode, $clean = TRUE) {
if ($entity->hasField($field_name)) {
$values = $this->getFieldValue($entity, $field_name, $langcode);
// Can be text, or link field.
$string = isset($values[0]['uri']) ? $values[0]['uri'] : (isset($values[0]['value']) ? $values[0]['value'] : '');
if ($string && is_string($string)) {
$string = $clean ? strip_tags($string, '<a><strong><em><span><small>') : Xss::filter($string, BlazyDefault::TAGS);
return trim($string);
}
}
return '';
}
/**
* Returns the formatted renderable array of the field.
*/
public function getFieldRenderable($entity, $field_name, $view_mode, $multiple = TRUE) {
if ($entity->hasField($field_name) && !empty($entity->{$field_name}->view($view_mode)[0])) {
$view = $entity->get($field_name)->view($view_mode);
// Prevents quickedit to operate here as otherwise JS error.
// @see 2314185, 2284917, 2160321.
// @see quickedit_preprocess_field().
// @todo: Remove when it respects plugin annotation.
$view['#view_mode'] = '_custom';
$weight = isset($view['#weight']) ? $view['#weight'] : 0;
// Intentionally clean markups as this is not meant for vanilla.
if ($multiple) {
$items = [];
foreach (Element::children($view) as $key) {
$items[$key] = $entity->get($field_name)->view($view_mode)[$key];
}
$items['#weight'] = $weight;
return $items;
}
return $view[0];
}
return [];
}
/**
* Returns the text or link value of the fields: link, or text.
*/
public function getFieldTextOrLink($entity, $field_name, $settings, $multiple = TRUE) {
if ($entity->hasField($field_name)) {
$langcode = $settings['langcode'];
if ($text = $this->getFieldValue($entity, $field_name, $langcode)) {
if (!empty($text[0]['value']) && !isset($text[0]['uri'])) {
// Prevents HTML-filter-enabled text from having bad markups (h2 > p),
// except for a few reasonable tags acceptable within H2 tag.
$text = $this->getFieldString($entity, $field_name, $langcode, FALSE);
}
elseif (isset($text[0]['uri']) && !empty($text[0]['title'])) {
$text = $this->getFieldRenderable($entity, $field_name, $settings['view_mode'], $multiple);
}
// Prevents HTML-filter-enabled text from having bad markups
// (h2 > p), save for few reasonable tags acceptable within H2 tag.
return is_string($text) ? ['#markup' => strip_tags($text, '<a><strong><em><span><small>')] : $text;
}
}
return [];
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment