Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / docs / source / KeyMap.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-util-KeyMap'>/**
19 </span> * @class Ext.util.KeyMap
20  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
21  * The constructor accepts the same config object as defined by {@link #addBinding}.
22  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
23  * combination it will call the function with this signature (if the match is a multi-key
24  * combination the callback will still be called only once): (String key, Ext.EventObject e)
25  * A KeyMap can also handle a string representation of keys.&lt;br /&gt;
26  * Usage:
27  &lt;pre&gt;&lt;code&gt;
28 // map one key by key code
29 var map = new Ext.util.KeyMap(&quot;my-element&quot;, {
30     key: 13, // or Ext.EventObject.ENTER
31     fn: myHandler,
32     scope: myObject
33 });
34
35 // map multiple keys to one action by string
36 var map = new Ext.util.KeyMap(&quot;my-element&quot;, {
37     key: &quot;a\r\n\t&quot;,
38     fn: myHandler,
39     scope: myObject
40 });
41
42 // map multiple keys to multiple actions by strings and array of codes
43 var map = new Ext.util.KeyMap(&quot;my-element&quot;, [
44     {
45         key: [10,13],
46         fn: function(){ alert(&quot;Return was pressed&quot;); }
47     }, {
48         key: &quot;abc&quot;,
49         fn: function(){ alert('a, b or c was pressed'); }
50     }, {
51         key: &quot;\t&quot;,
52         ctrl:true,
53         shift:true,
54         fn: function(){ alert('Control + shift + tab was pressed.'); }
55     }
56 ]);
57 &lt;/code&gt;&lt;/pre&gt;
58  * &lt;b&gt;Note: A KeyMap starts enabled&lt;/b&gt;
59  */
60 Ext.define('Ext.util.KeyMap', {
61     alternateClassName: 'Ext.KeyMap',
62
63 <span id='Ext-util-KeyMap-method-constructor'>    /**
64 </span>     * Creates new KeyMap.
65      * @param {Mixed} el The element to bind to
66      * @param {Object} binding The binding (see {@link #addBinding})
67      * @param {String} eventName (optional) The event to bind to (defaults to &quot;keydown&quot;)
68      */
69     constructor: function(el, binding, eventName){
70         var me = this;
71         
72         Ext.apply(me, {
73             el: Ext.get(el),
74             eventName: eventName || me.eventName,
75             bindings: []
76         });
77         if (binding) {
78             me.addBinding(binding);
79         }
80         me.enable();
81     },
82     
83     eventName: 'keydown',
84
85 <span id='Ext-util-KeyMap-method-addBinding'>    /**
86 </span>     * Add a new binding to this KeyMap. The following config object properties are supported:
87      * &lt;pre&gt;
88 Property            Type             Description
89 ----------          ---------------  ----------------------------------------------------------------------
90 key                 String/Array     A single keycode or an array of keycodes to handle
91 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)
92 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)
93 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)
94 handler             Function         The function to call when KeyMap finds the expected key combination
95 fn                  Function         Alias of handler (for backwards-compatibility)
96 scope               Object           The scope of the callback function
97 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. 
98 &lt;/pre&gt;
99      *
100      * Usage:
101      * &lt;pre&gt;&lt;code&gt;
102 // Create a KeyMap
103 var map = new Ext.util.KeyMap(document, {
104     key: Ext.EventObject.ENTER,
105     fn: handleKey,
106     scope: this
107 });
108
109 //Add a new binding to the existing KeyMap later
110 map.addBinding({
111     key: 'abc',
112     shift: true,
113     fn: handleKey,
114     scope: this
115 });
116 &lt;/code&gt;&lt;/pre&gt;
117      * @param {Object/Array} binding A single KeyMap config or an array of configs
118      */
119     addBinding : function(binding){
120         if (Ext.isArray(binding)) {
121             Ext.each(binding, this.addBinding, this);
122             return;
123         }
124         
125         var keyCode = binding.key,
126             processed = false,
127             key,
128             keys,
129             keyString,
130             i,
131             len;
132
133         if (Ext.isString(keyCode)) {
134             keys = [];
135             keyString = keyCode.toLowerCase();
136             
137             for (i = 0, len = keyString.length; i &lt; len; ++i){
138                 keys.push(keyString.charCodeAt(i));
139             }
140             keyCode = keys;
141             processed = true;
142         }
143         
144         if (!Ext.isArray(keyCode)) {
145             keyCode = [keyCode];
146         }
147         
148         if (!processed) {
149             for (i = 0, len = keyCode.length; i &lt; len; ++i) {
150                 key = keyCode[i];
151                 if (Ext.isString(key)) {
152                     keyCode[i] = key.toLowerCase().charCodeAt(0);
153                 }
154             }
155         }
156         
157         this.bindings.push(Ext.apply({
158             keyCode: keyCode
159         }, binding));
160     },
161     
162 <span id='Ext-util-KeyMap-method-handleKeyDown'>    /**
163 </span>     * Process any keydown events on the element
164      * @private
165      * @param {Ext.EventObject} event
166      */
167     handleKeyDown: function(event) {
168         if (this.enabled) { //just in case
169             var bindings = this.bindings,
170                 i = 0,
171                 len = bindings.length;
172                 
173             event = this.processEvent(event);
174             for(; i &lt; len; ++i){
175                 this.processBinding(bindings[i], event);
176             }
177         }
178     },
179     
180 <span id='Ext-util-KeyMap-method-processEvent'>    /**
181 </span>     * Ugly hack to allow this class to be tested. Currently WebKit gives
182      * no way to raise a key event properly with both
183      * a) A keycode
184      * b) The alt/ctrl/shift modifiers
185      * So we have to simulate them here. Yuk! 
186      * This is a stub method intended to be overridden by tests.
187      * More info: https://bugs.webkit.org/show_bug.cgi?id=16735
188      * @private
189      */
190     processEvent: function(event){
191         return event;
192     },
193     
194 <span id='Ext-util-KeyMap-method-processBinding'>    /**
195 </span>     * Process a particular binding and fire the handler if necessary.
196      * @private
197      * @param {Object} binding The binding information
198      * @param {Ext.EventObject} event
199      */
200     processBinding: function(binding, event){
201         if (this.checkModifiers(binding, event)) {
202             var key = event.getKey(),
203                 handler = binding.fn || binding.handler,
204                 scope = binding.scope || this,
205                 keyCode = binding.keyCode,
206                 defaultEventAction = binding.defaultEventAction,
207                 i,
208                 len,
209                 keydownEvent = new Ext.EventObjectImpl(event);
210                 
211             
212             for (i = 0, len = keyCode.length; i &lt; len; ++i) {
213                 if (key === keyCode[i]) {
214                     if (handler.call(scope, key, event) !== true &amp;&amp; defaultEventAction) {
215                         keydownEvent[defaultEventAction]();
216                     }
217                     break;
218                 }
219             }
220         }
221     },
222     
223 <span id='Ext-util-KeyMap-method-checkModifiers'>    /**
224 </span>     * Check if the modifiers on the event match those on the binding
225      * @private
226      * @param {Object} binding
227      * @param {Ext.EventObject} event
228      * @return {Boolean} True if the event matches the binding
229      */
230     checkModifiers: function(binding, e){
231         var keys = ['shift', 'ctrl', 'alt'],
232             i = 0,
233             len = keys.length,
234             val, key;
235             
236         for (; i &lt; len; ++i){
237             key = keys[i];
238             val = binding[key];
239             if (!(val === undefined || (val === e[key + 'Key']))) {
240                 return false;
241             }
242         }
243         return true;
244     },
245
246 <span id='Ext-util-KeyMap-method-on'>    /**
247 </span>     * Shorthand for adding a single key listener
248      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
249      * following options:
250      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
251      * @param {Function} fn The function to call
252      * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the function is executed. Defaults to the browser window.
253      */
254     on: function(key, fn, scope) {
255         var keyCode, shift, ctrl, alt;
256         if (Ext.isObject(key) &amp;&amp; !Ext.isArray(key)) {
257             keyCode = key.key;
258             shift = key.shift;
259             ctrl = key.ctrl;
260             alt = key.alt;
261         } else {
262             keyCode = key;
263         }
264         this.addBinding({
265             key: keyCode,
266             shift: shift,
267             ctrl: ctrl,
268             alt: alt,
269             fn: fn,
270             scope: scope
271         });
272     },
273
274 <span id='Ext-util-KeyMap-method-isEnabled'>    /**
275 </span>     * Returns true if this KeyMap is enabled
276      * @return {Boolean}
277      */
278     isEnabled : function(){
279         return this.enabled;
280     },
281
282 <span id='Ext-util-KeyMap-method-enable'>    /**
283 </span>     * Enables this KeyMap
284      */
285     enable: function(){
286         if(!this.enabled){
287             this.el.on(this.eventName, this.handleKeyDown, this);
288             this.enabled = true;
289         }
290     },
291
292 <span id='Ext-util-KeyMap-method-disable'>    /**
293 </span>     * Disable this KeyMap
294      */
295     disable: function(){
296         if(this.enabled){
297             this.el.removeListener(this.eventName, this.handleKeyDown, this);
298             this.enabled = false;
299         }
300     },
301
302 <span id='Ext-util-KeyMap-method-setDisabled'>    /**
303 </span>     * Convenience function for setting disabled/enabled by boolean.
304      * @param {Boolean} disabled
305      */
306     setDisabled : function(disabled){
307         if (disabled) {
308             this.disable();
309         } else {
310             this.enable();
311         }
312     },
313     
314 <span id='Ext-util-KeyMap-method-destroy'>    /**
315 </span>     * Destroys the KeyMap instance and removes all handlers.
316      * @param {Boolean} removeEl True to also remove the attached element
317      */
318     destroy: function(removeEl){
319         var me = this;
320         
321         me.bindings = [];
322         me.disable();
323         if (removeEl === true) {
324             me.el.remove();
325         }
326         delete me.el;
327     }
328 });</pre>
329 </body>
330 </html>