1<?php
2
3namespace ComboStrap\Tag;
4
5use ComboStrap\CallStack;
6use ComboStrap\ColorRgb;
7use ComboStrap\Dimension;
8use ComboStrap\FetcherSvg;
9use ComboStrap\IFetcherAbs;
10use ComboStrap\LinkMarkup;
11use ComboStrap\MarkupRef;
12use ComboStrap\MediaMarkup;
13use ComboStrap\PluginUtility;
14use ComboStrap\Position;
15use ComboStrap\TagAttribute\BackgroundAttribute;
16use ComboStrap\TagAttributes;
17use Doku_Renderer_metadata;
18use syntax_plugin_combo_media;
19
20
21/**
22 * The {@link BackgroundTag background tag} does not render as HTML tag
23 * but collects data to create a {@link BackgroundAttribute}
24 * on the parent node
25 *
26 * Implementation of a background
27 *
28 *
29 * Cool calm example of moving square background
30 * https://codepen.io/Lewitje/pen/BNNJjo
31 * Particles.js
32 * https://codepen.io/akey96/pen/oNgeQYX
33 * Gradient positioning above a photo
34 * https://codepen.io/uzoawili/pen/GypGOy
35 * Fire flies
36 * https://codepen.io/mikegolus/pen/Jegvym
37 *
38 * z-index:100 could also be on the front
39 * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_without_z-index
40 * https://getbootstrap.com/docs/5.0/layout/z-index/
41 */
42class BackgroundTag
43{
44
45    public const MARKUP_LONG = "background";
46    public const MARKUP_SHORT = "bg";
47    const LOGICAL_TAG = self::MARKUP_LONG;
48
49    /**
50     * Function used in the special and enter tag
51     * @param TagAttributes $attributes
52     */
53    public static function handleEnterAndSpecial(TagAttributes $attributes)
54    {
55
56        $color = $attributes->getValueAndRemoveIfPresent(ColorRgb::COLOR);
57        if ($color !== null) {
58            $attributes->addComponentAttributeValue(BackgroundAttribute::BACKGROUND_COLOR, $color);
59        }
60
61    }
62
63    /**
64     * @param CallStack $callStack
65     * @param TagAttributes $backgroundAttributes
66     * @param $state
67     * @return array
68     */
69    public static function setAttributesToParentAndReturnData(CallStack $callStack, TagAttributes $backgroundAttributes, $state): array
70    {
71
72        /**
73         * The data array
74         */
75        $data = array();
76
77        /**
78         * Set the backgrounds attributes
79         * to the parent
80         * There is two state (special and exit)
81         * Go to the opening call if in exit
82         */
83        if ($state == DOKU_LEXER_EXIT) {
84            $callStack->moveToEnd();
85           $openingCall =  $callStack->moveToPreviousCorrespondingOpeningCall();
86        }
87        $parentCall = $callStack->moveToParent();
88
89        /** @noinspection PhpPointlessBooleanExpressionInConditionInspection */
90        if ($parentCall != false) {
91            if ($parentCall->getTagName() == BackgroundAttribute::BACKGROUNDS) {
92                /**
93                 * The backgrounds node
94                 * (is already relative)
95                 */
96                $parentCall = $callStack->moveToParent();
97            } else {
98                /**
99                 * Another parent node
100                 * With a image background, the node should be relative
101                 */
102                if ($backgroundAttributes->hasComponentAttribute(BackgroundAttribute::BACKGROUND_IMAGE)) {
103                    $parentCall->addAttribute(Position::POSITION_ATTRIBUTE, "relative");
104                }
105            }
106            $backgrounds = $parentCall->getAttribute(BackgroundAttribute::BACKGROUNDS);
107            if ($backgrounds == null) {
108                $backgrounds = [$backgroundAttributes->toCallStackArray()];
109            } else {
110                $backgrounds[] = $backgroundAttributes->toCallStackArray();
111            }
112            $parentCall->addAttribute(BackgroundAttribute::BACKGROUNDS, $backgrounds);
113
114        } else {
115            $data[PluginUtility::EXIT_MESSAGE] = "A background should have a parent";
116        }
117
118        /**
119         * Return the image data for the metadata
120         * (Metadat is taken only from enter/exit)
121         */
122        if ($state === DOKU_LEXER_EXIT && isset($openingCall)) {
123            // exit state
124            $backgroundImage = $backgroundAttributes->getComponentAttributeValue(BackgroundAttribute::BACKGROUND_IMAGE);
125            $openingCall->setAttribute(BackgroundAttribute::BACKGROUND_IMAGE, $backgroundImage);
126        } else {
127            // special state
128            $data[PluginUtility::ATTRIBUTES] = $backgroundAttributes->toCallStackArray();
129        }
130        return $data;
131
132    }
133
134    /**
135     * Print only any error
136     */
137    public static function renderExitSpecialHtml($data): string
138    {
139
140        if (isset($data[PluginUtility::EXIT_MESSAGE])) {
141            $class = LinkMarkup::TEXT_ERROR_CLASS;
142            $error = $data[PluginUtility::EXIT_MESSAGE];
143            return "<p class=\"$class\">$error</p>" . DOKU_LF;
144        }
145
146        return "";
147    }
148
149    public static function handleExit($handler): array
150    {
151        $callStack = CallStack::createFromHandler($handler);
152        $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall();
153        $backgroundAttributes = TagAttributes::createFromCallStackArray($openingTag->getAttributes())
154            ->setLogicalTag(BackgroundTag::LOGICAL_TAG);
155
156        /**
157         * if the media syntax of Combo is not used, try to retrieve the media of dokuwiki
158         */
159        $imageTag = [syntax_plugin_combo_media::TAG, MediaMarkup::INTERNAL_MEDIA_CALL_NAME];
160
161        /**
162         * Collect the image if any
163         */
164        while ($actual = $callStack->next()) {
165
166            $tagName = $actual->getTagName();
167            if (in_array($tagName, $imageTag)) {
168                $imageAttribute = $actual->getAttributes();
169                if ($tagName == syntax_plugin_combo_media::TAG) {
170                    $backgroundImageAttribute = BackgroundAttribute::fromMediaToBackgroundImageStackArray($imageAttribute);
171
172                    /**
173                     * Hack for tile svg
174                     */
175                    $fill = $openingTag->getAttribute(BackgroundAttribute::BACKGROUND_FILL);
176                    if ($fill === FetcherSvg::TILE_TYPE) {
177                        $ref = $backgroundImageAttribute[MarkupRef::REF_ATTRIBUTE];
178                        if (!str_contains($ref, TagAttributes::TYPE_KEY) && str_contains($ref, "svg")) {
179                            if (str_contains($ref, "?")) {
180                                $ref = "$ref&type=$fill";
181                            } else {
182                                $ref = "$ref?type=$fill";
183                            }
184                        }
185                        $backgroundImageAttribute[MarkupRef::REF_ATTRIBUTE] = $ref;
186                    }
187                } else {
188                    /**
189                     * As seen in {@link Doku_Handler::media()}
190                     */
191                    $backgroundImageAttribute = [
192                        MediaMarkup::MEDIA_DOKUWIKI_TYPE => MediaMarkup::INTERNAL_MEDIA_CALL_NAME,
193                        MediaMarkup::DOKUWIKI_SRC => $imageAttribute[0],
194                        Dimension::WIDTH_KEY => $imageAttribute[3],
195                        Dimension::HEIGHT_KEY => $imageAttribute[4],
196                        IFetcherAbs::CACHE_KEY => $imageAttribute[5]
197                    ];
198                }
199                $backgroundAttributes->addComponentAttributeValue(BackgroundAttribute::BACKGROUND_IMAGE, $backgroundImageAttribute);
200                $callStack->deleteActualCallAndPrevious();
201            }
202        }
203        return BackgroundTag::setAttributesToParentAndReturnData($callStack, $backgroundAttributes, DOKU_LEXER_EXIT);
204    }
205
206    public static function renderEnterTag(): string
207    {
208        /**
209         * background is printed via the {@link BackgroundAttribute::processBackgroundAttributes()}
210         */
211        return "";
212    }
213
214    public static function renderMeta(array $data, Doku_Renderer_metadata $renderer)
215    {
216
217        $attributes = $data[PluginUtility::ATTRIBUTES];
218        if (isset($attributes[BackgroundAttribute::BACKGROUND_IMAGE])) {
219            $image = $attributes[BackgroundAttribute::BACKGROUND_IMAGE];
220            syntax_plugin_combo_media::registerImageMeta($image, $renderer);
221        }
222
223    }
224}
225