xref: /plugin/combo/ComboStrap/LazyLoad.php (revision 977ce05d19d8dab0a70c9a27f8da0b7039299e82)
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