1<?php 2 3 4use ComboStrap\Bootstrap; 5use ComboStrap\BrandButton; 6use ComboStrap\ButtonTag; 7use ComboStrap\CallStack; 8use ComboStrap\LogUtility; 9use ComboStrap\PluginUtility; 10use ComboStrap\TagAttributes; 11use ComboStrap\TagAttribute\Toggle; 12use ComboStrap\XmlTagProcessing; 13 14 15class syntax_plugin_combo_toggle extends DokuWiki_Syntax_Plugin 16{ 17 18 const TAG = "toggle"; 19 const CANONICAL = self::TAG; 20 const WIDGET_ATTRIBUTE = "widget"; 21 22 23 /** 24 * Syntax Type. 25 * 26 * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 27 * @see DokuWiki_Syntax_Plugin::getType() 28 */ 29 function getType(): string 30 { 31 return 'substition'; 32 } 33 34 /** 35 * How Dokuwiki will add P element 36 * 37 * * 'normal' - The plugin can be used inside paragraphs 38 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 39 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 40 * 41 * @see DokuWiki_Syntax_Plugin::getPType() 42 */ 43 function getPType(): string 44 { 45 // button or link 46 return 'normal'; 47 } 48 49 /** 50 * @return array 51 * Allow which kind of plugin inside 52 * 53 * array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 54 * 55 */ 56 function getAllowedTypes(): array 57 { 58 return array('baseonly', 'formatting', 'substition', 'protected', 'disabled'); 59 } 60 61 function getSort(): int 62 { 63 return 201; 64 } 65 66 public 67 function accepts($mode): bool 68 { 69 return syntax_plugin_combo_preformatted::disablePreformatted($mode) 70 && Toggle::disableEntity($mode); 71 } 72 73 74 /** 75 * Create a pattern that will called this plugin 76 * 77 * @param string $mode 78 * @see Doku_Parser_Mode::connectTo() 79 */ 80 function connectTo($mode) 81 { 82 83 $pattern = XmlTagProcessing::getContainerTagPattern(self::getTag()); 84 $this->Lexer->addEntryPattern($pattern, $mode, 'plugin_' . PluginUtility::PLUGIN_BASE_NAME . '_' . $this->getPluginComponent()); 85 86 87 } 88 89 function postConnect() 90 { 91 92 $this->Lexer->addExitPattern('</' . self::getTag() . '>', 'plugin_' . PluginUtility::PLUGIN_BASE_NAME . '_' . $this->getPluginComponent()); 93 94 } 95 96 function handle($match, $state, $pos, Doku_Handler $handler) 97 { 98 99 100 switch ($state) { 101 102 case DOKU_LEXER_ENTER : 103 104 /** 105 * Default parameters, type definition and parsing 106 */ 107 $defaultParameters[self::WIDGET_ATTRIBUTE] = BrandButton::WIDGET_BUTTON_VALUE; 108 $knownTypes = ButtonTag::TYPES; 109 $tagAttributes = TagAttributes::createFromTagMatch($match, $defaultParameters, $knownTypes) 110 ->setLogicalTag(self::TAG); 111 112 return array( 113 PluginUtility::STATE => $state, 114 PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray() 115 ); 116 117 case DOKU_LEXER_UNMATCHED : 118 return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 119 120 case DOKU_LEXER_EXIT : 121 122 return array( 123 PluginUtility::STATE => $state 124 ); 125 126 127 } 128 return array(); 129 130 } 131 132 /** 133 * Render the output 134 * @param string $format 135 * @param Doku_Renderer $renderer 136 * @param array $data - what the function handle() return 137 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 138 * @see DokuWiki_Syntax_Plugin::render() 139 * 140 * 141 */ 142 function render($format, Doku_Renderer $renderer, $data): bool 143 { 144 145 if ($format === "xhtml") { 146 $state = $data[PluginUtility::STATE]; 147 switch ($state) { 148 case DOKU_LEXER_ENTER: 149 150 $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]) 151 ->setLogicalTag(self::CANONICAL); 152 153 $targetId = $tagAttributes->getValueAndRemoveIfPresent("target-id"); 154 if ($targetId === null) { 155 $renderer->doc .= LogUtility::wrapInRedForHtml("The target id is mandatory"); 156 return false; 157 } 158 /** 159 * Snippet 160 */ 161 PluginUtility::getSnippetManager()->attachCssInternalStyleSheet(self::CANONICAL); 162 163 $bootstrapNamespace = "bs-"; 164 if (Bootstrap::getBootStrapMajorVersion() == Bootstrap::BootStrapFourMajorVersion) { 165 $bootstrapNamespace = ""; 166 } 167 /** 168 * Types 169 */ 170 $type = $tagAttributes->getType(); 171 if ($type !== null) { 172 $tagAttributes->addClassName("btn-$type"); 173 } 174 /** 175 * Should be in link form for bootstrap 176 */ 177 if (substr($targetId, 0, 1) != "#") { 178 $targetId = "#" . $targetId; 179 } 180 $tagAttributes->addComponentAttributeValue("data-{$bootstrapNamespace}toggle", "collapse"); 181 $tagAttributes->addComponentAttributeValue("data-{$bootstrapNamespace}target", $targetId); 182 183 /** 184 * Aria 185 */ 186 $toggleState = $tagAttributes->getValueAndRemove(Toggle::TOGGLE_STATE, Toggle::TOGGLE_STATE_COLLAPSED); 187 switch ($toggleState) { 188 case Toggle::TOGGLE_STATE_EXPANDED: 189 $tagAttributes->addComponentAttributeValue("aria-expanded", true); 190 break; 191 case Toggle::TOGGLE_STATE_COLLAPSED: 192 $tagAttributes->addComponentAttributeValue("aria-expanded", false); 193 $tagAttributes->addClassName("collapsed"); 194 break; 195 } 196 197 198 $targetLabel = $tagAttributes->getValueAndRemoveIfPresent("targetLabel"); 199 if ($targetLabel === null) { 200 $targetLabel = "Toggle $targetId"; 201 } 202 $tagAttributes->addComponentAttributeValue("aria-label", $targetLabel); 203 $tagAttributes->addClassName("btn"); 204 $renderer->doc .= $tagAttributes->toHtmlEnterTag("button"); 205 break; 206 case DOKU_LEXER_UNMATCHED: 207 $renderer->doc .= PluginUtility::renderUnmatched($data); 208 break; 209 case DOKU_LEXER_EXIT: 210 $renderer->doc .= "</button>"; 211 break; 212 213 } 214 return true; 215 } 216 217 // unsupported $mode 218 return false; 219 } 220 221 public 222 static function getTag(): string 223 { 224 return self::TAG; 225 } 226 227 228} 229 230