mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 10:35:48 +00:00
7db65f386c
Instead of using @emits in both, use our custom @fires in production (JSDuck 4), and in the future it'll just naturally use the native one. This way we can also index oojs without issues, which seems to have started using @fires already. Change-Id: I7c3b56dd112626d57fa87ab995d205fb782a0149
240 lines
4.8 KiB
JavaScript
240 lines
4.8 KiB
JavaScript
/*!
|
|
* VisualEditor Node class.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* Generic node.
|
|
*
|
|
* @abstract
|
|
* @mixins OO.EventEmitter
|
|
*
|
|
* @constructor
|
|
*/
|
|
ve.Node = function VeNode() {
|
|
// Properties
|
|
this.type = this.constructor.static.name;
|
|
this.parent = null;
|
|
this.root = null;
|
|
this.doc = null;
|
|
};
|
|
|
|
/**
|
|
* @event attach
|
|
* @param {ve.Node} parent
|
|
*/
|
|
|
|
/**
|
|
* @event detach
|
|
* @param {ve.Node} parent
|
|
*/
|
|
|
|
/**
|
|
* @event root
|
|
*/
|
|
|
|
/**
|
|
* @event unroot
|
|
*/
|
|
|
|
/* Abstract Methods */
|
|
|
|
/**
|
|
* Check if the node can have children.
|
|
*
|
|
* @method
|
|
* @abstract
|
|
* @returns {boolean} Node can have children
|
|
* @throws {Error} if not overridden
|
|
*/
|
|
ve.Node.prototype.canHaveChildren = function () {
|
|
throw new Error( 've.Node.canHaveChildren must be overridden in subclass' );
|
|
};
|
|
|
|
/**
|
|
* Check if the node can have children but not content nor be content.
|
|
*
|
|
* @method
|
|
* @abstract
|
|
* @returns {boolean} Node can have children but not content nor be content
|
|
* @throws {Error} if not overridden
|
|
*/
|
|
ve.Node.prototype.canHaveChildrenNotContent = function () {
|
|
throw new Error( 've.Node.canHaveChildrenNotContent must be overridden in subclass' );
|
|
};
|
|
|
|
/**
|
|
* Check if the node has a wrapped element in the document data.
|
|
*
|
|
* @method
|
|
* @abstract
|
|
* @returns {boolean} Node represents a wrapped element
|
|
* @throws {Error} if not overridden
|
|
*/
|
|
ve.Node.prototype.isWrapped = function () {
|
|
throw new Error( 've.Node.isWrapped must be overridden in subclass' );
|
|
};
|
|
|
|
/**
|
|
* Get the length of the node.
|
|
*
|
|
* @method
|
|
* @abstract
|
|
* @returns {number} Node length
|
|
* @throws {Error} if not overridden
|
|
*/
|
|
ve.Node.prototype.getLength = function () {
|
|
throw new Error( 've.Node.getLength must be overridden in subclass' );
|
|
};
|
|
|
|
/**
|
|
* Get the outer length of the node, which includes wrappers if present.
|
|
*
|
|
* @method
|
|
* @abstract
|
|
* @returns {number} Node outer length
|
|
* @throws {Error} if not overridden
|
|
*/
|
|
ve.Node.prototype.getOuterLength = function () {
|
|
throw new Error( 've.Node.getOuterLength must be overridden in subclass' );
|
|
};
|
|
|
|
/**
|
|
* Get the outer range of the node, which includes wrappers if present.
|
|
*
|
|
* @method
|
|
* @param {boolean} backwards Return a backwards range
|
|
* @returns {ve.Range} Node outer range
|
|
*/
|
|
ve.Node.prototype.getOuterRange = function ( backwards ) {
|
|
var range = new ve.Range( this.getOffset(), this.getOffset() + this.getOuterLength() );
|
|
if ( backwards ) {
|
|
return range.flip();
|
|
} else {
|
|
return range;
|
|
}
|
|
};
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Get the symbolic node type name.
|
|
*
|
|
* @method
|
|
* @returns {string} Symbolic name of element type
|
|
*/
|
|
ve.Node.prototype.getType = function () {
|
|
return this.type;
|
|
};
|
|
|
|
/**
|
|
* Get a reference to the node's parent.
|
|
*
|
|
* @method
|
|
* @returns {ve.Node} Reference to the node's parent
|
|
*/
|
|
ve.Node.prototype.getParent = function () {
|
|
return this.parent;
|
|
};
|
|
|
|
/**
|
|
* Get the root node of the tree the node is currently attached to.
|
|
*
|
|
* @method
|
|
* @returns {ve.Node} Root node
|
|
*/
|
|
ve.Node.prototype.getRoot = function () {
|
|
return this.root;
|
|
};
|
|
|
|
/**
|
|
* Set the root node.
|
|
*
|
|
* This method is overridden by nodes with children.
|
|
*
|
|
* @method
|
|
* @param {ve.Node} root Node to use as root
|
|
* @fires root
|
|
* @fires unroot
|
|
*/
|
|
ve.Node.prototype.setRoot = function ( root ) {
|
|
if ( root !== this.root ) {
|
|
this.root = root;
|
|
if ( this.getRoot() ) {
|
|
this.emit( 'root' );
|
|
} else {
|
|
this.emit( 'unroot' );
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get the document the node is a part of.
|
|
*
|
|
* @method
|
|
* @returns {ve.Document} Document the node is a part of
|
|
*/
|
|
ve.Node.prototype.getDocument = function () {
|
|
return this.doc;
|
|
};
|
|
|
|
/**
|
|
* Set the document the node is a part of.
|
|
*
|
|
* This method is overridden by nodes with children.
|
|
*
|
|
* @method
|
|
* @param {ve.Document} doc Document this node is a part of
|
|
*/
|
|
ve.Node.prototype.setDocument = function ( doc ) {
|
|
this.doc = doc;
|
|
};
|
|
|
|
/**
|
|
* Attach the node to another as a child.
|
|
*
|
|
* @method
|
|
* @param {ve.Node} parent Node to attach to
|
|
* @fires attach
|
|
*/
|
|
ve.Node.prototype.attach = function ( parent ) {
|
|
this.parent = parent;
|
|
this.setRoot( parent.getRoot() );
|
|
this.setDocument( parent.getDocument() );
|
|
this.emit( 'attach', parent );
|
|
};
|
|
|
|
/**
|
|
* Detach the node from its parent.
|
|
*
|
|
* @method
|
|
* @fires detach
|
|
*/
|
|
ve.Node.prototype.detach = function () {
|
|
var parent = this.parent;
|
|
this.parent = null;
|
|
this.setRoot( null );
|
|
this.setDocument( null );
|
|
this.emit( 'detach', parent );
|
|
};
|
|
|
|
/**
|
|
* Traverse tree of nodes (model or view) upstream.
|
|
*
|
|
* For each traversed node, the callback function will be passed the traversed node as a parameter.
|
|
*
|
|
* @param {Function} callback Callback method to be called for every traversed node
|
|
* @method
|
|
*/
|
|
ve.Node.prototype.traverseUpstream = function ( callback ) {
|
|
var node = this;
|
|
while ( node ) {
|
|
if ( callback ( node ) === false ) {
|
|
break;
|
|
}
|
|
node = node.getParent();
|
|
}
|
|
};
|