|
|
@@ -11,6 +11,7 @@ import Position from './position';
|
|
|
import ContainerElement from './containerelement';
|
|
|
import AttributeElement from './attributeelement';
|
|
|
import EmptyElement from './emptyelement';
|
|
|
+import UIElement from './uielement';
|
|
|
import Element from './element';
|
|
|
import Text from './text';
|
|
|
import TextProxy from './textproxy';
|
|
|
@@ -19,7 +20,6 @@ import TreeWalker from './treewalker';
|
|
|
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
|
|
|
import DocumentFragment from './documentfragment';
|
|
|
import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';
|
|
|
-import { stringify, parse } from '../dev-utils/view';
|
|
|
|
|
|
/**
|
|
|
* Contains functions used for composing view tree.
|
|
|
@@ -72,6 +72,10 @@ export default writer;
|
|
|
* when trying to break attributes
|
|
|
* inside {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
|
|
|
*
|
|
|
+ * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-ui-element`
|
|
|
+ * when trying to break attributes
|
|
|
+ * inside {@link module:engine/view/uielement~UIElement UIElement}.
|
|
|
+ *
|
|
|
* @see module:engine/view/attributeelement~AttributeElement
|
|
|
* @see module:engine/view/containerelement~ContainerElement
|
|
|
* @see module:engine/view/writer~writer.breakContainer
|
|
|
@@ -213,6 +217,11 @@ export function mergeAttributes( position ) {
|
|
|
return position;
|
|
|
}
|
|
|
|
|
|
+ // When one or both nodes are UIElements - no attributes to merge.
|
|
|
+ if ( ( nodeBefore instanceof UIElement ) || ( nodeAfter instanceof UIElement ) ) {
|
|
|
+ return position;
|
|
|
+ }
|
|
|
+
|
|
|
// When position is between two text nodes.
|
|
|
if ( nodeBefore instanceof Text && nodeAfter instanceof Text ) {
|
|
|
return mergeTextNodes( nodeBefore, nodeAfter );
|
|
|
@@ -313,22 +322,23 @@ export function breakViewRangePerContainer( range ) {
|
|
|
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-insert-invalid-node` when nodes to insert
|
|
|
* contains instances that are not {@link module:engine/view/text~Text Texts},
|
|
|
* {@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}.
|
|
|
*
|
|
|
* @function insert
|
|
|
* @param {module:engine/view/position~Position} position Insertion position.
|
|
|
* @param {module:engine/view/text~Text|module:engine/view/attributeelement~AttributeElement|
|
|
|
- * module:engine/view/containerelement~ContainerElement|module:engine/view/emptyelement~EmptyElement
|
|
|
- * |Iterable.<module:engine/view/text~Text|module:engine/view/attributeelement~AttributeElement|
|
|
|
- * module:engine/view/containerelement~ContainerElement|module:engine/view/emptyelement~EmptyElement>} nodes Node or
|
|
|
- * nodes to insert.
|
|
|
+ * module:engine/view/containerelement~ContainerElement|module:engine/view/emptyelement~EmptyElement|
|
|
|
+ * module:engine/view/uielement~UIElement|Iterable.<module:engine/view/text~Text|
|
|
|
+ * module:engine/view/attributeelement~AttributeElement|module:engine/view/containerelement~ContainerElement|
|
|
|
+ * module:engine/view/emptyelement~EmptyElement|module:engine/view/uielement~UIElement>} nodes Node or nodes to insert.
|
|
|
* @returns {module:engine/view/range~Range} Range around inserted nodes.
|
|
|
*/
|
|
|
export function insert( position, nodes ) {
|
|
|
nodes = isIterable( nodes ) ? [ ...nodes ] : [ nodes ];
|
|
|
|
|
|
- // Check if nodes to insert are instances of AttributeElements, ContainerElements, EmptyElements or Text.
|
|
|
+ // Check if nodes to insert are instances of AttributeElements, ContainerElements, EmptyElements, UIElements or Text.
|
|
|
validateNodesToInsert( nodes );
|
|
|
|
|
|
const container = getParentContainer( position );
|
|
|
@@ -439,7 +449,7 @@ export function clear( range, element ) {
|
|
|
} );
|
|
|
|
|
|
// If it is then create a range inside this element.
|
|
|
- if ( parentElement && parentElement) {
|
|
|
+ if ( parentElement && parentElement ) {
|
|
|
rangeToRemove = Range.createIn( parentElement );
|
|
|
}
|
|
|
}
|
|
|
@@ -760,8 +770,11 @@ function _breakAttributesRange( range, forceSplitText = false ) {
|
|
|
// Function used by public breakAttributes (without splitting text nodes) and by other methods (with
|
|
|
// splitting text nodes).
|
|
|
//
|
|
|
-// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-empty-element` break position is placed
|
|
|
-// inside {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
|
|
|
+// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-empty-element` when break position
|
|
|
+// is placed inside {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
|
|
|
+//
|
|
|
+// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-ui-element` when break position
|
|
|
+// is placed inside {@link module:engine/view/uielement~UIElement UIElement}.
|
|
|
//
|
|
|
// @param {module:engine/view/position~Position} position Position where to break attributes.
|
|
|
// @param {Boolean} [forceSplitText = false] If set to `true`, will break text nodes even if they are directly in
|
|
|
@@ -782,6 +795,16 @@ function _breakAttributes( position, forceSplitText = false ) {
|
|
|
throw new CKEditorError( 'view-writer-cannot-break-empty-element' );
|
|
|
}
|
|
|
|
|
|
+ // If position is placed inside UIElement - throw an exception as we cannot break inside.
|
|
|
+ if ( position.parent instanceof UIElement ) {
|
|
|
+ /**
|
|
|
+ * Cannot break inside UIElement instance.
|
|
|
+ *
|
|
|
+ * @error view-writer-cannot-break-ui-element
|
|
|
+ */
|
|
|
+ throw new CKEditorError( 'view-writer-cannot-break-ui-element' );
|
|
|
+ }
|
|
|
+
|
|
|
// There are no attributes to break and text nodes breaking is not forced.
|
|
|
if ( !forceSplitText && positionParent instanceof Text && isContainerOrFragment( positionParent.parent ) ) {
|
|
|
return Position.createFromPosition( position );
|
|
|
@@ -925,9 +948,10 @@ function wrapChildren( parent, startOffset, endOffset, attribute ) {
|
|
|
const isText = child instanceof Text;
|
|
|
const isAttribute = child instanceof AttributeElement;
|
|
|
const isEmpty = child instanceof EmptyElement;
|
|
|
+ const isUI = child instanceof UIElement;
|
|
|
|
|
|
- // Wrap text, empty elements or attributes with higher or equal priority.
|
|
|
- if ( isText || isEmpty || ( isAttribute && attribute.priority <= child.priority ) ) {
|
|
|
+ // Wrap text, empty elements, ui elements or attributes with higher or equal priority.
|
|
|
+ if ( isText || isEmpty || isUI || ( isAttribute && attribute.priority <= child.priority ) ) {
|
|
|
// Clone attribute.
|
|
|
const newAttribute = attribute.clone();
|
|
|
|
|
|
@@ -1172,12 +1196,14 @@ function rangeSpansOnAllChildren( range ) {
|
|
|
|
|
|
// Checks if provided nodes are valid to insert. Checks if each node is an instance of
|
|
|
// {@link module:engine/view/text~Text Text} or {@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}.
|
|
|
//
|
|
|
// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-insert-invalid-node` when nodes to insert
|
|
|
// contains instances that are not {@link module:engine/view/text~Text Texts},
|
|
|
// {@link module:engine/view/emptyelement~EmptyElement EmptyElements},
|
|
|
+// {@link module:engine/view/uielement~UIElement UIElements},
|
|
|
// {@link module:engine/view/attributeelement~AttributeElement AttributeElements} or
|
|
|
// {@link module:engine/view/containerelement~ContainerElement ContainerElements}.
|
|
|
//
|
|
|
@@ -1185,10 +1211,15 @@ function rangeSpansOnAllChildren( range ) {
|
|
|
// |module:engine/view/containerelement~ContainerElement> nodes
|
|
|
function validateNodesToInsert( nodes ) {
|
|
|
for ( let node of nodes ) {
|
|
|
- if ( !( node instanceof Text || node instanceof AttributeElement || node instanceof ContainerElement || node instanceof EmptyElement ) ) {
|
|
|
+ if (
|
|
|
+ !( node instanceof Text || node instanceof AttributeElement || node instanceof ContainerElement ||
|
|
|
+ node instanceof EmptyElement || node instanceof UIElement )
|
|
|
+ ) {
|
|
|
/**
|
|
|
* Inserted nodes should be instance of {@link module:engine/view/attributeelement~AttributeElement AttributeElement},
|
|
|
- * {@link module:engine/view/containerelement~ContainerElement ContainerElement} or {@link module:engine/view/text~Text Text}.
|
|
|
+ * {@link module:engine/view/containerelement~ContainerElement ContainerElement},
|
|
|
+ * {@link module:engine/view/emptyerelement~EmptyElement EmptyElement},
|
|
|
+ * {@link module:engine/view/uierelement~UIElement UIElement}, {@link module:engine/view/text~Text Text}.
|
|
|
*
|
|
|
* @error view-writer-insert-invalid-node
|
|
|
*/
|