/** * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /** * @module engine/controller/deletecontent */ import LivePosition from '../model/liveposition'; import Position from '../model/position'; import Element from '../model/element'; import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays'; /** * Deletes content of the selection and merge siblings. The resulting selection is always collapsed. * * @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted. * @param {module:engine/model/batch~Batch} batch Batch to which the deltas will be added. * @param {Object} [options] * @param {Boolean} [options.merge=false] Merge elements after removing the contents of the selection. * For example, `x[x

y]y

` will become: `x^y` with the option enabled * and: `x^

y

` without it. */ export default function deleteContent( selection, batch, options = {} ) { if ( selection.isCollapsed ) { return; } const selRange = selection.getFirstRange(); const startPos = selRange.start; const endPos = LivePosition.createFromPosition( selRange.end ); // 1. Remove the contents if there are any. if ( !selRange.isEmpty ) { batch.remove( selRange ); } // 2. Merge elements in the right branch to the elements in the left branch. // The only reasonable (in terms of data and selection correctness) case in which we need to do that is: // // Fo[]ar => Fo^ar // // However, the algorithm supports also merging deeper structures (up to the depth of the shallower branch), // as it's hard to imagine what should actually be the default behavior. Usually, specific features will // want to override that behavior anyway. if ( options.merge ) { const endPath = endPos.path; const mergeEnd = Math.min( startPos.path.length - 1, endPath.length - 1 ); let mergeDepth = compareArrays( startPos.path, endPath ); if ( typeof mergeDepth == 'number' ) { for ( ; mergeDepth < mergeEnd; mergeDepth++ ) { const mergePath = startPos.path.slice( 0, mergeDepth ); mergePath.push( startPos.path[ mergeDepth ] + 1 ); const mergePos = new Position( endPos.root, mergePath ); const nextNode = mergePos.nodeAfter; if ( nextNode.childCount > 0 ) { batch.merge( mergePos ); } else { batch.remove( nextNode ); } } } } selection.collapse( startPos ); // 3. Autoparagraphing. // Check if a text is allowed in the new container. If not, try to create a new paragraph (if it's allowed here). if ( shouldAutoparagraph( batch.document, startPos ) ) { const paragraph = new Element( 'paragraph' ); batch.insert( startPos, paragraph ); selection.collapse( paragraph ); } endPos.detach(); } function shouldAutoparagraph( doc, position ) { const isTextAllowed = doc.schema.check( { name: '$text', inside: position } ); const isParagraphAllowed = doc.schema.check( { name: 'paragraph', inside: position } ); return !isTextAllowed && isParagraphAllowed; }