delete.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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 typing/delete
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import DeleteCommand from './deletecommand';
  10. import DeleteObserver from './deleteobserver';
  11. import env from '@ckeditor/ckeditor5-utils/src/env';
  12. /**
  13. * The delete and backspace feature. Handles the <kbd>Delete</kbd> and <kbd>Backspace</kbd> keys in the editor.
  14. *
  15. * @extends module:core/plugin~Plugin
  16. */
  17. export default class Delete extends Plugin {
  18. /**
  19. * @inheritDoc
  20. */
  21. static get pluginName() {
  22. return 'Delete';
  23. }
  24. init() {
  25. const editor = this.editor;
  26. const view = editor.editing.view;
  27. const viewDocument = view.document;
  28. view.addObserver( DeleteObserver );
  29. editor.commands.add( 'forwardDelete', new DeleteCommand( editor, 'forward' ) );
  30. editor.commands.add( 'delete', new DeleteCommand( editor, 'backward' ) );
  31. this.listenTo( viewDocument, 'delete', ( evt, data ) => {
  32. const deleteCommandParams = { unit: data.unit, sequence: data.sequence };
  33. // If a specific (view) selection to remove was set, convert it to a model selection and set as a parameter for `DeleteCommand`.
  34. if ( data.selectionToRemove ) {
  35. const modelSelection = editor.model.createSelection();
  36. const ranges = [];
  37. for ( const viewRange of data.selectionToRemove.getRanges() ) {
  38. ranges.push( editor.editing.mapper.toModelRange( viewRange ) );
  39. }
  40. modelSelection.setTo( ranges );
  41. deleteCommandParams.selection = modelSelection;
  42. }
  43. editor.execute( data.direction == 'forward' ? 'forwardDelete' : 'delete', deleteCommandParams );
  44. data.preventDefault();
  45. view.scrollToTheSelection();
  46. } );
  47. // Android IMEs have a quirk - they change DOM selection after the input changes were performed by the browser.
  48. // This happens on `keyup` event. Android doesn't know anything about our deletion and selection handling. Even if the selection
  49. // was changed during input events, IME remembers the position where the selection "should" be placed and moves it there.
  50. //
  51. // To prevent incorrect selection, we save the selection after deleting here and then re-set it on `keyup`. This has to be done
  52. // on DOM selection level, because on `keyup` the model selection is still the same as it was just after deletion, so it
  53. // wouldn't be changed and the fix would do nothing.
  54. //
  55. if ( env.isAndroid ) {
  56. let domSelectionAfterDeletion = null;
  57. this.listenTo( viewDocument, 'delete', ( evt, data ) => {
  58. const domSelection = data.domTarget.ownerDocument.defaultView.getSelection();
  59. domSelectionAfterDeletion = {
  60. anchorNode: domSelection.anchorNode,
  61. anchorOffset: domSelection.anchorOffset,
  62. focusNode: domSelection.focusNode,
  63. focusOffset: domSelection.focusOffset
  64. };
  65. }, { priority: 'lowest' } );
  66. this.listenTo( viewDocument, 'keyup', ( evt, data ) => {
  67. if ( domSelectionAfterDeletion ) {
  68. const domSelection = data.domTarget.ownerDocument.defaultView.getSelection();
  69. domSelection.collapse( domSelectionAfterDeletion.anchorNode, domSelectionAfterDeletion.anchorOffset );
  70. domSelection.extend( domSelectionAfterDeletion.focusNode, domSelectionAfterDeletion.focusOffset );
  71. domSelectionAfterDeletion = null;
  72. }
  73. } );
  74. }
  75. }
  76. }