1<?php
2
3// implementation of
4// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite
5
6// must be run within Dokuwiki
7use ComboStrap\Bootstrap;
8use ComboStrap\CallStack;
9use ComboStrap\LogUtility;
10use ComboStrap\PanelTag;
11use ComboStrap\PluginUtility;
12use ComboStrap\TabsTag;
13use ComboStrap\XmlTagProcessing;
14
15
16class syntax_plugin_combo_label extends DokuWiki_Syntax_Plugin
17{
18
19
20    const TAG = "label";
21
22    /**
23     * The id of the heading element for a accordion label
24     */
25    const HEADING_ID = "headingId";
26    /**
27     * The id of the collapsable target
28     */
29    const TARGET_ID = "targetId";
30
31    /**
32     * An indicator attribute that tells if the accordion is collpased or not
33     */
34    const COLLAPSED = "collapsed";
35
36    function getType()
37    {
38        return 'formatting';
39    }
40
41    /**
42     * How Dokuwiki will add P element
43     *
44     *  * 'normal' - The plugin can be used inside paragraphs (inline)
45     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
46     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
47     *
48     * @see DokuWiki_Syntax_Plugin::getPType()
49     */
50    function getPType()
51    {
52        return 'block';
53    }
54
55    function getAllowedTypes()
56    {
57        return array('substition', 'formatting', 'disabled');
58    }
59
60    function getSort()
61    {
62        return 201;
63    }
64
65
66    function connectTo($mode)
67    {
68
69        $this->Lexer->addEntryPattern(XmlTagProcessing::getContainerTagPattern(self::TAG), $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
70    }
71
72    public function postConnect()
73    {
74        $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent()));
75    }
76
77    function handle($match, $state, $pos, Doku_Handler $handler)
78    {
79
80        switch ($state) {
81
82            case DOKU_LEXER_ENTER:
83                $tagAttributes = PluginUtility::getTagAttributes($match);
84                $callStack = CallStack::createFromHandler($handler);
85                $parentTag = $callStack->moveToParent();
86                $context = null;
87                if ($parentTag!==false) {
88                    $grandfather = $callStack->moveToParent();
89                    if ($grandfather !== false) {
90                        $grandFatherName = $grandfather->getTagName();
91                        switch ($grandFatherName) {
92                            case syntax_plugin_combo_accordion::TAG:
93                                $id = $parentTag->getAttribute("id");
94                                $tagAttributes["id"] = $id;
95                                $tagAttributes[self::HEADING_ID] = "heading" . ucfirst($id);
96                                $tagAttributes[self::TARGET_ID] = "collapse" . ucfirst($id);
97                                $parentAttribute = $parentTag->getAttributes();
98                                if (!key_exists(self::COLLAPSED, $parentAttribute)) {
99                                    // Accordion are collapsed by default
100                                    $tagAttributes[self::COLLAPSED] = "true";
101                                } else {
102                                    $tagAttributes[self::COLLAPSED] = $parentAttribute[self::COLLAPSED];
103                                }
104                                $context = syntax_plugin_combo_accordion::TAG;
105                                break;
106                            case  TabsTag::TAG:
107                                $context = TabsTag::TAG;
108                                $tagAttributes = $parentTag->getAttributes();
109                                break;
110                            default:
111                                LogUtility::log2FrontEnd("The label is included in the $grandFatherName component and this is unexpected", LogUtility::LVL_MSG_WARNING, self::TAG);
112                        }
113                    } else {
114                        /**
115                         * An panel may render alone in preview
116                         */
117                        if ($parentTag->getContext() == PanelTag::CONTEXT_PREVIEW_ALONE) {
118                            $context = PanelTag::CONTEXT_PREVIEW_ALONE;
119                        }
120                    }
121                }
122
123                return array(
124                    PluginUtility::STATE => $state,
125                    PluginUtility::ATTRIBUTES => $tagAttributes,
126                    PluginUtility::CONTEXT => $context
127                );
128
129            case DOKU_LEXER_UNMATCHED :
130                return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler);
131
132            case DOKU_LEXER_EXIT :
133                $callStack = CallStack::createFromHandler($handler);
134                $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall();
135                $context = $openingTag->getContext();
136
137                /**
138                 * An image in a label should have no link (ie no anchor)
139                 * because a anchor is used for navigation
140                 */
141                $callStack->processNoLinkOnImageToEndStack();
142
143                $callStack->closeAndResetPointer();
144
145
146                return array(
147                    PluginUtility::STATE => $state,
148                    PluginUtility::CONTEXT => $context,
149                    PluginUtility::ATTRIBUTES => $openingTag->getAttributes()
150                );
151
152
153        }
154        return array();
155
156    }
157
158    /**
159     * Render the output
160     * @param string $format
161     * @param Doku_Renderer $renderer
162     * @param array $data - what the function handle() return'ed
163     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
164     * @see DokuWiki_Syntax_Plugin::render()
165     *
166     *
167     */
168    function render($format, Doku_Renderer $renderer, $data): bool
169    {
170
171        if ($format == 'xhtml') {
172
173            /** @var Doku_Renderer_xhtml $renderer */
174            $state = $data[PluginUtility::STATE];
175            switch ($state) {
176
177                case DOKU_LEXER_ENTER:
178
179                    $context = $data[PluginUtility::CONTEXT];
180                    switch ($context) {
181                        case syntax_plugin_combo_accordion::TAG:
182                            $attribute = $data[PluginUtility::ATTRIBUTES];
183                            $headingId = $attribute[self::HEADING_ID];
184                            $collapseId = $attribute[self::TARGET_ID];
185                            $collapsed = $attribute[self::COLLAPSED];
186                            if ($collapsed == "false") {
187                                $collapsedClass = "collapsed";
188                                $ariaExpanded = "true";
189                            } else {
190                                $collapsedClass = "";
191                                $ariaExpanded = "false";
192                            }
193                            $renderer->doc .= "<div class=\"card-header\" id=\"$headingId\">" . DOKU_LF;
194                            $renderer->doc .= "<h2 class=\"mb-0\">";
195                            $dataNamespace = Bootstrap::getDataNamespace();
196                            /** @noinspection HtmlUnknownAttribute */
197                            $renderer->doc .= "<button class=\"btn btn-link btn-block text-left $collapsedClass\" type=\"button\" data{$dataNamespace}-toggle=\"collapse\" data{$dataNamespace}-target=\"#$collapseId\" aria-expanded=\"$ariaExpanded\" aria-controls=\"$collapseId\">";
198                            break;
199                        case TabsTag::TAG:
200                            $attributes = $data[PluginUtility::ATTRIBUTES];
201                            $renderer->doc .= TabsTag::openNavigationalTabElement($attributes);
202                            break;
203                        case PanelTag::CONTEXT_PREVIEW_ALONE:
204                            $attributes = PanelTag::CONTEXT_PREVIEW_ALONE_ATTRIBUTES;
205                            $renderer->doc .= "<ul style=\"list-style-type: none;padding-inline-start: 0;\">";
206                            $renderer->doc .= TabsTag::openNavigationalTabElement($attributes);
207                            break;
208                        default:
209                            LogUtility::log2FrontEnd("The context ($context) of the label is unknown in enter", LogUtility::LVL_MSG_WARNING, self::TAG);
210
211                    }
212                    break;
213
214                case DOKU_LEXER_UNMATCHED :
215                    $renderer->doc .= PluginUtility::renderUnmatched($data);
216                    break;
217
218                case DOKU_LEXER_EXIT:
219                    $context = $data[PluginUtility::CONTEXT];
220                    switch ($context) {
221                        case syntax_plugin_combo_accordion::TAG:
222                            $attribute = $data[PluginUtility::ATTRIBUTES];
223                            $collapseId = $attribute[self::TARGET_ID];
224                            $headingId = $attribute[self::HEADING_ID];
225                            $collapsed = $attribute[self::COLLAPSED];
226                            if ($collapsed == "false") {
227                                $showClass = "show";
228                            } else {
229                                $showClass = "";
230                            }
231                            $renderer->doc .= "</button></h2></div>";
232                            $dataNamespace = Bootstrap::getDataNamespace();
233                            $renderer->doc .= "<div id=\"$collapseId\" class=\"collapse $showClass\" aria-labelledby=\"$headingId\" data-{$dataNamespace}parent=\"#$headingId\">";
234                            $renderer->doc .= "<div class=\"card-body\">" . DOKU_LF;
235                            break;
236                        case TabsTag::TAG:
237                            $renderer->doc .= TabsTag::closeNavigationalTabElement();
238                            break;
239                        case PanelTag::CONTEXT_PREVIEW_ALONE:
240                            $renderer->doc .= TabsTag::closeNavigationalTabElement();
241                            $renderer->doc .= "</ul>";
242                            break;
243                        default:
244                            LogUtility::log2FrontEnd("The context ($context) of the label is unknown in exit", LogUtility::LVL_MSG_WARNING, self::TAG);
245
246
247                    }
248                    break;
249
250
251            }
252        }
253        // unsupported $mode
254        return false;
255    }
256
257
258}
259
260