| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- 'use strict';
- import clone from '../../../utils/lib/lodash/clone.js';
- import CKEditorError from '../../../utils/ckeditorerror.js';
- import AttributeOperation from '../operation/attributeoperation.js';
- import InsertOperation from '../operation/insertoperation.js';
- import MoveOperation from '../operation/moveoperation.js';
- import NoOperation from '../operation/nooperation.js';
- import Operation from '../operation/operation.js';
- import ReinsertOperation from '../operation/reinsertoperation.js';
- import RemoveOperation from '../operation/removeoperation.js';
- import RootAttributeOperation from '../operation/rootattributeoperation.js';
- /**
- * Base class for all deltas.
- *
- * Delta is a single, from the user action point of view, change in the editable document, like insert, split or
- * rename element. Delta is composed of operations, which are unit changes needed to be done to execute user action.
- *
- * Multiple deltas are grouped into a single {@link engine.treeModel.Batch}.
- *
- * @memberOf engine.treeModel.delta
- */
- export default class Delta {
- /**
- * Creates a delta instance.
- */
- constructor() {
- /**
- * {@link engine.treeModel.Batch} which delta is a part of. This property is null by default and set by the
- * {@link engine.treeModel.Batch#addDelta} method.
- *
- * @readonly
- * @member {engine.treeModel.Batch} engine.treeModel.delta.Delta#batch
- */
- this.batch = null;
- /**
- * Array of operations which compose delta.
- *
- * @readonly
- * @member {engine.treeModel.operation.Operation[]} engine.treeModel.delta.Delta#operations
- */
- this.operations = [];
- }
- /**
- * Returns delta base version which is equal to the base version of the first operation in delta. If there
- * are no operations in delta, returns `null`.
- *
- * @see engine.treeModel.Document
- * @type {Number|null}
- */
- get baseVersion() {
- if ( this.operations.length > 0 ) {
- return this.operations[ 0 ].baseVersion;
- }
- return null;
- }
- /**
- * A class that will be used when creating reversed delta.
- *
- * @private
- * @type {Function}
- */
- get _reverseDeltaClass() {
- return Delta;
- }
- /**
- * Add operation to the delta.
- *
- * @param {engine.treeModel.operation.Operation} operation Operation instance.
- */
- addOperation( operation ) {
- operation.delta = this;
- this.operations.push( operation );
- return operation;
- }
- /**
- * Creates and returns a delta that has the same parameters as this delta.
- *
- * @returns {engine.treeModel.delta.Delta} Clone of this delta.
- */
- clone() {
- let delta = new this.constructor();
- for ( let op of this.operations ) {
- delta.addOperation( op.clone() );
- }
- return delta;
- }
- /**
- * Creates and returns a reverse delta. Reverse delta when executed right after the original delta will bring back
- * tree model state to the point before the original delta execution. In other words, it reverses changes done
- * by the original delta.
- *
- * Keep in mind that tree model state may change since executing the original delta, so reverse delta may be "outdated".
- * In that case you will need to {@link engine.treeModel.delta.transform} it by all deltas that were executed after
- * the original delta.
- *
- * @returns {engine.treeModel.delta.Delta} Reversed delta.
- */
- getReversed() {
- let delta = new this._reverseDeltaClass();
- for ( let op of this.operations ) {
- delta.addOperation( op.getReversed() );
- }
- delta.operations.reverse();
- for ( let i = 0; i < delta.operations.length; i++ ) {
- delta.operations[ i ].baseVersion = this.operations[ this.operations.length - 1 ].baseVersion + i + 1;
- }
- return delta;
- }
- /**
- * Custom toJSON method to make deltas serializable.
- *
- * @returns {Object} Clone of this delta with added class name.
- */
- toJSON() {
- let json = clone( this );
- json.__className = this.constructor.className;
- // Remove parent batch to avoid circular dependencies.
- delete json.batch;
- return json;
- }
- /**
- * Delta class name. Used by {@link engine.treeModel.delta.Delta#toJSON} method for serialization and
- * {@link engine.treeModel.delta.Delta.fromJSON} during deserialization.
- *
- * @type {String}
- * @readonly
- */
- static get className() {
- return 'engine.treeModel.delta.Delta';
- }
- /**
- * Delta priority. Used in {@link engine.treeModel.delta.transform delta transformations}. Delta with the higher
- * priority will be treated as more important when resolving transformation conflicts. If deltas have same
- * priority, other factors will be used to determine which delta is more important.
- *
- * @private
- * @type {Number}
- */
- static get _priority() {
- return 0;
- }
- /**
- * Creates InsertDelta from deserialized object, i.e. from parsed JSON string.
- *
- * @param {Object} json
- * @param {engine.treeModel.Document} doc Document on which this delta will be applied.
- * @returns {engine.treeModel.delta.InsertDelta}
- */
- static fromJSON( json, doc ) {
- if ( !deserializers.has( json.__className ) ) {
- /**
- * This delta has no defined deserializer.
- *
- * @error delta-fromjson-no-deserializer
- * @param {String} name
- */
- throw new CKEditorError(
- 'delta-fromjson-no-deserializer: This delta has no defined deserializer',
- { name: json.__className }
- );
- }
- let Constructor = deserializers.get( json.__className );
- let delta = new Constructor();
- if ( json.operations.length ) {
- json.operations.forEach( ( operation ) => delta.addOperation( operations[ operation.__className ].fromJSON( operation, doc ) ) );
- }
- return delta;
- }
- }
- const operations = {};
- operations[ AttributeOperation.className ] = AttributeOperation;
- operations[ InsertOperation.className ] = InsertOperation;
- operations[ MoveOperation.className ] = MoveOperation;
- operations[ NoOperation.className ] = NoOperation;
- operations[ Operation.className ] = Operation;
- operations[ ReinsertOperation.className ] = ReinsertOperation;
- operations[ RemoveOperation.className ] = RemoveOperation;
- operations[ RootAttributeOperation.className ] = RootAttributeOperation;
- const deserializers = new Map();
- export function registerDeserializer( className, constructor ) {
- if ( deserializers.has( className ) ) {
- /**
- * This delta name is already defined.
- *
- * @error delta-register-deserializer-defined
- * @param {String} name
- */
- throw new CKEditorError(
- 'delta-register-deserializer-defined: This delta name is already defined.',
- { name: className } );
- }
- deserializers.set( className, constructor );
- }
|