[refactor] switch reflist rendering source of truth

Pure refactor which shouldn't change output in production.  Switches
to interfacing with MWDocumentReferences to get refs in index order.
Temporarily suppresses any subrefs, we only show top-level refs.

Bug: T247921
Change-Id: I9c8347b064173027f436722c87e15e0381c958bd
This commit is contained in:
Adam Wight 2024-06-28 11:54:20 +02:00
parent d5a4ecd647
commit d03d2d8d20
4 changed files with 84 additions and 10 deletions

View file

@ -71,6 +71,7 @@
"localBasePath": "modules/ve-cite",
"remoteExtPath": "Cite/modules/ve-cite",
"scripts": [
"ve.dm.MWDocumentReferences.js",
"ve.dm.MWReferenceModel.js",
"ve.dm.MWReferencesListNode.js",
"ve.dm.MWReferenceNode.js",

View file

@ -29,6 +29,7 @@
"mw.": "https://doc.wikimedia.org/mediawiki-core/master/js/{type}.html"
},
"prefixMapIgnore": [
"ve.dm.MWDocumentReferences",
"ve.dm.MWReference",
"ve.ce.MWReference",
"ve.ui.MWReference"

View file

@ -27,6 +27,7 @@ ve.ce.MWReferencesListNode = function VeCeMWReferencesListNode() {
this.internalList = null;
this.listNode = null;
this.modified = false;
this.docRefs = null;
// DOM changes
this.$element.addClass( 've-ce-mwReferencesListNode' );
@ -174,6 +175,7 @@ ve.ce.MWReferencesListNode.prototype.update = function () {
return;
}
this.docRefs = ve.dm.MWDocumentReferences.static.refsForDoc( model.getDocument() );
const internalList = model.getDocument().internalList;
const refGroup = model.getAttribute( 'refGroup' );
const listGroup = model.getAttribute( 'listGroup' );
@ -238,9 +240,11 @@ ve.ce.MWReferencesListNode.prototype.update = function () {
this.$refmsg.text( emptyText );
this.$element.append( this.$refmsg );
} else {
const groupedByParent = this.docRefs.getGroupRefsByParents( listGroup );
const topLevelNodes = groupedByParent[ '' ] || [];
this.$reflist.append(
nodes.indexOrder.map( ( index ) => this.renderListItem(
nodes, internalList, refGroup, index
topLevelNodes.map( ( node ) => this.renderListItem(
nodes, internalList, refGroup, node
) )
);
@ -256,11 +260,12 @@ ve.ce.MWReferencesListNode.prototype.update = function () {
* @param {Object} nodes Node group object, containing nodes and key order array
* @param {ve.dm.InternalList} internalList Internal list
* @param {string} refGroup Reference group
* @param {number} index Item index
* @return {jQuery} List item
* @param {ve.dm.MWReferenceNode} node Reference node to render as a footnote body
* @return {jQuery} Rendered list item
*/
ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internalList, refGroup, index ) {
const key = internalList.keys[ index ];
ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internalList, refGroup, node ) {
const listIndex = node.getAttribute( 'listIndex' );
const key = internalList.keys[ listIndex ];
const keyedNodes = ( nodes.keyedNodes[ key ] || [] )
.filter(
// Exclude placeholders and references defined inside the references list node
@ -271,7 +276,7 @@ ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internal
.append( this.renderBacklinks( keyedNodes, refGroup ), ' ' );
// Generate reference HTML from first item in key
const modelNode = internalList.getItemNode( index );
const modelNode = internalList.getItemNode( listIndex );
if ( modelNode && modelNode.length ) {
const refPreview = new ve.ui.MWPreviewElement( modelNode, { useView: true } );
$li.append(
@ -285,8 +290,7 @@ ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internal
// TODO: attach to the singleton click handler on the surface
$li.on( 'mousedown', ( e ) => {
if ( ve.isUnmodifiedLeftClick( e ) && modelNode && modelNode.length ) {
const firstNode = nodes.firstNodes[ index ];
const items = ve.ui.contextItemFactory.getRelatedItems( [ firstNode ] )
const items = ve.ui.contextItemFactory.getRelatedItems( [ node ] )
.filter( ( item ) => item.name !== 'mobileActions' );
if ( items.length ) {
const contextItem = ve.ui.contextItemFactory.lookup( items[ 0 ].name );
@ -296,7 +300,7 @@ ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internal
if ( command ) {
const fragmentArgs = {
fragment: surface.getModel()
.getLinearFragment( firstNode.getOuterRange(), true ),
.getLinearFragment( node.getOuterRange(), true ),
selectFragmentOnClose: false
};
const newArgs = ve.copy( command.args );

View file

@ -0,0 +1,68 @@
'use strict';
/*!
* @copyright 2024 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
* @license MIT
*/
/**
* A facade providing a simplified and safe interface to Cite `ref` and
* `references` tags in a document.
*
* @constructor
* @mixes OO.EventEmitter
* @param {ve.dm.Document} doc The document that reference tags will be embedded in.
*/
ve.dm.MWDocumentReferences = function VeDmMWDocumentReferences( doc ) {
// Mixin constructors
OO.EventEmitter.call( this );
// Properties
this.doc = doc;
};
/* Inheritance */
OO.mixinClass( ve.dm.MWDocumentReferences, OO.EventEmitter );
/* Methods */
/**
* Singleton MWDocumentReferences for a document.
*
* @param {ve.dm.Document} doc Source document associated with the singleton
* @return {ve.dm.MWDocumentReferences} Singleton docRefs
*/
ve.dm.MWDocumentReferences.static.refsForDoc = function ( doc ) {
let docRefs = doc.getStorage( 'document-references-store' );
if ( docRefs === undefined ) {
docRefs = new ve.dm.MWDocumentReferences( doc );
doc.setStorage( 'document-references-store', docRefs );
}
return docRefs;
};
/**
* Get all refs for a group, organized by parent ref
*
* This is appropriate when rendering a reflist organized hierarchically by
* subrefs using the `extends` feature.
*
* @param {string} groupName Filter by this group.
* @return {Object.<string, ve.dm.MWReferenceNode[]>} Mapping from parent ref
* name to a list of its subrefs. Note that the top-level refs are under the
* `null` value.
*/
ve.dm.MWDocumentReferences.prototype.getGroupRefsByParents = function ( groupName ) {
const nodeGroup = this.doc.getInternalList().getNodeGroup( groupName );
return ( nodeGroup ? nodeGroup.indexOrder : [] )
.reduce( ( acc, index ) => {
const node = nodeGroup.firstNodes[ index ];
const extendsRef = node.element.attributes.extendsRef || '';
if ( acc[ extendsRef ] === undefined ) {
acc[ extendsRef ] = [];
}
acc[ extendsRef ].push( node );
return acc;
}, {} );
};