xref: /plugin/combo/ComboStrap/LazyLoad.php (revision 1fa8c418ed5809db58049141be41b7738471dd32)
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
194    public static function getPlaceholder($imgTagWidth = null, $imgTagHeight = null): string
195    {
196        if ($imgTagWidth != null) {
197            $svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 $imgTagWidth $imgTagHeight'></svg>";
198            /**
199             * We encode it to be able to
200             * use it in a `srcset` attribute that does not
201             * want any space in the image definition
202             */
203            $svgBase64 = base64_encode($svg);
204            $image = "data:image/svg+xml;base64,$svgBase64";
205        } else {
206            /**
207             * Base64 transparent gif
208             * 1x1 image, it will produce a square
209             */
210            $image = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
211        }
212        return $image;
213    }
214}
215