1<?php 2 3 4require_once(__DIR__ . "/../class/Analytics.php"); 5require_once(__DIR__ . "/../class/PluginUtility.php"); 6require_once(__DIR__ . "/../class/LinkUtility.php"); 7require_once(__DIR__ . "/../class/HtmlUtility.php"); 8 9use ComboStrap\Analytics; 10use ComboStrap\Call; 11use ComboStrap\LogUtility; 12use ComboStrap\PluginUtility; 13use ComboStrap\Tag; 14use ComboStrap\TagAttributes; 15 16if (!defined('DOKU_INC')) die(); 17 18/** 19 * 20 * A paragraph syntax 21 * 22 * This syntax component is used dynamically while parsing (at the {@link DOKU_LEXER_END} of {@link \dokuwiki\Extension\SyntaxPlugin::handle()} 23 * with the function {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()} 24 * 25 * 26 * !!!!! 27 * 28 * Info: The `eol` call are temporary created with {@link \dokuwiki\Parsing\ParserMode\Eol} 29 * and transformed to `p_open` and `p_close` via {@link \dokuwiki\Parsing\Handler\Block::process()} 30 * 31 * Note: p_open call may appears when the {@link \ComboStrap\Syntax::getPType()} is set to `block` or `stack` 32 * and the next call is not a block or a stack 33 * 34 * !!!!! 35 * 36 * 37 * Note on Typography 38 * TODO: https://github.com/typekit/webfontloader 39 * https://www.dokuwiki.org/plugin:typography 40 * https://stackoverflow.design/email/base/typography/ 41 * http://kyleamathews.github.io/typography.js/ 42 * https://docs.gitbook.com/features/advanced-branding (Roboto, Roboto Slab, Open Sans, Source Sans Pro, Lato, Ubuntu, Raleway, Merriweather) 43 * http://themenectar.com/docs/salient/theme-options/typography/ (Doc) 44 * https://www.modularscale.com/ - see the size of font 45 * 46 * See the fonts on your computer 47 * https://wordmark.it/ 48 * 49 * What's a type ? Type terminology 50 * https://www.supremo.co.uk/typeterms/ 51 * 52 * https://theprotoolbox.com/browse/font-tools/ 53 */ 54class syntax_plugin_combo_para extends DokuWiki_Syntax_Plugin 55{ 56 57 const TAG = 'para'; 58 const COMPONENT = "combo_para"; 59 60 61 /** 62 * Syntax Type. 63 * 64 * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 65 * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 66 */ 67 function getType() 68 { 69 return 'paragraphs'; 70 } 71 72 /** 73 * How Dokuwiki will add P element 74 * 75 * * 'normal' - The plugin can be used inside paragraphs 76 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 77 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 78 * 79 * @see DokuWiki_Syntax_Plugin::getPType() 80 */ 81 function getPType() 82 { 83 /** 84 * !important! 85 * The {@link \dokuwiki\Parsing\Handler\Block::process()} 86 * will then not create an extra paragraph after it encounters a block 87 */ 88 return 'block'; 89 } 90 91 /** 92 * @return array 93 * Allow which kind of plugin inside 94 * 95 * No one of array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 96 * because we manage self the content and we call self the parser 97 */ 98 function getAllowedTypes() 99 { 100 /** 101 * Not needed as we don't have any {@link syntax_plugin_combo_para::connectTo()} 102 */ 103 return array(); 104 } 105 106 107 /** 108 * @see Doku_Parser_Mode::getSort() 109 * The mode with the lowest sort number will win out 110 * 111 */ 112 function getSort() 113 { 114 /** 115 * Not really needed as we don't have any {@link syntax_plugin_combo_para::connectTo()} 116 * 117 * Note: if we start to use it should be less than 370 118 * Ie Less than {@link \dokuwiki\Parsing\ParserMode\Eol::getSort()} 119 */ 120 return 369; 121 } 122 123 124 function connectTo($mode) 125 { 126 127 /** 128 * No need to connect 129 * This syntax plugin is added dynamically with the {@link Tag::processEolToEndStack()} 130 * function 131 */ 132 133 } 134 135 136 /** 137 * The handler for an internal link 138 * based on `internallink` in {@link Doku_Handler} 139 * The handler call the good renderer in {@link Doku_Renderer_xhtml} with 140 * the parameters (ie for instance internallink) 141 * @param string $match 142 * @param int $state 143 * @param int $pos 144 * @param Doku_Handler $handler 145 * @return array|bool 146 */ 147 function handle($match, $state, $pos, Doku_Handler $handler) 148 { 149 150 /** 151 * No need to handle, 152 * there is no {@link syntax_plugin_combo_para::connectTo() connection} 153 */ 154 return true; 155 156 157 } 158 159 /** 160 * Render the output 161 * @param string $format 162 * @param Doku_Renderer $renderer 163 * @param array $data - what the function handle() return'ed 164 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 165 * @see DokuWiki_Syntax_Plugin::render() 166 * 167 * 168 */ 169 function render($format, Doku_Renderer $renderer, $data) 170 { 171 // The data 172 switch ($format) { 173 case 'xhtml': 174 175 /** @var Doku_Renderer_xhtml $renderer */ 176 $state = $data[PluginUtility::STATE]; 177 switch ($state) { 178 case DOKU_LEXER_ENTER: 179 $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 180 $renderer->doc .= $tagAttributes->toHtmlEnterTag("p"); 181 break; 182 case DOKU_LEXER_SPECIAL: 183 $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 184 $renderer->doc .= $tagAttributes->toHtmlEnterTag("p"); 185 $renderer->doc .= "</p>"; 186 break; 187 case DOKU_LEXER_EXIT: 188 $renderer->doc .= "</p>"; 189 break; 190 } 191 return true; 192 193 case 'metadata': 194 195 /** @var Doku_Renderer_metadata $renderer */ 196 197 198 return true; 199 200 201 case Analytics::RENDERER_FORMAT: 202 203 /** 204 * @var renderer_plugin_combo_analytics $renderer 205 */ 206 return true; 207 208 } 209 // unsupported $mode 210 return false; 211 } 212 213 /** 214 * 215 * Transform EOL into paragraph 216 * between the {@link CallStack::getActualCall()} and the {@link CallStack::isPointerAtEnd()} 217 * of the stack 218 * 219 * Info: Basically, you get an new paragraph with a blank line or `\\` : https://www.dokuwiki.org/faq:newlines 220 * 221 * It replaces the {@link \dokuwiki\Parsing\Handler\Block::process() eol to paragraph Dokuwiki process} 222 * that takes place at the end of parsing process on the whole stack 223 * 224 * Info: The `eol` call are temporary created with {@link \dokuwiki\Parsing\ParserMode\Eol} 225 * and transformed to `p_open` and `p_close` via {@link \dokuwiki\Parsing\Handler\Block::process()} 226 * 227 * @param \ComboStrap\CallStack $callstack 228 * @param $class 229 */ 230 public static function fromEolToParagraphUntilEndOfStack(&$callstack, $class) 231 { 232 /** 233 * The attributes passed to the paragraph 234 */ 235 $attributes = array("class" => $class); 236 237 /** 238 * The syntax plugin that implements the paragraph 239 * ie {@link \syntax_plugin_combo_para} 240 * We will transform the eol with a call to this syntax plugin 241 * to create the paragraph 242 */ 243 $paragraphComponent = \syntax_plugin_combo_para::COMPONENT; 244 245 /** 246 * The running variables 247 */ 248 $paragraphIsOpen = false; // A pointer to see if the paragraph is open 249 while ($callstack->next()) { 250 251 $actualCall = $callstack->getActualCall(); 252 if ($actualCall->getTagName() === "eol") { 253 254 /** 255 * Next Call 256 */ 257 $nextCall = $callstack->next(); 258 $callstack->prev(); 259 if ($nextCall === false) { 260 $nextDisplay = "last"; 261 $nextCall = null; 262 } else { 263 $nextDisplay = $nextCall->getDisplay(); 264 } 265 266 267 /** 268 * Processing 269 */ 270 if (!$paragraphIsOpen) { 271 272 switch ($nextDisplay) { 273 case Call::BlOCK_DISPLAY: 274 case "last": 275 $callstack->deleteActualCallAndPrevious(); 276 break; 277 case Call::INLINE_DISPLAY: 278 $paragraphIsOpen = true; 279 $actualCall->updateToPluginComponent( 280 $paragraphComponent, 281 DOKU_LEXER_ENTER, 282 $attributes 283 ); 284 break; 285 case "eol": 286 /** 287 * Empty line 288 */ 289 $actualCall->updateToPluginComponent( 290 $paragraphComponent, 291 DOKU_LEXER_ENTER, 292 $attributes 293 ); 294 $nextCall->updateToPluginComponent( 295 $paragraphComponent, 296 DOKU_LEXER_EXIT 297 ); 298 $callstack->next(); 299 break; 300 default: 301 LogUtility::msg("The eol action for the combination enter / (" . $nextDisplay . ") of the call ( $nextCall ) was not implemented", LogUtility::LVL_MSG_ERROR); 302 break; 303 } 304 } else { 305 /** 306 * Paragraph is open 307 */ 308 switch ($nextDisplay) { 309 case "eol": 310 /** 311 * Empty line 312 */ 313 $actualCall->updateToPluginComponent( 314 $paragraphComponent, 315 DOKU_LEXER_EXIT 316 ); 317 $nextCall->updateToPluginComponent( 318 $paragraphComponent, 319 DOKU_LEXER_ENTER, 320 $attributes 321 ); 322 $callstack->next(); 323 break; 324 case Call::INLINE_DISPLAY: 325 // A space 326 $actualCall->updateEolToSpace(); 327 break; 328 case Call::BlOCK_DISPLAY: 329 case "last"; 330 $actualCall->updateToPluginComponent( 331 $paragraphComponent, 332 DOKU_LEXER_EXIT 333 ); 334 $paragraphIsOpen = false; 335 break; 336 default: 337 LogUtility::msg("The display for a open paragraph (" . $nextDisplay . ") is not implemented", LogUtility::LVL_MSG_ERROR); 338 break; 339 } 340 } 341 342 } 343 } 344 345 // if the paragraph is open close it 346 if ($paragraphIsOpen) { 347 $callstack->insertBefore( 348 Call::createComboCall( 349 \syntax_plugin_combo_para::TAG, 350 DOKU_LEXER_EXIT 351 ) 352 ); 353 } 354 } 355 356} 357 358