mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Cite
synced 2024-11-27 08:20:02 +00:00
Clean up reflist usage of MWGroupReferences
Switch this usage entirely to the new interface, moving much of the junk out. Bug: T372871 Change-Id: I445af469a823144b0b6fa5e6f4f23a858939e9e6
This commit is contained in:
parent
ab7d60ca05
commit
35e8af7d40
|
@ -25,6 +25,7 @@
|
||||||
"ve.ui.MW": "https://doc.wikimedia.org/VisualEditor/master/js/{type}.html",
|
"ve.ui.MW": "https://doc.wikimedia.org/VisualEditor/master/js/{type}.html",
|
||||||
"mw.": "https://doc.wikimedia.org/mediawiki-core/master/js/{type}.html",
|
"mw.": "https://doc.wikimedia.org/mediawiki-core/master/js/{type}.html",
|
||||||
"ve.dm.MWDocumentReferences": true,
|
"ve.dm.MWDocumentReferences": true,
|
||||||
|
"ve.dm.MWGroupReferences": true,
|
||||||
"ve.dm.MWReference": true,
|
"ve.dm.MWReference": true,
|
||||||
"ve.ce.MWReference": true,
|
"ve.ce.MWReference": true,
|
||||||
"ve.ui.MWReference": true
|
"ve.ui.MWReference": true
|
||||||
|
|
|
@ -27,7 +27,6 @@ ve.ce.MWReferencesListNode = function VeCeMWReferencesListNode() {
|
||||||
this.internalList = null;
|
this.internalList = null;
|
||||||
this.listNode = null;
|
this.listNode = null;
|
||||||
this.modified = false;
|
this.modified = false;
|
||||||
this.docRefs = null;
|
|
||||||
|
|
||||||
// DOM changes
|
// DOM changes
|
||||||
this.$element.addClass( 've-ce-mwReferencesListNode' );
|
this.$element.addClass( 've-ce-mwReferencesListNode' );
|
||||||
|
@ -175,12 +174,11 @@ ve.ce.MWReferencesListNode.prototype.update = function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.docRefs = ve.dm.MWDocumentReferences.static.refsForDoc( model.getDocument() );
|
|
||||||
const internalList = model.getDocument().internalList;
|
|
||||||
const refGroup = model.getAttribute( 'refGroup' );
|
const refGroup = model.getAttribute( 'refGroup' );
|
||||||
const listGroup = model.getAttribute( 'listGroup' );
|
|
||||||
const nodes = internalList.getNodeGroup( listGroup );
|
const docRefs = ve.dm.MWDocumentReferences.static.refsForDoc( model.getDocument() );
|
||||||
const hasModelReferences = !!( nodes && nodes.indexOrder.length );
|
const groupRefs = docRefs.getGroupRefs( refGroup );
|
||||||
|
const hasModelReferences = !groupRefs.isEmpty();
|
||||||
|
|
||||||
let emptyText;
|
let emptyText;
|
||||||
if ( refGroup !== '' ) {
|
if ( refGroup !== '' ) {
|
||||||
|
@ -240,14 +238,11 @@ ve.ce.MWReferencesListNode.prototype.update = function () {
|
||||||
this.$refmsg.text( emptyText );
|
this.$refmsg.text( emptyText );
|
||||||
this.$element.append( this.$refmsg );
|
this.$element.append( this.$refmsg );
|
||||||
} else {
|
} else {
|
||||||
const groupRefs = this.docRefs.getGroupRefs( listGroup );
|
// Render all at once.
|
||||||
this.$reflist.append(
|
this.$reflist.append(
|
||||||
// FIXME: Clean up access functions.
|
groupRefs.getTopLevelKeysInReflistOrder()
|
||||||
Object.keys( groupRefs.footnoteNumberLookup )
|
|
||||||
.filter( ( listKey ) => groupRefs.footnoteNumberLookup[ listKey ][ 1 ] === -1 )
|
|
||||||
.sort( ( aKey, bKey ) => groupRefs.footnoteNumberLookup[ aKey ][ 0 ] - groupRefs.footnoteNumberLookup[ bKey ][ 0 ] )
|
|
||||||
.map( ( listKey ) => this.renderListItem(
|
.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
|
* Render a reference list item
|
||||||
*
|
*
|
||||||
* @private
|
* @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 {ve.dm.MWGroupReferences} groupRefs object holding calculated information about all group refs
|
||||||
* @param {string} refGroup Reference group
|
* @param {string} refGroup Reference group
|
||||||
* @param {string} key top-level reference key, doesn't necessarily exist
|
* @param {string} key top-level reference key, doesn't necessarily exist
|
||||||
* @return {jQuery} Rendered list item
|
* @return {jQuery} Rendered list item
|
||||||
*/
|
*/
|
||||||
ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internalList, groupRefs, refGroup, key ) {
|
ve.ce.MWReferencesListNode.prototype.renderListItem = function ( groupRefs, refGroup, key ) {
|
||||||
const keyedNodes = nodes.keyedNodes[ key ] || [];
|
const ref = groupRefs.getInternalModelNode( key );
|
||||||
const node = keyedNodes ? keyedNodes[ 0 ] : null;
|
const backlinkNodes = groupRefs.getRefUsages( key );
|
||||||
const listIndex = node ? node.getAttribute( 'listIndex' ) : null;
|
const subrefs = groupRefs.getSubrefs( key );
|
||||||
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 ] || [];
|
|
||||||
|
|
||||||
const $li = $( '<li>' )
|
const $li = $( '<li>' )
|
||||||
.css( '--footnote-number', `"${ groupRefs.getIndexLabel( key ) }."` )
|
.css( '--footnote-number', `"${ groupRefs.getIndexLabel( key ) }."` )
|
||||||
.append( this.renderBacklinks( backlinkNodes, refGroup ), ' ' );
|
.append( this.renderBacklinks( backlinkNodes, refGroup ), ' ' );
|
||||||
|
|
||||||
// Generate reference HTML from first item in key
|
if ( ref && ref.length ) {
|
||||||
const modelNode = internalList.getItemNode( listIndex );
|
const refPreview = new ve.ui.MWPreviewElement( ref, { useView: true } );
|
||||||
if ( modelNode && modelNode.length ) {
|
|
||||||
const refPreview = new ve.ui.MWPreviewElement( modelNode, { useView: true } );
|
|
||||||
$li.append(
|
$li.append(
|
||||||
$( '<span>' )
|
$( '<span>' )
|
||||||
.addClass( 'reference-text' )
|
.addClass( 'reference-text' )
|
||||||
|
@ -295,7 +281,8 @@ ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internal
|
||||||
const surface = this.getRoot().getSurface().getSurface();
|
const surface = this.getRoot().getSurface().getSurface();
|
||||||
// TODO: attach to the singleton click handler on the surface
|
// TODO: attach to the singleton click handler on the surface
|
||||||
$li.on( 'mousedown', ( e ) => {
|
$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 ] )
|
const items = ve.ui.contextItemFactory.getRelatedItems( [ node ] )
|
||||||
.filter( ( item ) => item.name !== 'mobileActions' );
|
.filter( ( item ) => item.name !== 'mobileActions' );
|
||||||
if ( items.length ) {
|
if ( items.length ) {
|
||||||
|
@ -336,7 +323,7 @@ ve.ce.MWReferencesListNode.prototype.renderListItem = function ( nodes, internal
|
||||||
$li.append(
|
$li.append(
|
||||||
$( '<ol>' ).append(
|
$( '<ol>' ).append(
|
||||||
subrefs.map( ( subNode ) => this.renderListItem(
|
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}
|
* @return {ve.dm.MWGroupReferences}
|
||||||
*/
|
*/
|
||||||
ve.dm.MWDocumentReferences.prototype.getGroupRefs = function ( groupName ) {
|
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 () {
|
ve.dm.MWDocumentReferences.prototype.getAllGroupNames = function () {
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
* This structure is persisted in memory until a document change affects a ref
|
* 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.
|
* tag from this group, at which point it will be fully recalculated.
|
||||||
*
|
*
|
||||||
* @private
|
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
ve.dm.MWGroupReferences = function VeDmMWGroupReferences() {
|
ve.dm.MWGroupReferences = function VeDmMWGroupReferences() {
|
||||||
|
@ -19,13 +18,33 @@ ve.dm.MWGroupReferences = function VeDmMWGroupReferences() {
|
||||||
OO.EventEmitter.call( this );
|
OO.EventEmitter.call( this );
|
||||||
|
|
||||||
// Properties
|
// 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 = {};
|
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 = {};
|
this.footnoteLabelLookup = {};
|
||||||
|
/**
|
||||||
|
* Lookup from parent listKey to subrefs.
|
||||||
|
*
|
||||||
|
* @member {Object.<string, ve.dm.MWReferenceNode[]>}
|
||||||
|
*/
|
||||||
this.subRefsByParent = {};
|
this.subRefsByParent = {};
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
this.topLevelCounter = 1;
|
this.topLevelCounter = 1;
|
||||||
|
/** @private */
|
||||||
this.nodeGroup = null;
|
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[]}
|
* @return {ve.dm.MWReferenceNode[]}
|
||||||
*/
|
*/
|
||||||
ve.dm.MWGroupReferences.prototype.getAllRefsInDocumentOrder = function () {
|
ve.dm.MWGroupReferences.prototype.getAllRefsInDocumentOrder = function () {
|
||||||
|
@ -110,6 +143,64 @@ ve.dm.MWGroupReferences.prototype.getAllRefsInDocumentOrder = function () {
|
||||||
.map( ( nodes ) => nodes[ 0 ] );
|
.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
|
* @param {string} parentKey parent ref key
|
||||||
* @return {ve.dm.MWReferenceNode[]} List of subrefs for this parent
|
* @return {ve.dm.MWReferenceNode[]} List of subrefs for this parent
|
||||||
|
|
Loading…
Reference in a new issue