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