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