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