UNPKG

ckeditor5-image-upload-base64

Version:

The development environment of CKEditor 5 – the best browser-based rich text editor.

221 lines (195 loc) 6.19 kB
/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /** * @module engine/model/nodelist */ import Node from './node'; import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; /** * Provides an interface to operate on a list of {@link module:engine/model/node~Node nodes}. `NodeList` is used internally * in classes like {@link module:engine/model/element~Element Element} * or {@link module:engine/model/documentfragment~DocumentFragment DocumentFragment}. */ export default class NodeList { /** * Creates an empty node list. * * @protected * @param {Iterable.<module:engine/model/node~Node>} nodes Nodes contained in this node list. */ constructor( nodes ) { /** * Nodes contained in this node list. * * @private * @member {Array.<module:engine/model/node~Node>} */ this._nodes = []; if ( nodes ) { this._insertNodes( 0, nodes ); } } /** * Iterable interface. * * Iterates over all nodes contained inside this node list. * * @returns {Iterable.<module:engine/model/node~Node>} */ [ Symbol.iterator ]() { return this._nodes[ Symbol.iterator ](); } /** * Number of nodes contained inside this node list. * * @readonly * @type {Number} */ get length() { return this._nodes.length; } /** * Sum of {@link module:engine/model/node~Node#offsetSize offset sizes} of all nodes contained inside this node list. * * @readonly * @type {Number} */ get maxOffset() { return this._nodes.reduce( ( sum, node ) => sum + node.offsetSize, 0 ); } /** * Gets the node at the given index. Returns `null` if incorrect index was passed. * * @param {Number} index Index of node. * @returns {module:engine/model/node~Node|null} Node at given index. */ getNode( index ) { return this._nodes[ index ] || null; } /** * Returns an index of the given node. Returns `null` if given node is not inside this node list. * * @param {module:engine/model/node~Node} node Child node to look for. * @returns {Number|null} Child node's index. */ getNodeIndex( node ) { const index = this._nodes.indexOf( node ); return index == -1 ? null : index; } /** * Returns the starting offset of given node. Starting offset is equal to the sum of * {@link module:engine/model/node~Node#offsetSize offset sizes} of all nodes that are before this node in this node list. * * @param {module:engine/model/node~Node} node Node to look for. * @returns {Number|null} Node's starting offset. */ getNodeStartOffset( node ) { const index = this.getNodeIndex( node ); return index === null ? null : this._nodes.slice( 0, index ).reduce( ( sum, node ) => sum + node.offsetSize, 0 ); } /** * Converts index to offset in node list. * * Returns starting offset of a node that is at given index. Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} * `model-nodelist-index-out-of-bounds` if given index is less than `0` or more than {@link #length}. * * @param {Number} index Node's index. * @returns {Number} Node's starting offset. */ indexToOffset( index ) { if ( index == this._nodes.length ) { return this.maxOffset; } const node = this._nodes[ index ]; if ( !node ) { /** * Given index cannot be found in the node list. * * @error nodelist-index-out-of-bounds */ throw new CKEditorError( 'model-nodelist-index-out-of-bounds: Given index cannot be found in the node list.', this ); } return this.getNodeStartOffset( node ); } /** * Converts offset in node list to index. * * Returns index of a node that occupies given offset. Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} * `model-nodelist-offset-out-of-bounds` if given offset is less than `0` or more than {@link #maxOffset}. * * @param {Number} offset Offset to look for. * @returns {Number} Index of a node that occupies given offset. */ offsetToIndex( offset ) { let totalOffset = 0; for ( const node of this._nodes ) { if ( offset >= totalOffset && offset < totalOffset + node.offsetSize ) { return this.getNodeIndex( node ); } totalOffset += node.offsetSize; } if ( totalOffset != offset ) { /** * Given offset cannot be found in the node list. * * @error model-nodelist-offset-out-of-bounds * @param {Number} offset * @param {module:engine/model/nodelist~NodeList} nodeList Stringified node list. */ throw new CKEditorError( 'model-nodelist-offset-out-of-bounds: Given offset cannot be found in the node list.', this, { offset, nodeList: this } ); } return this.length; } /** * Inserts given nodes at given index. * * @protected * @param {Number} index Index at which nodes should be inserted. * @param {Iterable.<module:engine/model/node~Node>} nodes Nodes to be inserted. */ _insertNodes( index, nodes ) { // Validation. for ( const node of nodes ) { if ( !( node instanceof Node ) ) { /** * Trying to insert an object which is not a Node instance. * * @error nodelist-insertNodes-not-node */ throw new CKEditorError( 'model-nodelist-insertNodes-not-node: Trying to insert an object which is not a Node instance.', this ); } } this._nodes.splice( index, 0, ...nodes ); } /** * Removes one or more nodes starting at the given index. * * @protected * @param {Number} indexStart Index of the first node to remove. * @param {Number} [howMany=1] Number of nodes to remove. * @returns {Array.<module:engine/model/node~Node>} Array containing removed nodes. */ _removeNodes( indexStart, howMany = 1 ) { return this._nodes.splice( indexStart, howMany ); } /** * Converts `NodeList` instance to an array containing nodes that were inserted in the node list. Nodes * are also converted to their plain object representation. * * @returns {Array.<module:engine/model/node~Node>} `NodeList` instance converted to `Array`. */ toJSON() { return this._nodes.map( node => node.toJSON() ); } }