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