8
0

mergedelta.js 3.2 KB

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