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