/*! * VisualEditor Node class. * * @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt * @license The MIT License (MIT); see LICENSE.txt */ /** * Generic node. * * @abstract * @extends ve.EventEmitter * * @constructor * @param {string} type Symbolic name of node type */ ve.Node = function VeNode( type ) { // Parent constructor ve.EventEmitter.call( this ); // Properties this.type = type; this.parent = null; this.root = this; this.doc = null; }; /** * @event attach * @param parent */ /** * @event detach */ /* Inheritance */ ve.inheritClass( ve.Node, ve.EventEmitter ); /* 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 grandchildren. * * @method * @abstract * @returns {boolean} Node can have grandchildren * @throws {Error} if not overridden */ ve.Node.prototype.canHaveGrandchildren = function () { throw new Error( 've.Node.canHaveGrandchildren 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 */ ve.Node.prototype.setRoot = function ( root ) { this.root = root; }; /** * 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( this ); this.setDocument(); 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(); } };