Initial commit

This commit is contained in:
Marcin Kulik 2011-03-09 19:05:53 +01:00
commit 4c6a5bd129
17 changed files with 1278 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.sqlite3
*.rbc
public/system/*

13
Gemfile Normal file
View File

@ -0,0 +1,13 @@
source :rubygems
gem 'bundler'
gem 'sinatra'
gem 'dm-core'
gem 'dm-timestamps'
gem 'dm-migrations'
gem 'dm-validations'
gem 'dm-serializer'
gem 'dm-sqlite-adapter'
gem 'dm-postgres-adapter'
gem "dm-paperclip"
gem 'require_relative'

85
Gemfile.lock Normal file
View File

@ -0,0 +1,85 @@
GEM
remote: http://rubygems.org/
specs:
addressable (2.2.4)
data_objects (0.10.3)
addressable (~> 2.1)
datamapper (1.0.2)
dm-aggregates (= 1.0.2)
dm-constraints (= 1.0.2)
dm-core (= 1.0.2)
dm-migrations (= 1.0.2)
dm-serializer (= 1.0.2)
dm-timestamps (= 1.0.2)
dm-transactions (= 1.0.2)
dm-types (= 1.0.2)
dm-validations (= 1.0.2)
dm-aggregates (1.0.2)
dm-core (~> 1.0.2)
dm-constraints (1.0.2)
dm-core (~> 1.0.2)
dm-migrations (~> 1.0.2)
dm-core (1.0.2)
addressable (~> 2.2)
extlib (~> 0.9.15)
dm-do-adapter (1.0.2)
data_objects (~> 0.10.2)
dm-core (~> 1.0.2)
dm-migrations (1.0.2)
dm-core (~> 1.0.2)
dm-paperclip (2.4.1)
datamapper
extlib
dm-postgres-adapter (1.0.2)
dm-do-adapter (~> 1.0.2)
do_postgres (~> 0.10.2)
dm-serializer (1.0.2)
dm-core (~> 1.0.2)
fastercsv (~> 1.5.3)
json_pure (~> 1.4)
dm-sqlite-adapter (1.0.2)
dm-do-adapter (~> 1.0.2)
do_sqlite3 (~> 0.10.2)
dm-timestamps (1.0.2)
dm-core (~> 1.0.2)
dm-transactions (1.0.2)
dm-core (~> 1.0.2)
dm-types (1.0.2)
dm-core (~> 1.0.2)
fastercsv (~> 1.5.3)
json_pure (~> 1.4)
stringex (~> 1.1.0)
uuidtools (~> 2.1.1)
dm-validations (1.0.2)
dm-core (~> 1.0.2)
do_postgres (0.10.3)
data_objects (= 0.10.3)
do_sqlite3 (0.10.3)
data_objects (= 0.10.3)
extlib (0.9.15)
fastercsv (1.5.4)
json_pure (1.5.1)
rack (1.2.1)
require_relative (0.0.1)
sinatra (1.2.0)
rack (~> 1.1)
tilt (>= 1.2.2, < 2.0)
stringex (1.1.0)
tilt (1.2.2)
uuidtools (2.1.2)
PLATFORMS
ruby
DEPENDENCIES
bundler
dm-core
dm-migrations
dm-paperclip
dm-postgres-adapter
dm-serializer
dm-sqlite-adapter
dm-timestamps
dm-validations
require_relative
sinatra

68
app.rb Normal file
View File

@ -0,0 +1,68 @@
APP_ROOT = File.dirname(__FILE__)
require_relative 'config/init'
require_relative 'app/models'
set :root, File.dirname(__FILE__)
set :static, true
set :views, File.join(APP_ROOT, 'app', 'views')
helpers do
def player_data(movie)
data = File.read(movie.typescript.path).split("\n",2)[1]
time = File.read(movie.timing.path)
chars = "'" + data.bytes.map { |b| '\x' + format('%02x', b) }.join('') + "'";
formatted_time = '[' + time.split("\n").map { |line| delay, n = line.split; '[' + delay.to_f.to_s + ',' + n.to_i.to_s + ']'}.join(',') + ']'
out = "<script>\n"
out << "var data = #{chars};\n"
out << "var time = #{formatted_time};\n"
out << "var cols = #{movie.terminal_cols};\n"
out << "var lines = #{movie.terminal_lines};\n"
out << "</script>"
out
end
end
def make_paperclip_mash(file_hash)
mash = Mash.new
mash['tempfile'] = file_hash[:tempfile]
mash['filename'] = file_hash[:filename]
mash['content_type'] = file_hash[:type]
mash['size'] = file_hash[:tempfile].size
mash
end
get %r{/(?<id>\d+)} do
@movie = Movie.get(params[:id]) or pass
erb :show
end
get '/' do
@movies = Movie.all(:order => :created_at.desc)
erb :index
end
get '/about' do
erb :about
end
post '/scripts' do
movie = Movie.new(
:terminal_cols => params[:terminal_cols],
:terminal_lines => params[:terminal_lines],
:typescript => make_paperclip_mash(params[:typescript]),
:timing => make_paperclip_mash(params[:timing])
)
if movie.save
response.status = 201
content_type = :json
movie.to_json
else
response.status = 422
content_type = :json
movie.errors.to_json
end
end

32
app/models.rb Normal file
View File

@ -0,0 +1,32 @@
class Movie
include DataMapper::Resource
include Paperclip::Resource
property :id, Serial
property :name, String
property :typescript_file_name, String, :required => true, :length => 256
property :typescript_content_type, String, :length => 128
property :typescript_file_size, Integer
property :typescript_updated_at, DateTime
property :timing_file_name, String, :required => true, :length => 256
property :timing_content_type, String, :length => 128
property :timing_file_size, Integer
property :timing_updated_at, DateTime
property :terminal_type, String
property :terminal_cols, Integer, :required => true
property :terminal_lines, Integer, :required => true
timestamps :at
has_attached_file :typescript, :path => "#{APP_ROOT}/public/system/:attachment/:id"
has_attached_file :timing, :path => "#{APP_ROOT}/public/system/:attachment/:id"
validates_attachment_presence :typescript
validates_attachment_presence :timing
end
DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{File.expand_path(File.join(APP_ROOT, 'db', 'db.sqlite3'))}")

9
app/views/index.erb Normal file
View File

@ -0,0 +1,9 @@
<h2>Ansi.tv</h2>
<ul class="movies">
<% @movies.each do |movie| %>
<li class="movie">
<a href="/<%= movie.id %>"><%= movie.id %></a>
</li>
<% end %>
</ul>

31
app/views/layout.erb Normal file
View File

@ -0,0 +1,31 @@
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script src="/javascripts/utf8.js" type="text/javascript"></script>
<script src="/javascripts/misc.js" type="text/javascript"></script>
<script src="/javascripts/player.js" type="text/javascript"></script>
<script src="/javascripts/ansi-interpreter.js" type="text/javascript"></script>
<script src="/javascripts/terminal.js" type="text/javascript"></script>
<link rel="stylesheet" href="/stylesheets/reset.css" />
<link rel="stylesheet" href="/stylesheets/main.css" />
</head>
<body>
<div id="all">
<div id="top">
<div id="logo">
<h1>vterm.tv</h1>
</div>
<div id="menu">
<ul class="links">
<li>Sign in</li>
<li>Sign up</li>
</ul>
</div>
</div>
<div id="main">
<%= yield %>
<div class="clear"></div>
</div>
</div>
</body>
</html>

16
app/views/show.erb Normal file
View File

@ -0,0 +1,16 @@
<h2>Becoming more productive with Vim (<%= @movie.id %>)</h2>
<div class="movie">
<div class="player">
<pre class="term"></pre>
<div class="hud">--------------========</div>
</div>
<div class="clear"></div>
</div>
<div class="description">
<p>Ive been doing some reflecting this week on how I can work smarter (instead of harder), and one of the things I came up with was adding a few more tools to my Vim repertoire. I spend more than half of my engineering time in Vim (the other half usually being in a web browser), so I figured that a few minutes here and there would eventually add up in a big way.</p>
<p>But like anything else with Vim, there are always multiple ways of accomplishing the very same thing, so I make no guarantees that there arent simpler ways of getting this done — but I can say that this way gets the job done, and is pretty easy to get working on your own system.</p>
</div>
<%= player_data(@movie) %>

6
config.ru Normal file
View File

@ -0,0 +1,6 @@
require "bundler/setup"
Bundler.require
require File.expand_path(File.join(File.dirname(__FILE__), "app"))
run Sinatra::Application

3
config/init.rb Normal file
View File

@ -0,0 +1,3 @@
Paperclip.configure do |config|
config.use_dm_validations = true
end

View File

@ -0,0 +1,180 @@
SP.AnsiInterpreter = function(terminal) {
this.terminal = terminal;
this.compilePatterns();
}
SP.AnsiInterpreter.prototype = {
PATTERNS: {
"\x07": function(data) {
console.log("bell!");
},
"\x08": function(data) {
// console.log("bs");
this.terminal.bs();
},
"\x0a": function(data) {
this.terminal.cursorDown();
},
"\x0d": function(data) {
this.terminal.cr();
},
"\x0e": function(data) {
},
"\x0f": function(data) {
},
"\x82": function(data) { // Reserved (?)
},
"\x94": function(data) { // Cancel Character, ignore previous character
},
// 20 - 7e
"([\x20-\x7e]|\xe2..|[\xc5\xc4].)+": function(data, match) {
this.terminal.print(match[0]);
},
"\x1b\\(B": function(data) { // SCS (Set G0 Character SET)
},
"\x1b\\[(?:[0-9]+)?(?:;[0-9]+)*([\x40-\x7e])": function(data, match) {
this.params = [];
var re = /(\d+)/g;
var m;
while (m = re.exec(match[0])) {
this.params.push(parseInt(m[1]));
}
this.n = this.params[0];
this.m = this.params[1];
this.handleCSI(match[1]);
},
"\x1b\\[\\?([\x30-\x3f]+)([hlsr])": function(data, match) { // private standards
// h = Sets DEC/xterm specific mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#decmode)
// l = Resets mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#mode)
// 1049 + h = Save cursor position, switch to alternate screen buffer, and clear screen.
// 1049 + l = Clear screen, switch to normal screen buffer, and restore cursor position.
// 1001 + s = ?
// 1001 + r = ?
if (match[1] == '1049') {
if (match[2] == 'h') {
this.terminal.saveCursor();
} else if (match[2] == 'l') {
this.terminal.restoreCursor();
}
}
},
"\x1b\x3d": function(data) { // DECKPAM - Set keypad to applications mode (ESCape instead of digits)
},
"\x1b\x3e": function(data) { // DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
},
"\x1b\\\x5d\x30\x3b(?:.)*?\x07": function(data, match) { // OSC - Operating System Command (terminal title)
},
"\x1b\x37": function(data) { // save cursor pos and char attrs
this.terminal.saveCursor();
},
"\x1b\x38": function(data) { // restore cursor pos and char attrs
this.terminal.restoreCursor();
}
},
handleCSI: function(term) {
switch(term) {
case "@":
this.terminal.reserveCharacters(this.n);
break;
case "A":
this.terminal.cursorUp(this.n || 1);
break;
case "B":
this.terminal.cursorDown(this.n || 1);
break;
case "C":
this.terminal.cursorForward(this.n || 1);
break;
case "D":
this.terminal.cursorBack(this.n || 1);
break;
case "H":
this.terminal.setCursorPos(this.n || 1, this.m || 1);
break;
case "J":
this.terminal.eraseData(this.n || 0);
break;
case "K":
this.terminal.eraseLine(this.n || 0);
break;
case "l": // l, Reset mode
console.log("(TODO) reset: " + this.n);
break;
case "m":
this.terminal.setSGR(this.params);
break;
case "r": // Set top and bottom margins (scroll region on VT100)
break;
default:
console.log('no handler for CSI term: ' + term);
}
},
compilePatterns: function() {
this.COMPILED_PATTERNS = [];
var regexp;
for (re in this.PATTERNS) {
regexp = new RegExp('^' + re);
this.COMPILED_PATTERNS.push([regexp, this.PATTERNS[re]]);
}
},
feed: function(data) {
if (data.length == 0) return;
// console.log(data);
var match;
var handler;
for (var i=0; i<this.COMPILED_PATTERNS.length; i++) {
var pattern = this.COMPILED_PATTERNS[i];
match = pattern[0].exec(data);
if (match) {
handler = pattern[1];
break;
}
}
if (handler) {
handler.call(this, data, match);
this.feed(data.slice(match[0].length));
} else {
var s = data.slice(0, 10);
var hex = '';
for (i=0; i<s.length; i++) {
hex += '0x' + s[i].charCodeAt(0).toString(16) + ',';
}
console.log("failed matching: '" + s + "' (" + hex + ")");
throw 'bummer';
return;
}
}
}

View File

@ -0,0 +1,10 @@
Function.prototype.bind = function(object) {
var func = this;
return function() {
return func.apply(object, arguments);
};
};
String.prototype.times = function(n) {
return Array.prototype.join.call({length:n+1}, this);
};

View File

@ -0,0 +1,54 @@
var SP = {};
var speed = 1.0;
SP.Player = function(cols, lines, data, time) {
this.terminal = new SP.Terminal(cols, lines);
this.interpreter = new SP.AnsiInterpreter(this.terminal);
this.data = data;
this.time = time;
this.dataIndex = 0;
this.frame = 0;
this.currentData = "";
console.log("started");
this.nextFrame();
};
SP.Player.prototype = {
nextFrame: function() {
var timing = this.time[this.frame];
if (!timing) {
console.log("finished");
return;
}
this.terminal.restartCursorBlink();
// setTimeout(function() {
// console.log(this.dataIndex);
// console.log(this.currentData);
this.interpreter.feed(this.currentData);
this.terminal.updateDirtyLines();
var n = timing[1];
console.log(timing[0]);
console.log(n);
this.currentData = this.data.slice(this.dataIndex, this.dataIndex + n);
this.dataIndex += n;
this.frame += 1;
if (!window.stopped) {
this.nextFrame();
}
// }.bind(this), timing[0] * 1000 * (1.0 / speed));
}
}
$(function() {
$(window).bind('keyup', function(event) {
if (event.keyCode == 27) {
window.stopped = true;
}
});
});
$(function() { new SP.Player(cols, lines, data, time) });

View File

@ -0,0 +1,429 @@
/* var CP437_DICT = {
'\x01' : ["&#x263A;", "WHITE SMILING FACE"],
'\x02' : ["&#x263B;", "BLACK SMILING FACE"],
'\x03' : ["&#x2665;", "BLACK HEART SUIT"],
'\x04' : ["&#x2666;", "BLACK DIAMOND SUIT"],
'\x05' : ["&#x2663;", "BLACK CLUB SUIT"],
'\x06' : ["&#x2660;", "BLACK SPADE SUIT"],
'\x07' : ["&#x2022;", "BULLET"],
'\x08' : ["&#x25D8;", "INVERSE BULLET"],
'\x09' : ["&#x25CB;", "WHITE CIRCLE"],
'\x0a' : ["&#x25D9;", "INVERSE WHITE CIRCLE"],
'\x0b' : ["&#x2642;", "MALE SIGN"],
'\x0c' : ["&#x2640;", "FEMALE SIGN"],
'\x0d' : ["&#x266A;", "EIGHTH NOTE"],
'\x0e' : ["&#x266B;", "BEAMED EIGHTH NOTES"],
'\x0f' : ["&#x263C;", "WHITE SUN WITH RAYS"],
'\x10' : ["&#x25B8;", "BLACK RIGHT-POINTING SMALL TRIANGLE"],
'\x11' : ["&#x25C2;", "BLACK LEFT-POINTING SMALL TRIANGLE"],
'\x12' : ["&#x2195;", "UP DOWN ARROW"],
'\x13' : ["&#x203C;", "DOUBLE EXCLAMATION MARK"],
'\x14' : ["&#x00B6;", "PILCROW SIGN"],
'\x15' : ["&#x00A7;", "SECTION SIGN"],
'\x16' : ["&#x25AC;", "BLACK RECTANGLE"],
'\x17' : ["&#x21A8;", "UP DOWN ARROW WITH BASE"],
'\x18' : ["&#x2191;", "UPWARDS ARROW"],
'\x19' : ["&#x2193;", "DOWNWARDS ARROW"],
'\x1a' : ["&#x2192;", "RIGHTWARDS ARROW"],
'\x1b' : ["&#x2190;", "LEFTWARDS ARROW"],
'\x1c' : ["&#x221F;", "RIGHT ANGLE"],
'\x1d' : ["&#x2194;", "LEFT RIGHT ARROW"],
'\x1e' : ["&#x25B4;", "BLACK UP-POINTING SMALL TRIANGLE"],
'\x1f' : ["&#x25BE;", "BLACK DOWN-POINTING SMALL TRIANGLE"],
'\x21' : ["&#x0021;", "EXCLAMATION MARK"],
'\x22' : ["&#x0022;", "QUOTATION MARK"],
'\x23' : ["&#x0023;", "NUMBER SIGN"],
'\x24' : ["&#x0024;", "DOLLAR SIGN"],
'\x25' : ["&#x0025;", "PERCENT SIGN"],
'\x26' : ["&#x0026;", "AMPERSAND"],
'\x27' : ["&#x0027;", "APOSTROPHE"],
'\x28' : ["&#x0028;", "LEFT PARENTHESIS"],
'\x29' : ["&#x0029;", "RIGHT PARENTHESIS"],
'\x2a' : ["&#x002A;", "ASTERISK"],
'\x2b' : ["&#x002B;", "PLUS SIGN"],
'\x2c' : ["&#x002C;", "COMMA"],
'\x2d' : ["&#x002D;", "HYPHEN-MINUS"],
'\x2e' : ["&#x002E;", "FULL STOP"],
'\x2f' : ["&#x002F;", "SOLIDUS"],
'\x7f' : ["&#x2302;", "HOUSE"],
'\x80' : ["&#x00C7;", "LATIN CAPITAL LETTER C WITH CEDILLA"],
'\x81' : ["&#x00FC;", "LATIN SMALL LETTER U WITH DIAERESIS"],
'\x82' : ["&#x00E9;", "LATIN SMALL LETTER E WITH ACUTE"],
'\x83' : ["&#x00E2;", "LATIN SMALL LETTER A WITH CIRCUMFLEX"],
'\x84' : ["&#x00E4;", "LATIN SMALL LETTER A WITH DIAERESIS"],
'\x85' : ["&#x00E0;", "LATIN SMALL LETTER A WITH GRAVE"],
'\x86' : ["&#x00E5;", "LATIN SMALL LETTER A WITH RING ABOVE"],
'\x87' : ["&#x00E7;", "LATIN SMALL LETTER C WITH CEDILLA"],
'\x88' : ["&#x00EA;", "LATIN SMALL LETTER E WITH CIRCUMFLEX"],
'\x89' : ["&#x00EB;", "LATIN SMALL LETTER E WITH DIAERESIS"],
'\x8a' : ["&#x00E8;", "LATIN SMALL LETTER E WITH GRAVE"],
'\x8b' : ["&#x00EF;", "LATIN SMALL LETTER I WITH DIAERESIS"],
'\x8c' : ["&#x00EE;", "LATIN SMALL LETTER I WITH CIRCUMFLEX"],
'\x8d' : ["&#x00EC;", "LATIN SMALL LETTER I WITH GRAVE"],
'\x8e' : ["&#x00C4;", "LATIN CAPITAL LETTER A WITH DIAERESIS"],
'\x8f' : ["&#x00C5;", "LATIN CAPITAL LETTER A WITH RING ABOVE"],
'\x90' : ["&#x00C9;", "LATIN CAPITAL LETTER E WITH ACUTE"],
'\x91' : ["&#x00E6;", "LATIN SMALL LETTER AE"],
'\x92' : ["&#x00C6;", "LATIN CAPITAL LETTER AE"],
'\x93' : ["&#x00F4;", "LATIN SMALL LETTER O WITH CIRCUMFLEX"],
'\x94' : ["&#x00F6;", "LATIN SMALL LETTER O WITH DIAERESIS"],
'\x95' : ["&#x00F2;", "LATIN SMALL LETTER O WITH GRAVE"],
'\x96' : ["&#x00FB;", "LATIN SMALL LETTER U WITH CIRCUMFLEX"],
'\x97' : ["&#x00F9;", "LATIN SMALL LETTER U WITH GRAVE"],
'\x98' : ["&#x00FF;", "LATIN SMALL LETTER Y WITH DIAERESIS"],
'\x99' : ["&#x00D6;", "LATIN CAPITAL LETTER O WITH DIAERESIS"],
'\x9a' : ["&#x00DC;", "LATIN CAPITAL LETTER U WITH DIAERESIS"],
'\x9b' : ["&#x00A2;", "CENT SIGN"],
'\x9c' : ["&#x00A3;", "POUND SIGN"],
'\x9d' : ["&#x00A5;", "YEN SIGN"],
'\x9e' : ["&#x20A7;", "PESETA SIGN"],
'\x9f' : ["&#x0192;", "LATIN SMALL LETTER F WITH HOOK"],
'\xa0' : ["&#x00E1;", "LATIN SMALL LETTER A WITH ACUTE"],
'\xa1' : ["&#x00ED;", "LATIN SMALL LETTER I WITH ACUTE"],
'\xa2' : ["&#x00F3;", "LATIN SMALL LETTER O WITH ACUTE"],
'\xa3' : ["&#x00FA;", "LATIN SMALL LETTER U WITH ACUTE"],
'\xa4' : ["&#x00F1;", "LATIN SMALL LETTER N WITH TILDE"],
'\xa5' : ["&#x00D1;", "LATIN CAPITAL LETTER N WITH TILDE"],
'\xa6' : ["&#x00AA;", "FEMININE ORDINAL INDICATOR"],
'\xa7' : ["&#x00BA;", "MASCULINE ORDINAL INDICATOR"],
'\xa8' : ["&#x00BF;", "INVERTED QUESTION MARK"],
'\xa9' : ["&#x2310;", "REVERSED NOT SIGN"],
'\xaa' : ["&#x00AC;", "NOT SIGN"],
'\xab' : ["&#x00BD;", "VULGAR FRACTION ONE HALF"],
'\xac' : ["&#x00BC;", "VULGAR FRACTION ONE QUARTER"],
'\xad' : ["&#x00A1;", "INVERTED EXCLAMATION MARK"],
'\xae' : ["&#x00AB;", "LEFT-POINTING DOUBLE ANGLE QUOTATION MARK"],
'\xaf' : ["&#x00BB;", "RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK"],
'\xb0' : ["&#x2591;", "LIGHT SHADE"],
'\xb1' : ["&#x2592;", "MEDIUM SHADE"],
'\xb2' : ["&#x2593;", "DARK SHADE"],
'\xb3' : ["&#x2502;", "BOX DRAWINGS LIGHT VERTICAL"],
'\xb4' : ["&#x2524;", "BOX DRAWINGS LIGHT VERTICAL AND LEFT"],
'\xb5' : ["&#x2561;", "BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE"],
'\xb6' : ["&#x2562;", "BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE"],
'\xb7' : ["&#x2556;", "BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE"],
'\xb8' : ["&#x2555;", "BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE"],
'\xb9' : ["&#x2563;", "BOX DRAWINGS DOUBLE VERTICAL AND LEFT"],
'\xba' : ["&#x2551;", "BOX DRAWINGS DOUBLE VERTICAL"],
'\xbb' : ["&#x2557;", "BOX DRAWINGS DOUBLE DOWN AND LEFT"],
'\xbc' : ["&#x255D;", "BOX DRAWINGS DOUBLE UP AND LEFT"],
'\xbd' : ["&#x255C;", "BOX DRAWINGS UP DOUBLE AND LEFT SINGLE"],
'\xbe' : ["&#x255B;", "BOX DRAWINGS UP SINGLE AND LEFT DOUBLE"],
'\xbf' : ["&#x2510;", "BOX DRAWINGS LIGHT DOWN AND LEFT"],
'\xc0' : ["&#x2514;", "BOX DRAWINGS LIGHT UP AND RIGHT"],
'\xc1' : ["&#x2534;", "BOX DRAWINGS LIGHT UP AND HORIZONTAL"],
'\xc2' : ["&#x252C;", "BOX DRAWINGS LIGHT DOWN AND HORIZONTAL"],
'\xc3' : ["&#x251C;", "BOX DRAWINGS LIGHT VERTICAL AND RIGHT"],
'\xc4' : ["&#x2500;", "BOX DRAWINGS LIGHT HORIZONTAL"],
'\xc5' : ["&#x253C;", "BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL"],
'\xc6' : ["&#x255E;", "BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE"],
'\xc7' : ["&#x255F;", "BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE"],
'\xc8' : ["&#x255A;", "BOX DRAWINGS DOUBLE UP AND RIGHT"],
'\xc9' : ["&#x2554;", "BOX DRAWINGS DOUBLE DOWN AND RIGHT"],
'\xca' : ["&#x2569;", "BOX DRAWINGS DOUBLE UP AND HORIZONTAL"],
'\xcb' : ["&#x2566;", "BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL"],
'\xcc' : ["&#x2560;", "BOX DRAWINGS DOUBLE VERTICAL AND RIGHT"],
'\xcd' : ["&#x2550;", "BOX DRAWINGS DOUBLE HORIZONTAL"],
'\xce' : ["&#x256C;", "BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL"],
'\xcf' : ["&#x2567;", "BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE"],
'\xd0' : ["&#x2568;", "BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE"],
'\xd1' : ["&#x2564;", "BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE"],
'\xd2' : ["&#x2565;", "BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE"],
'\xd3' : ["&#x2559;", "BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE"],
'\xd4' : ["&#x2558;", "BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE"],
'\xd5' : ["&#x2552;", "BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE"],
'\xd6' : ["&#x2553;", "BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE"],
'\xd7' : ["&#x256B;", "BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE"],
'\xd8' : ["&#x256A;", "BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE"],
'\xd9' : ["&#x2518;", "BOX DRAWINGS LIGHT UP AND LEFT"],
'\xda' : ["&#x250C;", "BOX DRAWINGS LIGHT DOWN AND RIGHT"],
'\xdb' : ["&#x2588;", "FULL BLOCK"],
'\xdc' : ["&#x2584;", "LOWER HALF BLOCK"],
'\xdd' : ["&#x258C;", "LEFT HALF BLOCK"],
'\xde' : ["&#x2590;", "RIGHT HALF BLOCK"],
'\xdf' : ["&#x2580;", "UPPER HALF BLOCK"],
'\xe0' : ["&#x03B1;", "GREEK SMALL LETTER ALPHA"],
'\xe1' : ["&#x03B2;", "GREEK SMALL LETTER BETA"],
'\xe2' : ["&#x0393;", "GREEK CAPITAL LETTER GAMMA"],
'\xe3' : ["&#x03C0;", "GREEK SMALL LETTER PI"],
'\xe4' : ["&#x03A3;", "GREEK CAPITAL LETTER SIGMA"],
'\xe5' : ["&#x03C3;", "GREEK SMALL LETTER SIGMA"],
'\xe6' : ["&#x00B5;", "MICRO SIGN"],
'\xe7' : ["&#x03C4;", "GREEK SMALL LETTER TAU"],
'\xe8' : ["&#x03A6;", "GREEK CAPITAL LETTER PHI"],
'\xe9' : ["&#x0398;", "GREEK CAPITAL LETTER THETA"],
'\xea' : ["&#x03A9;", "GREEK CAPITAL LETTER OMEGA"],
'\xeb' : ["&#x03B4;", "GREEK SMALL LETTER DELTA"],
'\xec' : ["&#x221E;", "INFINITY"],
'\xed' : ["&#x2205;", "EMPTY SET"],
'\xee' : ["&#x2208;", "ELEMENT OF"],
'\xef' : ["&#x2229;", "INTERSECTION"],
'\xf0' : ["&#x2261;", "IDENTICAL TO"],
'\xf1' : ["&#x00B1;", "PLUS-MINUS SIGN"],
'\xf2' : ["&#x2265;", "GREATER-THAN OR EQUAL TO"],
'\xf3' : ["&#x2264;", "LESS-THAN OR EQUAL TO"],
'\xf4' : ["&#x2320;", "TOP HALF INTEGRAL"],
'\xf5' : ["&#x2321;", "BOTTOM HALF INTEGRAL"],
'\xf6' : ["&#x00F7;", "DIVISION SIGN"],
'\xf7' : ["&#x2248;", "ALMOST EQUAL TO"],
'\xf8' : ["&#x00B0;", "DEGREE SIGN"],
'\xf9' : ["&#x2219;", "BULLET OPERATOR"],
'\xfa' : ["&#x00B7;", "MIDDLE DOT"],
'\xfb' : ["&#x221A;", "SQUARE ROOT"],
'\xfc' : ["&#x207F;", "SUPERSCRIPT LATIN SMALL LETTER N"],
'\xfd' : ["&#x00B2;", "SUPERSCRIPT TWO"],
'\xfe' : ["&#x25AA;", "SMALL BLACK SQUARE"]
}
*/
SP.Terminal = function(cols, lines) {
this.cols = cols;
this.lines = lines;
this.cursorLine = 0;
this.cursorCol = 0;
this.lineData = [];
this.fg = this.bg = undefined;
this.dirtyLines = [];
this.initialize();
};
SP.Terminal.prototype = {
initialize: function() {
var container = $(".player .term");
this.element = container;
for (l = 0; l < this.lines; l++) {
var row = $("<span class='line'>");
container.append(row);
container.append("\n");
this.lineData[l] = [];
this.updateLine(l);
}
},
setSGR: function(codes) {
if (codes.length == 0) {
codes = [0];
}
for (var i=0; i<codes.length; i++) {
var n = codes[i];
if (n === 0) {
this.fg = this.bg = undefined;
this.bright = false;
} else if (n == 1) {
this.bright = true;
} else if (n >= 30 && n <= 37) {
this.fg = n - 30;
} else if (n >= 40 && n <= 47) {
this.bg = n - 40;
}
}
},
updateLine: function(n) {
n = (typeof n != "undefined" ? n : this.cursorLine);
this.dirtyLines.push(n);
},
updateDirtyLines: function() {
var updated = [];
for (var i=0; i<this.dirtyLines.length; i++) {
var n = this.dirtyLines[i];
if (updated.indexOf(n) == -1) {
this._updateLine(n);
updated.push(n);
}
}
this.dirtyLines = [];
},
_updateLine: function(n) {
var html;
if (n == this.cursorLine) {
var text = this.lineData[n];
html = text.slice(0, this.cursorCol).concat(['<span class="cursor">' + (text[this.cursorCol] || '') + "</span>"], text.slice(this.cursorCol + 1) || []);
} else {
html = this.lineData[n];
}
this.element.find(".line:eq(" + n + ")").html(html.join(''));
},
setCursorPos: function(line, col) {
line -= 1;
col -= 1;
var oldLine = this.cursorLine;
this.cursorLine = line;
this.cursorCol = col;
this.updateLine(oldLine);
this.updateLine();
},
saveCursor: function() {
this.savedCol = this.cursorCol;
this.savedLine = this.cursorLine;
},
restoreCursor: function() {
var oldLine = this.cursorLine;
this.cursorLine = this.savedLine;
this.cursorCol = this.savedCol;
this.updateLine(oldLine);
this.updateLine();
},
cursorLeft: function() {
if (this.cursorCol > 0)
this.cursorCol = this.cursorCol - 1;
this.updateLine();
},
cursorRight: function() {
if (this.cursorCol < this.cols)
this.cursorCol = this.cursorCol + 1;
this.updateLine();
},
cursorUp: function() {
if (this.cursorLine > 0)
this.cursorLine = this.cursorLine - 1;
this.updateLine(this.cursorLine);
this.updateLine(this.cursorLine+1);
},
cursorDown: function() {
if (this.cursorLine < this.lines)
this.cursorLine = this.cursorLine + 1;
this.updateLine(this.cursorLine);
this.updateLine(this.cursorLine-1);
},
cursorForward: function(n) {
for (var i=0; i<n; i++) this.cursorRight();
},
cursorBack: function(n) {
for (var i=0; i<n; i++) this.cursorLeft();
},
cr: function() {
this.cursorCol = 0;
this.updateLine();
},
bs: function() {
if (this.cursorCol > 0) {
this.lineData[this.cursorLine][this.cursorCol - 1] = ' ';
this.cursorCol = this.cursorCol - 1;
this.updateLine();
}
},
print: function(text) {
text = Utf8.decode(text);
for (var i=0; i<text.length; i++) {
this.fill(this.cursorLine, this.cursorCol, 1, text[i]);
this.cursorCol = this.cursorCol + 1;
}
this.updateLine();
},
eraseData: function(n) {
if (n == 0) {
this.eraseLine(n);
for (var l=this.cursorLine+1; l<this.lines; l++) {
this.lineData[l] = [];
this.updateLine(l);
}
} else if (n == 1) {
for (var l=0; l<this.cursorLine; l++) {
this.lineData[l] = [];
this.updateLine(l);
}
this.eraseLine(n);
} else if (n == 2) {
for (var l=0; l<this.lines; l++) {
this.lineData[l] = [];
this.updateLine(l);
}
}
},
eraseLine: function(n) {
if (n == 0) {
this.fill(this.cursorLine, this.cursorCol, this.cols - this.cursorCol, ' ');
// this.lineData[this.cursorLine] = this.lineData[this.cursorLine].slice(0, this.cursorCol);
// this.lineData[this.cursorLine] = this.lineData[this.cursorLine].slice(0, this.cursorCol) + " ".times(this.cols - this.cursorCol);
this.updateLine(this.cursorLine);
} else if (n == 1) {
this.fill(this.cursorLine, 0, this.cursorCol, ' ');
// this.lineData[this.cursorLine] = " ".times(this.cursorCol).split('').concat(this.lineData[this.cursorLine].slice(this.cursorCol));
// this.lineData[this.cursorLine] = " ".times(this.cursorCol) + this.lineData[this.cursorLine].slice(this.cursorCol);
this.updateLine(this.cursorLine);
} else if (n == 2) {
this.fill(this.cursorLine, 0, this.cols, ' ');
// this.lineData[this.cursorLine] = [] // " ".times(this.cols);
this.updateLine(this.cursorLine);
}
},
reserveCharacters: function(n) {
var line = this.lineData[this.cursorLine];
this.lineData[this.cursorLine] = line.slice(0, this.cursorCol).concat(" ".times(n).split(''), line.slice(this.cursorCol, this.cols - n));
this.updateLine();
},
fill: function(line, col, n, char) {
var prefix = '', postfix = '';
if (this.fg !== undefined || this.bg !== undefined || this.bright) {
prefix = '<span class="';
var brightOffset = this.bright ? 8 : 0;
if (this.fg !== undefined) {
prefix += ' fg' + (this.fg + brightOffset);
} else if (this.bright) {
prefix += ' bright';
}
if (this.bg !== undefined) {
prefix += ' bg' + this.bg;
}
prefix += '">';
postfix = '</span>';
}
var char = prefix + char + postfix;
for (var i=0; i<n; i++) {
this.lineData[line][col+i] = char;
}
// this.lineData[line] = this.lineData[line].slice(0, col).concat(char, this.lineData[line].slice(col + 1));
},
blinkCursor: function() {
var cursor = this.element.find(".cursor");
if (cursor.hasClass("inverted")) {
cursor.removeClass("inverted");
} else {
cursor.addClass("inverted");
}
},
restartCursorBlink: function() {
if (this.cursorTimerId) {
clearInterval(this.cursorTimerId);
this.cursorTimerId = null;
}
this.cursorTimerId = setInterval(this.blinkCursor.bind(this), 500);
}
};

View File

@ -0,0 +1,68 @@
/**
*
* UTF-8 data encode / decode
* http://www.webtoolkit.info/
*
**/
var Utf8 = {
// public method for url encoding
encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// public method for url decoding
decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}

183
public/stylesheets/main.css Normal file
View File

@ -0,0 +1,183 @@
.clear {
clear: both;
display: block;
visibility: hidden;
}
body {
background-color: #E7E7DE;
margin: 0;
padding: 0;
}
body, div, p {
font-family: arial, helvetica;
font-size: 12px;
}
/* 325A66 */
/* DEA140 */
/* A32B26 */
/* 590D0B */
h1 {
color: #3e3e3e;
font-family: 'Trebuchet MS';
font-size: 42px;
padding: 15px 0;
}
h2 {
color: #3e3e3e;
font-family: 'Trebuchet MS';
font-size: 26px;
padding: 15px 15px 40px 15px;
}
#top {
position: relative;
height: 80px;
}
#logo {
display: block;
width: 300px;
position: absolute;
top: 0;
left: 0;
}
#menu {
display: block;
width: 300px;
position: absolute;
top: 0;
right: 0;
font-size: 11px;
font-family: Verdana;
}
#menu .links {
background-color: #172322;
list-style: none;
padding: 10px;
border-bottom-left-radius: 10px;
}
#menu .links li {
color: #eee;
display: inline-block;
margin-right: 20px;
}
#all {
margin: 0 auto;
width: 980px;
text-align: left;
}
#main {
background-color: white;
width: 980px;
font-size: 11px;
border-top-left-radius: 10px;
}
.player {
/* border: 1px solid #777;*/
/* background-color: #333;*/
float: left;
display: block;
padding: 0px;
margin: 0px 0px 30px 20px;
position: relative;
}
.term {
padding: 0px;
margin: 0px;
display: block;
font-family: 'Droid Sans Mono', Monospace;
white-space: pre;
background-color: black;
line-height: 1.2em;
color: #ccc;
}
.term .line {
font-size: 12px;
/* background-color: black;*/
/* padding: 0;*/
/* margin: 0;*/
}
.line .cursor {
background-color: #D3D7CF;
}
.line span.cursor.inverted {
background-color: inherit;
}
.hud {
background-color: #333;
opacity: 0.85;
position: absolute;
left: 20px;
right: 20px;
bottom: 10px;
display: none;
height: 30px;
color: white;
}
.player:hover .hud {
display: block;
}
.fg0 { color: #000000 }
.fg1 { color: #CC0000 }
.fg2 { color: #4E9A06 }
.fg3 { color: #C4A000 }
.fg4 { color: #3465A4 }
.fg5 { color: #75507B }
.fg6 { color: #06989A }
.fg7 { color: #D3D7CF }
.fg8 { color: #555753; font-weight: bold }
.fg9 { color: #EF2929; font-weight: bold }
.fg10 { color: #8AE234; font-weight: bold }
.fg11 { color: #FCE94F; font-weight: bold }
.fg12 { color: #729FCF; font-weight: bold }
.fg13 { color: #AD7FA8; font-weight: bold }
.fg14 { color: #34E2E2; font-weight: bold }
.fg15 { color: #EEEEEC; font-weight: bold }
.bright { font-weight: bold }
.bg0 { background-color: #000000 }
.bg1 { background-color: #CC0000 }
.bg2 { background-color: #4E9A06 }
.bg3 { background-color: #C4A000 }
.bg4 { background-color: #3465A4 }
.bg5 { background-color: #75507B }
.bg6 { background-color: #06989A }
.bg7 { background-color: #D3D7CF }
.bg8 { background-color: #555753 }
.bg9 { background-color: #EF2929 }
.bg10 { background-color: #8AE234 }
.bg11 { background-color: #FCE94F }
.bg12 { background-color: #729FCF }
.bg13 { background-color: #AD7FA8 }
.bg14 { background-color: #34E2E2 }
.bg15 { background-color: #EEEEEC }
.description {
color: #666;
font-family: arial, helvetica;
font-size: 16px;
margin: 20px;
}
.description p {
margin-bottom: 20px;
}

View File

@ -0,0 +1,88 @@
/* html5doctor.com Reset Stylesheet v1.6.1
Last Updated: 2010-09-17
Author: Richard Clark - http://richclarkdesign.com
*/
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
margin:0;
padding:0;
border:0;
outline:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
body {
line-height:1;
}
article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
display:block;
}
nav ul {
list-style:none;
}
blockquote, q {
quotes:none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content:'';
content:none;
}
a {
margin:0;
padding:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
/* change colours to suit your needs */
ins {
background-color:#ff9;
color:#000;
text-decoration:none;
}
/* change colours to suit your needs */
mark {
background-color:#ff9;
color:#000;
font-style:italic;
font-weight:bold;
}
del {
text-decoration: line-through;
}
abbr[title], dfn[title] {
border-bottom:1px dotted;
cursor:help;
}
table {
border-collapse:collapse;
border-spacing:0;
}
/* change border colour to suit your needs */
hr {
display:block;
height:1px;
border:0;
border-top:1px solid #cccccc;
margin:1em 0;
padding:0;
}
input, select {
vertical-align:middle;
margin-top: 0;
}