8
0

positioniterator.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. CKEDITOR.define( [
  7. 'treemodel/character',
  8. 'treemodel/element',
  9. 'treemodel/position'
  10. ], ( Character, Element, Position ) => {
  11. const ELEMENT_ENTER = 0;
  12. const ELEMENT_LEAVE = 1;
  13. const CHARACTER = 2;
  14. /**
  15. * Position iterator class. It allows to iterate forward and backward over the tree document.
  16. *
  17. * @class treeModel.PositionIterator
  18. */
  19. class PositionIterator {
  20. /**
  21. * Creates a range iterator.
  22. *
  23. * @param {treeModel.Range} [boundaries] Range to define boundaries of the iterator.
  24. * @param {treeModel.Position} [iteratorPosition] Starting position.
  25. * @constructor
  26. */
  27. constructor( boundaries, iteratorPosition ) {
  28. if ( boundaries instanceof Position ) {
  29. this.position = boundaries;
  30. } else {
  31. this.boundaries = boundaries;
  32. this.position = iteratorPosition || boundaries.start;
  33. }
  34. /**
  35. * Iterator position.
  36. *
  37. * @property {treeModel.Position} position
  38. */
  39. /**
  40. * Iterator boundaries.
  41. *
  42. * When the {@link #next} method is called on the end boundary or the {@link #previous} method
  43. * on the start boundary, then `{ done: true }` is returned.
  44. *
  45. * If boundaries are not defined they are set before first and after last child of the root node.
  46. *
  47. * @property {treeModel.Range} boundaries
  48. */
  49. }
  50. /**
  51. * Moves the {@link #position} to the next position and returns the enctountered value.
  52. *
  53. * @returns {Object} Value between the previous and the new {@link #position}.
  54. * @returns {Boolean} return.done True if iterator is done.
  55. * @returns {Object} return.value
  56. * @returns {Number} return.value.type Encountered value type, possible options: {@link PositionIterator#ELEMENT_ENTER},
  57. * {@link PositionIterator#ELEMENT_LEAVE} or {@link PositionIterator#CHARACTER}.
  58. * @returns {treeModel.Node} return.value.node Encountered node.
  59. */
  60. next() {
  61. const position = this.position;
  62. const parent = position.parent;
  63. // Ugh... added here because of circular deps in AMD ;<.
  64. Element = CKEDITOR.require( 'treemodel/element' );
  65. // We are at the end of the root.
  66. if ( parent.parent === null && position.offset === parent.getChildCount() ) {
  67. return { done: true };
  68. }
  69. if ( this.boundaries && position.isEqual( this.boundaries.end ) ) {
  70. return { done: true };
  71. }
  72. const nodeAfter = position.nodeAfter;
  73. if ( nodeAfter instanceof Element ) {
  74. this.position = Position.createFromParentAndOffset( nodeAfter, 0 );
  75. return formatReturnValue( ELEMENT_ENTER, nodeAfter );
  76. } else if ( nodeAfter instanceof Character ) {
  77. this.position = Position.createFromParentAndOffset( parent, position.offset + 1 );
  78. return formatReturnValue( CHARACTER, nodeAfter );
  79. } else {
  80. this.position = Position.createFromParentAndOffset( parent.parent, parent.getIndex() + 1 );
  81. return formatReturnValue( ELEMENT_LEAVE, this.position.nodeBefore );
  82. }
  83. }
  84. /**
  85. * Moves the {@link #position} to the previous position and returns the encountered value.
  86. *
  87. * @returns {Object} Value between the previous and the new {@link #position}.
  88. * @returns {Boolean} return.done True if iterator is done.
  89. * @returns {Object} return.value
  90. * @returns {Number} return.value.type Encountered value type, possible options: {@link PositionIterator#ELEMENT_ENTER},
  91. * {@link PositionIterator#ELEMENT_LEAVE} or {@link PositionIterator#CHARACTER}.
  92. * @returns {treeModel.Node} return.value.node Scanned node.
  93. */
  94. previous() {
  95. const position = this.position;
  96. const parent = position.parent;
  97. // We are at the beginning of the root.
  98. if ( parent.parent === null && position.offset === 0 ) {
  99. return { done: true };
  100. }
  101. if ( this.boundaries && position.isEqual( this.boundaries.start ) ) {
  102. return { done: true };
  103. }
  104. const nodeBefore = position.nodeBefore;
  105. if ( nodeBefore instanceof Element ) {
  106. this.position = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
  107. return formatReturnValue( ELEMENT_LEAVE, nodeBefore );
  108. } else if ( nodeBefore instanceof Character ) {
  109. this.position = Position.createFromParentAndOffset( parent, position.offset - 1 );
  110. return formatReturnValue( CHARACTER, nodeBefore );
  111. } else {
  112. this.position = Position.createFromParentAndOffset( parent.parent, parent.getIndex() );
  113. return formatReturnValue( ELEMENT_ENTER, this.position.nodeAfter );
  114. }
  115. }
  116. }
  117. function formatReturnValue( type, node ) {
  118. return {
  119. done: false,
  120. value: {
  121. type: type,
  122. node: node
  123. }
  124. };
  125. }
  126. /**
  127. * Flag for character.
  128. *
  129. * @static
  130. * @readonly
  131. * @property {Number}
  132. */
  133. PositionIterator.CHARACTER = CHARACTER;
  134. /**
  135. * Flag for entering element.
  136. *
  137. * @static
  138. * @readonly
  139. * @property {Number}
  140. */
  141. PositionIterator.ELEMENT_ENTER = ELEMENT_ENTER;
  142. /**
  143. * Flag for leaving element.
  144. *
  145. * @static
  146. * @readonly
  147. * @property {Number}
  148. */
  149. PositionIterator.ELEMENT_LEAVE = ELEMENT_LEAVE;
  150. return PositionIterator;
  151. } );