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

Commit

Permalink
Add Position/Range/Selection factory methods to UpcastWriter.
Browse files Browse the repository at this point in the history
  • Loading branch information
jodator committed Oct 30, 2018
1 parent e6b1f99 commit a40fc17
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/view/downcastwriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -1012,8 +1012,8 @@ export default class DowncastWriter {
* @param {module:engine/view/element~Element} element Element which is a parent for the range.
* @returns {module:engine/view/range~Range}
*/
createRangeIn( item ) {
return Range._createIn( item );
createRangeIn( element ) {
return Range._createIn( element );
}

/**
Expand Down
144 changes: 144 additions & 0 deletions src/view/upcastwriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

import Element from './element';
import { isPlainObject } from 'lodash-es';
import Position from './position';
import Range from './range';
import Selection from './selection';

/**
* View upcast writer class. Provides set of methods used to properly manipulate nodes attached to
Expand Down Expand Up @@ -245,4 +248,145 @@ export default class UpcastWriter {
removeCustomProperty( key, element ) {
return element._removeCustomProperty( key );
}

/**
* Creates position at the given location. The location can be specified as:
*
* * a {@link module:engine/view/position~Position position},
* * parent element and offset (offset defaults to `0`),
* * parent element and `'end'` (sets position at the end of that element),
* * {@link module:engine/view/item~Item view item} and `'before'` or `'after'` (sets position before or after given view item).
*
* This method is a shortcut to other constructors such as:
*
* * {@link #createPositionBefore},
* * {@link #createPositionAfter},
*
* @param {module:engine/view/item~Item|module:engine/model/position~Position} itemOrPosition
* @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
* first parameter is a {@link module:engine/view/item~Item view item}.
*/
createPositionAt( itemOrPosition, offset ) {
return Position._createAt( itemOrPosition, offset );
}

/**
* Creates a new position after given view item.
*
* @param {module:engine/view/item~Item} item View item after which the position should be located.
* @returns {module:engine/view/position~Position}
*/
createPositionAfter( item ) {
return Position._createAfter( item );
}

/**
* Creates a new position before given view item.
*
* @param {module:engine/view/item~Item} item View item before which the position should be located.
* @returns {module:engine/view/position~Position}
*/
createPositionBefore( item ) {
return Position._createBefore( item );
}

/**
* Creates a range spanning from `start` position to `end` position.
*
* **Note:** This factory method creates it's own {@link module:engine/view/position~Position} instances basing on passed values.
*
* @param {module:engine/view/position~Position} start Start position.
* @param {module:engine/view/position~Position} [end] End position. If not set, range will be collapsed at `start` position.
* @returns {module:engine/view/range~Range}
*/
createRange( start, end ) {
return new Range( start, end );
}

/**
* Creates a range that starts before given {@link module:engine/view/item~Item view item} and ends after it.
*
* @param {module:engine/view/item~Item} item
* @returns {module:engine/view/range~Range}
*/
createRangeOn( item ) {
return Range._createOn( item );
}

/**
* Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
* that element and ends after the last child of that element.
*
* @param {module:engine/view/element~Element} element Element which is a parent for the range.
* @returns {module:engine/view/range~Range}
*/
createRangeIn( element ) {
return Range._createIn( element );
}

/**
Creates new {@link module:engine/view/selection~Selection} instance.
*
* // Creates empty selection without ranges.
* const selection = writer.createSelection();
*
* // Creates selection at the given range.
* const range = writer.createRange( start, end );
* const selection = writer.createSelection( range );
*
* // Creates selection at the given ranges
* const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
* const selection = writer.createSelection( ranges );
*
* // Creates selection from the other selection.
* const otherSelection = writer.createSelection();
* const selection = writer.createSelection( otherSelection );
*
* // Creates selection from the document selection.
* const selection = writer.createSelection( editor.editing.view.document.selection );
*
* // Creates selection at the given position.
* const position = writer.createPositionFromPath( root, path );
* const selection = writer.createSelection( position );
*
* // Creates collapsed selection at the position of given item and offset.
* const paragraph = writer.createContainerElement( 'paragraph' );
* const selection = writer.createSelection( paragraph, offset );
*
* // Creates a range inside an {@link module:engine/view/element~Element element} which starts before the
* // first child of that element and ends after the last child of that element.
* const selection = writer.createSelection( paragraph, 'in' );
*
* // Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends
* // just after the item.
* const selection = writer.createSelection( paragraph, 'on' );
*
* `Selection`'s constructor allow passing additional options (`backward`, `fake` and `label`) as the last argument.
*
* // Creates backward selection.
* const selection = writer.createSelection( range, { backward: true } );
*
* Fake selection does not render as browser native selection over selected elements and is hidden to the user.
* This way, no native selection UI artifacts are displayed to the user and selection over elements can be
* represented in other way, for example by applying proper CSS class.
*
* Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
* (and be properly handled by screen readers).
*
* // Creates fake selection with label.
* const selection = writer.createSelection( range, { fake: true, label: 'foo' } );
*
* @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection|
* module:engine/view/position~Position|Iterable.<module:engine/view/range~Range>|module:engine/view/range~Range|
* module:engine/view/item~Item|null} [selectable=null]
* @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Offset or place when selectable is an `Item`.
* @param {Object} [options]
* @param {Boolean} [options.backward] Sets this selection instance to be backward.
* @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
* @param {String} [options.label] Label for the fake selection.
* @returns {module:engine/view/selection~Selection}
*/
createSelection( selectable, placeOrOffset, options ) {
return new Selection( selectable, placeOrOffset, options );
}
}
50 changes: 50 additions & 0 deletions tests/view/upcastwriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import Element from '../../src/view/element';
import UpcastWriter from '../../src/view/upcastwriter';
import HtmlDataProcessor from '../../src/dataprocessor/htmldataprocessor';
import ViewPosition from '../../src/view/position';
import ViewRange from '../../src/view/range';
import ViewSelection from '../../src/view/selection';

describe( 'UpcastWriter', () => {
let writer, view, dataprocessor;
Expand Down Expand Up @@ -478,4 +481,51 @@ describe( 'UpcastWriter', () => {
expect( Array.from( el.getCustomProperties() ).length ).to.equal( 0 );
} );
} );

describe( 'createPositionAt()', () => {
it( 'should return instance of Position', () => {
const span = new Element( 'span' );
expect( writer.createPositionAt( span, 0 ) ).to.be.instanceof( ViewPosition );
} );
} );

describe( 'createPositionAfter()', () => {
it( 'should return instance of Position', () => {
const span = new Element( 'span', undefined, new Element( 'span' ) );
expect( writer.createPositionAfter( span.getChild( 0 ) ) ).to.be.instanceof( ViewPosition );
} );
} );

describe( 'createPositionBefore()', () => {
it( 'should return instance of Position', () => {
const span = new Element( 'span', undefined, new Element( 'span' ) );
expect( writer.createPositionBefore( span.getChild( 0 ) ) ).to.be.instanceof( ViewPosition );
} );
} );

describe( 'createRange()', () => {
it( 'should return instance of Range', () => {
expect( writer.createRange( writer.createPositionAt( new Element( 'span' ), 0 ) ) ).to.be.instanceof( ViewRange );
} );
} );

describe( 'createRangeIn()', () => {
it( 'should return instance of Range', () => {
const span = new Element( 'span', undefined, new Element( 'span' ) );
expect( writer.createRangeIn( span.getChild( 0 ) ) ).to.be.instanceof( ViewRange );
} );
} );

describe( 'createRangeOn()', () => {
it( 'should return instance of Range', () => {
const span = new Element( 'span', undefined, new Element( 'span' ) );
expect( writer.createRangeOn( span.getChild( 0 ) ) ).to.be.instanceof( ViewRange );
} );
} );

describe( 'createSelection()', () => {
it( 'should return instance of Selection', () => {
expect( writer.createSelection() ).to.be.instanceof( ViewSelection );
} );
} );
} );

0 comments on commit a40fc17

Please sign in to comment.