未完成です。休むともうさっぱり。睡魔にも勝てず・・・
きっとまた、あさっての方向を向いている。
<!DOCTYPE html> <html lang="ja"> <head> <title>Terminal Emulator</title> <meta charset="utf-8"> <style> body { margin : 0; width : 100%; } hgroup { color : white; background : black; padding : 0 6px 2px 6px; margin : 0; height : 16px; } hgroup > h1 { font-size : small; margin : 0; float : left; height : 16px; } hgroup > h2 { font-size : x-small; margin : 0; text-align : right; padding-top: 2px; } #DISPLAY { clear : both; width : 100%; margin : 0; } #DISPLAY > p, #DISPLAY > pre { margin : 0; width : 100%; } #DISPLAY > p > input.command_line { border : 0 none; background : transparent; width : 100%; margin : 0; font-size : medium; } </style> </head> <body> <hgroup> <h1><span id="TITLE">XXX</span> Terminal Emulator</h1> <h2><span id="SUB_TITLE">babu_babu_baboo<span></h2> </hgroup> <div id="DISPLAY"> </div> <script> // 指定したノードに、データを変換して、タイプ別のノードを生成して追加する function createHTMLConverter (target, type, converter) { if (1 > arguments.length) throw new Error ('ノードが指定されていない'); var doc = target.ownerDocument; return function (data) { var node; data = ('function' === typeof converter) ? converter (data): data; switch (type) { case 1 : case 'pre' : node = doc.createElement ('pre'); node.appendChild (doc.createTextNode (data.replace (/\n|\r|\n\r/g, '<br>'))); break; case 2 : case 'innerHTML' : node = doc.createDocumentFragment (); node.innerHTML = data; break; case 0 : case 'textarea' : default : node = doc.createElement ('textarea'); node.value = data; } if (node) target.appendChild (node); }; } //____________________________________________________________ function Input_Wrapper (obj) { if (1 > arguments.length) throw new Error ('引数がない'); return new function () { this.clear = function () { return obj.clear (); }; this.disabled = function () { return obj.disabled (); }; this.focus = function () { obj.focus (); }; this.standby = function () { return obj.standby (); }; this.setCurrentCommand = function (val) { return obj.setCurrentCommand (val); }; this.getCurrentCommand = function () { return obj.getCurrentCommand (); }; this.isTarget = function (node) { return obj.isTarget (node); }; this.init = function () { this.standby (); }; }; } //____________________________________________________________ function Output_Wrapper (obj) { if (1 > arguments.length) throw new Error ('引数がない'); return new function () { this.clear = function () { obj.clear (); }; this.print = function (data) { obj.print (data); }; this.init = function () { obj.init (); }; }; } //____________________________________________________________ var HTMLView = new function () { var doc = document; var target = doc.getElementById ('DISPLAY'); var INPUT = null; var prompt = '$>'; // Input this.input = new function () { this.init = function () { this.standby (); }; this.clear = function () { INPUT.value = ''; }; this.disabled = function () { if (INPUT) INPUT.disabled = true; }; this.focus = function () { if (INPUT) INPUT.focus (); }; this.isTarget = function (node) { return (INPUT && (node == INPUT)); }; this.setCurrentCommand = function (val) { INPUT.value = prompt + (val || '') }; this.getCurrentCommand = function () { return INPUT.value.substring (prompt.length); }; this.standby = function () { var p = doc.createElement ('p'); INPUT = doc.createElement ('input'); INPUT.type = 'text'; INPUT.className = 'command_line'; INPUT.value = prompt; p.appendChild (INPUT); target.appendChild (p); setTimeout (function (n) { var len = n.value.length; n.focus (); n.setSelectionRange (len, len); //error? }, 100, INPUT); }; }; // Output this.output = new function () { var printer = createHTMLConverter (target, 'pre'); this.clear = (function () { while (target.hasChildNodes ()) target.removeChild (target.firstChild); }); this.print = (function (data) { printer (data); }); this.init = this.clear; }; }; //____________________________________________________________ (function () { /*____________________________________________________________ * ターミナルエミュレーター */ function TerminalEmulator (processor, input, output) { if (2 > arguments.length) throw new Error ('引数が足りない'); if ('undefined' === typeof output) // output が省略されたら、alert で代用 output = alert_; var buf = []; // コマンドバッファー var idx = 0; // バッファーのインデックス var emu = new function () { this.init = // 各オブジェクトに対し、初期化を実行する (function () { initCall (processor); initCall (input); initCall (output); input.standby (); }); this.nextCommand = // コマンドラインバッファから次を選択 (function () { moveIndex (+1); input.setCurrentCommand (buf[idx]); }); this.previousCommand = // コマンドラインバッファから前を選択 (function () { moveIndex (-1); input.setCurrentCommand (buf[idx]); }); this.send = // processor に、コマンドを送信 (function (cmd) { var result; cmd = ('undefined' !== typeof cmd) ? cmd: input.getCurrentCommand (); buf.push (cmd); idx = buf.length; input.disabled (); // input 側を無効化 result = processor (cmd); output.print (result); // 結果を出力 input.standby (); // input 側に入力可能にする return result; }); this.isTarget = // その要素が、指令できるのか判断 (function (node) { return input.isTarget (node); }); this.commandLineFocus = (function () { input.focus (); }); }; emu.init (); return emu; //_______________ function moveIndex (n) { idx += n; if (idx < 0) idx = 0; else if (buf.length <= idx) idx = buf.length - 1; } } function alert_ (data) { alert (data.join ('\n')); } function initCall (func) { if ('function' === typeof func.init) func.init (); } // xxx.init が関数であれば実行 /*____________________________________________________________ * キーでオブジェクトを制御 */ function createKeyController (object) { return function (event) { var e = event.target; switch (event.type) { case 'keyup' : if ((! e.disabled) && (object.isTarget (e))) { switch (event.keyCode) { case 13 : object.send (); break; case 38 : object.previousCommand (); break; case 40 : object.nextCommand (); break; } } break; case 'focus' : object.commandLineFocus (); break; } }; } //____________________________________________________________ this.TerminalEmulator = TerminalEmulator; this.createKeyController = createKeyController; })(); //____________________________________________________________ var processor = function echo (command) { return 'echo:' + command; }; var inDevice = Input_Wrapper (HTMLView.input); var outDevice = Output_Wrapper (HTMLView.output); var emulator = TerminalEmulator (processor, inDevice, outDevice); var handler = createKeyController (emulator); document.addEventListener ('keyup', handler, false); window.addEventListener ('focus', handler, false); </script>