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 if ($level == 1) { 91 try { 92 $attributes["id"] = HeadingTag::getIdForLevel1(); 93 } catch (ExceptionNotFound $e) { 94 // dynamic execution 95 } 96 } 97 98 // Determine the type 99 $context = HeadingTag::getContext($callStack); 100 101 /** 102 * The context is needed: 103 * * to add the bootstrap class if it's a card title for instance 104 * * and to delete {@link HeadingTag::TYPE_OUTLINE} call 105 * in the {@link action_plugin_combo_headingpostprocess} (The rendering is done via Dokuwiki, 106 * see the exit processing for more info on the handling of outline headings) 107 * 108 */ 109 return array( 110 PluginUtility::STATE => $state, 111 PluginUtility::ATTRIBUTES => $attributes, 112 PluginUtility::CONTEXT => $context, 113 PluginUtility::POSITION => $pos 114 ); 115 } 116 return array(); 117 118 } 119 120 /** 121 * Render the output 122 * @param string $format 123 * @param Doku_Renderer $renderer 124 * @param array $data - what the function handle() return'ed 125 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 126 * @see DokuWiki_Syntax_Plugin::render() 127 * 128 * 129 */ 130 function render($format, Doku_Renderer $renderer, $data): bool 131 { 132 133 /** 134 * 135 * The atx special call is transformed by the {@link action_plugin_combo_headingpostprocess} 136 * into enter and exit call 137 */ 138 if ($format == 'xhtml') { 139 140 /** @var Doku_Renderer_xhtml $renderer */ 141 $state = $data[PluginUtility::STATE]; 142 $context = $data[PluginUtility::CONTEXT]; 143 switch ($state) { 144 145 case DOKU_LEXER_ENTER: 146 147 $attributes = $data[PluginUtility::ATTRIBUTES]; 148 149 $tagAttributes = TagAttributes::createFromCallStackArray($attributes, HeadingTag::HEADING_TAG); 150 $pos = $data[PluginUtility::POSITION]; 151 HeadingTag::processRenderEnterXhtml($context, $tagAttributes, $renderer, $pos); 152 return true; 153 154 155 case DOKU_LEXER_EXIT: 156 157 $attributes = $data[PluginUtility::ATTRIBUTES]; 158 $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 159 $renderer->doc .= HeadingTag::renderClosingTag($tagAttributes, $context); 160 return true; 161 162 } 163 } else if ($format == renderer_plugin_combo_analytics::RENDERER_FORMAT) { 164 165 /** 166 * @var renderer_plugin_combo_analytics $renderer 167 */ 168 HeadingTag::processMetadataAnalytics($data, $renderer); 169 170 } else if ($format == "metadata") { 171 172 /** 173 * @var Doku_Renderer_metadata $renderer 174 */ 175 HeadingTag::processHeadingEnterMetadata($data, $renderer); 176 177 } 178 179 return false; 180 } 181 182 183} 184 185