1*37748cd8SNickeau<?php 2*37748cd8SNickeau/** 3*37748cd8SNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4*37748cd8SNickeau * 5*37748cd8SNickeau * This source code is licensed under the GPL license found in the 6*37748cd8SNickeau * COPYING file in the root directory of this source tree. 7*37748cd8SNickeau * 8*37748cd8SNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9*37748cd8SNickeau * @author ComboStrap <support@combostrap.com> 10*37748cd8SNickeau * 11*37748cd8SNickeau */ 12*37748cd8SNickeau 13*37748cd8SNickeaunamespace ComboStrap; 14*37748cd8SNickeau 15*37748cd8SNickeau/** 16*37748cd8SNickeau * This one support background loading 17*37748cd8SNickeau * https://github.com/ApoorvSaxena/lozad.js 18*37748cd8SNickeau * 19*37748cd8SNickeau * 20*37748cd8SNickeau * TODO: implement no script pattern ? the https://github.com/aFarkas/lazysizes#the-noscript-pattern 21*37748cd8SNickeau * 22*37748cd8SNickeau */ 23*37748cd8SNickeauclass LazyLoad 24*37748cd8SNickeau{ 25*37748cd8SNickeau 26*37748cd8SNickeau const CONF_LAZY_LOADING_PLACEHOLDER_COLOR = "lazyLoadingPlaceholderColor"; 27*37748cd8SNickeau 28*37748cd8SNickeau /** 29*37748cd8SNickeau * Lozad was choosen because 30*37748cd8SNickeau * it was easier to add svg injection 31*37748cd8SNickeau * it supports background image 32*37748cd8SNickeau * it's most used (JsDelivr stats) 33*37748cd8SNickeau */ 34*37748cd8SNickeau const ACTIVE = self::LOZAD_ID; 35*37748cd8SNickeau 36*37748cd8SNickeau /** 37*37748cd8SNickeau * The id of the lazy loaders 38*37748cd8SNickeau */ 39*37748cd8SNickeau const LAZY_SIDE_ID = "lazy-sizes"; 40*37748cd8SNickeau const LOZAD_ID = "lozad"; 41*37748cd8SNickeau 42*37748cd8SNickeau 43*37748cd8SNickeau const CANONICAL = "lazy"; 44*37748cd8SNickeau const DEFAULT_COLOR = "#cbf1ea"; 45*37748cd8SNickeau 46*37748cd8SNickeau /** 47*37748cd8SNickeau * Used to select all lazy loaded 48*37748cd8SNickeau * resources and load them before print 49*37748cd8SNickeau */ 50*37748cd8SNickeau const LAZY_CLASS = "lazy-combo"; 51*37748cd8SNickeau 52*37748cd8SNickeau 53*37748cd8SNickeau public static function addSnippet() 54*37748cd8SNickeau { 55*37748cd8SNickeau switch (self::ACTIVE) { 56*37748cd8SNickeau case self::LAZY_SIDE_ID: 57*37748cd8SNickeau LazyLoad::addLazySizesSnippet(); 58*37748cd8SNickeau break; 59*37748cd8SNickeau case self::LOZAD_ID: 60*37748cd8SNickeau LazyLoad::addLozadSnippet(); 61*37748cd8SNickeau break; 62*37748cd8SNickeau default: 63*37748cd8SNickeau throw new \RuntimeException("The active lazy loaded is unknown (" . self::ACTIVE . ")"); 64*37748cd8SNickeau } 65*37748cd8SNickeau 66*37748cd8SNickeau } 67*37748cd8SNickeau 68*37748cd8SNickeau /** 69*37748cd8SNickeau * Add the lazy sizes snippet 70*37748cd8SNickeau */ 71*37748cd8SNickeau private static function addLazySizesSnippet() 72*37748cd8SNickeau { 73*37748cd8SNickeau 74*37748cd8SNickeau $snippetManager = PluginUtility::getSnippetManager(); 75*37748cd8SNickeau 76*37748cd8SNickeau $snippetManager->upsertTagsForBar(self::LAZY_SIDE_ID, 77*37748cd8SNickeau array( 78*37748cd8SNickeau 'script' => [ 79*37748cd8SNickeau array( 80*37748cd8SNickeau "src" => "https://cdn.jsdelivr.net/npm/lazysizes@5.3.1/lazysizes.min.js", 81*37748cd8SNickeau "integrity" => "sha256-bmG+LzdKASJRACVXiUC69++Nu8rz7MX1U1z8gb0c/Tk=", 82*37748cd8SNickeau "crossorigin" => "anonymous" 83*37748cd8SNickeau ) 84*37748cd8SNickeau ] 85*37748cd8SNickeau ) 86*37748cd8SNickeau ); 87*37748cd8SNickeau /** 88*37748cd8SNickeau * The Spinner effect 89*37748cd8SNickeau * lazysizes adds the class lazy loading while the images are loading 90*37748cd8SNickeau * and the class lazyloaded as soon as the image is loaded. 91*37748cd8SNickeau */ 92*37748cd8SNickeau $snippetManager->attachCssSnippetForBar(self::LAZY_SIDE_ID); 93*37748cd8SNickeau 94*37748cd8SNickeau } 95*37748cd8SNickeau 96*37748cd8SNickeau /** 97*37748cd8SNickeau * @param TagAttributes $attributes 98*37748cd8SNickeau */ 99*37748cd8SNickeau public static function addPlaceholderBackground(&$attributes) 100*37748cd8SNickeau { 101*37748cd8SNickeau // https://github.com/ApoorvSaxena/lozad.js#large-image-improvment 102*37748cd8SNickeau $placeholderColor = LazyLoad::getPlaceholderColor(); 103*37748cd8SNickeau if ($attributes->hasComponentAttribute(Background::BACKGROUND_COLOR)) { 104*37748cd8SNickeau $placeholderColor = $attributes->getValueAndRemove(Background::BACKGROUND_COLOR); 105*37748cd8SNickeau } 106*37748cd8SNickeau $attributes->addHtmlAttributeValue("data-placeholder-background", "$placeholderColor"); 107*37748cd8SNickeau 108*37748cd8SNickeau 109*37748cd8SNickeau } 110*37748cd8SNickeau 111*37748cd8SNickeau /** 112*37748cd8SNickeau * Add lozad 113*37748cd8SNickeau * Support background image 114*37748cd8SNickeau * https://github.com/ApoorvSaxena/lozad.js 115*37748cd8SNickeau */ 116*37748cd8SNickeau public static function addLozadSnippet() 117*37748cd8SNickeau { 118*37748cd8SNickeau 119*37748cd8SNickeau $snippetManager = PluginUtility::getSnippetManager(); 120*37748cd8SNickeau 121*37748cd8SNickeau // https://www.jsdelivr.com/package/npm/lozad 122*37748cd8SNickeau $snippetManager->upsertTagsForBar(self::LOZAD_ID, 123*37748cd8SNickeau array( 124*37748cd8SNickeau 'script' => [ 125*37748cd8SNickeau array( 126*37748cd8SNickeau "src" => "https://cdn.jsdelivr.net/npm/lozad@1.16.0/dist/lozad.min.js", 127*37748cd8SNickeau "integrity" => "sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=", 128*37748cd8SNickeau "crossorigin" => "anonymous" 129*37748cd8SNickeau 130*37748cd8SNickeau ) 131*37748cd8SNickeau ] 132*37748cd8SNickeau ) 133*37748cd8SNickeau ); 134*37748cd8SNickeau 135*37748cd8SNickeau /** 136*37748cd8SNickeau * Add the fading effect 137*37748cd8SNickeau */ 138*37748cd8SNickeau $snippetId = "lazy-load-fade"; 139*37748cd8SNickeau $snippetManager->attachCssSnippetForBar($snippetId); 140*37748cd8SNickeau 141*37748cd8SNickeau 142*37748cd8SNickeau /** 143*37748cd8SNickeau * Snippet to download the image before print 144*37748cd8SNickeau * 145*37748cd8SNickeau * The others javascript snippet to download lazy load depend on the image type 146*37748cd8SNickeau * and features and was therefore added in the code for svg or raster 147*37748cd8SNickeau */ 148*37748cd8SNickeau $snippetManager->attachJavascriptSnippetForBar("lozad-print"); 149*37748cd8SNickeau 150*37748cd8SNickeau 151*37748cd8SNickeau } 152*37748cd8SNickeau 153*37748cd8SNickeau /** 154*37748cd8SNickeau * Class selector to identify the element to lazy load 155*37748cd8SNickeau */ 156*37748cd8SNickeau public static function getClass() 157*37748cd8SNickeau { 158*37748cd8SNickeau switch (self::ACTIVE) { 159*37748cd8SNickeau case self::LAZY_SIDE_ID: 160*37748cd8SNickeau return "lazyload"; 161*37748cd8SNickeau case self::LOZAD_ID: 162*37748cd8SNickeau return "lozad"; 163*37748cd8SNickeau default: 164*37748cd8SNickeau throw new \RuntimeException("The active lazy loaded is unknown (" . self::ACTIVE . ")"); 165*37748cd8SNickeau } 166*37748cd8SNickeau } 167*37748cd8SNickeau 168*37748cd8SNickeau /** 169*37748cd8SNickeau * @return string - the lazy loading placeholder color 170*37748cd8SNickeau */ 171*37748cd8SNickeau public static function getPlaceholderColor() 172*37748cd8SNickeau { 173*37748cd8SNickeau return PluginUtility::getConfValue(self::CONF_LAZY_LOADING_PLACEHOLDER_COLOR, self::DEFAULT_COLOR); 174*37748cd8SNickeau } 175*37748cd8SNickeau 176*37748cd8SNickeau /** 177*37748cd8SNickeau * The placeholder is not mandatory 178*37748cd8SNickeau * but if present, it should have the same intrinsic ratio of the image 179*37748cd8SNickeau * 180*37748cd8SNickeau * This function is documenting this fact. 181*37748cd8SNickeau * 182*37748cd8SNickeau * @param null $imgTagWidth 183*37748cd8SNickeau * @param null $imgTagHeight 184*37748cd8SNickeau * @return string 185*37748cd8SNickeau * 186*37748cd8SNickeau 187*37748cd8SNickeau * 188*37748cd8SNickeau * Src is always set, this is the default 189*37748cd8SNickeau * src attribute is served to browsers that do not take the srcset attribute into account. 190*37748cd8SNickeau * When lazy loading, we set the srcset to a transparent image to not download the image in the src 191*37748cd8SNickeau * 192*37748cd8SNickeau */ 193*37748cd8SNickeau 194*37748cd8SNickeau public static function getPlaceholder($imgTagWidth = null, $imgTagHeight = null) 195*37748cd8SNickeau { 196*37748cd8SNickeau if ($imgTagWidth != null) { 197*37748cd8SNickeau $svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 $imgTagWidth $imgTagHeight'></svg>"; 198*37748cd8SNickeau /** 199*37748cd8SNickeau * We encode it to be able to 200*37748cd8SNickeau * use it in a `srcset` attribute that does not 201*37748cd8SNickeau * want any space in the image definition 202*37748cd8SNickeau */ 203*37748cd8SNickeau $svgBase64 = base64_encode($svg); 204*37748cd8SNickeau $image = "data:image/svg+xml;base64,$svgBase64"; 205*37748cd8SNickeau } else { 206*37748cd8SNickeau /** 207*37748cd8SNickeau * Base64 transparent gif 208*37748cd8SNickeau * 1x1 image, it will produce a square 209*37748cd8SNickeau */ 210*37748cd8SNickeau $image = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; 211*37748cd8SNickeau } 212*37748cd8SNickeau return $image; 213*37748cd8SNickeau } 214*37748cd8SNickeau} 215