]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Fix #4
authorparis <pariskasidiaris@gmail.com>
Wed, 10 Sep 2014 11:09:23 +0000 (11:09 +0000)
committerparis <pariskasidiaris@gmail.com>
Wed, 10 Sep 2014 11:09:23 +0000 (11:09 +0000)
addons/attach/attach.js
addons/fit/fit.js
addons/fullscreen/fullscreen.js
src/xterm.js

index 12eb1359e4d4028dbf5febc9cc0e3256ef5f2478..e7cac84d7189766c5cf620ce719ec18d654018e1 100644 (file)
@@ -5,19 +5,34 @@
  * The bidirectional argument indicates, whether the terminal should
  * send data to the socket as well and is true, by default.
  */
-Terminal.prototype.attach = function (socket, bidirectional) {
-  var term = this;
-  
-  bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
-  this.socket = socket;
-  
-  socket.addEventListener('message', function (ev) {
-    term.write(ev.data);
-  });
-  
-  if (bidirectional) {
-    this.on('data', function (data) {
-      socket.send(data);
-    });
-  }
-}
\ No newline at end of file
+
+(function (attach) {
+    if (typeof define == 'function') {
+        /*
+         * Require.js is available
+         */
+        define(['../../src/xterm'], attach);
+    } else {
+        /*
+         * Plain browser environment
+         */ 
+        attach(this.Xterm);
+    }
+})(function (Xterm) {
+    Xterm.prototype.attach = function (socket, bidirectional) {
+      var term = this;
+
+      bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
+      this.socket = socket;
+
+      socket.addEventListener('message', function (ev) {
+        term.write(ev.data);
+      });
+
+      if (bidirectional) {
+        this.on('data', function (data) {
+          socket.send(data);
+        });
+      }
+    };
+});
\ No newline at end of file
index 3dc339afb60cc4fab49dc4377d0cebf6546b13cc..69909c418c715cafe9ee7fb56710f4434dd45808 100644 (file)
  *               terminal row and truncate its width with the current number
  *               of columns)
  */
-Terminal.prototype.fit = function () {
-  var container = this.rowContainer,
-      subjectRow = this.rowContainer.firstElementChild,
-      rows,
-      contentBuffer,
-      characterWidth,
-      cols;
-
-  subjectRow.style.display = 'inline';
-  
-  contentBuffer = subjectRow.textContent;
-
-  subjectRow.innerHTML = '&nbsp;'; /* Arbitrary character to calculate its dimensions */
-  characterWidth = parseInt(subjectRow.offsetWidth);
-  characterHeight = parseInt(subjectRow.offsetHeight);
-
-  subjectRow.style.display = '';
-  
-  cols = container.offsetWidth / characterWidth;
-  cols = parseInt(cols);
-  
-  var parentElementStyle = window.getComputedStyle(this.element.parentElement),
-      parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
-      elementStyle = window.getComputedStyle(this.element),
-      elementPadding = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
-      availableHeight = parentElementHeight - elementPadding,
-      rowHeight = this.rowContainer.firstElementChild.offsetHeight;
-  
-  rows = parseInt(availableHeight / rowHeight);
-      
-  this.resize(cols, rows);
-};
\ No newline at end of file
+(function (fit) {
+    if (typeof define == 'function') {
+        /*
+         * Require.js is available
+         */
+        define(['../../src/xterm'], fit);
+    } else {
+        /*
+         * Plain browser environment
+         */ 
+        fit(this.Xterm);
+    }
+})(function (Xterm) {
+    Xterm.prototype.fit = function () {
+      var container = this.rowContainer,
+          subjectRow = this.rowContainer.firstElementChild,
+          rows,
+          contentBuffer,
+          characterWidth,
+          cols;
+
+      subjectRow.style.display = 'inline';
+
+      contentBuffer = subjectRow.textContent;
+
+      subjectRow.innerHTML = '&nbsp;'; /* Arbitrary character to calculate its dimensions */
+      characterWidth = parseInt(subjectRow.offsetWidth);
+      characterHeight = parseInt(subjectRow.offsetHeight);
+
+      subjectRow.style.display = '';
+
+      cols = container.offsetWidth / characterWidth;
+      cols = parseInt(cols);
+
+      var parentElementStyle = window.getComputedStyle(this.element.parentElement),
+          parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
+          elementStyle = window.getComputedStyle(this.element),
+          elementPadding = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
+          availableHeight = parentElementHeight - elementPadding,
+          rowHeight = this.rowContainer.firstElementChild.offsetHeight;
+
+      rows = parseInt(availableHeight / rowHeight);
+
+      this.resize(cols, rows);
+    };
+});
\ No newline at end of file
index a3012a7e40bbcd21c595016d605cb0480b459aa7..4816e951ee59b2c242cac8bde030cd3d4ce5dfee 100644 (file)
  * If the `fullscreen` argument has not been supplied, the
  * fullscreen mode is being toggled.
  */
-Terminal.prototype.toggleFullscreen = function (fullscreen) {
-  var fn;
-  
-  if (typeof fullscreen == 'undefined') {
-    fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add';
-  } else if (!fullscreen) {
-    fn = 'remove';
-  } else {
-    fn = 'add';
-  }
-  
-  this.element.classList[fn]('fullscreen');
-}
\ No newline at end of file
+(function (fullscreen) {
+    if (typeof define == 'function') {
+        /*
+         * Require.js is available
+         */
+        define(['../../src/xterm'], fullscreen);
+    } else {
+        /*
+         * Plain browser environment
+         */ 
+        fullscreen(this.Xterm);
+    }
+})(function (Xterm) {
+    Xterm.prototype.toggleFullscreen = function (fullscreen) {
+      var fn;
+
+      if (typeof fullscreen == 'undefined') {
+        fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add';
+      } else if (!fullscreen) {
+        fn = 'remove';
+      } else {
+        fn = 'add';
+      }
+
+      this.element.classList[fn]('fullscreen');
+    };
+});
\ No newline at end of file
index 44d048536ef85c82e9a2b4fa2c7ec05be7dc184b..15ac2bb96a9485dbd5774f6f1e77b1bb47721071 100644 (file)
  *   other features.
  */
 
-;(function() {
+(function (xterm) {
+    if (typeof define == 'function') {
+        /*
+         * Require.js is available
+         */
+        define([], xterm);
+    } else {
+        /*
+         * Plain browser environment
+         */ 
+        this.Xterm = xterm.call(this);
+        this.Terminal = this.Xterm; /* Backwards compatibility with term.js */
+    }
+})(function() {
+    /**
+     * Terminal Emulation References:
+     *   http://vt100.net/
+     *   http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
+     *   http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+     *   http://invisible-island.net/vttest/
+     *   http://www.inwap.com/pdp10/ansicode.txt
+     *   http://linux.die.net/man/4/console_codes
+     *   http://linux.die.net/man/7/urxvt
+     */
+
+    'use strict';
+
+    /**
+     * Shared
+     */
+
+    var window = this, document = this.document;
+
+    /**
+     * EventEmitter
+     */
+
+    function EventEmitter() {
+      this._events = this._events || {};
+    }
 
-/**
- * Terminal Emulation References:
- *   http://vt100.net/
- *   http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
- *   http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- *   http://invisible-island.net/vttest/
- *   http://www.inwap.com/pdp10/ansicode.txt
- *   http://linux.die.net/man/4/console_codes
- *   http://linux.die.net/man/7/urxvt
- */
+    EventEmitter.prototype.addListener = function(type, listener) {
+      this._events[type] = this._events[type] || [];
+      this._events[type].push(listener);
+    };
 
-'use strict';
+    EventEmitter.prototype.on = EventEmitter.prototype.addListener;
 
-/**
- * Shared
- */
+    EventEmitter.prototype.removeListener = function(type, listener) {
+      if (!this._events[type]) return;
 
-var window = this
-  , document = this.document;
+      var obj = this._events[type]
+        , i = obj.length;
 
-/**
- * EventEmitter
- */
+      while (i--) {
+        if (obj[i] === listener || obj[i].listener === listener) {
+          obj.splice(i, 1);
+          return;
+        }
+      }
+    };
+
+    EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
 
-function EventEmitter() {
-  this._events = this._events || {};
-}
+    EventEmitter.prototype.removeAllListeners = function(type) {
+      if (this._events[type]) delete this._events[type];
+    };
 
-EventEmitter.prototype.addListener = function(type, listener) {
-  this._events[type] = this._events[type] || [];
-  this._events[type].push(listener);
-};
+    EventEmitter.prototype.once = function(type, listener) {
+      function on() {
+        var args = Array.prototype.slice.call(arguments);
+        this.removeListener(type, on);
+        return listener.apply(this, args);
+      }
+      on.listener = listener;
+      return this.on(type, on);
+    };
 
-EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+    EventEmitter.prototype.emit = function(type) {
+      if (!this._events[type]) return;
 
-EventEmitter.prototype.removeListener = function(type, listener) {
-  if (!this._events[type]) return;
+      var args = Array.prototype.slice.call(arguments, 1)
+        , obj = this._events[type]
+        , l = obj.length
+        , i = 0;
 
-  var obj = this._events[type]
-    , i = obj.length;
+      for (; i < l; i++) {
+        obj[i].apply(this, args);
+      }
+    };
 
-  while (i--) {
-    if (obj[i] === listener || obj[i].listener === listener) {
-      obj.splice(i, 1);
-      return;
-    }
-  }
-};
+    EventEmitter.prototype.listeners = function(type) {
+      return this._events[type] = this._events[type] || [];
+    };
 
-EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
 
-EventEmitter.prototype.removeAllListeners = function(type) {
-  if (this._events[type]) delete this._events[type];
-};
+    /**
+     * States
+     */
+    var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6;
 
-EventEmitter.prototype.once = function(type, listener) {
-  function on() {
-    var args = Array.prototype.slice.call(arguments);
-    this.removeListener(type, on);
-    return listener.apply(this, args);
-  }
-  on.listener = listener;
-  return this.on(type, on);
-};
+    /**
+     * Terminal
+     */
 
-EventEmitter.prototype.emit = function(type) {
-  if (!this._events[type]) return;
+    function Terminal(options) {
+      var self = this;
 
-  var args = Array.prototype.slice.call(arguments, 1)
-    , obj = this._events[type]
-    , l = obj.length
-    , i = 0;
+      if (!(this instanceof Terminal)) {
+        return new Terminal(arguments[0], arguments[1], arguments[2]);
+      }
 
-  for (; i < l; i++) {
-    obj[i].apply(this, args);
-  }
-};
+      self.cancel = Terminal.cancel;
 
-EventEmitter.prototype.listeners = function(type) {
-  return this._events[type] = this._events[type] || [];
-};
+      EventEmitter.call(this);
 
+      if (typeof options === 'number') {
+        options = {
+          cols: arguments[0],
+          rows: arguments[1],
+          handler: arguments[2]
+        };
+      }
 
-/**
- * States
- */
-var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6;
+      options = options || {};
 
-/**
- * Terminal
- */
 
-function Terminal(options) {
-  var self = this;
+      Object.keys(Terminal.defaults).forEach(function(key) {
+        if (options[key] == null) {
+          options[key] = Terminal.options[key];
 
-  if (!(this instanceof Terminal)) {
-    return new Terminal(arguments[0], arguments[1], arguments[2]);
-  }
-  
-  self.cancel = Terminal.cancel;
+          if (Terminal[key] !== Terminal.defaults[key]) {
+            options[key] = Terminal[key];
+          }
+        }
+        self[key] = options[key];
+      });
 
-  EventEmitter.call(this);
+      if (options.colors.length === 8) {
+        options.colors = options.colors.concat(Terminal._colors.slice(8));
+      } else if (options.colors.length === 16) {
+        options.colors = options.colors.concat(Terminal._colors.slice(16));
+      } else if (options.colors.length === 10) {
+        options.colors = options.colors.slice(0, -2).concat(
+          Terminal._colors.slice(8, -2), options.colors.slice(-2));
+      } else if (options.colors.length === 18) {
+        options.colors = options.colors.concat(
+          Terminal._colors.slice(16, -2), options.colors.slice(-2));
+      }
+      this.colors = options.colors;
 
-  if (typeof options === 'number') {
-    options = {
-      cols: arguments[0],
-      rows: arguments[1],
-      handler: arguments[2]
-    };
-  }
+      this.options = options;
 
-  options = options || {};
-  
-  
-  Object.keys(Terminal.defaults).forEach(function(key) {
-    if (options[key] == null) {
-      options[key] = Terminal.options[key];
+      // this.context = options.context || window;
+      // this.document = options.document || document;
+      this.parent = options.body || options.parent
+        || (document ? document.getElementsByTagName('body')[0] : null);
 
-      if (Terminal[key] !== Terminal.defaults[key]) {
-        options[key] = Terminal[key];
+      this.cols = options.cols || options.geometry[0];
+      this.rows = options.rows || options.geometry[1];
+
+      if (options.handler) {
+        this.on('data', options.handler);
+      }
+
+      this.ybase = 0;
+      this.ydisp = 0;
+      this.x = 0;
+      this.y = 0;
+      this.cursorState = 0;
+      this.cursorHidden = false;
+      this.convertEol;
+      this.state = 0;
+      this.queue = '';
+      this.scrollTop = 0;
+      this.scrollBottom = this.rows - 1;
+
+      // modes
+      this.applicationKeypad = false;
+      this.applicationCursor = false;
+      this.originMode = false;
+      this.insertMode = false;
+      this.wraparoundMode = false;
+      this.normal = null;
+
+      // charset
+      this.charset = null;
+      this.gcharset = null;
+      this.glevel = 0;
+      this.charsets = [null];
+
+      // mouse properties
+      this.decLocator;
+      this.x10Mouse;
+      this.vt200Mouse;
+      this.vt300Mouse;
+      this.normalMouse;
+      this.mouseEvents;
+      this.sendFocus;
+      this.utfMouse;
+      this.sgrMouse;
+      this.urxvtMouse;
+
+      // misc
+      this.element;
+      this.children;
+      this.refreshStart;
+      this.refreshEnd;
+      this.savedX;
+      this.savedY;
+      this.savedCols;
+
+      // stream
+      this.readable = true;
+      this.writable = true;
+
+      this.defAttr = (0 << 18) | (257 << 9) | (256 << 0);
+      this.curAttr = this.defAttr;
+
+      this.params = [];
+      this.currentParam = 0;
+      this.prefix = '';
+      this.postfix = '';
+
+      this.lines = [];
+      var i = this.rows;
+      while (i--) {
+        this.lines.push(this.blankLine());
       }
+
+      this.tabs;
+      this.setupStops();
     }
-    self[key] = options[key];
-  });
-
-  if (options.colors.length === 8) {
-    options.colors = options.colors.concat(Terminal._colors.slice(8));
-  } else if (options.colors.length === 16) {
-    options.colors = options.colors.concat(Terminal._colors.slice(16));
-  } else if (options.colors.length === 10) {
-    options.colors = options.colors.slice(0, -2).concat(
-      Terminal._colors.slice(8, -2), options.colors.slice(-2));
-  } else if (options.colors.length === 18) {
-    options.colors = options.colors.concat(
-      Terminal._colors.slice(16, -2), options.colors.slice(-2));
-  }
-  this.colors = options.colors;
-
-  this.options = options;
-
-  // this.context = options.context || window;
-  // this.document = options.document || document;
-  this.parent = options.body || options.parent
-    || (document ? document.getElementsByTagName('body')[0] : null);
-
-  this.cols = options.cols || options.geometry[0];
-  this.rows = options.rows || options.geometry[1];
-
-  if (options.handler) {
-    this.on('data', options.handler);
-  }
-
-  this.ybase = 0;
-  this.ydisp = 0;
-  this.x = 0;
-  this.y = 0;
-  this.cursorState = 0;
-  this.cursorHidden = false;
-  this.convertEol;
-  this.state = 0;
-  this.queue = '';
-  this.scrollTop = 0;
-  this.scrollBottom = this.rows - 1;
-
-  // modes
-  this.applicationKeypad = false;
-  this.applicationCursor = false;
-  this.originMode = false;
-  this.insertMode = false;
-  this.wraparoundMode = false;
-  this.normal = null;
-
-  // charset
-  this.charset = null;
-  this.gcharset = null;
-  this.glevel = 0;
-  this.charsets = [null];
-
-  // mouse properties
-  this.decLocator;
-  this.x10Mouse;
-  this.vt200Mouse;
-  this.vt300Mouse;
-  this.normalMouse;
-  this.mouseEvents;
-  this.sendFocus;
-  this.utfMouse;
-  this.sgrMouse;
-  this.urxvtMouse;
-
-  // misc
-  this.element;
-  this.children;
-  this.refreshStart;
-  this.refreshEnd;
-  this.savedX;
-  this.savedY;
-  this.savedCols;
-
-  // stream
-  this.readable = true;
-  this.writable = true;
-
-  this.defAttr = (0 << 18) | (257 << 9) | (256 << 0);
-  this.curAttr = this.defAttr;
-
-  this.params = [];
-  this.currentParam = 0;
-  this.prefix = '';
-  this.postfix = '';
-
-  this.lines = [];
-  var i = this.rows;
-  while (i--) {
-    this.lines.push(this.blankLine());
-  }
-
-  this.tabs;
-  this.setupStops();
-}
-
-inherits(Terminal, EventEmitter);
-
-// back_color_erase feature for xterm.
-Terminal.prototype.eraseAttr = function() {
-  // if (this.is('screen')) return this.defAttr;
-  return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);
-};
 
-/**
- * Colors
- */
+    inherits(Terminal, EventEmitter);
 
-// Colors 0-15
-Terminal.tangoColors = [
-  // dark:
-  '#2e3436',
-  '#cc0000',
-  '#4e9a06',
-  '#c4a000',
-  '#3465a4',
-  '#75507b',
-  '#06989a',
-  '#d3d7cf',
-  // bright:
-  '#555753',
-  '#ef2929',
-  '#8ae234',
-  '#fce94f',
-  '#729fcf',
-  '#ad7fa8',
-  '#34e2e2',
-  '#eeeeec'
-];
-
-// Colors 0-15 + 16-255
-// Much thanks to TooTallNate for writing this.
-Terminal.colors = (function() {
-  var colors = Terminal.tangoColors.slice()
-    , r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
-    , i;
-
-  // 16-231
-  i = 0;
-  for (; i < 216; i++) {
-    out(r[(i / 36) % 6 | 0], r[(i / 6) % 6 | 0], r[i % 6]);
-  }
-
-  // 232-255 (grey)
-  i = 0;
-  for (; i < 24; i++) {
-    r = 8 + i * 10;
-    out(r, r, r);
-  }
-
-  function out(r, g, b) {
-    colors.push('#' + hex(r) + hex(g) + hex(b));
-  }
-
-  function hex(c) {
-    c = c.toString(16);
-    return c.length < 2 ? '0' + c : c;
-  }
-
-  return colors;
-})();
-
-Terminal._colors = Terminal.colors.slice();
-
-Terminal.vcolors = (function() {
-  var out = []
-    , colors = Terminal.colors
-    , i = 0
-    , color;
-
-  for (; i < 256; i++) {
-    color = parseInt(colors[i].substring(1), 16);
-    out.push([
-      (color >> 16) & 0xff,
-      (color >> 8) & 0xff,
-      color & 0xff
-    ]);
-  }
-
-  return out;
-})();
+    // back_color_erase feature for xterm.
+    Terminal.prototype.eraseAttr = function() {
+      // if (this.is('screen')) return this.defAttr;
+      return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);
+    };
 
-/**
- * Options
- */
+    /**
+     * Colors
+     */
+
+    // Colors 0-15
+    Terminal.tangoColors = [
+      // dark:
+      '#2e3436',
+      '#cc0000',
+      '#4e9a06',
+      '#c4a000',
+      '#3465a4',
+      '#75507b',
+      '#06989a',
+      '#d3d7cf',
+      // bright:
+      '#555753',
+      '#ef2929',
+      '#8ae234',
+      '#fce94f',
+      '#729fcf',
+      '#ad7fa8',
+      '#34e2e2',
+      '#eeeeec'
+    ];
+
+    // Colors 0-15 + 16-255
+    // Much thanks to TooTallNate for writing this.
+    Terminal.colors = (function() {
+      var colors = Terminal.tangoColors.slice()
+        , r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
+        , i;
+
+      // 16-231
+      i = 0;
+      for (; i < 216; i++) {
+        out(r[(i / 36) % 6 | 0], r[(i / 6) % 6 | 0], r[i % 6]);
+      }
 
-Terminal.defaults = {
-  colors: Terminal.colors,
-  theme: 'default',
-  convertEol: false,
-  termName: 'xterm',
-  geometry: [80, 24],
-  cursorBlink: false,
-  visualBell: false,
-  popOnBell: false,
-  scrollback: 1000,
-  screenKeys: false,
-  debug: false,
-  cancelEvents: false
-  // programFeatures: false,
-  // focusKeys: false,
-};
-
-Terminal.options = {};
-
-each(keys(Terminal.defaults), function(key) {
-  Terminal[key] = Terminal.defaults[key];
-  Terminal.options[key] = Terminal.defaults[key];
-});
+      // 232-255 (grey)
+      i = 0;
+      for (; i < 24; i++) {
+        r = 8 + i * 10;
+        out(r, r, r);
+      }
 
-/**
- * Focused Terminal
- */
-Terminal.prototype.focus = function() {
-  if (document.activeElement === this.textarea) {
-    return;
-  }
-
-  if (this.sendFocus) {
-    this.send('\x1b[I');
-  }
-  
-  this.showCursor();
-  this.textarea.focus();
-
-  this.emit('focus', {terminal: this});
-};
-
-Terminal.prototype.blur = function() {
-  if (Terminal.focus !== this) {
-    return;
-  }
-
-  this.cursorState = 0;
-  this.refresh(this.y, this.y);
-  
-  if (this.sendFocus) {
-    this.send('\x1b[O');
-  }
-
-  this.emit('blur', {terminal: this});
-};
+      function out(r, g, b) {
+        colors.push('#' + hex(r) + hex(g) + hex(b));
+      }
 
-/**
- * Initialize default behavior
- */
+      function hex(c) {
+        c = c.toString(16);
+        return c.length < 2 ? '0' + c : c;
+      }
 
-Terminal.prototype.initGlobal = function() {
-  Terminal.bindPaste(this);
-  Terminal.bindKeys(this);
-  Terminal.bindCopy(this);
-};
+      return colors;
+    })();
 
-/**
- * Bind to paste event
- */
-Terminal.bindPaste = function(term) {
-  on([term.textarea, term.element], 'paste', function(ev) {
-    ev.stopPropagation();
-    if (ev.clipboardData) {
-      var text = ev.clipboardData.getData('text/plain');
-      term.handler(text);
-    }
-    term.textarea.value = '';
-    return term.cancel(ev);
-  });
-};
-
-  
-/*
- * Apply key handling to the terminal
- */
-Terminal.bindKeys = function(term) {
-  on(term.element, 'keydown', function(ev) {
-    if (document.activeElement != this) {
-      return;
-    }
-    term.keyDown(ev);
-  }, true);
+    Terminal._colors = Terminal.colors.slice();
 
-  on(term.element, 'keypress', function(ev) {
-    if (document.activeElement != this) {
-      return;
-    }
-    term.keyPress(ev);
-  }, true);
-  
-  on(term.element, 'keyup', term.focus.bind(term));
-
-  on(term.textarea, 'keydown', function(ev) {
-    term.keyDown(ev);
-  }, true);
-
-  on(term.textarea, 'keypress', function(ev) {
-    term.keyPress(ev);
-    
-    /*
-    * Truncate the textarea's value, since it is not needed
-    */
-    this.value = '';
-  }, true);
-};
+    Terminal.vcolors = (function() {
+      var out = []
+        , colors = Terminal.colors
+        , i = 0
+        , color;
 
+      for (; i < 256; i++) {
+        color = parseInt(colors[i].substring(1), 16);
+        out.push([
+          (color >> 16) & 0xff,
+          (color >> 8) & 0xff,
+          color & 0xff
+        ]);
+      }
 
-/*
- * Bind copy event
- */
-Terminal.bindCopy = function(term) {
-  on(term.element, 'copy', function(ev) {
-    return; //temporary
-  });
-};
+      return out;
+    })();
+
+    /**
+     * Options
+     */
+
+    Terminal.defaults = {
+      colors: Terminal.colors,
+      theme: 'default',
+      convertEol: false,
+      termName: 'xterm',
+      geometry: [80, 24],
+      cursorBlink: false,
+      visualBell: false,
+      popOnBell: false,
+      scrollback: 1000,
+      screenKeys: false,
+      debug: false,
+      cancelEvents: false
+      // programFeatures: false,
+      // focusKeys: false,
+    };
 
+    Terminal.options = {};
 
-/*
- * Insert the given row to the terminal or produce a new one
- * if no row argument is passed. Return the inserted row.
- */
-Terminal.prototype.insertRow = function (row) {
-  if (typeof row != 'object') {
-    row = document.createElement('div');
-  }
-  
-  this.rowContainer.appendChild(row);
-  this.children.push(row);
-  
-  return row;
-}
-  
-  
-/*
- * Open Terminal in the DOM
- */
-Terminal.prototype.open = function(parent) {
-  var self=this, i=0, div;
-
-  this.parent = parent || this.parent;
-
-  if (!this.parent) {
-    throw new Error('Terminal requires a parent element.');
-  }
-
-  /*
-  * Grab global elements
-  */
-  this.context = this.parent.ownerDocument.defaultView;
-  this.document = this.parent.ownerDocument;
-  this.body = this.document.getElementsByTagName('body')[0];
-
-  /*
-  * Parse User-Agent
-  */
-  if (this.context.navigator && this.context.navigator.userAgent) {
-    this.isMac = !!~this.context.navigator.userAgent.indexOf('Mac');
-    this.isIpad = !!~this.context.navigator.userAgent.indexOf('iPad');
-    this.isIphone = !!~this.context.navigator.userAgent.indexOf('iPhone');
-    this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE');
-  }
-
-  /*
-  * Create main element container
-  */
-  this.element = this.document.createElement('div');
-  this.element.classList.add('terminal', 'xterm', 'xterm-theme-' + this.theme);
-  this.element.setAttribute('tabindex', 0);
-
-  /*
-  * Create the container that will hold the lines of the terminal and then
-  * produce the lines the lines.
-  */
-  this.rowContainer = document.createElement('div');
-  this.rowContainer.classList.add('xterm-rows');
-  this.element.appendChild(this.rowContainer);
-  this.children = [];
-  
-  /*
-  * Create the container that will hold helpers like the textarea for
-  * capturing DOM Events. Then produce the helpers.
-  */
-  this.helperContainer = document.createElement('div');
-  this.helperContainer.classList.add('xterm-helpers');
-  this.element.appendChild(this.helperContainer);
-  this.textarea = document.createElement('textarea');
-  this.textarea.classList.add('xterm-helper-textarea');
-  this.textarea.setAttribute('autocorrect', 'off');
-  this.textarea.setAttribute('autocapitalize', 'off');
-  this.textarea.setAttribute('spellcheck', 'false');
-  this.textarea.tabIndex = 0;
-  this.helperContainer.appendChild(this.textarea);
-  
-  for (; i < this.rows; i++) {
-    this.insertRow();
-  }
-  this.parent.appendChild(this.element);
-
-  // Draw the screen.
-  this.refresh(0, this.rows - 1);
-
-  // Initialize global actions that
-  // need to be taken on the document.
-  this.initGlobal();
-
-  // Ensure there is a Terminal.focus.
-  this.focus();
-
-  // Start blinking the cursor.
-  this.startBlink();
-
-  on(this.element, 'mouseup', function() {
-    var selection = document.getSelection();
-    if (selection.type != 'Range') {
-      self.focus();
-    }
-  });
-
-  // Listen for mouse events and translate
-  // them into terminal mouse protocols.
-  this.bindMouse();
-
-  // Figure out whether boldness affects
-  // the character width of monospace fonts.
-  if (Terminal.brokenBold == null) {
-    Terminal.brokenBold = isBoldBroken(this.document);
-  }
-
-  this.emit('open');
-};
-
-// XTerm mouse events
-// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
-// To better understand these
-// the xterm code is very helpful:
-// Relevant files:
-//   button.c, charproc.c, misc.c
-// Relevant functions in xterm/button.c:
-//   BtnCode, EmitButtonCode, EditorButton, SendMousePosition
-Terminal.prototype.bindMouse = function() {
-  var el = this.element
-    , self = this
-    , pressed = 32;
-
-  var wheelEvent = 'onmousewheel' in this.context
-    ? 'mousewheel'
-    : 'DOMMouseScroll';
-
-  // mouseup, mousedown, mousewheel
-  // left click: ^[[M 3<^[[M#3<
-  // mousewheel up: ^[[M`3>
-  function sendButton(ev) {
-    var button
-      , pos;
-
-    // get the xterm-style button
-    button = getButton(ev);
-
-    // get mouse coordinates
-    pos = getCoords(ev);
-    if (!pos) return;
-
-    sendEvent(button, pos);
-
-    switch (ev.type) {
-      case 'mousedown':
-        pressed = button;
-        break;
-      case 'mouseup':
-        // keep it at the left
-        // button, just in case.
-        pressed = 32;
-        break;
-      case wheelEvent:
-        // nothing. don't
-        // interfere with
-        // `pressed`.
-        break;
-    }
-  }
-
-  // motion example of a left click:
-  // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
-  function sendMove(ev) {
-    var button = pressed
-      , pos;
-
-    pos = getCoords(ev);
-    if (!pos) return;
-
-    // buttons marked as motions
-    // are incremented by 32
-    button += 32;
-
-    sendEvent(button, pos);
-  }
-
-  // encode button and
-  // position to characters
-  function encode(data, ch) {
-    if (!self.utfMouse) {
-      if (ch === 255) return data.push(0);
-      if (ch > 127) ch = 127;
-      data.push(ch);
-    } else {
-      if (ch === 2047) return data.push(0);
-      if (ch < 127) {
-        data.push(ch);
-      } else {
-        if (ch > 2047) ch = 2047;
-        data.push(0xC0 | (ch >> 6));
-        data.push(0x80 | (ch & 0x3F));
+    each(keys(Terminal.defaults), function(key) {
+      Terminal[key] = Terminal.defaults[key];
+      Terminal.options[key] = Terminal.defaults[key];
+    });
+
+    /**
+     * Focused Terminal
+     */
+    Terminal.prototype.focus = function() {
+      if (document.activeElement === this.textarea) {
+        return;
       }
-    }
-  }
-
-  // send a mouse event:
-  // regular/utf8: ^[[M Cb Cx Cy
-  // urxvt: ^[[ Cb ; Cx ; Cy M
-  // sgr: ^[[ Cb ; Cx ; Cy M/m
-  // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
-  // locator: CSI P e ; P b ; P r ; P c ; P p & w
-  function sendEvent(button, pos) {
-    // self.emit('mouse', {
-    //   x: pos.x - 32,
-    //   y: pos.x - 32,
-    //   button: button
-    // });
-
-    if (self.vt300Mouse) {
-      // NOTE: Unstable.
-      // http://www.vt100.net/docs/vt3xx-gp/chapter15.html
-      button &= 3;
-      pos.x -= 32;
-      pos.y -= 32;
-      var data = '\x1b[24';
-      if (button === 0) data += '1';
-      else if (button === 1) data += '3';
-      else if (button === 2) data += '5';
-      else if (button === 3) return;
-      else data += '0';
-      data += '~[' + pos.x + ',' + pos.y + ']\r';
-      self.send(data);
-      return;
-    }
 
-    if (self.decLocator) {
-      // NOTE: Unstable.
-      button &= 3;
-      pos.x -= 32;
-      pos.y -= 32;
-      if (button === 0) button = 2;
-      else if (button === 1) button = 4;
-      else if (button === 2) button = 6;
-      else if (button === 3) button = 3;
-      self.send('\x1b['
-        + button
-        + ';'
-        + (button === 3 ? 4 : 0)
-        + ';'
-        + pos.y
-        + ';'
-        + pos.x
-        + ';'
-        + (pos.page || 0)
-        + '&w');
-      return;
-    }
+      if (this.sendFocus) {
+        this.send('\x1b[I');
+      }
 
-    if (self.urxvtMouse) {
-      pos.x -= 32;
-      pos.y -= 32;
-      pos.x++;
-      pos.y++;
-      self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M');
-      return;
-    }
+      this.showCursor();
+      this.textarea.focus();
 
-    if (self.sgrMouse) {
-      pos.x -= 32;
-      pos.y -= 32;
-      self.send('\x1b[<'
-        + ((button & 3) === 3 ? button & ~3 : button)
-        + ';'
-        + pos.x
-        + ';'
-        + pos.y
-        + ((button & 3) === 3 ? 'm' : 'M'));
-      return;
-    }
+      this.emit('focus', {terminal: this});
+    };
 
-    var data = [];
-
-    encode(data, button);
-    encode(data, pos.x);
-    encode(data, pos.y);
-
-    self.send('\x1b[M' + String.fromCharCode.apply(String, data));
-  }
-
-  function getButton(ev) {
-    var button
-      , shift
-      , meta
-      , ctrl
-      , mod;
-
-    // two low bits:
-    // 0 = left
-    // 1 = middle
-    // 2 = right
-    // 3 = release
-    // wheel up/down:
-    // 1, and 2 - with 64 added
-    switch (ev.type) {
-      case 'mousedown':
-        button = ev.button != null
-          ? +ev.button
-          : ev.which != null
-            ? ev.which - 1
-            : null;
-
-        if (self.isMSIE) {
-          button = button === 1 ? 0 : button === 4 ? 1 : button;
-        }
-        break;
-      case 'mouseup':
-        button = 3;
-        break;
-      case 'DOMMouseScroll':
-        button = ev.detail < 0
-          ? 64
-          : 65;
-        break;
-      case 'mousewheel':
-        button = ev.wheelDeltaY > 0
-          ? 64
-          : 65;
-        break;
-    }
+    Terminal.prototype.blur = function() {
+      if (Terminal.focus !== this) {
+        return;
+      }
 
-    // next three bits are the modifiers:
-    // 4 = shift, 8 = meta, 16 = control
-    shift = ev.shiftKey ? 4 : 0;
-    meta = ev.metaKey ? 8 : 0;
-    ctrl = ev.ctrlKey ? 16 : 0;
-    mod = shift | meta | ctrl;
-
-    // no mods
-    if (self.vt200Mouse) {
-      // ctrl only
-      mod &= ctrl;
-    } else if (!self.normalMouse) {
-      mod = 0;
-    }
+      this.cursorState = 0;
+      this.refresh(this.y, this.y);
 
-    // increment to SP
-    button = (32 + (mod << 2)) + button;
+      if (this.sendFocus) {
+        this.send('\x1b[O');
+      }
 
-    return button;
-  }
+      this.emit('blur', {terminal: this});
+    };
 
-  // mouse coordinates measured in cols/rows
-  function getCoords(ev) {
-    var x, y, w, h, el;
+    /**
+     * Initialize default behavior
+     */
 
-    // ignore browsers without pageX for now
-    if (ev.pageX == null) return;
+    Terminal.prototype.initGlobal = function() {
+      Terminal.bindPaste(this);
+      Terminal.bindKeys(this);
+      Terminal.bindCopy(this);
+    };
 
-    x = ev.pageX;
-    y = ev.pageY;
-    el = self.element;
+    /**
+     * Bind to paste event
+     */
+    Terminal.bindPaste = function(term) {
+      on([term.textarea, term.element], 'paste', function(ev) {
+        ev.stopPropagation();
+        if (ev.clipboardData) {
+          var text = ev.clipboardData.getData('text/plain');
+          term.handler(text);
+        }
+        term.textarea.value = '';
+        return term.cancel(ev);
+      });
+    };
 
-    // should probably check offsetParent
-    // but this is more portable
-    while (el && el !== self.document.documentElement) {
-      x -= el.offsetLeft;
-      y -= el.offsetTop;
-      el = 'offsetParent' in el
-        ? el.offsetParent
-        : el.parentNode;
-    }
 
-    // convert to cols/rows
-    w = self.element.clientWidth;
-    h = self.element.clientHeight;
-    x = Math.round((x / w) * self.cols);
-    y = Math.round((y / h) * self.rows);
-
-    // be sure to avoid sending
-    // bad positions to the program
-    if (x < 0) x = 0;
-    if (x > self.cols) x = self.cols;
-    if (y < 0) y = 0;
-    if (y > self.rows) y = self.rows;
-
-    // xterm sends raw bytes and
-    // starts at 32 (SP) for each.
-    x += 32;
-    y += 32;
-
-    return {
-      x: x,
-      y: y,
-      type: ev.type === wheelEvent
-        ? 'mousewheel'
-        : ev.type
-    };
-  }
+    /*
+     * Apply key handling to the terminal
+     */
+    Terminal.bindKeys = function(term) {
+      on(term.element, 'keydown', function(ev) {
+        if (document.activeElement != this) {
+          return;
+        }
+        term.keyDown(ev);
+      }, true);
 
-  on(el, 'mousedown', function(ev) {
-    if (!self.mouseEvents) return;
+      on(term.element, 'keypress', function(ev) {
+        if (document.activeElement != this) {
+          return;
+        }
+        term.keyPress(ev);
+      }, true);
 
-    // send the button
-    sendButton(ev);
+      on(term.element, 'keyup', term.focus.bind(term));
 
-    // ensure focus
-    self.focus();
+      on(term.textarea, 'keydown', function(ev) {
+        term.keyDown(ev);
+      }, true);
 
-    // fix for odd bug
-    //if (self.vt200Mouse && !self.normalMouse) {
-    if (self.vt200Mouse) {
-      sendButton({ __proto__: ev, type: 'mouseup' });
-      return self.cancel(ev);
-    }
+      on(term.textarea, 'keypress', function(ev) {
+        term.keyPress(ev);
 
-    // bind events
-    if (self.normalMouse) on(self.document, 'mousemove', sendMove);
+        /*
+        * Truncate the textarea's value, since it is not needed
+        */
+        this.value = '';
+      }, true);
+    };
 
-    // x10 compatibility mode can't send button releases
-    if (!self.x10Mouse) {
-      on(self.document, 'mouseup', function up(ev) {
-        sendButton(ev);
-        if (self.normalMouse) off(self.document, 'mousemove', sendMove);
-        off(self.document, 'mouseup', up);
-        return self.cancel(ev);
+
+    /*
+     * Bind copy event
+     */
+    Terminal.bindCopy = function(term) {
+      on(term.element, 'copy', function(ev) {
+        return; //temporary
       });
-    }
+    };
 
-    return self.cancel(ev);
-  });
-
-  //if (self.normalMouse) {
-  //  on(self.document, 'mousemove', sendMove);
-  //}
-
-  on(el, wheelEvent, function(ev) {
-    if (!self.mouseEvents) return;
-    if (self.x10Mouse
-        || self.vt300Mouse
-        || self.decLocator) return;
-    sendButton(ev);
-    return self.cancel(ev);
-  });
-
-  // allow mousewheel scrolling in
-  // the shell for example
-  on(el, wheelEvent, function(ev) {
-    if (self.mouseEvents) return;
-    if (self.applicationKeypad) return;
-    if (ev.type === 'DOMMouseScroll') {
-      self.scrollDisp(ev.detail < 0 ? -1 : 1);
-    } else {
-      self.scrollDisp(ev.wheelDeltaY > 0 ? -1 : 1);
-    }
-    return self.cancel(ev);
-  });
-};
 
-/**
- * Destroy Terminal
- */
+    /*
+     * Insert the given row to the terminal or produce a new one
+     * if no row argument is passed. Return the inserted row.
+     */
+    Terminal.prototype.insertRow = function (row) {
+      if (typeof row != 'object') {
+        row = document.createElement('div');
+      }
 
-Terminal.prototype.destroy = function() {
-  this.readable = false;
-  this.writable = false;
-  this._events = {};
-  this.handler = function() {};
-  this.write = function() {};
-  if (this.element.parentNode) {
-    this.element.parentNode.removeChild(this.element);
-  }
-  //this.emit('close');
-};
-
-  
-/*
- * Rendering Engine
- *
- * In the screen buffer, each character
- * is stored as a an array with a character
- * and a 32-bit integer.
- * First value: a utf-16 character.
- * Second value:
- * Next 9 bits: background color (0-511).
- * Next 9 bits: foreground color (0-511).
- * Next 14 bits: a mask for misc. flags:
- *   1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible
-*/
-Terminal.prototype.refresh = function(start, end) {
-  var x, y, i, line, out, ch, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
-
-  if (end - start >= this.rows / 2) {
-    parent = this.element.parentNode;
-    if (parent) parent.removeChild(this.element);
-  }
-
-  width = this.cols;
-  y = start;
-
-  if (end >= this.lines.length) {
-    this.log('`end` is too large. Most likely a bad CSR.');
-    end = this.lines.length - 1;
-  }
-
-  for (; y <= end; y++) {
-    row = y + this.ydisp;
-
-    line = this.lines[row];
-    out = '';
-
-    if (y === this.y
-        && this.cursorState
-        && (this.ydisp === this.ybase)
-        && !this.cursorHidden) {
-      x = this.x;
-    } else {
-      x = -1;
+      this.rowContainer.appendChild(row);
+      this.children.push(row);
+
+      return row;
     }
 
-    attr = this.defAttr;
-    i = 0;
 
-    for (; i < width; i++) {
-      data = line[i][0];
-      ch = line[i][1];
+    /*
+     * Open Terminal in the DOM
+     */
+    Terminal.prototype.open = function(parent) {
+      var self=this, i=0, div;
 
-      if (i === x) data = -1;
+      this.parent = parent || this.parent;
 
-      if (data !== attr) {
-        if (attr !== this.defAttr) {
-          out += '</span>';
-        }
-        if (data !== this.defAttr) {
-          if (data === -1) {
-            out += '<span class="reverse-video terminal-cursor">';
-          } else {
-            out += '<span class="';
+      if (!this.parent) {
+        throw new Error('Terminal requires a parent element.');
+      }
 
-            bg = data & 0x1ff;
-            fg = (data >> 9) & 0x1ff;
-            flags = data >> 18;
+      /*
+      * Grab global elements
+      */
+      this.context = this.parent.ownerDocument.defaultView;
+      this.document = this.parent.ownerDocument;
+      this.body = this.document.getElementsByTagName('body')[0];
+
+      /*
+      * Parse User-Agent
+      */
+      if (this.context.navigator && this.context.navigator.userAgent) {
+        this.isMac = !!~this.context.navigator.userAgent.indexOf('Mac');
+        this.isIpad = !!~this.context.navigator.userAgent.indexOf('iPad');
+        this.isIphone = !!~this.context.navigator.userAgent.indexOf('iPhone');
+        this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE');
+      }
 
-            // bold
-            if (flags & 1) {
-              if (!Terminal.brokenBold) {
-                out += ' xterm-bold ';
-              }
-              // See: XTerm*boldColors
-              if (fg < 8) fg += 8;
-            }
+      /*
+      * Create main element container
+      */
+      this.element = this.document.createElement('div');
+      this.element.classList.add('terminal', 'xterm', 'xterm-theme-' + this.theme);
+      this.element.setAttribute('tabindex', 0);
+
+      /*
+      * Create the container that will hold the lines of the terminal and then
+      * produce the lines the lines.
+      */
+      this.rowContainer = document.createElement('div');
+      this.rowContainer.classList.add('xterm-rows');
+      this.element.appendChild(this.rowContainer);
+      this.children = [];
+
+      /*
+      * Create the container that will hold helpers like the textarea for
+      * capturing DOM Events. Then produce the helpers.
+      */
+      this.helperContainer = document.createElement('div');
+      this.helperContainer.classList.add('xterm-helpers');
+      this.element.appendChild(this.helperContainer);
+      this.textarea = document.createElement('textarea');
+      this.textarea.classList.add('xterm-helper-textarea');
+      this.textarea.setAttribute('autocorrect', 'off');
+      this.textarea.setAttribute('autocapitalize', 'off');
+      this.textarea.setAttribute('spellcheck', 'false');
+      this.textarea.tabIndex = 0;
+      this.helperContainer.appendChild(this.textarea);
+
+      for (; i < this.rows; i++) {
+        this.insertRow();
+      }
+      this.parent.appendChild(this.element);
 
-            // underline
-            if (flags & 2) {
-              out += ' xterm-underline ';
-            }
+      // Draw the screen.
+      this.refresh(0, this.rows - 1);
 
-            // blink
-            if (flags & 4) {
-                out += ' xterm-blink ';
-            }
+      // Initialize global actions that
+      // need to be taken on the document.
+      this.initGlobal();
 
-            // inverse
-            if (flags & 8) {
-              bg = (data >> 9) & 0x1ff;
-              fg = data & 0x1ff;
-              // Should inverse just be before the
-              // above boldColors effect instead?
-              if ((flags & 1) && fg < 8) fg += 8;
-            }
+      // Ensure there is a Terminal.focus.
+      this.focus();
 
-            // invisible
-            if (flags & 16) {
-              out += ' xterm-hidden ';
-            }
+      // Start blinking the cursor.
+      this.startBlink();
 
-            if (bg !== 256) {
-              out += ' xterm-bg-color-' + bg + ' ';
-            }
+      on(this.element, 'mouseup', function() {
+        var selection = document.getSelection();
+        if (selection.type != 'Range') {
+          self.focus();
+        }
+      });
 
-            if (fg !== 257) {
-              out += ' xterm-color-' + fg + ' ';
-            }
+      // Listen for mouse events and translate
+      // them into terminal mouse protocols.
+      this.bindMouse();
 
-            out += '">';
-          }
-        }
+      // Figure out whether boldness affects
+      // the character width of monospace fonts.
+      if (Terminal.brokenBold == null) {
+        Terminal.brokenBold = isBoldBroken(this.document);
       }
 
-      switch (ch) {
-        case '&':
-          out += '&amp;';
-          break;
-        case '<':
-          out += '&lt;';
-          break;
-        case '>':
-          out += '&gt;';
-          break;
-        default:
-          if (ch <= ' ') {
-            out += '&nbsp;';
-          } else {
-            if (isWide(ch)) i++;
-            out += ch;
-          }
-          break;
-      }
+      this.emit('open');
+    };
 
-      attr = data;
-    }
+    // XTerm mouse events
+    // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
+    // To better understand these
+    // the xterm code is very helpful:
+    // Relevant files:
+    //   button.c, charproc.c, misc.c
+    // Relevant functions in xterm/button.c:
+    //   BtnCode, EmitButtonCode, EditorButton, SendMousePosition
+    Terminal.prototype.bindMouse = function() {
+      var el = this.element
+        , self = this
+        , pressed = 32;
+
+      var wheelEvent = 'onmousewheel' in this.context
+        ? 'mousewheel'
+        : 'DOMMouseScroll';
 
-    if (attr !== this.defAttr) {
-      out += '</span>';
-    }
-    
-    this.children[y].innerHTML = out;
-  }
-
-  if (parent) {
-    parent.appendChild(this.element);
-  }
-  
-  /*
-  *  Return focus to previously focused element
-  */
-  focused.focus();
-  this.emit('refresh', {element: this.element, start: start, end: end});
-};
-
-Terminal.prototype._cursorBlink = function() {
-  if (Terminal.focus !== this) return;
-  this.cursorState ^= 1;
-  this.refresh(this.y, this.y);
-};
-
-Terminal.prototype.showCursor = function() {
-  if (!this.cursorState) {
-    this.cursorState = 1;
-    this.refresh(this.y, this.y);
-  } else {
-    // Temporarily disabled:
-    // this.refreshBlink();
-  }
-};
-
-Terminal.prototype.startBlink = function() {
-  if (!this.cursorBlink) return;
-  var self = this;
-  this._blinker = function() {
-    self._cursorBlink();
-  };
-  this._blink = setInterval(this._blinker, 500);
-};
-
-Terminal.prototype.refreshBlink = function() {
-  if (!this.cursorBlink) return;
-  clearInterval(this._blink);
-  this._blink = setInterval(this._blinker, 500);
-};
-
-Terminal.prototype.scroll = function() {
-  var row;
-
-  if (++this.ybase === this.scrollback) {
-    this.ybase = this.ybase / 2 | 0;
-    this.lines = this.lines.slice(-(this.ybase + this.rows) + 1);
-  }
-
-  this.ydisp = this.ybase;
-
-  // last line
-  row = this.ybase + this.rows - 1;
-
-  // subtract the bottom scroll region
-  row -= this.rows - 1 - this.scrollBottom;
-
-  if (row === this.lines.length) {
-    // potential optimization:
-    // pushing is faster than splicing
-    // when they amount to the same
-    // behavior.
-    this.lines.push(this.blankLine());
-  } else {
-    // add our new line
-    this.lines.splice(row, 0, this.blankLine());
-  }
-
-  if (this.scrollTop !== 0) {
-    if (this.ybase !== 0) {
-      this.ybase--;
-      this.ydisp = this.ybase;
-    }
-    this.lines.splice(this.ybase + this.scrollTop, 1);
-  }
-
-  // this.maxRange();
-  this.updateRange(this.scrollTop);
-  this.updateRange(this.scrollBottom);
-};
-
-Terminal.prototype.scrollDisp = function(disp) {
-  this.ydisp += disp;
-
-  if (this.ydisp > this.ybase) {
-    this.ydisp = this.ybase;
-  } else if (this.ydisp < 0) {
-    this.ydisp = 0;
-  }
-
-  this.refresh(0, this.rows - 1);
-};
-
-Terminal.prototype.write = function(data) {
-  var l = data.length, i = 0, j, cs, ch;
-
-  this.refreshStart = this.y;
-  this.refreshEnd = this.y;
-
-  if (this.ybase !== this.ydisp) {
-    this.ydisp = this.ybase;
-    this.maxRange();
-  }
-
-  for (; i < l; i++) {
-    ch = data[i];
-    switch (this.state) {
-      case normal:
-        switch (ch) {
-          case '\x07':
-            this.bell();
-            break;
+      // mouseup, mousedown, mousewheel
+      // left click: ^[[M 3<^[[M#3<
+      // mousewheel up: ^[[M`3>
+      function sendButton(ev) {
+        var button
+          , pos;
 
-          // '\n', '\v', '\f'
-          case '\n':
-          case '\x0b':
-          case '\x0c':
-            if (this.convertEol) {
-              this.x = 0;
-            }
-            this.y++;
-            if (this.y > this.scrollBottom) {
-              this.y--;
-              this.scroll();
-            }
-            break;
+        // get the xterm-style button
+        button = getButton(ev);
 
-          // '\r'
-          case '\r':
-            this.x = 0;
-            break;
+        // get mouse coordinates
+        pos = getCoords(ev);
+        if (!pos) return;
 
-          // '\b'
-          case '\x08':
-            if (this.x > 0) {
-              this.x--;
-            }
-            break;
+        sendEvent(button, pos);
 
-          // '\t'
-          case '\t':
-            this.x = this.nextStop();
+        switch (ev.type) {
+          case 'mousedown':
+            pressed = button;
             break;
-
-          // shift out
-          case '\x0e':
-            this.setgLevel(1);
+          case 'mouseup':
+            // keep it at the left
+            // button, just in case.
+            pressed = 32;
             break;
-
-          // shift in
-          case '\x0f':
-            this.setgLevel(0);
+          case wheelEvent:
+            // nothing. don't
+            // interfere with
+            // `pressed`.
             break;
+        }
+      }
 
-          // '\e'
-          case '\x1b':
-            this.state = escaped;
-            break;
+      // motion example of a left click:
+      // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
+      function sendMove(ev) {
+        var button = pressed
+          , pos;
 
-          default:
-            // ' '
-            if (ch >= ' ') {
-              if (this.charset && this.charset[ch]) {
-                ch = this.charset[ch];
-              }
+        pos = getCoords(ev);
+        if (!pos) return;
 
-              if (this.x >= this.cols) {
-                this.x = 0;
-                this.y++;
-                if (this.y > this.scrollBottom) {
-                  this.y--;
-                  this.scroll();
-                }
-              }
+        // buttons marked as motions
+        // are incremented by 32
+        button += 32;
 
-              this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch];
-              this.x++;
-              this.updateRange(this.y);
+        sendEvent(button, pos);
+      }
 
-              if (isWide(ch)) {
-                j = this.y + this.ybase;
-                if (this.cols < 2 || this.x >= this.cols) {
-                  this.lines[j][this.x - 1] = [this.curAttr, ' '];
-                  break;
-                }
-                this.lines[j][this.x] = [this.curAttr, ' '];
-                this.x++;
-              }
-            }
-            break;
+      // encode button and
+      // position to characters
+      function encode(data, ch) {
+        if (!self.utfMouse) {
+          if (ch === 255) return data.push(0);
+          if (ch > 127) ch = 127;
+          data.push(ch);
+        } else {
+          if (ch === 2047) return data.push(0);
+          if (ch < 127) {
+            data.push(ch);
+          } else {
+            if (ch > 2047) ch = 2047;
+            data.push(0xC0 | (ch >> 6));
+            data.push(0x80 | (ch & 0x3F));
+          }
         }
-        break;
-      case escaped:
-        switch (ch) {
-          // ESC [ Control Sequence Introducer ( CSI is 0x9b).
-          case '[':
-            this.params = [];
-            this.currentParam = 0;
-            this.state = csi;
-            break;
-
-          // ESC ] Operating System Command ( OSC is 0x9d).
-          case ']':
-            this.params = [];
-            this.currentParam = 0;
-            this.state = osc;
-            break;
+      }
 
-          // ESC P Device Control String ( DCS is 0x90).
-          case 'P':
-            this.params = [];
-            this.currentParam = 0;
-            this.state = dcs;
-            break;
+      // send a mouse event:
+      // regular/utf8: ^[[M Cb Cx Cy
+      // urxvt: ^[[ Cb ; Cx ; Cy M
+      // sgr: ^[[ Cb ; Cx ; Cy M/m
+      // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
+      // locator: CSI P e ; P b ; P r ; P c ; P p & w
+      function sendEvent(button, pos) {
+        // self.emit('mouse', {
+        //   x: pos.x - 32,
+        //   y: pos.x - 32,
+        //   button: button
+        // });
+
+        if (self.vt300Mouse) {
+          // NOTE: Unstable.
+          // http://www.vt100.net/docs/vt3xx-gp/chapter15.html
+          button &= 3;
+          pos.x -= 32;
+          pos.y -= 32;
+          var data = '\x1b[24';
+          if (button === 0) data += '1';
+          else if (button === 1) data += '3';
+          else if (button === 2) data += '5';
+          else if (button === 3) return;
+          else data += '0';
+          data += '~[' + pos.x + ',' + pos.y + ']\r';
+          self.send(data);
+          return;
+        }
 
-          // ESC _ Application Program Command ( APC is 0x9f).
-          case '_':
-            this.state = ignore;
-            break;
+        if (self.decLocator) {
+          // NOTE: Unstable.
+          button &= 3;
+          pos.x -= 32;
+          pos.y -= 32;
+          if (button === 0) button = 2;
+          else if (button === 1) button = 4;
+          else if (button === 2) button = 6;
+          else if (button === 3) button = 3;
+          self.send('\x1b['
+            + button
+            + ';'
+            + (button === 3 ? 4 : 0)
+            + ';'
+            + pos.y
+            + ';'
+            + pos.x
+            + ';'
+            + (pos.page || 0)
+            + '&w');
+          return;
+        }
 
-          // ESC ^ Privacy Message ( PM is 0x9e).
-          case '^':
-            this.state = ignore;
-            break;
+        if (self.urxvtMouse) {
+          pos.x -= 32;
+          pos.y -= 32;
+          pos.x++;
+          pos.y++;
+          self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M');
+          return;
+        }
 
-          // ESC c Full Reset (RIS).
-          case 'c':
-            this.reset();
-            break;
+        if (self.sgrMouse) {
+          pos.x -= 32;
+          pos.y -= 32;
+          self.send('\x1b[<'
+            + ((button & 3) === 3 ? button & ~3 : button)
+            + ';'
+            + pos.x
+            + ';'
+            + pos.y
+            + ((button & 3) === 3 ? 'm' : 'M'));
+          return;
+        }
 
-          // ESC E Next Line ( NEL is 0x85).
-          // ESC D Index ( IND is 0x84).
-          case 'E':
-            this.x = 0;
-            ;
-          case 'D':
-            this.index();
-            break;
+        var data = [];
 
-          // ESC M Reverse Index ( RI is 0x8d).
-          case 'M':
-            this.reverseIndex();
-            break;
+        encode(data, button);
+        encode(data, pos.x);
+        encode(data, pos.y);
 
-          // ESC % Select default/utf-8 character set.
-          // @ = default, G = utf-8
-          case '%':
-            //this.charset = null;
-            this.setgLevel(0);
-            this.setgCharset(0, Terminal.charsets.US);
-            this.state = normal;
-            i++;
-            break;
+        self.send('\x1b[M' + String.fromCharCode.apply(String, data));
+      }
 
-          // ESC (,),*,+,-,. Designate G0-G2 Character Set.
-          case '(': // <-- this seems to get all the attention
-          case ')':
-          case '*':
-          case '+':
-          case '-':
-          case '.':
-            switch (ch) {
-              case '(':
-                this.gcharset = 0;
-                break;
-              case ')':
-                this.gcharset = 1;
-                break;
-              case '*':
-                this.gcharset = 2;
-                break;
-              case '+':
-                this.gcharset = 3;
-                break;
-              case '-':
-                this.gcharset = 1;
-                break;
-              case '.':
-                this.gcharset = 2;
-                break;
+      function getButton(ev) {
+        var button
+          , shift
+          , meta
+          , ctrl
+          , mod;
+
+        // two low bits:
+        // 0 = left
+        // 1 = middle
+        // 2 = right
+        // 3 = release
+        // wheel up/down:
+        // 1, and 2 - with 64 added
+        switch (ev.type) {
+          case 'mousedown':
+            button = ev.button != null
+              ? +ev.button
+              : ev.which != null
+                ? ev.which - 1
+                : null;
+
+            if (self.isMSIE) {
+              button = button === 1 ? 0 : button === 4 ? 1 : button;
             }
-            this.state = charset;
-            break;
-
-          // Designate G3 Character Set (VT300).
-          // A = ISO Latin-1 Supplemental.
-          // Not implemented.
-          case '/':
-            this.gcharset = 3;
-            this.state = charset;
-            i--;
-            break;
-
-          // ESC N
-          // Single Shift Select of G2 Character Set
-          // ( SS2 is 0x8e). This affects next character only.
-          case 'N':
-            break;
-          // ESC O
-          // Single Shift Select of G3 Character Set
-          // ( SS3 is 0x8f). This affects next character only.
-          case 'O':
-            break;
-          // ESC n
-          // Invoke the G2 Character Set as GL (LS2).
-          case 'n':
-            this.setgLevel(2);
-            break;
-          // ESC o
-          // Invoke the G3 Character Set as GL (LS3).
-          case 'o':
-            this.setgLevel(3);
             break;
-          // ESC |
-          // Invoke the G3 Character Set as GR (LS3R).
-          case '|':
-            this.setgLevel(3);
+          case 'mouseup':
+            button = 3;
             break;
-          // ESC }
-          // Invoke the G2 Character Set as GR (LS2R).
-          case '}':
-            this.setgLevel(2);
+          case 'DOMMouseScroll':
+            button = ev.detail < 0
+              ? 64
+              : 65;
             break;
-          // ESC ~
-          // Invoke the G1 Character Set as GR (LS1R).
-          case '~':
-            this.setgLevel(1);
+          case 'mousewheel':
+            button = ev.wheelDeltaY > 0
+              ? 64
+              : 65;
             break;
+        }
 
-          // ESC 7 Save Cursor (DECSC).
-          case '7':
-            this.saveCursor();
-            this.state = normal;
-            break;
+        // next three bits are the modifiers:
+        // 4 = shift, 8 = meta, 16 = control
+        shift = ev.shiftKey ? 4 : 0;
+        meta = ev.metaKey ? 8 : 0;
+        ctrl = ev.ctrlKey ? 16 : 0;
+        mod = shift | meta | ctrl;
+
+        // no mods
+        if (self.vt200Mouse) {
+          // ctrl only
+          mod &= ctrl;
+        } else if (!self.normalMouse) {
+          mod = 0;
+        }
 
-          // ESC 8 Restore Cursor (DECRC).
-          case '8':
-            this.restoreCursor();
-            this.state = normal;
-            break;
+        // increment to SP
+        button = (32 + (mod << 2)) + button;
 
-          // ESC # 3 DEC line height/width
-          case '#':
-            this.state = normal;
-            i++;
-            break;
+        return button;
+      }
 
-          // ESC H Tab Set (HTS is 0x88).
-          case 'H':
-            this.tabSet();
-            break;
+      // mouse coordinates measured in cols/rows
+      function getCoords(ev) {
+        var x, y, w, h, el;
+
+        // ignore browsers without pageX for now
+        if (ev.pageX == null) return;
+
+        x = ev.pageX;
+        y = ev.pageY;
+        el = self.element;
+
+        // should probably check offsetParent
+        // but this is more portable
+        while (el && el !== self.document.documentElement) {
+          x -= el.offsetLeft;
+          y -= el.offsetTop;
+          el = 'offsetParent' in el
+            ? el.offsetParent
+            : el.parentNode;
+        }
 
-          // ESC = Application Keypad (DECPAM).
-          case '=':
-            this.log('Serial port requested application keypad.');
-            this.applicationKeypad = true;
-            this.state = normal;
-            break;
+        // convert to cols/rows
+        w = self.element.clientWidth;
+        h = self.element.clientHeight;
+        x = Math.round((x / w) * self.cols);
+        y = Math.round((y / h) * self.rows);
+
+        // be sure to avoid sending
+        // bad positions to the program
+        if (x < 0) x = 0;
+        if (x > self.cols) x = self.cols;
+        if (y < 0) y = 0;
+        if (y > self.rows) y = self.rows;
+
+        // xterm sends raw bytes and
+        // starts at 32 (SP) for each.
+        x += 32;
+        y += 32;
+
+        return {
+          x: x,
+          y: y,
+          type: ev.type === wheelEvent
+            ? 'mousewheel'
+            : ev.type
+        };
+      }
 
-          // ESC > Normal Keypad (DECPNM).
-          case '>':
-            this.log('Switching back to normal keypad.');
-            this.applicationKeypad = false;
-            this.state = normal;
-            break;
+      on(el, 'mousedown', function(ev) {
+        if (!self.mouseEvents) return;
 
-          default:
-            this.state = normal;
-            this.error('Unknown ESC control: %s.', ch);
-            break;
+        // send the button
+        sendButton(ev);
+
+        // ensure focus
+        self.focus();
+
+        // fix for odd bug
+        //if (self.vt200Mouse && !self.normalMouse) {
+        if (self.vt200Mouse) {
+          sendButton({ __proto__: ev, type: 'mouseup' });
+          return self.cancel(ev);
         }
-        break;
 
-      case charset:
-        switch (ch) {
-          case '0': // DEC Special Character and Line Drawing Set.
-            cs = Terminal.charsets.SCLD;
-            break;
-          case 'A': // UK
-            cs = Terminal.charsets.UK;
-            break;
-          case 'B': // United States (USASCII).
-            cs = Terminal.charsets.US;
-            break;
-          case '4': // Dutch
-            cs = Terminal.charsets.Dutch;
-            break;
-          case 'C': // Finnish
-          case '5':
-            cs = Terminal.charsets.Finnish;
-            break;
-          case 'R': // French
-            cs = Terminal.charsets.French;
-            break;
-          case 'Q': // FrenchCanadian
-            cs = Terminal.charsets.FrenchCanadian;
-            break;
-          case 'K': // German
-            cs = Terminal.charsets.German;
-            break;
-          case 'Y': // Italian
-            cs = Terminal.charsets.Italian;
-            break;
-          case 'E': // NorwegianDanish
-          case '6':
-            cs = Terminal.charsets.NorwegianDanish;
-            break;
-          case 'Z': // Spanish
-            cs = Terminal.charsets.Spanish;
-            break;
-          case 'H': // Swedish
-          case '7':
-            cs = Terminal.charsets.Swedish;
-            break;
-          case '=': // Swiss
-            cs = Terminal.charsets.Swiss;
-            break;
-          case '/': // ISOLatin (actually /A)
-            cs = Terminal.charsets.ISOLatin;
-            i++;
-            break;
-          default: // Default
-            cs = Terminal.charsets.US;
-            break;
+        // bind events
+        if (self.normalMouse) on(self.document, 'mousemove', sendMove);
+
+        // x10 compatibility mode can't send button releases
+        if (!self.x10Mouse) {
+          on(self.document, 'mouseup', function up(ev) {
+            sendButton(ev);
+            if (self.normalMouse) off(self.document, 'mousemove', sendMove);
+            off(self.document, 'mouseup', up);
+            return self.cancel(ev);
+          });
         }
-        this.setgCharset(this.gcharset, cs);
-        this.gcharset = null;
-        this.state = normal;
-        break;
-
-      case osc:
-        // OSC Ps ; Pt ST
-        // OSC Ps ; Pt BEL
-        //   Set Text Parameters.
-        if (ch === '\x1b' || ch === '\x07') {
-          if (ch === '\x1b') i++;
-
-          this.params.push(this.currentParam);
-
-          switch (this.params[0]) {
-            case 0:
-            case 1:
-            case 2:
-              if (this.params[1]) {
-                this.title = this.params[1];
-                this.handleTitle(this.title);
+
+        return self.cancel(ev);
+      });
+
+      //if (self.normalMouse) {
+      //  on(self.document, 'mousemove', sendMove);
+      //}
+
+      on(el, wheelEvent, function(ev) {
+        if (!self.mouseEvents) return;
+        if (self.x10Mouse
+            || self.vt300Mouse
+            || self.decLocator) return;
+        sendButton(ev);
+        return self.cancel(ev);
+      });
+
+      // allow mousewheel scrolling in
+      // the shell for example
+      on(el, wheelEvent, function(ev) {
+        if (self.mouseEvents) return;
+        if (self.applicationKeypad) return;
+        if (ev.type === 'DOMMouseScroll') {
+          self.scrollDisp(ev.detail < 0 ? -1 : 1);
+        } else {
+          self.scrollDisp(ev.wheelDeltaY > 0 ? -1 : 1);
+        }
+        return self.cancel(ev);
+      });
+    };
+
+    /**
+     * Destroy Terminal
+     */
+
+    Terminal.prototype.destroy = function() {
+      this.readable = false;
+      this.writable = false;
+      this._events = {};
+      this.handler = function() {};
+      this.write = function() {};
+      if (this.element.parentNode) {
+        this.element.parentNode.removeChild(this.element);
+      }
+      //this.emit('close');
+    };
+
+
+    /*
+     * Rendering Engine
+     *
+     * In the screen buffer, each character
+     * is stored as a an array with a character
+     * and a 32-bit integer.
+     * First value: a utf-16 character.
+     * Second value:
+     * Next 9 bits: background color (0-511).
+     * Next 9 bits: foreground color (0-511).
+     * Next 14 bits: a mask for misc. flags:
+     *   1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible
+    */
+    Terminal.prototype.refresh = function(start, end) {
+      var x, y, i, line, out, ch, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
+
+      if (end - start >= this.rows / 2) {
+        parent = this.element.parentNode;
+        if (parent) parent.removeChild(this.element);
+      }
+
+      width = this.cols;
+      y = start;
+
+      if (end >= this.lines.length) {
+        this.log('`end` is too large. Most likely a bad CSR.');
+        end = this.lines.length - 1;
+      }
+
+      for (; y <= end; y++) {
+        row = y + this.ydisp;
+
+        line = this.lines[row];
+        out = '';
+
+        if (y === this.y
+            && this.cursorState
+            && (this.ydisp === this.ybase)
+            && !this.cursorHidden) {
+          x = this.x;
+        } else {
+          x = -1;
+        }
+
+        attr = this.defAttr;
+        i = 0;
+
+        for (; i < width; i++) {
+          data = line[i][0];
+          ch = line[i][1];
+
+          if (i === x) data = -1;
+
+          if (data !== attr) {
+            if (attr !== this.defAttr) {
+              out += '</span>';
+            }
+            if (data !== this.defAttr) {
+              if (data === -1) {
+                out += '<span class="reverse-video terminal-cursor">';
+              } else {
+                out += '<span class="';
+
+                bg = data & 0x1ff;
+                fg = (data >> 9) & 0x1ff;
+                flags = data >> 18;
+
+                // bold
+                if (flags & 1) {
+                  if (!Terminal.brokenBold) {
+                    out += ' xterm-bold ';
+                  }
+                  // See: XTerm*boldColors
+                  if (fg < 8) fg += 8;
+                }
+
+                // underline
+                if (flags & 2) {
+                  out += ' xterm-underline ';
+                }
+
+                // blink
+                if (flags & 4) {
+                    out += ' xterm-blink ';
+                }
+
+                // inverse
+                if (flags & 8) {
+                  bg = (data >> 9) & 0x1ff;
+                  fg = data & 0x1ff;
+                  // Should inverse just be before the
+                  // above boldColors effect instead?
+                  if ((flags & 1) && fg < 8) fg += 8;
+                }
+
+                // invisible
+                if (flags & 16) {
+                  out += ' xterm-hidden ';
+                }
+
+                if (bg !== 256) {
+                  out += ' xterm-bg-color-' + bg + ' ';
+                }
+
+                if (fg !== 257) {
+                  out += ' xterm-color-' + fg + ' ';
+                }
+
+                out += '">';
               }
+            }
+          }
+
+          switch (ch) {
+            case '&':
+              out += '&amp;';
               break;
-            case 3:
-              // set X property
-              break;
-            case 4:
-            case 5:
-              // change dynamic colors
-              break;
-            case 10:
-            case 11:
-            case 12:
-            case 13:
-            case 14:
-            case 15:
-            case 16:
-            case 17:
-            case 18:
-            case 19:
-              // change dynamic ui colors
-              break;
-            case 46:
-              // change log file
-              break;
-            case 50:
-              // dynamic font
-              break;
-            case 51:
-              // emacs shell
+            case '<':
+              out += '&lt;';
               break;
-            case 52:
-              // manipulate selection data
+            case '>':
+              out += '&gt;';
               break;
-            case 104:
-            case 105:
-            case 110:
-            case 111:
-            case 112:
-            case 113:
-            case 114:
-            case 115:
-            case 116:
-            case 117:
-            case 118:
-              // reset colors
+            default:
+              if (ch <= ' ') {
+                out += '&nbsp;';
+              } else {
+                if (isWide(ch)) i++;
+                out += ch;
+              }
               break;
           }
 
-          this.params = [];
-          this.currentParam = 0;
-          this.state = normal;
-        } else {
-          if (!this.params.length) {
-            if (ch >= '0' && ch <= '9') {
-              this.currentParam =
-                this.currentParam * 10 + ch.charCodeAt(0) - 48;
-            } else if (ch === ';') {
-              this.params.push(this.currentParam);
-              this.currentParam = '';
-            }
-          } else {
-            this.currentParam += ch;
-          }
+          attr = data;
         }
-        break;
 
-      case csi:
-        // '?', '>', '!'
-        if (ch === '?' || ch === '>' || ch === '!') {
-          this.prefix = ch;
-          break;
+        if (attr !== this.defAttr) {
+          out += '</span>';
         }
 
-        // 0 - 9
-        if (ch >= '0' && ch <= '9') {
-          this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48;
-          break;
-        }
+        this.children[y].innerHTML = out;
+      }
 
-        // '$', '"', ' ', '\''
-        if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') {
-          this.postfix = ch;
-          break;
+      if (parent) {
+        parent.appendChild(this.element);
+      }
+
+      /*
+      *  Return focus to previously focused element
+      */
+      focused.focus();
+      this.emit('refresh', {element: this.element, start: start, end: end});
+    };
+
+    Terminal.prototype._cursorBlink = function() {
+      if (Terminal.focus !== this) return;
+      this.cursorState ^= 1;
+      this.refresh(this.y, this.y);
+    };
+
+    Terminal.prototype.showCursor = function() {
+      if (!this.cursorState) {
+        this.cursorState = 1;
+        this.refresh(this.y, this.y);
+      } else {
+        // Temporarily disabled:
+        // this.refreshBlink();
+      }
+    };
+
+    Terminal.prototype.startBlink = function() {
+      if (!this.cursorBlink) return;
+      var self = this;
+      this._blinker = function() {
+        self._cursorBlink();
+      };
+      this._blink = setInterval(this._blinker, 500);
+    };
+
+    Terminal.prototype.refreshBlink = function() {
+      if (!this.cursorBlink) return;
+      clearInterval(this._blink);
+      this._blink = setInterval(this._blinker, 500);
+    };
+
+    Terminal.prototype.scroll = function() {
+      var row;
+
+      if (++this.ybase === this.scrollback) {
+        this.ybase = this.ybase / 2 | 0;
+        this.lines = this.lines.slice(-(this.ybase + this.rows) + 1);
+      }
+
+      this.ydisp = this.ybase;
+
+      // last line
+      row = this.ybase + this.rows - 1;
+
+      // subtract the bottom scroll region
+      row -= this.rows - 1 - this.scrollBottom;
+
+      if (row === this.lines.length) {
+        // potential optimization:
+        // pushing is faster than splicing
+        // when they amount to the same
+        // behavior.
+        this.lines.push(this.blankLine());
+      } else {
+        // add our new line
+        this.lines.splice(row, 0, this.blankLine());
+      }
+
+      if (this.scrollTop !== 0) {
+        if (this.ybase !== 0) {
+          this.ybase--;
+          this.ydisp = this.ybase;
         }
+        this.lines.splice(this.ybase + this.scrollTop, 1);
+      }
 
-        this.params.push(this.currentParam);
-        this.currentParam = 0;
+      // this.maxRange();
+      this.updateRange(this.scrollTop);
+      this.updateRange(this.scrollBottom);
+    };
 
-        // ';'
-        if (ch === ';') break;
+    Terminal.prototype.scrollDisp = function(disp) {
+      this.ydisp += disp;
 
-        this.state = normal;
+      if (this.ydisp > this.ybase) {
+        this.ydisp = this.ybase;
+      } else if (this.ydisp < 0) {
+        this.ydisp = 0;
+      }
 
-        switch (ch) {
-          // CSI Ps A
-          // Cursor Up Ps Times (default = 1) (CUU).
-          case 'A':
-            this.cursorUp(this.params);
-            break;
+      this.refresh(0, this.rows - 1);
+    };
 
-          // CSI Ps B
-          // Cursor Down Ps Times (default = 1) (CUD).
-          case 'B':
-            this.cursorDown(this.params);
-            break;
+    Terminal.prototype.write = function(data) {
+      var l = data.length, i = 0, j, cs, ch;
 
-          // CSI Ps C
-          // Cursor Forward Ps Times (default = 1) (CUF).
-          case 'C':
-            this.cursorForward(this.params);
-            break;
+      this.refreshStart = this.y;
+      this.refreshEnd = this.y;
 
-          // CSI Ps D
-          // Cursor Backward Ps Times (default = 1) (CUB).
-          case 'D':
-            this.cursorBackward(this.params);
-            break;
+      if (this.ybase !== this.ydisp) {
+        this.ydisp = this.ybase;
+        this.maxRange();
+      }
 
-          // CSI Ps ; Ps H
-          // Cursor Position [row;column] (default = [1,1]) (CUP).
-          case 'H':
-            this.cursorPos(this.params);
-            break;
+      for (; i < l; i++) {
+        ch = data[i];
+        switch (this.state) {
+          case normal:
+            switch (ch) {
+              case '\x07':
+                this.bell();
+                break;
 
-          // CSI Ps J  Erase in Display (ED).
-          case 'J':
-            this.eraseInDisplay(this.params);
-            break;
+              // '\n', '\v', '\f'
+              case '\n':
+              case '\x0b':
+              case '\x0c':
+                if (this.convertEol) {
+                  this.x = 0;
+                }
+                this.y++;
+                if (this.y > this.scrollBottom) {
+                  this.y--;
+                  this.scroll();
+                }
+                break;
 
-          // CSI Ps K  Erase in Line (EL).
-          case 'K':
-            this.eraseInLine(this.params);
-            break;
+              // '\r'
+              case '\r':
+                this.x = 0;
+                break;
 
-          // CSI Pm m  Character Attributes (SGR).
-          case 'm':
-            if (!this.prefix) {
-              this.charAttributes(this.params);
-            }
-            break;
+              // '\b'
+              case '\x08':
+                if (this.x > 0) {
+                  this.x--;
+                }
+                break;
+
+              // '\t'
+              case '\t':
+                this.x = this.nextStop();
+                break;
 
-          // CSI Ps n  Device Status Report (DSR).
-          case 'n':
-            if (!this.prefix) {
-              this.deviceStatus(this.params);
+              // shift out
+              case '\x0e':
+                this.setgLevel(1);
+                break;
+
+              // shift in
+              case '\x0f':
+                this.setgLevel(0);
+                break;
+
+              // '\e'
+              case '\x1b':
+                this.state = escaped;
+                break;
+
+              default:
+                // ' '
+                if (ch >= ' ') {
+                  if (this.charset && this.charset[ch]) {
+                    ch = this.charset[ch];
+                  }
+
+                  if (this.x >= this.cols) {
+                    this.x = 0;
+                    this.y++;
+                    if (this.y > this.scrollBottom) {
+                      this.y--;
+                      this.scroll();
+                    }
+                  }
+
+                  this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch];
+                  this.x++;
+                  this.updateRange(this.y);
+
+                  if (isWide(ch)) {
+                    j = this.y + this.ybase;
+                    if (this.cols < 2 || this.x >= this.cols) {
+                      this.lines[j][this.x - 1] = [this.curAttr, ' '];
+                      break;
+                    }
+                    this.lines[j][this.x] = [this.curAttr, ' '];
+                    this.x++;
+                  }
+                }
+                break;
             }
             break;
+          case escaped:
+            switch (ch) {
+              // ESC [ Control Sequence Introducer ( CSI is 0x9b).
+              case '[':
+                this.params = [];
+                this.currentParam = 0;
+                this.state = csi;
+                break;
 
-          /**
-           * Additions
-           */
+              // ESC ] Operating System Command ( OSC is 0x9d).
+              case ']':
+                this.params = [];
+                this.currentParam = 0;
+                this.state = osc;
+                break;
 
-          // CSI Ps @
-          // Insert Ps (Blank) Character(s) (default = 1) (ICH).
-          case '@':
-            this.insertChars(this.params);
-            break;
+              // ESC P Device Control String ( DCS is 0x90).
+              case 'P':
+                this.params = [];
+                this.currentParam = 0;
+                this.state = dcs;
+                break;
 
-          // CSI Ps E
-          // Cursor Next Line Ps Times (default = 1) (CNL).
-          case 'E':
-            this.cursorNextLine(this.params);
-            break;
+              // ESC _ Application Program Command ( APC is 0x9f).
+              case '_':
+                this.state = ignore;
+                break;
 
-          // CSI Ps F
-          // Cursor Preceding Line Ps Times (default = 1) (CNL).
-          case 'F':
-            this.cursorPrecedingLine(this.params);
-            break;
+              // ESC ^ Privacy Message ( PM is 0x9e).
+              case '^':
+                this.state = ignore;
+                break;
 
-          // CSI Ps G
-          // Cursor Character Absolute  [column] (default = [row,1]) (CHA).
-          case 'G':
-            this.cursorCharAbsolute(this.params);
-            break;
+              // ESC c Full Reset (RIS).
+              case 'c':
+                this.reset();
+                break;
 
-          // CSI Ps L
-          // Insert Ps Line(s) (default = 1) (IL).
-          case 'L':
-            this.insertLines(this.params);
-            break;
+              // ESC E Next Line ( NEL is 0x85).
+              // ESC D Index ( IND is 0x84).
+              case 'E':
+                this.x = 0;
+                ;
+              case 'D':
+                this.index();
+                break;
 
-          // CSI Ps M
-          // Delete Ps Line(s) (default = 1) (DL).
-          case 'M':
-            this.deleteLines(this.params);
-            break;
+              // ESC M Reverse Index ( RI is 0x8d).
+              case 'M':
+                this.reverseIndex();
+                break;
 
-          // CSI Ps P
-          // Delete Ps Character(s) (default = 1) (DCH).
-          case 'P':
-            this.deleteChars(this.params);
-            break;
+              // ESC % Select default/utf-8 character set.
+              // @ = default, G = utf-8
+              case '%':
+                //this.charset = null;
+                this.setgLevel(0);
+                this.setgCharset(0, Terminal.charsets.US);
+                this.state = normal;
+                i++;
+                break;
 
-          // CSI Ps X
-          // Erase Ps Character(s) (default = 1) (ECH).
-          case 'X':
-            this.eraseChars(this.params);
-            break;
+              // ESC (,),*,+,-,. Designate G0-G2 Character Set.
+              case '(': // <-- this seems to get all the attention
+              case ')':
+              case '*':
+              case '+':
+              case '-':
+              case '.':
+                switch (ch) {
+                  case '(':
+                    this.gcharset = 0;
+                    break;
+                  case ')':
+                    this.gcharset = 1;
+                    break;
+                  case '*':
+                    this.gcharset = 2;
+                    break;
+                  case '+':
+                    this.gcharset = 3;
+                    break;
+                  case '-':
+                    this.gcharset = 1;
+                    break;
+                  case '.':
+                    this.gcharset = 2;
+                    break;
+                }
+                this.state = charset;
+                break;
 
-          // CSI Pm `  Character Position Absolute
-          //   [column] (default = [row,1]) (HPA).
-          case '`':
-            this.charPosAbsolute(this.params);
-            break;
+              // Designate G3 Character Set (VT300).
+              // A = ISO Latin-1 Supplemental.
+              // Not implemented.
+              case '/':
+                this.gcharset = 3;
+                this.state = charset;
+                i--;
+                break;
 
-          // 141 61 a * HPR -
-          // Horizontal Position Relative
-          case 'a':
-            this.HPositionRelative(this.params);
-            break;
+              // ESC N
+              // Single Shift Select of G2 Character Set
+              // ( SS2 is 0x8e). This affects next character only.
+              case 'N':
+                break;
+              // ESC O
+              // Single Shift Select of G3 Character Set
+              // ( SS3 is 0x8f). This affects next character only.
+              case 'O':
+                break;
+              // ESC n
+              // Invoke the G2 Character Set as GL (LS2).
+              case 'n':
+                this.setgLevel(2);
+                break;
+              // ESC o
+              // Invoke the G3 Character Set as GL (LS3).
+              case 'o':
+                this.setgLevel(3);
+                break;
+              // ESC |
+              // Invoke the G3 Character Set as GR (LS3R).
+              case '|':
+                this.setgLevel(3);
+                break;
+              // ESC }
+              // Invoke the G2 Character Set as GR (LS2R).
+              case '}':
+                this.setgLevel(2);
+                break;
+              // ESC ~
+              // Invoke the G1 Character Set as GR (LS1R).
+              case '~':
+                this.setgLevel(1);
+                break;
 
-          // CSI P s c
-          // Send Device Attributes (Primary DA).
-          // CSI > P s c
-          // Send Device Attributes (Secondary DA)
-          case 'c':
-            this.sendDeviceAttributes(this.params);
-            break;
+              // ESC 7 Save Cursor (DECSC).
+              case '7':
+                this.saveCursor();
+                this.state = normal;
+                break;
 
-          // CSI Pm d
-          // Line Position Absolute  [row] (default = [1,column]) (VPA).
-          case 'd':
-            this.linePosAbsolute(this.params);
-            break;
+              // ESC 8 Restore Cursor (DECRC).
+              case '8':
+                this.restoreCursor();
+                this.state = normal;
+                break;
 
-          // 145 65 e * VPR - Vertical Position Relative
-          case 'e':
-            this.VPositionRelative(this.params);
-            break;
+              // ESC # 3 DEC line height/width
+              case '#':
+                this.state = normal;
+                i++;
+                break;
 
-          // CSI Ps ; Ps f
-          //   Horizontal and Vertical Position [row;column] (default =
-          //   [1,1]) (HVP).
-          case 'f':
-            this.HVPosition(this.params);
-            break;
+              // ESC H Tab Set (HTS is 0x88).
+              case 'H':
+                this.tabSet();
+                break;
 
-          // CSI Pm h  Set Mode (SM).
-          // CSI ? Pm h - mouse escape codes, cursor escape codes
-          case 'h':
-            this.setMode(this.params);
-            break;
+              // ESC = Application Keypad (DECPAM).
+              case '=':
+                this.log('Serial port requested application keypad.');
+                this.applicationKeypad = true;
+                this.state = normal;
+                break;
 
-          // CSI Pm l  Reset Mode (RM).
-          // CSI ? Pm l
-          case 'l':
-            this.resetMode(this.params);
-            break;
+              // ESC > Normal Keypad (DECPNM).
+              case '>':
+                this.log('Switching back to normal keypad.');
+                this.applicationKeypad = false;
+                this.state = normal;
+                break;
 
-          // CSI Ps ; Ps r
-          //   Set Scrolling Region [top;bottom] (default = full size of win-
-          //   dow) (DECSTBM).
-          // CSI ? Pm r
-          case 'r':
-            this.setScrollRegion(this.params);
+              default:
+                this.state = normal;
+                this.error('Unknown ESC control: %s.', ch);
+                break;
+            }
             break;
 
-          // CSI s
-          //   Save cursor (ANSI.SYS).
-          case 's':
-            this.saveCursor(this.params);
+          case charset:
+            switch (ch) {
+              case '0': // DEC Special Character and Line Drawing Set.
+                cs = Terminal.charsets.SCLD;
+                break;
+              case 'A': // UK
+                cs = Terminal.charsets.UK;
+                break;
+              case 'B': // United States (USASCII).
+                cs = Terminal.charsets.US;
+                break;
+              case '4': // Dutch
+                cs = Terminal.charsets.Dutch;
+                break;
+              case 'C': // Finnish
+              case '5':
+                cs = Terminal.charsets.Finnish;
+                break;
+              case 'R': // French
+                cs = Terminal.charsets.French;
+                break;
+              case 'Q': // FrenchCanadian
+                cs = Terminal.charsets.FrenchCanadian;
+                break;
+              case 'K': // German
+                cs = Terminal.charsets.German;
+                break;
+              case 'Y': // Italian
+                cs = Terminal.charsets.Italian;
+                break;
+              case 'E': // NorwegianDanish
+              case '6':
+                cs = Terminal.charsets.NorwegianDanish;
+                break;
+              case 'Z': // Spanish
+                cs = Terminal.charsets.Spanish;
+                break;
+              case 'H': // Swedish
+              case '7':
+                cs = Terminal.charsets.Swedish;
+                break;
+              case '=': // Swiss
+                cs = Terminal.charsets.Swiss;
+                break;
+              case '/': // ISOLatin (actually /A)
+                cs = Terminal.charsets.ISOLatin;
+                i++;
+                break;
+              default: // Default
+                cs = Terminal.charsets.US;
+                break;
+            }
+            this.setgCharset(this.gcharset, cs);
+            this.gcharset = null;
+            this.state = normal;
             break;
 
-          // CSI u
-          //   Restore cursor (ANSI.SYS).
-          case 'u':
-            this.restoreCursor(this.params);
-            break;
+          case osc:
+            // OSC Ps ; Pt ST
+            // OSC Ps ; Pt BEL
+            //   Set Text Parameters.
+            if (ch === '\x1b' || ch === '\x07') {
+              if (ch === '\x1b') i++;
 
-          /**
-           * Lesser Used
-           */
+              this.params.push(this.currentParam);
 
-          // CSI Ps I
-          // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
-          case 'I':
-            this.cursorForwardTab(this.params);
-            break;
+              switch (this.params[0]) {
+                case 0:
+                case 1:
+                case 2:
+                  if (this.params[1]) {
+                    this.title = this.params[1];
+                    this.handleTitle(this.title);
+                  }
+                  break;
+                case 3:
+                  // set X property
+                  break;
+                case 4:
+                case 5:
+                  // change dynamic colors
+                  break;
+                case 10:
+                case 11:
+                case 12:
+                case 13:
+                case 14:
+                case 15:
+                case 16:
+                case 17:
+                case 18:
+                case 19:
+                  // change dynamic ui colors
+                  break;
+                case 46:
+                  // change log file
+                  break;
+                case 50:
+                  // dynamic font
+                  break;
+                case 51:
+                  // emacs shell
+                  break;
+                case 52:
+                  // manipulate selection data
+                  break;
+                case 104:
+                case 105:
+                case 110:
+                case 111:
+                case 112:
+                case 113:
+                case 114:
+                case 115:
+                case 116:
+                case 117:
+                case 118:
+                  // reset colors
+                  break;
+              }
 
-          // CSI Ps S  Scroll up Ps lines (default = 1) (SU).
-          case 'S':
-            this.scrollUp(this.params);
+              this.params = [];
+              this.currentParam = 0;
+              this.state = normal;
+            } else {
+              if (!this.params.length) {
+                if (ch >= '0' && ch <= '9') {
+                  this.currentParam =
+                    this.currentParam * 10 + ch.charCodeAt(0) - 48;
+                } else if (ch === ';') {
+                  this.params.push(this.currentParam);
+                  this.currentParam = '';
+                }
+              } else {
+                this.currentParam += ch;
+              }
+            }
             break;
 
-          // CSI Ps T  Scroll down Ps lines (default = 1) (SD).
-          // CSI Ps ; Ps ; Ps ; Ps ; Ps T
-          // CSI > Ps; Ps T
-          case 'T':
-            // if (this.prefix === '>') {
-            //   this.resetTitleModes(this.params);
-            //   break;
-            // }
-            // if (this.params.length > 2) {
-            //   this.initMouseTracking(this.params);
-            //   break;
-            // }
-            if (this.params.length < 2 && !this.prefix) {
-              this.scrollDown(this.params);
+          case csi:
+            // '?', '>', '!'
+            if (ch === '?' || ch === '>' || ch === '!') {
+              this.prefix = ch;
+              break;
             }
-            break;
 
-          // CSI Ps Z
-          // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
-          case 'Z':
-            this.cursorBackwardTab(this.params);
-            break;
+            // 0 - 9
+            if (ch >= '0' && ch <= '9') {
+              this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48;
+              break;
+            }
 
-          // CSI Ps b  Repeat the preceding graphic character Ps times (REP).
-          case 'b':
-            this.repeatPrecedingCharacter(this.params);
-            break;
+            // '$', '"', ' ', '\''
+            if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') {
+              this.postfix = ch;
+              break;
+            }
+
+            this.params.push(this.currentParam);
+            this.currentParam = 0;
+
+            // ';'
+            if (ch === ';') break;
+
+            this.state = normal;
+
+            switch (ch) {
+              // CSI Ps A
+              // Cursor Up Ps Times (default = 1) (CUU).
+              case 'A':
+                this.cursorUp(this.params);
+                break;
+
+              // CSI Ps B
+              // Cursor Down Ps Times (default = 1) (CUD).
+              case 'B':
+                this.cursorDown(this.params);
+                break;
+
+              // CSI Ps C
+              // Cursor Forward Ps Times (default = 1) (CUF).
+              case 'C':
+                this.cursorForward(this.params);
+                break;
+
+              // CSI Ps D
+              // Cursor Backward Ps Times (default = 1) (CUB).
+              case 'D':
+                this.cursorBackward(this.params);
+                break;
+
+              // CSI Ps ; Ps H
+              // Cursor Position [row;column] (default = [1,1]) (CUP).
+              case 'H':
+                this.cursorPos(this.params);
+                break;
+
+              // CSI Ps J  Erase in Display (ED).
+              case 'J':
+                this.eraseInDisplay(this.params);
+                break;
+
+              // CSI Ps K  Erase in Line (EL).
+              case 'K':
+                this.eraseInLine(this.params);
+                break;
+
+              // CSI Pm m  Character Attributes (SGR).
+              case 'm':
+                if (!this.prefix) {
+                  this.charAttributes(this.params);
+                }
+                break;
+
+              // CSI Ps n  Device Status Report (DSR).
+              case 'n':
+                if (!this.prefix) {
+                  this.deviceStatus(this.params);
+                }
+                break;
+
+              /**
+               * Additions
+               */
+
+              // CSI Ps @
+              // Insert Ps (Blank) Character(s) (default = 1) (ICH).
+              case '@':
+                this.insertChars(this.params);
+                break;
+
+              // CSI Ps E
+              // Cursor Next Line Ps Times (default = 1) (CNL).
+              case 'E':
+                this.cursorNextLine(this.params);
+                break;
+
+              // CSI Ps F
+              // Cursor Preceding Line Ps Times (default = 1) (CNL).
+              case 'F':
+                this.cursorPrecedingLine(this.params);
+                break;
+
+              // CSI Ps G
+              // Cursor Character Absolute  [column] (default = [row,1]) (CHA).
+              case 'G':
+                this.cursorCharAbsolute(this.params);
+                break;
+
+              // CSI Ps L
+              // Insert Ps Line(s) (default = 1) (IL).
+              case 'L':
+                this.insertLines(this.params);
+                break;
+
+              // CSI Ps M
+              // Delete Ps Line(s) (default = 1) (DL).
+              case 'M':
+                this.deleteLines(this.params);
+                break;
+
+              // CSI Ps P
+              // Delete Ps Character(s) (default = 1) (DCH).
+              case 'P':
+                this.deleteChars(this.params);
+                break;
+
+              // CSI Ps X
+              // Erase Ps Character(s) (default = 1) (ECH).
+              case 'X':
+                this.eraseChars(this.params);
+                break;
+
+              // CSI Pm `  Character Position Absolute
+              //   [column] (default = [row,1]) (HPA).
+              case '`':
+                this.charPosAbsolute(this.params);
+                break;
+
+              // 141 61 a * HPR -
+              // Horizontal Position Relative
+              case 'a':
+                this.HPositionRelative(this.params);
+                break;
+
+              // CSI P s c
+              // Send Device Attributes (Primary DA).
+              // CSI > P s c
+              // Send Device Attributes (Secondary DA)
+              case 'c':
+                this.sendDeviceAttributes(this.params);
+                break;
+
+              // CSI Pm d
+              // Line Position Absolute  [row] (default = [1,column]) (VPA).
+              case 'd':
+                this.linePosAbsolute(this.params);
+                break;
+
+              // 145 65 e * VPR - Vertical Position Relative
+              case 'e':
+                this.VPositionRelative(this.params);
+                break;
+
+              // CSI Ps ; Ps f
+              //   Horizontal and Vertical Position [row;column] (default =
+              //   [1,1]) (HVP).
+              case 'f':
+                this.HVPosition(this.params);
+                break;
+
+              // CSI Pm h  Set Mode (SM).
+              // CSI ? Pm h - mouse escape codes, cursor escape codes
+              case 'h':
+                this.setMode(this.params);
+                break;
+
+              // CSI Pm l  Reset Mode (RM).
+              // CSI ? Pm l
+              case 'l':
+                this.resetMode(this.params);
+                break;
+
+              // CSI Ps ; Ps r
+              //   Set Scrolling Region [top;bottom] (default = full size of win-
+              //   dow) (DECSTBM).
+              // CSI ? Pm r
+              case 'r':
+                this.setScrollRegion(this.params);
+                break;
+
+              // CSI s
+              //   Save cursor (ANSI.SYS).
+              case 's':
+                this.saveCursor(this.params);
+                break;
+
+              // CSI u
+              //   Restore cursor (ANSI.SYS).
+              case 'u':
+                this.restoreCursor(this.params);
+                break;
+
+              /**
+               * Lesser Used
+               */
+
+              // CSI Ps I
+              // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
+              case 'I':
+                this.cursorForwardTab(this.params);
+                break;
+
+              // CSI Ps S  Scroll up Ps lines (default = 1) (SU).
+              case 'S':
+                this.scrollUp(this.params);
+                break;
+
+              // CSI Ps T  Scroll down Ps lines (default = 1) (SD).
+              // CSI Ps ; Ps ; Ps ; Ps ; Ps T
+              // CSI > Ps; Ps T
+              case 'T':
+                // if (this.prefix === '>') {
+                //   this.resetTitleModes(this.params);
+                //   break;
+                // }
+                // if (this.params.length > 2) {
+                //   this.initMouseTracking(this.params);
+                //   break;
+                // }
+                if (this.params.length < 2 && !this.prefix) {
+                  this.scrollDown(this.params);
+                }
+                break;
+
+              // CSI Ps Z
+              // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
+              case 'Z':
+                this.cursorBackwardTab(this.params);
+                break;
+
+              // CSI Ps b  Repeat the preceding graphic character Ps times (REP).
+              case 'b':
+                this.repeatPrecedingCharacter(this.params);
+                break;
+
+              // CSI Ps g  Tab Clear (TBC).
+              case 'g':
+                this.tabClear(this.params);
+                break;
+
+              // CSI Pm i  Media Copy (MC).
+              // CSI ? Pm i
+              // case 'i':
+              //   this.mediaCopy(this.params);
+              //   break;
+
+              // CSI Pm m  Character Attributes (SGR).
+              // CSI > Ps; Ps m
+              // case 'm': // duplicate
+              //   if (this.prefix === '>') {
+              //     this.setResources(this.params);
+              //   } else {
+              //     this.charAttributes(this.params);
+              //   }
+              //   break;
+
+              // CSI Ps n  Device Status Report (DSR).
+              // CSI > Ps n
+              // case 'n': // duplicate
+              //   if (this.prefix === '>') {
+              //     this.disableModifiers(this.params);
+              //   } else {
+              //     this.deviceStatus(this.params);
+              //   }
+              //   break;
+
+              // CSI > Ps p  Set pointer mode.
+              // CSI ! p   Soft terminal reset (DECSTR).
+              // CSI Ps$ p
+              //   Request ANSI mode (DECRQM).
+              // CSI ? Ps$ p
+              //   Request DEC private mode (DECRQM).
+              // CSI Ps ; Ps " p
+              case 'p':
+                switch (this.prefix) {
+                  // case '>':
+                  //   this.setPointerMode(this.params);
+                  //   break;
+                  case '!':
+                    this.softReset(this.params);
+                    break;
+                  // case '?':
+                  //   if (this.postfix === '$') {
+                  //     this.requestPrivateMode(this.params);
+                  //   }
+                  //   break;
+                  // default:
+                  //   if (this.postfix === '"') {
+                  //     this.setConformanceLevel(this.params);
+                  //   } else if (this.postfix === '$') {
+                  //     this.requestAnsiMode(this.params);
+                  //   }
+                  //   break;
+                }
+                break;
+
+              // CSI Ps q  Load LEDs (DECLL).
+              // CSI Ps SP q
+              // CSI Ps " q
+              // case 'q':
+              //   if (this.postfix === ' ') {
+              //     this.setCursorStyle(this.params);
+              //     break;
+              //   }
+              //   if (this.postfix === '"') {
+              //     this.setCharProtectionAttr(this.params);
+              //     break;
+              //   }
+              //   this.loadLEDs(this.params);
+              //   break;
+
+              // CSI Ps ; Ps r
+              //   Set Scrolling Region [top;bottom] (default = full size of win-
+              //   dow) (DECSTBM).
+              // CSI ? Pm r
+              // CSI Pt; Pl; Pb; Pr; Ps$ r
+              // case 'r': // duplicate
+              //   if (this.prefix === '?') {
+              //     this.restorePrivateValues(this.params);
+              //   } else if (this.postfix === '$') {
+              //     this.setAttrInRectangle(this.params);
+              //   } else {
+              //     this.setScrollRegion(this.params);
+              //   }
+              //   break;
+
+              // CSI s     Save cursor (ANSI.SYS).
+              // CSI ? Pm s
+              // case 's': // duplicate
+              //   if (this.prefix === '?') {
+              //     this.savePrivateValues(this.params);
+              //   } else {
+              //     this.saveCursor(this.params);
+              //   }
+              //   break;
+
+              // CSI Ps ; Ps ; Ps t
+              // CSI Pt; Pl; Pb; Pr; Ps$ t
+              // CSI > Ps; Ps t
+              // CSI Ps SP t
+              // case 't':
+              //   if (this.postfix === '$') {
+              //     this.reverseAttrInRectangle(this.params);
+              //   } else if (this.postfix === ' ') {
+              //     this.setWarningBellVolume(this.params);
+              //   } else {
+              //     if (this.prefix === '>') {
+              //       this.setTitleModeFeature(this.params);
+              //     } else {
+              //       this.manipulateWindow(this.params);
+              //     }
+              //   }
+              //   break;
+
+              // CSI u     Restore cursor (ANSI.SYS).
+              // CSI Ps SP u
+              // case 'u': // duplicate
+              //   if (this.postfix === ' ') {
+              //     this.setMarginBellVolume(this.params);
+              //   } else {
+              //     this.restoreCursor(this.params);
+              //   }
+              //   break;
+
+              // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v
+              // case 'v':
+              //   if (this.postfix === '$') {
+              //     this.copyRectagle(this.params);
+              //   }
+              //   break;
+
+              // CSI Pt ; Pl ; Pb ; Pr ' w
+              // case 'w':
+              //   if (this.postfix === '\'') {
+              //     this.enableFilterRectangle(this.params);
+              //   }
+              //   break;
+
+              // CSI Ps x  Request Terminal Parameters (DECREQTPARM).
+              // CSI Ps x  Select Attribute Change Extent (DECSACE).
+              // CSI Pc; Pt; Pl; Pb; Pr$ x
+              // case 'x':
+              //   if (this.postfix === '$') {
+              //     this.fillRectangle(this.params);
+              //   } else {
+              //     this.requestParameters(this.params);
+              //     //this.__(this.params);
+              //   }
+              //   break;
+
+              // CSI Ps ; Pu ' z
+              // CSI Pt; Pl; Pb; Pr$ z
+              // case 'z':
+              //   if (this.postfix === '\'') {
+              //     this.enableLocatorReporting(this.params);
+              //   } else if (this.postfix === '$') {
+              //     this.eraseRectangle(this.params);
+              //   }
+              //   break;
+
+              // CSI Pm ' {
+              // CSI Pt; Pl; Pb; Pr$ {
+              // case '{':
+              //   if (this.postfix === '\'') {
+              //     this.setLocatorEvents(this.params);
+              //   } else if (this.postfix === '$') {
+              //     this.selectiveEraseRectangle(this.params);
+              //   }
+              //   break;
+
+              // CSI Ps ' |
+              // case '|':
+              //   if (this.postfix === '\'') {
+              //     this.requestLocatorPosition(this.params);
+              //   }
+              //   break;
+
+              // CSI P m SP }
+              // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
+              // case '}':
+              //   if (this.postfix === ' ') {
+              //     this.insertColumns(this.params);
+              //   }
+              //   break;
+
+              // CSI P m SP ~
+              // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
+              // case '~':
+              //   if (this.postfix === ' ') {
+              //     this.deleteColumns(this.params);
+              //   }
+              //   break;
+
+              default:
+                this.error('Unknown CSI code: %s.', ch);
+                break;
+            }
+
+            this.prefix = '';
+            this.postfix = '';
+            break;
+
+          case dcs:
+            if (ch === '\x1b' || ch === '\x07') {
+              if (ch === '\x1b') i++;
+
+              switch (this.prefix) {
+                // User-Defined Keys (DECUDK).
+                case '':
+                  break;
+
+                // Request Status String (DECRQSS).
+                // test: echo -e '\eP$q"p\e\\'
+                case '$q':
+                  var pt = this.currentParam
+                    , valid = false;
+
+                  switch (pt) {
+                    // DECSCA
+                    case '"q':
+                      pt = '0"q';
+                      break;
+
+                    // DECSCL
+                    case '"p':
+                      pt = '61"p';
+                      break;
+
+                    // DECSTBM
+                    case 'r':
+                      pt = ''
+                        + (this.scrollTop + 1)
+                        + ';'
+                        + (this.scrollBottom + 1)
+                        + 'r';
+                      break;
+
+                    // SGR
+                    case 'm':
+                      pt = '0m';
+                      break;
+
+                    default:
+                      this.error('Unknown DCS Pt: %s.', pt);
+                      pt = '';
+                      break;
+                  }
+
+                  this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\');
+                  break;
+
+                // Set Termcap/Terminfo Data (xterm, experimental).
+                case '+p':
+                  break;
+
+                // Request Termcap/Terminfo String (xterm, experimental)
+                // Regular xterm does not even respond to this sequence.
+                // This can cause a small glitch in vim.
+                // test: echo -ne '\eP+q6b64\e\\'
+                case '+q':
+                  var pt = this.currentParam
+                    , valid = false;
+
+                  this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\');
+                  break;
+
+                default:
+                  this.error('Unknown DCS prefix: %s.', this.prefix);
+                  break;
+              }
+
+              this.currentParam = 0;
+              this.prefix = '';
+              this.state = normal;
+            } else if (!this.currentParam) {
+              if (!this.prefix && ch !== '$' && ch !== '+') {
+                this.currentParam = ch;
+              } else if (this.prefix.length === 2) {
+                this.currentParam = ch;
+              } else {
+                this.prefix += ch;
+              }
+            } else {
+              this.currentParam += ch;
+            }
+            break;
+
+          case ignore:
+            // For PM and APC.
+            if (ch === '\x1b' || ch === '\x07') {
+              if (ch === '\x1b') i++;
+              this.state = normal;
+            }
+            break;
+        }
+      }
+
+      this.updateRange(this.y);
+      this.refresh(this.refreshStart, this.refreshEnd);
+    };
+
+    Terminal.prototype.writeln = function(data) {
+      this.write(data + '\r\n');
+    };
+
+    // Key Resources:
+    // https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
+    Terminal.prototype.keyDown = function(ev) {
+      var self = this, key;
+
+      switch (ev.keyCode) {
+        // backspace
+        case 8:
+          if (ev.shiftKey) {
+            key = '\x08'; // ^H
+            break;
+          }
+          key = '\x7f'; // ^?
+          break;
+        // tab
+        case 9:
+          if (ev.shiftKey) {
+            key = '\x1b[Z';
+            break;
+          }
+          key = '\t';
+          this.cancel(ev, true);
+          break;
+        // return/enter
+        case 13:
+          key = '\r';
+          this.cancel(ev, true);
+          break;
+        // escape
+        case 27:
+          key = '\x1b';
+          this.cancel(ev, true);
+          break;
+        // left-arrow
+        case 37:
+          if (this.applicationCursor) {
+            key = '\x1bOD'; // SS3 as ^[O for 7-bit
+            //key = '\x8fD'; // SS3 as 0x8f for 8-bit
+            break;
+          }
+          key = '\x1b[D';
+          break;
+        // right-arrow
+        case 39:
+          if (this.applicationCursor) {
+            key = '\x1bOC';
+            break;
+          }
+          key = '\x1b[C';
+          break;
+        // up-arrow
+        case 38:
+          if (this.applicationCursor) {
+            key = '\x1bOA';
+            break;
+          }
+          if (ev.ctrlKey) {
+            this.scrollDisp(-1);
+            return this.cancel(ev);
+          } else {
+            key = '\x1b[A';
+          }
+          break;
+        // down-arrow
+        case 40:
+          if (this.applicationCursor) {
+            key = '\x1bOB';
+            break;
+          }
+          if (ev.ctrlKey) {
+            this.scrollDisp(1);
+            return this.cancel(ev);
+          } else {
+            key = '\x1b[B';
+          }
+          break;
+        // delete
+        case 46:
+          key = '\x1b[3~';
+          break;
+        // insert
+        case 45:
+          key = '\x1b[2~';
+          break;
+        // home
+        case 36:
+          if (this.applicationKeypad) {
+            key = '\x1bOH';
+            break;
+          }
+          key = '\x1bOH';
+          break;
+        // end
+        case 35:
+          if (this.applicationKeypad) {
+            key = '\x1bOF';
+            break;
+          }
+          key = '\x1bOF';
+          break;
+        // page up
+        case 33:
+          if (ev.shiftKey) {
+            this.scrollDisp(-(this.rows - 1));
+            return this.cancel(ev);
+          } else {
+            key = '\x1b[5~';
+          }
+          break;
+        // page down
+        case 34:
+          if (ev.shiftKey) {
+            this.scrollDisp(this.rows - 1);
+            return this.cancel(ev);
+          } else {
+            key = '\x1b[6~';
+          }
+          break;
+        // F1
+        case 112:
+          key = '\x1bOP';
+          break;
+        // F2
+        case 113:
+          key = '\x1bOQ';
+          break;
+        // F3
+        case 114:
+          key = '\x1bOR';
+          break;
+        // F4
+        case 115:
+          key = '\x1bOS';
+          break;
+        // F5
+        case 116:
+          key = '\x1b[15~';
+          break;
+        // F6
+        case 117:
+          key = '\x1b[17~';
+          break;
+        // F7
+        case 118:
+          key = '\x1b[18~';
+          break;
+        // F8
+        case 119:
+          key = '\x1b[19~';
+          break;
+        // F9
+        case 120:
+          key = '\x1b[20~';
+          break;
+        // F10
+        case 121:
+          key = '\x1b[21~';
+          break;
+        // F11
+        case 122:
+          key = '\x1b[23~';
+          break;
+        // F12
+        case 123:
+          key = '\x1b[24~';
+          break;
+        default:
+          // a-z and space
+          if (ev.ctrlKey) {
+            if (ev.keyCode >= 65 && ev.keyCode <= 90) {
+              key = String.fromCharCode(ev.keyCode - 64);
+            } else if (ev.keyCode === 32) {
+              // NUL
+              key = String.fromCharCode(0);
+            } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
+              // escape, file sep, group sep, record sep, unit sep
+              key = String.fromCharCode(ev.keyCode - 51 + 27);
+            } else if (ev.keyCode === 56) {
+              // delete
+              key = String.fromCharCode(127);
+            } else if (ev.keyCode === 219) {
+              // ^[ - escape
+              key = String.fromCharCode(27);
+            } else if (ev.keyCode === 221) {
+              // ^] - group sep
+              key = String.fromCharCode(29);
+            }
+          } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) {
+            if (ev.keyCode >= 65 && ev.keyCode <= 90) {
+              key = '\x1b' + String.fromCharCode(ev.keyCode + 32);
+            } else if (ev.keyCode === 192) {
+              key = '\x1b`';
+            } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {
+              key = '\x1b' + (ev.keyCode - 48);
+            }
+          }
+          break;
+      }
+
+      if (!key || (this.isMac && ev.metaKey)) {
+        return true;
+      }
+
+      this.emit('keydown', ev);
+      this.emit('key', key, ev);
+      this.showCursor();  
+      this.handler(key);
+
+      return this.cancel(ev);
+    };
+
+    Terminal.prototype.setgLevel = function(g) {
+      this.glevel = g;
+      this.charset = this.charsets[g];
+    };
+
+    Terminal.prototype.setgCharset = function(g, charset) {
+      this.charsets[g] = charset;
+      if (this.glevel === g) {
+        this.charset = charset;
+      }
+    };
+
+    Terminal.prototype.keyPress = function(ev) {
+      var key;
+
+      this.cancel(ev);
+
+      if (ev.charCode) {
+        key = ev.charCode;
+      } else if (ev.which == null) {
+        key = ev.keyCode;
+      } else if (ev.which !== 0 && ev.charCode !== 0) {
+        key = ev.which;
+      } else {
+        return false;
+      }
+
+      if (!key || ev.ctrlKey || ev.altKey || ev.metaKey) {
+        return false;
+      }
+
+      key = String.fromCharCode(key);
+
+      this.emit('keypress', key, ev);
+      this.emit('key', key, ev);
+      this.showCursor();
+      this.handler(key);
+
+      return false;
+    };
+
+    Terminal.prototype.send = function(data) {
+      var self = this;
+
+      if (!this.queue) {
+        setTimeout(function() {
+          self.handler(self.queue);
+          self.queue = '';
+        }, 1);
+      }
+
+      this.queue += data;
+    };
+
+    Terminal.prototype.bell = function() {
+      if (!this.visualBell) return;
+      var self = this;
+      this.element.style.borderColor = 'white';
+      setTimeout(function() {
+        self.element.style.borderColor = '';
+      }, 10);
+      if (this.popOnBell) this.focus();
+    };
+
+    Terminal.prototype.log = function() {
+      if (!this.debug) return;
+      if (!this.context.console || !this.context.console.log) return;
+      var args = Array.prototype.slice.call(arguments);
+      this.context.console.log.apply(this.context.console, args);
+    };
+
+    Terminal.prototype.error = function() {
+      if (!this.debug) return;
+      if (!this.context.console || !this.context.console.error) return;
+      var args = Array.prototype.slice.call(arguments);
+      this.context.console.error.apply(this.context.console, args);
+    };
+
+    Terminal.prototype.resize = function(x, y) {
+      var line
+        , el
+        , i
+        , j
+        , ch;
+
+      if (x < 1) x = 1;
+      if (y < 1) y = 1;
+
+      // resize cols
+      j = this.cols;
+      if (j < x) {
+        ch = [this.defAttr, ' ']; // does xterm use the default attr?
+        i = this.lines.length;
+        while (i--) {
+          while (this.lines[i].length < x) {
+            this.lines[i].push(ch);
+          }
+        }
+      } else if (j > x) {
+        i = this.lines.length;
+        while (i--) {
+          while (this.lines[i].length > x) {
+            this.lines[i].pop();
+          }
+        }
+      }
+      this.setupStops(j);
+      this.cols = x;
+
+      // resize rows
+      j = this.rows;
+      if (j < y) {
+        el = this.element;
+        while (j++ < y) {
+          if (this.lines.length < y + this.ybase) {
+            this.lines.push(this.blankLine());
+          }
+          if (this.children.length < y) {
+            this.insertRow();
+          }
+        }
+      } else if (j > y) {
+        while (j-- > y) {
+          if (this.lines.length > y + this.ybase) {
+            this.lines.pop();
+          }
+          if (this.children.length > y) {
+            el = this.children.pop();
+            if (!el) continue;
+            el.parentNode.removeChild(el);
+          }
+        }
+      }
+      this.rows = y;
+
+      /*
+      *  Make sure that the cursor stays on screen
+      */
+      if (this.y >= y) {
+        this.y = y - 1;
+      }
+
+      if (this.x >= x) {
+        this.x = x - 1;
+      }
+
+      this.scrollTop = 0;
+      this.scrollBottom = y - 1;
+
+      this.refresh(0, this.rows - 1);
+
+      this.normal = null;
+
+      this.emit('resize', {terminal: this, cols: x, rows: y});
+    };
+
+    Terminal.prototype.updateRange = function(y) {
+      if (y < this.refreshStart) this.refreshStart = y;
+      if (y > this.refreshEnd) this.refreshEnd = y;
+      // if (y > this.refreshEnd) {
+      //   this.refreshEnd = y;
+      //   if (y > this.rows - 1) {
+      //     this.refreshEnd = this.rows - 1;
+      //   }
+      // }
+    };
+
+    Terminal.prototype.maxRange = function() {
+      this.refreshStart = 0;
+      this.refreshEnd = this.rows - 1;
+    };
+
+    Terminal.prototype.setupStops = function(i) {
+      if (i != null) {
+        if (!this.tabs[i]) {
+          i = this.prevStop(i);
+        }
+      } else {
+        this.tabs = {};
+        i = 0;
+      }
+
+      for (; i < this.cols; i += 8) {
+        this.tabs[i] = true;
+      }
+    };
+
+    Terminal.prototype.prevStop = function(x) {
+      if (x == null) x = this.x;
+      while (!this.tabs[--x] && x > 0);
+      return x >= this.cols
+        ? this.cols - 1
+        : x < 0 ? 0 : x;
+    };
+
+    Terminal.prototype.nextStop = function(x) {
+      if (x == null) x = this.x;
+      while (!this.tabs[++x] && x < this.cols);
+      return x >= this.cols
+        ? this.cols - 1
+        : x < 0 ? 0 : x;
+    };
+
+    Terminal.prototype.eraseRight = function(x, y) {
+      var line = this.lines[this.ybase + y]
+        , ch = [this.eraseAttr(), ' ']; // xterm
+
+
+      for (; x < this.cols; x++) {
+        line[x] = ch;
+      }
+
+      this.updateRange(y);
+    };
+
+    Terminal.prototype.eraseLeft = function(x, y) {
+      var line = this.lines[this.ybase + y]
+        , ch = [this.eraseAttr(), ' ']; // xterm
+
+      x++;
+      while (x--) line[x] = ch;
+
+      this.updateRange(y);
+    };
+
+    Terminal.prototype.eraseLine = function(y) {
+      this.eraseRight(0, y);
+    };
+
+    Terminal.prototype.blankLine = function(cur) {
+      var attr = cur
+        ? this.eraseAttr()
+        : this.defAttr;
+
+      var ch = [attr, ' ']
+        , line = []
+        , i = 0;
+
+      for (; i < this.cols; i++) {
+        line[i] = ch;
+      }
+
+      return line;
+    };
+
+    Terminal.prototype.ch = function(cur) {
+      return cur
+        ? [this.eraseAttr(), ' ']
+        : [this.defAttr, ' '];
+    };
+
+    Terminal.prototype.is = function(term) {
+      var name = this.termName;
+      return (name + '').indexOf(term) === 0;
+    };
+
+    Terminal.prototype.handler = function(data) {
+      this.emit('data', data);
+    };
+
+    Terminal.prototype.handleTitle = function(title) {
+      this.emit('title', title);
+    };
+
+    /**
+     * ESC
+     */
+
+    // ESC D Index (IND is 0x84).
+    Terminal.prototype.index = function() {
+      this.y++;
+      if (this.y > this.scrollBottom) {
+        this.y--;
+        this.scroll();
+      }
+      this.state = normal;
+    };
+
+    // ESC M Reverse Index (RI is 0x8d).
+    Terminal.prototype.reverseIndex = function() {
+      var j;
+      this.y--;
+      if (this.y < this.scrollTop) {
+        this.y++;
+        // possibly move the code below to term.reverseScroll();
+        // test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
+        // blankLine(true) is xterm/linux behavior
+        this.lines.splice(this.y + this.ybase, 0, this.blankLine(true));
+        j = this.rows - 1 - this.scrollBottom;
+        this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1);
+        // this.maxRange();
+        this.updateRange(this.scrollTop);
+        this.updateRange(this.scrollBottom);
+      }
+      this.state = normal;
+    };
+
+    // ESC c Full Reset (RIS).
+    Terminal.prototype.reset = function() {
+      this.options.rows = this.rows;
+      this.options.cols = this.cols;
+      Terminal.call(this, this.options);
+      this.refresh(0, this.rows - 1);
+    };
+
+    // ESC H Tab Set (HTS is 0x88).
+    Terminal.prototype.tabSet = function() {
+      this.tabs[this.x] = true;
+      this.state = normal;
+    };
+
+    /**
+     * CSI
+     */
+
+    // CSI Ps A
+    // Cursor Up Ps Times (default = 1) (CUU).
+    Terminal.prototype.cursorUp = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.y -= param;
+      if (this.y < 0) this.y = 0;
+    };
+
+    // CSI Ps B
+    // Cursor Down Ps Times (default = 1) (CUD).
+    Terminal.prototype.cursorDown = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.y += param;
+      if (this.y >= this.rows) {
+        this.y = this.rows - 1;
+      }
+    };
+
+    // CSI Ps C
+    // Cursor Forward Ps Times (default = 1) (CUF).
+    Terminal.prototype.cursorForward = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.x += param;
+      if (this.x >= this.cols) {
+        this.x = this.cols - 1;
+      }
+    };
+
+    // CSI Ps D
+    // Cursor Backward Ps Times (default = 1) (CUB).
+    Terminal.prototype.cursorBackward = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.x -= param;
+      if (this.x < 0) this.x = 0;
+    };
+
+    // CSI Ps ; Ps H
+    // Cursor Position [row;column] (default = [1,1]) (CUP).
+    Terminal.prototype.cursorPos = function(params) {
+      var row, col;
+
+      row = params[0] - 1;
+
+      if (params.length >= 2) {
+        col = params[1] - 1;
+      } else {
+        col = 0;
+      }
+
+      if (row < 0) {
+        row = 0;
+      } else if (row >= this.rows) {
+        row = this.rows - 1;
+      }
+
+      if (col < 0) {
+        col = 0;
+      } else if (col >= this.cols) {
+        col = this.cols - 1;
+      }
+
+      this.x = col;
+      this.y = row;
+    };
+
+    // CSI Ps J  Erase in Display (ED).
+    //     Ps = 0  -> Erase Below (default).
+    //     Ps = 1  -> Erase Above.
+    //     Ps = 2  -> Erase All.
+    //     Ps = 3  -> Erase Saved Lines (xterm).
+    // CSI ? Ps J
+    //   Erase in Display (DECSED).
+    //     Ps = 0  -> Selective Erase Below (default).
+    //     Ps = 1  -> Selective Erase Above.
+    //     Ps = 2  -> Selective Erase All.
+    Terminal.prototype.eraseInDisplay = function(params) {
+      var j;
+      switch (params[0]) {
+        case 0:
+          this.eraseRight(this.x, this.y);
+          j = this.y + 1;
+          for (; j < this.rows; j++) {
+            this.eraseLine(j);
+          }
+          break;
+        case 1:
+          this.eraseLeft(this.x, this.y);
+          j = this.y;
+          while (j--) {
+            this.eraseLine(j);
+          }
+          break;
+        case 2:
+          j = this.rows;
+          while (j--) this.eraseLine(j);
+          break;
+        case 3:
+          ; // no saved lines
+          break;
+      }
+    };
+
+    // CSI Ps K  Erase in Line (EL).
+    //     Ps = 0  -> Erase to Right (default).
+    //     Ps = 1  -> Erase to Left.
+    //     Ps = 2  -> Erase All.
+    // CSI ? Ps K
+    //   Erase in Line (DECSEL).
+    //     Ps = 0  -> Selective Erase to Right (default).
+    //     Ps = 1  -> Selective Erase to Left.
+    //     Ps = 2  -> Selective Erase All.
+    Terminal.prototype.eraseInLine = function(params) {
+      switch (params[0]) {
+        case 0:
+          this.eraseRight(this.x, this.y);
+          break;
+        case 1:
+          this.eraseLeft(this.x, this.y);
+          break;
+        case 2:
+          this.eraseLine(this.y);
+          break;
+      }
+    };
+
+    // CSI Pm m  Character Attributes (SGR).
+    //     Ps = 0  -> Normal (default).
+    //     Ps = 1  -> Bold.
+    //     Ps = 4  -> Underlined.
+    //     Ps = 5  -> Blink (appears as Bold).
+    //     Ps = 7  -> Inverse.
+    //     Ps = 8  -> Invisible, i.e., hidden (VT300).
+    //     Ps = 2 2  -> Normal (neither bold nor faint).
+    //     Ps = 2 4  -> Not underlined.
+    //     Ps = 2 5  -> Steady (not blinking).
+    //     Ps = 2 7  -> Positive (not inverse).
+    //     Ps = 2 8  -> Visible, i.e., not hidden (VT300).
+    //     Ps = 3 0  -> Set foreground color to Black.
+    //     Ps = 3 1  -> Set foreground color to Red.
+    //     Ps = 3 2  -> Set foreground color to Green.
+    //     Ps = 3 3  -> Set foreground color to Yellow.
+    //     Ps = 3 4  -> Set foreground color to Blue.
+    //     Ps = 3 5  -> Set foreground color to Magenta.
+    //     Ps = 3 6  -> Set foreground color to Cyan.
+    //     Ps = 3 7  -> Set foreground color to White.
+    //     Ps = 3 9  -> Set foreground color to default (original).
+    //     Ps = 4 0  -> Set background color to Black.
+    //     Ps = 4 1  -> Set background color to Red.
+    //     Ps = 4 2  -> Set background color to Green.
+    //     Ps = 4 3  -> Set background color to Yellow.
+    //     Ps = 4 4  -> Set background color to Blue.
+    //     Ps = 4 5  -> Set background color to Magenta.
+    //     Ps = 4 6  -> Set background color to Cyan.
+    //     Ps = 4 7  -> Set background color to White.
+    //     Ps = 4 9  -> Set background color to default (original).
+
+    //   If 16-color support is compiled, the following apply.  Assume
+    //   that xterm's resources are set so that the ISO color codes are
+    //   the first 8 of a set of 16.  Then the aixterm colors are the
+    //   bright versions of the ISO colors:
+    //     Ps = 9 0  -> Set foreground color to Black.
+    //     Ps = 9 1  -> Set foreground color to Red.
+    //     Ps = 9 2  -> Set foreground color to Green.
+    //     Ps = 9 3  -> Set foreground color to Yellow.
+    //     Ps = 9 4  -> Set foreground color to Blue.
+    //     Ps = 9 5  -> Set foreground color to Magenta.
+    //     Ps = 9 6  -> Set foreground color to Cyan.
+    //     Ps = 9 7  -> Set foreground color to White.
+    //     Ps = 1 0 0  -> Set background color to Black.
+    //     Ps = 1 0 1  -> Set background color to Red.
+    //     Ps = 1 0 2  -> Set background color to Green.
+    //     Ps = 1 0 3  -> Set background color to Yellow.
+    //     Ps = 1 0 4  -> Set background color to Blue.
+    //     Ps = 1 0 5  -> Set background color to Magenta.
+    //     Ps = 1 0 6  -> Set background color to Cyan.
+    //     Ps = 1 0 7  -> Set background color to White.
+
+    //   If xterm is compiled with the 16-color support disabled, it
+    //   supports the following, from rxvt:
+    //     Ps = 1 0 0  -> Set foreground and background color to
+    //     default.
+
+    //   If 88- or 256-color support is compiled, the following apply.
+    //     Ps = 3 8  ; 5  ; Ps -> Set foreground color to the second
+    //     Ps.
+    //     Ps = 4 8  ; 5  ; Ps -> Set background color to the second
+    //     Ps.
+    Terminal.prototype.charAttributes = function(params) {
+      // Optimize a single SGR0.
+      if (params.length === 1 && params[0] === 0) {
+        this.curAttr = this.defAttr;
+        return;
+      }
+
+      var l = params.length
+        , i = 0
+        , flags = this.curAttr >> 18
+        , fg = (this.curAttr >> 9) & 0x1ff
+        , bg = this.curAttr & 0x1ff
+        , p;
+
+      for (; i < l; i++) {
+        p = params[i];
+        if (p >= 30 && p <= 37) {
+          // fg color 8
+          fg = p - 30;
+        } else if (p >= 40 && p <= 47) {
+          // bg color 8
+          bg = p - 40;
+        } else if (p >= 90 && p <= 97) {
+          // fg color 16
+          p += 8;
+          fg = p - 90;
+        } else if (p >= 100 && p <= 107) {
+          // bg color 16
+          p += 8;
+          bg = p - 100;
+        } else if (p === 0) {
+          // default
+          flags = this.defAttr >> 18;
+          fg = (this.defAttr >> 9) & 0x1ff;
+          bg = this.defAttr & 0x1ff;
+          // flags = 0;
+          // fg = 0x1ff;
+          // bg = 0x1ff;
+        } else if (p === 1) {
+          // bold text
+          flags |= 1;
+        } else if (p === 4) {
+          // underlined text
+          flags |= 2;
+        } else if (p === 5) {
+          // blink
+          flags |= 4;
+        } else if (p === 7) {
+          // inverse and positive
+          // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
+          flags |= 8;
+        } else if (p === 8) {
+          // invisible
+          flags |= 16;
+        } else if (p === 22) {
+          // not bold
+          flags &= ~1;
+        } else if (p === 24) {
+          // not underlined
+          flags &= ~2;
+        } else if (p === 25) {
+          // not blink
+          flags &= ~4;
+        } else if (p === 27) {
+          // not inverse
+          flags &= ~8;
+        } else if (p === 28) {
+          // not invisible
+          flags &= ~16;
+        } else if (p === 39) {
+          // reset fg
+          fg = (this.defAttr >> 9) & 0x1ff;
+        } else if (p === 49) {
+          // reset bg
+          bg = this.defAttr & 0x1ff;
+        } else if (p === 38) {
+          // fg color 256
+          if (params[i + 1] === 2) {
+            i += 2;
+            fg = matchColor(
+              params[i] & 0xff,
+              params[i + 1] & 0xff,
+              params[i + 2] & 0xff);
+            if (fg === -1) fg = 0x1ff;
+            i += 2;
+          } else if (params[i + 1] === 5) {
+            i += 2;
+            p = params[i] & 0xff;
+            fg = p;
+          }
+        } else if (p === 48) {
+          // bg color 256
+          if (params[i + 1] === 2) {
+            i += 2;
+            bg = matchColor(
+              params[i] & 0xff,
+              params[i + 1] & 0xff,
+              params[i + 2] & 0xff);
+            if (bg === -1) bg = 0x1ff;
+            i += 2;
+          } else if (params[i + 1] === 5) {
+            i += 2;
+            p = params[i] & 0xff;
+            bg = p;
+          }
+        } else if (p === 100) {
+          // reset fg/bg
+          fg = (this.defAttr >> 9) & 0x1ff;
+          bg = this.defAttr & 0x1ff;
+        } else {
+          this.error('Unknown SGR attribute: %d.', p);
+        }
+      }
+
+      this.curAttr = (flags << 18) | (fg << 9) | bg;
+    };
+
+    // CSI Ps n  Device Status Report (DSR).
+    //     Ps = 5  -> Status Report.  Result (``OK'') is
+    //   CSI 0 n
+    //     Ps = 6  -> Report Cursor Position (CPR) [row;column].
+    //   Result is
+    //   CSI r ; c R
+    // CSI ? Ps n
+    //   Device Status Report (DSR, DEC-specific).
+    //     Ps = 6  -> Report Cursor Position (CPR) [row;column] as CSI
+    //     ? r ; c R (assumes page is zero).
+    //     Ps = 1 5  -> Report Printer status as CSI ? 1 0  n  (ready).
+    //     or CSI ? 1 1  n  (not ready).
+    //     Ps = 2 5  -> Report UDK status as CSI ? 2 0  n  (unlocked)
+    //     or CSI ? 2 1  n  (locked).
+    //     Ps = 2 6  -> Report Keyboard status as
+    //   CSI ? 2 7  ;  1  ;  0  ;  0  n  (North American).
+    //   The last two parameters apply to VT400 & up, and denote key-
+    //   board ready and LK01 respectively.
+    //     Ps = 5 3  -> Report Locator status as
+    //   CSI ? 5 3  n  Locator available, if compiled-in, or
+    //   CSI ? 5 0  n  No Locator, if not.
+    Terminal.prototype.deviceStatus = function(params) {
+      if (!this.prefix) {
+        switch (params[0]) {
+          case 5:
+            // status report
+            this.send('\x1b[0n');
+            break;
+          case 6:
+            // cursor position
+            this.send('\x1b['
+              + (this.y + 1)
+              + ';'
+              + (this.x + 1)
+              + 'R');
+            break;
+        }
+      } else if (this.prefix === '?') {
+        // modern xterm doesnt seem to
+        // respond to any of these except ?6, 6, and 5
+        switch (params[0]) {
+          case 6:
+            // cursor position
+            this.send('\x1b[?'
+              + (this.y + 1)
+              + ';'
+              + (this.x + 1)
+              + 'R');
+            break;
+          case 15:
+            // no printer
+            // this.send('\x1b[?11n');
+            break;
+          case 25:
+            // dont support user defined keys
+            // this.send('\x1b[?21n');
+            break;
+          case 26:
+            // north american keyboard
+            // this.send('\x1b[?27;1;0;0n');
+            break;
+          case 53:
+            // no dec locator/mouse
+            // this.send('\x1b[?50n');
+            break;
+        }
+      }
+    };
+
+    /**
+     * Additions
+     */
+
+    // CSI Ps @
+    // Insert Ps (Blank) Character(s) (default = 1) (ICH).
+    Terminal.prototype.insertChars = function(params) {
+      var param, row, j, ch;
+
+      param = params[0];
+      if (param < 1) param = 1;
+
+      row = this.y + this.ybase;
+      j = this.x;
+      ch = [this.eraseAttr(), ' ']; // xterm
+
+      while (param-- && j < this.cols) {
+        this.lines[row].splice(j++, 0, ch);
+        this.lines[row].pop();
+      }
+    };
+
+    // CSI Ps E
+    // Cursor Next Line Ps Times (default = 1) (CNL).
+    // same as CSI Ps B ?
+    Terminal.prototype.cursorNextLine = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.y += param;
+      if (this.y >= this.rows) {
+        this.y = this.rows - 1;
+      }
+      this.x = 0;
+    };
+
+    // CSI Ps F
+    // Cursor Preceding Line Ps Times (default = 1) (CNL).
+    // reuse CSI Ps A ?
+    Terminal.prototype.cursorPrecedingLine = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.y -= param;
+      if (this.y < 0) this.y = 0;
+      this.x = 0;
+    };
+
+    // CSI Ps G
+    // Cursor Character Absolute  [column] (default = [row,1]) (CHA).
+    Terminal.prototype.cursorCharAbsolute = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.x = param - 1;
+    };
+
+    // CSI Ps L
+    // Insert Ps Line(s) (default = 1) (IL).
+    Terminal.prototype.insertLines = function(params) {
+      var param, row, j;
+
+      param = params[0];
+      if (param < 1) param = 1;
+      row = this.y + this.ybase;
+
+      j = this.rows - 1 - this.scrollBottom;
+      j = this.rows - 1 + this.ybase - j + 1;
+
+      while (param--) {
+        // test: echo -e '\e[44m\e[1L\e[0m'
+        // blankLine(true) - xterm/linux behavior
+        this.lines.splice(row, 0, this.blankLine(true));
+        this.lines.splice(j, 1);
+      }
+
+      // this.maxRange();
+      this.updateRange(this.y);
+      this.updateRange(this.scrollBottom);
+    };
+
+    // CSI Ps M
+    // Delete Ps Line(s) (default = 1) (DL).
+    Terminal.prototype.deleteLines = function(params) {
+      var param, row, j;
+
+      param = params[0];
+      if (param < 1) param = 1;
+      row = this.y + this.ybase;
+
+      j = this.rows - 1 - this.scrollBottom;
+      j = this.rows - 1 + this.ybase - j;
+
+      while (param--) {
+        // test: echo -e '\e[44m\e[1M\e[0m'
+        // blankLine(true) - xterm/linux behavior
+        this.lines.splice(j + 1, 0, this.blankLine(true));
+        this.lines.splice(row, 1);
+      }
+
+      // this.maxRange();
+      this.updateRange(this.y);
+      this.updateRange(this.scrollBottom);
+    };
+
+    // CSI Ps P
+    // Delete Ps Character(s) (default = 1) (DCH).
+    Terminal.prototype.deleteChars = function(params) {
+      var param, row, ch;
+
+      param = params[0];
+      if (param < 1) param = 1;
+
+      row = this.y + this.ybase;
+      ch = [this.eraseAttr(), ' ']; // xterm
+
+      while (param--) {
+        this.lines[row].splice(this.x, 1);
+        this.lines[row].push(ch);
+      }
+    };
+
+    // CSI Ps X
+    // Erase Ps Character(s) (default = 1) (ECH).
+    Terminal.prototype.eraseChars = function(params) {
+      var param, row, j, ch;
+
+      param = params[0];
+      if (param < 1) param = 1;
+
+      row = this.y + this.ybase;
+      j = this.x;
+      ch = [this.eraseAttr(), ' ']; // xterm
+
+      while (param-- && j < this.cols) {
+        this.lines[row][j++] = ch;
+      }
+    };
+
+    // CSI Pm `  Character Position Absolute
+    //   [column] (default = [row,1]) (HPA).
+    Terminal.prototype.charPosAbsolute = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.x = param - 1;
+      if (this.x >= this.cols) {
+        this.x = this.cols - 1;
+      }
+    };
+
+    // 141 61 a * HPR -
+    // Horizontal Position Relative
+    // reuse CSI Ps C ?
+    Terminal.prototype.HPositionRelative = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.x += param;
+      if (this.x >= this.cols) {
+        this.x = this.cols - 1;
+      }
+    };
+
+    // CSI Ps c  Send Device Attributes (Primary DA).
+    //     Ps = 0  or omitted -> request attributes from terminal.  The
+    //     response depends on the decTerminalID resource setting.
+    //     -> CSI ? 1 ; 2 c  (``VT100 with Advanced Video Option'')
+    //     -> CSI ? 1 ; 0 c  (``VT101 with No Options'')
+    //     -> CSI ? 6 c  (``VT102'')
+    //     -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c  (``VT220'')
+    //   The VT100-style response parameters do not mean anything by
+    //   themselves.  VT220 parameters do, telling the host what fea-
+    //   tures the terminal supports:
+    //     Ps = 1  -> 132-columns.
+    //     Ps = 2  -> Printer.
+    //     Ps = 6  -> Selective erase.
+    //     Ps = 8  -> User-defined keys.
+    //     Ps = 9  -> National replacement character sets.
+    //     Ps = 1 5  -> Technical characters.
+    //     Ps = 2 2  -> ANSI color, e.g., VT525.
+    //     Ps = 2 9  -> ANSI text locator (i.e., DEC Locator mode).
+    // CSI > Ps c
+    //   Send Device Attributes (Secondary DA).
+    //     Ps = 0  or omitted -> request the terminal's identification
+    //     code.  The response depends on the decTerminalID resource set-
+    //     ting.  It should apply only to VT220 and up, but xterm extends
+    //     this to VT100.
+    //     -> CSI  > Pp ; Pv ; Pc c
+    //   where Pp denotes the terminal type
+    //     Pp = 0  -> ``VT100''.
+    //     Pp = 1  -> ``VT220''.
+    //   and Pv is the firmware version (for xterm, this was originally
+    //   the XFree86 patch number, starting with 95).  In a DEC termi-
+    //   nal, Pc indicates the ROM cartridge registration number and is
+    //   always zero.
+    // More information:
+    //   xterm/charproc.c - line 2012, for more information.
+    //   vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
+    Terminal.prototype.sendDeviceAttributes = function(params) {
+      if (params[0] > 0) return;
+
+      if (!this.prefix) {
+        if (this.is('xterm')
+            || this.is('rxvt-unicode')
+            || this.is('screen')) {
+          this.send('\x1b[?1;2c');
+        } else if (this.is('linux')) {
+          this.send('\x1b[?6c');
+        }
+      } else if (this.prefix === '>') {
+        // xterm and urxvt
+        // seem to spit this
+        // out around ~370 times (?).
+        if (this.is('xterm')) {
+          this.send('\x1b[>0;276;0c');
+        } else if (this.is('rxvt-unicode')) {
+          this.send('\x1b[>85;95;0c');
+        } else if (this.is('linux')) {
+          // not supported by linux console.
+          // linux console echoes parameters.
+          this.send(params[0] + 'c');
+        } else if (this.is('screen')) {
+          this.send('\x1b[>83;40003;0c');
+        }
+      }
+    };
+
+    // CSI Pm d
+    // Line Position Absolute  [row] (default = [1,column]) (VPA).
+    Terminal.prototype.linePosAbsolute = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.y = param - 1;
+      if (this.y >= this.rows) {
+        this.y = this.rows - 1;
+      }
+    };
+
+    // 145 65 e * VPR - Vertical Position Relative
+    // reuse CSI Ps B ?
+    Terminal.prototype.VPositionRelative = function(params) {
+      var param = params[0];
+      if (param < 1) param = 1;
+      this.y += param;
+      if (this.y >= this.rows) {
+        this.y = this.rows - 1;
+      }
+    };
+
+    // CSI Ps ; Ps f
+    //   Horizontal and Vertical Position [row;column] (default =
+    //   [1,1]) (HVP).
+    Terminal.prototype.HVPosition = function(params) {
+      if (params[0] < 1) params[0] = 1;
+      if (params[1] < 1) params[1] = 1;
+
+      this.y = params[0] - 1;
+      if (this.y >= this.rows) {
+        this.y = this.rows - 1;
+      }
+
+      this.x = params[1] - 1;
+      if (this.x >= this.cols) {
+        this.x = this.cols - 1;
+      }
+    };
+
+    // CSI Pm h  Set Mode (SM).
+    //     Ps = 2  -> Keyboard Action Mode (AM).
+    //     Ps = 4  -> Insert Mode (IRM).
+    //     Ps = 1 2  -> Send/receive (SRM).
+    //     Ps = 2 0  -> Automatic Newline (LNM).
+    // CSI ? Pm h
+    //   DEC Private Mode Set (DECSET).
+    //     Ps = 1  -> Application Cursor Keys (DECCKM).
+    //     Ps = 2  -> Designate USASCII for character sets G0-G3
+    //     (DECANM), and set VT100 mode.
+    //     Ps = 3  -> 132 Column Mode (DECCOLM).
+    //     Ps = 4  -> Smooth (Slow) Scroll (DECSCLM).
+    //     Ps = 5  -> Reverse Video (DECSCNM).
+    //     Ps = 6  -> Origin Mode (DECOM).
+    //     Ps = 7  -> Wraparound Mode (DECAWM).
+    //     Ps = 8  -> Auto-repeat Keys (DECARM).
+    //     Ps = 9  -> Send Mouse X & Y on button press.  See the sec-
+    //     tion Mouse Tracking.
+    //     Ps = 1 0  -> Show toolbar (rxvt).
+    //     Ps = 1 2  -> Start Blinking Cursor (att610).
+    //     Ps = 1 8  -> Print form feed (DECPFF).
+    //     Ps = 1 9  -> Set print extent to full screen (DECPEX).
+    //     Ps = 2 5  -> Show Cursor (DECTCEM).
+    //     Ps = 3 0  -> Show scrollbar (rxvt).
+    //     Ps = 3 5  -> Enable font-shifting functions (rxvt).
+    //     Ps = 3 8  -> Enter Tektronix Mode (DECTEK).
+    //     Ps = 4 0  -> Allow 80 -> 132 Mode.
+    //     Ps = 4 1  -> more(1) fix (see curses resource).
+    //     Ps = 4 2  -> Enable Nation Replacement Character sets (DECN-
+    //     RCM).
+    //     Ps = 4 4  -> Turn On Margin Bell.
+    //     Ps = 4 5  -> Reverse-wraparound Mode.
+    //     Ps = 4 6  -> Start Logging.  This is normally disabled by a
+    //     compile-time option.
+    //     Ps = 4 7  -> Use Alternate Screen Buffer.  (This may be dis-
+    //     abled by the titeInhibit resource).
+    //     Ps = 6 6  -> Application keypad (DECNKM).
+    //     Ps = 6 7  -> Backarrow key sends backspace (DECBKM).
+    //     Ps = 1 0 0 0  -> Send Mouse X & Y on button press and
+    //     release.  See the section Mouse Tracking.
+    //     Ps = 1 0 0 1  -> Use Hilite Mouse Tracking.
+    //     Ps = 1 0 0 2  -> Use Cell Motion Mouse Tracking.
+    //     Ps = 1 0 0 3  -> Use All Motion Mouse Tracking.
+    //     Ps = 1 0 0 4  -> Send FocusIn/FocusOut events.
+    //     Ps = 1 0 0 5  -> Enable Extended Mouse Mode.
+    //     Ps = 1 0 1 0  -> Scroll to bottom on tty output (rxvt).
+    //     Ps = 1 0 1 1  -> Scroll to bottom on key press (rxvt).
+    //     Ps = 1 0 3 4  -> Interpret "meta" key, sets eighth bit.
+    //     (enables the eightBitInput resource).
+    //     Ps = 1 0 3 5  -> Enable special modifiers for Alt and Num-
+    //     Lock keys.  (This enables the numLock resource).
+    //     Ps = 1 0 3 6  -> Send ESC   when Meta modifies a key.  (This
+    //     enables the metaSendsEscape resource).
+    //     Ps = 1 0 3 7  -> Send DEL from the editing-keypad Delete
+    //     key.
+    //     Ps = 1 0 3 9  -> Send ESC  when Alt modifies a key.  (This
+    //     enables the altSendsEscape resource).
+    //     Ps = 1 0 4 0  -> Keep selection even if not highlighted.
+    //     (This enables the keepSelection resource).
+    //     Ps = 1 0 4 1  -> Use the CLIPBOARD selection.  (This enables
+    //     the selectToClipboard resource).
+    //     Ps = 1 0 4 2  -> Enable Urgency window manager hint when
+    //     Control-G is received.  (This enables the bellIsUrgent
+    //     resource).
+    //     Ps = 1 0 4 3  -> Enable raising of the window when Control-G
+    //     is received.  (enables the popOnBell resource).
+    //     Ps = 1 0 4 7  -> Use Alternate Screen Buffer.  (This may be
+    //     disabled by the titeInhibit resource).
+    //     Ps = 1 0 4 8  -> Save cursor as in DECSC.  (This may be dis-
+    //     abled by the titeInhibit resource).
+    //     Ps = 1 0 4 9  -> Save cursor as in DECSC and use Alternate
+    //     Screen Buffer, clearing it first.  (This may be disabled by
+    //     the titeInhibit resource).  This combines the effects of the 1
+    //     0 4 7  and 1 0 4 8  modes.  Use this with terminfo-based
+    //     applications rather than the 4 7  mode.
+    //     Ps = 1 0 5 0  -> Set terminfo/termcap function-key mode.
+    //     Ps = 1 0 5 1  -> Set Sun function-key mode.
+    //     Ps = 1 0 5 2  -> Set HP function-key mode.
+    //     Ps = 1 0 5 3  -> Set SCO function-key mode.
+    //     Ps = 1 0 6 0  -> Set legacy keyboard emulation (X11R6).
+    //     Ps = 1 0 6 1  -> Set VT220 keyboard emulation.
+    //     Ps = 2 0 0 4  -> Set bracketed paste mode.
+    // Modes:
+    //   http://vt100.net/docs/vt220-rm/chapter4.html
+    Terminal.prototype.setMode = function(params) {
+      if (typeof params === 'object') {
+        var l = params.length
+          , i = 0;
+
+        for (; i < l; i++) {
+          this.setMode(params[i]);
+        }
+
+        return;
+      }
+
+      if (!this.prefix) {
+        switch (params) {
+          case 4:
+            this.insertMode = true;
+            break;
+          case 20:
+            //this.convertEol = true;
+            break;
+        }
+      } else if (this.prefix === '?') {
+        switch (params) {
+          case 1:
+            this.applicationCursor = true;
+            break;
+          case 2:
+            this.setgCharset(0, Terminal.charsets.US);
+            this.setgCharset(1, Terminal.charsets.US);
+            this.setgCharset(2, Terminal.charsets.US);
+            this.setgCharset(3, Terminal.charsets.US);
+            // set VT100 mode here
+            break;
+          case 3: // 132 col mode
+            this.savedCols = this.cols;
+            this.resize(132, this.rows);
+            break;
+          case 6:
+            this.originMode = true;
+            break;
+          case 7:
+            this.wraparoundMode = true;
+            break;
+          case 12:
+            // this.cursorBlink = true;
+            break;
+          case 66:
+            this.log('Serial port requested application keypad.');
+            this.applicationKeypad = true;
+            break;
+          case 9: // X10 Mouse
+            // no release, no motion, no wheel, no modifiers.
+          case 1000: // vt200 mouse
+            // no motion.
+            // no modifiers, except control on the wheel.
+          case 1002: // button event mouse
+          case 1003: // any event mouse
+            // any event - sends motion events,
+            // even if there is no button held down.
+            this.x10Mouse = params === 9;
+            this.vt200Mouse = params === 1000;
+            this.normalMouse = params > 1000;
+            this.mouseEvents = true;
+            this.element.style.cursor = 'default';
+            this.log('Binding to mouse events.');
+            break;
+          case 1004: // send focusin/focusout events
+            // focusin: ^[[I
+            // focusout: ^[[O
+            this.sendFocus = true;
+            break;
+          case 1005: // utf8 ext mode mouse
+            this.utfMouse = true;
+            // for wide terminals
+            // simply encodes large values as utf8 characters
+            break;
+          case 1006: // sgr ext mode mouse
+            this.sgrMouse = true;
+            // for wide terminals
+            // does not add 32 to fields
+            // press: ^[[<b;x;yM
+            // release: ^[[<b;x;ym
+            break;
+          case 1015: // urxvt ext mode mouse
+            this.urxvtMouse = true;
+            // for wide terminals
+            // numbers for fields
+            // press: ^[[b;x;yM
+            // motion: ^[[b;x;yT
+            break;
+          case 25: // show cursor
+            this.cursorHidden = false;
+            break;
+          case 1049: // alt screen buffer cursor
+            //this.saveCursor();
+            ; // FALL-THROUGH
+          case 47: // alt screen buffer
+          case 1047: // alt screen buffer
+            if (!this.normal) {
+              var normal = {
+                lines: this.lines,
+                ybase: this.ybase,
+                ydisp: this.ydisp,
+                x: this.x,
+                y: this.y,
+                scrollTop: this.scrollTop,
+                scrollBottom: this.scrollBottom,
+                tabs: this.tabs
+                // XXX save charset(s) here?
+                // charset: this.charset,
+                // glevel: this.glevel,
+                // charsets: this.charsets
+              };
+              this.reset();
+              this.normal = normal;
+              this.showCursor();
+            }
+            break;
+        }
+      }
+    };
+
+    // CSI Pm l  Reset Mode (RM).
+    //     Ps = 2  -> Keyboard Action Mode (AM).
+    //     Ps = 4  -> Replace Mode (IRM).
+    //     Ps = 1 2  -> Send/receive (SRM).
+    //     Ps = 2 0  -> Normal Linefeed (LNM).
+    // CSI ? Pm l
+    //   DEC Private Mode Reset (DECRST).
+    //     Ps = 1  -> Normal Cursor Keys (DECCKM).
+    //     Ps = 2  -> Designate VT52 mode (DECANM).
+    //     Ps = 3  -> 80 Column Mode (DECCOLM).
+    //     Ps = 4  -> Jump (Fast) Scroll (DECSCLM).
+    //     Ps = 5  -> Normal Video (DECSCNM).
+    //     Ps = 6  -> Normal Cursor Mode (DECOM).
+    //     Ps = 7  -> No Wraparound Mode (DECAWM).
+    //     Ps = 8  -> No Auto-repeat Keys (DECARM).
+    //     Ps = 9  -> Don't send Mouse X & Y on button press.
+    //     Ps = 1 0  -> Hide toolbar (rxvt).
+    //     Ps = 1 2  -> Stop Blinking Cursor (att610).
+    //     Ps = 1 8  -> Don't print form feed (DECPFF).
+    //     Ps = 1 9  -> Limit print to scrolling region (DECPEX).
+    //     Ps = 2 5  -> Hide Cursor (DECTCEM).
+    //     Ps = 3 0  -> Don't show scrollbar (rxvt).
+    //     Ps = 3 5  -> Disable font-shifting functions (rxvt).
+    //     Ps = 4 0  -> Disallow 80 -> 132 Mode.
+    //     Ps = 4 1  -> No more(1) fix (see curses resource).
+    //     Ps = 4 2  -> Disable Nation Replacement Character sets (DEC-
+    //     NRCM).
+    //     Ps = 4 4  -> Turn Off Margin Bell.
+    //     Ps = 4 5  -> No Reverse-wraparound Mode.
+    //     Ps = 4 6  -> Stop Logging.  (This is normally disabled by a
+    //     compile-time option).
+    //     Ps = 4 7  -> Use Normal Screen Buffer.
+    //     Ps = 6 6  -> Numeric keypad (DECNKM).
+    //     Ps = 6 7  -> Backarrow key sends delete (DECBKM).
+    //     Ps = 1 0 0 0  -> Don't send Mouse X & Y on button press and
+    //     release.  See the section Mouse Tracking.
+    //     Ps = 1 0 0 1  -> Don't use Hilite Mouse Tracking.
+    //     Ps = 1 0 0 2  -> Don't use Cell Motion Mouse Tracking.
+    //     Ps = 1 0 0 3  -> Don't use All Motion Mouse Tracking.
+    //     Ps = 1 0 0 4  -> Don't send FocusIn/FocusOut events.
+    //     Ps = 1 0 0 5  -> Disable Extended Mouse Mode.
+    //     Ps = 1 0 1 0  -> Don't scroll to bottom on tty output
+    //     (rxvt).
+    //     Ps = 1 0 1 1  -> Don't scroll to bottom on key press (rxvt).
+    //     Ps = 1 0 3 4  -> Don't interpret "meta" key.  (This disables
+    //     the eightBitInput resource).
+    //     Ps = 1 0 3 5  -> Disable special modifiers for Alt and Num-
+    //     Lock keys.  (This disables the numLock resource).
+    //     Ps = 1 0 3 6  -> Don't send ESC  when Meta modifies a key.
+    //     (This disables the metaSendsEscape resource).
+    //     Ps = 1 0 3 7  -> Send VT220 Remove from the editing-keypad
+    //     Delete key.
+    //     Ps = 1 0 3 9  -> Don't send ESC  when Alt modifies a key.
+    //     (This disables the altSendsEscape resource).
+    //     Ps = 1 0 4 0  -> Do not keep selection when not highlighted.
+    //     (This disables the keepSelection resource).
+    //     Ps = 1 0 4 1  -> Use the PRIMARY selection.  (This disables
+    //     the selectToClipboard resource).
+    //     Ps = 1 0 4 2  -> Disable Urgency window manager hint when
+    //     Control-G is received.  (This disables the bellIsUrgent
+    //     resource).
+    //     Ps = 1 0 4 3  -> Disable raising of the window when Control-
+    //     G is received.  (This disables the popOnBell resource).
+    //     Ps = 1 0 4 7  -> Use Normal Screen Buffer, clearing screen
+    //     first if in the Alternate Screen.  (This may be disabled by
+    //     the titeInhibit resource).
+    //     Ps = 1 0 4 8  -> Restore cursor as in DECRC.  (This may be
+    //     disabled by the titeInhibit resource).
+    //     Ps = 1 0 4 9  -> Use Normal Screen Buffer and restore cursor
+    //     as in DECRC.  (This may be disabled by the titeInhibit
+    //     resource).  This combines the effects of the 1 0 4 7  and 1 0
+    //     4 8  modes.  Use this with terminfo-based applications rather
+    //     than the 4 7  mode.
+    //     Ps = 1 0 5 0  -> Reset terminfo/termcap function-key mode.
+    //     Ps = 1 0 5 1  -> Reset Sun function-key mode.
+    //     Ps = 1 0 5 2  -> Reset HP function-key mode.
+    //     Ps = 1 0 5 3  -> Reset SCO function-key mode.
+    //     Ps = 1 0 6 0  -> Reset legacy keyboard emulation (X11R6).
+    //     Ps = 1 0 6 1  -> Reset keyboard emulation to Sun/PC style.
+    //     Ps = 2 0 0 4  -> Reset bracketed paste mode.
+    Terminal.prototype.resetMode = function(params) {
+      if (typeof params === 'object') {
+        var l = params.length
+          , i = 0;
+
+        for (; i < l; i++) {
+          this.resetMode(params[i]);
+        }
+
+        return;
+      }
+
+      if (!this.prefix) {
+        switch (params) {
+          case 4:
+            this.insertMode = false;
+            break;
+          case 20:
+            //this.convertEol = false;
+            break;
+        }
+      } else if (this.prefix === '?') {
+        switch (params) {
+          case 1:
+            this.applicationCursor = false;
+            break;
+          case 3:
+            if (this.cols === 132 && this.savedCols) {
+              this.resize(this.savedCols, this.rows);
+            }
+            delete this.savedCols;
+            break;
+          case 6:
+            this.originMode = false;
+            break;
+          case 7:
+            this.wraparoundMode = false;
+            break;
+          case 12:
+            // this.cursorBlink = false;
+            break;
+          case 66:
+            this.log('Switching back to normal keypad.');
+            this.applicationKeypad = false;
+            break;
+          case 9: // X10 Mouse
+          case 1000: // vt200 mouse
+          case 1002: // button event mouse
+          case 1003: // any event mouse
+            this.x10Mouse = false;
+            this.vt200Mouse = false;
+            this.normalMouse = false;
+            this.mouseEvents = false;
+            this.element.style.cursor = '';
+            break;
+          case 1004: // send focusin/focusout events
+            this.sendFocus = false;
+            break;
+          case 1005: // utf8 ext mode mouse
+            this.utfMouse = false;
+            break;
+          case 1006: // sgr ext mode mouse
+            this.sgrMouse = false;
+            break;
+          case 1015: // urxvt ext mode mouse
+            this.urxvtMouse = false;
+            break;
+          case 25: // hide cursor
+            this.cursorHidden = true;
+            break;
+          case 1049: // alt screen buffer cursor
+            ; // FALL-THROUGH
+          case 47: // normal screen buffer
+          case 1047: // normal screen buffer - clearing it first
+            if (this.normal) {
+              this.lines = this.normal.lines;
+              this.ybase = this.normal.ybase;
+              this.ydisp = this.normal.ydisp;
+              this.x = this.normal.x;
+              this.y = this.normal.y;
+              this.scrollTop = this.normal.scrollTop;
+              this.scrollBottom = this.normal.scrollBottom;
+              this.tabs = this.normal.tabs;
+              this.normal = null;
+              // if (params === 1049) {
+              //   this.x = this.savedX;
+              //   this.y = this.savedY;
+              // }
+              this.refresh(0, this.rows - 1);
+              this.showCursor();
+            }
+            break;
+        }
+      }
+    };
+
+    // CSI Ps ; Ps r
+    //   Set Scrolling Region [top;bottom] (default = full size of win-
+    //   dow) (DECSTBM).
+    // CSI ? Pm r
+    Terminal.prototype.setScrollRegion = function(params) {
+      if (this.prefix) return;
+      this.scrollTop = (params[0] || 1) - 1;
+      this.scrollBottom = (params[1] || this.rows) - 1;
+      this.x = 0;
+      this.y = 0;
+    };
+
+    // CSI s
+    //   Save cursor (ANSI.SYS).
+    Terminal.prototype.saveCursor = function(params) {
+      this.savedX = this.x;
+      this.savedY = this.y;
+    };
+
+    // CSI u
+    //   Restore cursor (ANSI.SYS).
+    Terminal.prototype.restoreCursor = function(params) {
+      this.x = this.savedX || 0;
+      this.y = this.savedY || 0;
+    };
+
+    /**
+     * Lesser Used
+     */
+
+    // CSI Ps I
+    //   Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
+    Terminal.prototype.cursorForwardTab = function(params) {
+      var param = params[0] || 1;
+      while (param--) {
+        this.x = this.nextStop();
+      }
+    };
+
+    // CSI Ps S  Scroll up Ps lines (default = 1) (SU).
+    Terminal.prototype.scrollUp = function(params) {
+      var param = params[0] || 1;
+      while (param--) {
+        this.lines.splice(this.ybase + this.scrollTop, 1);
+        this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine());
+      }
+      // this.maxRange();
+      this.updateRange(this.scrollTop);
+      this.updateRange(this.scrollBottom);
+    };
+
+    // CSI Ps T  Scroll down Ps lines (default = 1) (SD).
+    Terminal.prototype.scrollDown = function(params) {
+      var param = params[0] || 1;
+      while (param--) {
+        this.lines.splice(this.ybase + this.scrollBottom, 1);
+        this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine());
+      }
+      // this.maxRange();
+      this.updateRange(this.scrollTop);
+      this.updateRange(this.scrollBottom);
+    };
+
+    // CSI Ps ; Ps ; Ps ; Ps ; Ps T
+    //   Initiate highlight mouse tracking.  Parameters are
+    //   [func;startx;starty;firstrow;lastrow].  See the section Mouse
+    //   Tracking.
+    Terminal.prototype.initMouseTracking = function(params) {
+      // Relevant: DECSET 1001
+    };
+
+    // CSI > Ps; Ps T
+    //   Reset one or more features of the title modes to the default
+    //   value.  Normally, "reset" disables the feature.  It is possi-
+    //   ble to disable the ability to reset features by compiling a
+    //   different default for the title modes into xterm.
+    //     Ps = 0  -> Do not set window/icon labels using hexadecimal.
+    //     Ps = 1  -> Do not query window/icon labels using hexadeci-
+    //     mal.
+    //     Ps = 2  -> Do not set window/icon labels using UTF-8.
+    //     Ps = 3  -> Do not query window/icon labels using UTF-8.
+    //   (See discussion of "Title Modes").
+    Terminal.prototype.resetTitleModes = function(params) {
+      ;
+    };
+
+    // CSI Ps Z  Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
+    Terminal.prototype.cursorBackwardTab = function(params) {
+      var param = params[0] || 1;
+      while (param--) {
+        this.x = this.prevStop();
+      }
+    };
+
+    // CSI Ps b  Repeat the preceding graphic character Ps times (REP).
+    Terminal.prototype.repeatPrecedingCharacter = function(params) {
+      var param = params[0] || 1
+        , line = this.lines[this.ybase + this.y]
+        , ch = line[this.x - 1] || [this.defAttr, ' '];
+
+      while (param--) line[this.x++] = ch;
+    };
+
+    // CSI Ps g  Tab Clear (TBC).
+    //     Ps = 0  -> Clear Current Column (default).
+    //     Ps = 3  -> Clear All.
+    // Potentially:
+    //   Ps = 2  -> Clear Stops on Line.
+    //   http://vt100.net/annarbor/aaa-ug/section6.html
+    Terminal.prototype.tabClear = function(params) {
+      var param = params[0];
+      if (param <= 0) {
+        delete this.tabs[this.x];
+      } else if (param === 3) {
+        this.tabs = {};
+      }
+    };
+
+    // CSI Pm i  Media Copy (MC).
+    //     Ps = 0  -> Print screen (default).
+    //     Ps = 4  -> Turn off printer controller mode.
+    //     Ps = 5  -> Turn on printer controller mode.
+    // CSI ? Pm i
+    //   Media Copy (MC, DEC-specific).
+    //     Ps = 1  -> Print line containing cursor.
+    //     Ps = 4  -> Turn off autoprint mode.
+    //     Ps = 5  -> Turn on autoprint mode.
+    //     Ps = 1  0  -> Print composed display, ignores DECPEX.
+    //     Ps = 1  1  -> Print all pages.
+    Terminal.prototype.mediaCopy = function(params) {
+      ;
+    };
+
+    // CSI > Ps; Ps m
+    //   Set or reset resource-values used by xterm to decide whether
+    //   to construct escape sequences holding information about the
+    //   modifiers pressed with a given key.  The first parameter iden-
+    //   tifies the resource to set/reset.  The second parameter is the
+    //   value to assign to the resource.  If the second parameter is
+    //   omitted, the resource is reset to its initial value.
+    //     Ps = 1  -> modifyCursorKeys.
+    //     Ps = 2  -> modifyFunctionKeys.
+    //     Ps = 4  -> modifyOtherKeys.
+    //   If no parameters are given, all resources are reset to their
+    //   initial values.
+    Terminal.prototype.setResources = function(params) {
+      ;
+    };
+
+    // CSI > Ps n
+    //   Disable modifiers which may be enabled via the CSI > Ps; Ps m
+    //   sequence.  This corresponds to a resource value of "-1", which
+    //   cannot be set with the other sequence.  The parameter identi-
+    //   fies the resource to be disabled:
+    //     Ps = 1  -> modifyCursorKeys.
+    //     Ps = 2  -> modifyFunctionKeys.
+    //     Ps = 4  -> modifyOtherKeys.
+    //   If the parameter is omitted, modifyFunctionKeys is disabled.
+    //   When modifyFunctionKeys is disabled, xterm uses the modifier
+    //   keys to make an extended sequence of functions rather than
+    //   adding a parameter to each function key to denote the modi-
+    //   fiers.
+    Terminal.prototype.disableModifiers = function(params) {
+      ;
+    };
+
+    // CSI > Ps p
+    //   Set resource value pointerMode.  This is used by xterm to
+    //   decide whether to hide the pointer cursor as the user types.
+    //   Valid values for the parameter:
+    //     Ps = 0  -> never hide the pointer.
+    //     Ps = 1  -> hide if the mouse tracking mode is not enabled.
+    //     Ps = 2  -> always hide the pointer.  If no parameter is
+    //     given, xterm uses the default, which is 1 .
+    Terminal.prototype.setPointerMode = function(params) {
+      ;
+    };
+
+    // CSI ! p   Soft terminal reset (DECSTR).
+    // http://vt100.net/docs/vt220-rm/table4-10.html
+    Terminal.prototype.softReset = function(params) {
+      this.cursorHidden = false;
+      this.insertMode = false;
+      this.originMode = false;
+      this.wraparoundMode = false; // autowrap
+      this.applicationKeypad = false; // ?
+      this.applicationCursor = false;
+      this.scrollTop = 0;
+      this.scrollBottom = this.rows - 1;
+      this.curAttr = this.defAttr;
+      this.x = this.y = 0; // ?
+      this.charset = null;
+      this.glevel = 0; // ??
+      this.charsets = [null]; // ??
+    };
+
+    // CSI Ps$ p
+    //   Request ANSI mode (DECRQM).  For VT300 and up, reply is
+    //     CSI Ps; Pm$ y
+    //   where Ps is the mode number as in RM, and Pm is the mode
+    //   value:
+    //     0 - not recognized
+    //     1 - set
+    //     2 - reset
+    //     3 - permanently set
+    //     4 - permanently reset
+    Terminal.prototype.requestAnsiMode = function(params) {
+      ;
+    };
+
+    // CSI ? Ps$ p
+    //   Request DEC private mode (DECRQM).  For VT300 and up, reply is
+    //     CSI ? Ps; Pm$ p
+    //   where Ps is the mode number as in DECSET, Pm is the mode value
+    //   as in the ANSI DECRQM.
+    Terminal.prototype.requestPrivateMode = function(params) {
+      ;
+    };
+
+    // CSI Ps ; Ps " p
+    //   Set conformance level (DECSCL).  Valid values for the first
+    //   parameter:
+    //     Ps = 6 1  -> VT100.
+    //     Ps = 6 2  -> VT200.
+    //     Ps = 6 3  -> VT300.
+    //   Valid values for the second parameter:
+    //     Ps = 0  -> 8-bit controls.
+    //     Ps = 1  -> 7-bit controls (always set for VT100).
+    //     Ps = 2  -> 8-bit controls.
+    Terminal.prototype.setConformanceLevel = function(params) {
+      ;
+    };
+
+    // CSI Ps q  Load LEDs (DECLL).
+    //     Ps = 0  -> Clear all LEDS (default).
+    //     Ps = 1  -> Light Num Lock.
+    //     Ps = 2  -> Light Caps Lock.
+    //     Ps = 3  -> Light Scroll Lock.
+    //     Ps = 2  1  -> Extinguish Num Lock.
+    //     Ps = 2  2  -> Extinguish Caps Lock.
+    //     Ps = 2  3  -> Extinguish Scroll Lock.
+    Terminal.prototype.loadLEDs = function(params) {
+      ;
+    };
+
+    // CSI Ps SP q
+    //   Set cursor style (DECSCUSR, VT520).
+    //     Ps = 0  -> blinking block.
+    //     Ps = 1  -> blinking block (default).
+    //     Ps = 2  -> steady block.
+    //     Ps = 3  -> blinking underline.
+    //     Ps = 4  -> steady underline.
+    Terminal.prototype.setCursorStyle = function(params) {
+      ;
+    };
+
+    // CSI Ps " q
+    //   Select character protection attribute (DECSCA).  Valid values
+    //   for the parameter:
+    //     Ps = 0  -> DECSED and DECSEL can erase (default).
+    //     Ps = 1  -> DECSED and DECSEL cannot erase.
+    //     Ps = 2  -> DECSED and DECSEL can erase.
+    Terminal.prototype.setCharProtectionAttr = function(params) {
+      ;
+    };
+
+    // CSI ? Pm r
+    //   Restore DEC Private Mode Values.  The value of Ps previously
+    //   saved is restored.  Ps values are the same as for DECSET.
+    Terminal.prototype.restorePrivateValues = function(params) {
+      ;
+    };
+
+    // CSI Pt; Pl; Pb; Pr; Ps$ r
+    //   Change Attributes in Rectangular Area (DECCARA), VT400 and up.
+    //     Pt; Pl; Pb; Pr denotes the rectangle.
+    //     Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7.
+    // NOTE: xterm doesn't enable this code by default.
+    Terminal.prototype.setAttrInRectangle = function(params) {
+      var t = params[0]
+        , l = params[1]
+        , b = params[2]
+        , r = params[3]
+        , attr = params[4];
+
+      var line
+        , i;
+
+      for (; t < b + 1; t++) {
+        line = this.lines[this.ybase + t];
+        for (i = l; i < r; i++) {
+          line[i] = [attr, line[i][1]];
+        }
+      }
+
+      // this.maxRange();
+      this.updateRange(params[0]);
+      this.updateRange(params[2]);
+    };
+
+
+    // CSI Pc; Pt; Pl; Pb; Pr$ x
+    //   Fill Rectangular Area (DECFRA), VT420 and up.
+    //     Pc is the character to use.
+    //     Pt; Pl; Pb; Pr denotes the rectangle.
+    // NOTE: xterm doesn't enable this code by default.
+    Terminal.prototype.fillRectangle = function(params) {
+      var ch = params[0]
+        , t = params[1]
+        , l = params[2]
+        , b = params[3]
+        , r = params[4];
+
+      var line
+        , i;
+
+      for (; t < b + 1; t++) {
+        line = this.lines[this.ybase + t];
+        for (i = l; i < r; i++) {
+          line[i] = [line[i][0], String.fromCharCode(ch)];
+        }
+      }
+
+      // this.maxRange();
+      this.updateRange(params[1]);
+      this.updateRange(params[3]);
+    };
+
+    // CSI Ps ; Pu ' z
+    //   Enable Locator Reporting (DECELR).
+    //   Valid values for the first parameter:
+    //     Ps = 0  -> Locator disabled (default).
+    //     Ps = 1  -> Locator enabled.
+    //     Ps = 2  -> Locator enabled for one report, then disabled.
+    //   The second parameter specifies the coordinate unit for locator
+    //   reports.
+    //   Valid values for the second parameter:
+    //     Pu = 0  <- or omitted -> default to character cells.
+    //     Pu = 1  <- device physical pixels.
+    //     Pu = 2  <- character cells.
+    Terminal.prototype.enableLocatorReporting = function(params) {
+      var val = params[0] > 0;
+      //this.mouseEvents = val;
+      //this.decLocator = val;
+    };
+
+    // CSI Pt; Pl; Pb; Pr$ z
+    //   Erase Rectangular Area (DECERA), VT400 and up.
+    //     Pt; Pl; Pb; Pr denotes the rectangle.
+    // NOTE: xterm doesn't enable this code by default.
+    Terminal.prototype.eraseRectangle = function(params) {
+      var t = params[0]
+        , l = params[1]
+        , b = params[2]
+        , r = params[3];
+
+      var line
+        , i
+        , ch;
+
+      ch = [this.eraseAttr(), ' ']; // xterm?
+
+      for (; t < b + 1; t++) {
+        line = this.lines[this.ybase + t];
+        for (i = l; i < r; i++) {
+          line[i] = ch;
+        }
+      }
+
+      // this.maxRange();
+      this.updateRange(params[0]);
+      this.updateRange(params[2]);
+    };
 
-          // CSI Ps g  Tab Clear (TBC).
-          case 'g':
-            this.tabClear(this.params);
-            break;
 
-          // CSI Pm i  Media Copy (MC).
-          // CSI ? Pm i
-          // case 'i':
-          //   this.mediaCopy(this.params);
-          //   break;
-
-          // CSI Pm m  Character Attributes (SGR).
-          // CSI > Ps; Ps m
-          // case 'm': // duplicate
-          //   if (this.prefix === '>') {
-          //     this.setResources(this.params);
-          //   } else {
-          //     this.charAttributes(this.params);
-          //   }
-          //   break;
-
-          // CSI Ps n  Device Status Report (DSR).
-          // CSI > Ps n
-          // case 'n': // duplicate
-          //   if (this.prefix === '>') {
-          //     this.disableModifiers(this.params);
-          //   } else {
-          //     this.deviceStatus(this.params);
-          //   }
-          //   break;
-
-          // CSI > Ps p  Set pointer mode.
-          // CSI ! p   Soft terminal reset (DECSTR).
-          // CSI Ps$ p
-          //   Request ANSI mode (DECRQM).
-          // CSI ? Ps$ p
-          //   Request DEC private mode (DECRQM).
-          // CSI Ps ; Ps " p
-          case 'p':
-            switch (this.prefix) {
-              // case '>':
-              //   this.setPointerMode(this.params);
-              //   break;
-              case '!':
-                this.softReset(this.params);
-                break;
-              // case '?':
-              //   if (this.postfix === '$') {
-              //     this.requestPrivateMode(this.params);
-              //   }
-              //   break;
-              // default:
-              //   if (this.postfix === '"') {
-              //     this.setConformanceLevel(this.params);
-              //   } else if (this.postfix === '$') {
-              //     this.requestAnsiMode(this.params);
-              //   }
-              //   break;
-            }
-            break;
+    // CSI P m SP }
+    // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
+    // NOTE: xterm doesn't enable this code by default.
+    Terminal.prototype.insertColumns = function() {
+      var param = params[0]
+        , l = this.ybase + this.rows
+        , ch = [this.eraseAttr(), ' '] // xterm?
+        , i;
 
-          // CSI Ps q  Load LEDs (DECLL).
-          // CSI Ps SP q
-          // CSI Ps " q
-          // case 'q':
-          //   if (this.postfix === ' ') {
-          //     this.setCursorStyle(this.params);
-          //     break;
-          //   }
-          //   if (this.postfix === '"') {
-          //     this.setCharProtectionAttr(this.params);
-          //     break;
-          //   }
-          //   this.loadLEDs(this.params);
-          //   break;
-
-          // CSI Ps ; Ps r
-          //   Set Scrolling Region [top;bottom] (default = full size of win-
-          //   dow) (DECSTBM).
-          // CSI ? Pm r
-          // CSI Pt; Pl; Pb; Pr; Ps$ r
-          // case 'r': // duplicate
-          //   if (this.prefix === '?') {
-          //     this.restorePrivateValues(this.params);
-          //   } else if (this.postfix === '$') {
-          //     this.setAttrInRectangle(this.params);
-          //   } else {
-          //     this.setScrollRegion(this.params);
-          //   }
-          //   break;
-
-          // CSI s     Save cursor (ANSI.SYS).
-          // CSI ? Pm s
-          // case 's': // duplicate
-          //   if (this.prefix === '?') {
-          //     this.savePrivateValues(this.params);
-          //   } else {
-          //     this.saveCursor(this.params);
-          //   }
-          //   break;
-
-          // CSI Ps ; Ps ; Ps t
-          // CSI Pt; Pl; Pb; Pr; Ps$ t
-          // CSI > Ps; Ps t
-          // CSI Ps SP t
-          // case 't':
-          //   if (this.postfix === '$') {
-          //     this.reverseAttrInRectangle(this.params);
-          //   } else if (this.postfix === ' ') {
-          //     this.setWarningBellVolume(this.params);
-          //   } else {
-          //     if (this.prefix === '>') {
-          //       this.setTitleModeFeature(this.params);
-          //     } else {
-          //       this.manipulateWindow(this.params);
-          //     }
-          //   }
-          //   break;
-
-          // CSI u     Restore cursor (ANSI.SYS).
-          // CSI Ps SP u
-          // case 'u': // duplicate
-          //   if (this.postfix === ' ') {
-          //     this.setMarginBellVolume(this.params);
-          //   } else {
-          //     this.restoreCursor(this.params);
-          //   }
-          //   break;
-
-          // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v
-          // case 'v':
-          //   if (this.postfix === '$') {
-          //     this.copyRectagle(this.params);
-          //   }
-          //   break;
-
-          // CSI Pt ; Pl ; Pb ; Pr ' w
-          // case 'w':
-          //   if (this.postfix === '\'') {
-          //     this.enableFilterRectangle(this.params);
-          //   }
-          //   break;
-
-          // CSI Ps x  Request Terminal Parameters (DECREQTPARM).
-          // CSI Ps x  Select Attribute Change Extent (DECSACE).
-          // CSI Pc; Pt; Pl; Pb; Pr$ x
-          // case 'x':
-          //   if (this.postfix === '$') {
-          //     this.fillRectangle(this.params);
-          //   } else {
-          //     this.requestParameters(this.params);
-          //     //this.__(this.params);
-          //   }
-          //   break;
-
-          // CSI Ps ; Pu ' z
-          // CSI Pt; Pl; Pb; Pr$ z
-          // case 'z':
-          //   if (this.postfix === '\'') {
-          //     this.enableLocatorReporting(this.params);
-          //   } else if (this.postfix === '$') {
-          //     this.eraseRectangle(this.params);
-          //   }
-          //   break;
-
-          // CSI Pm ' {
-          // CSI Pt; Pl; Pb; Pr$ {
-          // case '{':
-          //   if (this.postfix === '\'') {
-          //     this.setLocatorEvents(this.params);
-          //   } else if (this.postfix === '$') {
-          //     this.selectiveEraseRectangle(this.params);
-          //   }
-          //   break;
-
-          // CSI Ps ' |
-          // case '|':
-          //   if (this.postfix === '\'') {
-          //     this.requestLocatorPosition(this.params);
-          //   }
-          //   break;
-
-          // CSI P m SP }
-          // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
-          // case '}':
-          //   if (this.postfix === ' ') {
-          //     this.insertColumns(this.params);
-          //   }
-          //   break;
-
-          // CSI P m SP ~
-          // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
-          // case '~':
-          //   if (this.postfix === ' ') {
-          //     this.deleteColumns(this.params);
-          //   }
-          //   break;
-
-          default:
-            this.error('Unknown CSI code: %s.', ch);
-            break;
+      while (param--) {
+        for (i = this.ybase; i < l; i++) {
+          this.lines[i].splice(this.x + 1, 0, ch);
+          this.lines[i].pop();
         }
+      }
 
-        this.prefix = '';
-        this.postfix = '';
-        break;
+      this.maxRange();
+    };
 
-      case dcs:
-        if (ch === '\x1b' || ch === '\x07') {
-          if (ch === '\x1b') i++;
+    // CSI P m SP ~
+    // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
+    // NOTE: xterm doesn't enable this code by default.
+    Terminal.prototype.deleteColumns = function() {
+      var param = params[0]
+        , l = this.ybase + this.rows
+        , ch = [this.eraseAttr(), ' '] // xterm?
+        , i;
+
+      while (param--) {
+        for (i = this.ybase; i < l; i++) {
+          this.lines[i].splice(this.x, 1);
+          this.lines[i].push(ch);
+        }
+      }
 
-          switch (this.prefix) {
-            // User-Defined Keys (DECUDK).
-            case '':
-              break;
+      this.maxRange();
+    };
 
-            // Request Status String (DECRQSS).
-            // test: echo -e '\eP$q"p\e\\'
-            case '$q':
-              var pt = this.currentParam
-                , valid = false;
 
-              switch (pt) {
-                // DECSCA
-                case '"q':
-                  pt = '0"q';
-                  break;
+    Terminal.prototype.copyBuffer = function(lines) {
+      var lines = lines || this.lines
+        , out = [];
 
-                // DECSCL
-                case '"p':
-                  pt = '61"p';
-                  break;
+      for (var y = 0; y < lines.length; y++) {
+        out[y] = [];
+        for (var x = 0; x < lines[y].length; x++) {
+          out[y][x] = [lines[y][x][0], lines[y][x][1]];
+        }
+      }
 
-                // DECSTBM
-                case 'r':
-                  pt = ''
-                    + (this.scrollTop + 1)
-                    + ';'
-                    + (this.scrollBottom + 1)
-                    + 'r';
-                  break;
+      return out;
+    };
 
-                // SGR
-                case 'm':
-                  pt = '0m';
-                  break;
+    Terminal.prototype.getCopyTextarea = function(text) {
+      var textarea = this._copyTextarea
+        , document = this.document;
+
+      if (!textarea) {
+        textarea = document.createElement('textarea');
+        textarea.style.position = 'absolute';
+        textarea.style.left = '-32000px';
+        textarea.style.top = '-32000px';
+        textarea.style.width = '0px';
+        textarea.style.height = '0px';
+        textarea.style.opacity = '0';
+        textarea.style.backgroundColor = 'transparent';
+        textarea.style.borderStyle = 'none';
+        textarea.style.outlineStyle = 'none';
+
+        document.getElementsByTagName('body')[0].appendChild(textarea);
+
+        this._copyTextarea = textarea;
+      }
 
-                default:
-                  this.error('Unknown DCS Pt: %s.', pt);
-                  pt = '';
-                  break;
-              }
+      return textarea;
+    };
 
-              this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\');
-              break;
+    // NOTE: Only works for primary selection on X11.
+    // Non-X11 users should use Ctrl-C instead.
+    Terminal.prototype.copyText = function(text) {
+      var self = this
+        , textarea = this.getCopyTextarea();
 
-            // Set Termcap/Terminfo Data (xterm, experimental).
-            case '+p':
-              break;
+      this.emit('copy', text);
 
-            // Request Termcap/Terminfo String (xterm, experimental)
-            // Regular xterm does not even respond to this sequence.
-            // This can cause a small glitch in vim.
-            // test: echo -ne '\eP+q6b64\e\\'
-            case '+q':
-              var pt = this.currentParam
-                , valid = false;
+      textarea.focus();
+      textarea.textContent = text;
+      textarea.value = text;
+      textarea.setSelectionRange(0, text.length);
 
-              this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\');
-              break;
+      setTimeout(function() {
+        self.element.focus();
+        self.focus();
+      }, 1);
+    };
 
-            default:
-              this.error('Unknown DCS prefix: %s.', this.prefix);
-              break;
-          }
+    Terminal.prototype.keyPrefix = function(ev, key) {
+      if (key === 'k' || key === '&') {
+        this.destroy();
+      } else if (key === 'p' || key === ']') {
+        this.emit('request paste');
+      } else if (key === 'c') {
+        this.emit('request create');
+      } else if (key >= '0' && key <= '9') {
+        key = +key - 1;
+        if (!~key) key = 9;
+        this.emit('request term', key);
+      } else if (key === 'n') {
+        this.emit('request term next');
+      } else if (key === 'P') {
+        this.emit('request term previous');
+      } else if (key === ':') {
+        this.emit('request command mode');
+      }
+    };
 
-          this.currentParam = 0;
-          this.prefix = '';
-          this.state = normal;
-        } else if (!this.currentParam) {
-          if (!this.prefix && ch !== '$' && ch !== '+') {
-            this.currentParam = ch;
-          } else if (this.prefix.length === 2) {
-            this.currentParam = ch;
-          } else {
-            this.prefix += ch;
-          }
+    Terminal.prototype.keySelect = function(ev, key) {
+      this.showCursor();
+
+      if (key === '\x04') { // ctrl-d
+        var y = this.ydisp + this.y;
+        if (this.ydisp === this.ybase) {
+          // Mimic vim behavior
+          this.y = Math.min(this.y + (this.rows - 1) / 2 | 0, this.rows - 1);
+          this.refresh(0, this.rows - 1);
         } else {
-          this.currentParam += ch;
+          this.scrollDisp((this.rows - 1) / 2 | 0);
         }
-        break;
+        return;
+      }
 
-      case ignore:
-        // For PM and APC.
-        if (ch === '\x1b' || ch === '\x07') {
-          if (ch === '\x1b') i++;
-          this.state = normal;
+      if (key === '\x15') { // ctrl-u
+        var y = this.ydisp + this.y;
+        if (this.ydisp === 0) {
+          // Mimic vim behavior
+          this.y = Math.max(this.y - (this.rows - 1) / 2 | 0, 0);
+          this.refresh(0, this.rows - 1);
+        } else {
+          this.scrollDisp(-(this.rows - 1) / 2 | 0);
         }
-        break;
-    }
-  }
-
-  this.updateRange(this.y);
-  this.refresh(this.refreshStart, this.refreshEnd);
-};
-
-Terminal.prototype.writeln = function(data) {
-  this.write(data + '\r\n');
-};
-
-// Key Resources:
-// https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
-Terminal.prototype.keyDown = function(ev) {
-  var self = this, key;
-
-  switch (ev.keyCode) {
-    // backspace
-    case 8:
-      if (ev.shiftKey) {
-        key = '\x08'; // ^H
-        break;
+        return;
       }
-      key = '\x7f'; // ^?
-      break;
-    // tab
-    case 9:
-      if (ev.shiftKey) {
-        key = '\x1b[Z';
-        break;
-      }
-      key = '\t';
-      this.cancel(ev, true);
-      break;
-    // return/enter
-    case 13:
-      key = '\r';
-      this.cancel(ev, true);
-      break;
-    // escape
-    case 27:
-      key = '\x1b';
-      this.cancel(ev, true);
-      break;
-    // left-arrow
-    case 37:
-      if (this.applicationCursor) {
-        key = '\x1bOD'; // SS3 as ^[O for 7-bit
-        //key = '\x8fD'; // SS3 as 0x8f for 8-bit
-        break;
-      }
-      key = '\x1b[D';
-      break;
-    // right-arrow
-    case 39:
-      if (this.applicationCursor) {
-        key = '\x1bOC';
-        break;
-      }
-      key = '\x1b[C';
-      break;
-    // up-arrow
-    case 38:
-      if (this.applicationCursor) {
-        key = '\x1bOA';
-        break;
-      }
-      if (ev.ctrlKey) {
-        this.scrollDisp(-1);
-        return this.cancel(ev);
-      } else {
-        key = '\x1b[A';
-      }
-      break;
-    // down-arrow
-    case 40:
-      if (this.applicationCursor) {
-        key = '\x1bOB';
-        break;
-      }
-      if (ev.ctrlKey) {
-        this.scrollDisp(1);
-        return this.cancel(ev);
-      } else {
-        key = '\x1b[B';
-      }
-      break;
-    // delete
-    case 46:
-      key = '\x1b[3~';
-      break;
-    // insert
-    case 45:
-      key = '\x1b[2~';
-      break;
-    // home
-    case 36:
-      if (this.applicationKeypad) {
-        key = '\x1bOH';
-        break;
-      }
-      key = '\x1bOH';
-      break;
-    // end
-    case 35:
-      if (this.applicationKeypad) {
-        key = '\x1bOF';
-        break;
+
+      if (key === '\x06') { // ctrl-f
+        var y = this.ydisp + this.y;
+        this.scrollDisp(this.rows - 1);
+        return;
       }
-      key = '\x1bOF';
-      break;
-    // page up
-    case 33:
-      if (ev.shiftKey) {
+
+      if (key === '\x02') { // ctrl-b
+        var y = this.ydisp + this.y;
         this.scrollDisp(-(this.rows - 1));
-        return this.cancel(ev);
-      } else {
-        key = '\x1b[5~';
-      }
-      break;
-    // page down
-    case 34:
-      if (ev.shiftKey) {
-        this.scrollDisp(this.rows - 1);
-        return this.cancel(ev);
-      } else {
-        key = '\x1b[6~';
+        return;
       }
-      break;
-    // F1
-    case 112:
-      key = '\x1bOP';
-      break;
-    // F2
-    case 113:
-      key = '\x1bOQ';
-      break;
-    // F3
-    case 114:
-      key = '\x1bOR';
-      break;
-    // F4
-    case 115:
-      key = '\x1bOS';
-      break;
-    // F5
-    case 116:
-      key = '\x1b[15~';
-      break;
-    // F6
-    case 117:
-      key = '\x1b[17~';
-      break;
-    // F7
-    case 118:
-      key = '\x1b[18~';
-      break;
-    // F8
-    case 119:
-      key = '\x1b[19~';
-      break;
-    // F9
-    case 120:
-      key = '\x1b[20~';
-      break;
-    // F10
-    case 121:
-      key = '\x1b[21~';
-      break;
-    // F11
-    case 122:
-      key = '\x1b[23~';
-      break;
-    // F12
-    case 123:
-      key = '\x1b[24~';
-      break;
-    default:
-      // a-z and space
-      if (ev.ctrlKey) {
-        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
-          key = String.fromCharCode(ev.keyCode - 64);
-        } else if (ev.keyCode === 32) {
-          // NUL
-          key = String.fromCharCode(0);
-        } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
-          // escape, file sep, group sep, record sep, unit sep
-          key = String.fromCharCode(ev.keyCode - 51 + 27);
-        } else if (ev.keyCode === 56) {
-          // delete
-          key = String.fromCharCode(127);
-        } else if (ev.keyCode === 219) {
-          // ^[ - escape
-          key = String.fromCharCode(27);
-        } else if (ev.keyCode === 221) {
-          // ^] - group sep
-          key = String.fromCharCode(29);
-        }
-      } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) {
-        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
-          key = '\x1b' + String.fromCharCode(ev.keyCode + 32);
-        } else if (ev.keyCode === 192) {
-          key = '\x1b`';
-        } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {
-          key = '\x1b' + (ev.keyCode - 48);
+
+      if (key === 'k' || key === '\x1b[A') {
+        var y = this.ydisp + this.y;
+        this.y--;
+        if (this.y < 0) {
+          this.y = 0;
+          this.scrollDisp(-1);
         }
+        this.refresh(this.y, this.y + 1);
+        return;
       }
-      break;
-  }
-
-  if (!key || (this.isMac && ev.metaKey)) {
-    return true;
-  }
-
-  this.emit('keydown', ev);
-  this.emit('key', key, ev);
-  this.showCursor();  
-  this.handler(key);
-
-  return this.cancel(ev);
-};
-
-Terminal.prototype.setgLevel = function(g) {
-  this.glevel = g;
-  this.charset = this.charsets[g];
-};
-
-Terminal.prototype.setgCharset = function(g, charset) {
-  this.charsets[g] = charset;
-  if (this.glevel === g) {
-    this.charset = charset;
-  }
-};
-
-Terminal.prototype.keyPress = function(ev) {
-  var key;
-
-  this.cancel(ev);
-
-  if (ev.charCode) {
-    key = ev.charCode;
-  } else if (ev.which == null) {
-    key = ev.keyCode;
-  } else if (ev.which !== 0 && ev.charCode !== 0) {
-    key = ev.which;
-  } else {
-    return false;
-  }
-
-  if (!key || ev.ctrlKey || ev.altKey || ev.metaKey) {
-    return false;
-  }
-
-  key = String.fromCharCode(key);
-
-  this.emit('keypress', key, ev);
-  this.emit('key', key, ev);
-  this.showCursor();
-  this.handler(key);
-
-  return false;
-};
-
-Terminal.prototype.send = function(data) {
-  var self = this;
-
-  if (!this.queue) {
-    setTimeout(function() {
-      self.handler(self.queue);
-      self.queue = '';
-    }, 1);
-  }
-
-  this.queue += data;
-};
-
-Terminal.prototype.bell = function() {
-  if (!this.visualBell) return;
-  var self = this;
-  this.element.style.borderColor = 'white';
-  setTimeout(function() {
-    self.element.style.borderColor = '';
-  }, 10);
-  if (this.popOnBell) this.focus();
-};
-
-Terminal.prototype.log = function() {
-  if (!this.debug) return;
-  if (!this.context.console || !this.context.console.log) return;
-  var args = Array.prototype.slice.call(arguments);
-  this.context.console.log.apply(this.context.console, args);
-};
-
-Terminal.prototype.error = function() {
-  if (!this.debug) return;
-  if (!this.context.console || !this.context.console.error) return;
-  var args = Array.prototype.slice.call(arguments);
-  this.context.console.error.apply(this.context.console, args);
-};
-
-Terminal.prototype.resize = function(x, y) {
-  var line
-    , el
-    , i
-    , j
-    , ch;
-
-  if (x < 1) x = 1;
-  if (y < 1) y = 1;
-
-  // resize cols
-  j = this.cols;
-  if (j < x) {
-    ch = [this.defAttr, ' ']; // does xterm use the default attr?
-    i = this.lines.length;
-    while (i--) {
-      while (this.lines[i].length < x) {
-        this.lines[i].push(ch);
-      }
-    }
-  } else if (j > x) {
-    i = this.lines.length;
-    while (i--) {
-      while (this.lines[i].length > x) {
-        this.lines[i].pop();
-      }
-    }
-  }
-  this.setupStops(j);
-  this.cols = x;
-
-  // resize rows
-  j = this.rows;
-  if (j < y) {
-    el = this.element;
-    while (j++ < y) {
-      if (this.lines.length < y + this.ybase) {
-        this.lines.push(this.blankLine());
-      }
-      if (this.children.length < y) {
-        this.insertRow();
-      }
-    }
-  } else if (j > y) {
-    while (j-- > y) {
-      if (this.lines.length > y + this.ybase) {
-        this.lines.pop();
+
+      if (key === 'j' || key === '\x1b[B') {
+        var y = this.ydisp + this.y;
+        this.y++;
+        if (this.y >= this.rows) {
+          this.y = this.rows - 1;
+          this.scrollDisp(1);
+        }
+        this.refresh(this.y - 1, this.y);
+        return;
       }
-      if (this.children.length > y) {
-        el = this.children.pop();
-        if (!el) continue;
-        el.parentNode.removeChild(el);
+
+      if (key === 'h' || key === '\x1b[D') {
+        var x = this.x;
+        this.x--;
+        if (this.x < 0) {
+          this.x = 0;
+        }
+        this.refresh(this.y, this.y);
+        return;
       }
-    }
-  }
-  this.rows = y;
-
-  /*
-  *  Make sure that the cursor stays on screen
-  */
-  if (this.y >= y) {
-    this.y = y - 1;
-  }
-  
-  if (this.x >= x) {
-    this.x = x - 1;
-  }
-
-  this.scrollTop = 0;
-  this.scrollBottom = y - 1;
-
-  this.refresh(0, this.rows - 1);
-
-  this.normal = null;
-  
-  this.emit('resize', {terminal: this, cols: x, rows: y});
-};
-
-Terminal.prototype.updateRange = function(y) {
-  if (y < this.refreshStart) this.refreshStart = y;
-  if (y > this.refreshEnd) this.refreshEnd = y;
-  // if (y > this.refreshEnd) {
-  //   this.refreshEnd = y;
-  //   if (y > this.rows - 1) {
-  //     this.refreshEnd = this.rows - 1;
-  //   }
-  // }
-};
-
-Terminal.prototype.maxRange = function() {
-  this.refreshStart = 0;
-  this.refreshEnd = this.rows - 1;
-};
-
-Terminal.prototype.setupStops = function(i) {
-  if (i != null) {
-    if (!this.tabs[i]) {
-      i = this.prevStop(i);
-    }
-  } else {
-    this.tabs = {};
-    i = 0;
-  }
-
-  for (; i < this.cols; i += 8) {
-    this.tabs[i] = true;
-  }
-};
-
-Terminal.prototype.prevStop = function(x) {
-  if (x == null) x = this.x;
-  while (!this.tabs[--x] && x > 0);
-  return x >= this.cols
-    ? this.cols - 1
-    : x < 0 ? 0 : x;
-};
-
-Terminal.prototype.nextStop = function(x) {
-  if (x == null) x = this.x;
-  while (!this.tabs[++x] && x < this.cols);
-  return x >= this.cols
-    ? this.cols - 1
-    : x < 0 ? 0 : x;
-};
-
-Terminal.prototype.eraseRight = function(x, y) {
-  var line = this.lines[this.ybase + y]
-    , ch = [this.eraseAttr(), ' ']; // xterm
-
-
-  for (; x < this.cols; x++) {
-    line[x] = ch;
-  }
-
-  this.updateRange(y);
-};
-
-Terminal.prototype.eraseLeft = function(x, y) {
-  var line = this.lines[this.ybase + y]
-    , ch = [this.eraseAttr(), ' ']; // xterm
-
-  x++;
-  while (x--) line[x] = ch;
-
-  this.updateRange(y);
-};
-
-Terminal.prototype.eraseLine = function(y) {
-  this.eraseRight(0, y);
-};
-
-Terminal.prototype.blankLine = function(cur) {
-  var attr = cur
-    ? this.eraseAttr()
-    : this.defAttr;
-
-  var ch = [attr, ' ']
-    , line = []
-    , i = 0;
-
-  for (; i < this.cols; i++) {
-    line[i] = ch;
-  }
-
-  return line;
-};
-
-Terminal.prototype.ch = function(cur) {
-  return cur
-    ? [this.eraseAttr(), ' ']
-    : [this.defAttr, ' '];
-};
-
-Terminal.prototype.is = function(term) {
-  var name = this.termName;
-  return (name + '').indexOf(term) === 0;
-};
-
-Terminal.prototype.handler = function(data) {
-  this.emit('data', data);
-};
-
-Terminal.prototype.handleTitle = function(title) {
-  this.emit('title', title);
-};
 
-/**
- * ESC
- */
+      if (key === 'l' || key === '\x1b[C') {
+        var x = this.x;
+        this.x++;
+        if (this.x >= this.cols) {
+          this.x = this.cols - 1;
+        }
+        this.refresh(this.y, this.y);
+        return;
+      }
 
-// ESC D Index (IND is 0x84).
-Terminal.prototype.index = function() {
-  this.y++;
-  if (this.y > this.scrollBottom) {
-    this.y--;
-    this.scroll();
-  }
-  this.state = normal;
-};
-
-// ESC M Reverse Index (RI is 0x8d).
-Terminal.prototype.reverseIndex = function() {
-  var j;
-  this.y--;
-  if (this.y < this.scrollTop) {
-    this.y++;
-    // possibly move the code below to term.reverseScroll();
-    // test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
-    // blankLine(true) is xterm/linux behavior
-    this.lines.splice(this.y + this.ybase, 0, this.blankLine(true));
-    j = this.rows - 1 - this.scrollBottom;
-    this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1);
-    // this.maxRange();
-    this.updateRange(this.scrollTop);
-    this.updateRange(this.scrollBottom);
-  }
-  this.state = normal;
-};
-
-// ESC c Full Reset (RIS).
-Terminal.prototype.reset = function() {
-  this.options.rows = this.rows;
-  this.options.cols = this.cols;
-  Terminal.call(this, this.options);
-  this.refresh(0, this.rows - 1);
-};
-
-// ESC H Tab Set (HTS is 0x88).
-Terminal.prototype.tabSet = function() {
-  this.tabs[this.x] = true;
-  this.state = normal;
-};
+      if (key === 'w' || key === 'W') {
+        var ox = this.x;
+        var oy = this.y;
+        var oyd = this.ydisp;
+
+        var x = this.x;
+        var y = this.y;
+        var yb = this.ydisp;
+        var saw_space = false;
+
+        for (;;) {
+          var line = this.lines[yb + y];
+          while (x < this.cols) {
+            if (line[x][1] <= ' ') {
+              saw_space = true;
+            } else if (saw_space) {
+              break;
+            }
+            x++;
+          }
+          if (x >= this.cols) x = this.cols - 1;
+          if (x === this.cols - 1 && line[x][1] <= ' ') {
+            x = 0;
+            if (++y >= this.rows) {
+              y--;
+              if (++yb > this.ybase) {
+                yb = this.ybase;
+                x = this.x;
+                break;
+              }
+            }
+            continue;
+          }
+          break;
+        }
 
-/**
- * CSI
- */
+        this.x = x, this.y = y;
+        this.scrollDisp(-this.ydisp + yb);
 
-// CSI Ps A
-// Cursor Up Ps Times (default = 1) (CUU).
-Terminal.prototype.cursorUp = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.y -= param;
-  if (this.y < 0) this.y = 0;
-};
-
-// CSI Ps B
-// Cursor Down Ps Times (default = 1) (CUD).
-Terminal.prototype.cursorDown = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.y += param;
-  if (this.y >= this.rows) {
-    this.y = this.rows - 1;
-  }
-};
-
-// CSI Ps C
-// Cursor Forward Ps Times (default = 1) (CUF).
-Terminal.prototype.cursorForward = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.x += param;
-  if (this.x >= this.cols) {
-    this.x = this.cols - 1;
-  }
-};
-
-// CSI Ps D
-// Cursor Backward Ps Times (default = 1) (CUB).
-Terminal.prototype.cursorBackward = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.x -= param;
-  if (this.x < 0) this.x = 0;
-};
-
-// CSI Ps ; Ps H
-// Cursor Position [row;column] (default = [1,1]) (CUP).
-Terminal.prototype.cursorPos = function(params) {
-  var row, col;
-
-  row = params[0] - 1;
-
-  if (params.length >= 2) {
-    col = params[1] - 1;
-  } else {
-    col = 0;
-  }
-
-  if (row < 0) {
-    row = 0;
-  } else if (row >= this.rows) {
-    row = this.rows - 1;
-  }
-
-  if (col < 0) {
-    col = 0;
-  } else if (col >= this.cols) {
-    col = this.cols - 1;
-  }
-
-  this.x = col;
-  this.y = row;
-};
-
-// CSI Ps J  Erase in Display (ED).
-//     Ps = 0  -> Erase Below (default).
-//     Ps = 1  -> Erase Above.
-//     Ps = 2  -> Erase All.
-//     Ps = 3  -> Erase Saved Lines (xterm).
-// CSI ? Ps J
-//   Erase in Display (DECSED).
-//     Ps = 0  -> Selective Erase Below (default).
-//     Ps = 1  -> Selective Erase Above.
-//     Ps = 2  -> Selective Erase All.
-Terminal.prototype.eraseInDisplay = function(params) {
-  var j;
-  switch (params[0]) {
-    case 0:
-      this.eraseRight(this.x, this.y);
-      j = this.y + 1;
-      for (; j < this.rows; j++) {
-        this.eraseLine(j);
-      }
-      break;
-    case 1:
-      this.eraseLeft(this.x, this.y);
-      j = this.y;
-      while (j--) {
-        this.eraseLine(j);
+        return;
       }
-      break;
-    case 2:
-      j = this.rows;
-      while (j--) this.eraseLine(j);
-      break;
-    case 3:
-      ; // no saved lines
-      break;
-  }
-};
-
-// CSI Ps K  Erase in Line (EL).
-//     Ps = 0  -> Erase to Right (default).
-//     Ps = 1  -> Erase to Left.
-//     Ps = 2  -> Erase All.
-// CSI ? Ps K
-//   Erase in Line (DECSEL).
-//     Ps = 0  -> Selective Erase to Right (default).
-//     Ps = 1  -> Selective Erase to Left.
-//     Ps = 2  -> Selective Erase All.
-Terminal.prototype.eraseInLine = function(params) {
-  switch (params[0]) {
-    case 0:
-      this.eraseRight(this.x, this.y);
-      break;
-    case 1:
-      this.eraseLeft(this.x, this.y);
-      break;
-    case 2:
-      this.eraseLine(this.y);
-      break;
-  }
-};
-
-// CSI Pm m  Character Attributes (SGR).
-//     Ps = 0  -> Normal (default).
-//     Ps = 1  -> Bold.
-//     Ps = 4  -> Underlined.
-//     Ps = 5  -> Blink (appears as Bold).
-//     Ps = 7  -> Inverse.
-//     Ps = 8  -> Invisible, i.e., hidden (VT300).
-//     Ps = 2 2  -> Normal (neither bold nor faint).
-//     Ps = 2 4  -> Not underlined.
-//     Ps = 2 5  -> Steady (not blinking).
-//     Ps = 2 7  -> Positive (not inverse).
-//     Ps = 2 8  -> Visible, i.e., not hidden (VT300).
-//     Ps = 3 0  -> Set foreground color to Black.
-//     Ps = 3 1  -> Set foreground color to Red.
-//     Ps = 3 2  -> Set foreground color to Green.
-//     Ps = 3 3  -> Set foreground color to Yellow.
-//     Ps = 3 4  -> Set foreground color to Blue.
-//     Ps = 3 5  -> Set foreground color to Magenta.
-//     Ps = 3 6  -> Set foreground color to Cyan.
-//     Ps = 3 7  -> Set foreground color to White.
-//     Ps = 3 9  -> Set foreground color to default (original).
-//     Ps = 4 0  -> Set background color to Black.
-//     Ps = 4 1  -> Set background color to Red.
-//     Ps = 4 2  -> Set background color to Green.
-//     Ps = 4 3  -> Set background color to Yellow.
-//     Ps = 4 4  -> Set background color to Blue.
-//     Ps = 4 5  -> Set background color to Magenta.
-//     Ps = 4 6  -> Set background color to Cyan.
-//     Ps = 4 7  -> Set background color to White.
-//     Ps = 4 9  -> Set background color to default (original).
-
-//   If 16-color support is compiled, the following apply.  Assume
-//   that xterm's resources are set so that the ISO color codes are
-//   the first 8 of a set of 16.  Then the aixterm colors are the
-//   bright versions of the ISO colors:
-//     Ps = 9 0  -> Set foreground color to Black.
-//     Ps = 9 1  -> Set foreground color to Red.
-//     Ps = 9 2  -> Set foreground color to Green.
-//     Ps = 9 3  -> Set foreground color to Yellow.
-//     Ps = 9 4  -> Set foreground color to Blue.
-//     Ps = 9 5  -> Set foreground color to Magenta.
-//     Ps = 9 6  -> Set foreground color to Cyan.
-//     Ps = 9 7  -> Set foreground color to White.
-//     Ps = 1 0 0  -> Set background color to Black.
-//     Ps = 1 0 1  -> Set background color to Red.
-//     Ps = 1 0 2  -> Set background color to Green.
-//     Ps = 1 0 3  -> Set background color to Yellow.
-//     Ps = 1 0 4  -> Set background color to Blue.
-//     Ps = 1 0 5  -> Set background color to Magenta.
-//     Ps = 1 0 6  -> Set background color to Cyan.
-//     Ps = 1 0 7  -> Set background color to White.
-
-//   If xterm is compiled with the 16-color support disabled, it
-//   supports the following, from rxvt:
-//     Ps = 1 0 0  -> Set foreground and background color to
-//     default.
-
-//   If 88- or 256-color support is compiled, the following apply.
-//     Ps = 3 8  ; 5  ; Ps -> Set foreground color to the second
-//     Ps.
-//     Ps = 4 8  ; 5  ; Ps -> Set background color to the second
-//     Ps.
-Terminal.prototype.charAttributes = function(params) {
-  // Optimize a single SGR0.
-  if (params.length === 1 && params[0] === 0) {
-    this.curAttr = this.defAttr;
-    return;
-  }
-
-  var l = params.length
-    , i = 0
-    , flags = this.curAttr >> 18
-    , fg = (this.curAttr >> 9) & 0x1ff
-    , bg = this.curAttr & 0x1ff
-    , p;
-
-  for (; i < l; i++) {
-    p = params[i];
-    if (p >= 30 && p <= 37) {
-      // fg color 8
-      fg = p - 30;
-    } else if (p >= 40 && p <= 47) {
-      // bg color 8
-      bg = p - 40;
-    } else if (p >= 90 && p <= 97) {
-      // fg color 16
-      p += 8;
-      fg = p - 90;
-    } else if (p >= 100 && p <= 107) {
-      // bg color 16
-      p += 8;
-      bg = p - 100;
-    } else if (p === 0) {
-      // default
-      flags = this.defAttr >> 18;
-      fg = (this.defAttr >> 9) & 0x1ff;
-      bg = this.defAttr & 0x1ff;
-      // flags = 0;
-      // fg = 0x1ff;
-      // bg = 0x1ff;
-    } else if (p === 1) {
-      // bold text
-      flags |= 1;
-    } else if (p === 4) {
-      // underlined text
-      flags |= 2;
-    } else if (p === 5) {
-      // blink
-      flags |= 4;
-    } else if (p === 7) {
-      // inverse and positive
-      // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
-      flags |= 8;
-    } else if (p === 8) {
-      // invisible
-      flags |= 16;
-    } else if (p === 22) {
-      // not bold
-      flags &= ~1;
-    } else if (p === 24) {
-      // not underlined
-      flags &= ~2;
-    } else if (p === 25) {
-      // not blink
-      flags &= ~4;
-    } else if (p === 27) {
-      // not inverse
-      flags &= ~8;
-    } else if (p === 28) {
-      // not invisible
-      flags &= ~16;
-    } else if (p === 39) {
-      // reset fg
-      fg = (this.defAttr >> 9) & 0x1ff;
-    } else if (p === 49) {
-      // reset bg
-      bg = this.defAttr & 0x1ff;
-    } else if (p === 38) {
-      // fg color 256
-      if (params[i + 1] === 2) {
-        i += 2;
-        fg = matchColor(
-          params[i] & 0xff,
-          params[i + 1] & 0xff,
-          params[i + 2] & 0xff);
-        if (fg === -1) fg = 0x1ff;
-        i += 2;
-      } else if (params[i + 1] === 5) {
-        i += 2;
-        p = params[i] & 0xff;
-        fg = p;
-      }
-    } else if (p === 48) {
-      // bg color 256
-      if (params[i + 1] === 2) {
-        i += 2;
-        bg = matchColor(
-          params[i] & 0xff,
-          params[i + 1] & 0xff,
-          params[i + 2] & 0xff);
-        if (bg === -1) bg = 0x1ff;
-        i += 2;
-      } else if (params[i + 1] === 5) {
-        i += 2;
-        p = params[i] & 0xff;
-        bg = p;
-      }
-    } else if (p === 100) {
-      // reset fg/bg
-      fg = (this.defAttr >> 9) & 0x1ff;
-      bg = this.defAttr & 0x1ff;
-    } else {
-      this.error('Unknown SGR attribute: %d.', p);
-    }
-  }
-
-  this.curAttr = (flags << 18) | (fg << 9) | bg;
-};
-
-// CSI Ps n  Device Status Report (DSR).
-//     Ps = 5  -> Status Report.  Result (``OK'') is
-//   CSI 0 n
-//     Ps = 6  -> Report Cursor Position (CPR) [row;column].
-//   Result is
-//   CSI r ; c R
-// CSI ? Ps n
-//   Device Status Report (DSR, DEC-specific).
-//     Ps = 6  -> Report Cursor Position (CPR) [row;column] as CSI
-//     ? r ; c R (assumes page is zero).
-//     Ps = 1 5  -> Report Printer status as CSI ? 1 0  n  (ready).
-//     or CSI ? 1 1  n  (not ready).
-//     Ps = 2 5  -> Report UDK status as CSI ? 2 0  n  (unlocked)
-//     or CSI ? 2 1  n  (locked).
-//     Ps = 2 6  -> Report Keyboard status as
-//   CSI ? 2 7  ;  1  ;  0  ;  0  n  (North American).
-//   The last two parameters apply to VT400 & up, and denote key-
-//   board ready and LK01 respectively.
-//     Ps = 5 3  -> Report Locator status as
-//   CSI ? 5 3  n  Locator available, if compiled-in, or
-//   CSI ? 5 0  n  No Locator, if not.
-Terminal.prototype.deviceStatus = function(params) {
-  if (!this.prefix) {
-    switch (params[0]) {
-      case 5:
-        // status report
-        this.send('\x1b[0n');
-        break;
-      case 6:
-        // cursor position
-        this.send('\x1b['
-          + (this.y + 1)
-          + ';'
-          + (this.x + 1)
-          + 'R');
-        break;
-    }
-  } else if (this.prefix === '?') {
-    // modern xterm doesnt seem to
-    // respond to any of these except ?6, 6, and 5
-    switch (params[0]) {
-      case 6:
-        // cursor position
-        this.send('\x1b[?'
-          + (this.y + 1)
-          + ';'
-          + (this.x + 1)
-          + 'R');
-        break;
-      case 15:
-        // no printer
-        // this.send('\x1b[?11n');
-        break;
-      case 25:
-        // dont support user defined keys
-        // this.send('\x1b[?21n');
-        break;
-      case 26:
-        // north american keyboard
-        // this.send('\x1b[?27;1;0;0n');
-        break;
-      case 53:
-        // no dec locator/mouse
-        // this.send('\x1b[?50n');
-        break;
-    }
-  }
-};
 
-/**
- * Additions
- */
+      if (key === 'b' || key === 'B') {
+        var ox = this.x;
+        var oy = this.y;
+        var oyd = this.ydisp;
+
+        var x = this.x;
+        var y = this.y;
+        var yb = this.ydisp;
+
+        for (;;) {
+          var line = this.lines[yb + y];
+          var saw_space = x > 0 && line[x][1] > ' ' && line[x - 1][1] > ' ';
+          while (x >= 0) {
+            if (line[x][1] <= ' ') {
+              if (saw_space && (x + 1 < this.cols && line[x + 1][1] > ' ')) {
+                x++;
+                break;
+              } else {
+                saw_space = true;
+              }
+            }
+            x--;
+          }
+          if (x < 0) x = 0;
+          if (x === 0 && (line[x][1] <= ' ' || !saw_space)) {
+            x = this.cols - 1;
+            if (--y < 0) {
+              y++;
+              if (--yb < 0) {
+                yb++;
+                x = 0;
+                break;
+              }
+            }
+            continue;
+          }
+          break;
+        }
 
-// CSI Ps @
-// Insert Ps (Blank) Character(s) (default = 1) (ICH).
-Terminal.prototype.insertChars = function(params) {
-  var param, row, j, ch;
-
-  param = params[0];
-  if (param < 1) param = 1;
-
-  row = this.y + this.ybase;
-  j = this.x;
-  ch = [this.eraseAttr(), ' ']; // xterm
-
-  while (param-- && j < this.cols) {
-    this.lines[row].splice(j++, 0, ch);
-    this.lines[row].pop();
-  }
-};
-
-// CSI Ps E
-// Cursor Next Line Ps Times (default = 1) (CNL).
-// same as CSI Ps B ?
-Terminal.prototype.cursorNextLine = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.y += param;
-  if (this.y >= this.rows) {
-    this.y = this.rows - 1;
-  }
-  this.x = 0;
-};
-
-// CSI Ps F
-// Cursor Preceding Line Ps Times (default = 1) (CNL).
-// reuse CSI Ps A ?
-Terminal.prototype.cursorPrecedingLine = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.y -= param;
-  if (this.y < 0) this.y = 0;
-  this.x = 0;
-};
-
-// CSI Ps G
-// Cursor Character Absolute  [column] (default = [row,1]) (CHA).
-Terminal.prototype.cursorCharAbsolute = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.x = param - 1;
-};
-
-// CSI Ps L
-// Insert Ps Line(s) (default = 1) (IL).
-Terminal.prototype.insertLines = function(params) {
-  var param, row, j;
-
-  param = params[0];
-  if (param < 1) param = 1;
-  row = this.y + this.ybase;
-
-  j = this.rows - 1 - this.scrollBottom;
-  j = this.rows - 1 + this.ybase - j + 1;
-
-  while (param--) {
-    // test: echo -e '\e[44m\e[1L\e[0m'
-    // blankLine(true) - xterm/linux behavior
-    this.lines.splice(row, 0, this.blankLine(true));
-    this.lines.splice(j, 1);
-  }
-
-  // this.maxRange();
-  this.updateRange(this.y);
-  this.updateRange(this.scrollBottom);
-};
-
-// CSI Ps M
-// Delete Ps Line(s) (default = 1) (DL).
-Terminal.prototype.deleteLines = function(params) {
-  var param, row, j;
-
-  param = params[0];
-  if (param < 1) param = 1;
-  row = this.y + this.ybase;
-
-  j = this.rows - 1 - this.scrollBottom;
-  j = this.rows - 1 + this.ybase - j;
-
-  while (param--) {
-    // test: echo -e '\e[44m\e[1M\e[0m'
-    // blankLine(true) - xterm/linux behavior
-    this.lines.splice(j + 1, 0, this.blankLine(true));
-    this.lines.splice(row, 1);
-  }
-
-  // this.maxRange();
-  this.updateRange(this.y);
-  this.updateRange(this.scrollBottom);
-};
-
-// CSI Ps P
-// Delete Ps Character(s) (default = 1) (DCH).
-Terminal.prototype.deleteChars = function(params) {
-  var param, row, ch;
-
-  param = params[0];
-  if (param < 1) param = 1;
-
-  row = this.y + this.ybase;
-  ch = [this.eraseAttr(), ' ']; // xterm
-
-  while (param--) {
-    this.lines[row].splice(this.x, 1);
-    this.lines[row].push(ch);
-  }
-};
-
-// CSI Ps X
-// Erase Ps Character(s) (default = 1) (ECH).
-Terminal.prototype.eraseChars = function(params) {
-  var param, row, j, ch;
-
-  param = params[0];
-  if (param < 1) param = 1;
-
-  row = this.y + this.ybase;
-  j = this.x;
-  ch = [this.eraseAttr(), ' ']; // xterm
-
-  while (param-- && j < this.cols) {
-    this.lines[row][j++] = ch;
-  }
-};
-
-// CSI Pm `  Character Position Absolute
-//   [column] (default = [row,1]) (HPA).
-Terminal.prototype.charPosAbsolute = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.x = param - 1;
-  if (this.x >= this.cols) {
-    this.x = this.cols - 1;
-  }
-};
-
-// 141 61 a * HPR -
-// Horizontal Position Relative
-// reuse CSI Ps C ?
-Terminal.prototype.HPositionRelative = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.x += param;
-  if (this.x >= this.cols) {
-    this.x = this.cols - 1;
-  }
-};
-
-// CSI Ps c  Send Device Attributes (Primary DA).
-//     Ps = 0  or omitted -> request attributes from terminal.  The
-//     response depends on the decTerminalID resource setting.
-//     -> CSI ? 1 ; 2 c  (``VT100 with Advanced Video Option'')
-//     -> CSI ? 1 ; 0 c  (``VT101 with No Options'')
-//     -> CSI ? 6 c  (``VT102'')
-//     -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c  (``VT220'')
-//   The VT100-style response parameters do not mean anything by
-//   themselves.  VT220 parameters do, telling the host what fea-
-//   tures the terminal supports:
-//     Ps = 1  -> 132-columns.
-//     Ps = 2  -> Printer.
-//     Ps = 6  -> Selective erase.
-//     Ps = 8  -> User-defined keys.
-//     Ps = 9  -> National replacement character sets.
-//     Ps = 1 5  -> Technical characters.
-//     Ps = 2 2  -> ANSI color, e.g., VT525.
-//     Ps = 2 9  -> ANSI text locator (i.e., DEC Locator mode).
-// CSI > Ps c
-//   Send Device Attributes (Secondary DA).
-//     Ps = 0  or omitted -> request the terminal's identification
-//     code.  The response depends on the decTerminalID resource set-
-//     ting.  It should apply only to VT220 and up, but xterm extends
-//     this to VT100.
-//     -> CSI  > Pp ; Pv ; Pc c
-//   where Pp denotes the terminal type
-//     Pp = 0  -> ``VT100''.
-//     Pp = 1  -> ``VT220''.
-//   and Pv is the firmware version (for xterm, this was originally
-//   the XFree86 patch number, starting with 95).  In a DEC termi-
-//   nal, Pc indicates the ROM cartridge registration number and is
-//   always zero.
-// More information:
-//   xterm/charproc.c - line 2012, for more information.
-//   vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
-Terminal.prototype.sendDeviceAttributes = function(params) {
-  if (params[0] > 0) return;
-
-  if (!this.prefix) {
-    if (this.is('xterm')
-        || this.is('rxvt-unicode')
-        || this.is('screen')) {
-      this.send('\x1b[?1;2c');
-    } else if (this.is('linux')) {
-      this.send('\x1b[?6c');
-    }
-  } else if (this.prefix === '>') {
-    // xterm and urxvt
-    // seem to spit this
-    // out around ~370 times (?).
-    if (this.is('xterm')) {
-      this.send('\x1b[>0;276;0c');
-    } else if (this.is('rxvt-unicode')) {
-      this.send('\x1b[>85;95;0c');
-    } else if (this.is('linux')) {
-      // not supported by linux console.
-      // linux console echoes parameters.
-      this.send(params[0] + 'c');
-    } else if (this.is('screen')) {
-      this.send('\x1b[>83;40003;0c');
-    }
-  }
-};
-
-// CSI Pm d
-// Line Position Absolute  [row] (default = [1,column]) (VPA).
-Terminal.prototype.linePosAbsolute = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.y = param - 1;
-  if (this.y >= this.rows) {
-    this.y = this.rows - 1;
-  }
-};
-
-// 145 65 e * VPR - Vertical Position Relative
-// reuse CSI Ps B ?
-Terminal.prototype.VPositionRelative = function(params) {
-  var param = params[0];
-  if (param < 1) param = 1;
-  this.y += param;
-  if (this.y >= this.rows) {
-    this.y = this.rows - 1;
-  }
-};
-
-// CSI Ps ; Ps f
-//   Horizontal and Vertical Position [row;column] (default =
-//   [1,1]) (HVP).
-Terminal.prototype.HVPosition = function(params) {
-  if (params[0] < 1) params[0] = 1;
-  if (params[1] < 1) params[1] = 1;
-
-  this.y = params[0] - 1;
-  if (this.y >= this.rows) {
-    this.y = this.rows - 1;
-  }
-
-  this.x = params[1] - 1;
-  if (this.x >= this.cols) {
-    this.x = this.cols - 1;
-  }
-};
-
-// CSI Pm h  Set Mode (SM).
-//     Ps = 2  -> Keyboard Action Mode (AM).
-//     Ps = 4  -> Insert Mode (IRM).
-//     Ps = 1 2  -> Send/receive (SRM).
-//     Ps = 2 0  -> Automatic Newline (LNM).
-// CSI ? Pm h
-//   DEC Private Mode Set (DECSET).
-//     Ps = 1  -> Application Cursor Keys (DECCKM).
-//     Ps = 2  -> Designate USASCII for character sets G0-G3
-//     (DECANM), and set VT100 mode.
-//     Ps = 3  -> 132 Column Mode (DECCOLM).
-//     Ps = 4  -> Smooth (Slow) Scroll (DECSCLM).
-//     Ps = 5  -> Reverse Video (DECSCNM).
-//     Ps = 6  -> Origin Mode (DECOM).
-//     Ps = 7  -> Wraparound Mode (DECAWM).
-//     Ps = 8  -> Auto-repeat Keys (DECARM).
-//     Ps = 9  -> Send Mouse X & Y on button press.  See the sec-
-//     tion Mouse Tracking.
-//     Ps = 1 0  -> Show toolbar (rxvt).
-//     Ps = 1 2  -> Start Blinking Cursor (att610).
-//     Ps = 1 8  -> Print form feed (DECPFF).
-//     Ps = 1 9  -> Set print extent to full screen (DECPEX).
-//     Ps = 2 5  -> Show Cursor (DECTCEM).
-//     Ps = 3 0  -> Show scrollbar (rxvt).
-//     Ps = 3 5  -> Enable font-shifting functions (rxvt).
-//     Ps = 3 8  -> Enter Tektronix Mode (DECTEK).
-//     Ps = 4 0  -> Allow 80 -> 132 Mode.
-//     Ps = 4 1  -> more(1) fix (see curses resource).
-//     Ps = 4 2  -> Enable Nation Replacement Character sets (DECN-
-//     RCM).
-//     Ps = 4 4  -> Turn On Margin Bell.
-//     Ps = 4 5  -> Reverse-wraparound Mode.
-//     Ps = 4 6  -> Start Logging.  This is normally disabled by a
-//     compile-time option.
-//     Ps = 4 7  -> Use Alternate Screen Buffer.  (This may be dis-
-//     abled by the titeInhibit resource).
-//     Ps = 6 6  -> Application keypad (DECNKM).
-//     Ps = 6 7  -> Backarrow key sends backspace (DECBKM).
-//     Ps = 1 0 0 0  -> Send Mouse X & Y on button press and
-//     release.  See the section Mouse Tracking.
-//     Ps = 1 0 0 1  -> Use Hilite Mouse Tracking.
-//     Ps = 1 0 0 2  -> Use Cell Motion Mouse Tracking.
-//     Ps = 1 0 0 3  -> Use All Motion Mouse Tracking.
-//     Ps = 1 0 0 4  -> Send FocusIn/FocusOut events.
-//     Ps = 1 0 0 5  -> Enable Extended Mouse Mode.
-//     Ps = 1 0 1 0  -> Scroll to bottom on tty output (rxvt).
-//     Ps = 1 0 1 1  -> Scroll to bottom on key press (rxvt).
-//     Ps = 1 0 3 4  -> Interpret "meta" key, sets eighth bit.
-//     (enables the eightBitInput resource).
-//     Ps = 1 0 3 5  -> Enable special modifiers for Alt and Num-
-//     Lock keys.  (This enables the numLock resource).
-//     Ps = 1 0 3 6  -> Send ESC   when Meta modifies a key.  (This
-//     enables the metaSendsEscape resource).
-//     Ps = 1 0 3 7  -> Send DEL from the editing-keypad Delete
-//     key.
-//     Ps = 1 0 3 9  -> Send ESC  when Alt modifies a key.  (This
-//     enables the altSendsEscape resource).
-//     Ps = 1 0 4 0  -> Keep selection even if not highlighted.
-//     (This enables the keepSelection resource).
-//     Ps = 1 0 4 1  -> Use the CLIPBOARD selection.  (This enables
-//     the selectToClipboard resource).
-//     Ps = 1 0 4 2  -> Enable Urgency window manager hint when
-//     Control-G is received.  (This enables the bellIsUrgent
-//     resource).
-//     Ps = 1 0 4 3  -> Enable raising of the window when Control-G
-//     is received.  (enables the popOnBell resource).
-//     Ps = 1 0 4 7  -> Use Alternate Screen Buffer.  (This may be
-//     disabled by the titeInhibit resource).
-//     Ps = 1 0 4 8  -> Save cursor as in DECSC.  (This may be dis-
-//     abled by the titeInhibit resource).
-//     Ps = 1 0 4 9  -> Save cursor as in DECSC and use Alternate
-//     Screen Buffer, clearing it first.  (This may be disabled by
-//     the titeInhibit resource).  This combines the effects of the 1
-//     0 4 7  and 1 0 4 8  modes.  Use this with terminfo-based
-//     applications rather than the 4 7  mode.
-//     Ps = 1 0 5 0  -> Set terminfo/termcap function-key mode.
-//     Ps = 1 0 5 1  -> Set Sun function-key mode.
-//     Ps = 1 0 5 2  -> Set HP function-key mode.
-//     Ps = 1 0 5 3  -> Set SCO function-key mode.
-//     Ps = 1 0 6 0  -> Set legacy keyboard emulation (X11R6).
-//     Ps = 1 0 6 1  -> Set VT220 keyboard emulation.
-//     Ps = 2 0 0 4  -> Set bracketed paste mode.
-// Modes:
-//   http://vt100.net/docs/vt220-rm/chapter4.html
-Terminal.prototype.setMode = function(params) {
-  if (typeof params === 'object') {
-    var l = params.length
-      , i = 0;
-
-    for (; i < l; i++) {
-      this.setMode(params[i]);
-    }
+        this.x = x, this.y = y;
+        this.scrollDisp(-this.ydisp + yb);
 
-    return;
-  }
-
-  if (!this.prefix) {
-    switch (params) {
-      case 4:
-        this.insertMode = true;
-        break;
-      case 20:
-        //this.convertEol = true;
-        break;
-    }
-  } else if (this.prefix === '?') {
-    switch (params) {
-      case 1:
-        this.applicationCursor = true;
-        break;
-      case 2:
-        this.setgCharset(0, Terminal.charsets.US);
-        this.setgCharset(1, Terminal.charsets.US);
-        this.setgCharset(2, Terminal.charsets.US);
-        this.setgCharset(3, Terminal.charsets.US);
-        // set VT100 mode here
-        break;
-      case 3: // 132 col mode
-        this.savedCols = this.cols;
-        this.resize(132, this.rows);
-        break;
-      case 6:
-        this.originMode = true;
-        break;
-      case 7:
-        this.wraparoundMode = true;
-        break;
-      case 12:
-        // this.cursorBlink = true;
-        break;
-      case 66:
-        this.log('Serial port requested application keypad.');
-        this.applicationKeypad = true;
-        break;
-      case 9: // X10 Mouse
-        // no release, no motion, no wheel, no modifiers.
-      case 1000: // vt200 mouse
-        // no motion.
-        // no modifiers, except control on the wheel.
-      case 1002: // button event mouse
-      case 1003: // any event mouse
-        // any event - sends motion events,
-        // even if there is no button held down.
-        this.x10Mouse = params === 9;
-        this.vt200Mouse = params === 1000;
-        this.normalMouse = params > 1000;
-        this.mouseEvents = true;
-        this.element.style.cursor = 'default';
-        this.log('Binding to mouse events.');
-        break;
-      case 1004: // send focusin/focusout events
-        // focusin: ^[[I
-        // focusout: ^[[O
-        this.sendFocus = true;
-        break;
-      case 1005: // utf8 ext mode mouse
-        this.utfMouse = true;
-        // for wide terminals
-        // simply encodes large values as utf8 characters
-        break;
-      case 1006: // sgr ext mode mouse
-        this.sgrMouse = true;
-        // for wide terminals
-        // does not add 32 to fields
-        // press: ^[[<b;x;yM
-        // release: ^[[<b;x;ym
-        break;
-      case 1015: // urxvt ext mode mouse
-        this.urxvtMouse = true;
-        // for wide terminals
-        // numbers for fields
-        // press: ^[[b;x;yM
-        // motion: ^[[b;x;yT
-        break;
-      case 25: // show cursor
-        this.cursorHidden = false;
-        break;
-      case 1049: // alt screen buffer cursor
-        //this.saveCursor();
-        ; // FALL-THROUGH
-      case 47: // alt screen buffer
-      case 1047: // alt screen buffer
-        if (!this.normal) {
-          var normal = {
-            lines: this.lines,
-            ybase: this.ybase,
-            ydisp: this.ydisp,
-            x: this.x,
-            y: this.y,
-            scrollTop: this.scrollTop,
-            scrollBottom: this.scrollBottom,
-            tabs: this.tabs
-            // XXX save charset(s) here?
-            // charset: this.charset,
-            // glevel: this.glevel,
-            // charsets: this.charsets
-          };
-          this.reset();
-          this.normal = normal;
-          this.showCursor();
-        }
-        break;
-    }
-  }
-};
-
-// CSI Pm l  Reset Mode (RM).
-//     Ps = 2  -> Keyboard Action Mode (AM).
-//     Ps = 4  -> Replace Mode (IRM).
-//     Ps = 1 2  -> Send/receive (SRM).
-//     Ps = 2 0  -> Normal Linefeed (LNM).
-// CSI ? Pm l
-//   DEC Private Mode Reset (DECRST).
-//     Ps = 1  -> Normal Cursor Keys (DECCKM).
-//     Ps = 2  -> Designate VT52 mode (DECANM).
-//     Ps = 3  -> 80 Column Mode (DECCOLM).
-//     Ps = 4  -> Jump (Fast) Scroll (DECSCLM).
-//     Ps = 5  -> Normal Video (DECSCNM).
-//     Ps = 6  -> Normal Cursor Mode (DECOM).
-//     Ps = 7  -> No Wraparound Mode (DECAWM).
-//     Ps = 8  -> No Auto-repeat Keys (DECARM).
-//     Ps = 9  -> Don't send Mouse X & Y on button press.
-//     Ps = 1 0  -> Hide toolbar (rxvt).
-//     Ps = 1 2  -> Stop Blinking Cursor (att610).
-//     Ps = 1 8  -> Don't print form feed (DECPFF).
-//     Ps = 1 9  -> Limit print to scrolling region (DECPEX).
-//     Ps = 2 5  -> Hide Cursor (DECTCEM).
-//     Ps = 3 0  -> Don't show scrollbar (rxvt).
-//     Ps = 3 5  -> Disable font-shifting functions (rxvt).
-//     Ps = 4 0  -> Disallow 80 -> 132 Mode.
-//     Ps = 4 1  -> No more(1) fix (see curses resource).
-//     Ps = 4 2  -> Disable Nation Replacement Character sets (DEC-
-//     NRCM).
-//     Ps = 4 4  -> Turn Off Margin Bell.
-//     Ps = 4 5  -> No Reverse-wraparound Mode.
-//     Ps = 4 6  -> Stop Logging.  (This is normally disabled by a
-//     compile-time option).
-//     Ps = 4 7  -> Use Normal Screen Buffer.
-//     Ps = 6 6  -> Numeric keypad (DECNKM).
-//     Ps = 6 7  -> Backarrow key sends delete (DECBKM).
-//     Ps = 1 0 0 0  -> Don't send Mouse X & Y on button press and
-//     release.  See the section Mouse Tracking.
-//     Ps = 1 0 0 1  -> Don't use Hilite Mouse Tracking.
-//     Ps = 1 0 0 2  -> Don't use Cell Motion Mouse Tracking.
-//     Ps = 1 0 0 3  -> Don't use All Motion Mouse Tracking.
-//     Ps = 1 0 0 4  -> Don't send FocusIn/FocusOut events.
-//     Ps = 1 0 0 5  -> Disable Extended Mouse Mode.
-//     Ps = 1 0 1 0  -> Don't scroll to bottom on tty output
-//     (rxvt).
-//     Ps = 1 0 1 1  -> Don't scroll to bottom on key press (rxvt).
-//     Ps = 1 0 3 4  -> Don't interpret "meta" key.  (This disables
-//     the eightBitInput resource).
-//     Ps = 1 0 3 5  -> Disable special modifiers for Alt and Num-
-//     Lock keys.  (This disables the numLock resource).
-//     Ps = 1 0 3 6  -> Don't send ESC  when Meta modifies a key.
-//     (This disables the metaSendsEscape resource).
-//     Ps = 1 0 3 7  -> Send VT220 Remove from the editing-keypad
-//     Delete key.
-//     Ps = 1 0 3 9  -> Don't send ESC  when Alt modifies a key.
-//     (This disables the altSendsEscape resource).
-//     Ps = 1 0 4 0  -> Do not keep selection when not highlighted.
-//     (This disables the keepSelection resource).
-//     Ps = 1 0 4 1  -> Use the PRIMARY selection.  (This disables
-//     the selectToClipboard resource).
-//     Ps = 1 0 4 2  -> Disable Urgency window manager hint when
-//     Control-G is received.  (This disables the bellIsUrgent
-//     resource).
-//     Ps = 1 0 4 3  -> Disable raising of the window when Control-
-//     G is received.  (This disables the popOnBell resource).
-//     Ps = 1 0 4 7  -> Use Normal Screen Buffer, clearing screen
-//     first if in the Alternate Screen.  (This may be disabled by
-//     the titeInhibit resource).
-//     Ps = 1 0 4 8  -> Restore cursor as in DECRC.  (This may be
-//     disabled by the titeInhibit resource).
-//     Ps = 1 0 4 9  -> Use Normal Screen Buffer and restore cursor
-//     as in DECRC.  (This may be disabled by the titeInhibit
-//     resource).  This combines the effects of the 1 0 4 7  and 1 0
-//     4 8  modes.  Use this with terminfo-based applications rather
-//     than the 4 7  mode.
-//     Ps = 1 0 5 0  -> Reset terminfo/termcap function-key mode.
-//     Ps = 1 0 5 1  -> Reset Sun function-key mode.
-//     Ps = 1 0 5 2  -> Reset HP function-key mode.
-//     Ps = 1 0 5 3  -> Reset SCO function-key mode.
-//     Ps = 1 0 6 0  -> Reset legacy keyboard emulation (X11R6).
-//     Ps = 1 0 6 1  -> Reset keyboard emulation to Sun/PC style.
-//     Ps = 2 0 0 4  -> Reset bracketed paste mode.
-Terminal.prototype.resetMode = function(params) {
-  if (typeof params === 'object') {
-    var l = params.length
-      , i = 0;
-
-    for (; i < l; i++) {
-      this.resetMode(params[i]);
-    }
+        return;
+      }
 
-    return;
-  }
-
-  if (!this.prefix) {
-    switch (params) {
-      case 4:
-        this.insertMode = false;
-        break;
-      case 20:
-        //this.convertEol = false;
-        break;
-    }
-  } else if (this.prefix === '?') {
-    switch (params) {
-      case 1:
-        this.applicationCursor = false;
-        break;
-      case 3:
-        if (this.cols === 132 && this.savedCols) {
-          this.resize(this.savedCols, this.rows);
-        }
-        delete this.savedCols;
-        break;
-      case 6:
-        this.originMode = false;
-        break;
-      case 7:
-        this.wraparoundMode = false;
-        break;
-      case 12:
-        // this.cursorBlink = false;
-        break;
-      case 66:
-        this.log('Switching back to normal keypad.');
-        this.applicationKeypad = false;
-        break;
-      case 9: // X10 Mouse
-      case 1000: // vt200 mouse
-      case 1002: // button event mouse
-      case 1003: // any event mouse
-        this.x10Mouse = false;
-        this.vt200Mouse = false;
-        this.normalMouse = false;
-        this.mouseEvents = false;
-        this.element.style.cursor = '';
-        break;
-      case 1004: // send focusin/focusout events
-        this.sendFocus = false;
-        break;
-      case 1005: // utf8 ext mode mouse
-        this.utfMouse = false;
-        break;
-      case 1006: // sgr ext mode mouse
-        this.sgrMouse = false;
-        break;
-      case 1015: // urxvt ext mode mouse
-        this.urxvtMouse = false;
-        break;
-      case 25: // hide cursor
-        this.cursorHidden = true;
-        break;
-      case 1049: // alt screen buffer cursor
-        ; // FALL-THROUGH
-      case 47: // normal screen buffer
-      case 1047: // normal screen buffer - clearing it first
-        if (this.normal) {
-          this.lines = this.normal.lines;
-          this.ybase = this.normal.ybase;
-          this.ydisp = this.normal.ydisp;
-          this.x = this.normal.x;
-          this.y = this.normal.y;
-          this.scrollTop = this.normal.scrollTop;
-          this.scrollBottom = this.normal.scrollBottom;
-          this.tabs = this.normal.tabs;
-          this.normal = null;
-          // if (params === 1049) {
-          //   this.x = this.savedX;
-          //   this.y = this.savedY;
-          // }
-          this.refresh(0, this.rows - 1);
-          this.showCursor();
+      if (key === 'e' || key === 'E') {
+        var x = this.x + 1;
+        var y = this.y;
+        var yb = this.ydisp;
+        if (x >= this.cols) x--;
+
+        for (;;) {
+          var line = this.lines[yb + y];
+          while (x < this.cols) {
+            if (line[x][1] <= ' ') {
+              x++;
+            } else {
+              break;
+            }
+          }
+          while (x < this.cols) {
+            if (line[x][1] <= ' ') {
+              if (x - 1 >= 0 && line[x - 1][1] > ' ') {
+                x--;
+                break;
+              }
+            }
+            x++;
+          }
+          if (x >= this.cols) x = this.cols - 1;
+          if (x === this.cols - 1 && line[x][1] <= ' ') {
+            x = 0;
+            if (++y >= this.rows) {
+              y--;
+              if (++yb > this.ybase) {
+                yb = this.ybase;
+                break;
+              }
+            }
+            continue;
+          }
+          break;
         }
-        break;
-    }
-  }
-};
-
-// CSI Ps ; Ps r
-//   Set Scrolling Region [top;bottom] (default = full size of win-
-//   dow) (DECSTBM).
-// CSI ? Pm r
-Terminal.prototype.setScrollRegion = function(params) {
-  if (this.prefix) return;
-  this.scrollTop = (params[0] || 1) - 1;
-  this.scrollBottom = (params[1] || this.rows) - 1;
-  this.x = 0;
-  this.y = 0;
-};
-
-// CSI s
-//   Save cursor (ANSI.SYS).
-Terminal.prototype.saveCursor = function(params) {
-  this.savedX = this.x;
-  this.savedY = this.y;
-};
-
-// CSI u
-//   Restore cursor (ANSI.SYS).
-Terminal.prototype.restoreCursor = function(params) {
-  this.x = this.savedX || 0;
-  this.y = this.savedY || 0;
-};
 
-/**
- * Lesser Used
- */
+        this.x = x, this.y = y;
+        this.scrollDisp(-this.ydisp + yb);
 
-// CSI Ps I
-//   Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
-Terminal.prototype.cursorForwardTab = function(params) {
-  var param = params[0] || 1;
-  while (param--) {
-    this.x = this.nextStop();
-  }
-};
-
-// CSI Ps S  Scroll up Ps lines (default = 1) (SU).
-Terminal.prototype.scrollUp = function(params) {
-  var param = params[0] || 1;
-  while (param--) {
-    this.lines.splice(this.ybase + this.scrollTop, 1);
-    this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine());
-  }
-  // this.maxRange();
-  this.updateRange(this.scrollTop);
-  this.updateRange(this.scrollBottom);
-};
-
-// CSI Ps T  Scroll down Ps lines (default = 1) (SD).
-Terminal.prototype.scrollDown = function(params) {
-  var param = params[0] || 1;
-  while (param--) {
-    this.lines.splice(this.ybase + this.scrollBottom, 1);
-    this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine());
-  }
-  // this.maxRange();
-  this.updateRange(this.scrollTop);
-  this.updateRange(this.scrollBottom);
-};
-
-// CSI Ps ; Ps ; Ps ; Ps ; Ps T
-//   Initiate highlight mouse tracking.  Parameters are
-//   [func;startx;starty;firstrow;lastrow].  See the section Mouse
-//   Tracking.
-Terminal.prototype.initMouseTracking = function(params) {
-  // Relevant: DECSET 1001
-};
-
-// CSI > Ps; Ps T
-//   Reset one or more features of the title modes to the default
-//   value.  Normally, "reset" disables the feature.  It is possi-
-//   ble to disable the ability to reset features by compiling a
-//   different default for the title modes into xterm.
-//     Ps = 0  -> Do not set window/icon labels using hexadecimal.
-//     Ps = 1  -> Do not query window/icon labels using hexadeci-
-//     mal.
-//     Ps = 2  -> Do not set window/icon labels using UTF-8.
-//     Ps = 3  -> Do not query window/icon labels using UTF-8.
-//   (See discussion of "Title Modes").
-Terminal.prototype.resetTitleModes = function(params) {
-  ;
-};
-
-// CSI Ps Z  Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
-Terminal.prototype.cursorBackwardTab = function(params) {
-  var param = params[0] || 1;
-  while (param--) {
-    this.x = this.prevStop();
-  }
-};
-
-// CSI Ps b  Repeat the preceding graphic character Ps times (REP).
-Terminal.prototype.repeatPrecedingCharacter = function(params) {
-  var param = params[0] || 1
-    , line = this.lines[this.ybase + this.y]
-    , ch = line[this.x - 1] || [this.defAttr, ' '];
-
-  while (param--) line[this.x++] = ch;
-};
-
-// CSI Ps g  Tab Clear (TBC).
-//     Ps = 0  -> Clear Current Column (default).
-//     Ps = 3  -> Clear All.
-// Potentially:
-//   Ps = 2  -> Clear Stops on Line.
-//   http://vt100.net/annarbor/aaa-ug/section6.html
-Terminal.prototype.tabClear = function(params) {
-  var param = params[0];
-  if (param <= 0) {
-    delete this.tabs[this.x];
-  } else if (param === 3) {
-    this.tabs = {};
-  }
-};
-
-// CSI Pm i  Media Copy (MC).
-//     Ps = 0  -> Print screen (default).
-//     Ps = 4  -> Turn off printer controller mode.
-//     Ps = 5  -> Turn on printer controller mode.
-// CSI ? Pm i
-//   Media Copy (MC, DEC-specific).
-//     Ps = 1  -> Print line containing cursor.
-//     Ps = 4  -> Turn off autoprint mode.
-//     Ps = 5  -> Turn on autoprint mode.
-//     Ps = 1  0  -> Print composed display, ignores DECPEX.
-//     Ps = 1  1  -> Print all pages.
-Terminal.prototype.mediaCopy = function(params) {
-  ;
-};
-
-// CSI > Ps; Ps m
-//   Set or reset resource-values used by xterm to decide whether
-//   to construct escape sequences holding information about the
-//   modifiers pressed with a given key.  The first parameter iden-
-//   tifies the resource to set/reset.  The second parameter is the
-//   value to assign to the resource.  If the second parameter is
-//   omitted, the resource is reset to its initial value.
-//     Ps = 1  -> modifyCursorKeys.
-//     Ps = 2  -> modifyFunctionKeys.
-//     Ps = 4  -> modifyOtherKeys.
-//   If no parameters are given, all resources are reset to their
-//   initial values.
-Terminal.prototype.setResources = function(params) {
-  ;
-};
-
-// CSI > Ps n
-//   Disable modifiers which may be enabled via the CSI > Ps; Ps m
-//   sequence.  This corresponds to a resource value of "-1", which
-//   cannot be set with the other sequence.  The parameter identi-
-//   fies the resource to be disabled:
-//     Ps = 1  -> modifyCursorKeys.
-//     Ps = 2  -> modifyFunctionKeys.
-//     Ps = 4  -> modifyOtherKeys.
-//   If the parameter is omitted, modifyFunctionKeys is disabled.
-//   When modifyFunctionKeys is disabled, xterm uses the modifier
-//   keys to make an extended sequence of functions rather than
-//   adding a parameter to each function key to denote the modi-
-//   fiers.
-Terminal.prototype.disableModifiers = function(params) {
-  ;
-};
-
-// CSI > Ps p
-//   Set resource value pointerMode.  This is used by xterm to
-//   decide whether to hide the pointer cursor as the user types.
-//   Valid values for the parameter:
-//     Ps = 0  -> never hide the pointer.
-//     Ps = 1  -> hide if the mouse tracking mode is not enabled.
-//     Ps = 2  -> always hide the pointer.  If no parameter is
-//     given, xterm uses the default, which is 1 .
-Terminal.prototype.setPointerMode = function(params) {
-  ;
-};
-
-// CSI ! p   Soft terminal reset (DECSTR).
-// http://vt100.net/docs/vt220-rm/table4-10.html
-Terminal.prototype.softReset = function(params) {
-  this.cursorHidden = false;
-  this.insertMode = false;
-  this.originMode = false;
-  this.wraparoundMode = false; // autowrap
-  this.applicationKeypad = false; // ?
-  this.applicationCursor = false;
-  this.scrollTop = 0;
-  this.scrollBottom = this.rows - 1;
-  this.curAttr = this.defAttr;
-  this.x = this.y = 0; // ?
-  this.charset = null;
-  this.glevel = 0; // ??
-  this.charsets = [null]; // ??
-};
-
-// CSI Ps$ p
-//   Request ANSI mode (DECRQM).  For VT300 and up, reply is
-//     CSI Ps; Pm$ y
-//   where Ps is the mode number as in RM, and Pm is the mode
-//   value:
-//     0 - not recognized
-//     1 - set
-//     2 - reset
-//     3 - permanently set
-//     4 - permanently reset
-Terminal.prototype.requestAnsiMode = function(params) {
-  ;
-};
-
-// CSI ? Ps$ p
-//   Request DEC private mode (DECRQM).  For VT300 and up, reply is
-//     CSI ? Ps; Pm$ p
-//   where Ps is the mode number as in DECSET, Pm is the mode value
-//   as in the ANSI DECRQM.
-Terminal.prototype.requestPrivateMode = function(params) {
-  ;
-};
-
-// CSI Ps ; Ps " p
-//   Set conformance level (DECSCL).  Valid values for the first
-//   parameter:
-//     Ps = 6 1  -> VT100.
-//     Ps = 6 2  -> VT200.
-//     Ps = 6 3  -> VT300.
-//   Valid values for the second parameter:
-//     Ps = 0  -> 8-bit controls.
-//     Ps = 1  -> 7-bit controls (always set for VT100).
-//     Ps = 2  -> 8-bit controls.
-Terminal.prototype.setConformanceLevel = function(params) {
-  ;
-};
-
-// CSI Ps q  Load LEDs (DECLL).
-//     Ps = 0  -> Clear all LEDS (default).
-//     Ps = 1  -> Light Num Lock.
-//     Ps = 2  -> Light Caps Lock.
-//     Ps = 3  -> Light Scroll Lock.
-//     Ps = 2  1  -> Extinguish Num Lock.
-//     Ps = 2  2  -> Extinguish Caps Lock.
-//     Ps = 2  3  -> Extinguish Scroll Lock.
-Terminal.prototype.loadLEDs = function(params) {
-  ;
-};
-
-// CSI Ps SP q
-//   Set cursor style (DECSCUSR, VT520).
-//     Ps = 0  -> blinking block.
-//     Ps = 1  -> blinking block (default).
-//     Ps = 2  -> steady block.
-//     Ps = 3  -> blinking underline.
-//     Ps = 4  -> steady underline.
-Terminal.prototype.setCursorStyle = function(params) {
-  ;
-};
-
-// CSI Ps " q
-//   Select character protection attribute (DECSCA).  Valid values
-//   for the parameter:
-//     Ps = 0  -> DECSED and DECSEL can erase (default).
-//     Ps = 1  -> DECSED and DECSEL cannot erase.
-//     Ps = 2  -> DECSED and DECSEL can erase.
-Terminal.prototype.setCharProtectionAttr = function(params) {
-  ;
-};
-
-// CSI ? Pm r
-//   Restore DEC Private Mode Values.  The value of Ps previously
-//   saved is restored.  Ps values are the same as for DECSET.
-Terminal.prototype.restorePrivateValues = function(params) {
-  ;
-};
-
-// CSI Pt; Pl; Pb; Pr; Ps$ r
-//   Change Attributes in Rectangular Area (DECCARA), VT400 and up.
-//     Pt; Pl; Pb; Pr denotes the rectangle.
-//     Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7.
-// NOTE: xterm doesn't enable this code by default.
-Terminal.prototype.setAttrInRectangle = function(params) {
-  var t = params[0]
-    , l = params[1]
-    , b = params[2]
-    , r = params[3]
-    , attr = params[4];
-
-  var line
-    , i;
-
-  for (; t < b + 1; t++) {
-    line = this.lines[this.ybase + t];
-    for (i = l; i < r; i++) {
-      line[i] = [attr, line[i][1]];
-    }
-  }
-
-  // this.maxRange();
-  this.updateRange(params[0]);
-  this.updateRange(params[2]);
-};
-
-
-// CSI Pc; Pt; Pl; Pb; Pr$ x
-//   Fill Rectangular Area (DECFRA), VT420 and up.
-//     Pc is the character to use.
-//     Pt; Pl; Pb; Pr denotes the rectangle.
-// NOTE: xterm doesn't enable this code by default.
-Terminal.prototype.fillRectangle = function(params) {
-  var ch = params[0]
-    , t = params[1]
-    , l = params[2]
-    , b = params[3]
-    , r = params[4];
-
-  var line
-    , i;
-
-  for (; t < b + 1; t++) {
-    line = this.lines[this.ybase + t];
-    for (i = l; i < r; i++) {
-      line[i] = [line[i][0], String.fromCharCode(ch)];
-    }
-  }
-
-  // this.maxRange();
-  this.updateRange(params[1]);
-  this.updateRange(params[3]);
-};
-
-// CSI Ps ; Pu ' z
-//   Enable Locator Reporting (DECELR).
-//   Valid values for the first parameter:
-//     Ps = 0  -> Locator disabled (default).
-//     Ps = 1  -> Locator enabled.
-//     Ps = 2  -> Locator enabled for one report, then disabled.
-//   The second parameter specifies the coordinate unit for locator
-//   reports.
-//   Valid values for the second parameter:
-//     Pu = 0  <- or omitted -> default to character cells.
-//     Pu = 1  <- device physical pixels.
-//     Pu = 2  <- character cells.
-Terminal.prototype.enableLocatorReporting = function(params) {
-  var val = params[0] > 0;
-  //this.mouseEvents = val;
-  //this.decLocator = val;
-};
-
-// CSI Pt; Pl; Pb; Pr$ z
-//   Erase Rectangular Area (DECERA), VT400 and up.
-//     Pt; Pl; Pb; Pr denotes the rectangle.
-// NOTE: xterm doesn't enable this code by default.
-Terminal.prototype.eraseRectangle = function(params) {
-  var t = params[0]
-    , l = params[1]
-    , b = params[2]
-    , r = params[3];
-
-  var line
-    , i
-    , ch;
-
-  ch = [this.eraseAttr(), ' ']; // xterm?
-
-  for (; t < b + 1; t++) {
-    line = this.lines[this.ybase + t];
-    for (i = l; i < r; i++) {
-      line[i] = ch;
-    }
-  }
-
-  // this.maxRange();
-  this.updateRange(params[0]);
-  this.updateRange(params[2]);
-};
-
-
-// CSI P m SP }
-// Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
-// NOTE: xterm doesn't enable this code by default.
-Terminal.prototype.insertColumns = function() {
-  var param = params[0]
-    , l = this.ybase + this.rows
-    , ch = [this.eraseAttr(), ' '] // xterm?
-    , i;
-
-  while (param--) {
-    for (i = this.ybase; i < l; i++) {
-      this.lines[i].splice(this.x + 1, 0, ch);
-      this.lines[i].pop();
-    }
-  }
-
-  this.maxRange();
-};
-
-// CSI P m SP ~
-// Delete P s Column(s) (default = 1) (DECDC), VT420 and up
-// NOTE: xterm doesn't enable this code by default.
-Terminal.prototype.deleteColumns = function() {
-  var param = params[0]
-    , l = this.ybase + this.rows
-    , ch = [this.eraseAttr(), ' '] // xterm?
-    , i;
-
-  while (param--) {
-    for (i = this.ybase; i < l; i++) {
-      this.lines[i].splice(this.x, 1);
-      this.lines[i].push(ch);
-    }
-  }
+        return;
+      }
 
-  this.maxRange();
-};
+      if (key === '^' || key === '0') {
+        var ox = this.x;
 
+        if (key === '0') {
+          this.x = 0;
+        } else if (key === '^') {
+          var line = this.lines[this.ydisp + this.y];
+          var x = 0;
+          while (x < this.cols) {
+            if (line[x][1] > ' ') {
+              break;
+            }
+            x++;
+          }
+          if (x >= this.cols) x = this.cols - 1;
+          this.x = x;
+        }
 
-Terminal.prototype.copyBuffer = function(lines) {
-  var lines = lines || this.lines
-    , out = [];
+        this.refresh(this.y, this.y);
+        return;
+      }
 
-  for (var y = 0; y < lines.length; y++) {
-    out[y] = [];
-    for (var x = 0; x < lines[y].length; x++) {
-      out[y][x] = [lines[y][x][0], lines[y][x][1]];
-    }
-  }
-
-  return out;
-};
-
-Terminal.prototype.getCopyTextarea = function(text) {
-  var textarea = this._copyTextarea
-    , document = this.document;
-
-  if (!textarea) {
-    textarea = document.createElement('textarea');
-    textarea.style.position = 'absolute';
-    textarea.style.left = '-32000px';
-    textarea.style.top = '-32000px';
-    textarea.style.width = '0px';
-    textarea.style.height = '0px';
-    textarea.style.opacity = '0';
-    textarea.style.backgroundColor = 'transparent';
-    textarea.style.borderStyle = 'none';
-    textarea.style.outlineStyle = 'none';
-
-    document.getElementsByTagName('body')[0].appendChild(textarea);
-
-    this._copyTextarea = textarea;
-  }
-
-  return textarea;
-};
-
-// NOTE: Only works for primary selection on X11.
-// Non-X11 users should use Ctrl-C instead.
-Terminal.prototype.copyText = function(text) {
-  var self = this
-    , textarea = this.getCopyTextarea();
-
-  this.emit('copy', text);
-
-  textarea.focus();
-  textarea.textContent = text;
-  textarea.value = text;
-  textarea.setSelectionRange(0, text.length);
-
-  setTimeout(function() {
-    self.element.focus();
-    self.focus();
-  }, 1);
-};
-
-Terminal.prototype.keyPrefix = function(ev, key) {
-  if (key === 'k' || key === '&') {
-    this.destroy();
-  } else if (key === 'p' || key === ']') {
-    this.emit('request paste');
-  } else if (key === 'c') {
-    this.emit('request create');
-  } else if (key >= '0' && key <= '9') {
-    key = +key - 1;
-    if (!~key) key = 9;
-    this.emit('request term', key);
-  } else if (key === 'n') {
-    this.emit('request term next');
-  } else if (key === 'P') {
-    this.emit('request term previous');
-  } else if (key === ':') {
-    this.emit('request command mode');
-  }
-};
-
-Terminal.prototype.keySelect = function(ev, key) {
-  this.showCursor();
-
-  if (key === '\x04') { // ctrl-d
-    var y = this.ydisp + this.y;
-    if (this.ydisp === this.ybase) {
-      // Mimic vim behavior
-      this.y = Math.min(this.y + (this.rows - 1) / 2 | 0, this.rows - 1);
-      this.refresh(0, this.rows - 1);
-    } else {
-      this.scrollDisp((this.rows - 1) / 2 | 0);
-    }
-    return;
-  }
-
-  if (key === '\x15') { // ctrl-u
-    var y = this.ydisp + this.y;
-    if (this.ydisp === 0) {
-      // Mimic vim behavior
-      this.y = Math.max(this.y - (this.rows - 1) / 2 | 0, 0);
-      this.refresh(0, this.rows - 1);
-    } else {
-      this.scrollDisp(-(this.rows - 1) / 2 | 0);
-    }
-    return;
-  }
-
-  if (key === '\x06') { // ctrl-f
-    var y = this.ydisp + this.y;
-    this.scrollDisp(this.rows - 1);
-    return;
-  }
-
-  if (key === '\x02') { // ctrl-b
-    var y = this.ydisp + this.y;
-    this.scrollDisp(-(this.rows - 1));
-    return;
-  }
-
-  if (key === 'k' || key === '\x1b[A') {
-    var y = this.ydisp + this.y;
-    this.y--;
-    if (this.y < 0) {
-      this.y = 0;
-      this.scrollDisp(-1);
-    }
-    this.refresh(this.y, this.y + 1);
-    return;
-  }
-
-  if (key === 'j' || key === '\x1b[B') {
-    var y = this.ydisp + this.y;
-    this.y++;
-    if (this.y >= this.rows) {
-      this.y = this.rows - 1;
-      this.scrollDisp(1);
-    }
-    this.refresh(this.y - 1, this.y);
-    return;
-  }
-
-  if (key === 'h' || key === '\x1b[D') {
-    var x = this.x;
-    this.x--;
-    if (this.x < 0) {
-      this.x = 0;
-    }
-    this.refresh(this.y, this.y);
-    return;
-  }
-
-  if (key === 'l' || key === '\x1b[C') {
-    var x = this.x;
-    this.x++;
-    if (this.x >= this.cols) {
-      this.x = this.cols - 1;
-    }
-    this.refresh(this.y, this.y);
-    return;
-  }
-
-  if (key === 'w' || key === 'W') {
-    var ox = this.x;
-    var oy = this.y;
-    var oyd = this.ydisp;
-
-    var x = this.x;
-    var y = this.y;
-    var yb = this.ydisp;
-    var saw_space = false;
-
-    for (;;) {
-      var line = this.lines[yb + y];
-      while (x < this.cols) {
-        if (line[x][1] <= ' ') {
-          saw_space = true;
-        } else if (saw_space) {
-          break;
+      if (key === '$') {
+        var ox = this.x;
+        var line = this.lines[this.ydisp + this.y];
+        var x = this.cols - 1;
+        while (x >= 0) {
+          x--;
         }
-        x++;
+        if (x < 0) x = 0;
+        this.x = x;
+        this.refresh(this.y, this.y);
+        return;
       }
-      if (x >= this.cols) x = this.cols - 1;
-      if (x === this.cols - 1 && line[x][1] <= ' ') {
-        x = 0;
-        if (++y >= this.rows) {
-          y--;
-          if (++yb > this.ybase) {
-            yb = this.ybase;
-            x = this.x;
-            break;
-          }
+
+      if (key === 'g' || key === 'G') {
+        var ox = this.x;
+        var oy = this.y;
+        var oyd = this.ydisp;
+        if (key === 'g') {
+          this.x = 0, this.y = 0;
+          this.scrollDisp(-this.ydisp);
+        } else if (key === 'G') {
+          this.x = 0, this.y = this.rows - 1;
+          this.scrollDisp(this.ybase);
         }
-        continue;
+        return;
       }
-      break;
-    }
-
-    this.x = x, this.y = y;
-    this.scrollDisp(-this.ydisp + yb);
 
-    return;
-  }
-
-  if (key === 'b' || key === 'B') {
-    var ox = this.x;
-    var oy = this.y;
-    var oyd = this.ydisp;
-
-    var x = this.x;
-    var y = this.y;
-    var yb = this.ydisp;
-
-    for (;;) {
-      var line = this.lines[yb + y];
-      var saw_space = x > 0 && line[x][1] > ' ' && line[x - 1][1] > ' ';
-      while (x >= 0) {
-        if (line[x][1] <= ' ') {
-          if (saw_space && (x + 1 < this.cols && line[x + 1][1] > ' ')) {
-            x++;
-            break;
-          } else {
-            saw_space = true;
-          }
+      if (key === 'H' || key === 'M' || key === 'L') {
+        var ox = this.x;
+        var oy = this.y;
+        if (key === 'H') {
+          this.x = 0, this.y = 0;
+        } else if (key === 'M') {
+          this.x = 0, this.y = this.rows / 2 | 0;
+        } else if (key === 'L') {
+          this.x = 0, this.y = this.rows - 1;
         }
-        x--;
+        this.refresh(oy, oy);
+        this.refresh(this.y, this.y);
+        return;
       }
-      if (x < 0) x = 0;
-      if (x === 0 && (line[x][1] <= ' ' || !saw_space)) {
-        x = this.cols - 1;
-        if (--y < 0) {
-          y++;
-          if (--yb < 0) {
-            yb++;
-            x = 0;
-            break;
+
+      if (key === '{' || key === '}') {
+        var ox = this.x;
+        var oy = this.y;
+        var oyd = this.ydisp;
+
+        var line;
+        var saw_full = false;
+        var found = false;
+        var first_is_space = -1;
+        var y = this.y + (key === '{' ? -1 : 1);
+        var yb = this.ydisp;
+        var i;
+
+        if (key === '{') {
+          if (y < 0) {
+            y++;
+            if (yb > 0) yb--;
+          }
+        } else if (key === '}') {
+          if (y >= this.rows) {
+            y--;
+            if (yb < this.ybase) yb++;
           }
         }
-        continue;
-      }
-      break;
-    }
 
-    this.x = x, this.y = y;
-    this.scrollDisp(-this.ydisp + yb);
+        for (;;) {
+          line = this.lines[yb + y];
 
-    return;
-  }
+          for (i = 0; i < this.cols; i++) {
+            if (line[i][1] > ' ') {
+              if (first_is_space === -1) {
+                first_is_space = 0;
+              }
+              saw_full = true;
+              break;
+            } else if (i === this.cols - 1) {
+              if (first_is_space === -1) {
+                first_is_space = 1;
+              } else if (first_is_space === 0) {
+                found = true;
+              } else if (first_is_space === 1) {
+                if (saw_full) found = true;
+              }
+              break;
+            }
+          }
 
-  if (key === 'e' || key === 'E') {
-    var x = this.x + 1;
-    var y = this.y;
-    var yb = this.ydisp;
-    if (x >= this.cols) x--;
+          if (found) break;
 
-    for (;;) {
-      var line = this.lines[yb + y];
-      while (x < this.cols) {
-        if (line[x][1] <= ' ') {
-          x++;
-        } else {
-          break;
-        }
-      }
-      while (x < this.cols) {
-        if (line[x][1] <= ' ') {
-          if (x - 1 >= 0 && line[x - 1][1] > ' ') {
-            x--;
-            break;
+          if (key === '{') {
+            y--;
+            if (y < 0) {
+              y++;
+              if (yb > 0) yb--;
+              else break;
+            }
+          } else if (key === '}') {
+            y++;
+            if (y >= this.rows) {
+              y--;
+              if (yb < this.ybase) yb++;
+              else break;
+            }
           }
         }
-        x++;
-      }
-      if (x >= this.cols) x = this.cols - 1;
-      if (x === this.cols - 1 && line[x][1] <= ' ') {
-        x = 0;
-        if (++y >= this.rows) {
-          y--;
-          if (++yb > this.ybase) {
+
+        if (!found) {
+          if (key === '{') {
+            y = 0;
+            yb = 0;
+          } else if (key === '}') {
+            y = this.rows - 1;
             yb = this.ybase;
-            break;
           }
         }
-        continue;
-      }
-      break;
-    }
 
-    this.x = x, this.y = y;
-    this.scrollDisp(-this.ydisp + yb);
+        this.x = 0, this.y = y;
+        this.scrollDisp(-this.ydisp + yb);
 
-    return;
-  }
+        return;
+      }
 
-  if (key === '^' || key === '0') {
-    var ox = this.x;
+      return false;
+    };
 
-    if (key === '0') {
-      this.x = 0;
-    } else if (key === '^') {
-      var line = this.lines[this.ydisp + this.y];
-      var x = 0;
-      while (x < this.cols) {
-        if (line[x][1] > ' ') {
-          break;
-        }
-        x++;
+    /**
+     * Character Sets
+     */
+
+    Terminal.charsets = {};
+
+    // DEC Special Character and Line Drawing Set.
+    // http://vt100.net/docs/vt102-ug/table5-13.html
+    // A lot of curses apps use this if they see TERM=xterm.
+    // testing: echo -e '\e(0a\e(B'
+    // The xterm output sometimes seems to conflict with the
+    // reference above. xterm seems in line with the reference
+    // when running vttest however.
+    // The table below now uses xterm's output from vttest.
+    Terminal.charsets.SCLD = { // (0
+      '`': '\u25c6', // '◆'
+      'a': '\u2592', // '▒'
+      'b': '\u0009', // '\t'
+      'c': '\u000c', // '\f'
+      'd': '\u000d', // '\r'
+      'e': '\u000a', // '\n'
+      'f': '\u00b0', // '°'
+      'g': '\u00b1', // '±'
+      'h': '\u2424', // '\u2424' (NL)
+      'i': '\u000b', // '\v'
+      'j': '\u2518', // '┘'
+      'k': '\u2510', // '┐'
+      'l': '\u250c', // '┌'
+      'm': '\u2514', // '└'
+      'n': '\u253c', // '┼'
+      'o': '\u23ba', // '⎺'
+      'p': '\u23bb', // '⎻'
+      'q': '\u2500', // '─'
+      'r': '\u23bc', // '⎼'
+      's': '\u23bd', // '⎽'
+      't': '\u251c', // '├'
+      'u': '\u2524', // '┤'
+      'v': '\u2534', // '┴'
+      'w': '\u252c', // '┬'
+      'x': '\u2502', // '│'
+      'y': '\u2264', // '≤'
+      'z': '\u2265', // '≥'
+      '{': '\u03c0', // 'π'
+      '|': '\u2260', // '≠'
+      '}': '\u00a3', // '£'
+      '~': '\u00b7'  // '·'
+    };
+
+    Terminal.charsets.UK = null; // (A
+    Terminal.charsets.US = null; // (B (USASCII)
+    Terminal.charsets.Dutch = null; // (4
+    Terminal.charsets.Finnish = null; // (C or (5
+    Terminal.charsets.French = null; // (R
+    Terminal.charsets.FrenchCanadian = null; // (Q
+    Terminal.charsets.German = null; // (K
+    Terminal.charsets.Italian = null; // (Y
+    Terminal.charsets.NorwegianDanish = null; // (E or (6
+    Terminal.charsets.Spanish = null; // (Z
+    Terminal.charsets.Swedish = null; // (H or (7
+    Terminal.charsets.Swiss = null; // (=
+    Terminal.charsets.ISOLatin = null; // /A
+
+    /**
+     * Helpers
+     */
+
+    function on(el, type, handler, capture) {
+      if (!Array.isArray(el)) {
+        el = [el];
       }
-      if (x >= this.cols) x = this.cols - 1;
-      this.x = x;
+      el.forEach(function (element) {
+        element.addEventListener(type, handler, capture || false);
+      });
     }
 
-    this.refresh(this.y, this.y);
-    return;
-  }
-
-  if (key === '$') {
-    var ox = this.x;
-    var line = this.lines[this.ydisp + this.y];
-    var x = this.cols - 1;
-    while (x >= 0) {
-      x--;
-    }
-    if (x < 0) x = 0;
-    this.x = x;
-    this.refresh(this.y, this.y);
-    return;
-  }
-
-  if (key === 'g' || key === 'G') {
-    var ox = this.x;
-    var oy = this.y;
-    var oyd = this.ydisp;
-    if (key === 'g') {
-      this.x = 0, this.y = 0;
-      this.scrollDisp(-this.ydisp);
-    } else if (key === 'G') {
-      this.x = 0, this.y = this.rows - 1;
-      this.scrollDisp(this.ybase);
+    function off(el, type, handler, capture) {
+      el.removeEventListener(type, handler, capture || false);
     }
-    return;
-  }
-
-  if (key === 'H' || key === 'M' || key === 'L') {
-    var ox = this.x;
-    var oy = this.y;
-    if (key === 'H') {
-      this.x = 0, this.y = 0;
-    } else if (key === 'M') {
-      this.x = 0, this.y = this.rows / 2 | 0;
-    } else if (key === 'L') {
-      this.x = 0, this.y = this.rows - 1;
-    }
-    this.refresh(oy, oy);
-    this.refresh(this.y, this.y);
-    return;
-  }
-
-  if (key === '{' || key === '}') {
-    var ox = this.x;
-    var oy = this.y;
-    var oyd = this.ydisp;
-
-    var line;
-    var saw_full = false;
-    var found = false;
-    var first_is_space = -1;
-    var y = this.y + (key === '{' ? -1 : 1);
-    var yb = this.ydisp;
-    var i;
-
-    if (key === '{') {
-      if (y < 0) {
-        y++;
-        if (yb > 0) yb--;
-      }
-    } else if (key === '}') {
-      if (y >= this.rows) {
-        y--;
-        if (yb < this.ybase) yb++;
+
+    function cancel(ev, force) {
+      if (!this.cancelEvents && !force) {
+        return;
       }
+      ev.preventDefault();
+      ev.stopPropagation();
+      return false;
     }
 
-    for (;;) {
-      line = this.lines[yb + y];
-
-      for (i = 0; i < this.cols; i++) {
-        if (line[i][1] > ' ') {
-          if (first_is_space === -1) {
-            first_is_space = 0;
-          }
-          saw_full = true;
-          break;
-        } else if (i === this.cols - 1) {
-          if (first_is_space === -1) {
-            first_is_space = 1;
-          } else if (first_is_space === 0) {
-            found = true;
-          } else if (first_is_space === 1) {
-            if (saw_full) found = true;
-          }
-          break;
-        }
+    function inherits(child, parent) {
+      function f() {
+        this.constructor = child;
       }
+      f.prototype = parent.prototype;
+      child.prototype = new f;
+    }
 
-      if (found) break;
+    // if bold is broken, we can't
+    // use it in the terminal.
+    function isBoldBroken(document) {
+      var body = document.getElementsByTagName('body')[0];
+      var el = document.createElement('span');
+      el.innerHTML = 'hello world';
+      body.appendChild(el);
+      var w1 = el.scrollWidth;
+      el.style.fontWeight = 'bold';
+      var w2 = el.scrollWidth;
+      body.removeChild(el);
+      return w1 !== w2;
+    }
 
-      if (key === '{') {
-        y--;
-        if (y < 0) {
-          y++;
-          if (yb > 0) yb--;
-          else break;
-        }
-      } else if (key === '}') {
-        y++;
-        if (y >= this.rows) {
-          y--;
-          if (yb < this.ybase) yb++;
-          else break;
-        }
+    var String = this.String;
+    var setTimeout = this.setTimeout;
+    var setInterval = this.setInterval;
+
+    function indexOf(obj, el) {
+      var i = obj.length;
+      while (i--) {
+        if (obj[i] === el) return i;
       }
+      return -1;
     }
 
-    if (!found) {
-      if (key === '{') {
-        y = 0;
-        yb = 0;
-      } else if (key === '}') {
-        y = this.rows - 1;
-        yb = this.ybase;
-      }
+    function isWide(ch) {
+      if (ch <= '\uff00') return false;
+      return (ch >= '\uff01' && ch <= '\uffbe')
+          || (ch >= '\uffc2' && ch <= '\uffc7')
+          || (ch >= '\uffca' && ch <= '\uffcf')
+          || (ch >= '\uffd2' && ch <= '\uffd7')
+          || (ch >= '\uffda' && ch <= '\uffdc')
+          || (ch >= '\uffe0' && ch <= '\uffe6')
+          || (ch >= '\uffe8' && ch <= '\uffee');
     }
 
-    this.x = 0, this.y = y;
-    this.scrollDisp(-this.ydisp + yb);
+    function matchColor(r1, g1, b1) {
+      var hash = (r1 << 16) | (g1 << 8) | b1;
+
+      if (matchColor._cache[hash] != null) {
+        return matchColor._cache[hash];
+      }
 
-    return;
-  }
+      var ldiff = Infinity
+        , li = -1
+        , i = 0
+        , c
+        , r2
+        , g2
+        , b2
+        , diff;
+
+      for (; i < Terminal.vcolors.length; i++) {
+        c = Terminal.vcolors[i];
+        r2 = c[0];
+        g2 = c[1];
+        b2 = c[2];
+
+        diff = matchColor.distance(r1, g1, b1, r2, g2, b2);
+
+        if (diff === 0) {
+          li = i;
+          break;
+        }
 
-  return false;
-};
+        if (diff < ldiff) {
+          ldiff = diff;
+          li = i;
+        }
+      }
 
-/**
- * Character Sets
- */
+      return matchColor._cache[hash] = li;
+    }
 
-Terminal.charsets = {};
-
-// DEC Special Character and Line Drawing Set.
-// http://vt100.net/docs/vt102-ug/table5-13.html
-// A lot of curses apps use this if they see TERM=xterm.
-// testing: echo -e '\e(0a\e(B'
-// The xterm output sometimes seems to conflict with the
-// reference above. xterm seems in line with the reference
-// when running vttest however.
-// The table below now uses xterm's output from vttest.
-Terminal.charsets.SCLD = { // (0
-  '`': '\u25c6', // '◆'
-  'a': '\u2592', // '▒'
-  'b': '\u0009', // '\t'
-  'c': '\u000c', // '\f'
-  'd': '\u000d', // '\r'
-  'e': '\u000a', // '\n'
-  'f': '\u00b0', // '°'
-  'g': '\u00b1', // '±'
-  'h': '\u2424', // '\u2424' (NL)
-  'i': '\u000b', // '\v'
-  'j': '\u2518', // '┘'
-  'k': '\u2510', // '┐'
-  'l': '\u250c', // '┌'
-  'm': '\u2514', // '└'
-  'n': '\u253c', // '┼'
-  'o': '\u23ba', // '⎺'
-  'p': '\u23bb', // '⎻'
-  'q': '\u2500', // '─'
-  'r': '\u23bc', // '⎼'
-  's': '\u23bd', // '⎽'
-  't': '\u251c', // '├'
-  'u': '\u2524', // '┤'
-  'v': '\u2534', // '┴'
-  'w': '\u252c', // '┬'
-  'x': '\u2502', // '│'
-  'y': '\u2264', // '≤'
-  'z': '\u2265', // '≥'
-  '{': '\u03c0', // 'π'
-  '|': '\u2260', // '≠'
-  '}': '\u00a3', // '£'
-  '~': '\u00b7'  // '·'
-};
-
-Terminal.charsets.UK = null; // (A
-Terminal.charsets.US = null; // (B (USASCII)
-Terminal.charsets.Dutch = null; // (4
-Terminal.charsets.Finnish = null; // (C or (5
-Terminal.charsets.French = null; // (R
-Terminal.charsets.FrenchCanadian = null; // (Q
-Terminal.charsets.German = null; // (K
-Terminal.charsets.Italian = null; // (Y
-Terminal.charsets.NorwegianDanish = null; // (E or (6
-Terminal.charsets.Spanish = null; // (Z
-Terminal.charsets.Swedish = null; // (H or (7
-Terminal.charsets.Swiss = null; // (=
-Terminal.charsets.ISOLatin = null; // /A
+    matchColor._cache = {};
 
-/**
- * Helpers
- */
+    // http://stackoverflow.com/questions/1633828
+    matchColor.distance = function(r1, g1, b1, r2, g2, b2) {
+      return Math.pow(30 * (r1 - r2), 2)
+        + Math.pow(59 * (g1 - g2), 2)
+        + Math.pow(11 * (b1 - b2), 2);
+    };
 
-function on(el, type, handler, capture) {
-  if (!Array.isArray(el)) {
-    el = [el];
-  }
-  el.forEach(function (element) {
-    element.addEventListener(type, handler, capture || false);
-  });
-}
-
-function off(el, type, handler, capture) {
-  el.removeEventListener(type, handler, capture || false);
-}
-
-function cancel(ev, force) {
-  if (!this.cancelEvents && !force) {
-    return;
-  }
-  ev.preventDefault();
-  ev.stopPropagation();
-  return false;
-}
-
-function inherits(child, parent) {
-  function f() {
-    this.constructor = child;
-  }
-  f.prototype = parent.prototype;
-  child.prototype = new f;
-}
-
-// if bold is broken, we can't
-// use it in the terminal.
-function isBoldBroken(document) {
-  var body = document.getElementsByTagName('body')[0];
-  var el = document.createElement('span');
-  el.innerHTML = 'hello world';
-  body.appendChild(el);
-  var w1 = el.scrollWidth;
-  el.style.fontWeight = 'bold';
-  var w2 = el.scrollWidth;
-  body.removeChild(el);
-  return w1 !== w2;
-}
-
-var String = this.String;
-var setTimeout = this.setTimeout;
-var setInterval = this.setInterval;
-
-function indexOf(obj, el) {
-  var i = obj.length;
-  while (i--) {
-    if (obj[i] === el) return i;
-  }
-  return -1;
-}
-
-function isWide(ch) {
-  if (ch <= '\uff00') return false;
-  return (ch >= '\uff01' && ch <= '\uffbe')
-      || (ch >= '\uffc2' && ch <= '\uffc7')
-      || (ch >= '\uffca' && ch <= '\uffcf')
-      || (ch >= '\uffd2' && ch <= '\uffd7')
-      || (ch >= '\uffda' && ch <= '\uffdc')
-      || (ch >= '\uffe0' && ch <= '\uffe6')
-      || (ch >= '\uffe8' && ch <= '\uffee');
-}
-
-function matchColor(r1, g1, b1) {
-  var hash = (r1 << 16) | (g1 << 8) | b1;
-
-  if (matchColor._cache[hash] != null) {
-    return matchColor._cache[hash];
-  }
-
-  var ldiff = Infinity
-    , li = -1
-    , i = 0
-    , c
-    , r2
-    , g2
-    , b2
-    , diff;
-
-  for (; i < Terminal.vcolors.length; i++) {
-    c = Terminal.vcolors[i];
-    r2 = c[0];
-    g2 = c[1];
-    b2 = c[2];
-
-    diff = matchColor.distance(r1, g1, b1, r2, g2, b2);
-
-    if (diff === 0) {
-      li = i;
-      break;
+    function each(obj, iter, con) {
+      if (obj.forEach) return obj.forEach(iter, con);
+      for (var i = 0; i < obj.length; i++) {
+        iter.call(con, obj[i], i, obj);
+      }
     }
 
-    if (diff < ldiff) {
-      ldiff = diff;
-      li = i;
-    }
-  }
-
-  return matchColor._cache[hash] = li;
-}
-
-matchColor._cache = {};
-
-// http://stackoverflow.com/questions/1633828
-matchColor.distance = function(r1, g1, b1, r2, g2, b2) {
-  return Math.pow(30 * (r1 - r2), 2)
-    + Math.pow(59 * (g1 - g2), 2)
-    + Math.pow(11 * (b1 - b2), 2);
-};
-
-function each(obj, iter, con) {
-  if (obj.forEach) return obj.forEach(iter, con);
-  for (var i = 0; i < obj.length; i++) {
-    iter.call(con, obj[i], i, obj);
-  }
-}
-
-function keys(obj) {
-  if (Object.keys) return Object.keys(obj);
-  var key, keys = [];
-  for (key in obj) {
-    if (Object.prototype.hasOwnProperty.call(obj, key)) {
-      keys.push(key);
+    function keys(obj) {
+      if (Object.keys) return Object.keys(obj);
+      var key, keys = [];
+      for (key in obj) {
+        if (Object.prototype.hasOwnProperty.call(obj, key)) {
+          keys.push(key);
+        }
+      }
+      return keys;
     }
-  }
-  return keys;
-}
 
-/**
- * Expose
- */
+    /**
+     * Expose
+     */
+
+    Terminal.EventEmitter = EventEmitter;
+    Terminal.inherits = inherits;
+    Terminal.on = on;
+    Terminal.off = off;
+    Terminal.cancel = cancel;
 
-Terminal.EventEmitter = EventEmitter;
-Terminal.inherits = inherits;
-Terminal.on = on;
-Terminal.off = off;
-Terminal.cancel = cancel;
-
-if (typeof module !== 'undefined') {
-  module.exports = Terminal;
-} else {
-  this.Terminal = Terminal;
-}
-
-}).call(function() {
-  return this || (typeof window !== 'undefined' ? window : global);
-}());
\ No newline at end of file
+    return Terminal;
+});
\ No newline at end of file