]> git.proxmox.com Git - mirror_xterm.js.git/blame - src/xterm.js
Support focus class on the .xterm element
[mirror_xterm.js.git] / src / xterm.js
CommitLineData
8bc844c0 1/**
5af18f8e
PK
2 * xterm.js: xterm, in the browser
3 * Copyright (c) 2014, sourceLair Limited (www.sourcelair.com (MIT License)
6cc8b3cd
CJ
4 * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
5 * https://github.com/chjj/term.js
8bc844c0
CJ
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 * Originally forked from (with the author's permission):
26 * Fabrice Bellard's javascript vt100 for jslinux:
27 * http://bellard.org/jslinux/
28 * Copyright (c) 2011 Fabrice Bellard
29 * The original design remains. The terminal itself
30 * has been extended to include xterm CSI codes, among
31 * other features.
32 */
33
3f455f90 34(function (xterm) {
6a87253d
AO
35 if (typeof exports === 'object' && typeof module === 'object') {
36 /*
bd215861 37 * CommonJS environment
6a87253d
AO
38 */
39 module.exports = xterm.call(this);
40 } else if (typeof define == 'function') {
3f455f90
PK
41 /*
42 * Require.js is available
43 */
87979aee 44 define([], xterm.bind(window));
3f455f90
PK
45 } else {
46 /*
47 * Plain browser environment
987f09d8 48 */
3f455f90
PK
49 this.Xterm = xterm.call(this);
50 this.Terminal = this.Xterm; /* Backwards compatibility with term.js */
51 }
52})(function() {
53 /**
54 * Terminal Emulation References:
55 * http://vt100.net/
56 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
57 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
58 * http://invisible-island.net/vttest/
59 * http://www.inwap.com/pdp10/ansicode.txt
60 * http://linux.die.net/man/4/console_codes
61 * http://linux.die.net/man/7/urxvt
62 */
63
64 'use strict';
65
66 /**
67 * Shared
68 */
69
70 var window = this, document = this.document;
71
72 /**
73 * EventEmitter
74 */
75
76 function EventEmitter() {
77 this._events = this._events || {};
78 }
8bc844c0 79
3f455f90
PK
80 EventEmitter.prototype.addListener = function(type, listener) {
81 this._events[type] = this._events[type] || [];
82 this._events[type].push(listener);
83 };
8bc844c0 84
3f455f90 85 EventEmitter.prototype.on = EventEmitter.prototype.addListener;
8bc844c0 86
3f455f90
PK
87 EventEmitter.prototype.removeListener = function(type, listener) {
88 if (!this._events[type]) return;
8bc844c0 89
3f455f90
PK
90 var obj = this._events[type]
91 , i = obj.length;
8bc844c0 92
3f455f90
PK
93 while (i--) {
94 if (obj[i] === listener || obj[i].listener === listener) {
95 obj.splice(i, 1);
96 return;
97 }
98 }
99 };
100
101 EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
8bc844c0 102
3f455f90
PK
103 EventEmitter.prototype.removeAllListeners = function(type) {
104 if (this._events[type]) delete this._events[type];
105 };
8bc844c0 106
3f455f90 107 EventEmitter.prototype.once = function(type, listener) {
6406a88d 108 var self = this;
3f455f90
PK
109 function on() {
110 var args = Array.prototype.slice.call(arguments);
fc7b22dc
DI
111 this.removeListener(type, on);
112 return listener.apply(this, args);
3f455f90
PK
113 }
114 on.listener = listener;
115 return this.on(type, on);
116 };
8bc844c0 117
3f455f90
PK
118 EventEmitter.prototype.emit = function(type) {
119 if (!this._events[type]) return;
8bc844c0 120
3f455f90
PK
121 var args = Array.prototype.slice.call(arguments, 1)
122 , obj = this._events[type]
123 , l = obj.length
124 , i = 0;
8bc844c0 125
3f455f90
PK
126 for (; i < l; i++) {
127 obj[i].apply(this, args);
128 }
129 };
8bc844c0 130
3f455f90
PK
131 EventEmitter.prototype.listeners = function(type) {
132 return this._events[type] = this._events[type] || [];
133 };
8bc844c0 134
8bc844c0 135
3f455f90
PK
136 /**
137 * States
138 */
139 var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6;
8bc844c0 140
3f455f90
PK
141 /**
142 * Terminal
143 */
8bc844c0 144
fd5be55d
DI
145 /**
146 * Creates a new `Terminal` object.
147 *
148 * @param {object} options An object containing a set of options, the available options are:
149 * - cursorBlink (boolean): Whether the terminal cursor blinks
150 *
151 * @public
152 */
3f455f90
PK
153 function Terminal(options) {
154 var self = this;
8bc844c0 155
3f455f90
PK
156 if (!(this instanceof Terminal)) {
157 return new Terminal(arguments[0], arguments[1], arguments[2]);
158 }
8bc844c0 159
3f455f90 160 self.cancel = Terminal.cancel;
8bc844c0 161
3f455f90 162 EventEmitter.call(this);
8bc844c0 163
3f455f90
PK
164 if (typeof options === 'number') {
165 options = {
166 cols: arguments[0],
167 rows: arguments[1],
168 handler: arguments[2]
169 };
170 }
5fd1948b 171
3f455f90 172 options = options || {};
8bc844c0 173
8bc844c0 174
3f455f90
PK
175 Object.keys(Terminal.defaults).forEach(function(key) {
176 if (options[key] == null) {
177 options[key] = Terminal.options[key];
86dad1b0 178
3f455f90
PK
179 if (Terminal[key] !== Terminal.defaults[key]) {
180 options[key] = Terminal[key];
181 }
182 }
183 self[key] = options[key];
184 });
91273161 185
3f455f90
PK
186 if (options.colors.length === 8) {
187 options.colors = options.colors.concat(Terminal._colors.slice(8));
188 } else if (options.colors.length === 16) {
189 options.colors = options.colors.concat(Terminal._colors.slice(16));
190 } else if (options.colors.length === 10) {
191 options.colors = options.colors.slice(0, -2).concat(
192 Terminal._colors.slice(8, -2), options.colors.slice(-2));
193 } else if (options.colors.length === 18) {
194 options.colors = options.colors.concat(
195 Terminal._colors.slice(16, -2), options.colors.slice(-2));
196 }
197 this.colors = options.colors;
8bc844c0 198
3f455f90 199 this.options = options;
91273161 200
3f455f90
PK
201 // this.context = options.context || window;
202 // this.document = options.document || document;
203 this.parent = options.body || options.parent
204 || (document ? document.getElementsByTagName('body')[0] : null);
5fd1948b 205
3f455f90
PK
206 this.cols = options.cols || options.geometry[0];
207 this.rows = options.rows || options.geometry[1];
208
209 if (options.handler) {
210 this.on('data', options.handler);
211 }
212
df268ad5
DI
213 /**
214 * The scroll position of the y cursor, ie. ybase + y = the y position within the entire
87c9370e 215 * buffer
df268ad5 216 */
3f455f90 217 this.ybase = 0;
df268ad5
DI
218
219 /**
220 * The scroll position of the viewport
221 */
3f455f90 222 this.ydisp = 0;
df268ad5
DI
223
224 /**
225 * The cursor's x position after ybase
226 */
3f455f90 227 this.x = 0;
df268ad5
DI
228
229 /**
230 * The cursor's y position after ybase
231 */
3f455f90 232 this.y = 0;
df268ad5 233
af29effb
DI
234 /**
235 * Used to debounce the refresh function
236 */
237 this.isRefreshing = false;
238
239 /**
240 * Whether there is a full terminal refresh queued
241 */
af29effb 242
3f455f90
PK
243 this.cursorState = 0;
244 this.cursorHidden = false;
245 this.convertEol;
246 this.state = 0;
247 this.queue = '';
248 this.scrollTop = 0;
249 this.scrollBottom = this.rows - 1;
250
251 // modes
252 this.applicationKeypad = false;
253 this.applicationCursor = false;
254 this.originMode = false;
255 this.insertMode = false;
4afa08da 256 this.wraparoundMode = true; // defaults: xterm - true, vt100 - false
3f455f90
PK
257 this.normal = null;
258
259 // charset
260 this.charset = null;
261 this.gcharset = null;
262 this.glevel = 0;
263 this.charsets = [null];
264
265 // mouse properties
266 this.decLocator;
267 this.x10Mouse;
268 this.vt200Mouse;
269 this.vt300Mouse;
270 this.normalMouse;
271 this.mouseEvents;
272 this.sendFocus;
273 this.utfMouse;
274 this.sgrMouse;
275 this.urxvtMouse;
276
277 // misc
278 this.element;
279 this.children;
280 this.refreshStart;
281 this.refreshEnd;
282 this.savedX;
283 this.savedY;
284 this.savedCols;
285
286 // stream
287 this.readable = true;
288 this.writable = true;
289
290 this.defAttr = (0 << 18) | (257 << 9) | (256 << 0);
291 this.curAttr = this.defAttr;
292
293 this.params = [];
294 this.currentParam = 0;
295 this.prefix = '';
296 this.postfix = '';
297
e3126ba3
JB
298 // leftover surrogate high from previous write invocation
299 this.surrogate_high = '';
874ba72f 300
df268ad5
DI
301 /**
302 * An array of all lines in the entire buffer, including the prompt. The lines are array of
303 * characters which are 2-length arrays where [0] is an attribute and [1] is the character.
304 */
3f455f90
PK
305 this.lines = [];
306 var i = this.rows;
307 while (i--) {
308 this.lines.push(this.blankLine());
86dad1b0 309 }
3f455f90
PK
310
311 this.tabs;
312 this.setupStops();
86dad1b0 313 }
8bc844c0 314
3f455f90 315 inherits(Terminal, EventEmitter);
8bc844c0 316
3f455f90
PK
317 // back_color_erase feature for xterm.
318 Terminal.prototype.eraseAttr = function() {
319 // if (this.is('screen')) return this.defAttr;
320 return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);
321 };
a68c8336 322
3f455f90
PK
323 /**
324 * Colors
325 */
326
327 // Colors 0-15
328 Terminal.tangoColors = [
329 // dark:
330 '#2e3436',
331 '#cc0000',
332 '#4e9a06',
333 '#c4a000',
334 '#3465a4',
335 '#75507b',
336 '#06989a',
337 '#d3d7cf',
338 // bright:
339 '#555753',
340 '#ef2929',
341 '#8ae234',
342 '#fce94f',
343 '#729fcf',
344 '#ad7fa8',
345 '#34e2e2',
346 '#eeeeec'
347 ];
348
349 // Colors 0-15 + 16-255
350 // Much thanks to TooTallNate for writing this.
351 Terminal.colors = (function() {
352 var colors = Terminal.tangoColors.slice()
353 , r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
354 , i;
355
356 // 16-231
357 i = 0;
358 for (; i < 216; i++) {
359 out(r[(i / 36) % 6 | 0], r[(i / 6) % 6 | 0], r[i % 6]);
360 }
8bc844c0 361
3f455f90
PK
362 // 232-255 (grey)
363 i = 0;
364 for (; i < 24; i++) {
365 r = 8 + i * 10;
366 out(r, r, r);
367 }
8bc844c0 368
3f455f90
PK
369 function out(r, g, b) {
370 colors.push('#' + hex(r) + hex(g) + hex(b));
371 }
8bc844c0 372
3f455f90
PK
373 function hex(c) {
374 c = c.toString(16);
375 return c.length < 2 ? '0' + c : c;
376 }
8bc844c0 377
3f455f90
PK
378 return colors;
379 })();
3b322929 380
3f455f90 381 Terminal._colors = Terminal.colors.slice();
31161ffe 382
3f455f90
PK
383 Terminal.vcolors = (function() {
384 var out = []
385 , colors = Terminal.colors
386 , i = 0
387 , color;
8bc844c0 388
3f455f90
PK
389 for (; i < 256; i++) {
390 color = parseInt(colors[i].substring(1), 16);
391 out.push([
392 (color >> 16) & 0xff,
393 (color >> 8) & 0xff,
394 color & 0xff
395 ]);
396 }
5fd1948b 397
3f455f90
PK
398 return out;
399 })();
400
401 /**
402 * Options
403 */
404
405 Terminal.defaults = {
406 colors: Terminal.colors,
407 theme: 'default',
408 convertEol: false,
409 termName: 'xterm',
410 geometry: [80, 24],
411 cursorBlink: false,
412 visualBell: false,
413 popOnBell: false,
414 scrollback: 1000,
415 screenKeys: false,
416 debug: false,
417 cancelEvents: false
418 // programFeatures: false,
419 // focusKeys: false,
420 };
cc7f4d0d 421
3f455f90 422 Terminal.options = {};
cd956bca 423
7988f634
SS
424 Terminal.focus = null;
425
3f455f90
PK
426 each(keys(Terminal.defaults), function(key) {
427 Terminal[key] = Terminal.defaults[key];
428 Terminal.options[key] = Terminal.defaults[key];
429 });
430
431 /**
a6e85ad5 432 * Focus the terminal. Delegates focus handling to the terminal's DOM element.
fd5be55d
DI
433 *
434 * @public
3f455f90
PK
435 */
436 Terminal.prototype.focus = function() {
fc7b22dc
DI
437 if (document.activeElement === this.textarea) {
438 return;
439 }
440
441 if (this.sendFocus) {
442 this.send('\x1b[I');
443 }
444
aaedcfc7 445 this.element.classList.add('focus');
fc7b22dc
DI
446 this.showCursor();
447 this.textarea.focus();
448 Terminal.focus = this;
a6e85ad5 449 };
8bc844c0 450
a6e85ad5
PK
451 /**
452 * Binds the desired focus behavior on a given terminal object.
453 *
454 * @static
455 */
456 Terminal.bindFocus = function (term) {
457 on(term.element, 'focus', function (ev) {
458 if (term.sendFocus) {
459 term.send('\x1b[I');
460 }
8bc844c0 461
a6e85ad5
PK
462 term.showCursor();
463 Terminal.focus = term;
464 term.emit('focus', {terminal: term});
465 });
3f455f90 466 };
8bc844c0 467
a6e85ad5
PK
468 /**
469 * Blur the terminal. Delegates blur handling to the terminal's DOM element.
470 *
471 * @public
472 */
3f455f90 473 Terminal.prototype.blur = function() {
fc7b22dc
DI
474 if (Terminal.focus !== this) {
475 return;
476 }
477
aaedcfc7 478 this.element.classList.remove('focus');
fc7b22dc
DI
479 this.cursorState = 0;
480 this.refresh(this.y, this.y);
481 this.textarea.blur();
482 if (this.sendFocus) {
483 this.send('\x1b[0]');
484 }
485 Terminal.focus = null;
a6e85ad5 486 };
8bc844c0 487
a6e85ad5
PK
488 /**
489 * Binds the desired blur behavior on a given terminal object.
490 *
491 * @static
492 */
493 Terminal.bindBlur = function (term) {
494 on(term.element, 'blur', function (ev) {
495 if (term.sendFocus) {
496 term.send('\x1b[O');
497 }
498 Terminal.focus = null;
499 term.emit('blur', {terminal: term});
500 });
3f455f90 501 };
8bc844c0 502
3f455f90
PK
503 /**
504 * Initialize default behavior
505 */
3f455f90 506 Terminal.prototype.initGlobal = function() {
c02cc844 507 Terminal.bindPaste(this);
fc7b22dc 508 Terminal.bindKeys(this);
3f455f90
PK
509 Terminal.bindCopy(this);
510 };
8bc844c0 511
3f455f90 512 /**
64721687
PK
513 * Bind to paste event and allow both keyboard and right-click pasting, without having the
514 * contentEditable value set to true.
3f455f90
PK
515 */
516 Terminal.bindPaste = function(term) {
fc7b22dc
DI
517 on([term.textarea, term.element], 'paste', function(ev) {
518 ev.stopPropagation();
3f455f90
PK
519 if (ev.clipboardData) {
520 var text = ev.clipboardData.getData('text/plain');
521 term.handler(text);
fc7b22dc
DI
522 term.textarea.value = '';
523 return term.cancel(ev);
3f455f90 524 }
3f455f90 525 });
3f455f90 526 };
8bc844c0 527
00f4232e
PK
528 /**
529 * Prepares text copied from terminal selection, to be saved in the clipboard by:
530 * 1. stripping all trailing white spaces
531 * 2. converting all non-breaking spaces to regular spaces
532 * @param {string} text The copied text that needs processing for storing in clipboard
fed92ac5 533 * @returns {string}
00f4232e
PK
534 * @static
535 */
536 Terminal.prepareCopiedTextForClipboard = function (text) {
537 var space = String.fromCharCode(32),
538 nonBreakingSpace = String.fromCharCode(160),
539 allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'),
540 processedText = text.split('\n').map(function (line) {
541 /**
542 * Strip all trailing white spaces and convert all non-breaking spaces to regular
543 * spaces.
544 */
545 var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
546
547 return processedLine;
548 }).join('\n');
549
550 return processedText;
551 };
8bc844c0 552
c02cc844 553 /**
fc7b22dc 554 * Apply key handling to the terminal
3f455f90 555 */
fc7b22dc
DI
556 Terminal.bindKeys = function(term) {
557 on(term.element, 'keydown', function(ev) {
558 if (document.activeElement != this) {
559 return;
560 }
561 term.keyDown(ev);
562 }, true);
00f4232e 563
fc7b22dc
DI
564 on(term.element, 'keypress', function(ev) {
565 if (document.activeElement != this) {
566 return;
567 }
568 term.keyPress(ev);
569 }, true);
8bc844c0 570
fc7b22dc 571 on(term.element, 'keyup', term.focus.bind(term));
8bc844c0 572
fc7b22dc
DI
573 on(term.textarea, 'keydown', function(ev) {
574 term.keyDown(ev);
575 }, true);
8bc844c0 576
fc7b22dc
DI
577 on(term.textarea, 'keypress', function(ev) {
578 term.keyPress(ev);
579 // Truncate the textarea's value, since it is not needed
580 this.value = '';
581 }, true);
3f455f90 582 };
8bc844c0 583
fc7b22dc
DI
584 /**
585 * Binds copy functionality to the given terminal.
586 * @static
587 */
588 Terminal.bindCopy = function(term) {
589 on(term.element, 'copy', function(ev) {
590 return; // temporary
8bc844c0 591 });
3f455f90 592 };
8bc844c0 593
8bc844c0 594
3f455f90
PK
595 /*
596 * Insert the given row to the terminal or produce a new one
597 * if no row argument is passed. Return the inserted row.
598 */
599 Terminal.prototype.insertRow = function (row) {
600 if (typeof row != 'object') {
601 row = document.createElement('div');
602 }
8bc844c0 603
3f455f90
PK
604 this.rowContainer.appendChild(row);
605 this.children.push(row);
606
607 return row;
f3bd6145 608 };
8bc844c0 609
8bc844c0 610
fd5be55d
DI
611 /**
612 * Opens the terminal within an element.
613 *
614 * @param {HTMLElement} parent The element to create the terminal within.
615 *
616 * @public
3f455f90
PK
617 */
618 Terminal.prototype.open = function(parent) {
619 var self=this, i=0, div;
8bc844c0 620
3f455f90 621 this.parent = parent || this.parent;
8bc844c0 622
3f455f90
PK
623 if (!this.parent) {
624 throw new Error('Terminal requires a parent element.');
625 }
8bc844c0 626
3f455f90
PK
627 /*
628 * Grab global elements
629 */
630 this.context = this.parent.ownerDocument.defaultView;
631 this.document = this.parent.ownerDocument;
632 this.body = this.document.getElementsByTagName('body')[0];
633
634 /*
635 * Parse User-Agent
636 */
637 if (this.context.navigator && this.context.navigator.userAgent) {
3f455f90
PK
638 this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE');
639 }
8bc844c0 640
b01165c1 641 /*
642 * Find the users platform. We use this to interpret the meta key
643 * and ISO third level shifts.
644 * http://stackoverflow.com/questions/19877924/what-is-the-list-of-possible-values-for-navigator-platform-as-of-today
645 */
646 if (this.context.navigator && this.context.navigator.platform) {
647 this.isMac = contains(
648 this.context.navigator.platform,
649 ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K']
650 );
651 this.isIpad = this.context.navigator.platform === 'iPad';
652 this.isIphone = this.context.navigator.platform === 'iPhone';
653 this.isMSWindows = contains(
654 this.context.navigator.platform,
655 ['Windows', 'Win16', 'Win32', 'WinCE']
656 );
657 }
658
3f455f90
PK
659 /*
660 * Create main element container
661 */
662 this.element = this.document.createElement('div');
a523803d
AK
663 this.element.classList.add('terminal');
664 this.element.classList.add('xterm');
665 this.element.classList.add('xterm-theme-' + this.theme);
3f455f90
PK
666 this.element.setAttribute('tabindex', 0);
667
668 /*
669 * Create the container that will hold the lines of the terminal and then
670 * produce the lines the lines.
671 */
672 this.rowContainer = document.createElement('div');
673 this.rowContainer.classList.add('xterm-rows');
674 this.element.appendChild(this.rowContainer);
675 this.children = [];
676
fc7b22dc
DI
677 /*
678 * Create the container that will hold helpers like the textarea for
679 * capturing DOM Events. Then produce the helpers.
680 */
681 this.helperContainer = document.createElement('div');
682 this.helperContainer.classList.add('xterm-helpers');
683 this.element.appendChild(this.helperContainer);
684 this.textarea = document.createElement('textarea');
685 this.textarea.classList.add('xterm-helper-textarea');
686 this.textarea.setAttribute('autocorrect', 'off');
687 this.textarea.setAttribute('autocapitalize', 'off');
688 this.textarea.setAttribute('spellcheck', 'false');
689 this.textarea.tabIndex = 0;
690 this.textarea.onfocus = function() {
691 self.emit('focus', {terminal: self});
692 }
693 this.textarea.onblur = function() {
694 self.emit('blur', {terminal: self});
695 }
696 this.helperContainer.appendChild(this.textarea);
697
3f455f90
PK
698 for (; i < this.rows; i++) {
699 this.insertRow();
700 }
701 this.parent.appendChild(this.element);
8bc844c0 702
3f455f90
PK
703 // Draw the screen.
704 this.refresh(0, this.rows - 1);
8bc844c0 705
3f455f90
PK
706 // Initialize global actions that
707 // need to be taken on the document.
708 this.initGlobal();
15f68335 709
3f455f90
PK
710 // Ensure there is a Terminal.focus.
711 this.focus();
15f68335 712
3f455f90 713 on(this.element, 'mouseup', function() {
f53f5735
TD
714 var selection = document.getSelection(),
715 collapsed = selection.isCollapsed,
716 isRange = typeof collapsed == 'boolean' ? !collapsed : selection.type == 'Range';
717 if (!isRange) {
3f455f90
PK
718 self.focus();
719 }
720 });
8bc844c0 721
3f455f90
PK
722 // Listen for mouse events and translate
723 // them into terminal mouse protocols.
724 this.bindMouse();
8bc844c0 725
3f455f90
PK
726 // Figure out whether boldness affects
727 // the character width of monospace fonts.
728 if (Terminal.brokenBold == null) {
729 Terminal.brokenBold = isBoldBroken(this.document);
8bc844c0
CJ
730 }
731
3f455f90
PK
732 this.emit('open');
733 };
8bc844c0 734
57300f51
PK
735
736 /**
737 * Attempts to load an add-on using CommonJS or RequireJS (whichever is available).
738 * @param {string} addon The name of the addon to load
739 * @static
740 */
741 Terminal.loadAddon = function(addon, callback) {
742 if (typeof exports === 'object' && typeof module === 'object') {
743 // CommonJS
744 return require(__dirname + '/../addons/' + addon);
745 } else if (typeof define == 'function') {
746 // RequireJS
747 return require(['../addons/' + addon + '/' + addon], callback);
748 } else {
749 console.error('Cannot load a module without a CommonJS or RequireJS environment.');
750 return false;
751 }
752 };
753
754
3f455f90
PK
755 // XTerm mouse events
756 // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
757 // To better understand these
758 // the xterm code is very helpful:
759 // Relevant files:
760 // button.c, charproc.c, misc.c
761 // Relevant functions in xterm/button.c:
762 // BtnCode, EmitButtonCode, EditorButton, SendMousePosition
763 Terminal.prototype.bindMouse = function() {
764 var el = this.element
765 , self = this
766 , pressed = 32;
767
768 var wheelEvent = 'onmousewheel' in this.context
769 ? 'mousewheel'
770 : 'DOMMouseScroll';
8bc844c0 771
3f455f90
PK
772 // mouseup, mousedown, mousewheel
773 // left click: ^[[M 3<^[[M#3<
774 // mousewheel up: ^[[M`3>
775 function sendButton(ev) {
776 var button
777 , pos;
8bc844c0 778
3f455f90
PK
779 // get the xterm-style button
780 button = getButton(ev);
8bc844c0 781
3f455f90
PK
782 // get mouse coordinates
783 pos = getCoords(ev);
784 if (!pos) return;
8bc844c0 785
3f455f90 786 sendEvent(button, pos);
8bc844c0 787
59b79e9b 788 switch (ev.overrideType || ev.type) {
3f455f90
PK
789 case 'mousedown':
790 pressed = button;
8bc844c0 791 break;
3f455f90
PK
792 case 'mouseup':
793 // keep it at the left
794 // button, just in case.
795 pressed = 32;
8bc844c0 796 break;
3f455f90
PK
797 case wheelEvent:
798 // nothing. don't
799 // interfere with
800 // `pressed`.
8bc844c0 801 break;
3f455f90
PK
802 }
803 }
8bc844c0 804
3f455f90
PK
805 // motion example of a left click:
806 // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
807 function sendMove(ev) {
808 var button = pressed
809 , pos;
8bc844c0 810
3f455f90
PK
811 pos = getCoords(ev);
812 if (!pos) return;
a7f50531 813
3f455f90
PK
814 // buttons marked as motions
815 // are incremented by 32
816 button += 32;
a7f50531 817
3f455f90
PK
818 sendEvent(button, pos);
819 }
efa0e3c1 820
3f455f90
PK
821 // encode button and
822 // position to characters
823 function encode(data, ch) {
824 if (!self.utfMouse) {
825 if (ch === 255) return data.push(0);
826 if (ch > 127) ch = 127;
827 data.push(ch);
828 } else {
829 if (ch === 2047) return data.push(0);
830 if (ch < 127) {
831 data.push(ch);
832 } else {
833 if (ch > 2047) ch = 2047;
834 data.push(0xC0 | (ch >> 6));
835 data.push(0x80 | (ch & 0x3F));
836 }
8bc844c0 837 }
3f455f90 838 }
8bc844c0 839
3f455f90
PK
840 // send a mouse event:
841 // regular/utf8: ^[[M Cb Cx Cy
842 // urxvt: ^[[ Cb ; Cx ; Cy M
843 // sgr: ^[[ Cb ; Cx ; Cy M/m
844 // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
845 // locator: CSI P e ; P b ; P r ; P c ; P p & w
846 function sendEvent(button, pos) {
847 // self.emit('mouse', {
848 // x: pos.x - 32,
849 // y: pos.x - 32,
850 // button: button
851 // });
852
853 if (self.vt300Mouse) {
854 // NOTE: Unstable.
855 // http://www.vt100.net/docs/vt3xx-gp/chapter15.html
856 button &= 3;
857 pos.x -= 32;
858 pos.y -= 32;
859 var data = '\x1b[24';
860 if (button === 0) data += '1';
861 else if (button === 1) data += '3';
862 else if (button === 2) data += '5';
863 else if (button === 3) return;
864 else data += '0';
865 data += '~[' + pos.x + ',' + pos.y + ']\r';
866 self.send(data);
867 return;
868 }
8bc844c0 869
3f455f90
PK
870 if (self.decLocator) {
871 // NOTE: Unstable.
872 button &= 3;
873 pos.x -= 32;
874 pos.y -= 32;
875 if (button === 0) button = 2;
876 else if (button === 1) button = 4;
877 else if (button === 2) button = 6;
878 else if (button === 3) button = 3;
879 self.send('\x1b['
880 + button
881 + ';'
882 + (button === 3 ? 4 : 0)
883 + ';'
884 + pos.y
885 + ';'
886 + pos.x
887 + ';'
888 + (pos.page || 0)
889 + '&w');
890 return;
891 }
8bc844c0 892
3f455f90
PK
893 if (self.urxvtMouse) {
894 pos.x -= 32;
895 pos.y -= 32;
896 pos.x++;
897 pos.y++;
898 self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M');
899 return;
900 }
8bc844c0 901
3f455f90
PK
902 if (self.sgrMouse) {
903 pos.x -= 32;
904 pos.y -= 32;
905 self.send('\x1b[<'
906 + ((button & 3) === 3 ? button & ~3 : button)
907 + ';'
908 + pos.x
909 + ';'
910 + pos.y
911 + ((button & 3) === 3 ? 'm' : 'M'));
912 return;
913 }
8bc844c0 914
3f455f90 915 var data = [];
8bc844c0 916
3f455f90
PK
917 encode(data, button);
918 encode(data, pos.x);
919 encode(data, pos.y);
8bc844c0 920
3f455f90
PK
921 self.send('\x1b[M' + String.fromCharCode.apply(String, data));
922 }
8bc844c0 923
3f455f90
PK
924 function getButton(ev) {
925 var button
926 , shift
927 , meta
928 , ctrl
929 , mod;
930
931 // two low bits:
932 // 0 = left
933 // 1 = middle
934 // 2 = right
935 // 3 = release
936 // wheel up/down:
937 // 1, and 2 - with 64 added
59b79e9b 938 switch (ev.overrideType || ev.type) {
3f455f90
PK
939 case 'mousedown':
940 button = ev.button != null
941 ? +ev.button
942 : ev.which != null
943 ? ev.which - 1
944 : null;
945
946 if (self.isMSIE) {
947 button = button === 1 ? 0 : button === 4 ? 1 : button;
8bc844c0 948 }
8bc844c0 949 break;
3f455f90
PK
950 case 'mouseup':
951 button = 3;
8bc844c0 952 break;
3f455f90
PK
953 case 'DOMMouseScroll':
954 button = ev.detail < 0
955 ? 64
956 : 65;
8bc844c0 957 break;
3f455f90
PK
958 case 'mousewheel':
959 button = ev.wheelDeltaY > 0
960 ? 64
961 : 65;
8bc844c0 962 break;
3f455f90 963 }
8bc844c0 964
3f455f90
PK
965 // next three bits are the modifiers:
966 // 4 = shift, 8 = meta, 16 = control
967 shift = ev.shiftKey ? 4 : 0;
968 meta = ev.metaKey ? 8 : 0;
969 ctrl = ev.ctrlKey ? 16 : 0;
970 mod = shift | meta | ctrl;
971
972 // no mods
973 if (self.vt200Mouse) {
974 // ctrl only
975 mod &= ctrl;
976 } else if (!self.normalMouse) {
977 mod = 0;
978 }
8bc844c0 979
3f455f90
PK
980 // increment to SP
981 button = (32 + (mod << 2)) + button;
8bc844c0 982
3f455f90
PK
983 return button;
984 }
8bc844c0 985
3f455f90
PK
986 // mouse coordinates measured in cols/rows
987 function getCoords(ev) {
988 var x, y, w, h, el;
989
990 // ignore browsers without pageX for now
991 if (ev.pageX == null) return;
992
993 x = ev.pageX;
994 y = ev.pageY;
995 el = self.element;
996
997 // should probably check offsetParent
998 // but this is more portable
999 while (el && el !== self.document.documentElement) {
1000 x -= el.offsetLeft;
1001 y -= el.offsetTop;
1002 el = 'offsetParent' in el
1003 ? el.offsetParent
1004 : el.parentNode;
1005 }
8bc844c0 1006
3f455f90
PK
1007 // convert to cols/rows
1008 w = self.element.clientWidth;
1009 h = self.element.clientHeight;
1010 x = Math.round((x / w) * self.cols);
1011 y = Math.round((y / h) * self.rows);
1012
1013 // be sure to avoid sending
1014 // bad positions to the program
1015 if (x < 0) x = 0;
1016 if (x > self.cols) x = self.cols;
1017 if (y < 0) y = 0;
1018 if (y > self.rows) y = self.rows;
1019
1020 // xterm sends raw bytes and
1021 // starts at 32 (SP) for each.
1022 x += 32;
1023 y += 32;
1024
1025 return {
1026 x: x,
1027 y: y,
59b79e9b 1028 type: (ev.overrideType || ev.type) === wheelEvent
3f455f90 1029 ? 'mousewheel'
59b79e9b 1030 : (ev.overrideType || ev.type)
3f455f90
PK
1031 };
1032 }
8bc844c0 1033
3f455f90
PK
1034 on(el, 'mousedown', function(ev) {
1035 if (!self.mouseEvents) return;
8bc844c0 1036
3f455f90
PK
1037 // send the button
1038 sendButton(ev);
1039
1040 // ensure focus
1041 self.focus();
1042
1043 // fix for odd bug
1044 //if (self.vt200Mouse && !self.normalMouse) {
1045 if (self.vt200Mouse) {
59b79e9b
BF
1046 ev.overrideType = 'mouseup';
1047 sendButton(ev);
3f455f90 1048 return self.cancel(ev);
8bc844c0 1049 }
8bc844c0 1050
3f455f90
PK
1051 // bind events
1052 if (self.normalMouse) on(self.document, 'mousemove', sendMove);
1053
1054 // x10 compatibility mode can't send button releases
1055 if (!self.x10Mouse) {
1056 on(self.document, 'mouseup', function up(ev) {
1057 sendButton(ev);
1058 if (self.normalMouse) off(self.document, 'mousemove', sendMove);
1059 off(self.document, 'mouseup', up);
1060 return self.cancel(ev);
1061 });
8bc844c0 1062 }
3f455f90
PK
1063
1064 return self.cancel(ev);
1065 });
1066
1067 //if (self.normalMouse) {
1068 // on(self.document, 'mousemove', sendMove);
1069 //}
1070
1071 on(el, wheelEvent, function(ev) {
1072 if (!self.mouseEvents) return;
1073 if (self.x10Mouse
1074 || self.vt300Mouse
1075 || self.decLocator) return;
1076 sendButton(ev);
1077 return self.cancel(ev);
1078 });
1079
1080 // allow mousewheel scrolling in
1081 // the shell for example
1082 on(el, wheelEvent, function(ev) {
1083 if (self.mouseEvents) return;
1084 if (self.applicationKeypad) return;
1085 if (ev.type === 'DOMMouseScroll') {
1086 self.scrollDisp(ev.detail < 0 ? -1 : 1);
1087 } else {
1088 self.scrollDisp(ev.wheelDeltaY > 0 ? -1 : 1);
1089 }
1090 return self.cancel(ev);
1091 });
1092 };
1093
1094 /**
fd5be55d
DI
1095 * Destroys the terminal.
1096 *
1097 * @public
3f455f90 1098 */
3f455f90
PK
1099 Terminal.prototype.destroy = function() {
1100 this.readable = false;
1101 this.writable = false;
1102 this._events = {};
1103 this.handler = function() {};
1104 this.write = function() {};
1105 if (this.element.parentNode) {
1106 this.element.parentNode.removeChild(this.element);
1107 }
1108 //this.emit('close');
1109 };
1110
1111
1a384616
PK
1112 /**
1113 * Flags used to render terminal text properly
1114 */
1115 Terminal.flags = {
5a56849d
PK
1116 BOLD: 1,
1117 UNDERLINE: 2,
1118 BLINK: 4,
1119 INVERSE: 8,
1120 INVISIBLE: 16
1a384616
PK
1121 }
1122
3f455f90
PK
1123 /*
1124 * Rendering Engine
1125 *
1126 * In the screen buffer, each character
1127 * is stored as a an array with a character
1128 * and a 32-bit integer.
1129 * First value: a utf-16 character.
1130 * Second value:
1131 * Next 9 bits: background color (0-511).
1132 * Next 9 bits: foreground color (0-511).
1133 * Next 14 bits: a mask for misc. flags:
1134 * 1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible
1135 */
fd5be55d
DI
1136
1137 /**
1138 * Refreshes terminal content within two rows (inclusive).
1139 *
1140 * @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
1141 * @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
a6e85ad5 1142 * @param {boolean} queue Whether the refresh should ran right now or be queued
fd5be55d
DI
1143 *
1144 * @public
1145 */
a6e85ad5
PK
1146 Terminal.prototype.refresh = function(start, end, queue) {
1147 var self = this;
1148
1149 // queue defaults to true
1150 queue = (typeof queue == 'undefined') ? true : queue;
3f455f90 1151
a6e85ad5
PK
1152 /**
1153 * The refresh queue allows refresh to execute only approximately 30 times a second. For
1154 * commands that pass a significant amount of output to the write function, this prevents the
1155 * terminal from maxing out the CPU and making the UI unresponsive. While commands can still
1156 * run beyond what they do on the terminal, it is far better with a debounce in place as
1157 * every single terminal manipulation does not need to be constructed in the DOM.
1158 *
1159 * A side-effect of this is that it makes ^C to interrupt a process seem more responsive.
1160 */
1161 if (queue) {
1162 // If refresh should be queued, order the refresh and return.
1163 if (this._refreshIsQueued) {
1164 // If a refresh has already been queued, just order a full refresh next
1165 this._fullRefreshNext = true;
1166 } else {
1167 setTimeout(function () {
1168 self.refresh(start, end, false);
1169 }, 34)
1170 this._refreshIsQueued = true;
1171 }
af29effb
DI
1172 return;
1173 }
a6e85ad5
PK
1174
1175 // If refresh should be run right now (not be queued), release the lock
1176 this._refreshIsQueued = false;
1177
1178 // If multiple refreshes were requested, make a full refresh.
1179 if (this._fullRefreshNext) {
1180 start = 0;
1181 end = this.rows - 1;
1182 this._fullRefreshNext = false // reset lock
1183 }
1184
874ba72f 1185 var x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
af29effb 1186
af7588ef 1187 // If this is a big refresh, remove the terminal rows from the DOM for faster calculations
3f455f90
PK
1188 if (end - start >= this.rows / 2) {
1189 parent = this.element.parentNode;
af7588ef
PK
1190 if (parent) {
1191 this.element.removeChild(this.rowContainer);
1192 }
3f455f90
PK
1193 }
1194
1195 width = this.cols;
1196 y = start;
1197
edf89718 1198 if (end >= this.rows.length) {
3f455f90 1199 this.log('`end` is too large. Most likely a bad CSR.');
edf89718 1200 end = this.rows.length - 1;
3f455f90
PK
1201 }
1202
1203 for (; y <= end; y++) {
1204 row = y + this.ydisp;
1205
1206 line = this.lines[row];
1207 out = '';
1208
da9f86f1 1209 if (this.y === y - (this.ybase - this.ydisp)
3f455f90 1210 && this.cursorState
3f455f90
PK
1211 && !this.cursorHidden) {
1212 x = this.x;
1213 } else {
1214 x = -1;
1215 }
1216
1217 attr = this.defAttr;
1218 i = 0;
1219
1220 for (; i < width; i++) {
1221 data = line[i][0];
1222 ch = line[i][1];
f951abb7 1223 ch_width = line[i][2];
874ba72f
JB
1224 if (!ch_width)
1225 continue;
3f455f90
PK
1226
1227 if (i === x) data = -1;
1228
1229 if (data !== attr) {
1230 if (attr !== this.defAttr) {
1231 out += '</span>';
1232 }
1233 if (data !== this.defAttr) {
1234 if (data === -1) {
0d803ac8
DI
1235 out += '<span class="reverse-video terminal-cursor';
1236 if (this.cursorBlink) {
1237 out += ' blinking';
1238 }
1239 out += '">';
3f455f90 1240 } else {
ae0f9637 1241 var classNames = [];
3f455f90
PK
1242
1243 bg = data & 0x1ff;
1244 fg = (data >> 9) & 0x1ff;
1245 flags = data >> 18;
1246
1a384616 1247 if (flags & Terminal.flags.BOLD) {
3f455f90 1248 if (!Terminal.brokenBold) {
ae0f9637 1249 classNames.push('xterm-bold');
3f455f90
PK
1250 }
1251 // See: XTerm*boldColors
1252 if (fg < 8) fg += 8;
1253 }
1254
1a384616 1255 if (flags & Terminal.flags.UNDERLINE) {
ae0f9637 1256 classNames.push('xterm-underline');
3f455f90
PK
1257 }
1258
1a384616 1259 if (flags & Terminal.flags.BLINK) {
ae0f9637 1260 classNames.push('xterm-blink');
3f455f90
PK
1261 }
1262
1a384616
PK
1263 /**
1264 * If inverse flag is on, then swap the foreground and background variables.
1265 */
1266 if (flags & Terminal.flags.INVERSE) {
1267 /* One-line variable swap in JavaScript: http://stackoverflow.com/a/16201730 */
1268 bg = [fg, fg = bg][0];
1269 // Should inverse just be before the
1270 // above boldColors effect instead?
1271 if ((flags & 1) && fg < 8) fg += 8;
3f455f90
PK
1272 }
1273
1a384616 1274 if (flags & Terminal.flags.INVISIBLE) {
ae0f9637 1275 classNames.push('xterm-hidden');
3f455f90
PK
1276 }
1277
1a384616
PK
1278 /**
1279 * Weird situation: Invert flag used black foreground and white background results
1280 * in invalid background color, positioned at the 256 index of the 256 terminal
1281 * color map. Pin the colors manually in such a case.
1282 *
1283 * Source: https://github.com/sourcelair/xterm.js/issues/57
1284 */
1285 if (flags & Terminal.flags.INVERSE) {
1286 if (bg == 257) {
1287 bg = 15;
1288 }
1289 if (fg == 256) {
1290 fg = 0;
1291 }
1292 }
1293
1294 if (bg < 256) {
ae0f9637 1295 classNames.push('xterm-bg-color-' + bg);
3f455f90
PK
1296 }
1297
1a384616 1298 if (fg < 256) {
ae0f9637 1299 classNames.push('xterm-color-' + fg);
3f455f90
PK
1300 }
1301
ae0f9637
DI
1302 out += '<span';
1303 if (classNames.length) {
1304 out += ' class="' + classNames.join(' ') + '"';
1305 }
1306 out += '>';
8bc844c0 1307 }
3f455f90
PK
1308 }
1309 }
1310
1311 switch (ch) {
1312 case '&':
1313 out += '&amp;';
8bc844c0 1314 break;
3f455f90
PK
1315 case '<':
1316 out += '&lt;';
8bc844c0 1317 break;
3f455f90
PK
1318 case '>':
1319 out += '&gt;';
8bc844c0 1320 break;
3f455f90
PK
1321 default:
1322 if (ch <= ' ') {
1323 out += '&nbsp;';
1324 } else {
3f455f90
PK
1325 out += ch;
1326 }
8bc844c0
CJ
1327 break;
1328 }
1329
3f455f90 1330 attr = data;
8bc844c0 1331 }
8bc844c0 1332
3f455f90
PK
1333 if (attr !== this.defAttr) {
1334 out += '</span>';
8bc844c0
CJ
1335 }
1336
3f455f90
PK
1337 this.children[y].innerHTML = out;
1338 }
8bc844c0 1339
3f455f90 1340 if (parent) {
af7588ef 1341 this.element.appendChild(this.rowContainer);
3f455f90
PK
1342 }
1343
3f455f90
PK
1344 this.emit('refresh', {element: this.element, start: start, end: end});
1345 };
1346
3f455f90
PK
1347 Terminal.prototype.showCursor = function() {
1348 if (!this.cursorState) {
1349 this.cursorState = 1;
1350 this.refresh(this.y, this.y);
3f455f90
PK
1351 }
1352 };
1353
3f455f90
PK
1354 Terminal.prototype.scroll = function() {
1355 var row;
1356
1357 if (++this.ybase === this.scrollback) {
1358 this.ybase = this.ybase / 2 | 0;
1359 this.lines = this.lines.slice(-(this.ybase + this.rows) + 1);
1360 }
1361
1362 this.ydisp = this.ybase;
1363
1364 // last line
1365 row = this.ybase + this.rows - 1;
1366
1367 // subtract the bottom scroll region
1368 row -= this.rows - 1 - this.scrollBottom;
1369
1370 if (row === this.lines.length) {
1371 // potential optimization:
1372 // pushing is faster than splicing
1373 // when they amount to the same
1374 // behavior.
1375 this.lines.push(this.blankLine());
1376 } else {
1377 // add our new line
1378 this.lines.splice(row, 0, this.blankLine());
1379 }
1380
1381 if (this.scrollTop !== 0) {
1382 if (this.ybase !== 0) {
1383 this.ybase--;
1384 this.ydisp = this.ybase;
8bc844c0 1385 }
3f455f90
PK
1386 this.lines.splice(this.ybase + this.scrollTop, 1);
1387 }
8bc844c0 1388
3f455f90
PK
1389 // this.maxRange();
1390 this.updateRange(this.scrollTop);
1391 this.updateRange(this.scrollBottom);
1392 };
8bc844c0 1393
3f455f90
PK
1394 Terminal.prototype.scrollDisp = function(disp) {
1395 this.ydisp += disp;
8bc844c0 1396
3f455f90
PK
1397 if (this.ydisp > this.ybase) {
1398 this.ydisp = this.ybase;
1399 } else if (this.ydisp < 0) {
1400 this.ydisp = 0;
1401 }
8bc844c0 1402
3f455f90
PK
1403 this.refresh(0, this.rows - 1);
1404 };
8bc844c0 1405
fd5be55d
DI
1406 /**
1407 * Writes text to the terminal.
1408 *
1409 * @param {string} text The text to write to the terminal.
1410 *
1411 * @public
1412 */
3f455f90 1413 Terminal.prototype.write = function(data) {
4afa08da 1414 var l = data.length, i = 0, j, cs, ch, code, low, ch_width, row;
8bc844c0 1415
3f455f90
PK
1416 this.refreshStart = this.y;
1417 this.refreshEnd = this.y;
8bc844c0 1418
3f455f90
PK
1419 if (this.ybase !== this.ydisp) {
1420 this.ydisp = this.ybase;
1421 this.maxRange();
1422 }
8bc844c0 1423
e3126ba3
JB
1424 // apply leftover surrogate high from last write
1425 if (this.surrogate_high) {
1426 data = this.surrogate_high + data;
1427 this.surrogate_high = '';
1428 }
874ba72f 1429
3f455f90
PK
1430 for (; i < l; i++) {
1431 ch = data[i];
874ba72f 1432
e3126ba3
JB
1433 // FIXME: higher chars than 0xa0 are not allowed in escape sequences
1434 // --> maybe move to default
874ba72f 1435 code = data.charCodeAt(i);
874ba72f 1436 if (0xD800 <= code && code <= 0xDBFF) {
e3126ba3
JB
1437 // we got a surrogate high
1438 // get surrogate low (next 2 bytes)
1439 low = data.charCodeAt(i+1);
1440 if (isNaN(low)) {
1441 // end of data stream, save surrogate high
1442 this.surrogate_high = ch;
1443 continue;
1444 }
1445 code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
1446 ch += data.charAt(i+1);
874ba72f
JB
1447 }
1448 // surrogate low - already handled above
1449 if (0xDC00 <= code && code <= 0xDFFF)
e3126ba3 1450 continue;
874ba72f 1451
3f455f90
PK
1452 switch (this.state) {
1453 case normal:
1454 switch (ch) {
1455 case '\x07':
1456 this.bell();
1457 break;
8bc844c0 1458
3f455f90
PK
1459 // '\n', '\v', '\f'
1460 case '\n':
1461 case '\x0b':
1462 case '\x0c':
1463 if (this.convertEol) {
1464 this.x = 0;
1465 }
1466 this.y++;
1467 if (this.y > this.scrollBottom) {
1468 this.y--;
1469 this.scroll();
1470 }
1471 break;
8bc844c0 1472
3f455f90
PK
1473 // '\r'
1474 case '\r':
1475 this.x = 0;
1476 break;
8bc844c0 1477
3f455f90
PK
1478 // '\b'
1479 case '\x08':
1480 if (this.x > 0) {
1481 this.x--;
1482 }
1483 break;
1484
1485 // '\t'
1486 case '\t':
1487 this.x = this.nextStop();
1488 break;
8bc844c0 1489
3f455f90
PK
1490 // shift out
1491 case '\x0e':
1492 this.setgLevel(1);
1493 break;
1494
1495 // shift in
1496 case '\x0f':
1497 this.setgLevel(0);
1498 break;
1499
1500 // '\e'
1501 case '\x1b':
1502 this.state = escaped;
1503 break;
1504
1505 default:
1506 // ' '
e3126ba3
JB
1507 // calculate print space
1508 // expensive call, therefore we save width in line buffer
1509 ch_width = wcwidth(code);
f951abb7 1510
3f455f90
PK
1511 if (ch >= ' ') {
1512 if (this.charset && this.charset[ch]) {
1513 ch = this.charset[ch];
1514 }
1515
4afa08da
JB
1516 row = this.y + this.ybase;
1517
e3126ba3 1518 // insert combining char in last cell
c3bc59b5 1519 // FIXME: needs handling after cursor jumps
874ba72f 1520 if (!ch_width && this.x) {
0b018fd4 1521
c3bc59b5 1522 // dont overflow left
4afa08da
JB
1523 if (this.lines[row][this.x-1]) {
1524 if (!this.lines[row][this.x-1][2]) {
c3bc59b5
JB
1525
1526 // found empty cell after fullwidth, need to go 2 cells back
4afa08da
JB
1527 if (this.lines[row][this.x-2])
1528 this.lines[row][this.x-2][1] += ch;
c3bc59b5
JB
1529
1530 } else {
4afa08da 1531 this.lines[row][this.x-1][1] += ch;
c3bc59b5
JB
1532 }
1533 this.updateRange(this.y);
1534 }
874ba72f
JB
1535 break;
1536 }
1537
e3126ba3
JB
1538 // goto next line if ch would overflow
1539 // TODO: needs a global min terminal width of 2
1540 if (this.x+ch_width-1 >= this.cols) {
4afa08da
JB
1541 // autowrap - DECAWM
1542 if (this.wraparoundMode) {
1543 this.x = 0;
1544 this.y++;
1545 if (this.y > this.scrollBottom) {
1546 this.y--;
1547 this.scroll();
1548 }
1549 } else {
1550 this.x = this.cols-1;
1551 if(ch_width===2) // FIXME: check for xterm behavior
1552 continue;
1553 }
1554 }
1555 row = this.y + this.ybase;
1556
1557 // insert mode: move characters to right
1558 if (this.insertMode) {
1559 // do this twice for a fullwidth char
1560 for (var moves=0; moves<ch_width; ++moves) {
1561 // remove last cell, if it's width is 0
1562 // we have to adjust the second last cell as well
1563 var removed = this.lines[this.y + this.ybase].pop();
1564 if (removed[2]===0
1565 && this.lines[row][this.cols-2]
1566 && this.lines[row][this.cols-2][2]===2)
1567 this.lines[row][this.cols-2] = [this.curAttr, ' ', 1];
1568
1569 // insert empty cell at cursor
1570 this.lines[row].splice(this.x, 0, [this.curAttr, ' ', 1]);
3f455f90
PK
1571 }
1572 }
1573
4afa08da 1574 this.lines[row][this.x] = [this.curAttr, ch, ch_width];
3f455f90
PK
1575 this.x++;
1576 this.updateRange(this.y);
1577
e3126ba3 1578 // fullwidth char - set next cell width to zero and advance cursor
4afa08da
JB
1579 if (ch_width===2) {
1580 this.lines[row][this.x] = [this.curAttr, '', 0];
3f455f90
PK
1581 this.x++;
1582 }
1583 }
1584 break;
a4607f90 1585 }
8bc844c0 1586 break;
3f455f90
PK
1587 case escaped:
1588 switch (ch) {
1589 // ESC [ Control Sequence Introducer ( CSI is 0x9b).
1590 case '[':
1591 this.params = [];
1592 this.currentParam = 0;
1593 this.state = csi;
1594 break;
8bc844c0 1595
3f455f90
PK
1596 // ESC ] Operating System Command ( OSC is 0x9d).
1597 case ']':
1598 this.params = [];
1599 this.currentParam = 0;
1600 this.state = osc;
1601 break;
8bc844c0 1602
3f455f90
PK
1603 // ESC P Device Control String ( DCS is 0x90).
1604 case 'P':
1605 this.params = [];
1606 this.currentParam = 0;
1607 this.state = dcs;
1608 break;
8bc844c0 1609
3f455f90
PK
1610 // ESC _ Application Program Command ( APC is 0x9f).
1611 case '_':
1612 this.state = ignore;
1613 break;
8bc844c0 1614
3f455f90
PK
1615 // ESC ^ Privacy Message ( PM is 0x9e).
1616 case '^':
1617 this.state = ignore;
1618 break;
8bc844c0 1619
3f455f90
PK
1620 // ESC c Full Reset (RIS).
1621 case 'c':
1622 this.reset();
1623 break;
8bc844c0 1624
3f455f90
PK
1625 // ESC E Next Line ( NEL is 0x85).
1626 // ESC D Index ( IND is 0x84).
1627 case 'E':
1628 this.x = 0;
1629 ;
1630 case 'D':
1631 this.index();
1632 break;
8bc844c0 1633
3f455f90
PK
1634 // ESC M Reverse Index ( RI is 0x8d).
1635 case 'M':
1636 this.reverseIndex();
1637 break;
8bc844c0 1638
3f455f90
PK
1639 // ESC % Select default/utf-8 character set.
1640 // @ = default, G = utf-8
1641 case '%':
1642 //this.charset = null;
1643 this.setgLevel(0);
1644 this.setgCharset(0, Terminal.charsets.US);
1645 this.state = normal;
1646 i++;
1647 break;
8bc844c0 1648
3f455f90
PK
1649 // ESC (,),*,+,-,. Designate G0-G2 Character Set.
1650 case '(': // <-- this seems to get all the attention
1651 case ')':
1652 case '*':
1653 case '+':
1654 case '-':
1655 case '.':
1656 switch (ch) {
1657 case '(':
1658 this.gcharset = 0;
1659 break;
1660 case ')':
1661 this.gcharset = 1;
1662 break;
1663 case '*':
1664 this.gcharset = 2;
1665 break;
1666 case '+':
1667 this.gcharset = 3;
1668 break;
1669 case '-':
1670 this.gcharset = 1;
1671 break;
1672 case '.':
1673 this.gcharset = 2;
1674 break;
1675 }
1676 this.state = charset;
1677 break;
8bc844c0 1678
3f455f90
PK
1679 // Designate G3 Character Set (VT300).
1680 // A = ISO Latin-1 Supplemental.
1681 // Not implemented.
1682 case '/':
1683 this.gcharset = 3;
1684 this.state = charset;
1685 i--;
1686 break;
8bc844c0 1687
3f455f90
PK
1688 // ESC N
1689 // Single Shift Select of G2 Character Set
1690 // ( SS2 is 0x8e). This affects next character only.
1691 case 'N':
1692 break;
1693 // ESC O
1694 // Single Shift Select of G3 Character Set
1695 // ( SS3 is 0x8f). This affects next character only.
1696 case 'O':
1697 break;
1698 // ESC n
1699 // Invoke the G2 Character Set as GL (LS2).
1700 case 'n':
1701 this.setgLevel(2);
1702 break;
1703 // ESC o
1704 // Invoke the G3 Character Set as GL (LS3).
1705 case 'o':
1706 this.setgLevel(3);
1707 break;
1708 // ESC |
1709 // Invoke the G3 Character Set as GR (LS3R).
1710 case '|':
1711 this.setgLevel(3);
1712 break;
1713 // ESC }
1714 // Invoke the G2 Character Set as GR (LS2R).
1715 case '}':
1716 this.setgLevel(2);
1717 break;
1718 // ESC ~
1719 // Invoke the G1 Character Set as GR (LS1R).
1720 case '~':
1721 this.setgLevel(1);
1722 break;
8bc844c0 1723
3f455f90
PK
1724 // ESC 7 Save Cursor (DECSC).
1725 case '7':
1726 this.saveCursor();
1727 this.state = normal;
1728 break;
8bc844c0 1729
3f455f90
PK
1730 // ESC 8 Restore Cursor (DECRC).
1731 case '8':
1732 this.restoreCursor();
1733 this.state = normal;
1734 break;
8bc844c0 1735
3f455f90
PK
1736 // ESC # 3 DEC line height/width
1737 case '#':
1738 this.state = normal;
1739 i++;
1740 break;
8bc844c0 1741
3f455f90
PK
1742 // ESC H Tab Set (HTS is 0x88).
1743 case 'H':
1744 this.tabSet();
1745 break;
8bc844c0 1746
3f455f90
PK
1747 // ESC = Application Keypad (DECPAM).
1748 case '=':
1749 this.log('Serial port requested application keypad.');
1750 this.applicationKeypad = true;
1751 this.state = normal;
1752 break;
8bc844c0 1753
3f455f90
PK
1754 // ESC > Normal Keypad (DECPNM).
1755 case '>':
1756 this.log('Switching back to normal keypad.');
1757 this.applicationKeypad = false;
1758 this.state = normal;
1759 break;
8bc844c0 1760
3f455f90
PK
1761 default:
1762 this.state = normal;
1763 this.error('Unknown ESC control: %s.', ch);
1764 break;
1765 }
8bc844c0
CJ
1766 break;
1767
3f455f90
PK
1768 case charset:
1769 switch (ch) {
1770 case '0': // DEC Special Character and Line Drawing Set.
1771 cs = Terminal.charsets.SCLD;
1772 break;
1773 case 'A': // UK
1774 cs = Terminal.charsets.UK;
1775 break;
1776 case 'B': // United States (USASCII).
1777 cs = Terminal.charsets.US;
1778 break;
1779 case '4': // Dutch
1780 cs = Terminal.charsets.Dutch;
1781 break;
1782 case 'C': // Finnish
1783 case '5':
1784 cs = Terminal.charsets.Finnish;
1785 break;
1786 case 'R': // French
1787 cs = Terminal.charsets.French;
1788 break;
1789 case 'Q': // FrenchCanadian
1790 cs = Terminal.charsets.FrenchCanadian;
1791 break;
1792 case 'K': // German
1793 cs = Terminal.charsets.German;
1794 break;
1795 case 'Y': // Italian
1796 cs = Terminal.charsets.Italian;
1797 break;
1798 case 'E': // NorwegianDanish
1799 case '6':
1800 cs = Terminal.charsets.NorwegianDanish;
1801 break;
1802 case 'Z': // Spanish
1803 cs = Terminal.charsets.Spanish;
1804 break;
1805 case 'H': // Swedish
1806 case '7':
1807 cs = Terminal.charsets.Swedish;
1808 break;
1809 case '=': // Swiss
1810 cs = Terminal.charsets.Swiss;
1811 break;
1812 case '/': // ISOLatin (actually /A)
1813 cs = Terminal.charsets.ISOLatin;
1814 i++;
1815 break;
1816 default: // Default
1817 cs = Terminal.charsets.US;
1818 break;
1819 }
1820 this.setgCharset(this.gcharset, cs);
1821 this.gcharset = null;
1822 this.state = normal;
8bc844c0
CJ
1823 break;
1824
3f455f90
PK
1825 case osc:
1826 // OSC Ps ; Pt ST
1827 // OSC Ps ; Pt BEL
1828 // Set Text Parameters.
1829 if (ch === '\x1b' || ch === '\x07') {
1830 if (ch === '\x1b') i++;
8bc844c0 1831
3f455f90 1832 this.params.push(this.currentParam);
8bc844c0 1833
3f455f90
PK
1834 switch (this.params[0]) {
1835 case 0:
1836 case 1:
1837 case 2:
1838 if (this.params[1]) {
1839 this.title = this.params[1];
1840 this.handleTitle(this.title);
1841 }
1842 break;
1843 case 3:
1844 // set X property
1845 break;
1846 case 4:
1847 case 5:
1848 // change dynamic colors
1849 break;
1850 case 10:
1851 case 11:
1852 case 12:
1853 case 13:
1854 case 14:
1855 case 15:
1856 case 16:
1857 case 17:
1858 case 18:
1859 case 19:
1860 // change dynamic ui colors
1861 break;
1862 case 46:
1863 // change log file
1864 break;
1865 case 50:
1866 // dynamic font
1867 break;
1868 case 51:
1869 // emacs shell
1870 break;
1871 case 52:
1872 // manipulate selection data
1873 break;
1874 case 104:
1875 case 105:
1876 case 110:
1877 case 111:
1878 case 112:
1879 case 113:
1880 case 114:
1881 case 115:
1882 case 116:
1883 case 117:
1884 case 118:
1885 // reset colors
1886 break;
1887 }
8bc844c0 1888
3f455f90
PK
1889 this.params = [];
1890 this.currentParam = 0;
1891 this.state = normal;
1892 } else {
1893 if (!this.params.length) {
1894 if (ch >= '0' && ch <= '9') {
1895 this.currentParam =
1896 this.currentParam * 10 + ch.charCodeAt(0) - 48;
1897 } else if (ch === ';') {
1898 this.params.push(this.currentParam);
1899 this.currentParam = '';
1900 }
1901 } else {
1902 this.currentParam += ch;
1903 }
1904 }
8bc844c0
CJ
1905 break;
1906
3f455f90
PK
1907 case csi:
1908 // '?', '>', '!'
1909 if (ch === '?' || ch === '>' || ch === '!') {
1910 this.prefix = ch;
1911 break;
8bc844c0 1912 }
8bc844c0 1913
3f455f90
PK
1914 // 0 - 9
1915 if (ch >= '0' && ch <= '9') {
1916 this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48;
1917 break;
1918 }
8bc844c0 1919
3f455f90
PK
1920 // '$', '"', ' ', '\''
1921 if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') {
1922 this.postfix = ch;
1923 break;
1924 }
1925
1926 this.params.push(this.currentParam);
1927 this.currentParam = 0;
1928
1929 // ';'
1930 if (ch === ';') break;
1931
1932 this.state = normal;
1933
1934 switch (ch) {
1935 // CSI Ps A
1936 // Cursor Up Ps Times (default = 1) (CUU).
1937 case 'A':
1938 this.cursorUp(this.params);
1939 break;
1940
1941 // CSI Ps B
1942 // Cursor Down Ps Times (default = 1) (CUD).
1943 case 'B':
1944 this.cursorDown(this.params);
1945 break;
1946
1947 // CSI Ps C
1948 // Cursor Forward Ps Times (default = 1) (CUF).
1949 case 'C':
1950 this.cursorForward(this.params);
1951 break;
1952
1953 // CSI Ps D
1954 // Cursor Backward Ps Times (default = 1) (CUB).
1955 case 'D':
1956 this.cursorBackward(this.params);
1957 break;
1958
1959 // CSI Ps ; Ps H
1960 // Cursor Position [row;column] (default = [1,1]) (CUP).
1961 case 'H':
1962 this.cursorPos(this.params);
1963 break;
1964
1965 // CSI Ps J Erase in Display (ED).
1966 case 'J':
1967 this.eraseInDisplay(this.params);
1968 break;
1969
1970 // CSI Ps K Erase in Line (EL).
1971 case 'K':
1972 this.eraseInLine(this.params);
1973 break;
1974
1975 // CSI Pm m Character Attributes (SGR).
1976 case 'm':
1977 if (!this.prefix) {
1978 this.charAttributes(this.params);
1979 }
1980 break;
1981
1982 // CSI Ps n Device Status Report (DSR).
1983 case 'n':
1984 if (!this.prefix) {
1985 this.deviceStatus(this.params);
1986 }
1987 break;
1988
1989 /**
1990 * Additions
1991 */
1992
1993 // CSI Ps @
1994 // Insert Ps (Blank) Character(s) (default = 1) (ICH).
1995 case '@':
1996 this.insertChars(this.params);
1997 break;
1998
1999 // CSI Ps E
2000 // Cursor Next Line Ps Times (default = 1) (CNL).
2001 case 'E':
2002 this.cursorNextLine(this.params);
2003 break;
2004
2005 // CSI Ps F
2006 // Cursor Preceding Line Ps Times (default = 1) (CNL).
2007 case 'F':
2008 this.cursorPrecedingLine(this.params);
2009 break;
2010
2011 // CSI Ps G
2012 // Cursor Character Absolute [column] (default = [row,1]) (CHA).
2013 case 'G':
2014 this.cursorCharAbsolute(this.params);
2015 break;
2016
2017 // CSI Ps L
2018 // Insert Ps Line(s) (default = 1) (IL).
2019 case 'L':
2020 this.insertLines(this.params);
2021 break;
2022
2023 // CSI Ps M
2024 // Delete Ps Line(s) (default = 1) (DL).
2025 case 'M':
2026 this.deleteLines(this.params);
2027 break;
2028
2029 // CSI Ps P
2030 // Delete Ps Character(s) (default = 1) (DCH).
2031 case 'P':
2032 this.deleteChars(this.params);
2033 break;
2034
2035 // CSI Ps X
2036 // Erase Ps Character(s) (default = 1) (ECH).
2037 case 'X':
2038 this.eraseChars(this.params);
2039 break;
2040
2041 // CSI Pm ` Character Position Absolute
2042 // [column] (default = [row,1]) (HPA).
2043 case '`':
2044 this.charPosAbsolute(this.params);
2045 break;
2046
2047 // 141 61 a * HPR -
2048 // Horizontal Position Relative
2049 case 'a':
2050 this.HPositionRelative(this.params);
2051 break;
2052
2053 // CSI P s c
2054 // Send Device Attributes (Primary DA).
2055 // CSI > P s c
2056 // Send Device Attributes (Secondary DA)
2057 case 'c':
2058 this.sendDeviceAttributes(this.params);
2059 break;
2060
2061 // CSI Pm d
2062 // Line Position Absolute [row] (default = [1,column]) (VPA).
2063 case 'd':
2064 this.linePosAbsolute(this.params);
2065 break;
2066
2067 // 145 65 e * VPR - Vertical Position Relative
2068 case 'e':
2069 this.VPositionRelative(this.params);
2070 break;
2071
2072 // CSI Ps ; Ps f
2073 // Horizontal and Vertical Position [row;column] (default =
2074 // [1,1]) (HVP).
2075 case 'f':
2076 this.HVPosition(this.params);
2077 break;
2078
2079 // CSI Pm h Set Mode (SM).
2080 // CSI ? Pm h - mouse escape codes, cursor escape codes
2081 case 'h':
2082 this.setMode(this.params);
2083 break;
2084
2085 // CSI Pm l Reset Mode (RM).
2086 // CSI ? Pm l
2087 case 'l':
2088 this.resetMode(this.params);
2089 break;
2090
2091 // CSI Ps ; Ps r
2092 // Set Scrolling Region [top;bottom] (default = full size of win-
2093 // dow) (DECSTBM).
2094 // CSI ? Pm r
2095 case 'r':
2096 this.setScrollRegion(this.params);
2097 break;
2098
2099 // CSI s
2100 // Save cursor (ANSI.SYS).
2101 case 's':
2102 this.saveCursor(this.params);
2103 break;
2104
2105 // CSI u
2106 // Restore cursor (ANSI.SYS).
2107 case 'u':
2108 this.restoreCursor(this.params);
2109 break;
2110
2111 /**
2112 * Lesser Used
2113 */
2114
2115 // CSI Ps I
2116 // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
2117 case 'I':
2118 this.cursorForwardTab(this.params);
2119 break;
2120
2121 // CSI Ps S Scroll up Ps lines (default = 1) (SU).
2122 case 'S':
2123 this.scrollUp(this.params);
2124 break;
2125
2126 // CSI Ps T Scroll down Ps lines (default = 1) (SD).
2127 // CSI Ps ; Ps ; Ps ; Ps ; Ps T
2128 // CSI > Ps; Ps T
2129 case 'T':
2130 // if (this.prefix === '>') {
2131 // this.resetTitleModes(this.params);
2132 // break;
2133 // }
2134 // if (this.params.length > 2) {
2135 // this.initMouseTracking(this.params);
2136 // break;
2137 // }
2138 if (this.params.length < 2 && !this.prefix) {
2139 this.scrollDown(this.params);
2140 }
2141 break;
2142
2143 // CSI Ps Z
2144 // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
2145 case 'Z':
2146 this.cursorBackwardTab(this.params);
2147 break;
2148
2149 // CSI Ps b Repeat the preceding graphic character Ps times (REP).
2150 case 'b':
2151 this.repeatPrecedingCharacter(this.params);
2152 break;
2153
2154 // CSI Ps g Tab Clear (TBC).
2155 case 'g':
2156 this.tabClear(this.params);
2157 break;
2158
2159 // CSI Pm i Media Copy (MC).
2160 // CSI ? Pm i
2161 // case 'i':
2162 // this.mediaCopy(this.params);
2163 // break;
2164
2165 // CSI Pm m Character Attributes (SGR).
2166 // CSI > Ps; Ps m
2167 // case 'm': // duplicate
2168 // if (this.prefix === '>') {
2169 // this.setResources(this.params);
2170 // } else {
2171 // this.charAttributes(this.params);
2172 // }
2173 // break;
2174
2175 // CSI Ps n Device Status Report (DSR).
2176 // CSI > Ps n
2177 // case 'n': // duplicate
2178 // if (this.prefix === '>') {
2179 // this.disableModifiers(this.params);
2180 // } else {
2181 // this.deviceStatus(this.params);
2182 // }
2183 // break;
2184
2185 // CSI > Ps p Set pointer mode.
2186 // CSI ! p Soft terminal reset (DECSTR).
2187 // CSI Ps$ p
2188 // Request ANSI mode (DECRQM).
2189 // CSI ? Ps$ p
2190 // Request DEC private mode (DECRQM).
2191 // CSI Ps ; Ps " p
2192 case 'p':
2193 switch (this.prefix) {
2194 // case '>':
2195 // this.setPointerMode(this.params);
2196 // break;
2197 case '!':
2198 this.softReset(this.params);
2199 break;
2200 // case '?':
2201 // if (this.postfix === '$') {
2202 // this.requestPrivateMode(this.params);
2203 // }
2204 // break;
2205 // default:
2206 // if (this.postfix === '"') {
2207 // this.setConformanceLevel(this.params);
2208 // } else if (this.postfix === '$') {
2209 // this.requestAnsiMode(this.params);
2210 // }
2211 // break;
2212 }
2213 break;
2214
2215 // CSI Ps q Load LEDs (DECLL).
2216 // CSI Ps SP q
2217 // CSI Ps " q
2218 // case 'q':
2219 // if (this.postfix === ' ') {
2220 // this.setCursorStyle(this.params);
2221 // break;
2222 // }
2223 // if (this.postfix === '"') {
2224 // this.setCharProtectionAttr(this.params);
2225 // break;
2226 // }
2227 // this.loadLEDs(this.params);
2228 // break;
2229
2230 // CSI Ps ; Ps r
2231 // Set Scrolling Region [top;bottom] (default = full size of win-
2232 // dow) (DECSTBM).
2233 // CSI ? Pm r
2234 // CSI Pt; Pl; Pb; Pr; Ps$ r
2235 // case 'r': // duplicate
2236 // if (this.prefix === '?') {
2237 // this.restorePrivateValues(this.params);
2238 // } else if (this.postfix === '$') {
2239 // this.setAttrInRectangle(this.params);
2240 // } else {
2241 // this.setScrollRegion(this.params);
2242 // }
2243 // break;
2244
2245 // CSI s Save cursor (ANSI.SYS).
2246 // CSI ? Pm s
2247 // case 's': // duplicate
2248 // if (this.prefix === '?') {
2249 // this.savePrivateValues(this.params);
2250 // } else {
2251 // this.saveCursor(this.params);
2252 // }
2253 // break;
2254
2255 // CSI Ps ; Ps ; Ps t
2256 // CSI Pt; Pl; Pb; Pr; Ps$ t
2257 // CSI > Ps; Ps t
2258 // CSI Ps SP t
2259 // case 't':
2260 // if (this.postfix === '$') {
2261 // this.reverseAttrInRectangle(this.params);
2262 // } else if (this.postfix === ' ') {
2263 // this.setWarningBellVolume(this.params);
2264 // } else {
2265 // if (this.prefix === '>') {
2266 // this.setTitleModeFeature(this.params);
2267 // } else {
2268 // this.manipulateWindow(this.params);
2269 // }
2270 // }
2271 // break;
2272
2273 // CSI u Restore cursor (ANSI.SYS).
2274 // CSI Ps SP u
2275 // case 'u': // duplicate
2276 // if (this.postfix === ' ') {
2277 // this.setMarginBellVolume(this.params);
2278 // } else {
2279 // this.restoreCursor(this.params);
2280 // }
2281 // break;
2282
2283 // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v
2284 // case 'v':
2285 // if (this.postfix === '$') {
2286 // this.copyRectagle(this.params);
2287 // }
2288 // break;
2289
2290 // CSI Pt ; Pl ; Pb ; Pr ' w
2291 // case 'w':
2292 // if (this.postfix === '\'') {
2293 // this.enableFilterRectangle(this.params);
2294 // }
2295 // break;
2296
2297 // CSI Ps x Request Terminal Parameters (DECREQTPARM).
2298 // CSI Ps x Select Attribute Change Extent (DECSACE).
2299 // CSI Pc; Pt; Pl; Pb; Pr$ x
2300 // case 'x':
2301 // if (this.postfix === '$') {
2302 // this.fillRectangle(this.params);
2303 // } else {
2304 // this.requestParameters(this.params);
2305 // //this.__(this.params);
2306 // }
2307 // break;
2308
2309 // CSI Ps ; Pu ' z
2310 // CSI Pt; Pl; Pb; Pr$ z
2311 // case 'z':
2312 // if (this.postfix === '\'') {
2313 // this.enableLocatorReporting(this.params);
2314 // } else if (this.postfix === '$') {
2315 // this.eraseRectangle(this.params);
2316 // }
2317 // break;
2318
2319 // CSI Pm ' {
2320 // CSI Pt; Pl; Pb; Pr$ {
2321 // case '{':
2322 // if (this.postfix === '\'') {
2323 // this.setLocatorEvents(this.params);
2324 // } else if (this.postfix === '$') {
2325 // this.selectiveEraseRectangle(this.params);
2326 // }
2327 // break;
2328
2329 // CSI Ps ' |
2330 // case '|':
2331 // if (this.postfix === '\'') {
2332 // this.requestLocatorPosition(this.params);
2333 // }
2334 // break;
2335
2336 // CSI P m SP }
2337 // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
2338 // case '}':
2339 // if (this.postfix === ' ') {
2340 // this.insertColumns(this.params);
2341 // }
2342 // break;
2343
2344 // CSI P m SP ~
2345 // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
2346 // case '~':
2347 // if (this.postfix === ' ') {
2348 // this.deleteColumns(this.params);
2349 // }
2350 // break;
2351
2352 default:
2353 this.error('Unknown CSI code: %s.', ch);
2354 break;
2355 }
2356
2357 this.prefix = '';
2358 this.postfix = '';
2359 break;
2360
2361 case dcs:
2362 if (ch === '\x1b' || ch === '\x07') {
2363 if (ch === '\x1b') i++;
2364
2365 switch (this.prefix) {
2366 // User-Defined Keys (DECUDK).
2367 case '':
2368 break;
2369
2370 // Request Status String (DECRQSS).
2371 // test: echo -e '\eP$q"p\e\\'
2372 case '$q':
2373 var pt = this.currentParam
2374 , valid = false;
2375
2376 switch (pt) {
2377 // DECSCA
2378 case '"q':
2379 pt = '0"q';
2380 break;
2381
2382 // DECSCL
2383 case '"p':
2384 pt = '61"p';
2385 break;
2386
2387 // DECSTBM
2388 case 'r':
2389 pt = ''
2390 + (this.scrollTop + 1)
2391 + ';'
2392 + (this.scrollBottom + 1)
2393 + 'r';
2394 break;
2395
2396 // SGR
2397 case 'm':
2398 pt = '0m';
2399 break;
2400
2401 default:
2402 this.error('Unknown DCS Pt: %s.', pt);
2403 pt = '';
2404 break;
2405 }
2406
2407 this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\');
2408 break;
2409
2410 // Set Termcap/Terminfo Data (xterm, experimental).
2411 case '+p':
2412 break;
2413
2414 // Request Termcap/Terminfo String (xterm, experimental)
2415 // Regular xterm does not even respond to this sequence.
2416 // This can cause a small glitch in vim.
2417 // test: echo -ne '\eP+q6b64\e\\'
2418 case '+q':
2419 var pt = this.currentParam
2420 , valid = false;
2421
2422 this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\');
2423 break;
2424
2425 default:
2426 this.error('Unknown DCS prefix: %s.', this.prefix);
2427 break;
2428 }
2429
2430 this.currentParam = 0;
2431 this.prefix = '';
2432 this.state = normal;
2433 } else if (!this.currentParam) {
2434 if (!this.prefix && ch !== '$' && ch !== '+') {
2435 this.currentParam = ch;
2436 } else if (this.prefix.length === 2) {
2437 this.currentParam = ch;
2438 } else {
2439 this.prefix += ch;
2440 }
2441 } else {
2442 this.currentParam += ch;
2443 }
2444 break;
2445
2446 case ignore:
2447 // For PM and APC.
2448 if (ch === '\x1b' || ch === '\x07') {
2449 if (ch === '\x1b') i++;
2450 this.state = normal;
2451 }
2452 break;
2453 }
2454 }
2455
2456 this.updateRange(this.y);
2457 this.refresh(this.refreshStart, this.refreshEnd);
2458 };
2459
2460 Terminal.prototype.writeln = function(data) {
2461 this.write(data + '\r\n');
2462 };
2463
2464 // Key Resources:
2465 // https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
2466 Terminal.prototype.keyDown = function(ev) {
3a866cf2
DI
2467 var self = this;
2468 var result = this.evaluateKeyEscapeSequence(ev);
2469
2470 if (result.scrollDisp) {
2471 this.scrollDisp(result.scrollDisp);
2472 return this.cancel(ev);
2473 }
2474
b01165c1 2475 if (isThirdLevelShift(this, ev)) {
2476 return true;
2477 }
2478
2479 if (result.cancel ) {
3a866cf2
DI
2480 // The event is canceled at the end already, is this necessary?
2481 this.cancel(ev, true);
2482 }
2483
b01165c1 2484 if (!result.key) {
3a866cf2
DI
2485 return true;
2486 }
2487
2488 this.emit('keydown', ev);
2489 this.emit('key', result.key, ev);
2490 this.showCursor();
2491 this.handler(result.key);
2492
2493 return this.cancel(ev, true);
2494 };
3f455f90 2495
0535f942
DI
2496 /**
2497 * Returns an object that determines how a KeyboardEvent should be handled. The key of the
2498 * returned value is the new key code to pass to the PTY.
2499 *
2500 * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
2501 */
3a866cf2
DI
2502 Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
2503 var result = {
2504 // Whether to cancel event propogation (NOTE: this may not be needed since the event is
2505 // canceled at the end of keyDown
2506 cancel: false,
2507 // The new key even to emit
2508 key: undefined,
2509 // The number of characters to scroll, if this is defined it will cancel the event
2510 scrollDisp: undefined
2511 };
3f455f90
PK
2512 switch (ev.keyCode) {
2513 // backspace
2514 case 8:
2515 if (ev.shiftKey) {
3a866cf2 2516 result.key = '\x08'; // ^H
3f455f90
PK
2517 break;
2518 }
3a866cf2 2519 result.key = '\x7f'; // ^?
3f455f90
PK
2520 break;
2521 // tab
2522 case 9:
2523 if (ev.shiftKey) {
3a866cf2 2524 result.key = '\x1b[Z';
3f455f90
PK
2525 break;
2526 }
3a866cf2
DI
2527 result.key = '\t';
2528 result.cancel = true;
3f455f90
PK
2529 break;
2530 // return/enter
2531 case 13:
3a866cf2
DI
2532 result.key = '\r';
2533 result.cancel = true;
3f455f90
PK
2534 break;
2535 // escape
2536 case 27:
3a866cf2
DI
2537 result.key = '\x1b';
2538 result.cancel = true;
3f455f90
PK
2539 break;
2540 // left-arrow
2541 case 37:
72ed03ad 2542 if (ev.altKey) {
3a866cf2
DI
2543 result.key = '\x1bb' // Jump a word back
2544 result.cancel = true;
72ed03ad 2545 break;
e2aaa8d3
DI
2546 }
2547 if (ev.ctrlKey) {
3a866cf2 2548 result.key = '\x1b[5D'; // Jump a word back
e2aaa8d3
DI
2549 break;
2550 }
2551 if (this.applicationCursor) {
3a866cf2 2552 result.key = '\x1bOD'; // SS3 as ^[O for 7-bit
3f455f90
PK
2553 break;
2554 }
3a866cf2 2555 result.key = '\x1b[D';
3f455f90
PK
2556 break;
2557 // right-arrow
2558 case 39:
72ed03ad 2559 if (ev.altKey) {
3a866cf2
DI
2560 result.key = '\x1bf' // Jump a word forward
2561 result.cancel = true;
72ed03ad 2562 break;
e2aaa8d3
DI
2563 }
2564 if (ev.ctrlKey) {
3a866cf2 2565 result.key = '\x1b[5C'; // Jump a word forward
e2aaa8d3
DI
2566 break;
2567 }
2568 if (this.applicationCursor) {
3a866cf2 2569 result.key = '\x1bOC';
3f455f90
PK
2570 break;
2571 }
3a866cf2 2572 result.key = '\x1b[C';
3f455f90
PK
2573 break;
2574 // up-arrow
2575 case 38:
2576 if (this.applicationCursor) {
3a866cf2 2577 result.key = '\x1bOA';
3f455f90
PK
2578 break;
2579 }
2580 if (ev.ctrlKey) {
3a866cf2 2581 result.scrollDisp = -1;
3f455f90 2582 } else {
3a866cf2 2583 result.key = '\x1b[A';
3f455f90
PK
2584 }
2585 break;
2586 // down-arrow
2587 case 40:
2588 if (this.applicationCursor) {
3a866cf2 2589 result.key = '\x1bOB';
3f455f90
PK
2590 break;
2591 }
2592 if (ev.ctrlKey) {
3a866cf2 2593 result.scrollDisp = 1;
3f455f90 2594 } else {
3a866cf2 2595 result.key = '\x1b[B';
3f455f90
PK
2596 }
2597 break;
3f455f90
PK
2598 // insert
2599 case 45:
524db022 2600 if (!ev.shiftKey && !ev.ctrlKey) {
2601 // <Ctrl> or <Shift> + <Insert> are used to
2602 // copy-paste on some systems.
740ae96f 2603 result.key = '\x1b[2~';
524db022 2604 }
3f455f90 2605 break;
0535f942
DI
2606 // delete
2607 case 46: result.key = '\x1b[3~'; break;
3f455f90
PK
2608 // home
2609 case 36:
2610 if (this.applicationKeypad) {
3a866cf2 2611 result.key = '\x1bOH';
3f455f90
PK
2612 break;
2613 }
3a866cf2 2614 result.key = '\x1bOH';
3f455f90
PK
2615 break;
2616 // end
2617 case 35:
2618 if (this.applicationKeypad) {
3a866cf2 2619 result.key = '\x1bOF';
3f455f90
PK
2620 break;
2621 }
3a866cf2 2622 result.key = '\x1bOF';
3f455f90
PK
2623 break;
2624 // page up
2625 case 33:
2626 if (ev.shiftKey) {
3a866cf2 2627 result.scrollDisp = -(this.rows - 1);
3f455f90 2628 } else {
3a866cf2 2629 result.key = '\x1b[5~';
3f455f90
PK
2630 }
2631 break;
2632 // page down
2633 case 34:
2634 if (ev.shiftKey) {
3a866cf2 2635 result.scrollDisp = this.rows - 1;
3f455f90 2636 } else {
3a866cf2 2637 result.key = '\x1b[6~';
3f455f90
PK
2638 }
2639 break;
3a866cf2
DI
2640 // F1-F12
2641 case 112: result.key = '\x1bOP'; break;
2642 case 113: result.key = '\x1bOQ'; break;
2643 case 114: result.key = '\x1bOR'; break;
2644 case 115: result.key = '\x1bOS'; break;
2645 case 116: result.key = '\x1b[15~'; break;
2646 case 117: result.key = '\x1b[17~'; break;
2647 case 118: result.key = '\x1b[18~'; break;
2648 case 119: result.key = '\x1b[19~'; break;
2649 case 120: result.key = '\x1b[20~'; break;
2650 case 121: result.key = '\x1b[21~'; break;
2651 case 122: result.key = '\x1b[23~'; break;
2652 case 123: result.key = '\x1b[24~'; break;
3f455f90
PK
2653 default:
2654 // a-z and space
369e1f4c 2655 if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
3f455f90 2656 if (ev.keyCode >= 65 && ev.keyCode <= 90) {
3a866cf2 2657 result.key = String.fromCharCode(ev.keyCode - 64);
3f455f90
PK
2658 } else if (ev.keyCode === 32) {
2659 // NUL
3a866cf2 2660 result.key = String.fromCharCode(0);
3f455f90
PK
2661 } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
2662 // escape, file sep, group sep, record sep, unit sep
3a866cf2 2663 result.key = String.fromCharCode(ev.keyCode - 51 + 27);
3f455f90
PK
2664 } else if (ev.keyCode === 56) {
2665 // delete
3a866cf2 2666 result.key = String.fromCharCode(127);
3f455f90
PK
2667 } else if (ev.keyCode === 219) {
2668 // ^[ - escape
3a866cf2 2669 result.key = String.fromCharCode(27);
3f455f90
PK
2670 } else if (ev.keyCode === 221) {
2671 // ^] - group sep
3a866cf2 2672 result.key = String.fromCharCode(29);
3f455f90 2673 }
b01165c1 2674 } else if (!this.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) {
2675 // On Mac this is a third level shift. Use <Esc> instead.
3f455f90 2676 if (ev.keyCode >= 65 && ev.keyCode <= 90) {
3a866cf2 2677 result.key = '\x1b' + String.fromCharCode(ev.keyCode + 32);
3f455f90 2678 } else if (ev.keyCode === 192) {
3a866cf2 2679 result.key = '\x1b`';
3f455f90 2680 } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {
3a866cf2 2681 result.key = '\x1b' + (ev.keyCode - 48);
3f455f90
PK
2682 }
2683 }
2684 break;
2685 }
3a866cf2 2686 return result;
3f455f90
PK
2687 };
2688
2689 Terminal.prototype.setgLevel = function(g) {
2690 this.glevel = g;
2691 this.charset = this.charsets[g];
2692 };
2693
2694 Terminal.prototype.setgCharset = function(g, charset) {
2695 this.charsets[g] = charset;
2696 if (this.glevel === g) {
2697 this.charset = charset;
2698 }
2699 };
2700
2701 Terminal.prototype.keyPress = function(ev) {
2702 var key;
2703
fc7b22dc
DI
2704 this.cancel(ev);
2705
3f455f90
PK
2706 if (ev.charCode) {
2707 key = ev.charCode;
2708 } else if (ev.which == null) {
2709 key = ev.keyCode;
2710 } else if (ev.which !== 0 && ev.charCode !== 0) {
2711 key = ev.which;
2712 } else {
2713 return false;
2714 }
2715
b01165c1 2716 if (!key || (
2717 (ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this, ev)
2718 )) {
3f455f90
PK
2719 return false;
2720 }
2721
2722 key = String.fromCharCode(key);
2723
2724 this.emit('keypress', key, ev);
2725 this.emit('key', key, ev);
2726 this.showCursor();
2727 this.handler(key);
2728
2729 return false;
2730 };
2731
2732 Terminal.prototype.send = function(data) {
2733 var self = this;
2734
2735 if (!this.queue) {
2736 setTimeout(function() {
2737 self.handler(self.queue);
2738 self.queue = '';
2739 }, 1);
2740 }
2741
2742 this.queue += data;
2743 };
2744
2745 Terminal.prototype.bell = function() {
2746 if (!this.visualBell) return;
2747 var self = this;
2748 this.element.style.borderColor = 'white';
2749 setTimeout(function() {
2750 self.element.style.borderColor = '';
2751 }, 10);
2752 if (this.popOnBell) this.focus();
2753 };
2754
2755 Terminal.prototype.log = function() {
2756 if (!this.debug) return;
2757 if (!this.context.console || !this.context.console.log) return;
2758 var args = Array.prototype.slice.call(arguments);
2759 this.context.console.log.apply(this.context.console, args);
2760 };
2761
2762 Terminal.prototype.error = function() {
2763 if (!this.debug) return;
2764 if (!this.context.console || !this.context.console.error) return;
2765 var args = Array.prototype.slice.call(arguments);
2766 this.context.console.error.apply(this.context.console, args);
2767 };
2768
fd5be55d
DI
2769 /**
2770 * Resizes the terminal.
2771 *
2772 * @param {number} x The number of columns to resize to.
2773 * @param {number} y The number of rows to resize to.
2774 *
2775 * @public
2776 */
3f455f90
PK
2777 Terminal.prototype.resize = function(x, y) {
2778 var line
2779 , el
2780 , i
2781 , j
eee99f62
DI
2782 , ch
2783 , addToY;
3f455f90 2784
e721bdc9
DI
2785 if (x === this.cols && y === this.rows) {
2786 return;
2787 }
2788
3f455f90
PK
2789 if (x < 1) x = 1;
2790 if (y < 1) y = 1;
2791
2792 // resize cols
2793 j = this.cols;
2794 if (j < x) {
838c11b6 2795 ch = [this.defAttr, ' ', 1]; // does xterm use the default attr?
3f455f90
PK
2796 i = this.lines.length;
2797 while (i--) {
2798 while (this.lines[i].length < x) {
2799 this.lines[i].push(ch);
2800 }
2801 }
e721bdc9 2802 } else { // (j > x)
3f455f90
PK
2803 i = this.lines.length;
2804 while (i--) {
2805 while (this.lines[i].length > x) {
2806 this.lines[i].pop();
2807 }
2808 }
2809 }
2810 this.setupStops(j);
2811 this.cols = x;
2812
2813 // resize rows
2814 j = this.rows;
eee99f62 2815 addToY = 0;
3f455f90
PK
2816 if (j < y) {
2817 el = this.element;
2818 while (j++ < y) {
eee99f62 2819 // y is rows, not this.y
3f455f90 2820 if (this.lines.length < y + this.ybase) {
eee99f62
DI
2821 if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
2822 // There is room above the buffer and there are no empty elements below the line,
2823 // scroll up
2824 this.ybase--;
eee99f62 2825 addToY++
05041722
DI
2826 if (this.ydisp > 0) {
2827 // Viewport is at the top of the buffer, must increase downwards
87c9370e
DI
2828 this.ydisp--;
2829 }
eee99f62
DI
2830 } else {
2831 // Add a blank line if there is no buffer left at the top to scroll to, or if there
2832 // are blank lines after the cursor
2833 this.lines.push(this.blankLine());
2834 }
3f455f90
PK
2835 }
2836 if (this.children.length < y) {
2837 this.insertRow();
2838 }
2839 }
e721bdc9 2840 } else { // (j > y)
3f455f90
PK
2841 while (j-- > y) {
2842 if (this.lines.length > y + this.ybase) {
eee99f62
DI
2843 if (this.lines.length > this.ybase + this.y + 1) {
2844 // The line is a blank line below the cursor, remove it
df268ad5
DI
2845 this.lines.pop();
2846 } else {
eee99f62 2847 // The line is the cursor, scroll down
df268ad5
DI
2848 this.ybase++;
2849 this.ydisp++;
2850 }
3f455f90
PK
2851 }
2852 if (this.children.length > y) {
685bc54b 2853 el = this.children.shift();
3f455f90
PK
2854 if (!el) continue;
2855 el.parentNode.removeChild(el);
2856 }
2857 }
2858 }
2859 this.rows = y;
2860
2861 /*
2862 * Make sure that the cursor stays on screen
2863 */
2864 if (this.y >= y) {
2865 this.y = y - 1;
2866 }
eee99f62
DI
2867 if (addToY) {
2868 this.y += addToY;
2869 }
3f455f90
PK
2870
2871 if (this.x >= x) {
2872 this.x = x - 1;
2873 }
2874
2875 this.scrollTop = 0;
2876 this.scrollBottom = y - 1;
2877
2878 this.refresh(0, this.rows - 1);
2879
2880 this.normal = null;
2881
2882 this.emit('resize', {terminal: this, cols: x, rows: y});
2883 };
2884
2885 Terminal.prototype.updateRange = function(y) {
2886 if (y < this.refreshStart) this.refreshStart = y;
2887 if (y > this.refreshEnd) this.refreshEnd = y;
2888 // if (y > this.refreshEnd) {
2889 // this.refreshEnd = y;
2890 // if (y > this.rows - 1) {
2891 // this.refreshEnd = this.rows - 1;
2892 // }
2893 // }
2894 };
2895
2896 Terminal.prototype.maxRange = function() {
2897 this.refreshStart = 0;
2898 this.refreshEnd = this.rows - 1;
2899 };
2900
2901 Terminal.prototype.setupStops = function(i) {
2902 if (i != null) {
2903 if (!this.tabs[i]) {
2904 i = this.prevStop(i);
2905 }
2906 } else {
2907 this.tabs = {};
2908 i = 0;
2909 }
2910
2911 for (; i < this.cols; i += 8) {
2912 this.tabs[i] = true;
2913 }
2914 };
2915
2916 Terminal.prototype.prevStop = function(x) {
2917 if (x == null) x = this.x;
2918 while (!this.tabs[--x] && x > 0);
2919 return x >= this.cols
2920 ? this.cols - 1
2921 : x < 0 ? 0 : x;
2922 };
2923
2924 Terminal.prototype.nextStop = function(x) {
2925 if (x == null) x = this.x;
2926 while (!this.tabs[++x] && x < this.cols);
2927 return x >= this.cols
2928 ? this.cols - 1
2929 : x < 0 ? 0 : x;
2930 };
2931
2932 Terminal.prototype.eraseRight = function(x, y) {
2933 var line = this.lines[this.ybase + y]
874ba72f 2934 , ch = [this.eraseAttr(), ' ', 1]; // xterm
3f455f90
PK
2935
2936
2937 for (; x < this.cols; x++) {
2938 line[x] = ch;
2939 }
2940
2941 this.updateRange(y);
2942 };
2943
2944 Terminal.prototype.eraseLeft = function(x, y) {
2945 var line = this.lines[this.ybase + y]
874ba72f 2946 , ch = [this.eraseAttr(), ' ', 1]; // xterm
3f455f90
PK
2947
2948 x++;
2949 while (x--) line[x] = ch;
2950
2951 this.updateRange(y);
2952 };
2953
2954 Terminal.prototype.eraseLine = function(y) {
2955 this.eraseRight(0, y);
2956 };
2957
2958 Terminal.prototype.blankLine = function(cur) {
2959 var attr = cur
2960 ? this.eraseAttr()
2961 : this.defAttr;
2962
874ba72f 2963 var ch = [attr, ' ', 1] // width defaults to 1 halfwidth character
3f455f90
PK
2964 , line = []
2965 , i = 0;
2966
2967 for (; i < this.cols; i++) {
2968 line[i] = ch;
2969 }
2970
2971 return line;
2972 };
2973
2974 Terminal.prototype.ch = function(cur) {
2975 return cur
874ba72f
JB
2976 ? [this.eraseAttr(), ' ', 1]
2977 : [this.defAttr, ' ', 1];
3f455f90
PK
2978 };
2979
2980 Terminal.prototype.is = function(term) {
2981 var name = this.termName;
2982 return (name + '').indexOf(term) === 0;
2983 };
2984
2985 Terminal.prototype.handler = function(data) {
2986 this.emit('data', data);
2987 };
2988
2989 Terminal.prototype.handleTitle = function(title) {
2990 this.emit('title', title);
2991 };
2992
2993 /**
2994 * ESC
2995 */
2996
2997 // ESC D Index (IND is 0x84).
2998 Terminal.prototype.index = function() {
2999 this.y++;
3000 if (this.y > this.scrollBottom) {
3001 this.y--;
3002 this.scroll();
3003 }
3004 this.state = normal;
3005 };
3006
3007 // ESC M Reverse Index (RI is 0x8d).
3008 Terminal.prototype.reverseIndex = function() {
3009 var j;
3010 this.y--;
3011 if (this.y < this.scrollTop) {
3012 this.y++;
3013 // possibly move the code below to term.reverseScroll();
3014 // test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
3015 // blankLine(true) is xterm/linux behavior
3016 this.lines.splice(this.y + this.ybase, 0, this.blankLine(true));
3017 j = this.rows - 1 - this.scrollBottom;
3018 this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1);
3019 // this.maxRange();
3020 this.updateRange(this.scrollTop);
3021 this.updateRange(this.scrollBottom);
3022 }
3023 this.state = normal;
3024 };
3025
3026 // ESC c Full Reset (RIS).
3027 Terminal.prototype.reset = function() {
3028 this.options.rows = this.rows;
3029 this.options.cols = this.cols;
3030 Terminal.call(this, this.options);
3031 this.refresh(0, this.rows - 1);
3032 };
3033
3034 // ESC H Tab Set (HTS is 0x88).
3035 Terminal.prototype.tabSet = function() {
3036 this.tabs[this.x] = true;
3037 this.state = normal;
3038 };
3039
3040 /**
3041 * CSI
3042 */
3043
3044 // CSI Ps A
3045 // Cursor Up Ps Times (default = 1) (CUU).
3046 Terminal.prototype.cursorUp = function(params) {
3047 var param = params[0];
3048 if (param < 1) param = 1;
3049 this.y -= param;
3050 if (this.y < 0) this.y = 0;
3051 };
3052
3053 // CSI Ps B
3054 // Cursor Down Ps Times (default = 1) (CUD).
3055 Terminal.prototype.cursorDown = function(params) {
3056 var param = params[0];
3057 if (param < 1) param = 1;
3058 this.y += param;
3059 if (this.y >= this.rows) {
3060 this.y = this.rows - 1;
3061 }
3062 };
3063
3064 // CSI Ps C
3065 // Cursor Forward Ps Times (default = 1) (CUF).
3066 Terminal.prototype.cursorForward = function(params) {
3067 var param = params[0];
3068 if (param < 1) param = 1;
3069 this.x += param;
3070 if (this.x >= this.cols) {
3071 this.x = this.cols - 1;
3072 }
3073 };
3074
3075 // CSI Ps D
3076 // Cursor Backward Ps Times (default = 1) (CUB).
3077 Terminal.prototype.cursorBackward = function(params) {
3078 var param = params[0];
3079 if (param < 1) param = 1;
3080 this.x -= param;
3081 if (this.x < 0) this.x = 0;
3082 };
3083
3084 // CSI Ps ; Ps H
3085 // Cursor Position [row;column] (default = [1,1]) (CUP).
3086 Terminal.prototype.cursorPos = function(params) {
3087 var row, col;
3088
3089 row = params[0] - 1;
3090
3091 if (params.length >= 2) {
3092 col = params[1] - 1;
3093 } else {
3094 col = 0;
3095 }
3096
3097 if (row < 0) {
3098 row = 0;
3099 } else if (row >= this.rows) {
3100 row = this.rows - 1;
3101 }
3102
3103 if (col < 0) {
3104 col = 0;
3105 } else if (col >= this.cols) {
3106 col = this.cols - 1;
3107 }
3108
3109 this.x = col;
3110 this.y = row;
3111 };
3112
3113 // CSI Ps J Erase in Display (ED).
3114 // Ps = 0 -> Erase Below (default).
3115 // Ps = 1 -> Erase Above.
3116 // Ps = 2 -> Erase All.
3117 // Ps = 3 -> Erase Saved Lines (xterm).
3118 // CSI ? Ps J
3119 // Erase in Display (DECSED).
3120 // Ps = 0 -> Selective Erase Below (default).
3121 // Ps = 1 -> Selective Erase Above.
3122 // Ps = 2 -> Selective Erase All.
3123 Terminal.prototype.eraseInDisplay = function(params) {
3124 var j;
3125 switch (params[0]) {
3126 case 0:
3127 this.eraseRight(this.x, this.y);
3128 j = this.y + 1;
3129 for (; j < this.rows; j++) {
3130 this.eraseLine(j);
3131 }
3132 break;
3133 case 1:
3134 this.eraseLeft(this.x, this.y);
3135 j = this.y;
3136 while (j--) {
3137 this.eraseLine(j);
3138 }
3139 break;
3140 case 2:
3141 j = this.rows;
3142 while (j--) this.eraseLine(j);
3143 break;
3144 case 3:
3145 ; // no saved lines
3146 break;
3147 }
3148 };
3149
3150 // CSI Ps K Erase in Line (EL).
3151 // Ps = 0 -> Erase to Right (default).
3152 // Ps = 1 -> Erase to Left.
3153 // Ps = 2 -> Erase All.
3154 // CSI ? Ps K
3155 // Erase in Line (DECSEL).
3156 // Ps = 0 -> Selective Erase to Right (default).
3157 // Ps = 1 -> Selective Erase to Left.
3158 // Ps = 2 -> Selective Erase All.
3159 Terminal.prototype.eraseInLine = function(params) {
3160 switch (params[0]) {
3161 case 0:
3162 this.eraseRight(this.x, this.y);
3163 break;
3164 case 1:
3165 this.eraseLeft(this.x, this.y);
3166 break;
3167 case 2:
3168 this.eraseLine(this.y);
3169 break;
3170 }
3171 };
3172
3173 // CSI Pm m Character Attributes (SGR).
3174 // Ps = 0 -> Normal (default).
3175 // Ps = 1 -> Bold.
3176 // Ps = 4 -> Underlined.
3177 // Ps = 5 -> Blink (appears as Bold).
3178 // Ps = 7 -> Inverse.
3179 // Ps = 8 -> Invisible, i.e., hidden (VT300).
3180 // Ps = 2 2 -> Normal (neither bold nor faint).
3181 // Ps = 2 4 -> Not underlined.
3182 // Ps = 2 5 -> Steady (not blinking).
3183 // Ps = 2 7 -> Positive (not inverse).
3184 // Ps = 2 8 -> Visible, i.e., not hidden (VT300).
3185 // Ps = 3 0 -> Set foreground color to Black.
3186 // Ps = 3 1 -> Set foreground color to Red.
3187 // Ps = 3 2 -> Set foreground color to Green.
3188 // Ps = 3 3 -> Set foreground color to Yellow.
3189 // Ps = 3 4 -> Set foreground color to Blue.
3190 // Ps = 3 5 -> Set foreground color to Magenta.
3191 // Ps = 3 6 -> Set foreground color to Cyan.
3192 // Ps = 3 7 -> Set foreground color to White.
3193 // Ps = 3 9 -> Set foreground color to default (original).
3194 // Ps = 4 0 -> Set background color to Black.
3195 // Ps = 4 1 -> Set background color to Red.
3196 // Ps = 4 2 -> Set background color to Green.
3197 // Ps = 4 3 -> Set background color to Yellow.
3198 // Ps = 4 4 -> Set background color to Blue.
3199 // Ps = 4 5 -> Set background color to Magenta.
3200 // Ps = 4 6 -> Set background color to Cyan.
3201 // Ps = 4 7 -> Set background color to White.
3202 // Ps = 4 9 -> Set background color to default (original).
3203
3204 // If 16-color support is compiled, the following apply. Assume
3205 // that xterm's resources are set so that the ISO color codes are
3206 // the first 8 of a set of 16. Then the aixterm colors are the
3207 // bright versions of the ISO colors:
3208 // Ps = 9 0 -> Set foreground color to Black.
3209 // Ps = 9 1 -> Set foreground color to Red.
3210 // Ps = 9 2 -> Set foreground color to Green.
3211 // Ps = 9 3 -> Set foreground color to Yellow.
3212 // Ps = 9 4 -> Set foreground color to Blue.
3213 // Ps = 9 5 -> Set foreground color to Magenta.
3214 // Ps = 9 6 -> Set foreground color to Cyan.
3215 // Ps = 9 7 -> Set foreground color to White.
3216 // Ps = 1 0 0 -> Set background color to Black.
3217 // Ps = 1 0 1 -> Set background color to Red.
3218 // Ps = 1 0 2 -> Set background color to Green.
3219 // Ps = 1 0 3 -> Set background color to Yellow.
3220 // Ps = 1 0 4 -> Set background color to Blue.
3221 // Ps = 1 0 5 -> Set background color to Magenta.
3222 // Ps = 1 0 6 -> Set background color to Cyan.
3223 // Ps = 1 0 7 -> Set background color to White.
3224
3225 // If xterm is compiled with the 16-color support disabled, it
3226 // supports the following, from rxvt:
3227 // Ps = 1 0 0 -> Set foreground and background color to
3228 // default.
3229
3230 // If 88- or 256-color support is compiled, the following apply.
3231 // Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
3232 // Ps.
3233 // Ps = 4 8 ; 5 ; Ps -> Set background color to the second
3234 // Ps.
3235 Terminal.prototype.charAttributes = function(params) {
3236 // Optimize a single SGR0.
3237 if (params.length === 1 && params[0] === 0) {
3238 this.curAttr = this.defAttr;
3239 return;
3240 }
3241
3242 var l = params.length
3243 , i = 0
3244 , flags = this.curAttr >> 18
3245 , fg = (this.curAttr >> 9) & 0x1ff
3246 , bg = this.curAttr & 0x1ff
3247 , p;
3248
3249 for (; i < l; i++) {
3250 p = params[i];
3251 if (p >= 30 && p <= 37) {
3252 // fg color 8
3253 fg = p - 30;
3254 } else if (p >= 40 && p <= 47) {
3255 // bg color 8
3256 bg = p - 40;
3257 } else if (p >= 90 && p <= 97) {
3258 // fg color 16
3259 p += 8;
3260 fg = p - 90;
3261 } else if (p >= 100 && p <= 107) {
3262 // bg color 16
3263 p += 8;
3264 bg = p - 100;
3265 } else if (p === 0) {
3266 // default
3267 flags = this.defAttr >> 18;
3268 fg = (this.defAttr >> 9) & 0x1ff;
3269 bg = this.defAttr & 0x1ff;
3270 // flags = 0;
3271 // fg = 0x1ff;
3272 // bg = 0x1ff;
3273 } else if (p === 1) {
3274 // bold text
3275 flags |= 1;
3276 } else if (p === 4) {
3277 // underlined text
3278 flags |= 2;
3279 } else if (p === 5) {
3280 // blink
3281 flags |= 4;
3282 } else if (p === 7) {
3283 // inverse and positive
3284 // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
3285 flags |= 8;
3286 } else if (p === 8) {
3287 // invisible
3288 flags |= 16;
3289 } else if (p === 22) {
3290 // not bold
3291 flags &= ~1;
3292 } else if (p === 24) {
3293 // not underlined
3294 flags &= ~2;
3295 } else if (p === 25) {
3296 // not blink
3297 flags &= ~4;
3298 } else if (p === 27) {
3299 // not inverse
3300 flags &= ~8;
3301 } else if (p === 28) {
3302 // not invisible
3303 flags &= ~16;
3304 } else if (p === 39) {
3305 // reset fg
3306 fg = (this.defAttr >> 9) & 0x1ff;
3307 } else if (p === 49) {
3308 // reset bg
3309 bg = this.defAttr & 0x1ff;
3310 } else if (p === 38) {
3311 // fg color 256
3312 if (params[i + 1] === 2) {
3313 i += 2;
3314 fg = matchColor(
3315 params[i] & 0xff,
3316 params[i + 1] & 0xff,
3317 params[i + 2] & 0xff);
3318 if (fg === -1) fg = 0x1ff;
3319 i += 2;
3320 } else if (params[i + 1] === 5) {
3321 i += 2;
3322 p = params[i] & 0xff;
3323 fg = p;
3324 }
3325 } else if (p === 48) {
3326 // bg color 256
3327 if (params[i + 1] === 2) {
3328 i += 2;
3329 bg = matchColor(
3330 params[i] & 0xff,
3331 params[i + 1] & 0xff,
3332 params[i + 2] & 0xff);
3333 if (bg === -1) bg = 0x1ff;
3334 i += 2;
3335 } else if (params[i + 1] === 5) {
3336 i += 2;
3337 p = params[i] & 0xff;
3338 bg = p;
3339 }
3340 } else if (p === 100) {
3341 // reset fg/bg
3342 fg = (this.defAttr >> 9) & 0x1ff;
3343 bg = this.defAttr & 0x1ff;
3344 } else {
3345 this.error('Unknown SGR attribute: %d.', p);
3346 }
3347 }
3348
3349 this.curAttr = (flags << 18) | (fg << 9) | bg;
3350 };
3351
3352 // CSI Ps n Device Status Report (DSR).
3353 // Ps = 5 -> Status Report. Result (``OK'') is
3354 // CSI 0 n
3355 // Ps = 6 -> Report Cursor Position (CPR) [row;column].
3356 // Result is
3357 // CSI r ; c R
3358 // CSI ? Ps n
3359 // Device Status Report (DSR, DEC-specific).
3360 // Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
3361 // ? r ; c R (assumes page is zero).
3362 // Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
3363 // or CSI ? 1 1 n (not ready).
3364 // Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
3365 // or CSI ? 2 1 n (locked).
3366 // Ps = 2 6 -> Report Keyboard status as
3367 // CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
3368 // The last two parameters apply to VT400 & up, and denote key-
3369 // board ready and LK01 respectively.
3370 // Ps = 5 3 -> Report Locator status as
3371 // CSI ? 5 3 n Locator available, if compiled-in, or
3372 // CSI ? 5 0 n No Locator, if not.
3373 Terminal.prototype.deviceStatus = function(params) {
3374 if (!this.prefix) {
3375 switch (params[0]) {
3376 case 5:
3377 // status report
3378 this.send('\x1b[0n');
3379 break;
3380 case 6:
3381 // cursor position
3382 this.send('\x1b['
3383 + (this.y + 1)
3384 + ';'
3385 + (this.x + 1)
3386 + 'R');
3387 break;
3388 }
3389 } else if (this.prefix === '?') {
3390 // modern xterm doesnt seem to
3391 // respond to any of these except ?6, 6, and 5
3392 switch (params[0]) {
3393 case 6:
3394 // cursor position
3395 this.send('\x1b[?'
3396 + (this.y + 1)
3397 + ';'
3398 + (this.x + 1)
3399 + 'R');
3400 break;
3401 case 15:
3402 // no printer
3403 // this.send('\x1b[?11n');
3404 break;
3405 case 25:
3406 // dont support user defined keys
3407 // this.send('\x1b[?21n');
3408 break;
3409 case 26:
3410 // north american keyboard
3411 // this.send('\x1b[?27;1;0;0n');
3412 break;
3413 case 53:
3414 // no dec locator/mouse
3415 // this.send('\x1b[?50n');
3416 break;
3417 }
3418 }
3419 };
3420
3421 /**
3422 * Additions
3423 */
3424
3425 // CSI Ps @
3426 // Insert Ps (Blank) Character(s) (default = 1) (ICH).
3427 Terminal.prototype.insertChars = function(params) {
3428 var param, row, j, ch;
3429
3430 param = params[0];
3431 if (param < 1) param = 1;
3432
3433 row = this.y + this.ybase;
3434 j = this.x;
4afa08da 3435 ch = [this.eraseAttr(), ' ', 1]; // xterm
3f455f90
PK
3436
3437 while (param-- && j < this.cols) {
3438 this.lines[row].splice(j++, 0, ch);
3439 this.lines[row].pop();
3440 }
3441 };
3442
3443 // CSI Ps E
3444 // Cursor Next Line Ps Times (default = 1) (CNL).
3445 // same as CSI Ps B ?
3446 Terminal.prototype.cursorNextLine = function(params) {
3447 var param = params[0];
3448 if (param < 1) param = 1;
3449 this.y += param;
3450 if (this.y >= this.rows) {
3451 this.y = this.rows - 1;
3452 }
3453 this.x = 0;
3454 };
3455
3456 // CSI Ps F
3457 // Cursor Preceding Line Ps Times (default = 1) (CNL).
3458 // reuse CSI Ps A ?
3459 Terminal.prototype.cursorPrecedingLine = function(params) {
3460 var param = params[0];
3461 if (param < 1) param = 1;
3462 this.y -= param;
3463 if (this.y < 0) this.y = 0;
3464 this.x = 0;
3465 };
3466
3467 // CSI Ps G
3468 // Cursor Character Absolute [column] (default = [row,1]) (CHA).
3469 Terminal.prototype.cursorCharAbsolute = function(params) {
3470 var param = params[0];
3471 if (param < 1) param = 1;
3472 this.x = param - 1;
3473 };
3474
3475 // CSI Ps L
3476 // Insert Ps Line(s) (default = 1) (IL).
3477 Terminal.prototype.insertLines = function(params) {
3478 var param, row, j;
3479
3480 param = params[0];
3481 if (param < 1) param = 1;
3482 row = this.y + this.ybase;
3483
3484 j = this.rows - 1 - this.scrollBottom;
3485 j = this.rows - 1 + this.ybase - j + 1;
3486
3487 while (param--) {
3488 // test: echo -e '\e[44m\e[1L\e[0m'
3489 // blankLine(true) - xterm/linux behavior
3490 this.lines.splice(row, 0, this.blankLine(true));
3491 this.lines.splice(j, 1);
3492 }
3493
3494 // this.maxRange();
3495 this.updateRange(this.y);
3496 this.updateRange(this.scrollBottom);
3497 };
3498
3499 // CSI Ps M
3500 // Delete Ps Line(s) (default = 1) (DL).
3501 Terminal.prototype.deleteLines = function(params) {
3502 var param, row, j;
3503
3504 param = params[0];
3505 if (param < 1) param = 1;
3506 row = this.y + this.ybase;
3507
3508 j = this.rows - 1 - this.scrollBottom;
3509 j = this.rows - 1 + this.ybase - j;
3510
3511 while (param--) {
3512 // test: echo -e '\e[44m\e[1M\e[0m'
3513 // blankLine(true) - xterm/linux behavior
3514 this.lines.splice(j + 1, 0, this.blankLine(true));
3515 this.lines.splice(row, 1);
3516 }
3517
3518 // this.maxRange();
3519 this.updateRange(this.y);
3520 this.updateRange(this.scrollBottom);
3521 };
3522
3523 // CSI Ps P
3524 // Delete Ps Character(s) (default = 1) (DCH).
3525 Terminal.prototype.deleteChars = function(params) {
3526 var param, row, ch;
3527
3528 param = params[0];
3529 if (param < 1) param = 1;
3530
3531 row = this.y + this.ybase;
4afa08da 3532 ch = [this.eraseAttr(), ' ', 1]; // xterm
3f455f90
PK
3533
3534 while (param--) {
3535 this.lines[row].splice(this.x, 1);
3536 this.lines[row].push(ch);
3537 }
3538 };
3539
3540 // CSI Ps X
3541 // Erase Ps Character(s) (default = 1) (ECH).
3542 Terminal.prototype.eraseChars = function(params) {
3543 var param, row, j, ch;
3544
3545 param = params[0];
3546 if (param < 1) param = 1;
3547
3548 row = this.y + this.ybase;
3549 j = this.x;
4afa08da 3550 ch = [this.eraseAttr(), ' ', 1]; // xterm
3f455f90
PK
3551
3552 while (param-- && j < this.cols) {
3553 this.lines[row][j++] = ch;
3554 }
3555 };
3556
3557 // CSI Pm ` Character Position Absolute
3558 // [column] (default = [row,1]) (HPA).
3559 Terminal.prototype.charPosAbsolute = function(params) {
3560 var param = params[0];
3561 if (param < 1) param = 1;
3562 this.x = param - 1;
3563 if (this.x >= this.cols) {
3564 this.x = this.cols - 1;
3565 }
3566 };
3567
3568 // 141 61 a * HPR -
3569 // Horizontal Position Relative
3570 // reuse CSI Ps C ?
3571 Terminal.prototype.HPositionRelative = function(params) {
3572 var param = params[0];
3573 if (param < 1) param = 1;
3574 this.x += param;
3575 if (this.x >= this.cols) {
3576 this.x = this.cols - 1;
3577 }
3578 };
3579
3580 // CSI Ps c Send Device Attributes (Primary DA).
3581 // Ps = 0 or omitted -> request attributes from terminal. The
3582 // response depends on the decTerminalID resource setting.
3583 // -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')
3584 // -> CSI ? 1 ; 0 c (``VT101 with No Options'')
3585 // -> CSI ? 6 c (``VT102'')
3586 // -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')
3587 // The VT100-style response parameters do not mean anything by
3588 // themselves. VT220 parameters do, telling the host what fea-
3589 // tures the terminal supports:
3590 // Ps = 1 -> 132-columns.
3591 // Ps = 2 -> Printer.
3592 // Ps = 6 -> Selective erase.
3593 // Ps = 8 -> User-defined keys.
3594 // Ps = 9 -> National replacement character sets.
3595 // Ps = 1 5 -> Technical characters.
3596 // Ps = 2 2 -> ANSI color, e.g., VT525.
3597 // Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).
3598 // CSI > Ps c
3599 // Send Device Attributes (Secondary DA).
3600 // Ps = 0 or omitted -> request the terminal's identification
3601 // code. The response depends on the decTerminalID resource set-
3602 // ting. It should apply only to VT220 and up, but xterm extends
3603 // this to VT100.
3604 // -> CSI > Pp ; Pv ; Pc c
3605 // where Pp denotes the terminal type
3606 // Pp = 0 -> ``VT100''.
3607 // Pp = 1 -> ``VT220''.
3608 // and Pv is the firmware version (for xterm, this was originally
3609 // the XFree86 patch number, starting with 95). In a DEC termi-
3610 // nal, Pc indicates the ROM cartridge registration number and is
3611 // always zero.
3612 // More information:
3613 // xterm/charproc.c - line 2012, for more information.
3614 // vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
3615 Terminal.prototype.sendDeviceAttributes = function(params) {
3616 if (params[0] > 0) return;
3617
3618 if (!this.prefix) {
3619 if (this.is('xterm')
3620 || this.is('rxvt-unicode')
3621 || this.is('screen')) {
3622 this.send('\x1b[?1;2c');
3623 } else if (this.is('linux')) {
3624 this.send('\x1b[?6c');
3625 }
3626 } else if (this.prefix === '>') {
3627 // xterm and urxvt
3628 // seem to spit this
3629 // out around ~370 times (?).
3630 if (this.is('xterm')) {
3631 this.send('\x1b[>0;276;0c');
3632 } else if (this.is('rxvt-unicode')) {
3633 this.send('\x1b[>85;95;0c');
3634 } else if (this.is('linux')) {
3635 // not supported by linux console.
3636 // linux console echoes parameters.
3637 this.send(params[0] + 'c');
3638 } else if (this.is('screen')) {
3639 this.send('\x1b[>83;40003;0c');
3640 }
3641 }
3642 };
3643
3644 // CSI Pm d
3645 // Line Position Absolute [row] (default = [1,column]) (VPA).
3646 Terminal.prototype.linePosAbsolute = function(params) {
3647 var param = params[0];
3648 if (param < 1) param = 1;
3649 this.y = param - 1;
3650 if (this.y >= this.rows) {
3651 this.y = this.rows - 1;
3652 }
3653 };
3654
3655 // 145 65 e * VPR - Vertical Position Relative
3656 // reuse CSI Ps B ?
3657 Terminal.prototype.VPositionRelative = function(params) {
3658 var param = params[0];
3659 if (param < 1) param = 1;
3660 this.y += param;
3661 if (this.y >= this.rows) {
3662 this.y = this.rows - 1;
3663 }
3664 };
3665
3666 // CSI Ps ; Ps f
3667 // Horizontal and Vertical Position [row;column] (default =
3668 // [1,1]) (HVP).
3669 Terminal.prototype.HVPosition = function(params) {
3670 if (params[0] < 1) params[0] = 1;
3671 if (params[1] < 1) params[1] = 1;
3672
3673 this.y = params[0] - 1;
3674 if (this.y >= this.rows) {
3675 this.y = this.rows - 1;
3676 }
3677
3678 this.x = params[1] - 1;
3679 if (this.x >= this.cols) {
3680 this.x = this.cols - 1;
3681 }
3682 };
3683
3684 // CSI Pm h Set Mode (SM).
3685 // Ps = 2 -> Keyboard Action Mode (AM).
3686 // Ps = 4 -> Insert Mode (IRM).
3687 // Ps = 1 2 -> Send/receive (SRM).
3688 // Ps = 2 0 -> Automatic Newline (LNM).
3689 // CSI ? Pm h
3690 // DEC Private Mode Set (DECSET).
3691 // Ps = 1 -> Application Cursor Keys (DECCKM).
3692 // Ps = 2 -> Designate USASCII for character sets G0-G3
3693 // (DECANM), and set VT100 mode.
3694 // Ps = 3 -> 132 Column Mode (DECCOLM).
3695 // Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).
3696 // Ps = 5 -> Reverse Video (DECSCNM).
3697 // Ps = 6 -> Origin Mode (DECOM).
3698 // Ps = 7 -> Wraparound Mode (DECAWM).
3699 // Ps = 8 -> Auto-repeat Keys (DECARM).
3700 // Ps = 9 -> Send Mouse X & Y on button press. See the sec-
3701 // tion Mouse Tracking.
3702 // Ps = 1 0 -> Show toolbar (rxvt).
3703 // Ps = 1 2 -> Start Blinking Cursor (att610).
3704 // Ps = 1 8 -> Print form feed (DECPFF).
3705 // Ps = 1 9 -> Set print extent to full screen (DECPEX).
3706 // Ps = 2 5 -> Show Cursor (DECTCEM).
3707 // Ps = 3 0 -> Show scrollbar (rxvt).
3708 // Ps = 3 5 -> Enable font-shifting functions (rxvt).
3709 // Ps = 3 8 -> Enter Tektronix Mode (DECTEK).
3710 // Ps = 4 0 -> Allow 80 -> 132 Mode.
3711 // Ps = 4 1 -> more(1) fix (see curses resource).
3712 // Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-
3713 // RCM).
3714 // Ps = 4 4 -> Turn On Margin Bell.
3715 // Ps = 4 5 -> Reverse-wraparound Mode.
3716 // Ps = 4 6 -> Start Logging. This is normally disabled by a
3717 // compile-time option.
3718 // Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-
3719 // abled by the titeInhibit resource).
3720 // Ps = 6 6 -> Application keypad (DECNKM).
3721 // Ps = 6 7 -> Backarrow key sends backspace (DECBKM).
3722 // Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
3723 // release. See the section Mouse Tracking.
3724 // Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
3725 // Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
3726 // Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
3727 // Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
3728 // Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
3729 // Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).
3730 // Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).
3731 // Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit.
3732 // (enables the eightBitInput resource).
3733 // Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-
3734 // Lock keys. (This enables the numLock resource).
3735 // Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This
3736 // enables the metaSendsEscape resource).
3737 // Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete
3738 // key.
3739 // Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This
3740 // enables the altSendsEscape resource).
3741 // Ps = 1 0 4 0 -> Keep selection even if not highlighted.
3742 // (This enables the keepSelection resource).
3743 // Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables
3744 // the selectToClipboard resource).
3745 // Ps = 1 0 4 2 -> Enable Urgency window manager hint when
3746 // Control-G is received. (This enables the bellIsUrgent
3747 // resource).
3748 // Ps = 1 0 4 3 -> Enable raising of the window when Control-G
3749 // is received. (enables the popOnBell resource).
3750 // Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be
3751 // disabled by the titeInhibit resource).
3752 // Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-
3753 // abled by the titeInhibit resource).
3754 // Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate
3755 // Screen Buffer, clearing it first. (This may be disabled by
3756 // the titeInhibit resource). This combines the effects of the 1
3757 // 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based
3758 // applications rather than the 4 7 mode.
3759 // Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.
3760 // Ps = 1 0 5 1 -> Set Sun function-key mode.
3761 // Ps = 1 0 5 2 -> Set HP function-key mode.
3762 // Ps = 1 0 5 3 -> Set SCO function-key mode.
3763 // Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).
3764 // Ps = 1 0 6 1 -> Set VT220 keyboard emulation.
3765 // Ps = 2 0 0 4 -> Set bracketed paste mode.
3766 // Modes:
3767 // http://vt100.net/docs/vt220-rm/chapter4.html
3768 Terminal.prototype.setMode = function(params) {
3769 if (typeof params === 'object') {
3770 var l = params.length
3771 , i = 0;
3772
3773 for (; i < l; i++) {
3774 this.setMode(params[i]);
3775 }
3776
3777 return;
3778 }
3779
3780 if (!this.prefix) {
3781 switch (params) {
3782 case 4:
3783 this.insertMode = true;
3784 break;
3785 case 20:
3786 //this.convertEol = true;
3787 break;
3788 }
3789 } else if (this.prefix === '?') {
3790 switch (params) {
3791 case 1:
3792 this.applicationCursor = true;
3793 break;
3794 case 2:
3795 this.setgCharset(0, Terminal.charsets.US);
3796 this.setgCharset(1, Terminal.charsets.US);
3797 this.setgCharset(2, Terminal.charsets.US);
3798 this.setgCharset(3, Terminal.charsets.US);
3799 // set VT100 mode here
3800 break;
3801 case 3: // 132 col mode
3802 this.savedCols = this.cols;
3803 this.resize(132, this.rows);
3804 break;
3805 case 6:
3806 this.originMode = true;
3807 break;
3808 case 7:
3809 this.wraparoundMode = true;
3810 break;
3811 case 12:
3812 // this.cursorBlink = true;
3813 break;
3814 case 66:
3815 this.log('Serial port requested application keypad.');
3816 this.applicationKeypad = true;
3817 break;
3818 case 9: // X10 Mouse
3819 // no release, no motion, no wheel, no modifiers.
3820 case 1000: // vt200 mouse
3821 // no motion.
3822 // no modifiers, except control on the wheel.
3823 case 1002: // button event mouse
3824 case 1003: // any event mouse
3825 // any event - sends motion events,
3826 // even if there is no button held down.
3827 this.x10Mouse = params === 9;
3828 this.vt200Mouse = params === 1000;
3829 this.normalMouse = params > 1000;
3830 this.mouseEvents = true;
3831 this.element.style.cursor = 'default';
3832 this.log('Binding to mouse events.');
3833 break;
3834 case 1004: // send focusin/focusout events
3835 // focusin: ^[[I
3836 // focusout: ^[[O
3837 this.sendFocus = true;
3838 break;
3839 case 1005: // utf8 ext mode mouse
3840 this.utfMouse = true;
3841 // for wide terminals
3842 // simply encodes large values as utf8 characters
3843 break;
3844 case 1006: // sgr ext mode mouse
3845 this.sgrMouse = true;
3846 // for wide terminals
3847 // does not add 32 to fields
3848 // press: ^[[<b;x;yM
3849 // release: ^[[<b;x;ym
3850 break;
3851 case 1015: // urxvt ext mode mouse
3852 this.urxvtMouse = true;
3853 // for wide terminals
3854 // numbers for fields
3855 // press: ^[[b;x;yM
3856 // motion: ^[[b;x;yT
3857 break;
3858 case 25: // show cursor
3859 this.cursorHidden = false;
3860 break;
3861 case 1049: // alt screen buffer cursor
3862 //this.saveCursor();
3863 ; // FALL-THROUGH
3864 case 47: // alt screen buffer
3865 case 1047: // alt screen buffer
3866 if (!this.normal) {
3867 var normal = {
3868 lines: this.lines,
3869 ybase: this.ybase,
3870 ydisp: this.ydisp,
3871 x: this.x,
3872 y: this.y,
3873 scrollTop: this.scrollTop,
3874 scrollBottom: this.scrollBottom,
3875 tabs: this.tabs
3876 // XXX save charset(s) here?
3877 // charset: this.charset,
3878 // glevel: this.glevel,
3879 // charsets: this.charsets
3880 };
3881 this.reset();
3882 this.normal = normal;
3883 this.showCursor();
3884 }
3885 break;
3886 }
3887 }
3888 };
3889
3890 // CSI Pm l Reset Mode (RM).
3891 // Ps = 2 -> Keyboard Action Mode (AM).
3892 // Ps = 4 -> Replace Mode (IRM).
3893 // Ps = 1 2 -> Send/receive (SRM).
3894 // Ps = 2 0 -> Normal Linefeed (LNM).
3895 // CSI ? Pm l
3896 // DEC Private Mode Reset (DECRST).
3897 // Ps = 1 -> Normal Cursor Keys (DECCKM).
3898 // Ps = 2 -> Designate VT52 mode (DECANM).
3899 // Ps = 3 -> 80 Column Mode (DECCOLM).
3900 // Ps = 4 -> Jump (Fast) Scroll (DECSCLM).
3901 // Ps = 5 -> Normal Video (DECSCNM).
3902 // Ps = 6 -> Normal Cursor Mode (DECOM).
3903 // Ps = 7 -> No Wraparound Mode (DECAWM).
3904 // Ps = 8 -> No Auto-repeat Keys (DECARM).
3905 // Ps = 9 -> Don't send Mouse X & Y on button press.
3906 // Ps = 1 0 -> Hide toolbar (rxvt).
3907 // Ps = 1 2 -> Stop Blinking Cursor (att610).
3908 // Ps = 1 8 -> Don't print form feed (DECPFF).
3909 // Ps = 1 9 -> Limit print to scrolling region (DECPEX).
3910 // Ps = 2 5 -> Hide Cursor (DECTCEM).
3911 // Ps = 3 0 -> Don't show scrollbar (rxvt).
3912 // Ps = 3 5 -> Disable font-shifting functions (rxvt).
3913 // Ps = 4 0 -> Disallow 80 -> 132 Mode.
3914 // Ps = 4 1 -> No more(1) fix (see curses resource).
3915 // Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-
3916 // NRCM).
3917 // Ps = 4 4 -> Turn Off Margin Bell.
3918 // Ps = 4 5 -> No Reverse-wraparound Mode.
3919 // Ps = 4 6 -> Stop Logging. (This is normally disabled by a
3920 // compile-time option).
3921 // Ps = 4 7 -> Use Normal Screen Buffer.
3922 // Ps = 6 6 -> Numeric keypad (DECNKM).
3923 // Ps = 6 7 -> Backarrow key sends delete (DECBKM).
3924 // Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
3925 // release. See the section Mouse Tracking.
3926 // Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
3927 // Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
3928 // Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
3929 // Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
3930 // Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
3931 // Ps = 1 0 1 0 -> Don't scroll to bottom on tty output
3932 // (rxvt).
3933 // Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).
3934 // Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables
3935 // the eightBitInput resource).
3936 // Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-
3937 // Lock keys. (This disables the numLock resource).
3938 // Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.
3939 // (This disables the metaSendsEscape resource).
3940 // Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad
3941 // Delete key.
3942 // Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.
3943 // (This disables the altSendsEscape resource).
3944 // Ps = 1 0 4 0 -> Do not keep selection when not highlighted.
3945 // (This disables the keepSelection resource).
3946 // Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables
3947 // the selectToClipboard resource).
3948 // Ps = 1 0 4 2 -> Disable Urgency window manager hint when
3949 // Control-G is received. (This disables the bellIsUrgent
3950 // resource).
3951 // Ps = 1 0 4 3 -> Disable raising of the window when Control-
3952 // G is received. (This disables the popOnBell resource).
3953 // Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen
3954 // first if in the Alternate Screen. (This may be disabled by
3955 // the titeInhibit resource).
3956 // Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be
3957 // disabled by the titeInhibit resource).
3958 // Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor
3959 // as in DECRC. (This may be disabled by the titeInhibit
3960 // resource). This combines the effects of the 1 0 4 7 and 1 0
3961 // 4 8 modes. Use this with terminfo-based applications rather
3962 // than the 4 7 mode.
3963 // Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.
3964 // Ps = 1 0 5 1 -> Reset Sun function-key mode.
3965 // Ps = 1 0 5 2 -> Reset HP function-key mode.
3966 // Ps = 1 0 5 3 -> Reset SCO function-key mode.
3967 // Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).
3968 // Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
3969 // Ps = 2 0 0 4 -> Reset bracketed paste mode.
3970 Terminal.prototype.resetMode = function(params) {
3971 if (typeof params === 'object') {
3972 var l = params.length
3973 , i = 0;
3974
3975 for (; i < l; i++) {
3976 this.resetMode(params[i]);
3977 }
3978
3979 return;
3980 }
3981
3982 if (!this.prefix) {
3983 switch (params) {
3984 case 4:
3985 this.insertMode = false;
3986 break;
3987 case 20:
3988 //this.convertEol = false;
3989 break;
3990 }
3991 } else if (this.prefix === '?') {
3992 switch (params) {
3993 case 1:
3994 this.applicationCursor = false;
3995 break;
3996 case 3:
3997 if (this.cols === 132 && this.savedCols) {
3998 this.resize(this.savedCols, this.rows);
3999 }
4000 delete this.savedCols;
4001 break;
4002 case 6:
4003 this.originMode = false;
4004 break;
4005 case 7:
4006 this.wraparoundMode = false;
4007 break;
4008 case 12:
4009 // this.cursorBlink = false;
4010 break;
4011 case 66:
4012 this.log('Switching back to normal keypad.');
4013 this.applicationKeypad = false;
4014 break;
4015 case 9: // X10 Mouse
4016 case 1000: // vt200 mouse
4017 case 1002: // button event mouse
4018 case 1003: // any event mouse
4019 this.x10Mouse = false;
4020 this.vt200Mouse = false;
4021 this.normalMouse = false;
4022 this.mouseEvents = false;
4023 this.element.style.cursor = '';
4024 break;
4025 case 1004: // send focusin/focusout events
4026 this.sendFocus = false;
4027 break;
4028 case 1005: // utf8 ext mode mouse
4029 this.utfMouse = false;
4030 break;
4031 case 1006: // sgr ext mode mouse
4032 this.sgrMouse = false;
4033 break;
4034 case 1015: // urxvt ext mode mouse
4035 this.urxvtMouse = false;
4036 break;
4037 case 25: // hide cursor
4038 this.cursorHidden = true;
4039 break;
4040 case 1049: // alt screen buffer cursor
4041 ; // FALL-THROUGH
4042 case 47: // normal screen buffer
4043 case 1047: // normal screen buffer - clearing it first
4044 if (this.normal) {
4045 this.lines = this.normal.lines;
4046 this.ybase = this.normal.ybase;
4047 this.ydisp = this.normal.ydisp;
4048 this.x = this.normal.x;
4049 this.y = this.normal.y;
4050 this.scrollTop = this.normal.scrollTop;
4051 this.scrollBottom = this.normal.scrollBottom;
4052 this.tabs = this.normal.tabs;
4053 this.normal = null;
4054 // if (params === 1049) {
4055 // this.x = this.savedX;
4056 // this.y = this.savedY;
4057 // }
4058 this.refresh(0, this.rows - 1);
4059 this.showCursor();
4060 }
4061 break;
4062 }
4063 }
4064 };
4065
4066 // CSI Ps ; Ps r
4067 // Set Scrolling Region [top;bottom] (default = full size of win-
4068 // dow) (DECSTBM).
4069 // CSI ? Pm r
4070 Terminal.prototype.setScrollRegion = function(params) {
4071 if (this.prefix) return;
4072 this.scrollTop = (params[0] || 1) - 1;
4073 this.scrollBottom = (params[1] || this.rows) - 1;
4074 this.x = 0;
4075 this.y = 0;
4076 };
4077
4078 // CSI s
4079 // Save cursor (ANSI.SYS).
4080 Terminal.prototype.saveCursor = function(params) {
4081 this.savedX = this.x;
4082 this.savedY = this.y;
4083 };
4084
4085 // CSI u
4086 // Restore cursor (ANSI.SYS).
4087 Terminal.prototype.restoreCursor = function(params) {
4088 this.x = this.savedX || 0;
4089 this.y = this.savedY || 0;
4090 };
4091
4092 /**
4093 * Lesser Used
4094 */
4095
4096 // CSI Ps I
4097 // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
4098 Terminal.prototype.cursorForwardTab = function(params) {
4099 var param = params[0] || 1;
4100 while (param--) {
4101 this.x = this.nextStop();
4102 }
4103 };
4104
4105 // CSI Ps S Scroll up Ps lines (default = 1) (SU).
4106 Terminal.prototype.scrollUp = function(params) {
4107 var param = params[0] || 1;
4108 while (param--) {
4109 this.lines.splice(this.ybase + this.scrollTop, 1);
4110 this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine());
4111 }
4112 // this.maxRange();
4113 this.updateRange(this.scrollTop);
4114 this.updateRange(this.scrollBottom);
4115 };
4116
4117 // CSI Ps T Scroll down Ps lines (default = 1) (SD).
4118 Terminal.prototype.scrollDown = function(params) {
4119 var param = params[0] || 1;
4120 while (param--) {
4121 this.lines.splice(this.ybase + this.scrollBottom, 1);
4122 this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine());
4123 }
4124 // this.maxRange();
4125 this.updateRange(this.scrollTop);
4126 this.updateRange(this.scrollBottom);
4127 };
4128
4129 // CSI Ps ; Ps ; Ps ; Ps ; Ps T
4130 // Initiate highlight mouse tracking. Parameters are
4131 // [func;startx;starty;firstrow;lastrow]. See the section Mouse
4132 // Tracking.
4133 Terminal.prototype.initMouseTracking = function(params) {
4134 // Relevant: DECSET 1001
4135 };
4136
4137 // CSI > Ps; Ps T
4138 // Reset one or more features of the title modes to the default
4139 // value. Normally, "reset" disables the feature. It is possi-
4140 // ble to disable the ability to reset features by compiling a
4141 // different default for the title modes into xterm.
4142 // Ps = 0 -> Do not set window/icon labels using hexadecimal.
4143 // Ps = 1 -> Do not query window/icon labels using hexadeci-
4144 // mal.
4145 // Ps = 2 -> Do not set window/icon labels using UTF-8.
4146 // Ps = 3 -> Do not query window/icon labels using UTF-8.
4147 // (See discussion of "Title Modes").
4148 Terminal.prototype.resetTitleModes = function(params) {
4149 ;
4150 };
4151
4152 // CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
4153 Terminal.prototype.cursorBackwardTab = function(params) {
4154 var param = params[0] || 1;
4155 while (param--) {
4156 this.x = this.prevStop();
4157 }
4158 };
4159
4160 // CSI Ps b Repeat the preceding graphic character Ps times (REP).
4161 Terminal.prototype.repeatPrecedingCharacter = function(params) {
4162 var param = params[0] || 1
4163 , line = this.lines[this.ybase + this.y]
4afa08da 4164 , ch = line[this.x - 1] || [this.defAttr, ' ', 1];
3f455f90
PK
4165
4166 while (param--) line[this.x++] = ch;
4167 };
4168
4169 // CSI Ps g Tab Clear (TBC).
4170 // Ps = 0 -> Clear Current Column (default).
4171 // Ps = 3 -> Clear All.
4172 // Potentially:
4173 // Ps = 2 -> Clear Stops on Line.
4174 // http://vt100.net/annarbor/aaa-ug/section6.html
4175 Terminal.prototype.tabClear = function(params) {
4176 var param = params[0];
4177 if (param <= 0) {
4178 delete this.tabs[this.x];
4179 } else if (param === 3) {
4180 this.tabs = {};
4181 }
4182 };
4183
4184 // CSI Pm i Media Copy (MC).
4185 // Ps = 0 -> Print screen (default).
4186 // Ps = 4 -> Turn off printer controller mode.
4187 // Ps = 5 -> Turn on printer controller mode.
4188 // CSI ? Pm i
4189 // Media Copy (MC, DEC-specific).
4190 // Ps = 1 -> Print line containing cursor.
4191 // Ps = 4 -> Turn off autoprint mode.
4192 // Ps = 5 -> Turn on autoprint mode.
4193 // Ps = 1 0 -> Print composed display, ignores DECPEX.
4194 // Ps = 1 1 -> Print all pages.
4195 Terminal.prototype.mediaCopy = function(params) {
4196 ;
4197 };
4198
4199 // CSI > Ps; Ps m
4200 // Set or reset resource-values used by xterm to decide whether
4201 // to construct escape sequences holding information about the
4202 // modifiers pressed with a given key. The first parameter iden-
4203 // tifies the resource to set/reset. The second parameter is the
4204 // value to assign to the resource. If the second parameter is
4205 // omitted, the resource is reset to its initial value.
4206 // Ps = 1 -> modifyCursorKeys.
4207 // Ps = 2 -> modifyFunctionKeys.
4208 // Ps = 4 -> modifyOtherKeys.
4209 // If no parameters are given, all resources are reset to their
4210 // initial values.
4211 Terminal.prototype.setResources = function(params) {
4212 ;
4213 };
4214
4215 // CSI > Ps n
4216 // Disable modifiers which may be enabled via the CSI > Ps; Ps m
4217 // sequence. This corresponds to a resource value of "-1", which
4218 // cannot be set with the other sequence. The parameter identi-
4219 // fies the resource to be disabled:
4220 // Ps = 1 -> modifyCursorKeys.
4221 // Ps = 2 -> modifyFunctionKeys.
4222 // Ps = 4 -> modifyOtherKeys.
4223 // If the parameter is omitted, modifyFunctionKeys is disabled.
4224 // When modifyFunctionKeys is disabled, xterm uses the modifier
4225 // keys to make an extended sequence of functions rather than
4226 // adding a parameter to each function key to denote the modi-
4227 // fiers.
4228 Terminal.prototype.disableModifiers = function(params) {
4229 ;
4230 };
4231
4232 // CSI > Ps p
4233 // Set resource value pointerMode. This is used by xterm to
4234 // decide whether to hide the pointer cursor as the user types.
4235 // Valid values for the parameter:
4236 // Ps = 0 -> never hide the pointer.
4237 // Ps = 1 -> hide if the mouse tracking mode is not enabled.
4238 // Ps = 2 -> always hide the pointer. If no parameter is
4239 // given, xterm uses the default, which is 1 .
4240 Terminal.prototype.setPointerMode = function(params) {
4241 ;
4242 };
4243
4244 // CSI ! p Soft terminal reset (DECSTR).
4245 // http://vt100.net/docs/vt220-rm/table4-10.html
4246 Terminal.prototype.softReset = function(params) {
4247 this.cursorHidden = false;
4248 this.insertMode = false;
4249 this.originMode = false;
4250 this.wraparoundMode = false; // autowrap
4251 this.applicationKeypad = false; // ?
4252 this.applicationCursor = false;
4253 this.scrollTop = 0;
4254 this.scrollBottom = this.rows - 1;
4255 this.curAttr = this.defAttr;
4256 this.x = this.y = 0; // ?
4257 this.charset = null;
4258 this.glevel = 0; // ??
4259 this.charsets = [null]; // ??
4260 };
4261
4262 // CSI Ps$ p
4263 // Request ANSI mode (DECRQM). For VT300 and up, reply is
4264 // CSI Ps; Pm$ y
4265 // where Ps is the mode number as in RM, and Pm is the mode
4266 // value:
4267 // 0 - not recognized
4268 // 1 - set
4269 // 2 - reset
4270 // 3 - permanently set
4271 // 4 - permanently reset
4272 Terminal.prototype.requestAnsiMode = function(params) {
4273 ;
4274 };
4275
4276 // CSI ? Ps$ p
4277 // Request DEC private mode (DECRQM). For VT300 and up, reply is
4278 // CSI ? Ps; Pm$ p
4279 // where Ps is the mode number as in DECSET, Pm is the mode value
4280 // as in the ANSI DECRQM.
4281 Terminal.prototype.requestPrivateMode = function(params) {
4282 ;
4283 };
4284
4285 // CSI Ps ; Ps " p
4286 // Set conformance level (DECSCL). Valid values for the first
4287 // parameter:
4288 // Ps = 6 1 -> VT100.
4289 // Ps = 6 2 -> VT200.
4290 // Ps = 6 3 -> VT300.
4291 // Valid values for the second parameter:
4292 // Ps = 0 -> 8-bit controls.
4293 // Ps = 1 -> 7-bit controls (always set for VT100).
4294 // Ps = 2 -> 8-bit controls.
4295 Terminal.prototype.setConformanceLevel = function(params) {
4296 ;
4297 };
4298
4299 // CSI Ps q Load LEDs (DECLL).
4300 // Ps = 0 -> Clear all LEDS (default).
4301 // Ps = 1 -> Light Num Lock.
4302 // Ps = 2 -> Light Caps Lock.
4303 // Ps = 3 -> Light Scroll Lock.
4304 // Ps = 2 1 -> Extinguish Num Lock.
4305 // Ps = 2 2 -> Extinguish Caps Lock.
4306 // Ps = 2 3 -> Extinguish Scroll Lock.
4307 Terminal.prototype.loadLEDs = function(params) {
4308 ;
4309 };
4310
4311 // CSI Ps SP q
4312 // Set cursor style (DECSCUSR, VT520).
4313 // Ps = 0 -> blinking block.
4314 // Ps = 1 -> blinking block (default).
4315 // Ps = 2 -> steady block.
4316 // Ps = 3 -> blinking underline.
4317 // Ps = 4 -> steady underline.
4318 Terminal.prototype.setCursorStyle = function(params) {
4319 ;
4320 };
4321
4322 // CSI Ps " q
4323 // Select character protection attribute (DECSCA). Valid values
4324 // for the parameter:
4325 // Ps = 0 -> DECSED and DECSEL can erase (default).
4326 // Ps = 1 -> DECSED and DECSEL cannot erase.
4327 // Ps = 2 -> DECSED and DECSEL can erase.
4328 Terminal.prototype.setCharProtectionAttr = function(params) {
4329 ;
4330 };
4331
4332 // CSI ? Pm r
4333 // Restore DEC Private Mode Values. The value of Ps previously
4334 // saved is restored. Ps values are the same as for DECSET.
4335 Terminal.prototype.restorePrivateValues = function(params) {
4336 ;
4337 };
4338
4339 // CSI Pt; Pl; Pb; Pr; Ps$ r
4340 // Change Attributes in Rectangular Area (DECCARA), VT400 and up.
4341 // Pt; Pl; Pb; Pr denotes the rectangle.
4342 // Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7.
4343 // NOTE: xterm doesn't enable this code by default.
4344 Terminal.prototype.setAttrInRectangle = function(params) {
4345 var t = params[0]
4346 , l = params[1]
4347 , b = params[2]
4348 , r = params[3]
4349 , attr = params[4];
4350
4351 var line
4352 , i;
4353
4354 for (; t < b + 1; t++) {
4355 line = this.lines[this.ybase + t];
4356 for (i = l; i < r; i++) {
4357 line[i] = [attr, line[i][1]];
4358 }
4359 }
4360
4361 // this.maxRange();
4362 this.updateRange(params[0]);
4363 this.updateRange(params[2]);
4364 };
4365
4366
4367 // CSI Pc; Pt; Pl; Pb; Pr$ x
4368 // Fill Rectangular Area (DECFRA), VT420 and up.
4369 // Pc is the character to use.
4370 // Pt; Pl; Pb; Pr denotes the rectangle.
4371 // NOTE: xterm doesn't enable this code by default.
4372 Terminal.prototype.fillRectangle = function(params) {
4373 var ch = params[0]
4374 , t = params[1]
4375 , l = params[2]
4376 , b = params[3]
4377 , r = params[4];
4378
4379 var line
4380 , i;
4381
4382 for (; t < b + 1; t++) {
4383 line = this.lines[this.ybase + t];
4384 for (i = l; i < r; i++) {
4385 line[i] = [line[i][0], String.fromCharCode(ch)];
4386 }
4387 }
4388
4389 // this.maxRange();
4390 this.updateRange(params[1]);
4391 this.updateRange(params[3]);
4392 };
4393
4394 // CSI Ps ; Pu ' z
4395 // Enable Locator Reporting (DECELR).
4396 // Valid values for the first parameter:
4397 // Ps = 0 -> Locator disabled (default).
4398 // Ps = 1 -> Locator enabled.
4399 // Ps = 2 -> Locator enabled for one report, then disabled.
4400 // The second parameter specifies the coordinate unit for locator
4401 // reports.
4402 // Valid values for the second parameter:
4403 // Pu = 0 <- or omitted -> default to character cells.
4404 // Pu = 1 <- device physical pixels.
4405 // Pu = 2 <- character cells.
4406 Terminal.prototype.enableLocatorReporting = function(params) {
4407 var val = params[0] > 0;
4408 //this.mouseEvents = val;
4409 //this.decLocator = val;
4410 };
4411
4412 // CSI Pt; Pl; Pb; Pr$ z
4413 // Erase Rectangular Area (DECERA), VT400 and up.
4414 // Pt; Pl; Pb; Pr denotes the rectangle.
4415 // NOTE: xterm doesn't enable this code by default.
4416 Terminal.prototype.eraseRectangle = function(params) {
4417 var t = params[0]
4418 , l = params[1]
4419 , b = params[2]
4420 , r = params[3];
4421
4422 var line
4423 , i
4424 , ch;
4425
4afa08da 4426 ch = [this.eraseAttr(), ' ', 1]; // xterm?
3f455f90
PK
4427
4428 for (; t < b + 1; t++) {
4429 line = this.lines[this.ybase + t];
4430 for (i = l; i < r; i++) {
4431 line[i] = ch;
4432 }
4433 }
4434
4435 // this.maxRange();
4436 this.updateRange(params[0]);
4437 this.updateRange(params[2]);
4438 };
8bc844c0 4439
8bc844c0 4440
3f455f90
PK
4441 // CSI P m SP }
4442 // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
4443 // NOTE: xterm doesn't enable this code by default.
4444 Terminal.prototype.insertColumns = function() {
4445 var param = params[0]
4446 , l = this.ybase + this.rows
4afa08da 4447 , ch = [this.eraseAttr(), ' ', 1] // xterm?
3f455f90 4448 , i;
8bc844c0 4449
3f455f90
PK
4450 while (param--) {
4451 for (i = this.ybase; i < l; i++) {
4452 this.lines[i].splice(this.x + 1, 0, ch);
4453 this.lines[i].pop();
8bc844c0 4454 }
3f455f90 4455 }
8bc844c0 4456
3f455f90
PK
4457 this.maxRange();
4458 };
8bc844c0 4459
178b611b 4460
3f455f90
PK
4461 // CSI P m SP ~
4462 // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
4463 // NOTE: xterm doesn't enable this code by default.
4464 Terminal.prototype.deleteColumns = function() {
4465 var param = params[0]
4466 , l = this.ybase + this.rows
4afa08da 4467 , ch = [this.eraseAttr(), ' ', 1] // xterm?
3f455f90
PK
4468 , i;
4469
4470 while (param--) {
4471 for (i = this.ybase; i < l; i++) {
4472 this.lines[i].splice(this.x, 1);
4473 this.lines[i].push(ch);
4474 }
4475 }
8bc844c0 4476
3f455f90
PK
4477 this.maxRange();
4478 };
8bc844c0 4479
3f455f90
PK
4480 /**
4481 * Character Sets
4482 */
4483
4484 Terminal.charsets = {};
4485
4486 // DEC Special Character and Line Drawing Set.
4487 // http://vt100.net/docs/vt102-ug/table5-13.html
4488 // A lot of curses apps use this if they see TERM=xterm.
4489 // testing: echo -e '\e(0a\e(B'
4490 // The xterm output sometimes seems to conflict with the
4491 // reference above. xterm seems in line with the reference
4492 // when running vttest however.
4493 // The table below now uses xterm's output from vttest.
4494 Terminal.charsets.SCLD = { // (0
4495 '`': '\u25c6', // '◆'
4496 'a': '\u2592', // '▒'
4497 'b': '\u0009', // '\t'
4498 'c': '\u000c', // '\f'
4499 'd': '\u000d', // '\r'
4500 'e': '\u000a', // '\n'
4501 'f': '\u00b0', // '°'
4502 'g': '\u00b1', // '±'
4503 'h': '\u2424', // '\u2424' (NL)
4504 'i': '\u000b', // '\v'
4505 'j': '\u2518', // '┘'
4506 'k': '\u2510', // '┐'
4507 'l': '\u250c', // '┌'
4508 'm': '\u2514', // '└'
4509 'n': '\u253c', // '┼'
4510 'o': '\u23ba', // '⎺'
4511 'p': '\u23bb', // '⎻'
4512 'q': '\u2500', // '─'
4513 'r': '\u23bc', // '⎼'
4514 's': '\u23bd', // '⎽'
4515 't': '\u251c', // '├'
4516 'u': '\u2524', // '┤'
4517 'v': '\u2534', // '┴'
4518 'w': '\u252c', // '┬'
4519 'x': '\u2502', // '│'
4520 'y': '\u2264', // '≤'
4521 'z': '\u2265', // '≥'
4522 '{': '\u03c0', // 'π'
4523 '|': '\u2260', // '≠'
4524 '}': '\u00a3', // '£'
4525 '~': '\u00b7' // '·'
4526 };
4527
4528 Terminal.charsets.UK = null; // (A
4529 Terminal.charsets.US = null; // (B (USASCII)
4530 Terminal.charsets.Dutch = null; // (4
4531 Terminal.charsets.Finnish = null; // (C or (5
4532 Terminal.charsets.French = null; // (R
4533 Terminal.charsets.FrenchCanadian = null; // (Q
4534 Terminal.charsets.German = null; // (K
4535 Terminal.charsets.Italian = null; // (Y
4536 Terminal.charsets.NorwegianDanish = null; // (E or (6
4537 Terminal.charsets.Spanish = null; // (Z
4538 Terminal.charsets.Swedish = null; // (H or (7
4539 Terminal.charsets.Swiss = null; // (=
4540 Terminal.charsets.ISOLatin = null; // /A
4541
4542 /**
4543 * Helpers
4544 */
4545
b01165c1 4546 function contains(el, arr) {
4547 for (var i = 0; i < arr.length; i += 1) {
4548 if (el === arr[i]) {
4549 return true;
4550 }
4551 }
4552 return false;
4553 }
4554
3f455f90
PK
4555 function on(el, type, handler, capture) {
4556 if (!Array.isArray(el)) {
4557 el = [el];
9e6cb6b6 4558 }
3f455f90
PK
4559 el.forEach(function (element) {
4560 element.addEventListener(type, handler, capture || false);
4561 });
9e6cb6b6 4562 }
8bc844c0 4563
3f455f90
PK
4564 function off(el, type, handler, capture) {
4565 el.removeEventListener(type, handler, capture || false);
5d3f8786 4566 }
3f455f90
PK
4567
4568 function cancel(ev, force) {
4569 if (!this.cancelEvents && !force) {
4570 return;
9e6cb6b6 4571 }
3f455f90
PK
4572 ev.preventDefault();
4573 ev.stopPropagation();
4574 return false;
9e6cb6b6 4575 }
8bc844c0 4576
3f455f90
PK
4577 function inherits(child, parent) {
4578 function f() {
4579 this.constructor = child;
9e6cb6b6 4580 }
3f455f90
PK
4581 f.prototype = parent.prototype;
4582 child.prototype = new f;
4583 }
8bc844c0 4584
3f455f90
PK
4585 // if bold is broken, we can't
4586 // use it in the terminal.
4587 function isBoldBroken(document) {
4588 var body = document.getElementsByTagName('body')[0];
4589 var el = document.createElement('span');
4590 el.innerHTML = 'hello world';
4591 body.appendChild(el);
4592 var w1 = el.scrollWidth;
4593 el.style.fontWeight = 'bold';
4594 var w2 = el.scrollWidth;
4595 body.removeChild(el);
4596 return w1 !== w2;
4597 }
8bc844c0 4598
3f455f90
PK
4599 var String = this.String;
4600 var setTimeout = this.setTimeout;
4601 var setInterval = this.setInterval;
4602
4603 function indexOf(obj, el) {
4604 var i = obj.length;
4605 while (i--) {
4606 if (obj[i] === el) return i;
9e6cb6b6 4607 }
3f455f90 4608 return -1;
9e6cb6b6 4609 }
8bc844c0 4610
b01165c1 4611 function isThirdLevelShift(term, ev) {
4612 var thirdLevelKey =
4613 (term.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||
4614 (term.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);
4615
4616 // Don't invoke for arrows, pageDown, home, backspace, etc.
4617 return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47);
4618 }
4619
3f455f90
PK
4620 function matchColor(r1, g1, b1) {
4621 var hash = (r1 << 16) | (g1 << 8) | b1;
4622
4623 if (matchColor._cache[hash] != null) {
4624 return matchColor._cache[hash];
4625 }
8bc844c0 4626
3f455f90
PK
4627 var ldiff = Infinity
4628 , li = -1
4629 , i = 0
4630 , c
4631 , r2
4632 , g2
4633 , b2
4634 , diff;
4635
4636 for (; i < Terminal.vcolors.length; i++) {
4637 c = Terminal.vcolors[i];
4638 r2 = c[0];
4639 g2 = c[1];
4640 b2 = c[2];
4641
4642 diff = matchColor.distance(r1, g1, b1, r2, g2, b2);
4643
4644 if (diff === 0) {
4645 li = i;
4646 break;
4647 }
9e6cb6b6 4648
3f455f90
PK
4649 if (diff < ldiff) {
4650 ldiff = diff;
4651 li = i;
4652 }
4653 }
8bc844c0 4654
3f455f90
PK
4655 return matchColor._cache[hash] = li;
4656 }
8bc844c0 4657
3f455f90 4658 matchColor._cache = {};
8bc844c0 4659
3f455f90
PK
4660 // http://stackoverflow.com/questions/1633828
4661 matchColor.distance = function(r1, g1, b1, r2, g2, b2) {
4662 return Math.pow(30 * (r1 - r2), 2)
4663 + Math.pow(59 * (g1 - g2), 2)
4664 + Math.pow(11 * (b1 - b2), 2);
4665 };
8bc844c0 4666
3f455f90
PK
4667 function each(obj, iter, con) {
4668 if (obj.forEach) return obj.forEach(iter, con);
4669 for (var i = 0; i < obj.length; i++) {
4670 iter.call(con, obj[i], i, obj);
4671 }
a68c8336
CJ
4672 }
4673
3f455f90
PK
4674 function keys(obj) {
4675 if (Object.keys) return Object.keys(obj);
4676 var key, keys = [];
4677 for (key in obj) {
4678 if (Object.prototype.hasOwnProperty.call(obj, key)) {
4679 keys.push(key);
4680 }
4681 }
4682 return keys;
86dad1b0 4683 }
86dad1b0 4684
e3126ba3
JB
4685 var wcwidth = (function(opts) {
4686 // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
4687 // combining characters
4688 var COMBINING = [
4689 [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],
4690 [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],
4691 [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603],
4692 [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670],
4693 [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED],
4694 [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A],
4695 [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902],
4696 [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D],
4697 [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981],
4698 [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD],
4699 [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],
4700 [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],
4701 [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC],
4702 [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD],
4703 [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],
4704 [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D],
4705 [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0],
4706 [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],
4707 [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC],
4708 [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD],
4709 [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D],
4710 [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6],
4711 [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E],
4712 [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC],
4713 [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35],
4714 [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E],
4715 [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97],
4716 [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030],
4717 [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039],
4718 [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F],
4719 [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753],
4720 [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD],
4721 [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD],
4722 [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922],
4723 [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B],
4724 [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34],
4725 [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42],
4726 [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF],
4727 [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063],
4728 [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],
4729 [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],
4730 [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],
4731 [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB],
4732 [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],
4733 [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169],
4734 [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],
4735 [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F],
4736 [0xE0100, 0xE01EF]
4737 ];
4738 // binary search
4739 function bisearch(ucs) {
4740 var min = 0;
4741 var max = COMBINING.length - 1;
4742 var mid;
4743 if (ucs < COMBINING[0][0] || ucs > COMBINING[max][1])
4744 return false;
4745 while (max >= min) {
4746 mid = Math.floor((min + max) / 2);
4747 if (ucs > COMBINING[mid][1])
4748 min = mid + 1;
4749 else if (ucs < COMBINING[mid][0])
4750 max = mid - 1;
4751 else
4752 return true;
4753 }
4754 return false;
4755 }
4756 function wcwidth(ucs) {
4757 // test for 8-bit control characters
4758 if (ucs === 0)
4759 return opts.nul;
4760 if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
4761 return opts.control;
4762 // binary search in table of non-spacing characters
4763 if (bisearch(ucs))
4764 return 0;
4765 // if we arrive here, ucs is not a combining or C0/C1 control character
4766 return 1 +
4767 (
4768 ucs >= 0x1100 &&
4769 (
4770 ucs <= 0x115f || // Hangul Jamo init. consonants
4771 ucs == 0x2329 ||
4772 ucs == 0x232a ||
4773 (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || // CJK..Yi
4774 (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables
4775 (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs
4776 (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms
4777 (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms
4778 (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms
4779 (ucs >= 0xffe0 && ucs <= 0xffe6) ||
4780 (ucs >= 0x20000 && ucs <= 0x2fffd) ||
4781 (ucs >= 0x30000 && ucs <= 0x3fffd)
4782 )
4783 );
4784 }
4785 return wcwidth;
4786 })({nul: 0, control: 0}); // configurable options
4787
3f455f90
PK
4788 /**
4789 * Expose
4790 */
4791
4792 Terminal.EventEmitter = EventEmitter;
4793 Terminal.inherits = inherits;
fd5be55d
DI
4794
4795 /**
4796 * Adds an event listener to the terminal.
4797 *
4798 * @param {string} event The name of the event. TODO: Document all event types
4799 * @param {function} callback The function to call when the event is triggered.
4800 *
4801 * @public
4802 */
3f455f90
PK
4803 Terminal.on = on;
4804 Terminal.off = off;
4805 Terminal.cancel = cancel;
8bc844c0 4806
3f455f90 4807 return Terminal;
987f09d8 4808});