Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 37
BRHandler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 6
462
0.00% covered (danger)
0.00%
0 / 37
 __construct
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 handle
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 10
 before
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 6
 after
0.00% covered (danger)
0.00%
0 / 1
42
0.00% covered (danger)
0.00%
0 / 13
 isPbr
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 4
 isPbrP
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 2
<?php
declare( strict_types = 1 );
namespace Wikimedia\Parsoid\Html2Wt\DOMHandlers;
use Wikimedia\Parsoid\DOM\Element;
use Wikimedia\Parsoid\DOM\Node;
use Wikimedia\Parsoid\Html2Wt\SerializerState;
use Wikimedia\Parsoid\Utils\DOMCompat;
use Wikimedia\Parsoid\Utils\DOMDataUtils;
use Wikimedia\Parsoid\Utils\DOMUtils;
class BRHandler extends DOMHandler {
    public function __construct() {
        parent::__construct( false );
    }
    /** @inheritDoc */
    public function handle(
        Element $node, SerializerState $state, bool $wrapperUnmodified = false
    ): ?Node {
        if ( $state->singleLineContext->enforced()
             || ( DOMDataUtils::getDataParsoid( $node )->stx ?? null ) === 'html'
             || DOMCompat::nodeName( $node->parentNode ) !== 'p'
        ) {
            // <br/> has special newline-based semantics in
            // parser-generated <p><br/>.. HTML
            $state->emitChunk( '<br />', $node );
        }
        // If P_BR (or P_BR_P), dont emit anything for the <br> so that
        // constraints propagate to the next node that emits content.
        return $node->nextSibling;
    }
    /** @inheritDoc */
    public function before( Element $node, Node $otherNode, SerializerState $state ): array {
        if ( $state->singleLineContext->enforced() || !$this->isPbr( $node ) ) {
            return [];
        }
        $c = $state->sep->constraints ?: [ 'min' => 0 ];
        // <h2>..</h2><p><br/>..
        // <p>..</p><p><br/>..
        // In all cases, we need at least 3 newlines before
        // any content that follows the <br/>.
        // Whether we need 4 depends what comes after <br/>.
        // content or a </p>. The after handler deals with it.
        return [ 'min' => max( 3, $c['min'] + 1 ) ];
    }
    /**
     * @inheritDoc
     * NOTE: There is an asymmetry in the before/after handlers.
     */
    public function after( Element $node, Node $otherNode, SerializerState $state ): array {
        // Note that the before handler has already forced 1 additional
        // newline for all <p><br/> scenarios which simplifies the work
        // of the after handler.
        //
        // Nothing changes with constraints if we are not
        // in a P-P transition. <br/> has special newline-based
        // semantics only in a parser-generated <p><br/>.. HTML.
        if ( $state->singleLineContext->enforced()
             || !PHandler::isPPTransition( DOMUtils::nextNonSepSibling( $node->parentNode ) )
        ) {
            return [];
        }
        $c = $state->sep->constraints ?: [ 'min' => 0 ];
        if ( $this->isPbrP( $node ) ) {
            // The <br/> forces an additional newline when part of
            // a <p><br/></p>.
            //
            // Ex: <p><br/></p><p>..</p> => at least 4 newlines before
            // content of the *next* p-tag.
            return [ 'min' => max( 4, $c['min'] + 1 ) ];
        } elseif ( $this->isPbr( $node ) ) {
            // Since the <br/> is followed by content, the newline
            // constraint isn't bumped.
            //
            // Ex: <p><br/>..<p><p>..</p> => at least 2 newlines after
            // content of *this* p-tag
            return [ 'min' => max( 2, $c['min'] ) ];
        }
        return [];
    }
    /**
     * @param Element $br
     * @return bool
     */
    private function isPbr( Element $br ): bool {
        return ( DOMDataUtils::getDataParsoid( $br )->stx ?? null ) !== 'html'
            && DOMCompat::nodeName( $br->parentNode ) === 'p'
            && DOMUtils::firstNonSepChild( $br->parentNode ) === $br;
    }
    /**
     * @param Element $br
     * @return bool
     */
    private function isPbrP( Element $br ): bool {
        return $this->isPbr( $br ) && DOMUtils::nextNonSepSibling( $br ) === null;
    }
}