| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- /**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- 'use strict';
- CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ], ( Position, PositionIterator, utils ) => {
- /**
- * Range class. Range is iterable.
- *
- * @class document.Range
- */
- class Range {
- /**
- * Creates a range.
- *
- * @param {document.Position} start Start position.
- * @param {document.Position} end End position.
- * @constructor
- */
- constructor( start, end ) {
- /**
- * Start position.
- *
- * @property {document.Position}
- */
- this.start = start;
- /**
- * End position.
- *
- * @property {document.Position}
- */
- this.end = end;
- }
- /**
- * Creates a range inside an element which starts before the first child and ends after the last child.
- *
- * @param {document.Element} element Element which is a parent for the range.
- * @returns {document.Range} Created range.
- */
- static createFromElement( element ) {
- return Range.createFromParentsAndOffsets( element, 0, element, element.getChildCount() );
- }
- /**
- * Creates a range from given parents and offsets.
- *
- * @param {document.Element} startElement Start position parent element.
- * @param {Number} startOffset Start position offset.
- * @param {document.Element} endElement End position parent element.
- * @param {Number} endOffset End position offset.
- * @returns {document.Range} Created range.
- */
- static createFromParentsAndOffsets( startElement, startOffset, endElement, endOffset ) {
- return new Range(
- Position.createFromParentAndOffset( startElement, startOffset ),
- Position.createFromParentAndOffset( endElement, endOffset )
- );
- }
- /**
- * Two ranges equal if their start and end positions equal.
- *
- * @param {document.Range} otherRange Range to compare with.
- * @returns {Boolean} True if ranges equal.
- */
- isEqual( otherRange ) {
- return this.start.isEqual( otherRange.start ) && this.end.isEqual( otherRange.end );
- }
- /**
- * Creates and returns a new instance of {@link document.Range}
- * that is equal to this {@link document.Range range}.
- *
- * @returns {document.Position} Cloned {@link document.Range range}.
- */
- clone() {
- return new Range( this.start.clone(), this.end.clone() );
- }
- /**
- * Checks whether this {document.Range range} contains given (document.Position position}.
- *
- * @param {document.Position} position Position to check.
- * @returns {boolean} True if given {document.Position position} is contained.
- */
- containsPosition( position ) {
- return position.isAfter( this.start ) && position.isBefore( this.end );
- }
- /**
- * Returns an array containing one or two {document.Range ranges} that are results of transforming this
- * {document.Range range} by inserting `howMany` nodes at `insertPosition`. Two {document.Range ranges} are
- * returned if the insertion was inside this {document.Range range}.
- *
- * Examples:
- * let range = new Range( new Position( [ 2, 7 ], root ), new Position( [ 4, 0, 1 ], root ) );
- * let transformed = range.getTransformedByInsertion( new Position( [ 1 ], root ), 2 );
- * // transformed array has one range from [ 4, 7 ] to [ 6, 0, 1 ]
- *
- * transformed = range.getTransformedByInsertion( new Position( [ 3, 2 ], root ), 4 );
- * // transformed array has two ranges: from [ 2, 7 ] to [ 3, 2 ] and from [ 3, 6 ] to [ 4, 0, 1 ]
- *
- * transformed = range.getTransformedByInsertion( new Position( [ 3, 2 ], root ), 4, true );
- * // transformed array has one range which is equal to `range`. This is because of spreadOnlyOnSameLevel flag.
- *
- * @param {document.Position} insertPosition Position where nodes are inserted.
- * @param {Number} howMany How many nodes are inserted.
- * @param {Boolean} spreadOnlyOnSameLevel Flag indicating whether this {document.Range range} should be spread
- * if insertion was inside a node from this {document.Range range} but not in the range itself.
- * @returns {Array.<document.Range>} Result of the transformation.
- */
- getTransformedByInsertion( insertPosition, howMany, spreadOnlyOnSameLevel ) {
- // Flag indicating whether this whole range and given insertPosition is on the same tree level.
- const areOnSameLevel = utils.compareArrays( this.start.getParentPath(), this.end.getParentPath() ) == utils.compareArrays.SAME &&
- utils.compareArrays( this.start.getParentPath(), insertPosition.getParentPath() ) == utils.compareArrays.SAME;
- if ( this.containsPosition( insertPosition ) && ( !spreadOnlyOnSameLevel || areOnSameLevel ) ) {
- // Range has to be spread. The first part is from original start to the spread point.
- // The other part is from spread point to the original end, but transformed by
- // insertion to reflect insertion changes.
- return [
- new Range(
- insertPosition.getTransformedByInsertion( insertPosition, howMany, true ),
- this.end.getTransformedByInsertion( insertPosition, howMany, true )
- ),
- new Range(
- this.start.clone(),
- insertPosition.clone()
- )
- ];
- } else {
- // If insertion is not inside the range, simply transform range boundaries (positions) by the insertion.
- // Both, one or none of them might be affected by the insertion.
- const range = this.clone();
- range.start = range.start.getTransformedByInsertion( insertPosition, howMany, true );
- range.end = range.end.getTransformedByInsertion( insertPosition, howMany, false );
- return [ range ];
- }
- }
- /**
- * Gets a part of this {document.Range range} that is not a part of given {document.Range range}. Returned
- * array contains zero, one or two {document.Range ranges}.
- *
- * Examples:
- * let range = new Range( new Position( [ 2, 7 ], root ), new Position( [ 4, 0, 1 ], root ) );
- * let otherRange = new Range( new Position( [ 1 ], root ), new Position( [ 5 ], root ) );
- * let transformed = range.getDifference( otherRange );
- * // transformed array has no ranges because `otherRange` contains `range`
- *
- * otherRange = new Range( new Position( [ 1 ], root ), new Position( [ 3 ], root ) );
- * transformed = range.getDifference( otherRange );
- * // transformed array has one range: from [ 3 ] to [ 4, 0, 1 ]
- *
- * otherRange = new Range( new Position( [ 3 ], root ), new Position( [ 4 ], root ) );
- * transformed = range.getDifference( otherRange );
- * // transformed array has two ranges: from [ 2, 7 ] to [ 3 ] and from [ 4 ] to [ 4, 0, 1 ]
- *
- * @param {document.Range} otherRange Range to differentiate against.
- * @returns {Array.<document.Range>} The difference between ranges.
- */
- getDifference( otherRange ) {
- const ranges = [];
- if ( this.start.isBefore( otherRange.end ) && this.end.isAfter( otherRange.start ) ) {
- // Ranges intersect.
- if ( this.containsPosition( otherRange.start ) ) {
- // Given range start is inside this range. This means that we have to
- // add shrunken range - from the start to the middle of this range.
- ranges.push(
- new Range(
- this.start.clone(),
- otherRange.start.clone()
- )
- );
- }
- if ( this.containsPosition( otherRange.end ) ) {
- // Given range end is inside this range. This means that we have to
- // add shrunken range - from the middle of this range to the end.
- ranges.push(
- new Range(
- otherRange.end.clone(),
- this.end.clone()
- )
- );
- }
- } else {
- // Ranges do not intersect, return the original range.
- ranges.push( this.clone() );
- }
- return ranges;
- }
- /**
- * Returns an intersection of this {document.Range range} and given {document.Range range}. Intersection
- * is a common part of both of those ranges. If ranges has no common part, returns null.
- *
- * Examples:
- * let range = new Range( new Position( [ 2, 7 ], root ), new Position( [ 4, 0, 1 ], root ) );
- * let otherRange = new Range( new Position( [ 1 ], root ), new Position( [ 2 ], root ) );
- * let transformed = range.getIntersection( otherRange ); // null - ranges have no common part
- *
- * otherRange = new Range( new Position( [ 3 ], root ), new Position( [ 5 ], root ) );
- * transformed = range.getIntersection( otherRange ); // range from [ 3 ] to [ 4, 0, 1 ]
- *
- * @param {document.Range} otherRange Range to check for intersection.
- * @returns {document.Range|null} A common part of given ranges or null if ranges have no common part.
- */
- getIntersection( otherRange ) {
- if ( this.start.isBefore( otherRange.end ) && this.end.isAfter( otherRange.start ) ) {
- // Ranges intersect, so a common range will be returned.
- // At most, it will be same as this range.
- let commonRangeStart = this.start;
- let commonRangeEnd = this.end;
- if ( this.containsPosition( otherRange.start ) ) {
- // Given range start is inside this range. This means thaNt we have to
- // shrink common range to the given range start.
- commonRangeStart = otherRange.start;
- }
- if ( this.containsPosition( otherRange.end ) ) {
- // Given range end is inside this range. This means that we have to
- // shrink common range to the given range end.
- commonRangeEnd = otherRange.end;
- }
- return new Range( commonRangeStart.clone(), commonRangeEnd.clone() );
- }
- // Ranges do not intersect, so they do not have common part.
- return null;
- }
- /**
- * Creates a new range spreading from specified position to the same position moved by given offset.
- *
- * @param {document.Position} position Beginning of the range.
- * @param {Number} offset How long the range should be.
- * @returns {document.Range}
- */
- static createFromPositionAndOffset( position, offset ) {
- let endPosition = position.clone();
- endPosition.offset += offset;
- return new Range( position, endPosition );
- }
- /**
- * Range iterator.
- *
- * @see document.PositionIterator
- */
- [ Symbol.iterator ]() {
- return new PositionIterator( this );
- }
- }
- return Range;
- } );
|