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