1<?php
2
3namespace ComboStrap;
4
5
6use syntax_plugin_combo_fragment;
7
8/**
9 * Carrousel
10 *
11 * We loved
12 * https://github.com/OwlCarousel2/OwlCarousel2
13 * but it's deprecated and
14 * send us to
15 * https://github.com/ganlanyuan/tiny-slider
16 * But it used as gutter the padding not the margin (http://ganlanyuan.github.io/tiny-slider/demo/#gutter_wrapper)
17 * Then we found
18 * https://glidejs.com/
19 *
20 * If we need another,
21 * * https://swiperjs.com/ - <a href="https://themes.getbootstrap.com/preview/?theme_id=5348">purpose template</a>
22 * * https://github.com/ganlanyuan/tiny-slider - https://themes.getbootstrap.com/preview/?theme_id=92520 - blogzine
23 *
24 *
25 */
26class CarrouselTag
27{
28    public const ELEMENT_WIDTH_ATTRIBUTE = "element-width";
29    /**
30     * To center the image inside a link in a carrousel
31     */
32    public const MEDIA_CENTER_LINK_CLASS = "justify-content-center align-items-center d-flex";
33    public const CONTROL_ATTRIBUTE = "control";
34    public const CANONICAL = CarrouselTag::TAG;
35    public const ELEMENTS_MIN_ATTRIBUTE = "elements-min";
36    public const ELEMENTS_MIN_DEFAULT = 3;
37    public const GLIDE_SLIDE_CLASS = "glide__slide";
38    public const TAG = 'carrousel';
39
40
41    /**
42     * Glide copy the HTML element and lozad does not see element that are not visible
43     * The element non-visible are not processed by lozad
44     * We set lazy loading to HTML loading attribute
45     */
46    public static function setLazyLoadToHtmlOnImageTagUntilTheEndOfTheStack(CallStack $callStack)
47    {
48        while ($actualCall = $callStack->next()) {
49            if ($actualCall->getState() === DOKU_LEXER_SPECIAL && in_array($actualCall->getTagName(), Call::IMAGE_TAGS)) {
50                $actualCall->addAttribute(
51                    LazyLoad::LAZY_LOAD_METHOD,
52                    LazyLoad::LAZY_LOAD_METHOD_HTML_VALUE
53                );
54            }
55        }
56    }
57
58    public static function handleEnter(\Doku_Handler $handler): array
59    {
60        $callStack = CallStack::createFromHandler($handler);
61        $parent = $callStack->moveToParent();
62        $context = null;
63        if ($parent !== false) {
64            $context = $parent->getTagName();
65        }
66        return array(PluginUtility::CONTEXT => $context);
67    }
68
69    public static function handleExit(\Doku_Handler $handler): array
70    {
71        $callStack = CallStack::createFromHandler($handler);
72        $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall();
73        $actualCall = $callStack->moveToFirstChildTag();
74        if ($actualCall !== false) {
75            if ($actualCall->getTagName() === FragmentTag::FRAGMENT_TAG) {
76                $templateEndCall = $callStack->moveToNextCorrespondingExitTag();
77                $templateCallStackInstructions = $templateEndCall->getPluginData(FragmentTag::CALLSTACK);
78                if ($templateCallStackInstructions !== null) {
79                    $templateCallStack = CallStack::createFromInstructions($templateCallStackInstructions);
80                    // Lazy load
81                    $templateCallStack->moveToStart();
82                    CarrouselTag::setLazyLoadToHtmlOnImageTagUntilTheEndOfTheStack($templateCallStack);
83                    $templateEndCall->setPluginData(FragmentTag::CALLSTACK, $templateCallStack->getStack());
84                }
85            } else {
86                // Lazy load
87                $callStack->moveToEnd();
88                $callStack->moveToPreviousCorrespondingOpeningCall();
89                CarrouselTag::setLazyLoadToHtmlOnImageTagUntilTheEndOfTheStack($callStack);
90            }
91        }
92        return array(PluginUtility::ATTRIBUTES => $openingCall->getAttributes());
93    }
94
95    public static function renderEnterXhtml(TagAttributes $tagAttributes, array $data): string
96    {
97        /**
98         * Control
99         */
100        $control = $tagAttributes->getValueAndRemoveIfPresent(CarrouselTag::CONTROL_ATTRIBUTE);
101        if ($control !== null) {
102            $tagAttributes->addOutputAttributeValue("data-" . CarrouselTag::CONTROL_ATTRIBUTE, $control);
103        }
104
105        /**
106         * Element Min
107         */
108        $elementsMin = $tagAttributes->getValueAndRemoveIfPresent(CarrouselTag::ELEMENTS_MIN_ATTRIBUTE, CarrouselTag::ELEMENTS_MIN_DEFAULT);
109        $tagAttributes->addOutputAttributeValue("data-" . CarrouselTag::ELEMENTS_MIN_ATTRIBUTE, $elementsMin);
110
111        /**
112         * Minimal Width
113         */
114        $slideMinimalWidth = $tagAttributes->getValueAndRemoveIfPresent(CarrouselTag::ELEMENT_WIDTH_ATTRIBUTE);
115        if ($slideMinimalWidth !== null) {
116            try {
117                $slideMinimalWidth = ConditionalLength::createFromString($slideMinimalWidth)->toPixelNumber();
118                $tagAttributes->addOutputAttributeValue("data-" . CarrouselTag::ELEMENT_WIDTH_ATTRIBUTE, $slideMinimalWidth);
119            } catch (ExceptionCompile $e) {
120                LogUtility::msg("The minimal width value ($slideMinimalWidth) is not a valid value. Error: {$e->getMessage()}");
121            }
122        }
123
124
125        /**
126         * Snippets
127         */
128        $snippetSystem = ExecutionContext::getActualOrCreateFromEnv()
129            ->getSnippetSystem();
130
131
132        $snippetId = CarrouselTag::TAG;
133
134        // Theme customized from the below official theme
135        // https://cdn.jsdelivr.net/npm/@glidejs/glide@3.5.2/dist/css/glide.theme.css
136        $snippetSystem->attachCssInternalStyleSheet($snippetId)->setCritical(false);
137
138        /**
139         * The dependency first
140         */
141        $snippetSystem->attachJavascriptFromComponentId("combo-loader");
142        $snippetSystem->attachJavascriptFromComponentId($snippetId);
143
144        /**
145         * Return
146         */
147        return $tagAttributes->toHtmlEnterTag("div");
148    }
149
150    public static function renderExitXhtml(): string
151    {
152        return '</div>';
153    }
154}
155
156