| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							| 
									
										
										
										
											2016-08-21 02:19:24 +03:00
										 |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							| 
									
										
										
										
											2016-08-20 22:49:36 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var object = require('lib/object') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var MODIFIERS = | 
					
						
							|  |  |  | module.MODIFIERS = [ 'ctrl', 'meta', 'alt', 'shift' ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var KEY_SEPARATORS = | 
					
						
							|  |  |  | module.KEY_SEPARATORS = ['+', '-', '_'] | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Neither SPECIAL_KEYS nor KEY_CODES are meant for direct access, use
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | // toKeyName(<code>) and toKeyCode(<name>) for a more uniform access.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: these are un-shifted ASCII key names rather than actual key 
 | 
					
						
							|  |  |  | // 		code translations.
 | 
					
						
							|  |  |  | // NOTE: ASCII letters (capital) are not present because they actually 
 | 
					
						
							|  |  |  | // 		match their key codes and are accessible via:
 | 
					
						
							|  |  |  | // 			String.fromCharCode(<code>) or <letter>.charCodeAt(0)
 | 
					
						
							|  |  |  | // NOTE: the lower case letters are accessible by adding 32 to the 
 | 
					
						
							|  |  |  | // 		capital key code.
 | 
					
						
							|  |  |  | // NOTE: don't understand why am I the one who has to write this...
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var SPECIAL_KEYS = | 
					
						
							|  |  |  | module.SPECIAL_KEYS = { | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	// Special Keys...
 | 
					
						
							|  |  |  | 	9:		'Tab',		33:		'PgUp',		45:		'Ins',		 | 
					
						
							|  |  |  | 	13:		'Enter',	34:		'PgDown',	46:		'Del',		 | 
					
						
							|  |  |  | 	16:		'Shift',	35:		'End',		 8:		'Backspace', | 
					
						
							|  |  |  | 	17:		'Ctrl',		36:		'Home',		91:		'Win',		 | 
					
						
							|  |  |  | 	18:		'Alt',		37:		'Left',		93:		'Menu',		 | 
					
						
							|  |  |  | 	20:		'Caps Lock',38:		'Up',	  | 
					
						
							|  |  |  | 	27:		'Esc',		39:		'Right',   | 
					
						
							|  |  |  | 	32:		'Space',	40:		'Down',   | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Function Keys...
 | 
					
						
							|  |  |  | 	112:	'F1',		116:	'F5',		120:	'F9',  | 
					
						
							|  |  |  | 	113:	'F2',		117:	'F6',		121:	'F10', | 
					
						
							|  |  |  | 	114:	'F3',		118:	'F7',		122:	'F11', | 
					
						
							|  |  |  | 	115:	'F4',		119:	'F8',		123:	'F12', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Number row..
 | 
					
						
							|  |  |  | 	// NOTE: to avoid conflicts with keys that have a code the same as
 | 
					
						
							|  |  |  | 	// 		the value of a number key...
 | 
					
						
							|  |  |  | 	// 			Ex:
 | 
					
						
							|  |  |  | 	// 				'Backspace' (8) vs. '8' (56)
 | 
					
						
							|  |  |  | 	// 				'Tab' (9) vs. '9' (57)
 | 
					
						
							|  |  |  | 	// 		...all of the numbers start with a '#'
 | 
					
						
							|  |  |  | 	// 		this is a problem due to JS coercing the types to string
 | 
					
						
							|  |  |  | 	// 		on object attr access.
 | 
					
						
							|  |  |  | 	// 			Ex:
 | 
					
						
							|  |  |  | 	// 				o = {1: 2}
 | 
					
						
							|  |  |  | 	// 				o[1] == o['1'] == true
 | 
					
						
							|  |  |  | 	49: '#1',	50: '#2',	51: '#3',	52: '#4',	53: '#5', | 
					
						
							|  |  |  | 	54: '#6', 	55: '#7',	56: '#8',	57: '#9',	48: '#0', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Punctuation...
 | 
					
						
							|  |  |  | 	// top row...
 | 
					
						
							|  |  |  | 	192: '`',		/* Numbers */		189: '-',	187: '=', | 
					
						
							|  |  |  | 	// right side of keyboard...
 | 
					
						
							|  |  |  | 				219: '[',	221: ']',	220: '\\', | 
					
						
							|  |  |  | 				186: ';',	222: '\'', | 
					
						
							|  |  |  | 	188: ',',	190: '.',	191: '/', | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var SHIFT_KEYS = | 
					
						
							|  |  |  | module.SHIFT_KEYS = { | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	'`': '~',	'-': '_',	'=':'+', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-19 22:32:11 +04:00
										 |  |  | 	'#1': '!',	'#2': '@',	'#3': '#',	'#4': '$',	'#5': '%', | 
					
						
							|  |  |  | 	'#6':'^',	'#7':'&',	'#8': '*',	'#9': '(',	'#0': ')',	 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	'[': '{',		']': '}',		'\\': '|', | 
					
						
							|  |  |  | 	';': ':',		'\'': '"', | 
					
						
							|  |  |  | 	',': '<',		'.': '>',		'/': '?' | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var UNSHIFT_KEYS =  | 
					
						
							|  |  |  | module.UNSHIFT_KEYS = {} | 
					
						
							|  |  |  | for(var k in SHIFT_KEYS){ | 
					
						
							|  |  |  | 	UNSHIFT_KEYS[SHIFT_KEYS[k]] = k | 
					
						
							| 
									
										
										
										
											2017-01-07 04:22:53 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // build a reverse map of SPECIAL_KEYS
 | 
					
						
							|  |  |  | var KEY_CODES = | 
					
						
							|  |  |  | module.KEY_CODES = {} | 
					
						
							|  |  |  | for(var k in SPECIAL_KEYS){ | 
					
						
							|  |  |  | 	KEY_CODES[SPECIAL_KEYS[k]] = k | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | var SPECIAL_KEYS_DICT = {} | 
					
						
							|  |  |  | for(var k in SPECIAL_KEYS){ | 
					
						
							|  |  |  | 	SPECIAL_KEYS_DICT[SPECIAL_KEYS[k].toLowerCase()] = SPECIAL_KEYS[k] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Documentation wrapper...
 | 
					
						
							| 
									
										
										
										
											2014-07-21 16:38:06 +04:00
										 |  |  | var doc = | 
					
						
							|  |  |  | module.doc = | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | function doc(text, func){ | 
					
						
							|  |  |  | 	func = !func ? function(){return true}: func | 
					
						
							|  |  |  | 	func.doc = text | 
					
						
							|  |  |  | 	return func | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Parse action call format...
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // supported format:
 | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | // 	<actio-name>[!][: <args>][-- <doc>]
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // <args> can contain space seporated:
 | 
					
						
							|  |  |  | // 	- numbers
 | 
					
						
							|  |  |  | // 	- strings
 | 
					
						
							|  |  |  | // 	- non-nested arrays or objects
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // XXX should this be here???
 | 
					
						
							|  |  |  | // XXX add support for suffix to return false / stop_propagation...
 | 
					
						
							|  |  |  | // XXX should this handle calls??? 
 | 
					
						
							|  |  |  | // 		i.e. have .call(..) / .apply(..) methods???
 | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | var parseActionCall = | 
					
						
							|  |  |  | module.parseActionCall = | 
					
						
							|  |  |  | function parseActionCall(txt){ | 
					
						
							|  |  |  | 	// split off the doc...
 | 
					
						
							|  |  |  | 	var c = txt.split('--') | 
					
						
							|  |  |  | 	var doc = (c[1] || '').trim() | 
					
						
							|  |  |  | 	// the actual code...
 | 
					
						
							|  |  |  | 	c = c[0].split(':') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// action and no default flag...
 | 
					
						
							|  |  |  | 	var action = c[0].trim() | 
					
						
							|  |  |  | 	var no_default = action.slice(-1) == '!' | 
					
						
							|  |  |  | 	action = no_default ? action.slice(0, -1) : action | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// parse arguments...
 | 
					
						
							|  |  |  | 	var args = JSON.parse('['+( | 
					
						
							|  |  |  | 		((c[1] || '') | 
					
						
							|  |  |  | 			.match(/"[^"]*"|'[^']*'|\{[^\}]*\}|\[[^\]]*\]|\d+|\d+\.\d*|null/gm)  | 
					
						
							|  |  |  | 		|| []) | 
					
						
							|  |  |  | 		.join(','))+']') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		action: action, | 
					
						
							|  |  |  | 		arguments: args, | 
					
						
							|  |  |  | 		doc: doc, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		no_default: no_default, | 
					
						
							|  |  |  | 		stop_propagation: false, | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | // Helpers and utility functions...
 | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Form standard key string from keyboard event...
 | 
					
						
							| 
									
										
										
										
											2015-09-15 02:57:31 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Format:
 | 
					
						
							|  |  |  | // 	"[ctrl+][meta+][alt+][shift+]<key>"
 | 
					
						
							|  |  |  | // 	
 | 
					
						
							|  |  |  | // 	<key> - string returned by code2key(..)
 | 
					
						
							|  |  |  | // 	
 | 
					
						
							|  |  |  | var event2key = | 
					
						
							|  |  |  | module.event2key = | 
					
						
							|  |  |  | function event2key(evt){ | 
					
						
							|  |  |  | 	evt = evt || event | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var key = [] | 
					
						
							|  |  |  | 	evt.ctrlKey && key.push('ctrl') | 
					
						
							|  |  |  | 	evt.altKey && key.push('alt') | 
					
						
							|  |  |  | 	evt.metaKey && key.push('meta') | 
					
						
							|  |  |  | 	evt.shiftKey && key.push('shift') | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var k = code2key(evt.keyCode) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add the key if it's not already in, this can happen if we just 
 | 
					
						
							|  |  |  | 	// pressed a modifier key...
 | 
					
						
							|  |  |  | 	key.indexOf(k.toLowerCase()) < 0 && key.push(k) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return key | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-09-15 02:57:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Get key code from key name...
 | 
					
						
							|  |  |  | var key2code = | 
					
						
							|  |  |  | module.key2code = | 
					
						
							|  |  |  | function key2code(key){ | 
					
						
							|  |  |  | 	return key in KEY_CODES ? KEY_CODES[key] | 
					
						
							|  |  |  | 		: key.length > 1 ? null | 
					
						
							|  |  |  | 		: key.charCodeAt(0) } | 
					
						
							| 
									
										
										
										
											2015-09-15 02:57:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Get key name from key code...
 | 
					
						
							|  |  |  | var code2key = | 
					
						
							|  |  |  | module.code2key = | 
					
						
							|  |  |  | function code2key(code){ | 
					
						
							|  |  |  | 	var name = String.fromCharCode(code) | 
					
						
							|  |  |  | 	return code in SPECIAL_KEYS ? SPECIAL_KEYS[code] | 
					
						
							|  |  |  | 		: name != '' ? name  | 
					
						
							|  |  |  | 		: null } | 
					
						
							| 
									
										
										
										
											2015-09-15 02:57:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Check if string is a standard key string...
 | 
					
						
							|  |  |  | var isKey = | 
					
						
							|  |  |  | module.isKey =  | 
					
						
							|  |  |  | function isKey(key){ | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 	if(!key || key.length == 0 || key.trim() == ''){ | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	var modifiers = MODIFIERS  | 
					
						
							| 
									
										
										
										
											2015-09-15 02:57:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	var mod = normalizeKey(splitKey(key)) | 
					
						
							|  |  |  | 	var k = mod.pop() | 
					
						
							| 
									
										
										
										
											2015-09-15 02:57:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	// key is either a key code or a valid key name...
 | 
					
						
							|  |  |  | 	return (!!parseInt(k) || key2code(k) != null) | 
					
						
							|  |  |  | 		// mod must be a subset of modifiers...
 | 
					
						
							|  |  |  | 		&& mod.filter(function(m){ return modifiers.indexOf(m) < 0 }).length == 0 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-03 20:57:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-03 02:55:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Split key...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: if this gets an array, it will get returned as-is... 
 | 
					
						
							|  |  |  | // NOTE: no checks are made on the key, use isKey(..) in conjunction 
 | 
					
						
							|  |  |  | // 		with normalizeKey(..) for checking...
 | 
					
						
							|  |  |  | var splitKey = | 
					
						
							|  |  |  | module.splitKey =  | 
					
						
							|  |  |  | function splitKey(key){ | 
					
						
							|  |  |  | 	var sep = KEY_SEPARATORS  | 
					
						
							|  |  |  | 	return key instanceof Array ? key | 
					
						
							|  |  |  | 		: typeof(key) == typeof(123) ? [key] | 
					
						
							|  |  |  | 		: key | 
					
						
							|  |  |  | 			.split(RegExp('[' | 
					
						
							|  |  |  | 				+sep.join('\\') | 
					
						
							|  |  |  | 				+']')) | 
					
						
							|  |  |  | 			.concat(sep.indexOf(key.slice(-1)) >= 0 ? key.slice(-1) : []) | 
					
						
							|  |  |  | 			.filter(function(c){ return c != '' }) } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | var joinKey = | 
					
						
							|  |  |  | module.joinKey =  | 
					
						
							|  |  |  | function joinKey(key){ | 
					
						
							|  |  |  | 	return key instanceof Array ?  | 
					
						
							|  |  |  | 		key.join(KEY_SEPARATORS[0] || '+')  | 
					
						
							|  |  |  | 		: key } | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Normalize key string/array...
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // NOTE: this will not check if a key is a key use isKey(..) for that.
 | 
					
						
							|  |  |  | var normalizeKey = | 
					
						
							|  |  |  | module.normalizeKey =  | 
					
						
							|  |  |  | function normalizeKey(key){ | 
					
						
							|  |  |  | 	var output = key instanceof Array ? 'array' : 'string' | 
					
						
							|  |  |  | 	var modifiers = MODIFIERS  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// special case: got a number...
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	if(typeof(key) == typeof(123)){ | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		return code2key(key) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	// sort modifiers via .modifiers and keep the key last...
 | 
					
						
							|  |  |  | 	key = splitKey(key) | 
					
						
							|  |  |  | 		.slice() | 
					
						
							|  |  |  | 		.sort(function(a, b){ | 
					
						
							|  |  |  | 			a = modifiers.indexOf(a.toLowerCase()) | 
					
						
							|  |  |  | 			b = modifiers.indexOf(b.toLowerCase()) | 
					
						
							|  |  |  | 			return a >= 0 && b >= 0 ? a - b | 
					
						
							|  |  |  | 				: a < 0 ? 1 | 
					
						
							|  |  |  | 				: -1 }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var k = key.pop() | 
					
						
							|  |  |  | 	k = parseInt(k) ? code2key(parseInt(k)) : k | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if(!k){ | 
					
						
							|  |  |  | 		return k | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// get the propper name...
 | 
					
						
							|  |  |  | 	k = SPECIAL_KEYS_DICT[k.toLowerCase()] || k | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	k = modifiers.indexOf(k.toLowerCase()) >= 0 ?  | 
					
						
							|  |  |  | 		k.toLowerCase()  | 
					
						
							|  |  |  | 		: k.capitalize() | 
					
						
							|  |  |  | 	key.push(k) | 
					
						
							|  |  |  | 	key = key.unique() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return output == 'array' ?  | 
					
						
							|  |  |  | 		key  | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 		: joinKey(key) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Get shifted key if available...
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // Examples:
 | 
					
						
							|  |  |  | // 	- '{' 		-> 'shift+['
 | 
					
						
							|  |  |  | // 	- ')'		-> 'shift+#0'
 | 
					
						
							|  |  |  | var shifted = | 
					
						
							|  |  |  | module.shifted =  | 
					
						
							|  |  |  | function shifted(key){ | 
					
						
							|  |  |  | 	var output = key instanceof Array ? 'array' : 'string' | 
					
						
							|  |  |  | 	key = normalizeKey(splitKey(key)).slice() | 
					
						
							|  |  |  | 	var k = key.pop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var s = (key.indexOf('shift') >= 0 ?  | 
					
						
							|  |  |  | 			SHIFT_KEYS[k] | 
					
						
							|  |  |  | 			: UNSHIFT_KEYS[k]) | 
					
						
							|  |  |  | 		|| null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var res = s == null ? key | 
					
						
							|  |  |  | 		: (key.indexOf('shift') >= 0 ? | 
					
						
							|  |  |  | 				key.filter(function(k){ return k != 'shift' }) | 
					
						
							|  |  |  | 				: key.concat(['shift'])) | 
					
						
							|  |  |  | 	res.push(s) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return s == null ? null  | 
					
						
							|  |  |  | 		: output == 'string' ?  | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 			joinKey(res) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		: res | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-07 04:22:53 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | // Generic keyboard handler...
 | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | // 
 | 
					
						
							|  |  |  | // Key binding format:
 | 
					
						
							|  |  |  | //	{
 | 
					
						
							|  |  |  | //		<section-title>: {
 | 
					
						
							|  |  |  | //			doc: <section-doc>,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			// list of keys to drop after this section is done.
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// Setting this to '*' will drop all keys...
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// NOTE: these keys will be handled in current section.
 | 
					
						
							|  |  |  | //			// NOTE: these keys will not get propagated to the next 
 | 
					
						
							|  |  |  | //			//		matching section...
 | 
					
						
							|  |  |  | //			// NOTE: it is possible to override this and explicitly pass
 | 
					
						
							|  |  |  | //			//		a key to the next section via 'NEXT' (see below).
 | 
					
						
							|  |  |  | //			drop: [ <key>, ... ] | '*',
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			// Key mapped to action...
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// NOTE: the system poses no restrictions on action format,
 | 
					
						
							|  |  |  | //			//		but it is recommended to stick to strings or use the
 | 
					
						
							|  |  |  | //			//		doc(..) wrapper...
 | 
					
						
							|  |  |  | //			<key>: <action>,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			// Key mapped to an alias...
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// An alias is any string that is also a key in bindings, it
 | 
					
						
							|  |  |  | //			// can be just a string or a key, when matching the string of
 | 
					
						
							|  |  |  | //			// aliases will be resolved till either an action (non-alias)
 | 
					
						
							|  |  |  | //			// is found or a loop is detected.
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// NOTE: in case of a loop, nothing will get called...
 | 
					
						
							|  |  |  | //			<key>: <alias> | <key>,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			// Alias-action mapping...
 | 
					
						
							|  |  |  | //			<alias>: <action>,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			// Explicitly drop key...
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// NOTE: this is similar in effect to .drop
 | 
					
						
							|  |  |  | //			<key>: 'DROP',
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			// Explicitly pass key to next section...
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// This can be useful when it is needed to drop all keys 
 | 
					
						
							|  |  |  | //			// except for a small sub-group, this can be dune by setting
 | 
					
						
							|  |  |  | //			// .drop to '*' (drop all) and explicitly setting the keys to
 | 
					
						
							|  |  |  | //			// be propagated to 'NEXT'.
 | 
					
						
							|  |  |  | //			//
 | 
					
						
							|  |  |  | //			// NOTE: his takes precedence over .drop 
 | 
					
						
							|  |  |  | //			<key>: 'NEXT',
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			...
 | 
					
						
							|  |  |  | //		},
 | 
					
						
							|  |  |  | //		...
 | 
					
						
							|  |  |  | //	}
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | var KeyboardClassPrototype = { | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	service_fields: ['doc', 'drop'], | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	event2key: event2key, | 
					
						
							|  |  |  | 	key2code: key2code, | 
					
						
							|  |  |  | 	code2key: code2key, | 
					
						
							|  |  |  | 	isKey: isKey, | 
					
						
							|  |  |  | 	splitKey: splitKey, | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 	joinKey: joinKey, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	normalizeKey: normalizeKey, | 
					
						
							|  |  |  | 	shifted: shifted | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | var KeyboardPrototype = { | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	//service_fields: ['doc', 'drop'],
 | 
					
						
							|  |  |  | 	special_handlers: { | 
					
						
							|  |  |  | 		DROP: 'drop key',  | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 		NEXT: 'handle key in next section', | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		<mode>: {
 | 
					
						
							|  |  |  | 	// 			doc: <doc>,
 | 
					
						
							|  |  |  | 	// 			drop: [ <key>, ... ] | '*',
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//			<alias>: <handler>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//			<key>: <handler>,
 | 
					
						
							|  |  |  | 	//			<key>: <alias>,
 | 
					
						
							|  |  |  | 	// 		},
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Reserved special handlers:
 | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 	// 	- DROP		- drop checking of key
 | 
					
						
							|  |  |  | 	// 					NOTE: this will prevent handling next sections
 | 
					
						
							|  |  |  | 	// 						for this key.
 | 
					
						
							|  |  |  | 	// 	- NEXT		- force check next section, this has priority 
 | 
					
						
							|  |  |  | 	// 					over .drop
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	__keyboard: null, | 
					
						
							|  |  |  | 	get keyboard(){ | 
					
						
							|  |  |  | 		return this.__keyboard instanceof Function ?  | 
					
						
							|  |  |  | 			this.__keyboard()  | 
					
						
							|  |  |  | 			: this.__keyboard }, | 
					
						
							|  |  |  | 	set keyboard(value){ | 
					
						
							|  |  |  | 		this.__keyboard = value }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX is this needed???
 | 
					
						
							|  |  |  | 	//context: null,
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// utils...
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | 	event2key: KeyboardClassPrototype.event2key, | 
					
						
							|  |  |  | 	key2code: KeyboardClassPrototype.key2code, | 
					
						
							|  |  |  | 	code2key: KeyboardClassPrototype.code2key, | 
					
						
							|  |  |  | 	shifted: KeyboardClassPrototype.shifted, | 
					
						
							|  |  |  | 	splitKey: KeyboardClassPrototype.splitKey, | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 	joinKey: KeyboardClassPrototype.joinKey, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | 	normalizeKey: KeyboardClassPrototype.normalizeKey, | 
					
						
							|  |  |  | 	isKey: KeyboardClassPrototype.isKey, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	//isModeApplicable: function(mode, keyboard, context){ return true },
 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	// XXX merge configs...
 | 
					
						
							|  |  |  | 	// 		- need to match and order groups (use 1'st as reference)...
 | 
					
						
							|  |  |  | 	// 		- need to create new set w/o changing the originals...
 | 
					
						
							|  |  |  | 	merge: function(){ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get keys for handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	List all handlers...
 | 
					
						
							|  |  |  | 	// 	.keys()
 | 
					
						
							|  |  |  | 	// 	.keys('*')
 | 
					
						
							|  |  |  | 	//		-> keys
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	List keys for handler...
 | 
					
						
							|  |  |  | 	//	.keys(handler)
 | 
					
						
							|  |  |  | 	//		-> keys
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	List keys for given handlers...
 | 
					
						
							|  |  |  | 	//	.keys(handler, ...)
 | 
					
						
							|  |  |  | 	//	.keys([handler, ...])
 | 
					
						
							|  |  |  | 	//		-> keys
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	List keys for handlers that pass the func predicate...
 | 
					
						
							|  |  |  | 	//	.keys(func)
 | 
					
						
							|  |  |  | 	//		-> keys
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		<mode>: {
 | 
					
						
							|  |  |  | 	// 			<handler>: [ <key>, ... ],
 | 
					
						
							|  |  |  | 	// 			...
 | 
					
						
							|  |  |  | 	// 		},
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this will also return non-key aliases...
 | 
					
						
							|  |  |  | 	// NOTE: to match several compatible handlers, pass a list of handlers,
 | 
					
						
							|  |  |  | 	// 		the result for each will be merged into one common list.
 | 
					
						
							|  |  |  | 	keys: function(handler){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		var res = {} | 
					
						
							|  |  |  | 		var keyboard = this.keyboard | 
					
						
							|  |  |  | 		var key_separators = KEY_SEPARATORS  | 
					
						
							|  |  |  | 		var service_fields = this.service_fields  | 
					
						
							|  |  |  | 			|| this.constructor.service_fields | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		handler = arguments.length > 1 ? [].slice.call(arguments) | 
					
						
							|  |  |  | 			: handler == null ? '*' | 
					
						
							|  |  |  | 			: handler == '*' || handler instanceof Function ? handler | 
					
						
							|  |  |  | 			: handler instanceof Array ? handler  | 
					
						
							|  |  |  | 			: [handler] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var walkAliases = function(res, rev, bindings, key, mod){ | 
					
						
							|  |  |  | 			mod = mod || [] | 
					
						
							|  |  |  | 			if(key in rev){ | 
					
						
							|  |  |  | 				rev[key].forEach(function(k){ | 
					
						
							| 
									
										
										
										
											2017-01-12 21:23:16 +03:00
										 |  |  | 					k = that.normalizeKey( | 
					
						
							|  |  |  | 						that.joinKey(mod | 
					
						
							|  |  |  | 							.concat(that.splitKey(k)) | 
					
						
							|  |  |  | 							.unique())) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 					res.indexOf(k) < 0  | 
					
						
							|  |  |  | 						&& res.push(k) | 
					
						
							|  |  |  | 						&& walkAliases(res, rev, bindings, k, mod) | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		Object.keys(keyboard).forEach(function(mode){ | 
					
						
							|  |  |  | 			var bindings = keyboard[mode] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// build a reverse index...
 | 
					
						
							|  |  |  | 			var rev = {} | 
					
						
							|  |  |  | 			// NOTE: this will not work for handlers that are not strings 
 | 
					
						
							|  |  |  | 			// 		and have no .doc...
 | 
					
						
							|  |  |  | 			Object.keys(bindings) | 
					
						
							|  |  |  | 				.filter(function(key){  | 
					
						
							|  |  |  | 					return service_fields.indexOf(key) < 0 }) | 
					
						
							|  |  |  | 				.forEach(function(key){ | 
					
						
							|  |  |  | 					var h = bindings[key] | 
					
						
							|  |  |  | 					h = typeof(h) == typeof('str') ? h | 
					
						
							|  |  |  | 						: (h.doc || h.name) | 
					
						
							|  |  |  | 					rev[h] = (rev[h] || []) | 
					
						
							|  |  |  | 						.concat((rev[bindings[key]] || []).concat([key])) | 
					
						
							|  |  |  | 						.unique() | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var seen = [] | 
					
						
							|  |  |  | 			var handlers = handler == '*' ?  Object.keys(rev)  | 
					
						
							|  |  |  | 				: handler instanceof Function ?  | 
					
						
							|  |  |  | 					Object.keys(rev) | 
					
						
							|  |  |  | 						.filter(handler) | 
					
						
							|  |  |  | 				: handler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			handlers | 
					
						
							|  |  |  | 				.forEach(function(h){ | 
					
						
							|  |  |  | 					var keys = (rev[h] || []).map(that.normalizeKey.bind(that)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// find all reachable keys from the ones we just found in reverse...
 | 
					
						
							|  |  |  | 					keys.slice() | 
					
						
							|  |  |  | 						.filter(function(key){ return seen.indexOf(key) < 0 }) | 
					
						
							|  |  |  | 						.forEach(function(key){ | 
					
						
							|  |  |  | 							// direct aliases...
 | 
					
						
							|  |  |  | 							walkAliases(keys, rev, bindings, key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							var mod = that.splitKey(key) | 
					
						
							|  |  |  | 							var k = mod.pop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							// aliases with modifiers...
 | 
					
						
							|  |  |  | 							k != key  | 
					
						
							|  |  |  | 								&& walkAliases(keys, rev, bindings, k, mod) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							seen.push(seen) | 
					
						
							|  |  |  | 						}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if(keys.length > 0){ | 
					
						
							|  |  |  | 						var m = res[mode] = res[mode] || {} | 
					
						
							|  |  |  | 						m[h] = keys | 
					
						
							| 
									
										
										
										
											2016-04-03 20:57:03 +03:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				}) | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2016-04-03 20:57:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		return res | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get/set/unset handler for key...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// In general if handler is not passed this will get the handlers,
 | 
					
						
							|  |  |  | 	// if a handler is given this will set the handler, if the passed 
 | 
					
						
							|  |  |  | 	// handler is either null or '' then it will be unbound.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Get handler for key in all modes...
 | 
					
						
							|  |  |  | 	// 	.handler(key)
 | 
					
						
							|  |  |  | 	// 	.handler('*', key)
 | 
					
						
							|  |  |  | 	// 		-> key-spec
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Get handlers for key in applicable modes...
 | 
					
						
							|  |  |  | 	// 	.handler('?', key)
 | 
					
						
							|  |  |  | 	// 	.handler('test', key)
 | 
					
						
							|  |  |  | 	// 		-> key-spec
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Get handler for key in a specific mode...
 | 
					
						
							|  |  |  | 	// 	.handler(mode, key)
 | 
					
						
							|  |  |  | 	// 		-> key-spec
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Get handler for key in a specific list of modes...
 | 
					
						
							|  |  |  | 	// 	.handler([mode, ..], key)
 | 
					
						
							|  |  |  | 	// 		-> key-spec
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Bind handler to key in specific mode...
 | 
					
						
							|  |  |  | 	// 	.handler(mode, key, handler)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Bind handler to key in all modes...
 | 
					
						
							|  |  |  | 	// 	.handler('*', key, handler)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Bind handler to key in applicable modes...
 | 
					
						
							|  |  |  | 	// 	.handler('?', key, handler)
 | 
					
						
							|  |  |  | 	// 	.handler('test', key, handler)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Bind handler to key in a specific list of modes...
 | 
					
						
							|  |  |  | 	// 	.handler([mode, ..], key, handler)
 | 
					
						
							|  |  |  | 	// 		-> this 
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Unbind handler from key in specific mode...
 | 
					
						
							|  |  |  | 	// 	.handler(mode, key, null)
 | 
					
						
							|  |  |  | 	// 	.handler(mode, key, '')
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Unbind handler from key in all modes...
 | 
					
						
							|  |  |  | 	// 	.handler('*', key, null)
 | 
					
						
							|  |  |  | 	// 	.handler('*', key, '')
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Unbind handler from key in applicable modes...
 | 
					
						
							|  |  |  | 	// 	.handler('?', key, null)
 | 
					
						
							|  |  |  | 	// 	.handler('?', key, '')
 | 
					
						
							|  |  |  | 	// 	.handler('test', key, null)
 | 
					
						
							|  |  |  | 	// 	.handler('test', key, '')
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Unbind handler from key in a specific list of modes...
 | 
					
						
							|  |  |  | 	// 	.handler([mode, ..], key, null)
 | 
					
						
							|  |  |  | 	// 	.handler([mode, ..], key, '')
 | 
					
						
							|  |  |  | 	// 		-> this 
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		<mode>: <handler>,
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Search order:
 | 
					
						
							|  |  |  | 	// 	- search for full key
 | 
					
						
							|  |  |  | 	// 	- search for shifted key if applicable
 | 
					
						
							|  |  |  | 	// 	- search for key without modifiers
 | 
					
						
							|  |  |  | 	// 		- if an alias is found it is first checked with and then 
 | 
					
						
							|  |  |  | 	// 			without modifiers
 | 
					
						
							|  |  |  | 	// 	- search for key code without modifiers
 | 
					
						
							|  |  |  | 	// 		- if an alias is found it is first checked with and then 
 | 
					
						
							|  |  |  | 	// 			without modifiers
 | 
					
						
							|  |  |  | 	handler: function(mode, key, handler){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		var keyboard = this.keyboard | 
					
						
							|  |  |  | 		var key_separators = KEY_SEPARATORS   | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(mode == null){ | 
					
						
							|  |  |  | 			return null | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		if(key == null && this.isKey(mode)){ | 
					
						
							|  |  |  | 			key = mode | 
					
						
							|  |  |  | 			mode = '*' | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		var genKeys = function(key, shift_key){ | 
					
						
							|  |  |  | 			// match candidates...
 | 
					
						
							|  |  |  | 			return key_separators | 
					
						
							|  |  |  | 				// full key...
 | 
					
						
							|  |  |  | 				.map(function(s){ return key.join(s) }) | 
					
						
							|  |  |  | 				// full shift key...
 | 
					
						
							|  |  |  | 				.concat(shift_key ?  | 
					
						
							|  |  |  | 					key_separators | 
					
						
							|  |  |  | 						.map(function(s){ return shift_key.join(s) })  | 
					
						
							|  |  |  | 					: []) | 
					
						
							|  |  |  | 	   			.unique() } | 
					
						
							|  |  |  | 		var walkAliases = function(bindings, handler, modifiers){ | 
					
						
							|  |  |  | 			var seen = [] | 
					
						
							|  |  |  | 			var modifiers = modifiers || [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			while(handler in bindings){ | 
					
						
							|  |  |  | 				handler = bindings[handler] | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				handler = modifiers | 
					
						
							|  |  |  | 						.filter(function(m){ | 
					
						
							|  |  |  | 							return handler.indexOf(m) < 0 | 
					
						
							|  |  |  | 								&& seen.indexOf(m+handler) < 0 | 
					
						
							|  |  |  | 								&& m+handler in bindings }) | 
					
						
							|  |  |  | 						.map(function(m){ return m+handler })[0] | 
					
						
							|  |  |  | 					|| handler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// check for loops...
 | 
					
						
							|  |  |  | 				if(seen.indexOf(handler) >= 0){ | 
					
						
							|  |  |  | 					handler = null | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				seen.push(handler) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			return handler | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		key = this.normalizeKey(this.splitKey(key)) | 
					
						
							|  |  |  | 		var shift_key = this.shifted(key) | 
					
						
							| 
									
										
										
										
											2014-10-20 06:51:21 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		// match candidates...
 | 
					
						
							|  |  |  | 		var keys = genKeys(key, shift_key).unique() | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		// get modes...
 | 
					
						
							|  |  |  | 		var modes = mode == '*' ? Object.keys(keyboard) | 
					
						
							|  |  |  | 			: mode == 'test' || mode == '?' ? this.modes() | 
					
						
							|  |  |  | 			: mode instanceof Array ? mode | 
					
						
							|  |  |  | 			: [mode] | 
					
						
							| 
									
										
										
										
											2016-04-02 17:34:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		// get...
 | 
					
						
							|  |  |  | 		if(handler === undefined){ | 
					
						
							|  |  |  | 			var res = {} | 
					
						
							|  |  |  | 			var k = key.slice(-1)[0] | 
					
						
							|  |  |  | 			var c = this.key2code(k)  | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			var mod = genKeys(key.slice(0, -1).concat('')) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			var drop = mode == 'test' || mode == '?' | 
					
						
							|  |  |  | 			for(var i=0; i < modes.length; i++){ | 
					
						
							|  |  |  | 				var m = modes[i] | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				var bindings = keyboard[m] | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				// stage 1: check key aliases with modifiers...
 | 
					
						
							|  |  |  | 				handler = walkAliases( | 
					
						
							|  |  |  | 					bindings,  | 
					
						
							|  |  |  | 					keys.filter(function(k){ return bindings[k] })[0]) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				// stage 2: check raw key aliases with and without modifiers...
 | 
					
						
							|  |  |  | 				if(!handler){ | 
					
						
							|  |  |  | 					handler = walkAliases( | 
					
						
							|  |  |  | 						bindings,  | 
					
						
							|  |  |  | 						[k, c].filter(function(k){ return bindings[k] })[0], | 
					
						
							|  |  |  | 						mod) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				// explicit DROP -- ignore next sections...
 | 
					
						
							|  |  |  | 				if(drop && handler == 'DROP'){ | 
					
						
							| 
									
										
										
										
											2016-04-28 16:11:55 +03:00
										 |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				// we got a match...
 | 
					
						
							|  |  |  | 				if(handler){ | 
					
						
							|  |  |  | 					res[m] = handler | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				// if key in .drop then ignore the rest...
 | 
					
						
							|  |  |  | 				if(drop  | 
					
						
							|  |  |  | 						// explicit go to next section...
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 						&& (!handler  | 
					
						
							|  |  |  | 							|| handler.slice(0, 4) != 'NEXT') | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 						&& (bindings.drop == '*' | 
					
						
							|  |  |  | 							// XXX should this be more flexible by adding a
 | 
					
						
							|  |  |  | 							// 		specific key combo?
 | 
					
						
							|  |  |  | 							// 		... if yes, we'll need to differentiate 
 | 
					
						
							|  |  |  | 							// 		between X meaning drop only X and drop
 | 
					
						
							|  |  |  | 							// 		all combos with X...
 | 
					
						
							|  |  |  | 							|| (bindings.drop || []).indexOf(k) >= 0)){ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			return (typeof(mode) == typeof('str')  | 
					
						
							|  |  |  | 					&& ['*', 'test', '?'].indexOf(mode) < 0) ?  | 
					
						
							|  |  |  | 				res[mode] | 
					
						
							|  |  |  | 				: res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// set / remove...
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			modes.forEach(function(m){ | 
					
						
							|  |  |  | 				var bindings = keyboard[m] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// remove all matching keys...
 | 
					
						
							|  |  |  | 				// NOTE: if key with modifiers, then this will remove only 
 | 
					
						
							|  |  |  | 				// 		the full matched keys and shifted matches but will 
 | 
					
						
							|  |  |  | 				// 		leave the key without modifiers as-is...
 | 
					
						
							|  |  |  | 				keys.forEach(function(k){ delete bindings[k] }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// set handler if given...
 | 
					
						
							|  |  |  | 				if(handler && handler != ''){ | 
					
						
							|  |  |  | 					keyboard[mode][key] = handler | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// get applicable modes...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	modes: function(context){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return that.isModeApplicable ? | 
					
						
							|  |  |  | 			Object.keys(this.keyboard) | 
					
						
							|  |  |  | 				.filter(function(mode){  | 
					
						
							|  |  |  | 					return that.isModeApplicable( | 
					
						
							|  |  |  | 							mode,  | 
					
						
							|  |  |  | 							that.keyboard,  | 
					
						
							|  |  |  | 							context || that.context) }) | 
					
						
							|  |  |  | 			: Object.keys(this.keyboard) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// init base data...
 | 
					
						
							|  |  |  | 	__init__: function(keyboard, is_mode_applicable){ | 
					
						
							|  |  |  | 		this.keyboard = keyboard | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(is_mode_applicable instanceof Function){ | 
					
						
							|  |  |  | 			this.isModeApplicable = is_mode_applicable | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var Keyboard =  | 
					
						
							|  |  |  | module.Keyboard =  | 
					
						
							|  |  |  | object.makeConstructor('Keyboard',  | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | 		KeyboardClassPrototype,  | 
					
						
							|  |  |  | 		KeyboardPrototype) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Keyboard handler with modes identified by CSS selectors...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var KeyboardWithCSSModesPrototype = { | 
					
						
							|  |  |  | 	service_fields: ['doc', 'drop', 'pattern'], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	isModeApplicable: function(mode, keyboard, context){  | 
					
						
							|  |  |  | 		var pattern = keyboard[mode].pattern || mode | 
					
						
							|  |  |  | 		context = context || this.context | 
					
						
							|  |  |  | 		return !pattern  | 
					
						
							|  |  |  | 			|| pattern == '*'  | 
					
						
							|  |  |  | 			// XXX can we join these into one search???
 | 
					
						
							|  |  |  | 			|| context.is(pattern) | 
					
						
							|  |  |  | 			|| context.find(pattern).length > 0 | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__init__: function(keyboard, context){ | 
					
						
							|  |  |  | 		object.superMethod(KeyboardWithCSSModes, '__init__').call(this, keyboard) | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		if(context instanceof Function){ | 
					
						
							|  |  |  | 			Object.defineProperty(this, 'context', { | 
					
						
							|  |  |  | 				get: context, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.context = context | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var KeyboardWithCSSModes =  | 
					
						
							|  |  |  | module.KeyboardWithCSSModes =  | 
					
						
							|  |  |  | object.makeConstructor('KeyboardWithCSSModes',  | 
					
						
							|  |  |  | 		KeyboardClassPrototype,  | 
					
						
							|  |  |  | 		KeyboardWithCSSModesPrototype) | 
					
						
							|  |  |  | // inherit from Keyboard...
 | 
					
						
							|  |  |  | KeyboardWithCSSModes.prototype.__proto__ = Keyboard.prototype | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Base event handler wrapper of Keyboard...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | // This will produce a handler that can be used in one of two ways:
 | 
					
						
							|  |  |  | // 	- event handler
 | 
					
						
							|  |  |  | // 		- an event is passed as the only argument 
 | 
					
						
							|  |  |  | // 		- the function can be used directly as an event handler
 | 
					
						
							|  |  |  | // 	- direct key handler
 | 
					
						
							|  |  |  | // 		- a key and optionally a no_match handler are passed
 | 
					
						
							|  |  |  | // 		
 | 
					
						
							|  |  |  | // 	Example:
 | 
					
						
							|  |  |  | // 		var handler = makeKeyboardHandler(kb, null, action)
 | 
					
						
							|  |  |  | // 		
 | 
					
						
							|  |  |  | // 		// event handler...
 | 
					
						
							|  |  |  | // 		$(window).keydown(handler)
 | 
					
						
							|  |  |  | // 		
 | 
					
						
							|  |  |  | // 		// used directly...
 | 
					
						
							|  |  |  | // 		handler('ctrl_C', function(k){ console.log('Not bound:', k) })
 | 
					
						
							|  |  |  | // 		
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var makeKeyboardHandler = | 
					
						
							|  |  |  | module.makeKeyboardHandler = | 
					
						
							|  |  |  | function makeKeyboardHandler(keyboard, unhandled, actions){ | 
					
						
							| 
									
										
										
										
											2017-01-05 03:06:06 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	var kb = keyboard instanceof Keyboard ?  | 
					
						
							|  |  |  | 		keyboard  | 
					
						
							|  |  |  | 		//: Keyboard(keyboard, checkGlobalMode)
 | 
					
						
							|  |  |  | 		: Keyboard(keyboard) | 
					
						
							| 
									
										
										
										
											2016-04-07 04:58:22 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 	return function(key, no_match){ | 
					
						
							|  |  |  | 		no_match = no_match || unhandled | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		var did_handling = false | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 		var evt = event | 
					
						
							|  |  |  | 		var res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		//if(key instanceof Event || key instanceof $.Event){
 | 
					
						
							|  |  |  | 		if(typeof(key) != typeof('str')){ | 
					
						
							|  |  |  | 			evt = key | 
					
						
							|  |  |  | 			key = kb.event2key(evt) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		var handlers = kb.handler('test', key) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		Object.keys(handlers).forEach(function(mode){ | 
					
						
							|  |  |  | 			if(res === false){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			var handler = handlers[mode] | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			// raw function handler...
 | 
					
						
							|  |  |  | 			if(handler instanceof Function){ | 
					
						
							|  |  |  | 				res = handler.call(actions) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			// action call syntax...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				var h = parseActionCall(handler) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				if(h && h.action in actions){ | 
					
						
							|  |  |  | 					did_handling = true | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 					evt  | 
					
						
							|  |  |  | 						&& h.no_default  | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 						&& evt.preventDefault() | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 					// call the handler...
 | 
					
						
							| 
									
										
										
										
											2017-01-12 05:26:09 +03:00
										 |  |  | 					res = actions[h.action].apply(actions, h.arguments) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 					evt  | 
					
						
							|  |  |  | 						&& h.stop_propagation | 
					
						
							|  |  |  | 						&& evt.stopPropagation() | 
					
						
							|  |  |  | 						&& (res = false) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				}  | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 		no_match  | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			&& !did_handling  | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 			&& no_match.call(actions, evt, key) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return res | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // handler wrappers...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Event handler wrapper to stop handling keys if check callback does 
 | 
					
						
							|  |  |  | // not pass (returns false)...
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var stoppableKeyboardRepeat =  | 
					
						
							|  |  |  | module.stoppableKeyboardRepeat =  | 
					
						
							|  |  |  | function(handler, check){ | 
					
						
							|  |  |  | 	return function(evt){ | 
					
						
							|  |  |  | 		return check() && handler(evt) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // Event handler wrapper that will drop identical keys repeating at rate
 | 
					
						
							|  |  |  | // greater than max_rate
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | // NOTE: this will only limit repeating key combinations thus no lag is 
 | 
					
						
							|  |  |  | // 		introduced...
 | 
					
						
							|  |  |  | var dropRepeatingkeys = | 
					
						
							|  |  |  | module.dropRepeatingkeys = | 
					
						
							|  |  |  | function dropRepeatingkeys(handler, max_rate){ | 
					
						
							|  |  |  | 	var _timeout = null | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	var key = null | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	var ctrl = null | 
					
						
							|  |  |  | 	var meta = null | 
					
						
							|  |  |  | 	var alt = null | 
					
						
							|  |  |  | 	var shift = null | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	return function(evt){ | 
					
						
							|  |  |  | 		if(_timeout != null | 
					
						
							|  |  |  | 				&& key == evt.keyCode | 
					
						
							|  |  |  | 				&& ctrl == evt.ctrlKey | 
					
						
							|  |  |  | 				&& meta == evt.metaKey | 
					
						
							|  |  |  | 				&& alt == evt.altKey | 
					
						
							|  |  |  | 				&& shift == evt.shiftKey){ | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		key = evt.keyCode | 
					
						
							|  |  |  | 		ctrl = evt.ctrlKey | 
					
						
							|  |  |  | 		meta = evt.metaKey | 
					
						
							|  |  |  | 		alt = evt.altKey | 
					
						
							|  |  |  | 		shift = evt.shiftKey | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		_timeout = setTimeout(function(){ | 
					
						
							|  |  |  | 				_timeout = null | 
					
						
							|  |  |  | 			},  | 
					
						
							|  |  |  | 			// XXX is this the right way to go???
 | 
					
						
							|  |  |  | 			typeof(max_rate) == typeof(123) ? max_rate : max_rate()) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		return handler(evt) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							| 
									
										
										
										
											2016-08-20 22:49:36 +03:00
										 |  |  | * vim:set ts=4 sw=4 :                               */ return module }) |