textproxy.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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 CharacterProxy from './characterproxy.js';
  7. import utils from '../../utils/utils.js';
  8. /**
  9. * TextProxy is an aggregator for multiple CharacterProxy instances that are placed next to each other in
  10. * tree model, in the same parent, and all have same attributes set. Instances of this class are created and returned
  11. * in various algorithms that "merge characters" (see {@link engine.treeModel.TreeWalker}, {@link engine.treeModel.Range}).
  12. *
  13. * **Note:** TextProxy instances are created on the fly basing on the current state of tree model and attributes
  14. * set on characters. Because of this it is highly unrecommended to store references to TextProxy instances
  15. * because they might get invalidated due to operations on Document. This is especially true when you change
  16. * attributes of TextProxy.
  17. *
  18. * Difference between {@link engine.treeModel.TextProxy} and {@link engine.treeModel.Text} is that the former is a set of
  19. * nodes taken from tree model, while {@link engine.treeModel.Text} is simply a string with attributes set.
  20. *
  21. * You should never create an instance of this class by your own. Instead, use string literals or {@link engine.treeModel.Text}.
  22. *
  23. * @memberOf engine.treeModel
  24. */
  25. export default class TextProxy {
  26. /**
  27. * Creates a text fragment.
  28. *
  29. * @protected
  30. * @param {engine.treeModel.CharacterProxy} firstCharacter First character node contained in {@link engine.treeModel.TextProxy}.
  31. * @param {Number} length Whole text contained in {@link engine.treeModel.TextProxy}.
  32. * @constructor
  33. */
  34. constructor( firstCharacter, length ) {
  35. /**
  36. * First character node contained in {@link engine.treeModel.TextProxy}.
  37. *
  38. * @readonly
  39. * @member {engine.treeModel.CharacterProxy} engine.treeModel.TextProxy#first
  40. */
  41. this.first = firstCharacter;
  42. /**
  43. * Characters contained in {@link engine.treeModel.TextProxy}.
  44. *
  45. * @readonly
  46. * @member {String} engine.treeModel.TextProxy#text
  47. */
  48. this.text = firstCharacter._nodeListText.text.substr( this.first._index, length );
  49. /**
  50. * Last {@link engine.treeModel.CharacterProxy character node} contained in {@link engine.treeModel.TextProxy}.
  51. *
  52. * @readonly
  53. * @member {engine.treeModel.CharacterProxy} engine.treeModel.TextProxy#last
  54. */
  55. this.last = this.getCharAt( this.text.length - 1 );
  56. }
  57. /**
  58. * A common parent of all character nodes contained in {@link engine.treeModel.TextProxy}.
  59. *
  60. * @type {engine.treeModel.Element}
  61. */
  62. get commonParent() {
  63. return this.first.parent;
  64. }
  65. /**
  66. * Gets a character at given index and creates a {@link engine.treeModel.CharacterProxy} out of it.
  67. *
  68. * @param {Number} index Character index.
  69. * @returns {engine.treeModel.CharacterProxy}
  70. */
  71. getCharAt( index ) {
  72. if ( index < 0 || index >= this.text.length ) {
  73. return null;
  74. }
  75. return new CharacterProxy( this.first._nodeListText, this.first._index + index );
  76. }
  77. /**
  78. * Checks if the text fragment has an attribute for given key.
  79. *
  80. * @param {String} key Key of attribute to check.
  81. * @returns {Boolean} `true` if attribute with given key is set on text fragment, `false` otherwise.
  82. */
  83. hasAttribute( key ) {
  84. return this.first.hasAttribute( key );
  85. }
  86. /**
  87. * Gets an attribute value for given key or undefined it that attribute is not set on text fragment.
  88. *
  89. * @param {String} key Key of attribute to look for.
  90. * @returns {*} Attribute value or null.
  91. */
  92. getAttribute( key ) {
  93. return this.first.getAttribute( key );
  94. }
  95. /**
  96. * Returns iterator that iterates over this text fragment attributes.
  97. *
  98. * @returns {Iterable.<*>}
  99. */
  100. getAttributes() {
  101. return this.first.getAttributes();
  102. }
  103. /**
  104. * Sets attribute on the text fragment. If attribute with the same key already is set, it overwrites its values.
  105. *
  106. * **Note:** Changing attributes of text fragment affects document state. This TextProxy instance properties
  107. * will be refreshed, but other may get invalidated. It is highly unrecommended to store references to TextProxy instances.
  108. *
  109. * @param {String} key Key of attribute to set.
  110. * @param {*} value Attribute value.
  111. */
  112. setAttribute( key, value ) {
  113. let index = this.first.getIndex();
  114. this.commonParent._children.setAttribute( this.first.getIndex(), this.text.length, key, value );
  115. this.first = this.commonParent.getChild( index );
  116. this.last = this.getCharAt( this.text.length - 1 );
  117. }
  118. /**
  119. * Removes all attributes from the text fragment and sets given attributes.
  120. *
  121. * **Note:** Changing attributes of text fragment affects document state. This TextProxy instance properties
  122. * will be refreshed, but other may get invalidated. It is highly unrecommended to store references to TextProxy instances.
  123. *
  124. * @param {Iterable|Object} attrs Iterable object containing attributes to be set.
  125. * See {@link engine.treeModel.TextProxy#getAttributes}.
  126. */
  127. setAttributesTo( attrs ) {
  128. let attrsMap = utils.toMap( attrs );
  129. this.clearAttributes();
  130. for ( let attr of attrsMap ) {
  131. this.setAttribute( attr[ 0 ], attr[ 1 ] );
  132. }
  133. }
  134. /**
  135. * Removes an attribute with given key from the text fragment.
  136. *
  137. * **Note:** Changing attributes of text fragment affects document state. This TextProxy instance properties
  138. * will be refreshed, but other may get invalidated. It is highly unrecommended to store references to TextProxy instances.
  139. *
  140. * @param {String} key Key of attribute to remove.
  141. */
  142. removeAttribute( key ) {
  143. this.setAttribute( key, null );
  144. }
  145. /**
  146. * Removes all attributes from the text fragment.
  147. *
  148. * **Note:** Changing attributes of text fragment affects document state. This TextProxy instance properties
  149. * will be refreshed, but other may get invalidated. It is highly unrecommended to store references to TextProxy instances.
  150. */
  151. clearAttributes() {
  152. for ( let attr of this.getAttributes() ) {
  153. this.removeAttribute( attr[ 0 ] );
  154. }
  155. }
  156. }