[76] | 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 | }; |
---|