moveoperation.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. CKEDITOR.define( [
  7. 'document/operation/operation',
  8. 'ckeditorerror',
  9. 'utils'
  10. ], ( Operation, CKEditorError, utils ) => {
  11. /**
  12. * Operation to move list of subsequent nodes from one position in the document to another.
  13. *
  14. * @class document.operation.MoveOperation
  15. */
  16. class MoveOperation extends Operation {
  17. /**
  18. * Creates a move operation.
  19. *
  20. * @param {document.Position} sourcePosition Position before the first element to move.
  21. * @param {document.Position} targetPosition Position where moved elements will be inserted.
  22. * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
  23. * @param {Number} baseVersion {@link document.Document#version} on which operation can be applied.
  24. * @constructor
  25. */
  26. constructor( sourcePosition, targetPosition, howMany, baseVersion ) {
  27. super( baseVersion );
  28. /**
  29. * Source move position.
  30. *
  31. * @type {document.Position}
  32. */
  33. this.sourcePosition = sourcePosition;
  34. /**
  35. * Target move position.
  36. *
  37. * @type {document.Position}
  38. */
  39. this.targetPosition = targetPosition;
  40. /**
  41. * How many nodes to move.
  42. *
  43. * @type {Number}
  44. */
  45. this.howMany = howMany;
  46. }
  47. clone() {
  48. return new MoveOperation( this.sourcePosition.clone(), this.targetPosition.clone(), this.howMany, this.baseVersion );
  49. }
  50. getReversed() {
  51. return new MoveOperation( this.targetPosition.clone(), this.sourcePosition.clone(), this.howMany, this.baseVersion + 1 );
  52. }
  53. _execute() {
  54. let sourceElement = this.sourcePosition.parent;
  55. let targetElement = this.targetPosition.parent;
  56. let sourceOffset = this.sourcePosition.offset;
  57. let targetOffset = this.targetPosition.offset;
  58. // Validate whether move operation has correct parameters.
  59. // Validation is pretty complex but move operation is one of the core ways to manipulate the document state.
  60. // We expect that many errors might be connected with one of scenarios described below.
  61. if ( !sourceElement || !targetElement ) {
  62. /**
  63. * Source position or target position is invalid.
  64. *
  65. * @error operation-move-position-invalid
  66. */
  67. throw new CKEditorError(
  68. 'operation-move-position-invalid: Source position or target position is invalid.'
  69. );
  70. } else if ( sourceOffset + this.howMany > sourceElement.getChildCount() ) {
  71. /**
  72. * The nodes which should be moved do not exist.
  73. *
  74. * @error operation-move-nodes-do-not-exist
  75. */
  76. throw new CKEditorError(
  77. 'operation-move-nodes-do-not-exist: The nodes which should be moved do not exist.'
  78. );
  79. } else if ( sourceElement === targetElement && sourceOffset <= targetOffset && targetOffset < sourceOffset + this.howMany ) {
  80. /**
  81. * Trying to move a range of nodes into the middle of that range.
  82. *
  83. * @error operation-move-range-into-itself
  84. */
  85. throw new CKEditorError(
  86. 'operation-move-range-into-itself: Trying to move a range of nodes to the inside of that range.'
  87. );
  88. } else {
  89. if ( utils.compareArrays( this.sourcePosition.getParentPath(), this.targetPosition.getParentPath() ) == utils.compareArrays.PREFIX ) {
  90. let i = this.sourcePosition.path.length - 1;
  91. if ( this.targetPosition.path[ i ] >= sourceOffset && this.targetPosition.path[ i ] < sourceOffset + this.howMany ) {
  92. /**
  93. * Trying to move a range of nodes into one of nodes from that range.
  94. *
  95. * @error operation-move-node-into-itself
  96. */
  97. throw new CKEditorError(
  98. 'operation-move-node-into-itself: Trying to move a range of nodes into one of nodes from that range.'
  99. );
  100. }
  101. }
  102. }
  103. // End of validation.
  104. // If we move children in the same element and we remove elements on the position before the target we
  105. // need to update a target offset.
  106. if ( sourceElement === targetElement && sourceOffset < targetOffset ) {
  107. targetOffset -= this.howMany;
  108. }
  109. const removedNodes = sourceElement.removeChildren( sourceOffset, this.howMany );
  110. targetElement.insertChildren( targetOffset, removedNodes );
  111. }
  112. }
  113. return MoveOperation;
  114. } );