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