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 #798 from ckeditor/t/788
Browse files Browse the repository at this point in the history
Feature: Introduced `view.UIElement` and `view.writer.clear()` method. Closes #788.

BREAKING CHANGES: Removed `view.DocumentFragment#getAncestors()`. Closes #803. Closes #805.
BREAKING CHANGES: `Position.getAncestors()` should return elements in the same order as `Node.getAncestors()`.
  • Loading branch information
Reinmar authored Feb 8, 2017
2 parents 3a15236 + e6dc605 commit 64be1f6
Show file tree
Hide file tree
Showing 25 changed files with 711 additions and 59 deletions.
47 changes: 32 additions & 15 deletions src/dev-utils/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Position from '../view/position';
import AttributeElement from '../view/attributeelement';
import ContainerElement from '../view/containerelement';
import EmptyElement from '../view/emptyelement';
import UIElement from '../view/uielement';
import ViewText from '../view/text';

const ELEMENT_RANGE_START_TOKEN = '[';
Expand All @@ -31,6 +32,7 @@ const allowedTypes = {
'container': ContainerElement,
'attribute': AttributeElement,
'empty': EmptyElement,
'ui': UIElement
};

/**
Expand Down Expand Up @@ -168,14 +170,17 @@ setData._parse = parse;
* If `options.showType` is set to `true`, element's types will be
* presented for {@link module:engine/view/attributeelement~AttributeElement AttributeElements},
* {@link module:engine/view/containerelement~ContainerElement ContainerElements}
* and {@link module:engine/view/emptyelement~EmptyElement EmptyElements}:
* {@link module:engine/view/emptyelement~EmptyElement EmptyElements}
* and {@link module:engine/view/uielement~UIElement UIElements}:
*
* const attribute = new AttributeElement( 'b' );
* const container = new ContainerElement( 'p' );
* const empty = new EmptyElement( 'img' );
* const ui = new UIElement( 'span' );
* getData( attribute, null, { showType: true } ); // '<attribute:b></attribute:b>'
* getData( container, null, { showType: true } ); // '<container:p></container:p>'
* getData( empty, null, { showType: true } ); // '<empty:img></empty:img>'
* getData( ui, null, { showType: true } ); // '<ui:span></ui:span>'
*
* If `options.showPriority` is set to `true`, priority will be displayed for all
* {@link module:engine/view/attributeelement~AttributeElement AttributeElements}.
Expand Down Expand Up @@ -432,8 +437,7 @@ class RangeParser {

brackets.push( {
bracket: bracket,
textOffset: index - offset,
outer: index === 0 || index == node._data.length - 1
textOffset: index - offset
} );

offset++;
Expand All @@ -453,7 +457,7 @@ class RangeParser {
// Non-empty text node.
if ( text ) {
if (
( this.sameSelectionCharacters && !item.outer ) ||
this.sameSelectionCharacters ||
( !this.sameSelectionCharacters && ( item.bracket == TEXT_RANGE_START_TOKEN || item.bracket == TEXT_RANGE_END_TOKEN ) )
) {
// Store information about text range delimiter.
Expand Down Expand Up @@ -765,10 +769,13 @@ class ViewStringify {

/**
* Converts passed {@link module:engine/view/element~Element Element's} type to its string representation
* Returns 'attribute' for {@link module:engine/view/attributeelement~AttributeElement AttributeElements},
* 'container' for {@link module:engine/view/containerelement~ContainerElement ContainerElements} and 'empty' for
* {@link module:engine/view/emptyelement~EmptyElement EmptyElements}. Returns empty string when current configuration is preventing
* showing elements' types.
*
* Returns:
* * 'attribute' for {@link module:engine/view/attributeelement~AttributeElement AttributeElements},
* * 'container' for {@link module:engine/view/containerelement~ContainerElement ContainerElements},
* * 'empty' for {@link module:engine/view/emptyelement~EmptyElement EmptyElements}.
* * 'ui' for {@link module:engine/view/uielement~UIElement UIElements}.
* * empty string when current configuration is preventing showing elements' types.
*
* @private
* @param {module:engine/view/element~Element} element
Expand Down Expand Up @@ -826,8 +833,9 @@ class ViewStringify {

// Converts {@link module:engine/view/element~Element Elements} to
// {@link module:engine/view/attributeelement~AttributeElement AttributeElements},
// {@link module:engine/view/containerelement~ContainerElement ContainerElements} or
// {@link module:engine/view/emptyelement~EmptyElement EmptyElements}.
// {@link module:engine/view/containerelement~ContainerElement ContainerElements},
// {@link module:engine/view/emptyelement~EmptyElement EmptyElements} or
// {@link module:engine/view/uielement~UIElement UIElements}.
// It converts whole tree starting from the `rootNode`. Conversion is based on element names.
// See `_convertElement` method for more details.
//
Expand All @@ -848,6 +856,10 @@ function _convertViewElements( rootNode ) {
throw new Error( `Parse error - cannot parse inside EmptyElement.` );
}

if ( convertedElement instanceof UIElement ) {
throw new Error( `Parse error - cannot parse inside UIElement.` );
}

convertedElement.appendChildren( _convertViewElements( child ) );
}

Expand All @@ -859,19 +871,23 @@ function _convertViewElements( rootNode ) {

// Converts {@link module:engine/view/element~Element Element} to
// {@link module:engine/view/attributeelement~AttributeElement AttributeElement},
// {@link module:engine/view/containerelement~ContainerElement ContainerElement} or
// {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
// {@link module:engine/view/containerelement~ContainerElement ContainerElement},
// {@link module:engine/view/emptyelement~EmptyElement EmptyElement} or
// {@link module:engine/view/uielement~UIElement UIElement}.
// If element's name is in format `attribute:b` with `view-priority="11"` attribute it will be converted to
// {@link module:engine/view/attributeelement~AttributeElement AttributeElement} with priority 11.
// If element's name is in format `container:p` - it will be converted to
// {@link module:engine/view/containerelement~ContainerElement ContainerElement}.
// If element's name is in format `empty:img` - it will be converted to
// {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
// If element's name is in format `ui:span` - it will be converted to
// {@link module:engine/view/uielement~UIElement UIElement}.
// If element's name will not contain any additional information - {@link module:engine/view/element~Element view Element} will be
// returned.
//
// @param {module:engine/view/element~Element} viewElement View element to convert.
// @returns {module:engine/view/element~Element|module:engine/view/attributeelement~AttributeElement|
// module:engine/view/emptyelement~EmptyElement|module:engine/view/uielement~UIElement|
// module:engine/view/containerelement~ContainerElement} Tree view
// element converted according to it's name.
function _convertElement( viewElement ) {
Expand All @@ -895,10 +911,11 @@ function _convertElement( viewElement ) {

// Converts `view-priority` attribute and {@link module:engine/view/element~Element#name Element's name} information needed for creating
// {@link module:engine/view/attributeelement~AttributeElement AttributeElement},
// {@link module:engine/view/containerelement~ContainerElement ContainerElement} or
// {@link module:engine/view/emptyelement~EmptyElement EmptyElement} instance.
// {@link module:engine/view/containerelement~ContainerElement ContainerElement},
// {@link module:engine/view/emptyelement~EmptyElement EmptyElement} or,
// {@link module:engine/view/uielement~UIElement UIElement}.
// Name can be provided in two formats: as a simple element's name (`div`), or as a type and name (`container:div`,
// `attribute:span`, `empty:img`);
// `attribute:span`, `empty:img`, `ui:span`);
//
// @param {module:engine/view/element~Element} element Element which name should be converted.
// @returns {Object} info Object with parsed information.
Expand Down
6 changes: 5 additions & 1 deletion src/model/position.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,11 @@ export default class Position {
* @returns {Array.<module:engine/model/item~Item>} Array with ancestors.
*/
getAncestors() {
return this.parent.getAncestors( { includeNode: true, parentFirst: true } );
if ( this.parent instanceof DocumentFragment ) {
return [ this.parent ];
} else {
return this.parent.getAncestors( { includeNode: true } );
}
}

/**
Expand Down
9 changes: 0 additions & 9 deletions src/view/documentfragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,6 @@ export default class DocumentFragment {
return null;
}

/**
* Returns ancestor elements of `DocumentFragment`, which is an empty array. Added for compatibility reasons.
*
* @returns {Array}
*/
getAncestors() {
return [];
}

/**
* {@link module:engine/view/documentfragment~DocumentFragment#insertChildren Insert} a child node or a list of child nodes at the end
* and sets the parent of these nodes to this fragment.
Expand Down
20 changes: 13 additions & 7 deletions src/view/position.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import Text from './text';
import TextProxy from './textproxy';
import DocumentFragment from './documentfragment';

import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
Expand All @@ -21,14 +22,15 @@ export default class Position {
/**
* Creates a position.
*
* @param {module:engine/view/node~Node} parent Position parent node.
* @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} parent Position parent.
* @param {Number} offset Position offset.
*/
constructor( parent, offset ) {
/**
* Position parent node.
* Position parent.
*
* @member {module:engine/view/node~Node} module:engine/view/position~Position#parent
* @member {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
* module:engine/view/position~Position#parent
*/
this.parent = parent;

Expand Down Expand Up @@ -143,7 +145,11 @@ export default class Position {
* @returns {Array} Array with ancestors.
*/
getAncestors() {
return this.parent.getAncestors( { includeNode: true, parentFirst: true } );
if ( this.parent instanceof DocumentFragment ) {
return [ this.parent ];
} else {
return this.parent.getAncestors( { includeNode: true } );
}
}

/**
Expand All @@ -153,7 +159,7 @@ export default class Position {
* @returns {Boolean} True if positions are same.
*/
isEqual( otherPosition ) {
return this == otherPosition || ( this.parent == otherPosition.parent && this.offset == otherPosition.offset );
return ( this.parent == otherPosition.parent && this.offset == otherPosition.offset );
}

/**
Expand Down Expand Up @@ -202,8 +208,8 @@ export default class Position {
}

// Get path from root to position's parent element.
const path = this.parent.getAncestors( { includeNode: true } );
const otherPath = otherPosition.parent.getAncestors( { includeNode: true } );
const path = this.getAncestors();
const otherPath = otherPosition.getAncestors();

// Compare both path arrays to find common ancestor.
const result = compareArrays( path, otherPath );
Expand Down
62 changes: 62 additions & 0 deletions src/view/uielement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module engine/view/uielement
*/

import Element from './element';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
import Node from './node';

/**
* UIElement class. It is used to represent UI not a content of the document.
* This element can't be split and selection can't be placed inside this element.
*/
export default class UIElement extends Element {
/**
* Creates new instance of UIElement.
*
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-uielement-cannot-add` when third parameter is passed,
* to inform that usage of UIElement is incorrect (adding child nodes to UIElement is forbidden).
*
* @param {String} name Node name.
* @param {Object|Iterable} [attributes] Collection of attributes.
*/
constructor( name, attributes, children ) {
super( name, attributes, children );

/**
* Returns `null` because filler is not needed for UIElements.
*
* @method #getFillerOffset
* @returns {null} Always returns null.
*/
this.getFillerOffset = getFillerOffset;
}

/**
* Overrides {@link module:engine/view/element~Element#insertChildren} method.
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-uielement-cannot-add` to prevent adding any child nodes
* to UIElement.
*/
insertChildren( index, nodes ) {
if ( nodes && ( nodes instanceof Node || Array.from( nodes ).length > 0 ) ) {
/**
* Cannot add children to {@link module:engine/view/uielement~UIElement}.
*
* @error view-uielement-cannot-add
*/
throw new CKEditorError( 'view-uielement-cannot-add: Cannot add child nodes to UIElement instance.' );
}
}
}

// Returns `null` because block filler is not needed for UIElements.
//
// @returns {null}
function getFillerOffset() {
return null;
}
Loading

0 comments on commit 64be1f6

Please sign in to comment.