|
@@ -1,8 +1,10 @@
|
|
|
/**
|
|
/**
|
|
|
- * The list indent command. It is used by the {@link list.List list feature}.
|
|
|
|
|
- *
|
|
|
|
|
- * @memberOf list
|
|
|
|
|
- * @namespace list.converters
|
|
|
|
|
|
|
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
|
|
|
|
|
+ * For licensing, see LICENSE.md.
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * @module list/converters
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
import ViewListItemElement from './viewlistitemelement.js';
|
|
import ViewListItemElement from './viewlistitemelement.js';
|
|
@@ -15,130 +17,16 @@ import ViewPosition from '../engine/view/position.js';
|
|
|
import ViewRange from '../engine/view/range.js';
|
|
import ViewRange from '../engine/view/range.js';
|
|
|
import viewWriter from '../engine/view/writer.js';
|
|
import viewWriter from '../engine/view/writer.js';
|
|
|
|
|
|
|
|
-// Helper function that creates a `<ul><li></li></ul>` structure out of given `modelItem` model `listItem` element.
|
|
|
|
|
-// Then, it binds created view list item (LI) with model `listItem` element.
|
|
|
|
|
-// The function then returns created view list item (LI).
|
|
|
|
|
-function generateLiInUl( modelItem, mapper ) {
|
|
|
|
|
- const listType = modelItem.getAttribute( 'type' ) == 'numbered' ? 'ol' : 'ul';
|
|
|
|
|
- const viewItem = new ViewListItemElement();
|
|
|
|
|
-
|
|
|
|
|
- const viewList = new ViewContainerElement( listType, null );
|
|
|
|
|
- viewList.appendChildren( viewItem );
|
|
|
|
|
-
|
|
|
|
|
- mapper.bindElements( modelItem, viewItem );
|
|
|
|
|
-
|
|
|
|
|
- return viewItem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Helper function that seeks for a sibling of given `modelItem` that is a `listItem` element and meets given criteria.
|
|
|
|
|
-// `options` object may contain one or more of given values (by default they are `false`):
|
|
|
|
|
-// `options.getNext` - whether next or previous siblings should be checked (default = previous)
|
|
|
|
|
-// `options.checkAllSiblings` - whether all siblings or just the first one should be checked (default = only one),
|
|
|
|
|
-// `options.sameIndent` - whether sought sibling should have same indent (default = no),
|
|
|
|
|
-// `options.biggerIndent` - whether sought sibling should have bigger indent (default = no).
|
|
|
|
|
-// Either `options.sameIndent` or `options.biggerIndent` should be set to `true`.
|
|
|
|
|
-function getSiblingListItem( modelItem, options ) {
|
|
|
|
|
- const direction = options.getNext ? 'nextSibling' : 'previousSibling';
|
|
|
|
|
- const checkAllSiblings = !!options.checkAllSiblings;
|
|
|
|
|
- const sameIndent = !!options.sameIndent;
|
|
|
|
|
- const biggerIndent = !!options.biggerIndent;
|
|
|
|
|
-
|
|
|
|
|
- const indent = modelItem.getAttribute( 'indent' );
|
|
|
|
|
-
|
|
|
|
|
- let item = modelItem[ direction ];
|
|
|
|
|
-
|
|
|
|
|
- while ( item && item.name == 'listItem' ) {
|
|
|
|
|
- let itemIndent = item.getAttribute( 'indent' );
|
|
|
|
|
-
|
|
|
|
|
- if ( sameIndent && indent == itemIndent || biggerIndent && indent < itemIndent ) {
|
|
|
|
|
- return item;
|
|
|
|
|
- } else if ( !checkAllSiblings || indent > itemIndent ) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- item = item[ direction ];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return null;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Helper function that takes two parameters, that are expected to be view list elements, and merges them.
|
|
|
|
|
-// The merge happen only if both parameters are UL or OL elements.
|
|
|
|
|
-function mergeViewLists( firstList, secondList ) {
|
|
|
|
|
- if ( firstList && secondList && ( firstList.name == 'ul' || firstList.name == 'ol' ) && firstList.name == secondList.name ) {
|
|
|
|
|
- viewWriter.mergeContainers( ViewPosition.createAfter( firstList ) );
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Helper function that takes model list item element `modelItem`, corresponding view list item element `injectedItem`
|
|
|
|
|
-// that is not added to the view and is inside a view list element (`ul` or `ol`) and is that's list only child.
|
|
|
|
|
-// The list is inserted at correct position (element breaking may be needed) and then merged with it's siblings.
|
|
|
|
|
-// See comments below to better understand the algorithm.
|
|
|
|
|
-function injectViewList( modelItem, injectedItem, mapper ) {
|
|
|
|
|
- const injectedList = injectedItem.parent;
|
|
|
|
|
-
|
|
|
|
|
- // 1. Break after previous `listItem` if it has same or bigger indent.
|
|
|
|
|
- const prevModelItem = getSiblingListItem( modelItem, { sameIndent: true, biggerIndent: true } );
|
|
|
|
|
-
|
|
|
|
|
- if ( prevModelItem ) {
|
|
|
|
|
- let viewItem = mapper.toViewElement( prevModelItem );
|
|
|
|
|
- let viewPosition = ViewPosition.createAfter( viewItem );
|
|
|
|
|
- viewWriter.breakContainer( viewPosition );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 2. Break after closest previous `listItem` sibling with same indent.
|
|
|
|
|
- const sameIndentModelItem = getSiblingListItem( modelItem, { sameIndent: true, checkAllSiblings: true } );
|
|
|
|
|
- // Position between broken lists will be a place where new list is inserted.
|
|
|
|
|
- // If there is nothing to break (`sameIndentModelItem` is falsy) it means that converted list item
|
|
|
|
|
- // is (will be) the first list item.
|
|
|
|
|
- let insertionPosition;
|
|
|
|
|
-
|
|
|
|
|
- if ( sameIndentModelItem ) {
|
|
|
|
|
- let viewItem = mapper.toViewElement( sameIndentModelItem );
|
|
|
|
|
- let viewPosition = ViewPosition.createAfter( viewItem );
|
|
|
|
|
- insertionPosition = viewWriter.breakContainer( viewPosition );
|
|
|
|
|
- } else {
|
|
|
|
|
- // If there is a list item before converted list item, it means that that list item has lower indent.
|
|
|
|
|
- // In such case the created view list should be appended as a child of that item.
|
|
|
|
|
- const prevSibling = modelItem.previousSibling;
|
|
|
|
|
-
|
|
|
|
|
- if ( prevSibling && prevSibling.name == 'listItem' ) {
|
|
|
|
|
- insertionPosition = ViewPosition.createAt( mapper.toViewElement( prevSibling ), 'end' );
|
|
|
|
|
- } else {
|
|
|
|
|
- // This is the very first list item, use position mapping to get correct insertion position.
|
|
|
|
|
- insertionPosition = mapper.toViewPosition( ModelPosition.createBefore( modelItem ) );
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 3. Append new UL/OL in position after breaking in step 2.
|
|
|
|
|
- viewWriter.insert( insertionPosition, injectedList );
|
|
|
|
|
-
|
|
|
|
|
- // 4. If next sibling is list item with bigger indent, append it's UL/OL to new LI.
|
|
|
|
|
- const nextModelItem = getSiblingListItem( modelItem, { getNext: true, biggerIndent: true } );
|
|
|
|
|
- const nextViewItem = mapper.toViewElement( nextModelItem );
|
|
|
|
|
-
|
|
|
|
|
- /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
|
|
|
|
|
- if ( nextViewItem ) {
|
|
|
|
|
- let sourceRange = ViewRange.createOn( nextViewItem.parent );
|
|
|
|
|
- let targetPosition = ViewPosition.createAt( injectedItem, 'end' );
|
|
|
|
|
- viewWriter.move( sourceRange, targetPosition );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 5. Merge new UL/OL with above and below items (ULs/OLs or LIs).
|
|
|
|
|
- mergeViewLists( injectedList, injectedList.nextSibling );
|
|
|
|
|
- mergeViewLists( injectedList.previousSibling, injectedList );
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* Model to view converter for `listItem` model element insertion.
|
|
* Model to view converter for `listItem` model element insertion.
|
|
|
*
|
|
*
|
|
|
* It creates `<ul><li></li><ul>` (or `<ol>`) view structure out of `listItem` model element, inserts it at correct
|
|
* It creates `<ul><li></li><ul>` (or `<ol>`) view structure out of `listItem` model element, inserts it at correct
|
|
|
* position, and merges the list with surrounding lists (if able).
|
|
* position, and merges the list with surrounding lists (if able).
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:insert
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:insert
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Additional information about the change.
|
|
* @param {Object} data Additional information about the change.
|
|
|
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface.
|
|
* @param {Object} conversionApi Conversion interface.
|
|
|
*/
|
|
*/
|
|
|
export function modelViewInsertion( evt, data, consumable, conversionApi ) {
|
|
export function modelViewInsertion( evt, data, consumable, conversionApi ) {
|
|
@@ -165,10 +53,10 @@ export function modelViewInsertion( evt, data, consumable, conversionApi ) {
|
|
|
* This change means that `<li>`s parent changes from `<ul>` to `<ol>` (or vice versa). This is accomplished by breaking
|
|
* This change means that `<li>`s parent changes from `<ul>` to `<ol>` (or vice versa). This is accomplished by breaking
|
|
|
* view elements, changing their name and merging them.
|
|
* view elements, changing their name and merging them.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:changeAttribute
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:changeAttribute
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Additional information about the change.
|
|
* @param {Object} data Additional information about the change.
|
|
|
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface.
|
|
* @param {Object} conversionApi Conversion interface.
|
|
|
*/
|
|
*/
|
|
|
export function modelViewChangeType( evt, data, consumable, conversionApi ) {
|
|
export function modelViewChangeType( evt, data, consumable, conversionApi ) {
|
|
@@ -197,10 +85,10 @@ export function modelViewChangeType( evt, data, consumable, conversionApi ) {
|
|
|
/**
|
|
/**
|
|
|
* Model to view converter for `listItem` model element remove.
|
|
* Model to view converter for `listItem` model element remove.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:remove
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:remove
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Additional information about the change.
|
|
* @param {Object} data Additional information about the change.
|
|
|
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface.
|
|
* @param {Object} conversionApi Conversion interface.
|
|
|
*/
|
|
*/
|
|
|
export function modelViewRemove( evt, data, consumable, conversionApi ) {
|
|
export function modelViewRemove( evt, data, consumable, conversionApi ) {
|
|
@@ -223,10 +111,10 @@ export function modelViewRemove( evt, data, consumable, conversionApi ) {
|
|
|
/**
|
|
/**
|
|
|
* Model to view converter for `listItem` model element move.
|
|
* Model to view converter for `listItem` model element move.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:move
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:move
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Additional information about the change.
|
|
* @param {Object} data Additional information about the change.
|
|
|
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface.
|
|
* @param {Object} conversionApi Conversion interface.
|
|
|
*/
|
|
*/
|
|
|
export function modelViewMove( evt, data, consumable, conversionApi ) {
|
|
export function modelViewMove( evt, data, consumable, conversionApi ) {
|
|
@@ -263,10 +151,10 @@ export function modelViewMove( evt, data, consumable, conversionApi ) {
|
|
|
/**
|
|
/**
|
|
|
* Model to view converter for `indent` attribute change on `listItem` model element.
|
|
* Model to view converter for `indent` attribute change on `listItem` model element.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:changeAttribute
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:changeAttribute
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Additional information about the change.
|
|
* @param {Object} data Additional information about the change.
|
|
|
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface.
|
|
* @param {Object} conversionApi Conversion interface.
|
|
|
*/
|
|
*/
|
|
|
export function modelViewChangeIndent( evt, data, consumable, conversionApi ) {
|
|
export function modelViewChangeIndent( evt, data, consumable, conversionApi ) {
|
|
@@ -296,7 +184,7 @@ export function modelViewChangeIndent( evt, data, consumable, conversionApi ) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * A special model to view converter introduced by {@link list.List List feature}. This converter is fired for
|
|
|
|
|
|
|
+ * A special model to view converter introduced by {@link module:list/list~List List feature}. This converter is fired for
|
|
|
* insert change of every model item, and should be fired before actual converter. The converter checks whether inserted
|
|
* insert change of every model item, and should be fired before actual converter. The converter checks whether inserted
|
|
|
* model item is a non-`listItem` element. If it is, and it is inserted inside a view list, the converter breaks the
|
|
* model item is a non-`listItem` element. If it is, and it is inserted inside a view list, the converter breaks the
|
|
|
* list so the model element is inserted to the view parent element corresponding to its model parent element.
|
|
* list so the model element is inserted to the view parent element corresponding to its model parent element.
|
|
@@ -314,10 +202,10 @@ export function modelViewChangeIndent( evt, data, consumable, conversionApi ) {
|
|
|
* <paragraph>xxx</paragraph> // Instead of this wrong view state:
|
|
* <paragraph>xxx</paragraph> // Instead of this wrong view state:
|
|
|
* <listItem>bar</listItem> <ul><li>foo</li><p>xxx</p><li>bar</li></ul>
|
|
* <listItem>bar</listItem> <ul><li>foo</li><p>xxx</p><li>bar</li></ul>
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:insert
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:insert
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Additional information about the change.
|
|
* @param {Object} data Additional information about the change.
|
|
|
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface.
|
|
* @param {Object} conversionApi Conversion interface.
|
|
|
*/
|
|
*/
|
|
|
export function modelViewSplitOnInsert( evt, data, consumable, conversionApi ) {
|
|
export function modelViewSplitOnInsert( evt, data, consumable, conversionApi ) {
|
|
@@ -340,7 +228,7 @@ export function modelViewSplitOnInsert( evt, data, consumable, conversionApi ) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * A special model to view converter introduced by {@link list.List List feature}. This converter takes care of
|
|
|
|
|
|
|
+ * A special model to view converter introduced by {@link module:list/list~List List feature}. This converter takes care of
|
|
|
* merging view lists after something is removed or moved from near them.
|
|
* merging view lists after something is removed or moved from near them.
|
|
|
*
|
|
*
|
|
|
* Example:
|
|
* Example:
|
|
@@ -356,11 +244,11 @@ export function modelViewSplitOnInsert( evt, data, consumable, conversionApi ) {
|
|
|
* <li>bar</li>
|
|
* <li>bar</li>
|
|
|
* </ul>
|
|
* </ul>
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:remove
|
|
|
|
|
- * @see engine.conversion.ModelConversionDispatcher#event:move
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:remove
|
|
|
|
|
+ * @see module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:move
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Additional information about the change.
|
|
* @param {Object} data Additional information about the change.
|
|
|
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface.
|
|
* @param {Object} conversionApi Conversion interface.
|
|
|
*/
|
|
*/
|
|
|
export function modelViewMergeAfter( evt, data, consumable, conversionApi ) {
|
|
export function modelViewMergeAfter( evt, data, consumable, conversionApi ) {
|
|
@@ -381,10 +269,10 @@ export function modelViewMergeAfter( evt, data, consumable, conversionApi ) {
|
|
|
* * checks `<li>`'s parent,
|
|
* * checks `<li>`'s parent,
|
|
|
* * passes `data.indent` value when `<li>`'s sub-items are converted.
|
|
* * passes `data.indent` value when `<li>`'s sub-items are converted.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ViewConversionDispatcher#event:element
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#event:element
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
|
|
* @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
|
|
|
- * @param {engine.conversion.ViewConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/viewconsumable~ViewConsumable} consumable Values to consume.
|
|
|
* @param {Object} conversionApi Conversion interface to be used by callback.
|
|
* @param {Object} conversionApi Conversion interface to be used by callback.
|
|
|
*/
|
|
*/
|
|
|
export function viewModelConverter( evt, data, consumable, conversionApi ) {
|
|
export function viewModelConverter( evt, data, consumable, conversionApi ) {
|
|
@@ -439,10 +327,10 @@ export function viewModelConverter( evt, data, consumable, conversionApi ) {
|
|
|
* This is mostly to clean white spaces from between `<li>` view elements inside the view list element, however also
|
|
* This is mostly to clean white spaces from between `<li>` view elements inside the view list element, however also
|
|
|
* incorrect data can be cleared if the view was incorrect.
|
|
* incorrect data can be cleared if the view was incorrect.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.ViewConversionDispatcher#event:element
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#event:element
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
|
|
* @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
|
|
|
- * @param {engine.conversion.ViewConsumable} consumable Values to consume.
|
|
|
|
|
|
|
+ * @param {module:engine/conversion/viewconsumable~ViewConsumable} consumable Values to consume.
|
|
|
*/
|
|
*/
|
|
|
export function cleanList( evt, data, consumable ) {
|
|
export function cleanList( evt, data, consumable ) {
|
|
|
if ( consumable.test( data.input, { name: true } ) ) {
|
|
if ( consumable.test( data.input, { name: true } ) ) {
|
|
@@ -458,12 +346,12 @@ export function cleanList( evt, data, consumable ) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Callback for model position to view position mapping for {@link engine.conversion.Mapper}. The callback fixes positions
|
|
|
|
|
|
|
+ * Callback for model position to view position mapping for {@link module:engine/conversion/mapper~Mapper}. The callback fixes positions
|
|
|
* between `listItem` elements, that would be incorrectly mapped because of how list items are represented in model
|
|
* between `listItem` elements, that would be incorrectly mapped because of how list items are represented in model
|
|
|
* and view.
|
|
* and view.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.Mapper#event:modelToViewPosition
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/mapper~Mapper#event:modelToViewPosition
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Object containing additional data and placeholder for mapping result.
|
|
* @param {Object} data Object containing additional data and placeholder for mapping result.
|
|
|
*/
|
|
*/
|
|
|
export function modelToViewPosition( evt, data ) {
|
|
export function modelToViewPosition( evt, data ) {
|
|
@@ -475,12 +363,12 @@ export function modelToViewPosition( evt, data ) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Callback for view position to model position mapping for {@link engine.conversion.Mapper}. The callback fixes positions
|
|
|
|
|
|
|
+ * Callback for view position to model position mapping for {@link module:engine/conversion/mapper~Mapper}. The callback fixes positions
|
|
|
* between `<li>` elements, that would be incorrectly mapped because of how list items are represented in model
|
|
* between `<li>` elements, that would be incorrectly mapped because of how list items are represented in model
|
|
|
* and view.
|
|
* and view.
|
|
|
*
|
|
*
|
|
|
- * @see engine.conversion.Mapper#event:viewToModelPosition
|
|
|
|
|
- * @param {utils.EventInfo} evt Object containing information about the fired event.
|
|
|
|
|
|
|
+ * @see module:engine/conversion/mapper~Mapper#event:viewToModelPosition
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
|
|
|
* @param {Object} data Object containing additional data and placeholder for mapping result.
|
|
* @param {Object} data Object containing additional data and placeholder for mapping result.
|
|
|
*/
|
|
*/
|
|
|
export function viewToModelPosition( evt, data ) {
|
|
export function viewToModelPosition( evt, data ) {
|
|
@@ -537,3 +425,117 @@ export function viewToModelPosition( evt, data ) {
|
|
|
evt.stop();
|
|
evt.stop();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+// Helper function that creates a `<ul><li></li></ul>` structure out of given `modelItem` model `listItem` element.
|
|
|
|
|
+// Then, it binds created view list item (LI) with model `listItem` element.
|
|
|
|
|
+// The function then returns created view list item (LI).
|
|
|
|
|
+function generateLiInUl( modelItem, mapper ) {
|
|
|
|
|
+ const listType = modelItem.getAttribute( 'type' ) == 'numbered' ? 'ol' : 'ul';
|
|
|
|
|
+ const viewItem = new ViewListItemElement();
|
|
|
|
|
+
|
|
|
|
|
+ const viewList = new ViewContainerElement( listType, null );
|
|
|
|
|
+ viewList.appendChildren( viewItem );
|
|
|
|
|
+
|
|
|
|
|
+ mapper.bindElements( modelItem, viewItem );
|
|
|
|
|
+
|
|
|
|
|
+ return viewItem;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Helper function that seeks for a sibling of given `modelItem` that is a `listItem` element and meets given criteria.
|
|
|
|
|
+// `options` object may contain one or more of given values (by default they are `false`):
|
|
|
|
|
+// `options.getNext` - whether next or previous siblings should be checked (default = previous)
|
|
|
|
|
+// `options.checkAllSiblings` - whether all siblings or just the first one should be checked (default = only one),
|
|
|
|
|
+// `options.sameIndent` - whether sought sibling should have same indent (default = no),
|
|
|
|
|
+// `options.biggerIndent` - whether sought sibling should have bigger indent (default = no).
|
|
|
|
|
+// Either `options.sameIndent` or `options.biggerIndent` should be set to `true`.
|
|
|
|
|
+function getSiblingListItem( modelItem, options ) {
|
|
|
|
|
+ const direction = options.getNext ? 'nextSibling' : 'previousSibling';
|
|
|
|
|
+ const checkAllSiblings = !!options.checkAllSiblings;
|
|
|
|
|
+ const sameIndent = !!options.sameIndent;
|
|
|
|
|
+ const biggerIndent = !!options.biggerIndent;
|
|
|
|
|
+
|
|
|
|
|
+ const indent = modelItem.getAttribute( 'indent' );
|
|
|
|
|
+
|
|
|
|
|
+ let item = modelItem[ direction ];
|
|
|
|
|
+
|
|
|
|
|
+ while ( item && item.name == 'listItem' ) {
|
|
|
|
|
+ let itemIndent = item.getAttribute( 'indent' );
|
|
|
|
|
+
|
|
|
|
|
+ if ( sameIndent && indent == itemIndent || biggerIndent && indent < itemIndent ) {
|
|
|
|
|
+ return item;
|
|
|
|
|
+ } else if ( !checkAllSiblings || indent > itemIndent ) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ item = item[ direction ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Helper function that takes two parameters, that are expected to be view list elements, and merges them.
|
|
|
|
|
+// The merge happen only if both parameters are UL or OL elements.
|
|
|
|
|
+function mergeViewLists( firstList, secondList ) {
|
|
|
|
|
+ if ( firstList && secondList && ( firstList.name == 'ul' || firstList.name == 'ol' ) && firstList.name == secondList.name ) {
|
|
|
|
|
+ viewWriter.mergeContainers( ViewPosition.createAfter( firstList ) );
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Helper function that takes model list item element `modelItem`, corresponding view list item element `injectedItem`
|
|
|
|
|
+// that is not added to the view and is inside a view list element (`ul` or `ol`) and is that's list only child.
|
|
|
|
|
+// The list is inserted at correct position (element breaking may be needed) and then merged with it's siblings.
|
|
|
|
|
+// See comments below to better understand the algorithm.
|
|
|
|
|
+function injectViewList( modelItem, injectedItem, mapper ) {
|
|
|
|
|
+ const injectedList = injectedItem.parent;
|
|
|
|
|
+
|
|
|
|
|
+ // 1. Break after previous `listItem` if it has same or bigger indent.
|
|
|
|
|
+ const prevModelItem = getSiblingListItem( modelItem, { sameIndent: true, biggerIndent: true } );
|
|
|
|
|
+
|
|
|
|
|
+ if ( prevModelItem ) {
|
|
|
|
|
+ let viewItem = mapper.toViewElement( prevModelItem );
|
|
|
|
|
+ let viewPosition = ViewPosition.createAfter( viewItem );
|
|
|
|
|
+ viewWriter.breakContainer( viewPosition );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. Break after closest previous `listItem` sibling with same indent.
|
|
|
|
|
+ const sameIndentModelItem = getSiblingListItem( modelItem, { sameIndent: true, checkAllSiblings: true } );
|
|
|
|
|
+ // Position between broken lists will be a place where new list is inserted.
|
|
|
|
|
+ // If there is nothing to break (`sameIndentModelItem` is falsy) it means that converted list item
|
|
|
|
|
+ // is (will be) the first list item.
|
|
|
|
|
+ let insertionPosition;
|
|
|
|
|
+
|
|
|
|
|
+ if ( sameIndentModelItem ) {
|
|
|
|
|
+ let viewItem = mapper.toViewElement( sameIndentModelItem );
|
|
|
|
|
+ let viewPosition = ViewPosition.createAfter( viewItem );
|
|
|
|
|
+ insertionPosition = viewWriter.breakContainer( viewPosition );
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // If there is a list item before converted list item, it means that that list item has lower indent.
|
|
|
|
|
+ // In such case the created view list should be appended as a child of that item.
|
|
|
|
|
+ const prevSibling = modelItem.previousSibling;
|
|
|
|
|
+
|
|
|
|
|
+ if ( prevSibling && prevSibling.name == 'listItem' ) {
|
|
|
|
|
+ insertionPosition = ViewPosition.createAt( mapper.toViewElement( prevSibling ), 'end' );
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // This is the very first list item, use position mapping to get correct insertion position.
|
|
|
|
|
+ insertionPosition = mapper.toViewPosition( ModelPosition.createBefore( modelItem ) );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. Append new UL/OL in position after breaking in step 2.
|
|
|
|
|
+ viewWriter.insert( insertionPosition, injectedList );
|
|
|
|
|
+
|
|
|
|
|
+ // 4. If next sibling is list item with bigger indent, append it's UL/OL to new LI.
|
|
|
|
|
+ const nextModelItem = getSiblingListItem( modelItem, { getNext: true, biggerIndent: true } );
|
|
|
|
|
+ const nextViewItem = mapper.toViewElement( nextModelItem );
|
|
|
|
|
+
|
|
|
|
|
+ /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
|
|
|
|
|
+ if ( nextViewItem ) {
|
|
|
|
|
+ let sourceRange = ViewRange.createOn( nextViewItem.parent );
|
|
|
|
|
+ let targetPosition = ViewPosition.createAt( injectedItem, 'end' );
|
|
|
|
|
+ viewWriter.move( sourceRange, targetPosition );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 5. Merge new UL/OL with above and below items (ULs/OLs or LIs).
|
|
|
|
|
+ mergeViewLists( injectedList, injectedList.nextSibling );
|
|
|
|
|
+ mergeViewLists( injectedList.previousSibling, injectedList );
|
|
|
|
|
+}
|