| 
									
										
										
										
											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 = | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | module.MODIFIERS = [ 'caps', 'ctrl', 'meta', 'alt', 'shift' ] | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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.
 | 
					
						
							| 
									
										
										
										
											2018-11-06 04:24:22 +03:00
										 |  |  | // NOTE: this is *mostly* language agnostic.
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | // 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...
 | 
					
						
							| 
									
										
										
										
											2018-11-05 01:29:06 +03:00
										 |  |  | 	 8: 'Backspace',		 9: 'Tab',				13: 'Enter',	 | 
					
						
							|  |  |  | 	16: 'Shift',			17: 'Ctrl',				18: 'Alt',			 | 
					
						
							|  |  |  | 	20: 'Caps Lock',		27: 'Esc',				32: 'Space',		 | 
					
						
							|  |  |  | 	33: 'PgUp',				34: 'PgDown',			35: 'End',			  | 
					
						
							|  |  |  | 	36: 'Home',				37: 'Left',				38: 'Up',	  | 
					
						
							|  |  |  | 	39: 'Right',  			40: 'Down',				45: 'Ins',		 | 
					
						
							|  |  |  | 	46: 'Del',				91: 'Win',				93: 'Menu',		 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Function Keys...
 | 
					
						
							| 
									
										
										
										
											2018-11-05 01:29:06 +03:00
										 |  |  | 	112: 'F1',		113: 'F2',		114: 'F3',		115: 'F4',		 | 
					
						
							|  |  |  | 	116: 'F5',		117: 'F6',		118: 'F7',		119: 'F8',		 | 
					
						
							|  |  |  | 	120: 'F9',		121: 'F10',		122: 'F11',		123: 'F12', | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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: '=', | 
					
						
							| 
									
										
										
										
											2018-11-05 01:08:06 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 							// right side of keyboard...
 | 
					
						
							|  |  |  | 							219: '[',	221: ']',	220: '\\', | 
					
						
							|  |  |  | 							186: ';',	222: '\'', | 
					
						
							|  |  |  | 				188: ',',	190: '.',	191: '/', | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var SHIFT_KEYS = | 
					
						
							|  |  |  | module.SHIFT_KEYS = { | 
					
						
							| 
									
										
										
										
											2018-11-05 01:08:06 +03:00
										 |  |  | 	// Number row...
 | 
					
						
							| 
									
										
										
										
											2014-10-19 22:32:11 +04:00
										 |  |  | 	'#1': '!',	'#2': '@',	'#3': '#',	'#4': '$',	'#5': '%', | 
					
						
							| 
									
										
										
										
											2018-11-05 01:08:06 +03:00
										 |  |  | 	'#6': '^',	'#7': '&',	'#8': '*',	'#9': '(',	'#0': ')',	 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// top row...
 | 
					
						
							|  |  |  | 	'`': '~',		/* Numbers */		'-':  '_',	'=':  '+', | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 01:08:06 +03:00
										 |  |  | 							// right side of keyboard...
 | 
					
						
							|  |  |  | 							'[': '{',	']':  '}',	'\\': '|', | 
					
						
							|  |  |  | 							';': ':',	'\'': '"', | 
					
						
							|  |  |  | 				',': '<',	'.':  '>',	'/':  '?', | 
					
						
							| 
									
										
										
										
											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){ | 
					
						
							| 
									
										
										
										
											2020-06-06 14:58:08 +03:00
										 |  |  | 	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){ | 
					
						
							| 
									
										
										
										
											2020-06-06 14:58:08 +03:00
										 |  |  | 	KEY_CODES[SPECIAL_KEYS[k]] = k } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 21:36:58 +03:00
										 |  |  | // This is used to identify and correct key notation...
 | 
					
						
							|  |  |  | // NOTE: the keys here are intentionally lowercase...
 | 
					
						
							|  |  |  | var SPECIAL_KEY_ALTERNATIVE_TITLES = { | 
					
						
							|  |  |  | 	1: '#1', 2: '#2', 3: '#3', 4: '#4', 5: '#5',  | 
					
						
							|  |  |  | 	6: '#6', 7: '#7', 8: '#8', 9: '#9', 0: '#0', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctl: 'Ctrl', control: 'Ctrl', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 01:29:06 +03:00
										 |  |  | 	capslock: 'Caps Lock', caps: 'Caps Lock', | 
					
						
							| 
									
										
										
										
											2017-01-18 21:36:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 01:29:06 +03:00
										 |  |  | 	'page up': 'PgUp', pageup: 'PgUp', | 
					
						
							| 
									
										
										
										
											2017-01-18 21:36:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 01:29:06 +03:00
										 |  |  | 	'page down': 'PgDown', pagedown: 'PgDown', | 
					
						
							| 
									
										
										
										
											2017-01-18 21:36:58 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	insert: 'Ins', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	delete: 'Del', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 01:29:06 +03:00
										 |  |  | 	bkspace : 'Backspace', 'back space' : 'Backspace', | 
					
						
							| 
									
										
										
										
											2017-01-18 21:36:58 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	windows: 'Win', | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | var SPECIAL_KEYS_DICT = {} | 
					
						
							|  |  |  | for(var k in SPECIAL_KEYS){ | 
					
						
							| 
									
										
										
										
											2020-06-06 14:58:08 +03:00
										 |  |  | 	SPECIAL_KEYS_DICT[SPECIAL_KEYS[k].toLowerCase()] = SPECIAL_KEYS[k] } | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2020-06-06 14:58:08 +03:00
										 |  |  | 	return func } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-11-19 01:01:44 +03:00
										 |  |  | // 	
 | 
					
						
							|  |  |  | // XXX EXPERIMENTAL...
 | 
					
						
							|  |  |  | // 	This will resolve names to context attributes 
 | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											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???
 | 
					
						
							| 
									
										
										
										
											2017-08-04 04:28:29 +03:00
										 |  |  | // XXX this is the same as actions.parseStringAction(..), reuse in a logical manner...
 | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | var parseActionCall = | 
					
						
							|  |  |  | module.parseActionCall = | 
					
						
							| 
									
										
										
										
											2017-11-19 01:01:44 +03:00
										 |  |  | function parseActionCall(txt, context){ | 
					
						
							|  |  |  | 	context = context || this | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | 	// 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...
 | 
					
						
							| 
									
										
										
										
											2017-11-19 01:01:44 +03:00
										 |  |  | 	/* | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | 	var args = JSON.parse('['+( | 
					
						
							|  |  |  | 		((c[1] || '') | 
					
						
							|  |  |  | 			.match(/"[^"]*"|'[^']*'|\{[^\}]*\}|\[[^\]]*\]|\d+|\d+\.\d*|null/gm)  | 
					
						
							|  |  |  | 		|| []) | 
					
						
							|  |  |  | 		.join(','))+']') | 
					
						
							| 
									
										
										
										
											2017-11-19 01:01:44 +03:00
										 |  |  | 	//*/
 | 
					
						
							|  |  |  | 	// XXX EXPERIMENTAL -- is this safe???
 | 
					
						
							|  |  |  | 	var args = ((c[1] || '') | 
					
						
							|  |  |  | 			.match(RegExp([ | 
					
						
							|  |  |  | 				'"[^"]*"', | 
					
						
							|  |  |  | 				"'[^']*", | 
					
						
							|  |  |  | 				'`[^`]*`', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				'\\{[^\\}]*\\}', | 
					
						
							|  |  |  | 				'\\[[^\\]]*\\]', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				'\\d+|\\d+\\.\\d*', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				'[a-zA-Z$@#_][0-9a-zA-Z$@#_]*', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				'null', | 
					
						
							|  |  |  | 			].join('|'), 'gm')) | 
					
						
							|  |  |  | 		|| []) | 
					
						
							|  |  |  | 		.map(function(e){ | 
					
						
							|  |  |  | 			// resolve vars to context attrs...
 | 
					
						
							|  |  |  | 			return /^[a-zA-Z$@#_][0-9a-zA-Z$@#_]*$/.test(e) ? | 
					
						
							|  |  |  | 				(context || {})[e] | 
					
						
							|  |  |  | 				: JSON.parse(e) }) | 
					
						
							| 
									
										
										
										
											2016-12-12 15:29:37 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		action: action, | 
					
						
							|  |  |  | 		arguments: args, | 
					
						
							|  |  |  | 		doc: doc, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		no_default: no_default, | 
					
						
							|  |  |  | 		stop_propagation: false, | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 	} } | 
					
						
							| 
									
										
										
										
											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){ | 
					
						
							| 
									
										
										
										
											2018-03-19 01:42:36 +03:00
										 |  |  | 	evt = evt || window.event | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 	// NOTE: we do not care about the jQuery wrapper here...
 | 
					
						
							|  |  |  | 	evt = evt.originalEvent || evt | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var key = [] | 
					
						
							|  |  |  | 	evt.ctrlKey && key.push('ctrl') | 
					
						
							|  |  |  | 	evt.altKey && key.push('alt') | 
					
						
							|  |  |  | 	evt.metaKey && key.push('meta') | 
					
						
							|  |  |  | 	evt.shiftKey && key.push('shift') | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 	evt.getModifierState  | 
					
						
							|  |  |  | 		&& evt.getModifierState('CapsLock')  | 
					
						
							|  |  |  | 		&& key.push('caps') | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +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...
 | 
					
						
							| 
									
										
										
										
											2019-06-18 20:58:40 +03:00
										 |  |  | 		&& 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-18 21:36:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 21:36:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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)){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +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){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 		return k } | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// get the propper name...
 | 
					
						
							| 
									
										
										
										
											2017-01-18 21:36:58 +03:00
										 |  |  | 	k = SPECIAL_KEY_ALTERNATIVE_TITLES[k.toLowerCase()] || k | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 	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  | 
					
						
							| 
									
										
										
										
											2020-06-06 14:58:08 +03:00
										 |  |  | 		: joinKey(key) } | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2020-06-06 14:58:08 +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
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 	// NOTE: if .__keyboard is set to a function, it will be used both as
 | 
					
						
							|  |  |  | 	// 		a getter and as a setter via the .keyboard prop, to overwrite
 | 
					
						
							|  |  |  | 	// 		write to .__keyboard directly...
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	__keyboard: null, | 
					
						
							|  |  |  | 	get keyboard(){ | 
					
						
							|  |  |  | 		return this.__keyboard instanceof Function ?  | 
					
						
							|  |  |  | 			this.__keyboard()  | 
					
						
							|  |  |  | 			: this.__keyboard }, | 
					
						
							| 
									
										
										
										
											2019-06-04 02:55:53 +03:00
										 |  |  | 	// XXX might be a good idea to normalize the value here...
 | 
					
						
							|  |  |  | 	// 		...i.e. normalize key specs as they are input by humans...
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	set keyboard(value){ | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 		if(this.__keyboard instanceof Function){ | 
					
						
							|  |  |  | 			this.__keyboard(value)  | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 			this.__keyboard = value } }, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// XXX is this needed???
 | 
					
						
							|  |  |  | 	//context: null,
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// string handler parser...
 | 
					
						
							|  |  |  | 	// 
 | 
					
						
							|  |  |  | 	// Return format:
 | 
					
						
							|  |  |  | 	// {
 | 
					
						
							|  |  |  | 	//		action: <str>,
 | 
					
						
							|  |  |  | 	//		arguments: <array>,
 | 
					
						
							|  |  |  | 	//		doc: <str> || null,
 | 
					
						
							|  |  |  | 	//		no_default: <bool>,
 | 
					
						
							|  |  |  | 	//		stop_propagation: <bool>,
 | 
					
						
							|  |  |  | 	// }
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-08-04 04:28:29 +03:00
										 |  |  | 	// XXX should this be a Keyboard thing or a context thing???
 | 
					
						
							|  |  |  | 	// XXX revise name...
 | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:42 +03:00
										 |  |  | 	parseStringHandler: parseActionCall, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-31 01:59:01 +03:00
										 |  |  | 	// call keyboard handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	callKeyboardHandler: function(data, context){ | 
					
						
							|  |  |  | 		// call the handler...
 | 
					
						
							|  |  |  | 		return data.action | 
					
						
							|  |  |  | 			.split('.')  | 
					
						
							|  |  |  | 			.reduce(function(res, k){  | 
					
						
							|  |  |  | 				context = res | 
					
						
							|  |  |  | 				return res[k]  | 
					
						
							|  |  |  | 			}, context) | 
					
						
							| 
									
										
										
										
											2018-08-31 13:24:30 +03:00
										 |  |  | 			.apply(context, data.arguments) }, | 
					
						
							| 
									
										
										
										
											2018-08-31 01:59:01 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	// 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(){ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Sort modes...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Sort via cmp function...
 | 
					
						
							|  |  |  | 	// 	.sortModes(func)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Sort to the same order as list...
 | 
					
						
							|  |  |  | 	// 	.sortModes(list)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: calling this with no arguments will have no effect.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX should this update the kb in-place???
 | 
					
						
							|  |  |  | 	sortModes: function(cmp){ | 
					
						
							|  |  |  | 		var ordered = {} | 
					
						
							|  |  |  | 		var bindings = this.keyboard | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(cmp == null){ | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cmp = cmp instanceof Function ? | 
					
						
							|  |  |  | 			Object.keys(bindings).sort(cmp) | 
					
						
							|  |  |  | 			: cmp | 
					
						
							|  |  |  | 				.concat(Object.keys(bindings)) | 
					
						
							|  |  |  | 				.unique() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cmp | 
					
						
							|  |  |  | 			.forEach(function(mode){ | 
					
						
							|  |  |  | 				ordered[mode] = bindings[mode] | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// reorder only if we moved all the modes...
 | 
					
						
							|  |  |  | 		if(Object.keys(bindings).length == Object.keys(ordered).length){ | 
					
						
							|  |  |  | 			this.keyboard = ordered | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	// Get keys for handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	List all handlers...
 | 
					
						
							|  |  |  | 	// 	.keys()
 | 
					
						
							|  |  |  | 	// 	.keys('*')
 | 
					
						
							|  |  |  | 	//		-> keys
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 	// 	List only applicable handlers...
 | 
					
						
							|  |  |  | 	// 	.keys('?')
 | 
					
						
							|  |  |  | 	//		-> keys
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	//	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.
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-09-09 01:22:30 +03:00
										 |  |  | 	// XXX drop/DROP/NEXT handling need more testing...
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 	// XXX this and .handler(..) in part repeat handling dropped keys, 
 | 
					
						
							|  |  |  | 	// 		can we unify this???
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 23:04:00 +03:00
										 |  |  | 		handler = arguments.length > 1 ? [...arguments] | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			: handler == null ? '*' | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 			: handler == '*' || handler == '?' || handler instanceof Function ? handler | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			: 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) | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 						&& walkAliases(res, rev, bindings, k, mod) }) } } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 		var modes = handler == '?' ? this.modes() : '*' | 
					
						
							|  |  |  | 		var drop = [] | 
					
						
							|  |  |  | 		var next = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		Object.keys(keyboard).forEach(function(mode){ | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 			// skip non-applicable modes...
 | 
					
						
							|  |  |  | 			if(modes != '*' && modes.indexOf(mode) < 0){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				return } | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			var bindings = keyboard[mode] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 			if(handler == '?'){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				next = next.concat(bindings.NEXT || []) } | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			// 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])) | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 						.unique() }) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			var seen = [] | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 			var handlers = handler == '*' || handler == '?' ?   | 
					
						
							|  |  |  | 					Object.keys(rev)  | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				: handler instanceof Function ?  | 
					
						
							|  |  |  | 					Object.keys(rev) | 
					
						
							|  |  |  | 						.filter(handler) | 
					
						
							|  |  |  | 				: handler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			handlers | 
					
						
							|  |  |  | 				.forEach(function(h){ | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 					if(handler == '?'&& h == 'NEXT'){ | 
					
						
							|  |  |  | 						return | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					var keys = (rev[h] || []).map(that.normalizeKey.bind(that)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if(handler == '?' &&  h == 'DROP'){ | 
					
						
							|  |  |  | 						drop = drop == '*' ?  '*' : drop.concat(keys) | 
					
						
							|  |  |  | 						next = next | 
					
						
							|  |  |  | 							.filter(function(k){ return keys.indexOf(k) >= 0 }) | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 						return } | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 					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) | 
					
						
							|  |  |  | 						}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 					if(handler == '?'){ | 
					
						
							|  |  |  | 						keys = keys | 
					
						
							|  |  |  | 							.filter(function(key){  | 
					
						
							|  |  |  | 								var k = that.splitKey(key) | 
					
						
							|  |  |  | 								return next.indexOf(key) >= 0 | 
					
						
							|  |  |  | 									|| next.indexOf(k) >= 0 | 
					
						
							|  |  |  | 									|| (drop != '*'  | 
					
						
							|  |  |  | 										&& drop.indexOf(key) < 0 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 										&& drop.indexOf(k) < 0) }) } | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 					if(keys.length > 0){ | 
					
						
							|  |  |  | 						var m = res[mode] = res[mode] || {} | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 						m[h] = keys } | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if(handler == '?'){ | 
					
						
							|  |  |  | 				drop = drop == '*' || bindings.drop == '*' ?  | 
					
						
							|  |  |  | 					'*'  | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 					: drop.concat(bindings.drop || []) } | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2016-04-03 20:57:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 		return res }, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:32:26 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX this and .keys('?') in part repeat handling dropped keys, 
 | 
					
						
							|  |  |  | 	// 		can we unify this???
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	handler: function(mode, key, handler){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		var keyboard = this.keyboard | 
					
						
							|  |  |  | 		var key_separators = KEY_SEPARATORS   | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(mode == null){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 			return null } | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		if(key == null && this.isKey(mode)){ | 
					
						
							|  |  |  | 			key = mode | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 			mode = '*' } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 		var joinKeys = function(key, shift_key){ | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 			// 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() } | 
					
						
							| 
									
										
										
										
											2017-04-22 06:36:30 +03:00
										 |  |  | 		// NOTE: the generated list is in the following order:
 | 
					
						
							|  |  |  | 		// 		- longest chain first
 | 
					
						
							|  |  |  | 		// 		- shifted keys first
 | 
					
						
							|  |  |  | 		// 		- modifiers are skipped in order, left to right
 | 
					
						
							|  |  |  | 		// XXX carefully revise key search order...
 | 
					
						
							| 
									
										
										
										
											2019-06-04 02:55:53 +03:00
										 |  |  | 		// XXX should we normalize what's in the bindings????
 | 
					
						
							|  |  |  | 		// 		...currently we will match 'Ins' but not 'insert'
 | 
					
						
							| 
									
										
										
										
											2017-04-22 07:06:14 +03:00
										 |  |  | 		var keyCombinations = function(key, shift_key, remove_single_keys){ | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 			if(key.length <= 1){ | 
					
						
							| 
									
										
										
										
											2017-04-22 07:06:14 +03:00
										 |  |  | 				//return shift_key ? [key, shift_key] : [key]
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				return key } | 
					
						
							| 
									
										
										
										
											2017-04-22 07:06:14 +03:00
										 |  |  | 			var k = remove_single_keys ? 1 : 0  | 
					
						
							| 
									
										
										
										
											2017-04-22 06:36:30 +03:00
										 |  |  | 			// generate recursively, breadth first...
 | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 			var _combinations = function(level, res){ | 
					
						
							|  |  |  | 				var next = [] | 
					
						
							|  |  |  | 				level | 
					
						
							|  |  |  | 					.map(function(elem){ | 
					
						
							| 
									
										
										
										
											2017-04-22 06:36:30 +03:00
										 |  |  | 						var c = elem.join('+++') | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 						res.indexOf(c) < 0 | 
					
						
							|  |  |  | 							&& res.push(c) | 
					
						
							|  |  |  | 							&& elem | 
					
						
							|  |  |  | 								.slice(0, -1) | 
					
						
							|  |  |  | 								.map(function(_, i){ | 
					
						
							|  |  |  | 									var s = elem.slice() | 
					
						
							|  |  |  | 									s.splice(i, 1) | 
					
						
							| 
									
										
										
										
											2017-04-22 07:06:14 +03:00
										 |  |  | 									// NOTE: we do not include single keys
 | 
					
						
							|  |  |  | 									// 		as they are searched separately...
 | 
					
						
							|  |  |  | 									//s.length > 0 
 | 
					
						
							|  |  |  | 									s.length > k  | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 										&& next.push(s) }) }) | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 				next.length > 0  | 
					
						
							|  |  |  | 					&& _combinations(next, res) | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				return res } | 
					
						
							| 
									
										
										
										
											2017-04-22 06:36:30 +03:00
										 |  |  | 			return _combinations(shift_key && shift_key.length > 0 ? | 
					
						
							|  |  |  | 				[key, shift_key]  | 
					
						
							|  |  |  | 				: [key], []) | 
					
						
							|  |  |  | 					.map(function(e){ return joinKeys(e.split(/\+\+\+/g)) }) | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 					.reduce(function(a, b){ return a.concat(b) }, []) } | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 		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){ | 
					
						
							| 
									
										
										
										
											2017-02-10 02:19:19 +03:00
										 |  |  | 							return handler instanceof Function  | 
					
						
							|  |  |  | 								|| (handler.indexOf(m) < 0 | 
					
						
							|  |  |  | 									&& seen.indexOf(m+handler) < 0 | 
					
						
							|  |  |  | 									&& m+handler in bindings) }) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 						.map(function(m){ return m+handler })[0] | 
					
						
							|  |  |  | 					|| handler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// check for loops...
 | 
					
						
							|  |  |  | 				if(seen.indexOf(handler) >= 0){ | 
					
						
							|  |  |  | 					handler = null | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 					break } | 
					
						
							|  |  |  | 				seen.push(handler) } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +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...
 | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 		//var keys = joinKeys(key, shift_key).unique()
 | 
					
						
							| 
									
										
										
										
											2017-04-22 07:06:14 +03:00
										 |  |  | 		// NOTE: we are skipping single keys from list as they are searched
 | 
					
						
							|  |  |  | 		// 		separately...
 | 
					
						
							|  |  |  | 		var keys = keyCombinations(key, shift_key, true) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-22 07:06:14 +03:00
										 |  |  | 		// XXX
 | 
					
						
							|  |  |  | 		//console.log(keys, '--', joinKeys(key, shift_key).unique())
 | 
					
						
							| 
									
										
										
										
											2017-04-22 06:36:30 +03: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-04-22 06:20:12 +03:00
										 |  |  | 			//var mod = joinKeys(key.slice(0, -1).concat(''))
 | 
					
						
							| 
									
										
										
										
											2017-04-22 07:06:14 +03:00
										 |  |  | 			var mod = keyCombinations(key.slice(0, -1).concat(''), null, true) | 
					
						
							| 
									
										
										
										
											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], | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 						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'){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 					break } | 
					
						
							| 
									
										
										
										
											2016-04-28 16:11:55 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 				// we got a match...
 | 
					
						
							|  |  |  | 				if(handler){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 					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  | 
					
						
							| 
									
										
										
										
											2017-01-27 17:33:21 +03:00
										 |  |  | 							|| (typeof(handler) == typeof('str')  | 
					
						
							|  |  |  | 								&& 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)){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03: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 != ''){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 					keyboard[mode][that.joinKey(key)] = handler } }) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Trigger handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX can this be implemented here???
 | 
					
						
							|  |  |  | 	// 		...we need context among other things...
 | 
					
						
							|  |  |  | 	// 		might be a good idea to add config options for everything 
 | 
					
						
							|  |  |  | 	// 		and just set things up in wrappers...
 | 
					
						
							|  |  |  | 	handle: function(mode, key){ | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 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) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 	// XXX EXPERIMENTAL...
 | 
					
						
							|  |  |  | 	// Basic binding editing API...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this is an event-like proxy to the .handler(..)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-09-30 01:25:38 +03:00
										 |  |  | 	// 	Bind handler key ('General' section)...
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 	// 	.on(key, handler)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.on(key, section, handler)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-09-30 01:25:38 +03:00
										 |  |  | 	// NOTE: default mode is 'General'...
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 	on: function(key, handler){ | 
					
						
							|  |  |  | 		// normalize args...
 | 
					
						
							|  |  |  | 		if(arguments.length == 3){ | 
					
						
							| 
									
										
										
										
											2020-09-28 17:42:19 +03:00
										 |  |  | 			var [key, mode, handler] = arguments | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-09-28 17:42:19 +03:00
										 |  |  | 			var [key, handler] = arguments } | 
					
						
							|  |  |  | 		var mode = mode  | 
					
						
							| 
									
										
										
										
											2020-09-30 01:25:38 +03:00
										 |  |  | 			//|| Object.keys(this.keyboard)[0]
 | 
					
						
							|  |  |  | 			|| 'General' | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 		// bind...
 | 
					
						
							|  |  |  | 		return this.handler(mode, key, handler) }, | 
					
						
							|  |  |  | 	off: function(key, mode){ | 
					
						
							|  |  |  | 		// normalize args...
 | 
					
						
							|  |  |  | 		mode = mode  | 
					
						
							| 
									
										
										
										
											2020-09-30 01:25:38 +03:00
										 |  |  | 			//|| Object.keys(this.keyboard)[0]
 | 
					
						
							|  |  |  | 			|| 'General' | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 		// unbind...
 | 
					
						
							|  |  |  | 		return this.handler(mode, key, null) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 	// init base data...
 | 
					
						
							|  |  |  | 	__init__: function(keyboard, is_mode_applicable){ | 
					
						
							|  |  |  | 		this.keyboard = keyboard | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(is_mode_applicable instanceof Function){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 			this.isModeApplicable = is_mode_applicable } }, | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var Keyboard =  | 
					
						
							|  |  |  | module.Keyboard =  | 
					
						
							| 
									
										
										
										
											2019-07-17 00:07:24 +03:00
										 |  |  | object.Constructor('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 == '*'  | 
					
						
							| 
									
										
										
										
											2018-05-18 00:10:06 +03:00
										 |  |  | 			// jQuery...
 | 
					
						
							| 
									
										
										
										
											2018-05-24 12:55:47 +03:00
										 |  |  | 			|| (context.is ?  | 
					
						
							| 
									
										
										
										
											2018-05-18 00:10:06 +03:00
										 |  |  | 				(context.is(pattern) | 
					
						
							|  |  |  | 					|| context.find(pattern).length > 0) | 
					
						
							| 
									
										
										
										
											2018-05-24 12:55:47 +03:00
										 |  |  | 				: false) | 
					
						
							| 
									
										
										
										
											2018-05-18 00:10:06 +03:00
										 |  |  | 			// Vanilla JS...
 | 
					
						
							| 
									
										
										
										
											2018-05-24 12:55:47 +03:00
										 |  |  | 			|| (context.matches ?  | 
					
						
							| 
									
										
										
										
											2018-05-18 00:10:06 +03:00
										 |  |  | 				(context.matches(pattern) | 
					
						
							|  |  |  | 					|| !!context.querySelector(pattern)) | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				: false) }, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	__init__: function(keyboard, context){ | 
					
						
							| 
									
										
										
										
											2020-04-27 16:57:23 +03:00
										 |  |  | 		object.parentCall(KeyboardWithCSSModesPrototype.__init__, this, keyboard) | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | 		 | 
					
						
							|  |  |  | 		if(context instanceof Function){ | 
					
						
							|  |  |  | 			Object.defineProperty(this, 'context', { | 
					
						
							|  |  |  | 				get: context, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 			this.context = context } }, | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var KeyboardWithCSSModes =  | 
					
						
							|  |  |  | module.KeyboardWithCSSModes =  | 
					
						
							| 
									
										
										
										
											2019-07-17 00:07:24 +03:00
										 |  |  | object.Constructor('KeyboardWithCSSModes',  | 
					
						
							| 
									
										
										
										
											2017-01-12 02:41:03 +03:00
										 |  |  | 		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-04-22 06:20:12 +03:00
										 |  |  | // NOTE: the handler will also set the .capslock attribute on the 
 | 
					
						
							|  |  |  | // 		keyboard object and update it on each key press...
 | 
					
						
							|  |  |  | // NOTE: if .capslock is false means that either it is not on or 
 | 
					
						
							|  |  |  | // 		undetectable...
 | 
					
						
							|  |  |  | // NOTE: before any key is pressed the .capslock is set to undefined
 | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:42 +03:00
										 |  |  | // 
 | 
					
						
							|  |  |  | // XXX not sure if handler calling mechanics should be outside of the 
 | 
					
						
							|  |  |  | // 		Keyboard object...
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | var makeKeyboardHandler = | 
					
						
							|  |  |  | module.makeKeyboardHandler = | 
					
						
							|  |  |  | function makeKeyboardHandler(keyboard, unhandled, actions){ | 
					
						
							|  |  |  | 	var kb = keyboard instanceof Keyboard ?  | 
					
						
							|  |  |  | 		keyboard  | 
					
						
							|  |  |  | 		//: Keyboard(keyboard, checkGlobalMode)
 | 
					
						
							|  |  |  | 		: Keyboard(keyboard) | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 	kb.capslock = undefined | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2018-03-19 01:42:36 +03:00
										 |  |  | 		var evt = window.event | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 		var res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		//if(key instanceof Event || key instanceof $.Event){
 | 
					
						
							| 
									
										
										
										
											2019-06-18 20:58:40 +03:00
										 |  |  | 		if(typeof(key) != typeof('str') && !(key instanceof Array)){ | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 			evt = key | 
					
						
							|  |  |  | 			key = kb.event2key(evt) | 
					
						
							| 
									
										
										
										
											2017-04-22 06:20:12 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			kb.capslock = key.indexOf('caps') >= 0 | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											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){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				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...
 | 
					
						
							| 
									
										
										
										
											2017-08-04 04:28:29 +03:00
										 |  |  | 			// XXX should this be a Keyboard thing or a context thing???
 | 
					
						
							|  |  |  | 			} else if(actions.parseStringHandler || kb.parseStringHandler){ | 
					
						
							| 
									
										
										
										
											2018-08-31 01:59:01 +03:00
										 |  |  | 				var h = actions.parseStringHandler ? | 
					
						
							|  |  |  | 					actions.parseStringHandler(handler, actions) | 
					
						
							|  |  |  | 					: kb.parseStringHandler(handler, actions) | 
					
						
							| 
									
										
										
										
											2018-06-02 22:24:31 +03:00
										 |  |  | 				var path = h ? h.action.split('.') : [] | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-02 22:24:31 +03:00
										 |  |  | 				if(path.length > 0 && path[0] in actions){ | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 					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...
 | 
					
						
							| 
									
										
										
										
											2018-08-31 01:59:01 +03:00
										 |  |  | 					res = actions.callKeyboardHandler ? | 
					
						
							|  |  |  | 						actions.callKeyboardHandler(h, actions) | 
					
						
							|  |  |  | 						: kb.callKeyboardHandler(h, actions) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 18:00:38 +03:00
										 |  |  | 					evt  | 
					
						
							|  |  |  | 						&& h.stop_propagation | 
					
						
							|  |  |  | 						&& evt.stopPropagation() | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 						&& (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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 		return res } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 21:07:29 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Pausable base event handler wrapper of Keyboard...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This is the same as .makeKeyboardHandler(..) but adds ability to 
 | 
					
						
							|  |  |  | // pause repeating key handling...
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // This will extend the keyboard object by adding:
 | 
					
						
							|  |  |  | // 		.pauseRepeat() 		- will pause repeating keys...
 | 
					
						
							|  |  |  | // 		
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:02:16 +03:00
										 |  |  | // XXX should we drop only when the same key is repeating or any keys 
 | 
					
						
							|  |  |  | // 		repeating (as is now)???
 | 
					
						
							| 
									
										
										
										
											2017-03-08 21:07:29 +03:00
										 |  |  | var makePausableKeyboardHandler = | 
					
						
							|  |  |  | module.makePausableKeyboardHandler = | 
					
						
							|  |  |  | function makePausableKeyboardHandler(keyboard, unhandled, actions, check_interval){ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var kb = keyboard instanceof Keyboard ?  | 
					
						
							|  |  |  | 		keyboard  | 
					
						
							|  |  |  | 		//: Keyboard(keyboard, checkGlobalMode)
 | 
					
						
							|  |  |  | 		: Keyboard(keyboard) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:02:16 +03:00
										 |  |  | 	kb.pauseRepeat = function(){ | 
					
						
							|  |  |  | 		this.__repeat_pause_timeout | 
					
						
							|  |  |  | 			&& clearTimeout(this.__repeat_pause_timeout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.__repeat_pause_timeout = setTimeout( | 
					
						
							|  |  |  | 			function(){ | 
					
						
							|  |  |  | 				delete kb.__repeat_pause_timeout  | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			(check_interval instanceof Function ? | 
					
						
							|  |  |  | 				check_interval.call(actions)  | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				: (check_interval || 100))) } | 
					
						
							| 
									
										
										
										
											2017-03-19 02:02:16 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 21:07:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return stoppableKeyboardRepeat( | 
					
						
							|  |  |  | 		makeKeyboardHandler(kb, unhandled, actions),  | 
					
						
							|  |  |  | 		function(){ | 
					
						
							| 
									
										
										
										
											2017-03-19 02:02:16 +03:00
										 |  |  | 			if(kb.__repeat_pause_timeout){ | 
					
						
							|  |  |  | 				kb.pauseRepeat() | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 				return false } | 
					
						
							|  |  |  | 			return true }) } | 
					
						
							| 
									
										
										
										
											2017-03-08 21:07:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 23:45:41 +03: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){ | 
					
						
							| 
									
										
										
										
											2019-06-18 20:58:40 +03:00
										 |  |  | 		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){ | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +03:00
										 |  |  | 			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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 21:11:19 +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 }) |