textproxy.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * `TextProxy` represents a part of {@link engine.model.Text text node}.
  7. *
  8. * Since {@link engine.model.Position positions} can be placed between characters of a text node,
  9. * {@link engine.model.Range ranges} may contain only parts of text nodes. When {@link engine.model.Range#getItems getting items}
  10. * contained in such range, we need to represent a part of that text node, since returning the whole text node would be incorrect.
  11. * `TextProxy` solves this issue.
  12. *
  13. * `TextProxy` has an API similar to {@link engine.model.Text Text} and allows to do most of the common tasks performed
  14. * on model nodes.
  15. *
  16. * **Note:** Some `TextProxy` instances may represent whole text node, not just a part of it.
  17. *
  18. * **Note:** `TextProxy` is not an instance of {@link engine.model.Node node}. Keep this in mind when using it as a
  19. * parameter of methods.
  20. *
  21. * **Note:** `TextProxy` is readonly interface. If you want to perform changes on model data represented by a `TextProxy`
  22. * use {@link engine.model.writer model writer API}.
  23. *
  24. * **Note:** `TextProxy` instances are created on the fly, basing on the current state of model. Because of this, it is
  25. * highly unrecommended to store references to `TextProxy` instances. `TextProxy` instances are not refreshed when
  26. * model changes, so they might get invalidated. Instead, consider creating {@link engine.model.LivePosition live position}.
  27. *
  28. * `TextProxy` instances are created by {@link engine.model.TreeWalker model tree walker}. You should not need to create
  29. * an instance of this class by your own.
  30. *
  31. * @memberOf engine.model
  32. */
  33. export default class TextProxy {
  34. /**
  35. * Creates a text proxy.
  36. *
  37. * @protected
  38. * @param {engine.model.Text} textNode Text node which part is represented by this text proxy.
  39. * @param {Number} offsetInText Offset in {@link engine.model.TextProxy#textNode text node} from which the text proxy starts.
  40. * @param {Number} length Text proxy length, that is how many text node's characters, starting from `offsetInText` it represents.
  41. * @constructor
  42. */
  43. constructor( textNode, offsetInText, length ) {
  44. /**
  45. * Text node which part is represented by this text proxy.
  46. *
  47. * @readonly
  48. * @member {engine.model.Text} engine.model.TextProxy#textNode
  49. */
  50. this.textNode = textNode;
  51. /**
  52. * Text data represented by this text proxy.
  53. *
  54. * @readonly
  55. * @member {String} engine.model.TextProxy#data
  56. */
  57. this.data = textNode.data.substring( offsetInText, offsetInText + length );
  58. /**
  59. * Offset in {@link engine.model.TextProxy#textNode text node} from which the text proxy starts.
  60. *
  61. * @readonly
  62. * @member {Number} engine.model.TextProxy#offsetInText
  63. */
  64. this.offsetInText = offsetInText;
  65. }
  66. /**
  67. * Parent of this text proxy, which is same as parent of text node represented by this text proxy.
  68. *
  69. * @readonly
  70. * @type {engine.model.Element|engine.model.DocumentFragment|null}
  71. */
  72. get parent() {
  73. return this.textNode.parent;
  74. }
  75. /**
  76. * Root of this text proxy, which is same as root of text node represented by this text proxy.
  77. *
  78. * @readonly
  79. * @type {engine.model.Element|engine.model.DocumentFragment}
  80. */
  81. get root() {
  82. return this.textNode.root;
  83. }
  84. /**
  85. * {@link engine.model.Document Document} that owns text node represented by this text proxy or `null` if the text node
  86. * has no parent or is inside a {@link engine.model.DocumentFragment DocumentFragment}.
  87. *
  88. * @returns {engine.model.Document|null}
  89. */
  90. get document() {
  91. return this.textNode.document;
  92. }
  93. /**
  94. * Offset at which this text proxy starts in it's parent.
  95. *
  96. * @see engine.model.Node#startOffset
  97. * @readonly
  98. * @type {Number}
  99. */
  100. get startOffset() {
  101. return this.textNode.startOffset !== null ? this.textNode.startOffset + this.offsetInText : null;
  102. }
  103. /**
  104. * Offset size of this text proxy. Equal to the number of characters represented by the text proxy.
  105. *
  106. * @see engine.model.Node#offsetSize
  107. * @readonly
  108. * @type {Number}
  109. */
  110. get offsetSize() {
  111. return this.data.length;
  112. }
  113. /**
  114. * Offset at which this text proxy ends in it's parent.
  115. *
  116. * @see engine.model.Node#endOffset
  117. * @readonly
  118. * @type {Number}
  119. */
  120. get endOffset() {
  121. return this.startOffset !== null ? this.startOffset + this.offsetSize : null;
  122. }
  123. /**
  124. * Gets path to this text proxy.
  125. *
  126. * @see engine.model.Node#getPath
  127. * @readonly
  128. * @type {Array.<Number>}
  129. */
  130. getPath() {
  131. const path = this.textNode.getPath();
  132. if ( path.length > 0 ) {
  133. path[ path.length - 1 ] += this.offsetInText;
  134. }
  135. return path;
  136. }
  137. /**
  138. * Checks if this text proxy has an attribute for given key.
  139. *
  140. * @param {String} key Key of attribute to check.
  141. * @returns {Boolean} `true` if attribute with given key is set on text proxy, `false` otherwise.
  142. */
  143. hasAttribute( key ) {
  144. return this.textNode.hasAttribute( key );
  145. }
  146. /**
  147. * Gets an attribute value for given key or `undefined` if that attribute is not set on text proxy.
  148. *
  149. * @param {String} key Key of attribute to look for.
  150. * @returns {*} Attribute value or `undefined`.
  151. */
  152. getAttribute( key ) {
  153. return this.textNode.getAttribute( key );
  154. }
  155. /**
  156. * Returns iterator that iterates over this node's attributes. Attributes are returned as arrays containing two
  157. * items. First one is attribute key and second is attribute value.
  158. *
  159. * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
  160. *
  161. * @returns {Iterable.<*>}
  162. */
  163. getAttributes() {
  164. return this.textNode.getAttributes();
  165. }
  166. /**
  167. * Returns iterator that iterates over this node's attribute keys.
  168. *
  169. * @returns {Iterator.<String>}
  170. */
  171. getAttributeKeys() {
  172. return this.textNode.getAttributeKeys();
  173. }
  174. }