| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- /**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- 'use strict';
- import Operation from './operation.js';
- import Position from '../position.js';
- import Range from '../range.js';
- import CKEditorError from '../../ckeditorerror.js';
- import utils from '../../utils.js';
- /**
- * Operation to move list of subsequent nodes from one position in the document to another.
- *
- * @memberOf core.treeModel.operation
- * @extends core.treeModel.operation.Operation
- */
- export default class MoveOperation extends Operation {
- /**
- * Creates a move operation.
- *
- * @param {core.treeModel.Position} sourcePosition Position before the first node to move.
- * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
- * @param {core.treeModel.Position} targetPosition Position where moved nodes will be inserted.
- * @param {Number} baseVersion {@link core.treeModel.Document#version} on which operation can be applied.
- */
- constructor( sourcePosition, howMany, targetPosition, baseVersion ) {
- super( baseVersion );
- /**
- * Source move position.
- *
- * @member {core.treeModel.Position} core.treeModel.operation.MoveOperation#sourcePosition
- */
- this.sourcePosition = Position.createFromPosition( sourcePosition );
- /**
- * How many nodes to move.
- *
- * @member {Number} core.treeModel.operation.MoveOperation#howMany
- */
- this.howMany = howMany;
- /**
- * Target move position.
- *
- * @member {core.treeModel.Position} core.treeModel.operation.MoveOperation#targetPosition
- */
- this.targetPosition = Position.createFromPosition( targetPosition );
- }
- get type() {
- return 'move';
- }
- /**
- * Defines whether `MoveOperation` is sticky. If `MoveOperation` is sticky, during
- * {@link core.treeModel.operation.transform operational transformation} if there will be an operation that
- * inserts some nodes at the position equal to the boundary of this `MoveOperation`, that operation will
- * get their insertion path updated to the position where this `MoveOperation` moves the range.
- *
- * @protected
- * @type {Boolean}
- */
- get isSticky() {
- return true;
- }
- /**
- * @returns {core.treeModel.operation.MoveOperation}
- */
- clone() {
- return new this.constructor( this.sourcePosition, this.howMany, this.targetPosition, this.baseVersion );
- }
- /**
- * @returns {core.treeModel.operation.MoveOperation}
- */
- getReversed() {
- let newSourcePosition = this.targetPosition.getTransformedByDeletion( this.sourcePosition, this.howMany );
- let newTargetPosition = this.sourcePosition.getTransformedByInsertion( this.targetPosition, this.howMany );
- return new this.constructor( newSourcePosition, this.howMany, newTargetPosition, this.baseVersion + 1 );
- }
- _execute() {
- let sourceElement = this.sourcePosition.parent;
- let targetElement = this.targetPosition.parent;
- let sourceOffset = this.sourcePosition.offset;
- let targetOffset = this.targetPosition.offset;
- // Validate whether move operation has correct parameters.
- // Validation is pretty complex but move operation is one of the core ways to manipulate the document state.
- // We expect that many errors might be connected with one of scenarios described below.
- if ( !sourceElement || !targetElement ) {
- /**
- * Source position or target position is invalid.
- *
- * @error operation-move-position-invalid
- */
- throw new CKEditorError(
- 'operation-move-position-invalid: Source position or target position is invalid.'
- );
- } else if ( sourceOffset + this.howMany > sourceElement.getChildCount() ) {
- /**
- * The nodes which should be moved do not exist.
- *
- * @error operation-move-nodes-do-not-exist
- */
- throw new CKEditorError(
- 'operation-move-nodes-do-not-exist: The nodes which should be moved do not exist.'
- );
- } else if ( sourceElement === targetElement && sourceOffset <= targetOffset && targetOffset < sourceOffset + this.howMany ) {
- /**
- * Trying to move a range of nodes into the middle of that range.
- *
- * @error operation-move-range-into-itself
- */
- throw new CKEditorError(
- 'operation-move-range-into-itself: Trying to move a range of nodes to the inside of that range.'
- );
- } else {
- if ( utils.compareArrays( this.sourcePosition.getParentPath(), this.targetPosition.getParentPath() ) == 'PREFIX' ) {
- let i = this.sourcePosition.path.length - 1;
- if ( this.targetPosition.path[ i ] >= sourceOffset && this.targetPosition.path[ i ] < sourceOffset + this.howMany ) {
- /**
- * Trying to move a range of nodes into one of nodes from that range.
- *
- * @error operation-move-node-into-itself
- */
- throw new CKEditorError(
- 'operation-move-node-into-itself: Trying to move a range of nodes into one of nodes from that range.'
- );
- }
- }
- }
- // End of validation.
- // If we move children in the same element and we remove elements on the position before the target we
- // need to update a target offset.
- if ( sourceElement === targetElement && sourceOffset < targetOffset ) {
- targetOffset -= this.howMany;
- }
- const removedNodes = sourceElement.removeChildren( sourceOffset, this.howMany );
- targetElement.insertChildren( targetOffset, removedNodes );
- return {
- sourcePosition: this.sourcePosition,
- range: Range.createFromPositionAndShift( this.targetPosition, this.howMany )
- };
- }
- }
|