|
@@ -371,22 +371,7 @@ export function viewModelConverter( evt, data, conversionApi ) {
|
|
|
|
|
|
|
|
writer.insert( listItem, splitResult.position );
|
|
writer.insert( listItem, splitResult.position );
|
|
|
|
|
|
|
|
- // Remember position after list item, next list items will be inserted at this position.
|
|
|
|
|
- let nextPosition = ModelPosition.createAfter( listItem );
|
|
|
|
|
-
|
|
|
|
|
- // Check all children of the converted `<li>`.
|
|
|
|
|
- // At this point we assume there are no "whitespace" view text nodes in view list, between view list items.
|
|
|
|
|
- // This should be handled by `<ul>` and `<ol>` converters.
|
|
|
|
|
- for ( const child of data.viewItem.getChildren() ) {
|
|
|
|
|
- // If this is a view list element, we will convert it after last `listItem` model element.
|
|
|
|
|
- if ( child.name == 'ul' || child.name == 'ol' ) {
|
|
|
|
|
- nextPosition = conversionApi.convertItem( child, nextPosition ).modelCursor;
|
|
|
|
|
- }
|
|
|
|
|
- // If it was not a list it was a "regular" list item content. Just convert it to `listItem`.
|
|
|
|
|
- else {
|
|
|
|
|
- conversionApi.convertItem( child, ModelPosition.createAt( listItem, 'end' ) );
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const nextPosition = viewToModelListItemChildrenConverter( listItem, data.viewItem.getChildren(), conversionApi, writer );
|
|
|
|
|
|
|
|
conversionStore.indent--;
|
|
conversionStore.indent--;
|
|
|
|
|
|
|
@@ -814,6 +799,85 @@ function generateLiInUl( modelItem, conversionApi ) {
|
|
|
return viewItem;
|
|
return viewItem;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Helper function that converts children of a given `<li>` view element into corresponding model elements.
|
|
|
|
|
+// The function maintains proper order of elements if model `listItem` is split during the conversion
|
|
|
|
|
+// due to block children conversion.
|
|
|
|
|
+//
|
|
|
|
|
+// @param {module:engine/model/element~Element} listItemModel List item model element to which converted children will be inserted.
|
|
|
|
|
+// @param {Iterable.<module:engine/view/node~Node>} viewChildren View elements which will be converted.
|
|
|
|
|
+// @param {Object} conversionApi Conversion interface to be used by the callback.
|
|
|
|
|
+// @param {module:engine/model/writer~Writer} writer Writer used to manipulate model elements.
|
|
|
|
|
+// @returns {module:engine/model/position~Position} Position on which next elements should be inserted after children conversion.
|
|
|
|
|
+function viewToModelListItemChildrenConverter( listItemModel, viewChildren, conversionApi, writer ) {
|
|
|
|
|
+ let lastListItem = listItemModel;
|
|
|
|
|
+ let nextPosition = ModelPosition.createAfter( lastListItem );
|
|
|
|
|
+
|
|
|
|
|
+ // Check all children of the converted `<li>`. At this point we assume there are no "whitespace" view text nodes
|
|
|
|
|
+ // in view list, between view list items. This should be handled by `<ul>` and `<ol>` converters.
|
|
|
|
|
+ for ( const child of viewChildren ) {
|
|
|
|
|
+ // If this is a view list element, we will convert it after last `listItem` model element.
|
|
|
|
|
+ if ( child.name == 'ul' || child.name == 'ol' ) {
|
|
|
|
|
+ nextPosition = conversionApi.convertItem( child, nextPosition ).modelCursor;
|
|
|
|
|
+ }
|
|
|
|
|
+ // If it was not a list view element it was a "regular" list item content. Convert it to a `listItem`.
|
|
|
|
|
+ else {
|
|
|
|
|
+ const result = conversionApi.convertItem( child, ModelPosition.createAt( lastListItem, 'end' ) );
|
|
|
|
|
+ const convertedChild = Array.from( result.modelRange )[ 0 ];
|
|
|
|
|
+
|
|
|
|
|
+ // If there is a block element child being converted it will split the current list item, for example:
|
|
|
|
|
+ //
|
|
|
|
|
+ // <li><p>Foo</p></li>
|
|
|
|
|
+ //
|
|
|
|
|
+ // will be converted to:
|
|
|
|
|
+ //
|
|
|
|
|
+ // <listItem></listItem><paragraph>Foo</paragraph><listItem></listItem>
|
|
|
|
|
+ //
|
|
|
|
|
+ // so we need to update reference to `lastListItem` and `nextPosition`.
|
|
|
|
|
+ if ( convertedChild.type === 'elementStart' && convertedChild.item.is( 'element' ) ) {
|
|
|
|
|
+ nextPosition = result.modelCursor;
|
|
|
|
|
+ lastListItem = nextPosition.getAncestors().pop();
|
|
|
|
|
+
|
|
|
|
|
+ // Depending on the used converter for block elements, usually the position (marked as #)
|
|
|
|
|
+ // points to the second list item after conversion:
|
|
|
|
|
+ //
|
|
|
|
|
+ // `<li><p>Foo</p></li>` -> `<listItem></listItem><paragraph>Foo</paragraph><listItem>#</listItem>`
|
|
|
|
|
+ //
|
|
|
|
|
+ // However, in some cases like autoparagraphing the position is placed on the end of the block element:
|
|
|
|
|
+ //
|
|
|
|
|
+ // `<li><h2>Foo</h2></li>` -> `<listItem></listItem><paragraph>Foo#</paragraph><listItem></listItem>`
|
|
|
|
|
+ //
|
|
|
|
|
+ // We need to check fo such cases and use proper list item and position based on it.
|
|
|
|
|
+ if ( !lastListItem.is( 'listItem' ) && lastListItem.nextSibling && lastListItem.nextSibling.is( 'listItem' ) ) {
|
|
|
|
|
+ lastListItem = lastListItem.nextSibling;
|
|
|
|
|
+ nextPosition = ModelPosition.createAt( lastListItem, 'end' );
|
|
|
|
|
+ }
|
|
|
|
|
+ // If list item was split by the block element, the text from splitted list item needs to be moved.
|
|
|
|
|
+ //
|
|
|
|
|
+ // `<li>Foo<p>123</p>Bar</li>`
|
|
|
|
|
+ //
|
|
|
|
|
+ // Is converted to:
|
|
|
|
|
+ //
|
|
|
|
|
+ // <listItem>FooBar</listItem><paragraph>123</paragraph><listItem></listItem>
|
|
|
|
|
+ //
|
|
|
|
|
+ // So the text which was after splitting element (`<p>` in this case) should be move to second list item:
|
|
|
|
|
+ //
|
|
|
|
|
+ // <listItem>Foo</listItem><paragraph>123</paragraph><listItem>Bar</listItem>
|
|
|
|
|
+ } else if ( convertedChild.type === 'text' && listItemModel !== lastListItem ) {
|
|
|
|
|
+ // 1. Remove text part from the list item positioned before splitting element.
|
|
|
|
|
+ writer.remove( ModelRange.createOn( convertedChild.item ) );
|
|
|
|
|
+
|
|
|
|
|
+ // 2. Insert text part into list item positioned after splitting element.
|
|
|
|
|
+ writer.insert( convertedChild.item, nextPosition );
|
|
|
|
|
+
|
|
|
|
|
+ // 3. Update 'nextPosition' so it points after last list item.
|
|
|
|
|
+ nextPosition = ModelPosition.createAfter( lastListItem );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return nextPosition;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Helper function that seeks for a list item sibling of given model item (or position) which meets given criteria.
|
|
// Helper function that seeks for a list item sibling of given model item (or position) which meets given criteria.
|
|
|
// `options` object may contain one or more of given values (by default they are `false`):
|
|
// `options` object may contain one or more of given values (by default they are `false`):
|
|
|
// `options.sameIndent` - whether sought sibling should have same indent (default = no),
|
|
// `options.sameIndent` - whether sought sibling should have same indent (default = no),
|