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->upsertTagsForBar(self::LAZY_SIDE_ID, 77 array( 78 'script' => [ 79 array( 80 "src" => "https://cdn.jsdelivr.net/npm/lazysizes@5.3.1/lazysizes.min.js", 81 "integrity" => "sha256-bmG+LzdKASJRACVXiUC69++Nu8rz7MX1U1z8gb0c/Tk=", 82 "crossorigin" => "anonymous" 83 ) 84 ] 85 ) 86 ); 87 /** 88 * The Spinner effect 89 * lazysizes adds the class lazy loading while the images are loading 90 * and the class lazyloaded as soon as the image is loaded. 91 */ 92 $snippetManager->attachCssSnippetForBar(self::LAZY_SIDE_ID); 93 94 } 95 96 /** 97 * @param TagAttributes $attributes 98 */ 99 public static function addPlaceholderBackground(&$attributes) 100 { 101 // https://github.com/ApoorvSaxena/lozad.js#large-image-improvment 102 $placeholderColor = LazyLoad::getPlaceholderColor(); 103 if ($attributes->hasComponentAttribute(Background::BACKGROUND_COLOR)) { 104 $placeholderColor = $attributes->getValueAndRemove(Background::BACKGROUND_COLOR); 105 } 106 $attributes->addHtmlAttributeValue("data-placeholder-background", "$placeholderColor"); 107 108 109 } 110 111 /** 112 * Add lozad 113 * Support background image 114 * https://github.com/ApoorvSaxena/lozad.js 115 */ 116 public static function addLozadSnippet() 117 { 118 119 $snippetManager = PluginUtility::getSnippetManager(); 120 121 // https://www.jsdelivr.com/package/npm/lozad 122 $snippetManager->upsertTagsForBar(self::LOZAD_ID, 123 array( 124 'script' => [ 125 array( 126 "src" => "https://cdn.jsdelivr.net/npm/lozad@1.16.0/dist/lozad.min.js", 127 "integrity" => "sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=", 128 "crossorigin" => "anonymous" 129 130 ) 131 ] 132 ) 133 ); 134 135 /** 136 * Add the fading effect 137 */ 138 $snippetId = "lazy-load-fade"; 139 $snippetManager->attachCssSnippetForBar($snippetId); 140 141 142 /** 143 * Snippet to download the image before print 144 * 145 * The others javascript snippet to download lazy load depend on the image type 146 * and features and was therefore added in the code for svg or raster 147 */ 148 $snippetManager->attachJavascriptSnippetForBar("lozad-print"); 149 150 151 } 152 153 /** 154 * Class selector to identify the element to lazy load 155 */ 156 public static function getClass() 157 { 158 switch (self::ACTIVE) { 159 case self::LAZY_SIDE_ID: 160 return "lazyload"; 161 case self::LOZAD_ID: 162 return "lozad"; 163 default: 164 throw new \RuntimeException("The active lazy loaded is unknown (" . self::ACTIVE . ")"); 165 } 166 } 167 168 /** 169 * @return string - the lazy loading placeholder color 170 */ 171 public static function getPlaceholderColor() 172 { 173 return PluginUtility::getConfValue(self::CONF_LAZY_LOADING_PLACEHOLDER_COLOR, self::DEFAULT_COLOR); 174 } 175 176 /** 177 * The placeholder is not mandatory 178 * but if present, it should have the same target ratio of the image 179 * 180 * This function is documenting this fact. 181 * 182 * @param null $imgTagWidth 183 * @param null $imgTagHeight 184 * @return string 185 * 186 187 * 188 * Src is always set, this is the default 189 * src attribute is served to browsers that do not take the srcset attribute into account. 190 * When lazy loading, we set the srcset to a transparent image to not download the image in the src 191 * 192 */ 193 public static function getPlaceholder($imgTagWidth = null, $imgTagHeight = null): string 194 { 195 if ($imgTagWidth != null) { 196 $svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 $imgTagWidth $imgTagHeight'></svg>"; 197 /** 198 * We encode it to be able to 199 * use it in a `srcset` attribute that does not 200 * want any space in the image definition 201 */ 202 $svgBase64 = base64_encode($svg); 203 $image = "data:image/svg+xml;base64,$svgBase64"; 204 } else { 205 /** 206 * Base64 transparent gif 207 * 1x1 image, it will produce a square 208 */ 209 $image = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; 210 } 211 return $image; 212 } 213} 214