keyboard.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import CKEditorError from './ckeditorerror.js';
  6. import env from './env.js';
  7. /**
  8. * Set of utils related to keyboard support.
  9. *
  10. * @namespace utils.keyboard
  11. */
  12. /**
  13. * Object with `keyName => keyCode` pairs for a set of known keys.
  14. *
  15. * Contains:
  16. *
  17. * * `a-z`,
  18. * * `0-9`,
  19. * * `arrow(left|up|right|bottom)`,
  20. * * `backspace`, `delete`, `enter`,
  21. * * `ctrl`, `cmd`, `shift`, `alt`.
  22. *
  23. * @member {Object} utils.keyboard.keyCodes
  24. */
  25. export const keyCodes = generateKnownKeyCodes();
  26. /**
  27. * Converts a key name or a {@link utils.keyboard.KeystrokeInfo keystroke info} into a key code.
  28. *
  29. * Note: Key names are matched with {@link utils.keyboard.keyCodes} in a case-insensitive way.
  30. *
  31. * @method utils.keyboard.getCode
  32. * @param {String|utils.keyboard.KeystrokeInfo} Key name (see {@link utils.keyboard.keyCodes})
  33. * or a keystroke data object.
  34. * @returns {Number} Key or keystroke code.
  35. */
  36. export function getCode( key ) {
  37. let keyCode;
  38. if ( typeof key == 'string' ) {
  39. keyCode = keyCodes[ key.toLowerCase() ];
  40. if ( !keyCode ) {
  41. /**
  42. * Unknown key name. Only key names contained by the {@link utils.keyboard.keyCodes} can be used.
  43. *
  44. * @errror keyboard-unknown-key
  45. * @param {String} key
  46. */
  47. throw new CKEditorError( 'keyboard-unknown-key: Unknown key name.', { key } );
  48. }
  49. } else {
  50. keyCode = key.keyCode +
  51. ( key.altKey ? keyCodes.alt : 0 ) +
  52. ( key.ctrlKey ? keyCodes.ctrl : 0 ) +
  53. ( key.shiftKey ? keyCodes.shift : 0 );
  54. }
  55. return keyCode;
  56. }
  57. /**
  58. * Parses keystroke and returns a keystroke code that will match the code returned by
  59. * link {@link utils.keyboard.getCode} for a corresponding {@link utils.keyboard.KeystrokeInfo keystroke info}.
  60. *
  61. * The keystroke can be passed in two formats:
  62. *
  63. * * as a single string – e.g. `ctrl + A`,
  64. * * as an array of {@link utils.keyboard.keyCodes known key names} and key codes – e.g.:
  65. * * `[ 'ctrl', 32 ]` (ctrl + space),
  66. * * `[ 'ctrl', 'a' ]` (ctrl + A).
  67. *
  68. * Note: Key names are matched with {@link utils.keyboard.keyCodes} in a case-insensitive way.
  69. *
  70. * Note: Only keystrokes with a single non-modifier key are supported (e.g. `ctrl+A` is OK, but `ctrl+A+B` is not).
  71. *
  72. * @method utils.keyboard.parseKeystroke
  73. * @param {String|Array.<Number|String>} keystroke Keystroke definition.
  74. * @returns {Number} Keystroke code.
  75. */
  76. export function parseKeystroke( keystroke ) {
  77. if ( typeof keystroke == 'string' ) {
  78. keystroke = splitKeystrokeText( keystroke );
  79. }
  80. return keystroke
  81. .map( key => ( typeof key == 'string' ) ? getCode( key ) : key )
  82. .reduce( ( key, sum ) => sum + key, 0 );
  83. }
  84. /**
  85. * It translates any keystroke string text like `"CTRL+A"` to an
  86. * environment–specific keystroke, i.e. `"⌘A"` on Mac OSX.
  87. *
  88. * @method utils.keyboard.getEnvKeystrokeText
  89. * @param {String} keystroke Keystroke text.
  90. * @returns {String} Keystroke text specific for the environment.
  91. */
  92. export function getEnvKeystrokeText( keystroke ) {
  93. const split = splitKeystrokeText( keystroke );
  94. if ( env.mac ) {
  95. if ( split[ 0 ].toLowerCase() == 'ctrl' ) {
  96. return '⌘' + ( split[ 1 ] || '' );
  97. }
  98. }
  99. return keystroke;
  100. }
  101. function generateKnownKeyCodes() {
  102. const keyCodes = {
  103. arrowleft: 37,
  104. arrowup: 38,
  105. arrowright: 39,
  106. arrowdown: 40,
  107. backspace: 8,
  108. delete: 46,
  109. enter: 13,
  110. // The idea about these numbers is that they do not collide with any real key codes, so we can use them
  111. // like bit masks.
  112. ctrl: 0x110000,
  113. // Has the same code as ctrl, because their behaviour should be unified across the editor.
  114. // See http://ckeditor.github.io/editor-recommendations/general-policies#ctrl-vs-cmd
  115. cmd: 0x110000,
  116. shift: 0x220000,
  117. alt: 0x440000
  118. };
  119. // a-z
  120. for ( let code = 65; code <= 90; code++ ) {
  121. const letter = String.fromCharCode( code );
  122. keyCodes[ letter.toLowerCase() ] = code;
  123. }
  124. // 0-9
  125. for ( let code = 48; code <= 57; code++ ) {
  126. keyCodes[ code - 48 ] = code;
  127. }
  128. return keyCodes;
  129. }
  130. function splitKeystrokeText( keystroke ) {
  131. return keystroke.split( /\s*\+\s*/ );
  132. }
  133. /**
  134. * Information about a keystroke.
  135. *
  136. * @interface utils.keyboard.KeystrokeInfo
  137. */
  138. /**
  139. * The [key code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode).
  140. *
  141. * @member {Number} utils.keyboard.KeystrokeInfo#keyCode
  142. */
  143. /**
  144. * Whether the <kbd>Alt</kbd> modifier was pressed.
  145. *
  146. * @member {Bolean} utils.keyboard.KeystrokeInfo#altKey
  147. */
  148. /**
  149. * Whether the <kbd>Ctrl</kbd> or <kbd>Cmd</kbd> modifier was pressed.
  150. *
  151. * @member {Bolean} utils.keyboard.KeystrokeInfo#ctrlKey
  152. */
  153. /**
  154. * Whether the <kbd>Shift</kbd> modifier was pressed.
  155. *
  156. * @member {Bolean} utils.keyboard.KeystrokeInfo#shiftKey
  157. */