mxKeyHandler.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxKeyHandler
  7. *
  8. * Event handler that listens to keystroke events. This is not a singleton,
  9. * however, it is normally only required once if the target is the document
  10. * element (default).
  11. *
  12. * This handler installs a key event listener in the topmost DOM node and
  13. * processes all events that originate from descandants of <mxGraph.container>
  14. * or from the topmost DOM node. The latter means that all unhandled keystrokes
  15. * are handled by this object regardless of the focused state of the <graph>.
  16. *
  17. * Example:
  18. *
  19. * The following example creates a key handler that listens to the delete key
  20. * (46) and deletes the selection cells if the graph is enabled.
  21. *
  22. * (code)
  23. * var keyHandler = new mxKeyHandler(graph);
  24. * keyHandler.bindKey(46, function(evt)
  25. * {
  26. * if (graph.isEnabled())
  27. * {
  28. * graph.removeCells();
  29. * }
  30. * });
  31. * (end)
  32. *
  33. * Keycodes:
  34. *
  35. * See http://tinyurl.com/yp8jgl or http://tinyurl.com/229yqw for a list of
  36. * keycodes or install a key event listener into the document element and print
  37. * the key codes of the respective events to the console.
  38. *
  39. * To support the Command key and the Control key on the Mac, the following
  40. * code can be used.
  41. *
  42. * (code)
  43. * keyHandler.getFunction = function(evt)
  44. * {
  45. * if (evt != null)
  46. * {
  47. * return (mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey)) ? this.controlKeys[evt.keyCode] : this.normalKeys[evt.keyCode];
  48. * }
  49. *
  50. * return null;
  51. * };
  52. * (end)
  53. *
  54. * Constructor: mxKeyHandler
  55. *
  56. * Constructs an event handler that executes functions bound to specific
  57. * keystrokes.
  58. *
  59. * Parameters:
  60. *
  61. * graph - Reference to the associated <mxGraph>.
  62. * target - Optional reference to the event target. If null, the document
  63. * element is used as the event target, that is, the object where the key
  64. * event listener is installed.
  65. */
  66. function mxKeyHandler(graph, target)
  67. {
  68. if (graph != null)
  69. {
  70. this.graph = graph;
  71. this.target = target || document.documentElement;
  72. // Creates the arrays to map from keycodes to functions
  73. this.normalKeys = [];
  74. this.shiftKeys = [];
  75. this.controlKeys = [];
  76. this.controlShiftKeys = [];
  77. this.keydownHandler = mxUtils.bind(this, function(evt)
  78. {
  79. this.keyDown(evt);
  80. });
  81. // Installs the keystroke listener in the target
  82. mxEvent.addListener(this.target, 'keydown', this.keydownHandler);
  83. // Automatically deallocates memory in IE
  84. if (mxClient.IS_IE)
  85. {
  86. mxEvent.addListener(window, 'unload',
  87. mxUtils.bind(this, function()
  88. {
  89. this.destroy();
  90. })
  91. );
  92. }
  93. }
  94. };
  95. /**
  96. * Variable: graph
  97. *
  98. * Reference to the <mxGraph> associated with this handler.
  99. */
  100. mxKeyHandler.prototype.graph = null;
  101. /**
  102. * Variable: target
  103. *
  104. * Reference to the target DOM, that is, the DOM node where the key event
  105. * listeners are installed.
  106. */
  107. mxKeyHandler.prototype.target = null;
  108. /**
  109. * Variable: normalKeys
  110. *
  111. * Maps from keycodes to functions for non-pressed control keys.
  112. */
  113. mxKeyHandler.prototype.normalKeys = null;
  114. /**
  115. * Variable: shiftKeys
  116. *
  117. * Maps from keycodes to functions for pressed shift keys.
  118. */
  119. mxKeyHandler.prototype.shiftKeys = null;
  120. /**
  121. * Variable: controlKeys
  122. *
  123. * Maps from keycodes to functions for pressed control keys.
  124. */
  125. mxKeyHandler.prototype.controlKeys = null;
  126. /**
  127. * Variable: controlShiftKeys
  128. *
  129. * Maps from keycodes to functions for pressed control and shift keys.
  130. */
  131. mxKeyHandler.prototype.controlShiftKeys = null;
  132. /**
  133. * Variable: enabled
  134. *
  135. * Specifies if events are handled. Default is true.
  136. */
  137. mxKeyHandler.prototype.enabled = true;
  138. /**
  139. * Function: isEnabled
  140. *
  141. * Returns true if events are handled. This implementation returns
  142. * <enabled>.
  143. */
  144. mxKeyHandler.prototype.isEnabled = function()
  145. {
  146. return this.enabled;
  147. };
  148. /**
  149. * Function: setEnabled
  150. *
  151. * Enables or disables event handling by updating <enabled>.
  152. *
  153. * Parameters:
  154. *
  155. * enabled - Boolean that specifies the new enabled state.
  156. */
  157. mxKeyHandler.prototype.setEnabled = function(enabled)
  158. {
  159. this.enabled = enabled;
  160. };
  161. /**
  162. * Function: bindKey
  163. *
  164. * Binds the specified keycode to the given function. This binding is used
  165. * if the control key is not pressed.
  166. *
  167. * Parameters:
  168. *
  169. * code - Integer that specifies the keycode.
  170. * funct - JavaScript function that takes the key event as an argument.
  171. */
  172. mxKeyHandler.prototype.bindKey = function(code, funct)
  173. {
  174. this.normalKeys[code] = funct;
  175. };
  176. /**
  177. * Function: bindShiftKey
  178. *
  179. * Binds the specified keycode to the given function. This binding is used
  180. * if the shift key is pressed.
  181. *
  182. * Parameters:
  183. *
  184. * code - Integer that specifies the keycode.
  185. * funct - JavaScript function that takes the key event as an argument.
  186. */
  187. mxKeyHandler.prototype.bindShiftKey = function(code, funct)
  188. {
  189. this.shiftKeys[code] = funct;
  190. };
  191. /**
  192. * Function: bindControlKey
  193. *
  194. * Binds the specified keycode to the given function. This binding is used
  195. * if the control key is pressed.
  196. *
  197. * Parameters:
  198. *
  199. * code - Integer that specifies the keycode.
  200. * funct - JavaScript function that takes the key event as an argument.
  201. */
  202. mxKeyHandler.prototype.bindControlKey = function(code, funct)
  203. {
  204. this.controlKeys[code] = funct;
  205. };
  206. /**
  207. * Function: bindControlShiftKey
  208. *
  209. * Binds the specified keycode to the given function. This binding is used
  210. * if the control and shift key are pressed.
  211. *
  212. * Parameters:
  213. *
  214. * code - Integer that specifies the keycode.
  215. * funct - JavaScript function that takes the key event as an argument.
  216. */
  217. mxKeyHandler.prototype.bindControlShiftKey = function(code, funct)
  218. {
  219. this.controlShiftKeys[code] = funct;
  220. };
  221. /**
  222. * Function: isControlDown
  223. *
  224. * Returns true if the control key is pressed. This uses <mxEvent.isControlDown>.
  225. *
  226. * Parameters:
  227. *
  228. * evt - Key event whose control key pressed state should be returned.
  229. */
  230. mxKeyHandler.prototype.isControlDown = function(evt)
  231. {
  232. return mxEvent.isControlDown(evt);
  233. };
  234. /**
  235. * Function: getFunction
  236. *
  237. * Returns the function associated with the given key event or null if no
  238. * function is associated with the given event.
  239. *
  240. * Parameters:
  241. *
  242. * evt - Key event whose associated function should be returned.
  243. */
  244. mxKeyHandler.prototype.getFunction = function(evt)
  245. {
  246. if (evt != null && !mxEvent.isAltDown(evt))
  247. {
  248. if (this.isControlDown(evt))
  249. {
  250. if (mxEvent.isShiftDown(evt))
  251. {
  252. return this.controlShiftKeys[evt.keyCode];
  253. }
  254. else
  255. {
  256. return this.controlKeys[evt.keyCode];
  257. }
  258. }
  259. else
  260. {
  261. if (mxEvent.isShiftDown(evt))
  262. {
  263. return this.shiftKeys[evt.keyCode];
  264. }
  265. else
  266. {
  267. return this.normalKeys[evt.keyCode];
  268. }
  269. }
  270. }
  271. return null;
  272. };
  273. /**
  274. * Function: isGraphEvent
  275. *
  276. * Returns true if the event should be processed by this handler, that is,
  277. * if the event source is either the target, one of its direct children, a
  278. * descendant of the <mxGraph.container>, or the <mxGraph.cellEditor> of the
  279. * <graph>.
  280. *
  281. * Parameters:
  282. *
  283. * evt - Key event that represents the keystroke.
  284. */
  285. mxKeyHandler.prototype.isGraphEvent = function(evt)
  286. {
  287. var source = mxEvent.getSource(evt);
  288. // Accepts events from the target object or
  289. // in-place editing inside graph
  290. if ((source == this.target || source.parentNode == this.target) ||
  291. (this.graph.cellEditor != null && this.graph.cellEditor.isEventSource(evt)))
  292. {
  293. return true;
  294. }
  295. // Accepts events from inside the container
  296. return mxUtils.isAncestorNode(this.graph.container, source);
  297. };
  298. /**
  299. * Function: keyDown
  300. *
  301. * Handles the event by invoking the function bound to the respective keystroke
  302. * if <isEnabledForEvent> returns true for the given event and if
  303. * <isEventIgnored> returns false, except for escape for which
  304. * <isEventIgnored> is not invoked.
  305. *
  306. * Parameters:
  307. *
  308. * evt - Key event that represents the keystroke.
  309. */
  310. mxKeyHandler.prototype.keyDown = function(evt)
  311. {
  312. if (this.isEnabledForEvent(evt))
  313. {
  314. // Cancels the editing if escape is pressed
  315. if (evt.keyCode == 27 /* Escape */)
  316. {
  317. this.escape(evt);
  318. }
  319. // Invokes the function for the keystroke
  320. else if (!this.isEventIgnored(evt))
  321. {
  322. var boundFunction = this.getFunction(evt);
  323. if (boundFunction != null)
  324. {
  325. boundFunction(evt);
  326. mxEvent.consume(evt);
  327. }
  328. }
  329. }
  330. };
  331. /**
  332. * Function: isEnabledForEvent
  333. *
  334. * Returns true if the given event should be handled. <isEventIgnored> is
  335. * called later if the event is not an escape key stroke, in which case
  336. * <escape> is called. This implementation returns true if <isEnabled>
  337. * returns true for both, this handler and <graph>, if the event is not
  338. * consumed and if <isGraphEvent> returns true.
  339. *
  340. * Parameters:
  341. *
  342. * evt - Key event that represents the keystroke.
  343. */
  344. mxKeyHandler.prototype.isEnabledForEvent = function(evt)
  345. {
  346. return (this.graph.isEnabled() && !mxEvent.isConsumed(evt) &&
  347. this.isGraphEvent(evt) && this.isEnabled());
  348. };
  349. /**
  350. * Function: isEventIgnored
  351. *
  352. * Returns true if the given keystroke should be ignored. This returns
  353. * graph.isEditing().
  354. *
  355. * Parameters:
  356. *
  357. * evt - Key event that represents the keystroke.
  358. */
  359. mxKeyHandler.prototype.isEventIgnored = function(evt)
  360. {
  361. return this.graph.isEditing();
  362. };
  363. /**
  364. * Function: escape
  365. *
  366. * Hook to process ESCAPE keystrokes. This implementation invokes
  367. * <mxGraph.stopEditing> to cancel the current editing, connecting
  368. * and/or other ongoing modifications.
  369. *
  370. * Parameters:
  371. *
  372. * evt - Key event that represents the keystroke. Possible keycode in this
  373. * case is 27 (ESCAPE).
  374. */
  375. mxKeyHandler.prototype.escape = function(evt)
  376. {
  377. if (this.graph.isEscapeEnabled())
  378. {
  379. this.graph.escape(evt);
  380. }
  381. };
  382. /**
  383. * Function: destroy
  384. *
  385. * Destroys the handler and all its references into the DOM. This does
  386. * normally not need to be called, it is called automatically when the
  387. * window unloads (in IE).
  388. */
  389. mxKeyHandler.prototype.destroy = function()
  390. {
  391. if (this.target != null && this.keydownHandler != null)
  392. {
  393. mxEvent.removeListener(this.target, 'keydown', this.keydownHandler);
  394. this.keydownHandler = null;
  395. }
  396. this.target = null;
  397. };
  398. __mxOutput.mxKeyHandler = typeof mxKeyHandler !== 'undefined' ? mxKeyHandler : undefined;