mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Cite
synced 2024-11-23 14:36:51 +00:00
Merge "Clean up reflist usage of MWGroupReferences"
This commit is contained in:
commit
95263957a1
|
@ -25,6 +25,7 @@
|
|||
"ve.ui.MW": "https://doc.wikimedia.org/VisualEditor/master/js/{type}.html",
|
||||
"mw.": "https://doc.wikimedia.org/mediawiki-core/master/js/{type}.html",
|
||||
"ve.dm.MWDocumentReferences": true,
|
||||
"ve.dm.MWGroupReferences": true,
|
||||
"ve.dm.MWReference": true,
|
||||
"ve.ce.MWReference": true,
|
||||
"ve.ui.MWReference": true
|
||||
|
|
|
@ -27,7 +27,6 @@ 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' );
|
||||
|
@ -175,12 +174,11 @@ 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' );
|
||||
const nodes = internalList.getNodeGroup( listGroup );
|
||||
const hasModelReferences = !!( nodes && nodes.indexOrder.length );
|
||||
|
||||
const docRefs = ve.dm.MWDocumentReferences.static.refsForDoc( model.getDocument() );
|
||||
const groupRefs = docRefs.getGroupRefs( refGroup );
|
||||
const hasModelReferences = !groupRefs.isEmpty();
|
||||
|
||||
let emptyText;
|
||||
if ( refGroup !== '' ) {
|
||||
|
@ -240,14 +238,11 @@ ve.ce.MWReferencesListNode.prototype.update = function () {
|
|||
this.$refmsg.text( emptyText );
|
||||
this.$element.append( this.$refmsg );
|
||||
} else {
|
||||
const groupRefs = this.docRefs.getGroupRefs( listGroup );
|
||||
// Render all at once.
|
||||
this.$reflist.append(
|
||||
// FIXME: Clean up access functions.
|
||||
Object.keys( groupRefs.footnoteNumberLookup )
|
||||
.filter( ( listKey ) => groupRefs.footnoteNumberLookup[ listKey ][ 1 ] === -1 )
|
||||
.sort( ( aKey, bKey ) => groupRefs.footnoteNumberLookup[ aKey ][ 0 ] - groupRefs.footnoteNumberLookup[ bKey ][ 0 ] )
|
||||
groupRefs.getTopLevelKeysInReflistOrder()
|
||||
.map( ( listKey ) => this.renderListItem(
|
||||
nodes, internalList, groupRefs, refGroup, listKey
|
||||
groupRefs, refGroup, listKey
|
||||
) )
|
||||
);
|
||||
|
||||
|
@ -260,31 +255,22 @@ ve.ce.MWReferencesListNode.prototype.update = function () {
|
|||
* Render a reference list item
|
||||
*
|
||||
* @private
|
||||
* @param {Object} nodes Node group object, containing nodes and key order array
|
||||
* @param {ve.dm.InternalList} internalList Internal list
|
||||
* @param {ve.dm.MWGroupReferences} groupRefs object holding calculated information about all group refs
|
||||
* @param {string} refGroup Reference group
|
||||
* @param {string} key top-level reference key, doesn't necessarily exist
|
||||
* @return {jQuery} Rendered list item
|
||||
*/
|
||||
ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internalList, groupRefs, refGroup, key ) {
|
||||
const keyedNodes = nodes.keyedNodes[ key ] || [];
|
||||
const node = keyedNodes ? keyedNodes[ 0 ] : null;
|
||||
const listIndex = node ? node.getAttribute( 'listIndex' ) : null;
|
||||
const backlinkNodes = keyedNodes.filter(
|
||||
// Exclude placeholders and references defined inside the references list node
|
||||
( backRefNode ) => !backRefNode.getAttribute( 'placeholder' ) && !backRefNode.findParent( ve.dm.MWReferencesListNode )
|
||||
);
|
||||
const subrefs = groupRefs.subRefsByParent[ key ] || [];
|
||||
ve.ce.MWReferencesListNode.prototype.renderListItem = function ( groupRefs, refGroup, key ) {
|
||||
const ref = groupRefs.getInternalModelNode( key );
|
||||
const backlinkNodes = groupRefs.getRefUsages( key );
|
||||
const subrefs = groupRefs.getSubrefs( key );
|
||||
|
||||
const $li = $( '<li>' )
|
||||
.css( '--footnote-number', `"${ groupRefs.getIndexLabel( key ) }."` )
|
||||
.append( this.renderBacklinks( backlinkNodes, refGroup ), ' ' );
|
||||
|
||||
// Generate reference HTML from first item in key
|
||||
const modelNode = internalList.getItemNode( listIndex );
|
||||
if ( modelNode && modelNode.length ) {
|
||||
const refPreview = new ve.ui.MWPreviewElement( modelNode, { useView: true } );
|
||||
if ( ref && ref.length ) {
|
||||
const refPreview = new ve.ui.MWPreviewElement( ref, { useView: true } );
|
||||
$li.append(
|
||||
$( '<span>' )
|
||||
.addClass( 'reference-text' )
|
||||
|
@ -295,7 +281,8 @@ ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internal
|
|||
const surface = this.getRoot().getSurface().getSurface();
|
||||
// TODO: attach to the singleton click handler on the surface
|
||||
$li.on( 'mousedown', ( e ) => {
|
||||
if ( ve.isUnmodifiedLeftClick( e ) && modelNode && modelNode.length ) {
|
||||
if ( ve.isUnmodifiedLeftClick( e ) ) {
|
||||
const node = groupRefs.getRefNode( key );
|
||||
const items = ve.ui.contextItemFactory.getRelatedItems( [ node ] )
|
||||
.filter( ( item ) => item.name !== 'mobileActions' );
|
||||
if ( items.length ) {
|
||||
|
@ -336,7 +323,7 @@ ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internal
|
|||
$li.append(
|
||||
$( '<ol>' ).append(
|
||||
subrefs.map( ( subNode ) => this.renderListItem(
|
||||
nodes, internalList, groupRefs, refGroup, subNode.getAttribute( 'listKey' )
|
||||
groupRefs, refGroup, subNode.getAttribute( 'listKey' )
|
||||
) )
|
||||
)
|
||||
);
|
||||
|
|
|
@ -85,7 +85,8 @@ ve.dm.MWDocumentReferences.prototype.updateGroup = function ( groupName ) {
|
|||
* @return {ve.dm.MWGroupReferences}
|
||||
*/
|
||||
ve.dm.MWDocumentReferences.prototype.getGroupRefs = function ( groupName ) {
|
||||
return this.cachedByGroup[ groupName.startsWith( 'mwReference/' ) ? groupName : 'mwReference/' + groupName ];
|
||||
return this.cachedByGroup[ groupName.startsWith( 'mwReference/' ) ? groupName : 'mwReference/' + groupName ] ||
|
||||
new ve.dm.MWGroupReferences();
|
||||
};
|
||||
|
||||
ve.dm.MWDocumentReferences.prototype.getAllGroupNames = function () {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
* This structure is persisted in memory until a document change affects a ref
|
||||
* tag from this group, at which point it will be fully recalculated.
|
||||
*
|
||||
* @private
|
||||
* @constructor
|
||||
*/
|
||||
ve.dm.MWGroupReferences = function VeDmMWGroupReferences() {
|
||||
|
@ -19,13 +18,33 @@ ve.dm.MWGroupReferences = function VeDmMWGroupReferences() {
|
|||
OO.EventEmitter.call( this );
|
||||
|
||||
// Properties
|
||||
/**
|
||||
* Lookup from listKey to a pair of integers which are the [major, minor] footnote numbers
|
||||
* that will be rendered on the ref in some digit system. Note that top-level refs always
|
||||
* have minor number `-1`.
|
||||
*
|
||||
* @member {Object.<string, number[]>}
|
||||
*/
|
||||
this.footnoteNumberLookup = {};
|
||||
// FIXME: push labeling to presentation code and drop from here.
|
||||
/**
|
||||
* Lookup from listKey to a rendered footnote number or subref number like "1.2", in the
|
||||
* local content language.
|
||||
*
|
||||
* FIXME: push labeling to presentation code and drop from here.
|
||||
*
|
||||
* @member {Object.<string, string>}
|
||||
*/
|
||||
this.footnoteLabelLookup = {};
|
||||
/**
|
||||
* Lookup from parent listKey to subrefs.
|
||||
*
|
||||
* @member {Object.<string, ve.dm.MWReferenceNode[]>}
|
||||
*/
|
||||
this.subRefsByParent = {};
|
||||
|
||||
/** @private */
|
||||
this.topLevelCounter = 1;
|
||||
/** @private */
|
||||
this.nodeGroup = null;
|
||||
};
|
||||
|
||||
|
@ -100,6 +119,20 @@ ve.dm.MWGroupReferences.prototype.addSubref = function ( parentKey, listKey, sub
|
|||
};
|
||||
|
||||
/**
|
||||
* Check whether the group has any references.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
ve.dm.MWGroupReferences.prototype.isEmpty = function () {
|
||||
// Use an internal shortcut, otherwise we could do something like
|
||||
// !!nodes.indexOrder.length
|
||||
return this.topLevelCounter === 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* List all document references in the order they first appear, ignoring reuses
|
||||
* and placeholders.
|
||||
*
|
||||
* @return {ve.dm.MWReferenceNode[]}
|
||||
*/
|
||||
ve.dm.MWGroupReferences.prototype.getAllRefsInDocumentOrder = function () {
|
||||
|
@ -110,6 +143,64 @@ ve.dm.MWGroupReferences.prototype.getAllRefsInDocumentOrder = function () {
|
|||
.map( ( nodes ) => nodes[ 0 ] );
|
||||
};
|
||||
|
||||
/**
|
||||
* List all reference listKeys in the order they appear in the reflist including
|
||||
* named refs, unnamed refs, and those that don't resolve
|
||||
*
|
||||
* @return {string[]} Reference listKeys
|
||||
*/
|
||||
ve.dm.MWGroupReferences.prototype.getTopLevelKeysInReflistOrder = function () {
|
||||
return Object.keys( this.footnoteNumberLookup )
|
||||
.sort( ( aKey, bKey ) => this.footnoteNumberLookup[ aKey ][ 0 ] - this.footnoteNumberLookup[ bKey ][ 0 ] )
|
||||
// TODO: Function could be split here, if a use case is found for a list of
|
||||
// all numbers including subrefs.
|
||||
.filter( ( listKey ) => this.footnoteNumberLookup[ listKey ][ 1 ] === -1 );
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the defining reference node for this key
|
||||
*
|
||||
* @see #getInternalModelNode
|
||||
*
|
||||
* @param {string} key in listKey format
|
||||
* @return {ve.dm.MWReferenceNode|undefined}
|
||||
*/
|
||||
ve.dm.MWGroupReferences.prototype.getRefNode = function ( key ) {
|
||||
const keyedNodes = this.nodeGroup.keyedNodes[ key ];
|
||||
return keyedNodes && keyedNodes[ 0 ];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the internalList internal item if it exists.
|
||||
*
|
||||
* @see #getRefNode
|
||||
*
|
||||
* @param {string} key in listKey format
|
||||
* @return {ve.dm.InternalItemNode|undefined}
|
||||
*/
|
||||
ve.dm.MWGroupReferences.prototype.getInternalModelNode = function ( key ) {
|
||||
const ref = this.getRefNode( key );
|
||||
return ref && ref.getInternalItem();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return document nodes for each usage of a ref key. This excludes usages
|
||||
* under the `<references>` section, so note that nested references won't behave
|
||||
* as expected. The reflist item for a ref is not counted as a reference,
|
||||
* either.
|
||||
*
|
||||
* FIXME: Implement backlinks from within a nested ref within the footnote body.
|
||||
*
|
||||
* @param {string} key in listKey format
|
||||
* @return {ve.dm.MWReferenceNode[]}
|
||||
*/
|
||||
ve.dm.MWGroupReferences.prototype.getRefUsages = function ( key ) {
|
||||
return ( this.nodeGroup.keyedNodes[ key ] || [] )
|
||||
.filter( ( node ) => !node.getAttribute( 'placeholder' ) &&
|
||||
!node.findParent( ve.dm.MWReferencesListNode )
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} parentKey parent ref key
|
||||
* @return {ve.dm.MWReferenceNode[]} List of subrefs for this parent
|
||||
|
|
Loading…
Reference in a new issue