mediawiki-extensions-Visual.../modules/ve/ve.Node.js
Ed Sanders 2925966944 Store DM nodes in InternalList
Also keep items in the order they appear in the document
and grouped by group and key.

Additions and removals are triggered by the new root/unroot events.

Change-Id: Ia3e90ccfdab88f352b89992b90554e5f03ff9952
2013-06-03 22:10:07 +01:00

224 lines
4.4 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 ve.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' );
};
/* 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
* @emits root
* @emits 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
* @emits 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
* @emits 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();
}
};