Skip to content
Commits on Apr 28, 2012
  1. fix typo in package.json

    committed Apr 29, 2012
  2. add package.json

    committed Apr 29, 2012
  3. edit package.json

    committed Apr 29, 2012
Commits on Apr 9, 2012
  1. minimize

    committed Apr 9, 2012
Commits on Apr 7, 2012
  1. [from now] 2012/04/07 16:03:29

    committed Apr 7, 2012
    diff --git a/server/app.js b/server/app.js
    index 6f4d8a5..20b7a25 100644
    --- a/server/app.js
    +++ b/server/app.js
    @@ -85,9 +85,10 @@ io.sockets.on('connection', function (socket) {
         io.sockets.emit('counter', viewers);
       }
    
    +  viewers ++;
    +  counter();
    +
       socket.on('create', function () {
    -    viewers ++;
    -    counter();
         socket.emit('snapshot', mterm.snapshot());
       });
       socket.on('data', function (data) {
  2. [from now] 2012/04/07 14:55:41

    committed Apr 7, 2012
    diff --git a/browser/index.html b/browser/index.html
    index 2644320..8b1b468 100644
    --- a/browser/index.html
    +++ b/browser/index.html
    @@ -10,6 +10,7 @@
     </head>
     <body>
       <div id="display">
    +    <div>Viewers: <span id="counter">0</span></div>
       </div>
     </body>
     </html>
    diff --git a/browser/main.js b/browser/main.js
    index de37b1f..6feced3 100644
    --- a/browser/main.js
    +++ b/browser/main.js
    @@ -10,6 +10,12 @@ window.onload = function () {
         socket.emit('create');
       });
    
    +  var counter = document.getElementById('counter');
    +
    +  socket.on('counter', function (num) {
    +    counter.innerText = num;
    +  });
    +
       socket.on('input', function (data) {
         term.write(data);
       });
  3. [from now] 2012/04/07 14:52:43

    committed Apr 7, 2012
    diff --git a/server/app.js b/server/app.js
    index a6dc3c2..6f4d8a5 100644
    --- a/server/app.js
    +++ b/server/app.js
    @@ -10,16 +10,19 @@ var router = new (require('router-line').Router),
     var mterm = new (require('../master_terminal'))(80, 30),
         app,
         io,
    +    viewers = 0,
         controllers = {};
    
     controllers.top = require('./controllers/top');
     controllers.channels = require('./controllers/channels');
    
     // routes
    +/*
     router.GET('/', {
       controller: controllers.top,
       action: 'index'
     });
    +*/
     router.GET('/about', {
       controller: controllers.top,
       action: 'about'
    @@ -78,13 +81,23 @@ io.configure(function () {
     });
    
     io.sockets.on('connection', function (socket) {
    +  function counter () {
    +    io.sockets.emit('counter', viewers);
    +  }
    +
       socket.on('create', function () {
    +    viewers ++;
    +    counter();
         socket.emit('snapshot', mterm.snapshot());
       });
       socket.on('data', function (data) {
         mterm.write(data);
         socket.broadcast.emit('input', data);
       });
    +  socket.on('disconnect', function () {
    +    viewers --;
    +    counter();
    +  });
     });
    
    -app.listen(8124);
    \ No newline at end of file
    +app.listen(process.env.PORT || 80);
    \ No newline at end of file
  4. [from now] 2012/04/07 13:30:51

    committed Apr 7, 2012
    diff --git a/browser/DejaVuSansMono.ttf b/browser/DejaVuSansMono.ttf
    new file mode 100644
    index 0000000..7260bd6
    Binary files /dev/null and b/browser/DejaVuSansMono.ttf differ
    diff --git a/browser/style.css b/browser/style.css
    index ad770ed..e9b70df 100644
    --- a/browser/style.css
    +++ b/browser/style.css
    @@ -1,4 +1,4 @@
    -/**
    +0;95;c/**
      * style.css (https://github.com/chjj/tty.js)
      * Copyright (c) 2012, Christopher Jeffrey (MIT License)
      */
    @@ -30,6 +30,11 @@ html {
       box-shadow: 0px 0px 10px #000;
     }
    
    +@font-face {
    +  font-family: "DejaVu Sans Mono";
    +  src: local("DejaVu Sans Mono"), url("DejaVuSansMono.ttf") format("truetype");
    +}
    +
     .term {
       font-family: "DejaVu Sans Mono", monospace;
       font-size: 11pt;
  5. [from now] 2012/04/07 12:58:17

    committed Apr 7, 2012
    diff --git a/server/controllers/channels.js b/server/controllers/channels.js
    new file mode 100644
    index 0000000..5f10216
    --- /dev/null
    +++ b/server/controllers/channels.js
    @@ -0,0 +1,28 @@
    +var util = require('util'),
    +    BaseController = require('./base');
    +
    +module.exports = (function () {
    +  function ChannelsController () {
    +    BaseController.apply(this, arguments);
    +
    +  }
    +  util.inherits(ChannelsController, BaseController);
    +
    +  ChannelsController.prototype.index = function () {
    +
    +  };
    +
    +  ChannelsController.prototype.create = function () {
    +
    +  };
    +
    +  ChannelsController.prototype.new = function () {
    +
    +  };
    +
    +  ChannelsController.prototype.show = function () {
    +
    +  };
    +
    +  return ChannelsController;
    +})();
    \ No newline at end of file
  6. [from now] 2012/04/07 12:54:41

    committed Apr 7, 2012
    diff --git a/browser/index.html b/browser/index.html
    index 37e10c4..2644320 100644
    --- a/browser/index.html
    +++ b/browser/index.html
    @@ -2,9 +2,14 @@
     <html>
     <head>
     <meta charset="UTF-8">
    -<title>live term test</title>
    +<title>liveterm</title>
    +<link href="./style.css" rel="stylesheet" type="text/css">
    +<script src="./socket.io/socket.io.js"></script>
    +<script src="./term.js"></script>
    +<script src="./main.js"></script>
     </head>
     <body>
    -<iframe src="liveterm.html" width="600" height="400" style="border: none;"></iframe>
    +  <div id="display">
    +  </div>
     </body>
     </html>
    diff --git a/browser/liveterm.html b/browser/liveterm.html
    deleted file mode 100644
    index 150ee77..0000000
    --- a/browser/liveterm.html
    +++ /dev/null
    @@ -1,14 +0,0 @@
    -<!DOCTYPE html>
    -<html>
    -<head>
    -<meta charset="UTF-8">
    -<title>liveterm</title>
    -<link href="./style.css" rel="stylesheet" type="text/css">
    -<script src="./socket.io/socket.io.js"></script>
    -<script src="./term.js"></script>
    -<script src="./main.js"></script>
    -</head>
    -<body>
    -
    -</body>
    -</html>
    diff --git a/browser/main.js b/browser/main.js
    index 6588cd8..de37b1f 100644
    --- a/browser/main.js
    +++ b/browser/main.js
    @@ -1,21 +1,23 @@
     var noop = function () { /* do nothing */ };
    
    -var socket = io.connect();
    +window.onload = function () {
    +  var socket = io.connect();
    
    -var term = new Terminal(80, 30, noop);
    +  var term = new Terminal(80, 30, noop);
    
    -socket.on('connect', function () {
    -  term.open();
    -  socket.emit('create');
    -});
    +  socket.on('connect', function () {
    +    term.open();
    +    socket.emit('create');
    +  });
    
    -socket.on('input', function (data) {
    -  term.write(data);
    -});
    +  socket.on('input', function (data) {
    +    term.write(data);
    +  });
    
    -socket.on('snapshot', function (data) {
    -  for (var prop in data) {
    -    term[prop] = data[prop];
    -  }
    -  term.refresh(0, 29);
    -});
    \ No newline at end of file
    +  socket.on('snapshot', function (data) {
    +    for (var prop in data) {
    +      term[prop] = data[prop];
    +    }
    +    term.refresh(0, 29);
    +  });
    +};
    \ No newline at end of file
    diff --git a/browser/style.css b/browser/style.css
    index a25d596..ad770ed 100644
    --- a/browser/style.css
    +++ b/browser/style.css
    @@ -18,9 +18,21 @@ html {
       background: #fff;
     }
    
    +#display {
    +  position: absolute;
    +  top: 24px;
    +  left: 24px;
    +}
    +
    +.terminal {
    +  position: absolute;
    +  border: white 1px solid;
    +  box-shadow: 0px 0px 10px #000;
    +}
    +
     .term {
       font-family: "DejaVu Sans Mono", monospace;
    -  font-size: 11px;
    +  font-size: 11pt;
       color: #f0f0f0;
       background: #000;
     }
    diff --git a/browser/term.js b/browser/term.js
    index 1d3cb06..5ea241e 100644
    --- a/browser/term.js
    +++ b/browser/term.js
    @@ -128,7 +128,8 @@ Terminal.prototype.open = function() {
         this.children.push(div);
       }
    
    -  document.body.appendChild(this.element);
    +  var display = document.getElementById('display');
    +  display.appendChild(this.element);
    
       this.refresh(0, this.rows - 1);
    
    diff --git a/caster/index.js b/caster/index.js
    index 1bca874..bb16877 100644
    --- a/caster/index.js
    +++ b/caster/index.js
    @@ -2,7 +2,8 @@ var tty = require('tty'),
         pty = require('pty.js'),
         io = require('socket.io-client');
    
    -var socket = io.connect('http://localhost:8124'),
    +var url = process.env.DEVELOP ? 'http://localhost:8124' : 'http://pronama14.koba789.com';,
    +    socket = io.connect(url),
         term = pty.spawn(process.env.SHELL, [], {
           name: 'xterm',
           cols: 80,
    @@ -15,7 +16,6 @@ term.on('data', function(data) {
       console.log(data);
     });
     */
    -
     process.stdin.resume();
     tty.setRawMode(true);
    
    diff --git a/server/app.js b/server/app.js
    index c389e27..a6dc3c2 100644
    --- a/server/app.js
    +++ b/server/app.js
    @@ -10,11 +10,10 @@ var router = new (require('router-line').Router),
     var mterm = new (require('../master_terminal'))(80, 30),
         app,
         io,
    -    controllers;
    +    controllers = {};
    
    -function isObject(obj) {
    -  return typeof obj === 'object' && obj !== null;
    -}
    +controllers.top = require('./controllers/top');
    +controllers.channels = require('./controllers/channels');
    
     // routes
     router.GET('/', {
    @@ -38,26 +37,26 @@ router.GET('/auth/:provider/callback', {
      */
    
     router.GET('/channels', {
    -  controller: controllers.terms,
    +  controller: controllers.channels,
       action: 'index'
     });
     router.POST('/channels', {
    -  controller: controllers.terms,
    +  controller: controllers.channels,
       action: 'create'
     });
     router.GET('/channels/new', {
    -  controller: controllers.terms,
    +  controller: controllers.channels,
       action: 'new'
     });
     router.GET('/channels/:id', {
    -  controller: controllers.terms,
    +  controller: controllers.channels,
       action: 'show'
     });
    
     app = http.createServer(function (req, res) {
       var reqUrl = url.parse(req.url);
       var target = router.route(req.method.toUpperCase(),
    -                             reqUrl.pathname);
    +                            reqUrl.pathname);
       if (target === undefined) {
         req.on('end', function () {
           fileServer.serve(req, res);
    @@ -74,6 +73,10 @@ mterm.open();
    
     io = socketio.listen(app);
    
    +io.configure(function () {
    +  io.disable('log');
    +});
    +
     io.sockets.on('connection', function (socket) {
       socket.on('create', function () {
         socket.emit('snapshot', mterm.snapshot());
    diff --git a/server/controllers/base.js b/server/controllers/base.js
    index dd9c877..da6e095 100644
    --- a/server/controllers/base.js
    +++ b/server/controllers/base.js
    @@ -1,3 +1,9 @@
    +var url = require('url');
    +
    +function isObject(obj) {
    +  return typeof obj === 'object' && obj !== null;
    +}
    +
     module.exports = (function () {
       function BaseController(req, res, params) {
         this.request = req;
    diff --git a/server/controllers/top.js b/server/controllers/top.js
    index 0a36448..9da4fd7 100644
    --- a/server/controllers/top.js
    +++ b/server/controllers/top.js
    @@ -1,19 +1,31 @@
     var util = require('util'),
    -    BaseController = require('./base');
    +    fs = require('fs'),
    +    path = require('path'),
    +    hogan = require('hogan.js'),
    +    BaseController = require('./base'),
    +    templates = {},
    +    basepath = function (filename) {
    +      return path.join(__dirname, '../../browser/', filename);
    +    };
    +
    +templates.index = '';
    +fs.readFile(basepath('index.html'), 'utf-8', function (err, data) {
    +  templates.index = data;
    +});
    
     module.exports = (function () {
       function TopController () {
    -    BaseController.call(this);
    -
    +    BaseController.apply(this, arguments);
       }
       util.inherits(TopController, BaseController);
    
       TopController.prototype.index = function () {
    -
    +    this.response.end(templates.index);
       };
    
       TopController.prototype.about = function () {
    -
    +
       };
    
    +  return TopController;
     })();
    \ No newline at end of file
  7. [from now] 2012/04/07 10:17:21

    committed Apr 7, 2012
    diff --git a/server/app.js b/server/app.js
    index f5c01c6..c389e27 100644
    --- a/server/app.js
    +++ b/server/app.js
    @@ -5,34 +5,54 @@ var http = require('http'),
    
     var router = new (require('router-line').Router),
         socketio = require('socket.io'),
    -    fileServer = new(require('node-static').Server)(path.join(process.cwd(), '/../browser'));
    +    fileServer = new(require('node-static').Server)(path.join(__dirname, '/../browser'));
    
     var mterm = new (require('../master_terminal'))(80, 30),
         app,
         io,
    -    handlers = {};
    -
    -mterm.open();
    +    controllers;
    
     function isObject(obj) {
       return typeof obj === 'object' && obj !== null;
     }
    
    +// routes
    +router.GET('/', {
    +  controller: controllers.top,
    +  action: 'index'
    +});
    +router.GET('/about', {
    +  controller: controllers.top,
    +  action: 'about'
    +});
    +
     /*
    - * Routing Table
    +router.GET('/auth/:provider', {
    +  controller: controllers.auth,
    +  action: 'show'
    +});
    +router.GET('/auth/:provider/callback', {
    +  controller: controllers.auth,
    +  action: 'create'
    +});
      */
    
    -function Helper(req, res, params) {
    -  this.request = req;
    -  this.response = res;
    -  this.params = params;
    -  var parsedUrl = url.parse(this.request.url, true);
    -  if (isObject(parsedUrl) && isObject(parsedUrl.query)) {
    -    for (var key in parsedUrl.query) {
    -      this.params[key] = parsedUrl.query[key];
    -    }
    -  }
    -}
    +router.GET('/channels', {
    +  controller: controllers.terms,
    +  action: 'index'
    +});
    +router.POST('/channels', {
    +  controller: controllers.terms,
    +  action: 'create'
    +});
    +router.GET('/channels/new', {
    +  controller: controllers.terms,
    +  action: 'new'
    +});
    +router.GET('/channels/:id', {
    +  controller: controllers.terms,
    +  action: 'show'
    +});
    
     app = http.createServer(function (req, res) {
       var reqUrl = url.parse(req.url);
    @@ -43,11 +63,15 @@ app = http.createServer(function (req, res) {
           fileServer.serve(req, res);
         });
       } else {
    -    var handler = new Helper(req, res, target.params);
    -    target.value.call(handler);
    +    var Controller = target.value.controller;
    +    var action = target.value.action;
    +    var controller = new Controller(req, res, target.params);
    +    controller[action]();
       }
     });
    
    +mterm.open();
    +
     io = socketio.listen(app);
    
     io.sockets.on('connection', function (socket) {
    diff --git a/server/controllers/auth.js b/server/controllers/auth.js
    new file mode 100644
    index 0000000..e69de29
    diff --git a/server/controllers/base.js b/server/controllers/base.js
    new file mode 100644
    index 0000000..dd9c877
    --- /dev/null
    +++ b/server/controllers/base.js
    @@ -0,0 +1,15 @@
    +module.exports = (function () {
    +  function BaseController(req, res, params) {
    +    this.request = req;
    +    this.response = res;
    +    this.params = params;
    +    var parsedUrl = url.parse(this.request.url, true);
    +    if (isObject(parsedUrl) && isObject(parsedUrl.query)) {
    +      for (var key in parsedUrl.query) {
    +        this.params[key] = parsedUrl.query[key];
    +      }
    +    }
    +  }
    +
    +  return BaseController;
    +})();
    \ No newline at end of file
    diff --git a/server/controllers/top.js b/server/controllers/top.js
    new file mode 100644
    index 0000000..0a36448
    --- /dev/null
    +++ b/server/controllers/top.js
    @@ -0,0 +1,19 @@
    +var util = require('util'),
    +    BaseController = require('./base');
    +
    +module.exports = (function () {
    +  function TopController () {
    +    BaseController.call(this);
    +
    +  }
    +  util.inherits(TopController, BaseController);
    +
    +  TopController.prototype.index = function () {
    +
    +  };
    +
    +  TopController.prototype.about = function () {
    +
    +  };
    +
    +})();
    \ No newline at end of file
Commits on Apr 5, 2012
  1. [from now] 2012/04/05 23:08:20

    committed Apr 5, 2012
    diff --git a/browser/main.js b/browser/main.js
    index cc36f55..6588cd8 100644
    --- a/browser/main.js
    +++ b/browser/main.js
    @@ -17,4 +17,5 @@ socket.on('snapshot', function (data) {
       for (var prop in data) {
         term[prop] = data[prop];
       }
    +  term.refresh(0, 29);
     });
    \ No newline at end of file
    diff --git a/browser/term.js b/browser/term.js
    index 7423080..1d3cb06 100644
    --- a/browser/term.js
    +++ b/browser/term.js
    @@ -150,7 +150,8 @@ Terminal.prototype.refresh = function(start, end) {
         , defAttr
         , fgColor
         , bgColor
    -    , row;
    +    , row
    +    , isInverse;
    
       for (y = start; y <= end; y++) {
         row = y + this.ydisp;
    @@ -187,14 +188,19 @@ Terminal.prototype.refresh = function(start, end) {
                 out += '<span style="';
                 fgColor = (data >> 3) & 7;
                 bgColor = data & 7;
    -            if (fgColor !== 7) {
    +            isInverse = (data >> 8) & 8;
    +            if (fgColor !== 7 || isInverse) {
                   out += 'color:'
    -                + this.fgColors[fgColor]
    +                + (!isInverse
    +                   ? this.fgColors[fgColor]
    +                   : this.bgColors[bgColor])
                     + ';';
                 }
    -            if (bgColor !== 0) {
    +            if (bgColor !== 0 || isInverse) {
                   out += 'background-color:'
    -                + this.bgColors[bgColor]
    +                + (isInverse
    +                   ? this.fgColors[fgColor]
    +                   : this.bgColors[bgColor])
                     + ';';
                 }
                 if ((data >> 8) & 1) {
    @@ -1640,12 +1646,18 @@ Terminal.prototype.charAttributes = function(params) {
           } else if (p === 4) {
             // underlined text
             this.curAttr = this.curAttr | (4 << 8);
    +      } else if (p === 7) {
    +        // inverse text
    +        this.curAttr = this.curAttr | (8 << 8);
           } else if (p === 22) {
             // not bold
             this.curAttr = this.curAttr & ~(1 << 8);
           } else if (p === 24) {
             // not underlined
             this.curAttr = this.curAttr & ~(4 << 8);
    +      } else if (p === 27) {
    +        // not inverse text
    +        this.curAttr = this.curAttr & ~(8 << 8);
           } else if (p === 39) {
             // reset fg
             p = this.curAttr & 7;
    diff --git a/master_terminal/term.js b/master_terminal/term.js
    index c16206b..e0dbb4e 100644
    --- a/master_terminal/term.js
    +++ b/master_terminal/term.js
    @@ -152,7 +152,8 @@ Terminal.prototype.refresh = function(start, end) {
         , defAttr
         , fgColor
         , bgColor
    -    , row;
    +    , row
    +    , isInverse;
    
       for (y = start; y <= end; y++) {
         row = y + this.ydisp;
    @@ -189,14 +190,19 @@ Terminal.prototype.refresh = function(start, end) {
                 out += '<span style="';
                 fgColor = (data >> 3) & 7;
                 bgColor = data & 7;
    -            if (fgColor !== 7) {
    +            isInverse = (data >> 8) & 8;
    +            if (fgColor !== 7 || isInverse) {
                   out += 'color:'
    -                + this.fgColors[fgColor]
    +                + (!isInverse
    +                   ? this.fgColors[fgColor]
    +                   : this.bgColors[bgColor])
                     + ';';
                 }
    -            if (bgColor !== 0) {
    +            if (bgColor !== 0 || isInverse) {
                   out += 'background-color:'
    -                + this.bgColors[bgColor]
    +                + (isInverse
    +                   ? this.fgColors[fgColor]
    +                   : this.bgColors[bgColor])
                     + ';';
                 }
                 if ((data >> 8) & 1) {
    @@ -1642,12 +1648,18 @@ Terminal.prototype.charAttributes = function(params) {
           } else if (p === 4) {
             // underlined text
             this.curAttr = this.curAttr | (4 << 8);
    +      } else if (p === 7) {
    +        // inverse text
    +        this.curAttr = this.curAttr | (8 << 8);
           } else if (p === 22) {
             // not bold
             this.curAttr = this.curAttr & ~(1 << 8);
           } else if (p === 24) {
             // not underlined
             this.curAttr = this.curAttr & ~(4 << 8);
    +      } else if (p === 27) {
    +        // not inverse text
    +        this.curAttr = this.curAttr & ~(8 << 8);
           } else if (p === 39) {
             // reset fg
             p = this.curAttr & 7;
    diff --git a/server/app.js b/server/app.js
    index c820649..f5c01c6 100644
    --- a/server/app.js
    +++ b/server/app.js
    @@ -12,6 +12,8 @@ var mterm = new (require('../master_terminal'))(80, 30),
         io,
         handlers = {};
    
    +mterm.open();
    +
     function isObject(obj) {
       return typeof obj === 'object' && obj !== null;
     }
  2. [from now] 2012/04/05 15:32:51

    committed Apr 5, 2012
    diff --git a/master_terminal/index.js b/master_terminal/index.js
    index 7086db0..c76e56e 100644
    --- a/master_terminal/index.js
    +++ b/master_terminal/index.js
    @@ -9,7 +9,12 @@ module.exports = (function () {
       util.inherits(MasterTerminal, Terminal);
    
       MasterTerminal.prototype.snapshot = function () {
    -    return this;
    +    var result = {};
    +    var copyProps = ['cols', 'rows', 'scrollback', 'ybase', 'ydisp', 'x', 'y', 'cursorState', 'cursorHidden', 'convertEol', 'state', 'outputQueue', 'scrollTop', 'scrollBottom', 'applicationKeypad', 'originMode', 'insertMode', 'wraparoundMode', 'tabs', 'charset', 'normal', 'bgColors', 'fgColors', 'defAttr', 'curAttr', 'keyState', 'keyStr', 'params', 'currentParam', 'lines'];
    +    for (var i = 0; i < copyProps.length; ++ i) {
    +      result[copyProps[i]] = this[copyProps[i]];
    +    }
    +    return result;
       };
    
       return MasterTerminal;
    diff --git a/master_terminal/term.js b/master_terminal/term.js
    index 475eeb6..c16206b 100644
    --- a/master_terminal/term.js
    +++ b/master_terminal/term.js
    @@ -13,81 +13,66 @@
      * The original design remains. The terminal itself
      * has been extended to include xterm CSI codes, among
      * other features.
    - */
    +*/
    
     ;(function() {
    +  var jsdom    = require("jsdom").jsdom,
    +      document = jsdom("<html><head></head><body></body></html>"),
    +      window   = document.createWindow();
    +/**
    + * 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
    + */
    
    -  /**
    -   * 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';
    +'use strict';
    
    -  /**
    -   * States
    -   */
    +/**
    + * States
    + */
    
    -  var normal = 0
    +var normal = 0
       , escaped = 1
       , csi = 2
       , osc = 3
       , charset = 4;
    
    -  /**
    -   * Terminal
    -   */
    -
    -  var Terminal = function(cols, rows) {
    -    this.cols = cols;
    -    this.rows = rows;
    -    this.ybase = 0;
    -    this.ydisp = 0;
    -    this.x = 0;
    -    this.y = 0;
    -    this.cursorState = 0;
    -    this.cursorHidden = false;
    -    this.convertEol = false;
    -    this.state = 0;
    -    this.scrollTop = 0;
    -    this.scrollBottom = this.rows - 1;
    -
    -    this.applicationKeypad = false;
    -    this.originMode = false;
    -    this.insertMode = false;
    -    this.wraparoundMode = false;
    -    this.mouseEvents;
    -    this.tabs = [];
    -    this.charset = null;
    -    this.normal = null;
    -
    -    this.defAttr = 16 | (17 << 5);
    -    this.curAttr = this.defAttr;
    -    this.keyState = 0;
    -    this.keyStr = '';
    -
    -    this.params = [];
    -    this.currentParam = 0;
    -
    -    var i = this.rows - 1;
    -    this.lines = [ this.blankLine() ];
    -    while (i--) {
    -      this.lines.push(this.lines[0].slice());
    -    }
    -  };
    -
    -  /**
    -   * Options
    -   */
    +/**
    + * Terminal
    + */
    
    -  Terminal.colors = [
    -    // dark:
    +var Terminal = function(cols, rows, handler) {
    +  this.cols = cols;
    +  this.rows = rows;
    +  this.handler = handler;
    +  this.scrollback = 1000;
    +  this.ybase = 0;
    +  this.ydisp = 0;
    +  this.x = 0;
    +  this.y = 0;
    +  this.cursorState = 0;
    +  this.cursorHidden = false;
    +  this.convertEol = false;
    +  this.state = 0;
    +  this.outputQueue = '';
    +  this.scrollTop = 0;
    +  this.scrollBottom = this.rows - 1;
    +
    +  this.applicationKeypad = false;
    +  this.originMode = false;
    +  this.insertMode = false;
    +  this.wraparoundMode = false;
    +  this.mouseEvents;
    +  this.tabs = [];
    +  this.charset = null;
    +  this.normal = null;
    +
    +  this.bgColors = [
         '#2e3436',
         '#cc0000',
         '#4e9a06',
    @@ -95,8 +80,10 @@
         '#3465a4',
         '#75507b',
         '#06989a',
    -    '#d3d7cf',
    -    // bright:
    +    '#d3d7cf'
    +  ];
    +
    +  this.fgColors = [
         '#555753',
         '#ef2929',
         '#8ae234',
    @@ -104,60 +91,57 @@
         '#729fcf',
         '#ad7fa8',
         '#34e2e2',
    -    '#eeeeec',
    -    // default bg/fg:
    -    '#000000',
    -    '#f0f0f0'
    +    '#eeeeec'
       ];
    
    -  Terminal.termName = '';
    -  Terminal.geometry = [80, 30];
    -  Terminal.cursorBlink = true;
    -  Terminal.visualBell = false;
    -  Terminal.popOnBell = false;
    -  Terminal.scrollback = 1000;
    -  Terminal.screenKeys = false;
    -
    -  /**
    -   * Focused Terminal
    -   */
    -
    -  Terminal.focus = null;
    -
    -  Terminal.prototype.focus = function() {
    -    if (Terminal.focus === this) return;
    -    if (Terminal.focus) {
    -      Terminal.focus.cursorState = 0;
    -      Terminal.focus.refresh(Terminal.focus.y, Terminal.focus.y);
    -    }
    -    Terminal.focus = this;
    -    this.showCursor();
    -  };
    +  this.defAttr = (7 << 3) | 0;
    +  this.curAttr = this.defAttr;
    +  this.isMac = ~window.navigator.userAgent.indexOf('Mac');
    +  this.keyState = 0;
    +  this.keyStr = '';
    
    -  /**
    -   * Open Terminal
    -   */
    +  this.params = [];
    +  this.currentParam = 0;
    +
    +  var i = this.rows - 1;
    +  this.lines = [ this.blankLine() ];
    +  while (i--) {
    +    this.lines.push(this.lines[0].slice());
    +  }
    +};
    
    -  Terminal.prototype.open = function() {
    -    this.refresh(0, this.rows - 1);
    +/**
    + * Open Terminal
    + */
    +
    +Terminal.prototype.open = function() {
    +  var self = this
    +    , i = 0
    +    , div;
    +
    +  this.element = document.createElement('div');
    +  this.element.className = 'terminal';
    +  this.children = [];
    +
    +  for (; i < this.rows; i++) {
    +    div = document.createElement('div');
    +    div.className = 'term';
    +    this.element.appendChild(div);
    +    this.children.push(div);
    +  }
    
    -    this.focus();
    -  };
    +  document.body.appendChild(this.element);
    
    -  /**
    -   * Rendering Engine
    -   */
    +  this.refresh(0, this.rows - 1);
    
    -  // In the screen buffer, each character
    -  // is stored as a 32-bit integer.
    -  // First 16 bits: a utf-16 character.
    -  // Next 5 bits: background color (0-31).
    -  // Next 5 bits: foreground color (0-31).
    -  // Next 6 bits: a mask for misc. flags:
    -  //   1=bold, 2=underline, 4=inverse
    +  setInterval(function() {
    +    self.cursorBlink();
    +  }, 500);
    +};
    
    -  Terminal.prototype.refresh = function(start, end) {
    -    var x
    +Terminal.prototype.refresh = function(start, end) {
    +  var element
    +    , x
         , y
         , i
         , line
    @@ -165,81 +149,68 @@
         , ch
         , width
         , data
    -    , attr
    +    , defAttr
         , fgColor
         , bgColor
    -    , flags
    -    , row
    -    , parent;
    +    , row;
    +
    +  for (y = start; y <= end; y++) {
    +    row = y + this.ydisp;
    
    +    line = this.lines[row];
    +    out = '';
         width = this.cols;
    
    -    for (y = start; y <= end; y++) {
    -      row = y + this.ydisp;
    +    if (y === this.y
    +        && this.cursorState
    +        && this.ydisp === this.ybase) {
    +      x = this.x;
    +    } else {
    +      x = -1;
    +    }
    
    -      line = this.lines[row];
    -      out = '';
    +    defAttr = this.defAttr;
    
    -      if (y === this.y
    -          && this.cursorState
    -          && this.ydisp === this.ybase
    -          && !this.cursorHidden) {
    -        x = this.x;
    -      } else {
    -        x = -1;
    +    for (i = 0; i < width; i++) {
    +      ch = line[i];
    +      data = ch >> 16;
    +      ch &= 0xffff;
    +      if (i === x) {
    +        data = -1;
           }
    
    -      attr = this.defAttr;
    -
    -      for (i = 0; i < width; i++) {
    -        ch = line[i];
    -        data = ch >> 16;
    -        ch &= 0xffff;
    -        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">';
    -            } else {
    -              out += '<span style="';
    -              fgColor = (data >> 5) & 31;
    -              bgColor = data & 31;
    -              flags = data >> 10;
    -
    -              if (flags & 1) {
    -                out += 'font-weight:bold;';
    -                // see: XTerm*boldColors
    -                if (fgColor < 8) fgColor += 8;
    -              }
    -
    -              if (flags & 2) {
    -                out += 'text-decoration:underline;';
    -              }
    -
    -              if (fgColor !== 17) {
    -                out += 'color:'
    -                  + Terminal.colors[fgColor]
    -                  + ';';
    -              }
    -
    -              if (bgColor !== 16) {
    -                out += 'background-color:'
    -                  + Terminal.colors[bgColor]
    -                  + ';';
    -              }
    -
    -              out += '">';
    +      if (data !== defAttr) {
    +        if (defAttr !== this.defAttr)
    +          out += '</span>';
    +        if (data !== this.defAttr) {
    +          if (data === -1) {
    +            out += '<span class="termReverse">';
    +          } else {
    +            out += '<span style="';
    +            fgColor = (data >> 3) & 7;
    +            bgColor = data & 7;
    +            if (fgColor !== 7) {
    +              out += 'color:'
    +                + this.fgColors[fgColor]
    +                + ';';
    +            }
    +            if (bgColor !== 0) {
    +              out += 'background-color:'
    +                + this.bgColors[bgColor]
    +                + ';';
                 }
    +            if ((data >> 8) & 1) {
    +              out += 'font-weight:bold;';
    +            }
    +            if ((data >> 8) & 4) {
    +              out += 'text-decoration:underline;';
    +            }
    +            out += '">';
               }
             }
    +      }
    
    -        switch (ch) {
    +      switch (ch) {
             case 32:
               out += '&nbsp;';
               break;
    @@ -259,325 +230,307 @@
                 out += String.fromCharCode(ch);
               }
               break;
    -        }
    -
    -        attr = data;
           }
    
    -      if (attr !== this.defAttr) {
    -        out += '</span>';
    -      }
    +      defAttr = data;
         }
    -  };
    
    -  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 (!Terminal.cursorBlink) return;
    -    var self = this;
    -    this._blinker = function() {
    -      self.cursorBlink();
    -    };
    -    this._blink = setInterval(this._blinker, 500);
    -  };
    -
    -  Terminal.prototype.refreshBlink = function() {
    -    if (!Terminal.cursorBlink) return;
    -    clearInterval(this._blink);
    -    this._blink = setInterval(this._blinker, 500);
    -  };
    -
    -  Terminal.prototype.scroll = function() {
    -    var row;
    -
    -    if (++this.ybase === Terminal.scrollback) {
    -      this.ybase = this.ybase / 2 | 0;
    -      this.lines = this.lines.slice(-(this.ybase + this.rows) + 1);
    +    if (defAttr !== this.defAttr) {
    +      out += '</span>';
         }
    
    -    this.ydisp = this.ybase;
    +    element = this.children[y];
    +    element.innerHTML = out;
    +  }
    +};
    
    -    // last line
    -    row = this.ybase + this.rows - 1;
    +Terminal.prototype.cursorBlink = function() {
    +  this.cursorState ^= 1;
    +  this.refresh(this.y, this.y);
    +};
    
    -    // subtract the bottom scroll region
    -    row -= this.rows - 1 - this.scrollBottom;
    +Terminal.prototype.showCursor = function() {
    +  if (!this.cursorState) {
    +    this.cursorState = 1;
    +    this.refresh(this.y, this.y);
    +  }
    +};
    
    -    // potential optimization
    -    // if (row === this.lines.length) {
    -    //   this.lines.push(this.blankLine());
    -    // } else
    +Terminal.prototype.scroll = function() {
    +  var row;
    
    -    // add our new line
    -    this.lines.splice(row, 0, this.blankLine());
    +  // maybe check this.lines.length ?
    +  if (++this.ybase === this.scrollback) {
    +    this.ybase = 0;
    +    this.ydisp = 0; // always reset disp to zero
    +    this.lines = this.lines.slice(-this.rows + 1);
    +  }
    
    -    if (this.scrollTop !== 0) {
    -      if (this.ybase !== 0) {
    -        this.ybase--;
    -        this.ydisp = this.ybase;
    -      }
    -      this.lines.splice(this.ybase + this.scrollTop, 1);
    -    }
    -  };
    +  // if (this.scrollTtyOutput)
    +  this.ydisp = this.ybase;
    
    -  Terminal.prototype.scrollDisp = function(disp) {
    -    this.ydisp += disp;
    +  // last line
    +  row = this.ybase + this.rows - 1;
    
    -    if (this.ydisp > this.ybase) {
    +  // subtract the bottom scroll region
    +  row -= this.rows - 1 - this.scrollBottom;
    +
    +  // 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;
    -    } else if (this.ydisp < 0) {
    -      this.ydisp = 0;
         }
    +    this.lines.splice(this.ybase + this.scrollTop, 1);
    +  }
    +};
    +
    +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);
    -  };
    +  this.refresh(0, this.rows - 1);
    +};
    
    -  Terminal.prototype.write = function(str) {
    -    // console.log(JSON.stringify(str.replace(/\x1b/g, '^[')));
    +Terminal.prototype.write = function(str) {
    +  // console.log(JSON.stringify(str.replace(/\x1b/g, '^[')));
    
    -    var l = str.length
    +  var l = str.length
         , i = 0
         , ch
         , param
         , row;
    
    -    this.refreshStart = this.rows;
    -    this.refreshEnd = -1;
    -    this.getRows(this.y);
    +  this.refreshStart = this.rows;
    +  this.refreshEnd = -1;
    +  this.getRows(this.y);
    
    -    if (this.ybase !== this.ydisp) {
    -      this.ydisp = this.ybase;
    -      this.refreshStart = 0;
    -      this.refreshEnd = this.rows - 1;
    -    }
    +  if (this.ybase !== this.ydisp) {
    +    this.ydisp = this.ybase;
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  }
    
    -    for (; i < l; i++) {
    -      ch = str.charCodeAt(i);
    -      switch (this.state) {
    +  for (; i < l; i++) {
    +    ch = str.charCodeAt(i);
    +    switch (this.state) {
           case normal:
             switch (ch) {
               // '\0'
    -        case 0:
    -          break;
    +          case 0:
    +            break;
    
               // '\a'
    -        case 7:
    -          this.bell();
    -          break;
    +          case 7:
    +            this.bell();
    +            break;
    
               // '\n', '\v', '\f'
    -        case 10:
    -        case 11:
    -        case 12:
    -          if (this.convertEol) {
    -            this.x = 0;
    -          }
    -          this.y++;
    -          if (this.y >= this.scrollBottom + 1) {
    -            this.y--;
    -            this.scroll();
    -            this.refreshStart = 0;
    -            this.refreshEnd = this.rows - 1;
    -          }
    -          break;
    +          case 10:
    +          case 11:
    +          case 12:
    +            if (this.convertEol) {
    +              this.x = 0;
    +            }
    +            this.y++;
    +            if (this.y >= this.scrollBottom + 1) {
    +              this.y--;
    +              this.scroll();
    +              this.refreshStart = 0;
    +              this.refreshEnd = this.rows - 1;
    +            }
    +            break;
    
               // '\r'
    -        case 13:
    -          this.x = 0;
    -          break;
    +          case 13:
    +            this.x = 0;
    +            break;
    
               // '\b'
    -        case 8:
    -          if (this.x > 0) {
    -            this.x--;
    -          }
    -          break;
    +          case 8:
    +            if (this.x > 0) {
    +              this.x--;
    +            }
    +            break;
    
               // '\t'
    -        case 9:
    -          // should check tabstops
    -          param = (this.x + 8) & ~7;
    -          if (param <= this.cols) {
    -            this.x = param;
    -          }
    -          break;
    +          case 9:
    +            // should check tabstops
    +            param = (this.x + 8) & ~7;
    +            if (param <= this.cols) {
    +              this.x = param;
    +            }
    +            break;
    
               // '\e'
    -        case 27:
    -          this.state = escaped;
    -          break;
    +          case 27:
    +            this.state = escaped;
    +            break;
    
    -        default:
    -          // ' '
    -          if (ch >= 32) {
    -            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 + 1) {
    -                this.y--;
    -                this.scroll();
    -                this.refreshStart = 0;
    -                this.refreshEnd = this.rows - 1;
    +          default:
    +            // ' '
    +            if (ch >= 32) {
    +              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 + 1) {
    +                  this.y--;
    +                  this.scroll();
    +                  this.refreshStart = 0;
    +                  this.refreshEnd = this.rows - 1;
    +                }
                   }
    +              row = this.y + this.ybase;
    +              this.lines[row][this.x] = (ch & 0xffff) | (this.curAttr << 16);
    +              this.x++;
    +              this.getRows(this.y);
                 }
    -            row = this.y + this.ybase;
    -            this.lines[row][this.x] = (ch & 0xffff) | (this.curAttr << 16);
    -            this.x++;
    -            this.getRows(this.y);
    -          }
    -          break;
    +            break;
             }
             break;
           case escaped:
             switch (str[i]) {
               // ESC [ Control Sequence Introducer ( CSI is 0x9b).
    -        case '[':
    -          this.params = [];
    -          this.currentParam = 0;
    -          this.state = csi;
    -          break;
    +          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;
    +          case ']':
    +            this.params = [];
    +            this.currentParam = 0;
    +            this.state = osc;
    +            break;
    
               // ESC P Device Control String ( DCS is 0x90).
    -        case 'P':
    -          this.state = osc;
    -          break;
    +          case 'P':
    +            this.state = osc;
    +            break;
    
               // ESC _ Application Program Command ( APC is 0x9f).
    -        case '_':
    -          this.state = osc;
    -          break;
    +          case '_':
    +            this.state = osc;
    +            break;
    
               // ESC ^ Privacy Message ( PM is 0x9e).
    -        case '^':
    -          this.state = osc;
    -          break;
    +          case '^':
    +            this.state = osc;
    +            break;
    
               // ESC c Full Reset (RIS).
    -        case 'c':
    -          this.reset();
    -          break;
    +          case 'c':
    +            this.reset();
    +            break;
    
               // ESC E Next Line ( NEL is 0x85).
               // ESC D Index ( IND is 0x84).
    -        case 'E':
    -          this.x = 0;
    -          ;
    -        case 'D':
    -          this.index();
    -          break;
    +          case 'E':
    +            this.x = 0;
    +            ;
    +          case 'D':
    +            this.index();
    +            break;
    
               // ESC M Reverse Index ( RI is 0x8d).
    -        case 'M':
    -          this.reverseIndex();
    -          break;
    +          case 'M':
    +            this.reverseIndex();
    +            break;
    
               // ESC % Select default/utf-8 character set.
               // @ = default, G = utf-8
    -        case '%':
    -          this.charset = null;
    -          this.state = normal;
    -          i++;
    -          break;
    +          case '%':
    +            this.charset = null;
    +            this.state = normal;
    +            i++;
    +            break;
    
               // ESC (,),*,+,-,. Designate G0-G2 Character Set.
    -        case '(': // <-- this seems to get all the attention
    -        case ')':
    -        case '*':
    -        case '+':
    -        case '-':
    -        case '.':
    -          this.state = charset;
    -          break;
    +          case '(': // <-- this seems to get all the attention
    +          case ')':
    +          case '*':
    +          case '+':
    +          case '-':
    +          case '.':
    +            this.state = charset;
    +            break;
    
               // Designate G3 Character Set (VT300).
               // A = ISO Latin-1 Supplemental.
               // Not implemented.
    -        case '/':
    -          this.charset = null;
    -          this.state = normal;
    -          i++;
    -          break;
    +          case '/':
    +            this.charset = null;
    +            this.state = normal;
    +            i++;
    +            break;
    
               // ESC 7 Save Cursor (DECSC).
    -        case '7':
    -          this.saveCursor();
    -          this.state = normal;
    -          break;
    +          case '7':
    +            this.saveCursor();
    +            this.state = normal;
    +            break;
    
               // ESC 8 Restore Cursor (DECRC).
    -        case '8':
    -          this.restoreCursor();
    -          this.state = normal;
    -          break;
    +          case '8':
    +            this.restoreCursor();
    +            this.state = normal;
    +            break;
    
               // ESC # 3 DEC line height/width
    -        case '#':
    -          this.state = normal;
    -          i++;
    -          break;
    +          case '#':
    +            this.state = normal;
    +            i++;
    +            break;
    
               // ESC H Tab Set ( HTS is 0x88).
    -        case 'H':
    -          // this.tabSet(this.x);
    -          this.state = normal;
    -          break;
    +          case 'H':
    +            // this.tabSet(this.x);
    +            this.state = normal;
    +            break;
    
               // ESC = Application Keypad (DECPAM).
    -        case '=':
    -          console.log('Serial port requested application keypad.');
    -          this.applicationKeypad = true;
    -          this.state = normal;
    -          break;
    +          case '=':
    +            console.log('Serial port requested application keypad.');
    +            this.applicationKeypad = true;
    +            this.state = normal;
    +            break;
    
               // ESC > Normal Keypad (DECPNM).
    -        case '>':
    -          console.log('Switching back to normal keypad.');
    -          this.applicationKeypad = false;
    -          this.state = normal;
    -          break;
    +          case '>':
    +            console.log('Switching back to normal keypad.');
    +            this.applicationKeypad = false;
    +            this.state = normal;
    +            break;
    
    -        default:
    -          this.state = normal;
    -          console.log('Unknown ESC control: ' + str[i] + '.');
    -          break;
    +          default:
    +            this.state = normal;
    +            console.log('Unknown ESC control: ' + str[i] + '.');
    +            break;
             }
             break;
    
           case charset:
             switch (str[i]) {
               // DEC Special Character and Line Drawing Set.
    -        case '0':
    -          this.charset = SCLD;
    -          break;
    +          case '0':
    +            this.charset = SCLD;
    +            break;
               // United States (USASCII).
    -        case 'B':
    -        default:
    -          this.charset = null;
    -          break;
    +          case 'B':
    +          default:
    +            this.charset = null;
    +            break;
             }
             this.state = normal;
             break;
    @@ -620,53 +573,53 @@
             switch (ch) {
               // CSI Ps A
               // Cursor Up Ps Times (default = 1) (CUU).
    -        case 65:
    -          this.cursorUp(this.params);
    -          break;
    +          case 65:
    +            this.cursorUp(this.params);
    +            break;
    
               // CSI Ps B
               // Cursor Down Ps Times (default = 1) (CUD).
    -        case 66:
    -          this.cursorDown(this.params);
    -          break;
    +          case 66:
    +            this.cursorDown(this.params);
    +            break;
    
               // CSI Ps C
               // Cursor Forward Ps Times (default = 1) (CUF).
    -        case 67:
    -          this.cursorForward(this.params);
    -          break;
    +          case 67:
    +            this.cursorForward(this.params);
    +            break;
    
               // CSI Ps D
               // Cursor Backward Ps Times (default = 1) (CUB).
    -        case 68:
    -          this.cursorBackward(this.params);
    -          break;
    +          case 68:
    +            this.cursorBackward(this.params);
    +            break;
    
               // CSI Ps ; Ps H
               // Cursor Position [row;column] (default = [1,1]) (CUP).
    -        case 72:
    -          this.cursorPos(this.params);
    -          break;
    +          case 72:
    +            this.cursorPos(this.params);
    +            break;
    
               // CSI Ps J  Erase in Display (ED).
    -        case 74:
    -          this.eraseInDisplay(this.params);
    -          break;
    +          case 74:
    +            this.eraseInDisplay(this.params);
    +            break;
    
               // CSI Ps K  Erase in Line (EL).
    -        case 75:
    -          this.eraseInLine(this.params);
    -          break;
    +          case 75:
    +            this.eraseInLine(this.params);
    +            break;
    
               // CSI Pm m  Character Attributes (SGR).
    -        case 109:
    -          this.charAttributes(this.params);
    -          break;
    +          case 109:
    +            this.charAttributes(this.params);
    +            break;
    
               // CSI Ps n  Device Status Report (DSR).
    -        case 110:
    -          this.deviceStatus(this.params);
    -          break;
    +          case 110:
    +            this.deviceStatus(this.params);
    +            break;
    
               /**
                * Additions
    @@ -674,119 +627,119 @@
    
               // CSI Ps @
               // Insert Ps (Blank) Character(s) (default = 1) (ICH).
    -        case 64:
    -          this.insertChars(this.params);
    -          break;
    +          case 64:
    +            this.insertChars(this.params);
    +            break;
    
               // CSI Ps E
               // Cursor Next Line Ps Times (default = 1) (CNL).
    -        case 69:
    -          this.cursorNextLine(this.params);
    -          break;
    +          case 69:
    +            this.cursorNextLine(this.params);
    +            break;
    
               // CSI Ps F
               // Cursor Preceding Line Ps Times (default = 1) (CNL).
    -        case 70:
    -          this.cursorPrecedingLine(this.params);
    -          break;
    +          case 70:
    +            this.cursorPrecedingLine(this.params);
    +            break;
    
               // CSI Ps G
               // Cursor Character Absolute  [column] (default = [row,1]) (CHA).
    -        case 71:
    -          this.cursorCharAbsolute(this.params);
    -          break;
    +          case 71:
    +            this.cursorCharAbsolute(this.params);
    +            break;
    
               // CSI Ps L
               // Insert Ps Line(s) (default = 1) (IL).
    -        case 76:
    -          this.insertLines(this.params);
    -          break;
    +          case 76:
    +            this.insertLines(this.params);
    +            break;
    
               // CSI Ps M
               // Delete Ps Line(s) (default = 1) (DL).
    -        case 77:
    -          this.deleteLines(this.params);
    -          break;
    +          case 77:
    +            this.deleteLines(this.params);
    +            break;
    
               // CSI Ps P
               // Delete Ps Character(s) (default = 1) (DCH).
    -        case 80:
    -          this.deleteChars(this.params);
    -          break;
    +          case 80:
    +            this.deleteChars(this.params);
    +            break;
    
               // CSI Ps X
               // Erase Ps Character(s) (default = 1) (ECH).
    -        case 88:
    -          this.eraseChars(this.params);
    -          break;
    +          case 88:
    +            this.eraseChars(this.params);
    +            break;
    
               // CSI Pm `  Character Position Absolute
               //   [column] (default = [row,1]) (HPA).
    -        case 96:
    -          this.charPosAbsolute(this.params);
    -          break;
    +          case 96:
    +            this.charPosAbsolute(this.params);
    +            break;
    
               // 141 61 a * HPR -
               // Horizontal Position Relative
    -        case 97:
    -          this.HPositionRelative(this.params);
    -          break;
    +          case 97:
    +            this.HPositionRelative(this.params);
    +            break;
    
               // CSI P s c
               // Send Device Attributes (Primary DA).
               // CSI > P s c
               // Send Device Attributes (Secondary DA)
    -        case 99:
    -          this.sendDeviceAttributes(this.params);
    -          break;
    +          case 99:
    +            this.sendDeviceAttributes(this.params);
    +            break;
    
               // CSI Pm d
               // Line Position Absolute  [row] (default = [1,column]) (VPA).
    -        case 100:
    -          this.linePosAbsolute(this.params);
    -          break;
    +          case 100:
    +            this.linePosAbsolute(this.params);
    +            break;
    
               // 145 65 e * VPR - Vertical Position Relative
    -        case 101:
    -          this.VPositionRelative(this.params);
    -          break;
    +          case 101:
    +            this.VPositionRelative(this.params);
    +            break;
    
               // CSI Ps ; Ps f
               //   Horizontal and Vertical Position [row;column] (default =
               //   [1,1]) (HVP).
    -        case 102:
    -          this.HVPosition(this.params);
    -          break;
    +          case 102:
    +            this.HVPosition(this.params);
    +            break;
    
               // CSI Pm h  Set Mode (SM).
               // CSI ? Pm h - mouse escape codes, cursor escape codes
    -        case 104:
    -          this.setMode(this.params);
    -          break;
    +          case 104:
    +            this.setMode(this.params);
    +            break;
    
               // CSI Pm l  Reset Mode (RM).
               // CSI ? Pm l
    -        case 108:
    -          this.resetMode(this.params);
    -          break;
    +          case 108:
    +            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 114:
    -          this.setScrollRegion(this.params);
    -          break;
    +          case 114:
    +            this.setScrollRegion(this.params);
    +            break;
    
               // CSI s     Save cursor (ANSI.SYS).
    -        case 115:
    -          this.saveCursor(this.params);
    -          break;
    +          case 115:
    +            this.saveCursor(this.params);
    +            break;
    
               // CSI u     Restore cursor (ANSI.SYS).
    -        case 117:
    -          this.restoreCursor(this.params);
    -          break;
    +          case 117:
    +            this.restoreCursor(this.params);
    +            break;
    
               /**
                * Lesser Used
    @@ -794,40 +747,40 @@
    
               // CSI Ps I
               // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
    -        case 73:
    -          this.cursorForwardTab(this.params);
    -          break;
    +          case 73:
    +            this.cursorForwardTab(this.params);
    +            break;
    
               // CSI Ps S  Scroll up Ps lines (default = 1) (SU).
    -        case 83:
    -          this.scrollUp(this.params);
    -          break;
    +          case 83:
    +            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 84:
    -          // if (this.prefix === '>') {
    -          //   this.resetTitleModes(this.params);
    -          //   break;
    -          // }
    -          // if (this.params.length > 1) {
    -          //   this.initMouseTracking(this.params);
    -          //   break;
    -          // }
    -          this.scrollDown(this.params);
    -          break;
    +          case 84:
    +            // if (this.prefix === '>') {
    +            //   this.resetTitleModes(this.params);
    +            //   break;
    +            // }
    +            // if (this.params.length > 1) {
    +            //   this.initMouseTracking(this.params);
    +            //   break;
    +            // }
    +            this.scrollDown(this.params);
    +            break;
    
               // CSI Ps Z
               // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
    -        case 90:
    -          this.cursorBackwardTab(this.params);
    -          break;
    +          case 90:
    +            this.cursorBackwardTab(this.params);
    +            break;
    
               // CSI Ps b  Repeat the preceding graphic character Ps times (REP).
    -        case 98:
    -          this.repeatPrecedingCharacter(this.params);
    -          break;
    +          case 98:
    +            this.repeatPrecedingCharacter(this.params);
    +            break;
    
               // CSI Ps g  Tab Clear (TBC).
               // case 103:
    @@ -867,28 +820,28 @@
               // CSI ? Ps$ p
               //   Request DEC private mode (DECRQM).
               // CSI Ps ; Ps " p
    -        case 112:
    -          switch (this.prefix) {
    -            // case '>':
    -            //   this.setPointerMode(this.params);
    -            //   break;
    -          case '!':
    -            this.softReset(this.params);
    +          case 112:
    +            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;
    -            // 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
    @@ -1027,263 +980,525 @@
               //   }
               //   break;
    
    -        default:
    -          console.log(
    -            'Unknown CSI code: %s',
    -            str[i], this.params);
    -          break;
    +          default:
    +            console.log(
    +              'Unknown CSI code: %s',
    +              str[i], this.params);
    +            break;
             }
    
             this.prefix = '';
             this.postfix = '';
             break;
    -      }
         }
    +  }
    +
    +  this.getRows(this.y);
    +
    +  if (this.refreshEnd >= this.refreshStart) {
    +    this.refresh(this.refreshStart, this.refreshEnd);
    +  }
    +};
    +
    +Terminal.prototype.writeln = function(str) {
    +  this.write(str + '\r\n');
    +};
    +
    +Terminal.prototype.keyDownHandler = function(ev) {
    +  var str = '';
    +  switch (ev.keyCode) {
    +    // backspace
    +    case 8:
    +      str = '\x7f'; // ^?
    +      //str = '\x08'; // ^H
    +      break;
    +    // tab
    +    case 9:
    +      str = '\t';
    +      break;
    +    // return/enter
    +    case 13:
    +      str = '\r';
    +      break;
    +    // escape
    +    case 27:
    +      str = '\x1b';
    +      break;
    +    // left-arrow
    +    case 37:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOD'; // SS3 as ^O for 7-bit
    +        //str = '\x8fD'; // SS3 as 0x8f for 8-bit
    +        break;
    +      }
    +      str = '\x1b[D';
    +      break;
    +    // right-arrow
    +    case 39:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOC';
    +        break;
    +      }
    +      str = '\x1b[C';
    +      break;
    +    // up-arrow
    +    case 38:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOA';
    +        break;
    +      }
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(-1);
    +      } else {
    +        str = '\x1b[A';
    +      }
    +      break;
    +    // down-arrow
    +    case 40:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOB';
    +        break;
    +      }
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(1);
    +      } else {
    +        str = '\x1b[B';
    +      }
    +      break;
    +    // delete
    +    case 46:
    +      str = '\x1b[3~';
    +      break;
    +    // insert
    +    case 45:
    +      str = '\x1b[2~';
    +      break;
    +    // home
    +    case 36:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOH';
    +        break;
    +      }
    +      str = '\x1bOH';
    +      break;
    +    // end
    +    case 35:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOF';
    +        break;
    +      }
    +      str = '\x1bOF';
    +      break;
    +    // page up
    +    case 33:
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(-(this.rows - 1));
    +      } else {
    +        str = '\x1b[5~';
    +      }
    +      break;
    +    // page down
    +    case 34:
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(this.rows - 1);
    +      } else {
    +        str = '\x1b[6~';
    +      }
    +      break;
    +    // F1
    +    case 112:
    +      str = '\x1bOP';
    +      break;
    +    // F2
    +    case 113:
    +      str = '\x1bOQ';
    +      break;
    +    // F3
    +    case 114:
    +      str = '\x1bOR';
    +      break;
    +    // F4
    +    case 115:
    +      str = '\x1bOS';
    +      break;
    +    // F5
    +    case 116:
    +      str = '\x1b[15~';
    +      break;
    +    // F6
    +    case 117:
    +      str = '\x1b[17~';
    +      break;
    +    // F7
    +    case 118:
    +      str = '\x1b[18~';
    +      break;
    +    // F8
    +    case 119:
    +      str = '\x1b[19~';
    +      break;
    +    // F9
    +    case 120:
    +      str = '\x1b[20~';
    +      break;
    +    // F10
    +    case 121:
    +      str = '\x1b[21~';
    +      break;
    +    // F11
    +    case 122:
    +      str = '\x1b[23~';
    +      break;
    +    // F12
    +    case 123:
    +      str = '\x1b[24~';
    +      break;
    +    default:
    +      // a-z and space
    +      if (ev.ctrlKey) {
    +        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    +          str = String.fromCharCode(ev.keyCode - 64);
    +        } else if (ev.keyCode === 32) {
    +          // NUL
    +          str = String.fromCharCode(0);
    +        } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
    +          // escape, file sep, group sep, record sep, unit sep
    +          str = String.fromCharCode(ev.keyCode - 51 + 27);
    +        } else if (ev.keyCode === 56) {
    +          // delete
    +          str = String.fromCharCode(127);
    +        }
    +      } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) {
    +        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    +          str = '\x1b' + String.fromCharCode(ev.keyCode + 32);
    +        } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {
    +          str = '\x1b' + (ev.keyCode - 48);
    +        }
    +      }
    +      break;
    +  }
    +
    +  if (str) {
    +    cancel(ev);
    
    -    this.getRows(this.y);
    +    this.showCursor();
    +    this.keyState = 1;
    +    this.keyStr = str;
    +    this.handler(str);
    
    -    if (this.refreshEnd >= this.refreshStart) {
    -      this.refresh(this.refreshStart, this.refreshEnd);
    +    return false;
    +  } else {
    +    this.keyState = 0;
    +    return true;
    +  }
    +};
    +
    +Terminal.prototype.keyPressHandler = function(ev) {
    +  var str = ''
    +    , key;
    +
    +  cancel(ev);
    +
    +  if (!('charCode' in ev)) {
    +    key = ev.keyCode;
    +    if (this.keyState === 1) {
    +      this.keyState = 2;
    +      return false;
    +    } else if (this.keyState === 2) {
    +      this.showCursor();
    +      this.handler(this.keyStr);
    +      return false;
         }
    -  };
    +  } else {
    +    key = ev.charCode;
    +  }
    +
    +  if (key !== 0) {
    +    if (!ev.ctrlKey
    +        && ((!this.isMac && !ev.altKey)
    +        || (this.isMac && !ev.metaKey))) {
    +      str = String.fromCharCode(key);
    +    }
    +  }
    +
    +  if (str) {
    +    this.showCursor();
    +    this.handler(str);
    +    return false;
    +  } else {
    +    return true;
    +  }
    +};
    +
    +Terminal.prototype.queueChars = function(str) {
    +  var self = this;
    
    -  Terminal.prototype.bell = function() {
    -    if (!Terminal.visualBell) return;
    -    if (Terminal.popOnBell) this.focus();
    -  };
    +  this.outputQueue += str;
    +
    +  if (this.outputQueue) {
    +    setTimeout(function() {
    +      self.outputHandler();
    +    }, 1);
    +  }
    +};
    
    -  Terminal.prototype.resize = function(x, y) {
    -    var line
    +Terminal.prototype.outputHandler = function() {
    +  if (this.outputQueue) {
    +    this.handler(this.outputQueue);
    +    this.outputQueue = '';
    +  }
    +};
    +
    +Terminal.prototype.bell = function() {
    +  if (!this.useBell) return;
    +  var self = this;
    +  this.element.style.borderColor = 'white';
    +  setTimeout(function() {
    +    self.element.style.borderColor = '';
    +  }, 10);
    +};
    +
    +Terminal.prototype.resize = function(x, y) {
    +  var line
         , el
         , i
         , j;
    
    -    if (x < 1) x = 1;
    -    if (y < 1) y = 1;
    +  if (x < 1) x = 1;
    +  if (y < 1) y = 1;
    
    -    // resize cols
    -    j = this.cols;
    -    if (j < x) {
    -      i = this.lines.length;
    -      while (i--) {
    -        while (this.lines[i].length < x) {
    -          this.lines[i].push((this.defAttr << 16) | 32);
    -        }
    +  // make sure the cursor stays on screen
    +  if (this.y >= y) this.y = y - 1;
    +  if (this.x >= x) this.x = x - 1;
    +
    +  if (this.cols < x) {
    +    i = this.lines.length;
    +    while (i--) {
    +      while (this.lines[i].length < x) {
    +        this.lines[i].push((this.defAttr << 16) | 32);
           }
    -    } else if (j > x) {
    -      i = this.lines.length;
    -      while (i--) {
    -        while (this.lines[i].length > x) {
    -          this.lines[i].pop();
    -        }
    +    }
    +  } else if (this.cols > x) {
    +    i = this.lines.length;
    +    while (i--) {
    +      while (this.lines[i].length > x) {
    +        this.lines[i].pop();
           }
         }
    -    this.cols = x;
    -
    -    // resize rows
    -    j = this.rows;
    -    if (j < y) {
    -      while (j++ < y) {
    -        if (this.lines.length < y + this.ybase) {
    -          this.lines.push(this.blankLine());
    -        }
    +  }
    +
    +  j = this.rows;
    +  if (j < y) {
    +    el = this.element;
    +    while (j++ < y) {
    +      if (this.lines.length < y + this.ybase) {
    +        this.lines.push(this.blankLine());
           }
    -    } else if (j > y) {
    -      while (j-- > y) {
    -        if (this.lines.length > y + this.ybase) {
    -          this.lines.pop();
    -        }
    +      if (this.children.length < y) {
    +        line = document.createElement('div');
    +        line.className = 'term';
    +        el.appendChild(line);
    +        this.children.push(line);
    +      }
    +    }
    +  } else if (j > y) {
    +    while (j-- > y) {
    +      if (this.lines.length > y + this.ybase) {
    +        this.lines.shift();
    +      }
    +      if (this.children.length > y) {
    +        el = this.children.pop();
    +        if (!el) continue;
    +        el.parentNode.removeChild(el);
           }
         }
    -    this.rows = y;
    +  }
    
    -    // make sure the cursor stays on screen
    -    if (this.y >= y) this.y = y - 1;
    -    if (this.x >= x) this.x = x - 1;
    +  this.cols = x;
    +  this.rows = y;
    +  this.scrollTop = 0;
    +  this.scrollBottom = y - 1;
    +  this.refreshStart = 0;
    +  this.refreshEnd = y - 1;
    
    -    this.scrollTop = 0;
    -    this.scrollBottom = y - 1;
    -    this.refreshStart = 0;
    -    this.refreshEnd = y - 1;
    +  this.refresh(0, this.rows - 1);
    
    -    this.refresh(0, this.rows - 1);
    +  // it's a real nightmare trying
    +  // to resize the original
    +  // screen buffer. just set it
    +  // to null for now.
    +  this.normal = null;
    +};
    
    -    // it's a real nightmare trying
    -    // to resize the original
    -    // screen buffer. just set it
    -    // to null for now.
    -    this.normal = null;
    -  };
    +Terminal.prototype.getRows = function(y) {
    +  this.refreshStart = Math.min(this.refreshStart, y);
    +  this.refreshEnd = Math.max(this.refreshEnd, y);
    +};
    
    -  Terminal.prototype.getRows = function(y) {
    -    this.refreshStart = Math.min(this.refreshStart, y);
    -    this.refreshEnd = Math.max(this.refreshEnd, y);
    -  };
    +Terminal.prototype.eraseLine = function(x, y) {
    +  var line, i, ch, row;
    
    -  Terminal.prototype.eraseLine = function(x, y) {
    -    var line, i, ch, row;
    +  row = this.ybase + y;
    
    -    row = this.ybase + y;
    +  line = this.lines[row];
    +  // screen:
    +  // ch = 32 | (this.defAttr << 16);
    +  // xterm, linux:
    +  ch = 32 | (this.curAttr << 16);
    
    -    line = this.lines[row];
    -    // screen:
    -    // ch = 32 | (this.defAttr << 16);
    -    // xterm, linux:
    -    ch = 32 | (this.curAttr << 16);
    -
    -    for (i = x; i < this.cols; i++) {
    -      line[i] = ch;
    -    }
    +  for (i = x; i < this.cols; i++) {
    +    line[i] = ch;
    +  }
    
    -    this.getRows(y);
    -  };
    +  this.getRows(y);
    +};
    
    -  Terminal.prototype.blankLine = function(cur) {
    -    var attr = cur
    -          ? this.curAttr
    -          : this.defAttr;
    +Terminal.prototype.blankLine = function(cur) {
    +  var attr = cur
    +    ? this.curAttr
    +    : this.defAttr;
    
    -    var ch = 32 | (attr << 16)
    +  var ch = 32 | (attr << 16)
         , line = []
         , i = 0;
    
    -    for (; i < this.cols; i++) {
    -      line[i] = ch;
    -    }
    +  for (; i < this.cols; i++) {
    +    line[i] = ch;
    +  }
    
    -    return line;
    -  };
    +  return line;
    +};
    
    -  /**
    -   * ESC
    -   */
    +/**
    + * ESC
    + */
    
    -  // ESC D Index (IND is 0x84).
    -  Terminal.prototype.index = function() {
    +// ESC D Index (IND is 0x84).
    +Terminal.prototype.index = function() {
    +  this.y++;
    +  if (this.y >= this.scrollBottom + 1) {
    +    this.y--;
    +    this.scroll();
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  }
    +  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++;
    -    if (this.y >= this.scrollBottom + 1) {
    -      this.y--;
    -      this.scroll();
    -      this.refreshStart = 0;
    -      this.refreshEnd = this.rows - 1;
    -    }
    -    this.state = normal;
    -  };
    +    // echo -ne '\e[1;1H\e[44m\eM\e[0m'
    +    // use this.blankLine(false) for screen behavior
    +    this.lines.splice(this.y + this.ybase, 0, this.blankLine(true));
    +    j = this.rows - 1 - this.scrollBottom;
    +    // add an extra one because we just added a line
    +    // maybe put this above
    +    this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1);
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  }
    +  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++;
    -      // echo -ne '\e[1;1H\e[44m\eM\e[0m'
    -      // use this.blankLine(false) for screen behavior
    -      this.lines.splice(this.y + this.ybase, 0, this.blankLine(true));
    -      j = this.rows - 1 - this.scrollBottom;
    -      // add an extra one because we just added a line
    -      // maybe put this above
    -      this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1);
    -      this.refreshStart = 0;
    -      this.refreshEnd = this.rows - 1;
    -    }
    -    this.state = normal;
    -  };
    -
    -  // ESC c Full Reset (RIS).
    -  Terminal.prototype.reset = function() {
    -    Terminal.call(this, this.cols, this.rows);
    -  };
    -
    -  /**
    -   * CSI
    -   */
    -
    -  // CSI Ps A
    -  // Cursor Up Ps Times (default = 1) (CUU).
    -  Terminal.prototype.cursorUp = function(params) {
    -    var param, row;
    -    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, row;
    -    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, row;
    -    param = params[0];
    -    if (param < 1) param = 1;
    -    this.x += param;
    -    if (this.x >= this.cols - 1) {
    -      this.x = this.cols - 1;
    -    }
    -  };
    -
    -  // CSI Ps D
    -  // Cursor Backward Ps Times (default = 1) (CUB).
    -  Terminal.prototype.cursorBackward = function(params) {
    -    var param, row;
    -    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 param, row, col;
    -
    -    row = params[0] - 1;
    -
    -    if (params.length >= 2) {
    -      col = params[1] - 1;
    -    } else {
    -      col = 0;
    -    }
    +// ESC c Full Reset (RIS).
    +Terminal.prototype.reset = function() {
    +  Terminal.call(this, this.cols, this.rows, this.handler);
    +};
    
    -    if (row < 0) {
    -      row = 0;
    -    } else if (row >= this.rows) {
    -      row = this.rows - 1;
    -    }
    +/**
    + * CSI
    + */
    
    -    if (col < 0) {
    -      col = 0;
    -    } else if (col >= this.cols) {
    -      col = this.cols - 1;
    -    }
    +// CSI Ps A
    +// Cursor Up Ps Times (default = 1) (CUU).
    +Terminal.prototype.cursorUp = function(params) {
    +  var param, row;
    +  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, row;
    +  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, row;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  this.x += param;
    +  if (this.x >= this.cols - 1) {
    +    this.x = this.cols - 1;
    +  }
    +};
    +
    +// CSI Ps D
    +// Cursor Backward Ps Times (default = 1) (CUB).
    +Terminal.prototype.cursorBackward = function(params) {
    +  var param, row;
    +  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 param, row, col;
    +
    +  row = params[0] - 1;
    +
    +  if (params.length >= 2) {
    +    col = params[1] - 1;
    +  } else {
    +    col = 0;
    +  }
    
    -    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 param, row, j;
    -    switch (params[0] || 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 param, row, j;
    +  switch (params[0] || 0) {
         case 0:
           this.eraseLine(this.x, this.y);
           for (j = this.y + 1; j < this.rows; j++) {
    @@ -1304,20 +1519,20 @@
         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] || 0) {
    +  }
    +};
    +
    +// 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] || 0) {
         case 0:
           this.eraseLine(this.x, this.y);
           break;
    @@ -1339,160 +1554,145 @@
           var ch = (this.curAttr << 16) | 32;
           while (x--) line[x] = ch;
           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) {
    -    var i, p;
    -    if (params.length === 0) {
    -      this.curAttr = this.defAttr;
    -    } else {
    -      for (i = 0; i < params.length; i++) {
    -        p = params[i];
    -        if (p >= 30 && p <= 37) {
    -          this.curAttr = (this.curAttr & ~(31 << 5)) | ((p - 30) << 5);
    -        } else if (p >= 40 && p <= 47) {
    -          this.curAttr = (this.curAttr & ~31) | (p - 40);
    -        } else if (p >= 90 && p <= 97) {
    -          this.curAttr = (this.curAttr & ~(31 << 5)) | ((p - 90) << 5);
    -          this.curAttr = this.curAttr | (8 << 5);
    -        } else if (p >= 100 && p <= 107) {
    -          this.curAttr = (this.curAttr & ~31) | (p - 100);
    -          this.curAttr = this.curAttr | 8;
    -        } else if (p === 0) {
    -          this.curAttr = this.defAttr;
    -        } else if (p === 1) {
    -          // bold text
    -          this.curAttr = this.curAttr | (1 << 10);
    -        } else if (p === 4) {
    -          // underlined text
    -          this.curAttr = this.curAttr | (2 << 10);
    -        } else if (p === 7 || p === 27) {
    -          // inverse and positive
    -          // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
    -          if (p === 7) {
    -            if ((this.curAttr >> 10) & 4) continue;
    -            this.curAttr = this.curAttr | (4 << 10);
    -          } else if (p === 27) {
    -            if (~(this.curAttr >> 10) & 4) continue;
    -            this.curAttr = this.curAttr & ~(4 << 10);
    -          }
    -          var bg = this.curAttr & 31;
    -          var fg = (this.curAttr >> 5) & 31;
    -          this.curAttr = (this.curAttr & ~1023) | ((bg << 5) | fg);
    -        } else if (p === 22) {
    -          // not bold
    -          this.curAttr = this.curAttr & ~(1 << 10);
    -        } else if (p === 24) {
    -          // not underlined
    -          this.curAttr = this.curAttr & ~(2 << 10);
    -        } else if (p === 39) {
    -          // reset fg
    -          this.curAttr = this.curAttr & ~(31 << 5);
    -          this.curAttr = this.curAttr | (((this.defAttr >> 5) & 31) << 5);
    -        } else if (p === 49) {
    -          // reset bg
    -          this.curAttr = this.curAttr & ~31;
    -          this.curAttr = this.curAttr | (this.defAttr & 31);
    -        }
    +  }
    +};
    +
    +// 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) {
    +  var i, p;
    +  if (params.length === 0) {
    +    this.curAttr = this.defAttr;
    +  } else {
    +    for (i = 0; i < params.length; i++) {
    +      p = params[i];
    +      if (p >= 30 && p <= 37) {
    +        this.curAttr = (this.curAttr & ~(7 << 3)) | ((p - 30) << 3);
    +      } else if (p >= 40 && p <= 47) {
    +        this.curAttr = (this.curAttr & ~7) | (p - 40);
    +      } else if (p >= 90 && p <= 97) {
    +        this.curAttr = (this.curAttr & ~(7 << 3)) | ((p - 90) << 3);
    +      } else if (p >= 100 && p <= 107) {
    +        this.curAttr = (this.curAttr & ~7) | (p - 100);
    +      } else if (p === 0) {
    +        this.curAttr = this.defAttr;
    +      } else if (p === 1) {
    +        // bold text
    +        this.curAttr = this.curAttr | (1 << 8);
    +      } else if (p === 4) {
    +        // underlined text
    +        this.curAttr = this.curAttr | (4 << 8);
    +      } else if (p === 22) {
    +        // not bold
    +        this.curAttr = this.curAttr & ~(1 << 8);
    +      } else if (p === 24) {
    +        // not underlined
    +        this.curAttr = this.curAttr & ~(4 << 8);
    +      } else if (p === 39) {
    +        // reset fg
    +        p = this.curAttr & 7;
    +        this.curAttr = (this.defAttr & ~7) | p;
    +      } else if (p === 49) {
    +        // reset bg
    +        p = (this.curAttr >> 3) & 7;
    +        this.curAttr = (this.defAttr & ~(7 << 3)) | (p << 3);
           }
         }
    -  };
    -
    -  // 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 === '?') {
    -      // modern xterm doesnt seem to
    -      // respond to any of these except ?6, 6, and 5
    -      switch (params[0]) {
    +  }
    +};
    +
    +// 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 === '?') {
    +    // modern xterm doesnt seem to
    +    // respond to any of these except ?6, 6, and 5
    +    switch (params[0]) {
           case 6:
             this.queueChars('\x1b['
    -                        + (this.y + 1)
    -                        + ';'
    -                        + (this.x + 1)
    -                        + 'R');
    +          + (this.y + 1)
    +          + ';'
    +          + (this.x + 1)
    +          + 'R');
             break;
           case 15:
             // no printer
    @@ -1509,376 +1709,376 @@
             // no dec locator/mouse
             // this.queueChars('\x1b[?50n');
             break;
    -      }
    -      return;
         }
    -    switch (params[0]) {
    +    return;
    +  }
    +  switch (params[0]) {
         case 5:
           this.queueChars('\x1b[0n');
           break;
         case 6:
           this.queueChars('\x1b['
    -                      + (this.y + 1)
    -                      + ';'
    -                      + (this.x + 1)
    -                      + 'R');
    +        + (this.y + 1)
    +        + ';'
    +        + (this.x + 1)
    +        + 'R');
           break;
    -    }
    -  };
    -
    -  /**
    -   * Additions
    -   */
    -
    -  // CSI Ps @
    -  // Insert Ps (Blank) Character(s) (default = 1) (ICH).
    -  Terminal.prototype.insertChars = function(params) {
    -    var param, row, j;
    -    param = params[0];
    -    if (param < 1) param = 1;
    -    row = this.y + this.ybase;
    -    j = this.x;
    -    while (param-- && j < this.cols) …
  3. [from now] 2012/04/05 14:57:28

    committed Apr 5, 2012
    diff --git a/browser/index.html b/browser/index.html
    new file mode 100644
    index 0000000..37e10c4
    --- /dev/null
    +++ b/browser/index.html
    @@ -0,0 +1,10 @@
    +<!DOCTYPE html>
    +<html>
    +<head>
    +<meta charset="UTF-8">
    +<title>live term test</title>
    +</head>
    +<body>
    +<iframe src="liveterm.html" width="600" height="400" style="border: none;"></iframe>
    +</body>
    +</html>
    diff --git a/browser/liveterm.html b/browser/liveterm.html
    new file mode 100644
    index 0000000..150ee77
    --- /dev/null
    +++ b/browser/liveterm.html
    @@ -0,0 +1,14 @@
    +<!DOCTYPE html>
    +<html>
    +<head>
    +<meta charset="UTF-8">
    +<title>liveterm</title>
    +<link href="./style.css" rel="stylesheet" type="text/css">
    +<script src="./socket.io/socket.io.js"></script>
    +<script src="./term.js"></script>
    +<script src="./main.js"></script>
    +</head>
    +<body>
    +
    +</body>
    +</html>
    diff --git a/browser/main.js b/browser/main.js
    new file mode 100644
    index 0000000..cc36f55
    --- /dev/null
    +++ b/browser/main.js
    @@ -0,0 +1,20 @@
    +var noop = function () { /* do nothing */ };
    +
    +var socket = io.connect();
    +
    +var term = new Terminal(80, 30, noop);
    +
    +socket.on('connect', function () {
    +  term.open();
    +  socket.emit('create');
    +});
    +
    +socket.on('input', function (data) {
    +  term.write(data);
    +});
    +
    +socket.on('snapshot', function (data) {
    +  for (var prop in data) {
    +    term[prop] = data[prop];
    +  }
    +});
    \ No newline at end of file
    diff --git a/browser/style.css b/browser/style.css
    new file mode 100644
    index 0000000..a25d596
    --- /dev/null
    +++ b/browser/style.css
    @@ -0,0 +1,31 @@
    +/**
    + * style.css (https://github.com/chjj/tty.js)
    + * Copyright (c) 2012, Christopher Jeffrey (MIT License)
    + */
    +
    +html, body {
    +  margin: 0;
    +  padding: 0;
    +}
    +
    +div {
    +  border: 0;
    +  padding: 0;
    +  margin: 0;
    +}
    +
    +html {
    +  background: #fff;
    +}
    +
    +.term {
    +  font-family: "DejaVu Sans Mono", monospace;
    +  font-size: 11px;
    +  color: #f0f0f0;
    +  background: #000;
    +}
    +
    +.termReverse {
    +  color: #000;
    +  background: #f0f0f0;
    +}
    \ No newline at end of file
    diff --git a/browser/term.js b/browser/term.js
    new file mode 100644
    index 0000000..7423080
    --- /dev/null
    +++ b/browser/term.js
    @@ -0,0 +1,2950 @@
    +/**
    + * tty.js - an xterm emulator
    + * Christopher Jeffrey (https://github.com/chjj/tty.js)
    + *
    + * Originally forked from (with the author's permission):
    + *
    + * Fabrice Bellard's javascript vt100 for jslinux:
    + * http://bellard.org/jslinux/
    + * Copyright (c) 2011 Fabrice Bellard
    + * (Redistribution or commercial use is prohibited
    + *  without the author's permission.)
    + *
    + * The original design remains. The terminal itself
    + * has been extended to include xterm CSI codes, among
    + * other features.
    +*/
    +
    +;(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';
    +
    +/**
    + * States
    + */
    +
    +var normal = 0
    +  , escaped = 1
    +  , csi = 2
    +  , osc = 3
    +  , charset = 4;
    +
    +/**
    + * Terminal
    + */
    +
    +var Terminal = function(cols, rows, handler) {
    +  this.cols = cols;
    +  this.rows = rows;
    +  this.handler = handler;
    +  this.scrollback = 1000;
    +  this.ybase = 0;
    +  this.ydisp = 0;
    +  this.x = 0;
    +  this.y = 0;
    +  this.cursorState = 0;
    +  this.cursorHidden = false;
    +  this.convertEol = false;
    +  this.state = 0;
    +  this.outputQueue = '';
    +  this.scrollTop = 0;
    +  this.scrollBottom = this.rows - 1;
    +
    +  this.applicationKeypad = false;
    +  this.originMode = false;
    +  this.insertMode = false;
    +  this.wraparoundMode = false;
    +  this.mouseEvents;
    +  this.tabs = [];
    +  this.charset = null;
    +  this.normal = null;
    +
    +  this.bgColors = [
    +    '#2e3436',
    +    '#cc0000',
    +    '#4e9a06',
    +    '#c4a000',
    +    '#3465a4',
    +    '#75507b',
    +    '#06989a',
    +    '#d3d7cf'
    +  ];
    +
    +  this.fgColors = [
    +    '#555753',
    +    '#ef2929',
    +    '#8ae234',
    +    '#fce94f',
    +    '#729fcf',
    +    '#ad7fa8',
    +    '#34e2e2',
    +    '#eeeeec'
    +  ];
    +
    +  this.defAttr = (7 << 3) | 0;
    +  this.curAttr = this.defAttr;
    +  this.isMac = ~navigator.userAgent.indexOf('Mac');
    +  this.keyState = 0;
    +  this.keyStr = '';
    +
    +  this.params = [];
    +  this.currentParam = 0;
    +
    +  var i = this.rows - 1;
    +  this.lines = [ this.blankLine() ];
    +  while (i--) {
    +    this.lines.push(this.lines[0].slice());
    +  }
    +};
    +
    +/**
    + * Open Terminal
    + */
    +
    +Terminal.prototype.open = function() {
    +  var self = this
    +    , i = 0
    +    , div;
    +
    +  this.element = document.createElement('div');
    +  this.element.className = 'terminal';
    +  this.children = [];
    +
    +  for (; i < this.rows; i++) {
    +    div = document.createElement('div');
    +    div.className = 'term';
    +    this.element.appendChild(div);
    +    this.children.push(div);
    +  }
    +
    +  document.body.appendChild(this.element);
    +
    +  this.refresh(0, this.rows - 1);
    +
    +  setInterval(function() {
    +    self.cursorBlink();
    +  }, 500);
    +};
    +
    +Terminal.prototype.refresh = function(start, end) {
    +  var element
    +    , x
    +    , y
    +    , i
    +    , line
    +    , out
    +    , ch
    +    , width
    +    , data
    +    , defAttr
    +    , fgColor
    +    , bgColor
    +    , row;
    +
    +  for (y = start; y <= end; y++) {
    +    row = y + this.ydisp;
    +
    +    line = this.lines[row];
    +    out = '';
    +    width = this.cols;
    +
    +    if (y === this.y
    +        && this.cursorState
    +        && this.ydisp === this.ybase) {
    +      x = this.x;
    +    } else {
    +      x = -1;
    +    }
    +
    +    defAttr = this.defAttr;
    +
    +    for (i = 0; i < width; i++) {
    +      ch = line[i];
    +      data = ch >> 16;
    +      ch &= 0xffff;
    +      if (i === x) {
    +        data = -1;
    +      }
    +
    +      if (data !== defAttr) {
    +        if (defAttr !== this.defAttr)
    +          out += '</span>';
    +        if (data !== this.defAttr) {
    +          if (data === -1) {
    +            out += '<span class="termReverse">';
    +          } else {
    +            out += '<span style="';
    +            fgColor = (data >> 3) & 7;
    +            bgColor = data & 7;
    +            if (fgColor !== 7) {
    +              out += 'color:'
    +                + this.fgColors[fgColor]
    +                + ';';
    +            }
    +            if (bgColor !== 0) {
    +              out += 'background-color:'
    +                + this.bgColors[bgColor]
    +                + ';';
    +            }
    +            if ((data >> 8) & 1) {
    +              out += 'font-weight:bold;';
    +            }
    +            if ((data >> 8) & 4) {
    +              out += 'text-decoration:underline;';
    +            }
    +            out += '">';
    +          }
    +        }
    +      }
    +
    +      switch (ch) {
    +        case 32:
    +          out += '&nbsp;';
    +          break;
    +        case 38:
    +          out += '&amp;';
    +          break;
    +        case 60:
    +          out += '&lt;';
    +          break;
    +        case 62:
    +          out += '&gt;';
    +          break;
    +        default:
    +          if (ch < 32) {
    +            out += '&nbsp;';
    +          } else {
    +            out += String.fromCharCode(ch);
    +          }
    +          break;
    +      }
    +
    +      defAttr = data;
    +    }
    +
    +    if (defAttr !== this.defAttr) {
    +      out += '</span>';
    +    }
    +
    +    element = this.children[y];
    +    element.innerHTML = out;
    +  }
    +};
    +
    +Terminal.prototype.cursorBlink = function() {
    +  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);
    +  }
    +};
    +
    +Terminal.prototype.scroll = function() {
    +  var row;
    +
    +  // maybe check this.lines.length ?
    +  if (++this.ybase === this.scrollback) {
    +    this.ybase = 0;
    +    this.ydisp = 0; // always reset disp to zero
    +    this.lines = this.lines.slice(-this.rows + 1);
    +  }
    +
    +  // if (this.scrollTtyOutput)
    +  this.ydisp = this.ybase;
    +
    +  // last line
    +  row = this.ybase + this.rows - 1;
    +
    +  // subtract the bottom scroll region
    +  row -= this.rows - 1 - this.scrollBottom;
    +
    +  // 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);
    +  }
    +};
    +
    +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(str) {
    +  // console.log(JSON.stringify(str.replace(/\x1b/g, '^[')));
    +
    +  var l = str.length
    +    , i = 0
    +    , ch
    +    , param
    +    , row;
    +
    +  this.refreshStart = this.rows;
    +  this.refreshEnd = -1;
    +  this.getRows(this.y);
    +
    +  if (this.ybase !== this.ydisp) {
    +    this.ydisp = this.ybase;
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  }
    +
    +  for (; i < l; i++) {
    +    ch = str.charCodeAt(i);
    +    switch (this.state) {
    +      case normal:
    +        switch (ch) {
    +          // '\0'
    +          case 0:
    +            break;
    +
    +          // '\a'
    +          case 7:
    +            this.bell();
    +            break;
    +
    +          // '\n', '\v', '\f'
    +          case 10:
    +          case 11:
    +          case 12:
    +            if (this.convertEol) {
    +              this.x = 0;
    +            }
    +            this.y++;
    +            if (this.y >= this.scrollBottom + 1) {
    +              this.y--;
    +              this.scroll();
    +              this.refreshStart = 0;
    +              this.refreshEnd = this.rows - 1;
    +            }
    +            break;
    +
    +          // '\r'
    +          case 13:
    +            this.x = 0;
    +            break;
    +
    +          // '\b'
    +          case 8:
    +            if (this.x > 0) {
    +              this.x--;
    +            }
    +            break;
    +
    +          // '\t'
    +          case 9:
    +            // should check tabstops
    +            param = (this.x + 8) & ~7;
    +            if (param <= this.cols) {
    +              this.x = param;
    +            }
    +            break;
    +
    +          // '\e'
    +          case 27:
    +            this.state = escaped;
    +            break;
    +
    +          default:
    +            // ' '
    +            if (ch >= 32) {
    +              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 + 1) {
    +                  this.y--;
    +                  this.scroll();
    +                  this.refreshStart = 0;
    +                  this.refreshEnd = this.rows - 1;
    +                }
    +              }
    +              row = this.y + this.ybase;
    +              this.lines[row][this.x] = (ch & 0xffff) | (this.curAttr << 16);
    +              this.x++;
    +              this.getRows(this.y);
    +            }
    +            break;
    +        }
    +        break;
    +      case escaped:
    +        switch (str[i]) {
    +          // 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.state = osc;
    +            break;
    +
    +          // ESC _ Application Program Command ( APC is 0x9f).
    +          case '_':
    +            this.state = osc;
    +            break;
    +
    +          // ESC ^ Privacy Message ( PM is 0x9e).
    +          case '^':
    +            this.state = osc;
    +            break;
    +
    +          // ESC c Full Reset (RIS).
    +          case 'c':
    +            this.reset();
    +            break;
    +
    +          // ESC E Next Line ( NEL is 0x85).
    +          // ESC D Index ( IND is 0x84).
    +          case 'E':
    +            this.x = 0;
    +            ;
    +          case 'D':
    +            this.index();
    +            break;
    +
    +          // ESC M Reverse Index ( RI is 0x8d).
    +          case 'M':
    +            this.reverseIndex();
    +            break;
    +
    +          // ESC % Select default/utf-8 character set.
    +          // @ = default, G = utf-8
    +          case '%':
    +            this.charset = null;
    +            this.state = normal;
    +            i++;
    +            break;
    +
    +          // ESC (,),*,+,-,. Designate G0-G2 Character Set.
    +          case '(': // <-- this seems to get all the attention
    +          case ')':
    +          case '*':
    +          case '+':
    +          case '-':
    +          case '.':
    +            this.state = charset;
    +            break;
    +
    +          // Designate G3 Character Set (VT300).
    +          // A = ISO Latin-1 Supplemental.
    +          // Not implemented.
    +          case '/':
    +            this.charset = null;
    +            this.state = normal;
    +            i++;
    +            break;
    +
    +          // ESC 7 Save Cursor (DECSC).
    +          case '7':
    +            this.saveCursor();
    +            this.state = normal;
    +            break;
    +
    +          // ESC 8 Restore Cursor (DECRC).
    +          case '8':
    +            this.restoreCursor();
    +            this.state = normal;
    +            break;
    +
    +          // ESC # 3 DEC line height/width
    +          case '#':
    +            this.state = normal;
    +            i++;
    +            break;
    +
    +          // ESC H Tab Set ( HTS is 0x88).
    +          case 'H':
    +            // this.tabSet(this.x);
    +            this.state = normal;
    +            break;
    +
    +          // ESC = Application Keypad (DECPAM).
    +          case '=':
    +            console.log('Serial port requested application keypad.');
    +            this.applicationKeypad = true;
    +            this.state = normal;
    +            break;
    +
    +          // ESC > Normal Keypad (DECPNM).
    +          case '>':
    +            console.log('Switching back to normal keypad.');
    +            this.applicationKeypad = false;
    +            this.state = normal;
    +            break;
    +
    +          default:
    +            this.state = normal;
    +            console.log('Unknown ESC control: ' + str[i] + '.');
    +            break;
    +        }
    +        break;
    +
    +      case charset:
    +        switch (str[i]) {
    +          // DEC Special Character and Line Drawing Set.
    +          case '0':
    +            this.charset = SCLD;
    +            break;
    +          // United States (USASCII).
    +          case 'B':
    +          default:
    +            this.charset = null;
    +            break;
    +        }
    +        this.state = normal;
    +        break;
    +
    +      case osc:
    +        if (ch !== 27 && ch !== 7) break;
    +        console.log('Unknown OSC code.');
    +        this.state = normal;
    +        // increment for the trailing slash in ST
    +        if (ch === 27) i++;
    +        break;
    +
    +      case csi:
    +        // '?', '>', '!'
    +        if (ch === 63 || ch === 62 || ch === 33) {
    +          this.prefix = str[i];
    +          break;
    +        }
    +
    +        // 0 - 9
    +        if (ch >= 48 && ch <= 57) {
    +          this.currentParam = this.currentParam * 10 + ch - 48;
    +          break;
    +        }
    +
    +        // '$', '"', ' ', '\''
    +        if (ch === 36 || ch === 34 || ch === 32 || ch === 39) {
    +          this.postfix = str[i];
    +          break;
    +        }
    +
    +        this.params[this.params.length] = this.currentParam;
    +        this.currentParam = 0;
    +
    +        // ';'
    +        if (ch === 59) break;
    +
    +        this.state = normal;
    +
    +        switch (ch) {
    +          // CSI Ps A
    +          // Cursor Up Ps Times (default = 1) (CUU).
    +          case 65:
    +            this.cursorUp(this.params);
    +            break;
    +
    +          // CSI Ps B
    +          // Cursor Down Ps Times (default = 1) (CUD).
    +          case 66:
    +            this.cursorDown(this.params);
    +            break;
    +
    +          // CSI Ps C
    +          // Cursor Forward Ps Times (default = 1) (CUF).
    +          case 67:
    +            this.cursorForward(this.params);
    +            break;
    +
    +          // CSI Ps D
    +          // Cursor Backward Ps Times (default = 1) (CUB).
    +          case 68:
    +            this.cursorBackward(this.params);
    +            break;
    +
    +          // CSI Ps ; Ps H
    +          // Cursor Position [row;column] (default = [1,1]) (CUP).
    +          case 72:
    +            this.cursorPos(this.params);
    +            break;
    +
    +          // CSI Ps J  Erase in Display (ED).
    +          case 74:
    +            this.eraseInDisplay(this.params);
    +            break;
    +
    +          // CSI Ps K  Erase in Line (EL).
    +          case 75:
    +            this.eraseInLine(this.params);
    +            break;
    +
    +          // CSI Pm m  Character Attributes (SGR).
    +          case 109:
    +            this.charAttributes(this.params);
    +            break;
    +
    +          // CSI Ps n  Device Status Report (DSR).
    +          case 110:
    +            this.deviceStatus(this.params);
    +            break;
    +
    +          /**
    +           * Additions
    +           */
    +
    +          // CSI Ps @
    +          // Insert Ps (Blank) Character(s) (default = 1) (ICH).
    +          case 64:
    +            this.insertChars(this.params);
    +            break;
    +
    +          // CSI Ps E
    +          // Cursor Next Line Ps Times (default = 1) (CNL).
    +          case 69:
    +            this.cursorNextLine(this.params);
    +            break;
    +
    +          // CSI Ps F
    +          // Cursor Preceding Line Ps Times (default = 1) (CNL).
    +          case 70:
    +            this.cursorPrecedingLine(this.params);
    +            break;
    +
    +          // CSI Ps G
    +          // Cursor Character Absolute  [column] (default = [row,1]) (CHA).
    +          case 71:
    +            this.cursorCharAbsolute(this.params);
    +            break;
    +
    +          // CSI Ps L
    +          // Insert Ps Line(s) (default = 1) (IL).
    +          case 76:
    +            this.insertLines(this.params);
    +            break;
    +
    +          // CSI Ps M
    +          // Delete Ps Line(s) (default = 1) (DL).
    +          case 77:
    +            this.deleteLines(this.params);
    +            break;
    +
    +          // CSI Ps P
    +          // Delete Ps Character(s) (default = 1) (DCH).
    +          case 80:
    +            this.deleteChars(this.params);
    +            break;
    +
    +          // CSI Ps X
    +          // Erase Ps Character(s) (default = 1) (ECH).
    +          case 88:
    +            this.eraseChars(this.params);
    +            break;
    +
    +          // CSI Pm `  Character Position Absolute
    +          //   [column] (default = [row,1]) (HPA).
    +          case 96:
    +            this.charPosAbsolute(this.params);
    +            break;
    +
    +          // 141 61 a * HPR -
    +          // Horizontal Position Relative
    +          case 97:
    +            this.HPositionRelative(this.params);
    +            break;
    +
    +          // CSI P s c
    +          // Send Device Attributes (Primary DA).
    +          // CSI > P s c
    +          // Send Device Attributes (Secondary DA)
    +          case 99:
    +            this.sendDeviceAttributes(this.params);
    +            break;
    +
    +          // CSI Pm d
    +          // Line Position Absolute  [row] (default = [1,column]) (VPA).
    +          case 100:
    +            this.linePosAbsolute(this.params);
    +            break;
    +
    +          // 145 65 e * VPR - Vertical Position Relative
    +          case 101:
    +            this.VPositionRelative(this.params);
    +            break;
    +
    +          // CSI Ps ; Ps f
    +          //   Horizontal and Vertical Position [row;column] (default =
    +          //   [1,1]) (HVP).
    +          case 102:
    +            this.HVPosition(this.params);
    +            break;
    +
    +          // CSI Pm h  Set Mode (SM).
    +          // CSI ? Pm h - mouse escape codes, cursor escape codes
    +          case 104:
    +            this.setMode(this.params);
    +            break;
    +
    +          // CSI Pm l  Reset Mode (RM).
    +          // CSI ? Pm l
    +          case 108:
    +            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 114:
    +            this.setScrollRegion(this.params);
    +            break;
    +
    +          // CSI s     Save cursor (ANSI.SYS).
    +          case 115:
    +            this.saveCursor(this.params);
    +            break;
    +
    +          // CSI u     Restore cursor (ANSI.SYS).
    +          case 117:
    +            this.restoreCursor(this.params);
    +            break;
    +
    +          /**
    +           * Lesser Used
    +           */
    +
    +          // CSI Ps I
    +          // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
    +          case 73:
    +            this.cursorForwardTab(this.params);
    +            break;
    +
    +          // CSI Ps S  Scroll up Ps lines (default = 1) (SU).
    +          case 83:
    +            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 84:
    +            // if (this.prefix === '>') {
    +            //   this.resetTitleModes(this.params);
    +            //   break;
    +            // }
    +            // if (this.params.length > 1) {
    +            //   this.initMouseTracking(this.params);
    +            //   break;
    +            // }
    +            this.scrollDown(this.params);
    +            break;
    +
    +          // CSI Ps Z
    +          // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
    +          case 90:
    +            this.cursorBackwardTab(this.params);
    +            break;
    +
    +          // CSI Ps b  Repeat the preceding graphic character Ps times (REP).
    +          case 98:
    +            this.repeatPrecedingCharacter(this.params);
    +            break;
    +
    +          // CSI Ps g  Tab Clear (TBC).
    +          // case 103:
    +          //   this.tabClear(this.params);
    +          //   break;
    +
    +          // CSI Pm i  Media Copy (MC).
    +          // CSI ? Pm i
    +          // case 105:
    +          //   this.mediaCopy(this.params);
    +          //   break;
    +
    +          // CSI Pm m  Character Attributes (SGR).
    +          // CSI > Ps; Ps m
    +          // case 109: // 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 110: // 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 112:
    +            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 113:
    +          //   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 114: // 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 115: // 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 116:
    +          //   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 117: // 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 118:
    +          //   if (this.postfix === '$') {
    +          //     this.copyRectagle(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI Pt ; Pl ; Pb ; Pr ' w
    +          // case 119:
    +          //   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 120:
    +          //   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 122:
    +          //   if (this.postfix === '\'') {
    +          //     this.enableLocatorReporting(this.params);
    +          //   } else if (this.postfix === '$') {
    +          //     this.eraseRectangle(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI Pm ' {
    +          // CSI Pt; Pl; Pb; Pr$ {
    +          // case 123:
    +          //   if (this.postfix === '\'') {
    +          //     this.setLocatorEvents(this.params);
    +          //   } else if (this.postfix === '$') {
    +          //     this.selectiveEraseRectangle(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI Ps ' |
    +          // case 124:
    +          //   if (this.postfix === '\'') {
    +          //     this.requestLocatorPosition(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI P m SP }
    +          // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
    +          // case 125:
    +          //   if (this.postfix === ' ') {
    +          //     this.insertColumns(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI P m SP ~
    +          // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
    +          // case 126:
    +          //   if (this.postfix === ' ') {
    +          //     this.deleteColumns(this.params);
    +          //   }
    +          //   break;
    +
    +          default:
    +            console.log(
    +              'Unknown CSI code: %s',
    +              str[i], this.params);
    +            break;
    +        }
    +
    +        this.prefix = '';
    +        this.postfix = '';
    +        break;
    +    }
    +  }
    +
    +  this.getRows(this.y);
    +
    +  if (this.refreshEnd >= this.refreshStart) {
    +    this.refresh(this.refreshStart, this.refreshEnd);
    +  }
    +};
    +
    +Terminal.prototype.writeln = function(str) {
    +  this.write(str + '\r\n');
    +};
    +
    +Terminal.prototype.keyDownHandler = function(ev) {
    +  var str = '';
    +  switch (ev.keyCode) {
    +    // backspace
    +    case 8:
    +      str = '\x7f'; // ^?
    +      //str = '\x08'; // ^H
    +      break;
    +    // tab
    +    case 9:
    +      str = '\t';
    +      break;
    +    // return/enter
    +    case 13:
    +      str = '\r';
    +      break;
    +    // escape
    +    case 27:
    +      str = '\x1b';
    +      break;
    +    // left-arrow
    +    case 37:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOD'; // SS3 as ^O for 7-bit
    +        //str = '\x8fD'; // SS3 as 0x8f for 8-bit
    +        break;
    +      }
    +      str = '\x1b[D';
    +      break;
    +    // right-arrow
    +    case 39:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOC';
    +        break;
    +      }
    +      str = '\x1b[C';
    +      break;
    +    // up-arrow
    +    case 38:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOA';
    +        break;
    +      }
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(-1);
    +      } else {
    +        str = '\x1b[A';
    +      }
    +      break;
    +    // down-arrow
    +    case 40:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOB';
    +        break;
    +      }
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(1);
    +      } else {
    +        str = '\x1b[B';
    +      }
    +      break;
    +    // delete
    +    case 46:
    +      str = '\x1b[3~';
    +      break;
    +    // insert
    +    case 45:
    +      str = '\x1b[2~';
    +      break;
    +    // home
    +    case 36:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOH';
    +        break;
    +      }
    +      str = '\x1bOH';
    +      break;
    +    // end
    +    case 35:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOF';
    +        break;
    +      }
    +      str = '\x1bOF';
    +      break;
    +    // page up
    +    case 33:
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(-(this.rows - 1));
    +      } else {
    +        str = '\x1b[5~';
    +      }
    +      break;
    +    // page down
    +    case 34:
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(this.rows - 1);
    +      } else {
    +        str = '\x1b[6~';
    +      }
    +      break;
    +    // F1
    +    case 112:
    +      str = '\x1bOP';
    +      break;
    +    // F2
    +    case 113:
    +      str = '\x1bOQ';
    +      break;
    +    // F3
    +    case 114:
    +      str = '\x1bOR';
    +      break;
    +    // F4
    +    case 115:
    +      str = '\x1bOS';
    +      break;
    +    // F5
    +    case 116:
    +      str = '\x1b[15~';
    +      break;
    +    // F6
    +    case 117:
    +      str = '\x1b[17~';
    +      break;
    +    // F7
    +    case 118:
    +      str = '\x1b[18~';
    +      break;
    +    // F8
    +    case 119:
    +      str = '\x1b[19~';
    +      break;
    +    // F9
    +    case 120:
    +      str = '\x1b[20~';
    +      break;
    +    // F10
    +    case 121:
    +      str = '\x1b[21~';
    +      break;
    +    // F11
    +    case 122:
    +      str = '\x1b[23~';
    +      break;
    +    // F12
    +    case 123:
    +      str = '\x1b[24~';
    +      break;
    +    default:
    +      // a-z and space
    +      if (ev.ctrlKey) {
    +        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    +          str = String.fromCharCode(ev.keyCode - 64);
    +        } else if (ev.keyCode === 32) {
    +          // NUL
    +          str = String.fromCharCode(0);
    +        } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
    +          // escape, file sep, group sep, record sep, unit sep
    +          str = String.fromCharCode(ev.keyCode - 51 + 27);
    +        } else if (ev.keyCode === 56) {
    +          // delete
    +          str = String.fromCharCode(127);
    +        }
    +      } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) {
    +        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    +          str = '\x1b' + String.fromCharCode(ev.keyCode + 32);
    +        } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {
    +          str = '\x1b' + (ev.keyCode - 48);
    +        }
    +      }
    +      break;
    +  }
    +
    +  if (str) {
    +    cancel(ev);
    +
    +    this.showCursor();
    +    this.keyState = 1;
    +    this.keyStr = str;
    +    this.handler(str);
    +
    +    return false;
    +  } else {
    +    this.keyState = 0;
    +    return true;
    +  }
    +};
    +
    +Terminal.prototype.keyPressHandler = function(ev) {
    +  var str = ''
    +    , key;
    +
    +  cancel(ev);
    +
    +  if (!('charCode' in ev)) {
    +    key = ev.keyCode;
    +    if (this.keyState === 1) {
    +      this.keyState = 2;
    +      return false;
    +    } else if (this.keyState === 2) {
    +      this.showCursor();
    +      this.handler(this.keyStr);
    +      return false;
    +    }
    +  } else {
    +    key = ev.charCode;
    +  }
    +
    +  if (key !== 0) {
    +    if (!ev.ctrlKey
    +        && ((!this.isMac && !ev.altKey)
    +        || (this.isMac && !ev.metaKey))) {
    +      str = String.fromCharCode(key);
    +    }
    +  }
    +
    +  if (str) {
    +    this.showCursor();
    +    this.handler(str);
    +    return false;
    +  } else {
    +    return true;
    +  }
    +};
    +
    +Terminal.prototype.queueChars = function(str) {
    +  var self = this;
    +
    +  this.outputQueue += str;
    +
    +  if (this.outputQueue) {
    +    setTimeout(function() {
    +      self.outputHandler();
    +    }, 1);
    +  }
    +};
    +
    +Terminal.prototype.outputHandler = function() {
    +  if (this.outputQueue) {
    +    this.handler(this.outputQueue);
    +    this.outputQueue = '';
    +  }
    +};
    +
    +Terminal.prototype.bell = function() {
    +  if (!this.useBell) return;
    +  var self = this;
    +  this.element.style.borderColor = 'white';
    +  setTimeout(function() {
    +    self.element.style.borderColor = '';
    +  }, 10);
    +};
    +
    +Terminal.prototype.resize = function(x, y) {
    +  var line
    +    , el
    +    , i
    +    , j;
    +
    +  if (x < 1) x = 1;
    +  if (y < 1) y = 1;
    +
    +  // make sure the cursor stays on screen
    +  if (this.y >= y) this.y = y - 1;
    +  if (this.x >= x) this.x = x - 1;
    +
    +  if (this.cols < x) {
    +    i = this.lines.length;
    +    while (i--) {
    +      while (this.lines[i].length < x) {
    +        this.lines[i].push((this.defAttr << 16) | 32);
    +      }
    +    }
    +  } else if (this.cols > x) {
    +    i = this.lines.length;
    +    while (i--) {
    +      while (this.lines[i].length > x) {
    +        this.lines[i].pop();
    +      }
    +    }
    +  }
    +
    +  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) {
    +        line = document.createElement('div');
    +        line.className = 'term';
    +        el.appendChild(line);
    +        this.children.push(line);
    +      }
    +    }
    +  } else if (j > y) {
    +    while (j-- > y) {
    +      if (this.lines.length > y + this.ybase) {
    +        this.lines.shift();
    +      }
    +      if (this.children.length > y) {
    +        el = this.children.pop();
    +        if (!el) continue;
    +        el.parentNode.removeChild(el);
    +      }
    +    }
    +  }
    +
    +  this.cols = x;
    +  this.rows = y;
    +  this.scrollTop = 0;
    +  this.scrollBottom = y - 1;
    +  this.refreshStart = 0;
    +  this.refreshEnd = y - 1;
    +
    +  this.refresh(0, this.rows - 1);
    +
    +  // it's a real nightmare trying
    +  // to resize the original
    +  // screen buffer. just set it
    +  // to null for now.
    +  this.normal = null;
    +};
    +
    +Terminal.prototype.getRows = function(y) {
    +  this.refreshStart = Math.min(this.refreshStart, y);
    +  this.refreshEnd = Math.max(this.refreshEnd, y);
    +};
    +
    +Terminal.prototype.eraseLine = function(x, y) {
    +  var line, i, ch, row;
    +
    +  row = this.ybase + y;
    +
    +  line = this.lines[row];
    +  // screen:
    +  // ch = 32 | (this.defAttr << 16);
    +  // xterm, linux:
    +  ch = 32 | (this.curAttr << 16);
    +
    +  for (i = x; i < this.cols; i++) {
    +    line[i] = ch;
    +  }
    +
    +  this.getRows(y);
    +};
    +
    +Terminal.prototype.blankLine = function(cur) {
    +  var attr = cur
    +    ? this.curAttr
    +    : this.defAttr;
    +
    +  var ch = 32 | (attr << 16)
    +    , line = []
    +    , i = 0;
    +
    +  for (; i < this.cols; i++) {
    +    line[i] = ch;
    +  }
    +
    +  return line;
    +};
    +
    +/**
    + * ESC
    + */
    +
    +// ESC D Index (IND is 0x84).
    +Terminal.prototype.index = function() {
    +  this.y++;
    +  if (this.y >= this.scrollBottom + 1) {
    +    this.y--;
    +    this.scroll();
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  }
    +  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++;
    +    // echo -ne '\e[1;1H\e[44m\eM\e[0m'
    +    // use this.blankLine(false) for screen behavior
    +    this.lines.splice(this.y + this.ybase, 0, this.blankLine(true));
    +    j = this.rows - 1 - this.scrollBottom;
    +    // add an extra one because we just added a line
    +    // maybe put this above
    +    this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1);
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  }
    +  this.state = normal;
    +};
    +
    +// ESC c Full Reset (RIS).
    +Terminal.prototype.reset = function() {
    +  Terminal.call(this, this.cols, this.rows, this.handler);
    +};
    +
    +/**
    + * CSI
    + */
    +
    +// CSI Ps A
    +// Cursor Up Ps Times (default = 1) (CUU).
    +Terminal.prototype.cursorUp = function(params) {
    +  var param, row;
    +  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, row;
    +  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, row;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  this.x += param;
    +  if (this.x >= this.cols - 1) {
    +    this.x = this.cols - 1;
    +  }
    +};
    +
    +// CSI Ps D
    +// Cursor Backward Ps Times (default = 1) (CUB).
    +Terminal.prototype.cursorBackward = function(params) {
    +  var param, row;
    +  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 param, 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 param, row, j;
    +  switch (params[0] || 0) {
    +    case 0:
    +      this.eraseLine(this.x, this.y);
    +      for (j = this.y + 1; j < this.rows; j++) {
    +        this.eraseLine(0, j);
    +      }
    +      break;
    +    case 1:
    +      this.eraseInLine([1]);
    +      j = this.y;
    +      while (j--) {
    +        this.eraseLine(0, j);
    +      }
    +      break;
    +    case 2:
    +      this.eraseInDisplay([0]);
    +      this.eraseInDisplay([1]);
    +      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] || 0) {
    +    case 0:
    +      this.eraseLine(this.x, this.y);
    +      break;
    +    case 1:
    +      var x = this.x + 1;
    +      var line = this.lines[this.ybase + this.y];
    +      // screen:
    +      //var ch = (this.defAttr << 16) | 32;
    +      // xterm, linux:
    +      var ch = (this.curAttr << 16) | 32;
    +      while (x--) line[x] = ch;
    +      break;
    +    case 2:
    +      var x = this.cols;
    +      var line = this.lines[this.ybase + this.y];
    +      // screen:
    +      //var ch = (this.defAttr << 16) | 32;
    +      // xterm, linux:
    +      var ch = (this.curAttr << 16) | 32;
    +      while (x--) line[x] = ch;
    +      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) {
    +  var i, p;
    +  if (params.length === 0) {
    +    this.curAttr = this.defAttr;
    +  } else {
    +    for (i = 0; i < params.length; i++) {
    +      p = params[i];
    +      if (p >= 30 && p <= 37) {
    +        this.curAttr = (this.curAttr & ~(7 << 3)) | ((p - 30) << 3);
    +      } else if (p >= 40 && p <= 47) {
    +        this.curAttr = (this.curAttr & ~7) | (p - 40);
    +      } else if (p >= 90 && p <= 97) {
    +        this.curAttr = (this.curAttr & ~(7 << 3)) | ((p - 90) << 3);
    +      } else if (p >= 100 && p <= 107) {
    +        this.curAttr = (this.curAttr & ~7) | (p - 100);
    +      } else if (p === 0) {
    +        this.curAttr = this.defAttr;
    +      } else if (p === 1) {
    +        // bold text
    +        this.curAttr = this.curAttr | (1 << 8);
    +      } else if (p === 4) {
    +        // underlined text
    +        this.curAttr = this.curAttr | (4 << 8);
    +      } else if (p === 22) {
    +        // not bold
    +        this.curAttr = this.curAttr & ~(1 << 8);
    +      } else if (p === 24) {
    +        // not underlined
    +        this.curAttr = this.curAttr & ~(4 << 8);
    +      } else if (p === 39) {
    +        // reset fg
    +        p = this.curAttr & 7;
    +        this.curAttr = (this.defAttr & ~7) | p;
    +      } else if (p === 49) {
    +        // reset bg
    +        p = (this.curAttr >> 3) & 7;
    +        this.curAttr = (this.defAttr & ~(7 << 3)) | (p << 3);
    +      }
    +    }
    +  }
    +};
    +
    +// 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 === '?') {
    +    // modern xterm doesnt seem to
    +    // respond to any of these except ?6, 6, and 5
    +    switch (params[0]) {
    +      case 6:
    +        this.queueChars('\x1b['
    +          + (this.y + 1)
    +          + ';'
    +          + (this.x + 1)
    +          + 'R');
    +        break;
    +      case 15:
    +        // no printer
    +        // this.queueChars('\x1b[?11n');
    +        break;
    +      case 25:
    +        // dont support user defined keys
    +        // this.queueChars('\x1b[?21n');
    +        break;
    +      case 26:
    +        // this.queueChars('\x1b[?27;1;0;0n');
    +        break;
    +      case 53:
    +        // no dec locator/mouse
    +        // this.queueChars('\x1b[?50n');
    +        break;
    +    }
    +    return;
    +  }
    +  switch (params[0]) {
    +    case 5:
    +      this.queueChars('\x1b[0n');
    +      break;
    +    case 6:
    +      this.queueChars('\x1b['
    +        + (this.y + 1)
    +        + ';'
    +        + (this.x + 1)
    +        + 'R');
    +      break;
    +  }
    +};
    +
    +/**
    + * Additions
    + */
    +
    +// CSI Ps @
    +// Insert Ps (Blank) Character(s) (default = 1) (ICH).
    +Terminal.prototype.insertChars = function(params) {
    +  var param, row, j;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  row = this.y + this.ybase;
    +  j = this.x;
    +  while (param-- && j < this.cols) {
    +    // screen:
    +    //this.lines[row].splice(j++, 0, (this.defAttr << 16) | 32);
    +    // xterm, linux:
    +    this.lines[row].splice(j++, 0, (this.curAttr << 16) | 32);
    +    this.lines[row].pop();
    +  }
    +};
    +
    +// CSI Ps E
    +// Cursor Next Line Ps Times (default = 1) (CNL).
    +Terminal.prototype.cursorNextLine = function(params) {
    +  var param, row;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  this.y += param;
    +  if (this.y >= this.rows) {
    +    this.y = this.rows - 1;
    +  }
    +  // above is the same as CSI Ps B
    +  this.x = 0;
    +};
    +
    +// CSI Ps F
    +// Cursor Preceding Line Ps Times (default = 1) (CNL).
    +Terminal.prototype.cursorPrecedingLine = function(params) {
    +  var param, row;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  this.y -= param;
    +  if (this.y < 0) this.y = 0;
    +  // above is the same as CSI Ps A
    +  this.x = 0;
    +};
    +
    +// CSI Ps G
    +// Cursor Character Absolute  [column] (default = [row,1]) (CHA).
    +Terminal.prototype.cursorCharAbsolute = function(params) {
    +  var param, row;
    +  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;
    +  // add an extra one because we added one
    +  // above
    +  j = this.rows - 1 + this.ybase - j + 1;
    +
    +  while (param--) {
    +    // this.blankLine(false) for screen behavior
    +    // test: echo -e '\e[44m\e[1L\e[0m'
    +    this.lines.splice(row, 0, this.blankLine(true));
    +    this.lines.splice(j, 1);
    +  }
    +
    +  //this.refresh(0, this.rows - 1);
    +  this.refreshStart = 0;
    +  this.refreshEnd = this.rows - 1;
    +};
    +
    +// 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--) {
    +    // this.blankLine(false) for screen behavior
    +    // test: echo -e '\e[44m\e[1M\e[0m'
    +    this.lines.splice(j + 1, 0, this.blankLine(true));
    +    this.lines.splice(row, 1);
    +  }
    +
    +  //this.refresh(0, this.rows - 1);
    +  this.refreshStart = 0;
    +  this.refreshEnd = this.rows - 1;
    +};
    +
    +// CSI Ps P
    +// Delete Ps Character(s) (default = 1) (DCH).
    +Terminal.prototype.deleteChars = function(params) {
    +  var param, row;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  row = this.y + this.ybase;
    +  while (param--) {
    +    this.lines[row].splice(this.x, 1);
    +    // screen:
    +    //this.lines.push((this.defAttr << 16) | 32);
    +    // xterm, linux:
    +    this.lines.push((this.curAttr << 16) | 32);
    +  }
    +};
    +
    +// CSI Ps X
    +// Erase Ps Character(s) (default = 1) (ECH).
    +Terminal.prototype.eraseChars = function(params) {
    +  var param, row, j;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  row = this.y + this.ybase;
    +  j = this.x;
    +  while (param-- && j < this.cols) {
    +    // screen:
    +    // this.lines[row][j++] = (this.defAttr << 16) | 32;
    +    // xterm, linux:
    +    this.lines[row][j++] = (this.curAttr << 16) | 32;
    +  }
    +};
    +
    +// CSI Pm `  Character Position Absolute
    +//   [column] (default = [row,1]) (HPA).
    +Terminal.prototype.charPosAbsolute = function(params) {
    +  var param, row;
    +  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
    +Terminal.prototype.HPositionRelative = function(params) {
    +  var param, row;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  this.x += param;
    +  if (this.x >= this.cols - 1) {
    +    this.x = this.cols - 1;
    +  }
    +  // above is the same as CSI Ps C
    +};
    +
    +// 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.
    +Terminal.prototype.sendDeviceAttributes = function(params) {
    +  // This severely breaks things if
    +  // TERM is set to `linux`. xterm
    +  // is fine.
    +  return;
    +
    +  if (this.prefix !== '>') {
    +    this.queueChars('\x1b[?1;2c');
    +  } else {
    +    // say we're a vt100 with
    +    // firmware version 95
    +    // this.queueChars('\x1b[>0;95;0c');
    +    // modern xterm responds with:
    +    this.queueChars('\x1b[>0;276;0c');
    +  }
    +};
    +
    +// CSI Pm d
    +// Line Position Absolute  [row] (default = [1,column]) (VPA).
    +Terminal.prototype.linePosAbsolute = function(params) {
    +  var param, row;
    +  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
    +Terminal.prototype.VPositionRelative = function(params) {
    +  var param, row;
    +  param = params[0];
    +  if (param < 1) param = 1;
    +  this.y += param;
    +  if (this.y >= this.rows) {
    +    this.y = this.rows - 1;
    +  }
    +  // above is same as CSI Ps B
    +};
    +
    +// 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') {
    +    while (params.length) this.setMode(params.shift());
    +    return;
    +  }
    +
    +  if (this.prefix !== '?') {
    +    switch (params) {
    +      case 4:
    +        this.insertMode = true;
    +        break;
    +      case 20:
    +        //this.convertEol = true;
    +        break;
    +    }
    +  } else {
    +    switch (params) {
    +      case 1:
    +        this.applicationKeypad = true;
    +        break;
    +      case 6:
    +        this.originMode = true;
    +        break;
    +      case 7:
    +        this.wraparoundMode = true;
    +        break;
    +      case 9: // X10 Mouse
    +        // button press only.
    +        break;
    +      case 1000: // vt200 mouse
    +        // no wheel events, no motion.
    +        // no modifiers except control.
    +        // button press, release.
    +        break;
    +      case 1001: // vt200 highlight mouse
    +        // no wheel events, no motion.
    +        // first event is to send tracking instead
    +        // of button press, *then* button release.
    +        break;
    +      case 1002: // button event mouse
    +      case 1003: // any event mouse
    +        // button press, release, wheel, and motion.
    +        // no modifiers except control.
    +        console.log('binding to mouse events - warning: experimental!');
    +        this.mouseEvents = true;
    +        this.element.style.cursor = 'default';
    +        break;
    +      case 1004: // send focusin/focusout events
    +        // focusin: ^[[>I
    +        // focusout: ^[[>O
    +        break;
    +      case 1005: // utf8 ext mode mouse
    +        // for wide terminals
    +        // simply encodes large values as utf8 characters
    +        break;
    +      case 1006: // sgr ext mode mouse
    +        // for wide terminals
    +        // does not add 32 to fields
    +        // press: ^[[<b;x;yM
    +        // release: ^[[<b;x;ym
    +        break;
    +      case 1015: // urxvt ext mode mouse
    +        // 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
    +          };
    +          this.reset();
    +          this.normal = normal;
    +        }
    +        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 para…
Commits on Apr 4, 2012
  1. [from now] 2012/04/04 22:43:05

    committed Apr 4, 2012
    diff --git a/hub/term.js b/hub/term.js
    index 51777fb..9ad43dc 100644
    --- a/hub/term.js
    +++ b/hub/term.js
    @@ -56,7 +56,6 @@
         this.cursorHidden = false;
         this.convertEol = false;
         this.state = 0;
    -    this.outputQueue = '';
         this.scrollTop = 0;
         this.scrollBottom = this.rows - 1;
    
    @@ -137,310 +136,13 @@
       };
    
       /**
    -   * Global Events for key handling
    -   */
    -
    -  Terminal.bindKeys = function() {
    -    if (Terminal.focus) return;
    -
    -    // We could put an "if (Term.focus)" check
    -    // here, but it shouldn't be necessary.
    -    on(document, 'keydown', function(key) {
    -      return Terminal.focus.keyDownHandler(key);
    -    }, true);
    -
    -    on(document, 'keypress', function(key) {
    -      return Terminal.focus.keyPressHandler(key);
    -    }, true);
    -  };
    -
    -  /**
        * Open Terminal
        */
    
       Terminal.prototype.open = function() {
    -    var self = this
    -    , i = 0
    -    , div;
    -
    -    this.element = document.createElement('div');
    -    this.element.className = 'terminal';
    -    this.children = [];
    -
    -    for (; i < this.rows; i++) {
    -      div = document.createElement('div');
    -      this.element.appendChild(div);
    -      this.children.push(div);
    -    }
    -
    -    document.body.appendChild(this.element);
    -
         this.refresh(0, this.rows - 1);
    
    -    Terminal.bindKeys();
         this.focus();
    -
    -    this.startBlink();
    -
    -    on(this.element, 'mousedown', function() {
    -      self.focus();
    -    });
    -
    -    // This probably shouldn't work,
    -    // ... but it does. Firefox's paste
    -    // event seems to only work for textareas?
    -    on(this.element, 'mousedown', function(ev) {
    -      var button = ev.button != null
    -            ? +ev.button
    -            : ev.which != null
    -            ? ev.which - 1
    -            : null;
    -
    -      // Does IE9 do this?
    -      if (~navigator.userAgent.indexOf('MSIE')) {
    -        button = button === 1 ? 0 : button === 4 ? 1 : button;
    -      }
    -
    -      if (button !== 2) return;
    -
    -      self.element.contentEditable = 'true';
    -      setTimeout(function() {
    -        self.element.contentEditable = 'inherit'; // 'false';
    -      }, 1);
    -    }, true);
    -
    -    on(this.element, 'paste', function(ev) {
    -      if (ev.clipboardData) {
    -        self.queueChars(ev.clipboardData.getData('text/plain'));
    -      } else if (window.clipboardData) {
    -        self.queueChars(window.clipboardData.getData('Text'));
    -      }
    -      // Not necessary. Do it anyway for good measure.
    -      self.element.contentEditable = 'inherit';
    -      return cancel(ev);
    -    });
    -
    -    this.bindMouse();
    -
    -    // XXX - hack, move this somewhere else.
    -    if (Terminal.brokenBold == null) {
    -      Terminal.brokenBold = isBoldBroken();
    -    }
    -
    -    // sync default bg/fg colors
    -    this.element.style.backgroundColor = Terminal.colors[16];
    -    this.element.style.color = Terminal.colors[17];
    -
    -    // otherwise:
    -    // Terminal.colors[16] = css(this.element, 'background-color');
    -    // Terminal.colors[17] = css(this.element, 'color');
    -  };
    -
    -  // 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 window
    -          ? '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);
    -    }
    -
    -    // send a mouse event:
    -    // ^[[M Cb Cx Cy
    -    function sendEvent(button, pos) {
    -      self.queueChars('\x1b[M' + String.fromCharCode(button, pos.x, pos.y));
    -    }
    -
    -    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 (~navigator.userAgent.indexOf('MSIE')) {
    -          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;
    -      }
    -
    -      // 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;
    -
    -      // increment to SP
    -      button = (32 + (mod << 2)) + button;
    -
    -      return button;
    -    }
    -
    -    // 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 !== document.documentElement) {
    -        x -= el.offsetLeft;
    -        y -= el.offsetTop;
    -        el = el.parentNode;
    -      }
    -
    -      // convert to cols/rows
    -      w = self.element.clientWidth;
    -      h = self.element.clientHeight;
    -      x = ((x / w) * self.cols) | 0;
    -      y = ((y / h) * self.rows) | 0;
    -
    -      // 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 };
    -    }
    -
    -    on(el, 'mousedown', function(ev) {
    -      if (!self.mouseEvents) return;
    -
    -      // send the button
    -      sendButton(ev);
    -
    -      // ensure focus
    -      self.focus();
    -
    -      // bind events
    -      on(document, 'mousemove', sendMove);
    -      on(document, 'mouseup', function up(ev) {
    -        sendButton(ev);
    -        off(document, 'mousemove', sendMove);
    -        off(document, 'mouseup', up);
    -        return cancel(ev);
    -      });
    -
    -      return cancel(ev);
    -    });
    -
    -    on(el, wheelEvent, function(ev) {
    -      if (!self.mouseEvents) return;
    -      sendButton(ev);
    -      return 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 ? -5 : 5);
    -      } else {
    -        self.scrollDisp(ev.wheelDeltaY > 0 ? -5 : 5);
    -      }
    -      return cancel(ev);
    -    });
       };
    
       /**
    @@ -473,11 +175,6 @@
    
         width = this.cols;
    
    -    if (end - start === this.rows - 1) {
    -      parent = this.element.parentNode;
    -      if (parent) parent.removeChild(this.element);
    -    }
    -
         for (y = start; y <= end; y++) {
           row = y + this.ydisp;
    
    @@ -571,11 +268,7 @@
           if (attr !== this.defAttr) {
             out += '</span>';
           }
    -
    -      this.children[y].innerHTML = out;
         }
    -
    -    if (parent) parent.appendChild(this.element);
       };
    
       Terminal.prototype.cursorBlink = function() {
    @@ -1355,272 +1048,8 @@
         }
       };
    
    -  Terminal.prototype.writeln = function(str) {
    -    this.write(str + '\r\n');
    -  };
    -
    -  Terminal.prototype.keyDownHandler = function(ev) {
    -    var str = '';
    -    switch (ev.keyCode) {
    -      // backspace
    -    case 8:
    -      str = '\x7f'; // ^?
    -      //str = '\x08'; // ^H
    -      break;
    -      // tab
    -    case 9:
    -      str = '\t';
    -      break;
    -      // return/enter
    -    case 13:
    -      str = '\r';
    -      break;
    -      // escape
    -    case 27:
    -      str = '\x1b';
    -      break;
    -      // left-arrow
    -    case 37:
    -      if (this.applicationKeypad) {
    -        str = '\x1bOD'; // SS3 as ^[O for 7-bit
    -        //str = '\x8fD'; // SS3 as 0x8f for 8-bit
    -        break;
    -      }
    -      str = '\x1b[D';
    -      break;
    -      // right-arrow
    -    case 39:
    -      if (this.applicationKeypad) {
    -        str = '\x1bOC';
    -        break;
    -      }
    -      str = '\x1b[C';
    -      break;
    -      // up-arrow
    -    case 38:
    -      if (this.applicationKeypad) {
    -        str = '\x1bOA';
    -        break;
    -      }
    -      if (ev.ctrlKey) {
    -        this.scrollDisp(-1);
    -        return cancel(ev);
    -      } else {
    -        str = '\x1b[A';
    -      }
    -      break;
    -      // down-arrow
    -    case 40:
    -      if (this.applicationKeypad) {
    -        str = '\x1bOB';
    -        break;
    -      }
    -      if (ev.ctrlKey) {
    -        this.scrollDisp(1);
    -        return cancel(ev);
    -      } else {
    -        str = '\x1b[B';
    -      }
    -      break;
    -      // delete
    -    case 46:
    -      str = '\x1b[3~';
    -      break;
    -      // insert
    -    case 45:
    -      str = '\x1b[2~';
    -      break;
    -      // home
    -    case 36:
    -      if (this.applicationKeypad) {
    -        str = '\x1bOH';
    -        break;
    -      }
    -      str = '\x1bOH';
    -      break;
    -      // end
    -    case 35:
    -      if (this.applicationKeypad) {
    -        str = '\x1bOF';
    -        break;
    -      }
    -      str = '\x1bOF';
    -      break;
    -      // page up
    -    case 33:
    -      if (ev.shiftKey) {
    -        this.scrollDisp(-(this.rows - 1));
    -        return cancel(ev);
    -      } else {
    -        str = '\x1b[5~';
    -      }
    -      break;
    -      // page down
    -    case 34:
    -      if (ev.shiftKey) {
    -        this.scrollDisp(this.rows - 1);
    -        return cancel(ev);
    -      } else {
    -        str = '\x1b[6~';
    -      }
    -      break;
    -      // F1
    -    case 112:
    -      str = '\x1bOP';
    -      break;
    -      // F2
    -    case 113:
    -      str = '\x1bOQ';
    -      break;
    -      // F3
    -    case 114:
    -      str = '\x1bOR';
    -      break;
    -      // F4
    -    case 115:
    -      str = '\x1bOS';
    -      break;
    -      // F5
    -    case 116:
    -      str = '\x1b[15~';
    -      break;
    -      // F6
    -    case 117:
    -      str = '\x1b[17~';
    -      break;
    -      // F7
    -    case 118:
    -      str = '\x1b[18~';
    -      break;
    -      // F8
    -    case 119:
    -      str = '\x1b[19~';
    -      break;
    -      // F9
    -    case 120:
    -      str = '\x1b[20~';
    -      break;
    -      // F10
    -    case 121:
    -      str = '\x1b[21~';
    -      break;
    -      // F11
    -    case 122:
    -      str = '\x1b[23~';
    -      break;
    -      // F12
    -    case 123:
    -      str = '\x1b[24~';
    -      break;
    -    default:
    -      // a-z and space
    -      if (ev.ctrlKey) {
    -        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    -          str = String.fromCharCode(ev.keyCode - 64);
    -        } else if (ev.keyCode === 32) {
    -          // NUL
    -          str = String.fromCharCode(0);
    -        } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
    -          // escape, file sep, group sep, record sep, unit sep
    -          str = String.fromCharCode(ev.keyCode - 51 + 27);
    -        } else if (ev.keyCode === 56) {
    -          // delete
    -          str = String.fromCharCode(127);
    -        } else if (ev.keyCode === 219) {
    -          // ^[ - escape
    -          str = String.fromCharCode(27);
    -        } else if (ev.keyCode === 221) {
    -          // ^] - group sep
    -          str = String.fromCharCode(29);
    -        }
    -      } else if ((!isMac && ev.altKey) || (isMac && ev.metaKey)) {
    -        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    -          str = '\x1b' + String.fromCharCode(ev.keyCode + 32);
    -        } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {
    -          str = '\x1b' + (ev.keyCode - 48);
    -        }
    -      }
    -      break;
    -    }
    -
    -    if (str) {
    -      cancel(ev);
    -
    -      this.showCursor();
    -      this.keyState = 1;
    -      this.keyStr = str;
    -      this.handler(str);
    -
    -      return false;
    -    } else {
    -      this.keyState = 0;
    -      return true;
    -    }
    -  };
    -
    -  Terminal.prototype.keyPressHandler = function(ev) {
    -    var str = ''
    -    , key;
    -
    -    cancel(ev);
    -
    -    if (!('charCode' in ev)) {
    -      key = ev.keyCode;
    -      if (this.keyState === 1) {
    -        this.keyState = 2;
    -        return false;
    -      } else if (this.keyState === 2) {
    -        this.showCursor();
    -        this.handler(this.keyStr);
    -        return false;
    -      }
    -    } else {
    -      key = ev.charCode;
    -    }
    -
    -    if (key !== 0) {
    -      if (!ev.ctrlKey
    -          && ((!isMac && !ev.altKey)
    -              || (isMac && !ev.metaKey))) {
    -        str = String.fromCharCode(key);
    -      }
    -    }
    -
    -    if (str) {
    -      this.showCursor();
    -      this.handler(str);
    -      return false;
    -    } else {
    -      return true;
    -    }
    -  };
    -
    -  Terminal.prototype.queueChars = function(str) {
    -    var self = this;
    -
    -    this.outputQueue += str;
    -
    -    if (this.outputQueue) {
    -      setTimeout(function() {
    -        self.outputHandler();
    -      }, 1);
    -    }
    -  };
    -
    -  Terminal.prototype.outputHandler = function() {
    -    if (this.outputQueue) {
    -      this.handler(this.outputQueue);
    -      this.outputQueue = '';
    -    }
    -  };
    -
       Terminal.prototype.bell = function() {
         if (!Terminal.visualBell) return;
    -    var self = this;
    -    this.element.style.borderColor = 'white';
    -    setTimeout(function() {
    -      self.element.style.borderColor = '';
    -    }, 10);
         if (Terminal.popOnBell) this.focus();
       };
    
    @@ -1655,27 +1084,16 @@
         // 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) {
    -          line = document.createElement('div');
    -          el.appendChild(line);
    -          this.children.push(line);
    -        }
           }
         } 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;
    @@ -2488,9 +1906,7 @@
           case 1003: // any event mouse
             // button press, release, wheel, and motion.
             // no modifiers except control.
    -        console.log('binding to mouse events - warning: experimental!');
             this.mouseEvents = true;
    -        this.element.style.cursor = 'default';
             break;
           case 1004: // send focusin/focusout events
             // focusin: ^[[>I
    @@ -2652,7 +2068,6 @@
           case 1004:
           case 1005:
             this.mouseEvents = false;
    -        this.element.style.cursor = '';
             break;
           case 25: // hide cursor
             this.cursorHidden = true;
    @@ -2756,14 +2171,6 @@
         this.refreshEnd = this.rows - 1;
       };
    
    -  // 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) {
    -    console.log('mouse tracking');
    -  };
    -
       // 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-
    @@ -3326,21 +2733,6 @@
         return false;
       }
    
    -  var isMac = ~navigator.userAgent.indexOf('Mac');
    -
    -  // if bold is broken, we can't
    -  // use it in the terminal.
    -  function isBoldBroken() {
    -    var el = document.createElement('span');
    -    el.innerHTML = 'hello world';
    -    document.body.appendChild(el);
    -    var w1 = el.scrollWidth;
    -    el.style.fontWeight = 'bold';
    -    var w2 = el.scrollWidth;
    -    document.body.removeChild(el);
    -    return w1 !== w2;
    -  }
    -
       /**
        * Expose
        */
  2. [from now] 2012/04/04 16:45:02

    committed Apr 4, 2012
    diff --git a/caster/index.js b/caster/index.js
    index 909bf1b..5eb547c 100644
    --- a/caster/index.js
    +++ b/caster/index.js
    @@ -2,7 +2,7 @@ var tty = require('tty'),
         pty = require('pty.js');
    
     var term = pty.spawn(process.env.SHELL, [], {
    -  name: 'xterm-color',
    +  name: 'xterm',
       cols: 80,
       rows: 30,
       cwd: process.env.HOME
    diff --git a/hub/index.js b/hub/index.js
    new file mode 100644
    index 0000000..f197484
    --- /dev/null
    +++ b/hub/index.js
    @@ -0,0 +1,13 @@
    +var term = require('./term.js');
    +
    +module.exports = (function () {
    +  function Hub () {
    +
    +  }
    +
    +  Hub.prototype.snapshot = function () {
    +
    +  };
    +
    +  return Hub;
    +})();
    \ No newline at end of file
    diff --git a/hub/term.js b/hub/term.js
    new file mode 100644
    index 0000000..51777fb
    --- /dev/null
    +++ b/hub/term.js
    @@ -0,0 +1,3350 @@
    +/**
    + * tty.js - an xterm emulator
    + * Christopher Jeffrey (https://github.com/chjj/tty.js)
    + *
    + * Originally forked from (with the author's permission):
    + *
    + * Fabrice Bellard's javascript vt100 for jslinux:
    + * http://bellard.org/jslinux/
    + * Copyright (c) 2011 Fabrice Bellard
    + * (Redistribution or commercial use is prohibited
    + *  without the author's permission.)
    + *
    + * The original design remains. The terminal itself
    + * has been extended to include xterm CSI codes, among
    + * other features.
    + */
    +
    +;(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';
    +
    +  /**
    +   * States
    +   */
    +
    +  var normal = 0
    +  , escaped = 1
    +  , csi = 2
    +  , osc = 3
    +  , charset = 4;
    +
    +  /**
    +   * Terminal
    +   */
    +
    +  var Terminal = function(cols, rows, handler) {
    +    this.cols = cols;
    +    this.rows = rows;
    +    this.handler = handler;
    +    this.ybase = 0;
    +    this.ydisp = 0;
    +    this.x = 0;
    +    this.y = 0;
    +    this.cursorState = 0;
    +    this.cursorHidden = false;
    +    this.convertEol = false;
    +    this.state = 0;
    +    this.outputQueue = '';
    +    this.scrollTop = 0;
    +    this.scrollBottom = this.rows - 1;
    +
    +    this.applicationKeypad = false;
    +    this.originMode = false;
    +    this.insertMode = false;
    +    this.wraparoundMode = false;
    +    this.mouseEvents;
    +    this.tabs = [];
    +    this.charset = null;
    +    this.normal = null;
    +
    +    this.defAttr = 16 | (17 << 5);
    +    this.curAttr = this.defAttr;
    +    this.keyState = 0;
    +    this.keyStr = '';
    +
    +    this.params = [];
    +    this.currentParam = 0;
    +
    +    var i = this.rows - 1;
    +    this.lines = [ this.blankLine() ];
    +    while (i--) {
    +      this.lines.push(this.lines[0].slice());
    +    }
    +  };
    +
    +  /**
    +   * Options
    +   */
    +
    +  Terminal.colors = [
    +    // dark:
    +    '#2e3436',
    +    '#cc0000',
    +    '#4e9a06',
    +    '#c4a000',
    +    '#3465a4',
    +    '#75507b',
    +    '#06989a',
    +    '#d3d7cf',
    +    // bright:
    +    '#555753',
    +    '#ef2929',
    +    '#8ae234',
    +    '#fce94f',
    +    '#729fcf',
    +    '#ad7fa8',
    +    '#34e2e2',
    +    '#eeeeec',
    +    // default bg/fg:
    +    '#000000',
    +    '#f0f0f0'
    +  ];
    +
    +  Terminal.termName = '';
    +  Terminal.geometry = [80, 30];
    +  Terminal.cursorBlink = true;
    +  Terminal.visualBell = false;
    +  Terminal.popOnBell = false;
    +  Terminal.scrollback = 1000;
    +  Terminal.screenKeys = false;
    +
    +  /**
    +   * Focused Terminal
    +   */
    +
    +  Terminal.focus = null;
    +
    +  Terminal.prototype.focus = function() {
    +    if (Terminal.focus === this) return;
    +    if (Terminal.focus) {
    +      Terminal.focus.cursorState = 0;
    +      Terminal.focus.refresh(Terminal.focus.y, Terminal.focus.y);
    +    }
    +    Terminal.focus = this;
    +    this.showCursor();
    +  };
    +
    +  /**
    +   * Global Events for key handling
    +   */
    +
    +  Terminal.bindKeys = function() {
    +    if (Terminal.focus) return;
    +
    +    // We could put an "if (Term.focus)" check
    +    // here, but it shouldn't be necessary.
    +    on(document, 'keydown', function(key) {
    +      return Terminal.focus.keyDownHandler(key);
    +    }, true);
    +
    +    on(document, 'keypress', function(key) {
    +      return Terminal.focus.keyPressHandler(key);
    +    }, true);
    +  };
    +
    +  /**
    +   * Open Terminal
    +   */
    +
    +  Terminal.prototype.open = function() {
    +    var self = this
    +    , i = 0
    +    , div;
    +
    +    this.element = document.createElement('div');
    +    this.element.className = 'terminal';
    +    this.children = [];
    +
    +    for (; i < this.rows; i++) {
    +      div = document.createElement('div');
    +      this.element.appendChild(div);
    +      this.children.push(div);
    +    }
    +
    +    document.body.appendChild(this.element);
    +
    +    this.refresh(0, this.rows - 1);
    +
    +    Terminal.bindKeys();
    +    this.focus();
    +
    +    this.startBlink();
    +
    +    on(this.element, 'mousedown', function() {
    +      self.focus();
    +    });
    +
    +    // This probably shouldn't work,
    +    // ... but it does. Firefox's paste
    +    // event seems to only work for textareas?
    +    on(this.element, 'mousedown', function(ev) {
    +      var button = ev.button != null
    +            ? +ev.button
    +            : ev.which != null
    +            ? ev.which - 1
    +            : null;
    +
    +      // Does IE9 do this?
    +      if (~navigator.userAgent.indexOf('MSIE')) {
    +        button = button === 1 ? 0 : button === 4 ? 1 : button;
    +      }
    +
    +      if (button !== 2) return;
    +
    +      self.element.contentEditable = 'true';
    +      setTimeout(function() {
    +        self.element.contentEditable = 'inherit'; // 'false';
    +      }, 1);
    +    }, true);
    +
    +    on(this.element, 'paste', function(ev) {
    +      if (ev.clipboardData) {
    +        self.queueChars(ev.clipboardData.getData('text/plain'));
    +      } else if (window.clipboardData) {
    +        self.queueChars(window.clipboardData.getData('Text'));
    +      }
    +      // Not necessary. Do it anyway for good measure.
    +      self.element.contentEditable = 'inherit';
    +      return cancel(ev);
    +    });
    +
    +    this.bindMouse();
    +
    +    // XXX - hack, move this somewhere else.
    +    if (Terminal.brokenBold == null) {
    +      Terminal.brokenBold = isBoldBroken();
    +    }
    +
    +    // sync default bg/fg colors
    +    this.element.style.backgroundColor = Terminal.colors[16];
    +    this.element.style.color = Terminal.colors[17];
    +
    +    // otherwise:
    +    // Terminal.colors[16] = css(this.element, 'background-color');
    +    // Terminal.colors[17] = css(this.element, 'color');
    +  };
    +
    +  // 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 window
    +          ? '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);
    +    }
    +
    +    // send a mouse event:
    +    // ^[[M Cb Cx Cy
    +    function sendEvent(button, pos) {
    +      self.queueChars('\x1b[M' + String.fromCharCode(button, pos.x, pos.y));
    +    }
    +
    +    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 (~navigator.userAgent.indexOf('MSIE')) {
    +          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;
    +      }
    +
    +      // 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;
    +
    +      // increment to SP
    +      button = (32 + (mod << 2)) + button;
    +
    +      return button;
    +    }
    +
    +    // 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 !== document.documentElement) {
    +        x -= el.offsetLeft;
    +        y -= el.offsetTop;
    +        el = el.parentNode;
    +      }
    +
    +      // convert to cols/rows
    +      w = self.element.clientWidth;
    +      h = self.element.clientHeight;
    +      x = ((x / w) * self.cols) | 0;
    +      y = ((y / h) * self.rows) | 0;
    +
    +      // 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 };
    +    }
    +
    +    on(el, 'mousedown', function(ev) {
    +      if (!self.mouseEvents) return;
    +
    +      // send the button
    +      sendButton(ev);
    +
    +      // ensure focus
    +      self.focus();
    +
    +      // bind events
    +      on(document, 'mousemove', sendMove);
    +      on(document, 'mouseup', function up(ev) {
    +        sendButton(ev);
    +        off(document, 'mousemove', sendMove);
    +        off(document, 'mouseup', up);
    +        return cancel(ev);
    +      });
    +
    +      return cancel(ev);
    +    });
    +
    +    on(el, wheelEvent, function(ev) {
    +      if (!self.mouseEvents) return;
    +      sendButton(ev);
    +      return 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 ? -5 : 5);
    +      } else {
    +        self.scrollDisp(ev.wheelDeltaY > 0 ? -5 : 5);
    +      }
    +      return cancel(ev);
    +    });
    +  };
    +
    +  /**
    +   * Rendering Engine
    +   */
    +
    +  // In the screen buffer, each character
    +  // is stored as a 32-bit integer.
    +  // First 16 bits: a utf-16 character.
    +  // Next 5 bits: background color (0-31).
    +  // Next 5 bits: foreground color (0-31).
    +  // Next 6 bits: a mask for misc. flags:
    +  //   1=bold, 2=underline, 4=inverse
    +
    +  Terminal.prototype.refresh = function(start, end) {
    +    var x
    +    , y
    +    , i
    +    , line
    +    , out
    +    , ch
    +    , width
    +    , data
    +    , attr
    +    , fgColor
    +    , bgColor
    +    , flags
    +    , row
    +    , parent;
    +
    +    width = this.cols;
    +
    +    if (end - start === this.rows - 1) {
    +      parent = this.element.parentNode;
    +      if (parent) parent.removeChild(this.element);
    +    }
    +
    +    for (y = start; 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;
    +
    +      for (i = 0; i < width; i++) {
    +        ch = line[i];
    +        data = ch >> 16;
    +        ch &= 0xffff;
    +        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">';
    +            } else {
    +              out += '<span style="';
    +              fgColor = (data >> 5) & 31;
    +              bgColor = data & 31;
    +              flags = data >> 10;
    +
    +              if (flags & 1) {
    +                out += 'font-weight:bold;';
    +                // see: XTerm*boldColors
    +                if (fgColor < 8) fgColor += 8;
    +              }
    +
    +              if (flags & 2) {
    +                out += 'text-decoration:underline;';
    +              }
    +
    +              if (fgColor !== 17) {
    +                out += 'color:'
    +                  + Terminal.colors[fgColor]
    +                  + ';';
    +              }
    +
    +              if (bgColor !== 16) {
    +                out += 'background-color:'
    +                  + Terminal.colors[bgColor]
    +                  + ';';
    +              }
    +
    +              out += '">';
    +            }
    +          }
    +        }
    +
    +        switch (ch) {
    +        case 32:
    +          out += '&nbsp;';
    +          break;
    +        case 38:
    +          out += '&amp;';
    +          break;
    +        case 60:
    +          out += '&lt;';
    +          break;
    +        case 62:
    +          out += '&gt;';
    +          break;
    +        default:
    +          if (ch < 32) {
    +            out += '&nbsp;';
    +          } else {
    +            out += String.fromCharCode(ch);
    +          }
    +          break;
    +        }
    +
    +        attr = data;
    +      }
    +
    +      if (attr !== this.defAttr) {
    +        out += '</span>';
    +      }
    +
    +      this.children[y].innerHTML = out;
    +    }
    +
    +    if (parent) parent.appendChild(this.element);
    +  };
    +
    +  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 (!Terminal.cursorBlink) return;
    +    var self = this;
    +    this._blinker = function() {
    +      self.cursorBlink();
    +    };
    +    this._blink = setInterval(this._blinker, 500);
    +  };
    +
    +  Terminal.prototype.refreshBlink = function() {
    +    if (!Terminal.cursorBlink) return;
    +    clearInterval(this._blink);
    +    this._blink = setInterval(this._blinker, 500);
    +  };
    +
    +  Terminal.prototype.scroll = function() {
    +    var row;
    +
    +    if (++this.ybase === Terminal.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;
    +
    +    // potential optimization
    +    // if (row === this.lines.length) {
    +    //   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);
    +    }
    +  };
    +
    +  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(str) {
    +    // console.log(JSON.stringify(str.replace(/\x1b/g, '^[')));
    +
    +    var l = str.length
    +    , i = 0
    +    , ch
    +    , param
    +    , row;
    +
    +    this.refreshStart = this.rows;
    +    this.refreshEnd = -1;
    +    this.getRows(this.y);
    +
    +    if (this.ybase !== this.ydisp) {
    +      this.ydisp = this.ybase;
    +      this.refreshStart = 0;
    +      this.refreshEnd = this.rows - 1;
    +    }
    +
    +    for (; i < l; i++) {
    +      ch = str.charCodeAt(i);
    +      switch (this.state) {
    +      case normal:
    +        switch (ch) {
    +          // '\0'
    +        case 0:
    +          break;
    +
    +          // '\a'
    +        case 7:
    +          this.bell();
    +          break;
    +
    +          // '\n', '\v', '\f'
    +        case 10:
    +        case 11:
    +        case 12:
    +          if (this.convertEol) {
    +            this.x = 0;
    +          }
    +          this.y++;
    +          if (this.y >= this.scrollBottom + 1) {
    +            this.y--;
    +            this.scroll();
    +            this.refreshStart = 0;
    +            this.refreshEnd = this.rows - 1;
    +          }
    +          break;
    +
    +          // '\r'
    +        case 13:
    +          this.x = 0;
    +          break;
    +
    +          // '\b'
    +        case 8:
    +          if (this.x > 0) {
    +            this.x--;
    +          }
    +          break;
    +
    +          // '\t'
    +        case 9:
    +          // should check tabstops
    +          param = (this.x + 8) & ~7;
    +          if (param <= this.cols) {
    +            this.x = param;
    +          }
    +          break;
    +
    +          // '\e'
    +        case 27:
    +          this.state = escaped;
    +          break;
    +
    +        default:
    +          // ' '
    +          if (ch >= 32) {
    +            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 + 1) {
    +                this.y--;
    +                this.scroll();
    +                this.refreshStart = 0;
    +                this.refreshEnd = this.rows - 1;
    +              }
    +            }
    +            row = this.y + this.ybase;
    +            this.lines[row][this.x] = (ch & 0xffff) | (this.curAttr << 16);
    +            this.x++;
    +            this.getRows(this.y);
    +          }
    +          break;
    +        }
    +        break;
    +      case escaped:
    +        switch (str[i]) {
    +          // 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.state = osc;
    +          break;
    +
    +          // ESC _ Application Program Command ( APC is 0x9f).
    +        case '_':
    +          this.state = osc;
    +          break;
    +
    +          // ESC ^ Privacy Message ( PM is 0x9e).
    +        case '^':
    +          this.state = osc;
    +          break;
    +
    +          // ESC c Full Reset (RIS).
    +        case 'c':
    +          this.reset();
    +          break;
    +
    +          // ESC E Next Line ( NEL is 0x85).
    +          // ESC D Index ( IND is 0x84).
    +        case 'E':
    +          this.x = 0;
    +          ;
    +        case 'D':
    +          this.index();
    +          break;
    +
    +          // ESC M Reverse Index ( RI is 0x8d).
    +        case 'M':
    +          this.reverseIndex();
    +          break;
    +
    +          // ESC % Select default/utf-8 character set.
    +          // @ = default, G = utf-8
    +        case '%':
    +          this.charset = null;
    +          this.state = normal;
    +          i++;
    +          break;
    +
    +          // ESC (,),*,+,-,. Designate G0-G2 Character Set.
    +        case '(': // <-- this seems to get all the attention
    +        case ')':
    +        case '*':
    +        case '+':
    +        case '-':
    +        case '.':
    +          this.state = charset;
    +          break;
    +
    +          // Designate G3 Character Set (VT300).
    +          // A = ISO Latin-1 Supplemental.
    +          // Not implemented.
    +        case '/':
    +          this.charset = null;
    +          this.state = normal;
    +          i++;
    +          break;
    +
    +          // ESC 7 Save Cursor (DECSC).
    +        case '7':
    +          this.saveCursor();
    +          this.state = normal;
    +          break;
    +
    +          // ESC 8 Restore Cursor (DECRC).
    +        case '8':
    +          this.restoreCursor();
    +          this.state = normal;
    +          break;
    +
    +          // ESC # 3 DEC line height/width
    +        case '#':
    +          this.state = normal;
    +          i++;
    +          break;
    +
    +          // ESC H Tab Set ( HTS is 0x88).
    +        case 'H':
    +          // this.tabSet(this.x);
    +          this.state = normal;
    +          break;
    +
    +          // ESC = Application Keypad (DECPAM).
    +        case '=':
    +          console.log('Serial port requested application keypad.');
    +          this.applicationKeypad = true;
    +          this.state = normal;
    +          break;
    +
    +          // ESC > Normal Keypad (DECPNM).
    +        case '>':
    +          console.log('Switching back to normal keypad.');
    +          this.applicationKeypad = false;
    +          this.state = normal;
    +          break;
    +
    +        default:
    +          this.state = normal;
    +          console.log('Unknown ESC control: ' + str[i] + '.');
    +          break;
    +        }
    +        break;
    +
    +      case charset:
    +        switch (str[i]) {
    +          // DEC Special Character and Line Drawing Set.
    +        case '0':
    +          this.charset = SCLD;
    +          break;
    +          // United States (USASCII).
    +        case 'B':
    +        default:
    +          this.charset = null;
    +          break;
    +        }
    +        this.state = normal;
    +        break;
    +
    +      case osc:
    +        if (ch !== 27 && ch !== 7) break;
    +        console.log('Unknown OSC code.');
    +        this.state = normal;
    +        // increment for the trailing slash in ST
    +        if (ch === 27) i++;
    +        break;
    +
    +      case csi:
    +        // '?', '>', '!'
    +        if (ch === 63 || ch === 62 || ch === 33) {
    +          this.prefix = str[i];
    +          break;
    +        }
    +
    +        // 0 - 9
    +        if (ch >= 48 && ch <= 57) {
    +          this.currentParam = this.currentParam * 10 + ch - 48;
    +          break;
    +        }
    +
    +        // '$', '"', ' ', '\''
    +        if (ch === 36 || ch === 34 || ch === 32 || ch === 39) {
    +          this.postfix = str[i];
    +          break;
    +        }
    +
    +        this.params[this.params.length] = this.currentParam;
    +        this.currentParam = 0;
    +
    +        // ';'
    +        if (ch === 59) break;
    +
    +        this.state = normal;
    +
    +        switch (ch) {
    +          // CSI Ps A
    +          // Cursor Up Ps Times (default = 1) (CUU).
    +        case 65:
    +          this.cursorUp(this.params);
    +          break;
    +
    +          // CSI Ps B
    +          // Cursor Down Ps Times (default = 1) (CUD).
    +        case 66:
    +          this.cursorDown(this.params);
    +          break;
    +
    +          // CSI Ps C
    +          // Cursor Forward Ps Times (default = 1) (CUF).
    +        case 67:
    +          this.cursorForward(this.params);
    +          break;
    +
    +          // CSI Ps D
    +          // Cursor Backward Ps Times (default = 1) (CUB).
    +        case 68:
    +          this.cursorBackward(this.params);
    +          break;
    +
    +          // CSI Ps ; Ps H
    +          // Cursor Position [row;column] (default = [1,1]) (CUP).
    +        case 72:
    +          this.cursorPos(this.params);
    +          break;
    +
    +          // CSI Ps J  Erase in Display (ED).
    +        case 74:
    +          this.eraseInDisplay(this.params);
    +          break;
    +
    +          // CSI Ps K  Erase in Line (EL).
    +        case 75:
    +          this.eraseInLine(this.params);
    +          break;
    +
    +          // CSI Pm m  Character Attributes (SGR).
    +        case 109:
    +          this.charAttributes(this.params);
    +          break;
    +
    +          // CSI Ps n  Device Status Report (DSR).
    +        case 110:
    +          this.deviceStatus(this.params);
    +          break;
    +
    +          /**
    +           * Additions
    +           */
    +
    +          // CSI Ps @
    +          // Insert Ps (Blank) Character(s) (default = 1) (ICH).
    +        case 64:
    +          this.insertChars(this.params);
    +          break;
    +
    +          // CSI Ps E
    +          // Cursor Next Line Ps Times (default = 1) (CNL).
    +        case 69:
    +          this.cursorNextLine(this.params);
    +          break;
    +
    +          // CSI Ps F
    +          // Cursor Preceding Line Ps Times (default = 1) (CNL).
    +        case 70:
    +          this.cursorPrecedingLine(this.params);
    +          break;
    +
    +          // CSI Ps G
    +          // Cursor Character Absolute  [column] (default = [row,1]) (CHA).
    +        case 71:
    +          this.cursorCharAbsolute(this.params);
    +          break;
    +
    +          // CSI Ps L
    +          // Insert Ps Line(s) (default = 1) (IL).
    +        case 76:
    +          this.insertLines(this.params);
    +          break;
    +
    +          // CSI Ps M
    +          // Delete Ps Line(s) (default = 1) (DL).
    +        case 77:
    +          this.deleteLines(this.params);
    +          break;
    +
    +          // CSI Ps P
    +          // Delete Ps Character(s) (default = 1) (DCH).
    +        case 80:
    +          this.deleteChars(this.params);
    +          break;
    +
    +          // CSI Ps X
    +          // Erase Ps Character(s) (default = 1) (ECH).
    +        case 88:
    +          this.eraseChars(this.params);
    +          break;
    +
    +          // CSI Pm `  Character Position Absolute
    +          //   [column] (default = [row,1]) (HPA).
    +        case 96:
    +          this.charPosAbsolute(this.params);
    +          break;
    +
    +          // 141 61 a * HPR -
    +          // Horizontal Position Relative
    +        case 97:
    +          this.HPositionRelative(this.params);
    +          break;
    +
    +          // CSI P s c
    +          // Send Device Attributes (Primary DA).
    +          // CSI > P s c
    +          // Send Device Attributes (Secondary DA)
    +        case 99:
    +          this.sendDeviceAttributes(this.params);
    +          break;
    +
    +          // CSI Pm d
    +          // Line Position Absolute  [row] (default = [1,column]) (VPA).
    +        case 100:
    +          this.linePosAbsolute(this.params);
    +          break;
    +
    +          // 145 65 e * VPR - Vertical Position Relative
    +        case 101:
    +          this.VPositionRelative(this.params);
    +          break;
    +
    +          // CSI Ps ; Ps f
    +          //   Horizontal and Vertical Position [row;column] (default =
    +          //   [1,1]) (HVP).
    +        case 102:
    +          this.HVPosition(this.params);
    +          break;
    +
    +          // CSI Pm h  Set Mode (SM).
    +          // CSI ? Pm h - mouse escape codes, cursor escape codes
    +        case 104:
    +          this.setMode(this.params);
    +          break;
    +
    +          // CSI Pm l  Reset Mode (RM).
    +          // CSI ? Pm l
    +        case 108:
    +          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 114:
    +          this.setScrollRegion(this.params);
    +          break;
    +
    +          // CSI s     Save cursor (ANSI.SYS).
    +        case 115:
    +          this.saveCursor(this.params);
    +          break;
    +
    +          // CSI u     Restore cursor (ANSI.SYS).
    +        case 117:
    +          this.restoreCursor(this.params);
    +          break;
    +
    +          /**
    +           * Lesser Used
    +           */
    +
    +          // CSI Ps I
    +          // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
    +        case 73:
    +          this.cursorForwardTab(this.params);
    +          break;
    +
    +          // CSI Ps S  Scroll up Ps lines (default = 1) (SU).
    +        case 83:
    +          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 84:
    +          // if (this.prefix === '>') {
    +          //   this.resetTitleModes(this.params);
    +          //   break;
    +          // }
    +          // if (this.params.length > 1) {
    +          //   this.initMouseTracking(this.params);
    +          //   break;
    +          // }
    +          this.scrollDown(this.params);
    +          break;
    +
    +          // CSI Ps Z
    +          // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
    +        case 90:
    +          this.cursorBackwardTab(this.params);
    +          break;
    +
    +          // CSI Ps b  Repeat the preceding graphic character Ps times (REP).
    +        case 98:
    +          this.repeatPrecedingCharacter(this.params);
    +          break;
    +
    +          // CSI Ps g  Tab Clear (TBC).
    +          // case 103:
    +          //   this.tabClear(this.params);
    +          //   break;
    +
    +          // CSI Pm i  Media Copy (MC).
    +          // CSI ? Pm i
    +          // case 105:
    +          //   this.mediaCopy(this.params);
    +          //   break;
    +
    +          // CSI Pm m  Character Attributes (SGR).
    +          // CSI > Ps; Ps m
    +          // case 109: // 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 110: // 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 112:
    +          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 113:
    +          //   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 114: // 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 115: // 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 116:
    +          //   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 117: // 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 118:
    +          //   if (this.postfix === '$') {
    +          //     this.copyRectagle(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI Pt ; Pl ; Pb ; Pr ' w
    +          // case 119:
    +          //   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 120:
    +          //   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 122:
    +          //   if (this.postfix === '\'') {
    +          //     this.enableLocatorReporting(this.params);
    +          //   } else if (this.postfix === '$') {
    +          //     this.eraseRectangle(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI Pm ' {
    +          // CSI Pt; Pl; Pb; Pr$ {
    +          // case 123:
    +          //   if (this.postfix === '\'') {
    +          //     this.setLocatorEvents(this.params);
    +          //   } else if (this.postfix === '$') {
    +          //     this.selectiveEraseRectangle(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI Ps ' |
    +          // case 124:
    +          //   if (this.postfix === '\'') {
    +          //     this.requestLocatorPosition(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI P m SP }
    +          // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
    +          // case 125:
    +          //   if (this.postfix === ' ') {
    +          //     this.insertColumns(this.params);
    +          //   }
    +          //   break;
    +
    +          // CSI P m SP ~
    +          // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
    +          // case 126:
    +          //   if (this.postfix === ' ') {
    +          //     this.deleteColumns(this.params);
    +          //   }
    +          //   break;
    +
    +        default:
    +          console.log(
    +            'Unknown CSI code: %s',
    +            str[i], this.params);
    +          break;
    +        }
    +
    +        this.prefix = '';
    +        this.postfix = '';
    +        break;
    +      }
    +    }
    +
    +    this.getRows(this.y);
    +
    +    if (this.refreshEnd >= this.refreshStart) {
    +      this.refresh(this.refreshStart, this.refreshEnd);
    +    }
    +  };
    +
    +  Terminal.prototype.writeln = function(str) {
    +    this.write(str + '\r\n');
    +  };
    +
    +  Terminal.prototype.keyDownHandler = function(ev) {
    +    var str = '';
    +    switch (ev.keyCode) {
    +      // backspace
    +    case 8:
    +      str = '\x7f'; // ^?
    +      //str = '\x08'; // ^H
    +      break;
    +      // tab
    +    case 9:
    +      str = '\t';
    +      break;
    +      // return/enter
    +    case 13:
    +      str = '\r';
    +      break;
    +      // escape
    +    case 27:
    +      str = '\x1b';
    +      break;
    +      // left-arrow
    +    case 37:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOD'; // SS3 as ^[O for 7-bit
    +        //str = '\x8fD'; // SS3 as 0x8f for 8-bit
    +        break;
    +      }
    +      str = '\x1b[D';
    +      break;
    +      // right-arrow
    +    case 39:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOC';
    +        break;
    +      }
    +      str = '\x1b[C';
    +      break;
    +      // up-arrow
    +    case 38:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOA';
    +        break;
    +      }
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(-1);
    +        return cancel(ev);
    +      } else {
    +        str = '\x1b[A';
    +      }
    +      break;
    +      // down-arrow
    +    case 40:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOB';
    +        break;
    +      }
    +      if (ev.ctrlKey) {
    +        this.scrollDisp(1);
    +        return cancel(ev);
    +      } else {
    +        str = '\x1b[B';
    +      }
    +      break;
    +      // delete
    +    case 46:
    +      str = '\x1b[3~';
    +      break;
    +      // insert
    +    case 45:
    +      str = '\x1b[2~';
    +      break;
    +      // home
    +    case 36:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOH';
    +        break;
    +      }
    +      str = '\x1bOH';
    +      break;
    +      // end
    +    case 35:
    +      if (this.applicationKeypad) {
    +        str = '\x1bOF';
    +        break;
    +      }
    +      str = '\x1bOF';
    +      break;
    +      // page up
    +    case 33:
    +      if (ev.shiftKey) {
    +        this.scrollDisp(-(this.rows - 1));
    +        return cancel(ev);
    +      } else {
    +        str = '\x1b[5~';
    +      }
    +      break;
    +      // page down
    +    case 34:
    +      if (ev.shiftKey) {
    +        this.scrollDisp(this.rows - 1);
    +        return cancel(ev);
    +      } else {
    +        str = '\x1b[6~';
    +      }
    +      break;
    +      // F1
    +    case 112:
    +      str = '\x1bOP';
    +      break;
    +      // F2
    +    case 113:
    +      str = '\x1bOQ';
    +      break;
    +      // F3
    +    case 114:
    +      str = '\x1bOR';
    +      break;
    +      // F4
    +    case 115:
    +      str = '\x1bOS';
    +      break;
    +      // F5
    +    case 116:
    +      str = '\x1b[15~';
    +      break;
    +      // F6
    +    case 117:
    +      str = '\x1b[17~';
    +      break;
    +      // F7
    +    case 118:
    +      str = '\x1b[18~';
    +      break;
    +      // F8
    +    case 119:
    +      str = '\x1b[19~';
    +      break;
    +      // F9
    +    case 120:
    +      str = '\x1b[20~';
    +      break;
    +      // F10
    +    case 121:
    +      str = '\x1b[21~';
    +      break;
    +      // F11
    +    case 122:
    +      str = '\x1b[23~';
    +      break;
    +      // F12
    +    case 123:
    +      str = '\x1b[24~';
    +      break;
    +    default:
    +      // a-z and space
    +      if (ev.ctrlKey) {
    +        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    +          str = String.fromCharCode(ev.keyCode - 64);
    +        } else if (ev.keyCode === 32) {
    +          // NUL
    +          str = String.fromCharCode(0);
    +        } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
    +          // escape, file sep, group sep, record sep, unit sep
    +          str = String.fromCharCode(ev.keyCode - 51 + 27);
    +        } else if (ev.keyCode === 56) {
    +          // delete
    +          str = String.fromCharCode(127);
    +        } else if (ev.keyCode === 219) {
    +          // ^[ - escape
    +          str = String.fromCharCode(27);
    +        } else if (ev.keyCode === 221) {
    +          // ^] - group sep
    +          str = String.fromCharCode(29);
    +        }
    +      } else if ((!isMac && ev.altKey) || (isMac && ev.metaKey)) {
    +        if (ev.keyCode >= 65 && ev.keyCode <= 90) {
    +          str = '\x1b' + String.fromCharCode(ev.keyCode + 32);
    +        } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {
    +          str = '\x1b' + (ev.keyCode - 48);
    +        }
    +      }
    +      break;
    +    }
    +
    +    if (str) {
    +      cancel(ev);
    +
    +      this.showCursor();
    +      this.keyState = 1;
    +      this.keyStr = str;
    +      this.handler(str);
    +
    +      return false;
    +    } else {
    +      this.keyState = 0;
    +      return true;
    +    }
    +  };
    +
    +  Terminal.prototype.keyPressHandler = function(ev) {
    +    var str = ''
    +    , key;
    +
    +    cancel(ev);
    +
    +    if (!('charCode' in ev)) {
    +      key = ev.keyCode;
    +      if (this.keyState === 1) {
    +        this.keyState = 2;
    +        return false;
    +      } else if (this.keyState === 2) {
    +        this.showCursor();
    +        this.handler(this.keyStr);
    +        return false;
    +      }
    +    } else {
    +      key = ev.charCode;
    +    }
    +
    +    if (key !== 0) {
    +      if (!ev.ctrlKey
    +          && ((!isMac && !ev.altKey)
    +              || (isMac && !ev.metaKey))) {
    +        str = String.fromCharCode(key);
    +      }
    +    }
    +
    +    if (str) {
    +      this.showCursor();
    +      this.handler(str);
    +      return false;
    +    } else {
    +      return true;
    +    }
    +  };
    +
    +  Terminal.prototype.queueChars = function(str) {
    +    var self = this;
    +
    +    this.outputQueue += str;
    +
    +    if (this.outputQueue) {
    +      setTimeout(function() {
    +        self.outputHandler();
    +      }, 1);
    +    }
    +  };
    +
    +  Terminal.prototype.outputHandler = function() {
    +    if (this.outputQueue) {
    +      this.handler(this.outputQueue);
    +      this.outputQueue = '';
    +    }
    +  };
    +
    +  Terminal.prototype.bell = function() {
    +    if (!Terminal.visualBell) return;
    +    var self = this;
    +    this.element.style.borderColor = 'white';
    +    setTimeout(function() {
    +      self.element.style.borderColor = '';
    +    }, 10);
    +    if (Terminal.popOnBell) this.focus();
    +  };
    +
    +  Terminal.prototype.resize = function(x, y) {
    +    var line
    +    , el
    +    , i
    +    , j;
    +
    +    if (x < 1) x = 1;
    +    if (y < 1) y = 1;
    +
    +    // resize cols
    +    j = this.cols;
    +    if (j < x) {
    +      i = this.lines.length;
    +      while (i--) {
    +        while (this.lines[i].length < x) {
    +          this.lines[i].push((this.defAttr << 16) | 32);
    +        }
    +      }
    +    } else if (j > x) {
    +      i = this.lines.length;
    +      while (i--) {
    +        while (this.lines[i].length > x) {
    +          this.lines[i].pop();
    +        }
    +      }
    +    }
    +    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) {
    +          line = document.createElement('div');
    +          el.appendChild(line);
    +          this.children.push(line);
    +        }
    +      }
    +    } 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 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.refreshStart = 0;
    +    this.refreshEnd = y - 1;
    +
    +    this.refresh(0, this.rows - 1);
    +
    +    // it's a real nightmare trying
    +    // to resize the original
    +    // screen buffer. just set it
    +    // to null for now.
    +    this.normal = null;
    +  };
    +
    +  Terminal.prototype.getRows = function(y) {
    +    this.refreshStart = Math.min(this.refreshStart, y);
    +    this.refreshEnd = Math.max(this.refreshEnd, y);
    +  };
    +
    +  Terminal.prototype.eraseLine = function(x, y) {
    +    var line, i, ch, row;
    +
    +    row = this.ybase + y;
    +
    +    line = this.lines[row];
    +    // screen:
    +    // ch = 32 | (this.defAttr << 16);
    +    // xterm, linux:
    +    ch = 32 | (this.curAttr << 16);
    +
    +    for (i = x; i < this.cols; i++) {
    +      line[i] = ch;
    +    }
    +
    +    this.getRows(y);
    +  };
    +
    +  Terminal.prototype.blankLine = function(cur) {
    +    var attr = cur
    +          ? this.curAttr
    +          : this.defAttr;
    +
    +    var ch = 32 | (attr << 16)
    +    , line = []
    +    , i = 0;
    +
    +    for (; i < this.cols; i++) {
    +      line[i] = ch;
    +    }
    +
    +    return line;
    +  };
    +
    +  /**
    +   * ESC
    +   */
    +
    +  // ESC D Index (IND is 0x84).
    +  Terminal.prototype.index = function() {
    +    this.y++;
    +    if (this.y >= this.scrollBottom + 1) {
    +      this.y--;
    +      this.scroll();
    +      this.refreshStart = 0;
    +      this.refreshEnd = this.rows - 1;
    +    }
    +    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++;
    +      // echo -ne '\e[1;1H\e[44m\eM\e[0m'
    +      // use this.blankLine(false) for screen behavior
    +      this.lines.splice(this.y + this.ybase, 0, this.blankLine(true));
    +      j = this.rows - 1 - this.scrollBottom;
    +      // add an extra one because we just added a line
    +      // maybe put this above
    +      this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1);
    +      this.refreshStart = 0;
    +      this.refreshEnd = this.rows - 1;
    +    }
    +    this.state = normal;
    +  };
    +
    +  // ESC c Full Reset (RIS).
    +  Terminal.prototype.reset = function() {
    +    Terminal.call(this, this.cols, this.rows, this.handler);
    +  };
    +
    +  /**
    +   * CSI
    +   */
    +
    +  // CSI Ps A
    +  // Cursor Up Ps Times (default = 1) (CUU).
    +  Terminal.prototype.cursorUp = function(params) {
    +    var param, row;
    +    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, row;
    +    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, row;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    this.x += param;
    +    if (this.x >= this.cols - 1) {
    +      this.x = this.cols - 1;
    +    }
    +  };
    +
    +  // CSI Ps D
    +  // Cursor Backward Ps Times (default = 1) (CUB).
    +  Terminal.prototype.cursorBackward = function(params) {
    +    var param, row;
    +    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 param, 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 param, row, j;
    +    switch (params[0] || 0) {
    +    case 0:
    +      this.eraseLine(this.x, this.y);
    +      for (j = this.y + 1; j < this.rows; j++) {
    +        this.eraseLine(0, j);
    +      }
    +      break;
    +    case 1:
    +      this.eraseInLine([1]);
    +      j = this.y;
    +      while (j--) {
    +        this.eraseLine(0, j);
    +      }
    +      break;
    +    case 2:
    +      this.eraseInDisplay([0]);
    +      this.eraseInDisplay([1]);
    +      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] || 0) {
    +    case 0:
    +      this.eraseLine(this.x, this.y);
    +      break;
    +    case 1:
    +      var x = this.x + 1;
    +      var line = this.lines[this.ybase + this.y];
    +      // screen:
    +      //var ch = (this.defAttr << 16) | 32;
    +      // xterm, linux:
    +      var ch = (this.curAttr << 16) | 32;
    +      while (x--) line[x] = ch;
    +      break;
    +    case 2:
    +      var x = this.cols;
    +      var line = this.lines[this.ybase + this.y];
    +      // screen:
    +      //var ch = (this.defAttr << 16) | 32;
    +      // xterm, linux:
    +      var ch = (this.curAttr << 16) | 32;
    +      while (x--) line[x] = ch;
    +      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) {
    +    var i, p;
    +    if (params.length === 0) {
    +      this.curAttr = this.defAttr;
    +    } else {
    +      for (i = 0; i < params.length; i++) {
    +        p = params[i];
    +        if (p >= 30 && p <= 37) {
    +          this.curAttr = (this.curAttr & ~(31 << 5)) | ((p - 30) << 5);
    +        } else if (p >= 40 && p <= 47) {
    +          this.curAttr = (this.curAttr & ~31) | (p - 40);
    +        } else if (p >= 90 && p <= 97) {
    +          this.curAttr = (this.curAttr & ~(31 << 5)) | ((p - 90) << 5);
    +          this.curAttr = this.curAttr | (8 << 5);
    +        } else if (p >= 100 && p <= 107) {
    +          this.curAttr = (this.curAttr & ~31) | (p - 100);
    +          this.curAttr = this.curAttr | 8;
    +        } else if (p === 0) {
    +          this.curAttr = this.defAttr;
    +        } else if (p === 1) {
    +          // bold text
    +          this.curAttr = this.curAttr | (1 << 10);
    +        } else if (p === 4) {
    +          // underlined text
    +          this.curAttr = this.curAttr | (2 << 10);
    +        } else if (p === 7 || p === 27) {
    +          // inverse and positive
    +          // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
    +          if (p === 7) {
    +            if ((this.curAttr >> 10) & 4) continue;
    +            this.curAttr = this.curAttr | (4 << 10);
    +          } else if (p === 27) {
    +            if (~(this.curAttr >> 10) & 4) continue;
    +            this.curAttr = this.curAttr & ~(4 << 10);
    +          }
    +          var bg = this.curAttr & 31;
    +          var fg = (this.curAttr >> 5) & 31;
    +          this.curAttr = (this.curAttr & ~1023) | ((bg << 5) | fg);
    +        } else if (p === 22) {
    +          // not bold
    +          this.curAttr = this.curAttr & ~(1 << 10);
    +        } else if (p === 24) {
    +          // not underlined
    +          this.curAttr = this.curAttr & ~(2 << 10);
    +        } else if (p === 39) {
    +          // reset fg
    +          this.curAttr = this.curAttr & ~(31 << 5);
    +          this.curAttr = this.curAttr | (((this.defAttr >> 5) & 31) << 5);
    +        } else if (p === 49) {
    +          // reset bg
    +          this.curAttr = this.curAttr & ~31;
    +          this.curAttr = this.curAttr | (this.defAttr & 31);
    +        }
    +      }
    +    }
    +  };
    +
    +  // 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 === '?') {
    +      // modern xterm doesnt seem to
    +      // respond to any of these except ?6, 6, and 5
    +      switch (params[0]) {
    +      case 6:
    +        this.queueChars('\x1b['
    +                        + (this.y + 1)
    +                        + ';'
    +                        + (this.x + 1)
    +                        + 'R');
    +        break;
    +      case 15:
    +        // no printer
    +        // this.queueChars('\x1b[?11n');
    +        break;
    +      case 25:
    +        // dont support user defined keys
    +        // this.queueChars('\x1b[?21n');
    +        break;
    +      case 26:
    +        // this.queueChars('\x1b[?27;1;0;0n');
    +        break;
    +      case 53:
    +        // no dec locator/mouse
    +        // this.queueChars('\x1b[?50n');
    +        break;
    +      }
    +      return;
    +    }
    +    switch (params[0]) {
    +    case 5:
    +      this.queueChars('\x1b[0n');
    +      break;
    +    case 6:
    +      this.queueChars('\x1b['
    +                      + (this.y + 1)
    +                      + ';'
    +                      + (this.x + 1)
    +                      + 'R');
    +      break;
    +    }
    +  };
    +
    +  /**
    +   * Additions
    +   */
    +
    +  // CSI Ps @
    +  // Insert Ps (Blank) Character(s) (default = 1) (ICH).
    +  Terminal.prototype.insertChars = function(params) {
    +    var param, row, j;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    row = this.y + this.ybase;
    +    j = this.x;
    +    while (param-- && j < this.cols) {
    +      // screen:
    +      //this.lines[row].splice(j++, 0, (this.defAttr << 16) | 32);
    +      // xterm, linux:
    +      this.lines[row].splice(j++, 0, (this.curAttr << 16) | 32);
    +      this.lines[row].pop();
    +    }
    +  };
    +
    +  // CSI Ps E
    +  // Cursor Next Line Ps Times (default = 1) (CNL).
    +  Terminal.prototype.cursorNextLine = function(params) {
    +    var param, row;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    this.y += param;
    +    if (this.y >= this.rows) {
    +      this.y = this.rows - 1;
    +    }
    +    // above is the same as CSI Ps B
    +    this.x = 0;
    +  };
    +
    +  // CSI Ps F
    +  // Cursor Preceding Line Ps Times (default = 1) (CNL).
    +  Terminal.prototype.cursorPrecedingLine = function(params) {
    +    var param, row;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    this.y -= param;
    +    if (this.y < 0) this.y = 0;
    +    // above is the same as CSI Ps A
    +    this.x = 0;
    +  };
    +
    +  // CSI Ps G
    +  // Cursor Character Absolute  [column] (default = [row,1]) (CHA).
    +  Terminal.prototype.cursorCharAbsolute = function(params) {
    +    var param, row;
    +    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;
    +    // add an extra one because we added one
    +    // above
    +    j = this.rows - 1 + this.ybase - j + 1;
    +
    +    while (param--) {
    +      // this.blankLine(false) for screen behavior
    +      // test: echo -e '\e[44m\e[1L\e[0m'
    +      this.lines.splice(row, 0, this.blankLine(true));
    +      this.lines.splice(j, 1);
    +    }
    +
    +    //this.refresh(0, this.rows - 1);
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  };
    +
    +  // 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--) {
    +      // this.blankLine(false) for screen behavior
    +      // test: echo -e '\e[44m\e[1M\e[0m'
    +      this.lines.splice(j + 1, 0, this.blankLine(true));
    +      this.lines.splice(row, 1);
    +    }
    +
    +    //this.refresh(0, this.rows - 1);
    +    this.refreshStart = 0;
    +    this.refreshEnd = this.rows - 1;
    +  };
    +
    +  // CSI Ps P
    +  // Delete Ps Character(s) (default = 1) (DCH).
    +  Terminal.prototype.deleteChars = function(params) {
    +    var param, row;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    row = this.y + this.ybase;
    +    while (param--) {
    +      this.lines[row].splice(this.x, 1);
    +      // screen:
    +      //this.lines[row].push((this.defAttr << 16) | 32);
    +      // xterm, linux:
    +      this.lines[row].push((this.curAttr << 16) | 32);
    +    }
    +  };
    +
    +  // CSI Ps X
    +  // Erase Ps Character(s) (default = 1) (ECH).
    +  Terminal.prototype.eraseChars = function(params) {
    +    var param, row, j;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    row = this.y + this.ybase;
    +    j = this.x;
    +    while (param-- && j < this.cols) {
    +      // screen:
    +      // this.lines[row][j++] = (this.defAttr << 16) | 32;
    +      // xterm, linux:
    +      this.lines[row][j++] = (this.curAttr << 16) | 32;
    +    }
    +  };
    +
    +  // CSI Pm `  Character Position Absolute
    +  //   [column] (default = [row,1]) (HPA).
    +  Terminal.prototype.charPosAbsolute = function(params) {
    +    var param, row;
    +    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
    +  Terminal.prototype.HPositionRelative = function(params) {
    +    var param, row;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    this.x += param;
    +    if (this.x >= this.cols - 1) {
    +      this.x = this.cols - 1;
    +    }
    +    // above is the same as CSI Ps C
    +  };
    +
    +  // 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.
    +  Terminal.prototype.sendDeviceAttributes = function(params) {
    +    // This severely breaks things if
    +    // TERM is set to `linux`. xterm
    +    // is fine.
    +    return;
    +
    +    if (this.prefix !== '>') {
    +      this.queueChars('\x1b[?1;2c');
    +    } else {
    +      // say we're a vt100 with
    +      // firmware version 95
    +      // this.queueChars('\x1b[>0;95;0c');
    +      // modern xterm responds with:
    +      this.queueChars('\x1b[>0;276;0c');
    +    }
    +  };
    +
    +  // CSI Pm d
    +  // Line Position Absolute  [row] (default = [1,column]) (VPA).
    +  Terminal.prototype.linePosAbsolute = function(params) {
    +    var param, row;
    +    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
    +  Terminal.prototype.VPositionRelative = function(params) {
    +    var param, row;
    +    param = params[0];
    +    if (param < 1) param = 1;
    +    this.y += param;
    +    if (this.y >= this.rows) {
    +      this.y = this.rows - 1;
    +    }
    +    // above is same as CSI Ps B
    +  };
    +
    +  // 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).
    +  /…
  3. [from now] 2012/04/04 16:44:45

    committed Apr 4, 2012
    diff --git a/server/app.js b/server/app.js
    index bc2f709..027f4e3 100644
    --- a/server/app.js
    +++ b/server/app.js
    @@ -3,13 +3,55 @@ var http = require('http'),
         EventEmitter = require('events').EventEmitter;
    
     var router = new (require('router-line').Router),
    -    pty = require('pty.js'),
    -    socketio = require('socket.io');
    +    socketio = require('socket.io'),
    +    fileServer = new(require('node-static').Server)('../static');
    
    -var app = http.createServer(function (req, res) {
    -
    +var hub = new (require('../hub')),
    +    app,
    +    io,
    +    handlers = {};
    +
    +function isObject(obj) {
    +  return typeof obj === 'object' && obj !== null;
    +}
    +
    +/*
    + * Routing Table
    + */
    +
    +function Helper(req, res, params) {
    +  this.request = req;
    +  this.response = res;
    +  this.params = params;
    +  var parsedUrl = url.parse(this.request.url, true);
    +  if (isObject(parsedUrl) && isObject(parsedUrl.query)) {
    +    for (var key in parsedUrl.query) {
    +      this.params[key] = parsedUrl.query[key];
    +    }
    +  }
    +}
    +
    +app = http.createServer(function (req, res) {
    +  var reqUrl = url.parse(req.url);
    +  var target = router.router(req.method.toUpperCase(),
    +                             reqUrl.pathname);
    +  if (target === undefined) {
    +    req.on('end', function () {
    +      fileServer.serve(req, res);
    +    });
    +  } else {
    +    var handler = new Helper(req, res, target.params);
    +    target.value.call(handler);
    +  }
     });
    
    -var io = socketio.listen(app);
    +io = socketio.listen(app);
    +
    +io.sockets.on('connection', function (socket) {
    +  socket.emit('snapshot', hub.snapshot());
    +  socket.on('data', function () {
    +    socket.emit('data');
    +  });
    +});
    
     app.listen(8124);
    \ No newline at end of file
    diff --git a/server/package.json b/server/package.json
    index 0fe82d4..66f3b8e 100644
    --- a/server/package.json
    +++ b/server/package.json
    @@ -9,7 +9,8 @@
       "dependencies": {
         "pty.js": "*",
         "router-line": "*",
    -    "socket.io": "*"
    +    "socket.io": "*",
    +    "node-static": "*"
       },
       "devDependencies": {
         "should": "*",
  4. [from now] 2012/04/04 14:59:53

    committed Apr 4, 2012
    diff --git a/caster/index.js b/caster/index.js
    new file mode 100644
    index 0000000..909bf1b
    --- /dev/null
    +++ b/caster/index.js
    @@ -0,0 +1,25 @@
    +var tty = require('tty'),
    +    pty = require('pty.js');
    +
    +var term = pty.spawn(process.env.SHELL, [], {
    +  name: 'xterm-color',
    +  cols: 80,
    +  rows: 30,
    +  cwd: process.env.HOME
    +});
    +
    +/*
    +term.on('data', function(data) {
    +  console.log(data);
    +});
    +*/
    +
    +process.stdin.resume();
    +tty.setRawMode(true);
    +
    +term.pipe(process.stdout);
    +process.stdin.pipe(term);
    +
    +term.on('exit', function () {
    +  process.exit();
    +});
    \ No newline at end of file
Commits on Apr 3, 2012
Something went wrong with that request. Please try again.