Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
isBefore( node ) {
// Given node is not before this node if they are same.
if ( this == node ) {
return false;
}
// Return `false` if it is impossible to compare nodes.
if ( this.root !== node.root ) {
return false;
}
const thisPath = this.getPath();
const nodePath = node.getPath();
const result = compareArrays( thisPath, nodePath );
switch ( result ) {
case 'prefix':
return true;
case 'extension':
return false;
default:
return thisPath[ result ] < nodePath[ result ];
}
}
_getTransformedByDeletion( deletePosition, howMany ) {
// This position can't be affected if deletion was in a different root.
if ( this.root != deletePosition.root ) {
return Position.createFromPosition( this );
}
const comparisonResult = compareArrays( deletePosition.getParentPath(), this.getParentPath() );
if ( comparisonResult == 'same' ) {
// If nodes are removed from the node that is pointed by this position...
if ( deletePosition.offset < this.offset ) {
// And are removed from before an offset of that position...
if ( deletePosition.offset + howMany > this.offset ) {
// Position is in removed range, it's no longer in the tree.
return null;
} else {
return this.getShiftedBy( -howMany );
}
}
} else if ( comparisonResult == 'prefix' ) {
// If nodes are removed from a node that is on a path to this position...
const i = deletePosition.path.length - 1;
return transformed;
}
if ( compareArrays( deletePosition.getParentPath(), this.getParentPath() ) == 'same' ) {
// If nodes are removed from the node that is pointed by this position...
if ( deletePosition.offset < this.offset ) {
// And are removed from before an offset of that position...
if ( deletePosition.offset + howMany > this.offset ) {
// Position is in removed range, it's no longer in the tree.
return null;
} else {
// Decrement the offset accordingly.
transformed.offset -= howMany;
}
}
} else if ( compareArrays( deletePosition.getParentPath(), this.getParentPath() ) == 'prefix' ) {
// If nodes are removed from a node that is on a path to this position...
const i = deletePosition.path.length - 1;
if ( deletePosition.offset <= this.path[ i ] ) {
// And are removed from before next node of that path...
if ( deletePosition.offset + howMany > this.path[ i ] ) {
// If the next node of that path is removed return null
// because the node containing this position got removed.
return null;
} else {
// Otherwise, decrement index on that path.
transformed.path[ i ] -= howMany;
}
}
}
_getTransformedByInsertion( insertPosition, howMany, insertBefore ) {
// This position can't be affected if insertion was in a different root.
if ( this.root != insertPosition.root ) {
return Position.createFromPosition( this );
}
if ( compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == 'same' ) {
// If nodes are inserted in the node that is pointed by this position...
if ( insertPosition.offset < this.offset || ( insertPosition.offset == this.offset && insertBefore ) ) {
// And are inserted before an offset of that position...
// "Push" this positions offset.
return this.getShiftedBy( howMany );
}
} else if ( compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == 'prefix' ) {
// If nodes are inserted in a node that is on a path to this position...
const i = insertPosition.path.length - 1;
if ( insertPosition.offset <= this.path[ i ] ) {
// And are inserted before next node of that path...
// "Push" the index on that path.
const path = this.path.slice();
path[ i ] += howMany;
return new Position( this.root, path );
}
}
return Position.createFromPosition( this );
}
//
// The element to rename has been split. In this case, the new element should be also renamed.
//
// User decides to change the paragraph to a list item:
// Foobar
//
// However, in meantime, split happens:
// Foobar
//
// As a result, rename both elements:
// Foobar
//
const renamePath = a.position.path;
const splitPath = b.splitPosition.getParentPath();
if ( compareArrays( renamePath, splitPath ) == 'same' && !b.graveyardPosition ) {
const extraRename = new RenameOperation( a.position.getShiftedBy( 1 ), a.oldName, a.newName, 0 );
return [ a, extraRename ];
}
// The default case.
//
a.position = a.position._getTransformedBySplitOperation( b );
return [ a ];
} );
addTransformationCase( WrapDelta, SplitDelta, ( a, b, context ) => {
// Do not apply special transformation case if `SplitDelta` has `NoOperation` as the second operation.
if ( !b.position ) {
return defaultTransform( a, b, context );
}
// If incoming wrap delta tries to wrap range that contains split position, we have to cancel the split and apply
// the wrap. Since split was already applied, we have to revert it.
const sameRoot = a.range.start.root == b.position.root;
const operateInSameParent = sameRoot && compareArrays( a.range.start.getParentPath(), b.position.getParentPath() ) === 'same';
const splitInsideWrapRange = a.range.start.offset < b.position.offset && a.range.end.offset >= b.position.offset;
if ( operateInSameParent && splitInsideWrapRange ) {
return [
b.getReversed(),
a.clone()
];
} else if ( sameRoot && compareArrays( b.position.getParentPath(), a.range.end.getShiftedBy( -1 ).path ) === 'same' ) {
const delta = a.clone();
// Move wrapping element insert position one node further so it is after the split node insertion.
delta._insertOperation.position = delta._insertOperation.position.getShiftedBy( 1 );
// Include the split node copy.
delta._moveOperation.howMany++;
// Do not apply special transformation case if transformation is in undo mode.
if ( undoMode ) {
return defaultTransform( a, b, context );
}
// Do not apply special transformation case if `SplitDelta` has `NoOperation` as the second operation.
if ( !a.position || !b.position ) {
return defaultTransform( a, b, context );
}
const pathA = a.position.getParentPath();
const pathB = b.position.getParentPath();
// The special case is for splits inside the same parent.
if ( a.position.root == b.position.root && compareArrays( pathA, pathB ) == 'same' ) {
a = a.clone();
if ( a.position.offset < b.position.offset || ( a.position.offset == b.position.offset && context.isStrong ) ) {
// If both first operations are `ReinsertOperation`s, we might need to transform `a._cloneOperation`,
// so it will take correct node from graveyard.
if (
a._cloneOperation instanceof ReinsertOperation && b._cloneOperation instanceof ReinsertOperation &&
a._cloneOperation.sourcePosition.offset > b._cloneOperation.sourcePosition.offset
) {
a._cloneOperation.sourcePosition = a._cloneOperation.sourcePosition.getShiftedBy( -1 );
}
// `a` splits closer or at same offset.
// Change how many nodes are moved. Do not move nodes that were moved by delta `b`.
const aRange = Range.createFromPositionAndShift( a.position, a._moveOperation.howMany );
const bRange = Range.createFromPositionAndShift( b.position, b._moveOperation.howMany );
compareWith( otherPosition ) {
if ( this.root != otherPosition.root ) {
return 'different';
}
const result = compareArrays( this.path, otherPosition.path );
switch ( result ) {
case 'same':
return 'same';
case 'prefix':
return 'before';
case 'extension':
return 'after';
default:
return this.path[ result ] < otherPosition.path[ result ] ? 'before' : 'after';
}
}
function _isOperationAffected( opA, opB ) {
const target = opA.targetPosition;
const source = opB.sourcePosition;
const cmpResult = compareArrays( source.getParentPath(), target.getParentPath() );
if ( target.root != source.root ) {
return false;
}
return cmpResult == 'same' && source.offset < target.offset;
}