xref: /plugin/combo/ComboStrap/LazyLoad.php (revision 4cadd4f8c541149bdda95f080e38a6d4e3a640ca)
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
1537748cd8SNickeau/**
1637748cd8SNickeau * This one support background loading
1737748cd8SNickeau * https://github.com/ApoorvSaxena/lozad.js
1837748cd8SNickeau *
1937748cd8SNickeau *
2037748cd8SNickeau * TODO: implement no script pattern ? the https://github.com/aFarkas/lazysizes#the-noscript-pattern
2137748cd8SNickeau *
2237748cd8SNickeau */
2337748cd8SNickeauclass LazyLoad
2437748cd8SNickeau{
2537748cd8SNickeau
2637748cd8SNickeau    const CONF_LAZY_LOADING_PLACEHOLDER_COLOR = "lazyLoadingPlaceholderColor";
2737748cd8SNickeau
2837748cd8SNickeau    /**
2937748cd8SNickeau     * Lozad was choosen because
3037748cd8SNickeau     * it was easier to add svg injection
3137748cd8SNickeau     * it supports background image
3237748cd8SNickeau     * it's most used (JsDelivr stats)
3337748cd8SNickeau     */
3437748cd8SNickeau    const ACTIVE = self::LOZAD_ID;
3537748cd8SNickeau
3637748cd8SNickeau    /**
3737748cd8SNickeau     * The id of the lazy loaders
3837748cd8SNickeau     */
3937748cd8SNickeau    const LAZY_SIDE_ID = "lazy-sizes";
4037748cd8SNickeau    const LOZAD_ID = "lozad";
4137748cd8SNickeau
4237748cd8SNickeau
4337748cd8SNickeau    const CANONICAL = "lazy";
4437748cd8SNickeau    const DEFAULT_COLOR = "#cbf1ea";
4537748cd8SNickeau
4637748cd8SNickeau    /**
4737748cd8SNickeau     * Used to select all lazy loaded
4837748cd8SNickeau     * resources and load them before print
4937748cd8SNickeau     */
5037748cd8SNickeau    const LAZY_CLASS = "lazy-combo";
5137748cd8SNickeau
5237748cd8SNickeau
5337748cd8SNickeau    public static function addSnippet()
5437748cd8SNickeau    {
5537748cd8SNickeau        switch (self::ACTIVE) {
5637748cd8SNickeau            case self::LAZY_SIDE_ID:
5737748cd8SNickeau                LazyLoad::addLazySizesSnippet();
5837748cd8SNickeau                break;
5937748cd8SNickeau            case self::LOZAD_ID:
6037748cd8SNickeau                LazyLoad::addLozadSnippet();
6137748cd8SNickeau                break;
6237748cd8SNickeau            default:
6337748cd8SNickeau                throw new \RuntimeException("The active lazy loaded is unknown (" . self::ACTIVE . ")");
6437748cd8SNickeau        }
6537748cd8SNickeau
6637748cd8SNickeau    }
6737748cd8SNickeau
6837748cd8SNickeau    /**
6937748cd8SNickeau     * Add the lazy sizes snippet
7037748cd8SNickeau     */
7137748cd8SNickeau    private static function addLazySizesSnippet()
7237748cd8SNickeau    {
7337748cd8SNickeau
7437748cd8SNickeau        $snippetManager = PluginUtility::getSnippetManager();
7537748cd8SNickeau
76*4cadd4f8SNickeau        $snippetManager->attachJavascriptLibraryForSlot(
77*4cadd4f8SNickeau            self::LAZY_SIDE_ID,
78*4cadd4f8SNickeau            "https://cdn.jsdelivr.net/npm/lazysizes@5.3.1/lazysizes.min.js",
79*4cadd4f8SNickeau            "sha256-bmG+LzdKASJRACVXiUC69++Nu8rz7MX1U1z8gb0c/Tk="
8037748cd8SNickeau        );
8137748cd8SNickeau        /**
8237748cd8SNickeau         * The Spinner effect
8337748cd8SNickeau         * lazysizes adds the class lazy loading while the images are loading
8437748cd8SNickeau         * and the class lazyloaded as soon as the image is loaded.
8537748cd8SNickeau         */
86*4cadd4f8SNickeau        $snippetManager->attachCssInternalStyleSheetForSlot(self::LAZY_SIDE_ID);
8737748cd8SNickeau
8837748cd8SNickeau    }
8937748cd8SNickeau
9037748cd8SNickeau    /**
9137748cd8SNickeau     * @param TagAttributes $attributes
9237748cd8SNickeau     */
9337748cd8SNickeau    public static function addPlaceholderBackground(&$attributes)
9437748cd8SNickeau    {
9537748cd8SNickeau        // https://github.com/ApoorvSaxena/lozad.js#large-image-improvment
9637748cd8SNickeau        $placeholderColor = LazyLoad::getPlaceholderColor();
9737748cd8SNickeau        if ($attributes->hasComponentAttribute(Background::BACKGROUND_COLOR)) {
9837748cd8SNickeau            $placeholderColor = $attributes->getValueAndRemove(Background::BACKGROUND_COLOR);
9937748cd8SNickeau        }
100*4cadd4f8SNickeau        $attributes->addOutputAttributeValue("data-placeholder-background", "$placeholderColor");
10137748cd8SNickeau
10237748cd8SNickeau
10337748cd8SNickeau    }
10437748cd8SNickeau
10537748cd8SNickeau    /**
10637748cd8SNickeau     * Add lozad
10737748cd8SNickeau     * Support background image
10837748cd8SNickeau     * https://github.com/ApoorvSaxena/lozad.js
10937748cd8SNickeau     */
11037748cd8SNickeau    public static function addLozadSnippet()
11137748cd8SNickeau    {
11237748cd8SNickeau
11337748cd8SNickeau        $snippetManager = PluginUtility::getSnippetManager();
11437748cd8SNickeau
11537748cd8SNickeau        // https://www.jsdelivr.com/package/npm/lozad
116*4cadd4f8SNickeau        $snippetManager
117*4cadd4f8SNickeau            ->attachJavascriptLibraryForSlot(
118*4cadd4f8SNickeau                self::LOZAD_ID,
119*4cadd4f8SNickeau                "https://cdn.jsdelivr.net/npm/lozad@1.16.0/dist/lozad.min.js",
120*4cadd4f8SNickeau                "sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc="
12137748cd8SNickeau            )
122*4cadd4f8SNickeau            ->setDoesManipulateTheDomOnRun(false);
12337748cd8SNickeau
12437748cd8SNickeau        /**
12537748cd8SNickeau         * Add the fading effect
12637748cd8SNickeau         */
12737748cd8SNickeau        $snippetId = "lazy-load-fade";
128*4cadd4f8SNickeau        $snippetManager->attachCssInternalStyleSheetForSlot($snippetId);
12937748cd8SNickeau
13037748cd8SNickeau
13137748cd8SNickeau        /**
13237748cd8SNickeau         * Snippet to download the image before print
13337748cd8SNickeau         *
13437748cd8SNickeau         * The others javascript snippet to download lazy load depend on the image type
13537748cd8SNickeau         * and features and was therefore added in the code for svg or raster
13637748cd8SNickeau         */
137*4cadd4f8SNickeau        $snippetManager->attachInternalJavascriptForSlot("lozad-print");
13837748cd8SNickeau
13937748cd8SNickeau
14037748cd8SNickeau    }
14137748cd8SNickeau
14237748cd8SNickeau    /**
14337748cd8SNickeau     * Class selector to identify the element to lazy load
14437748cd8SNickeau     */
14537748cd8SNickeau    public static function getClass()
14637748cd8SNickeau    {
14737748cd8SNickeau        switch (self::ACTIVE) {
14837748cd8SNickeau            case self::LAZY_SIDE_ID:
14937748cd8SNickeau                return "lazyload";
15037748cd8SNickeau            case self::LOZAD_ID:
15137748cd8SNickeau                return "lozad";
15237748cd8SNickeau            default:
15337748cd8SNickeau                throw new \RuntimeException("The active lazy loaded is unknown (" . self::ACTIVE . ")");
15437748cd8SNickeau        }
15537748cd8SNickeau    }
15637748cd8SNickeau
15737748cd8SNickeau    /**
15837748cd8SNickeau     * @return string - the lazy loading placeholder color
15937748cd8SNickeau     */
16037748cd8SNickeau    public static function getPlaceholderColor()
16137748cd8SNickeau    {
16237748cd8SNickeau        return PluginUtility::getConfValue(self::CONF_LAZY_LOADING_PLACEHOLDER_COLOR, self::DEFAULT_COLOR);
16337748cd8SNickeau    }
16437748cd8SNickeau
16537748cd8SNickeau    /**
16637748cd8SNickeau     * The placeholder is not mandatory
1671fa8c418SNickeau     * but if present, it should have the same target ratio of the image
16837748cd8SNickeau     *
16937748cd8SNickeau     * This function is documenting this fact.
17037748cd8SNickeau     *
17137748cd8SNickeau     * @param null $imgTagWidth
17237748cd8SNickeau     * @param null $imgTagHeight
17337748cd8SNickeau     * @return string
17437748cd8SNickeau     *
17537748cd8SNickeau     *
17637748cd8SNickeau     * Src is always set, this is the default
17737748cd8SNickeau     * src attribute is served to browsers that do not take the srcset attribute into account.
17837748cd8SNickeau     * When lazy loading, we set the srcset to a transparent image to not download the image in the src
17937748cd8SNickeau     *
18037748cd8SNickeau     */
1811fa8c418SNickeau    public static function getPlaceholder($imgTagWidth = null, $imgTagHeight = null): string
18237748cd8SNickeau    {
18337748cd8SNickeau        if ($imgTagWidth != null) {
18437748cd8SNickeau            $svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 $imgTagWidth $imgTagHeight'></svg>";
18537748cd8SNickeau            /**
18637748cd8SNickeau             * We encode it to be able to
18737748cd8SNickeau             * use it in a `srcset` attribute that does not
18837748cd8SNickeau             * want any space in the image definition
18937748cd8SNickeau             */
19037748cd8SNickeau            $svgBase64 = base64_encode($svg);
19137748cd8SNickeau            $image = "data:image/svg+xml;base64,$svgBase64";
19237748cd8SNickeau        } else {
19337748cd8SNickeau            /**
19437748cd8SNickeau             * Base64 transparent gif
19537748cd8SNickeau             * 1x1 image, it will produce a square
19637748cd8SNickeau             */
19737748cd8SNickeau            $image = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
19837748cd8SNickeau        }
19937748cd8SNickeau        return $image;
20037748cd8SNickeau    }
20137748cd8SNickeau}
202