Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 52 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
SpanHandler | |
0.00% |
0 / 52 |
|
0.00% |
0 / 3 |
506 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
handle | |
0.00% |
0 / 44 |
|
0.00% |
0 / 1 |
420 | |||
isRecognizedSpanWrapper | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\Html2Wt\DOMHandlers; |
5 | |
6 | use Wikimedia\Parsoid\Core\MediaStructure; |
7 | use Wikimedia\Parsoid\DOM\Element; |
8 | use Wikimedia\Parsoid\DOM\Node; |
9 | use Wikimedia\Parsoid\DOM\Text; |
10 | use Wikimedia\Parsoid\Html2Wt\LinkHandlerUtils; |
11 | use Wikimedia\Parsoid\Html2Wt\SerializerState; |
12 | use Wikimedia\Parsoid\Html2Wt\WTSUtils; |
13 | use Wikimedia\Parsoid\Tokens\KV; |
14 | use Wikimedia\Parsoid\Utils\DiffDOMUtils; |
15 | use Wikimedia\Parsoid\Utils\DOMCompat; |
16 | use Wikimedia\Parsoid\Utils\DOMDataUtils; |
17 | use Wikimedia\Parsoid\Utils\DOMUtils; |
18 | use Wikimedia\Parsoid\Utils\Utils; |
19 | use Wikimedia\Parsoid\Utils\WTUtils; |
20 | |
21 | class SpanHandler extends DOMHandler { |
22 | |
23 | public function __construct() { |
24 | parent::__construct( false ); |
25 | } |
26 | |
27 | /** @inheritDoc */ |
28 | public function handle( |
29 | Element $node, SerializerState $state, bool $wrapperUnmodified = false |
30 | ): ?Node { |
31 | $env = $state->getEnv(); |
32 | $dp = DOMDataUtils::getDataParsoid( $node ); |
33 | if ( self::isRecognizedSpanWrapper( $node ) ) { |
34 | if ( DOMUtils::hasTypeOf( $node, 'mw:Nowiki' ) ) { |
35 | $ext = $env->getSiteConfig()->getExtTagImpl( 'nowiki' ); |
36 | $src = $ext->domToWikitext( $state->extApi, $node, $wrapperUnmodified ); |
37 | $state->singleLineContext->disable(); |
38 | $state->serializer->emitWikitext( $src, $node ); |
39 | $state->singleLineContext->pop(); |
40 | } elseif ( WTUtils::isInlineMedia( $node ) ) { |
41 | LinkHandlerUtils::figureHandler( |
42 | $state, $node, MediaStructure::parse( $node ) |
43 | ); |
44 | } elseif ( |
45 | DOMUtils::hasTypeOf( $node, 'mw:Entity' ) && |
46 | DiffDOMUtils::hasNChildren( $node, 1 ) |
47 | ) { |
48 | $contentSrc = ( $node->textContent != '' ) ? $node->textContent |
49 | : DOMCompat::getInnerHTML( $node ); |
50 | // handle a new mw:Entity (not handled by selser) by |
51 | // serializing its children |
52 | if ( isset( $dp->src ) && $contentSrc === ( $dp->srcContent ?? null ) ) { |
53 | $state->serializer->emitWikitext( $dp->src, $node ); |
54 | } elseif ( $node->firstChild instanceof Text ) { |
55 | $state->emitChunk( |
56 | Utils::entityEncodeAll( $node->firstChild->nodeValue ), |
57 | $node->firstChild ); |
58 | } else { |
59 | $state->serializeChildren( $node ); |
60 | } |
61 | } elseif ( DOMUtils::hasTypeOf( $node, 'mw:DisplaySpace' ) ) { |
62 | // FIXME(T254501): Throw an UnreachableException here instead |
63 | $state->emitChunk( ' ', $node ); |
64 | } elseif ( DOMUtils::matchTypeOf( $node, '#^mw:Placeholder(/|$)#' ) ) { |
65 | if ( isset( $dp->src ) ) { |
66 | $this->emitPlaceholderSrc( $node, $state ); |
67 | return $node->nextSibling; |
68 | } else { |
69 | ( new FallbackHTMLHandler )->handle( $node, $state ); |
70 | } |
71 | } |
72 | } elseif ( $node->hasAttribute( 'data-mw-selser-wrapper' ) ) { |
73 | $state->serializeChildren( $node ); |
74 | } else { |
75 | $kvs = array_filter( WTSUtils::getAttributeKVArray( $node ), static function ( KV $kv ) { |
76 | return !preg_match( '/^data-parsoid/', $kv->k ) |
77 | && ( $kv->k !== DOMDataUtils::DATA_OBJECT_ATTR_NAME ) |
78 | && !( $kv->k === 'id' && preg_match( '/^mw[\w-]{2,}$/D', $kv->v ) ); |
79 | } ); |
80 | if ( !empty( $dp->misnested ) && ( $dp->stx ?? null ) !== 'html' |
81 | && !count( $kvs ) |
82 | ) { |
83 | // Discard span wrappers added to flag misnested content. |
84 | // Warn since selser should have reused source. |
85 | $env->log( 'warn', 'Serializing misnested content: ' . DOMCompat::getOuterHTML( $node ) ); |
86 | $state->serializeChildren( $node ); |
87 | } else { |
88 | // Fall back to plain HTML serialization for spans created |
89 | // by the editor. |
90 | ( new FallbackHTMLHandler )->handle( $node, $state ); |
91 | } |
92 | } |
93 | return $node->nextSibling; |
94 | } |
95 | |
96 | private static function isRecognizedSpanWrapper( Element $node ): ?string { |
97 | return DOMUtils::matchTypeOf( |
98 | $node, |
99 | // FIXME(T254501): Remove mw:DisplaySpace |
100 | // TODO: Remove "Image|Video|Audio" when version 2.4.0 of the content |
101 | // is no longer supported |
102 | '#^mw:(' |
103 | . 'Nowiki|Entity|DisplaySpace|Placeholder(/\w+)?' |
104 | . '|(File|Image|Video|Audio)(/(Frameless|Frame|Thumb))?' |
105 | . ')$#' |
106 | ); |
107 | } |
108 | |
109 | } |