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