entercommand.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module enter/entercommand
  7. */
  8. import Command from '@ckeditor/ckeditor5-core/src/command';
  9. import { getCopyOnEnterAttributes } from './utils';
  10. /**
  11. * Enter command. It is used by the {@link module:enter/enter~Enter Enter feature} to handle the <kbd>Enter</kbd> key.
  12. *
  13. * @extends module:core/command~Command
  14. */
  15. export default class EnterCommand extends Command {
  16. /**
  17. * @inheritDoc
  18. */
  19. execute() {
  20. const model = this.editor.model;
  21. const doc = model.document;
  22. model.change( writer => {
  23. enterBlock( this.editor.model, writer, doc.selection, model.schema );
  24. this.fire( 'afterExecute', { writer } );
  25. } );
  26. }
  27. }
  28. // Creates a new block in the way that the <kbd>Enter</kbd> key is expected to work.
  29. //
  30. // @param {module:engine/model~Model} model
  31. // @param {module:engine/model/writer~Writer} writer
  32. // @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
  33. // Selection on which the action should be performed.
  34. // @param {module:engine/model/schema~Schema} schema
  35. function enterBlock( model, writer, selection, schema ) {
  36. const isSelectionEmpty = selection.isCollapsed;
  37. const range = selection.getFirstRange();
  38. const startElement = range.start.parent;
  39. const endElement = range.end.parent;
  40. // Don't touch the roots and other limit elements.
  41. if ( schema.isLimit( startElement ) || schema.isLimit( endElement ) ) {
  42. // Delete the selected content but only if inside a single limit element.
  43. // Abort, when crossing limit elements boundary (e.g. <limit1>x[x</limit1>donttouchme<limit2>y]y</limit2>).
  44. // This is an edge case and it's hard to tell what should actually happen because such a selection
  45. // is not entirely valid.
  46. if ( !isSelectionEmpty && startElement == endElement ) {
  47. model.deleteContent( selection );
  48. }
  49. return;
  50. }
  51. if ( isSelectionEmpty ) {
  52. const attributesToCopy = getCopyOnEnterAttributes( writer.model.schema, selection.getAttributes() );
  53. splitBlock( writer, range.start );
  54. writer.setSelectionAttribute( attributesToCopy );
  55. } else {
  56. const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd );
  57. const isContainedWithinOneElement = ( startElement == endElement );
  58. model.deleteContent( selection, { leaveUnmerged } );
  59. if ( leaveUnmerged ) {
  60. // Partially selected elements.
  61. //
  62. // <h>x[xx]x</h> -> <h>x^x</h> -> <h>x</h><h>^x</h>
  63. if ( isContainedWithinOneElement ) {
  64. splitBlock( writer, selection.focus );
  65. }
  66. // Selection over multiple elements.
  67. //
  68. // <h>x[x</h><p>y]y<p> -> <h>x^</h><p>y</p> -> <h>x</h><p>^y</p>
  69. else {
  70. writer.setSelection( endElement, 0 );
  71. }
  72. }
  73. }
  74. }
  75. function splitBlock( writer, splitPos ) {
  76. writer.split( splitPos );
  77. writer.setSelection( splitPos.parent.nextSibling, 0 );
  78. }