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