mergedelta.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Delta from './delta.js';
  6. import DeltaFactory from './deltafactory.js';
  7. import SplitDelta from './splitdelta.js';
  8. import { register } from '../batch.js';
  9. import Position from '../position.js';
  10. import Element from '../element.js';
  11. import RemoveOperation from '../operation/removeoperation.js';
  12. import MoveOperation from '../operation/moveoperation.js';
  13. import CKEditorError from '../../../utils/ckeditorerror.js';
  14. /**
  15. * @classdesc
  16. * To provide specific OT behavior and better collisions solving, {@link engine.model.Batch#merge} method
  17. * uses the `MergeDelta` class which inherits from the `Delta` class and may overwrite some methods.
  18. *
  19. * @memberOf engine.model.delta
  20. */
  21. export default class MergeDelta extends Delta {
  22. /**
  23. * Position between to merged nodes or `null` if the delta has no operations.
  24. *
  25. * @readonly
  26. * @type {engine.model.Position|null}
  27. */
  28. get position() {
  29. return this._removeOperation ? this._removeOperation.sourcePosition : null;
  30. }
  31. /**
  32. * @inheritDoc
  33. */
  34. getReversed() {
  35. let delta = super.getReversed();
  36. if ( delta.operations.length > 0 ) {
  37. delta.operations[ 1 ].isSticky = false;
  38. }
  39. return delta;
  40. }
  41. /**
  42. * Operation in this delta that removes the node after merge position (which will be empty at that point) or
  43. * `null` if the delta has no operations. Note, that after {@link engine.model.delta.transform transformation}
  44. * this might be an instance of {@link engine.model.operation.MoveOperation} instead of
  45. * {@link engine.model.operation.RemoveOperation}.
  46. *
  47. * @readonly
  48. * @protected
  49. * @type {engine.model.operation.MoveOperation|null}
  50. */
  51. get _removeOperation() {
  52. return this.operations[ 1 ] || null;
  53. }
  54. /**
  55. * @inheritDoc
  56. */
  57. get _reverseDeltaClass() {
  58. return SplitDelta;
  59. }
  60. /**
  61. * @inheritDoc
  62. */
  63. static get className() {
  64. return 'engine.model.delta.MergeDelta';
  65. }
  66. }
  67. /**
  68. * Merges two siblings at the given position.
  69. *
  70. * Node before and after the position have to be an element. Otherwise `batch-merge-no-element-before` or
  71. * `batch-merge-no-element-after` error will be thrown.
  72. *
  73. * @chainable
  74. * @method engine.model.Batch#merge
  75. * @param {engine.model.Position} position Position of merge.
  76. */
  77. register( 'merge', function( position ) {
  78. const delta = new MergeDelta();
  79. this.addDelta( delta );
  80. const nodeBefore = position.nodeBefore;
  81. const nodeAfter = position.nodeAfter;
  82. if ( !( nodeBefore instanceof Element ) ) {
  83. /**
  84. * Node before merge position must be an element.
  85. *
  86. * @error batch-merge-no-element-before
  87. */
  88. throw new CKEditorError( 'batch-merge-no-element-before: Node before merge position must be an element.' );
  89. }
  90. if ( !( nodeAfter instanceof Element ) ) {
  91. /**
  92. * Node after merge position must be an element.
  93. *
  94. * @error batch-merge-no-element-after
  95. */
  96. throw new CKEditorError( 'batch-merge-no-element-after: Node after merge position must be an element.' );
  97. }
  98. const positionAfter = Position.createFromParentAndOffset( nodeAfter, 0 );
  99. const positionBefore = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getMaxOffset() );
  100. const move = new MoveOperation( positionAfter, nodeAfter.getMaxOffset(), positionBefore, this.document.version );
  101. move.isSticky = true;
  102. delta.addOperation( move );
  103. this.document.applyOperation( move );
  104. const remove = new RemoveOperation( position, 1, this.document.version );
  105. delta.addOperation( remove );
  106. this.document.applyOperation( remove );
  107. return this;
  108. } );
  109. DeltaFactory.register( MergeDelta );