111 if ( $preposition === TreeBuilder::ROOT ) {
112 return [ $this->serializer->getRootNode(), null ];
113 } elseif ( $preposition === TreeBuilder::BEFORE ) {
114 $refNode = $refElement->userData;
115 return [ $this->serializer->getParentNode( $refNode ), $refNode ];
117 $refNode = $refElement->userData;
118 $refData = $refNode->snData;
119 if ( $refData->currentCloneElement ) {
121 $origRefData = $refData;
122 while ( $refData->currentCloneElement ) {
123 $refElement = $refData->currentCloneElement;
124 $refNode = $refElement->userData;
125 $refData = $refNode->snData;
128 $origRefData->currentCloneElement = $refElement;
129 } elseif ( $refData->childPElement ) {
130 $refElement = $refData->childPElement;
131 $refNode = $refElement->userData;
133 return [ $refNode, $refNode ];
145 $pWrap =
new Element( HTMLData::NS_HTML,
'mw:p-wrap',
new PlainAttributes );
146 $this->serializer->insertElement( TreeBuilder::UNDER, $parent, $pWrap,
false,
149 $data->isPWrapper =
true;
150 $data->wrapBaseNode = $parent;
151 $pWrap->userData->snData = $data;
152 $parent->snData->childPElement = $pWrap;
153 return $pWrap->userData;
156 public function characters( $preposition, $refElement, $text, $start, $length,
157 $sourceStart, $sourceLength
159 $isBlank = strspn( $text,
"\t\n\f\r ", $start, $length ) === $length;
162 $parentData = $parent->snData;
164 if ( $preposition === TreeBuilder::UNDER ) {
165 if ( $parentData->needsPWrapping && !$isBlank ) {
169 $parentData = $parent->snData;
170 } elseif ( $parentData->isSplittable && !$parentData->ancestorPNode ) {
172 $refNode = $this->
splitTagStack( $refNode,
true, $sourceStart );
174 $parentData = $parent->snData;
180 $parentData->nonblankNodeCount++;
182 $this->serializer->characters( $preposition, $refNode, $text, $start,
183 $length, $sourceStart, $sourceLength );
243 public function insertElement( $preposition, $refElement, Element $element, $void,
244 $sourceStart, $sourceLength
247 $parentData = $parent->snData;
248 $parentNs = $parent->namespace;
249 $parentName = $parent->name;
250 $elementName = $element->htmlName;
252 $inline = isset( self::$onlyInlineElements[$elementName] );
253 $under = $preposition === TreeBuilder::UNDER;
255 if ( $under && $parentData->isPWrapper && !$inline ) {
258 $this->
trace(
'insert B/b' );
259 $newParent = $this->serializer->getParentNode( $parent );
260 $parent = $newParent;
261 $parentData = $parent->snData;
262 $pElement = $parentData->childPElement;
263 $parentData->childPElement =
null;
264 $newRef = $refElement->userData;
265 } elseif ( $under && $parentData->isSplittable
266 && (
bool)$parentData->ancestorPNode !== $inline
271 $this->
trace( $inline ?
'insert DS/i' :
'insert CS/b' );
272 $newRef = $this->
splitTagStack( $newRef, $inline, $sourceStart );
274 $parentData = $parent->snData;
275 } elseif ( $under && $parentData->needsPWrapping && $inline ) {
278 $this->
trace(
'insert A/i' );
281 $parentData = $parent->snData;
282 } elseif ( $parentData->ancestorPNode && !$inline ) {
286 $this->
trace(
'insert CU/b' );
290 $this->
trace(
'insert normal' );
294 $parentData->nonblankNodeCount++;
297 $this->serializer->insertElement( $preposition, $newRef,
298 $element, $void, $sourceStart, $sourceLength );
301 if ( !$element->userData->snData ) {
304 $elementData = $element->userData->snData;
306 if ( ( $parentData->isPWrapper || $parentData->isSplittable )
307 && isset( self::$formattingElements[$elementName] )
309 $elementData->isSplittable =
true;
311 if ( $parentData->isPWrapper ) {
312 $elementData->ancestorPNode = $parent;
313 } elseif ( $parentData->ancestorPNode ) {
314 $elementData->ancestorPNode = $parentData->ancestorPNode;
316 if ( $parentData->wrapBaseNode ) {
317 $elementData->wrapBaseNode = $parentData->wrapBaseNode;
318 } elseif ( $parentData->needsPWrapping ) {
319 $elementData->wrapBaseNode = $parent;
321 if ( $elementName ===
'body'
322 || $elementName ===
'blockquote'
323 || $elementName ===
'html'
325 $elementData->needsPWrapping =
true;
337 private function splitTagStack( SerializerNode $parentNode, $inline, $pos ) {
338 $parentData = $parentNode->snData;
339 $wrapBase = $parentData->wrapBaseNode;
340 $pWrap = $parentData->ancestorPNode;
342 $cloneEnd = $wrapBase;
344 $cloneEnd = $parentData->ancestorPNode;
347 $serializer = $this->serializer;
349 $root = $serializer->getRootNode();
351 $removableNodes = [];
352 $haveContent =
false;
353 while ( $node !== $cloneEnd ) {
354 $nextParent = $serializer->getParentNode( $node );
355 if ( $nextParent === $root ) {
356 throw new \Exception(
'Did not find end of clone range' );
359 if ( $node->snData->nonblankNodeCount === 0 ) {
360 $removableNodes[] = $node;
361 $nextParent->snData->nonblankNodeCount--;
372 $wrapBase->snData->childPElement =
null;
378 for ( $i = count( $nodes ) - 1; $i >= 0; $i-- ) {
379 $oldNode = $nodes[$i];
380 $oldData = $oldNode->snData;
382 $element =
new Element( $oldNode->namespace, $oldNode->name, $oldNode->attrs );
383 $this->serializer->insertElement( TreeBuilder::UNDER, $nodeParent,
384 $element,
false, $pos, 0 );
385 $oldData->currentCloneElement = $element;
387 $newNode = $element->userData;
390 $newData->ancestorPNode = $pWrap;
392 $newData->isSplittable =
true;
393 $newData->wrapBaseNode = $wrapBase;
394 $newData->isPWrapper = $oldData->isPWrapper;
396 $nodeParent->snData->nonblankNodeCount++;
400 foreach ( $removableNodes as $rNode ) {
401 $fakeElement =
new Element( $rNode->namespace, $rNode->name, $rNode->attrs );
402 $fakeElement->userData = $rNode;
403 $this->serializer->removeNode( $fakeElement, $pos );
413 $nodeData = $node->snData;
414 $pWrapNode = $nodeData->ancestorPNode;
415 $newParent = $this->serializer->getParentNode( $pWrapNode );
416 if ( $pWrapNode !== $this->serializer->getLastChild( $newParent ) ) {
423 $victim = $nextParent;
424 $victim->snData->ancestorPNode =
null;
425 $nextParent = $this->serializer->getParentNode( $victim );
426 }
while ( $nextParent !== $pWrapNode );
429 $victimElement =
new Element( $victim->namespace, $victim->name, $victim->attrs );
430 $victimElement->userData = $victim;
433 $this->serializer->insertElement( TreeBuilder::UNDER, $newParent, $victimElement,
434 false, $sourceStart, 0 );
437 $pWrapNode->snData->nonblankNodeCount--;
440 $newParent->snData->childPElement =
null;
443 public function endTag( Element $element, $sourceStart, $sourceLength ) {
444 $data = $element->userData->snData;
445 if ( $data->childPElement ) {
446 $this->
endTag( $data->childPElement, $sourceStart, 0 );
448 $this->serializer->endTag( $element, $sourceStart, $sourceLength );
449 $element->userData->snData =
null;
450 $element->userData =
null;
453 public function doctype( $name, $public, $system, $quirks, $sourceStart, $sourceLength ) {
454 $this->serializer->doctype( $name, $public, $system, $quirks,
455 $sourceStart, $sourceLength );
458 public function comment( $preposition, $refElement, $text, $sourceStart, $sourceLength ) {
460 $this->serializer->comment( $preposition, $refNode, $text,
461 $sourceStart, $sourceLength );
477 $self = $element->userData;
478 if (
$self->snData->childPElement ) {
492 $children =
$self->children;
493 $self->children = [];
494 $this->
insertElement( TreeBuilder::UNDER, $element, $newParent,
false, $sourceStart, 0 );
495 $newParentNode = $newParent->userData;
496 $newParentId = $newParentNode->id;
497 foreach ( $children as $child ) {
498 if ( is_object( $child ) ) {
499 $this->
trace(
"reparent <{$child->name}>" );
500 $child->parentId = $newParentId;
503 $newParentNode->children = $children;