Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 60 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 1 |
DiffDOMUtils | |
0.00% |
0 / 60 |
|
0.00% |
0 / 12 |
1892 | |
0.00% |
0 / 1 |
hasNChildren | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
30 | |||
isContentNode | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
firstNonSepChild | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
lastNonSepChild | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
previousNonSepSibling | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
nextNonSepSibling | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
numNonDeletedChildNodes | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
firstNonDeletedChild | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
lastNonDeletedChild | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
nextNonDeletedSibling | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
previousNonDeletedSibling | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
nodeEssentiallyEmpty | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
72 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\Utils; |
5 | |
6 | use Wikimedia\Parsoid\DOM\Comment; |
7 | use Wikimedia\Parsoid\DOM\Element; |
8 | use Wikimedia\Parsoid\DOM\Node; |
9 | use Wikimedia\Parsoid\DOM\Text; |
10 | use Wikimedia\Parsoid\Html2Wt\DiffUtils; |
11 | |
12 | /** |
13 | * Some diff marker aware DOM utils. |
14 | */ |
15 | class DiffDOMUtils { |
16 | |
17 | /** |
18 | * Test the number of children this node has without using |
19 | * `DOMNode::$childNodes->count()`. This walks the sibling list and so |
20 | * takes O(`nchildren`) time -- so `nchildren` is expected to be small |
21 | * (say: 0, 1, or 2). |
22 | * |
23 | * Skips all diff markers by default. |
24 | * @param Node $node |
25 | * @param int $nchildren |
26 | * @param bool $countDiffMarkers |
27 | * @return bool |
28 | */ |
29 | public static function hasNChildren( |
30 | Node $node, int $nchildren, bool $countDiffMarkers = false |
31 | ): bool { |
32 | for ( $child = $node->firstChild; $child; $child = $child->nextSibling ) { |
33 | if ( !$countDiffMarkers && DiffUtils::isDiffMarker( $child ) ) { |
34 | continue; |
35 | } |
36 | if ( $nchildren <= 0 ) { |
37 | return false; |
38 | } |
39 | $nchildren -= 1; |
40 | } |
41 | return ( $nchildren === 0 ); |
42 | } |
43 | |
44 | /** |
45 | * Is a node a content node? |
46 | * |
47 | * @param ?Node $node |
48 | * @return bool |
49 | */ |
50 | public static function isContentNode( ?Node $node ): bool { |
51 | return !( $node instanceof Comment ) && |
52 | !DOMUtils::isIEW( $node ) && |
53 | !DiffUtils::isDiffMarker( $node ); |
54 | } |
55 | |
56 | /** |
57 | * Get the first child element or non-IEW text node, ignoring |
58 | * whitespace-only text nodes, comments, and deleted nodes. |
59 | * |
60 | * @param Node $node |
61 | * @return Node|null |
62 | */ |
63 | public static function firstNonSepChild( Node $node ): ?Node { |
64 | $child = $node->firstChild; |
65 | while ( $child && !self::isContentNode( $child ) ) { |
66 | $child = $child->nextSibling; |
67 | } |
68 | return $child; |
69 | } |
70 | |
71 | /** |
72 | * Get the last child element or non-IEW text node, ignoring |
73 | * whitespace-only text nodes, comments, and deleted nodes. |
74 | * |
75 | * @param Node $node |
76 | * @return Node|null |
77 | */ |
78 | public static function lastNonSepChild( Node $node ): ?Node { |
79 | $child = $node->lastChild; |
80 | while ( $child && !self::isContentNode( $child ) ) { |
81 | $child = $child->previousSibling; |
82 | } |
83 | return $child; |
84 | } |
85 | |
86 | /** |
87 | * Get the previous non separator sibling node. |
88 | * |
89 | * @param Node $node |
90 | * @return Node|null |
91 | */ |
92 | public static function previousNonSepSibling( Node $node ): ?Node { |
93 | $prev = $node->previousSibling; |
94 | while ( $prev && !self::isContentNode( $prev ) ) { |
95 | $prev = $prev->previousSibling; |
96 | } |
97 | return $prev; |
98 | } |
99 | |
100 | /** |
101 | * Get the next non separator sibling node. |
102 | * |
103 | * @param Node $node |
104 | * @return Node|null |
105 | */ |
106 | public static function nextNonSepSibling( Node $node ): ?Node { |
107 | $next = $node->nextSibling; |
108 | while ( $next && !self::isContentNode( $next ) ) { |
109 | $next = $next->nextSibling; |
110 | } |
111 | return $next; |
112 | } |
113 | |
114 | /** |
115 | * Return the numbler of non deleted child nodes. |
116 | * |
117 | * @param Node $node |
118 | * @return int |
119 | */ |
120 | public static function numNonDeletedChildNodes( Node $node ): int { |
121 | $n = 0; |
122 | $child = $node->firstChild; |
123 | while ( $child ) { |
124 | if ( !DiffUtils::isDiffMarker( $child ) ) { // FIXME: This is ignoring both inserted/deleted |
125 | $n++; |
126 | } |
127 | $child = $child->nextSibling; |
128 | } |
129 | return $n; |
130 | } |
131 | |
132 | /** |
133 | * Get the first non-deleted child of node. |
134 | * |
135 | * @param Node $node |
136 | * @return Node|null |
137 | */ |
138 | public static function firstNonDeletedChild( Node $node ): ?Node { |
139 | $child = $node->firstChild; |
140 | // FIXME: This is ignoring both inserted/deleted |
141 | while ( $child && DiffUtils::isDiffMarker( $child ) ) { |
142 | $child = $child->nextSibling; |
143 | } |
144 | return $child; |
145 | } |
146 | |
147 | /** |
148 | * Get the last non-deleted child of node. |
149 | * |
150 | * @param Node $node |
151 | * @return Node|null |
152 | */ |
153 | public static function lastNonDeletedChild( Node $node ): ?Node { |
154 | $child = $node->lastChild; |
155 | // FIXME: This is ignoring both inserted/deleted |
156 | while ( $child && DiffUtils::isDiffMarker( $child ) ) { |
157 | $child = $child->previousSibling; |
158 | } |
159 | return $child; |
160 | } |
161 | |
162 | /** |
163 | * Get the next non deleted sibling. |
164 | * |
165 | * @param Node $node |
166 | * @return Node|null |
167 | */ |
168 | public static function nextNonDeletedSibling( Node $node ): ?Node { |
169 | $node = $node->nextSibling; |
170 | while ( $node && DiffUtils::isDiffMarker( $node ) ) { // FIXME: This is ignoring both inserted/deleted |
171 | $node = $node->nextSibling; |
172 | } |
173 | return $node; |
174 | } |
175 | |
176 | /** |
177 | * Get the previous non deleted sibling. |
178 | * |
179 | * @param Node $node |
180 | * @return Node|null |
181 | */ |
182 | public static function previousNonDeletedSibling( Node $node ): ?Node { |
183 | $node = $node->previousSibling; |
184 | while ( $node && DiffUtils::isDiffMarker( $node ) ) { // FIXME: This is ignoring both inserted/deleted |
185 | $node = $node->previousSibling; |
186 | } |
187 | return $node; |
188 | } |
189 | |
190 | /** |
191 | * Does `node` contain nothing or just non-newline whitespace? |
192 | * `strict` adds the condition that all whitespace is forbidden. |
193 | * |
194 | * @param Node $node |
195 | * @param bool $strict |
196 | * @return bool |
197 | */ |
198 | public static function nodeEssentiallyEmpty( Node $node, bool $strict = false ): bool { |
199 | $n = $node->firstChild; |
200 | while ( $n ) { |
201 | if ( $n instanceof Element && !DiffUtils::isDiffMarker( $n ) ) { |
202 | return false; |
203 | } elseif ( $n instanceof Text && |
204 | ( $strict || !preg_match( '/^[ \t]*$/D', $n->nodeValue ) ) |
205 | ) { |
206 | return false; |
207 | } elseif ( $n instanceof Comment ) { |
208 | return false; |
209 | } |
210 | $n = $n->nextSibling; |
211 | } |
212 | return true; |
213 | } |
214 | |
215 | } |