entercommand.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Command from '../core/command/command.js';
  6. /**
  7. * Enter command. It is used by the {@link enter.Enter Enter feature} to handle the <kbd>Enter</kbd> key.
  8. *
  9. * @member enter
  10. * @extends core.command.Command
  11. */
  12. export default class EnterCommand extends Command {
  13. /**
  14. * Executes command.
  15. *
  16. * @protected
  17. * @returns {Object} data Data object, available in {@link core.Command#event:afterExecute}
  18. * @returns {engine.model.Batch} data.batch Batch created and used by the command.
  19. */
  20. _doExecute() {
  21. const doc = this.editor.document;
  22. const batch = doc.batch();
  23. doc.enqueueChanges( () => {
  24. enterBlock( batch, doc.selection );
  25. this.fire( 'afterExecute', { batch } );
  26. } );
  27. }
  28. }
  29. /**
  30. * Creates a new block in the way that the <kbd>Enter</kbd> key is expected to work.
  31. *
  32. * @param {engine.model.Batch} batch A batch to which the deltas will be added.
  33. * @param {engine.model.Selection} selection Selection on which the action should be performed.
  34. */
  35. function enterBlock( batch, selection ) {
  36. const doc = batch.document;
  37. const isSelectionEmpty = selection.isCollapsed;
  38. const range = selection.getFirstRange();
  39. const startElement = range.start.parent;
  40. const endElement = range.end.parent;
  41. // Don't touch the root.
  42. if ( startElement.root == startElement ) {
  43. if ( !isSelectionEmpty ) {
  44. doc.composer.deleteContents( batch, selection );
  45. }
  46. return;
  47. }
  48. if ( isSelectionEmpty ) {
  49. splitBlock( batch, selection, range.start );
  50. } else {
  51. const shouldMerge = range.start.isAtStart && range.end.isAtEnd;
  52. const isContainedWithinOneElement = ( startElement == endElement );
  53. doc.composer.deleteContents( batch, selection, { merge: shouldMerge } );
  54. if ( !shouldMerge ) {
  55. // Partially selected elements.
  56. //
  57. // <h>x[xx]x</h> -> <h>x^x</h> -> <h>x</h><h>^x</h>
  58. if ( isContainedWithinOneElement ) {
  59. splitBlock( batch, selection, selection.focus );
  60. }
  61. // Selection over multiple elements.
  62. //
  63. // <h>x[x</h><p>y]y<p> -> <h>x^</h><p>y</p> -> <h>x</h><p>^y</p>
  64. else {
  65. selection.collapse( endElement );
  66. }
  67. }
  68. }
  69. }
  70. function splitBlock( batch, selection, splitPos ) {
  71. batch.split( splitPos );
  72. selection.collapse( splitPos.parent.nextSibling );
  73. }