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