8
0

deletecontent.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module engine/controller/deletecontent
  7. */
  8. import LivePosition from '../model/liveposition';
  9. import Position from '../model/position';
  10. import Element from '../model/element';
  11. import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays';
  12. /**
  13. * Deletes content of the selection and merge siblings. The resulting selection is always collapsed.
  14. *
  15. * @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted.
  16. * @param {module:engine/model/batch~Batch} batch Batch to which the deltas will be added.
  17. * @param {Object} [options]
  18. * @param {Boolean} [options.merge=false] Merge elements after removing the contents of the selection.
  19. * For example, `<h>x[x</h><p>y]y</p>` will become: `<h>x^y</h>` with the option enabled
  20. * and: `<h>x^</h><p>y</p>` without it.
  21. * Note: {@link module:engine/model/schema~Schema#objects object} and {@link module:engine/model/schema~Schema#limits limit}
  22. * elements will not be merged.
  23. */
  24. export default function deleteContent( selection, batch, options = {} ) {
  25. if ( selection.isCollapsed ) {
  26. return;
  27. }
  28. const selRange = selection.getFirstRange();
  29. const startPos = selRange.start;
  30. const endPos = LivePosition.createFromPosition( selRange.end );
  31. // 1. Remove the contents if there are any.
  32. if ( !selRange.start.isTouching( selRange.end ) ) {
  33. batch.remove( selRange );
  34. }
  35. // 2. Merge elements in the right branch to the elements in the left branch.
  36. // The only reasonable (in terms of data and selection correctness) case in which we need to do that is:
  37. //
  38. // <heading type=1>Fo[</heading><paragraph>]ar</paragraph> => <heading type=1>Fo^ar</heading>
  39. //
  40. // However, the algorithm supports also merging deeper structures (up to the depth of the shallower branch),
  41. // as it's hard to imagine what should actually be the default behavior. Usually, specific features will
  42. // want to override that behavior anyway.
  43. if ( options.merge ) {
  44. mergeBranches( batch, startPos, endPos );
  45. }
  46. selection.collapse( startPos );
  47. // 3. Autoparagraphing.
  48. // Check if a text is allowed in the new container. If not, try to create a new paragraph (if it's allowed here).
  49. if ( shouldAutoparagraph( batch.document, startPos ) ) {
  50. const paragraph = new Element( 'paragraph' );
  51. batch.insert( startPos, paragraph );
  52. selection.collapse( paragraph );
  53. }
  54. endPos.detach();
  55. }
  56. function mergeBranches( batch, startPos, endPos ) {
  57. const endPath = endPos.path;
  58. const mergeEnd = Math.min( startPos.path.length - 1, endPath.length - 1 );
  59. let mergeDepth = compareArrays( startPos.path, endPath );
  60. if ( typeof mergeDepth == 'number' ) {
  61. for ( ; mergeDepth < mergeEnd; mergeDepth++ ) {
  62. const mergePath = startPos.path.slice( 0, mergeDepth );
  63. mergePath.push( startPos.path[ mergeDepth ] + 1 );
  64. const mergePos = new Position( endPos.root, mergePath );
  65. const previousNode = mergePos.nodeBefore;
  66. const nextNode = mergePos.nodeAfter;
  67. if ( !checkCanBeMerged( previousNode ) || !checkCanBeMerged( nextNode ) ) {
  68. return;
  69. }
  70. if ( nextNode.childCount > 0 ) {
  71. batch.merge( mergePos );
  72. } else {
  73. batch.remove( nextNode );
  74. }
  75. }
  76. }
  77. }
  78. function shouldAutoparagraph( doc, position ) {
  79. const isTextAllowed = doc.schema.check( { name: '$text', inside: position } );
  80. const isParagraphAllowed = doc.schema.check( { name: 'paragraph', inside: position } );
  81. return !isTextAllowed && isParagraphAllowed;
  82. }
  83. function checkCanBeMerged( element ) {
  84. const schema = element.document.schema;
  85. return !schema.objects.has( element.name ) && !schema.limits.has( element.name );
  86. }