quotes = $quotes; } /** @inheritDoc */ public function handle( DOMElement $node, SerializerState $state, bool $wrapperUnmodified = false ): ?DOMNode { if ( $this->precedingQuoteEltRequiresEscape( $node ) ) { WTSUtils::emitStartTag( '', $node, $state ); } WTSUtils::emitStartTag( $this->quotes, $node, $state ); if ( !$node->hasChildNodes() ) { // Empty nodes like or need // a in place of the empty content so that // they parse back identically. if ( WTSUtils::emitEndTag( $this->quotes, $node, $state, true ) ) { WTSUtils::emitStartTag( '', $node, $state ); WTSUtils::emitEndTag( $this->quotes, $node, $state ); } } else { $state->serializeChildren( $node ); WTSUtils::emitEndTag( $this->quotes, $node, $state ); } return $node->nextSibling; } /** * @param DOMElement $node * @return bool */ private function precedingQuoteEltRequiresEscape( DOMElement $node ): bool { // * and siblings don't need a separation // as long as quote chars in text nodes are always // properly escaped -- which they are right now. // // * Adjacent quote siblings need a separation // between them if either of them will individually // generate a sequence of quotes 4 or longer. That can // only happen when either prev or node is of the form: // ... // // For new/edited DOMs, this can never happen because // wts.minimizeQuoteTags.js does quote tag minimization. // // For DOMs from existing wikitext, this can only happen // because of auto-inserted end/start tags. (Ex: ''a''' b ''c''') $prev = DOMUtils::previousNonDeletedSibling( $node ); return $prev && DOMUtils::isQuoteElt( $prev ) && ( DOMUtils::isQuoteElt( DOMUtils::lastNonDeletedChild( $prev ) ) || DOMUtils::isQuoteElt( DOMUtils::firstNonDeletedChild( $node ) ) ); } }