121913ab3SNickeau<?php 221913ab3SNickeau 321913ab3SNickeau 421913ab3SNickeaurequire_once(__DIR__ . "/../class/Analytics.php"); 521913ab3SNickeaurequire_once(__DIR__ . "/../class/PluginUtility.php"); 621913ab3SNickeaurequire_once(__DIR__ . "/../class/LinkUtility.php"); 721913ab3SNickeaurequire_once(__DIR__ . "/../class/HtmlUtility.php"); 821913ab3SNickeau 921913ab3SNickeauuse ComboStrap\Analytics; 1021913ab3SNickeauuse ComboStrap\Call; 1121913ab3SNickeauuse ComboStrap\LogUtility; 1221913ab3SNickeauuse ComboStrap\PluginUtility; 1321913ab3SNickeauuse ComboStrap\Tag; 1421913ab3SNickeauuse ComboStrap\TagAttributes; 1521913ab3SNickeau 1621913ab3SNickeauif (!defined('DOKU_INC')) die(); 1721913ab3SNickeau 1821913ab3SNickeau/** 1921913ab3SNickeau * 2021913ab3SNickeau * A paragraph syntax 2121913ab3SNickeau * 2221913ab3SNickeau * This syntax component is used dynamically while parsing (at the {@link DOKU_LEXER_END} of {@link \dokuwiki\Extension\SyntaxPlugin::handle()} 2321913ab3SNickeau * with the function {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()} 2421913ab3SNickeau * 2521913ab3SNickeau * 2621913ab3SNickeau * !!!!! 2721913ab3SNickeau * 2821913ab3SNickeau * Info: The `eol` call are temporary created with {@link \dokuwiki\Parsing\ParserMode\Eol} 2921913ab3SNickeau * and transformed to `p_open` and `p_close` via {@link \dokuwiki\Parsing\Handler\Block::process()} 3021913ab3SNickeau * 3121913ab3SNickeau * Note: p_open call may appears when the {@link \ComboStrap\Syntax::getPType()} is set to `block` or `stack` 3221913ab3SNickeau * and the next call is not a block or a stack 3321913ab3SNickeau * 3421913ab3SNickeau * !!!!! 3521913ab3SNickeau * 3621913ab3SNickeau * 3721913ab3SNickeau * Note on Typography 3821913ab3SNickeau * TODO: https://github.com/typekit/webfontloader 3921913ab3SNickeau * https://www.dokuwiki.org/plugin:typography 4021913ab3SNickeau * https://stackoverflow.design/email/base/typography/ 4121913ab3SNickeau * http://kyleamathews.github.io/typography.js/ 4221913ab3SNickeau * https://docs.gitbook.com/features/advanced-branding (Roboto, Roboto Slab, Open Sans, Source Sans Pro, Lato, Ubuntu, Raleway, Merriweather) 4321913ab3SNickeau * http://themenectar.com/docs/salient/theme-options/typography/ (Doc) 4421913ab3SNickeau * https://www.modularscale.com/ - see the size of font 4521913ab3SNickeau * 4621913ab3SNickeau * See the fonts on your computer 4721913ab3SNickeau * https://wordmark.it/ 4821913ab3SNickeau * 4921913ab3SNickeau * What's a type ? Type terminology 5021913ab3SNickeau * https://www.supremo.co.uk/typeterms/ 5121913ab3SNickeau * 5221913ab3SNickeau * https://theprotoolbox.com/browse/font-tools/ 5321913ab3SNickeau */ 5421913ab3SNickeauclass syntax_plugin_combo_para extends DokuWiki_Syntax_Plugin 5521913ab3SNickeau{ 5621913ab3SNickeau 5721913ab3SNickeau const TAG = 'para'; 5821913ab3SNickeau const COMPONENT = "combo_para"; 5921913ab3SNickeau 6021913ab3SNickeau 6121913ab3SNickeau /** 62*531e725cSNickeau * The component that have a `normal` {@link \dokuwiki\Extension\SyntaxPlugin::getPType()} 63*531e725cSNickeau * are wrapped in a p element by {@link Block::process()} if they are not in a component with a `block` ptype 64*531e725cSNickeau * 65*531e725cSNickeau * This function makes it easy for the test 66*531e725cSNickeau * to do it and gives a little bit of documentation 67*531e725cSNickeau * on why there is a `p` in the test 68*531e725cSNickeau * @param $html 69*531e725cSNickeau * @return string the html wrapped in p 70*531e725cSNickeau */ 71*531e725cSNickeau public static function wrapInP($html) 72*531e725cSNickeau { 73*531e725cSNickeau return "<p>" . $html . "</p>"; 74*531e725cSNickeau } 75*531e725cSNickeau 76*531e725cSNickeau 77*531e725cSNickeau /** 7821913ab3SNickeau * Syntax Type. 7921913ab3SNickeau * 8021913ab3SNickeau * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 8121913ab3SNickeau * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 8221913ab3SNickeau */ 8321913ab3SNickeau function getType() 8421913ab3SNickeau { 8521913ab3SNickeau return 'paragraphs'; 8621913ab3SNickeau } 8721913ab3SNickeau 8821913ab3SNickeau /** 8921913ab3SNickeau * How Dokuwiki will add P element 9021913ab3SNickeau * 9121913ab3SNickeau * * 'normal' - The plugin can be used inside paragraphs 9221913ab3SNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 9321913ab3SNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 9421913ab3SNickeau * 9521913ab3SNickeau * @see DokuWiki_Syntax_Plugin::getPType() 9621913ab3SNickeau */ 9721913ab3SNickeau function getPType() 9821913ab3SNickeau { 9921913ab3SNickeau /** 10021913ab3SNickeau * !important! 10121913ab3SNickeau * The {@link \dokuwiki\Parsing\Handler\Block::process()} 10221913ab3SNickeau * will then not create an extra paragraph after it encounters a block 10321913ab3SNickeau */ 10421913ab3SNickeau return 'block'; 10521913ab3SNickeau } 10621913ab3SNickeau 10721913ab3SNickeau /** 10821913ab3SNickeau * @return array 10921913ab3SNickeau * Allow which kind of plugin inside 11021913ab3SNickeau * 11121913ab3SNickeau * No one of array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 11221913ab3SNickeau * because we manage self the content and we call self the parser 11321913ab3SNickeau */ 11421913ab3SNickeau function getAllowedTypes() 11521913ab3SNickeau { 11621913ab3SNickeau /** 11721913ab3SNickeau * Not needed as we don't have any {@link syntax_plugin_combo_para::connectTo()} 11821913ab3SNickeau */ 11921913ab3SNickeau return array(); 12021913ab3SNickeau } 12121913ab3SNickeau 12221913ab3SNickeau 12321913ab3SNickeau /** 12421913ab3SNickeau * @see Doku_Parser_Mode::getSort() 12521913ab3SNickeau * The mode with the lowest sort number will win out 12621913ab3SNickeau * 12721913ab3SNickeau */ 12821913ab3SNickeau function getSort() 12921913ab3SNickeau { 13021913ab3SNickeau /** 13121913ab3SNickeau * Not really needed as we don't have any {@link syntax_plugin_combo_para::connectTo()} 13221913ab3SNickeau * 13321913ab3SNickeau * Note: if we start to use it should be less than 370 13421913ab3SNickeau * Ie Less than {@link \dokuwiki\Parsing\ParserMode\Eol::getSort()} 13521913ab3SNickeau */ 13621913ab3SNickeau return 369; 13721913ab3SNickeau } 13821913ab3SNickeau 13921913ab3SNickeau 14021913ab3SNickeau function connectTo($mode) 14121913ab3SNickeau { 14221913ab3SNickeau 14321913ab3SNickeau /** 14421913ab3SNickeau * No need to connect 14521913ab3SNickeau * This syntax plugin is added dynamically with the {@link Tag::processEolToEndStack()} 14621913ab3SNickeau * function 14721913ab3SNickeau */ 14821913ab3SNickeau 14921913ab3SNickeau } 15021913ab3SNickeau 15121913ab3SNickeau 15221913ab3SNickeau /** 15321913ab3SNickeau * The handler for an internal link 15421913ab3SNickeau * based on `internallink` in {@link Doku_Handler} 15521913ab3SNickeau * The handler call the good renderer in {@link Doku_Renderer_xhtml} with 15621913ab3SNickeau * the parameters (ie for instance internallink) 15721913ab3SNickeau * @param string $match 15821913ab3SNickeau * @param int $state 15921913ab3SNickeau * @param int $pos 16021913ab3SNickeau * @param Doku_Handler $handler 16121913ab3SNickeau * @return array|bool 16221913ab3SNickeau */ 16321913ab3SNickeau function handle($match, $state, $pos, Doku_Handler $handler) 16421913ab3SNickeau { 16521913ab3SNickeau 16621913ab3SNickeau /** 16721913ab3SNickeau * No need to handle, 16821913ab3SNickeau * there is no {@link syntax_plugin_combo_para::connectTo() connection} 16921913ab3SNickeau */ 17021913ab3SNickeau return true; 17121913ab3SNickeau 17221913ab3SNickeau 17321913ab3SNickeau } 17421913ab3SNickeau 17521913ab3SNickeau /** 17621913ab3SNickeau * Render the output 17721913ab3SNickeau * @param string $format 17821913ab3SNickeau * @param Doku_Renderer $renderer 17921913ab3SNickeau * @param array $data - what the function handle() return'ed 18021913ab3SNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 18121913ab3SNickeau * @see DokuWiki_Syntax_Plugin::render() 18221913ab3SNickeau * 18321913ab3SNickeau * 18421913ab3SNickeau */ 18521913ab3SNickeau function render($format, Doku_Renderer $renderer, $data) 18621913ab3SNickeau { 18721913ab3SNickeau // The data 18821913ab3SNickeau switch ($format) { 18921913ab3SNickeau case 'xhtml': 19021913ab3SNickeau 19121913ab3SNickeau /** @var Doku_Renderer_xhtml $renderer */ 19221913ab3SNickeau $state = $data[PluginUtility::STATE]; 193*531e725cSNickeau 19421913ab3SNickeau switch ($state) { 19521913ab3SNickeau case DOKU_LEXER_ENTER: 196*531e725cSNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 197*531e725cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 198*531e725cSNickeau if ($tagAttributes->hasComponentAttribute(TagAttributes::TYPE_KEY)) { 199*531e725cSNickeau $class = $tagAttributes->getType(); 200*531e725cSNickeau $tagAttributes->addClassName($class); 201*531e725cSNickeau } 20221913ab3SNickeau $renderer->doc .= $tagAttributes->toHtmlEnterTag("p"); 20321913ab3SNickeau break; 20421913ab3SNickeau case DOKU_LEXER_SPECIAL: 205*531e725cSNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 206*531e725cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 20721913ab3SNickeau $renderer->doc .= $tagAttributes->toHtmlEnterTag("p"); 20821913ab3SNickeau $renderer->doc .= "</p>"; 20921913ab3SNickeau break; 21021913ab3SNickeau case DOKU_LEXER_EXIT: 21121913ab3SNickeau $renderer->doc .= "</p>"; 21221913ab3SNickeau break; 21321913ab3SNickeau } 21421913ab3SNickeau return true; 21521913ab3SNickeau 21621913ab3SNickeau case 'metadata': 21721913ab3SNickeau 21821913ab3SNickeau /** @var Doku_Renderer_metadata $renderer */ 21921913ab3SNickeau 22021913ab3SNickeau 22121913ab3SNickeau return true; 22221913ab3SNickeau 22321913ab3SNickeau 224*531e725cSNickeau case renderer_plugin_combo_analytics::RENDERER_FORMAT: 22521913ab3SNickeau 22621913ab3SNickeau /** 22721913ab3SNickeau * @var renderer_plugin_combo_analytics $renderer 22821913ab3SNickeau */ 22921913ab3SNickeau return true; 23021913ab3SNickeau 23121913ab3SNickeau } 23221913ab3SNickeau // unsupported $mode 23321913ab3SNickeau return false; 23421913ab3SNickeau } 23521913ab3SNickeau 23621913ab3SNickeau /** 23721913ab3SNickeau * 23821913ab3SNickeau * Transform EOL into paragraph 23921913ab3SNickeau * between the {@link CallStack::getActualCall()} and the {@link CallStack::isPointerAtEnd()} 24021913ab3SNickeau * of the stack 24121913ab3SNickeau * 24221913ab3SNickeau * Info: Basically, you get an new paragraph with a blank line or `\\` : https://www.dokuwiki.org/faq:newlines 24321913ab3SNickeau * 24421913ab3SNickeau * It replaces the {@link \dokuwiki\Parsing\Handler\Block::process() eol to paragraph Dokuwiki process} 24521913ab3SNickeau * that takes place at the end of parsing process on the whole stack 24621913ab3SNickeau * 24721913ab3SNickeau * Info: The `eol` call are temporary created with {@link \dokuwiki\Parsing\ParserMode\Eol} 24821913ab3SNickeau * and transformed to `p_open` and `p_close` via {@link \dokuwiki\Parsing\Handler\Block::process()} 24921913ab3SNickeau * 25021913ab3SNickeau * @param \ComboStrap\CallStack $callstack 251*531e725cSNickeau * @param array $attributes - the attributes passed to the paragraph 25221913ab3SNickeau */ 253*531e725cSNickeau public static function fromEolToParagraphUntilEndOfStack(&$callstack, $attributes) 25421913ab3SNickeau { 255*531e725cSNickeau 256*531e725cSNickeau if (!is_array($attributes)) { 257*531e725cSNickeau LogUtility::msg("The passed attributes array ($attributes) for the creation of the paragraph is not an array", LogUtility::LVL_MSG_ERROR); 258*531e725cSNickeau $attributes = []; 259*531e725cSNickeau } 26021913ab3SNickeau 26121913ab3SNickeau /** 26221913ab3SNickeau * The syntax plugin that implements the paragraph 26321913ab3SNickeau * ie {@link \syntax_plugin_combo_para} 26421913ab3SNickeau * We will transform the eol with a call to this syntax plugin 26521913ab3SNickeau * to create the paragraph 26621913ab3SNickeau */ 26721913ab3SNickeau $paragraphComponent = \syntax_plugin_combo_para::COMPONENT; 268*531e725cSNickeau $paragraphTag = \syntax_plugin_combo_para::TAG; 26921913ab3SNickeau 27021913ab3SNickeau /** 27121913ab3SNickeau * The running variables 27221913ab3SNickeau */ 27321913ab3SNickeau $paragraphIsOpen = false; // A pointer to see if the paragraph is open 274*531e725cSNickeau while ($actualCall = $callstack->next()) { 27521913ab3SNickeau 276*531e725cSNickeau /** 277*531e725cSNickeau * end of line is not always present 278*531e725cSNickeau * because the pattern is eating it 279*531e725cSNickeau * Example (list_open) 280*531e725cSNickeau */ 281*531e725cSNickeau if ($paragraphIsOpen && $actualCall->getTagName() !== "eol") { 282*531e725cSNickeau if ($actualCall->getDisplay() == Call::BlOCK_DISPLAY) { 283*531e725cSNickeau $paragraphIsOpen = false; 284*531e725cSNickeau $callstack->insertBefore( 285*531e725cSNickeau Call::createComboCall( 286*531e725cSNickeau $paragraphTag, 287*531e725cSNickeau DOKU_LEXER_EXIT, 288*531e725cSNickeau $attributes 289*531e725cSNickeau ) 290*531e725cSNickeau ); 291*531e725cSNickeau } 292*531e725cSNickeau } 293*531e725cSNickeau 29421913ab3SNickeau if ($actualCall->getTagName() === "eol") { 29521913ab3SNickeau 29621913ab3SNickeau /** 297*531e725cSNickeau * Next Call that is not the empty string 298*531e725cSNickeau * Because Empty string would create an empty paragraph 299*531e725cSNickeau * 300*531e725cSNickeau * Start at 1 because we may not do 301*531e725cSNickeau * a loop if we are at the end, the next call 302*531e725cSNickeau * will return false 30321913ab3SNickeau */ 304*531e725cSNickeau $i = 1; 305*531e725cSNickeau while ($nextCall = $callstack->next()) { 306*531e725cSNickeau if (!( 307*531e725cSNickeau trim($nextCall->getCapturedContent()) == "" && 308*531e725cSNickeau $nextCall->isTextCall() 309*531e725cSNickeau )) { 310*531e725cSNickeau break; 311*531e725cSNickeau } 312*531e725cSNickeau $i++; 313*531e725cSNickeau } 314*531e725cSNickeau while ($i > 0) { // go back 315*531e725cSNickeau $i--; 316*531e725cSNickeau $callstack->previous(); 317*531e725cSNickeau } 31821913ab3SNickeau if ($nextCall === false) { 31921913ab3SNickeau $nextDisplay = "last"; 32021913ab3SNickeau $nextCall = null; 32121913ab3SNickeau } else { 32221913ab3SNickeau $nextDisplay = $nextCall->getDisplay(); 32321913ab3SNickeau } 32421913ab3SNickeau 32521913ab3SNickeau 32621913ab3SNickeau /** 32721913ab3SNickeau * Processing 32821913ab3SNickeau */ 32921913ab3SNickeau if (!$paragraphIsOpen) { 33021913ab3SNickeau 33121913ab3SNickeau switch ($nextDisplay) { 33221913ab3SNickeau case Call::BlOCK_DISPLAY: 33321913ab3SNickeau case "last": 33421913ab3SNickeau $callstack->deleteActualCallAndPrevious(); 33521913ab3SNickeau break; 33621913ab3SNickeau case Call::INLINE_DISPLAY: 33721913ab3SNickeau $paragraphIsOpen = true; 33821913ab3SNickeau $actualCall->updateToPluginComponent( 33921913ab3SNickeau $paragraphComponent, 34021913ab3SNickeau DOKU_LEXER_ENTER, 34121913ab3SNickeau $attributes 34221913ab3SNickeau ); 34321913ab3SNickeau break; 34421913ab3SNickeau case "eol": 34521913ab3SNickeau /** 34621913ab3SNickeau * Empty line 34721913ab3SNickeau */ 34821913ab3SNickeau $actualCall->updateToPluginComponent( 34921913ab3SNickeau $paragraphComponent, 35021913ab3SNickeau DOKU_LEXER_ENTER, 35121913ab3SNickeau $attributes 35221913ab3SNickeau ); 35321913ab3SNickeau $nextCall->updateToPluginComponent( 35421913ab3SNickeau $paragraphComponent, 35521913ab3SNickeau DOKU_LEXER_EXIT 35621913ab3SNickeau ); 35721913ab3SNickeau $callstack->next(); 35821913ab3SNickeau break; 35921913ab3SNickeau default: 36021913ab3SNickeau LogUtility::msg("The eol action for the combination enter / (" . $nextDisplay . ") of the call ( $nextCall ) was not implemented", LogUtility::LVL_MSG_ERROR); 36121913ab3SNickeau break; 36221913ab3SNickeau } 36321913ab3SNickeau } else { 36421913ab3SNickeau /** 36521913ab3SNickeau * Paragraph is open 36621913ab3SNickeau */ 36721913ab3SNickeau switch ($nextDisplay) { 36821913ab3SNickeau case "eol": 36921913ab3SNickeau /** 37021913ab3SNickeau * Empty line 37121913ab3SNickeau */ 37221913ab3SNickeau $actualCall->updateToPluginComponent( 37321913ab3SNickeau $paragraphComponent, 37421913ab3SNickeau DOKU_LEXER_EXIT 37521913ab3SNickeau ); 37621913ab3SNickeau $nextCall->updateToPluginComponent( 37721913ab3SNickeau $paragraphComponent, 37821913ab3SNickeau DOKU_LEXER_ENTER, 37921913ab3SNickeau $attributes 38021913ab3SNickeau ); 38121913ab3SNickeau $callstack->next(); 38221913ab3SNickeau break; 38321913ab3SNickeau case Call::INLINE_DISPLAY: 38421913ab3SNickeau // A space 38521913ab3SNickeau $actualCall->updateEolToSpace(); 38621913ab3SNickeau break; 38721913ab3SNickeau case Call::BlOCK_DISPLAY: 38821913ab3SNickeau case "last"; 38921913ab3SNickeau $actualCall->updateToPluginComponent( 39021913ab3SNickeau $paragraphComponent, 39121913ab3SNickeau DOKU_LEXER_EXIT 39221913ab3SNickeau ); 39321913ab3SNickeau $paragraphIsOpen = false; 39421913ab3SNickeau break; 39521913ab3SNickeau default: 39621913ab3SNickeau LogUtility::msg("The display for a open paragraph (" . $nextDisplay . ") is not implemented", LogUtility::LVL_MSG_ERROR); 39721913ab3SNickeau break; 39821913ab3SNickeau } 39921913ab3SNickeau } 40021913ab3SNickeau 40121913ab3SNickeau } 40221913ab3SNickeau } 40321913ab3SNickeau 40421913ab3SNickeau // if the paragraph is open close it 40521913ab3SNickeau if ($paragraphIsOpen) { 40621913ab3SNickeau $callstack->insertBefore( 40721913ab3SNickeau Call::createComboCall( 40821913ab3SNickeau \syntax_plugin_combo_para::TAG, 40921913ab3SNickeau DOKU_LEXER_EXIT 41021913ab3SNickeau ) 41121913ab3SNickeau ); 41221913ab3SNickeau } 41321913ab3SNickeau } 41421913ab3SNickeau 41521913ab3SNickeau} 41621913ab3SNickeau 417