node.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import clone from '../lib/lodash/clone.js';
  7. import utils from '../utils.js';
  8. import CKEditorError from '../ckeditorerror.js';
  9. /**
  10. * Creates a tree node.
  11. *
  12. * This is an abstract class, so this constructor should not be used directly.
  13. *
  14. * @param {Iterable|Object} attrs Iterable collection of attributes.
  15. *
  16. * @abstract
  17. * @class core.treeModel.Node
  18. * @classdesc Abstract document tree node class.
  19. */
  20. export default class Node {
  21. constructor( attrs ) {
  22. /**
  23. * Parent element. Null by default. Set by {@link core.treeModel.Element#insertChildren}.
  24. *
  25. * @member core.treeModel.Node#parent
  26. * @readonly
  27. * @member {core.treeModel.Element|null} core.treeModel.Node#parent
  28. */
  29. this.parent = null;
  30. /**
  31. * List of attributes set on this node.
  32. *
  33. * **Note:** It is **important** that attributes of nodes already attached to the document must be changed
  34. * only by an {@link core.treeModel.operation.AttributeOperation}. Do not set attributes of such nodes
  35. * using {@link core.treeModel.Node} methods.
  36. *
  37. * @protected
  38. * @member {Map} core.treeModel.Node#_attrs
  39. */
  40. this._attrs = utils.toMap( attrs );
  41. }
  42. /**
  43. * Depth of the node, which equals to total number of its parents.
  44. *
  45. * @readonly
  46. * @member {Number} core.treeModel.Node#depth
  47. */
  48. get depth() {
  49. let depth = 0;
  50. let parent = this.parent;
  51. while ( parent ) {
  52. depth++;
  53. parent = parent.parent;
  54. }
  55. return depth;
  56. }
  57. /**
  58. * Nodes next sibling or `null` if it is the last child.
  59. *
  60. * @readonly
  61. * @member {core.treeModel.Node|null} core.treeModel.Node#nextSibling
  62. */
  63. get nextSibling() {
  64. const index = this.getIndex();
  65. return ( index !== null && this.parent.getChild( index + 1 ) ) || null;
  66. }
  67. /**
  68. * Nodes previous sibling or null if it is the last child.
  69. *
  70. * @readonly
  71. * @member {core.treeModel.Node|null} core.treeModel.Node#previousSibling
  72. */
  73. get previousSibling() {
  74. const index = this.getIndex();
  75. return ( index !== null && this.parent.getChild( index - 1 ) ) || null;
  76. }
  77. /**
  78. * The top parent for the node. If node has no parent it is the root itself.
  79. *
  80. * @readonly
  81. * @member {Number} core.treeModel.Node#root
  82. */
  83. get root() {
  84. let root = this;
  85. while ( root.parent ) {
  86. root = root.parent;
  87. }
  88. return root;
  89. }
  90. /**
  91. * Index of the node in the parent element or null if the node has no parent.
  92. *
  93. * Throws error if the parent element does not contain this node.
  94. *
  95. * @method core.treeModel.Node#getIndes
  96. * @returns {Number|Null} Index of the node in the parent element or null if the node has not parent.
  97. */
  98. getIndex() {
  99. let pos;
  100. if ( !this.parent ) {
  101. return null;
  102. }
  103. if ( ( pos = this.parent.getChildIndex( this ) ) == -1 ) {
  104. /**
  105. * The node's parent does not contain this node.
  106. *
  107. * @error node-not-found-in-parent
  108. */
  109. throw new CKEditorError( 'node-not-found-in-parent: The node\'s parent does not contain this node.' );
  110. }
  111. return pos;
  112. }
  113. /**
  114. * Gets path to the node. For example if the node is the second child of the first child of the root then the path
  115. * will be `[ 1, 2 ]`. This path can be used as a parameter of {@link core.treeModel.Position}.
  116. *
  117. * @method core.treeModel.Node#getPath
  118. * @returns {Number[]} The path.
  119. */
  120. getPath() {
  121. const path = [];
  122. let node = this;
  123. while ( node.parent ) {
  124. path.unshift( node.getIndex() );
  125. node = node.parent;
  126. }
  127. return path;
  128. }
  129. /**
  130. * Custom toJSON method to solve child-parent circular dependencies.
  131. *
  132. * @method core.treeModel.Node#toJSON
  133. * @returns {Object} Clone of this object with the parent property replaced with its name.
  134. */
  135. toJSON() {
  136. const json = clone( this );
  137. // Due to circular references we need to remove parent reference.
  138. json.parent = this.parent ? this.parent.name : null;
  139. return json;
  140. }
  141. /**
  142. * Checks if the node has an attribute for given key.
  143. *
  144. * @method core.treeModel.Node#hasAttribute
  145. * @param {String} key Key of attribute to check.
  146. * @returns {Boolean} `true` if attribute with given key is set on node, `false` otherwise.
  147. */
  148. hasAttribute( key ) {
  149. return this._attrs.has( key );
  150. }
  151. /**
  152. * Gets an attribute value for given key or undefined if that attribute is not set on node.
  153. *
  154. * @method core.treeModel.Node#getAttribute
  155. * @param {String} key Key of attribute to look for.
  156. * @returns {*} Attribute value or null.
  157. */
  158. getAttribute( key ) {
  159. return this._attrs.get( key );
  160. }
  161. /**
  162. * Returns iterator that iterates over this node attributes.
  163. *
  164. * @method core.treeModel.Node#getAttributes
  165. * @returns {Iterable.<*>}
  166. */
  167. getAttributes() {
  168. return this._attrs[ Symbol.iterator ]();
  169. }
  170. }