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