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