8
0

batch.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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} document Document which this Batch changes.
  35. * @param {'transparent'|'default'} [type='default'] Type of the batch.
  36. */
  37. constructor( document, type = 'default' ) {
  38. /**
  39. * Document which this batch changes.
  40. *
  41. * @readonly
  42. * @member {engine.model.Document} engine.model.Batch#document
  43. */
  44. this.document = document;
  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. * * `'transparent'` - batch that should be ignored by other features, i.e. initial batch or collaborative editing changes.
  58. *
  59. * @readonly
  60. * @member {'transparent'|'default'} engine.model.Batch#type
  61. */
  62. this.type = type;
  63. }
  64. /**
  65. * Returns this batch base version, which is equal to the base version of first delta in the batch.
  66. * If there are no deltas in the batch, it returns `null`.
  67. *
  68. * @readonly
  69. * @type {Number|null}
  70. */
  71. get baseVersion() {
  72. return this.deltas.length > 0 ? this.deltas[ 0 ].baseVersion : null;
  73. }
  74. /**
  75. * Adds delta to the batch instance. All modification methods (insert, remove, split, etc.) use this method
  76. * to add created deltas.
  77. *
  78. * @param {engine.model.delta.Delta} delta Delta to add.
  79. * @return {engine.model.delta.Delta} Added delta.
  80. */
  81. addDelta( delta ) {
  82. delta.batch = this;
  83. this.deltas.push( delta );
  84. return delta;
  85. }
  86. /**
  87. * Gets an iterable collection of operations.
  88. *
  89. * @returns {Iterable.<engine.model.operation.Operation>}
  90. */
  91. *getOperations() {
  92. for ( let delta of this.deltas ) {
  93. yield* delta.operations;
  94. }
  95. }
  96. }
  97. /**
  98. * Function to register batch methods. To make code scalable Batch do not have modification
  99. * methods built in. They can be registered using this method.
  100. *
  101. * This method checks if there is no naming collision and throws `batch-register-taken` if the method name
  102. * is already taken.
  103. *
  104. * Besides that no magic happens here, the method is added to the `Batch` class prototype.
  105. *
  106. * For example:
  107. *
  108. * Batch.register( 'insert', function( position, nodes ) {
  109. * // You can use a class inherit from Delta if that class should handle OT in the special way.
  110. * const delta = new Delta();
  111. *
  112. * // Add delta to the Batch instance. It is important to add delta to batch before applying any operation.
  113. * this.addDelta( delta );
  114. *
  115. * // Create operations which should be components of this delta.
  116. * const operation = new InsertOperation( position, nodes, this.document.version );
  117. *
  118. * // Add operation to the delta. It is important to add operation before applying it.
  119. * delta.addOperation( operation );
  120. *
  121. * // Remember to apply every operation, no magic, you need to do it manually.
  122. * this.document.applyOperation( operation );
  123. *
  124. * // Make this method chainable.
  125. * return this;
  126. * } );
  127. *
  128. * @method engine.model.Batch.register
  129. * @param {String} name Method name.
  130. * @param {Function} creator Method body.
  131. */
  132. export function register( name, creator ) {
  133. if ( Batch.prototype[ name ] ) {
  134. /**
  135. * This batch method name is already taken.
  136. *
  137. * @error batch-register-taken
  138. * @param {String} name
  139. */
  140. throw new CKEditorError(
  141. 'batch-register-taken: This batch method name is already taken.',
  142. { name: name } );
  143. }
  144. Batch.prototype[ name ] = creator;
  145. }