Merge pull request #1174 from piranha/master

update keymage to 1.0.1
This commit is contained in:
Lachlan Collins
2013-04-26 20:27:13 -07:00
3 changed files with 323 additions and 1 deletions
+316
View File
@@ -0,0 +1,316 @@
// keymage.js - Javascript keyboard event handling
// http://github.com/piranha/keymage
//
// (c) 2012 Alexander Solovyov
// under terms of ISC License
(function(define, undefined) {
define(function() {
var VERSION = '1.0.1';
// Defining all keys
var MODPROPS = ['shiftKey', 'ctrlKey', 'altKey', 'metaKey'];
var MODS = {
'shift': 'shift',
'ctrl': 'ctrl', 'control': 'ctrl',
'alt': 'alt', 'option': 'alt',
'win': 'meta', 'cmd': 'meta', 'super': 'meta',
'meta': 'meta',
// default modifier for os x is cmd and for others is ctrl
'defmod': ~navigator.userAgent.indexOf('Mac OS X') ?
'meta' : 'ctrl'
};
var MODORDER = ['shift', 'ctrl', 'alt', 'meta'];
var MODNUMS = [16, 17, 18, 91];
var KEYS = {
'backspace': 8,
'tab': 9,
'enter': 13, 'return': 13,
'pause': 19,
'caps': 20, 'capslock': 20,
'escape': 27, 'esc': 27,
'space': 32,
'pgup': 33, 'pageup': 33,
'pgdown': 34, 'pagedown': 34,
'end': 35,
'home': 36,
'ins': 45, 'insert': 45,
'del': 46, 'delete': 46,
'left': 37,
'up': 38,
'right': 39,
'down': 40,
'*': 106,
'+': 107, 'plus': 107,
'-': 109, 'minus': 109,
';': 186,
'=': 187,
',': 188,
'.': 190,
'/': 191,
'`': 192,
'[': 219,
'\\': 220,
']': 221,
"'": 222
};
var i;
// numpad
for (i = 0; i < 10; i++) {
KEYS['num-' + i] = i + 95;
}
// top row 0-9
for (i = 0; i < 10; i++) {
KEYS[i.toString()] = i + 48;
}
// f1-f24
for (i = 1; i < 25; i++) {
KEYS['f' + i] = i + 111;
}
// alphabet
for (i = 65; i < 91; i++) {
KEYS[String.fromCharCode(i).toLowerCase()] = i;
}
// Reverse key codes
var KEYREV = {};
for (var k in KEYS) {
var val = KEYS[k];
if (!KEYREV[val] || KEYREV[val].length < k.length) {
KEYREV[val] = k;
}
}
// -----------------------
// Actual work is done here
var currentScope = '';
var allChains = {};
function parseKeyString(keystring) {
var bits = keystring.split('-');
var button = bits[bits.length - 1];
var key = {code: KEYS[button]};
if (!key.code) {
throw 'Unknown key "' + button + '" in keystring "' +
keystring + '"';
}
var mod;
for (var i = 0; i < bits.length - 1; i++) {
button = bits[i];
mod = MODS[button];
if (!mod) {
throw 'Unknown modifier "' + button + '" in keystring "' +
keystring + '"';
}
key[mod] = true;
}
return key;
}
function stringifyKey(key) {
var s = '';
for (var i = 0; i < MODORDER.length; i++) {
if (key[MODORDER[i]]) {
s += MODORDER[i] + '-';
}
}
s += KEYREV[key.code];
return s;
}
function normalizeKeyChain(keychainString) {
var keychain = [];
var keys = keychainString.split(' ');
for (var i = 0; i < keys.length; i++) {
var key = parseKeyString(keys[i]);
key = stringifyKey(key);
keychain.push(key);
}
keychain.original = keychainString;
return keychain;
}
function eventKeyString(e) {
var key = {code: e.keyCode};
for (var i = 0; i < MODPROPS.length; i++) {
var mod = MODPROPS[i];
if (e[mod]) {
key[mod.slice(0, mod.length - 3)] = true;
}
}
return stringifyKey(key);
}
function getNestedChains(chains, scope) {
for (var i = 0; i < scope.length; i++) {
var bit = scope[i];
if (bit) {
chains = chains[bit];
}
if (!chains) {
break;
}
}
return chains;
}
var sequence = [];
function dispatch(e) {
// Skip all modifiers
if (~MODNUMS.indexOf(e.keyCode)) {
return;
}
var seq = sequence.slice();
seq.push(eventKeyString(e));
var scope = currentScope.split('.');
var matched, chains, key;
for (var i = scope.length; i >= 0; i--) {
chains = getNestedChains(allChains, scope.slice(0, i));
if (!chains) {
continue;
}
matched = true;
for (var j = 0; j < seq.length; j++) {
key = seq[j];
if (!chains[key]) {
matched = false;
break;
}
chains = chains[key];
}
if (matched) {
break;
}
}
var definitionScope = scope.slice(0, i).join('.');
var preventDefault = chains.preventDefault;
// partial match, save the sequence
if (matched && !chains.handlers) {
sequence = seq;
if (preventDefault) {
e.preventDefault();
}
return;
}
if (matched) {
for (i = 0; i < chains.handlers.length; i++) {
var handler = chains.handlers[i];
var options = handler._keymage;
var res = handler.call(options.context, e, {
shortcut: options.original,
scope: currentScope,
definitionScope: definitionScope
});
if (res === false || preventDefault) {
e.preventDefault();
}
}
}
// either matched or not, drop the sequence
sequence = [];
}
function assignKey(scope, keychain, fn) {
var bits = scope.split('.');
var chains = allChains;
bits = bits.concat(keychain);
for (var i = 0, l = bits.length; i < l; i++) {
var bit = bits[i];
if (!bit) continue;
chains = chains[bit] || (chains[bit] = {});
if (fn._keymage.preventDefault) {
chains.preventDefault = true;
}
if (i === l - 1) {
var handlers = chains.handlers || (chains.handlers = []);
handlers.push(fn);
}
}
}
// optional arguments: scope, options.
function keymage(scope, keychain, fn, options) {
if (keychain === undefined && fn === undefined) {
return function(keychain, fn) {
return keymage(scope, keychain, fn);
};
}
if (typeof keychain === 'function') {
options = fn;
fn = keychain;
keychain = scope;
scope = '';
}
var normalized = normalizeKeyChain(keychain);
fn._keymage = options || {};
fn._keymage.original = keychain;
assignKey(scope, normalized, fn);
}
keymage.parse = parseKeyString;
keymage.stringify = stringifyKey;
keymage.bindings = allChains;
keymage.setScope = function(scope) {
currentScope = scope ? scope : '';
};
keymage.getScope = function() { return currentScope; };
keymage.pushScope = function(scope) {
currentScope = (currentScope ? currentScope + '.' : '') + scope;
return currentScope;
};
keymage.popScope = function(scope) {
var i;
if (!scope) {
i = currentScope.lastIndexOf('.');
scope = currentScope.slice(i + 1);
currentScope = i == -1 ? '' : currentScope.slice(0, i);
return scope;
}
currentScope = currentScope.replace(
new RegExp('(^|\\.)' + scope + '(\\.|$).*'), '');
return scope;
};
keymage.version = VERSION;
window.addEventListener('keydown', dispatch, false);
return keymage;
});
})(typeof define !== 'undefined' ? define : function(factory) {
window.keymage = factory();
});
+6
View File
@@ -0,0 +1,6 @@
// keymage.js - Javascript keyboard event handling
// http://github.com/piranha/keymage
//
// (c) 2012 Alexander Solovyov
// under terms of ISC License
(function(a,b){a(function(){function n(a){var b=a.split("-"),c=b[b.length-1],e={code:g[c]};if(!e.code)throw'Unknown key "'+c+'" in keystring "'+a+'"';var f;for(var h=0;h<b.length-1;h++){c=b[h],f=d[c];if(!f)throw'Unknown modifier "'+c+'" in keystring "'+a+'"';e[f]=!0}return e}function o(a){var b="";for(var c=0;c<e.length;c++)a[e[c]]&&(b+=e[c]+"-");return b+=i[a.code],b}function p(a){var b=[],c=a.split(" ");for(var d=0;d<c.length;d++){var e=n(c[d]);e=o(e),b.push(e)}return b.original=a,b}function q(a){var b={code:a.keyCode};for(var d=0;d<c.length;d++){var e=c[d];a[e]&&(b[e.slice(0,e.length-3)]=!0)}return o(b)}function r(a,b){for(var c=0;c<b.length;c++){var d=b[c];d&&(a=a[d]);if(!a)break}return a}function t(a){if(~f.indexOf(a.keyCode))return;var b=s.slice();b.push(q(a));var c=l.split("."),d,e,g;for(var h=c.length;h>=0;h--){e=r(m,c.slice(0,h));if(!e)continue;d=!0;for(var i=0;i<b.length;i++){g=b[i];if(!e[g]){d=!1;break}e=e[g]}if(d)break}var j=c.slice(0,h).join("."),k=e.preventDefault;if(d&&!e.handlers){s=b,k&&a.preventDefault();return}if(d)for(h=0;h<e.handlers.length;h++){var n=e.handlers[h],o=n._keymage,p=n.call(o.context,a,{shortcut:o.original,scope:l,definitionScope:j});(p===!1||k)&&a.preventDefault()}s=[]}function u(a,b,c){var d=a.split("."),e=m;d=d.concat(b);for(var f=0,g=d.length;f<g;f++){var h=d[f];if(!h)continue;e=e[h]||(e[h]={}),c._keymage.preventDefault&&(e.preventDefault=!0);if(f===g-1){var i=e.handlers||(e.handlers=[]);i.push(c)}}}function v(a,c,d,e){if(c===b&&d===b)return function(b,c){return v(a,b,c)};typeof c=="function"&&(e=d,d=c,c=a,a="");var f=p(c);d._keymage=e||{},d._keymage.original=c,u(a,f,d)}var a="1.0.1",c=["shiftKey","ctrlKey","altKey","metaKey"],d={shift:"shift",ctrl:"ctrl",control:"ctrl",alt:"alt",option:"alt",win:"meta",cmd:"meta","super":"meta",meta:"meta",defmod:~navigator.userAgent.indexOf("Mac OS X")?"meta":"ctrl"},e=["shift","ctrl","alt","meta"],f=[16,17,18,91],g={backspace:8,tab:9,enter:13,"return":13,pause:19,caps:20,capslock:20,escape:27,esc:27,space:32,pgup:33,pageup:33,pgdown:34,pagedown:34,end:35,home:36,ins:45,insert:45,del:46,"delete":46,left:37,up:38,right:39,down:40,"*":106,"+":107,plus:107,"-":109,minus:109,";":186,"=":187,",":188,".":190,"/":191,"`":192,"[":219,"\\":220,"]":221,"'":222},h;for(h=0;h<10;h++)g["num-"+h]=h+95;for(h=0;h<10;h++)g[h.toString()]=h+48;for(h=1;h<25;h++)g["f"+h]=h+111;for(h=65;h<91;h++)g[String.fromCharCode(h).toLowerCase()]=h;var i={};for(var j in g){var k=g[j];if(!i[k]||i[k].length<j.length)i[k]=j}var l="",m={},s=[];return v.parse=n,v.stringify=o,v.bindings=m,v.setScope=function(a){l=a?a:""},v.getScope=function(){return l},v.pushScope=function(a){return l=(l?l+".":"")+a,l},v.popScope=function(a){var b;return a?(l=l.replace(new RegExp("(^|\\.)"+a+"(\\.|$).*"),""),a):(b=l.lastIndexOf("."),a=l.slice(b+1),l=b==-1?"":l.slice(0,b),a)},v.version=a,window.addEventListener("keydown",t,!1),v})})(typeof define!="undefined"?define:function(a){window.keymage=a()});
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "keymage",
"filename": "keymage.min.js",
"version": "1.0.0",
"version": "1.0.1",
"description": "Yet Another JS Keybinding library",
"homepage": "https://github.com/piranha/keymage",
"keywords": [