rlm@46: /** rlm@46: * @class Ext.KeyMap rlm@46: * Handles mapping keys to actions for an element. One key map can be used for multiple actions. rlm@46: * The constructor accepts the same config object as defined by {@link #addBinding}. rlm@46: * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key rlm@46: * combination it will call the function with this signature (if the match is a multi-key rlm@46: * combination the callback will still be called only once): (String key, Ext.EventObject e) rlm@46: * A KeyMap can also handle a string representation of keys.
rlm@46: * Usage: rlm@46:

rlm@46: // map one key by key code
rlm@46: var map = new Ext.KeyMap("my-element", {
rlm@46:     key: 13, // or Ext.EventObject.ENTER
rlm@46:     fn: myHandler,
rlm@46:     scope: myObject
rlm@46: });
rlm@46: 
rlm@46: // map multiple keys to one action by string
rlm@46: var map = new Ext.KeyMap("my-element", {
rlm@46:     key: "a\r\n\t",
rlm@46:     fn: myHandler,
rlm@46:     scope: myObject
rlm@46: });
rlm@46: 
rlm@46: // map multiple keys to multiple actions by strings and array of codes
rlm@46: var map = new Ext.KeyMap("my-element", [
rlm@46:     {
rlm@46:         key: [10,13],
rlm@46:         fn: function(){ alert("Return was pressed"); }
rlm@46:     }, {
rlm@46:         key: "abc",
rlm@46:         fn: function(){ alert('a, b or c was pressed'); }
rlm@46:     }, {
rlm@46:         key: "\t",
rlm@46:         ctrl:true,
rlm@46:         shift:true,
rlm@46:         fn: function(){ alert('Control + shift + tab was pressed.'); }
rlm@46:     }
rlm@46: ]);
rlm@46: 
rlm@46: * Note: A KeyMap starts enabled rlm@46: * @constructor rlm@46: * @param {String/HTMLElement/Ext.Element} el The element to bind to rlm@46: * @param {Object} config The config (see {@link #addBinding}) rlm@46: * @param {String} eventName (optional) The event to bind to (defaults to "keydown") rlm@46: */ rlm@46: Ext.KeyMap = function(el, config, eventName){ rlm@46: this.el = Ext.get(el); rlm@46: this.eventName = eventName || "keydown"; rlm@46: this.bindings = []; rlm@46: if(config){ rlm@46: this.addBinding(config); rlm@46: } rlm@46: this.enable(); rlm@46: }; rlm@46: rlm@46: Ext.KeyMap.prototype = { rlm@46: /** rlm@46: * True to stop the event from bubbling and prevent the default browser action if the rlm@46: * key was handled by the KeyMap (defaults to false) rlm@46: * @type Boolean rlm@46: */ rlm@46: stopEvent : false, rlm@46: rlm@46: /** rlm@46: * Add a new binding to this KeyMap. The following config object properties are supported: rlm@46: *
rlm@46: Property    Type             Description
rlm@46: ----------  ---------------  ----------------------------------------------------------------------
rlm@46: key         String/Array     A single keycode or an array of keycodes to handle
rlm@46: shift       Boolean          True to handle key only when shift is pressed (defaults to false)
rlm@46: ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
rlm@46: alt         Boolean          True to handle key only when alt is pressed (defaults to false)
rlm@46: fn          Function         The function to call when KeyMap finds the expected key combination
rlm@46: scope       Object           The scope of the callback function
rlm@46: 
rlm@46: * rlm@46: * Usage: rlm@46: *

rlm@46: // Create a KeyMap
rlm@46: var map = new Ext.KeyMap(document, {
rlm@46:     key: Ext.EventObject.ENTER,
rlm@46:     fn: handleKey,
rlm@46:     scope: this
rlm@46: });
rlm@46: 
rlm@46: //Add a new binding to the existing KeyMap later
rlm@46: map.addBinding({
rlm@46:     key: 'abc',
rlm@46:     shift: true,
rlm@46:     fn: handleKey,
rlm@46:     scope: this
rlm@46: });
rlm@46: 
rlm@46: * @param {Object/Array} config A single KeyMap config or an array of configs rlm@46: */ rlm@46: addBinding : function(config){ rlm@46: if(config instanceof Array){ rlm@46: for(var i = 0, len = config.length; i < len; i++){ rlm@46: this.addBinding(config[i]); rlm@46: } rlm@46: return; rlm@46: } rlm@46: var keyCode = config.key, rlm@46: shift = config.shift, rlm@46: ctrl = config.ctrl, rlm@46: alt = config.alt, rlm@46: fn = config.fn, rlm@46: scope = config.scope; rlm@46: if(typeof keyCode == "string"){ rlm@46: var ks = []; rlm@46: var keyString = keyCode.toUpperCase(); rlm@46: for(var j = 0, len = keyString.length; j < len; j++){ rlm@46: ks.push(keyString.charCodeAt(j)); rlm@46: } rlm@46: keyCode = ks; rlm@46: } rlm@46: var keyArray = keyCode instanceof Array; rlm@46: var handler = function(e){ rlm@46: if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){ rlm@46: var k = e.getKey(); rlm@46: if(keyArray){ rlm@46: for(var i = 0, len = keyCode.length; i < len; i++){ rlm@46: if(keyCode[i] == k){ rlm@46: if(this.stopEvent){ rlm@46: e.stopEvent(); rlm@46: } rlm@46: fn.call(scope || window, k, e); rlm@46: return; rlm@46: } rlm@46: } rlm@46: }else{ rlm@46: if(k == keyCode){ rlm@46: if(this.stopEvent){ rlm@46: e.stopEvent(); rlm@46: } rlm@46: fn.call(scope || window, k, e); rlm@46: } rlm@46: } rlm@46: } rlm@46: }; rlm@46: this.bindings.push(handler); rlm@46: }, rlm@46: rlm@46: /** rlm@46: * Shorthand for adding a single key listener rlm@46: * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the rlm@46: * following options: rlm@46: * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)} rlm@46: * @param {Function} fn The function to call rlm@46: * @param {Object} scope (optional) The scope of the function rlm@46: */ rlm@46: on : function(key, fn, scope){ rlm@46: var keyCode, shift, ctrl, alt; rlm@46: if(typeof key == "object" && !(key instanceof Array)){ rlm@46: keyCode = key.key; rlm@46: shift = key.shift; rlm@46: ctrl = key.ctrl; rlm@46: alt = key.alt; rlm@46: }else{ rlm@46: keyCode = key; rlm@46: } rlm@46: this.addBinding({ rlm@46: key: keyCode, rlm@46: shift: shift, rlm@46: ctrl: ctrl, rlm@46: alt: alt, rlm@46: fn: fn, rlm@46: scope: scope rlm@46: }) rlm@46: }, rlm@46: rlm@46: // private rlm@46: handleKeyDown : function(e){ rlm@46: if(this.enabled){ //just in case rlm@46: var b = this.bindings; rlm@46: for(var i = 0, len = b.length; i < len; i++){ rlm@46: b[i].call(this, e); rlm@46: } rlm@46: } rlm@46: }, rlm@46: rlm@46: /** rlm@46: * Returns true if this KeyMap is enabled rlm@46: * @return {Boolean} rlm@46: */ rlm@46: isEnabled : function(){ rlm@46: return this.enabled; rlm@46: }, rlm@46: rlm@46: /** rlm@46: * Enables this KeyMap rlm@46: */ rlm@46: enable: function(){ rlm@46: if(!this.enabled){ rlm@46: this.el.on(this.eventName, this.handleKeyDown, this); rlm@46: this.enabled = true; rlm@46: } rlm@46: }, rlm@46: rlm@46: /** rlm@46: * Disable this KeyMap rlm@46: */ rlm@46: disable: function(){ rlm@46: if(this.enabled){ rlm@46: this.el.removeListener(this.eventName, this.handleKeyDown, this); rlm@46: this.enabled = false; rlm@46: } rlm@46: } rlm@46: };