mediawiki-extensions-Visual.../modules/ve-mw/dm/models/ve.dm.MWReferenceModel.js
Moriel Schottlender e0894b807f Surfaces should have parent document direction
Make sure all surface widgets have documents that are initialized
with the direction of the parent document, so the alignment is set up
correctly.

Bug: T71969
Change-Id: I6e5f003e18a9c5808d9a4b148eddf0d0f7e29e67
2014-12-10 16:46:16 -05:00

326 lines
8.4 KiB
JavaScript

/*!
* VisualEditor DataModel MWReferenceModel class.
*
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* MediaWiki reference model.
*
* @class
* @mixins OO.EventEmitter
*
* @constructor
*/
ve.dm.MWReferenceModel = function VeDmMWReferenceModel() {
// Mixin constructors
OO.EventEmitter.call( this );
// Properties
this.listKey = '';
this.listGroup = '';
this.listIndex = null;
this.group = '';
this.doc = null;
this.deferDoc = null;
this.dir = null;
this.lang = null;
};
/* Inheritance */
OO.mixinClass( ve.dm.MWReferenceModel, OO.EventEmitter );
/* Static Methods */
/**
* Create a reference model from a reference internal item.
*
* @param {ve.dm.MWReferenceNode} node Reference node
* @return {ve.dm.MWReferenceModel} Reference model
*/
ve.dm.MWReferenceModel.static.newFromReferenceNode = function ( node ) {
var doc = node.getDocument(),
internalList = doc.getInternalList(),
attr = node.getAttributes(),
ref = new ve.dm.MWReferenceModel();
ref.setListKey( attr.listKey );
ref.setListGroup( attr.listGroup );
ref.setListIndex( attr.listIndex );
ref.setGroup( attr.refGroup );
ref.setDir( doc.getDir() );
ref.setLang( doc.getLang() );
ref.deferDoc = function () {
// cloneFromRange is very expensive, so lazy evaluate it
return doc.cloneFromRange( internalList.getItemNode( attr.listIndex ).getRange() );
};
return ref;
};
/* Methods */
/**
* Find matching item in a surface.
*
* @param {ve.dm.Surface} surfaceModel Surface reference is in
* @returns {ve.dm.InternalItemNode|null} Internal reference item, null if none exists
*/
ve.dm.MWReferenceModel.prototype.findInternalItem = function ( surfaceModel ) {
if ( this.listIndex !== null ) {
return surfaceModel.getDocument().getInternalList().getItemNode( this.listIndex );
}
return null;
};
/**
* Insert reference internal item into a surface.
*
* If the internal item for this reference doesn't exist, use this method to create one.
* The inserted reference is empty and auto-numbered.
*
* @param {ve.dm.Surface} surfaceModel Surface model of main document
*/
ve.dm.MWReferenceModel.prototype.insertInternalItem = function ( surfaceModel ) {
// Create new internal item
var item,
doc = surfaceModel.getDocument(),
internalList = doc.getInternalList();
// Fill in data
this.setListKey( 'auto/' + internalList.getNextUniqueNumber() );
this.setListGroup( 'mwReference/' + this.group );
// Insert internal reference item into document
item = internalList.getItemInsertion( this.listGroup, this.listKey, [] );
surfaceModel.change( item.transaction );
this.setListIndex( item.index );
// Inject reference document into internal reference item
surfaceModel.change(
ve.dm.Transaction.newFromDocumentInsertion(
doc,
internalList.getItemNode( item.index ).getRange().start,
this.getDocument()
)
);
};
/**
* Update an internal reference item.
*
* An internal item for the reference will be created if no `ref` argument is given.
*
* @param {ve.dm.Surface} surfaceModel Surface model of main document
*/
ve.dm.MWReferenceModel.prototype.updateInternalItem = function ( surfaceModel ) {
var i, len, txs, group, refNodes, keyIndex, itemNodeRange,
doc = surfaceModel.getDocument(),
internalList = doc.getInternalList(),
listGroup = 'mwReference/' + this.group;
// Group/key has changed
if ( this.listGroup !== listGroup ) {
// Get all reference nodes with the same group and key
group = internalList.getNodeGroup( this.listGroup );
refNodes = group.keyedNodes[this.listKey] ?
group.keyedNodes[this.listKey].slice() :
[ group.firstNodes[this.listIndex] ];
// Check for name collision when moving items between groups
keyIndex = internalList.getKeyIndex( this.listGroup, this.listKey );
if ( keyIndex !== undefined ) {
// Resolve name collision by generating a new list key
this.listKey = 'auto/' + internalList.getNextUniqueNumber();
}
// Update the group name of all references nodes with the same group and key
txs = [];
for ( i = 0, len = refNodes.length; i < len; i++ ) {
// HACK: Removing and re-inserting nodes to/from the internal list is done
// because internal list doesn't yet support attribute changes
refNodes[i].removeFromInternalList();
txs.push( ve.dm.Transaction.newFromAttributeChanges(
doc,
refNodes[i].getOuterRange().start,
{ refGroup: this.group, listGroup: listGroup }
) );
}
surfaceModel.change( txs );
// HACK: Same as above, internal list issues
for ( i = 0, len = refNodes.length; i < len; i++ ) {
refNodes[i].addToInternalList();
}
this.listGroup = listGroup;
}
// Update internal node content
itemNodeRange = internalList.getItemNode( this.listIndex ).getRange();
surfaceModel.change( ve.dm.Transaction.newFromRemoval( doc, itemNodeRange, true ) );
surfaceModel.change(
ve.dm.Transaction.newFromDocumentInsertion( doc, itemNodeRange.start, this.getDocument() )
);
};
/**
* Insert reference at the end of a surface fragment.
*
* @param {ve.dm.SurfaceFragment} surfaceModel Surface fragment to insert at
*/
ve.dm.MWReferenceModel.prototype.insertReferenceNode = function ( surfaceFragment ) {
surfaceFragment
.insertContent( [
{
type: 'mwReference',
attributes: {
listKey: this.listKey,
listGroup: this.listGroup,
listIndex: this.listIndex,
refGroup: this.group
}
},
{ type: '/mwReference' }
] );
};
/**
* Get the key of a reference in the references list.
*
* @returns {string} Reference's list key
*/
ve.dm.MWReferenceModel.prototype.getListKey = function () {
return this.listKey;
};
/**
* Get the name of the group a references list is in.
*
* @returns {string} References list's group
*/
ve.dm.MWReferenceModel.prototype.getListGroup = function () {
return this.listGroup;
};
/**
* Get the index of reference in the references list.
*
* @returns {string} Reference's index
*/
ve.dm.MWReferenceModel.prototype.getListIndex = function () {
return this.listIndex;
};
/**
* Get the name of the group a reference is in.
*
* @returns {string} Reference's group
*/
ve.dm.MWReferenceModel.prototype.getGroup = function () {
return this.group;
};
/**
* Get reference document.
*
* Auto-generates a blank document if no document exists.
*
* @returns {ve.dm.Document} Reference document
*/
ve.dm.MWReferenceModel.prototype.getDocument = function () {
if ( !this.doc ) {
if ( this.deferDoc ) {
this.doc = this.deferDoc();
} else {
this.doc = new ve.dm.Document( [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
/* htmlDocument */ null,
/* parentDocument */ null,
/* internalList */ null,
/* innerWhitespace */ null,
/* lang */ this.getLang(),
/* dir */ this.getDir() );
}
}
return this.doc;
};
/**
* Set the directionality of the reference document
* @param {string} dir Document directionality
*/
ve.dm.MWReferenceModel.prototype.setDir = function ( dir ) {
this.dir = dir;
};
/**
* Get the directionality of the reference document
* @returns {string} Document directionality
*/
ve.dm.MWReferenceModel.prototype.getDir = function () {
return this.dir;
};
/**
* Set the language of the reference document
* @param {string} lang Document language
*/
ve.dm.MWReferenceModel.prototype.setLang = function ( lang ) {
this.lang = lang;
};
/**
* Get the language of the reference document
* @returns {string} Document language
*/
ve.dm.MWReferenceModel.prototype.getLang = function () {
return this.lang;
};
/**
* Set key of reference in list.
*
* @param {string} Reference's list key
*/
ve.dm.MWReferenceModel.prototype.setListKey = function ( listKey ) {
this.listKey = listKey;
};
/**
* Set name of the group a references list is in.
*
* @param {string} References list's group
*/
ve.dm.MWReferenceModel.prototype.setListGroup = function ( listGroup ) {
this.listGroup = listGroup;
};
/**
* Set the index of reference in list.
*
* @param {string} Reference's list index
*/
ve.dm.MWReferenceModel.prototype.setListIndex = function ( listIndex ) {
this.listIndex = listIndex;
};
/**
* Set the name of the group a reference is in.
*
* @param {string} group Reference's group
*/
ve.dm.MWReferenceModel.prototype.setGroup = function ( group ) {
this.group = group;
};
/**
* Set the reference document.
*
* @param {ve.dm.Document} Reference document
*/
ve.dm.MWReferenceModel.prototype.setDocument = function ( doc ) {
this.doc = doc;
};