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