deleteobserver.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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/deleteobserver
  7. */
  8. import Observer from '@ckeditor/ckeditor5-engine/src/view/observer/observer';
  9. import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
  10. import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
  11. import env from '@ckeditor/ckeditor5-utils/src/env';
  12. /**
  13. * Delete observer introduces the {@link module:engine/view/document~Document#event:delete} event.
  14. *
  15. * @extends module:engine/view/observer/observer~Observer
  16. */
  17. export default class DeleteObserver extends Observer {
  18. constructor( view ) {
  19. super( view );
  20. const document = view.document;
  21. let sequence = 0;
  22. document.on( 'keyup', ( evt, data ) => {
  23. if ( data.keyCode == keyCodes.delete || data.keyCode == keyCodes.backspace ) {
  24. sequence = 0;
  25. }
  26. } );
  27. document.on( 'keydown', ( evt, data ) => {
  28. const deleteData = {};
  29. if ( data.keyCode == keyCodes.delete ) {
  30. deleteData.direction = 'forward';
  31. deleteData.unit = 'character';
  32. } else if ( data.keyCode == keyCodes.backspace ) {
  33. deleteData.direction = 'backward';
  34. deleteData.unit = 'codePoint';
  35. } else {
  36. return;
  37. }
  38. const hasWordModifier = env.isMac ? data.altKey : data.ctrlKey;
  39. deleteData.unit = hasWordModifier ? 'word' : deleteData.unit;
  40. deleteData.sequence = ++sequence;
  41. fireViewDeleteEvent( evt, data.domEvent, deleteData );
  42. } );
  43. // `beforeinput` is handled only for Android devices. Desktop Chrome and iOS are skipped because they are working fine now.
  44. if ( env.isAndroid ) {
  45. document.on( 'beforeinput', ( evt, data ) => {
  46. // If event type is other than `deleteContentBackward` then this is not deleting.
  47. if ( data.domEvent.inputType != 'deleteContentBackward' ) {
  48. return;
  49. }
  50. const deleteData = {
  51. unit: 'codepoint',
  52. direction: 'backward',
  53. sequence: 1
  54. };
  55. // Android IMEs may change the DOM selection on `beforeinput` event so that the selection contains all the text
  56. // that the IME wants to remove. We will pass this information to `delete` event so proper part of the content is removed.
  57. //
  58. // Sometimes it is only expanding by a one character (in case of collapsed selection). In this case we don't need to
  59. // set a different selection to remove, it will work just fine.
  60. const domSelection = data.domTarget.ownerDocument.defaultView.getSelection();
  61. if ( domSelection.anchorNode == domSelection.focusNode && domSelection.anchorOffset + 1 != domSelection.focusOffset ) {
  62. deleteData.selectionToRemove = view.domConverter.domSelectionToView( domSelection );
  63. }
  64. fireViewDeleteEvent( evt, data.domEvent, deleteData );
  65. } );
  66. }
  67. function fireViewDeleteEvent( originalEvent, domEvent, deleteData ) {
  68. // Save the event object to check later if it was stopped or not.
  69. let event;
  70. document.once( 'delete', evt => ( event = evt ), { priority: Number.POSITIVE_INFINITY } );
  71. document.fire( 'delete', new DomEventData( document, domEvent, deleteData ) );
  72. // Stop the original event if `delete` event was stopped.
  73. // https://github.com/ckeditor/ckeditor5/issues/753
  74. if ( event && event.stop.called ) {
  75. originalEvent.stop();
  76. }
  77. }
  78. }
  79. /**
  80. * @inheritDoc
  81. */
  82. observe() {}
  83. }
  84. /**
  85. * Event fired when the user tries to delete content (e.g. presses <kbd>Delete</kbd> or <kbd>Backspace</kbd>).
  86. *
  87. * Note: This event is fired by the {@link module:typing/deleteobserver~DeleteObserver observer}
  88. * (usually registered by the {@link module:typing/delete~Delete delete feature}).
  89. *
  90. * @event module:engine/view/document~Document#event:delete
  91. * @param {module:engine/view/observer/domeventdata~DomEventData} data
  92. * @param {'forward'|'delete'} data.direction The direction in which the deletion should happen.
  93. * @param {'character'|'word'} data.unit The "amount" of content that should be deleted.
  94. * @param {Number} data.sequence A number describing which subsequent delete event it is without the key being released.
  95. * If it's 2 or more it means that the key was pressed and hold.
  96. * @param {module:engine/view/selection~Selection} [data.selectionToRemove] View selection which content should be removed. If not set,
  97. * current selection should be used.
  98. */