Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 30 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
MediaStructure | |
0.00% |
0 / 30 |
|
0.00% |
0 / 9 |
462 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
isRedLink | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
hasResource | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getResource | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
hasAlt | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAlt | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
hasMediaUrl | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getMediaUrl | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
parse | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\Core; |
5 | |
6 | use Wikimedia\Parsoid\DOM\Element; |
7 | use Wikimedia\Parsoid\DOM\Node; |
8 | use Wikimedia\Parsoid\Utils\DOMCompat; |
9 | use Wikimedia\Parsoid\Utils\DOMUtils; |
10 | use Wikimedia\Parsoid\Utils\WTUtils; |
11 | use Wikimedia\Parsoid\Wikitext\Consts; |
12 | |
13 | /** |
14 | * All media should have a fixed structure: |
15 | * |
16 | * ``` |
17 | * <conatinerElt> |
18 | * <linkElt><mediaElt /></linkElt> |
19 | * <captionElt>...</captionElt> |
20 | * </containerElt> |
21 | * ``` |
22 | * |
23 | * Pull out this fixed structure, being as generous as possible with |
24 | * possibly-broken HTML. |
25 | */ |
26 | class MediaStructure { |
27 | |
28 | /** |
29 | * Node names: figure, span |
30 | * |
31 | * @var ?Element |
32 | */ |
33 | public $containerElt; |
34 | |
35 | /** |
36 | * Node names: a, span |
37 | * |
38 | * @var ?Element |
39 | */ |
40 | public $linkElt; |
41 | |
42 | /** |
43 | * Node names: img, audio, video, span |
44 | * |
45 | * @var ?Element |
46 | */ |
47 | public $mediaElt; |
48 | |
49 | /** |
50 | * Node names: figcaption |
51 | * |
52 | * @var ?Element |
53 | */ |
54 | public $captionElt; |
55 | |
56 | /** |
57 | * @param Element $mediaElt |
58 | * @param ?Element $linkElt |
59 | * @param ?Element $containerElt |
60 | */ |
61 | public function __construct( |
62 | Element $mediaElt, ?Element $linkElt = null, |
63 | ?Element $containerElt = null |
64 | ) { |
65 | $this->mediaElt = $mediaElt; |
66 | $this->linkElt = $linkElt; |
67 | $this->containerElt = $containerElt; |
68 | if ( $containerElt && DOMCompat::nodeName( $containerElt ) === 'figure' ) { |
69 | // FIXME: Support last child, which is not the linkElt, as the caption? |
70 | $this->captionElt = DOMCompat::querySelector( $containerElt, 'figcaption' ); |
71 | } |
72 | } |
73 | |
74 | /** |
75 | * We were not able to fetch info for the title, so the media was |
76 | * considered missing and rendered as a span. |
77 | * |
78 | * @return bool |
79 | */ |
80 | public function isRedLink(): bool { |
81 | return ( DOMCompat::nodeName( $this->mediaElt ) === 'span' ); |
82 | } |
83 | |
84 | /** |
85 | * @return bool |
86 | */ |
87 | public function hasResource(): bool { |
88 | return $this->mediaElt->hasAttribute( 'resource' ); |
89 | } |
90 | |
91 | /** |
92 | * @return string |
93 | */ |
94 | public function getResource(): string { |
95 | return $this->mediaElt->getAttribute( 'resource' ) ?? ''; |
96 | } |
97 | |
98 | /** |
99 | * @return bool |
100 | */ |
101 | public function hasAlt(): bool { |
102 | return $this->mediaElt->hasAttribute( 'alt' ); |
103 | } |
104 | |
105 | /** |
106 | * @return string |
107 | */ |
108 | public function getAlt(): string { |
109 | return $this->mediaElt->getAttribute( 'alt' ) ?? ''; |
110 | } |
111 | |
112 | /** |
113 | * @return bool |
114 | */ |
115 | public function hasMediaUrl(): bool { |
116 | return $this->linkElt && $this->linkElt->hasAttribute( 'href' ); |
117 | } |
118 | |
119 | /** |
120 | * @return string |
121 | */ |
122 | public function getMediaUrl(): string { |
123 | return $this->linkElt ? ( $this->linkElt->getAttribute( 'href' ) ?? '' ) : ''; |
124 | } |
125 | |
126 | /** |
127 | * @param Node $node |
128 | * @return ?MediaStructure |
129 | */ |
130 | public static function parse( Node $node ): ?MediaStructure { |
131 | if ( !WTUtils::isGeneratedFigure( $node ) ) { |
132 | return null; |
133 | } |
134 | '@phan-var Element $node'; // @var Element $node |
135 | $linkElt = $node; |
136 | do { |
137 | // Try being lenient, maybe there was a content model violation when |
138 | // parsing and an active formatting element was reopened in the wrapper |
139 | $linkElt = DOMUtils::firstNonSepChild( $linkElt ); |
140 | } while ( |
141 | $linkElt instanceof Element && DOMCompat::nodeName( $linkElt ) !== 'a' && |
142 | isset( Consts::$HTML['FormattingTags'][DOMCompat::nodeName( $linkElt )] ) |
143 | ); |
144 | if ( |
145 | !( $linkElt instanceof Element && |
146 | in_array( DOMCompat::nodeName( $linkElt ), [ 'a', 'span' ], true ) ) |
147 | ) { |
148 | if ( $linkElt instanceof Element ) { |
149 | // Try being lenient, maybe this is the media element and we don't |
150 | // have a link elt. See the test, "Image: from basic HTML (1)" |
151 | $mediaElt = $linkElt; |
152 | $linkElt = null; |
153 | } else { |
154 | return null; |
155 | } |
156 | } else { |
157 | $mediaElt = DOMUtils::firstNonSepChild( $linkElt ); |
158 | } |
159 | if ( |
160 | !( $mediaElt instanceof Element && |
161 | in_array( DOMCompat::nodeName( $mediaElt ), [ 'audio', 'img', 'span', 'video' ], true ) ) |
162 | ) { |
163 | return null; |
164 | } |
165 | return new MediaStructure( $mediaElt, $linkElt, $node ); |
166 | } |
167 | |
168 | } |