provide installation instructions
[extjs.git] / source / util / KeyMap.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.KeyMap\r
11  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.\r
12  * The constructor accepts the same config object as defined by {@link #addBinding}.\r
13  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key\r
14  * combination it will call the function with this signature (if the match is a multi-key\r
15  * combination the callback will still be called only once): (String key, Ext.EventObject e)\r
16  * A KeyMap can also handle a string representation of keys.<br />\r
17  * Usage:\r
18  <pre><code>\r
19 // map one key by key code\r
20 var map = new Ext.KeyMap("my-element", {\r
21     key: 13, // or Ext.EventObject.ENTER\r
22     fn: myHandler,\r
23     scope: myObject\r
24 });\r
25 \r
26 // map multiple keys to one action by string\r
27 var map = new Ext.KeyMap("my-element", {\r
28     key: "a\r\n\t",\r
29     fn: myHandler,\r
30     scope: myObject\r
31 });\r
32 \r
33 // map multiple keys to multiple actions by strings and array of codes\r
34 var map = new Ext.KeyMap("my-element", [\r
35     {\r
36         key: [10,13],\r
37         fn: function(){ alert("Return was pressed"); }\r
38     }, {\r
39         key: "abc",\r
40         fn: function(){ alert('a, b or c was pressed'); }\r
41     }, {\r
42         key: "\t",\r
43         ctrl:true,\r
44         shift:true,\r
45         fn: function(){ alert('Control + shift + tab was pressed.'); }\r
46     }\r
47 ]);\r
48 </code></pre>\r
49  * <b>Note: A KeyMap starts enabled</b>\r
50  * @constructor\r
51  * @param {Mixed} el The element to bind to\r
52  * @param {Object} config The config (see {@link #addBinding})\r
53  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")\r
54  */\r
55 Ext.KeyMap = function(el, config, eventName){\r
56     this.el  = Ext.get(el);\r
57     this.eventName = eventName || "keydown";\r
58     this.bindings = [];\r
59     if(config){\r
60         this.addBinding(config);\r
61     }\r
62     this.enable();\r
63 };\r
64 \r
65 Ext.KeyMap.prototype = {\r
66     /**\r
67      * True to stop the event from bubbling and prevent the default browser action if the\r
68      * key was handled by the KeyMap (defaults to false)\r
69      * @type Boolean\r
70      */\r
71     stopEvent : false,\r
72 \r
73     /**\r
74      * Add a new binding to this KeyMap. The following config object properties are supported:\r
75      * <pre>\r
76 Property    Type             Description\r
77 ----------  ---------------  ----------------------------------------------------------------------\r
78 key         String/Array     A single keycode or an array of keycodes to handle\r
79 shift       Boolean          True to handle key only when shift is pressed (defaults to false)\r
80 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)\r
81 alt         Boolean          True to handle key only when alt is pressed (defaults to false)\r
82 handler     Function         The function to call when KeyMap finds the expected key combination\r
83 fn          Function         Alias of handler (for backwards-compatibility)\r
84 scope       Object           The scope of the callback function\r
85 stopEvent   Boolean          True to stop the event \r
86 </pre>\r
87      *\r
88      * Usage:\r
89      * <pre><code>\r
90 // Create a KeyMap\r
91 var map = new Ext.KeyMap(document, {\r
92     key: Ext.EventObject.ENTER,\r
93     fn: handleKey,\r
94     scope: this\r
95 });\r
96 \r
97 //Add a new binding to the existing KeyMap later\r
98 map.addBinding({\r
99     key: 'abc',\r
100     shift: true,\r
101     fn: handleKey,\r
102     scope: this\r
103 });\r
104 </code></pre>\r
105      * @param {Object/Array} config A single KeyMap config or an array of configs\r
106      */\r
107         addBinding : function(config){\r
108         if(Ext.isArray(config)){\r
109             for(var i = 0, len = config.length; i < len; i++){\r
110                 this.addBinding(config[i]);\r
111             }\r
112             return;\r
113         }\r
114         var keyCode = config.key,\r
115             shift = config.shift,\r
116             ctrl = config.ctrl,\r
117             alt = config.alt,\r
118             fn = config.fn || config.handler,\r
119             scope = config.scope;\r
120         \r
121         if (config.stopEvent) {\r
122             this.stopEvent = config.stopEvent;    \r
123         }       \r
124 \r
125         if(typeof keyCode == "string"){\r
126             var ks = [];\r
127             var keyString = keyCode.toUpperCase();\r
128             for(var j = 0, len = keyString.length; j < len; j++){\r
129                 ks.push(keyString.charCodeAt(j));\r
130             }\r
131             keyCode = ks;\r
132         }\r
133         var keyArray = Ext.isArray(keyCode);\r
134         \r
135         var handler = function(e){\r
136             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){\r
137                 var k = e.getKey();\r
138                 if(keyArray){\r
139                     for(var i = 0, len = keyCode.length; i < len; i++){\r
140                         if(keyCode[i] == k){\r
141                           if(this.stopEvent){\r
142                               e.stopEvent();\r
143                           }\r
144                           fn.call(scope || window, k, e);\r
145                           return;\r
146                         }\r
147                     }\r
148                 }else{\r
149                     if(k == keyCode){\r
150                         if(this.stopEvent){\r
151                            e.stopEvent();\r
152                         }\r
153                         fn.call(scope || window, k, e);\r
154                     }\r
155                 }\r
156             }\r
157         };\r
158         this.bindings.push(handler);\r
159         },\r
160 \r
161     /**\r
162      * Shorthand for adding a single key listener\r
163      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the\r
164      * following options:\r
165      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
166      * @param {Function} fn The function to call\r
167      * @param {Object} scope (optional) The scope of the function\r
168      */\r
169     on : function(key, fn, scope){\r
170         var keyCode, shift, ctrl, alt;\r
171         if(typeof key == "object" && !Ext.isArray(key)){\r
172             keyCode = key.key;\r
173             shift = key.shift;\r
174             ctrl = key.ctrl;\r
175             alt = key.alt;\r
176         }else{\r
177             keyCode = key;\r
178         }\r
179         this.addBinding({\r
180             key: keyCode,\r
181             shift: shift,\r
182             ctrl: ctrl,\r
183             alt: alt,\r
184             fn: fn,\r
185             scope: scope\r
186         })\r
187     },\r
188 \r
189     // private\r
190     handleKeyDown : function(e){\r
191             if(this.enabled){ //just in case\r
192             var b = this.bindings;\r
193             for(var i = 0, len = b.length; i < len; i++){\r
194                 b[i].call(this, e);\r
195             }\r
196             }\r
197         },\r
198 \r
199         /**\r
200          * Returns true if this KeyMap is enabled\r
201          * @return {Boolean}\r
202          */\r
203         isEnabled : function(){\r
204             return this.enabled;\r
205         },\r
206 \r
207         /**\r
208          * Enables this KeyMap\r
209          */\r
210         enable: function(){\r
211                 if(!this.enabled){\r
212                     this.el.on(this.eventName, this.handleKeyDown, this);\r
213                     this.enabled = true;\r
214                 }\r
215         },\r
216 \r
217         /**\r
218          * Disable this KeyMap\r
219          */\r
220         disable: function(){\r
221                 if(this.enabled){\r
222                     this.el.removeListener(this.eventName, this.handleKeyDown, this);\r
223                     this.enabled = false;\r
224                 }\r
225         }\r
226 };