'Poem',
'domProcessors' => [
PoemProcessor::class,
],
'tags' => [
[
'name' => 'poem',
'handler' => self::class,
]
]
];
}
/**
* @param ParsoidExtensionAPI|null $extApi
*/
public function __construct( ParsoidExtensionAPI $extApi = null ) {
/* @phan-suppress-previous-line PhanEmptyPublicMethod */
/* The dom post processor doesn't need to use $extApi, so ignore it */
}
/** @inheritDoc */
public function sourceToDom(
ParsoidExtensionAPI $extApi, string $content, array $extArgs
): DOMDocument {
/*
* Transform wikitext found in ...
* 1. Strip leading & trailing newlines
* 2. Suppress indent-pre by replacing leading space with
* 3. Replace colons with ...
* 4. Add
for newlines except (a) in nowikis (b) after ----
*/
if ( strlen( $content ) > 0 ) {
// 1. above
$content = preg_replace( '/^\n/', '', $content, 1 );
$content = preg_replace( '/\n$/D', '', $content, 1 );
// 2. above
$content = preg_replace( '/^ /m', ' ', $content );
// 3. above
$contentArray = explode( "\n", $content );
$contentMap = array_map( function ( $line ) use ( $extApi ) {
$i = 0;
$lineLength = strlen( $line );
while ( $i < $lineLength && $line[$i] === ':' ) {
$i++;
}
if ( $i > 0 && $i < $lineLength ) {
$doc = $extApi->htmlToDom( '' ); // Empty doc
$span = $doc->createElement( 'span' );
$span->setAttribute( 'class', 'mw-poem-indented' );
$span->setAttribute( 'style', 'display: inline-block; margin-inline-start: ' . $i . 'em;' );
$span->appendChild( $doc->createTextNode( ltrim( $line, ':' ) ) );
return DOMCompat::getOuterHTML( $span );
} else {
return $line;
}
}, $contentArray );
$content = implode( "\n", $contentMap ); // use faster? preg_replace
// 4. above
// Split on .. fragments.
// Process newlines inside nowikis in a post-processing pass.
// If
s are added here, Parsoid will escape them to plaintext.
$splitContent = preg_split( '/([\s\S]*?<\/nowiki>)/', $content,
-1, PREG_SPLIT_DELIM_CAPTURE );
$content = implode( '',
array_map( function ( $p, $i ) {
if ( $i % 2 === 1 ) {
return $p;
}
// This is a hack that exploits the fact that
// cannot show up in the extension's content.
return preg_replace( '/^(-+)<\/poem>/m', "\$1\n",
preg_replace( '/\n/m', "
\n",
preg_replace( '/(^----+)\n/m', '$1', $p ) ) );
},
$splitContent,
range( 0, count( $splitContent ) - 1 ) )
);
}
// Add the 'poem' class to the 'class' attribute, or if not found, add it
$value = $extApi->findAndUpdateArg( $extArgs, 'class', function ( string $value ) {
return strlen( $value ) ? "poem {$value}" : 'poem';
} );
if ( !$value ) {
$extApi->addNewArg( $extArgs, 'class', 'poem' );
}
return $extApi->extTagToDOM( $extArgs, '', $content, [
'wrapperTag' => 'div',
'parseOpts' => [ 'extTag' => 'poem' ],
// Create new frame, because $content doesn't literally appear in
// the parent frame's sourceText (our copy has been munged)
'processInNewFrame' => true,
// We've shifted the content around quite a bit when we preprocessed
// it. In the future if we wanted to enable selser inside the
// body we should create a proper offset map and then apply it to the
// result after the parse, like we do in the Gallery extension.
// But for now, since we don't selser the contents, just strip the
// DSR info so it doesn't cause problems/confusion with unicode
// offset conversion (and so it's clear you can't selser what we're
// currently emitting).
'clearDSROffsets' => true
]
);
}
}