1<?php 2declare( strict_types = 1 ); 3 4namespace Wikimedia\Parsoid\Ext\Cite; 5 6use stdClass; 7use Wikimedia\Parsoid\DOM\Document; 8use Wikimedia\Parsoid\DOM\Element; 9use Wikimedia\Parsoid\Ext\DOMUtils; 10use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI; 11use Wikimedia\Parsoid\Utils\DOMCompat; 12 13/** 14 * Helper class used by `<references>` implementation. 15 */ 16class RefGroup { 17 18 /** 19 * @var string 20 */ 21 public $name; 22 23 /** 24 * @var stdClass[] 25 */ 26 public $refs; 27 28 /** 29 * @var stdClass[] 30 */ 31 public $indexByName; 32 33 /** 34 * @param string $group 35 */ 36 public function __construct( string $group = '' ) { 37 $this->name = $group; 38 $this->refs = []; 39 $this->indexByName = []; 40 } 41 42 /** 43 * Generate leading linkbacks 44 * @param ParsoidExtensionAPI $extApi 45 * @param string $href 46 * @param ?string $group 47 * @param string $text 48 * @param Document $ownerDoc 49 * @return Element 50 */ 51 private static function createLinkback( 52 ParsoidExtensionAPI $extApi, string $href, ?string $group, 53 string $text, Document $ownerDoc 54 ): Element { 55 $a = $ownerDoc->createElement( 'a' ); 56 $s = $ownerDoc->createElement( 'span' ); 57 $textNode = $ownerDoc->createTextNode( $text . ' ' ); 58 $a->setAttribute( 'href', $extApi->getPageUri() . '#' . $href ); 59 $s->setAttribute( 'class', 'mw-linkback-text' ); 60 if ( $group ) { 61 $a->setAttribute( 'data-mw-group', $group ); 62 } 63 $s->appendChild( $textNode ); 64 $a->appendChild( $s ); 65 return $a; 66 } 67 68 /** 69 * @param ParsoidExtensionAPI $extApi 70 * @param Element $refsList 71 * @param stdClass $ref 72 */ 73 public function renderLine( 74 ParsoidExtensionAPI $extApi, Element $refsList, stdClass $ref 75 ): void { 76 $ownerDoc = $refsList->ownerDocument; 77 78 // Generate the li and set ref content first, so the HTML gets parsed. 79 // We then append the rest of the ref nodes before the first node 80 $li = $ownerDoc->createElement( 'li' ); 81 $refDir = $ref->dir; 82 $refTarget = $ref->target; 83 $refContentId = $ref->contentId; 84 $refGroup = $ref->group; 85 DOMUtils::addAttributes( $li, [ 86 'about' => '#' . $refTarget, 87 'id' => $refTarget, 88 'class' => ( $refDir === 'rtl' || $refDir === 'ltr' ) ? 'mw-cite-dir-' . $refDir : null 89 ] 90 ); 91 $reftextSpan = $ownerDoc->createElement( 'span' ); 92 DOMUtils::addAttributes( 93 $reftextSpan, 94 [ 95 'id' => 'mw-reference-text-' . $refTarget, 96 'class' => 'mw-reference-text', 97 ] 98 ); 99 if ( $refContentId ) { 100 // `sup` is the wrapper created by Ref::sourceToDom()'s call to 101 // `extApi->extTagToDOM()`. Only its contents are relevant. 102 $sup = $extApi->getContentDOM( $refContentId )->firstChild; 103 DOMUtils::migrateChildren( $sup, $reftextSpan ); 104 '@phan-var Element $sup'; /** @var Element $sup */ 105 DOMCompat::remove( $sup ); 106 $extApi->clearContentDOM( $refContentId ); 107 } 108 $li->appendChild( $reftextSpan ); 109 110 if ( count( $ref->linkbacks ) === 1 ) { 111 $linkback = self::createLinkback( $extApi, $ref->id, $refGroup, "↑", $ownerDoc ); 112 $linkback->setAttribute( 'rel', 'mw:referencedBy' ); 113 $li->insertBefore( $linkback, $reftextSpan ); 114 } else { 115 // 'mw:referencedBy' span wrapper 116 $span = $ownerDoc->createElement( 'span' ); 117 $span->setAttribute( 'rel', 'mw:referencedBy' ); 118 $li->insertBefore( $span, $reftextSpan ); 119 120 foreach ( $ref->linkbacks as $i => $lb ) { 121 $span->appendChild( 122 self::createLinkback( $extApi, $lb, $refGroup, (string)( $i + 1 ), $ownerDoc ) 123 ); 124 } 125 } 126 127 // Space before content node 128 $li->insertBefore( $ownerDoc->createTextNode( ' ' ), $reftextSpan ); 129 130 // Add it to the ref list 131 $refsList->appendChild( $li ); 132 } 133} 134