1<?php 2 3 4use ComboStrap\CallStack; 5use ComboStrap\ExceptionNotFound; 6use ComboStrap\HeadingTag; 7use ComboStrap\LogUtility; 8use ComboStrap\PluginUtility; 9use ComboStrap\TagAttributes; 10 11 12if (!defined('DOKU_INC')) die(); 13 14/** 15 * Atx headings 16 * https://github.github.com/gfm/#atx-headings 17 * https://spec.commonmark.org/0.29/#atx-heading 18 * http://www.aaronsw.com/2002/atx/intro 19 */ 20class syntax_plugin_combo_headingatx extends DokuWiki_Syntax_Plugin 21{ 22 23 24 const TAG = "headingatx"; 25 const EXIT_PATTERN = "\r??\n"; 26 27 28 function getType(): string 29 { 30 return 'formatting'; 31 } 32 33 /** 34 * 35 * How Dokuwiki will add P element 36 * 37 * * 'normal' - The plugin can be used inside paragraphs (inline) 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 */ 44 function getPType(): string 45 { 46 return 'block'; 47 } 48 49 /** 50 * @return array 51 * Allow which kind of plugin inside 52 * 53 * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 54 * because we manage self the content and we call self the parser 55 * 56 * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php 57 */ 58 function getAllowedTypes(): array 59 { 60 return array('formatting', 'substition', 'protected', 'disabled'); 61 } 62 63 /** 64 * 65 * @return int 66 */ 67 function getSort(): int 68 { 69 return 49; 70 } 71 72 73 function connectTo($mode) 74 { 75 76 $pattern = '\r??\n\s*#{1,6}\s?(?=.*' . self::EXIT_PATTERN . ')'; 77 $this->Lexer->addSpecialPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 78 79 } 80 81 82 function handle($match, $state, $pos, Doku_Handler $handler): array 83 { 84 85 if ($state == DOKU_LEXER_SPECIAL) { 86 $level = strlen(trim($match)); 87 $attributes = [HeadingTag::LEVEL => $level]; 88 $callStack = CallStack::createFromHandler($handler); 89 90 // Determine the type 91 $context = HeadingTag::getContext($callStack); 92 93 /** 94 * The context is needed: 95 * * to add the bootstrap class if it's a card title for instance 96 * * and to delete {@link HeadingTag::TYPE_OUTLINE} call 97 * in the {@link action_plugin_combo_headingpostprocess} (The rendering is done via Dokuwiki, 98 * see the exit processing for more info on the handling of outline headings) 99 * 100 */ 101 return array( 102 PluginUtility::STATE => $state, 103 PluginUtility::ATTRIBUTES => $attributes, 104 PluginUtility::CONTEXT => $context, 105 PluginUtility::POSITION => $pos 106 ); 107 } 108 return array(); 109 110 } 111 112 /** 113 * Render the output 114 * @param string $format 115 * @param Doku_Renderer $renderer 116 * @param array $data - what the function handle() return'ed 117 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 118 * @see DokuWiki_Syntax_Plugin::render() 119 * 120 * 121 */ 122 function render($format, Doku_Renderer $renderer, $data): bool 123 { 124 125 /** 126 * 127 * The atx special call is transformed by the {@link action_plugin_combo_headingpostprocess} 128 * into enter and exit call 129 */ 130 if ($format == 'xhtml') { 131 132 /** @var Doku_Renderer_xhtml $renderer */ 133 $state = $data[PluginUtility::STATE]; 134 $context = $data[PluginUtility::CONTEXT]; 135 switch ($state) { 136 137 case DOKU_LEXER_ENTER: 138 139 $attributes = $data[PluginUtility::ATTRIBUTES]; 140 141 $tagAttributes = TagAttributes::createFromCallStackArray($attributes, HeadingTag::HEADING_TAG); 142 $pos = $data[PluginUtility::POSITION]; 143 HeadingTag::processRenderEnterXhtml($context, $tagAttributes, $renderer, $pos); 144 return true; 145 146 147 case DOKU_LEXER_EXIT: 148 149 $attributes = $data[PluginUtility::ATTRIBUTES]; 150 $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 151 $renderer->doc .= HeadingTag::renderClosingTag($tagAttributes, $context); 152 return true; 153 154 } 155 } else if ($format == renderer_plugin_combo_analytics::RENDERER_FORMAT) { 156 157 /** 158 * @var renderer_plugin_combo_analytics $renderer 159 */ 160 HeadingTag::processMetadataAnalytics($data, $renderer); 161 162 } else if ($format == "metadata") { 163 164 /** 165 * @var Doku_Renderer_metadata $renderer 166 */ 167 HeadingTag::processHeadingEnterMetadata($data, $renderer); 168 169 } 170 171 return false; 172 } 173 174 175} 176 177