mirror of
				https://github.com/flynx/PortableMag.git
				synced 2025-10-31 12:00:11 +00:00 
			
		
		
		
	started project revival -- updated keyboard.js and split out the bindings...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									2b8081ce9d
								
							
						
					
					
						commit
						95301411e4
					
				
							
								
								
									
										141
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								index.html
									
									
									
									
									
								
							| @ -110,6 +110,9 @@ CKEDITOR.disableAutoInline = true | |||||||
| 
 | 
 | ||||||
| <script src="platform.js"></script> | <script src="platform.js"></script> | ||||||
| 
 | 
 | ||||||
|  | <!-- XXX load this as early as possible... --> | ||||||
|  | <script src="keybindings.js"></script> | ||||||
|  | 
 | ||||||
| <script> | <script> | ||||||
| 
 | 
 | ||||||
| function showInOverlay(text){ | function showInOverlay(text){ | ||||||
| @ -237,144 +240,6 @@ function handleFileSelect(evt) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| var KEYBOARD_CONFIG = { |  | ||||||
| 	'.overlay': { |  | ||||||
| 		title: 'Overlay mode.', |  | ||||||
| 		doc: '', |  | ||||||
| 
 |  | ||||||
| 		ignore: '*', |  | ||||||
| 
 |  | ||||||
| 		Esc: function(){ |  | ||||||
| 				removeOverlay() |  | ||||||
| 				return false |  | ||||||
| 			}, |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	'.editor:not(.inline-editor-mode)': { |  | ||||||
| 		title: 'Editor mode.', |  | ||||||
| 		doc: '', |  | ||||||
| 
 |  | ||||||
| 		'0': function(){ |  | ||||||
| 				var n = getPageNumber() |  | ||||||
| 				if(togglePageView('?') == 'on'){ |  | ||||||
| 					setMagazineScale(getPageTargetScale(1)) |  | ||||||
| 				} else { |  | ||||||
| 					setMagazineScale(getPageTargetScale(PAGES_IN_RIBBON)) |  | ||||||
| 				} |  | ||||||
| 				setCurrentPage(n) |  | ||||||
| 			}, |  | ||||||
| 		Esc: '0', |  | ||||||
| 
 |  | ||||||
| 		'=': function(){ |  | ||||||
| 				var n = getPageNumber() |  | ||||||
| 				setMagazineScale(Math.min( |  | ||||||
| 						getMagazineScale() * 1.2,  |  | ||||||
| 						getPageTargetScale(1))) |  | ||||||
| 				setCurrentPage(n) |  | ||||||
| 			}, |  | ||||||
| 		'-': function(){ |  | ||||||
| 				var n = getPageNumber() |  | ||||||
| 				setMagazineScale(Math.max( |  | ||||||
| 						getMagazineScale() * 0.8,  |  | ||||||
| 						getPageTargetScale(PAGES_IN_RIBBON*2))) |  | ||||||
| 				setCurrentPage(n) |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 		'O': { |  | ||||||
| 				// load... |  | ||||||
| 				// XXX needs testing... |  | ||||||
| 				'ctrl': function(){ |  | ||||||
| 					showInOverlay('<h1>Open Issue</h1>'+ |  | ||||||
| 						'<input type="file" id="upload" name="file" multiple onchange="handleFileSelect(event)"/>') |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		'S': { |  | ||||||
| 				// save... |  | ||||||
| 				// XXX needs testing... |  | ||||||
| 				'ctrl': function(){ |  | ||||||
| 					showInOverlay('<h1>Save Issue</h1>'+ |  | ||||||
| 						'<p>NOTE: this download will not include the actual '+ |  | ||||||
| 						'images. at this point, images should be added manually.</p>'+ |  | ||||||
| 						'<p><a id="data_download" href="#">Download</a></p>') |  | ||||||
| 
 |  | ||||||
| 					// setup the data... |  | ||||||
| 					$(generateMagazineDownload) |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 		// ? |  | ||||||
| 		'/': function(){ |  | ||||||
| 				showInOverlay('<h1>Controls</h1>'+ |  | ||||||
| 					'<p>NOTE: this section is a stub.<p>'+ |  | ||||||
| 					'<table width="100%">'+ |  | ||||||
| 						'<tr><td align="right" width="45%"><b> C-O </b></td><td> Load issue from file. </td></tr>'+ |  | ||||||
| 						'<tr><td align="right"><b> C-S </b></td><td> Save issue to file. </td></tr>'+ |  | ||||||
| 						'<tr><td align="right"><b> - / + </b></td><td> Zoom out/in. </td></tr>'+ |  | ||||||
| 						'<tr><td align="right"><b> 0 </b></td><td> Set default zoom level. </td></tr>'+ |  | ||||||
| 					'</table>') |  | ||||||
| 			}, |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	// ignore all keys except Esc here... |  | ||||||
| 	'.inline-editor-mode': { |  | ||||||
| 		title: 'Inline editor mode.', |  | ||||||
| 		doc: '', |  | ||||||
| 
 |  | ||||||
| 		//ignore: '*' |  | ||||||
| 		Esc: function(){ |  | ||||||
| 				$(':focus').blur() |  | ||||||
| 				return false |  | ||||||
| 			}, |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	'.chrome:not(.inline-editor-mode)': { |  | ||||||
| 		title: 'Global bindings.', |  | ||||||
| 		doc: '', |  | ||||||
| 
 |  | ||||||
| 		Esc: function(){ |  | ||||||
| 				if(toggleEditor('?') == 'on'){ |  | ||||||
| 					toggleEditor('off') |  | ||||||
| 				} else { |  | ||||||
| 					togglePageView('off') |  | ||||||
| 				} |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 		Home: firstPage, |  | ||||||
| 		End: lastPage,  |  | ||||||
| 		Left: { |  | ||||||
| 				default: function(){ prevPage() }, |  | ||||||
| 				shift: prevBookmark, |  | ||||||
| 				ctrl: prevArticle, |  | ||||||
| 			}, |  | ||||||
| 		Right: { |  | ||||||
| 				default: function(){ nextPage() }, |  | ||||||
| 				shift: nextBookmark, |  | ||||||
| 				ctrl: nextArticle, |  | ||||||
| 			}, |  | ||||||
| 		Space:	{ |  | ||||||
| 				default: 'Right', |  | ||||||
| 				shift: 'Left' |  | ||||||
| 			}, |  | ||||||
| 		//Tab: 'Space', |  | ||||||
| 		Tab: function(){ return false }, |  | ||||||
| 		Enter: function(){ togglePageView('on') }, |  | ||||||
| 		// combined navigation with actions.. |  | ||||||
| 		Up: function(){ togglePageView() }, |  | ||||||
| 		Down: function(){ togglePageView() }, |  | ||||||
| 
 |  | ||||||
| 		F: function(){ togglePageFitMode() }, |  | ||||||
| 		B: { |  | ||||||
| 				default: function(){ toggleBookmark() }, |  | ||||||
| 				ctrl: function(){ toggleThemes() }, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 		// XXX this should not be in the production viewer... |  | ||||||
| 		E: function(){ toggleEditor() }, |  | ||||||
| 	}, |  | ||||||
| }  |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| $(document).ready(function(){ | $(document).ready(function(){ | ||||||
| 
 | 
 | ||||||
| 	// this is to fix some sort of bug baking things align in a wrong  | 	// this is to fix some sort of bug baking things align in a wrong  | ||||||
|  | |||||||
							
								
								
									
										223
									
								
								keybindings.js
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								keybindings.js
									
									
									
									
									
								
							| @ -1,94 +1,173 @@ | |||||||
| /*********************************************************************/ | /*********************************************************************/ | ||||||
| // NOTE: use String.fromCharCode(code)...
 |  | ||||||
| // list of keys to be ignored by handler but still handled by the browser...
 |  | ||||||
| 
 | 
 | ||||||
| var keybindings = { | var KEYBOARD_CONFIG = { | ||||||
| 	/* | 	'Global bindings': { | ||||||
| 	// global bindings...
 | 		doc: 'NOTE: binding priority is the same as the order of sections '+ | ||||||
| 	'*': { | 			'on this page.', | ||||||
| 		title: 'Global', | 		pattern: '*', | ||||||
|  | 
 | ||||||
|  | 		F4: { | ||||||
|  | 			alt: doc('Close viewer',  | ||||||
|  | 				function(){  | ||||||
|  | 					if(window.require != null){ | ||||||
|  | 						require('nw.gui') | ||||||
|  | 							.Window.get().close() | ||||||
|  | 						return false | ||||||
|  | 					} | ||||||
|  | 				}), | ||||||
|  | 		}, | ||||||
|  | 		F5: doc('Full reload viewer',  | ||||||
|  | 			function(){  | ||||||
|  | 				if(window.require != null){ | ||||||
|  | 					require('nw.gui') | ||||||
|  | 						.Window.get().reload() | ||||||
|  | 					return false | ||||||
|  | 				} | ||||||
|  | 			}), | ||||||
|  | 		F12: doc('Show devTools',  | ||||||
|  | 			function(){  | ||||||
|  | 				if(window.require != null){ | ||||||
|  | 					require('nw.gui') | ||||||
|  | 						.Window.get().showDevTools() | ||||||
|  | 					return false | ||||||
|  | 				} | ||||||
|  | 			}), | ||||||
|  | 	},	 | ||||||
|  | 
 | ||||||
|  | 	'.overlay': { | ||||||
|  | 		title: 'Overlay mode.', | ||||||
| 		doc: '', | 		doc: '', | ||||||
| 
 | 
 | ||||||
| 		ignore: [ | 		ignore: '*', | ||||||
| 			116,												//	F5
 |  | ||||||
| 			122,												//	F11
 |  | ||||||
| 			123,												//	F12
 |  | ||||||
| 			8													//	BkSp
 |  | ||||||
| 		], |  | ||||||
| 
 | 
 | ||||||
| 		// ignore the modifiers (shift, alt, ctrl, caps)...
 | 		Esc: function(){ | ||||||
| 		16:		function(){}, | 				removeOverlay() | ||||||
| 		17:		16, | 				return false | ||||||
| 		18:		16, | 			}, | ||||||
| 		20:		16,												//	Caps Lock
 |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | 	'.editor:not(.inline-editor-mode)': { | ||||||
|  | 		title: 'Editor mode.', | ||||||
|  | 		doc: '', | ||||||
| 
 | 
 | ||||||
| 	// overlay...
 | 		'0': function(){ | ||||||
| 	'.overlay-mode': { | 				var n = getPageNumber() | ||||||
| 		title: 'Overlay mode', | 				if(togglePageView('?') == 'on'){ | ||||||
| 		doc: 'Overlay mode key bindings.', | 					setMagazineScale(getPageTargetScale(1)) | ||||||
| 
 | 				} else { | ||||||
| 		ignore: [ | 					setMagazineScale(getPageTargetScale(PAGES_IN_RIBBON)) | ||||||
| 			33,													//	PgUp
 | 				} | ||||||
| 			34,													//	PgDown
 | 				setCurrentPage(n) | ||||||
| 			37,													//	Left
 |  | ||||||
| 			39,													//	Right
 |  | ||||||
| 			36,													//	Home
 |  | ||||||
| 			32,													//	Space
 |  | ||||||
| 			35,													//	End
 |  | ||||||
| 			38,													//	Up
 |  | ||||||
| 			40,													//	Down
 |  | ||||||
| 		], |  | ||||||
| 			}, | 			}, | ||||||
| 	*/ | 		Esc: '0', | ||||||
| 
 | 
 | ||||||
| 	// ignore all keys here...
 | 		'=': function(){ | ||||||
|  | 				var n = getPageNumber() | ||||||
|  | 				setMagazineScale(Math.min( | ||||||
|  | 						getMagazineScale() * 1.2,  | ||||||
|  | 						getPageTargetScale(1))) | ||||||
|  | 				setCurrentPage(n) | ||||||
|  | 			}, | ||||||
|  | 		'-': function(){ | ||||||
|  | 				var n = getPageNumber() | ||||||
|  | 				setMagazineScale(Math.max( | ||||||
|  | 						getMagazineScale() * 0.8,  | ||||||
|  | 						getPageTargetScale(PAGES_IN_RIBBON*2))) | ||||||
|  | 				setCurrentPage(n) | ||||||
|  | 			}, | ||||||
|  | 
 | ||||||
|  | 		'O': { | ||||||
|  | 				// load...
 | ||||||
|  | 				// XXX needs testing...
 | ||||||
|  | 				'ctrl': function(){ | ||||||
|  | 					showInOverlay('<h1>Open Issue</h1>'+ | ||||||
|  | 						'<input type="file" id="upload" name="file" multiple onchange="handleFileSelect(event)"/>') | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		'S': { | ||||||
|  | 				// save...
 | ||||||
|  | 				// XXX needs testing...
 | ||||||
|  | 				'ctrl': function(){ | ||||||
|  | 					showInOverlay('<h1>Save Issue</h1>'+ | ||||||
|  | 						'<p>NOTE: this download will not include the actual '+ | ||||||
|  | 						'images. at this point, images should be added manually.</p>'+ | ||||||
|  | 						'<p><a id="data_download" href="#">Download</a></p>') | ||||||
|  | 
 | ||||||
|  | 					// setup the data...
 | ||||||
|  | 					$(generateMagazineDownload) | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 
 | ||||||
|  | 		// ?
 | ||||||
|  | 		'/': function(){ | ||||||
|  | 				showInOverlay('<h1>Controls</h1>'+ | ||||||
|  | 					'<p>NOTE: this section is a stub.<p>'+ | ||||||
|  | 					'<table width="100%">'+ | ||||||
|  | 						'<tr><td align="right" width="45%"><b> C-O </b></td><td> Load issue from file. </td></tr>'+ | ||||||
|  | 						'<tr><td align="right"><b> C-S </b></td><td> Save issue to file. </td></tr>'+ | ||||||
|  | 						'<tr><td align="right"><b> - / + </b></td><td> Zoom out/in. </td></tr>'+ | ||||||
|  | 						'<tr><td align="right"><b> 0 </b></td><td> Set default zoom level. </td></tr>'+ | ||||||
|  | 					'</table>') | ||||||
|  | 			}, | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	// ignore all keys except Esc here...
 | ||||||
| 	'.inline-editor-mode': { | 	'.inline-editor-mode': { | ||||||
| 		ignore: '*' | 		title: 'Inline editor mode.', | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	// everything except overlays...
 |  | ||||||
| 	'.viewer:not(.inline-editor-mode)': { |  | ||||||
| 		title: 'Ribbon and Viewer', |  | ||||||
| 		doc: '', | 		doc: '', | ||||||
| 
 | 
 | ||||||
| 		// navigation...
 | 		//ignore: '*'
 | ||||||
| 		36:		goToMagazineCover,								//	Home
 | 		Esc: function(){ | ||||||
| 		219:	36,												//	[
 | 				$(':focus').blur() | ||||||
| 		35:		goToMagazineEnd,								//	End
 | 				return false | ||||||
| 		221:	35,												//	]
 |  | ||||||
| 		37:	{ |  | ||||||
| 			'default': prevPage,								//	Right
 |  | ||||||
| 			'ctrl': prevArticle,								//	ctrl-Right
 |  | ||||||
| 			'shift': prevBookmark								//	shift-Right
 |  | ||||||
| 			}, | 			}, | ||||||
| 		188:	37,												//	<
 |  | ||||||
| 		39:	{ |  | ||||||
| 			'default': nextPage,								//	Left
 |  | ||||||
| 			'ctrl': nextArticle,								//	ctrl-Left
 |  | ||||||
| 			'shift': nextBookmark								//	shift-Left
 |  | ||||||
| 		}, |  | ||||||
| 		32:	{ |  | ||||||
| 			'default': nextPage,								//	Space
 |  | ||||||
| 			'shift': prevPage									//	shift-Space
 |  | ||||||
| 		}, |  | ||||||
| 		190:	39,												//	>
 |  | ||||||
| 
 |  | ||||||
| 		66: { |  | ||||||
| 			'default': toggleBookmark,							//	B
 |  | ||||||
| 			'ctrl': function(){toggleThemes()},					//	ctrl-B
 |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | 	'.chrome:not(.inline-editor-mode)': { | ||||||
|  | 		title: 'Global bindings.', | ||||||
|  | 		doc: '', | ||||||
|  | 
 | ||||||
|  | 		Esc: function(){ | ||||||
|  | 				if(toggleEditor('?') == 'on'){ | ||||||
|  | 					toggleEditor('off') | ||||||
|  | 				} else { | ||||||
|  | 					togglePageView('off') | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 
 | ||||||
|  | 		Home: firstPage, | ||||||
|  | 		End: lastPage,  | ||||||
|  | 		Left: { | ||||||
|  | 				default: function(){ prevPage() }, | ||||||
|  | 				shift: prevBookmark, | ||||||
|  | 				ctrl: prevArticle, | ||||||
|  | 			}, | ||||||
|  | 		Right: { | ||||||
|  | 				default: function(){ nextPage() }, | ||||||
|  | 				shift: nextBookmark, | ||||||
|  | 				ctrl: nextArticle, | ||||||
|  | 			}, | ||||||
|  | 		Space:	{ | ||||||
|  | 				default: 'Right', | ||||||
|  | 				shift: 'Left' | ||||||
|  | 			}, | ||||||
|  | 		//Tab: 'Space',
 | ||||||
|  | 		Tab: function(){ return false }, | ||||||
|  | 		Enter: function(){ togglePageView('on') }, | ||||||
| 		// combined navigation with actions..
 | 		// combined navigation with actions..
 | ||||||
| 		38: function(){togglePageView()},						//	Up
 | 		Up: function(){ togglePageView() }, | ||||||
| 		40: function(){togglePageView()},						//	Down
 | 		Down: function(){ togglePageView() }, | ||||||
| 
 | 
 | ||||||
| 		13:	function(){togglePageView('on')},					//	Enter
 | 		F: function(){ togglePageFitMode() }, | ||||||
| 		27:	function(){togglePageView('off')},					//	Esc
 | 		B: { | ||||||
| 	} | 				default: function(){ toggleBookmark() }, | ||||||
| } | 				ctrl: function(){ toggleThemes() }, | ||||||
|  | 			}, | ||||||
| 
 | 
 | ||||||
|  | 		// XXX this should not be in the production viewer...
 | ||||||
|  | 		E: function(){ toggleEditor() }, | ||||||
|  | 	}, | ||||||
|  | }  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /*********************************************************************/ | /*********************************************************************/ | ||||||
|  | |||||||
							
								
								
									
										278
									
								
								lib/keyboard.js
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								lib/keyboard.js
									
									
									
									
									
								
							| @ -10,6 +10,17 @@ | |||||||
| 
 | 
 | ||||||
| /*********************************************************************/ | /*********************************************************************/ | ||||||
| 
 | 
 | ||||||
|  | // Attributes to be ignored my the key handler...
 | ||||||
|  | //
 | ||||||
|  | // These are used for system tasks.
 | ||||||
|  | var KEYBOARD_SYSTEM_ATTRS = [ | ||||||
|  | 	'doc', | ||||||
|  | 	'title', | ||||||
|  | 	'ignore', | ||||||
|  | 	'pattern' | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| // Neither _SPECIAL_KEYS nor _KEY_CODES are meant for direct access, use
 | // Neither _SPECIAL_KEYS nor _KEY_CODES are meant for direct access, use
 | ||||||
| // toKeyName(<code>) and toKeyCode(<name>) for a more uniform access.
 | // toKeyName(<code>) and toKeyCode(<name>) for a more uniform access.
 | ||||||
| //
 | //
 | ||||||
| @ -109,22 +120,49 @@ function toKeyCode(c){ | |||||||
| 
 | 
 | ||||||
| // documentation wrapper...
 | // documentation wrapper...
 | ||||||
| function doc(text, func){ | function doc(text, func){ | ||||||
| 	func = func == null ? function(){return true}: func | 	func = !func ? function(){return true}: func | ||||||
| 	func.doc = text | 	func.doc = text | ||||||
| 	return func | 	return func | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // Build or normalize a modifier string. 
 | ||||||
|  | //
 | ||||||
|  | // Acceptable argument sets:
 | ||||||
|  | // 	- none				-> ""
 | ||||||
|  | // 	- true, false, true	-> "ctrl+shift"
 | ||||||
|  | // 	- true, false		-> "ctrl"
 | ||||||
|  | // 	- [true, false]		-> "ctrl"
 | ||||||
|  | // 	- 'alt+shift'		-> "alt+shift"
 | ||||||
|  | // 	- 'shift - alt'		-> "alt+shift"
 | ||||||
|  | //
 | ||||||
|  | function normalizeModifiers(c, a, s){ | ||||||
|  | 		if(c != null && c.constructor.name == 'Array'){ | ||||||
|  | 			a = c[1] | ||||||
|  | 			s = c[2] | ||||||
|  | 			c = c[0] | ||||||
|  | 		} | ||||||
|  | 		if(typeof(c) == typeof('str')){ | ||||||
|  | 			var modifiers = c | ||||||
|  | 		} else { | ||||||
|  | 			var modifiers = (c ? 'ctrl' : '')  | ||||||
|  | 				+ (a ? ' alt' : '')  | ||||||
|  | 				+ (s ? ' shift' : '') | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// build the dormalized modifier string...
 | ||||||
|  | 		var res = /ctrl/i.test(modifiers) ? 'ctrl' : '' | ||||||
|  | 		res += /alt/i.test(modifiers) ? (res != '' ? '+alt' : 'alt') : '' | ||||||
|  | 		res += /shift/i.test(modifiers) ? (res != '' ? '+shift' : 'shift') : '' | ||||||
|  | 
 | ||||||
|  | 		return res | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Key handler getter | /* Key handler getter | ||||||
|  * |  * | ||||||
|  * For doc on format see makeKeyboardHandler(...) |  * For doc on format see makeKeyboardHandler(...) | ||||||
|  * |  * | ||||||
|  * modes can be: |  | ||||||
|  * 	- 'any'	(default)	- Get list of all applicable handlers up until |  | ||||||
|  * 							the first applicable ignore. |  | ||||||
|  * 	- 'all'				- Get ALL handlers, including ignores |  | ||||||
|  * 	- <mode>			- Get handlers for an explicit mode |  | ||||||
|  * |  | ||||||
|  * modifiers can be: |  * modifiers can be: | ||||||
|  * 	- '' (default)		- No modifiers |  * 	- '' (default)		- No modifiers | ||||||
|  * 	- '?'				- Return list of applicable modifiers per mode |  * 	- '?'				- Return list of applicable modifiers per mode | ||||||
| @ -135,6 +173,15 @@ function doc(text, func){ | |||||||
|  * 							Ex: |  * 							Ex: | ||||||
|  * 								'ctrl+shift' |  * 								'ctrl+shift' | ||||||
|  * 							NOTE: 'shift+ctrl' is wrong. |  * 							NOTE: 'shift+ctrl' is wrong. | ||||||
|  |  * 							NOTE: normalizeModifiers(...) can be used as | ||||||
|  |  * 								a reference, if in doubt. | ||||||
|  |  * | ||||||
|  |  * modes can be: | ||||||
|  |  * 	- 'any'	(default)	- Get list of all applicable handlers up until | ||||||
|  |  * 							the first applicable ignore. | ||||||
|  |  * 	- 'all'				- Get ALL handlers, including ignores | ||||||
|  |  * 	- <mode>			- Get handlers for an explicit mode | ||||||
|  |  * | ||||||
|  * |  * | ||||||
|  * This will also resolve several shifted keys by name, for example: |  * This will also resolve several shifted keys by name, for example: | ||||||
|  * 'shift-/' is the same as '?', and either can be used, but the shorter  |  * 'shift-/' is the same as '?', and either can be used, but the shorter  | ||||||
| @ -148,24 +195,41 @@ function doc(text, func){ | |||||||
|  * 	} |  * 	} | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  |  * <handler> can be: | ||||||
|  |  * 	- <function>		- handler | ||||||
|  |  * 	- [<doc>, <function>] | ||||||
|  |  * 						- lisp-style handler | ||||||
|  |  * 	- 'IGNORE'			- if mode is 'all' and key is in .ignore | ||||||
|  |  * 	- [<function>, 'IGNORE NEXT'] | ||||||
|  |  * 						- if mode is 'all' and the key is both in .ignore | ||||||
|  |  * 						  and a handler is defined in the current section | ||||||
|  |  * 						  NOTE: in this case if this mode matches, all | ||||||
|  |  * 						  		the subsequent handlers will get ignored | ||||||
|  |  * 						  		in normal modes... | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * NOTE: adding a key to the ignore list has the same effect as returning | ||||||
|  |  * 		false form it's handler in the same context. | ||||||
|  * NOTE: it is not possible to do a shift-? as it is already shifted. |  * NOTE: it is not possible to do a shift-? as it is already shifted. | ||||||
|  * NOTE: if a key is not handled in a mode, that mode will not be  |  * NOTE: if a key is not handled in a mode, that mode will not be  | ||||||
|  * 		present in the resulting object. |  * 		present in the resulting object. | ||||||
|  * NOTE: this will not unwrap lisp-style (see below) handlers. |  * NOTE: this will not unwrap lisp-style (see below) handlers. | ||||||
|  * NOTE: modes are prioritized by order of occurrence. |  * NOTE: modes are prioritized by order of occurrence. | ||||||
|  |  * NOTE: modifiers can be a list of three bools... | ||||||
|  |  * 		(see: normalizeModifiers(...) for further information) | ||||||
|  * |  * | ||||||
|  * XXX need an explicit way to prioritize modes, avoiding object attr  |  | ||||||
|  * 		ordering... |  | ||||||
|  * XXX check do we need did_handling here... |  * XXX check do we need did_handling here... | ||||||
|  * |  | ||||||
|  * XXX BUG explicitly given modes do not yield results if the pattern  |  * XXX BUG explicitly given modes do not yield results if the pattern  | ||||||
|  * 		does not match... |  * 		does not match... | ||||||
|  */ |  */ | ||||||
| function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | ||||||
| 	var chr = null | 	var chr = null | ||||||
| 	var s_chr = null | 	var s_chr = null | ||||||
|  | 	// XXX I do not understand why this is here...
 | ||||||
| 	var did_handling = false | 	var did_handling = false | ||||||
|  | 	var did_ignore = false | ||||||
| 	modifiers = modifiers == null ? '' : modifiers | 	modifiers = modifiers == null ? '' : modifiers | ||||||
|  | 	modifiers = modifiers != '?' ? normalizeModifiers(modifiers) : modifiers | ||||||
| 	modes = modes == null ? 'any' : modes | 	modes = modes == null ? 'any' : modes | ||||||
| 	shifted_keys = shifted_keys == null ? _SHIFT_KEYS : shifted_keys | 	shifted_keys = shifted_keys == null ? _SHIFT_KEYS : shifted_keys | ||||||
| 
 | 
 | ||||||
| @ -184,18 +248,41 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
| 
 | 
 | ||||||
| 	res = {} | 	res = {} | ||||||
| 
 | 
 | ||||||
| 	for(var mode in keybindings){ | 	for(var title in keybindings){ | ||||||
| 
 | 
 | ||||||
| 		// test for mode compatibility...
 | 		// If a key is ignored then look no further...
 | ||||||
| 		// XXX this fails for explicitly given mode...
 | 		if(did_ignore){ | ||||||
| 		if(modes != 'all'  | 			if(modes != 'all'){ | ||||||
| 				&& (modes != 'any'  | 				break | ||||||
| 					&& modes != mode  | 			} else { | ||||||
| 					|| $(mode).length == 0)){ | 				did_ignore = false | ||||||
|  | 				if(modifiers != '?' && res[mode] != 'IGNORE'){ | ||||||
|  | 					res[mode] = [ res[mode], 'IGNORE NEXT'] | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// older version compatibility...
 | ||||||
|  | 		if(keybindings[title].pattern != null){ | ||||||
|  | 			var mode = keybindings[title].pattern | ||||||
|  | 		} else { | ||||||
|  | 			var mode = title | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// check if we need to skip this mode...
 | ||||||
|  | 		if( !(modes == 'all' | ||||||
|  | 			// explicit mode match...
 | ||||||
|  | 			|| modes == mode | ||||||
|  | 			// 'any' means we need to check the mode...
 | ||||||
|  | 			|| (modes == 'any' | ||||||
|  | 				// '*' always matches...
 | ||||||
|  | 				&& mode == '*' | ||||||
|  | 				// match the mode...
 | ||||||
|  | 				|| $(mode).length != 0))){ | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		var bindings = keybindings[mode] | 		var bindings = keybindings[title] | ||||||
| 
 | 
 | ||||||
| 		if(s_chr != null && s_chr in bindings){ | 		if(s_chr != null && s_chr in bindings){ | ||||||
| 			var handler = bindings[s_chr] | 			var handler = bindings[s_chr] | ||||||
| @ -208,6 +295,7 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// alias...
 | 		// alias...
 | ||||||
|  | 		// XXX should this be before after or combined with ignore handling...
 | ||||||
| 		while( handler != null  | 		while( handler != null  | ||||||
| 				&& (typeof(handler) == typeof(123)  | 				&& (typeof(handler) == typeof(123)  | ||||||
| 					|| typeof(handler) == typeof('str') | 					|| typeof(handler) == typeof('str') | ||||||
| @ -244,8 +332,6 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// no handler...
 |  | ||||||
| 		if(handler == null){ |  | ||||||
| 		// if something is ignored then just breakout and stop handling...
 | 		// if something is ignored then just breakout and stop handling...
 | ||||||
| 		if(bindings.ignore == '*'  | 		if(bindings.ignore == '*'  | ||||||
| 				|| bindings.ignore != null  | 				|| bindings.ignore != null  | ||||||
| @ -254,11 +340,16 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
| 			did_handling = true | 			did_handling = true | ||||||
| 			// ignoring a key will stop processing it...
 | 			// ignoring a key will stop processing it...
 | ||||||
| 			if(modes == 'all' || mode == modes){ | 			if(modes == 'all' || mode == modes){ | ||||||
|  | 				// NOTE: if a handler is defined in this section, this 
 | ||||||
|  | 				// 		will be overwritten...
 | ||||||
|  | 				// XXX need to add the handler to this if it's defined...
 | ||||||
| 				res[mode] = 'IGNORE' | 				res[mode] = 'IGNORE' | ||||||
| 				} else { |  | ||||||
| 					break |  | ||||||
| 			} | 			} | ||||||
|  | 			did_ignore = true | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		// no handler...
 | ||||||
|  | 		if(handler == null){ | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -308,10 +399,9 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
| /* Basic key binding format: | /* Basic key binding format: | ||||||
|  * |  * | ||||||
|  * { |  * { | ||||||
|  * 		<css-selector>: { |  * 		<title>: { | ||||||
|  *			// meta-data used to generate user docs/help/config
 |  | ||||||
|  * 			title: <text>, |  | ||||||
|  * 			doc: <text>, |  * 			doc: <text>, | ||||||
|  |  * 			pattern: <css-selector>, | ||||||
|  * |  * | ||||||
|  *			// this defines the list of keys to ignore by the handler.
 |  *			// this defines the list of keys to ignore by the handler.
 | ||||||
|  *			// NOTE: use "*" to ignore all keys other than explicitly 
 |  *			// NOTE: use "*" to ignore all keys other than explicitly 
 | ||||||
| @ -338,12 +428,14 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
|  * 				default: <callback> | <key-def-x>, |  * 				default: <callback> | <key-def-x>, | ||||||
|  * |  * | ||||||
|  *				// a modifier can be any single modifier, like shift or a 
 |  *				// a modifier can be any single modifier, like shift or a 
 | ||||||
|  *				// combination of modifiers like 'ctrl+shift', given in order 
 |  *				// combination of modifiers like 'ctrl+shift', in order 
 | ||||||
|  *				// of priority.
 |  *				// of priority.
 | ||||||
|  *				// supported modifiers are (in order of priority):
 |  *				// supported modifiers, ordered by priority, are:
 | ||||||
|  *				//	- ctrl
 |  *				//	- ctrl
 | ||||||
|  *				//	- alt
 |  *				//	- alt
 | ||||||
|  *				//	- shift
 |  *				//	- shift
 | ||||||
|  |  *				// NOTE: if in doubt use normalizeModifiers(..) as a 
 | ||||||
|  |  *				//		reference...
 | ||||||
|  * 				<modifer>: [...], |  * 				<modifer>: [...], | ||||||
|  * 				... |  * 				... | ||||||
|  * 			}, |  * 			}, | ||||||
| @ -354,6 +446,13 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
|  *			... |  *			... | ||||||
|  * 		}, |  * 		}, | ||||||
|  * |  * | ||||||
|  |  *		// legacy format, still supported... (deprecated)
 | ||||||
|  |  * 		<css-selector>: { | ||||||
|  |  *			// meta-data used to generate user docs/help/config
 | ||||||
|  |  * 			title: <text>, | ||||||
|  |  * 			... | ||||||
|  |  * 		}, | ||||||
|  |  * | ||||||
|  * 		... |  * 		... | ||||||
|  * } |  * } | ||||||
|  * |  * | ||||||
| @ -365,6 +464,8 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
|  * 	- action -- any arbitrary string that is not in the above categories. |  * 	- action -- any arbitrary string that is not in the above categories. | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  |  * NOTE: adding a key to the ignore list has the same effect as returning | ||||||
|  |  * 		false form it's handler in the same context. | ||||||
|  * NOTE: actions,the last case, are used for alias referencing, they will |  * NOTE: actions,the last case, are used for alias referencing, they will | ||||||
|  * 		never match a real key, but will get resolved in alias searches. |  * 		never match a real key, but will get resolved in alias searches. | ||||||
|  * NOTE: to test what to use as <key-def> use toKeyCode(..) / toKeyName(..). |  * NOTE: to test what to use as <key-def> use toKeyCode(..) / toKeyName(..). | ||||||
| @ -380,8 +481,8 @@ function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ | |||||||
|  * NOTE: the number keys are named with a leading hash '#' (e.g. '#8')  |  * NOTE: the number keys are named with a leading hash '#' (e.g. '#8')  | ||||||
|  * 		to avoid conflicsts with keys that have the code with the same  |  * 		to avoid conflicsts with keys that have the code with the same  | ||||||
|  * 		value (e.g. 'backspace' (8)). |  * 		value (e.g. 'backspace' (8)). | ||||||
|  * NOTE: one can use a doc(<doc-string>, <callback>) as a shorthand to assign |  * NOTE: one can use a doc(<doc-string>, <callback>) as a shorthand to  | ||||||
|  * 		a docstring to a handler. |  * 		assign a docstring to a handler. | ||||||
|  * 		it will only assign .doc attr and return the original function. |  * 		it will only assign .doc attr and return the original function. | ||||||
|  * |  * | ||||||
|  * XXX need an explicit way to prioritize modes... |  * XXX need an explicit way to prioritize modes... | ||||||
| @ -398,10 +499,8 @@ function makeKeyboardHandler(keybindings, unhandled){ | |||||||
| 		// key data...
 | 		// key data...
 | ||||||
| 		var key = evt.keyCode | 		var key = evt.keyCode | ||||||
| 
 | 
 | ||||||
| 		// normalize the modifiers...
 | 		// get modifiers...
 | ||||||
| 		var modifiers = evt.ctrlKey ? 'ctrl' : '' | 		var modifiers = [evt.ctrlKey, evt.altKey, evt.shiftKey] | ||||||
| 		modifiers += evt.altKey ? (modifiers != '' ? '+alt' : 'alt') : '' |  | ||||||
| 		modifiers += evt.shiftKey ? (modifiers != '' ? '+shift' : 'shift') : '' |  | ||||||
| 
 | 
 | ||||||
| 		//window.DEBUG && console.log('KEY:', key, chr, modifiers)
 | 		//window.DEBUG && console.log('KEY:', key, chr, modifiers)
 | ||||||
| 
 | 
 | ||||||
| @ -450,16 +549,25 @@ function makeKeyboardHandler(keybindings, unhandled){ | |||||||
| * | * | ||||||
| * NOTE: this will not add keys (key names) that are not explicit key names. | * NOTE: this will not add keys (key names) that are not explicit key names. | ||||||
| */ | */ | ||||||
|  | // XXX do we need to normalize/pre-process keybindings???
 | ||||||
|  | // 		- might be a good idea to normalize the <modifiers>...
 | ||||||
| function buildKeybindingsHelp(keybindings, shifted_keys){ | function buildKeybindingsHelp(keybindings, shifted_keys){ | ||||||
| 	shifted_keys = shifted_keys == null ? _SHIFT_KEYS : shifted_keys | 	shifted_keys = shifted_keys == null ? _SHIFT_KEYS : shifted_keys | ||||||
| 	var res = {} | 	var res = {} | ||||||
| 	var mode, title | 	var mode, title | ||||||
| 
 | 
 | ||||||
| 	for(var pattern in keybindings){ | 	for(var title in keybindings){ | ||||||
| 		mode = keybindings[pattern] | 		mode = keybindings[title] | ||||||
| 
 | 
 | ||||||
|  | 		// older version compatibility...
 | ||||||
|  | 		if(keybindings[title].pattern != null){ | ||||||
|  | 			var pattern = keybindings[title].pattern | ||||||
|  | 		} else { | ||||||
|  | 			var pattern = title | ||||||
| 			// titles and docs...
 | 			// titles and docs...
 | ||||||
| 		title = mode.title == null ? pattern : mode.title | 			var title = mode.title == null ? pattern : mode.title | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		res[title] = { | 		res[title] = { | ||||||
| 			doc: mode.doc == null ? '' : mode.doc | 			doc: mode.doc == null ? '' : mode.doc | ||||||
| 		} | 		} | ||||||
| @ -467,25 +575,27 @@ function buildKeybindingsHelp(keybindings, shifted_keys){ | |||||||
| 
 | 
 | ||||||
| 		// handlers...
 | 		// handlers...
 | ||||||
| 		for(var key in mode){ | 		for(var key in mode){ | ||||||
| 			if(key == 'doc' || key == 'title' || key == 'ignore'){ | 			if(KEYBOARD_SYSTEM_ATTRS.indexOf(key) >= 0){ | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			//var modifiers = getKeyHandlers(key, '?', keybindings, pattern)[pattern]
 |  | ||||||
| 			var modifiers = getKeyHandlers(key, '?', keybindings, 'all')[pattern] | 			var modifiers = getKeyHandlers(key, '?', keybindings, 'all')[pattern] | ||||||
| 			modifiers = modifiers == 'none' || modifiers == undefined ? [''] : modifiers | 			modifiers = modifiers == 'none' || modifiers == undefined ? [''] : modifiers | ||||||
| 
 | 
 | ||||||
| 			for(var i=0; i < modifiers.length; i++){ | 			for(var i=0; i < modifiers.length; i++){ | ||||||
| 				var mod = modifiers[i] | 				var mod = modifiers[i] | ||||||
| 
 | 
 | ||||||
| 				//var handler = getKeyHandlers(key, mod, keybindings, pattern)[pattern]
 |  | ||||||
| 				var handler = getKeyHandlers(key, mod, keybindings, 'all')[pattern] | 				var handler = getKeyHandlers(key, mod, keybindings, 'all')[pattern] | ||||||
| 
 | 
 | ||||||
|  | 				if(handler.constructor.name == 'Array' && handler[1] == 'IGNORE NEXT'){ | ||||||
|  | 					handler = handler[0] | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				// standard object doc...
 | 				// standard object doc...
 | ||||||
| 				if('doc' in handler){ | 				if('doc' in handler){ | ||||||
| 					var doc = handler.doc | 					var doc = handler.doc | ||||||
| 
 | 
 | ||||||
| 				// lisp style...
 | 				// lisp style...
 | ||||||
| 				} else if(typeof(handler) == typeof([]) && handler.constructor.name == 'Array'){ | 				} else if(handler.constructor.name == 'Array'){ | ||||||
| 					var doc = handler[1] | 					var doc = handler[1] | ||||||
| 
 | 
 | ||||||
| 				// no doc...
 | 				// no doc...
 | ||||||
| @ -528,6 +638,31 @@ function buildKeybindingsHelp(keybindings, shifted_keys){ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // Get a list of keys associated with a given doc...
 | ||||||
|  | //
 | ||||||
|  | // The second argument must be a structure formated as returned by 
 | ||||||
|  | // buildKeybindingsHelp(...)
 | ||||||
|  | //
 | ||||||
|  | // Returned format:
 | ||||||
|  | // 	{
 | ||||||
|  | // 		<section-name> : <key-spec>
 | ||||||
|  | // 		...
 | ||||||
|  | // 	}
 | ||||||
|  | //
 | ||||||
|  | // NOTE: <key-spec> is the same as generated by buildKeybindingsHelp(..)
 | ||||||
|  | function getKeysByDoc(doc, help){ | ||||||
|  | 	var res = {} | ||||||
|  | 	for(var mode in help){ | ||||||
|  | 		var name = mode | ||||||
|  | 		var section = help[mode] | ||||||
|  | 		if(doc in section){ | ||||||
|  | 			res[mode] = section[doc] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| // Build a basic HTML table with keyboard help...
 | // Build a basic HTML table with keyboard help...
 | ||||||
| //
 | //
 | ||||||
| //	The table will look like this:
 | //	The table will look like this:
 | ||||||
| @ -584,6 +719,69 @@ function buildKeybindingsHelpHTML(keybindings){ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // Build HTML for a single key definition...
 | ||||||
|  | //
 | ||||||
|  | // Format if combining sections (default):
 | ||||||
|  | // 		<span class="key-doc">
 | ||||||
|  | // 			<span class="doc"> DOC </span>
 | ||||||
|  | // 			<span class="keys"> KEYS </span>
 | ||||||
|  | // 		</span>
 | ||||||
|  | //
 | ||||||
|  | // Format if not combining sections:
 | ||||||
|  | // 		<span class="key-doc">
 | ||||||
|  | // 			<span class="doc"> DOC </span>
 | ||||||
|  | // 			<span class="section">
 | ||||||
|  | // 				<span class="name"> MODE NAME </span>
 | ||||||
|  | // 				<span class="keys"> KEYS </span>
 | ||||||
|  | // 			</span>
 | ||||||
|  | // 			...
 | ||||||
|  | // 		</span>
 | ||||||
|  | //
 | ||||||
|  | // XXX not yet sure if we are handling the sections correctly...
 | ||||||
|  | function getKeysByDocHTML(doc, help, combine_sections){ | ||||||
|  | 	combine_sections = combine_sections == null ? true : combine_sections | ||||||
|  | 
 | ||||||
|  | 	var spec = getKeysByDoc(doc, help) | ||||||
|  | 	var res = '<span class="key-doc">' | ||||||
|  | 
 | ||||||
|  | 	res += '<span class="doc">'+ doc +'</span>' | ||||||
|  | 	 | ||||||
|  | 	var keys = [] | ||||||
|  | 
 | ||||||
|  | 	for(var section in spec){ | ||||||
|  | 		if(!combine_sections){ | ||||||
|  | 			keys = spec[section].join(', ') | ||||||
|  | 			res += '<span class="section">' | ||||||
|  | 					+'<span class="name">'+ section +'</span>' | ||||||
|  | 					+'<span class="keys">'+ keys +'</span>' | ||||||
|  | 				+'</span>' | ||||||
|  | 		} else { | ||||||
|  | 			keys = keys.concat(spec[section]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if(combine_sections){ | ||||||
|  | 		res += '<span class="keys">'+ keys.join(', ') +'</span>' | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res + '</span>' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Update key definitions...
 | ||||||
|  | //
 | ||||||
|  | // NOTE: this does not support multiple sections...
 | ||||||
|  | function updateHTMLKeyDoc(help, root){ | ||||||
|  | 	root = root == null ? $('body') : root | ||||||
|  | 	return root.find('.key-doc').each(function(i, e){ | ||||||
|  | 		e = $(e) | ||||||
|  | 		var doc = e.find('.doc') | ||||||
|  | 		var keys = $(getKeysByDocHTML(doc.html(), help)).find('.keys') | ||||||
|  | 		e.find('.keys').html(keys.html()) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /********************************************************************** | /********************************************************************** | ||||||
| * Key binding editor... | * Key binding editor... | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user