1 xq.Shortcut = xq.Class({
  2 	initialize: function(keymapOrExpression) {
  3 		xq.addToFinalizeQueue(this);
  4 
  5 		this.keymap = (typeof keymapOrExpression == "string") ?
  6 			xq.Shortcut.interprete(keymapOrExpression).keymap :
  7 			keymapOrExpression;
  8 	},
  9 	matches: function(e) {
 10 		var which = xq.Browser.isGecko && xq.Browser.isMac ? (e.keyCode + "_" + e.charCode) : e.keyCode;
 11 		
 12 		var keyMatches =
 13 			(this.keymap.which == which) ||
 14 			(this.keymap.which == 32 && which == 25); // 25 is SPACE in Type-3 keyboard.
 15 		
 16 		if(typeof e.metaKey == "undefined") e.metaKey = false;
 17 		
 18 		var modifierMatches = 
 19 			(typeof this.keymap.shiftKey == "undefined" || this.keymap.shiftKey == e.shiftKey) &&
 20 			(typeof this.keymap.altKey == "undefined" || this.keymap.altKey == e.altKey) &&
 21 			(typeof this.keymap.ctrlKey == "undefined" || this.keymap.ctrlKey == e.ctrlKey) &&
 22 			(typeof this.keymap.metaKey == "undefined" || this.keymap.metaKey == e.metaKey)
 23 		
 24 		return modifierMatches && keyMatches;
 25 	}
 26 });
 27 
 28 xq.Shortcut.interprete = function(expression) {
 29 	expression = expression.toUpperCase();
 30 	
 31 	var which = xq.Shortcut._interpreteWhich(expression.split("+").pop());
 32 	var ctrlKey = xq.Shortcut._interpreteModifier(expression, "CTRL");
 33 	var altKey = xq.Shortcut._interpreteModifier(expression, "ALT");
 34 	var shiftKey = xq.Shortcut._interpreteModifier(expression, "SHIFT");
 35 	var metaKey = xq.Shortcut._interpreteModifier(expression, "META");
 36 	
 37 	var keymap = {};
 38 	
 39 	keymap.which = which;
 40 	if(typeof ctrlKey != "undefined") keymap.ctrlKey = ctrlKey;
 41 	if(typeof altKey != "undefined") keymap.altKey = altKey;
 42 	if(typeof shiftKey != "undefined") keymap.shiftKey = shiftKey;
 43 	if(typeof metaKey != "undefined") keymap.metaKey = metaKey;
 44 	
 45 	return new xq.Shortcut(keymap);
 46 }
 47 
 48 xq.Shortcut._interpreteModifier = function(expression, modifierName) {
 49 	return expression.match("\\(" + modifierName + "\\)") ?
 50 		undefined :
 51 			expression.match(modifierName) ?
 52 			true : false;
 53 }
 54 xq.Shortcut._interpreteWhich = function(keyName) {
 55 	var which = keyName.length == 1 ?
 56 		((xq.Browser.isMac && xq.Browser.isGecko) ? "0_" + keyName.toLowerCase().charCodeAt(0) : keyName.charCodeAt(0)) :
 57 		xq.Shortcut._keyNames[keyName];
 58 	
 59 	if(typeof which == "undefined") throw "Unknown special key name: [" + keyName + "]"
 60 	
 61 	return which;
 62 }
 63 xq.Shortcut._keyNames =
 64 	xq.Browser.isMac && xq.Browser.isGecko ?
 65 	{
 66 		BACKSPACE: "8_0",
 67 		TAB: "9_0",
 68 		RETURN: "13_0",
 69 		ENTER: "13_0",
 70 		ESC: "27_0",
 71 		SPACE: "0_32",
 72 		LEFT: "37_0",
 73 		UP: "38_0",
 74 		RIGHT: "39_0",
 75 		DOWN: "40_0",
 76 		DELETE: "46_0",
 77 		HOME: "36_0",
 78 		END: "35_0",
 79 		PAGEUP: "33_0",
 80 		PAGEDOWN: "34_0",
 81 		COMMA: "0_44",
 82 		HYPHEN: "0_45",
 83 		EQUAL: "0_61",
 84 		PERIOD: "0_46",
 85 		SLASH: "0_47",
 86 		F1: "112_0",
 87 		F2: "113_0",
 88 		F3: "114_0",
 89 		F4: "115_0",
 90 		F5: "116_0",
 91 		F6: "117_0",
 92 		F7: "118_0",
 93 		F8: "119_0"
 94 	}
 95 	:
 96 	{
 97 		BACKSPACE: 8,
 98 		TAB: 9,
 99 		RETURN: 13,
100 		ENTER: 13,
101 		ESC: 27,
102 		SPACE: 32,
103 		LEFT: 37,
104 		UP: 38,
105 		RIGHT: 39,
106 		DOWN: 40,
107 		DELETE: 46,
108 		HOME: 36,
109 		END: 35,
110 		PAGEUP: 33,
111 		PAGEDOWN: 34,
112 		COMMA: 188,
113 		HYPHEN: xq.Browser.isTrident ? 189 : 109,
114 		EQUAL: xq.Browser.isTrident ? 187 : 61,
115 		PERIOD: 190,
116 		SLASH: 191,
117 		F1:112,
118 		F2:113,
119 		F3:114,
120 		F4:115,
121 		F5:116,
122 		F6:117,
123 		F7:118,
124 		F8:119,
125 		F9:120,
126 		F10:121,
127 		F11:122,
128 		F12:123
129 	}
130