无聊的时候想到用jscript模拟Console程序,结果试了一下,觉得还蛮好玩的:P
实现了一个jscript“控制台”类
在读代码前先放出三个Demo:
http://akira.bigwww.com/Silver%20Luna/Demos/ConsoleDemo.html
通过“控制台”输入两个数求和
http://akira.bigwww.com/Silver%20Luna/Demos/ConsoleDemo2.html
通过“控制台”输入一个句子,统计字符类型和数量
http://akira.bigwww.com/Silver%20Luna/Demos/ConsoleDemo3.html
一个可以通过键盘敲入命令回车执行的模拟,还支持
begin
Codes...
end
的模式 :)
下面是程序代码:
//控制台类,模拟运行在控制台上的程序,可以控制“标准输入输出流”(程序模拟)
//使用方法:在页面上包含Console.js文件,构造Console对象,定义Main()函数,在Main函数中写控制命令
//版本:1.00
//作者:Akira
//编写日期:2004-11-19
function Console(page, consoleName)
{
//静态成员
Console.KEY_ENTER = 13; //回车键
Console.KEY_RETURN = 13; //回车
Console.KEY_NEWLINE = 10; //换行
Console.KEY_TAB = 9; //TAB键
Console.KEY_BACKSPACE = 8; //退格键
Console.KEY_LEFT = 37; //左移
Console.KEY_RIGHT = 38; //右移
Console.KEY_UP = 39; //上移
Console.KEY_DOWN = 40; //下移
//初始化Page
if (page == null)
{
page = self;
}
if (page != self)
{
//do sth here...
throw new Error("参数错误:目前版本不支持为其他窗体对象初始化控制台\n请使用self参数");
}
this.page = page;
//输入输出流属性,用字符串数组模拟流
this.stream = new Object();
this.stream.stdin = new Array(); //标准输入流
this.stream.stdout = new Array(); //标准输出流
this.stream.stderr = new Array(); //标准错误流
this.stream.stdin.opened = false; //判断当前输入流是否开启,开启=允许写入
this.stream.stdin.ReadOffset = 0; //判断当前输入流偏置,Read()方法使用
this.stream.semaphores = 0; //流信号量,用来控制输出流显示,当调用输入函数时,信号量增加,
//当信号量大于stdin成员数目的时候,抛出异常阻塞标准输出流,用以模拟输入等待
Console.prototype.Signal = function()
{
if(this.stream.semaphores == this.stream.stdin.length)
{
return true;
}
if(this.stream.semaphores > this.stream.stdin.length)
{
throw new WaitForInput(); //抛出异常阻止程序往下执行,用以等待输入
}
return false;
}
this.body = page.document.body;
if (this.body == null)
{
throw new Error("控制台缺初始化失败,检查主体文档是否缺少BODY标记");
}
this.page.document.title += "——Javascript控制台";
this.page.PageLoadEventSender = this;
this.body.onload = function(){this.PageLoadEventSender.PageLoad(this.PageLoadEventSender,this.PageLoadEventSender.page.event);} //定义PageLoad事件
this.page.ResizeEventSender = this;
this.body.onresize = function(){this.ResizeEventSender.OnResize(this.ResizeEventSender,this.ResizeEventSender.page.event);}
this.ConsoleMain = page.document.getElementById(consoleName); //可以通过参数指定初始化对象TEXTAREA的名称,这样可以把控制台
//主窗口嵌入在网页中
//如果没有指定获得TEXTAREA对象,则建立控制台主窗口,并让窗口占据整个页面
if (this.ConsoleMain == null)
{
this.body.style.background = "#000000";
this.page.document.bgColor = "#000000";
this.ConsoleMain = page.document.createElement("textarea");
page.document.body.appendChild(this.ConsoleMain);
this.ConsoleMain.style.width="100%";
this.ConsoleMain.style.height="100%";
this.ConsoleMain.style.overflowX="hidden";
this.ConsoleMain.style.overflowY="hidden";
this.ConsoleMain.style.border="0px";
this.ConsoleMain.style.background="#000000";
this.ConsoleMain.style.color="C0C0C0";
}
if (this.ConsoleMain.tagName.toUpperCase() != "TEXTAREA")
{
throw new Error("控制台主窗口类型错误,主窗口对象必须为TEXTAREA类型");
}
this.ConsoleMain.KeyPressSender = this; //KeyPress事件
this.ConsoleMain.onkeypress = function(){this.KeyPressSender.ConsoleMain_KeyPress(this.KeyPressSender, this.KeyPressSender.page.event);}
this.ConsoleMain.KeyUpSender = this; //KeyUp事件
this.ConsoleMain.onkeyup = function(){this.KeyUpSender.ConsoleMain_KeyUp(this.KeyUpSender, this.KeyUpSender.page.event);}
this.ConsoleMain.KeyDownSender = this; //KeyDown事件
this.ConsoleMain.onkeydown = function(){return(this.KeyDownSender.ConsoleMain_KeyDown(this.KeyDownSender, this.KeyDownSender.page.event));}
this.ConsoleMain.ClickSender = this; //Click事件,用来避免光标位置随鼠标点击而改变
this.ConsoleMain.onclick = function(){this.ClickSender.ConsoleMain_Click(this.ClickSender, this.ClickSender.page.event);}
//Event Handlers
Console.prototype.PageLoad = function(sender, event) //PageLoad事件处理,调用this.Main()方法用来初始化控制台
{
//create frameConsole
this.Main();
}
Console.prototype.OnResize = function(sender, event) //Resize事件处理,将文字正确显示在控制台顶部
{
this.SetCursor();
}
Console.prototype.ConsoleMain_KeyDown = function(sender, event)
{
if (event.keyCode == Console.KEY_TAB) //处理TAB键,避免主窗口失去焦点
{
this.ConsoleMain.value += "\t";
if (!this.stream.stdin.opened)
{
this.stream.stdin.push("\t");
this.stream.stdin.opened = true;
}
else
{
this.stream.stdin[this.stream.stdin.length - 1] += "\t";
}
return false;
}
if (event.keyCode == Console.KEY_LEFT
||event.keyCode == Console.KEY_RIGHT
||event.keyCode == Console.KEY_UP
||event.keyCode == Console.KEY_DOWN) //忽略方向键,锁定光标
{
return false;
}
return true;
}
Console.prototype.ConsoleMain_KeyUp = function(sender, event)
{
if (event.keyCode == Console.KEY_ENTER) //处理回车键结束流的写入
{
if(!this.stream.stdin.opened) //忽略空行和连续回车
return;
else
{
this.stream.stdin.opened = false;
this.Main(); //结束流的写入,并让程序继续往下执行
}
}
if (event.keyCode == Console.KEY_BACKSPACE) //处理退格键
{
if (!this.stream.stdin.opened) //忽略当前流为空时的退格键
{
return;
}
else
{
if (this.stream.stdin[this.stream.stdin.length - 1].length == 0)
{
return;
}
this.stream.stdin[this.stream.stdin.length - 1] = this.stream.stdin[this.stream.stdin.length - 1].substr(0, this.stream.stdin[this.stream.stdin.length - 1].length - 1);
if (this.stream.stdin[this.stream.stdin.length - 1].length == 0)
{
this.stream.stdin.length--;
this.stream.stdin.opened = false;
}
}
}
}
Console.prototype.ConsoleMain_KeyPress = function(sender, event) //键盘事件,将字符写入流
{
if (!this.stream.stdin.opened) //New Stream
{
if (event.keyCode != Console.KEY_ENTER)
{
this.stream.stdin.push(String.fromCharCode(event.keyCode));
this.stream.stdin.opened = true;
}
}
else
{
if (event.keyCode != Console.KEY_ENTER)
this.stream.stdin[this.stream.stdin.length - 1] += String.fromCharCode(event.keyCode);
}
}
Console.prototype.ConsoleMain_Click = function(sender, event)
{
this.SetCursor();
}
//Methods
Console.prototype.init = function() //初始化方法,在this.Main()中被调用
{
//this.ConsoleMain.value="控制台初始化完成...\n";
this.ConsoleMain.contentEditable = true;
this.stream.semaphores = 0;
this.stream.stdin.ReadOffset = 0;
this.stream.stdin.opened = false;
}
Console.prototype.WriteLine = function(msg) //写入一行文本到标准输出流
{
if (this.Signal())
{
this.ConsoleMain.value+=msg+"\n";
this.stream.stdout.push(msg+"\n");
}
}
Console.prototype.Write = function(msg) //写入一个字符串到标准输出流
{
if (this.Signal())
{
this.ConsoleMain.value+=msg;
this.stream.stdout.push(msg);
}
}
Console.prototype.Error = function(msg) //写入错误信息到标准错误流
{
if (this.Signal())
{
this.ConsoleMain.value+=msg;
this.stream.stderr.push(msg);
}
}
Console.prototype.ReadLine = function() //从标准输入流读取一行
{
if (this.Signal())
{
this.SetCursor(); //光标移至输入位置
throw new WaitForInput(); //抛出异常阻止程序往下执行,用以等待输入
}
this.stream.semaphores++;
if (this.stream.semaphores <= this.stream.stdin.length)
{
if (isNaN(this.stream.stdin[this.stream.semaphores - 1])) //非数字返回对象
{
return this.stream.stdin[this.stream.semaphores - 1];
}
else
{
return this.stream.stdin[this.stream.semaphores - 1] - 0; //数字返回数值
}
}
}
Console.prototype.SetCursor = function()
{
this.ConsoleMain.focus();
var range = this.ConsoleMain.createTextRange();
range.moveStart('character',this.ConsoleMain.value.lenght);
range.collapse(false);
range.select();
}
Console.prototype.Read = function() //从标准输入流读取下一个字符
{
if (this.Signal() && this.stream.stdin.ReadOffset == 0)
{
this.ConsoleMain.focus();
var range = this.ConsoleMain.createTextRange();
range.moveStart('character',this.ConsoleMain.value.lenght);
range.collapse(false);
range.select();
this.stream.stdin.push("");
this.stream.stdin.opened=true;
throw new WaitForInput(); //抛出异常阻止程序往下执行,用以等待输入
}
else if (this.stream.semaphores <= this.stream.stdin.length)
{
if (this.stream.stdin.ReadOffset == 0) //读取流的第一个字符,信号量增加
{
this.stream.semaphores++;
}
if (this.stream.stdin.ReadOffset < this.stream.stdin[this.stream.semaphores - 1].length)
{
return this.stream.stdin[this.stream.semaphores - 1].charCodeAt(this.stream.stdin.ReadOffset++);
}
else if(this.stream.stdin.ReadOffset == this.stream.stdin[this.stream.semaphores - 1].length) //恢复在流的末尾被忽略的回车和换行
{
this.stream.stdin.ReadOffset++;
return Console.KEY_RETURN;
}
else if(this.stream.stdin.ReadOffset == this.stream.stdin[this.stream.semaphores - 1].length + 1)
{
this.stream.stdin.ReadOffset=0;
return Console.KEY_NEWLINE;
}
else
{
throw new Error("Stack over flow! Read Error!");
}
}
}
Console.prototype.Main = function()
{
this.init();
try
{
Main();
if (this.Signal())
{
this.Dispose();
}
}
catch(e)
{
if (e instanceof WaitForInput)
{
}
else
{
this.Error("错误:" + e.number + "," + e.name + ":" + e.message); //将错误信息写入标准错误流
this.Dispose();
}
}
}
Console.prototype.Dispose = function() //结束控制台流程,按任意键关闭窗口
{
this.ConsoleMain.contentEditable = false;
this.ConsoleMain.focus();
this.Write("\nPress any key to continue...\n");
this.ConsoleMain.KeyDownSender = this;
this.ConsoleMain.onkeydown = function(){this.KeyDownSender.page.close();return false;}
}
}
//等待输入的“异常”类,用来在要求用户输入的时候阻塞程序的运行。
function WaitForInput()
{
}WaitForInput.prototype = new Error();
//调试控制台——可运行和调试javascript程序段
//版本:0.01 Demo
//作者:Akira
//日期:2004-11-19
function DebugConsole()
{
DebugConsole.prototype.RunCommand = function(command)
{
if (this.Signal())
{
var regexp = new RegExp("document.writeln","g");
command = command.replace(regexp, "this.WriteLine");
regexp = new RegExp("document.write","g");
command = command.replace(regexp, "this.Write");
try
{
eval(command);
}
catch(e)
{
this.Error("指令无效:" + e.number + "," + e.name + ":" + e.message + "\n");
}
}
}
DebugConsole.prototype.Start = function()
{
this.WriteLine("调试控制台初始化...完成!");
var command = "";
var process = "";
var runProc = false;
while ((command = this.ReadLine()) != "stop")
{
if (command == "begin")
{
process="";
runProc = true; //执行一组命令
continue;
}
if (command == "end")
{
this.RunCommand(process);
runProc = false;
continue;
}
if (command.charAt(0) == "=")
{
command = "this.WriteLine(" + command.substring(1) + ");";
}
if (!runProc)
{
this.RunCommand(command); //执行一条命令
}
else
{
process += command;
}
}
}
}DebugConsole.prototype = new Console();
//这个控制台目前的缺点是:1)异常信息无法显示行号 2)不能在新开窗口中动态建立控制台
//有待改进......