Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #1258 from ckeditor/t/1227
Browse files Browse the repository at this point in the history
Other: Moved `Document#getNearesetSelectionRange` to `Schema`. Closes #1227.
  • Loading branch information
Piotr Jasiun authored Jan 30, 2018
2 parents 7db1fee + ca6a82a commit d1838a4
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 329 deletions.
96 changes: 2 additions & 94 deletions src/model/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import Position from './position';
import RootElement from './rootelement';
import History from './history';
import DocumentSelection from './documentselection';
import TreeWalker from './treewalker';
import Collection from '@ckeditor/ckeditor5-utils/src/collection';
import clone from '@ckeditor/ckeditor5-utils/src/lib/lodash/clone';
import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
Expand Down Expand Up @@ -248,59 +247,6 @@ export default class Document {
return Array.from( this.roots, root => root.rootName ).filter( name => name != graveyardName );
}

/**
* Basing on given `position`, finds and returns a {@link module:engine/model/range~Range Range} instance that is
* nearest to that `position` and is a correct range for selection.
*
* Correct selection range might be collapsed - when it's located in position where text node can be placed.
* Non-collapsed range is returned when selection can be placed around element marked as "object" in
* {@link module:engine/model/schema~Schema schema}.
*
* Direction of searching for nearest correct selection range can be specified as:
* * `both` - searching will be performed in both ways,
* * `forward` - searching will be performed only forward,
* * `backward` - searching will be performed only backward.
*
* When valid selection range cannot be found, `null` is returned.
*
* @param {module:engine/model/position~Position} position Reference position where new selection range should be looked for.
* @param {'both'|'forward'|'backward'} [direction='both'] Search direction.
* @returns {module:engine/model/range~Range|null} Nearest selection range or `null` if one cannot be found.
*/
getNearestSelectionRange( position, direction = 'both' ) {
const schema = this.model.schema;

// Return collapsed range if provided position is valid.
if ( schema.checkChild( position, '$text' ) ) {
return new Range( position );
}

let backwardWalker, forwardWalker;

if ( direction == 'both' || direction == 'backward' ) {
backwardWalker = new TreeWalker( { startPosition: position, direction: 'backward' } );
}

if ( direction == 'both' || direction == 'forward' ) {
forwardWalker = new TreeWalker( { startPosition: position } );
}

for ( const data of combineWalkers( backwardWalker, forwardWalker ) ) {
const type = ( data.walker == backwardWalker ? 'elementEnd' : 'elementStart' );
const value = data.value;

if ( value.type == type && schema.isObject( value.item ) ) {
return Range.createOn( value.item );
}

if ( schema.checkChild( value.nextPosition, '$text' ) ) {
return new Range( value.nextPosition );
}
}

return null;
}

/**
* Used to register a post-fixer callback. Post-fixers mechanism guarantees that the features that listen to
* {@link module:engine/model/model~Model#event:_change model's change event} will operate on a correct model state.
Expand Down Expand Up @@ -380,10 +326,11 @@ export default class Document {
*/
_getDefaultRange() {
const defaultRoot = this._getDefaultRoot();
const schema = this.model.schema;

// Find the first position where the selection can be put.
const position = new Position( defaultRoot, [ 0 ] );
const nearestRange = this.getNearestSelectionRange( position );
const nearestRange = schema.getNearestSelectionRange( position );

// If valid selection range is not found - return range collapsed at the beginning of the root.
return nearestRange || new Range( position );
Expand Down Expand Up @@ -446,42 +393,3 @@ function validateTextNodePosition( rangeBoundary ) {

return true;
}

// Generator function returning values from provided walkers, switching between them at each iteration. If only one walker
// is provided it will return data only from that walker.
//
// @param {module:engine/module/treewalker~TreeWalker} [backward] Walker iterating in backward direction.
// @param {module:engine/module/treewalker~TreeWalker} [forward] Walker iterating in forward direction.
// @returns {Iterable.<Object>} Object returned at each iteration contains `value` and `walker` (informing which walker returned
// given value) fields.
function* combineWalkers( backward, forward ) {
let done = false;

while ( !done ) {
done = true;

if ( backward ) {
const step = backward.next();

if ( !step.done ) {
done = false;
yield {
walker: backward,
value: step.value
};
}
}

if ( forward ) {
const step = forward.next();

if ( !step.done ) {
done = false;
yield {
walker: forward,
value: step.value
};
}
}
}
}
2 changes: 1 addition & 1 deletion src/model/documentselection.js
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ class LiveSelection extends Selection {
const positionCandidate = Position.createFromPosition( removedRangeStart );

// Find a range that is a correct selection range and is closest to the start of removed range.
const selectionRange = this._document.getNearestSelectionRange( positionCandidate );
const selectionRange = this._model.schema.getNearestSelectionRange( positionCandidate );

// Remove the old selection range before preparing and adding new selection range. This order is important,
// because new range, in some cases, may intersect with old range (it depends on `getNearestSelectionRange()` result).
Expand Down
92 changes: 92 additions & 0 deletions src/model/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
import Range from './range';
import Position from './position';
import Element from './element';
import TreeWalker from './treewalker';

/**
* The model's schema. It defines allowed and disallowed structures of nodes as well as nodes' attributes.
Expand Down Expand Up @@ -667,6 +668,58 @@ export default class Schema {
return validRanges;
}

/**
* Basing on given `position`, finds and returns a {@link module:engine/model/range~Range Range} instance that is
* nearest to that `position` and is a correct range for selection.
*
* Correct selection range might be collapsed - when it's located in position where text node can be placed.
* Non-collapsed range is returned when selection can be placed around element marked as "object" in
* {@link module:engine/model/schema~Schema schema}.
*
* Direction of searching for nearest correct selection range can be specified as:
* * `both` - searching will be performed in both ways,
* * `forward` - searching will be performed only forward,
* * `backward` - searching will be performed only backward.
*
* When valid selection range cannot be found, `null` is returned.
*
* @param {module:engine/model/position~Position} position Reference position where new selection range should be looked for.
* @param {'both'|'forward'|'backward'} [direction='both'] Search direction.
* @returns {module:engine/model/range~Range|null} Nearest selection range or `null` if one cannot be found.
*/

getNearestSelectionRange( position, direction = 'both' ) {
// Return collapsed range if provided position is valid.
if ( this.checkChild( position, '$text' ) ) {
return new Range( position );
}

let backwardWalker, forwardWalker;

if ( direction == 'both' || direction == 'backward' ) {
backwardWalker = new TreeWalker( { startPosition: position, direction: 'backward' } );
}

if ( direction == 'both' || direction == 'forward' ) {
forwardWalker = new TreeWalker( { startPosition: position } );
}

for ( const data of combineWalkers( backwardWalker, forwardWalker ) ) {
const type = ( data.walker == backwardWalker ? 'elementEnd' : 'elementStart' );
const value = data.value;

if ( value.type == type && this.isObject( value.item ) ) {
return Range.createOn( value.item );
}

if ( this.checkChild( value.nextPosition, '$text' ) ) {
return new Range( value.nextPosition );
}
}

return null;
}

/**
* Removes attributes disallowed by the schema.
*
Expand Down Expand Up @@ -1384,3 +1437,42 @@ function mapContextItem( ctxItem ) {
};
}
}

// Generator function returning values from provided walkers, switching between them at each iteration. If only one walker
// is provided it will return data only from that walker.
//
// @param {module:engine/module/treewalker~TreeWalker} [backward] Walker iterating in backward direction.
// @param {module:engine/module/treewalker~TreeWalker} [forward] Walker iterating in forward direction.
// @returns {Iterable.<Object>} Object returned at each iteration contains `value` and `walker` (informing which walker returned
// given value) fields.
function* combineWalkers( backward, forward ) {
let done = false;

while ( !done ) {
done = true;

if ( backward ) {
const step = backward.next();

if ( !step.done ) {
done = false;
yield {
walker: backward,
value: step.value
};
}
}

if ( forward ) {
const step = forward.next();

if ( !step.done ) {
done = false;
yield {
walker: forward,
value: step.value
};
}
}
}
}
2 changes: 1 addition & 1 deletion src/model/utils/insertcontent.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class Insertion {
return Range.createOn( this.nodeToSelect );
}

return this.model.document.getNearestSelectionRange( this.position );
return this.model.schema.getNearestSelectionRange( this.position );
}

/**
Expand Down
Loading

0 comments on commit d1838a4

Please sign in to comment.