unwrapdelta.js 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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 WrapDelta from './wrapdelta.js';
  8. import { register } from '../batch.js';
  9. import { registerDeserializer } from './delta.js';
  10. import Position from '../position.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.treeModel.Batch#merge} method
  17. * uses the `UnwrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
  18. *
  19. * @memberOf engine.treeModel.delta
  20. */
  21. export default class UnwrapDelta extends Delta {
  22. /**
  23. * Position before unwrapped element or `null` if there are no operations in the delta.
  24. *
  25. * @type {engine.treeModel.Position|null}
  26. */
  27. get position() {
  28. return this._moveOperation ? this._moveOperation.targetPosition : null;
  29. }
  30. /**
  31. * Operation in the delta that moves unwrapped nodes to their new parent or `null` if there are no operations in the delta.
  32. *
  33. * @protected
  34. * @type {engine.treeModel.operation.MoveOperation|null}
  35. */
  36. get _moveOperation() {
  37. return this.operations[ 0 ] || null;
  38. }
  39. get _reverseDeltaClass() {
  40. return WrapDelta;
  41. }
  42. static get className() {
  43. return 'engine.treeModel.delta.UnwrapDelta';
  44. }
  45. static get _priority() {
  46. return 10;
  47. }
  48. }
  49. /**
  50. * Unwraps specified element, that is moves all it's children before it and then removes it. Throws
  51. * error if you try to unwrap an element that does not have a parent.
  52. *
  53. * @chainable
  54. * @method engine.treeModel.Batch#unwrap
  55. * @param {engine.treeModel.Element} position Element to unwrap.
  56. */
  57. register( 'unwrap', function( element ) {
  58. if ( element.parent === null ) {
  59. /**
  60. * Trying to unwrap an element that has no parent.
  61. *
  62. * @error batch-unwrap-element-no-parent
  63. */
  64. throw new CKEditorError( 'batch-unwrap-element-no-parent: Trying to unwrap an element that has no parent.' );
  65. }
  66. const delta = new UnwrapDelta();
  67. this.addDelta( delta );
  68. let sourcePosition = Position.createFromParentAndOffset( element, 0 );
  69. const move = new MoveOperation( sourcePosition, element.getChildCount(), Position.createBefore( element ), this.doc.version );
  70. move.isSticky = true;
  71. delta.addOperation( move );
  72. this.doc.applyOperation( move );
  73. // Computing new position because we moved some nodes before `element`.
  74. // If we would cache `Position.createBefore( element )` we remove wrong node.
  75. const remove = new RemoveOperation( Position.createBefore( element ), 1, this.doc.version );
  76. delta.addOperation( remove );
  77. this.doc.applyOperation( remove );
  78. return this;
  79. } );
  80. registerDeserializer( UnwrapDelta.className, UnwrapDelta );