node.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module engine/view/node
  7. */
  8. import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  9. import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
  10. import mix from '@ckeditor/ckeditor5-utils/src/mix';
  11. /**
  12. * Abstract tree view node class.
  13. *
  14. * @abstract
  15. */
  16. export default class Node {
  17. /**
  18. * Creates a tree view node.
  19. *
  20. * This is an abstract class, so this constructor should not be used directly.
  21. */
  22. constructor() {
  23. /**
  24. * Parent element. Null by default. Set by {@link module:engine/view/element~Element#insertChildren}.
  25. *
  26. * @readonly
  27. * @member {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment|null}
  28. */
  29. this.parent = null;
  30. }
  31. /**
  32. * Index of the node in the parent element or null if the node has no parent.
  33. *
  34. * Accessing this property throws an error if this node's parent element does not contain it.
  35. * This means that view tree got broken.
  36. *
  37. * @readonly
  38. * @type {Number|null}
  39. */
  40. get index() {
  41. let pos;
  42. if ( !this.parent ) {
  43. return null;
  44. }
  45. // No parent or child doesn't exist in parent's children.
  46. if ( ( pos = this.parent.getChildIndex( this ) ) == -1 ) {
  47. /**
  48. * The node's parent does not contain this node. It means that the document tree is corrupted.
  49. *
  50. * @error view-node-not-found-in-parent
  51. */
  52. throw new CKEditorError( 'view-node-not-found-in-parent: The node\'s parent does not contain this node.' );
  53. }
  54. return pos;
  55. }
  56. /**
  57. * Node's next sibling, or `null` if it is the last child.
  58. *
  59. * @readonly
  60. * @type {module:engine/view/node~Node|null}
  61. */
  62. get nextSibling() {
  63. const index = this.index;
  64. return ( index !== null && this.parent.getChild( index + 1 ) ) || null;
  65. }
  66. /**
  67. * Node's previous sibling, or `null` if it is the first child.
  68. *
  69. * @readonly
  70. * @type {module:engine/view/node~Node|null}
  71. */
  72. get previousSibling() {
  73. const index = this.index;
  74. return ( index !== null && this.parent.getChild( index - 1 ) ) || null;
  75. }
  76. /**
  77. * Top-most ancestor of the node. If the node has no parent it is the root itself.
  78. *
  79. * @readonly
  80. * @type {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
  81. */
  82. get root() {
  83. let root = this;
  84. while ( root.parent ) {
  85. root = root.parent;
  86. }
  87. return root;
  88. }
  89. /**
  90. * {@link module:engine/view/document~Document View document} that owns this node, or `null` if the node is inside
  91. * {@link module:engine/view/documentfragment~DocumentFragment document fragment}.
  92. *
  93. * @readonly
  94. * @type {module:engine/view/document~Document|null}
  95. */
  96. get document() {
  97. // Parent might be Node, null or DocumentFragment.
  98. if ( this.parent instanceof Node ) {
  99. return this.parent.document;
  100. } else {
  101. return null;
  102. }
  103. }
  104. /**
  105. * Returns ancestors array of this node.
  106. *
  107. * @param {Object} options Options object.
  108. * @param {Boolean} [options.includeNode=false] When set to `true` this node will be also included in parent's array.
  109. * @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from node's parent to root element,
  110. * otherwise root element will be the first item in the array.
  111. * @returns {Array} Array with ancestors.
  112. */
  113. getAncestors( options = { includeNode: false, parentFirst: false } ) {
  114. const ancestors = [];
  115. let parent = options.includeNode ? this : this.parent;
  116. while ( parent ) {
  117. ancestors[ options.parentFirst ? 'push' : 'unshift' ]( parent );
  118. parent = parent.parent;
  119. }
  120. return ancestors;
  121. }
  122. /**
  123. * Removes node from parent.
  124. */
  125. remove() {
  126. this.parent.removeChildren( this.index );
  127. }
  128. /**
  129. * @param {module:engine/view/document~ChangeType} type Type of the change.
  130. * @param {module:engine/view/node~Node} node Changed node.
  131. * @fires change
  132. */
  133. _fireChange( type, node ) {
  134. this.fire( 'change:' + type, node );
  135. if ( this.parent ) {
  136. this.parent._fireChange( type, node );
  137. }
  138. }
  139. /**
  140. * Clones this node.
  141. *
  142. * @method #clone
  143. * @returns {module:engine/view/node~Node} Clone of this node.
  144. */
  145. /**
  146. * Checks if provided node is similar to this node.
  147. *
  148. * @method #isSimilar
  149. * @returns {Boolean} True if nodes are similar.
  150. */
  151. }
  152. /**
  153. * Fired when list of {@link module:engine/view/element~Element elements} children changes.
  154. *
  155. * Change event is bubbled – it is fired on all ancestors.
  156. *
  157. * @event change:children
  158. * @param {module:engine/view/node~Node} Changed node.
  159. */
  160. /**
  161. * Fired when list of {@link module:engine/view/element~Element elements} attributes changes.
  162. *
  163. * Change event is bubbled – it is fired on all ancestors.
  164. *
  165. * @event change:attributes
  166. * @param {module:engine/view/node~Node} Changed node.
  167. */
  168. /**
  169. * Fired when {@link module:engine/view/text~Text text nodes} data changes.
  170. *
  171. * Change event is bubbled – it is fired on all ancestors.
  172. *
  173. * @event change:text
  174. * @param {module:engine/view/node~Node} Changed node.
  175. */
  176. /**
  177. * @event change
  178. */
  179. mix( Node, EmitterMixin );