mergedelta.js 3.5 KB

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