8
0

keystrokehandler.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module utils/keystrokehandler
  7. */
  8. import DomEmitterMixin from './dom/emittermixin';
  9. import { getCode, parseKeystroke } from './keyboard';
  10. /**
  11. * Keystroke handler registers keystrokes so the callbacks associated
  12. * with these keystrokes will be executed if the matching `keydown` is fired
  13. * by a defined emitter.
  14. *
  15. * const handler = new KeystrokeHandler();
  16. *
  17. * handler.listenTo( emitter );
  18. *
  19. * handler.set( 'Ctrl+A', ( keyEvtData, cancel ) => {
  20. * console.log( 'Ctrl+A has been pressed' );
  21. * cancel();
  22. * } );
  23. */
  24. export default class KeystrokeHandler {
  25. /**
  26. * Creates an instance of the keystroke handler.
  27. */
  28. constructor() {
  29. /**
  30. * Listener used to listen to events for easier keystroke handler destruction.
  31. *
  32. * @protected
  33. * @member {module:utils/dom/emittermixin~Emitter}
  34. */
  35. this._listener = Object.create( DomEmitterMixin );
  36. }
  37. /**
  38. * Starts listening for `keydown` events from a given emitter.
  39. *
  40. * @param {module:utils/emittermixin~Emitter} emitter
  41. */
  42. listenTo( emitter ) {
  43. // The #_listener works here as a kind of dispatcher. It groups the events coming from the same
  44. // keystroke so the listeners can be attached to them with different priorities.
  45. //
  46. // E.g. all the keystrokes with the `keyCode` of 42 coming from the `emitter` are propagated
  47. // as a `_keydown:42` event by the `_listener`. If there's a callback created by the `set`
  48. // method for this 42 keystroke, it listens to the `_listener#_keydown:42` event only and interacts
  49. // only with other listeners of this particular event, thus making it possible to prioritize
  50. // the listeners and safely cancel execution, when needed. Instead of duplicating the Emitter logic,
  51. // the KeystrokeHandler re–uses it to do its job.
  52. this._listener.listenTo( emitter, 'keydown', ( evt, keyEvtData ) => {
  53. this._listener.fire( '_keydown:' + getCode( keyEvtData ), keyEvtData );
  54. } );
  55. }
  56. /**
  57. * Registers a handler for the specified keystroke.
  58. *
  59. * @param {String|Array.<String|Number>} keystroke Keystroke defined in a format accepted by
  60. * the {@link module:utils/keyboard~parseKeystroke} function.
  61. * @param {Function} callback A function called with the
  62. * {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
  63. * a helper to both `preventDefault` and `stopPropagation` of the event.
  64. * @param {Object} [options={}] Additional options.
  65. * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of the keystroke
  66. * callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority
  67. * are called in the order they were added.
  68. */
  69. set( keystroke, callback, options = {} ) {
  70. const keyCode = parseKeystroke( keystroke );
  71. const priority = options.priority;
  72. // Execute the passed callback on KeystrokeHandler#_keydown.
  73. // TODO: https://github.com/ckeditor/ckeditor5-utils/issues/144
  74. this._listener.listenTo( this._listener, '_keydown:' + keyCode, ( evt, keyEvtData ) => {
  75. callback( keyEvtData, () => {
  76. // Stop the event in the DOM: no listener in the web page
  77. // will be triggered by this event.
  78. keyEvtData.preventDefault();
  79. keyEvtData.stopPropagation();
  80. // Stop the event in the KeystrokeHandler: no more callbacks
  81. // will be executed for this keystroke.
  82. evt.stop();
  83. } );
  84. // Mark this keystroke as handled by the callback. See: #press.
  85. evt.return = true;
  86. }, { priority } );
  87. }
  88. /**
  89. * Triggers a keystroke handler for a specified key combination, if such a keystroke was {@link #set defined}.
  90. *
  91. * @param {module:engine/view/observer/keyobserver~KeyEventData} keyEvtData Key event data.
  92. * @returns {Boolean} Whether the keystroke was handled.
  93. */
  94. press( keyEvtData ) {
  95. return !!this._listener.fire( '_keydown:' + getCode( keyEvtData ), keyEvtData );
  96. }
  97. /**
  98. * Destroys the keystroke handler.
  99. */
  100. destroy() {
  101. this._listener.stopListening();
  102. }
  103. }