wrapdelta.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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 UnwrapDelta from './unwrapdelta.js';
  8. import { register } from '../batch.js';
  9. import Position from '../position.js';
  10. import Range from '../range.js';
  11. import Element from '../element.js';
  12. import InsertOperation from '../operation/insertoperation.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 core.treeModel.Batch#merge} method
  18. * uses the `WrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
  19. *
  20. * @memberOf core.treeModel.delta
  21. */
  22. export default class WrapDelta extends Delta {
  23. /**
  24. * Range to wrap or `null` if there are no operations in the delta.
  25. *
  26. * @type {core.treeModel.Range|null}
  27. */
  28. get range() {
  29. let moveOp = this._moveOperation;
  30. return moveOp ? Range.createFromPositionAndShift( moveOp.sourcePosition, moveOp.howMany ) : null;
  31. }
  32. /**
  33. * Operation that moves wrapped nodes to their new parent or `null` if there are no operations in the delta.
  34. *
  35. * @protected
  36. * @type {core.treeModel.operation.MoveOperation|null}
  37. */
  38. get _moveOperation() {
  39. return this.operations[ 1 ] || null;
  40. }
  41. /**
  42. * @see core.treeModel.delta.Delta#_reverseDeltaClass
  43. * @private
  44. * @type {Object}
  45. */
  46. get _reverseDeltaClass() {
  47. return UnwrapDelta;
  48. }
  49. static get _priority() {
  50. return 10;
  51. }
  52. }
  53. /**
  54. * Wraps given range with given element or with a new element of specified name if string has been passed.
  55. * **Note:** given range should be a "flat range" (see {@link core.treeModel.Range#isFlat}). If not, error will be thrown.
  56. *
  57. * @chainable
  58. * @method core.treeModel.Batch#wrap
  59. * @param {core.treeModel.Range} range Range to wrap.
  60. * @param {core.treeModel.Element|String} elementOrString Element or name of element to wrap the range with.
  61. */
  62. register( 'wrap', function( range, elementOrString ) {
  63. if ( !range.isFlat ) {
  64. /**
  65. * Range to wrap is not flat.
  66. *
  67. * @error batch-wrap-range-not-flat
  68. */
  69. throw new CKEditorError( 'batch-wrap-range-not-flat: Range to wrap is not flat.' );
  70. }
  71. let element = elementOrString instanceof Element ? elementOrString : new Element( elementOrString );
  72. if ( element.getChildCount() > 0 ) {
  73. /**
  74. * Element to wrap with is not empty.
  75. *
  76. * @error batch-wrap-element-not-empty
  77. */
  78. throw new CKEditorError( 'batch-wrap-element-not-empty: Element to wrap with is not empty.' );
  79. }
  80. if ( element.parent !== null ) {
  81. /**
  82. * Element to wrap with is already attached to a tree model.
  83. *
  84. * @error batch-wrap-element-attached
  85. */
  86. throw new CKEditorError( 'batch-wrap-element-attached: Element to wrap with is already attached to tree model.' );
  87. }
  88. const delta = new WrapDelta();
  89. let insert = new InsertOperation( range.end, element, this.doc.version );
  90. delta.addOperation( insert );
  91. this.doc.applyOperation( insert );
  92. let targetPosition = Position.createFromParentAndOffset( element, 0 );
  93. let move = new MoveOperation( range.start, range.end.offset - range.start.offset, targetPosition, this.doc.version );
  94. delta.addOperation( move );
  95. this.doc.applyOperation( move );
  96. this.addDelta( delta );
  97. return this;
  98. } );