Socket
Socket
Sign inDemoInstall

@ckeditor/ckeditor5-engine

Package Overview
Dependencies
Maintainers
1
Versions
662
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 42.0.2 to 43.0.0-alpha.0

2

dist/dev-utils/model.d.ts

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

batchType?: BatchType;
inlineObjectElements?: Array<string>;
}): void;

@@ -126,2 +127,3 @@ export declare namespace setData {

context?: SchemaContextDefinition;
inlineObjectElements?: Array<string>;
}): ModelNode | ModelDocumentFragment | {

@@ -128,0 +130,0 @@ model: ModelNode | ModelDocumentFragment;

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

sameSelectionCharacters?: boolean;
inlineObjectElements?: Array<string>;
}): ViewNode | ViewDocumentFragment | {

@@ -322,0 +323,0 @@ view: ViewNode | ViewDocumentFragment;

4

dist/index.d.ts

@@ -57,6 +57,7 @@ /**

export type { default as RootElement } from './model/rootelement.js';
export type { default as Schema, SchemaAttributeCheckCallback, SchemaChildCheckCallback, AttributeProperties, SchemaItemDefinition } from './model/schema.js';
export type { default as Schema, SchemaAttributeCheckCallback, SchemaChildCheckCallback, AttributeProperties, SchemaItemDefinition, SchemaContext } from './model/schema.js';
export type { default as Selection, Selectable } from './model/selection.js';
export type { default as TypeCheckable } from './model/typecheckable.js';
export type { default as Writer } from './model/writer.js';
export * from './model/utils/autoparagraphing.js';
export type { DocumentChangeEvent } from './model/document.js';

@@ -107,2 +108,3 @@ export type { DocumentSelectionChangeEvent } from './model/documentselection.js';

export type { ViewDocumentInputEvent } from './view/observer/inputobserver.js';
export type { ViewDocumentMutationsEvent, MutationData } from './view/observer/mutationobserver.js';
export type { ViewDocumentKeyDownEvent, ViewDocumentKeyUpEvent, KeyEventData } from './view/observer/keyobserver.js';

@@ -109,0 +111,0 @@ export type { ViewDocumentLayoutChangedEvent } from './view/document.js';

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

private readonly _attributeProperties;
/**
* Stores additional callbacks registered for schema items, which are evaluated when {@link ~Schema#checkChild} is called.
*
* Keys are schema item names for which the callbacks are registered. Values are arrays with the callbacks.
*
* Some checks are added under {@link ~Schema#_genericCheckSymbol} key, these are evaluated for every {@link ~Schema#checkChild} call.
*/
private readonly _customChildChecks;
/**
* Stores additional callbacks registered for attribute names, which are evaluated when {@link ~Schema#checkAttribute} is called.
*
* Keys are schema attribute names for which the callbacks are registered. Values are arrays with the callbacks.
*
* Some checks are added under {@link ~Schema#_genericCheckSymbol} key, these are evaluated for every
* {@link ~Schema#checkAttribute} call.
*/
private readonly _customAttributeChecks;
private readonly _genericCheckSymbol;
private _compiledDefinitions?;

@@ -221,3 +239,3 @@ /**

/**
* Checks whether the given node (`child`) can be a child of the given context.
* Checks whether the given node can be a child of the given context.
*

@@ -230,10 +248,17 @@ * ```ts

* } );
*
* schema.checkChild( model.document.getRoot(), paragraph ); // -> true
* ```
*
* Note: When verifying whether the given node can be a child of the given context, the
* schema also verifies the entire context &ndash; from its root to its last element. Therefore, it is possible
* for `checkChild()` to return `false` even though the context's last element can contain the checked child.
* It happens if one of the context's elements does not allow its child.
* Both {@link module:engine/model/schema~Schema#addChildCheck callback checks} and declarative rules (added when
* {@link module:engine/model/schema~Schema#register registering} and {@link module:engine/model/schema~Schema#extend extending} items)
* are evaluated when this method is called.
*
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* Note that when verifying whether the given node can be a child of the given context, the schema also verifies the entire
* context &ndash; from its root to its last element. Therefore, it is possible for `checkChild()` to return `false` even though
* the `context` last element can contain the checked child. It happens if one of the `context` elements does not allow its child.
* When `context` is verified, {@link module:engine/model/schema~Schema#addChildCheck custom checks} are considered as well.
*
* @fires checkChild

@@ -245,4 +270,3 @@ * @param context The context in which the child will be checked.

/**
* Checks whether the given attribute can be applied in the given context (on the last
* item of the context).
* Checks whether the given attribute can be applied in the given context (on the last item of the context).
*

@@ -255,25 +279,20 @@ * ```ts

* } );
*
* schema.checkAttribute( textNode, 'bold' ); // -> true
* ```
*
* Both {@link module:engine/model/schema~Schema#addAttributeCheck callback checks} and declarative rules (added when
* {@link module:engine/model/schema~Schema#register registering} and {@link module:engine/model/schema~Schema#extend extending} items)
* are evaluated when this method is called.
*
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* @fires checkAttribute
* @param context The context in which the attribute will be checked.
* @param attributeName Name of attribute to check in the given context.
*/
checkAttribute(context: SchemaContextDefinition, attributeName: string): boolean;
checkMerge(position: Position): boolean;
checkMerge(baseElement: Element, elementToMerge: Element): boolean;
/**
* Checks whether the given element (`elementToMerge`) can be merged with the specified base element (`positionOrBaseElement`).
*
* In other words &ndash; whether `elementToMerge`'s children {@link #checkChild are allowed} in the `positionOrBaseElement`.
*
* This check ensures that elements merged with {@link module:engine/model/writer~Writer#merge `Writer#merge()`}
* will be valid.
*
* Instead of elements, you can pass the instance of the {@link module:engine/model/position~Position} class as the
* `positionOrBaseElement`. It means that the elements before and after the position will be checked whether they can be merged.
*
* @param positionOrBaseElement The position or base element to which the `elementToMerge` will be merged.
* @param elementToMerge The element to merge. Required if `positionOrBaseElement` is an element.
*/
checkMerge(positionOrBaseElement: Position | Element, elementToMerge: Element): boolean;
/**
* Allows registering a callback to the {@link #checkChild} method calls.

@@ -283,20 +302,35 @@ *

* by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
* For example, by using this method you can disallow elements in specific contexts.
*
* This method is a shorthand for using the {@link #event:checkChild} event. For even better control,
* you can use that event instead.
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* Example:
* For example, by using this method you can disallow elements in specific contexts:
*
* ```ts
* // Disallow heading1 directly inside a blockQuote.
* // Disallow `heading1` inside a `blockQuote` that is inside a table.
* schema.addChildCheck( ( context, childDefinition ) => {
* if ( context.endsWith( 'blockQuote' ) && childDefinition.name == 'heading1' ) {
* if ( context.endsWith( 'tableCell blockQuote' ) ) {
* return false;
* }
* }, 'heading1' );
* ```
*
* You can skip the optional `itemName` parameter to evaluate the callback for every `checkChild()` call.
*
* ```ts
* // Inside specific custom element, allow only children, which allows for a specific attribute.
* schema.addChildCheck( ( context, childDefinition ) => {
* if ( context.endsWith( 'myElement' ) ) {
* return childDefinition.allowAttributes.includes( 'myAttribute' );
* }
* } );
* ```
*
* Which translates to:
* Please note that the generic callbacks may affect the editor performance and should be avoided if possible.
*
* When one of the callbacks makes a decision (returns `true` or `false`) the processing is finished and other callbacks are not fired.
* Callbacks are fired in the order they were added, however generic callbacks are fired before callbacks added for a specified item.
*
* You can also use `checkChild` event, if you need even better control. The result from the example above could also be
* achieved with following event callback:
*
* ```ts

@@ -307,7 +341,7 @@ * schema.on( 'checkChild', ( evt, args ) => {

*
* if ( context.endsWith( 'blockQuote' ) && childDefinition && childDefinition.name == 'heading1' ) {
* if ( context.endsWith( 'myElement' ) ) {
* // Prevent next listeners from being called.
* evt.stop();
* // Set the checkChild()'s return value.
* evt.return = false;
* // Set the `checkChild()` return value.
* evt.return = childDefinition.allowAttributes.includes( 'myAttribute' );
* }

@@ -317,9 +351,15 @@ * }, { priority: 'high' } );

*
* Note that the callback checks and declarative rules checks are processed on `normal` priority.
*
* Adding callbacks this way can also negatively impact editor performance.
*
* @param callback The callback to be called. It is called with two parameters:
* {@link module:engine/model/schema~SchemaContext} (context) instance and
* {@link module:engine/model/schema~SchemaCompiledItemDefinition} (child-to-check definition).
* The callback may return `true/false` to override `checkChild()`'s return value. If it does not return
* a boolean value, the default algorithm (or other callbacks) will define `checkChild()`'s return value.
* {@link module:engine/model/schema~SchemaCompiledItemDefinition} (definition). The callback may return `true/false` to override
* `checkChild()`'s return value. If it does not return a boolean value, the default algorithm (or other callbacks) will define
* `checkChild()`'s return value.
* @param itemName Name of the schema item for which the callback is registered. If specified, the callback will be run only for
* `checkChild()` calls which `def` parameter matches the `itemName`. Otherwise, the callback will run for every `checkChild` call.
*/
addChildCheck(callback: SchemaChildCheckCallback): void;
addChildCheck(callback: SchemaChildCheckCallback, itemName?: string): void;
/**

@@ -330,14 +370,22 @@ * Allows registering a callback to the {@link #checkAttribute} method calls.

* by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
* For example, by using this method you can disallow attribute if node to which it is applied
* is contained within some other element (e.g. you want to disallow `bold` on `$text` within `heading1`).
*
* This method is a shorthand for using the {@link #event:checkAttribute} event. For even better control,
* you can use that event instead.
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* Example:
* For example, by using this method you can disallow setting attributes on nodes in specific contexts:
*
* ```ts
* // Disallow bold on $text inside heading1.
* // Disallow setting `bold` on text inside `heading1` element:
* schema.addAttributeCheck( context => {
* if ( context.endsWith( 'heading1 $text' ) ) {
* return false;
* }
* }, 'bold' );
* ```
*
* You can skip the optional `attributeName` parameter to evaluate the callback for every `checkAttribute()` call.
*
* ```ts
* // Disallow formatting attributes on text inside custom `myTitle` element:
* schema.addAttributeCheck( ( context, attributeName ) => {
* if ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {
* if ( context.endsWith( 'myTitle $text' ) && schema.getAttributeProperties( attributeName ).isFormatting ) {
* return false;

@@ -348,4 +396,10 @@ * }

*
* Which translates to:
* Please note that the generic callbacks may affect the editor performance and should be avoided if possible.
*
* When one of the callbacks makes a decision (returns `true` or `false`) the processing is finished and other callbacks are not fired.
* Callbacks are fired in the order they were added, however generic callbacks are fired before callbacks added for a specified item.
*
* You can also use {@link #event:checkAttribute} event, if you need even better control. The result from the example above could also
* be achieved with following event callback:
*
* ```ts

@@ -356,6 +410,6 @@ * schema.on( 'checkAttribute', ( evt, args ) => {

*
* if ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {
* if ( context.endsWith( 'myTitle $text' ) && schema.getAttributeProperties( attributeName ).isFormatting ) {
* // Prevent next listeners from being called.
* evt.stop();
* // Set the checkAttribute()'s return value.
* // Set the `checkAttribute()` return value.
* evt.return = false;

@@ -366,8 +420,14 @@ * }

*
* Note that the callback checks and declarative rules checks are processed on `normal` priority.
*
* Adding callbacks this way can also negatively impact editor performance.
*
* @param callback The callback to be called. It is called with two parameters:
* {@link module:engine/model/schema~SchemaContext} (context) instance and attribute name.
* The callback may return `true/false` to override `checkAttribute()`'s return value. If it does not return
* a boolean value, the default algorithm (or other callbacks) will define `checkAttribute()`'s return value.
* {@link module:engine/model/schema~SchemaContext `context`} and attribute name. The callback may return `true` or `false`, to
* override `checkAttribute()`'s return value. If it does not return a boolean value, the default algorithm (or other callbacks)
* will define `checkAttribute()`'s return value.
* @param attributeName Name of the attribute for which the callback is registered. If specified, the callback will be run only for
* `checkAttribute()` calls with matching `attributeName`. Otherwise, the callback will run for every `checkAttribute()` call.
*/
addAttributeCheck(callback: SchemaAttributeCheckCallback): void;
addAttributeCheck(callback: SchemaAttributeCheckCallback, attributeName?: string): void;
/**

@@ -516,2 +576,18 @@ * This method allows assigning additional metadata to the model attributes. For example,

/**
* Calls child check callbacks to decide whether `def` is allowed in `context`. It uses both generic and specific (defined for `def`
* item) callbacks. If neither callback makes a decision, `undefined` is returned.
*
* Note that the first callback that makes a decision "wins", i.e., if any callback returns `true` or `false`, then the processing
* is over and that result is returned.
*/
private _evaluateChildChecks;
/**
* Calls attribute check callbacks to decide whether `attributeName` can be set on the last element of `context`. It uses both
* generic and specific (defined for `attributeName`) callbacks. If neither callback makes a decision, `undefined` is returned.
*
* Note that the first callback that makes a decision "wins", i.e., if any callback returns `true` or `false`, then the processing
* is over and that result is returned.
*/
private _evaluateAttributeChecks;
/**
* Takes a flat range and an attribute name. Traverses the range recursively and deeply to find and return all ranges

@@ -895,2 +971,4 @@ * inside the given range on which the attribute can be applied.

* Inherits "allowed children" from other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -900,2 +978,4 @@ allowContentOf?: string | Array<string>;

* Inherits "allowed in" from other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -905,2 +985,4 @@ allowWhere?: string | Array<string>;

* Inherits "allowed attributes" from other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -910,2 +992,4 @@ allowAttributesOf?: string | Array<string>;

* Inherits `is*` properties of other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -915,2 +999,4 @@ inheritTypesFrom?: string | Array<string>;

* A shorthand for `allowContentOf`, `allowWhere`, `allowAttributesOf`, `inheritTypesFrom`.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -1086,2 +1172,14 @@ inheritAllFrom?: string;

/**
* Returns a new schema context that is based on this context but has the last item removed.
*
* ```ts
* const ctxParagraph = new SchemaContext( [ '$root', 'blockQuote', 'paragraph' ] );
* const ctxBlockQuote = ctxParagraph.trimLast(); // Items in `ctxBlockQuote` are: `$root` an `blockQuote`.
* const ctxRoot = ctxBlockQuote.trimLast(); // Items in `ctxRoot` are: `$root`.
* ```
*
* @returns A new reduced schema context instance.
*/
trimLast(): SchemaContext;
/**
* Gets an item on the given index.

@@ -1135,3 +1233,3 @@ */

* * By defining a {@link module:engine/model/schema~SchemaContext} instance - in this case the same instance as provided
* will be return.
* will be returned.
*

@@ -1138,0 +1236,0 @@ * Examples of context definitions passed to the {@link module:engine/model/schema~Schema#checkChild `Schema#checkChild()`}

@@ -54,2 +54,14 @@ /**

destroy(): void;
/**
* The `focus` event handler.
*/
private _handleFocus;
/**
* The `blur` event handler.
*/
private _handleBlur;
/**
* Clears timeout.
*/
private _clearTimeout;
}

@@ -56,0 +68,0 @@ /**

@@ -14,4 +14,5 @@ /**

import type DomConverter from '../domconverter.js';
import type Renderer from '../renderer.js';
import type View from '../view.js';
import type ViewNode from '../node.js';
import type { ChangeType } from '../document.js';
/**

@@ -34,6 +35,2 @@ * Mutation observer's role is to watch for any DOM changes inside the editor that weren't

/**
* Reference to the {@link module:engine/view/view~View#_renderer}.
*/
readonly renderer: Renderer;
/**
* Native mutation observer config.

@@ -93,1 +90,33 @@ */

}
/**
* Event fired on DOM mutations detected.
*
* This event is introduced by {@link module:engine/view/observer/mutationobserver~MutationObserver} and available
* by default in all editor instances (attached by {@link module:engine/view/view~View}).
*
* @eventName module:engine/view/document~Document#mutations
* @param data Event data containing detailed information about the event.
*/
export type ViewDocumentMutationsEvent = {
name: 'mutations';
args: [data: MutationsEventData];
};
/**
* The value of {@link ~ViewDocumentMutationsEvent}.
*/
export type MutationsEventData = {
mutations: Array<MutationData>;
};
/**
* A single entry in {@link ~MutationsEventData} mutations array.
*/
export type MutationData = {
/**
* Type of mutation detected.
*/
type: ChangeType;
/**
* The view node related to the detected mutation.
*/
node: ViewNode;
};

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

import MutationObserver from './mutationobserver.js';
import FocusObserver from './focusobserver.js';
import type View from '../view.js';

@@ -19,3 +20,2 @@ import type DocumentSelection from '../documentselection.js';

import type Selection from '../selection.js';
import FocusObserver from './focusobserver.js';
type DomSelection = globalThis.Selection;

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

*
* @param domEvent DOM event.
* @param domDocument DOM document.

@@ -100,0 +99,0 @@ */

@@ -221,2 +221,14 @@ /**

/**
* Checks if text needs to be updated and possibly updates it by removing and inserting only parts
* of the data from the existing text node to reduce impact on the IME composition.
*
* @param domText DOM text node to update.
* @param expectedText The expected data of a text node.
*/
private _updateTextNode;
/**
* Part of the `_updateTextNode` method extracted for easier testing.
*/
private _updateTextNodeInternal;
/**
* Marks text nodes to be synchronized.

@@ -223,0 +235,0 @@ *

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

import DowncastWriter from './downcastwriter.js';
import Renderer from './renderer.js';
import DomConverter from './domconverter.js';

@@ -103,6 +102,4 @@ import Position, { type PositionOffset } from './position.js';

* Instance of the {@link module:engine/view/renderer~Renderer renderer}.
*
* @internal
*/
readonly _renderer: Renderer;
private readonly _renderer;
/**

@@ -109,0 +106,0 @@ * A DOM root attributes cache. It saves the initial values of DOM root attributes before the DOM element

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

@@ -27,3 +27,3 @@ "keywords": [

"dependencies": {
"@ckeditor/ckeditor5-utils": "42.0.2",
"@ckeditor/ckeditor5-utils": "43.0.0-alpha.0",
"lodash-es": "4.17.21"

@@ -30,0 +30,0 @@ },

@@ -469,10 +469,3 @@ /**

}
// Wrap `$text` in paragraph and include any marker that is directly before `$text`. See #13053.
const nodeBefore = position.nodeBefore;
position = wrapInParagraph(position, writer);
if (nodeBefore && nodeBefore.is('element', '$marker')) {
// Move `$marker` to the paragraph.
writer.move(writer.createRangeOn(nodeBefore), position);
position = writer.createPositionAfter(nodeBefore);
}
}

@@ -479,0 +472,0 @@ consumable.consume(data.viewItem);

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

batchType?: BatchType;
inlineObjectElements?: Array<string>;
}): void;

@@ -122,2 +123,3 @@ export declare namespace setData {

context?: SchemaContextDefinition;
inlineObjectElements?: Array<string>;
}): ModelNode | ModelDocumentFragment | {

@@ -124,0 +126,0 @@ model: ModelNode | ModelDocumentFragment;

@@ -100,3 +100,4 @@ /**

selectionAttributes: options.selectionAttributes,
context: [modelRoot.name]
context: [modelRoot.name],
inlineObjectElements: options.inlineObjectElements
});

@@ -270,3 +271,4 @@ // Retrieve DocumentFragment and Selection from parsed model.

sameSelectionCharacters: true,
lastRangeBackward: !!options.lastRangeBackward
lastRangeBackward: !!options.lastRangeBackward,
inlineObjectElements: options.inlineObjectElements
});

@@ -273,0 +275,0 @@ // Retrieve DocumentFragment and Selection from parsed view.

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

/* globals console */
// @if CK_DEBUG_TYPING // const { debounce } = require( 'lodash-es' );
/**

@@ -75,1 +76,7 @@ * Helper function, converts a map to the 'key1="value1" key2="value1"' format.

}
// @if CK_DEBUG_TYPING // export const _debouncedLine = debounce( () => {
// @if CK_DEBUG_TYPING // console.log(
// @if CK_DEBUG_TYPING // '%c───────────────────────────────────────────────────────────────────────────────────────────────────────',
// @if CK_DEBUG_TYPING // 'font-weight: bold; color: red'
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }, 300 );

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

sameSelectionCharacters?: boolean;
inlineObjectElements?: Array<string>;
}): ViewNode | ViewDocumentFragment | {

@@ -318,0 +319,0 @@ view: ViewNode | ViewDocumentFragment;

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

});
if (options.inlineObjectElements) {
processor.domConverter.inlineObjectElements.push(...options.inlineObjectElements);
}
// Convert data to view.

@@ -369,0 +372,0 @@ let view = processor.toView(data);

@@ -53,6 +53,7 @@ /**

export type { default as RootElement } from './model/rootelement.js';
export type { default as Schema, SchemaAttributeCheckCallback, SchemaChildCheckCallback, AttributeProperties, SchemaItemDefinition } from './model/schema.js';
export type { default as Schema, SchemaAttributeCheckCallback, SchemaChildCheckCallback, AttributeProperties, SchemaItemDefinition, SchemaContext } from './model/schema.js';
export type { default as Selection, Selectable } from './model/selection.js';
export type { default as TypeCheckable } from './model/typecheckable.js';
export type { default as Writer } from './model/writer.js';
export * from './model/utils/autoparagraphing.js';
export type { DocumentChangeEvent } from './model/document.js';

@@ -103,2 +104,3 @@ export type { DocumentSelectionChangeEvent } from './model/documentselection.js';

export type { ViewDocumentInputEvent } from './view/observer/inputobserver.js';
export type { ViewDocumentMutationsEvent, MutationData } from './view/observer/mutationobserver.js';
export type { ViewDocumentKeyDownEvent, ViewDocumentKeyUpEvent, KeyEventData } from './view/observer/keyobserver.js';

@@ -105,0 +107,0 @@ export type { ViewDocumentLayoutChangedEvent } from './view/document.js';

@@ -41,2 +41,4 @@ /**

export { default as TextProxy } from './model/textproxy.js';
// Model utils.
export * from './model/utils/autoparagraphing.js';
// View.

@@ -43,0 +45,0 @@ export { default as DataTransfer } from './view/datatransfer.js';

@@ -93,7 +93,3 @@ /**

this.schema.register('$marker');
this.schema.addChildCheck((context, childDefinition) => {
if (childDefinition.name === '$marker') {
return true;
}
});
this.schema.addChildCheck(() => true, '$marker'); // Allow everywhere.
injectSelectionPostFixer(this);

@@ -100,0 +96,0 @@ // Post-fixer which takes care of adding empty paragraph elements to the empty roots.

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

private readonly _attributeProperties;
/**
* Stores additional callbacks registered for schema items, which are evaluated when {@link ~Schema#checkChild} is called.
*
* Keys are schema item names for which the callbacks are registered. Values are arrays with the callbacks.
*
* Some checks are added under {@link ~Schema#_genericCheckSymbol} key, these are evaluated for every {@link ~Schema#checkChild} call.
*/
private readonly _customChildChecks;
/**
* Stores additional callbacks registered for attribute names, which are evaluated when {@link ~Schema#checkAttribute} is called.
*
* Keys are schema attribute names for which the callbacks are registered. Values are arrays with the callbacks.
*
* Some checks are added under {@link ~Schema#_genericCheckSymbol} key, these are evaluated for every
* {@link ~Schema#checkAttribute} call.
*/
private readonly _customAttributeChecks;
private readonly _genericCheckSymbol;
private _compiledDefinitions?;

@@ -217,3 +235,3 @@ /**

/**
* Checks whether the given node (`child`) can be a child of the given context.
* Checks whether the given node can be a child of the given context.
*

@@ -226,10 +244,17 @@ * ```ts

* } );
*
* schema.checkChild( model.document.getRoot(), paragraph ); // -> true
* ```
*
* Note: When verifying whether the given node can be a child of the given context, the
* schema also verifies the entire context &ndash; from its root to its last element. Therefore, it is possible
* for `checkChild()` to return `false` even though the context's last element can contain the checked child.
* It happens if one of the context's elements does not allow its child.
* Both {@link module:engine/model/schema~Schema#addChildCheck callback checks} and declarative rules (added when
* {@link module:engine/model/schema~Schema#register registering} and {@link module:engine/model/schema~Schema#extend extending} items)
* are evaluated when this method is called.
*
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* Note that when verifying whether the given node can be a child of the given context, the schema also verifies the entire
* context &ndash; from its root to its last element. Therefore, it is possible for `checkChild()` to return `false` even though
* the `context` last element can contain the checked child. It happens if one of the `context` elements does not allow its child.
* When `context` is verified, {@link module:engine/model/schema~Schema#addChildCheck custom checks} are considered as well.
*
* @fires checkChild

@@ -241,4 +266,3 @@ * @param context The context in which the child will be checked.

/**
* Checks whether the given attribute can be applied in the given context (on the last
* item of the context).
* Checks whether the given attribute can be applied in the given context (on the last item of the context).
*

@@ -251,25 +275,20 @@ * ```ts

* } );
*
* schema.checkAttribute( textNode, 'bold' ); // -> true
* ```
*
* Both {@link module:engine/model/schema~Schema#addAttributeCheck callback checks} and declarative rules (added when
* {@link module:engine/model/schema~Schema#register registering} and {@link module:engine/model/schema~Schema#extend extending} items)
* are evaluated when this method is called.
*
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* @fires checkAttribute
* @param context The context in which the attribute will be checked.
* @param attributeName Name of attribute to check in the given context.
*/
checkAttribute(context: SchemaContextDefinition, attributeName: string): boolean;
checkMerge(position: Position): boolean;
checkMerge(baseElement: Element, elementToMerge: Element): boolean;
/**
* Checks whether the given element (`elementToMerge`) can be merged with the specified base element (`positionOrBaseElement`).
*
* In other words &ndash; whether `elementToMerge`'s children {@link #checkChild are allowed} in the `positionOrBaseElement`.
*
* This check ensures that elements merged with {@link module:engine/model/writer~Writer#merge `Writer#merge()`}
* will be valid.
*
* Instead of elements, you can pass the instance of the {@link module:engine/model/position~Position} class as the
* `positionOrBaseElement`. It means that the elements before and after the position will be checked whether they can be merged.
*
* @param positionOrBaseElement The position or base element to which the `elementToMerge` will be merged.
* @param elementToMerge The element to merge. Required if `positionOrBaseElement` is an element.
*/
checkMerge(positionOrBaseElement: Position | Element, elementToMerge: Element): boolean;
/**
* Allows registering a callback to the {@link #checkChild} method calls.

@@ -279,20 +298,35 @@ *

* by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
* For example, by using this method you can disallow elements in specific contexts.
*
* This method is a shorthand for using the {@link #event:checkChild} event. For even better control,
* you can use that event instead.
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* Example:
* For example, by using this method you can disallow elements in specific contexts:
*
* ```ts
* // Disallow heading1 directly inside a blockQuote.
* // Disallow `heading1` inside a `blockQuote` that is inside a table.
* schema.addChildCheck( ( context, childDefinition ) => {
* if ( context.endsWith( 'blockQuote' ) && childDefinition.name == 'heading1' ) {
* if ( context.endsWith( 'tableCell blockQuote' ) ) {
* return false;
* }
* }, 'heading1' );
* ```
*
* You can skip the optional `itemName` parameter to evaluate the callback for every `checkChild()` call.
*
* ```ts
* // Inside specific custom element, allow only children, which allows for a specific attribute.
* schema.addChildCheck( ( context, childDefinition ) => {
* if ( context.endsWith( 'myElement' ) ) {
* return childDefinition.allowAttributes.includes( 'myAttribute' );
* }
* } );
* ```
*
* Which translates to:
* Please note that the generic callbacks may affect the editor performance and should be avoided if possible.
*
* When one of the callbacks makes a decision (returns `true` or `false`) the processing is finished and other callbacks are not fired.
* Callbacks are fired in the order they were added, however generic callbacks are fired before callbacks added for a specified item.
*
* You can also use `checkChild` event, if you need even better control. The result from the example above could also be
* achieved with following event callback:
*
* ```ts

@@ -303,7 +337,7 @@ * schema.on( 'checkChild', ( evt, args ) => {

*
* if ( context.endsWith( 'blockQuote' ) && childDefinition && childDefinition.name == 'heading1' ) {
* if ( context.endsWith( 'myElement' ) ) {
* // Prevent next listeners from being called.
* evt.stop();
* // Set the checkChild()'s return value.
* evt.return = false;
* // Set the `checkChild()` return value.
* evt.return = childDefinition.allowAttributes.includes( 'myAttribute' );
* }

@@ -313,9 +347,15 @@ * }, { priority: 'high' } );

*
* Note that the callback checks and declarative rules checks are processed on `normal` priority.
*
* Adding callbacks this way can also negatively impact editor performance.
*
* @param callback The callback to be called. It is called with two parameters:
* {@link module:engine/model/schema~SchemaContext} (context) instance and
* {@link module:engine/model/schema~SchemaCompiledItemDefinition} (child-to-check definition).
* The callback may return `true/false` to override `checkChild()`'s return value. If it does not return
* a boolean value, the default algorithm (or other callbacks) will define `checkChild()`'s return value.
* {@link module:engine/model/schema~SchemaCompiledItemDefinition} (definition). The callback may return `true/false` to override
* `checkChild()`'s return value. If it does not return a boolean value, the default algorithm (or other callbacks) will define
* `checkChild()`'s return value.
* @param itemName Name of the schema item for which the callback is registered. If specified, the callback will be run only for
* `checkChild()` calls which `def` parameter matches the `itemName`. Otherwise, the callback will run for every `checkChild` call.
*/
addChildCheck(callback: SchemaChildCheckCallback): void;
addChildCheck(callback: SchemaChildCheckCallback, itemName?: string): void;
/**

@@ -326,14 +366,22 @@ * Allows registering a callback to the {@link #checkAttribute} method calls.

* by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
* For example, by using this method you can disallow attribute if node to which it is applied
* is contained within some other element (e.g. you want to disallow `bold` on `$text` within `heading1`).
*
* This method is a shorthand for using the {@link #event:checkAttribute} event. For even better control,
* you can use that event instead.
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
*
* Example:
* For example, by using this method you can disallow setting attributes on nodes in specific contexts:
*
* ```ts
* // Disallow bold on $text inside heading1.
* // Disallow setting `bold` on text inside `heading1` element:
* schema.addAttributeCheck( context => {
* if ( context.endsWith( 'heading1 $text' ) ) {
* return false;
* }
* }, 'bold' );
* ```
*
* You can skip the optional `attributeName` parameter to evaluate the callback for every `checkAttribute()` call.
*
* ```ts
* // Disallow formatting attributes on text inside custom `myTitle` element:
* schema.addAttributeCheck( ( context, attributeName ) => {
* if ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {
* if ( context.endsWith( 'myTitle $text' ) && schema.getAttributeProperties( attributeName ).isFormatting ) {
* return false;

@@ -344,4 +392,10 @@ * }

*
* Which translates to:
* Please note that the generic callbacks may affect the editor performance and should be avoided if possible.
*
* When one of the callbacks makes a decision (returns `true` or `false`) the processing is finished and other callbacks are not fired.
* Callbacks are fired in the order they were added, however generic callbacks are fired before callbacks added for a specified item.
*
* You can also use {@link #event:checkAttribute} event, if you need even better control. The result from the example above could also
* be achieved with following event callback:
*
* ```ts

@@ -352,6 +406,6 @@ * schema.on( 'checkAttribute', ( evt, args ) => {

*
* if ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {
* if ( context.endsWith( 'myTitle $text' ) && schema.getAttributeProperties( attributeName ).isFormatting ) {
* // Prevent next listeners from being called.
* evt.stop();
* // Set the checkAttribute()'s return value.
* // Set the `checkAttribute()` return value.
* evt.return = false;

@@ -362,8 +416,14 @@ * }

*
* Note that the callback checks and declarative rules checks are processed on `normal` priority.
*
* Adding callbacks this way can also negatively impact editor performance.
*
* @param callback The callback to be called. It is called with two parameters:
* {@link module:engine/model/schema~SchemaContext} (context) instance and attribute name.
* The callback may return `true/false` to override `checkAttribute()`'s return value. If it does not return
* a boolean value, the default algorithm (or other callbacks) will define `checkAttribute()`'s return value.
* {@link module:engine/model/schema~SchemaContext `context`} and attribute name. The callback may return `true` or `false`, to
* override `checkAttribute()`'s return value. If it does not return a boolean value, the default algorithm (or other callbacks)
* will define `checkAttribute()`'s return value.
* @param attributeName Name of the attribute for which the callback is registered. If specified, the callback will be run only for
* `checkAttribute()` calls with matching `attributeName`. Otherwise, the callback will run for every `checkAttribute()` call.
*/
addAttributeCheck(callback: SchemaAttributeCheckCallback): void;
addAttributeCheck(callback: SchemaAttributeCheckCallback, attributeName?: string): void;
/**

@@ -512,2 +572,18 @@ * This method allows assigning additional metadata to the model attributes. For example,

/**
* Calls child check callbacks to decide whether `def` is allowed in `context`. It uses both generic and specific (defined for `def`
* item) callbacks. If neither callback makes a decision, `undefined` is returned.
*
* Note that the first callback that makes a decision "wins", i.e., if any callback returns `true` or `false`, then the processing
* is over and that result is returned.
*/
private _evaluateChildChecks;
/**
* Calls attribute check callbacks to decide whether `attributeName` can be set on the last element of `context`. It uses both
* generic and specific (defined for `attributeName`) callbacks. If neither callback makes a decision, `undefined` is returned.
*
* Note that the first callback that makes a decision "wins", i.e., if any callback returns `true` or `false`, then the processing
* is over and that result is returned.
*/
private _evaluateAttributeChecks;
/**
* Takes a flat range and an attribute name. Traverses the range recursively and deeply to find and return all ranges

@@ -891,2 +967,4 @@ * inside the given range on which the attribute can be applied.

* Inherits "allowed children" from other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -896,2 +974,4 @@ allowContentOf?: string | Array<string>;

* Inherits "allowed in" from other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -901,2 +981,4 @@ allowWhere?: string | Array<string>;

* Inherits "allowed attributes" from other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -906,2 +988,4 @@ allowAttributesOf?: string | Array<string>;

* Inherits `is*` properties of other items.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -911,2 +995,4 @@ inheritTypesFrom?: string | Array<string>;

* A shorthand for `allowContentOf`, `allowWhere`, `allowAttributesOf`, `inheritTypesFrom`.
*
* Note that the item's "own" rules take precedence over "inherited" rules and can overwrite them.
*/

@@ -1082,2 +1168,14 @@ inheritAllFrom?: string;

/**
* Returns a new schema context that is based on this context but has the last item removed.
*
* ```ts
* const ctxParagraph = new SchemaContext( [ '$root', 'blockQuote', 'paragraph' ] );
* const ctxBlockQuote = ctxParagraph.trimLast(); // Items in `ctxBlockQuote` are: `$root` an `blockQuote`.
* const ctxRoot = ctxBlockQuote.trimLast(); // Items in `ctxRoot` are: `$root`.
* ```
*
* @returns A new reduced schema context instance.
*/
trimLast(): SchemaContext;
/**
* Gets an item on the given index.

@@ -1131,3 +1229,3 @@ */

* * By defining a {@link module:engine/model/schema~SchemaContext} instance - in this case the same instance as provided
* will be return.
* will be returned.
*

@@ -1134,0 +1232,0 @@ * Examples of context definitions passed to the {@link module:engine/model/schema~Schema#checkChild `Schema#checkChild()`}

@@ -302,21 +302,10 @@ /**

_handleNode(node) {
// Let's handle object in a special way.
// * They should never be merged with other elements.
// * If they are not allowed in any of the selection ancestors, they could be either autoparagraphed or totally removed.
if (this.schema.isObject(node)) {
this._handleObject(node);
return;
}
// Try to find a place for the given node.
// Check if a node can be inserted in the given position or it would be accepted if a paragraph would be inserted.
// Inserts the auto paragraph if it would allow for insertion.
let isAllowed = this._checkAndAutoParagraphToAllowedPosition(node);
if (!isAllowed) {
// Split the position.parent's branch up to a point where the node can be inserted.
// If it isn't allowed in the whole branch, then of course don't split anything.
isAllowed = this._checkAndSplitToAllowedPosition(node);
if (!isAllowed) {
// Split the position.parent's branch up to a point where the node can be inserted.
// If it isn't allowed in the whole branch, then of course don't split anything.
if (!this._checkAndSplitToAllowedPosition(node)) {
// Handle element children if it's not an object (strip container).
if (!this.schema.isObject(node)) {
this._handleDisallowedNode(node);
return;
}
return;
}

@@ -359,15 +348,2 @@ // Add node to the current temporary DocumentFragment.

/**
* @param node The object element.
*/
_handleObject(node) {
// Try finding it a place in the tree.
if (this._checkAndSplitToAllowedPosition(node)) {
this._appendToFragment(node);
}
// Try autoparagraphing.
else {
this._tryAutoparagraphing(node);
}
}
/**
* @param node The disallowed node which needs to be handled.

@@ -380,6 +356,2 @@ */

}
// If text is not allowed, try autoparagraphing it.
else {
this._tryAutoparagraphing(node);
}
}

@@ -600,33 +572,5 @@ /**

/**
* Tries wrapping the node in a new paragraph and inserting it this way.
*
* @param node The node which needs to be autoparagraphed.
* Inserts a paragraph and moves the insertion position into it.
*/
_tryAutoparagraphing(node) {
const paragraph = this.writer.createElement('paragraph');
// Do not autoparagraph if the paragraph won't be allowed there,
// cause that would lead to an infinite loop. The paragraph would be rejected in
// the next _handleNode() call and we'd be here again.
if (this._getAllowedIn(this.position.parent, paragraph) && this.schema.checkChild(paragraph, node)) {
paragraph._appendChild(node);
this._handleNode(paragraph);
}
}
/**
* Checks if a node can be inserted in the given position or it would be accepted if a paragraph would be inserted.
* It also handles inserting the paragraph.
*
* @returns Whether an allowed position was found.
* `false` is returned if the node isn't allowed at the current position or in auto paragraph, `true` if was.
*/
_checkAndAutoParagraphToAllowedPosition(node) {
if (this.schema.checkChild(this.position.parent, node)) {
return true;
}
// Do not auto paragraph if the paragraph won't be allowed there,
// cause that would lead to an infinite loop. The paragraph would be rejected in
// the next _handleNode() call and we'd be here again.
if (!this.schema.checkChild(this.position.parent, 'paragraph') || !this.schema.checkChild('paragraph', node)) {
return false;
}
_insertAutoParagraph() {
// Insert nodes collected in temporary DocumentFragment if the position parent needs change to process further nodes.

@@ -640,3 +584,2 @@ this._insertPartialFragment();

this.position = this.writer.createPositionAt(paragraph, 0);
return true;
}

@@ -688,2 +631,8 @@ /**

}
// At this point, we split elements up to the parent in which `node` is allowed.
// Note that `_getAllowedIn()` checks if the `node` is allowed either directly, or when auto-paragraphed.
// So, let's check if the `node` is allowed directly. If not, we need to auto-paragraph it.
if (!this.schema.checkChild(this.position.parent, node)) {
this._insertAutoParagraph();
}
return true;

@@ -694,2 +643,4 @@ }

*
* It also verifies if auto-paragraphing could help.
*
* @param contextElement The element in which context the node should be checked.

@@ -699,5 +650,10 @@ * @param childNode The node to check.

_getAllowedIn(contextElement, childNode) {
// Check if a node can be inserted in the given context...
if (this.schema.checkChild(contextElement, childNode)) {
return contextElement;
}
// ...or it would be accepted if a paragraph would be inserted.
if (this.schema.checkChild(contextElement, 'paragraph') && this.schema.checkChild('paragraph', childNode)) {
return contextElement;
}
// If the child wasn't allowed in the context element and the element is a limit there's no point in

@@ -704,0 +660,0 @@ // checking any further towards the root. This is it: the limit is unsplittable and there's nothing

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

import DomEventObserver from './domeventobserver.js';
// @if CK_DEBUG_TYPING // const { _debouncedLine } = require( '../../dev-utils/utils.js' );
/**

@@ -52,2 +53,3 @@ * {@link module:engine/view/document~Document#event:compositionstart Compositionstart},

// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // _debouncedLine();
// @if CK_DEBUG_TYPING // console.group( `%c[CompositionObserver]%c ${ domEvent.type }`, 'color: green', '' );

@@ -54,0 +56,0 @@ // @if CK_DEBUG_TYPING // }

@@ -50,2 +50,14 @@ /**

destroy(): void;
/**
* The `focus` event handler.
*/
private _handleFocus;
/**
* The `blur` event handler.
*/
private _handleBlur;
/**
* Clears timeout.
*/
private _clearTimeout;
}

@@ -52,0 +64,0 @@ /**

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

/**
* Identifier of the timeout currently used by focus listener to delay rendering execution.
*/
this._renderTimeoutId = null;
/**
* Set to `true` if the document is in the process of setting the focus.

@@ -37,27 +41,13 @@ *

const document = this.document;
document.on('focus', () => {
this._isFocusChanging = true;
// Unfortunately native `selectionchange` event is fired asynchronously.
// We need to wait until `SelectionObserver` handle the event and then render. Otherwise rendering will
// overwrite new DOM selection with selection from the view.
// See https://github.com/ckeditor/ckeditor5-engine/issues/795 for more details.
// Long timeout is needed to solve #676 and https://github.com/ckeditor/ckeditor5-engine/issues/1157 issues.
//
// Using `view.change()` instead of `view.forceRender()` to prevent double rendering
// in a situation where `selectionchange` already caused selection change.
this._renderTimeoutId = setTimeout(() => {
this.flush();
view.change(() => { });
}, 50);
});
document.on('blur', (evt, data) => {
const selectedEditable = document.selection.editableElement;
if (selectedEditable === null || selectedEditable === data.target) {
document.isFocused = false;
this._isFocusChanging = false;
// Re-render the document to update view elements
// (changing document.isFocused already marked view as changed since last rendering).
view.change(() => { });
document.on('focus', () => this._handleFocus());
document.on('blur', (evt, data) => this._handleBlur(data));
// Focus the editor in cases where browser dispatches `beforeinput` event to a not-focused editable element.
// This is flushed by the beforeinput listener in the `InsertTextObserver`.
// Note that focus is set only if the document is not focused yet.
// See https://github.com/ckeditor/ckeditor5/issues/14702.
document.on('beforeinput', () => {
if (!document.isFocused) {
this._handleFocus();
}
});
}, { priority: 'highest' });
}

@@ -83,7 +73,47 @@ /**

destroy() {
this._clearTimeout();
super.destroy();
}
/**
* The `focus` event handler.
*/
_handleFocus() {
this._clearTimeout();
this._isFocusChanging = true;
// Unfortunately native `selectionchange` event is fired asynchronously.
// We need to wait until `SelectionObserver` handle the event and then render. Otherwise rendering will
// overwrite new DOM selection with selection from the view.
// See https://github.com/ckeditor/ckeditor5-engine/issues/795 for more details.
// Long timeout is needed to solve #676 and https://github.com/ckeditor/ckeditor5-engine/issues/1157 issues.
//
// Using `view.change()` instead of `view.forceRender()` to prevent double rendering
// in a situation where `selectionchange` already caused selection change.
this._renderTimeoutId = setTimeout(() => {
this._renderTimeoutId = null;
this.flush();
this.view.change(() => { });
}, 50);
}
/**
* The `blur` event handler.
*/
_handleBlur(data) {
const selectedEditable = this.document.selection.editableElement;
if (selectedEditable === null || selectedEditable === data.target) {
this.document.isFocused = false;
this._isFocusChanging = false;
// Re-render the document to update view elements
// (changing document.isFocused already marked view as changed since last rendering).
this.view.change(() => { });
}
}
/**
* Clears timeout.
*/
_clearTimeout() {
if (this._renderTimeoutId) {
clearTimeout(this._renderTimeoutId);
this._renderTimeoutId = null;
}
super.destroy();
}
}

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

import { env } from '@ckeditor/ckeditor5-utils';
// @if CK_DEBUG_TYPING // const { _debouncedLine } = require( '../../dev-utils/utils.js' );
/**

@@ -31,2 +32,3 @@ * Observer for events connected with data input.

// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // _debouncedLine();
// @if CK_DEBUG_TYPING // console.group( `%c[InputObserver]%c ${ domEvent.type }: ${ domEvent.inputType }`,

@@ -49,3 +51,3 @@ // @if CK_DEBUG_TYPING // 'color: green', 'color: default'

// @if CK_DEBUG_TYPING // console.info( `%c[InputObserver]%c event data: %c${ JSON.stringify( data ) }`,
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', 'color: blue;'
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', 'color: blue;'
// @if CK_DEBUG_TYPING // );

@@ -58,3 +60,3 @@ // @if CK_DEBUG_TYPING // }

// @if CK_DEBUG_TYPING // console.info( `%c[InputObserver]%c event data transfer: %c${ JSON.stringify( data ) }`,
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', 'color: blue;'
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', 'color: blue;'
// @if CK_DEBUG_TYPING // );

@@ -70,3 +72,3 @@ // @if CK_DEBUG_TYPING // }

// @if CK_DEBUG_TYPING // console.info( '%c[InputObserver]%c using fake selection:',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', targetRanges,
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', targetRanges,
// @if CK_DEBUG_TYPING // viewDocument.selection.isFake ? 'fake view selection' : 'fake DOM parent'

@@ -93,3 +95,3 @@ // @if CK_DEBUG_TYPING // );

// @if CK_DEBUG_TYPING // console.info( '%c[InputObserver]%c using target ranges:',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', targetRanges
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', targetRanges
// @if CK_DEBUG_TYPING // );

@@ -105,3 +107,3 @@ // @if CK_DEBUG_TYPING // }

// @if CK_DEBUG_TYPING // console.info( '%c[InputObserver]%c using selection ranges:',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', targetRanges
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', targetRanges
// @if CK_DEBUG_TYPING // );

@@ -108,0 +110,0 @@ // @if CK_DEBUG_TYPING // }

@@ -10,4 +10,5 @@ /**

import type DomConverter from '../domconverter.js';
import type Renderer from '../renderer.js';
import type View from '../view.js';
import type ViewNode from '../node.js';
import type { ChangeType } from '../document.js';
/**

@@ -30,6 +31,2 @@ * Mutation observer's role is to watch for any DOM changes inside the editor that weren't

/**
* Reference to the {@link module:engine/view/view~View#_renderer}.
*/
readonly renderer: Renderer;
/**
* Native mutation observer config.

@@ -89,1 +86,33 @@ */

}
/**
* Event fired on DOM mutations detected.
*
* This event is introduced by {@link module:engine/view/observer/mutationobserver~MutationObserver} and available
* by default in all editor instances (attached by {@link module:engine/view/view~View}).
*
* @eventName module:engine/view/document~Document#mutations
* @param data Event data containing detailed information about the event.
*/
export type ViewDocumentMutationsEvent = {
name: 'mutations';
args: [data: MutationsEventData];
};
/**
* The value of {@link ~ViewDocumentMutationsEvent}.
*/
export type MutationsEventData = {
mutations: Array<MutationData>;
};
/**
* A single entry in {@link ~MutationsEventData} mutations array.
*/
export type MutationData = {
/**
* Type of mutation detected.
*/
type: ChangeType;
/**
* The view node related to the detected mutation.
*/
node: ViewNode;
};

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

import { isEqualWith } from 'lodash-es';
// @if CK_DEBUG_TYPING // const { _debouncedLine } = require( '../../dev-utils/utils.js' );
/**

@@ -36,3 +37,2 @@ * Mutation observer's role is to watch for any DOM changes inside the editor that weren't

this.domConverter = view.domConverter;
this.renderer = view._renderer;
this._domElements = new Set();

@@ -144,6 +144,5 @@ this._mutationObserver = new window.MutationObserver(this._onMutations.bind(this));

// same node multiple times in case of duplication.
let hasMutations = false;
const mutations = [];
for (const textNode of mutatedTextNodes) {
hasMutations = true;
this.renderer.markToSync('text', textNode);
mutations.push({ type: 'text', node: textNode });
}

@@ -157,16 +156,14 @@ for (const viewElement of elementsWithMutatedChildren) {

if (!isEqualWith(viewChildren, newViewChildren, sameNodes)) {
hasMutations = true;
this.renderer.markToSync('children', viewElement);
mutations.push({ type: 'children', node: viewElement });
}
}
// In case only non-relevant mutations were recorded it skips the event and force render (#5600).
if (hasMutations) {
if (mutations.length) {
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // _debouncedLine();
// @if CK_DEBUG_TYPING // console.group( '%c[MutationObserver]%c Mutations detected',
// @if CK_DEBUG_TYPING // 'font-weight:bold;color:green', ''
// @if CK_DEBUG_TYPING // 'font-weight: bold; color: green', 'font-weight: bold'
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
// At this point we have "dirty DOM" (changed) and de-synched view (which has not been changed).
// In order to "reset DOM" we render the view again.
this.view.forceRender();
this.document.fire('mutations', { mutations });
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {

@@ -173,0 +170,0 @@ // @if CK_DEBUG_TYPING // console.groupEnd();

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

import MutationObserver from './mutationobserver.js';
import FocusObserver from './focusobserver.js';
import type View from '../view.js';

@@ -15,3 +16,2 @@ import type DocumentSelection from '../documentselection.js';

import type Selection from '../selection.js';
import FocusObserver from './focusobserver.js';
type DomSelection = globalThis.Selection;

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

*
* @param domEvent DOM event.
* @param domDocument DOM document.

@@ -96,0 +95,0 @@ */

@@ -11,5 +11,5 @@ /**

import MutationObserver from './mutationobserver.js';
import FocusObserver from './focusobserver.js';
import { env } from '@ckeditor/ckeditor5-utils';
import { debounce } from 'lodash-es';
import FocusObserver from './focusobserver.js';
/**

@@ -56,3 +56,3 @@ * Selection observer class observes selection changes in the document. If a selection changes on the document this

// Sometimes `selectionchange` events could arrive after the `mouseup` event and that selection could be already outdated.
this._handleSelectionChange(null, domDocument);
this._handleSelectionChange(domDocument);
this.document.isSelecting = false;

@@ -77,6 +77,7 @@ // The safety timeout can be canceled when the document leaves the "is selecting" state.

// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // _debouncedLine();
// @if CK_DEBUG_TYPING // const domSelection = domDocument.defaultView!.getSelection();
// @if CK_DEBUG_TYPING // console.group( '%c[SelectionObserver]%c selectionchange', 'color:green', ''
// @if CK_DEBUG_TYPING // console.group( '%c[SelectionObserver]%c selectionchange', 'color: green', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c DOM Selection:', 'font-weight:bold;color:green', '',
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c DOM Selection:', 'font-weight: bold; color: green', '',
// @if CK_DEBUG_TYPING // { node: domSelection!.anchorNode, offset: domSelection!.anchorOffset },

@@ -91,3 +92,3 @@ // @if CK_DEBUG_TYPING // { node: domSelection!.focusNode, offset: domSelection!.focusOffset }

// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c Selection change ignored (isComposing)',
// @if CK_DEBUG_TYPING // 'font-weight:bold;color:green', ''
// @if CK_DEBUG_TYPING // 'font-weight: bold; color: green', ''
// @if CK_DEBUG_TYPING // );

@@ -98,3 +99,3 @@ // @if CK_DEBUG_TYPING // console.groupEnd();

}
this._handleSelectionChange(domEvent, domDocument);
this._handleSelectionChange(domDocument);
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {

@@ -107,2 +108,20 @@ // @if CK_DEBUG_TYPING // console.groupEnd();

});
// Update the model DocumentSelection just after the Renderer and the SelectionObserver are locked.
// We do this synchronously (without waiting for the `selectionchange` DOM event) as browser updates
// the DOM selection (but not visually) to span the text that is under composition and could be replaced.
this.listenTo(this.view.document, 'compositionstart', () => {
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // const domSelection = domDocument.defaultView!.getSelection();
// @if CK_DEBUG_TYPING // console.group( '%c[SelectionObserver]%c update selection on compositionstart', 'color: green', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c DOM Selection:', 'font-weight: bold; color: green', '',
// @if CK_DEBUG_TYPING // { node: domSelection!.anchorNode, offset: domSelection!.anchorOffset },
// @if CK_DEBUG_TYPING // { node: domSelection!.focusNode, offset: domSelection!.focusOffset }
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
this._handleSelectionChange(domDocument);
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.groupEnd();
// @if CK_DEBUG_TYPING // }
}, { priority: 'lowest' });
this._documents.add(domDocument);

@@ -137,6 +156,5 @@ }

*
* @param domEvent DOM event.
* @param domDocument DOM document.
*/
_handleSelectionChange(domEvent, domDocument) {
_handleSelectionChange(domDocument) {
if (!this.isEnabled) {

@@ -190,3 +208,3 @@ return;

// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c Fire selection change:',
// @if CK_DEBUG_TYPING // 'font-weight:bold;color:green', '',
// @if CK_DEBUG_TYPING // 'font-weight: bold; color: green', '',
// @if CK_DEBUG_TYPING // newViewSelection.getFirstRange()

@@ -193,0 +211,0 @@ // @if CK_DEBUG_TYPING // );

@@ -217,2 +217,14 @@ /**

/**
* Checks if text needs to be updated and possibly updates it by removing and inserting only parts
* of the data from the existing text node to reduce impact on the IME composition.
*
* @param domText DOM text node to update.
* @param expectedText The expected data of a text node.
*/
private _updateTextNode;
/**
* Part of the `_updateTextNode` method extracted for easier testing.
*/
private _updateTextNodeInternal;
/**
* Marks text nodes to be synchronized.

@@ -219,0 +231,0 @@ *

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

this.set('isSelecting', false);
this.set('isComposing', false);
// Rendering the selection and inline filler manipulation should be postponed in (non-Android) Blink until the user finishes

@@ -75,8 +76,2 @@ // creating the selection in DOM to avoid accidental selection collapsing

}
this.set('isComposing', false);
this.on('change:isComposing', () => {
if (!this.isComposing) {
this.render();
}
});
}

@@ -143,4 +138,4 @@ /**

// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Rendering aborted while isComposing',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Rendering aborted while isComposing.',
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic'
// @if CK_DEBUG_TYPING // );

@@ -152,3 +147,3 @@ // @if CK_DEBUG_TYPING // }

// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Rendering',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold'
// @if CK_DEBUG_TYPING // );

@@ -439,6 +434,6 @@ // @if CK_DEBUG_TYPING // }

// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update text',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: normal'
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
updateTextNode(domText, expectedText);
this._updateTextNode(domText, expectedText);
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {

@@ -494,3 +489,3 @@ // @if CK_DEBUG_TYPING // console.groupEnd();

// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update children',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: normal'
// @if CK_DEBUG_TYPING // );

@@ -525,2 +520,7 @@ // @if CK_DEBUG_TYPING // }

const actions = this._findUpdateActions(diff, actualDomChildren, expectedDomChildren, areTextNodes);
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping && actions.every( a => a == 'equal' ) ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Nothing to update.',
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic'
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
let i = 0;

@@ -537,5 +537,18 @@ const nodesToUnbind = new Set();

// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Remove node',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '', actualDomChildren[ i ]
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // const node = actualDomChildren[ i ];
// @if CK_DEBUG_TYPING // if ( isText( node ) ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Remove text node' +
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: ` +
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( node.data ) }%c (${ node.data.length })`,
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold',
// @if CK_DEBUG_TYPING // this.isComposing ? 'color: red; font-weight: bold' : '', 'color: blue', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // } else {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Remove element' +
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: `,
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold',
// @if CK_DEBUG_TYPING // this.isComposing ? 'color: red; font-weight: bold' : '',
// @if CK_DEBUG_TYPING // node
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
// @if CK_DEBUG_TYPING // }

@@ -553,5 +566,17 @@ nodesToUnbind.add(actualDomChildren[i]);

// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Insert node',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '', expectedDomChildren[ i ]
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // const node = expectedDomChildren[ i ];
// @if CK_DEBUG_TYPING // if ( isText( node ) ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Insert text node' +
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: ` +
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( node.data ) }%c (${ node.data.length })`,
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold',
// @if CK_DEBUG_TYPING // this.isComposing ? 'color: red; font-weight: bold' : '',
// @if CK_DEBUG_TYPING // 'color: blue', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // } else {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Insert element:',
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: normal',
// @if CK_DEBUG_TYPING // node
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
// @if CK_DEBUG_TYPING // }

@@ -561,14 +586,6 @@ insertAt(domElement, i, expectedDomChildren[i]);

}
// Update the existing text node data. Note that replace action is generated only for Android for now.
// Update the existing text node data.
else if (action === 'update') {
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update text node',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
updateTextNode(actualDomChildren[i], expectedDomChildren[i].data);
this._updateTextNode(actualDomChildren[i], expectedDomChildren[i].data);
i++;
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.groupEnd();
// @if CK_DEBUG_TYPING // }
}

@@ -653,2 +670,59 @@ else if (action === 'equal') {

/**
* Checks if text needs to be updated and possibly updates it by removing and inserting only parts
* of the data from the existing text node to reduce impact on the IME composition.
*
* @param domText DOM text node to update.
* @param expectedText The expected data of a text node.
*/
_updateTextNode(domText, expectedText) {
const actualText = domText.data;
if (actualText == expectedText) {
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Text node does not need update:%c ' +
// @if CK_DEBUG_TYPING // `${ _escapeTextNodeData( actualText ) }%c (${ actualText.length })`,
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic', 'color: blue', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
return;
}
// Our approach to interleaving space character with NBSP might differ with the one implemented by the browser.
// Avoid modifying the text node in the DOM if only NBSPs and spaces are interchanged.
// We should avoid DOM modifications while composing to avoid breakage of composition.
// See: https://github.com/ckeditor/ckeditor5/issues/13994.
if (env.isAndroid && this.isComposing && actualText.replace(/\u00A0/g, ' ') == expectedText.replace(/\u00A0/g, ' ')) {
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Text node ignore NBSP changes while composing: ' +
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( actualText ) }%c (${ actualText.length }) ->` +
// @if CK_DEBUG_TYPING // ` %c${ _escapeTextNodeData( expectedText ) }%c (${ expectedText.length })`,
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic', 'color: blue', '', 'color: blue', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
return;
}
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update text node' +
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: ` +
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( actualText ) }%c (${ actualText.length }) ->` +
// @if CK_DEBUG_TYPING // ` %c${ _escapeTextNodeData( expectedText ) }%c (${ expectedText.length })`,
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', this.isComposing ? 'color: red; font-weight: bold' : '',
// @if CK_DEBUG_TYPING // 'color: blue', '', 'color: blue', ''
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
this._updateTextNodeInternal(domText, expectedText);
}
/**
* Part of the `_updateTextNode` method extracted for easier testing.
*/
_updateTextNodeInternal(domText, expectedText) {
const actions = fastDiff(domText.data, expectedText);
for (const action of actions) {
if (action.type === 'insert') {
domText.insertData(action.index, action.values.join(''));
}
else { // 'delete'
domText.deleteData(action.index, action.howMany);
}
}
}
/**
* Marks text nodes to be synchronized.

@@ -759,3 +833,3 @@ *

// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update DOM selection:',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '', anchor, focus
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', '', anchor, focus
// @if CK_DEBUG_TYPING // );

@@ -984,35 +1058,9 @@ // @if CK_DEBUG_TYPING // }

}
/**
* Checks if text needs to be updated and possibly updates it by removing and inserting only parts
* of the data from the existing text node to reduce impact on the IME composition.
*
* @param domText DOM text node to update.
* @param expectedText The expected data of a text node.
*/
function updateTextNode(domText, expectedText) {
const actualText = domText.data;
if (actualText == expectedText) {
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Text node does not need update:',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '',
// @if CK_DEBUG_TYPING // `"${ domText.data }" (${ domText.data.length })`
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
return;
}
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update text node:',
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '',
// @if CK_DEBUG_TYPING // `"${ domText.data }" (${ domText.data.length }) -> "${ expectedText }" (${ expectedText.length })`
// @if CK_DEBUG_TYPING // );
// @if CK_DEBUG_TYPING // }
const actions = fastDiff(actualText, expectedText);
for (const action of actions) {
if (action.type === 'insert') {
domText.insertData(action.index, action.values.join(''));
}
else { // 'delete'
domText.deleteData(action.index, action.howMany);
}
}
}
// @if CK_DEBUG_TYPING // function _escapeTextNodeData( text ) {
// @if CK_DEBUG_TYPING // const escapedText = text
// @if CK_DEBUG_TYPING // .replace( /&/g, '&amp;' )
// @if CK_DEBUG_TYPING // .replace( /\u00A0/g, '&nbsp;' )
// @if CK_DEBUG_TYPING // .replace( /\u2060/g, '&NoBreak;' );
// @if CK_DEBUG_TYPING //
// @if CK_DEBUG_TYPING // return `"${ escapedText }"`;
// @if CK_DEBUG_TYPING // }

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

import DowncastWriter from './downcastwriter.js';
import Renderer from './renderer.js';
import DomConverter from './domconverter.js';

@@ -99,6 +98,4 @@ import Position, { type PositionOffset } from './position.js';

* Instance of the {@link module:engine/view/renderer~Renderer renderer}.
*
* @internal
*/
readonly _renderer: Renderer;
private readonly _renderer;
/**

@@ -105,0 +102,0 @@ * A DOM root attributes cache. It saves the initial values of DOM root attributes before the DOM element

@@ -147,2 +147,11 @@ /**

}
// Listen to external content mutations (directly in the DOM) and mark them to get verified by the renderer.
this.listenTo(this.document, 'mutations', (evt, { mutations }) => {
mutations.forEach(mutation => this._renderer.markToSync(mutation.type, mutation.node));
}, { priority: 'low' });
// After all mutated nodes were marked to sync we can trigger view to DOM synchronization
// to make sure the DOM structure matches the view.
this.listenTo(this.document, 'mutations', () => {
this.forceRender();
}, { priority: 'lowest' });
}

@@ -149,0 +158,0 @@ /**

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

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

Sorry, the diff of this file is not supported yet

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

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