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 7*21913ab3SNickeauuse ComboStrap\Bootstrap; 8*21913ab3SNickeauuse ComboStrap\CallStack; 9*21913ab3SNickeauuse ComboStrap\InternalMediaLink; 105f891b7eSNickeauuse ComboStrap\LogUtility; 115f891b7eSNickeauuse ComboStrap\PluginUtility; 125f891b7eSNickeauuse ComboStrap\Tag; 13*21913ab3SNickeauuse ComboStrap\TagAttributes; 14*21913ab3SNickeau 155f891b7eSNickeau 165f891b7eSNickeaurequire_once(__DIR__ . '/../class/HeaderUtility.php'); 175f891b7eSNickeau 185f891b7eSNickeauif (!defined('DOKU_INC')) die(); 195f891b7eSNickeau 205f891b7eSNickeau 215f891b7eSNickeauclass syntax_plugin_combo_label extends DokuWiki_Syntax_Plugin 225f891b7eSNickeau{ 235f891b7eSNickeau 245f891b7eSNickeau 255f891b7eSNickeau const TAG = "label"; 265f891b7eSNickeau 275f891b7eSNickeau /** 285f891b7eSNickeau * The id of the heading element for a accordion label 295f891b7eSNickeau */ 305f891b7eSNickeau const HEADING_ID = "headingId"; 315f891b7eSNickeau /** 325f891b7eSNickeau * The id of the collapsable target 335f891b7eSNickeau */ 345f891b7eSNickeau const TARGET_ID = "targetId"; 355f891b7eSNickeau 365f891b7eSNickeau /** 375f891b7eSNickeau * An indicator attribute that tells if the accordion is collpased or not 385f891b7eSNickeau */ 395f891b7eSNickeau const COLLAPSED = "collapsed"; 405f891b7eSNickeau 415f891b7eSNickeau function getType() 425f891b7eSNickeau { 435f891b7eSNickeau return 'formatting'; 445f891b7eSNickeau } 455f891b7eSNickeau 465f891b7eSNickeau /** 475f891b7eSNickeau * How Dokuwiki will add P element 485f891b7eSNickeau * 495f891b7eSNickeau * * 'normal' - The plugin can be used inside paragraphs (inline) 505f891b7eSNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 515f891b7eSNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 525f891b7eSNickeau * 535f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::getPType() 545f891b7eSNickeau */ 555f891b7eSNickeau function getPType() 565f891b7eSNickeau { 57*21913ab3SNickeau return 'block'; 585f891b7eSNickeau } 595f891b7eSNickeau 605f891b7eSNickeau function getAllowedTypes() 615f891b7eSNickeau { 625f891b7eSNickeau return array('substition', 'formatting', 'disabled'); 635f891b7eSNickeau } 645f891b7eSNickeau 655f891b7eSNickeau function getSort() 665f891b7eSNickeau { 675f891b7eSNickeau return 201; 685f891b7eSNickeau } 695f891b7eSNickeau 705f891b7eSNickeau 715f891b7eSNickeau function connectTo($mode) 725f891b7eSNickeau { 735f891b7eSNickeau 745f891b7eSNickeau $this->Lexer->addEntryPattern(PluginUtility::getContainerTagPattern(self::TAG), $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 755f891b7eSNickeau } 765f891b7eSNickeau 775f891b7eSNickeau public function postConnect() 785f891b7eSNickeau { 795f891b7eSNickeau $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeForComponent($this->getPluginComponent())); 805f891b7eSNickeau } 815f891b7eSNickeau 825f891b7eSNickeau function handle($match, $state, $pos, Doku_Handler $handler) 835f891b7eSNickeau { 845f891b7eSNickeau 855f891b7eSNickeau switch ($state) { 865f891b7eSNickeau 875f891b7eSNickeau case DOKU_LEXER_ENTER: 885f891b7eSNickeau $tagAttributes = PluginUtility::getTagAttributes($match); 895f891b7eSNickeau 905f891b7eSNickeau $tag = new Tag(self::TAG, $tagAttributes, $state, $handler); 915f891b7eSNickeau $parentTag = $tag->getParent(); 925f891b7eSNickeau $context = null; 935f891b7eSNickeau if ($parentTag != null) { 945f891b7eSNickeau $grandfather = $parentTag->getParent(); 955f891b7eSNickeau if ($grandfather != null) { 965f891b7eSNickeau $grandFatherName = $grandfather->getName(); 975f891b7eSNickeau switch ($grandFatherName) { 985f891b7eSNickeau case syntax_plugin_combo_accordion::TAG: 995f891b7eSNickeau $id = $parentTag->getAttribute("id"); 1005f891b7eSNickeau $tagAttributes["id"] = $id; 1015f891b7eSNickeau $tagAttributes[self::HEADING_ID] = "heading" . ucfirst($id); 1025f891b7eSNickeau $tagAttributes[self::TARGET_ID] = "collapse" . ucfirst($id); 1035f891b7eSNickeau $parentAttribute = $parentTag->getAttributes(); 1045f891b7eSNickeau if (!key_exists(self::COLLAPSED, $parentAttribute)) { 1055f891b7eSNickeau // Accordion are collapsed by default 1065f891b7eSNickeau $tagAttributes[self::COLLAPSED] = "true"; 1075f891b7eSNickeau } else { 1085f891b7eSNickeau $tagAttributes[self::COLLAPSED] = $parentAttribute[self::COLLAPSED]; 1095f891b7eSNickeau } 1105f891b7eSNickeau $context = syntax_plugin_combo_accordion::TAG; 1115f891b7eSNickeau break; 1125f891b7eSNickeau case syntax_plugin_combo_tabs::TAG: 1135f891b7eSNickeau $context = syntax_plugin_combo_tabs::TAG; 1145f891b7eSNickeau $tagAttributes = $parentTag->getAttributes(); 1155f891b7eSNickeau break; 1165f891b7eSNickeau default: 1175f891b7eSNickeau LogUtility::log2FrontEnd("The label is included in the $grandFatherName component and this is unexpected", LogUtility::LVL_MSG_WARNING, self::TAG); 1185f891b7eSNickeau } 119*21913ab3SNickeau } else { 120*21913ab3SNickeau /** 121*21913ab3SNickeau * An panel may render alone in preview 122*21913ab3SNickeau */ 123*21913ab3SNickeau if ($parentTag->getContext() == syntax_plugin_combo_panel::CONTEXT_PREVIEW_ALONE) { 124*21913ab3SNickeau $context = syntax_plugin_combo_panel::CONTEXT_PREVIEW_ALONE; 125*21913ab3SNickeau } 1265f891b7eSNickeau } 1275f891b7eSNickeau } 1285f891b7eSNickeau 1295f891b7eSNickeau return array( 1305f891b7eSNickeau PluginUtility::STATE => $state, 1315f891b7eSNickeau PluginUtility::ATTRIBUTES => $tagAttributes, 1325f891b7eSNickeau PluginUtility::CONTEXT => $context 1335f891b7eSNickeau ); 1345f891b7eSNickeau 1355f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 13632b85071SNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 1375f891b7eSNickeau 1385f891b7eSNickeau case DOKU_LEXER_EXIT : 139*21913ab3SNickeau $callStack = CallStack::createFromHandler($handler); 140*21913ab3SNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 1415f891b7eSNickeau $context = $openingTag->getContext(); 142*21913ab3SNickeau 143*21913ab3SNickeau /** 144*21913ab3SNickeau * An image in a label should have no link (ie no anchor) 145*21913ab3SNickeau * because a anchor is used for navigation 146*21913ab3SNickeau */ 147*21913ab3SNickeau while ($callStack->next()) { 148*21913ab3SNickeau $actualCall = $callStack->getActualCall(); 149*21913ab3SNickeau if ($actualCall->getTagName() == syntax_plugin_combo_media::TAG) { 150*21913ab3SNickeau $actualCall->addAttribute(TagAttributes::LINKING_KEY, InternalMediaLink::LINKING_NOLINK_VALUE); 151*21913ab3SNickeau } 152*21913ab3SNickeau } 153*21913ab3SNickeau 154*21913ab3SNickeau $callStack->closeAndResetPointer(); 155*21913ab3SNickeau 1565f891b7eSNickeau return array( 1575f891b7eSNickeau PluginUtility::STATE => $state, 1585f891b7eSNickeau PluginUtility::CONTEXT => $context, 1595f891b7eSNickeau PluginUtility::ATTRIBUTES => $openingTag->getAttributes() 1605f891b7eSNickeau ); 1615f891b7eSNickeau 1625f891b7eSNickeau 1635f891b7eSNickeau } 1645f891b7eSNickeau return array(); 1655f891b7eSNickeau 1665f891b7eSNickeau } 1675f891b7eSNickeau 1685f891b7eSNickeau /** 1695f891b7eSNickeau * Render the output 1705f891b7eSNickeau * @param string $format 1715f891b7eSNickeau * @param Doku_Renderer $renderer 1725f891b7eSNickeau * @param array $data - what the function handle() return'ed 1735f891b7eSNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 1745f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::render() 1755f891b7eSNickeau * 1765f891b7eSNickeau * 1775f891b7eSNickeau */ 1785f891b7eSNickeau function render($format, Doku_Renderer $renderer, $data) 1795f891b7eSNickeau { 1805f891b7eSNickeau 1815f891b7eSNickeau if ($format == 'xhtml') { 1825f891b7eSNickeau 1835f891b7eSNickeau /** @var Doku_Renderer_xhtml $renderer */ 1845f891b7eSNickeau $state = $data[PluginUtility::STATE]; 1855f891b7eSNickeau switch ($state) { 1865f891b7eSNickeau 1875f891b7eSNickeau case DOKU_LEXER_ENTER: 1885f891b7eSNickeau 1895f891b7eSNickeau $context = $data[PluginUtility::CONTEXT]; 1905f891b7eSNickeau switch ($context) { 1915f891b7eSNickeau case syntax_plugin_combo_accordion::TAG: 1925f891b7eSNickeau $attribute = $data[PluginUtility::ATTRIBUTES]; 1935f891b7eSNickeau $headingId = $attribute[self::HEADING_ID]; 1945f891b7eSNickeau $collapseId = $attribute[self::TARGET_ID]; 1955f891b7eSNickeau $collapsed = $attribute[self::COLLAPSED]; 1965f891b7eSNickeau if ($collapsed == "false") { 1975f891b7eSNickeau $collapsedClass = "collapsed"; 1985f891b7eSNickeau } else { 1995f891b7eSNickeau $collapsedClass = ""; 2005f891b7eSNickeau } 2015f891b7eSNickeau $renderer->doc .= "<div class=\"card-header\" id=\"$headingId\">" . DOKU_LF; 2025f891b7eSNickeau $renderer->doc .= "<h2 class=\"mb-0\">"; 203*21913ab3SNickeau $dataNamespace = Bootstrap::getDataNamespace(); 204*21913ab3SNickeau $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\">"; 2055f891b7eSNickeau break; 2065f891b7eSNickeau case syntax_plugin_combo_tabs::TAG: 2075f891b7eSNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 2085f891b7eSNickeau $renderer->doc .= syntax_plugin_combo_tabs::openNavigationalTabElement($attributes); 2095f891b7eSNickeau break; 210*21913ab3SNickeau case syntax_plugin_combo_panel::CONTEXT_PREVIEW_ALONE: 211*21913ab3SNickeau $attributes = syntax_plugin_combo_panel::CONTEXT_PREVIEW_ALONE_ATTRIBUTES; 212*21913ab3SNickeau $renderer->doc .= "<ul style=\"list-style-type: none;padding-inline-start: 0;\">"; 213*21913ab3SNickeau $renderer->doc .= syntax_plugin_combo_tabs::openNavigationalTabElement($attributes); 214*21913ab3SNickeau break; 2155f891b7eSNickeau default: 216*21913ab3SNickeau LogUtility::log2FrontEnd("The context ($context) of the label is unknown in enter", LogUtility::LVL_MSG_WARNING, self::TAG); 217*21913ab3SNickeau 2185f891b7eSNickeau } 2195f891b7eSNickeau break; 2205f891b7eSNickeau 2215f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 22232b85071SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 2235f891b7eSNickeau break; 2245f891b7eSNickeau 2255f891b7eSNickeau case DOKU_LEXER_EXIT: 2265f891b7eSNickeau $context = $data[PluginUtility::CONTEXT]; 2275f891b7eSNickeau switch ($context) { 2285f891b7eSNickeau case syntax_plugin_combo_accordion::TAG: 2295f891b7eSNickeau $attribute = $data[PluginUtility::ATTRIBUTES]; 2305f891b7eSNickeau $collapseId = $attribute[self::TARGET_ID]; 2315f891b7eSNickeau $headingId = $attribute[self::HEADING_ID]; 2325f891b7eSNickeau $collapsed = $attribute[self::COLLAPSED]; 2335f891b7eSNickeau if ($collapsed == "false") { 2345f891b7eSNickeau $showClass = "show"; 2355f891b7eSNickeau } else { 2365f891b7eSNickeau $showClass = ""; 2375f891b7eSNickeau } 2385f891b7eSNickeau $renderer->doc .= "</button></h2></div>"; 239*21913ab3SNickeau $dataNamespace = Bootstrap::getDataNamespace(); 240*21913ab3SNickeau $renderer->doc .= "<div id=\"$collapseId\" class=\"collapse $showClass\" aria-labelledby=\"$headingId\" data-{$dataNamespace}parent=\"#$headingId\">"; 2415f891b7eSNickeau $renderer->doc .= "<div class=\"card-body\">" . DOKU_LF; 2425f891b7eSNickeau break; 2435f891b7eSNickeau case syntax_plugin_combo_tabs::TAG: 2445f891b7eSNickeau $renderer->doc .= syntax_plugin_combo_tabs::closeNavigationalTabElement(); 2455f891b7eSNickeau break; 246*21913ab3SNickeau case syntax_plugin_combo_panel::CONTEXT_PREVIEW_ALONE: 247*21913ab3SNickeau $renderer->doc .= syntax_plugin_combo_tabs::closeNavigationalTabElement(); 248*21913ab3SNickeau $renderer->doc .= "</ul>"; 249*21913ab3SNickeau break; 2505f891b7eSNickeau default: 2515f891b7eSNickeau LogUtility::log2FrontEnd("The context ($context) of the label is unknown in exit", LogUtility::LVL_MSG_WARNING, self::TAG); 2525f891b7eSNickeau 253*21913ab3SNickeau 2545f891b7eSNickeau } 2555f891b7eSNickeau break; 2565f891b7eSNickeau 2575f891b7eSNickeau 2585f891b7eSNickeau } 2595f891b7eSNickeau } 2605f891b7eSNickeau // unsupported $mode 2615f891b7eSNickeau return false; 2625f891b7eSNickeau } 2635f891b7eSNickeau 2645f891b7eSNickeau 2655f891b7eSNickeau} 2665f891b7eSNickeau 267