3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.util.KeyMap
17 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
18 * The constructor accepts the same config object as defined by {@link #addBinding}.
19 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
20 * combination it will call the function with this signature (if the match is a multi-key
21 * combination the callback will still be called only once): (String key, Ext.EventObject e)
22 * A KeyMap can also handle a string representation of keys.<br />
25 // map one key by key code
26 var map = new Ext.util.KeyMap("my-element", {
27 key: 13, // or Ext.EventObject.ENTER
32 // map multiple keys to one action by string
33 var map = new Ext.util.KeyMap("my-element", {
39 // map multiple keys to multiple actions by strings and array of codes
40 var map = new Ext.util.KeyMap("my-element", [
43 fn: function(){ alert("Return was pressed"); }
46 fn: function(){ alert('a, b or c was pressed'); }
51 fn: function(){ alert('Control + shift + tab was pressed.'); }
55 * <b>Note: A KeyMap starts enabled</b>
57 Ext.define('Ext.util.KeyMap', {
58 alternateClassName: 'Ext.KeyMap',
62 * @param {Mixed} el The element to bind to
63 * @param {Object} binding The binding (see {@link #addBinding})
64 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
66 constructor: function(el, binding, eventName){
71 eventName: eventName || me.eventName,
75 me.addBinding(binding);
83 * Add a new binding to this KeyMap. The following config object properties are supported:
85 Property Type Description
86 ---------- --------------- ----------------------------------------------------------------------
87 key String/Array A single keycode or an array of keycodes to handle
88 shift Boolean True to handle key only when shift is pressed, False to handle the key only when shift is not pressed (defaults to undefined)
89 ctrl Boolean True to handle key only when ctrl is pressed, False to handle the key only when ctrl is not pressed (defaults to undefined)
90 alt Boolean True to handle key only when alt is pressed, False to handle the key only when alt is not pressed (defaults to undefined)
91 handler Function The function to call when KeyMap finds the expected key combination
92 fn Function Alias of handler (for backwards-compatibility)
93 scope Object The scope of the callback function
94 defaultEventAction String A default action to apply to the event. Possible values are: stopEvent, stopPropagation, preventDefault. If no value is set no action is performed.
100 var map = new Ext.util.KeyMap(document, {
101 key: Ext.EventObject.ENTER,
106 //Add a new binding to the existing KeyMap later
114 * @param {Object/Array} binding A single KeyMap config or an array of configs
116 addBinding : function(binding){
117 if (Ext.isArray(binding)) {
118 Ext.each(binding, this.addBinding, this);
122 var keyCode = binding.key,
130 if (Ext.isString(keyCode)) {
132 keyString = keyCode.toLowerCase();
134 for (i = 0, len = keyString.length; i < len; ++i){
135 keys.push(keyString.charCodeAt(i));
141 if (!Ext.isArray(keyCode)) {
146 for (i = 0, len = keyCode.length; i < len; ++i) {
148 if (Ext.isString(key)) {
149 keyCode[i] = key.toLowerCase().charCodeAt(0);
154 this.bindings.push(Ext.apply({
160 * Process any keydown events on the element
162 * @param {Ext.EventObject} event
164 handleKeyDown: function(event) {
165 if (this.enabled) { //just in case
166 var bindings = this.bindings,
168 len = bindings.length;
170 event = this.processEvent(event);
172 this.processBinding(bindings[i], event);
178 * Ugly hack to allow this class to be tested. Currently WebKit gives
179 * no way to raise a key event properly with both
181 * b) The alt/ctrl/shift modifiers
182 * So we have to simulate them here. Yuk!
183 * This is a stub method intended to be overridden by tests.
184 * More info: https://bugs.webkit.org/show_bug.cgi?id=16735
187 processEvent: function(event){
192 * Process a particular binding and fire the handler if necessary.
194 * @param {Object} binding The binding information
195 * @param {Ext.EventObject} event
197 processBinding: function(binding, event){
198 if (this.checkModifiers(binding, event)) {
199 var key = event.getKey(),
200 handler = binding.fn || binding.handler,
201 scope = binding.scope || this,
202 keyCode = binding.keyCode,
203 defaultEventAction = binding.defaultEventAction,
206 keydownEvent = new Ext.EventObjectImpl(event);
209 for (i = 0, len = keyCode.length; i < len; ++i) {
210 if (key === keyCode[i]) {
211 if (handler.call(scope, key, event) !== true && defaultEventAction) {
212 keydownEvent[defaultEventAction]();
221 * Check if the modifiers on the event match those on the binding
223 * @param {Object} binding
224 * @param {Ext.EventObject} event
225 * @return {Boolean} True if the event matches the binding
227 checkModifiers: function(binding, e){
228 var keys = ['shift', 'ctrl', 'alt'],
233 for (; i < len; ++i){
236 if (!(val === undefined || (val === e[key + 'Key']))) {
244 * Shorthand for adding a single key listener
245 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
247 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
248 * @param {Function} fn The function to call
249 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
251 on: function(key, fn, scope) {
252 var keyCode, shift, ctrl, alt;
253 if (Ext.isObject(key) && !Ext.isArray(key)) {
272 * Returns true if this KeyMap is enabled
275 isEnabled : function(){
280 * Enables this KeyMap
284 this.el.on(this.eventName, this.handleKeyDown, this);
290 * Disable this KeyMap
294 this.el.removeListener(this.eventName, this.handleKeyDown, this);
295 this.enabled = false;
300 * Convenience function for setting disabled/enabled by boolean.
301 * @param {Boolean} disabled
303 setDisabled : function(disabled){
312 * Destroys the KeyMap instance and removes all handlers.
313 * @param {Boolean} removeEl True to also remove the attached element
315 destroy: function(removeEl){
320 if (removeEl === true) {