textproxy.js 5.7 KB

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