Socket
Socket
Sign inDemoInstall

@ckeditor/ckeditor5-engine

Package Overview
Dependencies
Maintainers
1
Versions
584
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 21.0.0 to 22.0.0

src/model/utils/autoparagraphing.js

34

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

@@ -24,21 +24,21 @@ "keywords": [

"dependencies": {
"@ckeditor/ckeditor5-utils": "^21.0.0",
"@ckeditor/ckeditor5-utils": "^22.0.0",
"lodash-es": "^4.17.15"
},
"devDependencies": {
"@ckeditor/ckeditor5-basic-styles": "^21.0.0",
"@ckeditor/ckeditor5-block-quote": "^21.0.0",
"@ckeditor/ckeditor5-core": "^21.0.0",
"@ckeditor/ckeditor5-editor-classic": "^21.0.0",
"@ckeditor/ckeditor5-enter": "^21.0.0",
"@ckeditor/ckeditor5-essentials": "^21.0.0",
"@ckeditor/ckeditor5-heading": "^21.0.0",
"@ckeditor/ckeditor5-link": "^21.0.0",
"@ckeditor/ckeditor5-list": "^21.0.0",
"@ckeditor/ckeditor5-paragraph": "^21.0.0",
"@ckeditor/ckeditor5-table": "^21.0.0",
"@ckeditor/ckeditor5-theme-lark": "^21.0.0",
"@ckeditor/ckeditor5-typing": "^21.0.0",
"@ckeditor/ckeditor5-undo": "^21.0.0",
"@ckeditor/ckeditor5-widget": "^21.0.0"
"@ckeditor/ckeditor5-basic-styles": "^22.0.0",
"@ckeditor/ckeditor5-block-quote": "^22.0.0",
"@ckeditor/ckeditor5-core": "^22.0.0",
"@ckeditor/ckeditor5-editor-classic": "^22.0.0",
"@ckeditor/ckeditor5-enter": "^22.0.0",
"@ckeditor/ckeditor5-essentials": "^22.0.0",
"@ckeditor/ckeditor5-heading": "^22.0.0",
"@ckeditor/ckeditor5-link": "^22.0.0",
"@ckeditor/ckeditor5-list": "^22.0.0",
"@ckeditor/ckeditor5-paragraph": "^22.0.0",
"@ckeditor/ckeditor5-table": "^22.0.0",
"@ckeditor/ckeditor5-theme-lark": "^22.0.0",
"@ckeditor/ckeditor5-typing": "^22.0.0",
"@ckeditor/ckeditor5-undo": "^22.0.0",
"@ckeditor/ckeditor5-widget": "^22.0.0"
},

@@ -45,0 +45,0 @@ "engines": {

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

import ModelRange from '../model/range';
import { autoParagraphEmptyRoots } from '../model/utils/autoparagraphing';

@@ -130,4 +131,4 @@ /**

// Note that if there is no default converter for the element it will be skipped, for instance `<b>foo</b>` will be
// converted to nothing. We add `convertToModelFragment` as a last converter so it converts children of that
// element to the document fragment so `<b>foo</b>` will be converted to `foo` if there is no converter for `<b>`.
// converted to nothing. We therefore add `convertToModelFragment` as a last converter so it converts children of that
// element to the document fragment and so `<b>foo</b>` will be converted to `foo` if there is no converter for `<b>`.
this.upcastDispatcher.on( 'text', convertText(), { priority: 'lowest' } );

@@ -140,7 +141,13 @@ this.upcastDispatcher.on( 'element', convertToModelFragment(), { priority: 'lowest' } );

// Fire `ready` event when initialisation has completed. Such low level listener gives possibility
// to plug into initialisation pipeline without interrupting the initialisation flow.
// Fire the `ready` event when the initialization has completed. Such low-level listener gives possibility
// to plug into the initialization pipeline without interrupting the initialization flow.
this.on( 'init', () => {
this.fire( 'ready' );
}, { priority: 'lowest' } );
// Fix empty roots after DataController is 'ready' (note that init method could be decorated and stopped).
// We need to handle this event because initial data could be empty and post-fixer would not get triggered.
this.on( 'ready', () => {
this.model.enqueueChange( 'transparent', autoParagraphEmptyRoots );
}, { priority: 'lowest' } );
}

@@ -152,3 +159,4 @@

*
* @param {Object} [options]
* @param {Object} [options] Additional configuration for the retrieved data. `DataController` provides two optional
* properties: `rootName` and `trim`. Other properties of this object are specified by various editor features.
* @param {String} [options.rootName='main'] Root name.

@@ -160,4 +168,4 @@ * @param {String} [options.trim='empty'] Whether returned data should be trimmed. This option is set to `empty` by default,

*/
get( options ) {
const { rootName = 'main', trim = 'empty' } = options || {};
get( options = {} ) {
const { rootName = 'main', trim = 'empty' } = options;

@@ -185,3 +193,3 @@ if ( !this._checkIfRootsExists( [ rootName ] ) ) {

return this.stringify( root );
return this.stringify( root, options );
}

@@ -196,7 +204,8 @@

* Element whose content will be stringified.
* @param {Object} [options] Additional configuration passed to the conversion process.
* @returns {String} Output data.
*/
stringify( modelElementOrFragment ) {
stringify( modelElementOrFragment, options ) {
// Model -> view.
const viewDocumentFragment = this.toView( modelElementOrFragment );
const viewDocumentFragment = this.toView( modelElementOrFragment, options );

@@ -215,5 +224,7 @@ // View -> data.

* Element or document fragment whose content will be converted.
* @param {Object} [options] Additional configuration that will be available through
* {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi#options} during the conversion process.
* @returns {module:engine/view/documentfragment~DocumentFragment} Output view DocumentFragment.
*/
toView( modelElementOrFragment ) {
toView( modelElementOrFragment, options ) {
const viewDocument = this.viewDocument;

@@ -231,3 +242,6 @@ const viewWriter = this._viewWriter;

// We have no view controller and rendering do DOM in DataController so view.change() block is not used here.
// Make additional options available during conversion process through `conversionApi`.
this.downcastDispatcher.conversionApi.options = options;
// We have no view controller and rendering to DOM in DataController so view.change() block is not used here.
this.downcastDispatcher.convertInsert( modelRange, viewWriter );

@@ -245,2 +259,5 @@

// Clean `conversionApi`.
delete this.downcastDispatcher.conversionApi.options;
return viewDocumentFragment;

@@ -452,3 +469,3 @@ }

/**
* Event fired once data initialisation has finished.
* Event fired once the data initialization has finished.
*

@@ -459,7 +476,7 @@ * @event ready

/**
* Event fired after {@link #init init() method} has been run. It can be {@link #listenTo listened to} to adjust/modify
* the initialisation flow. However, if the `init` event is stopped or prevented, the {@link #event:ready ready event}
* Event fired after the {@link #init `init()` method} was run. It can be {@link #listenTo listened to} in order to adjust or modify
* the initialization flow. However, if the `init` event is stopped or prevented, the {@link #event:ready `ready` event}
* should be fired manually.
*
* The `init` event is fired by decorated {@link #init} method.
* The `init` event is fired by the decorated {@link #init} method.
* See {@link module:utils/observablemixin~ObservableMixin#decorate} for more information and samples.

@@ -466,0 +483,0 @@ *

@@ -76,3 +76,3 @@ /**

// When plugins listen on model changes (on selection change, post fixers, etc) and change the view as a result of
// When plugins listen on model changes (on selection change, post fixers, etc.) and change the view as a result of
// model's change, they might trigger view rendering before the conversion is completed (e.g. before the selection

@@ -79,0 +79,0 @@ // is converted). We disable rendering for the length of the outermost model change() block to prevent that.

@@ -14,70 +14,69 @@ /**

import mix from '@ckeditor/ckeditor5-utils/src/mix';
import { extend } from 'lodash-es';
/**
* `DowncastDispatcher` is a central point of downcasting (conversion from model to view), which is a process of reacting to changes
* in the model and firing a set of events. Callbacks listening to those events are called converters. Those
* converters role is to convert the model changes to changes in view (for example, adding view nodes or
* Downcast dispatcher is a central point of downcasting (conversion from the model to the view), which is a process of reacting to changes
* in the model and firing a set of events. Callbacks listening to these events are called converters. The
* converters' role is to convert the model changes to changes in view (for example, adding view nodes or
* changing attributes on view elements).
*
* During conversion process, `DowncastDispatcher` fires events, basing on state of the model and prepares
* data for those events. It is important to understand that those events are connected with changes done on model,
* for example: "node has been inserted" or "attribute has changed". This is in a contrary to upcasting (view to model conversion),
* where we convert view state (view nodes) to a model tree.
* During the conversion process, downcast dispatcher fires events basing on the state of the model and prepares
* data for these events. It is important to understand that the events are connected with the changes done on the model,
* for example: "a node has been inserted" or "an attribute has changed". This is in contrary to upcasting (a view-to-model conversion)
* where you convert the view state (view nodes) to a model tree.
*
* The events are prepared basing on a diff created by {@link module:engine/model/differ~Differ Differ}, which buffers them
* and then passes to `DowncastDispatcher` as a diff between old model state and new model state.
* and then passes to the downcast dispatcher as a diff between the old model state and the new model state.
*
* Note, that because changes are converted there is a need to have a mapping between model structure and view structure.
* To map positions and elements during downcast (model to view conversion) use {@link module:engine/conversion/mapper~Mapper}.
* Note that because the changes are converted, there is a need to have a mapping between the model structure and the view structure.
* To map positions and elements during the downcast (a model-to-view conversion), use {@link module:engine/conversion/mapper~Mapper}.
*
* `DowncastDispatcher` fires following events for model tree changes:
* Downcast dispatcher fires the following events for model tree changes:
*
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert insert}
* if a range of nodes has been inserted to the model tree,
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:remove remove}
* if a range of nodes has been removed from the model tree,
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute attribute}
* if attribute has been added, changed or removed from a model node.
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert `insert`} &ndash;
* If a range of nodes was inserted to the model tree.
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:remove `remove`} &ndash;
* If a range of nodes was removed from the model tree.
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute`} &ndash;
* If an attribute was added, changed or removed from a model node.
*
* For {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert insert}
* and {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute attribute},
* `DowncastDispatcher` generates {@link module:engine/conversion/modelconsumable~ModelConsumable consumables}.
* These are used to have a control over which changes has been already consumed. It is useful when some converters
* overwrite other or converts multiple changes (for example converts insertion of an element and also converts that
* element's attributes during insertion).
* For {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert `insert`}
* and {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute`},
* downcast dispatcher generates {@link module:engine/conversion/modelconsumable~ModelConsumable consumables}.
* These are used to have control over which changes have already been consumed. It is useful when some converters
* overwrite others or convert multiple changes (for example, it converts an insertion of an element and also converts that
* element's attributes during the insertion).
*
* Additionally, `DowncastDispatcher` fires events for {@link module:engine/model/markercollection~Marker marker} changes:
* Additionally, downcast dispatcher fires events for {@link module:engine/model/markercollection~Marker marker} changes:
*
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker} if a marker has been added,
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:removeMarker} if a marker has been removed.
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker} &ndash; If a marker was added.
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:removeMarker} &ndash; If a marker was removed.
*
* Note, that changing a marker is done through removing the marker from the old range, and adding on the new range,
* so both those events are fired.
* Note that changing a marker is done through removing the marker from the old range and adding it on the new range,
* so both events are fired.
*
* Finally, `DowncastDispatcher` also handles firing events for {@link module:engine/model/selection model selection}
* Finally, downcast dispatcher also handles firing events for the {@link module:engine/model/selection model selection}
* conversion:
*
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:selection}
* which converts selection from model to view,
* &ndash; Converts the selection from the model to the view.
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute}
* which is fired for every selection attribute,
* &ndash; Fired for every selection attribute.
* * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}
* which is fired for every marker which contains selection.
* &ndash; Fired for every marker that contains a selection.
*
* Unlike model tree and markers, events for selection are not fired for changes but for selection state.
*
* When providing custom listeners for `DowncastDispatcher` remember to check whether given change has not been
* When providing custom listeners for downcast dispatcher, remember to check whether a given change has not been
* {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed} yet.
*
* When providing custom listeners for `DowncastDispatcher` keep in mind that any callback that had
* When providing custom listeners for downcast dispatcher, keep in mind that any callback that has
* {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed} a value from a consumable and
* converted the change should also stop the event (for efficiency purposes).
*
* When providing custom listeners for `DowncastDispatcher` remember to use provided
* When providing custom listeners for downcast dispatcher, remember to use the provided
* {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer} to apply changes to the view document.
*
* Example of a custom converter for `DowncastDispatcher`:
* An example of a custom converter for the downcast dispatcher:
*
* // We will convert inserting "paragraph" model element into the model.
* // You will convert inserting a "paragraph" model element into the model.
* downcastDispatcher.on( 'insert:paragraph', ( evt, data, conversionApi ) => {

@@ -89,9 +88,9 @@ * // Remember to check whether the change has not been consumed yet and consume it.

*
* // Translate position in model to position in view.
* // Translate the position in the model to a position in the view.
* const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
*
* // Create <p> element that will be inserted in view at `viewPosition`.
* // Create a <p> element that will be inserted into the view at the `viewPosition`.
* const viewElement = conversionApi.writer.createContainerElement( 'p' );
*
* // Bind the newly created view element to model element so positions will map accordingly in future.
* // Bind the newly created view element to the model element so positions will map accordingly in the future.
* conversionApi.mapper.bindElements( data.item, viewElement );

@@ -108,23 +107,23 @@ *

/**
* Creates a `DowncastDispatcher` instance.
* Creates a downcast dispatcher instance.
*
* @see module:engine/conversion/downcastdispatcher~DowncastConversionApi
* @param {Object} conversionApi Additional properties for interface that will be passed to events fired
* by `DowncastDispatcher`.
* @param {Object} conversionApi Additional properties for an interface that will be passed to events fired
* by the downcast dispatcher.
*/
constructor( conversionApi ) {
/**
* Interface passed by dispatcher to the events callbacks.
* An interface passed by the dispatcher to the event callbacks.
*
* @member {module:engine/conversion/downcastdispatcher~DowncastConversionApi}
*/
this.conversionApi = extend( { dispatcher: this }, conversionApi );
this.conversionApi = Object.assign( { dispatcher: this }, conversionApi );
}
/**
* Takes {@link module:engine/model/differ~Differ model differ} object with buffered changes and fires conversion basing on it.
* Takes a {@link module:engine/model/differ~Differ model differ} object with buffered changes and fires conversion basing on it.
*
* @param {module:engine/model/differ~Differ} differ Differ object with buffered changes.
* @param {module:engine/model/markercollection~MarkerCollection} markers Markers connected with converted model.
* @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.
* @param {module:engine/model/differ~Differ} differ The differ object with buffered changes.
* @param {module:engine/model/markercollection~MarkerCollection} markers Markers connected with the converted model.
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer that should be used to modify the view document.
*/

@@ -163,11 +162,11 @@ convertChanges( differ, markers, writer ) {

/**
* Starts conversion of a range insertion.
* Starts a conversion of a range insertion.
*
* For each node in the range, {@link #event:insert insert event is fired}. For each attribute on each node,
* {@link #event:attribute attribute event is fired}.
* For each node in the range, {@link #event:insert `insert` event is fired}. For each attribute on each node,
* {@link #event:attribute `attribute` event is fired}.
*
* @fires insert
* @fires attribute
* @param {module:engine/model/range~Range} range Inserted range.
* @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.
* @param {module:engine/model/range~Range} range The inserted range.
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer that should be used to modify the view document.
*/

@@ -561,3 +560,3 @@ convertInsert( range, writer ) {

/**
* Fired when a new marker is added to the model. Also fired when collapsed model selection that is inside marker is converted.
* Fired when a new marker is added to the model. Also fired when collapsed model selection that is inside a marker is converted.
*

@@ -642,3 +641,3 @@ * `addMarker` is a namespace for a class of events. Names of actually called events follow this pattern:

* and is passed as one of parameters when {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher dispatcher}
* fires it's events.
* fires its events.
*

@@ -678,1 +677,7 @@ * @interface module:engine/conversion/downcastdispatcher~DowncastConversionApi

*/
/**
* An object with an additional configuration which can be used during conversion process. Available only for data downcast conversion.
*
* @member {Object} #options
*/

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

import { SchemaContext } from '../model/schema';
import { isParagraphable, wrapInParagraph } from '../model/utils/autoparagraphing';

@@ -21,25 +22,31 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';

/**
* `UpcastDispatcher` is a central point of {@link module:engine/view/view view} conversion, which is a process of
* `UpcastDispatcher` is a central point of the view to model conversion, which is a process of
* converting given {@link module:engine/view/documentfragment~DocumentFragment view document fragment} or
* {@link module:engine/view/element~Element} into another structure.
* In default application, {@link module:engine/view/view view} is converted to {@link module:engine/model/model}.
* {@link module:engine/view/element~Element view element} into a correct model structure.
*
* During conversion process, for all {@link module:engine/view/node~Node view nodes} from the converted view document fragment,
* `UpcastDispatcher` fires corresponding events. Special callbacks called "converters" should listen to
* `UpcastDispatcher` for those events.
* During the conversion process, the dispatcher fires events for all {@link module:engine/view/node~Node view nodes}
* from the converted view document fragment.
* Special callbacks called "converters" should listen to these events in order to convert these view nodes.
*
* Each callback, as the second argument, is passed a special object `data` that has `viewItem`, `modelCursor` and
* `modelRange` properties. `viewItem` property contains {@link module:engine/view/node~Node view node} or
* The second parameter of the callback is the `data` object with the following properties:
*
* * `data.viewItem` contains {@link module:engine/view/node~Node view node} or
* {@link module:engine/view/documentfragment~DocumentFragment view document fragment}
* that is converted at the moment and might be handled by the callback. `modelRange` property should be used to save the result
* of conversion and is always a {@link module:engine/model/range~Range} when conversion result is correct.
* `modelCursor` property is a {@link module:engine/model/position~Position position} on which conversion result will be inserted
* and is a context according to {@link module:engine/model/schema~Schema schema} will be checked before the conversion.
* See also {@link ~UpcastDispatcher#convert}. It is also shared by reference by all callbacks listening to given event.
* that is converted at the moment and might be handled by the callback.
* * `data.modelRange` is used to point to the result
* of the current conversion (e.g. the element that is being inserted)
* and is always a {@link module:engine/model/range~Range} when the succeeds.
* * `data.modelCursor` is a {@link module:engine/model/position~Position position} on which the converter should insert
* newly created items.
*
* The third parameter passed to a callback is an instance of {@link ~UpcastDispatcher}
* The third parameter of the callback is an instance of {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi}
* which provides additional tools for converters.
*
* Examples of providing callbacks for `UpcastDispatcher`:
* You can read more about conversion in the following guides:
*
* * {@glink framework/guides/deep-dive/conversion/conversion-introduction Advanced conversion concepts &mdash; attributes}
* * {@glink framework/guides/deep-dive/conversion/custom-element-conversion Custom element conversion}
*
* Examples of event-based converters:
*
* // Converter for links (<a>).

@@ -81,39 +88,28 @@ * editor.data.upcastDispatcher.on( 'element:a', ( evt, data, conversionApi ) => {

*
* // Convert all elements which have no custom converter into paragraph (autoparagraphing).
* editor.data.upcastDispatcher.on( 'element', ( evt, data, conversionApi ) => {
* // When element is already consumed by higher priority converters then do nothing.
* if ( conversionApi.consumable.test( data.viewItem, { name: data.viewItem.name } ) ) {
* const paragraph = conversionApi.writer.createElement( 'paragraph' );
* // Convert all elements which have no custom converter into a paragraph (autoparagraphing).
* editor.data.upcastDispatcher.on( 'element', ( evt, data, conversionApi ) => {
* // Check if element can be converted.
* if ( !conversionApi.consumable.test( data.viewItem, { name: data.viewItem.name } ) ) {
* // When element is already consumed by higher priority converters then do nothing.
* return;
* }
*
* // Find allowed parent for paragraph that we are going to insert. If current parent does not allow
* // to insert paragraph but one of the ancestors does then split nodes to allowed parent.
* const splitResult = conversionApi.splitToAllowedParent( paragraph, data.modelCursor );
* const paragraph = conversionApi.writer.createElement( 'paragraph' );
*
* // When there is no split result it means that we can't insert paragraph in this position.
* if ( splitResult ) {
* // Insert paragraph in allowed position.
* conversionApi.writer.insert( paragraph, splitResult.position );
* // Try to safely insert paragraph at model cursor - it will find an allowed parent for a current element.
* if ( !conversionApi.safeInsert( paragraph, data.modelCursor ) ) {
* // When element was not inserted it means that we can't insert paragraph at this position.
* return;
* }
*
* // Convert children to paragraph.
* const { modelRange } = conversionApi.convertChildren(
* data.viewItem,
* conversionApi.writer.createPositionAt( paragraph, 0 )
* );
* // Consume the inserted element.
* conversionApi.consumable.consume( data.viewItem, { name: data.viewItem.name } ) );
*
* // Set as conversion result, attribute converters may use this property.
* data.modelRange = conversionApi.writer.createRange(
* conversionApi.writer.createPositionBefore( paragraph ),
* modelRange.end
* );
* // Convert children to paragraph.
* const { modelRange } = conversionApi.convertChildren( data.viewItem, paragraph ) );
*
* // Continue conversion inside paragraph.
* data.modelCursor = data.modelRange.end;
* }
* }
* }
* }, { priority: 'low' } );
* // Update `modelRange` and `modelCursor` in a `data` as a conversion result.
* conversionApi.updateConversionResult( paragraph, data );
* }, { priority: 'low' } );
*
* Before each conversion process, `UpcastDispatcher` fires {@link ~UpcastDispatcher#event:viewCleanup}
* event which can be used to prepare tree view for conversion.
*
* @mixes module:utils/emittermixin~EmitterMixin

@@ -145,2 +141,12 @@ * @fires viewCleanup

/**
* List of cursor parent elements that were created during splitting.
*
* After conversion process the list is cleared.
*
* @private
* @type {Map.<module:engine/model/element~Element,Array.<module:engine/model/element~Element>>}
*/
this._cursorParents = new Map();
/**
* Position in the temporary structure where the converted content is inserted. The structure reflect the context of

@@ -162,6 +168,9 @@ * the target position where the content will be inserted. This property is build based on the context parameter of the

// `convertItem`, `convertChildren` and `splitToAllowedParent` are bound to this `UpcastDispatcher`
// instance and set on `conversionApi`. This way only a part of `UpcastDispatcher` API is exposed.
// The below methods are bound to this `UpcastDispatcher` instance and set on `conversionApi`.
// This way only a part of `UpcastDispatcher` API is exposed.
this.conversionApi.convertItem = this._convertItem.bind( this );
this.conversionApi.convertChildren = this._convertChildren.bind( this );
this.conversionApi.safeInsert = this._safeInsert.bind( this );
this.conversionApi.updateConversionResult = this._updateConversionResult.bind( this );
// Advanced API - use only if custom position handling is needed.
this.conversionApi.splitToAllowedParent = this._splitToAllowedParent.bind( this );

@@ -225,4 +234,5 @@ this.conversionApi.getSplitParts = this._getSplitParts.bind( this );

// Clear split elements lists.
// Clear split elements & parents lists.
this._splitParts.clear();
this._cursorParents.clear();

@@ -271,6 +281,8 @@ // Clear conversion API.

*/
_convertChildren( viewItem, modelCursor ) {
const modelRange = new ModelRange( modelCursor );
let nextModelCursor = modelCursor;
_convertChildren( viewItem, elementOrModelCursor ) {
let nextModelCursor = elementOrModelCursor.is( 'position' ) ?
elementOrModelCursor : ModelPosition._createAt( elementOrModelCursor, 0 );
const modelRange = new ModelRange( nextModelCursor );
for ( const viewChild of Array.from( viewItem.getChildren() ) ) {

@@ -290,21 +302,86 @@ const result = this._convertItem( viewChild, nextModelCursor );

* @private
* @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#safeInsert
*/
_safeInsert( modelElement, position ) {
// Find allowed parent for element that we are going to insert.
// If current parent does not allow to insert element but one of the ancestors does
// then split nodes to allowed parent.
const splitResult = this._splitToAllowedParent( modelElement, position );
// When there is no split result it means that we can't insert element to model tree, so let's skip it.
if ( !splitResult ) {
return false;
}
// Insert element on allowed position.
this.conversionApi.writer.insert( modelElement, splitResult.position );
return true;
}
/**
* @private
* @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#updateConversionResult
*/
_updateConversionResult( modelElement, data ) {
const parts = this._getSplitParts( modelElement );
const writer = this.conversionApi.writer;
// Set conversion result range - only if not set already.
if ( !data.modelRange ) {
data.modelRange = writer.createRange(
writer.createPositionBefore( modelElement ),
writer.createPositionAfter( parts[ parts.length - 1 ] )
);
}
const savedCursorParent = this._cursorParents.get( modelElement );
// Now we need to check where the `modelCursor` should be.
if ( savedCursorParent ) {
// If we split parent to insert our element then we want to continue conversion in the new part of the split parent.
//
// before: <allowed><notAllowed>foo[]</notAllowed></allowed>
// after: <allowed><notAllowed>foo</notAllowed> <converted></converted> <notAllowed>[]</notAllowed></allowed>
data.modelCursor = writer.createPositionAt( savedCursorParent, 0 );
} else {
// Otherwise just continue after inserted element.
data.modelCursor = data.modelRange.end;
}
}
/**
* @private
* @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#splitToAllowedParent
*/
_splitToAllowedParent( node, modelCursor ) {
const { schema, writer } = this.conversionApi;
// Try to find allowed parent.
const allowedParent = this.conversionApi.schema.findAllowedParent( modelCursor, node );
let allowedParent = schema.findAllowedParent( modelCursor, node );
// When there is no parent that allows to insert node then return `null`.
if ( !allowedParent ) {
return null;
}
if ( allowedParent ) {
// When current position parent allows to insert node then return this position.
if ( allowedParent === modelCursor.parent ) {
return { position: modelCursor };
}
// When current position parent allows to insert node then return this position.
if ( allowedParent === modelCursor.parent ) {
return { position: modelCursor };
// When allowed parent is in context tree (it's outside the converted tree).
if ( this._modelCursor.parent.getAncestors().includes( allowedParent ) ) {
allowedParent = null;
}
}
// When allowed parent is in context tree.
if ( this._modelCursor.parent.getAncestors().includes( allowedParent ) ) {
return null;
if ( !allowedParent ) {
// Check if the node wrapped with a paragraph would be accepted by the schema.
if ( !isParagraphable( modelCursor, node, schema ) ) {
return null;
}
return {
position: wrapInParagraph( modelCursor, writer )
};
}

@@ -343,5 +420,8 @@

const cursorParent = splitResult.range.end.parent;
this._cursorParents.set( node, cursorParent );
return {
position: splitResult.position,
cursorParent: splitResult.range.end.parent
cursorParent
};

@@ -427,10 +507,5 @@ }

* @event element
* @param {Object} data Conversion data. Keep in mind that this object is shared by reference between all
* callbacks that will be called. This means that callbacks can override values if needed, and those values will
* be available in other callbacks.
* @param {module:engine/view/item~Item} data.viewItem Converted item.
* @param {module:engine/model/position~Position} data.modelCursor Position where a converter should start changes.
* Change this value for the next converter to tell where the conversion should continue.
* @param {module:engine/model/range~Range} data.modelRange The current state of conversion result. Every change to
* converted element should be reflected by setting or modifying this property.
* @param {module:engine/conversion/upcastdispatcher~UpcastConversionData} data Conversion data. Keep in mind that this object is shared
* by reference between all callbacks that will be called. This means that callbacks can override values if needed, and those values
* will be available in other callbacks.
* @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by callback.

@@ -520,5 +595,4 @@ */

/**
* Conversion interface that is registered for given {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher}
* and is passed as one of parameters when {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher dispatcher}
* fires it's events.
* A set of conversion utils available as the third parameter of
* {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher upcast dispatcher}'s events.
*

@@ -555,3 +629,3 @@ * @interface module:engine/conversion/upcastdispatcher~UpcastConversionApi

* @param {module:engine/view/item~Item} viewItem Element which children should be converted.
* @param {module:engine/model/position~Position} modelCursor Position of conversion.
* @param {module:engine/model/position~Position|module:engine/model/element~Element} positionOrElement Position or element of conversion.
* @returns {Object} result Conversion result.

@@ -564,2 +638,63 @@ * @returns {module:engine/model/range~Range} result.modelRange Model range containing results of conversion of all children of given item.

/**
* Safely inserts an element to the document checking {@link module:engine/model/schema~Schema schema} to find allowed parent for
* an element that we are going to insert starting from given position. If current parent does not allow to insert element
* but one of the ancestors does then split nodes to allowed parent.
*
* If schema allows to insert node in given position, nothing is split.
*
* If it was not possible to find allowed parent, `false` is returned, nothing is split.
*
* Otherwise, ancestors are split.
*
* For instance, if `<image>` is not allowed in `<paragraph>` but is allowed in `$root`:
*
* <paragraph>foo[]bar</paragraph>
*
* -> safe insert for `<image>` will split ->
*
* <paragraph>foo</paragraph>[]<paragraph>bar</paragraph>
*
* Example usage:
*
* const myElement = conversionApi.writer.createElement( 'myElement' );
*
* if ( !conversionApi.safeInsert( myElement, data.modelCursor ) ) {
* return;
* }
*
* The split result is saved and {@link #updateConversionResult} should be used to update
* {@link module:engine/conversion/upcastdispatcher~UpcastConversionData conversion data}.
*
* @method #safeInsert
* @param {module:engine/model/node~Node} node Node to insert.
* @param {module:engine/model/position~Position} position Position on which element is going to be inserted.
* @returns {Boolean} Split result. If it was not possible to find allowed position `false` is returned.
*/
/**
* Updates the conversion result and sets proper {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelRange} and
* next {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelCursor} after the conversion.
* Used together with {@link #safeInsert} enables you to easily convert elements without worrying if the node was split
* during its children conversion.
*
* Example of a usage in a converter code:
*
* const myElement = conversionApi.writer.createElement( 'myElement' );
*
* if ( !conversionApi.safeInsert( myElement, data.modelCursor ) ) {
* return;
* }
*
* // Children conversion may split `myElement`.
* conversionApi.convertChildren( data.viewItem, myElement );
*
* conversionApi.updateConversionResult( myElement, data );
*
* @method #updateConversionResult
* @param {module:engine/model/element~Element} element
* @param {module:engine/conversion/upcastdispatcher~UpcastConversionData} data Conversion data.
* @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by callback.
*/
/**
* Checks {@link module:engine/model/schema~Schema schema} to find allowed parent for element that we are going to insert

@@ -579,5 +714,5 @@ * starting from given position. If current parent does not allow to insert element but one of the ancestors does then

*
* -> split for `<image>` ->
* -> split for `<image>` ->
*
* <paragraph>foo</paragraph>[]<paragraph>bar</paragraph>
* <paragraph>foo</paragraph>[]<paragraph>bar</paragraph>
*

@@ -587,2 +722,4 @@ * In the sample above position between `<paragraph>` elements will be returned as `position` and the second `paragraph`

*
* **Note:** This is an advanced method. For most cases {@link #safeInsert} and {@link #updateConversionResult} should be used.
*
* @method #splitToAllowedParent

@@ -614,3 +751,3 @@ * @param {module:engine/model/position~Position} position Position on which element is going to be inserted.

* // Children conversion may split `myElement`.
* conversionApi.convertChildren( myElement, modelCursor );
* conversionApi.convertChildren( data.viewItem, data.modelCursor );
*

@@ -633,2 +770,4 @@ * const splitParts = conversionApi.getSplitParts( myElement );

*
* **Note:** This is an advanced method. For most cases {@link #safeInsert} and {@link #updateConversionResult} should be used.
*
* @method #getSplitParts

@@ -668,1 +807,16 @@ * @param {module:engine/model/element~Element} element

*/
/**
* Conversion data.
*
* **Note:** Keep in mind that this object is shared by reference between all conversion callbacks that will be called.
* This means that callbacks can override values if needed, and those values will be available in other callbacks.
*
* @typedef {Object} module:engine/conversion/upcastdispatcher~UpcastConversionData
*
* @property {module:engine/view/item~Item} viewItem Converted item.
* @property {module:engine/model/position~Position} modelCursor Position where a converter should start changes.
* Change this value for the next converter to tell where the conversion should continue.
* @property {module:engine/model/range~Range} [modelRange] The current state of conversion result. Every change to
* converted element should be reflected by setting or modifying this property.
*/

@@ -7,10 +7,9 @@ /**

import Matcher from '../view/matcher';
import ModelRange from '../model/range';
import ConversionHelpers from './conversionhelpers';
import { cloneDeep } from 'lodash-es';
import ModelSelection from '../model/selection';
import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
import { isParagraphable, wrapInParagraph } from '../model/utils/autoparagraphing';

@@ -65,3 +64,5 @@ /* global console */

* },
* model: ( viewElement, modelWriter ) => {
* model: ( viewElement, conversionApi ) => {
* const modelWriter = conversionApi.writer;
*
* return modelWriter.createElement( 'heading', { level: viewElement.getAttribute( 'data-level' ) } );

@@ -78,4 +79,5 @@ * }

* set, the converter will fire for every view element.
* @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element
* instance or a function that takes a view element and returns a model element. The model element will be inserted in the model.
* @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element instance or a
* function that takes a view element and {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API}
* and returns a model element. The model element will be inserted in the model.
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.

@@ -143,3 +145,3 @@ * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}

* key: 'fontSize',
* value: viewElement => {
* value: ( viewElement, conversionApi ) => {
* const fontSize = viewElement.getStyle( 'font-size' );

@@ -166,3 +168,4 @@ * const value = fontSize.substr( 0, fontSize.length - 2 );

* @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
* the model attribute. `value` property may be set as a function that takes a view element and returns the value.
* the model attribute. `value` property may be set as a function that takes a view element and
* {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.
* If `String` is given, the model attribute value will be set to `true`.

@@ -241,3 +244,3 @@ * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.

* key: 'styled'
* value: viewElement => {
* value: ( viewElement, conversionApi ) => {
* const regexp = /styled-([\S]+)/;

@@ -274,3 +277,3 @@ * const match = viewElement.getAttribute( 'class' ).match( regexp );

* key: 'lineHeight',
* value: viewElement => viewElement.getStyle( 'line-height' )
* value: ( viewElement, conversionApi ) => viewElement.getStyle( 'line-height' )
* }

@@ -290,3 +293,4 @@ * } );

* @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
* the model attribute. `value` property may be set as a function that takes a view element and returns the value.
* the model attribute. `value` property may be set as a function that takes a view element and
* {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.
* If `String` is given, the model attribute value will be same as view attribute value.

@@ -323,3 +327,3 @@ * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.

* view: 'marker-search',
* model: viewElement => 'comment:' + viewElement.getAttribute( 'data-comment-id' )
* model: ( viewElement, conversionApi ) => 'comment:' + viewElement.getAttribute( 'data-comment-id' )
* } );

@@ -352,3 +356,3 @@ *

* The {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToMarker `UpcastHelpers#elementToMarker()`}
* method has been deprecated and will be removed in the near future.
* method was deprecated and will be removed in the near future.
* Please use {@link module:engine/conversion/upcasthelpers~UpcastHelpers#dataToMarker `UpcastHelpers#dataToMarker()`} instead.

@@ -361,3 +365,3 @@ *

'upcast-helpers-element-to-marker-deprecated: ' +
'The UpcastHelpers#elementToMarker() method has been deprecated and will be removed in the near future. ' +
'The UpcastHelpers#elementToMarker() method was deprecated and will be removed in the near future. ' +
'Please use UpcastHelpers#dataToMarker() instead.'

@@ -371,3 +375,3 @@ )

/**
* View to model marker conversion helper.
* View-to-model marker conversion helper.
*

@@ -383,3 +387,3 @@ * Converts view data created by {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToData `#markerToData()`}

*
* By default, this converter creates markers with `group:name` name convention (to match the default `markerToData` conversion).
* By default, this converter creates markers with the `group:name` name convention (to match the default `markerToData` conversion).
*

@@ -393,3 +397,3 @@ * The conversion configuration can take a function that will generate a marker name.

* // Using the default conversion.
* // In this case, all markers from `comment` group will be converted.
* // In this case, all markers from the `comment` group will be converted.
* // The conversion will look for `<comment-start>` and `<comment-end>` tags and

@@ -412,16 +416,16 @@ * // `data-comment-start-before`, `data-comment-start-after`,

*
* Where `[]` are boundaries of a marker that will receive `comment:commentId:uid` name.
* Where `[]` are boundaries of a marker that will receive the `comment:commentId:uid` name.
*
* Other examples of usage:
*
* // Using custom function which is the same as the default conversion:
* // Using a custom function which is the same as the default conversion:
* editor.conversion.for( 'upcast' ).dataToMarker( {
* view: 'comment',
* model: name => 'comment:' + name,
* model: ( name, conversionApi ) => 'comment:' + name,
* } );
*
* // Using converter priority:
* // Using the converter priority:
* editor.conversion.for( 'upcast' ).dataToMarker( {
* view: 'comment',
* model: name => 'comment:' + name,
* model: ( name, conversionApi ) => 'comment:' + name,
* converterPriority: 'high'

@@ -435,4 +439,5 @@ * } );

* @param {Object} config Conversion configuration.
* @param {String} config.view Marker group name to convert.
* @param {Function} [config.model] Function that takes `name` part from the view element or attribute and returns the marker name.
* @param {String} config.view The marker group name to convert.
* @param {Function} [config.model] A function that takes the `name` part from the view element or attribute and
* {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the marker name.
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.

@@ -479,13 +484,29 @@ * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}

export function convertText() {
return ( evt, data, conversionApi ) => {
if ( conversionApi.schema.checkChild( data.modelCursor, '$text' ) ) {
if ( conversionApi.consumable.consume( data.viewItem ) ) {
const text = conversionApi.writer.createText( data.viewItem.data );
return ( evt, data, { schema, consumable, writer } ) => {
let position = data.modelCursor;
conversionApi.writer.insert( text, data.modelCursor );
// When node is already converted then do nothing.
if ( !consumable.test( data.viewItem ) ) {
return;
}
data.modelRange = ModelRange._createFromPositionAndShift( data.modelCursor, text.offsetSize );
data.modelCursor = data.modelRange.end;
if ( !schema.checkChild( position, '$text' ) ) {
if ( !isParagraphable( position, '$text', schema ) ) {
return;
}
position = wrapInParagraph( position, writer );
}
consumable.consume( data.viewItem );
const text = writer.createText( data.viewItem.data );
writer.insert( text, position );
data.modelRange = writer.createRange(
position,
position.getShiftedBy( text.offsetSize )
);
data.modelCursor = data.modelRange.end;
};

@@ -511,3 +532,2 @@ }

const viewSelection = data.newSelection;
const modelSelection = new ModelSelection();

@@ -520,3 +540,3 @@ const ranges = [];

modelSelection.setTo( ranges, { backward: viewSelection.isBackward } );
const modelSelection = model.createSelection( ranges, { backward: viewSelection.isBackward } );

@@ -718,3 +738,3 @@ if ( !modelSelection.isEqual( model.document.selection ) ) {

for ( const markerViewName of markerViewNames ) {
const markerName = config.model( markerViewName );
const markerName = config.model( markerViewName, conversionApi );
const element = conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );

@@ -758,76 +778,33 @@

function prepareToElementConverter( config ) {
const matcher = config.view ? new Matcher( config.view ) : null;
const matcher = new Matcher( config.view );
return ( evt, data, conversionApi ) => {
let match = {};
const matcherResult = matcher.match( data.viewItem );
// If `config.view` has not been passed do not try matching. In this case, the converter should fire for all elements.
if ( matcher ) {
// This will be usually just one pattern but we support matchers with many patterns too.
const matcherResult = matcher.match( data.viewItem );
if ( !matcherResult ) {
return;
}
// If there is no match, this callback should not do anything.
if ( !matcherResult ) {
return;
}
const match = matcherResult.match;
match = matcherResult.match;
}
// Force consuming element's name.
match.name = true;
// Create model element basing on config.
const modelElement = getModelElement( config.model, data.viewItem, conversionApi.writer );
// Do not convert if element building function returned falsy value.
if ( !modelElement ) {
if ( !conversionApi.consumable.test( data.viewItem, match ) ) {
return;
}
// When element was already consumed then skip it.
if ( !conversionApi.consumable.test( data.viewItem, match ) ) {
const modelElement = getModelElement( config.model, data.viewItem, conversionApi );
if ( !modelElement ) {
return;
}
// Find allowed parent for element that we are going to insert.
// If current parent does not allow to insert element but one of the ancestors does
// then split nodes to allowed parent.
const splitResult = conversionApi.splitToAllowedParent( modelElement, data.modelCursor );
// When there is no split result it means that we can't insert element to model tree, so let's skip it.
if ( !splitResult ) {
if ( !conversionApi.safeInsert( modelElement, data.modelCursor ) ) {
return;
}
// Insert element on allowed position.
conversionApi.writer.insert( modelElement, splitResult.position );
// Convert children and insert to element.
conversionApi.convertChildren( data.viewItem, conversionApi.writer.createPositionAt( modelElement, 0 ) );
// Consume appropriate value from consumable values list.
conversionApi.consumable.consume( data.viewItem, match );
const parts = conversionApi.getSplitParts( modelElement );
// Set conversion result range.
data.modelRange = new ModelRange(
conversionApi.writer.createPositionBefore( modelElement ),
conversionApi.writer.createPositionAfter( parts[ parts.length - 1 ] )
);
// Now we need to check where the `modelCursor` should be.
if ( splitResult.cursorParent ) {
// If we split parent to insert our element then we want to continue conversion in the new part of the split parent.
//
// before: <allowed><notAllowed>foo[]</notAllowed></allowed>
// after: <allowed><notAllowed>foo</notAllowed><converted></converted><notAllowed>[]</notAllowed></allowed>
data.modelCursor = conversionApi.writer.createPositionAt( splitResult.cursorParent, 0 );
} else {
// Otherwise just continue after inserted element.
data.modelCursor = data.modelRange.end;
}
conversionApi.convertChildren( data.viewItem, modelElement );
conversionApi.updateConversionResult( modelElement, data );
};

@@ -841,8 +818,8 @@ }

// @param {module:engine/view/node~Node} input The converted view node.
// @param {module:engine/model/writer~Writer} writer A writer instance to use to create the model element.
function getModelElement( model, input, writer ) {
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi The upcast conversion API.
function getModelElement( model, input, conversionApi ) {
if ( model instanceof Function ) {
return model( input, writer );
return model( input, conversionApi );
} else {
return writer.createElement( model );
return conversionApi.writer.createElement( model );
}

@@ -925,3 +902,4 @@ }

const modelKey = config.model.key;
const modelValue = typeof config.model.value == 'function' ? config.model.value( data.viewItem ) : config.model.value;
const modelValue = typeof config.model.value == 'function' ?
config.model.value( data.viewItem, conversionApi ) : config.model.value;

@@ -1007,6 +985,6 @@ // Do not convert if attribute building function returned falsy value.

config.model = ( viewElement, modelWriter ) => {
const markerName = typeof oldModel == 'string' ? oldModel : oldModel( viewElement );
config.model = ( viewElement, conversionApi ) => {
const markerName = typeof oldModel == 'string' ? oldModel : oldModel( viewElement, conversionApi );
return modelWriter.createElement( '$marker', { 'data-name': markerName } );
return conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );
};

@@ -1025,7 +1003,7 @@ }

configForElements.model = ( viewElement, modelWriter ) => {
configForElements.model = ( viewElement, conversionApi ) => {
const viewName = viewElement.getAttribute( 'name' );
const markerName = config.model( viewName );
const markerName = config.model( viewName, conversionApi );
return modelWriter.createElement( '$marker', { 'data-name': markerName } );
return conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );
};

@@ -1032,0 +1010,0 @@

@@ -232,4 +232,4 @@ /**

if ( data.item instanceof ModelSelection || data.item instanceof DocumentSelection || data.item.is( '$textProxy' ) ) {
const converter = wrap( ( modelAttributeValue, viewWriter ) => {
return viewWriter.createAttributeElement(
const converter = wrap( ( modelAttributeValue, { writer } ) => {
return writer.createAttributeElement(
'model-text-with-attributes',

@@ -252,3 +252,3 @@ { [ data.attributeKey ]: stringifyAttributeValue( modelAttributeValue ) }

downcastDispatcher.on( 'selection', convertCollapsedSelection() );
downcastDispatcher.on( 'addMarker', insertUIElement( ( data, writer ) => {
downcastDispatcher.on( 'addMarker', insertUIElement( ( data, { writer } ) => {
const name = data.markerName + ':' + ( data.isOpening ? 'start' : 'end' );

@@ -411,3 +411,3 @@

conversionApi.convertChildren( data.viewItem, ModelPosition._createAt( element, 0 ) );
conversionApi.convertChildren( data.viewItem, element );

@@ -414,0 +414,0 @@ data.modelRange = ModelRange._createOn( element );

@@ -54,5 +54,5 @@ /**

* instead of `<p>`, `<attribute:b>` instead of `<b>` and `<empty:img>` instead of `<img>`).
* @param {Boolean} [options.showPriority=false] When set to `true`, attribute element's priority will be printed
* @param {Boolean} [options.showPriority=false] When set to `true`, the attribute element's priority will be printed
* (`<span view-priority="12">`, `<b view-priority="10">`).
* @param {Boolean} [options.showAttributeElementId=false] When set to `true`, attribute element's id will be printed
* @param {Boolean} [options.showAttributeElementId=false] When set to `true`, the attribute element's ID will be printed
* (`<span id="marker:foo">`).

@@ -59,0 +59,0 @@ * @param {Boolean} [options.renderUIElements=false] When set to `true`, the inner content of each

@@ -1098,3 +1098,3 @@ /**

while ( node && !attrs ) {
while ( node && !schema.isInline( node ) && !attrs ) {
node = node.previousSibling;

@@ -1109,3 +1109,3 @@ attrs = getAttrsIfCharacter( node );

while ( node && !attrs ) {
while ( node && !schema.isInline( node ) && !attrs ) {
node = node.nextSibling;

@@ -1112,0 +1112,0 @@ attrs = getAttrsIfCharacter( node );

@@ -102,3 +102,3 @@ /**

*/
throw new CKEditorError( 'markercollection-incorrect-marker-name: Marker name cannot contain "," character.', this );
throw new CKEditorError( 'markercollection-incorrect-marker-name: Marker name cannot contain the "," character.', this );
}

@@ -105,0 +105,0 @@

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

import { injectSelectionPostFixer } from './utils/selection-post-fixer';
import { autoParagraphEmptyRoots } from './utils/autoparagraphing';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';

@@ -104,3 +105,4 @@

allowIn: '$block',
isInline: true
isInline: true,
isContent: true
} );

@@ -126,2 +128,5 @@ this.schema.register( '$clipboardHolder', {

// Post-fixer which takes care of adding empty paragraph elements to the empty roots.
this.document.registerPostFixer( autoParagraphEmptyRoots );
// @if CK_DEBUG_ENGINE // this.on( 'applyOperation', () => {

@@ -546,3 +551,3 @@ // @if CK_DEBUG_ENGINE // dumpTrees( this.document, this.document.version );

* any non-whitespace characters),
* * or any {@link module:engine/model/schema~Schema#isObject object element},
* * or any {@link module:engine/model/schema~Schema#isContent content element},
* * or any {@link module:engine/model/markercollection~Marker marker} which

@@ -580,10 +585,12 @@ * {@link module:engine/model/markercollection~Marker#_affectsData affects data}.

for ( const item of range.getItems() ) {
if ( item.is( '$textProxy' ) ) {
if ( !ignoreWhitespaces ) {
if ( this.schema.isContent( item ) ) {
if ( item.is( '$textProxy' ) ) {
if ( !ignoreWhitespaces ) {
return true;
} else if ( item.data.search( /\S/ ) !== -1 ) {
return true;
}
} else {
return true;
} else if ( item.data.search( /\S/ ) !== -1 ) {
return true;
}
} else if ( this.schema.isObject( item ) ) {
return true;
}

@@ -590,0 +597,0 @@ }

@@ -446,8 +446,13 @@ /**

/**
* An internal error occured during merging insertion content with siblings.
* An internal error occurred during merging inserted content with its siblings.
* The insertion position should equal to the merge position.
*
* If you encountered this error, report it back to the CKEditor 5 team
* with as many details regarding the content being inserted and the insertion position
* as possible.
*
* @error insertcontent-invalid-insertion-position
*/
throw new CKEditorError( 'insertcontent-invalid-insertion-position', this );
throw new CKEditorError( 'insertcontent-invalid-insertion-position: ' +
'An internal error occurred during insertContent().', this );
}

@@ -454,0 +459,0 @@

@@ -95,22 +95,25 @@ /**

function tryExtendingTo( data, value ) {
const { isForward, walker, unit, schema } = data;
const { type, item, nextPosition } = value;
// If found text, we can certainly put the focus in it. Let's just find a correct position
// based on the unit.
if ( value.type == 'text' ) {
if ( type == 'text' ) {
if ( data.unit === 'word' ) {
return getCorrectWordBreakPosition( data.walker, data.isForward );
return getCorrectWordBreakPosition( walker, isForward );
}
return getCorrectPosition( data.walker, data.unit, data.isForward );
return getCorrectPosition( walker, unit, isForward );
}
// Entering an element.
if ( value.type == ( data.isForward ? 'elementStart' : 'elementEnd' ) ) {
// If it's an object, we can select it now.
if ( data.schema.isObject( value.item ) ) {
return Position._createAt( value.item, data.isForward ? 'after' : 'before' );
if ( type == ( isForward ? 'elementStart' : 'elementEnd' ) ) {
// If it's a selectable, we can select it now.
if ( schema.isSelectable( item ) ) {
return Position._createAt( item, isForward ? 'after' : 'before' );
}
// If text allowed on this position, extend to this place.
if ( data.schema.checkChild( value.nextPosition, '$text' ) ) {
return value.nextPosition;
if ( schema.checkChild( nextPosition, '$text' ) ) {
return nextPosition;
}

@@ -121,5 +124,5 @@ }

// If leaving a limit element, stop.
if ( data.schema.isLimit( value.item ) ) {
if ( schema.isLimit( item ) ) {
// NOTE: Fast-forward the walker until the end.
data.walker.skip( () => true );
walker.skip( () => true );

@@ -130,4 +133,4 @@ return;

// If text allowed on this position, extend to this place.
if ( data.schema.checkChild( value.nextPosition, '$text' ) ) {
return value.nextPosition;
if ( schema.checkChild( nextPosition, '$text' ) ) {
return nextPosition;
}

@@ -134,0 +137,0 @@ }

@@ -25,3 +25,3 @@ /**

* boundary (a range must be rooted within one limit element).
* * Only {@link module:engine/model/schema~Schema#isObject object elements} can be selected from the outside
* * Only {@link module:engine/model/schema~Schema#isSelectable selectable elements} can be selected from the outside
* (e.g. `[<paragraph>foo</paragraph>]` is invalid). This rule applies independently to both selection ends, so this

@@ -158,4 +158,3 @@ * selection is correct: `<paragraph>f[oo</paragraph><image></image>]`.

function tryFixingNonCollapsedRage( range, schema ) {
const start = range.start;
const end = range.end;
const { start, end } = range;

@@ -181,9 +180,9 @@ const isTextAllowedOnStart = schema.checkChild( start, '$text' );

// - <block>f[oo</block>] -> <block>f[oo]</block>
// - [<block>foo</block><object></object>] -> <block>[foo</block><object></object>]
// - [<block>foo</block><selectable></selectable>] -> <block>[foo</block><selectable></selectable>]
if ( checkSelectionOnNonLimitElements( start, end, schema ) ) {
const isStartObject = start.nodeAfter && schema.isObject( start.nodeAfter );
const fixedStart = isStartObject ? null : schema.getNearestSelectionRange( start, 'forward' );
const isStartBeforeSelectable = start.nodeAfter && schema.isSelectable( start.nodeAfter );
const fixedStart = isStartBeforeSelectable ? null : schema.getNearestSelectionRange( start, 'forward' );
const isEndObject = end.nodeBefore && schema.isObject( end.nodeBefore );
const fixedEnd = isEndObject ? null : schema.getNearestSelectionRange( end, 'backward' );
const isEndAfterSelectable = end.nodeBefore && schema.isSelectable( end.nodeBefore );
const fixedEnd = isEndAfterSelectable ? null : schema.getNearestSelectionRange( end, 'backward' );

@@ -206,4 +205,4 @@ // The schema.getNearestSelectionRange might return null - if that happens use original position.

const expandStart = isStartInLimit && ( !bothInSameParent || !isInObject( start.nodeAfter, schema ) );
const expandEnd = isEndInLimit && ( !bothInSameParent || !isInObject( end.nodeBefore, schema ) );
const expandStart = isStartInLimit && ( !bothInSameParent || !isSelectable( start.nodeAfter, schema ) );
const expandEnd = isEndInLimit && ( !bothInSameParent || !isSelectable( end.nodeBefore, schema ) );

@@ -292,3 +291,3 @@ // Although we've already found limit element on start/end positions we must find the outer-most limit element.

// Checks if node exists and if it's an object.
// Checks if node exists and if it's a selectable.
//

@@ -298,4 +297,4 @@ // @param {module:engine/model/node~Node} node

// @returns {Boolean}
function isInObject( node, schema ) {
return node && schema.isObject( node );
function isSelectable( node, schema ) {
return node && schema.isSelectable( node );
}

@@ -35,8 +35,9 @@ /**

*
* The instance of `DOMConverter` is available under {@link module:engine/view/view~View#domConverter `editor.editing.view.domConverter`}.
* An instance of the DOM converter is available under
* {@link module:engine/view/view~View#domConverter `editor.editing.view.domConverter`}.
*
* `DomConverter` does not check which nodes should be rendered (use {@link module:engine/view/renderer~Renderer}), does not keep a
* state of a tree nor keeps synchronization between tree view and DOM tree (use {@link module:engine/view/document~Document}).
* The DOM converter does not check which nodes should be rendered (use {@link module:engine/view/renderer~Renderer}), does not keep the
* state of a tree nor keeps the synchronization between the tree view and the DOM tree (use {@link module:engine/view/document~Document}).
*
* `DomConverter` keeps DOM elements to View element bindings, so when the converter gets destroyed, the bindings are lost.
* The DOM converter keeps DOM elements to view element bindings, so when the converter gets destroyed, the bindings are lost.
* Two converters will keep separate binding maps, so one tree view can be bound with two DOM trees.

@@ -46,6 +47,6 @@ */

/**
* Creates DOM converter.
* Creates a DOM converter.
*
* @param {module:engine/view/document~Document} document The view document instance.
* @param {Object} options Object with configuration options.
* @param {Object} options An object with configuration options.
* @param {module:engine/view/filler~BlockFillerMode} [options.blockFillerMode='br'] The type of the block filler to use.

@@ -61,3 +62,3 @@ */

/**
* The mode of a block filler used by DOM converter.
* The mode of a block filler used by the DOM converter.
*

@@ -92,3 +93,3 @@ * @readonly

* Block {@link module:engine/view/filler filler} creator, which is used to create all block fillers during the
* view to DOM conversion and to recognize block fillers during the DOM to view conversion.
* view-to-DOM conversion and to recognize block fillers during the DOM-to-view conversion.
*

@@ -102,3 +103,3 @@ * @readonly

/**
* DOM to View mapping.
* The DOM-to-view mapping.
*

@@ -111,3 +112,3 @@ * @private

/**
* View to DOM mapping.
* The view-to-DOM mapping.
*

@@ -120,3 +121,3 @@ * @private

/**
* Holds mapping between fake selection containers and corresponding view selections.
* Holds the mapping between fake selection containers and corresponding view selections.
*

@@ -904,11 +905,11 @@ * @private

/**
* Checks if given selection's boundaries are at correct places.
* Checks if the given selection's boundaries are at correct places.
*
* The following places are considered as incorrect for selection boundaries:
*
* * before or in the middle of the inline filler sequence,
* * before or in the middle of an inline filler sequence,
* * inside a DOM element which represents {@link module:engine/view/uielement~UIElement a view UI element},
* * inside a DOM element which represents {@link module:engine/view/rawelement~RawElement a view raw element}.
*
* @param {Selection} domSelection DOM Selection object to be checked.
* @param {Selection} domSelection The DOM selection object to be checked.
* @returns {Boolean} `true` if the given selection is at a correct place, `false` otherwise.

@@ -915,0 +916,0 @@ */

@@ -17,8 +17,8 @@ /**

*
* Raw elements work as data containers ("wrappers", "sandboxes") but their children are not managed or
* The raw elements work as data containers ("wrappers", "sandboxes") but their children are not managed or
* even recognized by the editor. This encapsulation allows integrations to maintain custom DOM structures
* in the editor content without, for instance, worrying about compatibility with other editor features.
* Raw elements make a perfect tool for integration with external frameworks and data sources.
* Raw elements are a perfect tool for integration with external frameworks and data sources.
*
* Unlike {@link module:engine/view/uielement~UIElement ui elements}, raw elements act like a real editor
* Unlike {@link module:engine/view/uielement~UIElement UI elements}, raw elements act like real editor
* content (similar to {@link module:engine/view/containerelement~ContainerElement} or

@@ -28,3 +28,3 @@ * {@link module:engine/view/emptyelement~EmptyElement}), they are considered by the editor selection and

*
* To create a new raw element use the
* To create a new raw element, use the
* {@link module:engine/view/downcastwriter~DowncastWriter#createRawElement `downcastWriter#createRawElement()`} method.

@@ -36,6 +36,6 @@ *

/**
* Creates new instance of RawElement.
* Creates a new instance of a raw element.
*
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-rawelement-cannot-add` when `children` parameter
* is passed, to inform that usage of `RawElement` is incorrect (adding child nodes to `RawElement` is forbidden).
* Throws the `view-rawelement-cannot-add` {@link module:utils/ckeditorerror~CKEditorError CKEditorError} when the `children`
* parameter is passed to inform that the usage of `RawElement` is incorrect (adding child nodes to `RawElement` is forbidden).
*

@@ -45,6 +45,6 @@ * @see module:engine/view/downcastwriter~DowncastWriter#createRawElement

* @param {module:engine/view/document~Document} document The document instance to which this element belongs.
* @param {String} name Node name.
* @param {Object|Iterable} [attrs] Collection of attributes.
* @param {String} name A node name.
* @param {Object|Iterable} [attrs] The collection of attributes.
* @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
* A list of nodes to be inserted into created element.
* A list of nodes to be inserted into the created element.
*/

@@ -55,3 +55,3 @@ constructor( document, name, attrs, children ) {

/**
* Returns `null` because filler is not needed for RawElements.
* Returns `null` because filler is not needed for raw elements.
*

@@ -80,3 +80,3 @@ * @method #getFillerOffset

*
* rawElement.is( 'img' ); // -> true if this is a img element
* rawElement.is( 'img' ); // -> true if this is an img element
* rawElement.is( 'rawElement', 'img' ); // -> same as above

@@ -87,5 +87,5 @@ * text.is( 'img' ); -> false

*
* @param {String} type Type to check when `name` parameter is present.
* @param {String} type The type to check when the `name` parameter is present.
* Otherwise, it acts like the `name` parameter.
* @param {String} [name] Element name.
* @param {String} [name] The element name.
* @returns {Boolean}

@@ -109,5 +109,5 @@ */

/**
* Overrides {@link module:engine/view/element~Element#_insertChild} method.
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-rawelement-cannot-add` to prevent
* adding any child nodes to a `RawElement`.
* Overrides the {@link module:engine/view/element~Element#_insertChild} method.
* Throws the `view-rawelement-cannot-add` {@link module:utils/ckeditorerror~CKEditorError CKEditorError} to prevent
* adding any child nodes to a raw element.
*

@@ -131,7 +131,7 @@ * @protected

/**
* Allows rendering the children of a {@link module:engine/view/rawelement~RawElement} on the DOM level.
* This allows rendering the children of a {@link module:engine/view/rawelement~RawElement} on the DOM level.
* This method is called by the {@link module:engine/view/domconverter~DomConverter} with the raw DOM element
* passed as an argument leaving the number and shape of the children up to the integrator.
* passed as an argument, leaving the number and shape of the children up to the integrator.
*
* This method **must be defined** for the `RawElement` to work:
* This method **must be defined** for the raw element to work:
*

@@ -149,3 +149,3 @@ * const myRawElement = downcastWriter.createRawElement( 'div' );

// Returns `null` because block filler is not needed for RawElements.
// Returns `null` because block filler is not needed for raw elements.
//

@@ -152,0 +152,0 @@ // @returns {null}

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 too big to display

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc