| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -5,60 +5,48 @@ const frakturExceptions = { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  'I': '\u2111', | 
					 | 
					 | 
					 | 
					  'I': '\u2111', | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  'R': '\u211c', | 
					 | 
					 | 
					 | 
					  'R': '\u211c', | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  'Z': '\u2128' | 
					 | 
					 | 
					 | 
					  'Z': '\u2128' | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					}; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					// constants for decoding the update blob
 | 
					 | 
					 | 
					 | 
					// constants for decoding the update blob
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const SEQ_SET_COLOR_ATTR = 1 | 
					 | 
					 | 
					 | 
					const SEQ_SET_COLOR_ATTR = 1; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const SEQ_REPEAT = 2 | 
					 | 
					 | 
					 | 
					const SEQ_REPEAT = 2; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const SEQ_SET_COLOR = 3 | 
					 | 
					 | 
					 | 
					const SEQ_SET_COLOR = 3; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const SEQ_SET_ATTR = 4 | 
					 | 
					 | 
					 | 
					const SEQ_SET_ATTR = 4; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const SELECTION_BG = '#b2d7fe' | 
					 | 
					 | 
					 | 
					const SELECTION_BG = '#b2d7fe'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const SELECTION_FG = '#333' | 
					 | 
					 | 
					 | 
					const SELECTION_FG = '#333'; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const themes = [ | 
					 | 
					 | 
					 | 
					const themes = [ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  [ // Tango
 | 
					 | 
					 | 
					 | 
					  [ // Tango
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#111213', '#CC0000', '#4E9A06', '#C4A000', '#3465A4', '#75507B', '#06989A', | 
					 | 
					 | 
					 | 
					    '#111213', '#CC0000', '#4E9A06', '#C4A000', '#3465A4', '#75507B', '#06989A', '#D3D7CF', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#D3D7CF', | 
					 | 
					 | 
					 | 
					    '#555753', '#EF2929', '#8AE234', '#FCE94F', '#729FCF', '#AD7FA8', '#34E2E2', '#EEEEEC', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#555753', '#EF2929', '#8AE234', '#FCE94F', '#729FCF', '#AD7FA8', '#34E2E2', | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#EEEEEC' | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ], | 
					 | 
					 | 
					 | 
					  ], | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  [ // Linux
 | 
					 | 
					 | 
					 | 
					  [ // Linux
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#000000', '#aa0000', '#00aa00', '#aa5500', '#0000aa', '#aa00aa', '#00aaaa', | 
					 | 
					 | 
					 | 
					    '#000000', '#aa0000', '#00aa00', '#aa5500', '#0000aa', '#aa00aa', '#00aaaa', '#aaaaaa', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#aaaaaa', | 
					 | 
					 | 
					 | 
					    '#555555', '#ff5555', '#55ff55', '#ffff55', '#5555ff', '#ff55ff', '#55ffff', '#ffffff', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#555555', '#ff5555', '#55ff55', '#ffff55', '#5555ff', '#ff55ff', '#55ffff', | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#ffffff' | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ], | 
					 | 
					 | 
					 | 
					  ], | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  [ // xterm
 | 
					 | 
					 | 
					 | 
					  [ // xterm
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', | 
					 | 
					 | 
					 | 
					    '#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#e5e5e5', | 
					 | 
					 | 
					 | 
					    '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#ffffff' | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ], | 
					 | 
					 | 
					 | 
					  ], | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  [ // rxvt
 | 
					 | 
					 | 
					 | 
					  [ // rxvt
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000cd', '#cd00cd', '#00cdcd', | 
					 | 
					 | 
					 | 
					    '#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000cd', '#cd00cd', '#00cdcd', '#faebd7', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#faebd7', | 
					 | 
					 | 
					 | 
					    '#404040', '#ff0000', '#00ff00', '#ffff00', '#0000ff', '#ff00ff', '#00ffff', '#ffffff', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#404040', '#ff0000', '#00ff00', '#ffff00', '#0000ff', '#ff00ff', '#00ffff', | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#ffffff' | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ], | 
					 | 
					 | 
					 | 
					  ], | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  [ // Ambience
 | 
					 | 
					 | 
					 | 
					  [ // Ambience
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#2e3436', '#cc0000', '#4e9a06', '#c4a000', '#3465a4', '#75507b', '#06989a', | 
					 | 
					 | 
					 | 
					    '#2e3436', '#cc0000', '#4e9a06', '#c4a000', '#3465a4', '#75507b', '#06989a', '#d3d7cf', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#d3d7cf', | 
					 | 
					 | 
					 | 
					    '#555753', '#ef2929', '#8ae234', '#fce94f', '#729fcf', '#ad7fa8', '#34e2e2', '#eeeeec', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#555753', '#ef2929', '#8ae234', '#fce94f', '#729fcf', '#ad7fa8', '#34e2e2', | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#eeeeec' | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ], | 
					 | 
					 | 
					 | 
					  ], | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  [ // Solarized
 | 
					 | 
					 | 
					 | 
					  [ // Solarized
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#073642', '#dc322f', '#859900', '#b58900', '#268bd2', '#d33682', '#2aa198', | 
					 | 
					 | 
					 | 
					    '#073642', '#dc322f', '#859900', '#b58900', '#268bd2', '#d33682', '#2aa198', '#eee8d5', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#eee8d5', | 
					 | 
					 | 
					 | 
					    '#002b36', '#cb4b16', '#586e75', '#657b83', '#839496', '#6c71c4', '#93a1a1', '#fdf6e3', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#002b36', '#cb4b16', '#586e75', '#657b83', '#839496', '#6c71c4', '#93a1a1', | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    '#fdf6e3' | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ] | 
					 | 
					 | 
					 | 
					  ] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					]; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					class TermScreen { | 
					 | 
					 | 
					 | 
					class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  constructor () { | 
					 | 
					 | 
					 | 
					  constructor () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.canvas = document.createElement('canvas') | 
					 | 
					 | 
					 | 
					    this.canvas = document.createElement('canvas'); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.ctx = this.canvas.getContext('2d') | 
					 | 
					 | 
					 | 
					    this.ctx = this.canvas.getContext('2d'); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if ('AudioContext' in window || 'webkitAudioContext' in window) { | 
					 | 
					 | 
					 | 
					    if ('AudioContext' in window || 'webkitAudioContext' in window) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.audioCtx = new (window.AudioContext || window.webkitAudioContext)() | 
					 | 
					 | 
					 | 
					      this.audioCtx = new (window.AudioContext || window.webkitAudioContext)() | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -76,23 +64,22 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      visible: true, | 
					 | 
					 | 
					 | 
					      visible: true, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      hanging: false, | 
					 | 
					 | 
					 | 
					      hanging: false, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      style: 'block', | 
					 | 
					 | 
					 | 
					      style: 'block', | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      blinkInterval: null | 
					 | 
					 | 
					 | 
					      blinkInterval: 0, | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    }; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this._colors = themes[0] | 
					 | 
					 | 
					 | 
					    this._colors = themes[0]; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this._window = { | 
					 | 
					 | 
					 | 
					    this._window = { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      width: 0, | 
					 | 
					 | 
					 | 
					      width: 0, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      height: 0, | 
					 | 
					 | 
					 | 
					      height: 0, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      devicePixelRatio: 1, | 
					 | 
					 | 
					 | 
					      devicePixelRatio: 1, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      fontFamily: '"DejaVu Sans Mono", "Liberation Mono", "Inconsolata", ' + | 
					 | 
					 | 
					 | 
					      fontFamily: '"DejaVu Sans Mono", "Liberation Mono", "Inconsolata", monospace', | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        'monospace', | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      fontSize: 20, | 
					 | 
					 | 
					 | 
					      fontSize: 20, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      gridScaleX: 1.0, | 
					 | 
					 | 
					 | 
					      gridScaleX: 1.0, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      gridScaleY: 1.2, | 
					 | 
					 | 
					 | 
					      gridScaleY: 1.2, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      blinkStyleOn: true, | 
					 | 
					 | 
					 | 
					      blinkStyleOn: true, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      blinkInterval: null | 
					 | 
					 | 
					 | 
					      blinkInterval: null, | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    }; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.windowState = { | 
					 | 
					 | 
					 | 
					    this.windowState = { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      width: 0, | 
					 | 
					 | 
					 | 
					      width: 0, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      height: 0, | 
					 | 
					 | 
					 | 
					      height: 0, | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -100,110 +87,109 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      gridScaleX: 0, | 
					 | 
					 | 
					 | 
					      gridScaleX: 0, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      gridScaleY: 0, | 
					 | 
					 | 
					 | 
					      gridScaleY: 0, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      fontFamily: '', | 
					 | 
					 | 
					 | 
					      fontFamily: '', | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      fontSize: 0 | 
					 | 
					 | 
					 | 
					      fontSize: 0, | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    }; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.selection = { | 
					 | 
					 | 
					 | 
					    this.selection = { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      selectable: true, | 
					 | 
					 | 
					 | 
					      selectable: true, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      start: [0, 0], | 
					 | 
					 | 
					 | 
					      start: [0, 0], | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      end: [0, 0] | 
					 | 
					 | 
					 | 
					      end: [0, 0], | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    }; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.mouseMode = { clicks: false, movement: false } | 
					 | 
					 | 
					 | 
					    this.mouseMode = { clicks: false, movement: false }; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const self = this | 
					 | 
					 | 
					 | 
					    const self = this; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.window = new Proxy(this._window, { | 
					 | 
					 | 
					 | 
					    this.window = new Proxy(this._window, { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      set (target, key, value, receiver) { | 
					 | 
					 | 
					 | 
					      set (target, key, value, receiver) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        target[key] = value | 
					 | 
					 | 
					 | 
					        target[key] = value; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.updateSize() | 
					 | 
					 | 
					 | 
					        self.updateSize(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.scheduleDraw() | 
					 | 
					 | 
					 | 
					        self.scheduleDraw(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return true | 
					 | 
					 | 
					 | 
					        return true | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screen = [] | 
					 | 
					 | 
					 | 
					    this.screen = []; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screenFG = [] | 
					 | 
					 | 
					 | 
					    this.screenFG = []; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screenBG = [] | 
					 | 
					 | 
					 | 
					    this.screenBG = []; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screenAttrs = [] | 
					 | 
					 | 
					 | 
					    this.screenAttrs = []; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.resetBlink() | 
					 | 
					 | 
					 | 
					    this.resetBlink(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.resetCursorBlink() | 
					 | 
					 | 
					 | 
					    this.resetCursorBlink(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let selecting = false | 
					 | 
					 | 
					 | 
					    let selecting = false; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('mousedown', e => { | 
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('mousedown', e => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (this.selection.selectable || e.altKey) { | 
					 | 
					 | 
					 | 
					      if (this.selection.selectable || e.altKey) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        let x = e.offsetX | 
					 | 
					 | 
					 | 
					        let x = e.offsetX; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        let y = e.offsetY | 
					 | 
					 | 
					 | 
					        let y = e.offsetY; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        selecting = true | 
					 | 
					 | 
					 | 
					        selecting = true; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.selection.start = this.selection.end = this.screenToGrid(x, y) | 
					 | 
					 | 
					 | 
					        this.selection.start = this.selection.end = this.screenToGrid(x, y); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.scheduleDraw() | 
					 | 
					 | 
					 | 
					        this.scheduleDraw() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } else { | 
					 | 
					 | 
					 | 
					      } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Input.onMouseDown(...this.screenToGrid(e.offsetX, e.offsetY), | 
					 | 
					 | 
					 | 
					        Input.onMouseDown(...this.screenToGrid(e.offsetX, e.offsetY), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          e.button + 1) | 
					 | 
					 | 
					 | 
					          e.button + 1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    window.addEventListener('mousemove', e => { | 
					 | 
					 | 
					 | 
					    window.addEventListener('mousemove', e => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (selecting) { | 
					 | 
					 | 
					 | 
					      if (selecting) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.selection.end = this.screenToGrid(e.offsetX, e.offsetY) | 
					 | 
					 | 
					 | 
					        this.selection.end = this.screenToGrid(e.offsetX, e.offsetY); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.scheduleDraw() | 
					 | 
					 | 
					 | 
					        this.scheduleDraw() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    window.addEventListener('mouseup', e => { | 
					 | 
					 | 
					 | 
					    window.addEventListener('mouseup', e => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (selecting) { | 
					 | 
					 | 
					 | 
					      if (selecting) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        selecting = false | 
					 | 
					 | 
					 | 
					        selecting = false; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.selection.end = this.screenToGrid(e.offsetX, e.offsetY) | 
					 | 
					 | 
					 | 
					        this.selection.end = this.screenToGrid(e.offsetX, e.offsetY); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.scheduleDraw() | 
					 | 
					 | 
					 | 
					        this.scheduleDraw(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Object.assign(this.selection, this.getNormalizedSelection()) | 
					 | 
					 | 
					 | 
					        Object.assign(this.selection, this.getNormalizedSelection()) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('mousemove', e => { | 
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('mousemove', e => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (!selecting) { | 
					 | 
					 | 
					 | 
					      if (!selecting) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Input.onMouseMove(...this.screenToGrid(e.offsetX, e.offsetY)) | 
					 | 
					 | 
					 | 
					        Input.onMouseMove(...this.screenToGrid(e.offsetX, e.offsetY)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('mouseup', e => { | 
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('mouseup', e => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (!selecting) { | 
					 | 
					 | 
					 | 
					      if (!selecting) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Input.onMouseUp(...this.screenToGrid(e.offsetX, e.offsetY), | 
					 | 
					 | 
					 | 
					        Input.onMouseUp(...this.screenToGrid(e.offsetX, e.offsetY), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          e.button + 1) | 
					 | 
					 | 
					 | 
					          e.button + 1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('wheel', e => { | 
					 | 
					 | 
					 | 
					    this.canvas.addEventListener('wheel', e => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (this.mouseMode.clicks) { | 
					 | 
					 | 
					 | 
					      if (this.mouseMode.clicks) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        Input.onMouseWheel(...this.screenToGrid(e.offsetX, e.offsetY), | 
					 | 
					 | 
					 | 
					        Input.onMouseWheel(...this.screenToGrid(e.offsetX, e.offsetY), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          e.deltaY > 0 ? 1 : -1) | 
					 | 
					 | 
					 | 
					          e.deltaY > 0 ? 1 : -1); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // prevent page scrolling
 | 
					 | 
					 | 
					 | 
					        // prevent page scrolling
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        e.preventDefault() | 
					 | 
					 | 
					 | 
					        e.preventDefault() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // bind ctrl+shift+c to copy
 | 
					 | 
					 | 
					 | 
					    // bind ctrl+shift+c to copy
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    key('⌃+⇧+c', e => { | 
					 | 
					 | 
					 | 
					    key('⌃+⇧+c', e => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      e.preventDefault() | 
					 | 
					 | 
					 | 
					      e.preventDefault(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.copySelectionToClipboard() | 
					 | 
					 | 
					 | 
					      this.copySelectionToClipboard() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }) | 
					 | 
					 | 
					 | 
					    }) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  get colors () { return this._colors } | 
					 | 
					 | 
					 | 
					  get colors () { return this._colors } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  set colors (theme) { | 
					 | 
					 | 
					 | 
					  set colors (theme) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this._colors = theme | 
					 | 
					 | 
					 | 
					    this._colors = theme; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.scheduleDraw() | 
					 | 
					 | 
					 | 
					    this.scheduleDraw() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // schedule a draw in the next tick
 | 
					 | 
					 | 
					 | 
					  // schedule a draw in the next tick
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  scheduleDraw () { | 
					 | 
					 | 
					 | 
					  scheduleDraw () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    clearTimeout(this._scheduledDraw) | 
					 | 
					 | 
					 | 
					    clearTimeout(this._scheduledDraw); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this._scheduledDraw = setTimeout(() => this.draw(), 1) | 
					 | 
					 | 
					 | 
					    this._scheduledDraw = setTimeout(() => this.draw(), 1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  getFont (modifiers = {}) { | 
					 | 
					 | 
					 | 
					  getFont (modifiers = {}) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let fontStyle = modifiers.style || 'normal' | 
					 | 
					 | 
					 | 
					    let fontStyle = modifiers.style || 'normal'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let fontWeight = modifiers.weight || 'normal' | 
					 | 
					 | 
					 | 
					    let fontWeight = modifiers.weight || 'normal'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return `${fontStyle} normal ${fontWeight} ${ | 
					 | 
					 | 
					 | 
					    return `${fontStyle} normal ${fontWeight} ${this.window.fontSize}px ${this.window.fontFamily}` | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.window.fontSize}px ${this.window.fontFamily}` | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  getCharSize () { | 
					 | 
					 | 
					 | 
					  getCharSize () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.ctx.font = this.getFont() | 
					 | 
					 | 
					 | 
					    this.ctx.font = this.getFont(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return { | 
					 | 
					 | 
					 | 
					    return { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      width: this.ctx.measureText(' ').width, | 
					 | 
					 | 
					 | 
					      width: this.ctx.measureText(' ').width, | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -212,59 +198,59 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  updateSize () { | 
					 | 
					 | 
					 | 
					  updateSize () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this._window.devicePixelRatio = window.devicePixelRatio || 1 | 
					 | 
					 | 
					 | 
					    this._window.devicePixelRatio = window.devicePixelRatio || 1; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let didChange = false | 
					 | 
					 | 
					 | 
					    let didChange = false; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for (let key in this.windowState) { | 
					 | 
					 | 
					 | 
					    for (let key in this.windowState) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (this.windowState[key] !== this.window[key]) { | 
					 | 
					 | 
					 | 
					      if (this.windowState.hasOwnProperty(key) && this.windowState[key] !== this.window[key]) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        didChange = true | 
					 | 
					 | 
					 | 
					        didChange = true; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.windowState[key] = this.window[key] | 
					 | 
					 | 
					 | 
					        this.windowState[key] = this.window[key] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (didChange) { | 
					 | 
					 | 
					 | 
					    if (didChange) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      const { | 
					 | 
					 | 
					 | 
					      const { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        width, height, devicePixelRatio, gridScaleX, gridScaleY, fontSize | 
					 | 
					 | 
					 | 
					        width, height, devicePixelRatio, gridScaleX, gridScaleY | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } = this.window | 
					 | 
					 | 
					 | 
					      } = this.window; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      const charSize = this.getCharSize() | 
					 | 
					 | 
					 | 
					      const charSize = this.getCharSize(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.canvas.width = width * devicePixelRatio * charSize.width * gridScaleX | 
					 | 
					 | 
					 | 
					      this.canvas.width = width * devicePixelRatio * charSize.width * gridScaleX; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.canvas.style.width = `${Math.ceil(width * charSize.width * | 
					 | 
					 | 
					 | 
					      this.canvas.style.width = `${Math.ceil(width * charSize.width * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        gridScaleX)}px` | 
					 | 
					 | 
					 | 
					        gridScaleX)}px`;
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.canvas.height = height * devicePixelRatio * charSize.height * | 
					 | 
					 | 
					 | 
					      this.canvas.height = height * devicePixelRatio * charSize.height * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        gridScaleY | 
					 | 
					 | 
					 | 
					        gridScaleY; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.canvas.style.height = `${Math.ceil(height * charSize.height * | 
					 | 
					 | 
					 | 
					      this.canvas.style.height = `${Math.ceil(height * charSize.height * | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        gridScaleY)}px` | 
					 | 
					 | 
					 | 
					        gridScaleY)}px` | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  resetCursorBlink () { | 
					 | 
					 | 
					 | 
					  resetCursorBlink () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.cursor.blinkOn = true | 
					 | 
					 | 
					 | 
					    this.cursor.blinkOn = true; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    clearInterval(this.cursor.blinkInterval) | 
					 | 
					 | 
					 | 
					    clearInterval(this.cursor.blinkInterval); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.cursor.blinkInterval = setInterval(() => { | 
					 | 
					 | 
					 | 
					    this.cursor.blinkInterval = setInterval(() => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.cursor.blinkOn = !this.cursor.blinkOn | 
					 | 
					 | 
					 | 
					      this.cursor.blinkOn = !this.cursor.blinkOn; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.scheduleDraw() | 
					 | 
					 | 
					 | 
					      this.scheduleDraw() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }, 500) | 
					 | 
					 | 
					 | 
					    }, 500) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  resetBlink () { | 
					 | 
					 | 
					 | 
					  resetBlink () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.window.blinkStyleOn = true | 
					 | 
					 | 
					 | 
					    this.window.blinkStyleOn = true; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    clearInterval(this.window.blinkInterval) | 
					 | 
					 | 
					 | 
					    clearInterval(this.window.blinkInterval); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let intervals = 0 | 
					 | 
					 | 
					 | 
					    let intervals = 0; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.window.blinkInterval = setInterval(() => { | 
					 | 
					 | 
					 | 
					    this.window.blinkInterval = setInterval(() => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      intervals++ | 
					 | 
					 | 
					 | 
					      intervals++; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (intervals >= 4 && this.window.blinkStyleOn) { | 
					 | 
					 | 
					 | 
					      if (intervals >= 4 && this.window.blinkStyleOn) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.window.blinkStyleOn = false | 
					 | 
					 | 
					 | 
					        this.window.blinkStyleOn = false; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        intervals = 0 | 
					 | 
					 | 
					 | 
					        intervals = 0 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } else if (intervals >= 1 && !this.window.blinkStyleOn) { | 
					 | 
					 | 
					 | 
					      } else if (intervals >= 1 && !this.window.blinkStyleOn) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.window.blinkStyleOn = true | 
					 | 
					 | 
					 | 
					        this.window.blinkStyleOn = true; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        intervals = 0 | 
					 | 
					 | 
					 | 
					        intervals = 0 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }, 200) | 
					 | 
					 | 
					 | 
					    }, 200) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  getNormalizedSelection () { | 
					 | 
					 | 
					 | 
					  getNormalizedSelection () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let { start, end } = this.selection | 
					 | 
					 | 
					 | 
					    let { start, end } = this.selection; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // if the start line is after the end line, or if they're both on the same
 | 
					 | 
					 | 
					 | 
					    // if the start line is after the end line, or if they're both on the same
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // line but the start column comes after the end column, swap
 | 
					 | 
					 | 
					 | 
					    // line but the start column comes after the end column, swap
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (start[1] > end[1] || (start[1] === end[1] && start[0] > end[0])) { | 
					 | 
					 | 
					 | 
					    if (start[1] > end[1] || (start[1] === end[1] && start[0] > end[0])) { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -274,30 +260,30 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  isInSelection (col, line) { | 
					 | 
					 | 
					 | 
					  isInSelection (col, line) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let { start, end } = this.getNormalizedSelection() | 
					 | 
					 | 
					 | 
					    let { start, end } = this.getNormalizedSelection(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let colAfterStart = start[0] <= col | 
					 | 
					 | 
					 | 
					    let colAfterStart = start[0] <= col; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let colBeforeEnd = col < end[0] | 
					 | 
					 | 
					 | 
					    let colBeforeEnd = col < end[0]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let onStartLine = line === start[1] | 
					 | 
					 | 
					 | 
					    let onStartLine = line === start[1]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let onEndLine = line === end[1] | 
					 | 
					 | 
					 | 
					    let onEndLine = line === end[1]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (onStartLine && onEndLine) return colAfterStart && colBeforeEnd | 
					 | 
					 | 
					 | 
					    if (onStartLine && onEndLine) return colAfterStart && colBeforeEnd; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    else if (onStartLine) return colAfterStart | 
					 | 
					 | 
					 | 
					    else if (onStartLine) return colAfterStart; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    else if (onEndLine) return colBeforeEnd | 
					 | 
					 | 
					 | 
					    else if (onEndLine) return colBeforeEnd; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    else return start[1] < line && line < end[1] | 
					 | 
					 | 
					 | 
					    else return start[1] < line && line < end[1] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  getSelectedText () { | 
					 | 
					 | 
					 | 
					  getSelectedText () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const screenLength = this.window.width * this.window.height | 
					 | 
					 | 
					 | 
					    const screenLength = this.window.width * this.window.height; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let lines = [] | 
					 | 
					 | 
					 | 
					    let lines = []; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let previousLineIndex = -1 | 
					 | 
					 | 
					 | 
					    let previousLineIndex = -1; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for (let cell = 0; cell < screenLength; cell++) { | 
					 | 
					 | 
					 | 
					    for (let cell = 0; cell < screenLength; cell++) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let x = cell % this.window.width | 
					 | 
					 | 
					 | 
					      let x = cell % this.window.width; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let y = Math.floor(cell / this.window.width) | 
					 | 
					 | 
					 | 
					      let y = Math.floor(cell / this.window.width); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (this.isInSelection(x, y)) { | 
					 | 
					 | 
					 | 
					      if (this.isInSelection(x, y)) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (previousLineIndex !== y) { | 
					 | 
					 | 
					 | 
					        if (previousLineIndex !== y) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          previousLineIndex = y | 
					 | 
					 | 
					 | 
					          previousLineIndex = y; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          lines.push('') | 
					 | 
					 | 
					 | 
					          lines.push('') | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        lines[lines.length - 1] += this.screen[cell] | 
					 | 
					 | 
					 | 
					        lines[lines.length - 1] += this.screen[cell] | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -308,71 +294,71 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  copySelectionToClipboard () { | 
					 | 
					 | 
					 | 
					  copySelectionToClipboard () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let selectedText = this.getSelectedText() | 
					 | 
					 | 
					 | 
					    let selectedText = this.getSelectedText(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // don't copy anything if nothing is selected
 | 
					 | 
					 | 
					 | 
					    // don't copy anything if nothing is selected
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (!selectedText) return | 
					 | 
					 | 
					 | 
					    if (!selectedText) return; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let textarea = document.createElement('textarea') | 
					 | 
					 | 
					 | 
					    let textarea = document.createElement('textarea'); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    document.body.appendChild(textarea) | 
					 | 
					 | 
					 | 
					    document.body.appendChild(textarea); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    textarea.value = selectedText | 
					 | 
					 | 
					 | 
					    textarea.value = selectedText; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    textarea.select() | 
					 | 
					 | 
					 | 
					    textarea.select(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (document.execCommand('copy')) { | 
					 | 
					 | 
					 | 
					    if (document.execCommand('copy')) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      Notify.show(`Copied to clipboard`) | 
					 | 
					 | 
					 | 
					      Notify.show('Copied to clipboard'); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } else { | 
					 | 
					 | 
					 | 
					    } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      console.warn('Copy failed'); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      // unsuccessful copy
 | 
					 | 
					 | 
					 | 
					      // unsuccessful copy
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      // TODO: do something?
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    document.body.removeChild(textarea) | 
					 | 
					 | 
					 | 
					    document.body.removeChild(textarea); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  screenToGrid (x, y) { | 
					 | 
					 | 
					 | 
					  screenToGrid (x, y) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let charSize = this.getCharSize() | 
					 | 
					 | 
					 | 
					    let charSize = this.getCharSize(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let cellWidth = charSize.width * this.window.gridScaleX | 
					 | 
					 | 
					 | 
					    let cellWidth = charSize.width * this.window.gridScaleX; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let cellHeight = charSize.height * this.window.gridScaleY | 
					 | 
					 | 
					 | 
					    let cellHeight = charSize.height * this.window.gridScaleY; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return [ | 
					 | 
					 | 
					 | 
					    return [ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      Math.floor((x + cellWidth / 2) / cellWidth), | 
					 | 
					 | 
					 | 
					      Math.floor((x + cellWidth / 2) / cellWidth), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      Math.floor(y / cellHeight) | 
					 | 
					 | 
					 | 
					      Math.floor(y / cellHeight) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ] | 
					 | 
					 | 
					 | 
					    ]; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs }, | 
					 | 
					 | 
					 | 
					  drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs }, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            compositeAbove = false) { | 
					 | 
					 | 
					 | 
					            compositeAbove = false) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const ctx = this.ctx | 
					 | 
					 | 
					 | 
					    const ctx = this.ctx; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const inSelection = this.isInSelection(x, y) | 
					 | 
					 | 
					 | 
					    const inSelection = this.isInSelection(x, y); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.fillStyle = inSelection ? SELECTION_BG : this.colors[bg] | 
					 | 
					 | 
					 | 
					    ctx.fillStyle = inSelection ? SELECTION_BG : this.colors[bg]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (!compositeAbove) ctx.globalCompositeOperation = 'destination-over' | 
					 | 
					 | 
					 | 
					    if (!compositeAbove) ctx.globalCompositeOperation = 'destination-over'; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.fillRect(x * cellWidth, y * cellHeight, | 
					 | 
					 | 
					 | 
					    ctx.fillRect(x * cellWidth, y * cellHeight, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      Math.ceil(cellWidth), Math.ceil(cellHeight)) | 
					 | 
					 | 
					 | 
					      Math.ceil(cellWidth), Math.ceil(cellHeight)); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.globalCompositeOperation = 'source-over' | 
					 | 
					 | 
					 | 
					    ctx.globalCompositeOperation = 'source-over'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let fontModifiers = {} | 
					 | 
					 | 
					 | 
					    let fontModifiers = {}; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let underline = false | 
					 | 
					 | 
					 | 
					    let underline = false; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let blink = false | 
					 | 
					 | 
					 | 
					    let blink = false; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let strike = false | 
					 | 
					 | 
					 | 
					    let strike = false; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (attrs & 1) fontModifiers.weight = 'bold' | 
					 | 
					 | 
					 | 
					    if (attrs & 1) fontModifiers.weight = 'bold'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (attrs & 1 << 1) ctx.globalAlpha = 0.5 | 
					 | 
					 | 
					 | 
					    if (attrs & 1 << 1) ctx.globalAlpha = 0.5; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (attrs & 1 << 2) fontModifiers.style = 'italic' | 
					 | 
					 | 
					 | 
					    if (attrs & 1 << 2) fontModifiers.style = 'italic'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (attrs & 1 << 3) underline = true | 
					 | 
					 | 
					 | 
					    if (attrs & 1 << 3) underline = true; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (attrs & 1 << 4) blink = true | 
					 | 
					 | 
					 | 
					    if (attrs & 1 << 4) blink = true; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (attrs & 1 << 5) text = TermScreen.alphaToFraktur(text) | 
					 | 
					 | 
					 | 
					    if (attrs & 1 << 5) text = TermScreen.alphaToFraktur(text); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (attrs & 1 << 6) strike = true | 
					 | 
					 | 
					 | 
					    if (attrs & 1 << 6) strike = true; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (!blink || this.window.blinkStyleOn) { | 
					 | 
					 | 
					 | 
					    if (!blink || this.window.blinkStyleOn) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      ctx.font = this.getFont(fontModifiers) | 
					 | 
					 | 
					 | 
					      ctx.font = this.getFont(fontModifiers); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      ctx.fillStyle = inSelection ? SELECTION_FG : this.colors[fg] | 
					 | 
					 | 
					 | 
					      ctx.fillStyle = inSelection ? SELECTION_FG : this.colors[fg]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      ctx.fillText(text, (x + 0.5) * cellWidth, (y + 0.5) * cellHeight) | 
					 | 
					 | 
					 | 
					      ctx.fillText(text, (x + 0.5) * cellWidth, (y + 0.5) * cellHeight); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (underline || strike) { | 
					 | 
					 | 
					 | 
					      if (underline || strike) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        let lineY = underline | 
					 | 
					 | 
					 | 
					        let lineY = underline | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          ? y * cellHeight + charSize.height | 
					 | 
					 | 
					 | 
					          ? y * cellHeight + charSize.height | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          : (y + 0.5) * cellHeight | 
					 | 
					 | 
					 | 
					          : (y + 0.5) * cellHeight; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.strokeStyle = inSelection ? SELECTION_FG : this.colors[fg] | 
					 | 
					 | 
					 | 
					        ctx.strokeStyle = inSelection ? SELECTION_FG : this.colors[fg]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.lineWidth = this.window.fontSize / 10 | 
					 | 
					 | 
					 | 
					        ctx.lineWidth = this.window.fontSize / 10; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.lineCap = 'round' | 
					 | 
					 | 
					 | 
					        ctx.lineCap = 'round'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.beginPath() | 
					 | 
					 | 
					 | 
					        ctx.beginPath(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.moveTo(x * cellWidth, lineY) | 
					 | 
					 | 
					 | 
					        ctx.moveTo(x * cellWidth, lineY); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.lineTo((x + 1) * cellWidth, lineY) | 
					 | 
					 | 
					 | 
					        ctx.lineTo((x + 1) * cellWidth, lineY); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.stroke() | 
					 | 
					 | 
					 | 
					        ctx.stroke() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -381,76 +367,73 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  draw () { | 
					 | 
					 | 
					 | 
					  draw () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const ctx = this.ctx | 
					 | 
					 | 
					 | 
					    const ctx = this.ctx; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const { | 
					 | 
					 | 
					 | 
					    const { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      width, | 
					 | 
					 | 
					 | 
					      width, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      height, | 
					 | 
					 | 
					 | 
					      height, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      devicePixelRatio, | 
					 | 
					 | 
					 | 
					      devicePixelRatio, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      fontFamily, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      fontSize, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      gridScaleX, | 
					 | 
					 | 
					 | 
					      gridScaleX, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      gridScaleY | 
					 | 
					 | 
					 | 
					      gridScaleY | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } = this.window | 
					 | 
					 | 
					 | 
					    } = this.window; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const charSize = this.getCharSize() | 
					 | 
					 | 
					 | 
					    const charSize = this.getCharSize(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const cellWidth = charSize.width * gridScaleX | 
					 | 
					 | 
					 | 
					    const cellWidth = charSize.width * gridScaleX; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const cellHeight = charSize.height * gridScaleY | 
					 | 
					 | 
					 | 
					    const cellHeight = charSize.height * gridScaleY; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const screenWidth = width * cellWidth | 
					 | 
					 | 
					 | 
					    const screenWidth = width * cellWidth; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const screenHeight = height * cellHeight | 
					 | 
					 | 
					 | 
					    const screenHeight = height * cellHeight; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const screenLength = width * height | 
					 | 
					 | 
					 | 
					    const screenLength = width * height; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.setTransform(this.window.devicePixelRatio, 0, 0, | 
					 | 
					 | 
					 | 
					    ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.window.devicePixelRatio, 0, 0) | 
					 | 
					 | 
					 | 
					    ctx.clearRect(0, 0, screenWidth, screenHeight); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.clearRect(0, 0, screenWidth, screenHeight) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.font = this.getFont() | 
					 | 
					 | 
					 | 
					    ctx.font = this.getFont(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.textAlign = 'center' | 
					 | 
					 | 
					 | 
					    ctx.textAlign = 'center'; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ctx.textBaseline = 'middle' | 
					 | 
					 | 
					 | 
					    ctx.textBaseline = 'middle'; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for (let cell = 0; cell < screenLength; cell++) { | 
					 | 
					 | 
					 | 
					    for (let cell = 0; cell < screenLength; cell++) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let x = cell % width | 
					 | 
					 | 
					 | 
					      let x = cell % width; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let y = Math.floor(cell / width) | 
					 | 
					 | 
					 | 
					      let y = Math.floor(cell / width); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let isCursor = this.cursor.x === x && this.cursor.y === y | 
					 | 
					 | 
					 | 
					      let isCursor = this.cursor.x === x && this.cursor.y === y; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (this.cursor.hanging) isCursor = false | 
					 | 
					 | 
					 | 
					      if (this.cursor.hanging) isCursor = false; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let invertForCursor = isCursor && this.cursor.blinkOn && | 
					 | 
					 | 
					 | 
					      let invertForCursor = isCursor && this.cursor.blinkOn && | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.cursor.style === 'block' | 
					 | 
					 | 
					 | 
					        this.cursor.style === 'block'; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let text = this.screen[cell] | 
					 | 
					 | 
					 | 
					      let text = this.screen[cell]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let fg = invertForCursor ? this.screenBG[cell] : this.screenFG[cell] | 
					 | 
					 | 
					 | 
					      let fg = invertForCursor ? this.screenBG[cell] : this.screenFG[cell]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let bg = invertForCursor ? this.screenFG[cell] : this.screenBG[cell] | 
					 | 
					 | 
					 | 
					      let bg = invertForCursor ? this.screenFG[cell] : this.screenBG[cell]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let attrs = this.screenAttrs[cell] | 
					 | 
					 | 
					 | 
					      let attrs = this.screenAttrs[cell]; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      // HACK: ensure cursor is visible
 | 
					 | 
					 | 
					 | 
					      // HACK: ensure cursor is visible
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (invertForCursor && fg === bg) bg = fg === 0 ? 7 : 0 | 
					 | 
					 | 
					 | 
					      if (invertForCursor && fg === bg) bg = fg === 0 ? 7 : 0; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      this.drawCell({ | 
					 | 
					 | 
					 | 
					      this.drawCell({ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs | 
					 | 
					 | 
					 | 
					        x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      }) | 
					 | 
					 | 
					 | 
					      }); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (isCursor && this.cursor.blinkOn && this.cursor.style !== 'block') { | 
					 | 
					 | 
					 | 
					      if (isCursor && this.cursor.blinkOn && this.cursor.style !== 'block') { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.save() | 
					 | 
					 | 
					 | 
					        ctx.save(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.beginPath() | 
					 | 
					 | 
					 | 
					        ctx.beginPath(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (this.cursor.style === 'bar') { | 
					 | 
					 | 
					 | 
					        if (this.cursor.style === 'bar') { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          // vertical bar
 | 
					 | 
					 | 
					 | 
					          // vertical bar
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          let barWidth = 2 | 
					 | 
					 | 
					 | 
					          let barWidth = 2; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          ctx.rect(x * cellWidth, y * cellHeight, barWidth, cellHeight) | 
					 | 
					 | 
					 | 
					          ctx.rect(x * cellWidth, y * cellHeight, barWidth, cellHeight) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } else if (this.cursor.style === 'line') { | 
					 | 
					 | 
					 | 
					        } else if (this.cursor.style === 'line') { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          // underline
 | 
					 | 
					 | 
					 | 
					          // underline
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          let lineHeight = 2 | 
					 | 
					 | 
					 | 
					          let lineHeight = 2; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          ctx.rect(x * cellWidth, y * cellHeight + charSize.height, | 
					 | 
					 | 
					 | 
					          ctx.rect(x * cellWidth, y * cellHeight + charSize.height, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            cellWidth, lineHeight) | 
					 | 
					 | 
					 | 
					            cellWidth, lineHeight) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.clip() | 
					 | 
					 | 
					 | 
					        ctx.clip(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // swap foreground/background
 | 
					 | 
					 | 
					 | 
					        // swap foreground/background
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        fg = this.screenBG[cell] | 
					 | 
					 | 
					 | 
					        fg = this.screenBG[cell]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        bg = this.screenFG[cell] | 
					 | 
					 | 
					 | 
					        bg = this.screenFG[cell]; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // HACK: ensure cursor is visible
 | 
					 | 
					 | 
					 | 
					        // HACK: ensure cursor is visible
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (fg === bg) bg = fg === 0 ? 7 : 0 | 
					 | 
					 | 
					 | 
					        if (fg === bg) bg = fg === 0 ? 7 : 0; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.drawCell({ | 
					 | 
					 | 
					 | 
					        this.drawCell({ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs | 
					 | 
					 | 
					 | 
					          x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        }, true) | 
					 | 
					 | 
					 | 
					        }, true); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ctx.restore() | 
					 | 
					 | 
					 | 
					        ctx.restore() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -458,115 +441,119 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  loadContent (str) { | 
					 | 
					 | 
					 | 
					  loadContent (str) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // current index
 | 
					 | 
					 | 
					 | 
					    // current index
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let i = 0 | 
					 | 
					 | 
					 | 
					    let i = 0; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // window size
 | 
					 | 
					 | 
					 | 
					    // window size
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.window.height = parse2B(str, i) | 
					 | 
					 | 
					 | 
					    this.window.height = parse2B(str, i); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.window.width = parse2B(str, i + 2) | 
					 | 
					 | 
					 | 
					    this.window.width = parse2B(str, i + 2); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.updateSize() | 
					 | 
					 | 
					 | 
					    this.updateSize(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    i += 4 | 
					 | 
					 | 
					 | 
					    i += 4; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // cursor position
 | 
					 | 
					 | 
					 | 
					    // cursor position
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let [cursorY, cursorX] = [parse2B(str, i), parse2B(str, i + 2)] | 
					 | 
					 | 
					 | 
					    let [cursorY, cursorX] = [parse2B(str, i), parse2B(str, i + 2)]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    i += 4 | 
					 | 
					 | 
					 | 
					    i += 4; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let cursorMoved = (cursorX !== this.cursor.x || cursorY !== this.cursor.y) | 
					 | 
					 | 
					 | 
					    let cursorMoved = (cursorX !== this.cursor.x || cursorY !== this.cursor.y); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.cursor.x = cursorX | 
					 | 
					 | 
					 | 
					    this.cursor.x = cursorX; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.cursor.y = cursorY | 
					 | 
					 | 
					 | 
					    this.cursor.y = cursorY; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (cursorMoved) this.resetCursorBlink() | 
					 | 
					 | 
					 | 
					    if (cursorMoved) this.resetCursorBlink(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // attributes
 | 
					 | 
					 | 
					 | 
					    // attributes
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let attributes = parse2B(str, i) | 
					 | 
					 | 
					 | 
					    let attributes = parse2B(str, i); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    i += 2 | 
					 | 
					 | 
					 | 
					    i += 2; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.cursor.visible = !!(attributes & 1) | 
					 | 
					 | 
					 | 
					    this.cursor.visible = !!(attributes & 1); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.cursor.hanging = !!(attributes & 1 << 1) | 
					 | 
					 | 
					 | 
					    this.cursor.hanging = !!(attributes & 1 << 1); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Input.setAlts( | 
					 | 
					 | 
					 | 
					    Input.setAlts( | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      !!(attributes & 1 << 2), // cursors alt
 | 
					 | 
					 | 
					 | 
					      !!(attributes & 1 << 2), // cursors alt
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      !!(attributes & 1 << 3), // numpad alt
 | 
					 | 
					 | 
					 | 
					      !!(attributes & 1 << 3), // numpad alt
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      !!(attributes & 1 << 4)  // fn keys alt
 | 
					 | 
					 | 
					 | 
					      !!(attributes & 1 << 4)  // fn keys alt
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ) | 
					 | 
					 | 
					 | 
					    ); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let trackMouseClicks = !!(attributes & 1 << 5) | 
					 | 
					 | 
					 | 
					    let trackMouseClicks = !!(attributes & 1 << 5); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let trackMouseMovement = !!(attributes & 1 << 6) | 
					 | 
					 | 
					 | 
					    let trackMouseMovement = !!(attributes & 1 << 6); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Input.setMouseMode(trackMouseClicks, trackMouseMovement) | 
					 | 
					 | 
					 | 
					    Input.setMouseMode(trackMouseClicks, trackMouseMovement); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.selection.selectable = !trackMouseMovement | 
					 | 
					 | 
					 | 
					    this.selection.selectable = !trackMouseMovement; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.mouseMode = { | 
					 | 
					 | 
					 | 
					    this.mouseMode = { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      clicks: trackMouseClicks, | 
					 | 
					 | 
					 | 
					      clicks: trackMouseClicks, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      movement: trackMouseMovement | 
					 | 
					 | 
					 | 
					      movement: trackMouseMovement | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    }; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let showButtons = !!(attributes & 1 << 7) | 
					 | 
					 | 
					 | 
					    let showButtons = !!(attributes & 1 << 7); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let showConfigLinks = !!(attributes & 1 << 8) | 
					 | 
					 | 
					 | 
					    let showConfigLinks = !!(attributes & 1 << 8); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    $('.x-term-conf-btn').toggleClass('hidden', !showConfigLinks) | 
					 | 
					 | 
					 | 
					    $('.x-term-conf-btn').toggleClass('hidden', !showConfigLinks); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    $('#action-buttons').toggleClass('hidden', !showButtons) | 
					 | 
					 | 
					 | 
					    $('#action-buttons').toggleClass('hidden', !showButtons); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // content
 | 
					 | 
					 | 
					 | 
					    // content
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let fg = 7 | 
					 | 
					 | 
					 | 
					    let fg = 7; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let bg = 0 | 
					 | 
					 | 
					 | 
					    let bg = 0; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let attrs = 0 | 
					 | 
					 | 
					 | 
					    let attrs = 0; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let cell = 0 // cell index
 | 
					 | 
					 | 
					 | 
					    let cell = 0; // cell index
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let text = ' ' | 
					 | 
					 | 
					 | 
					    let text = ' '; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let screenLength = this.window.width * this.window.height | 
					 | 
					 | 
					 | 
					    let screenLength = this.window.width * this.window.height; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screen = new Array(screenLength).fill(' ') | 
					 | 
					 | 
					 | 
					    this.screen = new Array(screenLength).fill(' '); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screenFG = new Array(screenLength).fill(' ') | 
					 | 
					 | 
					 | 
					    this.screenFG = new Array(screenLength).fill(' '); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screenBG = new Array(screenLength).fill(' ') | 
					 | 
					 | 
					 | 
					    this.screenBG = new Array(screenLength).fill(' '); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.screenAttrs = new Array(screenLength).fill(' ') | 
					 | 
					 | 
					 | 
					    this.screenAttrs = new Array(screenLength).fill(' '); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    while (i < str.length && cell < screenLength) { | 
					 | 
					 | 
					 | 
					    while (i < str.length && cell < screenLength) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let character = str[i++] | 
					 | 
					 | 
					 | 
					      let character = str[i++]; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      let charCode = character.charCodeAt(0) | 
					 | 
					 | 
					 | 
					      let charCode = character.charCodeAt(0); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (charCode === SEQ_SET_COLOR_ATTR) { | 
					 | 
					 | 
					 | 
					      if (charCode === SEQ_SET_COLOR_ATTR) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        let data = parse3B(str, i) | 
					 | 
					 | 
					 | 
					        let data = parse3B(str, i); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        i += 3 | 
					 | 
					 | 
					 | 
					        i += 3; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        fg = data & 0xF | 
					 | 
					 | 
					 | 
					        fg = data & 0xF; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        bg = data >> 4 & 0xF | 
					 | 
					 | 
					 | 
					        bg = data >> 4 & 0xF; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        attrs = data >> 8 & 0xFF | 
					 | 
					 | 
					 | 
					        attrs = data >> 8 & 0xFF | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } else if (charCode == SEQ_SET_COLOR) { | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        let data = parse2B(str, i) | 
					 | 
					 | 
					 | 
					      else if (charCode == SEQ_SET_COLOR) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        i += 2 | 
					 | 
					 | 
					 | 
					        let data = parse2B(str, i); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        fg = data & 0xF | 
					 | 
					 | 
					 | 
					        i += 2; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        fg = data & 0xF; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        bg = data >> 4 & 0xF | 
					 | 
					 | 
					 | 
					        bg = data >> 4 & 0xF | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } else if (charCode === SEQ_SET_ATTR) { | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        let data = parse2B(str, i) | 
					 | 
					 | 
					 | 
					      else if (charCode === SEQ_SET_ATTR) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        i += 2 | 
					 | 
					 | 
					 | 
					        let data = parse2B(str, i); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        i += 2; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        attrs = data & 0xFF | 
					 | 
					 | 
					 | 
					        attrs = data & 0xFF | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } else if (charCode === SEQ_REPEAT) { | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        let count = parse2B(str, i) | 
					 | 
					 | 
					 | 
					      else if (charCode === SEQ_REPEAT) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        i += 2 | 
					 | 
					 | 
					 | 
					        let count = parse2B(str, i); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        i += 2; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for (let j = 0; j < count; j++) { | 
					 | 
					 | 
					 | 
					        for (let j = 0; j < count; j++) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          this.screen[cell] = text | 
					 | 
					 | 
					 | 
					          this.screen[cell] = text; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          this.screenFG[cell] = fg | 
					 | 
					 | 
					 | 
					          this.screenFG[cell] = fg; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          this.screenBG[cell] = bg | 
					 | 
					 | 
					 | 
					          this.screenBG[cell] = bg; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          this.screenAttrs[cell] = attrs | 
					 | 
					 | 
					 | 
					          this.screenAttrs[cell] = attrs; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          if (++cell > screenLength) break | 
					 | 
					 | 
					 | 
					          if (++cell > screenLength) break | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } else { | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // unique cell character
 | 
					 | 
					 | 
					 | 
					        // unique cell character
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.screen[cell] = text = character | 
					 | 
					 | 
					 | 
					        this.screen[cell] = text = character; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.screenFG[cell] = fg | 
					 | 
					 | 
					 | 
					        this.screenFG[cell] = fg; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.screenBG[cell] = bg | 
					 | 
					 | 
					 | 
					        this.screenBG[cell] = bg; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.screenAttrs[cell] = attrs | 
					 | 
					 | 
					 | 
					        this.screenAttrs[cell] = attrs; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        cell++ | 
					 | 
					 | 
					 | 
					        cell++ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    this.scheduleDraw() | 
					 | 
					 | 
					 | 
					    this.scheduleDraw(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (this.onload) this.onload() | 
					 | 
					 | 
					 | 
					    if (this.onload) this.onload() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  /** Apply labels to buttons and screen title (leading T removed already) */ | 
					 | 
					 | 
					 | 
					  /** Apply labels to buttons and screen title (leading T removed already) */ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  loadLabels (str) { | 
					 | 
					 | 
					 | 
					  loadLabels (str) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let pieces = str.split('\x01') | 
					 | 
					 | 
					 | 
					    let pieces = str.split('\x01'); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    qs('h1').textContent = pieces[0] | 
					 | 
					 | 
					 | 
					    qs('h1').textContent = pieces[0]; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    $('#action-buttons button').forEach((button, i) => { | 
					 | 
					 | 
					 | 
					    $('#action-buttons button').forEach((button, i) => { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      var label = pieces[i + 1].trim() | 
					 | 
					 | 
					 | 
					      let label = pieces[i + 1].trim(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      // if empty string, use the "dim" effect and put nbsp instead to
 | 
					 | 
					 | 
					 | 
					      // if empty string, use the "dim" effect and put nbsp instead to
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      // stretch the button vertically
 | 
					 | 
					 | 
					 | 
					      // stretch the button vertically
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      button.innerHTML = label ? e(label) : ' '; | 
					 | 
					 | 
					 | 
					      button.innerHTML = label ? e(label) : ' '; | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -575,34 +562,33 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  load (str) { | 
					 | 
					 | 
					 | 
					  load (str) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const content = str.substr(1) | 
					 | 
					 | 
					 | 
					    const content = str.substr(1); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    switch (str[0]) { | 
					 | 
					 | 
					 | 
					    switch (str[0]) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      case 'S': | 
					 | 
					 | 
					 | 
					      case 'S': | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.loadContent(content) | 
					 | 
					 | 
					 | 
					        this.loadContent(content); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        break | 
					 | 
					 | 
					 | 
					        break; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      case 'T': | 
					 | 
					 | 
					 | 
					      case 'T': | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.loadLabels(content) | 
					 | 
					 | 
					 | 
					        this.loadLabels(content); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        break | 
					 | 
					 | 
					 | 
					        break; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      case 'B': | 
					 | 
					 | 
					 | 
					      case 'B': | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        this.beep() | 
					 | 
					 | 
					 | 
					        this.beep(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        break | 
					 | 
					 | 
					 | 
					        break; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      default: | 
					 | 
					 | 
					 | 
					      default: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        console.warn(`Bad data message type; ignoring.\n${ | 
					 | 
					 | 
					 | 
					        console.warn(`Bad data message type; ignoring.\n${JSON.stringify(content)}`) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          JSON.stringify(content)}`)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  beep () { | 
					 | 
					 | 
					 | 
					  beep () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    const audioCtx = this.audioCtx | 
					 | 
					 | 
					 | 
					    const audioCtx = this.audioCtx; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (!audioCtx) return | 
					 | 
					 | 
					 | 
					    if (!audioCtx) return; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    let osc, gain | 
					 | 
					 | 
					 | 
					    let osc, gain; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // main beep
 | 
					 | 
					 | 
					 | 
					    // main beep
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    osc = audioCtx.createOscillator() | 
					 | 
					 | 
					 | 
					    osc = audioCtx.createOscillator(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    gain = audioCtx.createGain() | 
					 | 
					 | 
					 | 
					    gain = audioCtx.createGain(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    osc.connect(gain) | 
					 | 
					 | 
					 | 
					    osc.connect(gain); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    gain.connect(audioCtx.destination); | 
					 | 
					 | 
					 | 
					    gain.connect(audioCtx.destination); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    gain.gain.value = 0.5; | 
					 | 
					 | 
					 | 
					    gain.gain.value = 0.5; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    osc.frequency.value = 750; | 
					 | 
					 | 
					 | 
					    osc.frequency.value = 750; | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -633,13 +619,16 @@ class TermScreen { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					const Screen = new TermScreen() | 
					 | 
					 | 
					 | 
					const Screen = new TermScreen(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					let didAddScreen = false | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					let didAddScreen = false; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					Screen.onload = function () { | 
					 | 
					 | 
					 | 
					Screen.onload = function () { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (didAddScreen) return | 
					 | 
					 | 
					 | 
					  if (didAddScreen) return; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  didAddScreen = true | 
					 | 
					 | 
					 | 
					  didAddScreen = true; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  qs('#screen').appendChild(Screen.canvas) | 
					 | 
					 | 
					 | 
					  qs('#screen').appendChild(Screen.canvas); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (let item of qs('#screen').classList) { | 
					 | 
					 | 
					 | 
					  for (let item of qs('#screen').classList) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (item.startsWith('theme-')) Screen.colors = themes[item.substr(6)] | 
					 | 
					 | 
					 | 
					    if (item.startsWith('theme-')) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      Screen.colors = themes[item.substr(6)] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}; | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
					 | 
					 | 
					
  |