markerdelta.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module engine/model/delta/markerdelta
  7. */
  8. import Delta from './delta';
  9. import DeltaFactory from './deltafactory';
  10. import { register } from '../batch';
  11. import MarkerOperation from '../operation/markeroperation';
  12. import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  13. /**
  14. * @classdesc
  15. * To provide specific OT behavior and better collisions solving, the {@link module:engine/model/batch~Batch#setMarker Batch#setMarker}
  16. * and {@link module:engine/model/batch~Batch#removeMarker Batch#removeMarker} methods use the `MarkerDelta` class which inherits
  17. * from the `Delta` class and may overwrite some methods.
  18. */
  19. export default class MarkerDelta extends Delta {
  20. /**
  21. * @inheritDoc
  22. */
  23. get type() {
  24. return 'marker';
  25. }
  26. /**
  27. * A class that will be used when creating reversed delta.
  28. *
  29. * @private
  30. * @type {Function}
  31. */
  32. get _reverseDeltaClass() {
  33. return MarkerDelta;
  34. }
  35. /**
  36. * @inheritDoc
  37. */
  38. static get className() {
  39. return 'engine.model.delta.MarkerDelta';
  40. }
  41. }
  42. /**
  43. * Adds or updates {@link module:engine/model/markercollection~Marker marker} with given name to given `range`.
  44. *
  45. * If passed name is a name of already existing marker (or {@link module:engine/model/markercollection~Marker Marker} instance
  46. * is passed), `range` parameter may be omitted. In this case marker will not be updated in
  47. * {@link module:engine/model/document~Document#markers document marker collection}. However the marker will be added to
  48. * the document history. This may be important for other features, like undo. From document history point of view, it will
  49. * look like the marker was created and added to the document at the moment when it is set using this method.
  50. *
  51. * This is useful if the marker is created before it can be added to document history (e.g. a feature creating the marker
  52. * is waiting for additional data, etc.). In this case, the marker may be first created directly through
  53. * {@link module:engine/model/markercollection~MarkerCollection MarkerCollection API} and only later added using `Batch` API.
  54. *
  55. * @chainable
  56. * @method module:engine/model/batch~Batch#setMarker
  57. * @param {module:engine/model/markercollection~Marker|String} markerOrName Marker or marker name to add or update.
  58. * @param {module:engine/model/range~Range} [newRange] Marker range.
  59. */
  60. register( 'setMarker', function( markerOrName, newRange ) {
  61. const name = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
  62. const currentMarker = this.document.markers.get( name );
  63. if ( !newRange && !currentMarker ) {
  64. /**
  65. * Range parameter is required when adding a new marker.
  66. *
  67. * @error batch-setMarker-no-range
  68. */
  69. throw new CKEditorError( 'batch-setMarker-no-range: Range parameter is required when adding a new marker.' );
  70. }
  71. const currentRange = currentMarker ? currentMarker.getRange() : null;
  72. if ( !newRange ) {
  73. // If `newRange` is not given, treat this as synchronizing existing marker.
  74. // Create `MarkerOperation` with `oldRange` set to `null`, so reverse operation will remove the marker.
  75. addOperation( this, name, null, currentRange );
  76. } else {
  77. // Just change marker range.
  78. addOperation( this, name, currentRange, newRange );
  79. }
  80. return this;
  81. } );
  82. /**
  83. * Removes given {@link module:engine/model/markercollection~Marker marker} or marker with given name.
  84. *
  85. * @chainable
  86. * @method module:engine/model/batch~Batch#removeMarker
  87. * @param {module:engine/model/markercollection~Marker|String} markerOrName Marker or marker name to remove.
  88. */
  89. register( 'removeMarker', function( markerOrName ) {
  90. const name = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
  91. if ( !this.document.markers.has( name ) ) {
  92. /**
  93. * Trying to remove marker that does not exist.
  94. *
  95. * @error batch-removeMarker-no-marker
  96. */
  97. throw new CKEditorError( 'batch-removeMarker-no-marker: Trying to remove marker that does not exist.' );
  98. }
  99. const oldRange = this.document.markers.get( name ).getRange();
  100. addOperation( this, name, oldRange, null );
  101. return this;
  102. } );
  103. function addOperation( batch, name, oldRange, newRange ) {
  104. const doc = batch.document;
  105. const delta = new MarkerDelta();
  106. const operation = new MarkerOperation( name, oldRange, newRange, doc.markers, doc.version );
  107. batch.addDelta( delta );
  108. delta.addOperation( operation );
  109. doc.applyOperation( operation );
  110. }
  111. DeltaFactory.register( MarkerDelta );