Skip to content

Instantly share code, notes, and snippets.

@pce
Last active December 14, 2015 04:59
Show Gist options
  • Select an option

  • Save pce/5032084 to your computer and use it in GitHub Desktop.

Select an option

Save pce/5032084 to your computer and use it in GitHub Desktop.
"use strict";
function trace(s) {
if (typeof console != "undefined") {console.log(s);}
}
var TerminalHistory = function () {
this.cmd = [];
this.cmdIndex = 0;
// add empty line
this.cmd[this.cmdIndex] = "";
this.cmdIndex++;
};
TerminalHistory.prototype = {
add:function(cmd) {
this.cmd[this.cmdIndex]=cmd;
this.cmdIndex++;
},
get:function() {
return this.cmd[this.cmdIndex];
},
up:function() {
this.cmdIndex--;
if (this.cmdIndex < 0) this.cmdIndex = 0;
var cmd = this.cmd[this.cmdIndex];
return cmd;
},
down:function() {
this.cmdIndex++;
if (this.cmdIndex > this.cmd.length -1) this.cmdIndex = this.cmd.length -1;
var cmd = this.cmd[this.cmdIndex];
return cmd;
}
};
var Terminal = function (id, history) {
this.id = id;
this.history = history || false;
this.timeStamp = new Date().getTime();
this.line = [];
this.curLine = 0;
// workarround lines with prompt
this.userLine = [];
};
Terminal.prototype = {
prompt: '$ ',
rows:24,
cols:80,
cursorChar:'_',
maxLength:1024,
onKeyPressEvent:function(event) {
trace(event);
event = event || window.event;
var key = event.charCode;
switch (key) {
default:
if (key > 32)
this.line[this.curLine] += String.fromCharCode(key);
break;
}
this.update();
},
onKeyDownEvent:function(event) {
trace(event);
event = event || window.event;
var key = event.keyCode;
switch (key) {
case 6:
case 8:
if (this.line[this.curLine].length)
this.line[this.curLine] = this.line[this.curLine].slice(0, -1);
break;
case 9:
// tab
// this.onReadLine();
break;
case 13:
// enter
this.onEnter();
break;
case 16:
// shift
break;
case 17:
// ctrl
break;
case 18:
// alt
break;
case 27:
// escape
break;
case 32:
this.line[this.curLine] += " ";//"&nbsp;";
break;
case 37:
// left arrow
break;
case 38:
// history (up arrow)
if (this.history)
this.line[this.curLine] = this.history.up();
break;
case 40:
// history (down arrow)
if (this.history)
this.line[this.curLine] = this.history.down();
break;
case 39:
// right arrow
break;
case 46:
// del
break;
case 60:
this.line[this.curLine] += "<";// "&lt;";
break;
case 62:
this.line[this.curLine] += ">";// "&gt;";
break;
default:
// console.log(key);
break;
}
this.update();
},
registerDefaultEvents:function() {
document.onkeydown=this.onKeyDownEvent.bind(this);
document.onkeypress=this.onKeyPressEvent.bind(this);
},
init:function() {
var self;
this.line[this.curLine] = "";
this.userLine.push(this.curLine);
document.getElementById(this.id).focus();
this.registerDefaultEvents();
self = this;
setInterval(function () {
self.update();
}, 1000);
},
echo:function(text) {
this.curLine++;
this.line[this.curLine] = text;
//this.curLine++;
//this.line[this.curLine] = "";
},
onEnter:function() {
this.userLine.push(this.curLine);
this.parseLine();
this.curLine++;
this.line[this.curLine] = "";
this.userLine.push(this.curLine);
},
cmd: {
echo:function(self, argv) {
argv = argv.slice(1, argv.length).join(" ");
self.echo(argv);
},
help:function(self, argv) {
self.echo("JavaScript Fun Terminal, version 0.1");
self.echo("These shell commands are defined internally.");
self.echo("`help' prints this list.");
self.echo("ls - list clickable related links");
self.echo("echo [arg...] - prints to stdout");
},
ls:function(self, argv) {
self.echo('<a href="http://3nv.org" target="_blank">music</a>');
self.echo('<a href="http://github.com/pce" target="_blank">code</a>');
}
},
addCommand:function(cmd, func) {
this.cmd[cmd] = func;
},
parseLine:function() {
if (!this.line[this.curLine].length) return;
if (this.history)
this.history.add(this.line[this.curLine]);
// todo command sep,-operators [";", "&&", "||"]
var cl = this.line[this.curLine].split(" ");
if (typeof this.cmd[cl[0]] != "undefined" && typeof this.cmd[cl[0]] == "function") {
this.cmd[cl[0]](this, cl);
} else {
this.echo("command not found");
}
},
escapeLine:function(curLine) {
return this.line[curLine].replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
},
isUserLine:function(curLine) {
for(var p=0; p < this.userLine.length; p++)
if (curLine == this.userLine[p])
return true;
return false;
},
update:function() {
document.getElementById(this.id).innerHTML = "";
for (var lineIndex=0; lineIndex < this.line.length; lineIndex++) {
if (this.isUserLine(lineIndex)) {
document.getElementById(this.id).innerHTML += this.prompt;
document.getElementById(this.id).innerHTML += this.escapeLine(lineIndex);
} else {
document.getElementById(this.id).innerHTML += this.line[lineIndex];
}
if (lineIndex == this.line.length-1) {
document.getElementById(this.id).innerHTML += this.cursorChar;
} else
document.getElementById(this.id).innerHTML += "<br/>";
}
if (new Date().getTime() - this.timeStamp >= 50) {
this.timeStamp = new Date().getTime(); // Reset the time stamp
if (this.cursor == false) {
this.cursor = true;
if (this.line[this.curLine].length < this.maxLength)
this.cursorChar += "_";
} else {
this.cursor = false;
if (this.cursorChar.length != 0)
this.cursorChar = "";
}
}
}
};
// #IF TEST
// ----------------------------------------
/*
<style>
body {
background-color:black;
font-family: "DejaVu Sans Mono",
Monaco,
"Bitstream Vera Sans Mono",
"Lucida Console",
Terminal,
monospace;
color:#ccc;
}
.terminal {
color:#ccc;
white-space:pre;
}
a {text-decoration:none;}
</style>
<section id="terminal" class="terminal"></section>
*/
// var terminal; // global for console access
window.onload = function() {
var history = new TerminalHistory();
history.add("ls");
var terminal = new Terminal("terminal", history);
terminal.init();
terminal.addCommand("date", function(self, argv) {
self.echo(new Date().toLocaleString());
});
}
// #ENDIF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment