fakeselectionobserver.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module engine/view/observer/fakeselectionobserver
  7. */
  8. import Observer from './observer';
  9. import ViewDocumentSelection from '../documentselection';
  10. import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
  11. import debounce from '@ckeditor/ckeditor5-utils/src/lib/lodash/debounce';
  12. /**
  13. * Fake selection observer class. If view selection is fake it is placed in dummy DOM container. This observer listens
  14. * on {@link module:engine/view/document~Document#event:keydown keydown} events and handles moving fake view selection to the correct place
  15. * if arrow keys are pressed.
  16. * Fires {@link module:engine/view/document~Document#event:selectionChange selectionChange event} simulating natural behaviour of
  17. * {@link module:engine/view/observer/selectionobserver~SelectionObserver SelectionObserver}.
  18. *
  19. * @extends module:engine/view/observer/observer~Observer.Observer
  20. */
  21. export default class FakeSelectionObserver extends Observer {
  22. /**
  23. * Creates new FakeSelectionObserver instance.
  24. *
  25. * @param {module:engine/view/view~View} view
  26. */
  27. constructor( view ) {
  28. super( view );
  29. /**
  30. * Fires debounced event `selectionChangeDone`. It uses `lodash#debounce` method to delay function call.
  31. *
  32. * @private
  33. * @param {Object} data Selection change data.
  34. * @method #_fireSelectionChangeDoneDebounced
  35. */
  36. this._fireSelectionChangeDoneDebounced = debounce( data => this.document.fire( 'selectionChangeDone', data ), 200 );
  37. }
  38. /**
  39. * @inheritDoc
  40. */
  41. observe() {
  42. const document = this.document;
  43. document.on( 'keydown', ( eventInfo, data ) => {
  44. const selection = document.selection;
  45. if ( selection.isFake && _isArrowKeyCode( data.keyCode ) && this.isEnabled ) {
  46. // Prevents default key down handling - no selection change will occur.
  47. data.preventDefault();
  48. this._handleSelectionMove( data.keyCode );
  49. }
  50. }, { priority: 'lowest' } );
  51. }
  52. /**
  53. * @inheritDoc
  54. */
  55. destroy() {
  56. super.destroy();
  57. this._fireSelectionChangeDoneDebounced.cancel();
  58. }
  59. /**
  60. * Handles collapsing view selection according to given key code. If left or up key is provided - new selection will be
  61. * collapsed to left. If right or down key is pressed - new selection will be collapsed to right.
  62. *
  63. * This method fires {@link module:engine/view/document~Document#event:selectionChange} and
  64. * {@link module:engine/view/document~Document#event:selectionChangeDone} events imitating behaviour of
  65. * {@link module:engine/view/observer/selectionobserver~SelectionObserver}.
  66. *
  67. * @private
  68. * @param {Number} keyCode
  69. * @fires module:engine/view/document~Document#event:selectionChange
  70. * @fires module:engine/view/document~Document#event:selectionChangeDone
  71. */
  72. _handleSelectionMove( keyCode ) {
  73. const selection = this.document.selection;
  74. const newSelection = new ViewDocumentSelection( selection.getRanges(), { backward: selection.isBackward, fake: false } );
  75. // Left or up arrow pressed - move selection to start.
  76. if ( keyCode == keyCodes.arrowleft || keyCode == keyCodes.arrowup ) {
  77. newSelection._setTo( newSelection.getFirstPosition() );
  78. }
  79. // Right or down arrow pressed - move selection to end.
  80. if ( keyCode == keyCodes.arrowright || keyCode == keyCodes.arrowdown ) {
  81. newSelection._setTo( newSelection.getLastPosition() );
  82. }
  83. const data = {
  84. oldSelection: selection,
  85. newSelection,
  86. domSelection: null
  87. };
  88. // Fire dummy selection change event.
  89. this.document.fire( 'selectionChange', data );
  90. // Call` #_fireSelectionChangeDoneDebounced` every time when `selectionChange` event is fired.
  91. // This function is debounced what means that `selectionChangeDone` event will be fired only when
  92. // defined int the function time will elapse since the last time the function was called.
  93. // So `selectionChangeDone` will be fired when selection will stop changing.
  94. this._fireSelectionChangeDoneDebounced( data );
  95. }
  96. }
  97. // Checks if one of the arrow keys is pressed.
  98. //
  99. // @private
  100. // @param {Number} keyCode
  101. // @returns {Boolean}
  102. function _isArrowKeyCode( keyCode ) {
  103. return keyCode == keyCodes.arrowright ||
  104. keyCode == keyCodes.arrowleft ||
  105. keyCode == keyCodes.arrowup ||
  106. keyCode == keyCodes.arrowdown;
  107. }