1<?php
2
3namespace ComboStrap;
4
5
6use ComboStrap\Tag\AdTag;
7use ComboStrap\Tag\BackgroundTag;
8use ComboStrap\Tag\BarTag;
9use ComboStrap\Tag\BoxTag;
10use ComboStrap\Tag\FollowTag;
11use ComboStrap\Tag\MermaidTag;
12use ComboStrap\Tag\RelatedTag;
13use ComboStrap\Tag\ShareTag;
14use ComboStrap\Tag\SubscribeTag;
15use ComboStrap\Tag\TableTag;
16use ComboStrap\Tag\WebCodeTag;
17use ComboStrap\TagAttribute\Hero;
18use Doku_Handler;
19use Doku_Renderer;
20use Doku_Renderer_metadata;
21use Doku_Renderer_xhtml;
22use DokuWiki_Syntax_Plugin;
23use Mpdf\Tag\Sub;
24use renderer_plugin_combo_analytics;
25use renderer_plugin_combo_xml;
26use syntax_plugin_combo_code;
27
28
29class XmlTagProcessing
30{
31
32
33    /**
34     * The start tag pattern does not allow > or /
35     * in the data to not compete with the empty tag pattern (ie <empty/>
36     *
37     * No numbers to not have a greater than `<1200` as tag
38     */
39    public const START_TAG_PATTERN = '<[A-Za-z-]+[^/><]*>';
40
41
42    public static function renderStaticExitXhtml(TagAttributes $tagAttributes, Doku_Renderer_xhtml $renderer, array $data, DokuWiki_Syntax_Plugin $plugin): bool
43    {
44        $logicalTag = $tagAttributes->getLogicalTag();
45        switch ($logicalTag) {
46            case BlockquoteTag::TAG:
47                BlockquoteTag::renderExitXhtml($tagAttributes, $renderer, $data);
48                return true;
49            case BoxTag::TAG:
50                $renderer->doc .= BoxTag::renderExitXhtml($tagAttributes);
51                return true;
52            case ButtonTag::LOGICAL_TAG:
53                $renderer->doc .= ButtonTag::renderExitXhtml($data);
54                return true;
55            case CardTag::LOGICAL_TAG:
56                CardTag::handleExitXhtml($data, $renderer);
57                return true;
58            case BarTag::LOGICAL_TAG:
59                $renderer->doc .= BarTag::renderExitXhtml($data);
60                return true;
61            case CarrouselTag::TAG:
62                $renderer->doc .= CarrouselTag::renderExitXhtml();
63                return true;
64            case PrismTags::CONSOLE_TAG:
65            case PrismTags::FILE_TAG:
66                PrismTags::processExitXhtml($tagAttributes, $renderer);
67                return true;
68            case ContainerTag::TAG:
69                $renderer->doc .= ContainerTag::renderExitXhtml();
70                return true;
71            case GridTag::LOGICAL_TAG:
72                $renderer->doc .= GridTag::renderExitXhtml($tagAttributes);
73                return true;
74            case PipelineTag::TAG:
75            case DateTag::TAG:
76            case PageExplorerTag::LOGICAL_TAG:
77            case PermalinkTag::TAG:
78            case IconTag::TAG:
79            case MermaidTag::LOGICAL_TAG:
80                return true;
81            case DropDownTag::TAG:
82                $renderer->doc .= DropDownTag::renderExitXhtml();
83                return true;
84            case HeadingTag::LOGICAL_TAG:
85                $context = $data[PluginUtility::CONTEXT];
86                $renderer->doc .= HeadingTag::renderClosingTag($tagAttributes, $context);
87                return true;
88            case NoteTag::TAG_INOTE:
89                $renderer->doc .= NoteTag::renderClosingInlineNote();
90                return true;
91            case JumbotronTag::TAG:
92                $renderer->doc .= JumbotronTag::renderExitHtml();
93                return true;
94            case MasonryTag::LOGICAL_TAG:
95                $renderer->doc .= MasonryTag::renderExitHtml();
96                return true;
97            case SectionTag::TAG:
98                $renderer->doc .= SectionTag::renderExitXhtml();
99                return true;
100            case TabsTag::TAG:
101                $renderer->doc .= TabsTag::renderExitXhtml($tagAttributes, $data);
102                return true;
103            case PanelTag::PANEL_LOGICAL_MARKUP:
104                $renderer->doc .= PanelTag::renderExitXhtml($data);
105                return true;
106            case BackgroundTag::LOGICAL_TAG:
107                $renderer->doc .= BackgroundTag::renderExitSpecialHtml($data);
108                return true;
109            case WebCodeTag::TAG:
110                $renderer->doc .= WebCodeTag::renderExit($tagAttributes, $data);
111                return true;
112            case ShareTag::MARKUP:
113                $renderer->doc .= ShareTag::renderExit($tagAttributes);
114                return true;
115            case FollowTag::MARKUP:
116                $renderer->doc .= FollowTag::renderExit();
117                return true;
118            default:
119                LogUtility::warning("The exit tag (" . $logicalTag . ") was not processed.");
120                return false;
121        }
122
123    }
124
125    /**
126     * Static because it handle inline and block tag
127     * @param string $match
128     * @param int $state
129     * @param int $pos
130     * @param Doku_Handler $handler
131     * @param DokuWiki_Syntax_Plugin $plugin
132     * @return array
133     */
134    public static function handleStatic(string $match, int $state, int $pos, Doku_Handler $handler, DokuWiki_Syntax_Plugin $plugin): array
135    {
136        /**
137         * Logical Tag Building
138         */
139        switch ($state) {
140
141            case DOKU_LEXER_ENTER:
142
143                return self::handleStaticEnter($match, $pos, $handler, $plugin);
144
145            case DOKU_LEXER_UNMATCHED :
146
147                $data = PluginUtility::handleAndReturnUnmatchedData(null, $match, $handler);
148                /**
149                 * Attribute of parent are send for context
150                 * (example `display = none`)
151                 */
152                $callStack = CallStack::createFromHandler($handler);
153                $parentTag = $callStack->moveToParent();
154                if ($parentTag !== false) {
155                    $tagAttributes = $parentTag->getAttributes();
156                    $data[PluginUtility::ATTRIBUTES] = $tagAttributes;
157                }
158                return $data;
159
160            case DOKU_LEXER_EXIT :
161
162                return self::handleStaticExit($match, $pos, $handler, $plugin);
163
164
165            default:
166                throw new ExceptionRuntimeInternal("Should not happen");
167        }
168    }
169
170    public static function renderStaticEnterXhtml(TagAttributes $tagAttributes, Doku_Renderer_xhtml $renderer, array $data, DokuWiki_Syntax_Plugin $plugin): bool
171    {
172
173        $context = $data[PluginUtility::CONTEXT] ?? null;
174        $pos = $data[PluginUtility::POSITION] ?? null;
175        $logicalTag = $tagAttributes->getLogicalTag();
176        switch ($logicalTag) {
177            case BlockquoteTag::TAG:
178                $renderer->doc .= BlockquoteTag::renderEnterXhtml($tagAttributes, $data, $renderer);
179                return true;
180            case BoxTag::TAG:
181                $renderer->doc .= BoxTag::renderEnterXhtml($tagAttributes);
182                return true;
183            case ButtonTag::LOGICAL_TAG:
184                $renderer->doc .= ButtonTag::renderEnterXhtml($tagAttributes, $plugin, $data);
185                return true;
186            case CardTag::LOGICAL_TAG:
187                $renderer->doc .= CardTag::renderEnterXhtml($tagAttributes, $renderer, $data);
188                return true;
189            case BarTag::LOGICAL_TAG:
190                $renderer->doc .= BarTag::renderEnterXhtml($tagAttributes, $data);
191                return true;
192            case CarrouselTag::TAG:
193                $renderer->doc .= CarrouselTag::renderEnterXhtml($tagAttributes, $data);
194                return true;
195            case PrismTags::CONSOLE_TAG:
196            case PrismTags::FILE_TAG:
197                PrismTags::processEnterXhtml($tagAttributes, $plugin, $renderer);
198                return true;
199            case ContainerTag::TAG:
200                $renderer->doc .= ContainerTag::renderEnterXhtml($tagAttributes);
201                return true;
202            case GridTag::LOGICAL_TAG:
203                $renderer->doc .= GridTag::renderEnterXhtml($tagAttributes);
204                return true;
205            case PipelineTag::TAG:
206                $renderer->doc .= PipelineTag::renderEnterXhtml($tagAttributes);
207                return true;
208            case DateTag::TAG:
209                $renderer->doc .= DateTag::renderHtml($tagAttributes);
210                return true;
211            case DropDownTag::TAG:
212                $renderer->doc .= DropDownTag::renderEnterXhtml($tagAttributes);
213                return true;
214            case HeadingTag::LOGICAL_TAG:
215                HeadingTag::processRenderEnterXhtml($context, $tagAttributes, $renderer, $pos);
216                return true;
217            case NoteTag::TAG_INOTE:
218                $renderer->doc .= NoteTag::renderEnterInlineNote($tagAttributes);
219                return true;
220            case JumbotronTag::TAG:
221                $renderer->doc .= JumbotronTag::renderEnterXhtml($tagAttributes);
222                return true;
223            case MasonryTag::LOGICAL_TAG:
224                $renderer->doc .= MasonryTag::renderEnterTag();
225                return true;
226            case PageExplorerTag::LOGICAL_TAG:
227                $renderer->doc .= PageExplorerTag::renderEnterTag($tagAttributes, $data);
228                return true;
229            case SectionTag::TAG:
230                $renderer->doc .= SectionTag::renderEnterXhtml($tagAttributes);
231                return true;
232            case TabsTag::TAG:
233                $renderer->doc .= TabsTag::renderEnterXhtml($tagAttributes, $data);
234                return true;
235            case PanelTag::PANEL_LOGICAL_MARKUP:
236                $renderer->doc .= PanelTag::renderEnterXhtml($tagAttributes, $data);
237                return true;
238            case PermalinkTag::TAG:
239                $renderer->doc .= PermalinkTag::renderEnterSpecialXhtml($data);
240                return true;
241            case HrTag::TAG:
242                $renderer->doc .= HrTag::render($tagAttributes);
243                return true;
244            case IconTag::TAG:
245                $renderer->doc .= IconTag::renderEnterTag($tagAttributes);
246                return true;
247            case BackgroundTag::LOGICAL_TAG:
248                $renderer->doc .= BackgroundTag::renderEnterTag();
249                return true;
250            case MermaidTag::LOGICAL_TAG:
251                $renderer->doc .= MermaidTag::renderEnter($tagAttributes);
252                return true;
253            case WebCodeTag::TAG:
254                return true;
255            case ShareTag::MARKUP:
256                $renderer->doc .= ShareTag::renderSpecialEnter($tagAttributes, DOKU_LEXER_ENTER);
257                return true;
258            case FollowTag::MARKUP:
259                $renderer->doc .= FollowTag::renderSpecialEnterNode($tagAttributes, DOKU_LEXER_ENTER);
260                return true;
261            case TableTag::TAG:
262                TableTag::renderEnterXhtml($tagAttributes, $renderer);
263                return true;
264            case RelatedTag::TAG:
265                LogUtility::warning("The related tag should be closed. You should write <related/> and not <related>.");
266                $renderer->doc .= RelatedTag::render($tagAttributes);
267                return true;
268            default:
269                LogUtility::warning("The enter tag (" . $logicalTag . ") was not processed.");
270                return false;
271        }
272    }
273
274    public static function handleStaticEnter(string $match, int $pos, Doku_Handler $handler, DokuWiki_Syntax_Plugin $plugin): array
275    {
276        // context data
277        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
278
279        // Normalize Trim and delete eol to start clean
280        $match = trim($match);
281        $match = str_replace("\n", " ", $match);
282
283        // Markup
284        $markupTag = PluginUtility::getMarkupTag($match);
285        $logicalTag = $markupTag;
286        $defaultAttributes = [];
287        $knownTypes = [];
288        $allowAnyFirstBooleanAttributesAsType = false;
289
290        // code block allow a second attribute value as file name
291        $hasTwoBooleanAttribute = false;
292        $secondBooleanAttribute = null;
293
294        switch ($markupTag) {
295            case BlockquoteTag::TAG:
296                // Suppress the component name
297                $defaultAttributes = array("type" => BlockquoteTag::CARD_TYPE);
298                $knownTypes = [BlockquoteTag::TYPO_TYPE, BlockquoteTag::CARD_TYPE];;
299                break;
300            case BoxTag::TAG:
301                $defaultAttributes[BoxTag::HTML_TAG_ATTRIBUTE] = BoxTag::DEFAULT_HTML_TAG;
302                $defaultAttributes[BoxTag::LOGICAL_TAG_ATTRIBUTE] = BoxTag::LOGICAL_TAG_DEFAUT;
303                break;
304            case ButtonTag::MARKUP_SHORT:
305            case ButtonTag::MARKUP_LONG:
306                $logicalTag = ButtonTag::LOGICAL_TAG;
307                $knownTypes = ButtonTag::TYPES;
308                $defaultAttributes = array(
309                    Skin::SKIN_ATTRIBUTE => Skin::FILLED_VALUE,
310                    TagAttributes::TYPE_KEY => ColorRgb::PRIMARY_VALUE
311                );
312                break;
313            case CardTag::CARD_TAG:
314            case CardTag::TEASER_TAG:
315                $logicalTag = CardTag::LOGICAL_TAG;
316                break;
317            case BarTag::BAR_TAG:
318            case BarTag::SLIDE_TAG:
319                $logicalTag = BarTag::LOGICAL_TAG;
320                $defaultAttributes[Hero::ATTRIBUTE] = "sm";
321                break;
322            case PrismTags::CONSOLE_TAG:
323            case PrismTags::FILE_TAG:
324                $hasTwoBooleanAttribute = true;
325                $secondBooleanAttribute = syntax_plugin_combo_code::FILE_PATH_KEY;
326                $allowAnyFirstBooleanAttributesAsType = true;
327                break;
328            case ContainerTag::TAG:
329                $knownTypes = ContainerTag::CONTAINER_VALUES;
330                $defaultAttributes[TagAttributes::TYPE_KEY] = $executionContext->getConfig()->getValue(ContainerTag::DEFAULT_LAYOUT_CONTAINER_CONF, ContainerTag::DEFAULT_LAYOUT_CONTAINER_DEFAULT_VALUE);
331                break;
332            case GridTag::ROW_TAG:
333            case GridTag::GRID_TAG:
334                $logicalTag = GridTag::LOGICAL_TAG;
335                $knownTypes = GridTag::KNOWN_TYPES;
336                break;
337            case HeadingTag::HEADING_TAG:
338            case HeadingTag::TITLE_TAG:
339                $logicalTag = HeadingTag::LOGICAL_TAG;
340                $knownTypes = HeadingTag::getAllTypes();
341                break;
342            case NoteTag::TAG_INOTE:
343                $defaultConfValue = $plugin->getConf(NoteTag::INOTE_CONF_DEFAULT_ATTRIBUTES_KEY);
344                $defaultAttributes = PluginUtility::parseAttributes($defaultConfValue);
345                if (!isset($defaultAttributes[TagAttributes::TYPE_KEY])) {
346                    $defaultAttributes[TagAttributes::TYPE_KEY] = "info";
347                }
348                $knownTypes = NoteTag::KNOWN_TYPES;
349                break;
350            case JumbotronTag::TAG:
351                $defaultAttributes = JumbotronTag::getDefault();
352                break;
353            case MasonryTag::CARD_COLUMNS_TAG:
354            case MasonryTag::TEASER_COLUMNS_TAG:
355            case MasonryTag::MASONRY_TAG:
356                $logicalTag = MasonryTag::LOGICAL_TAG;
357                break;
358            case PageExplorerTag::NTOC_MARKUP:
359            case PageExplorerTag::PAGE_EXPLORER_MARKUP:
360                $logicalTag = PageExplorerTag::LOGICAL_TAG;
361                $defaultAttributes = [TagAttributes::TYPE_KEY => PageExplorerTag::LIST_TYPE];
362                $knownTypes = [PageExplorerTag::TYPE_TREE, PageExplorerTag::LIST_TYPE];
363                break;
364            case PageExplorerTag::INDEX_HOME_TAG:
365            case PageExplorerTag::INDEX_TAG:
366                $logicalTag = PageExplorerTag::LOGICAL_INDEX_TAG;
367                break;
368            case PageExplorerTag::NAMESPACE_ITEM_TAG:
369            case PageExplorerTag::NAMESPACE_LONG_TAG:
370            case PageExplorerTag::NAMESPACE_SHORT_TAG:
371                $logicalTag = PageExplorerTag::NAMESPACE_LOGICAL_TAG;
372                break;
373            case PageExplorerTag::PAGE_ITEM_TAG:
374            case PageExplorerTag::PAGE_TAG:
375                $logicalTag = PageExplorerTag::PAGE_LOGICAL_TAG;
376                break;
377            case TabsTag::TAG:
378                $knownTypes = [TabsTag::ENCLOSED_PILLS_TYPE, TabsTag::ENCLOSED_TABS_TYPE, TabsTag::PILLS_TYPE, TabsTag::TABS_TYPE];
379                break;
380            case PanelTag::PANEL_MARKUP:
381            case PanelTag::TAB_PANEL_MARKUP:
382                $logicalTag = PanelTag::PANEL_LOGICAL_MARKUP;
383                break;
384            case PermalinkTag::TAG:
385                $knownTypes = PermalinkTag::getKnownTypes();
386                $defaultAttributes = [TagAttributes::TYPE_KEY => PermalinkTag::GENERATED_TYPE];
387                break;
388            case BackgroundTag::MARKUP_LONG:
389            case BackgroundTag::MARKUP_SHORT:
390                $logicalTag = BackgroundTag::LOGICAL_TAG;
391                break;
392            case MermaidTag::MARKUP_MERMAID:
393            case WebCodeTag::TAG:
394                $logicalTag = Tag\WebCodeTag::TAG;
395                $defaultAttributes = WebCodeTag::getDefaultAttributes();
396                break;
397            case MermaidTag::MARKUP_SEQUENCE_DIAGRAM:
398            case MermaidTag::MARKUP_CLASS_DIAGRAM:
399            case MermaidTag::MARKUP_FLOWCHART:
400            case MermaidTag::MARKUP_GANTT:
401            case MermaidTag::MARKUP_ERD:
402            case MermaidTag::MARKUP_JOURNEY:
403            case MermaidTag::MARKUP_PIECHART:
404            case MermaidTag::MARKUP_STATE_DIAGRAM:
405                $logicalTag = MermaidTag::LOGICAL_TAG;
406                break;
407            case ShareTag::MARKUP:
408                $knownTypes = ShareTag::getKnownTypes();
409                break;
410            case FollowTag::MARKUP:
411                $knownTypes = FollowTag::getKnownTypes();
412                break;
413        }
414
415        /**
416         * Build tag Attributes
417         */
418        if (!$hasTwoBooleanAttribute) {
419            $tagAttributes = TagAttributes::createFromTagMatch($match, $defaultAttributes, $knownTypes, $allowAnyFirstBooleanAttributesAsType);
420        } else {
421            $tagAttributes = TagAttributes::createEmpty();
422            $attributesArray = PluginUtility::getQualifiedTagAttributes($match, true, $secondBooleanAttribute, $knownTypes, $allowAnyFirstBooleanAttributesAsType);
423            foreach ($attributesArray as $key => $value) {
424                $tagAttributes->addComponentAttributeValue($key, $value);
425            }
426        }
427        $tagAttributes->setLogicalTag($logicalTag);
428
429        /**
430         * Calculate extra returned key in the table
431         */
432        $returnedArray = [];
433        switch ($logicalTag) {
434            case BlockquoteTag::TAG:
435                $returnedArray = BlockquoteTag::handleEnter($handler);
436                break;
437            case BoxTag::TAG:
438                BoxTag::handleEnter($tagAttributes);
439                break;
440            case ButtonTag::LOGICAL_TAG:
441                $returnedArray = ButtonTag::handleEnter($tagAttributes, $handler);
442                break;
443            case CardTag::CARD_TAG:
444                $returnedArray = CardTag::handleEnter($tagAttributes, $handler);
445                break;
446            case BarTag::LOGICAL_TAG:
447                $returnedArray = BarTag::handleEnter($tagAttributes);
448                break;
449            case CarrouselTag::TAG:
450                $returnedArray = CarrouselTag::handleEnter($handler);
451                break;
452            case GridTag::LOGICAL_TAG:
453                GridTag::processEnter($tagAttributes, $handler, $match);
454                break;
455            case DateTag::TAG:
456                DateTag::handleEnterAndSpecial();
457                break;
458            case HeadingTag::LOGICAL_TAG:
459                $returnedArray = HeadingTag::handleEnter($handler, $tagAttributes, $markupTag);
460                break;
461            case PanelTag::PANEL_LOGICAL_MARKUP:
462                $returnedArray = PanelTag::handleEnter($tagAttributes, $handler, $markupTag);
463                break;
464            case PermalinkTag::TAG:
465                $returnedArray = PermalinkTag::handleEnterSpecial($tagAttributes, DOKU_LEXER_ENTER, $handler);
466                break;
467            case IconTag::TAG:
468                $returnedArray = IconTag::handleEnter($tagAttributes, $handler);
469                break;
470            case BackgroundTag::LOGICAL_TAG:
471                BackgroundTag::handleEnterAndSpecial($tagAttributes);
472                break;
473        }
474
475        /**
476         * Common default
477         */
478        $defaultReturnedArray[PluginUtility::STATE] = DOKU_LEXER_ENTER;
479        $defaultReturnedArray[PluginUtility::TAG] = $logicalTag;
480        $defaultReturnedArray[PluginUtility::MARKUP_TAG] = $markupTag;
481        $defaultReturnedArray[PluginUtility::POSITION] = $pos;
482        $defaultReturnedArray[PluginUtility::ATTRIBUTES] = $tagAttributes->toCallStackArray();
483
484        return array_merge($defaultReturnedArray, $returnedArray);
485
486    }
487
488    public static function renderStatic(string $format, Doku_Renderer $renderer, array $data, DokuWiki_Syntax_Plugin $plugin): bool
489    {
490        $logicalTag = $data[PluginUtility::TAG] ?? null;
491        $attributes = $data[PluginUtility::ATTRIBUTES] ?? null;
492        $context = $data[PluginUtility::CONTEXT] ?? null;
493        $state = $data[PluginUtility::STATE] ?? null;
494        $pos = $data[PluginUtility::POSITION] ?? null;
495        $tagAttributes = TagAttributes::createFromCallStackArray($attributes)->setLogicalTag($logicalTag);
496        switch ($format) {
497            case "xhtml":
498                /** @var Doku_Renderer_xhtml $renderer */
499                switch ($state) {
500                    case DOKU_LEXER_ENTER:
501                        return self::renderStaticEnterXhtml($tagAttributes, $renderer, $data, $plugin);
502                    case DOKU_LEXER_UNMATCHED:
503
504                        $renderer->doc .= PluginUtility::renderUnmatched($data);
505                        return true;
506
507                    case DOKU_LEXER_EXIT:
508
509                        return XmlTagProcessing::renderStaticExitXhtml($tagAttributes, $renderer, $data, $plugin);
510                }
511                break;
512            case 'metadata':
513                /** @var Doku_Renderer_metadata $renderer */
514                if (!in_array($state, [DOKU_LEXER_ENTER, DOKU_LEXER_SPECIAL])) {
515                    return true;
516                }
517                return XmlTagProcessing::renderStaticEnterSpecialMetadata($tagAttributes, $renderer, $data, $plugin);
518            case 'xml':
519                /** @var renderer_plugin_combo_xml $renderer */
520                switch ($state) {
521                    case DOKU_LEXER_ENTER:
522                        switch ($logicalTag) {
523                            default:
524                            case BarTag::LOGICAL_TAG:
525                                $renderer->doc .= "<$logicalTag>";
526                                return true;
527                        }
528                    case DOKU_LEXER_UNMATCHED :
529                        $renderer->doc .= PluginUtility::renderUnmatched($data);
530                        break;
531                    case DOKU_LEXER_EXIT :
532                        switch ($logicalTag) {
533                            default:
534                            case BarTag::LOGICAL_TAG:
535                                $renderer->doc .= "</$logicalTag>";
536                                return true;
537                        }
538                }
539                return false;
540            case renderer_plugin_combo_analytics::RENDERER_FORMAT:
541                /**
542                 * @var renderer_plugin_combo_analytics $renderer
543                 */
544                switch ($logicalTag) {
545                    default:
546                    case HeadingTag::LOGICAL_TAG:
547                        HeadingTag::processMetadataAnalytics($data, $renderer);
548                        return true;
549                }
550
551        }
552
553        // unsupported $mode
554        return false;
555    }
556
557    public static function handleStaticExit(string $match, int $pos, Doku_Handler $handler, DokuWiki_Syntax_Plugin $plugin): array
558    {
559        $markupTag = PluginUtility::getMarkupTag($match);
560        $logicalTag = $markupTag;
561        $returnedArray = [];
562        switch ($markupTag) {
563            case BlockquoteTag::TAG:
564                $returnedArray = BlockquoteTag::handleExit($handler);
565                break;
566            case BoxTag::TAG:
567                $returnedArray = BoxTag::handleExit($handler);
568                break;
569            case ButtonTag::MARKUP_SHORT:
570            case ButtonTag::MARKUP_LONG:
571                $logicalTag = ButtonTag::LOGICAL_TAG;
572                $returnedArray = ButtonTag::handleExit($handler);
573                break;
574            case CardTag::CARD_TAG:
575            case CardTag::TEASER_TAG:
576                $logicalTag = CardTag::LOGICAL_TAG;
577                $returnedArray = CardTag::handleExit($handler, $pos, $match);
578                break;
579            case BarTag::BAR_TAG:
580            case BarTag::SLIDE_TAG:
581                $logicalTag = BarTag::LOGICAL_TAG;
582                $returnedArray = BarTag::handleExit($handler, $pos, $match);
583                break;
584            case CarrouselTag::TAG:
585                $returnedArray = CarrouselTag::handleExit($handler);
586                break;
587            case PrismTags::CONSOLE_TAG:
588            case PrismTags::FILE_TAG:
589                $returnedArray = PrismTags::handleExit($handler);
590                break;
591            case PipelineTag::TAG:
592                PipelineTag::processExit($handler);
593                break;
594            case GridTag::GRID_TAG:
595            case GridTag::ROW_TAG:
596                $logicalTag = GridTag::LOGICAL_TAG;
597                $returnedArray = GridTag::handleExit($handler);
598                break;
599            case DateTag::TAG:
600                DateTag::handleExit($handler);
601                break;
602            case HeadingTag::TITLE_TAG:
603            case HeadingTag::HEADING_TAG:
604                $logicalTag = HeadingTag::LOGICAL_TAG;
605                $returnedArray = HeadingTag::handleExit($handler);
606                break;
607            case MasonryTag::CARD_COLUMNS_TAG:
608            case MasonryTag::TEASER_COLUMNS_TAG:
609            case MasonryTag::MASONRY_TAG:
610                $logicalTag = MasonryTag::LOGICAL_TAG;
611                MasonryTag::handleExit($handler);
612                break;
613            case PageExplorerTag::NTOC_MARKUP:
614            case PageExplorerTag::PAGE_EXPLORER_MARKUP:
615                $logicalTag = PageExplorerTag::LOGICAL_TAG;
616                PageExplorerTag::handleExit($handler);
617                break;
618            case PageExplorerTag::INDEX_HOME_TAG:
619            case PageExplorerTag::INDEX_TAG:
620                $logicalTag = PageExplorerTag::LOGICAL_INDEX_TAG;
621                break;
622            case PageExplorerTag::NAMESPACE_ITEM_TAG:
623            case PageExplorerTag::NAMESPACE_LONG_TAG:
624            case PageExplorerTag::NAMESPACE_SHORT_TAG:
625                $logicalTag = PageExplorerTag::NAMESPACE_LOGICAL_TAG;
626                break;
627            case PageExplorerTag::PAGE_ITEM_TAG:
628            case PageExplorerTag::PAGE_TAG:
629                $logicalTag = PageExplorerTag::PAGE_LOGICAL_TAG;
630                break;
631            case PageExplorerTag::PARENT_TAG:
632                // nothing as the content is captured and deleted by page-explorer
633                break;
634            case TabsTag::TAG:
635                $returnedArray = TabsTag::handleExit($handler);
636                break;
637            case PanelTag::PANEL_MARKUP:
638            case PanelTag::TAB_PANEL_MARKUP:
639                $logicalTag = PanelTag::PANEL_LOGICAL_MARKUP;
640                $returnedArray = PanelTag::handleExit($handler, $pos, $markupTag, $match);
641                break;
642            case PermalinkTag::TAG:
643                PermalinkTag::handeExit($handler);
644                break;
645            case IconTag::TAG:
646                $returnedArray = IconTag::handleExit($handler);
647                break;
648            case BackgroundTag::MARKUP_SHORT:
649            case BackgroundTag::MARKUP_LONG:
650                $logicalTag = BackgroundTag::LOGICAL_TAG;
651                $returnedArray = BackgroundTag::handleExit($handler);
652                break;
653            case MermaidTag::MARKUP_SEQUENCE_DIAGRAM:
654            case MermaidTag::MARKUP_CLASS_DIAGRAM:
655            case MermaidTag::MARKUP_FLOWCHART:
656            case MermaidTag::MARKUP_GANTT:
657            case MermaidTag::MARKUP_ERD:
658            case MermaidTag::MARKUP_JOURNEY:
659            case MermaidTag::MARKUP_PIECHART:
660            case MermaidTag::MARKUP_STATE_DIAGRAM:
661                $logicalTag = MermaidTag::LOGICAL_TAG;
662                MermaidTag::handleExit($handler);
663                break;
664            case MermaidTag::MARKUP_MERMAID: // bug
665            case WebCodeTag::TAG:
666                $logicalTag = WebCodeTag::TAG;
667                $returnedArray = WebCodeTag::handleExit($handler);
668                break;
669            case ShareTag::MARKUP:
670                $returnedArray = ShareTag::handleExit($handler);
671
672                break;
673
674        }
675        /**
676         * Common exit attributes
677         */
678        $defaultReturnedArray[PluginUtility::STATE] = DOKU_LEXER_EXIT;
679        $defaultReturnedArray[PluginUtility::TAG] = $logicalTag;
680        $defaultReturnedArray[PluginUtility::MARKUP_TAG] = $markupTag;
681        return array_merge($defaultReturnedArray, $returnedArray);
682    }
683
684    /**
685     * @param $tag
686     * @return string
687     *
688     * Create a lookahead pattern for a container tag used to enter in a mode
689     */
690    public static function getContainerTagPattern($tag): string
691    {
692        // this pattern ensure that the tag
693        // `accordion` will not intercept also the tag `accordionitem`
694        // where:
695        // ?: means non capturing group (to not capture the last >)
696        // (\s.*?): is a capturing group that starts with a space
697        $pattern = "(?:\s.*?>|>)";
698        return '<' . $tag . $pattern . '(?=.*?<\/' . $tag . '>)';
699    }
700
701    public static function handleStaticEmptyTag(string $match, int $state, int $pos, Doku_Handler $handler, DokuWiki_Syntax_Plugin $plugin): array
702    {
703        /**
704         * Logical Tag Building
705         */
706        $logicalTag = PluginUtility::getMarkupTag($match);
707        $defaultAttributes = [];
708        $knownTypes = [];
709        $allowAnyFirstBooleanAttributesAsType = false;
710        switch ($logicalTag) {
711            case SearchTag::TAG:
712                $defaultAttributes = array(
713                    'ajax' => true,
714                    'autocomplete' => false
715                );
716                break;
717            case PageImageTag::MARKUP:
718                $knownTypes = PageImageTag::TYPES;
719                $defaultAttributes = PageImageTag::getDefaultAttributes();
720                break;
721            case ShareTag::MARKUP:
722                $knownTypes = ShareTag::getKnownTypes();
723                break;
724            case FollowTag::MARKUP:
725                $knownTypes = FollowTag::getKnownTypes();
726                break;
727            case BrandListTag::MARKUP:
728                $knownTypes = BrandButton::TYPE_BUTTONS;
729                $defaultAttributes = [TagAttributes::TYPE_KEY => BrandButton::TYPE_BUTTON_BRAND];
730                break;
731            case BrandTag::MARKUP:
732                $defaultAttributes = [TagAttributes::TYPE_KEY => Brand::CURRENT_BRAND];
733                $allowAnyFirstBooleanAttributesAsType = true;
734                break;
735            case PermalinkTag::TAG:
736                $knownTypes = PermalinkTag::getKnownTypes();
737                $defaultAttributes = [TagAttributes::TYPE_KEY => PermalinkTag::GENERATED_TYPE];
738                break;
739            case BreadcrumbTag::MARKUP_BLOCK:
740                $logicalTag = BreadcrumbTag::LOGICAL_TAG;
741                $knownTypes = BreadcrumbTag::TYPES;
742                $defaultAttributes = BreadcrumbTag::getDefaultBlockAttributes();
743                break;
744        }
745        $tagAttributes = TagAttributes::createFromTagMatch($match, $defaultAttributes, $knownTypes, $allowAnyFirstBooleanAttributesAsType)
746            ->setLogicalTag($logicalTag);
747
748        /**
749         * Calculate extra returned key in the table
750         */
751        $returnedArray = [];
752        switch ($logicalTag) {
753            case IconTag::TAG:
754                $returnedArray = IconTag::handleSpecial($tagAttributes, $handler);
755                break;
756            case PageImageTag::MARKUP:
757                $returnedArray = PageImageTag::handle($tagAttributes, $handler);
758                break;
759            case BrandTag::MARKUP:
760                $returnedArray = BrandTag::handleSpecialEnter($tagAttributes, $handler);
761                break;
762            case CacheTag::MARKUP:
763                $returnedArray = CacheTag::handle($tagAttributes);
764                break;
765            case BackgroundTag::MARKUP_SHORT:
766            case BackgroundTag::MARKUP_LONG:
767                BackgroundTag::handleEnterAndSpecial($tagAttributes);
768                $callStack = CallStack::createFromHandler($handler);
769                $returnedArray = BackgroundTag::setAttributesToParentAndReturnData($callStack, $tagAttributes, $state);
770                break;
771            case DateTag::TAG:
772                DateTag::handleEnterAndSpecial();
773                break;
774            case PermalinkTag::TAG:
775                $returnedArray = PermalinkTag::handleEnterSpecial($tagAttributes, $state, $handler);
776                break;
777            case BreadcrumbTag::MARKUP_BLOCK:
778                $returnedArray = BreadcrumbTag::handleEnter($tagAttributes);
779                break;
780            case HrTag::TAG:
781                /**
782                 * Block tag
783                 */
784                $returnedArray = [PluginUtility::DISPLAY => HrTag::getDisplay()];
785                break;
786        }
787
788        /**
789         * Common default
790         * {@link PluginUtility::DISPLAY} should be set on handle
791         */
792        $defaultReturnedArray[PluginUtility::STATE] = $state;
793        $defaultReturnedArray[PluginUtility::TAG] = $logicalTag;
794        $defaultReturnedArray[PluginUtility::ATTRIBUTES] = $tagAttributes->toCallStackArray();
795
796        return array_merge($defaultReturnedArray, $returnedArray);
797    }
798
799    public static function renderStaticEmptyTag(string $format, Doku_Renderer $renderer, array $data, DokuWiki_Syntax_Plugin $plugin): bool
800    {
801
802        $tag = $data[PluginUtility::TAG];
803        $attributes = $data[PluginUtility::ATTRIBUTES];
804        $state = DOKU_LEXER_SPECIAL;
805        $tagAttributes = TagAttributes::createFromCallStackArray($attributes)->setLogicalTag($tag);
806        switch ($format) {
807            case "xhtml":
808                /** @var Doku_Renderer_xhtml $renderer */
809                switch ($tag) {
810                    case HrTag::TAG:
811                        $renderer->doc .= HrTag::render($tagAttributes);
812                        break;
813                    case SearchTag::TAG:
814                        $renderer->doc .= SearchTag::render($tagAttributes);
815                        break;
816                    case IconTag::TAG:
817                        $renderer->doc .= IconTag::renderEmptyTag($tagAttributes);
818                        break;
819                    case BreadcrumbTag::LOGICAL_TAG:
820                        $renderer->doc .= BreadcrumbTag::render($tagAttributes);
821                        break;
822                    case PageImageTag::MARKUP:
823                        $renderer->doc .= PageImageTag::render($tagAttributes, $data);
824                        break;
825                    case ShareTag::MARKUP:
826                        $renderer->doc .= ShareTag::renderSpecialEnter($tagAttributes, $state);
827                        break;
828                    case BrandListTag::MARKUP:
829                        $renderer->doc .= BrandListTag::render($tagAttributes);
830                        break;
831                    case BrandTag::MARKUP:
832                        $renderer->doc .= BrandTag::render($tagAttributes, $state, $data);
833                        break;
834                    case CacheTag::MARKUP:
835                        $renderer->doc .= CacheTag::renderXhtml($data);
836                        break;
837                    case BackgroundTag::MARKUP_LONG:
838                    case BackgroundTag::MARKUP_SHORT:
839                        $renderer->doc .= BackgroundTag::renderExitSpecialHtml($data);
840                        break;
841                    case DateTag::TAG:
842                        $renderer->doc .= DateTag::renderHtml($tagAttributes);
843                        return true;
844                    case PermalinkTag::TAG:
845                        $renderer->doc .= PermalinkTag::renderEnterSpecialXhtml($data);
846                        return true;
847                    case QualityTag::MARKUP_TAG:
848                        $renderer->doc .= QualityTag::renderXhtml($tagAttributes);
849                        return true;
850                    case FollowTag::MARKUP:
851                        $renderer->doc .= FollowTag::renderSpecialEnterNode($tagAttributes, DOKU_LEXER_SPECIAL);
852                        return true;
853                    case MediaMarkup::TAG:
854                        $renderer->doc .= MediaMarkup::renderSpecial($data, $renderer);
855                        return true;
856                    case SubscribeTag::LOGICAL_TAG:
857                        $renderer->doc .= SubscribeTag::renderEnterXhtml($tagAttributes);
858                        return true;
859                    case AdTag::MARKUP:
860                        $renderer->doc .= AdTag::render($tagAttributes);
861                        return true;
862                    case RelatedTag::TAG:
863                        $renderer->doc .= RelatedTag::render($tagAttributes);
864                        return true;
865                    default:
866                        LogUtility::errorIfDevOrTest("The empty tag (" . $tag . ") was not processed.");
867                }
868                break;
869            case 'metadata':
870                /** @var Doku_Renderer_metadata $renderer */
871                switch ($tag) {
872                    case IconTag::TAG:
873                        IconTag::metadata($renderer, $tagAttributes);
874                        return true;
875                    case CacheTag::MARKUP:
876                        CacheTag::metadata($data);
877                        return true;
878                    case MediaMarkup::TAG:
879                        MediaMarkup::metadata($data, $renderer);
880                        return true;
881                }
882                break;
883            case renderer_plugin_combo_analytics::RENDERER_FORMAT:
884
885        }
886        // unsupported $mode
887        return false;
888    }
889
890    private static function renderStaticEnterSpecialMetadata(TagAttributes $tagAttributes, Doku_Renderer_metadata $renderer, array $data, DokuWiki_Syntax_Plugin $plugin): bool
891    {
892        $logicalTag = $tagAttributes->getLogicalTag();
893        switch ($logicalTag) {
894            case HeadingTag::LOGICAL_TAG:
895                HeadingTag::processHeadingEnterMetadata($data, $renderer);
896                return true;
897            case IconTag::TAG:
898                IconTag::metadata($renderer, $tagAttributes);
899                return true;
900            case BackgroundTag::LOGICAL_TAG:
901                BackgroundTag::renderMeta($data, $renderer);
902                return true;
903        }
904        return false;
905    }
906}
907
908