batch.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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 CKEditorError from '../../utils/ckeditorerror.js';
  7. /**
  8. * The Batch class groups document changes (deltas). All deltas grouped in a single Batch can be
  9. * reverted together, so you can think about the Batch as a single undo step. If you want to extend one
  10. * undo step you can call another method on the same Batch object. If you want to create a separate undo step
  11. * you can create a new Batch.
  12. *
  13. * For example to create two separate undo steps you can call:
  14. *
  15. * doc.batch().insert( firstPosition, 'foo' );
  16. * doc.batch().insert( secondPosition, 'bar' );
  17. *
  18. * To create a single undo step:
  19. *
  20. * const batch = doc.batch();
  21. * batch.insert( firstPosition, 'foo' );
  22. * batch.insert( secondPosition, 'bar' );
  23. *
  24. * Note that all document modification methods (insert, remove, split, etc.) are chainable so you can shorten code to:
  25. *
  26. * doc.batch().insert( firstPosition, 'foo' ).insert( secondPosition, 'bar' );
  27. *
  28. * @memberOf engine.model
  29. */
  30. export default class Batch {
  31. /**
  32. * Creates Batch instance. Not recommended to use directly, use {@link engine.model.Document#batch} instead.
  33. *
  34. * @param {engine.model.Document} doc Document which this Batch changes.
  35. * @param {'ignore'|'undo'|'redo'|'default'} type Type of the batch. Defaults to `'default'`.
  36. */
  37. constructor( doc, type = 'default' ) {
  38. /**
  39. * Document which this batch changes.
  40. *
  41. * @readonly
  42. * @member {engine.model.Document} engine.model.Batch#doc
  43. */
  44. this.doc = doc;
  45. /**
  46. * Array of deltas which compose this batch.
  47. *
  48. * @readonly
  49. * @member {Array.<engine.model.delta.Delta>} engine.model.Batch#deltas
  50. */
  51. this.deltas = [];
  52. /**
  53. * Type of the batch.
  54. *
  55. * Can be one of the following values:
  56. * * `'default'` - all "normal" batches. Most commonly used type.
  57. * * `'undo'` - batch created by undo command.
  58. * * `'redo'` - batch created by redo command.
  59. * * `'ignore'` - batch that should be ignored by other features.
  60. *
  61. * @readonly
  62. * @member {'ignore'|'undo'|'redo'|'default'} engine.model.Batch#type
  63. */
  64. this.type = type;
  65. }
  66. /**
  67. * Returns this batch base version, which is equal to the base version of first delta in the batch.
  68. * If there are no deltas in the batch, it returns `null`.
  69. *
  70. * @readonly
  71. * @type {Number|null}
  72. */
  73. get baseVersion() {
  74. return this.deltas.length > 0 ? this.deltas[ 0 ].baseVersion : null;
  75. }
  76. /**
  77. * Adds delta to the batch instance. All modification methods (insert, remove, split, etc.) use this method
  78. * to add created deltas.
  79. *
  80. * @param {engine.model.delta.Delta} delta Delta to add.
  81. * @return {engine.model.delta.Delta} Added delta.
  82. */
  83. addDelta( delta ) {
  84. delta.batch = this;
  85. this.deltas.push( delta );
  86. return delta;
  87. }
  88. /**
  89. * Gets an iterable collection of operations.
  90. *
  91. * @returns {Iterable.<engine.model.operation.Operation>}
  92. */
  93. *getOperations() {
  94. for ( let delta of this.deltas ) {
  95. yield* delta.operations;
  96. }
  97. }
  98. }
  99. /**
  100. * Function to register batch methods. To make code scalable Batch do not have modification
  101. * methods built in. They can be registered using this method.
  102. *
  103. * This method checks if there is no naming collision and throws `batch-register-taken` if the method name
  104. * is already taken.
  105. *
  106. * Besides that no magic happens here, the method is added to the `Batch` class prototype.
  107. *
  108. * For example:
  109. *
  110. * Batch.register( 'insert', function( position, nodes ) {
  111. * // You can use a class inherit from Delta if that class should handle OT in the special way.
  112. * const delta = new Delta();
  113. *
  114. * // Add delta to the Batch instance. It is important to add delta to batch before applying any operation.
  115. * this.addDelta( delta );
  116. *
  117. * // Create operations which should be components of this delta.
  118. * const operation = new InsertOperation( position, nodes, this.doc.version );
  119. *
  120. * // Add operation to the delta. It is important to add operation before applying it.
  121. * delta.addOperation( operation );
  122. *
  123. * // Remember to apply every operation, no magic, you need to do it manually.
  124. * this.doc.applyOperation( operation );
  125. *
  126. * // Make this method chainable.
  127. * return this;
  128. * } );
  129. *
  130. * @method engine.model.Batch.register
  131. * @param {String} name Method name.
  132. * @param {Function} creator Method body.
  133. */
  134. export function register( name, creator ) {
  135. if ( Batch.prototype[ name ] ) {
  136. /**
  137. * This batch method name is already taken.
  138. *
  139. * @error batch-register-taken
  140. * @param {String} name
  141. */
  142. throw new CKEditorError(
  143. 'batch-register-taken: This batch method name is already taken.',
  144. { name: name } );
  145. }
  146. Batch.prototype[ name ] = creator;
  147. }