6use RemexHtml\Serializer\Serializer;
7use RemexHtml\Serializer\SerializerNode;
8use RemexHtml\Tokenizer\Attributes;
9use RemexHtml\Tokenizer\PlainAttributes;
10use RemexHtml\TreeBuilder\TreeBuilder;
11use RemexHtml\TreeBuilder\TreeHandler;
12use RemexHtml\TreeBuilder\Element;
108 $this->serializer->startDocument( $fragmentNamespace, $fragmentName );
109 $root = $this->serializer->getRootNode();
111 $root->snData->needsPWrapping =
true;
115 $this->serializer->endDocument( $pos );
119 if ( $preposition === TreeBuilder::ROOT ) {
120 return [ $this->serializer->getRootNode(), null ];
121 } elseif ( $preposition === TreeBuilder::BEFORE ) {
122 $refNode = $refElement->userData;
123 return [ $this->serializer->getParentNode( $refNode ), $refNode ];
125 $refNode = $refElement->userData;
126 $refData = $refNode->snData;
127 if ( $refData->currentCloneElement ) {
129 $origRefData = $refData;
130 while ( $refData->currentCloneElement ) {
131 $refElement = $refData->currentCloneElement;
132 $refNode = $refElement->userData;
133 $refData = $refNode->snData;
136 $origRefData->currentCloneElement = $refElement;
137 } elseif ( $refData->childPElement ) {
138 $refElement = $refData->childPElement;
139 $refNode = $refElement->userData;
141 return [ $refNode, $refNode ];
153 $pWrap =
new Element( HTMLData::NS_HTML,
'mw:p-wrap',
new PlainAttributes );
154 $this->serializer->insertElement( TreeBuilder::UNDER,
$parent, $pWrap,
false,
157 $data->isPWrapper =
true;
159 $pWrap->userData->snData = $data;
160 $parent->snData->childPElement = $pWrap;
161 return $pWrap->userData;
164 public function characters( $preposition, $refElement, $text, $start, $length,
165 $sourceStart, $sourceLength
167 $isBlank = strspn( $text,
"\t\n\f\r ", $start, $length ) === $length;
172 if ( $preposition === TreeBuilder::UNDER ) {
173 if ( $parentData->needsPWrapping && !$isBlank ) {
178 } elseif ( $parentData->isSplittable && !$parentData->ancestorPNode ) {
180 $refNode = $this->
splitTagStack( $refNode,
true, $sourceStart );
188 $parentData->nonblankNodeCount++;
190 $this->serializer->characters( $preposition, $refNode, $text, $start,
191 $length, $sourceStart, $sourceLength );
195 if ( $this->
trace ) {
253 public function insertElement( $preposition, $refElement, Element $element, $void,
254 $sourceStart, $sourceLength
258 $elementName = $element->htmlName;
260 $inline = isset( self::$onlyInlineElements[$elementName] );
261 $under = $preposition === TreeBuilder::UNDER;
262 $elementToEnd =
null;
264 if ( $under && $parentData->isPWrapper && !$inline ) {
267 $this->
trace(
'insert B/b' );
268 $newParent = $this->serializer->getParentNode(
$parent );
271 $pElement = $parentData->childPElement;
272 $parentData->childPElement =
null;
273 $newRef = $refElement->userData;
274 } elseif ( $under && $parentData->isSplittable
275 && (
bool)$parentData->ancestorPNode !== $inline
280 $this->
trace( $inline ?
'insert DS/i' :
'insert CS/b' );
281 $newRef = $this->
splitTagStack( $newRef, $inline, $sourceStart );
284 } elseif ( $under && $parentData->needsPWrapping && $inline ) {
287 $this->
trace(
'insert A/i' );
291 } elseif ( $parentData->ancestorPNode && !$inline ) {
295 $this->
trace(
'insert CU/b' );
299 $this->
trace(
'insert normal' );
303 $parentData->nonblankNodeCount++;
306 $this->serializer->insertElement( $preposition, $newRef,
307 $element, $void, $sourceStart, $sourceLength );
310 if ( !$element->userData->snData ) {
313 $elementData = $element->userData->snData;
315 if ( ( $parentData->isPWrapper || $parentData->isSplittable )
316 && isset( self::$formattingElements[$elementName] )
318 $elementData->isSplittable =
true;
320 if ( $parentData->isPWrapper ) {
321 $elementData->ancestorPNode =
$parent;
322 } elseif ( $parentData->ancestorPNode ) {
323 $elementData->ancestorPNode = $parentData->ancestorPNode;
325 if ( $parentData->wrapBaseNode ) {
326 $elementData->wrapBaseNode = $parentData->wrapBaseNode;
327 } elseif ( $parentData->needsPWrapping ) {
328 $elementData->wrapBaseNode =
$parent;
330 if ( $elementName ===
'body'
331 || $elementName ===
'blockquote'
332 || $elementName ===
'html'
334 $elementData->needsPWrapping =
true;
346 private function splitTagStack( SerializerNode $parentNode, $inline, $pos ) {
347 $parentData = $parentNode->snData;
348 $wrapBase = $parentData->wrapBaseNode;
349 $pWrap = $parentData->ancestorPNode;
351 $cloneEnd = $wrapBase;
353 $cloneEnd = $parentData->ancestorPNode;
360 $removableNodes = [];
361 while ( $node !== $cloneEnd ) {
363 if ( $nextParent === $root ) {
364 throw new \Exception(
'Did not find end of clone range' );
367 if ( $node->snData->nonblankNodeCount === 0 ) {
368 $removableNodes[] = $node;
369 $nextParent->snData->nonblankNodeCount--;
380 $wrapBase->snData->childPElement =
null;
386 for ( $i = count( $nodes ) - 1; $i >= 0; $i-- ) {
387 $oldNode = $nodes[$i];
388 $oldData = $oldNode->snData;
390 $element =
new Element( $oldNode->namespace, $oldNode->name, $oldNode->attrs );
391 $this->serializer->insertElement( TreeBuilder::UNDER, $nodeParent,
392 $element,
false, $pos, 0 );
393 $oldData->currentCloneElement = $element;
395 $newNode = $element->userData;
398 $newData->ancestorPNode = $pWrap;
400 $newData->isSplittable =
true;
401 $newData->wrapBaseNode = $wrapBase;
402 $newData->isPWrapper = $oldData->isPWrapper;
404 $nodeParent->snData->nonblankNodeCount++;
408 foreach ( $removableNodes as $rNode ) {
409 $fakeElement =
new Element( $rNode->namespace, $rNode->name, $rNode->attrs );
410 $fakeElement->userData = $rNode;
411 $this->serializer->removeNode( $fakeElement, $pos );
421 $nodeData = $node->snData;
422 $pWrapNode = $nodeData->ancestorPNode;
423 $newParent = $this->serializer->getParentNode( $pWrapNode );
424 if ( $pWrapNode !== $this->serializer->getLastChild( $newParent ) ) {
431 $victim = $nextParent;
432 $victim->snData->ancestorPNode =
null;
433 $nextParent = $this->serializer->getParentNode( $victim );
434 }
while ( $nextParent !== $pWrapNode );
437 $victimElement =
new Element( $victim->namespace, $victim->name, $victim->attrs );
438 $victimElement->userData = $victim;
441 $this->serializer->insertElement( TreeBuilder::UNDER, $newParent, $victimElement,
442 false, $sourceStart, 0 );
445 $pWrapNode->snData->nonblankNodeCount--;
448 $newParent->snData->childPElement =
null;
451 public function endTag( Element $element, $sourceStart, $sourceLength ) {
452 $data = $element->userData->snData;
453 if ( $data->childPElement ) {
454 $this->
endTag( $data->childPElement, $sourceStart, 0 );
456 $this->serializer->endTag( $element, $sourceStart, $sourceLength );
457 $element->userData->snData =
null;
458 $element->userData =
null;
461 public function doctype( $name, $public, $system, $quirks, $sourceStart, $sourceLength ) {
462 $this->serializer->doctype( $name, $public, $system, $quirks,
463 $sourceStart, $sourceLength );
466 public function comment( $preposition, $refElement, $text, $sourceStart, $sourceLength ) {
468 $this->serializer->comment( $preposition, $refNode, $text,
469 $sourceStart, $sourceLength );
472 public function error( $text, $pos ) {
473 $this->serializer->error( $text, $pos );
476 public function mergeAttributes( Element $element, Attributes $attrs, $sourceStart ) {
477 $this->serializer->mergeAttributes( $element, $attrs, $sourceStart );
480 public function removeNode( Element $element, $sourceStart ) {
481 $this->serializer->removeNode( $element, $sourceStart );
485 $self = $element->userData;
486 if (
$self->snData->childPElement ) {
500 $children =
$self->children;
501 $self->children = [];
502 $this->
insertElement( TreeBuilder::UNDER, $element, $newParent,
false, $sourceStart, 0 );
503 $newParentNode = $newParent->userData;
504 $newParentId = $newParentNode->id;
505 foreach ( $children as $child ) {
506 if ( is_object( $child ) ) {
507 $this->
trace(
"reparent <{$child->name}>" );
508 $child->parentId = $newParentId;
511 $newParentNode->children = $children;
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list