|
@@ -28,12 +28,11 @@ import RenameOperation from './operation/renameoperation';
|
|
|
import RootAttributeOperation from './operation/rootattributeoperation';
|
|
import RootAttributeOperation from './operation/rootattributeoperation';
|
|
|
|
|
|
|
|
import DocumentFragment from './documentfragment';
|
|
import DocumentFragment from './documentfragment';
|
|
|
|
|
+import Text from './text';
|
|
|
import Element from './element';
|
|
import Element from './element';
|
|
|
import Position from './position';
|
|
import Position from './position';
|
|
|
import Range from './range.js';
|
|
import Range from './range.js';
|
|
|
|
|
|
|
|
-import { normalizeNodes } from './writer';
|
|
|
|
|
-
|
|
|
|
|
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
|
|
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -129,34 +128,47 @@ export default class Batch {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Inserts a node or nodes at the given position.
|
|
|
|
|
- *
|
|
|
|
|
- * When inserted element is a {@link module:engine/model/documentfragment~DocumentFragment} and has markers its markers will
|
|
|
|
|
- * be set to {@link module:engine/model/document~Document#markers}.
|
|
|
|
|
- *
|
|
|
|
|
- * @chainable
|
|
|
|
|
- * @param {module:engine/model/position~Position} position Position of insertion.
|
|
|
|
|
- * @param {module:engine/model/node~NodeSet} nodes The list of nodes to be inserted.
|
|
|
|
|
- */
|
|
|
|
|
- insert( position, nodes ) {
|
|
|
|
|
- const normalizedNodes = normalizeNodes( nodes );
|
|
|
|
|
|
|
+ createText( data, attributes = {} ) {
|
|
|
|
|
+ return new Text( data, attributes );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createElement( name, attributes ) {
|
|
|
|
|
+ return new Element( name, attributes );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createDocumentFragment() {
|
|
|
|
|
+ return new DocumentFragment();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ insert( item, itemOrPosition, offset ) {
|
|
|
|
|
+ const position = Position.createAt( itemOrPosition, offset );
|
|
|
|
|
|
|
|
- // If nothing is inserted do not create delta and operation.
|
|
|
|
|
- if ( normalizedNodes.length === 0 ) {
|
|
|
|
|
- return this;
|
|
|
|
|
|
|
+ // For text that has no parent we need to make WeakInsert.
|
|
|
|
|
+ const delta = item instanceof Text && !item.parent ? new WeakInsertDelta() : new InsertDelta();
|
|
|
|
|
+
|
|
|
|
|
+ // If item is already in parent.
|
|
|
|
|
+ if ( item.parent ) {
|
|
|
|
|
+ // We need to check if item is going to be inserted to the same root.
|
|
|
|
|
+ if ( item.root === position.root ) {
|
|
|
|
|
+ // If it's we just need to move it.
|
|
|
|
|
+ return this.move( Range.createOn( item ), position );
|
|
|
|
|
+ }
|
|
|
|
|
+ // If it isn't the same root
|
|
|
|
|
+ else {
|
|
|
|
|
+ // We need to remove this item from old position first.
|
|
|
|
|
+ this.remove( item );
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const delta = new InsertDelta();
|
|
|
|
|
- const insert = new InsertOperation( position, normalizedNodes, this.document.version );
|
|
|
|
|
|
|
+ const insert = new InsertOperation( position, item, this.document.version );
|
|
|
|
|
|
|
|
this.addDelta( delta );
|
|
this.addDelta( delta );
|
|
|
delta.addOperation( insert );
|
|
delta.addOperation( insert );
|
|
|
this.document.applyOperation( insert );
|
|
this.document.applyOperation( insert );
|
|
|
|
|
|
|
|
// When element is a DocumentFragment we need to move its markers to Document#markers.
|
|
// When element is a DocumentFragment we need to move its markers to Document#markers.
|
|
|
- if ( nodes instanceof DocumentFragment ) {
|
|
|
|
|
- for ( const [ markerName, markerRange ] of nodes.markers ) {
|
|
|
|
|
|
|
+ if ( item instanceof DocumentFragment ) {
|
|
|
|
|
+ for ( const [ markerName, markerRange ] of item.markers ) {
|
|
|
// We need to migrate marker range from DocumentFragment to Document.
|
|
// We need to migrate marker range from DocumentFragment to Document.
|
|
|
const rangeRootPosition = Position.createAt( markerRange.root );
|
|
const rangeRootPosition = Position.createAt( markerRange.root );
|
|
|
const range = new Range(
|
|
const range = new Range(
|
|
@@ -171,40 +183,24 @@ export default class Batch {
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Inserts a node or nodes at given position. {@link module:engine/model/batch~Batch#weakInsert weakInsert} is commonly used for actions
|
|
|
|
|
- * like typing or plain-text paste (without formatting). There are two differences between
|
|
|
|
|
- * {@link module:engine/model/batch~Batch#insert insert} and {@link module:engine/model/batch~Batch#weakInsert weakInsert}:
|
|
|
|
|
- *
|
|
|
|
|
- * * When using `weakInsert`, inserted nodes will have same attributes as the current attributes of
|
|
|
|
|
- * {@link module:engine/model/document~Document#selection document selection}.
|
|
|
|
|
- * * If {@link module:engine/model/operation/insertoperation~InsertOperation insert operation} position is inside a range changed by
|
|
|
|
|
- * {@link module:engine/model/operation/attributeoperation~AttributeOperation attribute operation},
|
|
|
|
|
- * the attribute operation is split into two operations.
|
|
|
|
|
- * Thanks to this, attribute change "omits" the inserted nodes. The correct behavior for `WeakInsertDelta` is that
|
|
|
|
|
- * {@link module:engine/model/operation/attributeoperation~AttributeOperation AttributeOperation} does not "break" and also
|
|
|
|
|
- * applies attributes for inserted nodes. This behavior has to be reflected during
|
|
|
|
|
- * {@link module:engine/model/delta/transform~transform delta transformation}.
|
|
|
|
|
- *
|
|
|
|
|
- * @chainable
|
|
|
|
|
- * @param {module:engine/model/position~Position} position Position of insertion.
|
|
|
|
|
- * @param {module:engine/model/node~NodeSet} nodes The list of nodes to be inserted.
|
|
|
|
|
- */
|
|
|
|
|
- weakInsert( position, nodes ) {
|
|
|
|
|
- const delta = new WeakInsertDelta();
|
|
|
|
|
- this.addDelta( delta );
|
|
|
|
|
|
|
+ insertText( text, attributes, itemOrPosition, offset ) {
|
|
|
|
|
+ return this.insert( this.createText( text, attributes ), itemOrPosition, offset );
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- nodes = normalizeNodes( nodes );
|
|
|
|
|
|
|
+ insertElement( name, attributes, itemOrPosition, offset ) {
|
|
|
|
|
+ return this.insert( this.createElement( name, attributes ), itemOrPosition, offset );
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- for ( const node of nodes ) {
|
|
|
|
|
- node.setAttributesTo( this.document.selection.getAttributes() );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ append( item, parent ) {
|
|
|
|
|
+ return this.insert( item, parent, 'end' );
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const operation = new InsertOperation( position, nodes, this.document.version );
|
|
|
|
|
- delta.addOperation( operation );
|
|
|
|
|
- this.document.applyOperation( operation );
|
|
|
|
|
|
|
+ appendText( text, attributes, parent ) {
|
|
|
|
|
+ return this.insert( this.createText( text, attributes ), parent, 'end' );
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return this;
|
|
|
|
|
|
|
+ appendElement( text, attributes, parent ) {
|
|
|
|
|
+ return this.insert( this.createElement( text, attributes ), parent, 'end' );
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -309,34 +305,25 @@ export default class Batch {
|
|
|
* Moves given {@link module:engine/model/item~Item model item} or given range to target position.
|
|
* Moves given {@link module:engine/model/item~Item model item} or given range to target position.
|
|
|
*
|
|
*
|
|
|
* @chainable
|
|
* @chainable
|
|
|
- * @method module:engine/model/batch~Batch#move
|
|
|
|
|
- * @param {module:engine/model/item~Item|module:engine/model/range~Range} itemOrRange Model item or range of nodes to move.
|
|
|
|
|
- * @param {module:engine/model/position~Position} targetPosition Position where moved nodes will be inserted.
|
|
|
|
|
*/
|
|
*/
|
|
|
- move( itemOrRange, targetPosition ) {
|
|
|
|
|
- const delta = new MoveDelta();
|
|
|
|
|
- this.addDelta( delta );
|
|
|
|
|
|
|
+ move( range, itemOrPosition, offset ) {
|
|
|
|
|
+ if ( !range.isFlat ) {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Range to move is not flat.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @error batch-move-range-not-flat
|
|
|
|
|
+ */
|
|
|
|
|
+ throw new CKEditorError( 'batch-move-range-not-flat: Range to move is not flat.' );
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const addOperation = ( sourcePosition, howMany, targetPosition ) => {
|
|
|
|
|
- const operation = new MoveOperation( sourcePosition, howMany, targetPosition, this.document.version );
|
|
|
|
|
- delta.addOperation( operation );
|
|
|
|
|
- this.document.applyOperation( operation );
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ const position = Position.createAt( itemOrPosition, offset );
|
|
|
|
|
|
|
|
- if ( itemOrRange instanceof Range ) {
|
|
|
|
|
- if ( !itemOrRange.isFlat ) {
|
|
|
|
|
- /**
|
|
|
|
|
- * Range to move is not flat.
|
|
|
|
|
- *
|
|
|
|
|
- * @error batch-move-range-not-flat
|
|
|
|
|
- */
|
|
|
|
|
- throw new CKEditorError( 'batch-move-range-not-flat: Range to move is not flat.' );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const delta = new MoveDelta();
|
|
|
|
|
+ this.addDelta( delta );
|
|
|
|
|
|
|
|
- addOperation( itemOrRange.start, itemOrRange.end.offset - itemOrRange.start.offset, targetPosition );
|
|
|
|
|
- } else {
|
|
|
|
|
- addOperation( Position.createBefore( itemOrRange ), 1, targetPosition );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const operation = new MoveOperation( range.start, range.end.offset - range.start.offset, position, this.document.version );
|
|
|
|
|
+ delta.addOperation( operation );
|
|
|
|
|
+ this.document.applyOperation( operation );
|
|
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
@@ -704,9 +691,8 @@ function setAttributeToItem( batch, key, value, item ) {
|
|
|
const previousValue = item.getAttribute( key );
|
|
const previousValue = item.getAttribute( key );
|
|
|
let range, operation;
|
|
let range, operation;
|
|
|
|
|
|
|
|
- const delta = item.is( 'rootElement' ) ? new RootAttributeDelta() : new AttributeDelta();
|
|
|
|
|
-
|
|
|
|
|
if ( previousValue != value ) {
|
|
if ( previousValue != value ) {
|
|
|
|
|
+ const delta = item.is( 'rootElement' ) ? new RootAttributeDelta() : new AttributeDelta();
|
|
|
batch.addDelta( delta );
|
|
batch.addDelta( delta );
|
|
|
|
|
|
|
|
if ( item.is( 'rootElement' ) ) {
|
|
if ( item.is( 'rootElement' ) ) {
|