history.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. // Load all basic deltas and transformations, they register themselves, but they need to be imported somewhere.
  7. import deltas from './delta/basic-deltas.js';
  8. import transformations from './delta/basic-transformations.js';
  9. /*jshint unused: false*/
  10. import transform from './delta/transform.js';
  11. import CKEditorError from '../../utils/ckeditorerror.js';
  12. /**
  13. * History keeps the track of all the deltas applied to the {@link core.treeModel.Document document} and provides
  14. * utility tools to operate on the history. Most of times history is needed to transform a delta that has wrong
  15. * {@link core.treeModel.delta.Delta#baseVersion} to a state where it can be applied to the document.
  16. *
  17. * @memberOf core.treeModel
  18. */
  19. export default class History {
  20. /**
  21. * Creates an empty History instance.
  22. */
  23. constructor() {
  24. /**
  25. * Deltas added to the history.
  26. *
  27. * @private
  28. * @member {Array.<core.treeModel.delta.Delta>} core.treeModel.History#_deltas
  29. */
  30. this._deltas = [];
  31. /**
  32. * Helper structure that maps added delta's base version to the index in {@link core.treeModel.History#_deltas}
  33. * at which the delta was added.
  34. *
  35. * @private
  36. * @member {Map} core.treeModel.History#_historyPoints
  37. */
  38. this._historyPoints = new Map();
  39. }
  40. /**
  41. * Gets the number of base version which an up-to-date operation should have.
  42. *
  43. * @private
  44. * @type {Number}
  45. */
  46. get _nextHistoryPoint() {
  47. const lastDelta = this._deltas[ this._deltas.length - 1 ];
  48. return lastDelta.baseVersion + lastDelta.operations.length;
  49. }
  50. /**
  51. * Adds an operation to the history.
  52. *
  53. * @param {core.treeModel.operation.Operation} operation Operation to add.
  54. */
  55. addOperation( operation ) {
  56. const delta = operation.delta;
  57. // History cares about deltas not singular operations.
  58. // Operations from a delta are added one by one, from first to last.
  59. // Operations from one delta cannot be mixed with operations from other deltas.
  60. // This all leads us to the conclusion that we could just save deltas history.
  61. // What is more, we need to check only the last position in history to check if delta is already in the history.
  62. if ( delta && this._deltas[ this._deltas.length - 1 ] !== delta ) {
  63. const index = this._deltas.length;
  64. this._deltas[ index ] = delta;
  65. this._historyPoints.set( delta.baseVersion, index );
  66. }
  67. }
  68. /**
  69. * Transforms out-dated delta by all deltas that were added to the history since the given delta's base version. In other
  70. * words, it makes the delta up-to-date with the history. The transformed delta(s) is (are) ready to be applied
  71. * to the {@link core.treeModel.Document document}.
  72. *
  73. * @param {core.treeModel.delta.Delta} delta Delta to update.
  74. * @returns {Array.<core.treeModel.delta.Delta>} Result of transformation which is an array containing one or more deltas.
  75. */
  76. getTransformedDelta( delta ) {
  77. if ( delta.baseVersion === this._nextHistoryPoint ) {
  78. return [ delta ];
  79. }
  80. let index = this._historyPoints.get( delta.baseVersion );
  81. if ( index === undefined ) {
  82. throw new CKEditorError( 'history-wrong-version: Cannot retrieve point in history that is a base for given delta.' );
  83. }
  84. let transformed = [ delta ];
  85. while ( index < this._deltas.length ) {
  86. const historyDelta = this._deltas[ index ];
  87. let allResults = [];
  88. for ( let deltaToTransform of transformed ) {
  89. const transformedDelta = History._transform( deltaToTransform, historyDelta );
  90. allResults = allResults.concat( transformedDelta );
  91. }
  92. transformed = allResults;
  93. index++;
  94. }
  95. return transformed;
  96. }
  97. /**
  98. * Transforms given delta by another given delta.
  99. *
  100. * @private
  101. * @param {core.treeModel.delta.Delta} toTransform Delta to be transformed.
  102. * @param {core.treeModel.delta.Delta} transformBy Delta to transform by.
  103. */
  104. static _transform( toTransform, transformBy ) {
  105. return transform( toTransform, transformBy, false );
  106. }
  107. }