104 if ( $preposition === TreeBuilder::ROOT ) {
105 return [ $this->serializer->getRootNode(), null ];
106 } elseif ( $preposition === TreeBuilder::BEFORE ) {
107 $refNode = $refElement->userData;
108 return [ $this->serializer->getParentNode( $refNode ), $refNode ];
110 $refNode = $refElement->userData;
111 $refData = $refNode->snData;
112 if ( $refData->currentCloneElement ) {
114 $origRefData = $refData;
115 while ( $refData->currentCloneElement ) {
116 $refElement = $refData->currentCloneElement;
117 $refNode = $refElement->userData;
118 $refData = $refNode->snData;
121 $origRefData->currentCloneElement = $refElement;
122 } elseif ( $refData->childPElement ) {
123 $refElement = $refData->childPElement;
124 $refNode = $refElement->userData;
126 return [ $refNode, $refNode ];
138 $pWrap =
new Element( HTMLData::NS_HTML,
'mw:p-wrap',
new PlainAttributes );
139 $this->serializer->insertElement( TreeBuilder::UNDER, $parent, $pWrap,
false,
142 $data->isPWrapper =
true;
143 $data->wrapBaseNode = $parent;
144 $pWrap->userData->snData = $data;
145 $parent->snData->childPElement = $pWrap;
146 return $pWrap->userData;
149 public function characters( $preposition, $refElement, $text, $start, $length,
150 $sourceStart, $sourceLength
152 $isBlank = strspn( $text,
"\t\n\f\r ", $start, $length ) === $length;
155 $parentData = $parent->snData;
157 if ( $preposition === TreeBuilder::UNDER ) {
158 if ( $parentData->needsPWrapping && !$isBlank ) {
162 $parentData = $parent->snData;
163 } elseif ( $parentData->isSplittable && !$parentData->ancestorPNode ) {
165 $refNode = $this->
splitTagStack( $refNode,
true, $sourceStart );
167 $parentData = $parent->snData;
173 $parentData->nonblankNodeCount++;
175 $this->serializer->characters( $preposition, $refNode, $text, $start,
176 $length, $sourceStart, $sourceLength );
232 public function insertElement( $preposition, $refElement, Element $element, $void,
233 $sourceStart, $sourceLength
236 $parentData = $parent->snData;
237 $parentNs = $parent->namespace;
238 $parentName = $parent->name;
239 $elementName = $element->htmlName;
241 $inline = isset( self::$onlyInlineElements[$elementName] );
242 $under = $preposition === TreeBuilder::UNDER;
244 if ( $under && $parentData->isPWrapper && !$inline ) {
247 $newParent = $this->serializer->getParentNode( $parent );
248 $parent = $newParent;
249 $parentData = $parent->snData;
250 $pElement = $parentData->childPElement;
251 $parentData->childPElement =
null;
252 $newRef = $refElement->userData;
253 $this->
endTag( $pElement, $sourceStart, 0 );
254 } elseif ( $under && $parentData->isSplittable
255 && (
bool)$parentData->ancestorPNode !== $inline
260 $newRef = $this->
splitTagStack( $newRef, $inline, $sourceStart );
262 $parentData = $parent->snData;
263 } elseif ( $under && $parentData->needsPWrapping && $inline ) {
268 $parentData = $parent->snData;
269 } elseif ( $parentData->ancestorPNode && !$inline ) {
278 $parentData->nonblankNodeCount++;
281 $this->serializer->insertElement( $preposition, $newRef,
282 $element, $void, $sourceStart, $sourceLength );
285 if ( !$element->userData->snData ) {
288 $elementData = $element->userData->snData;
290 if ( ( $parentData->isPWrapper || $parentData->isSplittable )
291 && isset( self::$formattingElements[$elementName] )
293 $elementData->isSplittable =
true;
295 if ( $parentData->isPWrapper ) {
296 $elementData->ancestorPNode = $parent;
297 } elseif ( $parentData->ancestorPNode ) {
298 $elementData->ancestorPNode = $parentData->ancestorPNode;
300 if ( $parentData->wrapBaseNode ) {
301 $elementData->wrapBaseNode = $parentData->wrapBaseNode;
302 } elseif ( $parentData->needsPWrapping ) {
303 $elementData->wrapBaseNode = $parent;
305 if ( $elementName ===
'body'
306 || $elementName ===
'blockquote'
307 || $elementName ===
'html'
309 $elementData->needsPWrapping =
true;
321 private function splitTagStack( SerializerNode $parentNode, $inline, $pos ) {
322 $parentData = $parentNode->snData;
323 $wrapBase = $parentData->wrapBaseNode;
324 $pWrap = $parentData->ancestorPNode;
326 $cloneEnd = $wrapBase;
328 $cloneEnd = $parentData->ancestorPNode;
331 $serializer = $this->serializer;
333 $root = $serializer->getRootNode();
335 $removableNodes = [];
336 $haveContent =
false;
337 while ( $node !== $cloneEnd ) {
338 $nextParent = $serializer->getParentNode( $node );
339 if ( $nextParent === $root ) {
340 throw new \Exception(
'Did not find end of clone range' );
343 if ( $node->snData->nonblankNodeCount === 0 ) {
344 $removableNodes[] = $node;
345 $nextParent->snData->nonblankNodeCount--;
356 $wrapBase->snData->childPElement =
null;
362 for ( $i = count( $nodes ) - 1; $i >= 0; $i-- ) {
363 $oldNode = $nodes[$i];
364 $oldData = $oldNode->snData;
366 $element =
new Element( $oldNode->namespace, $oldNode->name, $oldNode->attrs );
367 $this->serializer->insertElement( TreeBuilder::UNDER, $nodeParent,
368 $element,
false, $pos, 0 );
369 $oldData->currentCloneElement = $element;
371 $newNode = $element->userData;
374 $newData->ancestorPNode = $pWrap;
376 $newData->isSplittable =
true;
377 $newData->wrapBaseNode = $wrapBase;
378 $newData->isPWrapper = $oldData->isPWrapper;
380 $nodeParent->snData->nonblankNodeCount++;
384 foreach ( $removableNodes
as $rNode ) {
385 $fakeElement =
new Element( $rNode->namespace, $rNode->name, $rNode->attrs );
386 $fakeElement->userData = $rNode;
387 $this->serializer->removeNode( $fakeElement, $pos );
397 $nodeData = $node->snData;
398 $pWrapNode = $nodeData->ancestorPNode;
399 $newParent = $this->serializer->getParentNode( $pWrapNode );
400 if ( $pWrapNode !== $this->serializer->getLastChild( $newParent ) ) {
407 $victim = $nextParent;
408 $victim->snData->ancestorPNode =
null;
409 $nextParent = $this->serializer->getParentNode( $victim );
410 }
while ( $nextParent !== $pWrapNode );
413 $victimElement =
new Element( $victim->namespace, $victim->name, $victim->attrs );
414 $victimElement->userData = $victim;
417 $this->serializer->insertElement( TreeBuilder::UNDER, $newParent, $victimElement,
418 false, $sourceStart, 0 );
421 $pWrapNode->snData->nonblankNodeCount--;
424 $newParent->snData->childPElement =
null;
427 public function endTag( Element $element, $sourceStart, $sourceLength ) {
428 $data = $element->userData->snData;
429 if ( $data->childPElement ) {
430 $this->
endTag( $data->childPElement, $sourceStart, 0 );
432 $this->serializer->endTag( $element, $sourceStart, $sourceLength );
433 $element->userData->snData =
null;
434 $element->userData =
null;
437 public function doctype( $name, $public, $system, $quirks, $sourceStart, $sourceLength ) {
438 $this->serializer->doctype(
$name, $public, $system, $quirks,
439 $sourceStart, $sourceLength );
442 public function comment( $preposition, $refElement, $text, $sourceStart, $sourceLength ) {
444 $this->serializer->comment( $preposition, $refNode, $text,
445 $sourceStart, $sourceLength );
461 $self = $element->userData;
462 $children =
$self->children;
463 $self->children = [];
464 $this->
insertElement( TreeBuilder::UNDER, $element, $newParent,
false, $sourceStart, 0 );
465 $newParentNode = $newParent->userData;
466 $newParentId = $newParentNode->id;
467 foreach ( $children
as $child ) {
468 if ( is_object( $child ) ) {
469 $child->parentId = $newParentId;
472 $newParentNode->children = $children;