github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/static/app/term.js (about)

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