Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
Poem
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 2
20
0.00% covered (danger)
0.00%
0 / 1
 onParserFirstCallInit
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 renderPoem
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace MediaWiki\Extension\Poem;
4
5use MediaWiki\Hook\ParserFirstCallInitHook;
6use MediaWiki\Html\Html;
7use MediaWiki\Parser\Parser;
8use MediaWiki\Parser\PPFrame;
9use MediaWiki\Parser\Sanitizer;
10
11/**
12 * This class handles formatting poems in WikiText, specifically anything within
13 * <poem></poem> tags.
14 *
15 * @license CC0-1.0
16 * @author Nikola Smolenski <smolensk@eunet.yu>
17 */
18class Poem implements ParserFirstCallInitHook {
19    /**
20     * Bind the renderPoem function to the <poem> tag
21     * @param Parser $parser
22     */
23    public function onParserFirstCallInit( $parser ) {
24        $parser->setHook( 'poem', [ $this, 'renderPoem' ] );
25    }
26
27    /**
28     * Parse the text into proper poem format
29     * @param string|null $in The text inside the poem tag
30     * @param string[] $param
31     * @param Parser $parser
32     * @param PPFrame $frame
33     * @return string
34     */
35    public function renderPoem( $in, array $param, Parser $parser, PPFrame $frame ) {
36        // using newlines in the text will cause the parser to add <p> tags,
37        // which may not be desired in some cases
38        $newline = isset( $param['compact'] ) ? '' : "\n";
39
40        $tag = $parser->insertStripItem( "<br />" );
41
42        // replace colons with indented spans
43        $text = preg_replace_callback(
44            '/^(:++)(.+)$/m',
45            static function ( array $matches ) {
46                $indentation = strlen( $matches[1] ) . 'em';
47                return Html::rawElement(
48                    'span',
49                    [
50                        'class' => 'mw-poem-indented',
51                        'style' => 'display: inline-block; ' .
52                            "margin-inline-start: $indentation;",
53                    ],
54                    $matches[2]
55                );
56            },
57            $in
58        );
59
60        // replace newlines with <br /> tags unless they are at the beginning or end
61        // of the poem, or would directly follow exactly 4 dashes. See Parser::internalParse() for
62        // the exact syntax for horizontal rules.
63        $text = preg_replace(
64            [ '/^\n/', '/\n$/D', '/(?<!^----)\n/m' ],
65            [ "", "", "$tag\n" ],
66            $text
67        );
68
69        // replace spaces at the beginning of a line with non-breaking spaces
70        $text = preg_replace_callback(
71            '/^ +/m',
72            static function ( array $matches ) {
73                return str_repeat( '&#160;', strlen( $matches[0] ) );
74            },
75            $text
76        );
77
78        $text = $parser->recursiveTagParse( $text, $frame );
79
80        // Because of limitations of the regular expression above, horizontal rules with more than 4
81        // dashes still need special handling.
82        $text = str_replace( '<hr />' . $tag, '<hr />', $text );
83
84        $attribs = Sanitizer::validateTagAttributes( $param, 'div' );
85
86        // Wrap output in a <div> with "poem" class.
87        if ( isset( $attribs['class'] ) ) {
88            $attribs['class'] = 'poem ' . $attribs['class'];
89        } else {
90            $attribs['class'] = 'poem';
91        }
92
93        return Html::rawElement( 'div', $attribs, $newline . trim( $text ) . $newline );
94    }
95}