xref: /template/strap/ComboStrap/LazyLoad.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
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