textfragment.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 Position from './position.js';
  7. import Range from './range.js';
  8. /**
  9. * TextFragment 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 treeModel.TreeWalker}, {@link treeModel.Range}).
  12. *
  13. * Difference between {@link treeModel.TextFragment} and {@link treeModel.Text} is that the former is bound to tree model,
  14. * while {@link treeModel.Text} is simply a string with attributes set.
  15. *
  16. * You should never create an instance of this class by your own. When passing parameters to constructors,
  17. * use string literals or {@link treeModel.Text} instead.
  18. *
  19. * @class treeModel.TextFragment
  20. */
  21. export default class TextFragment {
  22. /**
  23. * Creates a text fragment.
  24. *
  25. * @param {treeModel.Position} startPosition Position in the tree model where the {@link treeModel.TextFragment} starts.
  26. * @param {String} text Characters contained in {@link treeModel.TextFragment}.
  27. * @protected
  28. * @constructor
  29. */
  30. constructor( startPosition, text ) {
  31. /**
  32. * First {@link treeModel.CharacterProxy character node} contained in {@link treeModel.TextFragment}.
  33. *
  34. * @readonly
  35. * @property {treeModel.CharacterProxy} first
  36. */
  37. this.first = startPosition.nodeAfter;
  38. /**
  39. * Characters contained in {@link treeModel.TextFragment}.
  40. *
  41. * @readonly
  42. * @property {String} text
  43. */
  44. this.text = text;
  45. /**
  46. * Last {@link treeModel.CharacterProxy character node} contained in {@link treeModel.TextFragment}.
  47. *
  48. * @readonly
  49. * @property {treeModel.CharacterProxy} last
  50. */
  51. this.last = this.getCharAt( this.text.length - 1 );
  52. /**
  53. * List of attributes common for all characters in this {@link treeModel.TextFragment}.
  54. *
  55. * @readonly
  56. * @property {@link treeModel.AttributeList} attrs
  57. */
  58. this.attrs = this.first.attrs;
  59. }
  60. /**
  61. * Gets a character at given index and creates a {@link treeModel.CharacterProxy} out of it.
  62. *
  63. * @param {Number} index Character index.
  64. * @returns {treeModel.CharacterProxy}
  65. */
  66. getCharAt( index ) {
  67. return this.first.parent.getChild( this.first._index + index );
  68. /**
  69. * Checks if the text fragment has an attribute that is {@link treeModel.Attribute#isEqual equal} to given attribute or
  70. * attribute with given key if string was passed.
  71. *
  72. * @param {treeModel.Attribute|String} attrOrKey Attribute or key of attribute to check.
  73. * @returns {Boolean} `true` if given attribute or attribute with given key is set on text fragment, `false` otherwise.
  74. */
  75. hasAttribute( attrOrKey ) {
  76. return this.first.hasAttribute( attrOrKey );
  77. }
  78. /**
  79. * Gets a text fragment attribute by its key.
  80. *
  81. * @param {String} key Key of attribute to look for.
  82. * @returns {treeModel.Attribute|null} Attribute with given key or null if the attribute has not been set on the text fragment.
  83. */
  84. getAttribute( key ) {
  85. return this.first.getAttribute( key );
  86. }
  87. /**
  88. * Gets a text fragment attribute value by attribute key.
  89. *
  90. * @param {String} key Key of attribute to look for.
  91. * @returns {*} Value of attribute with given key or null if the attribute has not been set on the text fragment.
  92. */
  93. getAttributeValue( key ) {
  94. return this.first.getAttributeValue( key );
  95. }
  96. /**
  97. * Returns iterator that iterates over this text fragment's attributes.
  98. *
  99. * @returns {Iterable.<treeModel.Attribute>}
  100. */
  101. getAttributes() {
  102. return this.first.getAttributes();
  103. }
  104. /**
  105. * Sets attribute on the text fragment. If attribute with the same key already is set, it overwrites its values.
  106. *
  107. * To change attributes of nodes (also characters) that are attached to the tree model, you
  108. * should use {@link treeModel.AttributeDelta}. This method is used by tree model internal mechanisms.
  109. *
  110. * @protected
  111. * @param {treeModel.Attribute} attr Attribute to set or overwrite with.
  112. */
  113. setAttribute( attr ) {
  114. // Do note that this changes attributes on whole NodeListText, not only on character nodes specified by
  115. // this TextFragment. Split NodeList at proper index before using this.
  116. this.first._nodeListText._attrs.set( attr );
  117. // Refreshing first and last character proxies because they would have wrong attributes.
  118. this.first = this.getCharAt( 0 );
  119. this.last = this.getCharAt( this.text.length - 1 );
  120. }
  121. /**
  122. * Removes an attribute with given key from the text fragment.
  123. *
  124. * To change attributes of nodes (also characters) that are attached to the tree model, you
  125. * should use {@link treeModel.AttributeDelta}. This method is used by tree model internal mechanisms.
  126. *
  127. * @protected
  128. * @param {String} key Key of attribute to remove.
  129. * @returns {Boolean} `true` if the attribute was set on the element, `false` otherwise.
  130. */
  131. removeAttribute( key ) {
  132. // Do note that this changes attributes on whole NodeListText, not only on character nodes specified by
  133. // this TextFragment. Split NodeList at proper index before using this.
  134. let result = this.first._nodeListText._attrs.delete( key );
  135. // Refreshing first and last character proxies because they would have wrong attributes.
  136. this.first = this.getCharAt( 0 );
  137. this.last = this.getCharAt( this.text.length - 1 );
  138. return result;
  139. }
  140. }