121913ab3SNickeau<?php 221913ab3SNickeau 321913ab3SNickeau 437748cd8SNickeaurequire_once(__DIR__ . "/../ComboStrap/PluginUtility.php"); 5c3437056SNickeau 621913ab3SNickeau 721913ab3SNickeauuse ComboStrap\Call; 89337a630SNickeauuse ComboStrap\CallStack; 921913ab3SNickeauuse ComboStrap\LogUtility; 1021913ab3SNickeauuse ComboStrap\PluginUtility; 1121913ab3SNickeauuse ComboStrap\TagAttributes; 1221913ab3SNickeau 1321913ab3SNickeau 1421913ab3SNickeau/** 1521913ab3SNickeau * 1621913ab3SNickeau * A paragraph syntax 1721913ab3SNickeau * 1821913ab3SNickeau * This syntax component is used dynamically while parsing (at the {@link DOKU_LEXER_END} of {@link \dokuwiki\Extension\SyntaxPlugin::handle()} 1921913ab3SNickeau * with the function {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()} 2021913ab3SNickeau * 2121913ab3SNickeau * 2221913ab3SNickeau * !!!!! 2321913ab3SNickeau * 2421913ab3SNickeau * Info: The `eol` call are temporary created with {@link \dokuwiki\Parsing\ParserMode\Eol} 2521913ab3SNickeau * and transformed to `p_open` and `p_close` via {@link \dokuwiki\Parsing\Handler\Block::process()} 2621913ab3SNickeau * 2721913ab3SNickeau * Note: p_open call may appears when the {@link \ComboStrap\Syntax::getPType()} is set to `block` or `stack` 2821913ab3SNickeau * and the next call is not a block or a stack 2921913ab3SNickeau * 3021913ab3SNickeau * !!!!! 3121913ab3SNickeau * 3221913ab3SNickeau * 3321913ab3SNickeau * Note on Typography 3421913ab3SNickeau * TODO: https://github.com/typekit/webfontloader 3521913ab3SNickeau * https://www.dokuwiki.org/plugin:typography 3621913ab3SNickeau * https://stackoverflow.design/email/base/typography/ 3721913ab3SNickeau * http://kyleamathews.github.io/typography.js/ 3821913ab3SNickeau * https://docs.gitbook.com/features/advanced-branding (Roboto, Roboto Slab, Open Sans, Source Sans Pro, Lato, Ubuntu, Raleway, Merriweather) 3921913ab3SNickeau * http://themenectar.com/docs/salient/theme-options/typography/ (Doc) 4021913ab3SNickeau * https://www.modularscale.com/ - see the size of font 4121913ab3SNickeau * 4221913ab3SNickeau * See the fonts on your computer 4321913ab3SNickeau * https://wordmark.it/ 4421913ab3SNickeau * 4521913ab3SNickeau * What's a type ? Type terminology 4621913ab3SNickeau * https://www.supremo.co.uk/typeterms/ 4721913ab3SNickeau * 4821913ab3SNickeau * https://theprotoolbox.com/browse/font-tools/ 4921913ab3SNickeau */ 5021913ab3SNickeauclass syntax_plugin_combo_para extends DokuWiki_Syntax_Plugin 5121913ab3SNickeau{ 5221913ab3SNickeau 5321913ab3SNickeau const TAG = 'para'; 5421913ab3SNickeau const COMPONENT = "combo_para"; 5521913ab3SNickeau 5621913ab3SNickeau 5721913ab3SNickeau /** 58531e725cSNickeau * The component that have a `normal` {@link \dokuwiki\Extension\SyntaxPlugin::getPType()} 59531e725cSNickeau * are wrapped in a p element by {@link Block::process()} if they are not in a component with a `block` ptype 60531e725cSNickeau * 61531e725cSNickeau * This function makes it easy for the test 62531e725cSNickeau * to do it and gives a little bit of documentation 63531e725cSNickeau * on why there is a `p` in the test 64531e725cSNickeau * @param $html 65531e725cSNickeau * @return string the html wrapped in p 66531e725cSNickeau */ 67531e725cSNickeau public static function wrapInP($html) 68531e725cSNickeau { 69531e725cSNickeau return "<p>" . $html . "</p>"; 70531e725cSNickeau } 71531e725cSNickeau 72531e725cSNickeau 73531e725cSNickeau /** 7421913ab3SNickeau * Syntax Type. 7521913ab3SNickeau * 7621913ab3SNickeau * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 7721913ab3SNickeau * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 7821913ab3SNickeau */ 7904fd306cSNickeau function getType(): string 8021913ab3SNickeau { 8104fd306cSNickeau /** 8204fd306cSNickeau * Not `paragraphs' because we don't allow them in {@link syntax_plugin_combo_xmlblocktag} 8304fd306cSNickeau */ 8404fd306cSNickeau return 'formatting'; 8521913ab3SNickeau } 8621913ab3SNickeau 8721913ab3SNickeau /** 8821913ab3SNickeau * How Dokuwiki will add P element 8921913ab3SNickeau * 9021913ab3SNickeau * * 'normal' - The plugin can be used inside paragraphs 9121913ab3SNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 9221913ab3SNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 9321913ab3SNickeau * 9421913ab3SNickeau * @see DokuWiki_Syntax_Plugin::getPType() 9521913ab3SNickeau */ 9621913ab3SNickeau function getPType() 9721913ab3SNickeau { 9821913ab3SNickeau /** 9921913ab3SNickeau * !important! 10021913ab3SNickeau * The {@link \dokuwiki\Parsing\Handler\Block::process()} 10121913ab3SNickeau * will then not create an extra paragraph after it encounters a block 10221913ab3SNickeau */ 10321913ab3SNickeau return 'block'; 10421913ab3SNickeau } 10521913ab3SNickeau 10621913ab3SNickeau /** 10721913ab3SNickeau * @return array 10821913ab3SNickeau * Allow which kind of plugin inside 10921913ab3SNickeau * 11021913ab3SNickeau * No one of array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 11121913ab3SNickeau * because we manage self the content and we call self the parser 11221913ab3SNickeau */ 11321913ab3SNickeau function getAllowedTypes() 11421913ab3SNickeau { 11521913ab3SNickeau /** 11621913ab3SNickeau * Not needed as we don't have any {@link syntax_plugin_combo_para::connectTo()} 11721913ab3SNickeau */ 11821913ab3SNickeau return array(); 11921913ab3SNickeau } 12021913ab3SNickeau 12121913ab3SNickeau 12221913ab3SNickeau /** 12321913ab3SNickeau * @see Doku_Parser_Mode::getSort() 12421913ab3SNickeau * The mode with the lowest sort number will win out 12521913ab3SNickeau * 12621913ab3SNickeau */ 12721913ab3SNickeau function getSort() 12821913ab3SNickeau { 12921913ab3SNickeau /** 13021913ab3SNickeau * Not really needed as we don't have any {@link syntax_plugin_combo_para::connectTo()} 13121913ab3SNickeau * 13221913ab3SNickeau * Note: if we start to use it should be less than 370 13321913ab3SNickeau * Ie Less than {@link \dokuwiki\Parsing\ParserMode\Eol::getSort()} 13421913ab3SNickeau */ 13521913ab3SNickeau return 369; 13621913ab3SNickeau } 13721913ab3SNickeau 13821913ab3SNickeau 13921913ab3SNickeau function connectTo($mode) 14021913ab3SNickeau { 14121913ab3SNickeau 14221913ab3SNickeau /** 14321913ab3SNickeau * No need to connect 14404fd306cSNickeau * This syntax plugin is added dynamically with the {@link CallStack::processEolToEndStack()} 14521913ab3SNickeau * function 14621913ab3SNickeau */ 14721913ab3SNickeau 14821913ab3SNickeau } 14921913ab3SNickeau 15021913ab3SNickeau 15121913ab3SNickeau /** 15221913ab3SNickeau * The handler for an internal link 15321913ab3SNickeau * based on `internallink` in {@link Doku_Handler} 15421913ab3SNickeau * The handler call the good renderer in {@link Doku_Renderer_xhtml} with 15521913ab3SNickeau * the parameters (ie for instance internallink) 15621913ab3SNickeau * @param string $match 15721913ab3SNickeau * @param int $state 15821913ab3SNickeau * @param int $pos 15921913ab3SNickeau * @param Doku_Handler $handler 16021913ab3SNickeau * @return array|bool 16121913ab3SNickeau */ 16221913ab3SNickeau function handle($match, $state, $pos, Doku_Handler $handler) 16321913ab3SNickeau { 16421913ab3SNickeau 16521913ab3SNickeau /** 16621913ab3SNickeau * No need to handle, 16721913ab3SNickeau * there is no {@link syntax_plugin_combo_para::connectTo() connection} 16821913ab3SNickeau */ 16921913ab3SNickeau return true; 17021913ab3SNickeau 17121913ab3SNickeau 17221913ab3SNickeau } 17321913ab3SNickeau 17421913ab3SNickeau /** 17521913ab3SNickeau * Render the output 17621913ab3SNickeau * @param string $format 17721913ab3SNickeau * @param Doku_Renderer $renderer 17821913ab3SNickeau * @param array $data - what the function handle() return'ed 17921913ab3SNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 18021913ab3SNickeau * @see DokuWiki_Syntax_Plugin::render() 18121913ab3SNickeau * 18221913ab3SNickeau * 18321913ab3SNickeau */ 1844cadd4f8SNickeau function render($format, Doku_Renderer $renderer, $data): bool 18521913ab3SNickeau { 18621913ab3SNickeau // The data 18721913ab3SNickeau switch ($format) { 18821913ab3SNickeau case 'xhtml': 18921913ab3SNickeau 19021913ab3SNickeau /** @var Doku_Renderer_xhtml $renderer */ 19121913ab3SNickeau $state = $data[PluginUtility::STATE]; 192531e725cSNickeau 19321913ab3SNickeau switch ($state) { 19421913ab3SNickeau case DOKU_LEXER_ENTER: 195531e725cSNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 196531e725cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 197531e725cSNickeau if ($tagAttributes->hasComponentAttribute(TagAttributes::TYPE_KEY)) { 198531e725cSNickeau $class = $tagAttributes->getType(); 199531e725cSNickeau $tagAttributes->addClassName($class); 200531e725cSNickeau } 20121913ab3SNickeau $renderer->doc .= $tagAttributes->toHtmlEnterTag("p"); 20221913ab3SNickeau break; 20321913ab3SNickeau case DOKU_LEXER_SPECIAL: 204531e725cSNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 205531e725cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 20621913ab3SNickeau $renderer->doc .= $tagAttributes->toHtmlEnterTag("p"); 20721913ab3SNickeau $renderer->doc .= "</p>"; 20821913ab3SNickeau break; 20921913ab3SNickeau case DOKU_LEXER_EXIT: 21021913ab3SNickeau $renderer->doc .= "</p>"; 21121913ab3SNickeau break; 21221913ab3SNickeau } 21321913ab3SNickeau return true; 21421913ab3SNickeau 21521913ab3SNickeau case 'metadata': 21621913ab3SNickeau 21721913ab3SNickeau /** @var Doku_Renderer_metadata $renderer */ 21821913ab3SNickeau 21921913ab3SNickeau 22021913ab3SNickeau return true; 22121913ab3SNickeau 22221913ab3SNickeau 223531e725cSNickeau case renderer_plugin_combo_analytics::RENDERER_FORMAT: 22421913ab3SNickeau 22521913ab3SNickeau /** 22621913ab3SNickeau * @var renderer_plugin_combo_analytics $renderer 22721913ab3SNickeau */ 22821913ab3SNickeau return true; 22921913ab3SNickeau 23021913ab3SNickeau } 23121913ab3SNickeau // unsupported $mode 23221913ab3SNickeau return false; 23321913ab3SNickeau } 23421913ab3SNickeau 23521913ab3SNickeau /** 23621913ab3SNickeau * 23721913ab3SNickeau * Transform EOL into paragraph 23821913ab3SNickeau * between the {@link CallStack::getActualCall()} and the {@link CallStack::isPointerAtEnd()} 23921913ab3SNickeau * of the stack 24021913ab3SNickeau * 24121913ab3SNickeau * Info: Basically, you get an new paragraph with a blank line or `\\` : https://www.dokuwiki.org/faq:newlines 24221913ab3SNickeau * 24321913ab3SNickeau * It replaces the {@link \dokuwiki\Parsing\Handler\Block::process() eol to paragraph Dokuwiki process} 24421913ab3SNickeau * that takes place at the end of parsing process on the whole stack 24521913ab3SNickeau * 24621913ab3SNickeau * Info: The `eol` call are temporary created with {@link \dokuwiki\Parsing\ParserMode\Eol} 24721913ab3SNickeau * and transformed to `p_open` and `p_close` via {@link \dokuwiki\Parsing\Handler\Block::process()} 24821913ab3SNickeau * 2499337a630SNickeau * @param CallStack $callstack 250531e725cSNickeau * @param array $attributes - the attributes passed to the paragraph 25121913ab3SNickeau */ 2521fa8c418SNickeau public static function fromEolToParagraphUntilEndOfStack(CallStack &$callstack, array $attributes) 25321913ab3SNickeau { 254531e725cSNickeau 255531e725cSNickeau if (!is_array($attributes)) { 256531e725cSNickeau LogUtility::msg("The passed attributes array ($attributes) for the creation of the paragraph is not an array", LogUtility::LVL_MSG_ERROR); 257531e725cSNickeau $attributes = []; 258531e725cSNickeau } 25921913ab3SNickeau 26021913ab3SNickeau /** 26121913ab3SNickeau * The syntax plugin that implements the paragraph 26221913ab3SNickeau * ie {@link \syntax_plugin_combo_para} 26321913ab3SNickeau * We will transform the eol with a call to this syntax plugin 26421913ab3SNickeau * to create the paragraph 26521913ab3SNickeau */ 26621913ab3SNickeau $paragraphComponent = \syntax_plugin_combo_para::COMPONENT; 267531e725cSNickeau $paragraphTag = \syntax_plugin_combo_para::TAG; 26821913ab3SNickeau 26921913ab3SNickeau /** 27021913ab3SNickeau * The running variables 27121913ab3SNickeau */ 27221913ab3SNickeau $paragraphIsOpen = false; // A pointer to see if the paragraph is open 273531e725cSNickeau while ($actualCall = $callstack->next()) { 27421913ab3SNickeau 275531e725cSNickeau /** 276531e725cSNickeau * end of line is not always present 277531e725cSNickeau * because the pattern is eating it 278531e725cSNickeau * Example (list_open) 279531e725cSNickeau */ 280531e725cSNickeau if ($paragraphIsOpen && $actualCall->getTagName() !== "eol") { 281531e725cSNickeau if ($actualCall->getDisplay() == Call::BlOCK_DISPLAY) { 282531e725cSNickeau $paragraphIsOpen = false; 283531e725cSNickeau $callstack->insertBefore( 284531e725cSNickeau Call::createComboCall( 285531e725cSNickeau $paragraphTag, 286531e725cSNickeau DOKU_LEXER_EXIT, 287531e725cSNickeau $attributes 288531e725cSNickeau ) 289531e725cSNickeau ); 290531e725cSNickeau } 291531e725cSNickeau } 292531e725cSNickeau 29321913ab3SNickeau if ($actualCall->getTagName() === "eol") { 29421913ab3SNickeau 29521913ab3SNickeau /** 296531e725cSNickeau * Next Call that is not the empty string 297531e725cSNickeau * Because Empty string would create an empty paragraph 298531e725cSNickeau * 299531e725cSNickeau * Start at 1 because we may not do 300531e725cSNickeau * a loop if we are at the end, the next call 301531e725cSNickeau * will return false 30221913ab3SNickeau */ 303531e725cSNickeau $i = 1; 304531e725cSNickeau while ($nextCall = $callstack->next()) { 305*70bbd7f1Sgerardnico $capturedContent = $nextCall->getCapturedContent(); 306*70bbd7f1Sgerardnico $captureContentisEmpty = $capturedContent===null || trim($capturedContent) == ""; 307531e725cSNickeau if (!( 308*70bbd7f1Sgerardnico $captureContentisEmpty && 309531e725cSNickeau $nextCall->isTextCall() 310531e725cSNickeau )) { 311531e725cSNickeau break; 312531e725cSNickeau } 313531e725cSNickeau $i++; 314531e725cSNickeau } 315531e725cSNickeau while ($i > 0) { // go back 316531e725cSNickeau $i--; 317531e725cSNickeau $callstack->previous(); 318531e725cSNickeau } 31921913ab3SNickeau if ($nextCall === false) { 32021913ab3SNickeau $nextDisplay = "last"; 32121913ab3SNickeau $nextCall = null; 32221913ab3SNickeau } else { 32321913ab3SNickeau $nextDisplay = $nextCall->getDisplay(); 32421913ab3SNickeau } 32521913ab3SNickeau 32621913ab3SNickeau 32721913ab3SNickeau /** 32821913ab3SNickeau * Processing 32921913ab3SNickeau */ 33021913ab3SNickeau if (!$paragraphIsOpen) { 33121913ab3SNickeau 33221913ab3SNickeau switch ($nextDisplay) { 33321913ab3SNickeau case Call::BlOCK_DISPLAY: 33421913ab3SNickeau case "last": 33521913ab3SNickeau $callstack->deleteActualCallAndPrevious(); 33621913ab3SNickeau break; 33721913ab3SNickeau case Call::INLINE_DISPLAY: 33821913ab3SNickeau $paragraphIsOpen = true; 33921913ab3SNickeau $actualCall->updateToPluginComponent( 34021913ab3SNickeau $paragraphComponent, 34121913ab3SNickeau DOKU_LEXER_ENTER, 34221913ab3SNickeau $attributes 34321913ab3SNickeau ); 34421913ab3SNickeau break; 34521913ab3SNickeau case "eol": 34621913ab3SNickeau /** 34721913ab3SNickeau * Empty line 34821913ab3SNickeau */ 34921913ab3SNickeau $actualCall->updateToPluginComponent( 35021913ab3SNickeau $paragraphComponent, 35121913ab3SNickeau DOKU_LEXER_ENTER, 35221913ab3SNickeau $attributes 35321913ab3SNickeau ); 35421913ab3SNickeau $nextCall->updateToPluginComponent( 35521913ab3SNickeau $paragraphComponent, 35621913ab3SNickeau DOKU_LEXER_EXIT 35721913ab3SNickeau ); 35821913ab3SNickeau $callstack->next(); 35921913ab3SNickeau break; 36021913ab3SNickeau default: 36121913ab3SNickeau LogUtility::msg("The eol action for the combination enter / (" . $nextDisplay . ") of the call ( $nextCall ) was not implemented", LogUtility::LVL_MSG_ERROR); 36221913ab3SNickeau break; 36321913ab3SNickeau } 36421913ab3SNickeau } else { 36521913ab3SNickeau /** 36621913ab3SNickeau * Paragraph is open 36721913ab3SNickeau */ 36821913ab3SNickeau switch ($nextDisplay) { 36921913ab3SNickeau case "eol": 37021913ab3SNickeau /** 37121913ab3SNickeau * Empty line 37221913ab3SNickeau */ 37321913ab3SNickeau $actualCall->updateToPluginComponent( 37421913ab3SNickeau $paragraphComponent, 37521913ab3SNickeau DOKU_LEXER_EXIT 37621913ab3SNickeau ); 37721913ab3SNickeau $nextCall->updateToPluginComponent( 37821913ab3SNickeau $paragraphComponent, 37921913ab3SNickeau DOKU_LEXER_ENTER, 38021913ab3SNickeau $attributes 38121913ab3SNickeau ); 38221913ab3SNickeau $callstack->next(); 38321913ab3SNickeau break; 38421913ab3SNickeau case Call::INLINE_DISPLAY: 38521913ab3SNickeau // A space 38621913ab3SNickeau $actualCall->updateEolToSpace(); 38721913ab3SNickeau break; 38821913ab3SNickeau case Call::BlOCK_DISPLAY: 38921913ab3SNickeau case "last"; 39021913ab3SNickeau $actualCall->updateToPluginComponent( 39121913ab3SNickeau $paragraphComponent, 39221913ab3SNickeau DOKU_LEXER_EXIT 39321913ab3SNickeau ); 39421913ab3SNickeau $paragraphIsOpen = false; 39521913ab3SNickeau break; 39621913ab3SNickeau default: 39721913ab3SNickeau LogUtility::msg("The display for a open paragraph (" . $nextDisplay . ") is not implemented", LogUtility::LVL_MSG_ERROR); 39821913ab3SNickeau break; 39921913ab3SNickeau } 40021913ab3SNickeau } 40121913ab3SNickeau 40221913ab3SNickeau } 40321913ab3SNickeau } 40421913ab3SNickeau 40521913ab3SNickeau // if the paragraph is open close it 40621913ab3SNickeau if ($paragraphIsOpen) { 40721913ab3SNickeau $callstack->insertBefore( 40821913ab3SNickeau Call::createComboCall( 40921913ab3SNickeau \syntax_plugin_combo_para::TAG, 41021913ab3SNickeau DOKU_LEXER_EXIT 41121913ab3SNickeau ) 41221913ab3SNickeau ); 41321913ab3SNickeau } 41421913ab3SNickeau } 41521913ab3SNickeau 41621913ab3SNickeau} 41721913ab3SNickeau 418