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