15f891b7eSNickeau<?php 25f891b7eSNickeau 35f891b7eSNickeau// implementation of 45f891b7eSNickeau// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite 55f891b7eSNickeau 65f891b7eSNickeau// must be run within Dokuwiki 721913ab3SNickeauuse ComboStrap\Bootstrap; 821913ab3SNickeauuse ComboStrap\CallStack; 95f891b7eSNickeauuse ComboStrap\LogUtility; 10*04fd306cSNickeauuse ComboStrap\PanelTag; 115f891b7eSNickeauuse ComboStrap\PluginUtility; 12*04fd306cSNickeauuse ComboStrap\TabsTag; 13*04fd306cSNickeauuse ComboStrap\XmlTagProcessing; 145f891b7eSNickeau 155f891b7eSNickeau 165f891b7eSNickeauclass syntax_plugin_combo_label extends DokuWiki_Syntax_Plugin 175f891b7eSNickeau{ 185f891b7eSNickeau 195f891b7eSNickeau 205f891b7eSNickeau const TAG = "label"; 215f891b7eSNickeau 225f891b7eSNickeau /** 235f891b7eSNickeau * The id of the heading element for a accordion label 245f891b7eSNickeau */ 255f891b7eSNickeau const HEADING_ID = "headingId"; 265f891b7eSNickeau /** 275f891b7eSNickeau * The id of the collapsable target 285f891b7eSNickeau */ 295f891b7eSNickeau const TARGET_ID = "targetId"; 305f891b7eSNickeau 315f891b7eSNickeau /** 325f891b7eSNickeau * An indicator attribute that tells if the accordion is collpased or not 335f891b7eSNickeau */ 345f891b7eSNickeau const COLLAPSED = "collapsed"; 355f891b7eSNickeau 365f891b7eSNickeau function getType() 375f891b7eSNickeau { 385f891b7eSNickeau return 'formatting'; 395f891b7eSNickeau } 405f891b7eSNickeau 415f891b7eSNickeau /** 425f891b7eSNickeau * How Dokuwiki will add P element 435f891b7eSNickeau * 445f891b7eSNickeau * * 'normal' - The plugin can be used inside paragraphs (inline) 455f891b7eSNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 465f891b7eSNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 475f891b7eSNickeau * 485f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::getPType() 495f891b7eSNickeau */ 505f891b7eSNickeau function getPType() 515f891b7eSNickeau { 5221913ab3SNickeau return 'block'; 535f891b7eSNickeau } 545f891b7eSNickeau 555f891b7eSNickeau function getAllowedTypes() 565f891b7eSNickeau { 575f891b7eSNickeau return array('substition', 'formatting', 'disabled'); 585f891b7eSNickeau } 595f891b7eSNickeau 605f891b7eSNickeau function getSort() 615f891b7eSNickeau { 625f891b7eSNickeau return 201; 635f891b7eSNickeau } 645f891b7eSNickeau 655f891b7eSNickeau 665f891b7eSNickeau function connectTo($mode) 675f891b7eSNickeau { 685f891b7eSNickeau 69*04fd306cSNickeau $this->Lexer->addEntryPattern(XmlTagProcessing::getContainerTagPattern(self::TAG), $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 705f891b7eSNickeau } 715f891b7eSNickeau 725f891b7eSNickeau public function postConnect() 735f891b7eSNickeau { 749337a630SNickeau $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent())); 755f891b7eSNickeau } 765f891b7eSNickeau 775f891b7eSNickeau function handle($match, $state, $pos, Doku_Handler $handler) 785f891b7eSNickeau { 795f891b7eSNickeau 805f891b7eSNickeau switch ($state) { 815f891b7eSNickeau 825f891b7eSNickeau case DOKU_LEXER_ENTER: 835f891b7eSNickeau $tagAttributes = PluginUtility::getTagAttributes($match); 84*04fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 85*04fd306cSNickeau $parentTag = $callStack->moveToParent(); 865f891b7eSNickeau $context = null; 87*04fd306cSNickeau if ($parentTag!==false) { 88*04fd306cSNickeau $grandfather = $callStack->moveToParent(); 89*04fd306cSNickeau if ($grandfather !== false) { 90*04fd306cSNickeau $grandFatherName = $grandfather->getTagName(); 915f891b7eSNickeau switch ($grandFatherName) { 925f891b7eSNickeau case syntax_plugin_combo_accordion::TAG: 935f891b7eSNickeau $id = $parentTag->getAttribute("id"); 945f891b7eSNickeau $tagAttributes["id"] = $id; 955f891b7eSNickeau $tagAttributes[self::HEADING_ID] = "heading" . ucfirst($id); 965f891b7eSNickeau $tagAttributes[self::TARGET_ID] = "collapse" . ucfirst($id); 975f891b7eSNickeau $parentAttribute = $parentTag->getAttributes(); 985f891b7eSNickeau if (!key_exists(self::COLLAPSED, $parentAttribute)) { 995f891b7eSNickeau // Accordion are collapsed by default 1005f891b7eSNickeau $tagAttributes[self::COLLAPSED] = "true"; 1015f891b7eSNickeau } else { 1025f891b7eSNickeau $tagAttributes[self::COLLAPSED] = $parentAttribute[self::COLLAPSED]; 1035f891b7eSNickeau } 1045f891b7eSNickeau $context = syntax_plugin_combo_accordion::TAG; 1055f891b7eSNickeau break; 106*04fd306cSNickeau case TabsTag::TAG: 107*04fd306cSNickeau $context = TabsTag::TAG; 1085f891b7eSNickeau $tagAttributes = $parentTag->getAttributes(); 1095f891b7eSNickeau break; 1105f891b7eSNickeau default: 1115f891b7eSNickeau LogUtility::log2FrontEnd("The label is included in the $grandFatherName component and this is unexpected", LogUtility::LVL_MSG_WARNING, self::TAG); 1125f891b7eSNickeau } 11321913ab3SNickeau } else { 11421913ab3SNickeau /** 11521913ab3SNickeau * An panel may render alone in preview 11621913ab3SNickeau */ 117*04fd306cSNickeau if ($parentTag->getContext() == PanelTag::CONTEXT_PREVIEW_ALONE) { 118*04fd306cSNickeau $context = PanelTag::CONTEXT_PREVIEW_ALONE; 11921913ab3SNickeau } 1205f891b7eSNickeau } 1215f891b7eSNickeau } 1225f891b7eSNickeau 1235f891b7eSNickeau return array( 1245f891b7eSNickeau PluginUtility::STATE => $state, 1255f891b7eSNickeau PluginUtility::ATTRIBUTES => $tagAttributes, 1265f891b7eSNickeau PluginUtility::CONTEXT => $context 1275f891b7eSNickeau ); 1285f891b7eSNickeau 1295f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 13032b85071SNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 1315f891b7eSNickeau 1325f891b7eSNickeau case DOKU_LEXER_EXIT : 13321913ab3SNickeau $callStack = CallStack::createFromHandler($handler); 13421913ab3SNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 1355f891b7eSNickeau $context = $openingTag->getContext(); 13621913ab3SNickeau 13721913ab3SNickeau /** 13821913ab3SNickeau * An image in a label should have no link (ie no anchor) 13921913ab3SNickeau * because a anchor is used for navigation 14021913ab3SNickeau */ 141531e725cSNickeau $callStack->processNoLinkOnImageToEndStack(); 14221913ab3SNickeau 14321913ab3SNickeau $callStack->closeAndResetPointer(); 14421913ab3SNickeau 145531e725cSNickeau 1465f891b7eSNickeau return array( 1475f891b7eSNickeau PluginUtility::STATE => $state, 1485f891b7eSNickeau PluginUtility::CONTEXT => $context, 1495f891b7eSNickeau PluginUtility::ATTRIBUTES => $openingTag->getAttributes() 1505f891b7eSNickeau ); 1515f891b7eSNickeau 1525f891b7eSNickeau 1535f891b7eSNickeau } 1545f891b7eSNickeau return array(); 1555f891b7eSNickeau 1565f891b7eSNickeau } 1575f891b7eSNickeau 1585f891b7eSNickeau /** 1595f891b7eSNickeau * Render the output 1605f891b7eSNickeau * @param string $format 1615f891b7eSNickeau * @param Doku_Renderer $renderer 1625f891b7eSNickeau * @param array $data - what the function handle() return'ed 1635f891b7eSNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 1645f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::render() 1655f891b7eSNickeau * 1665f891b7eSNickeau * 1675f891b7eSNickeau */ 168*04fd306cSNickeau function render($format, Doku_Renderer $renderer, $data): bool 1695f891b7eSNickeau { 1705f891b7eSNickeau 1715f891b7eSNickeau if ($format == 'xhtml') { 1725f891b7eSNickeau 1735f891b7eSNickeau /** @var Doku_Renderer_xhtml $renderer */ 1745f891b7eSNickeau $state = $data[PluginUtility::STATE]; 1755f891b7eSNickeau switch ($state) { 1765f891b7eSNickeau 1775f891b7eSNickeau case DOKU_LEXER_ENTER: 1785f891b7eSNickeau 1795f891b7eSNickeau $context = $data[PluginUtility::CONTEXT]; 1805f891b7eSNickeau switch ($context) { 1815f891b7eSNickeau case syntax_plugin_combo_accordion::TAG: 1825f891b7eSNickeau $attribute = $data[PluginUtility::ATTRIBUTES]; 1835f891b7eSNickeau $headingId = $attribute[self::HEADING_ID]; 1845f891b7eSNickeau $collapseId = $attribute[self::TARGET_ID]; 1855f891b7eSNickeau $collapsed = $attribute[self::COLLAPSED]; 1865f891b7eSNickeau if ($collapsed == "false") { 1875f891b7eSNickeau $collapsedClass = "collapsed"; 188*04fd306cSNickeau $ariaExpanded = "true"; 1895f891b7eSNickeau } else { 1905f891b7eSNickeau $collapsedClass = ""; 191*04fd306cSNickeau $ariaExpanded = "false"; 1925f891b7eSNickeau } 1935f891b7eSNickeau $renderer->doc .= "<div class=\"card-header\" id=\"$headingId\">" . DOKU_LF; 1945f891b7eSNickeau $renderer->doc .= "<h2 class=\"mb-0\">"; 19521913ab3SNickeau $dataNamespace = Bootstrap::getDataNamespace(); 196*04fd306cSNickeau /** @noinspection HtmlUnknownAttribute */ 197*04fd306cSNickeau $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\">"; 1985f891b7eSNickeau break; 199*04fd306cSNickeau case TabsTag::TAG: 2005f891b7eSNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 201*04fd306cSNickeau $renderer->doc .= TabsTag::openNavigationalTabElement($attributes); 2025f891b7eSNickeau break; 203*04fd306cSNickeau case PanelTag::CONTEXT_PREVIEW_ALONE: 204*04fd306cSNickeau $attributes = PanelTag::CONTEXT_PREVIEW_ALONE_ATTRIBUTES; 20521913ab3SNickeau $renderer->doc .= "<ul style=\"list-style-type: none;padding-inline-start: 0;\">"; 206*04fd306cSNickeau $renderer->doc .= TabsTag::openNavigationalTabElement($attributes); 20721913ab3SNickeau break; 2085f891b7eSNickeau default: 20921913ab3SNickeau LogUtility::log2FrontEnd("The context ($context) of the label is unknown in enter", LogUtility::LVL_MSG_WARNING, self::TAG); 21021913ab3SNickeau 2115f891b7eSNickeau } 2125f891b7eSNickeau break; 2135f891b7eSNickeau 2145f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 21532b85071SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 2165f891b7eSNickeau break; 2175f891b7eSNickeau 2185f891b7eSNickeau case DOKU_LEXER_EXIT: 2195f891b7eSNickeau $context = $data[PluginUtility::CONTEXT]; 2205f891b7eSNickeau switch ($context) { 2215f891b7eSNickeau case syntax_plugin_combo_accordion::TAG: 2225f891b7eSNickeau $attribute = $data[PluginUtility::ATTRIBUTES]; 2235f891b7eSNickeau $collapseId = $attribute[self::TARGET_ID]; 2245f891b7eSNickeau $headingId = $attribute[self::HEADING_ID]; 2255f891b7eSNickeau $collapsed = $attribute[self::COLLAPSED]; 2265f891b7eSNickeau if ($collapsed == "false") { 2275f891b7eSNickeau $showClass = "show"; 2285f891b7eSNickeau } else { 2295f891b7eSNickeau $showClass = ""; 2305f891b7eSNickeau } 2315f891b7eSNickeau $renderer->doc .= "</button></h2></div>"; 23221913ab3SNickeau $dataNamespace = Bootstrap::getDataNamespace(); 23321913ab3SNickeau $renderer->doc .= "<div id=\"$collapseId\" class=\"collapse $showClass\" aria-labelledby=\"$headingId\" data-{$dataNamespace}parent=\"#$headingId\">"; 2345f891b7eSNickeau $renderer->doc .= "<div class=\"card-body\">" . DOKU_LF; 2355f891b7eSNickeau break; 236*04fd306cSNickeau case TabsTag::TAG: 237*04fd306cSNickeau $renderer->doc .= TabsTag::closeNavigationalTabElement(); 2385f891b7eSNickeau break; 239*04fd306cSNickeau case PanelTag::CONTEXT_PREVIEW_ALONE: 240*04fd306cSNickeau $renderer->doc .= TabsTag::closeNavigationalTabElement(); 24121913ab3SNickeau $renderer->doc .= "</ul>"; 24221913ab3SNickeau break; 2435f891b7eSNickeau default: 2445f891b7eSNickeau LogUtility::log2FrontEnd("The context ($context) of the label is unknown in exit", LogUtility::LVL_MSG_WARNING, self::TAG); 2455f891b7eSNickeau 24621913ab3SNickeau 2475f891b7eSNickeau } 2485f891b7eSNickeau break; 2495f891b7eSNickeau 2505f891b7eSNickeau 2515f891b7eSNickeau } 2525f891b7eSNickeau } 2535f891b7eSNickeau // unsupported $mode 2545f891b7eSNickeau return false; 2555f891b7eSNickeau } 2565f891b7eSNickeau 2575f891b7eSNickeau 2585f891b7eSNickeau} 2595f891b7eSNickeau 260