3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('event-key', function(Y) {
10 * Functionality to listen for one or more specific key combinations.
12 * @submodule event-key
32 _typeRE: /^(up|down|press):/,
33 _keysRE: /^(?:up|down|press):|\+(alt|ctrl|meta|shift)/g,
35 processArgs: function (args) {
36 var spec = args.splice(3,1)[0],
37 mods = Y.Array.hash(spec.match(/\+(?:alt|ctrl|meta|shift)\b/g) || []),
39 type: this._typeRE.test(spec) ? RegExp.$1 : null,
43 // strip type and modifiers from spec, leaving only keyCodes
44 bits = spec.replace(this._keysRE, ''),
48 bits = bits.split(',');
52 // FIXME: need to support '65,esc' => keypress, keydown
53 for (i = bits.length - 1; i >= 0; --i) {
56 // catch sloppy filters, trailing commas, etc 'a,,'
61 // non-numerics are single characters or key names
63 config.keys[chr] = mods;
65 lc = chr.toLowerCase();
67 if (this.KEY_MAP[lc]) {
68 config.keys[this.KEY_MAP[lc]] = mods;
69 // FIXME: '65,enter' defaults keydown for both
71 config.type = "down"; // safest
74 // FIXME: Character mapping only works for keypress
75 // events. Otherwise, it uses String.fromCharCode()
76 // from the keyCode, which is wrong.
78 uc = chr.toUpperCase();
84 // FIXME: stupid assumption that
85 // the keycode of the lower case == the
86 // charCode of the upper case
87 // a (key:65,char:97), A (key:65,char:65)
88 config.keys[chr.charCodeAt(0)] =
90 // upper case chars get +shift free
91 Y.merge(mods, { "+shift": true }) :
99 config.type = "press";
105 on: function (node, sub, notifier, filter) {
106 var spec = sub._extra,
107 type = "key" + spec.type,
109 method = (filter) ? "delegate" : "on";
111 // Note: without specifying any keyCodes, this becomes a
112 // horribly inefficient alias for 'keydown' (et al), but I
113 // can't abort this subscription for a simple
114 // Y.on('keypress', ...);
115 // Please use keyCodes or just subscribe directly to keydown,
116 // keyup, or keypress
117 sub._detach = node[method](type, function (e) {
118 var key = keys ? keys[e.which] : spec.mods;
121 (!key[ALT] || (key[ALT] && e.altKey)) &&
122 (!key[CTRL] || (key[CTRL] && e.ctrlKey)) &&
123 (!key[META] || (key[META] && e.metaKey)) &&
124 (!key[SHIFT] || (key[SHIFT] && e.shiftKey)))
131 detach: function (node, sub, notifier) {
132 sub._detach.detach();
136 eventDef.delegate = eventDef.on;
137 eventDef.detachDelegate = eventDef.detach;
140 * <p>Add a key listener. The listener will only be notified if the
141 * keystroke detected meets the supplied specification. The
142 * specification is a string that is defined as:</p>
146 * <dd><code>[{type}:]{code}[,{code}]*</code></dd>
148 * <dd><code>"down", "up", or "press"</code></dd>
150 * <dd><code>{keyCode|character|keyName}[+{modifier}]*</code></dd>
152 * <dd><code>"shift", "ctrl", "alt", or "meta"</code></dd>
154 * <dd><code>"enter", "backspace", "esc", "tab", "pageup", or "pagedown"</code></dd>
159 * <li><code>Y.on("key", callback, "press:12,65+shift+ctrl", "#my-input");</code></li>
160 * <li><code>Y.delegate("key", preventSubmit, "enter", "#forms", "input[type=text]");</code></li>
161 * <li><code>Y.one("doc").on("key", viNav, "j,k,l,;");</code></li>
166 * @param type {string} 'key'
167 * @param fn {function} the function to execute
168 * @param id {string|HTMLElement|collection} the element(s) to bind
169 * @param spec {string} the keyCode and modifier specification
170 * @param o optional context object
171 * @param args 0..n additional arguments to provide to the listener.
172 * @return {Event.Handle} the detach handle
174 Y.Event.define('key', eventDef, true);
177 }, '3.5.1' ,{requires:['event-synthetic']});