delta.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 clone from '../../../utils/lib/lodash/clone.js';
  7. import CKEditorError from '../../../utils/ckeditorerror.js';
  8. import AttributeOperation from '../operation/attributeoperation.js';
  9. import InsertOperation from '../operation/insertoperation.js';
  10. import MoveOperation from '../operation/moveoperation.js';
  11. import NoOperation from '../operation/nooperation.js';
  12. import Operation from '../operation/operation.js';
  13. import ReinsertOperation from '../operation/reinsertoperation.js';
  14. import RemoveOperation from '../operation/removeoperation.js';
  15. import RootAttributeOperation from '../operation/rootattributeoperation.js';
  16. /**
  17. * Base class for all deltas.
  18. *
  19. * Delta is a single, from the user action point of view, change in the editable document, like insert, split or
  20. * rename element. Delta is composed of operations, which are unit changes needed to be done to execute user action.
  21. *
  22. * Multiple deltas are grouped into a single {@link engine.treeModel.Batch}.
  23. *
  24. * @memberOf engine.treeModel.delta
  25. */
  26. export default class Delta {
  27. /**
  28. * Creates a delta instance.
  29. */
  30. constructor() {
  31. /**
  32. * {@link engine.treeModel.Batch} which delta is a part of. This property is null by default and set by the
  33. * {@link engine.treeModel.Batch#addDelta} method.
  34. *
  35. * @readonly
  36. * @member {engine.treeModel.Batch} engine.treeModel.delta.Delta#batch
  37. */
  38. this.batch = null;
  39. /**
  40. * Array of operations which compose delta.
  41. *
  42. * @readonly
  43. * @member {engine.treeModel.operation.Operation[]} engine.treeModel.delta.Delta#operations
  44. */
  45. this.operations = [];
  46. }
  47. /**
  48. * Returns delta base version which is equal to the base version of the first operation in delta. If there
  49. * are no operations in delta, returns `null`.
  50. *
  51. * @see engine.treeModel.Document
  52. * @type {Number|null}
  53. */
  54. get baseVersion() {
  55. if ( this.operations.length > 0 ) {
  56. return this.operations[ 0 ].baseVersion;
  57. }
  58. return null;
  59. }
  60. /**
  61. * A class that will be used when creating reversed delta.
  62. *
  63. * @private
  64. * @type {Function}
  65. */
  66. get _reverseDeltaClass() {
  67. return Delta;
  68. }
  69. /**
  70. * Add operation to the delta.
  71. *
  72. * @param {engine.treeModel.operation.Operation} operation Operation instance.
  73. */
  74. addOperation( operation ) {
  75. operation.delta = this;
  76. this.operations.push( operation );
  77. return operation;
  78. }
  79. /**
  80. * Creates and returns a delta that has the same parameters as this delta.
  81. *
  82. * @returns {engine.treeModel.delta.Delta} Clone of this delta.
  83. */
  84. clone() {
  85. let delta = new this.constructor();
  86. for ( let op of this.operations ) {
  87. delta.addOperation( op.clone() );
  88. }
  89. return delta;
  90. }
  91. /**
  92. * Creates and returns a reverse delta. Reverse delta when executed right after the original delta will bring back
  93. * tree model state to the point before the original delta execution. In other words, it reverses changes done
  94. * by the original delta.
  95. *
  96. * Keep in mind that tree model state may change since executing the original delta, so reverse delta may be "outdated".
  97. * In that case you will need to {@link engine.treeModel.delta.transform} it by all deltas that were executed after
  98. * the original delta.
  99. *
  100. * @returns {engine.treeModel.delta.Delta} Reversed delta.
  101. */
  102. getReversed() {
  103. let delta = new this._reverseDeltaClass();
  104. for ( let op of this.operations ) {
  105. delta.addOperation( op.getReversed() );
  106. }
  107. delta.operations.reverse();
  108. for ( let i = 0; i < delta.operations.length; i++ ) {
  109. delta.operations[ i ].baseVersion = this.operations[ this.operations.length - 1 ].baseVersion + i + 1;
  110. }
  111. return delta;
  112. }
  113. /**
  114. * Custom toJSON method to make deltas serializable.
  115. *
  116. * @returns {Object} Clone of this delta with added class name.
  117. */
  118. toJSON() {
  119. let json = clone( this );
  120. json.__className = this.constructor.className;
  121. // Remove parent batch to avoid circular dependencies.
  122. delete json.batch;
  123. return json;
  124. }
  125. /**
  126. * Delta class name. Used by {@link engine.treeModel.delta.Delta#toJSON} method for serialization and
  127. * {@link engine.treeModel.delta.Delta.fromJSON} during deserialization.
  128. *
  129. * @type {String}
  130. * @readonly
  131. */
  132. static get className() {
  133. return 'engine.treeModel.delta.Delta';
  134. }
  135. /**
  136. * Delta priority. Used in {@link engine.treeModel.delta.transform delta transformations}. Delta with the higher
  137. * priority will be treated as more important when resolving transformation conflicts. If deltas have same
  138. * priority, other factors will be used to determine which delta is more important.
  139. *
  140. * @private
  141. * @type {Number}
  142. */
  143. static get _priority() {
  144. return 0;
  145. }
  146. /**
  147. * Creates InsertDelta from deserialized object, i.e. from parsed JSON string.
  148. *
  149. * @param {Object} json
  150. * @param {engine.treeModel.Document} doc Document on which this delta will be applied.
  151. * @returns {engine.treeModel.delta.InsertDelta}
  152. */
  153. static fromJSON( json, doc ) {
  154. if ( !deserializers.has( json.__className ) ) {
  155. /**
  156. * This delta has no defined deserializer.
  157. *
  158. * @error delta-fromjson-no-deserializer
  159. * @param {String} name
  160. */
  161. throw new CKEditorError(
  162. 'delta-fromjson-no-deserializer: This delta has no defined deserializer',
  163. { name: json.__className }
  164. );
  165. }
  166. let Constructor = deserializers.get( json.__className );
  167. let delta = new Constructor();
  168. if ( json.operations.length ) {
  169. json.operations.forEach( ( operation ) => delta.addOperation( operations[ operation.__className ].fromJSON( operation, doc ) ) );
  170. }
  171. return delta;
  172. }
  173. }
  174. const operations = {};
  175. operations[ AttributeOperation.className ] = AttributeOperation;
  176. operations[ InsertOperation.className ] = InsertOperation;
  177. operations[ MoveOperation.className ] = MoveOperation;
  178. operations[ NoOperation.className ] = NoOperation;
  179. operations[ Operation.className ] = Operation;
  180. operations[ ReinsertOperation.className ] = ReinsertOperation;
  181. operations[ RemoveOperation.className ] = RemoveOperation;
  182. operations[ RootAttributeOperation.className ] = RootAttributeOperation;
  183. const deserializers = new Map();
  184. export function registerDeserializer( className, constructor ) {
  185. if ( deserializers.has( className ) ) {
  186. /**
  187. * This delta name is already defined.
  188. *
  189. * @error delta-register-deserializer-defined
  190. * @param {String} name
  191. */
  192. throw new CKEditorError(
  193. 'delta-register-deserializer-defined: This delta name is already defined.',
  194. { name: className } );
  195. }
  196. deserializers.set( className, constructor );
  197. }