Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ckeditor/ckeditor5-engine

Package Overview
Dependencies
Maintainers
1
Versions
704
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-engine - npm Package Compare versions

Comparing version 37.0.0-alpha.3 to 37.0.0-rc.0

src/model/operation/rootoperation.d.ts

46

package.json
{
"name": "@ckeditor/ckeditor5-engine",
"version": "37.0.0-alpha.3",
"version": "37.0.0-rc.0",
"description": "The editing engine of CKEditor 5 – the best browser-based rich text editor.",

@@ -26,26 +26,26 @@ "keywords": [

"dependencies": {
"@ckeditor/ckeditor5-utils": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-utils": "^37.0.0-rc.0",
"lodash-es": "^4.17.15"
},
"devDependencies": {
"@ckeditor/ckeditor5-basic-styles": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-block-quote": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-clipboard": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-cloud-services": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-core": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-editor-classic": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-enter": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-essentials": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-heading": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-image": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-link": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-list": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-mention": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-paragraph": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-table": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-theme-lark": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-typing": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-ui": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-undo": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-widget": "^37.0.0-alpha.3",
"@ckeditor/ckeditor5-basic-styles": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-block-quote": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-clipboard": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-cloud-services": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-core": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-editor-classic": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-enter": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-essentials": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-heading": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-image": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-link": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-list": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-mention": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-paragraph": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-table": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-theme-lark": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-typing": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-ui": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-undo": "^37.0.0-rc.0",
"@ckeditor/ckeditor5-widget": "^37.0.0-rc.0",
"typescript": "^4.8.4",

@@ -56,3 +56,3 @@ "webpack": "^5.58.1",

"engines": {
"node": ">=14.0.0",
"node": ">=16.0.0",
"npm": ">=5.7.1"

@@ -59,0 +59,0 @@ },

@@ -93,2 +93,5 @@ /**

*
* A warning is logged when you try to retrieve data for a detached root, as most probably this is a mistake. A detached root should
* be treated like it is removed, and you should not save its data. Note, that the detached root data is always an empty string.
*
* @fires get

@@ -95,0 +98,0 @@ * @param options Additional configuration for the retrieved data. `DataController` provides two optional

@@ -20,2 +20,3 @@ /**

import HtmlDataProcessor from '../dataprocessor/htmldataprocessor';
import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
/**

@@ -91,2 +92,5 @@ * Controller for the data pipeline. The data pipeline controls how data is retrieved from the document

*
* A warning is logged when you try to retrieve data for a detached root, as most probably this is a mistake. A detached root should
* be treated like it is removed, and you should not save its data. Note, that the detached root data is always an empty string.
*
* @fires get

@@ -121,2 +125,13 @@ * @param options Additional configuration for the retrieved data. `DataController` provides two optional

const root = this.model.document.getRoot(rootName);
if (!root.isAttached()) {
/**
* Retrieving document data for a detached root.
*
* This usually indicates an error as a detached root should be considered "removed" and should not be included in the
* document data.
*
* @error datacontroller-get-detached-root
*/
logWarning('datacontroller-get-detached-root', this);
}
if (trim === 'empty' && !this.model.hasContent(root, { ignoreWhitespaces: true })) {

@@ -393,3 +408,3 @@ return '';

for (const rootName of rootNames) {
if (!this.model.document.getRootNames().includes(rootName)) {
if (!this.model.document.getRoot(rootName)) {
return false;

@@ -396,0 +411,0 @@ }

@@ -27,4 +27,6 @@ /**

export { default as OperationFactory } from './model/operation/operationfactory';
export type { default as AttributeOperation } from './model/operation/attributeoperation';
export type { default as RenameOperation } from './model/operation/renameoperation';
export { default as AttributeOperation } from './model/operation/attributeoperation';
export { default as RenameOperation } from './model/operation/renameoperation';
export { default as RootAttributeOperation } from './model/operation/rootattributeoperation';
export { default as RootOperation } from './model/operation/rootoperation';
export { transformSets } from './model/operation/transform';

@@ -31,0 +33,0 @@ export { default as DocumentSelection, type DocumentSelectionChangeRangeEvent } from './model/documentselection';

@@ -21,2 +21,6 @@ /**

export { default as OperationFactory } from './model/operation/operationfactory';
export { default as AttributeOperation } from './model/operation/attributeoperation';
export { default as RenameOperation } from './model/operation/renameoperation';
export { default as RootAttributeOperation } from './model/operation/rootattributeoperation';
export { default as RootOperation } from './model/operation/rootoperation';
export { transformSets } from './model/operation/transform';

@@ -23,0 +27,0 @@ // Model.

@@ -49,2 +49,8 @@ /**

/**
* A map that stores all roots that have been changed.
*
* The keys are the names of the roots while value represents the changes.
*/
private readonly _changedRoots;
/**
* Stores the number of changes that were processed. Used to order the changes chronologically. It is important

@@ -87,5 +93,2 @@ * when changes are sorted.

*
* Operation type is checked and it is checked which nodes it will affect. These nodes are then stored in `Differ`
* in the state before the operation is executed.
*
* @param operationToBuffer An operation to buffer.

@@ -137,2 +140,3 @@ */

* * attribute changes,
* * a root is added or detached,
* * changes of markers which were defined as `affectsData`,

@@ -164,2 +168,8 @@ * * changes of markers' `affectsData` property.

/**
* Returns all roots that have changed (either were attached, or detached, or their attributes changed).
*
* @returns Diff between the old and the new roots state.
*/
getChangedRoots(): Array<DiffItemRoot>;
/**
* Returns a set of model items that were marked to get refreshed.

@@ -173,2 +183,10 @@ */

/**
* Buffers the root state change after the root was attached or detached
*/
private _bufferRootStateChange;
/**
* Buffers a root attribute change.
*/
private _bufferRootAttributeChange;
/**
* Marks the given `item` in differ to be "refreshed". It means that the item will be marked as removed and inserted

@@ -261,3 +279,3 @@ * in the differ changes set, so it will be effectively re-converted when the differ changes are handled by a dispatcher.

/**
* The single diff item for inserted nodes.
* A single diff item for inserted nodes.
*/

@@ -282,3 +300,3 @@ export interface DiffItemInsert {

/**
* The length of an inserted text node. For elements it is always 1 as each inserted element is counted as a one.
* The length of an inserted text node. For elements, it is always 1 as each inserted element is counted as a one.
*/

@@ -288,3 +306,3 @@ length: number;

/**
* The single diff item for removed nodes.
* A single diff item for removed nodes.
*/

@@ -309,3 +327,3 @@ export interface DiffItemRemove {

/**
* The length of a removed text node. For elements it is always 1 as each removed element is counted as a one.
* The length of a removed text node. For elements, it is always 1, as each removed element is counted as a one.
*/

@@ -315,3 +333,3 @@ length: number;

/**
* The single diff item for attribute change.
* A single diff item for attribute change.
*/

@@ -340,1 +358,27 @@ export interface DiffItemAttribute {

}
/**
* A single diff item for a changed root.
*/
export interface DiffItemRoot {
/**
* Name of the changed root.
*/
name: string;
/**
* Set accordingly if the root got attached or detached. Otherwise, not set.
*/
state?: 'attached' | 'detached';
/**
* Keeps all attribute changes that happened on the root.
*
* The keys are keys of the changed attributes. The values are objects containing the attribute value before the change
* (`oldValue`) and after the change (`newValue`).
*
* Note, that if the root state changed (`state` is set), then `attributes` property will not be set. All attributes should be
* handled together with the root being attached or detached.
*/
attributes?: Record<string, {
oldValue: unknown;
newValue: unknown;
}>;
}

@@ -48,2 +48,8 @@ /**

/**
* A map that stores all roots that have been changed.
*
* The keys are the names of the roots while value represents the changes.
*/
this._changedRoots = new Map();
/**
* Stores the number of changes that were processed. Used to order the changes chronologically. It is important

@@ -79,3 +85,3 @@ * when changes are sorted.

get isEmpty() {
return this._changesInElement.size == 0 && this._changedMarkers.size == 0;
return this._changesInElement.size == 0 && this._changedMarkers.size == 0 && this._changedRoots.size == 0;
}

@@ -85,5 +91,2 @@ /**

*
* Operation type is checked and it is checked which nodes it will affect. These nodes are then stored in `Differ`
* in the state before the operation is executed.
*
* @param operationToBuffer An operation to buffer.

@@ -180,2 +183,14 @@ */

}
case 'detachRoot':
case 'addRoot': {
this._bufferRootStateChange(operation.rootName, operation.isAdd);
break;
}
case 'addRootAttribute':
case 'removeRootAttribute':
case 'changeRootAttribute': {
const rootName = operation.root.rootName;
this._bufferRootAttributeChange(rootName, operation.key, operation.oldValue, operation.newValue);
break;
}
}

@@ -256,2 +271,3 @@ // Clear cache after each buffered operation as it is no longer valid.

* * attribute changes,
* * a root is added or detached,
* * changes of markers which were defined as `affectsData`,

@@ -264,2 +280,5 @@ * * changes of markers' `affectsData` property.

}
if (this._changedRoots.size > 0) {
return true;
}
for (const { newMarkerData, oldMarkerData } of this._changedMarkers.values()) {

@@ -439,2 +458,23 @@ if (newMarkerData.affectsData !== oldMarkerData.affectsData) {

/**
* Returns all roots that have changed (either were attached, or detached, or their attributes changed).
*
* @returns Diff between the old and the new roots state.
*/
getChangedRoots() {
return Array.from(this._changedRoots.values()).map(diffItem => {
const entry = { ...diffItem };
if (entry.state !== undefined) {
// The root was attached or detached -- do not return its attributes changes.
// If the root was attached, it should be handled as a whole, together with its attributes, the same way as model nodes.
// If the root was detached, its attributes should be discarded anyway.
//
// Keep in mind that filtering must happen on this stage (when retrieving changes). If filtering happens on-the-fly as
// the attributes change, it may lead to incorrect situation, e.g.: detach root, change attribute, re-attach root.
// In this case, attribute change cannot be filtered. After the root is re-attached, the attribute change must be kept.
delete entry.attributes;
}
return entry;
});
}
/**
* Returns a set of model items that were marked to get refreshed.

@@ -452,2 +492,3 @@ */

this._changedMarkers.clear();
this._changedRoots.clear();
this._refreshedItems = new Set();

@@ -457,2 +498,61 @@ this._cachedChanges = null;

/**
* Buffers the root state change after the root was attached or detached
*/
_bufferRootStateChange(rootName, isAttached) {
if (!this._changedRoots.has(rootName)) {
this._changedRoots.set(rootName, { name: rootName, state: isAttached ? 'attached' : 'detached' });
return;
}
const diffItem = this._changedRoots.get(rootName);
if (diffItem.state !== undefined) {
// Root `state` can only toggle between of the values ('attached' or 'detached') and no value. It cannot be any other way,
// because if the root was originally attached it can only become detached. Then, if it is re-attached in the same batch of
// changes, it gets back to "no change" (which means no value). Same if the root was originally detached.
delete diffItem.state;
if (diffItem.attributes === undefined) {
// If there is no `state` change and no `attributes` change, remove the entry.
this._changedRoots.delete(rootName);
}
}
else {
diffItem.state = isAttached ? 'attached' : 'detached';
}
}
/**
* Buffers a root attribute change.
*/
_bufferRootAttributeChange(rootName, key, oldValue, newValue) {
const diffItem = this._changedRoots.get(rootName) || { name: rootName };
const attrs = diffItem.attributes || {};
if (attrs[key]) {
// If this attribute or metadata was already changed earlier and is changed again, check to what value it is changed.
const attrEntry = attrs[key];
if (newValue === attrEntry.oldValue) {
// If it was changed back to the old value, remove the entry.
delete attrs[key];
}
else {
// If it was changed to a different value, update the entry.
attrEntry.newValue = newValue;
}
}
else {
// If this attribute or metadata was not set earlier, add an entry.
attrs[key] = { oldValue, newValue };
}
if (Object.entries(attrs).length === 0) {
// If attributes or metadata changes set became empty, remove it from the diff item.
delete diffItem.attributes;
if (diffItem.state === undefined) {
// If there is no `state` change and no `attributes` change, remove the entry.
this._changedRoots.delete(rootName);
}
}
else {
// Make sure that, if a new object in the structure was created, it gets set.
diffItem.attributes = attrs;
this._changedRoots.set(rootName, diffItem);
}
}
/**
* Marks the given `item` in differ to be "refreshed". It means that the item will be marked as removed and inserted

@@ -459,0 +559,0 @@ * in the differ changes set, so it will be effectively re-converted when the differ changes are handled by a dispatcher.

@@ -51,4 +51,4 @@ /**

/**
* A list of roots that are owned and managed by this document. Use {@link #createRoot} and
* {@link #getRoot} to manipulate it.
* A list of roots that are owned and managed by this document. Use {@link #createRoot}, {@link #getRoot} and
* {@link #getRootNames} to manipulate it.
*/

@@ -65,3 +65,3 @@ readonly roots: Collection<RootElement>;

/**
* A boolean indicates whether the selection has changed until
* A flag that indicates whether the selection has changed since last change block.
*/

@@ -92,4 +92,7 @@ private _hasSelectionChangedFromTheLastChangeBlock;

*
* **Note:** do not use this method after the editor has been initialized! If you want to dynamically add a root, use
* {@link module:engine/model/writer~Writer#addRoot `model.Writer#addRoot`} instead.
*
* @param elementName The element name. Defaults to `'$root'` which also has some basic schema defined
* (`$block`s are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
* (e.g. `$block` elements are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
* @param rootName A unique root name.

@@ -106,3 +109,6 @@ * @returns The created root.

*
* @param name A unique root name.
* Detached roots are returned by this method. This is to be able to operate on the detached root (for example, to be able to create
* a position inside such a root for undo feature purposes).
*
* @param name The root name of the root to return.
* @returns The root registered under a given name or `null` when there is no root with the given name.

@@ -112,7 +118,11 @@ */

/**
* Returns an array with names of all roots (without the {@link #graveyard}) added to the document.
* Returns an array with names of all roots added to the document (except the {@link #graveyard graveyard root}).
*
* Detached roots **are not** returned by this method by default. This is to make sure that all features or algorithms that operate
* on the document data know which roots are still a part of the document and should be processed.
*
* @param includeDetached Specified whether detached roots should be returned as well.
* @returns Roots names.
*/
getRootNames(): Array<string>;
getRootNames(includeDetached?: boolean): Array<string>;
/**

@@ -119,0 +129,0 @@ * Used to register a post-fixer callback. A post-fixer mechanism guarantees that the features

@@ -82,2 +82,29 @@ /**

});
// This is a solution for a problem that may occur during real-time editing. If one client detached a root and another added
// something there at the same moment, the OT does not solve this problem currently. In such situation, the added elements would
// stay in the detached root.
//
// This is incorrect, a detached root should be empty and all elements from it should be removed. To solve this, the post-fixer will
// remove any element that is left in a detached root.
//
// Similarly, markers that are created at the beginning or at the end of the detached root will not be removed as well.
//
// The drawback of this solution over the OT solution is that the elements removed by the post-fixer will never be brought back.
// If the root detachment gets undone (and the root is brought back), the removed elements will not be there.
this.registerPostFixer(writer => {
let result = false;
for (const root of this.roots) {
if (!root.isAttached() && !root.isEmpty) {
writer.remove(writer.createRangeIn(root));
result = true;
}
}
for (const marker of this.model.markers) {
if (!marker.getRange().root.isAttached()) {
writer.removeMarker(marker);
result = true;
}
}
return result;
});
}

@@ -108,4 +135,7 @@ /**

*
* **Note:** do not use this method after the editor has been initialized! If you want to dynamically add a root, use
* {@link module:engine/model/writer~Writer#addRoot `model.Writer#addRoot`} instead.
*
* @param elementName The element name. Defaults to `'$root'` which also has some basic schema defined
* (`$block`s are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
* (e.g. `$block` elements are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
* @param rootName A unique root name.

@@ -137,3 +167,6 @@ * @returns The created root.

*
* @param name A unique root name.
* Detached roots are returned by this method. This is to be able to operate on the detached root (for example, to be able to create
* a position inside such a root for undo feature purposes).
*
* @param name The root name of the root to return.
* @returns The root registered under a given name or `null` when there is no root with the given name.

@@ -145,8 +178,14 @@ */

/**
* Returns an array with names of all roots (without the {@link #graveyard}) added to the document.
* Returns an array with names of all roots added to the document (except the {@link #graveyard graveyard root}).
*
* Detached roots **are not** returned by this method by default. This is to make sure that all features or algorithms that operate
* on the document data know which roots are still a part of the document and should be processed.
*
* @param includeDetached Specified whether detached roots should be returned as well.
* @returns Roots names.
*/
getRootNames() {
return Array.from(this.roots, root => root.rootName).filter(name => name != graveyardName);
getRootNames(includeDetached = false) {
return Array.from(this.roots)
.filter(root => root.rootName != graveyardName && (includeDetached || root.isAttached()))
.map(root => root.rootName);
}

@@ -153,0 +192,0 @@ /**

@@ -86,2 +86,6 @@ /**

/**
* Returns `false` as `DocumentFragment` by definition is not attached to a document. Added for compatibility reasons.
*/
isAttached(): false;
/**
* Returns empty array. Added for compatibility reasons.

@@ -88,0 +92,0 @@ */

@@ -104,2 +104,8 @@ /**

/**
* Returns `false` as `DocumentFragment` by definition is not attached to a document. Added for compatibility reasons.
*/
isAttached() {
return false;
}
/**
* Returns empty array. Added for compatibility reasons.

@@ -106,0 +112,0 @@ */

@@ -69,3 +69,3 @@ /**

/**
* Index of this node in it's parent or `null` if the node has no parent.
* Index of this node in its parent or `null` if the node has no parent.
*

@@ -77,4 +77,4 @@ * Accessing this property throws an error if this node's parent element does not contain it.

/**
* Offset at which this node starts in it's parent. It is equal to the sum of {@link #offsetSize offsetSize}
* of all it's previous siblings. Equals to `null` if node has no parent.
* Offset at which this node starts in its parent. It is equal to the sum of {@link #offsetSize offsetSize}
* of all its previous siblings. Equals to `null` if node has no parent.
*

@@ -112,3 +112,3 @@ * Accessing this property throws an error if this node's parent element does not contain it.

/**
* Returns true if the node is in a tree rooted in the document (is a descendant of one of its roots).
* Returns `true` if the node is inside a document root that is attached to the document.
*/

@@ -115,0 +115,0 @@ isAttached(): boolean;

@@ -66,3 +66,3 @@ /**

/**
* Index of this node in it's parent or `null` if the node has no parent.
* Index of this node in its parent or `null` if the node has no parent.
*

@@ -83,4 +83,4 @@ * Accessing this property throws an error if this node's parent element does not contain it.

/**
* Offset at which this node starts in it's parent. It is equal to the sum of {@link #offsetSize offsetSize}
* of all it's previous siblings. Equals to `null` if node has no parent.
* Offset at which this node starts in its parent. It is equal to the sum of {@link #offsetSize offsetSize}
* of all its previous siblings. Equals to `null` if node has no parent.
*

@@ -147,6 +147,10 @@ * Accessing this property throws an error if this node's parent element does not contain it.

/**
* Returns true if the node is in a tree rooted in the document (is a descendant of one of its roots).
* Returns `true` if the node is inside a document root that is attached to the document.
*/
isAttached() {
return this.root.is('rootElement');
// If the node has no parent it means that it is a root.
// But this is not a `RootElement`, so it means that it is not attached.
//
// If this is not the root, check if this element's root is attached.
return this.parent === null ? false : this.root.isAttached();
}

@@ -153,0 +157,0 @@ /**

@@ -92,3 +92,3 @@ /**

/**
* Creates `AttributeOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `AttributeOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -95,0 +95,0 @@ * @param json Deserialized JSON object.

@@ -134,3 +134,3 @@ /**

/**
* Creates `AttributeOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `AttributeOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -137,0 +137,0 @@ * @param json Deserialized JSON object.

@@ -79,3 +79,3 @@ /**

/**
* Creates `InsertOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `InsertOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -82,0 +82,0 @@ * @param json Deserialized JSON object.

@@ -108,3 +108,3 @@ /**

/**
* Creates `InsertOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `InsertOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -111,0 +111,0 @@ * @param json Deserialized JSON object.

@@ -89,3 +89,3 @@ /**

/**
* Creates `MergeOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `MergeOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -92,0 +92,0 @@ * @param json Deserialized JSON object.

@@ -144,3 +144,3 @@ /**

/**
* Creates `MergeOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `MergeOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -147,0 +147,0 @@ * @param json Deserialized JSON object.

@@ -85,3 +85,3 @@ /**

/**
* Creates `MoveOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `MoveOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -88,0 +88,0 @@ * @param json Deserialized JSON object.

@@ -145,3 +145,3 @@ /**

/**
* Creates `MoveOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `MoveOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -148,0 +148,0 @@ * @param json Deserialized JSON object.

@@ -83,3 +83,3 @@ /**

/**
* Creates Operation object from deserilized object, i.e. from parsed JSON string.
* Creates `Operation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -86,0 +86,0 @@ * @param json Deserialized JSON object.

@@ -54,3 +54,3 @@ /**

/**
* Creates Operation object from deserilized object, i.e. from parsed JSON string.
* Creates `Operation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -57,0 +57,0 @@ * @param json Deserialized JSON object.

@@ -16,2 +16,3 @@ /**

import RootAttributeOperation from './rootattributeoperation';
import RootOperation from './rootoperation';
import SplitOperation from './splitoperation';

@@ -28,2 +29,3 @@ import MergeOperation from './mergeoperation';

operations[RootAttributeOperation.className] = RootAttributeOperation;
operations[RootOperation.className] = RootOperation;
operations[SplitOperation.className] = SplitOperation;

@@ -30,0 +32,0 @@ operations[MergeOperation.className] = MergeOperation;

@@ -24,14 +24,10 @@ /**

* Root element to change.
*
* @readonly
*/
root: RootElement;
readonly root: RootElement;
/**
* Key of an attribute to change or remove.
*
* @readonly
*/
key: string;
readonly key: string;
/**
* Old value of the attribute with given key or `null` if adding a new attribute.
* Old value of the attribute with given key or `null`, if attribute was not set before.
*

@@ -42,3 +38,3 @@ * @readonly

/**
* New value to set for the attribute. If `null`, then the operation just removes the attribute.
* New value of the attribute with given key or `null`, if operation should remove attribute.
*

@@ -54,4 +50,4 @@ * @readonly

* @param key Key of an attribute to change or remove.
* @param oldValue Old value of the attribute with given key or `null` if adding a new attribute.
* @param newValue New value to set for the attribute. If `null`, then the operation just removes the attribute.
* @param oldValue Old value of the attribute with given key or `null`, if attribute was not set before.
* @param newValue New value of the attribute with given key or `null`, if operation should remove attribute.
* @param baseVersion Document {@link module:engine/model/document~Document#version} on which operation

@@ -94,3 +90,3 @@ * can be applied or `null` if the operation operates on detached (non-document) tree.

/**
* Creates RootAttributeOperation object from deserilized object, i.e. from parsed JSON string.
* Creates `RootAttributeOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -97,0 +93,0 @@ * @param json Deserialized JSON object.

@@ -27,4 +27,4 @@ /**

* @param key Key of an attribute to change or remove.
* @param oldValue Old value of the attribute with given key or `null` if adding a new attribute.
* @param newValue New value to set for the attribute. If `null`, then the operation just removes the attribute.
* @param oldValue Old value of the attribute with given key or `null`, if attribute was not set before.
* @param newValue New value of the attribute with given key or `null`, if operation should remove attribute.
* @param baseVersion Document {@link module:engine/model/document~Document#version} on which operation

@@ -37,4 +37,4 @@ * can be applied or `null` if the operation operates on detached (non-document) tree.

this.key = key;
this.oldValue = oldValue;
this.newValue = newValue;
this.oldValue = oldValue === undefined ? null : oldValue;
this.newValue = newValue === undefined ? null : newValue;
}

@@ -87,3 +87,3 @@ /**

/**
* The attribute which should be removed does not exists for the given node.
* The attribute which should be removed does not exist for the given node.
*

@@ -135,3 +135,3 @@ * @error rootattribute-operation-wrong-old-value

/**
* Creates RootAttributeOperation object from deserilized object, i.e. from parsed JSON string.
* Creates `RootAttributeOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -138,0 +138,0 @@ * @param json Deserialized JSON object.

@@ -98,3 +98,3 @@ /**

/**
* Creates `SplitOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `SplitOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -101,0 +101,0 @@ * @param json Deserialized JSON object.

@@ -169,3 +169,3 @@ /**

/**
* Creates `SplitOperation` object from deserilized object, i.e. from parsed JSON string.
* Creates `SplitOperation` object from deserialized object, i.e. from parsed JSON string.
*

@@ -172,0 +172,0 @@ * @param json Deserialized JSON object.

@@ -23,2 +23,6 @@ /**

/**
* @internal
*/
_isAttached: boolean;
/**
* Creates root element.

@@ -36,4 +40,14 @@ *

/**
* Converts `RootElement` instance to `string` containing it's name.
* Informs if the root element is currently attached to the document, or not.
*
* A detached root is equivalent to being removed and cannot contain any children or markers.
*
* By default, a newly added root is attached. It can be detached using
* {@link module:engine/model/writer~Writer#detachRoot `Writer#detachRoot`}. A detached root can be re-attached again using
* {@link module:engine/model/writer~Writer#addRoot `Writer#addRoot`}.
*/
isAttached(): boolean;
/**
* Converts `RootElement` instance to `string` containing its name.
*
* @returns `RootElement` instance converted to `string`.

@@ -40,0 +54,0 @@ */

@@ -22,2 +22,6 @@ /**

super(name);
/**
* @internal
*/
this._isAttached = true;
this._document = document;

@@ -33,4 +37,16 @@ this.rootName = rootName;

/**
* Converts `RootElement` instance to `string` containing it's name.
* Informs if the root element is currently attached to the document, or not.
*
* A detached root is equivalent to being removed and cannot contain any children or markers.
*
* By default, a newly added root is attached. It can be detached using
* {@link module:engine/model/writer~Writer#detachRoot `Writer#detachRoot`}. A detached root can be re-attached again using
* {@link module:engine/model/writer~Writer#addRoot `Writer#addRoot`}.
*/
isAttached() {
return this._isAttached;
}
/**
* Converts `RootElement` instance to `string` containing its name.
*
* @returns `RootElement` instance converted to `string`.

@@ -37,0 +53,0 @@ */

@@ -9,2 +9,3 @@ /**

import Range from './range';
import RootElement from './rootelement';
import Text from './text';

@@ -370,3 +371,3 @@ import type { Marker } from './markercollection';

*
* These parameters works the same way as {@link #createPositionAt `writer.createPositionAt()`}.
* These parameters work the same way as {@link #createPositionAt `writer.createPositionAt()`}.
*

@@ -629,2 +630,29 @@ * Note that items can be moved only within the same tree. It means that you can move items within the same root

/**
* Adds a new root to the document (or re-attaches a {@link #detachRoot detached root}).
*
* Throws an error, if trying to add a root that is already added and attached.
*
* @param rootName Name of the added root.
* @param elementName The element name. Defaults to `'$root'` which also has some basic schema defined
* (e.g. `$block` elements are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
* @returns The added root element.
*/
addRoot(rootName: string, elementName?: string): RootElement;
/**
* Detaches the root from the document.
*
* All content and markers are removed from the root upon detaching. New content and new markers cannot be added to the root, as long
* as it is detached.
*
* A root cannot be fully removed from the document, it can be only detached. A root is permanently removed only after you
* re-initialize the editor and do not specify the root in the initial data.
*
* A detached root can be re-attached using {@link #addRoot}.
*
* Throws an error if the root does not exist or the root is already detached.
*
* @param rootOrName Name of the detached root.
*/
detachRoot(rootOrName: string | RootElement): void;
/**
* Sets the document's selection (ranges and direction) to the specified location based on the given

@@ -631,0 +659,0 @@ * {@link module:engine/model/selection~Selectable selectable} or creates an empty selection if no arguments were passed.

@@ -16,2 +16,3 @@ /**

import RootAttributeOperation from './operation/rootattributeoperation';
import RootOperation from './operation/rootoperation';
import SplitOperation from './operation/splitoperation';

@@ -365,3 +366,3 @@ import DocumentFragment from './documentfragment';

*
* These parameters works the same way as {@link #createPositionAt `writer.createPositionAt()`}.
* These parameters work the same way as {@link #createPositionAt `writer.createPositionAt()`}.
*

@@ -922,2 +923,74 @@ * Note that items can be moved only within the same tree. It means that you can move items within the same root

}
/**
* Adds a new root to the document (or re-attaches a {@link #detachRoot detached root}).
*
* Throws an error, if trying to add a root that is already added and attached.
*
* @param rootName Name of the added root.
* @param elementName The element name. Defaults to `'$root'` which also has some basic schema defined
* (e.g. `$block` elements are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
* @returns The added root element.
*/
addRoot(rootName, elementName = '$root') {
this._assertWriterUsedCorrectly();
const root = this.model.document.getRoot(rootName);
if (root && root.isAttached()) {
/**
* Root with provided name already exists and is attached.
*
* @error writer-addroot-root-exists
*/
throw new CKEditorError('writer-addroot-root-exists', this);
}
const document = this.model.document;
const operation = new RootOperation(rootName, elementName, true, document, document.version);
this.batch.addOperation(operation);
this.model.applyOperation(operation);
return this.model.document.getRoot(rootName);
}
/**
* Detaches the root from the document.
*
* All content and markers are removed from the root upon detaching. New content and new markers cannot be added to the root, as long
* as it is detached.
*
* A root cannot be fully removed from the document, it can be only detached. A root is permanently removed only after you
* re-initialize the editor and do not specify the root in the initial data.
*
* A detached root can be re-attached using {@link #addRoot}.
*
* Throws an error if the root does not exist or the root is already detached.
*
* @param rootOrName Name of the detached root.
*/
detachRoot(rootOrName) {
this._assertWriterUsedCorrectly();
const root = typeof rootOrName == 'string' ? this.model.document.getRoot(rootOrName) : rootOrName;
if (!root || !root.isAttached()) {
/**
* Root with provided name does not exist or is already detached.
*
* @error writer-detachroot-no-root
*/
throw new CKEditorError('writer-detachroot-no-root', this);
}
// First, remove all markers from the root. It is better to do it before removing stuff for undo purposes.
// However, looking through all the markers may not be the best performance wise. But there's no better solution for now.
for (const marker of this.model.markers) {
if (marker.getRange().root === root) {
this.removeMarker(marker);
}
}
// Remove all attributes from the root.
for (const key of root.getAttributeKeys()) {
this.removeAttribute(key, root);
}
// Remove all contents of the root.
this.remove(this.createRangeIn(root));
// Finally, detach the root.
const document = this.model.document;
const operation = new RootOperation(root.rootName, root.name, false, document, document.version);
this.batch.addOperation(operation);
this.model.applyOperation(operation);
}
setSelection(...args) {

@@ -924,0 +997,0 @@ this._assertWriterUsedCorrectly();

@@ -450,3 +450,3 @@ /**

*
* Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* Refer to the {@glink updating/guides/update-to-29##update-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* and {@link module:engine/view/matcher~MatcherPattern} documentation.

@@ -482,3 +482,3 @@ *

*
* Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* Refer to the {@glink updating/guides/update-to-29##update-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* and the {@link module:engine/view/matcher~MatcherPattern} documentation.

@@ -485,0 +485,0 @@ *

@@ -471,3 +471,3 @@ /**

*
* Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* Refer to the {@glink updating/guides/update-to-29##update-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* and {@link module:engine/view/matcher~MatcherPattern} documentation.

@@ -503,3 +503,3 @@ *

*
* Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* Refer to the {@glink updating/guides/update-to-29##update-to-ckeditor-5-v2910 Migration to v29.1.0} guide
* and the {@link module:engine/view/matcher~MatcherPattern} documentation.

@@ -506,0 +506,0 @@ *

@@ -26,2 +26,6 @@ /**

observe(): void;
/**
* @inheritDoc
*/
stopObserving(): void;
}

@@ -28,0 +32,0 @@ /**

@@ -36,2 +36,6 @@ /**

observe() { }
/**
* @inheritDoc
*/
stopObserving() { }
}

@@ -60,2 +60,6 @@ /**

/**
* @inheritDoc
*/
stopObserving(domElement: HTMLElement): void;
/**
* Calls `Document#fire()` if observer {@link #isEnabled is enabled}.

@@ -62,0 +66,0 @@ *

@@ -60,2 +60,8 @@ /**

/**
* @inheritDoc
*/
stopObserving(domElement) {
this.stopListening(domElement);
}
/**
* Calls `Document#fire()` if observer {@link #isEnabled is enabled}.

@@ -62,0 +68,0 @@ *

@@ -33,2 +33,6 @@ /**

*/
stopObserving(): void;
/**
* @inheritDoc
*/
destroy(): void;

@@ -35,0 +39,0 @@ /**

@@ -51,2 +51,6 @@ /**

*/
stopObserving() { }
/**
* @inheritDoc
*/
destroy() {

@@ -53,0 +57,0 @@ super.destroy();

@@ -59,2 +59,6 @@ /**

*/
stopObserving(domElement: HTMLElement): void;
/**
* @inheritDoc
*/
enable(): void;

@@ -61,0 +65,0 @@ /**

@@ -36,3 +36,3 @@ /**

this.renderer = view._renderer;
this._domElements = [];
this._domElements = new Set();
this._mutationObserver = new window.MutationObserver(this._onMutations.bind(this));

@@ -50,3 +50,3 @@ }

observe(domElement) {
this._domElements.push(domElement);
this._domElements.add(domElement);
if (this.isEnabled) {

@@ -59,2 +59,16 @@ this._mutationObserver.observe(domElement, this._config);

*/
stopObserving(domElement) {
this._domElements.delete(domElement);
if (this.isEnabled) {
// Unfortunately, it is not possible to stop observing particular DOM element.
// In order to stop observing one of multiple DOM elements, we need to re-connect the mutation observer.
this._mutationObserver.disconnect();
for (const domElement of this._domElements) {
this._mutationObserver.observe(domElement, this._config);
}
}
}
/**
* @inheritDoc
*/
enable() {

@@ -61,0 +75,0 @@ super.enable();

@@ -74,7 +74,12 @@ /**

/**
* Starts observing the given root element.
* Starts observing given DOM element.
*
* @param name The name of the root element.
* @param domElement DOM element to observe.
* @param name The name of the related root element.
*/
abstract observe(domElement: HTMLElement, name: string): void;
/**
* Stops observing given DOM element.
*/
abstract stopObserving(domElement: HTMLElement): void;
}

@@ -81,0 +86,0 @@ /**

@@ -80,2 +80,6 @@ /**

*/
stopObserving(domElement: HTMLElement): void;
/**
* @inheritDoc
*/
destroy(): void;

@@ -82,0 +86,0 @@ private _reportInfiniteLoop;

@@ -107,2 +107,8 @@ /**

*/
stopObserving(domElement) {
this.stopListening(domElement);
}
/**
* @inheritDoc
*/
destroy() {

@@ -109,0 +115,0 @@ super.destroy();

@@ -27,2 +27,6 @@ /**

observe(): void;
/**
* @inheritDoc
*/
stopObserving(): void;
}

@@ -29,0 +33,0 @@ /**

@@ -38,2 +38,6 @@ /**

observe() { }
/**
* @inheritDoc
*/
stopObserving() { }
}

@@ -59,6 +59,6 @@ /**

const doc = element.document;
if (!documentPlaceholders.has(doc)) {
return;
}
view.change(writer => {
if (!documentPlaceholders.has(doc)) {
return;
}
const placeholders = documentPlaceholders.get(doc);

@@ -65,0 +65,0 @@ const config = placeholders.get(element);

@@ -211,7 +211,7 @@ /**

* @param expectedDom Expected DOM children.
* @param options Options
* @param options.replaceText Mark text nodes replacement.
* @returns Actions array modified with the `replace` actions.
* @param comparator A comparator function that should return `true` if the given node should be reused
* (either by the update of a text node data or an element children list for similar elements).
* @returns Actions array modified with the `update` actions.
*/
private _findReplaceActions;
private _findUpdateActions;
/**

@@ -218,0 +218,0 @@ * Marks text nodes to be synchronized.

@@ -264,7 +264,7 @@ /**

const diff = this._diffNodeLists(actualDomChildren, expectedDomChildren);
const actions = this._findReplaceActions(diff, actualDomChildren, expectedDomChildren);
if (actions.indexOf('replace') !== -1) {
const actions = this._findUpdateActions(diff, actualDomChildren, expectedDomChildren, areSimilarElements);
if (actions.indexOf('update') !== -1) {
const counter = { equal: 0, insert: 0, delete: 0 };
for (const action of actions) {
if (action === 'replace') {
if (action === 'update') {
const insertIndex = counter.equal + counter.insert;

@@ -516,13 +516,5 @@ const deleteIndex = counter.equal + counter.delete;

const diff = this._diffNodeLists(actualDomChildren, expectedDomChildren);
// The rendering is not disabled on Android in the composition mode.
// Composition events are not cancellable and browser will modify the DOM tree.
// On Android composition events are immediately applied to the model, so we don't need to skip rendering,
// and we should not do it because the difference between view and DOM could lead to position mapping problems.
// Since the composition is fragile and often breaks if the composed text node is replaced while composing
// we need to make sure that we update the existing text node and not replace it with another one.
// We don't want to change the behavior on other browsers for safety, but maybe one day cause it seems to make sense.
// https://github.com/ckeditor/ckeditor5/issues/12455.
const actions = env.isAndroid ?
this._findReplaceActions(diff, actualDomChildren, expectedDomChildren, { replaceText: true }) :
diff;
// We need to make sure that we update the existing text node and not replace it with another one.
// The composition and different "language" browser extensions are fragile to text node being completely replaced.
const actions = this._findUpdateActions(diff, actualDomChildren, expectedDomChildren, areTextNodes);
let i = 0;

@@ -546,3 +538,3 @@ const nodesToUnbind = new Set();

}
else if (action === 'equal' || action === 'replace') {
else if (action === 'equal' || action === 'update') {
i++;

@@ -563,3 +555,3 @@ }

// Update the existing text node data. Note that replace action is generated only for Android for now.
else if (action === 'replace') {
else if (action === 'update') {
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {

@@ -620,7 +612,7 @@ // @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update text node',

* @param expectedDom Expected DOM children.
* @param options Options
* @param options.replaceText Mark text nodes replacement.
* @returns Actions array modified with the `replace` actions.
* @param comparator A comparator function that should return `true` if the given node should be reused
* (either by the update of a text node data or an element children list for similar elements).
* @returns Actions array modified with the `update` actions.
*/
_findReplaceActions(actions, actualDom, expectedDom, options = {}) {
_findUpdateActions(actions, actualDom, expectedDom, comparator) {
// If there is no both 'insert' and 'delete' actions, no need to check for replaced elements.

@@ -642,4 +634,4 @@ if (actions.indexOf('insert') === -1 || actions.indexOf('delete') === -1) {

else { // equal
newActions = newActions.concat(diff(actualSlice, expectedSlice, options.replaceText ? areTextNodes : areSimilar)
.map(x => x === 'equal' ? 'replace' : x));
newActions = newActions.concat(diff(actualSlice, expectedSlice, comparator)
.map(action => action === 'equal' ? 'update' : action));
newActions.push('equal');

@@ -652,4 +644,4 @@ // Reset stored elements on 'equal'.

}
return newActions.concat(diff(actualSlice, expectedSlice, options.replaceText ? areTextNodes : areSimilar)
.map(x => x === 'equal' ? 'replace' : x));
return newActions.concat(diff(actualSlice, expectedSlice, comparator)
.map(action => action === 'equal' ? 'update' : action));
}

@@ -889,3 +881,3 @@ /**

*/
function areSimilar(node1, node2) {
function areSimilarElements(node1, node2) {
return isNode(node1) && isNode(node2) &&

@@ -892,0 +884,0 @@ !isText(node1) && !isText(node2) &&

@@ -218,2 +218,5 @@ /**

this.domConverter.unbindDomElement(domRoot);
for (const observer of this._observers.values()) {
observer.stopObserving(domRoot);
}
}

@@ -220,0 +223,0 @@ /**

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc