1<?php 2 3// implementation of 4// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code 5 6// must be run within Dokuwiki 7use ComboStrap\PluginUtility; 8use ComboStrap\Prism; 9use ComboStrap\Tag; 10use ComboStrap\TagAttributes; 11 12require_once(__DIR__ . '/../class/StringUtility.php'); 13require_once(__DIR__ . '/../class/Prism.php'); 14 15if (!defined('DOKU_INC')) die(); 16 17/** 18 * Class syntax_plugin_combo_code 19 * 20 * Support <a href="https://github.github.com/gfm/#fenced-code-blocks">Github code block</a> 21 * 22 * The original code markdown code block is the {@link syntax_plugin_combo_preformatted} 23 */ 24class syntax_plugin_combo_code extends DokuWiki_Syntax_Plugin 25{ 26 27 /** 28 * Enable or disable the code component 29 */ 30 const CONF_CODE_ENABLE = 'codeEnable'; 31 32 33 /** 34 * The tag of the ui component 35 */ 36 const CODE_TAG = "code"; 37 const FILE_PATH_KEY = "file-path"; 38 39 40 function getType() 41 { 42 /** 43 * You can't write in a code block 44 */ 45 return 'protected'; 46 } 47 48 /** 49 * How DokuWiki will add P element 50 * 51 * * 'normal' - The plugin can be used inside paragraphs 52 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 53 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 54 * 55 * @see DokuWiki_Syntax_Plugin::getPType() 56 */ 57 function getPType() 58 { 59 return 'block'; 60 } 61 62 /** 63 * @return array 64 * Allow which kind of plugin inside 65 * 66 * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 67 * because we manage self the content and we call self the parser 68 * 69 * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php 70 */ 71 function getAllowedTypes() 72 { 73 return array(); 74 } 75 76 function getSort() 77 { 78 /** 79 * Should be less than the code syntax plugin 80 * which is 200 81 **/ 82 return 199; 83 } 84 85 86 function connectTo($mode) 87 { 88 89 if ($this->getConf(self::CONF_CODE_ENABLE)) { 90 $pattern = PluginUtility::getContainerTagPattern(self::CODE_TAG); 91 $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 92 } 93 94 95 } 96 97 98 function postConnect() 99 { 100 if ($this->getConf(self::CONF_CODE_ENABLE)) { 101 $this->Lexer->addExitPattern('</' . self::CODE_TAG . '>', PluginUtility::getModeForComponent($this->getPluginComponent())); 102 } 103 104 105 } 106 107 /** 108 * 109 * The handle function goal is to parse the matched syntax through the pattern function 110 * and to return the result for use in the renderer 111 * This result is always cached until the page is modified. 112 * @param string $match 113 * @param int $state 114 * @param int $pos - byte position in the original source file 115 * @param Doku_Handler $handler 116 * @return array|bool 117 * @see DokuWiki_Syntax_Plugin::handle() 118 * 119 */ 120 function handle($match, $state, $pos, Doku_Handler $handler) 121 { 122 123 switch ($state) { 124 125 case DOKU_LEXER_ENTER : 126 $tagAttributes = PluginUtility::getQualifiedTagAttributes($match, true, self::FILE_PATH_KEY); 127 return array( 128 PluginUtility::STATE => $state, 129 PluginUtility::ATTRIBUTES => $tagAttributes 130 ); 131 132 case DOKU_LEXER_UNMATCHED : 133 134 $data = PluginUtility::handleAndReturnUnmatchedData(self::CODE_TAG, $match, $handler); 135 /** 136 * Attribute are send for the 137 * export of code functionality 138 * and display = none 139 */ 140 $tag = new Tag(self::CODE_TAG, array(), $state, $handler); 141 $tagAttributes = $tag->getParent()->getAttributes(); 142 $data[PluginUtility::ATTRIBUTES] = $tagAttributes; 143 return $data; 144 145 146 case DOKU_LEXER_EXIT : 147 /** 148 * Tag Attributes are passed 149 * because it's possible to not display a code with the display attributes = none 150 */ 151 $tag = new Tag(self::CODE_TAG, array(), $state, $handler); 152 $tagAttributes = $tag->getOpeningTag()->getAttributes(); 153 return array( 154 PluginUtility::STATE => $state, 155 PluginUtility::ATTRIBUTES => $tagAttributes 156 ); 157 158 159 } 160 return array(); 161 162 } 163 164 /** 165 * Render the output 166 * @param string $format 167 * @param Doku_Renderer $renderer 168 * @param array $data - what the function handle() return'ed 169 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 170 * @see DokuWiki_Syntax_Plugin::render() 171 * 172 * 173 */ 174 function render($format, Doku_Renderer $renderer, $data) 175 { 176 177 178 if ($format == 'xhtml') { 179 180 /** @var Doku_Renderer_xhtml $renderer */ 181 $state = $data [PluginUtility::STATE]; 182 switch ($state) { 183 case DOKU_LEXER_ENTER : 184 $attributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES], self::CODE_TAG); 185 Prism::htmlEnter($renderer, $this, $attributes); 186 break; 187 188 case DOKU_LEXER_UNMATCHED : 189 190 $attributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 191 $display = $attributes->getValue("display"); 192 if ($display != "none") { 193 // Delete the eol at the beginning and end 194 // otherwise we get a big block 195 $payload = trim($data[PluginUtility::PAYLOAD], "\n\r"); 196 $renderer->doc .= PluginUtility::htmlEncode($payload); 197 } 198 break; 199 200 case DOKU_LEXER_EXIT : 201 $attributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 202 Prism::htmlExit($renderer, $attributes); 203 break; 204 205 } 206 return true; 207 } else if ($format == 'code') { 208 209 /** 210 * The renderer to download the code 211 * @var Doku_Renderer_code $renderer 212 */ 213 $state = $data [PluginUtility::STATE]; 214 if ($state == DOKU_LEXER_UNMATCHED) { 215 216 $attributes = $data[PluginUtility::ATTRIBUTES]; 217 $text = $data[PluginUtility::PAYLOAD]; 218 $filename = $attributes[self::FILE_PATH_KEY]; 219 $language = strtolower($attributes["type"]); 220 $renderer->code($text, $language, $filename); 221 222 } 223 } 224 225 // unsupported $mode 226 return false; 227 228 } 229 230 231} 232 233