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