Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 46 |
|
0.00% |
0 / 1 |
CRAP | |
0.00% |
0 / 1 |
Poem | |
0.00% |
0 / 46 |
|
0.00% |
0 / 1 |
90 | |
0.00% |
0 / 1 |
sourceToDom | |
0.00% |
0 / 46 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Poem\Parsoid; |
5 | |
6 | use Wikimedia\Parsoid\DOM\DocumentFragment; |
7 | use Wikimedia\Parsoid\Ext\ExtensionTagHandler; |
8 | use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI; |
9 | use Wikimedia\Parsoid\Ext\PHPUtils; |
10 | use Wikimedia\Parsoid\Utils\DOMCompat; |
11 | |
12 | class Poem extends ExtensionTagHandler { |
13 | /** @inheritDoc */ |
14 | public function sourceToDom( |
15 | ParsoidExtensionAPI $extApi, string $content, array $extArgs |
16 | ): DocumentFragment { |
17 | /* |
18 | * Transform wikitext found in <poem>...</poem> |
19 | * 1. Strip leading & trailing newlines |
20 | * 2. Suppress indent-pre by replacing leading space with |
21 | * 3. Replace colons with <span class='...' style='...'>...</span> |
22 | * 4. Add <br/> for newlines except (a) in nowikis (b) after ---- |
23 | */ |
24 | |
25 | if ( strlen( $content ) > 0 ) { |
26 | // 1. above |
27 | $content = PHPUtils::stripPrefix( $content, "\n" ); |
28 | $content = PHPUtils::stripSuffix( $content, "\n" ); |
29 | |
30 | // 2. above |
31 | $content = preg_replace( '/^ /m', ' ', $content ); |
32 | |
33 | // 3. above |
34 | $contentArray = explode( "\n", $content ); |
35 | $contentMap = array_map( static function ( $line ) use ( $extApi ) { |
36 | $i = 0; |
37 | $lineLength = strlen( $line ); |
38 | while ( $i < $lineLength && $line[$i] === ':' ) { |
39 | $i++; |
40 | } |
41 | if ( $i > 0 && $i < $lineLength ) { |
42 | $domFragment = $extApi->htmlToDom( '' ); |
43 | $doc = $domFragment->ownerDocument; |
44 | $span = $doc->createElement( 'span' ); |
45 | $span->setAttribute( 'class', 'mw-poem-indented' ); |
46 | $span->setAttribute( 'style', 'display: inline-block; margin-inline-start: ' . $i . 'em;' ); |
47 | // $line isn't an HTML text node, it's wikitext that will be passed to extTagToDOM |
48 | return substr( DOMCompat::getOuterHTML( $span ), 0, -7 ) . |
49 | ltrim( $line, ':' ) . '</span>'; |
50 | } else { |
51 | return $line; |
52 | } |
53 | }, $contentArray ); |
54 | // TODO: Use faster? preg_replace |
55 | $content = implode( "\n", $contentMap ); |
56 | |
57 | // 4. above |
58 | // Split on <nowiki>..</nowiki> fragments. |
59 | // Process newlines inside nowikis in a post-processing pass. |
60 | // If <br/>s are added here, Parsoid will escape them to plaintext. |
61 | $splitContent = preg_split( '/(<nowiki>[\s\S]*?<\/nowiki>)/', $content, |
62 | -1, PREG_SPLIT_DELIM_CAPTURE ); |
63 | $content = implode( '', |
64 | array_map( static function ( $p, $i ) { |
65 | if ( $i % 2 === 1 ) { |
66 | return $p; |
67 | } |
68 | |
69 | // This is a hack that exploits the fact that </poem> |
70 | // cannot show up in the extension's content. |
71 | return preg_replace( '/^(-+)<\/poem>/m', "\$1\n", |
72 | preg_replace( '/\n/m', "<br/>\n", |
73 | preg_replace( '/(^----+)\n/m', '$1</poem>', $p ) ) ); |
74 | }, |
75 | $splitContent, |
76 | range( 0, count( $splitContent ) - 1 ) ) |
77 | ); |
78 | |
79 | } |
80 | |
81 | // Add the 'poem' class to the 'class' attribute, or if not found, add it |
82 | $value = $extApi->findAndUpdateArg( $extArgs, 'class', static function ( string $value ) { |
83 | return strlen( $value ) ? "poem {$value}" : 'poem'; |
84 | } ); |
85 | |
86 | if ( !$value ) { |
87 | $extApi->addNewArg( $extArgs, 'class', 'poem' ); |
88 | } |
89 | |
90 | return $extApi->extTagToDOM( $extArgs, $content, [ |
91 | 'wrapperTag' => 'div', |
92 | 'parseOpts' => [ 'extTag' => 'poem' ], |
93 | // Create new frame, because $content doesn't literally appear in |
94 | // the parent frame's sourceText (our copy has been munged) |
95 | 'processInNewFrame' => true, |
96 | // We've shifted the content around quite a bit when we preprocessed |
97 | // it. In the future if we wanted to enable selser inside the <poem> |
98 | // body we should create a proper offset map and then apply it to the |
99 | // result after the parse, like we do in the Gallery extension. |
100 | // But for now, since we don't selser the contents, just strip the |
101 | // DSR info so it doesn't cause problems/confusion with unicode |
102 | // offset conversion (and so it's clear you can't selser what we're |
103 | // currently emitting). |
104 | 'clearDSROffsets' => true |
105 | ] |
106 | ); |
107 | } |
108 | } |