xref: /plugin/combo/ComboStrap/LazyLoad.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
137748cd8SNickeau<?php
237748cd8SNickeau/**
337748cd8SNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved.
437748cd8SNickeau *
537748cd8SNickeau * This source code is licensed under the GPL license found in the
637748cd8SNickeau * COPYING  file in the root directory of this source tree.
737748cd8SNickeau *
837748cd8SNickeau * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
937748cd8SNickeau * @author   ComboStrap <support@combostrap.com>
1037748cd8SNickeau *
1137748cd8SNickeau */
1237748cd8SNickeau
1337748cd8SNickeaunamespace ComboStrap;
1437748cd8SNickeau
15*04fd306cSNickeauuse ComboStrap\TagAttribute\BackgroundAttribute;
16*04fd306cSNickeauuse ComboStrap\TagAttribute\StyleAttribute;
17*04fd306cSNickeau
1837748cd8SNickeau/**
1937748cd8SNickeau * This one support background loading
2037748cd8SNickeau * https://github.com/ApoorvSaxena/lozad.js
2137748cd8SNickeau *
2237748cd8SNickeau *
2337748cd8SNickeau * TODO: implement no script pattern ? the https://github.com/aFarkas/lazysizes#the-noscript-pattern
2437748cd8SNickeau *
2537748cd8SNickeau */
2637748cd8SNickeauclass LazyLoad
2737748cd8SNickeau{
2837748cd8SNickeau
2937748cd8SNickeau    const CONF_LAZY_LOADING_PLACEHOLDER_COLOR = "lazyLoadingPlaceholderColor";
3037748cd8SNickeau
3137748cd8SNickeau    /**
3237748cd8SNickeau     * Lozad was choosen because
3337748cd8SNickeau     * it was easier to add svg injection
3437748cd8SNickeau     * it supports background image
3537748cd8SNickeau     * it's most used (JsDelivr stats)
3637748cd8SNickeau     */
3737748cd8SNickeau    const ACTIVE = self::LOZAD_ID;
3837748cd8SNickeau
3937748cd8SNickeau    /**
4037748cd8SNickeau     * The id of the lazy loaders
4137748cd8SNickeau     */
4237748cd8SNickeau    const LAZY_SIDE_ID = "lazy-sizes";
4337748cd8SNickeau    const LOZAD_ID = "lozad";
4437748cd8SNickeau
4537748cd8SNickeau
4637748cd8SNickeau    const CANONICAL = "lazy";
4737748cd8SNickeau    const DEFAULT_COLOR = "#cbf1ea";
4837748cd8SNickeau
49*04fd306cSNickeau    public const LAZY_LOAD_METHOD_HTML_VALUE = "html";
50*04fd306cSNickeau    public const LAZY_LOAD_METHOD_LOZAD_VALUE = "lozad";
51*04fd306cSNickeau
52*04fd306cSNickeau    /**
53*04fd306cSNickeau     * The method on how to lazy load resources (Ie media)
54*04fd306cSNickeau     */
55*04fd306cSNickeau    public const LAZY_LOAD_METHOD = "lazy";
56*04fd306cSNickeau    /**
57*04fd306cSNickeau     * The default when the image are above the fold
58*04fd306cSNickeau     */
59*04fd306cSNickeau    public const LAZY_LOAD_METHOD_NONE_VALUE = "none";
60*04fd306cSNickeau    /**
61*04fd306cSNickeau     * Used internal for now on test
62*04fd306cSNickeau     */
63*04fd306cSNickeau    const CONF_LAZY_LOAD_METHOD = "internal-lazy-load-method-combo";
64*04fd306cSNickeau    public const CONF_RASTER_ENABLE = "rasterImageLazyLoadingEnable";
65*04fd306cSNickeau    public const CONF_RASTER_ENABLE_DEFAULT = 1;
66*04fd306cSNickeau    public const HTML_LOADING_ATTRIBUTE = "loading";
67*04fd306cSNickeau
6837748cd8SNickeau    /**
6937748cd8SNickeau     * Used to select all lazy loaded
7037748cd8SNickeau     * resources and load them before print
7137748cd8SNickeau     */
72*04fd306cSNickeau    public static function getLazyClass(): string
73*04fd306cSNickeau    {
74*04fd306cSNickeau        return StyleAttribute::addComboStrapSuffix(self::CANONICAL);
75*04fd306cSNickeau    }
7637748cd8SNickeau
7737748cd8SNickeau    public static function addSnippet()
7837748cd8SNickeau    {
7937748cd8SNickeau        switch (self::ACTIVE) {
8037748cd8SNickeau            case self::LAZY_SIDE_ID:
8137748cd8SNickeau                LazyLoad::addLazySizesSnippet();
8237748cd8SNickeau                break;
8337748cd8SNickeau            case self::LOZAD_ID:
8437748cd8SNickeau                LazyLoad::addLozadSnippet();
8537748cd8SNickeau                break;
8637748cd8SNickeau            default:
8737748cd8SNickeau                throw new \RuntimeException("The active lazy loaded is unknown (" . self::ACTIVE . ")");
8837748cd8SNickeau        }
8937748cd8SNickeau
9037748cd8SNickeau    }
9137748cd8SNickeau
9237748cd8SNickeau    /**
9337748cd8SNickeau     * Add the lazy sizes snippet
9437748cd8SNickeau     */
9537748cd8SNickeau    private static function addLazySizesSnippet()
9637748cd8SNickeau    {
9737748cd8SNickeau
9837748cd8SNickeau        $snippetManager = PluginUtility::getSnippetManager();
9937748cd8SNickeau
100*04fd306cSNickeau        $snippetManager->attachRemoteJavascriptLibrary(
1014cadd4f8SNickeau            self::LAZY_SIDE_ID,
1024cadd4f8SNickeau            "https://cdn.jsdelivr.net/npm/lazysizes@5.3.1/lazysizes.min.js",
1034cadd4f8SNickeau            "sha256-bmG+LzdKASJRACVXiUC69++Nu8rz7MX1U1z8gb0c/Tk="
10437748cd8SNickeau        );
10537748cd8SNickeau        /**
10637748cd8SNickeau         * The Spinner effect
10737748cd8SNickeau         * lazysizes adds the class lazy loading while the images are loading
10837748cd8SNickeau         * and the class lazyloaded as soon as the image is loaded.
10937748cd8SNickeau         */
110*04fd306cSNickeau        $snippetManager->attachCssInternalStyleSheet(self::LAZY_SIDE_ID);
11137748cd8SNickeau
11237748cd8SNickeau    }
11337748cd8SNickeau
11437748cd8SNickeau    /**
11537748cd8SNickeau     * @param TagAttributes $attributes
11637748cd8SNickeau     */
11737748cd8SNickeau    public static function addPlaceholderBackground(&$attributes)
11837748cd8SNickeau    {
11937748cd8SNickeau        // https://github.com/ApoorvSaxena/lozad.js#large-image-improvment
12037748cd8SNickeau        $placeholderColor = LazyLoad::getPlaceholderColor();
121*04fd306cSNickeau        if ($attributes->hasComponentAttribute(BackgroundAttribute::BACKGROUND_COLOR)) {
122*04fd306cSNickeau            $placeholderColor = $attributes->getValueAndRemove(BackgroundAttribute::BACKGROUND_COLOR);
12337748cd8SNickeau        }
1244cadd4f8SNickeau        $attributes->addOutputAttributeValue("data-placeholder-background", "$placeholderColor");
12537748cd8SNickeau
12637748cd8SNickeau
12737748cd8SNickeau    }
12837748cd8SNickeau
12937748cd8SNickeau    /**
13037748cd8SNickeau     * Add lozad
13137748cd8SNickeau     * Support background image
13237748cd8SNickeau     * https://github.com/ApoorvSaxena/lozad.js
13337748cd8SNickeau     */
13437748cd8SNickeau    public static function addLozadSnippet()
13537748cd8SNickeau    {
13637748cd8SNickeau
13737748cd8SNickeau        $snippetManager = PluginUtility::getSnippetManager();
13837748cd8SNickeau
13937748cd8SNickeau        // https://www.jsdelivr.com/package/npm/lozad
1404cadd4f8SNickeau        $snippetManager
141*04fd306cSNickeau            ->attachRemoteJavascriptLibrary(
1424cadd4f8SNickeau                self::LOZAD_ID,
1434cadd4f8SNickeau                "https://cdn.jsdelivr.net/npm/lozad@1.16.0/dist/lozad.min.js",
1444cadd4f8SNickeau                "sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc="
14537748cd8SNickeau            )
1464cadd4f8SNickeau            ->setDoesManipulateTheDomOnRun(false);
14737748cd8SNickeau
14837748cd8SNickeau        /**
14937748cd8SNickeau         * Add the fading effect
15037748cd8SNickeau         */
15137748cd8SNickeau        $snippetId = "lazy-load-fade";
152*04fd306cSNickeau        $snippetManager->attachCssInternalStyleSheet($snippetId);
15337748cd8SNickeau
15437748cd8SNickeau
15537748cd8SNickeau        /**
15637748cd8SNickeau         * Snippet to download the image before print
15737748cd8SNickeau         *
15837748cd8SNickeau         * The others javascript snippet to download lazy load depend on the image type
15937748cd8SNickeau         * and features and was therefore added in the code for svg or raster
16037748cd8SNickeau         */
161*04fd306cSNickeau        $snippetManager->attachJavascriptFromComponentId("lozad-print");
16237748cd8SNickeau
16337748cd8SNickeau
16437748cd8SNickeau    }
16537748cd8SNickeau
16637748cd8SNickeau    /**
16737748cd8SNickeau     * Class selector to identify the element to lazy load
16837748cd8SNickeau     */
16937748cd8SNickeau    public static function getClass()
17037748cd8SNickeau    {
17137748cd8SNickeau        switch (self::ACTIVE) {
17237748cd8SNickeau            case self::LAZY_SIDE_ID:
17337748cd8SNickeau                return "lazyload";
17437748cd8SNickeau            case self::LOZAD_ID:
17537748cd8SNickeau                return "lozad";
17637748cd8SNickeau            default:
17737748cd8SNickeau                throw new \RuntimeException("The active lazy loaded is unknown (" . self::ACTIVE . ")");
17837748cd8SNickeau        }
17937748cd8SNickeau    }
18037748cd8SNickeau
18137748cd8SNickeau    /**
18237748cd8SNickeau     * @return string - the lazy loading placeholder color
18337748cd8SNickeau     */
18437748cd8SNickeau    public static function getPlaceholderColor()
18537748cd8SNickeau    {
186*04fd306cSNickeau        return SiteConfig::getConfValue(self::CONF_LAZY_LOADING_PLACEHOLDER_COLOR, self::DEFAULT_COLOR);
18737748cd8SNickeau    }
18837748cd8SNickeau
18937748cd8SNickeau    /**
19037748cd8SNickeau     * The placeholder is not mandatory
1911fa8c418SNickeau     * but if present, it should have the same target ratio of the image
19237748cd8SNickeau     *
19337748cd8SNickeau     * This function is documenting this fact.
19437748cd8SNickeau     *
19537748cd8SNickeau     * @param null $imgTagWidth
19637748cd8SNickeau     * @param null $imgTagHeight
19737748cd8SNickeau     * @return string
19837748cd8SNickeau     *
19937748cd8SNickeau     *
20037748cd8SNickeau     * Src is always set, this is the default
20137748cd8SNickeau     * src attribute is served to browsers that do not take the srcset attribute into account.
20237748cd8SNickeau     * When lazy loading, we set the srcset to a transparent image to not download the image in the src
20337748cd8SNickeau     *
20437748cd8SNickeau     */
2051fa8c418SNickeau    public static function getPlaceholder($imgTagWidth = null, $imgTagHeight = null): string
20637748cd8SNickeau    {
20737748cd8SNickeau        if ($imgTagWidth != null) {
20837748cd8SNickeau            $svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 $imgTagWidth $imgTagHeight'></svg>";
20937748cd8SNickeau            /**
21037748cd8SNickeau             * We encode it to be able to
21137748cd8SNickeau             * use it in a `srcset` attribute that does not
21237748cd8SNickeau             * want any space in the image definition
21337748cd8SNickeau             */
21437748cd8SNickeau            $svgBase64 = base64_encode($svg);
21537748cd8SNickeau            $image = "data:image/svg+xml;base64,$svgBase64";
21637748cd8SNickeau        } else {
21737748cd8SNickeau            /**
21837748cd8SNickeau             * Base64 transparent gif
21937748cd8SNickeau             * 1x1 image, it will produce a square
22037748cd8SNickeau             */
22137748cd8SNickeau            $image = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
22237748cd8SNickeau        }
22337748cd8SNickeau        return $image;
22437748cd8SNickeau    }
225*04fd306cSNickeau
226*04fd306cSNickeau    /**
227*04fd306cSNickeau     * @return void
228*04fd306cSNickeau     * @deprecated use {@link SiteConfig::disableLazyLoad()}
229*04fd306cSNickeau     */
230*04fd306cSNickeau    public static function disable()
231*04fd306cSNickeau    {
232*04fd306cSNickeau        ExecutionContext::getActualOrCreateFromEnv()
233*04fd306cSNickeau            ->getConfig()
234*04fd306cSNickeau            ->disableLazyLoad();
235*04fd306cSNickeau    }
236*04fd306cSNickeau
237*04fd306cSNickeau    /**
238*04fd306cSNickeau     *
239*04fd306cSNickeau     * By default, the image above the fold should not be lazy loaded
240*04fd306cSNickeau     * Above-the-fold images that are lazily loaded render later in the page lifecycle, which can delay the largest contentful paint.
241*04fd306cSNickeau     *
242*04fd306cSNickeau     *
243*04fd306cSNickeau     */
244*04fd306cSNickeau    public static function getDefault()
245*04fd306cSNickeau    {
246*04fd306cSNickeau
247*04fd306cSNickeau        try {
248*04fd306cSNickeau            /**
249*04fd306cSNickeau             * Above-the-fold images that are lazily loaded render later in the page lifecycle,
250*04fd306cSNickeau             * which can delay the largest contentful paint.
251*04fd306cSNickeau             */
252*04fd306cSNickeau            $sourcePath = ExecutionContext::getActualOrCreateFromEnv()
253*04fd306cSNickeau                ->getExecutingMarkupHandler()
254*04fd306cSNickeau                ->getSourcePath();
255*04fd306cSNickeau            if(SlotSystem::isMainHeaderSlot($sourcePath)){
256*04fd306cSNickeau                return LazyLoad::LAZY_LOAD_METHOD_NONE_VALUE;
257*04fd306cSNickeau            }
258*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
259*04fd306cSNickeau            // not a path execution
260*04fd306cSNickeau        }
261*04fd306cSNickeau
262*04fd306cSNickeau        /**
263*04fd306cSNickeau         * HTML and not lozad as default because in a Hbs template, in a {@link TemplateForWebPage},
264*04fd306cSNickeau         * it would not work as the script would not be added
265*04fd306cSNickeau         */
266*04fd306cSNickeau        return ExecutionContext::getActualOrCreateFromEnv()
267*04fd306cSNickeau            ->getConfig()
268*04fd306cSNickeau            ->getValue(LazyLoad::CONF_LAZY_LOAD_METHOD,LazyLoad::LAZY_LOAD_METHOD_HTML_VALUE) ;
269*04fd306cSNickeau    }
270*04fd306cSNickeau
27137748cd8SNickeau}
272