UNPKG

@ckeditor/ckeditor5-engine

Version:

The editing engine of CKEditor 5 – the best browser-based rich text editor.

921 lines (920 loc) 43.9 kB
/** * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options */ /** * @module engine/model/model */ import { Batch, type BatchType } from './batch.js'; import { ModelDocument } from './document.js'; import { MarkerCollection } from './markercollection.js'; import { ModelPosition, type ModelPositionOffset, type ModelPositionStickiness } from './position.js'; import { ModelRange } from './range.js'; import { ModelSelection, type ModelPlaceOrOffset, type ModelSelectable } from './selection.js'; import { ModelDocumentSelection } from './documentselection.js'; import { ModelSchema } from './schema.js'; import { ModelWriter } from './writer.js'; import { ModelNode } from './node.js'; import { type ModelDocumentFragment } from './documentfragment.js'; import { type ModelItem } from './item.js'; import { type ModelElement } from './element.js'; import { type Operation } from './operation/operation.js'; import { type DecoratedMethodEvent } from '@ckeditor/ckeditor5-utils'; declare const Model_base: { new (): import("@ckeditor/ckeditor5-utils").Observable; prototype: import("@ckeditor/ckeditor5-utils").Observable; }; /** * Editor's data model. Read about the model in the * {@glink framework/architecture/editing-engine engine architecture} guide. */ export declare class Model extends /* #__PURE__ */ Model_base { /** * Model's marker collection. */ readonly markers: MarkerCollection; /** * Model's document. */ readonly document: ModelDocument; /** * Model's schema. */ readonly schema: ModelSchema; /** * All callbacks added by {@link module:engine/model/model~Model#change} or * {@link module:engine/model/model~Model#enqueueChange} methods waiting to be executed. */ private readonly _pendingChanges; /** * The last created and currently used writer instance. */ private _currentWriter; constructor(); /** * The `change()` method is the primary way of changing the model. You should use it to modify all document nodes * (including detached nodes – i.e. nodes not added to the {@link module:engine/model/model~Model#document model document}), * the {@link module:engine/model/document~ModelDocument#selection document's selection}, and * {@link module:engine/model/model~Model#markers model markers}. * * ```ts * model.change( writer => { * writer.insertText( 'foo', paragraph, 'end' ); * } ); * ``` * * All changes inside the change block use the same {@link module:engine/model/batch~Batch} so they are combined * into a single undo step. * * ```ts * model.change( writer => { * writer.insertText( 'foo', paragraph, 'end' ); // foo. * * model.change( writer => { * writer.insertText( 'bar', paragraph, 'end' ); // foobar. * } ); * * writer.insertText( 'bom', paragraph, 'end' ); // foobarbom. * } ); * ``` * * The callback of the `change()` block is executed synchronously. * * You can also return a value from the change block. * * ```ts * const img = model.change( writer => { * return writer.createElement( 'img' ); * } ); * ``` * * @see #enqueueChange * @typeParam TReturn The return type of the provided callback. * @param callback Callback function which may modify the model. */ change<TReturn>(callback: (writer: ModelWriter) => TReturn): TReturn; /** * The `enqueueChange()` method performs similar task as the {@link #change `change()` method}, with two major differences. * * First, the callback of `enqueueChange()` is executed when all other enqueued changes are done. It might be executed * immediately if it is not nested in any other change block, but if it is nested in another (enqueue)change block, * it will be delayed and executed after the outermost block. * * ```ts * model.change( writer => { * console.log( 1 ); * * model.enqueueChange( writer => { * console.log( 2 ); * } ); * * console.log( 3 ); * } ); // Will log: 1, 3, 2. * ``` * * In addition to that, the changes enqueued with `enqueueChange()` will be converted separately from the changes * done in the outer `change()` block. * * By default, a new batch with the default {@link module:engine/model/batch~Batch#constructor batch type} is created. * To define the {@link module:engine/model/batch~Batch} into which you want to add your changes, * use {@link #enqueueChange:CUSTOM_BATCH `enqueueChange( batchOrType, callback )`}. * * @label DEFAULT_BATCH * @param callback Callback function which may modify the model. */ enqueueChange(callback: (writer: ModelWriter) => unknown): void; /** * The `enqueueChange()` method performs similar task as the {@link #change `change()` method}, with two major differences. * * First, the callback of `enqueueChange()` is executed when all other enqueued changes are done. It might be executed * immediately if it is not nested in any other change block, but if it is nested in another (enqueue)change block, * it will be delayed and executed after the outermost block. * * ```ts * model.change( new Batch(), writer => { * console.log( 1 ); * * model.enqueueChange( new Batch(), writer => { * console.log( 2 ); * } ); * * console.log( 3 ); * } ); // Will log: 1, 3, 2. * ``` * * In addition to that, the changes enqueued with `enqueueChange()` will be converted separately from the changes * done in the outer `change()` block. * * Second, it lets you define the {@link module:engine/model/batch~Batch} into which you want to add your changes. * If you want to use default {@link module:engine/model/batch~Batch#constructor batch type}, use * {@link #enqueueChange:DEFAULT_BATCH `enqueueChange( callback )`}. * * ```ts * model.enqueueChange( { isUndoable: false }, writer => { * writer.insertText( 'foo', paragraph, 'end' ); * } ); * ``` * * When using the `enqueueChange()` block you can also add some changes to the batch you used before. * * ```ts * model.enqueueChange( batch, writer => { * writer.insertText( 'foo', paragraph, 'end' ); * } ); * ``` * * In order to make a nested `enqueueChange()` create a single undo step together with the changes done in the outer `change()` * block, you can obtain the batch instance from the {@link module:engine/model/writer~ModelWriter#batch writer} of the outer block. * * @label CUSTOM_BATCH * @param batchOrType A batch or a {@link module:engine/model/batch~Batch#constructor batch type} that should be used in the callback. * If not defined, a new batch with the default type will be created. * @param callback Callback function which may modify the model. */ enqueueChange(batchOrType: Batch | BatchType | undefined, callback: (writer: ModelWriter) => unknown): void; /** * {@link module:utils/observablemixin~Observable#decorate Decorated} function for applying * {@link module:engine/model/operation/operation~Operation operations} to the model. * * This is a low-level way of changing the model. It is exposed for very specific use cases (like the undo feature). * Normally, to modify the model, you will want to use {@link module:engine/model/writer~ModelWriter `Writer`}. * See also {@glink framework/architecture/editing-engine#changing-the-model Changing the model} section * of the {@glink framework/architecture/editing-engine Editing architecture} guide. * * @param operation The operation to apply. */ applyOperation(operation: Operation): void; /** * Inserts content at the position in the editor specified by the selection, as one would expect the paste * functionality to work. * * **Note**: If you want to insert an {@glink framework/deep-dive/schema#object-elements object element} * (e.g. a {@link module:widget/utils~toWidget widget}), see {@link #insertObject} instead. * * This is a high-level method. It takes the {@link #schema schema} into consideration when inserting * the content, clears the given selection's content before inserting nodes and moves the selection * to its target position at the end of the process. * It can split elements, merge them, wrap bare text nodes with paragraphs, etc. &ndash; just like the * pasting feature should do. * * For lower-level methods see {@link module:engine/model/writer~ModelWriter `Writer`}. * * This method, unlike {@link module:engine/model/writer~ModelWriter `Writer`}'s methods, does not have to be used * inside a {@link #change `change()` block}. * * # Conversion and schema * * Inserting elements and text nodes into the model is not enough to make CKEditor 5 render that content * to the user. CKEditor 5 implements a model-view-controller architecture and what `model.insertContent()` does * is only adding nodes to the model. Additionally, you need to define * {@glink framework/architecture/editing-engine#conversion converters} between the model and view * and define those nodes in the {@glink framework/architecture/editing-engine#schema schema}. * * So, while this method may seem similar to CKEditor 4 `editor.insertHtml()` (in fact, both methods * are used for paste-like content insertion), the CKEditor 5 method cannot be use to insert arbitrary HTML * unless converters are defined for all elements and attributes in that HTML. * * # Examples * * Using `insertContent()` with a manually created model structure: * * ```ts * // Let's create a document fragment containing such content as: * // * // <paragraph>foo</paragraph> * // <blockQuote> * // <paragraph>bar</paragraph> * // </blockQuote> * const docFrag = editor.model.change( writer => { * const p1 = writer.createElement( 'paragraph' ); * const p2 = writer.createElement( 'paragraph' ); * const blockQuote = writer.createElement( 'blockQuote' ); * const docFrag = writer.createDocumentFragment(); * * writer.append( p1, docFrag ); * writer.append( blockQuote, docFrag ); * writer.append( p2, blockQuote ); * writer.insertText( 'foo', p1 ); * writer.insertText( 'bar', p2 ); * * return docFrag; * } ); * * // insertContent() does not have to be used in a change() block. It can, though, * // so this code could be moved to the callback defined above. * editor.model.insertContent( docFrag ); * ``` * * Using `insertContent()` with an HTML string converted to a model document fragment (similar to the pasting mechanism): * * ```ts * // You can create your own HtmlDataProcessor instance or use editor.data.processor * // if you have not overridden the default one (which is the HtmlDataProcessor instance). * const htmlDP = new HtmlDataProcessor( viewDocument ); * * // Convert an HTML string to a view document fragment: * const viewFragment = htmlDP.toView( htmlString ); * * // Convert the view document fragment to a model document fragment * // in the context of $root. This conversion takes the schema into * // account so if, for example, the view document fragment contained a bare text node, * // this text node cannot be a child of $root, so it will be automatically * // wrapped with a <paragraph>. You can define the context yourself (in the second parameter), * // and e.g. convert the content like it would happen in a <paragraph>. * // Note: The clipboard feature uses a custom context called $clipboardHolder * // which has a loosened schema. * const modelFragment = editor.data.toModel( viewFragment ); * * editor.model.insertContent( modelFragment ); * ``` * * By default this method will use the document selection but it can also be used with a position, range or selection instance. * * ```ts * // Insert text at the current document selection position. * editor.model.change( writer => { * editor.model.insertContent( writer.createText( 'x' ) ); * } ); * * // Insert text at a given position - the document selection will not be modified. * editor.model.change( writer => { * editor.model.insertContent( writer.createText( 'x' ), doc.getRoot(), 2 ); * * // Which is a shorthand for: * editor.model.insertContent( writer.createText( 'x' ), writer.createPositionAt( doc.getRoot(), 2 ) ); * } ); * ``` * * If you want the document selection to be moved to the inserted content, use the * {@link module:engine/model/writer~ModelWriter#setSelection `setSelection()`} method of the writer after inserting * the content: * * ```ts * editor.model.change( writer => { * const paragraph = writer.createElement( 'paragraph' ); * * // Insert an empty paragraph at the beginning of the root. * editor.model.insertContent( paragraph, writer.createPositionAt( editor.model.document.getRoot(), 0 ) ); * * // Move the document selection to the inserted paragraph. * writer.setSelection( paragraph, 'in' ); * } ); * ``` * * If an instance of the {@link module:engine/model/selection~ModelSelection model selection} is passed as `selectable`, * the new content will be inserted at the passed selection (instead of document selection): * * ```ts * editor.model.change( writer => { * // Create a selection in a paragraph that will be used as a place of insertion. * const selection = writer.createSelection( paragraph, 'in' ); * * // Insert the new text at the created selection. * editor.model.insertContent( writer.createText( 'x' ), selection ); * * // insertContent() modifies the passed selection instance so it can be used to set the document selection. * // Note: This is not necessary when you passed the document selection to insertContent(). * writer.setSelection( selection ); * } ); * ``` * * @fires insertContent * @param content The content to insert. * @param selectable The selection into which the content should be inserted. * If not provided the current model document selection will be used. * @param placeOrOffset To be used when a model item was passed as `selectable`. * This param defines a position in relation to that item. * at the insertion position. */ insertContent(content: ModelItem | ModelDocumentFragment, selectable?: ModelSelectable, placeOrOffset?: ModelPlaceOrOffset, ...rest: Array<unknown>): ModelRange; /** * Inserts an {@glink framework/deep-dive/schema#object-elements object element} at a specific position in the editor content. * * This is a high-level API: * * It takes the {@link #schema schema} into consideration, * * It clears the content of passed `selectable` before inserting, * * It can move the selection at the end of the process, * * It will copy the selected block's attributes to preserve them upon insertion, * * It can split elements or wrap inline objects with paragraphs if they are not allowed in target position, * * etc. * * # Notes * * * If you want to insert a non-object content, see {@link #insertContent} instead. * * For lower-level API, see {@link module:engine/model/writer~ModelWriter `Writer`}. * * Unlike {@link module:engine/model/writer~ModelWriter `Writer`}, this method does not have to be used inside * a {@link #change `change()` block}. * * Inserting object into the model is not enough to make CKEditor 5 render that content to the user. * CKEditor 5 implements a model-view-controller architecture and what `model.insertObject()` does * is only adding nodes to the model. Additionally, you need to define * {@glink framework/architecture/editing-engine#conversion converters} between the model and view * and define those nodes in the {@glink framework/architecture/editing-engine#schema schema}. * * # Examples * * Use the following code to insert an object at the current selection and keep the selection on the inserted element: * * ```ts * const rawHtmlEmbedElement = writer.createElement( 'rawHtml' ); * * model.insertObject( rawHtmlEmbedElement, null, null, { * setSelection: 'on' * } ); * ``` * * Use the following code to insert an object at the current selection and nudge the selection after the inserted object: * * ```ts * const pageBreakElement = writer.createElement( 'pageBreak' ); * * model.insertObject( pageBreakElement, null, null, { * setSelection: 'after' * } ); * ``` * * Use the following code to insert an object at the current selection and avoid splitting the content (non-destructive insertion): * * ```ts * const tableElement = writer.createElement( 'table' ); * * model.insertObject( tableElement, null, null, { * findOptimalPosition: 'auto' * } ); * ``` * * Use the following code to insert an object at the specific range (also: replace the content of the range): * * ```ts * const tableElement = writer.createElement( 'table' ); * const range = model.createRangeOn( model.document.getRoot().getChild( 1 ) ); * * model.insertObject( tableElement, range ); * ``` * * @param element An object to be inserted into the model document. * @param selectable A selectable where the content should be inserted. If not specified, the current * {@link module:engine/model/document~ModelDocument#selection document selection} will be used instead. * @param placeOrOffset Specifies the exact place or offset for the insertion to take place, relative to `selectable`. * @param options Additional options. * @param options.findOptimalPosition An option that, when set, adjusts the insertion position (relative to * `selectable` and `placeOrOffset`) so that the content of `selectable` is not split upon insertion (a.k.a. non-destructive insertion). * * When `'auto'`, the algorithm will decide whether to insert the object before or after `selectable` to avoid content splitting. * * When `'before'`, the closest position before `selectable` will be used that will not result in content splitting. * * When `'after'`, the closest position after `selectable` will be used that will not result in content splitting. * * Note that this option only works for block objects. Inline objects are inserted into text and do not split blocks. * @param options.setSelection An option that, when set, moves the * {@link module:engine/model/document~ModelDocument#selection document selection} after inserting the object. * * When `'on'`, the document selection will be set on the inserted object. * * When `'after'`, the document selection will move to the closest text node after the inserted object. If there is no * such text node, a paragraph will be created and the document selection will be moved inside it. * at the insertion position. */ insertObject(element: ModelElement, selectable?: ModelSelectable, placeOrOffset?: ModelPlaceOrOffset | null, options?: { findOptimalPosition?: 'auto' | 'before' | 'after'; setSelection?: 'on' | 'after'; }, ...rest: Array<unknown>): ModelRange; /** * Deletes content of the selection and merge siblings. The resulting selection is always collapsed. * * **Note:** For the sake of predictability, the resulting selection should always be collapsed. * In cases where a feature wants to modify deleting behavior so selection isn't collapsed * (e.g. a table feature may want to keep row selection after pressing <kbd>Backspace</kbd>), * then that behavior should be implemented in the view's listener. At the same time, the table feature * will need to modify this method's behavior too, e.g. to "delete contents and then collapse * the selection inside the last selected cell" or "delete the row and collapse selection somewhere near". * That needs to be done in order to ensure that other features which use `deleteContent()` will work well with tables. * * @fires deleteContent * @param selection Selection of which the content should be deleted. * @param options.leaveUnmerged Whether to merge elements after removing the content of the selection. * * For example `<heading1>x[x</heading1><paragraph>y]y</paragraph>` will become: * * * `<heading1>x^y</heading1>` with the option disabled (`leaveUnmerged == false`) * * `<heading1>x^</heading1><paragraph>y</paragraph>` with enabled (`leaveUnmerged == true`). * * Note: {@link module:engine/model/schema~ModelSchema#isObject object} and {@link module:engine/model/schema~ModelSchema#isLimit limit} * elements will not be merged. * * @param options.doNotResetEntireContent Whether to skip replacing the entire content with a * paragraph when the entire content was selected. * * For example `<heading1>[x</heading1><paragraph>y]</paragraph>` will become: * * * `<paragraph>^</paragraph>` with the option disabled (`doNotResetEntireContent == false`) * * `<heading1>^</heading1>` with enabled (`doNotResetEntireContent == true`) * * @param options.doNotAutoparagraph Whether to create a paragraph if after content deletion selection is moved * to a place where text cannot be inserted. * * For example `<paragraph>x</paragraph>[<imageBlock src="foo.jpg"></imageBlock>]` will become: * * * `<paragraph>x</paragraph><paragraph>[]</paragraph>` with the option disabled (`doNotAutoparagraph == false`) * * `<paragraph>x[]</paragraph>` with the option enabled (`doNotAutoparagraph == true`). * * **Note:** if there is no valid position for the selection, the paragraph will always be created: * * `[<imageBlock src="foo.jpg"></imageBlock>]` -> `<paragraph>[]</paragraph>`. * * @param options.direction The direction in which the content is being consumed. * Deleting backward corresponds to using the <kbd>Backspace</kbd> key, while deleting content forward corresponds to * the <kbd>Shift</kbd>+<kbd>Backspace</kbd> keystroke. */ deleteContent(selection: ModelSelection | ModelDocumentSelection, options?: { leaveUnmerged?: boolean; doNotResetEntireContent?: boolean; doNotAutoparagraph?: boolean; direction?: 'forward' | 'backward'; [i: string]: unknown; }): void; /** * Modifies the selection. Currently, the supported modifications are: * * * Extending. The selection focus is moved in the specified `options.direction` with a step specified in `options.unit`. * Possible values for `unit` are: * * `'character'` (default) - moves selection by one user-perceived character. In most cases this means moving by one * character in `String` sense. However, unicode also defines "combing marks". These are special symbols, that combines * with a symbol before it ("base character") to create one user-perceived character. For example, `q̣̇` is a normal * letter `q` with two "combining marks": upper dot (`Ux0307`) and lower dot (`Ux0323`). For most actions, i.e. extending * selection by one position, it is correct to include both "base character" and all of it's "combining marks". That is * why `'character'` value is most natural and common method of modifying selection. * * `'codePoint'` - moves selection by one unicode code point. In contrary to, `'character'` unit, this will insert * selection between "base character" and "combining mark", because "combining marks" have their own unicode code points. * However, for technical reasons, unicode code points with values above `UxFFFF` are represented in native `String` by * two characters, called "surrogate pairs". Halves of "surrogate pairs" have a meaning only when placed next to each other. * For example `𨭎` is represented in `String` by `\uD862\uDF4E`. Both `\uD862` and `\uDF4E` do not have any meaning * outside the pair (are rendered as ? when alone). Position between them would be incorrect. In this case, selection * extension will include whole "surrogate pair". * * `'word'` - moves selection by a whole word. * * **Note:** if you extend a forward selection in a backward direction you will in fact shrink it. * * @fires modifySelection * @param selection The selection to modify. * @param options.direction The direction in which the selection should be modified. * @param options.unit The unit by which selection should be modified. * @param options.treatEmojiAsSingleUnit Whether multi-characer emoji sequences should be handled as single unit. */ modifySelection(selection: ModelSelection | ModelDocumentSelection, options?: { direction?: 'forward' | 'backward'; unit?: 'character' | 'codePoint' | 'word'; treatEmojiAsSingleUnit?: boolean; }): void; /** * Gets a clone of the selected content. * * For example, for the following selection: * * ```html * <paragraph>x</paragraph> * <blockQuote> * <paragraph>y</paragraph> * <heading1>fir[st</heading1> * </blockQuote> * <paragraph>se]cond</paragraph> * <paragraph>z</paragraph> * ``` * * It will return a document fragment with such a content: * * ```html * <blockQuote> * <heading1>st</heading1> * </blockQuote> * <paragraph>se</paragraph> * ``` * * @fires getSelectedContent * @param selection The selection of which content will be returned. */ getSelectedContent(selection: ModelSelection | ModelDocumentSelection): ModelDocumentFragment; /** * Checks whether the given {@link module:engine/model/range~ModelRange range} or * {@link module:engine/model/element~ModelElement element} has any meaningful content. * * Meaningful content is: * * * any text node (`options.ignoreWhitespaces` allows controlling whether this text node must also contain * any non-whitespace characters), * * or any {@link module:engine/model/schema~ModelSchema#isContent content element}, * * or any {@link module:engine/model/markercollection~Marker marker} which * {@link module:engine/model/markercollection~Marker#_affectsData affects data}. * * This means that a range containing an empty `<paragraph></paragraph>` is not considered to have a meaningful content. * However, a range containing an `<imageBlock></imageBlock>` (which would normally be marked in the schema as an object element) * is considered non-empty. * * @param rangeOrElement Range or element to check. * @param options.ignoreWhitespaces Whether text node with whitespaces only should be considered empty. * @param options.ignoreMarkers Whether markers should be ignored. */ hasContent(rangeOrElement: ModelRange | ModelElement | ModelDocumentFragment, options?: { ignoreWhitespaces?: boolean; ignoreMarkers?: boolean; }): boolean; /** * Check whether given selectable is at a place in the model where it can be edited (returns `true`) or not (returns `false`). * * Should be used instead of {@link module:core/editor/editor~Editor#isReadOnly} to check whether a user action can happen at * given selectable. It may be decorated and used differently in different environment (e.g. multi-root editor can disable * a particular root). * * This method is decorated. Although this method accepts any parameter of `Selectable` type, the * {@link ~Model#event:canEditAt `canEditAt` event} is fired with `selectable` normalized to an instance of * {@link module:engine/model/selection~ModelSelection} or {@link module:engine/model/documentselection~ModelDocumentSelection} * * @fires canEditAt */ canEditAt(selectable: ModelSelectable): boolean; /** * Creates a position from the given root and path in that root. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createPositionFromPath `Writer#createPositionFromPath()`}. * * @param root Root of the position. * @param path Position path. See {@link module:engine/model/position~ModelPosition#path}. * @param stickiness Position stickiness. See {@link module:engine/model/position~ModelPositionStickiness}. */ createPositionFromPath(root: ModelElement | ModelDocumentFragment, path: ReadonlyArray<number>, stickiness?: ModelPositionStickiness): ModelPosition; /** * Creates position at the given location. The location can be specified as: * * * a {@link module:engine/model/position~ModelPosition position}, * * a parent element and offset in that element, * * a parent element and `'end'` (the position will be set at the end of that element), * * a {@link module:engine/model/item~ModelItem model item} and `'before'` or `'after'` * (the position will be set before or after the given model item). * * This method is a shortcut to other factory methods such as: * * * {@link module:engine/model/model~Model#createPositionBefore `createPositionBefore()`}, * * {@link module:engine/model/model~Model#createPositionAfter `createPositionAfter()`}. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createPositionAt `Writer#createPositionAt()`}, * * @param itemOrPosition * @param offset Offset or one of the flags. Used only when first parameter is a {@link module:engine/model/item~ModelItem model item}. */ createPositionAt(itemOrPosition: ModelItem | ModelPosition | ModelDocumentFragment, offset?: ModelPositionOffset): ModelPosition; /** * Creates a new position after the given {@link module:engine/model/item~ModelItem model item}. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createPositionAfter `Writer#createPositionAfter()`}. * * @param item Item after which the position should be placed. */ createPositionAfter(item: ModelItem): ModelPosition; /** * Creates a new position before the given {@link module:engine/model/item~ModelItem model item}. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createPositionBefore `Writer#createPositionBefore()`}. * * @param item Item before which the position should be placed. */ createPositionBefore(item: ModelItem): ModelPosition; /** * Creates a range spanning from the `start` position to the `end` position. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createRange `Writer#createRange()`}: * * ```ts * model.change( writer => { * const range = writer.createRange( start, end ); * } ); * ``` * * @param start Start position. * @param end End position. If not set, the range will be collapsed to the `start` position. */ createRange(start: ModelPosition, end?: ModelPosition): ModelRange; /** * Creates a range inside the given element which starts before the first child of * that element and ends after the last child of that element. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createRangeIn `Writer#createRangeIn()`}: * * ```ts * model.change( writer => { * const range = writer.createRangeIn( paragraph ); * } ); * ``` * * @param element Element which is a parent for the range. */ createRangeIn(element: ModelElement | ModelDocumentFragment): ModelRange; /** * Creates a range that starts before the given {@link module:engine/model/item~ModelItem model item} and ends after it. * * Note: This method is also available on `writer` instance as * {@link module:engine/model/writer~ModelWriter#createRangeOn `Writer.createRangeOn()`}: * * ```ts * model.change( writer => { * const range = writer.createRangeOn( paragraph ); * } ); * ``` * * @param item */ createRangeOn(item: ModelItem): ModelRange; /** * Creates a new selection instance based on the given {@link module:engine/model/selection~ModelSelectable selectable} * or creates an empty selection if no arguments were passed. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createSelection `Writer#createSelection()`}. * * ```ts * // Creates selection at the given offset in the given element. * const paragraph = writer.createElement( 'paragraph' ); * const selection = writer.createSelection( paragraph, offset ); * * // Creates a range inside an {@link module:engine/model/element~ModelElement element} which starts before the * // first child of that element and ends after the last child of that element. * const selection = writer.createSelection( paragraph, 'in' ); * * // Creates a range on an {@link module:engine/model/item~ModelItem item} which starts before the item and ends * // just after the item. * const selection = writer.createSelection( paragraph, 'on' ); * * // Additional options (`'backward'`) can be specified as the last argument. * * // Creates backward selection. * const selection = writer.createSelection( element, 'in', { backward: true } ); * ``` * * See also: {@link #createSelection:SELECTABLE `createSelection( selectable, options )`}. * * @label NODE_OFFSET */ createSelection(selectable: ModelNode, placeOrOffset: ModelPlaceOrOffset, options?: { backward?: boolean; }): ModelSelection; /** * Creates a new selection instance based on the given {@link module:engine/model/selection~ModelSelectable selectable} * or creates an empty selection if no arguments were passed. * * Note: This method is also available as * {@link module:engine/model/writer~ModelWriter#createSelection `Writer#createSelection()`}. * * ```ts * // Creates empty selection without ranges. * const selection = writer.createSelection(); * * // Creates selection at the given range. * const range = writer.createRange( start, end ); * const selection = writer.createSelection( range ); * * // Creates selection at the given ranges * const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ]; * const selection = writer.createSelection( ranges ); * * // Creates selection from the other selection. * // Note: It doesn't copies selection attributes. * const otherSelection = writer.createSelection(); * const selection = writer.createSelection( otherSelection ); * * // Creates selection from the given document selection. * // Note: It doesn't copies selection attributes. * const documentSelection = model.document.selection; * const selection = writer.createSelection( documentSelection ); * * // Creates selection at the given position. * const position = writer.createPositionFromPath( root, path ); * const selection = writer.createSelection( position ); * * // Additional options (`'backward'`) can be specified as the last argument. * * // Creates backward selection. * const selection = writer.createSelection( range, { backward: true } ); * ``` * * See also: {@link #createSelection:NODE_OFFSET `createSelection( node, placeOrOffset, options )`}. * * @label SELECTABLE */ createSelection(selectable?: Exclude<ModelSelectable, ModelNode>, options?: { backward?: boolean; }): ModelSelection; /** * Creates a {@link module:engine/model/batch~Batch} instance. * * **Note:** In most cases creating a batch instance is not necessary as they are created when using: * * * {@link #change `change()`}, * * {@link #enqueueChange `enqueueChange()`}. * * @param type {@link module:engine/model/batch~Batch#constructor The type} of the batch. */ createBatch(type?: BatchType): Batch; /** * Creates an operation instance from a JSON object (parsed JSON string). * * This is an alias for {@link module:engine/model/operation/operationfactory~OperationFactory.fromJSON `OperationFactory.fromJSON()`}. * * @param json Deserialized JSON object. */ createOperationFromJSON(json: unknown): Operation; /** * Removes all events listeners set by model instance and destroys {@link module:engine/model/document~ModelDocument}. */ destroy(): void; /** * Common part of {@link module:engine/model/model~Model#change} and {@link module:engine/model/model~Model#enqueueChange} * which calls callbacks and returns array of values returned by these callbacks. */ private _runPendingChanges; } /** * Fired when entering the outermost {@link module:engine/model/model~Model#enqueueChange} or * {@link module:engine/model/model~Model#change} block. * * @internal * @eventName ~Model#_beforeChanges */ export type BeforeChangesEvent = { name: '_beforeChanges'; args: []; }; /** * Fired when leaving the outermost {@link module:engine/model/model~Model#enqueueChange} or * {@link module:engine/model/model~Model#change} block. * * @internal * @eventName ~Model#_afterChanges */ export type AfterChangesEvent = { name: '_afterChanges'; args: []; }; /** * Fired every time any {@link module:engine/model/operation/operation~Operation operation} is applied on the model * using {@link ~Model#applyOperation}. * * Note that this event is suitable only for very specific use-cases. Use it if you need to listen to every single operation * applied on the document. However, in most cases {@link module:engine/model/document~ModelDocument#event:change} should * be used. * * A few callbacks are already added to this event by engine internal classes: * * * with `highest` priority operation is validated, * * with `normal` priority operation is executed, * * with `low` priority the {@link module:engine/model/document~ModelDocument} updates its version, * * with `low` priority {@link module:engine/model/liveposition~ModelLivePosition} and {@link module:engine/model/liverange~ModelLiveRange} * update themselves. * * @eventName ~Model#applyOperation * @param args Arguments of the `applyOperation` which is an array with a single element - applied * {@link module:engine/model/operation/operation~Operation operation}. */ export type ModelApplyOperationEvent = DecoratedMethodEvent<Model, 'applyOperation'>; /** * Event fired when {@link ~Model#insertContent} method is called. * * The {@link ~Model#insertContent default action of that method} is implemented as a * listener to this event so it can be fully customized by the features. * * **Note** The `selectable` parameter for the {@link ~Model#insertContent} is optional. When `undefined` value is passed the method uses * {@link module:engine/model/document~ModelDocument#selection document selection}. * * @eventName ~Model#insertContent * @param args The arguments passed to the original method. */ export type ModelInsertContentEvent = { name: 'insertContent'; args: [ [ content: ModelItem | ModelDocumentFragment, selectable?: ModelSelection | ModelDocumentSelection, ...rest: Array<unknown> ] ]; return: ModelRange; }; /** * Event fired when the {@link ~Model#insertObject} method is called. * * The {@link ~Model#insertObject default action of that method} is implemented as a * listener to this event so it can be fully customized by the features. * * **Note** The `selectable` parameter for the {@link ~Model#insertObject} is optional. When `undefined` value is passed the method uses * {@link module:engine/model/document~ModelDocument#selection document selection}. * * @eventName ~Model#insertObject * @param args The arguments passed to the original method. */ export type ModelInsertObjectEvent = { name: 'insertObject'; args: [ [ element: ModelElement, selectable?: ModelSelection | ModelDocumentSelection | null, options?: { findOptimalPosition?: 'auto' | 'before' | 'after'; setSelection?: 'on' | 'after'; }, ...rest: Array<unknown> ] ]; return: ModelRange; }; /** * Event fired when {@link ~Model#deleteContent} method is called. * * The {@link ~Model#deleteContent default action of that method} is implemented as a * listener to this event, so it can be fully customized by the features. * * @eventName ~Model#deleteContent * @param args The arguments passed to the original method. */ export type ModelDeleteContentEvent = DecoratedMethodEvent<Model, 'deleteContent'>; /** * Event fired when {@link ~Model#modifySelection} method is called. * * The {@link ~Model#modifySelection default action of that method} is implemented as a * listener to this event, so it can be fully customized by the features. * * @eventName ~Model#modifySelection * @param args The arguments passed to the original method. */ export type ModelModifySelectionEvent = DecoratedMethodEvent<Model, 'modifySelection'>; /** * Event fired when {@link ~Model#getSelectedContent} method is called. * * The {@link ~Model#getSelectedContent default action of that method} is implemented as a * listener to this event, so it can be fully customized by the features. * * @eventName ~Model#getSelectedContent * @param args The arguments passed to the original method. */ export type ModelGetSelectedContentEvent = DecoratedMethodEvent<Model, 'getSelectedContent'>; /** * Event fired when {@link ~Model#canEditAt} method is called. * * The {@link ~Model#canEditAt default action of that method} is implemented as a * listener to this event, so it can be fully customized by the features. * * Although the original method accepts any parameter of `Selectable` type, this event is fired with `selectable` normalized * to an instance of {@link module:engine/model/selection~ModelSelection} or * {@link module:engine/model/documentselection~ModelDocumentSelection}. * * @eventName ~Model#canEditAt * @param args The arguments passed to the original method. */ export type ModelCanEditAtEvent = { name: 'canEditAt'; args: [ [ selectable?: ModelSelection | ModelDocumentSelection ] ]; return: boolean; }; export {};