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