changeoperation.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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. 'document/operation/operation',
  8. 'ckeditorerror'
  9. ], ( Operation, CKEditorError ) => {
  10. /**
  11. * Operation to change nodes' attribute. Using this class you can add, remove or change value of the attribute.
  12. *
  13. * @class document.operation.ChangeOperation
  14. */
  15. class ChangeOperation extends Operation {
  16. /**
  17. * Creates a change operation.
  18. *
  19. * If only the new attribute is set, then it will be inserted. Note that in all nodes in ranges there must be
  20. * no attributes with the same key as the new attribute.
  21. *
  22. * If only the old attribute is set, then it will be removed. Note that this attribute must be present in all nodes in
  23. * ranges.
  24. *
  25. * If both new and old attributes are set, then the operation will change the attribute value. Note that both new and
  26. * old attributes have to have the same key and the old attribute must be present in all nodes in ranges.
  27. *
  28. * @param {document.Range} range Range on which the operation should be applied.
  29. * @param {document.Attribute|null} oldAttr Attribute to be removed. If `null`, then the operation just inserts a new attribute.
  30. * @param {document.Attribute|null} newAttr Attribute to be added. If `null`, then the operation just removes the attribute.
  31. * @param {Number} baseVersion {@link document.Document#version} on which the operation can be applied.
  32. * @constructor
  33. */
  34. constructor( range, oldAttr, newAttr, baseVersion ) {
  35. super( baseVersion );
  36. /**
  37. * Range on which operation should be applied.
  38. *
  39. * @readonly
  40. * @type {document.Range}
  41. */
  42. this.range = range;
  43. /**
  44. * Old attribute to change. Set to `null` if operation inserts a new attribute.
  45. *
  46. * @readonly
  47. * @type {document.Attribute|null}
  48. */
  49. this.oldAttr = oldAttr;
  50. /**
  51. * New attribute. Set to `null` if operation removes the attribute.
  52. *
  53. * @readonly
  54. * @type {document.Attribute|null}
  55. */
  56. this.newAttr = newAttr;
  57. }
  58. _execute() {
  59. const oldAttr = this.oldAttr;
  60. const newAttr = this.newAttr;
  61. let value;
  62. if ( oldAttr !== null && newAttr !== null && oldAttr.key != newAttr.key ) {
  63. /**
  64. * Old and new attributes should have the same keys.
  65. *
  66. * @error operation-change-different-keys
  67. * @param {document.Attribute} oldAttr
  68. * @param {document.Attribute} newAttr
  69. */
  70. throw new CKEditorError(
  71. 'operation-change-different-keys: Old and new attributes should have the same keys.',
  72. { oldAttr: oldAttr, newAttr: newAttr } );
  73. }
  74. // Remove or change.
  75. if ( oldAttr !== null ) {
  76. for ( value of this.range ) {
  77. if ( !value.node.hasAttr( oldAttr ) ) {
  78. /**
  79. * The attribute which should be removed does not exists for the given node.
  80. *
  81. * @error operation-change-no-attr-to-remove
  82. * @param {document.Node} node
  83. * @param {document.Attribute} attr
  84. */
  85. throw new CKEditorError(
  86. 'operation-change-no-attr-to-remove: The attribute which should be removed does not exists for given node.',
  87. { node: value.node, attr: oldAttr } );
  88. }
  89. // There is no use in removing attribute if we will overwrite it later.
  90. // Still it is profitable to run through the loop to check if all nodes in the range has old attribute.
  91. if ( newAttr === null ) {
  92. value.node.removeAttr( oldAttr.key );
  93. }
  94. }
  95. }
  96. // Insert or change.
  97. if ( newAttr !== null ) {
  98. for ( value of this.range ) {
  99. if ( oldAttr === null && value.node.hasAttr( newAttr.key ) ) {
  100. /**
  101. * The attribute with given key already exists for the given node.
  102. *
  103. * @error operation-change-attr-exists
  104. * @param {document.Node} node
  105. * @param {document.Attribute} attr
  106. */
  107. throw new CKEditorError(
  108. 'operation-change-attr-exists: The attribute with given key already exists.',
  109. { node: value.node, attr: newAttr } );
  110. }
  111. value.node.setAttr( newAttr );
  112. }
  113. }
  114. }
  115. getReversed() {
  116. return new ChangeOperation( this.range, this.newAttr, this.oldAttr, this.baseVersion + 1 );
  117. }
  118. clone() {
  119. return new ChangeOperation( this.range.clone(), this.oldAttr, this.newAttr, this.baseVersion );
  120. }
  121. }
  122. return ChangeOperation;
  123. } );