<repo_name>i-van/sheet<file_sep>/public/javascripts/spreadsheet.js
var Spreadsheet = function() {
  this.init.apply(this, arguments);
}
Spreadsheet.prototype = {
  init: function(id, cols, rows) {
    this._wrapper = $('#' + id).addClass('spreadsheet').hide();
    this._cols = cols || 10;
    this._rows = rows || 25;
    this._instance = 0;
    
    this._initTable();
    this._initInput();
    this._initSelect();
    
    for (var i = 0, n = this._cols; i < n; ++i) {
      this.setWidth(i, 100);
    }
    for (var j = 0, m = this._rows; j < m; ++j) {
      this.setHeight(j, 20);
    }
  },
  _initSelect: function() {
    var that = this;
    this._table.delegate('td', 'click', function() {
      if (!!that.onSelect) {
        var cell = that._parseCell($(this));
        that.onSelect.call(that, cell);
      }
    });
  },
  selectCell: function(cell, color) {
    var color = color || '#C8C8C8';
    if (color == 'clear') {
      color = 'inherit';
    }
    this._findCell(cell).css({'background-color': color});
  },
  _initInput: function() {
    this._input = $('<input />')
      .attr('type', 'text')
      .css({position: 'absolute', 'z-index': 100, display:'none'})
      .appendTo(this._wrapper);
      
    var that = this;
    this._table.delegate('td', 'dblclick', function() {
      var e = $(this)
        , position = e.position();
      that._input
        .css({left:position.left - 1 + 'px', top: position.top - 1 + 'px'})
        .width(e.width())
        .height(e.height())
        .val(e.text())
        .show()
        .focus();
        
      that._activeCell = that._parseCell(e);
    });
    this._input.blur(function() {
      var e = $(this).hide();
    });
    this._input.change(function() {
      if (!!that.onEdit) {
        that.onEdit.call(that, that._activeCell);
      }
    });
  },
  _initTable: function() {
    this._table = $('<table></table>')
      .attr('cellspacing', 0)
      .attr('cellpadding', 0)
      .appendTo(this._wrapper);

    var body = []
      , n = this._rows
      , m = this._cols
      , id;
    for (var i = 0; i < n; ++i) {
      if (i == 0) {
        body.push('<tr>');
        body.push('<th></th>');
        for (var j = 0; j < m; ++j) {
          id = ['column', this._instance, j].join('-');
          body.push('<th id="' + id + '"></th>');
        }
        body.push('</tr>');
      }
      body.push('<tr>');
      id = ['row', this._instance, i].join('-');
      body.push('<th id="' + id + '"></th>');
      for (var j = 0; j < m; ++j) {
        id = ['cell', this._instance, i, j].join('-');
        body.push('<td id="' + id + '"></td>');
      }
      body.push('</tr>');
    }
    this._table.html(body.join(''));
    
    this._findCell({row: 0, column: 0}).css({'border-width': '1px'});
    for (var i = 1; i < n; ++i) {
      this._findCell({row: i, column: 0}).css({'border-width': '0 1px 1px 1px'});
    }
    for (var i = 1; i < m; ++i) {
      this._findCell({row: 0, column: i}).css({'border-width': '1px 1px 1px 0'});
    }
  },
  setData: function(data) {
    var n = this._rows
      , m = this._cols;
    for (var i = 0; i < n; ++i) {
      for (var j = 0; j < m; ++j) {
        var cell = {row: i, column: j};
        this.setCell(cell, data[i][j]);
      }
    }
  },
  setWidth: function(column, width) {
    $('#' + ['column', this._instance, column].join('-')).width(width);
    return this;
  },
  setHeight: function(row, height) {
    $('#' + ['row', this._instance, row].join('-')).height(height);
    return this;
  },
  setCell: function(cell, data) {
    this._findCell(cell).text(data);
    return this;
  },
  _findCell: function(cell) {
    return $('#' + ['cell', this._instance, cell.row, cell.column].join('-'));
  },
  _parseCell: function(element) {
    var parts = element.attr('id').split('-');
    return {row:parts[2], column:parts[3]};
  },
  show: function() {
    this._wrapper.show();
  },
  hide: function() {
    this._wrapper.hide();
  }
};
<file_sep>/models/sheet.js
/**
 * sheet
 */

module.exports = ({
  _cols: 10,
  _rows: 25,
  _storage: [],
  _default: '',
  set: function(cell, value) {
    this._storage[cell.row][cell.column] = value;
  },
  get: function(cell) {
    return this._storage[cell.row][cell.column];
  },
  init: function() {
    var n = this._rows
      , m = this._cols
      , row;

    for (var i = 0; i < n; ++i) {
      row = [];
      for (var j = 0; j < m; ++j) {
        row.push(this._default);
      }
      this._storage.push(row);
    }

    return this;
  },
  getStorage: function() {
    return this._storage;
  }
}).init();<file_sep>/models/users.js
/**
 * users
 */

module.exports = {
  _storage: [],
  add: function(user) {
    this._storage.push(user);
  },
  remove: function(id) {
    for (var i = this._storage.length; i--;) {
      if (this._storage[i]['id'] == id) {
        this._storage.splice(i, 1);
      }
    }
  },
  find: function(id) {
    for (var i = this._storage.length; i--;) {
      if (this._storage[i]['id'] == id) {
        return this._storage[i];
      }
    }
  },
  getStorage: function() {
    return this._storage;
  }
};<file_sep>/app.js
/**
 * module dependencies
 */

var express = require('express')
  , io = require('socket.io');

var app = module.exports = express.createServer()
  , socket = io.listen(app, {transports: ['websocket', 'xhr-polling', 'xhr-multipart']});

// configuration

app.configure(function() {
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.compiler({src: __dirname + '/public', enable: ['sass']}));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});
app.configure('development', function() {
  app.use(express.errorHandler({
    dumpExceptions: true,
    showStack: true
  }));
});
app.configure('production', function() {
  app.use(express.errorHandler());
});

// routes

require('./config/routes')(app);

// socket.io

require('./controllers/socket.io')(socket);

// run

app.listen(3000);
console.log("Express server listening on port %d", app.address().port);
<file_sep>/config/routes.js
/**
 * init routes
 */

var mainController = require('../controllers/main');

module.exports = function(app) {
  app.get('/', mainController.index);
  app.get('/dump', mainController.dump);
};
