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: };