790 lines
22 KiB
JavaScript
790 lines
22 KiB
JavaScript
// browser/anyterm.js
|
|
// This file is part of Anyterm; see http://anyterm.org/
|
|
// (C) 2005-2006 Philip Endecott
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
var undefined;
|
|
|
|
var url_prefix = "";
|
|
|
|
var frame;
|
|
var term;
|
|
var open=false;
|
|
var session;
|
|
|
|
var method="POST";
|
|
//var method="GET";
|
|
|
|
// Random sequence numbers are needed to prevent Opera from caching
|
|
// replies
|
|
|
|
var is_opera = navigator.userAgent.toLowerCase().indexOf("opera") != -1;
|
|
if (is_opera) {
|
|
method="GET";
|
|
}
|
|
|
|
var seqnum_val=Math.round(Math.random()*100000);
|
|
function cachebust() {
|
|
if (is_opera) {
|
|
seqnum_val++;
|
|
return "&x="+seqnum_val;
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
|
|
// Cross-platform creation of XMLHttpRequest object:
|
|
|
|
function new_XMLHttpRequest() {
|
|
if (window.XMLHttpRequest) {
|
|
// For most browsers:
|
|
return new XMLHttpRequest();
|
|
} else {
|
|
// For IE, it's active-X voodoo.
|
|
// There are different versions in different browsers.
|
|
// The ones we try are the ones that Sarissa tried. The disabled ones
|
|
// apparently also exist, but it seems to work OK without trying them.
|
|
|
|
//try{ return new ActiveXObject("MSXML3.XMLHTTP"); } catch(e){}
|
|
try{ return new ActiveXObject("Msxml2.XMLHTTP.5.0"); } catch(e){}
|
|
try{ return new ActiveXObject("Msxml2.XMLHTTP.4.0"); } catch(e){}
|
|
try{ return new ActiveXObject("MSXML2.XMLHTTP.3.0"); } catch(e){}
|
|
try{ return new ActiveXObject("MSXML2.XMLHTTP"); } catch(e){}
|
|
//try{ return new ActiveXObject("Msxml2.XMLHTTP"); } catch(e){}
|
|
try{ return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e){}
|
|
throw new Error("Could not find an XMLHttpRequest active-X class.")
|
|
}
|
|
}
|
|
|
|
|
|
// Asynchronous and Synchronous XmlHttpRequest wrappers
|
|
|
|
// AsyncLoader is a class; an instance specifies a callback function.
|
|
// Call load to get something and the callback is invoked with the
|
|
// returned document.
|
|
|
|
function AsyncLoader(cb) {
|
|
this.callback = cb;
|
|
this.load = function (url,query) {
|
|
var xmlhttp = new_XMLHttpRequest();
|
|
var cbk = this.callback;
|
|
//var timeoutID = window.setTimeout("alert('No response after 20 secs')",20000);
|
|
xmlhttp.onreadystatechange = function () {
|
|
if (xmlhttp.readyState==4) {
|
|
//window.clearTimeout(timeoutID);
|
|
if (xmlhttp.status==200) {
|
|
cbk(xmlhttp.responseText);
|
|
} else {
|
|
alert("Server returned status code "+xmlhttp.status+":\n"+xmlhttp.statusText);
|
|
cbk(null);
|
|
}
|
|
}
|
|
}
|
|
if (method=="GET") {
|
|
xmlhttp.open(method, url+"?"+query, true);
|
|
xmlhttp.send(null);
|
|
} else if (method=="POST") {
|
|
xmlhttp.open(method, url, true);
|
|
xmlhttp.setRequestHeader('Content-Type',
|
|
'application/x-www-form-urlencoded');
|
|
xmlhttp.send(query);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Synchronous loader is a simple function
|
|
|
|
function sync_load(url,query) {
|
|
var xmlhttp = new_XMLHttpRequest();
|
|
if (method=="GET") {
|
|
xmlhttp.open(method, url+"?"+query, false);
|
|
xmlhttp.send(null);
|
|
} else if (method=="POST") {
|
|
xmlhttp.open(method, url, false);
|
|
xmlhttp.setRequestHeader('Foo','1234');
|
|
xmlhttp.setRequestHeader('Content-Type',
|
|
'application/x-www-form-urlencoded');
|
|
xmlhttp.send(query);
|
|
}
|
|
if (xmlhttp.status!=200) {
|
|
alert("Server returned status code "+xmlhttp.status+":\n"+xmlhttp.statusText);
|
|
return null;
|
|
}
|
|
return xmlhttp.responseText;
|
|
}
|
|
|
|
|
|
// Process error message from server:
|
|
|
|
function handle_resp_error(resp) {
|
|
if (resp.charAt(0)=="E") {
|
|
var msg = resp.substr(1);
|
|
alert(msg);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Receive channel:
|
|
|
|
var rcv_loader;
|
|
|
|
var disp="";
|
|
|
|
|
|
|
|
function process_editscript(edscr) {
|
|
|
|
var ndisp="";
|
|
|
|
var i=0;
|
|
var dp=0;
|
|
while (i<edscr.length) {
|
|
var cmd=edscr.charAt(i);
|
|
i++;
|
|
var cp=edscr.indexOf(":",i);
|
|
var num=Number(edscr.substr(i,cp-i));
|
|
i=cp+1;
|
|
//alert("cmd="+cmd+" num="+num);
|
|
if (cmd=="d") {
|
|
dp+=num;
|
|
} else if (cmd=="k") {
|
|
ndisp+=disp.substr(dp,num);
|
|
dp+=num;
|
|
} else if (cmd=="i") {
|
|
//if (edscr.length<i+num) {
|
|
//alert("edit script ended early; expecting "+num+" but got only "+edscr.length-cp);
|
|
//}
|
|
ndisp+=edscr.substr(i,num);
|
|
i+=num;
|
|
}
|
|
}
|
|
|
|
return ndisp;
|
|
}
|
|
|
|
|
|
var visible_height_frac = 1;
|
|
|
|
function display(edscr) {
|
|
|
|
//alert(edscr);
|
|
|
|
var ndisp;
|
|
if (edscr=="n") {
|
|
return;
|
|
} else if (edscr.charAt(0)=="R") {
|
|
ndisp = edscr.substr(1);
|
|
} else {
|
|
ndisp = process_editscript(edscr);
|
|
}
|
|
|
|
disp=ndisp;
|
|
|
|
term.innerHTML=ndisp;
|
|
|
|
if (visible_height_frac != 1) {
|
|
var termheight = visible_height_frac * term.scrollHeight;
|
|
term.style.height = termheight+"px";
|
|
term.scrollTop = term.scrollHeight;
|
|
}
|
|
}
|
|
|
|
|
|
function scrollterm(pages) {
|
|
term.scrollTop += pages * visible_height_frac * term.scrollHeight;
|
|
}
|
|
|
|
|
|
var rcv_timeout;
|
|
|
|
function get() {
|
|
//alert("get");
|
|
rcv_loader.load(url_prefix+"anyterm-module","a=rcv&s="+session+cachebust());
|
|
rcv_timeout = window.setTimeout("alert('no response from server after 60 secs')",60000);
|
|
}
|
|
|
|
function rcv(resp) {
|
|
// Called asynchronously when the received document has returned
|
|
// from the server.
|
|
|
|
window.clearTimeout(rcv_timeout);
|
|
|
|
if (!open) {
|
|
return;
|
|
}
|
|
|
|
if (resp=="") {
|
|
// We seem to get this if the connection to the server fails.
|
|
alert("Connection to server failed");
|
|
return;
|
|
}
|
|
|
|
if (handle_resp_error(resp)) {
|
|
return;
|
|
}
|
|
|
|
display(resp);
|
|
get();
|
|
}
|
|
|
|
rcv_loader = new AsyncLoader(rcv);
|
|
|
|
|
|
// Transmit channel:
|
|
|
|
var kb_buf="";
|
|
var send_loader;
|
|
var send_in_progress=false;
|
|
|
|
function send() {
|
|
send_in_progress=true;
|
|
send_loader.load(url_prefix+"anyterm-module",
|
|
"a=send&s="+session+cachebust()+"&k="+encodeURIComponent(kb_buf));
|
|
kb_buf="";
|
|
}
|
|
|
|
function send_done(resp) {
|
|
send_in_progress=false;
|
|
if (handle_resp_error(resp)) {
|
|
return;
|
|
}
|
|
if (kb_buf!="") {
|
|
send();
|
|
}
|
|
}
|
|
|
|
send_loader = new AsyncLoader(send_done);
|
|
|
|
|
|
function maybe_send() {
|
|
if (!send_in_progress && open && kb_buf!="") {
|
|
send();
|
|
}
|
|
}
|
|
|
|
|
|
function process_key(k) {
|
|
// alert("key="+k);
|
|
// return;
|
|
kb_buf+=k;
|
|
maybe_send();
|
|
}
|
|
|
|
|
|
function esc_seq(s) {
|
|
return String.fromCharCode(27)+"["+s;
|
|
}
|
|
|
|
|
|
function key_ev_stop(ev) {
|
|
// We want this key event to do absolutely nothing else.
|
|
ev.cancelBubble=true;
|
|
if (ev.stopPropagation) ev.stopPropagation();
|
|
if (ev.preventDefault) ev.preventDefault();
|
|
try { ev.keyCode=0; } catch(e){}
|
|
}
|
|
|
|
function key_ev_supress(ev) {
|
|
// We want this keydown event to become a keypress event, but nothing else.
|
|
ev.cancelBubble=true;
|
|
if (ev.stopPropagation) ev.stopPropagation();
|
|
}
|
|
|
|
|
|
// When a key is pressed the browser delivers several events: typically first a keydown
|
|
// event, then a keypress event, then a keyup event. Ideally we'd just use the keypress
|
|
// event, but there's a problem with that: the browser may not send a keypress event for
|
|
// unusual keys such as function keys, control keys, cursor keys and so on. The exact
|
|
// behaviour varies between browsers and probably versions of browsers.
|
|
//
|
|
// So to get these keys we need to get the keydown events. They have a couple of
|
|
// problems. Firstly, you get these events for things like pressing the shift key.
|
|
// Secondly, unlike keypress events you don't get auto-repeat.
|
|
|
|
function keypress(ev) {
|
|
if (!ev) var ev=window.event;
|
|
|
|
// Only handle "safe" characters here. Anything unusual is ignored; it would
|
|
// have been handled earlier by the keydown function below.
|
|
if ((ev.ctrlKey && !ev.altKey) // Ctrl is pressed (but not altgr, which is reported
|
|
// as ctrl+alt in at least some browsers).
|
|
|| (ev.which==0) // there's no key in the event; maybe a shift key?
|
|
// (Mozilla sends which==0 && keyCode==0 when you press
|
|
// the 'windows logo' key.)
|
|
|| (ev.keyCode==8) // backspace
|
|
|| (ev.keyCode==16)) { // shift; Opera sends this.
|
|
key_ev_stop(ev);
|
|
return false;
|
|
}
|
|
|
|
var kc;
|
|
if (ev.keyCode) kc=ev.keyCode;
|
|
if (ev.which) kc=ev.which;
|
|
|
|
var k=String.fromCharCode(kc);
|
|
|
|
// When a key is pressed with ALT, we send ESC followed by the key's normal
|
|
// code. But we don't want to do this when ALT-GR is pressed.
|
|
if (ev.altKey && !ev.ctrlKey) {
|
|
k = String.fromCharCode(27)+k;
|
|
}
|
|
|
|
// alert("keypress keyCode="+ev.keyCode+" which="+ev.which+
|
|
// " shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey);
|
|
|
|
process_key(k);
|
|
|
|
key_ev_stop(ev);
|
|
return false;
|
|
}
|
|
|
|
|
|
function keydown(ev) {
|
|
if (!ev) var ev=window.event;
|
|
|
|
// alert("keydown keyCode="+ev.keyCode+" which="+ev.which+
|
|
// " shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey);
|
|
|
|
var k;
|
|
|
|
var kc=ev.keyCode;
|
|
|
|
// Handle special keys. We do this here because IE doesn't send
|
|
// keypress events for these (or at least some versions of IE don't for
|
|
// at least many of them). This is unfortunate as it means that the
|
|
// cursor keys don't auto-repeat, even in browsers where that would be
|
|
// possible. That could be improved.
|
|
|
|
// Interpret shift-pageup/down locally
|
|
if (ev.shiftKey && kc==33) { scrollterm(-0.5); key_ev_stop(ev); return false; }
|
|
else if (ev.shiftKey && kc==34) { scrollterm(0.5); key_ev_stop(ev); return false; }
|
|
|
|
else if (kc==33) k=esc_seq("5~"); // PgUp
|
|
else if (kc==34) k=esc_seq("6~"); // PgDn
|
|
else if (kc==35) k=esc_seq("4~"); // End
|
|
else if (kc==36) k=esc_seq("1~"); // Home
|
|
else if (kc==37) k=esc_seq("D"); // Left
|
|
else if (kc==38) k=esc_seq("A"); // Up
|
|
else if (kc==39) k=esc_seq("C"); // Right
|
|
else if (kc==40) k=esc_seq("B"); // Down
|
|
else if (kc==45) k=esc_seq("2~"); // Ins
|
|
else if (kc==46) k=esc_seq("3~"); // Del
|
|
else if (kc==27) k=String.fromCharCode(27); // Escape
|
|
else if (kc==9) k=String.fromCharCode(9); // Tab
|
|
else if (kc==8) k=String.fromCharCode(8); // Backspace
|
|
else if (kc==112) k=esc_seq(ev.shiftKey ? "25~" : "[A"); // F1
|
|
else if (kc==113) k=esc_seq(ev.shiftKey ? "26~" : "[B"); // F2
|
|
else if (kc==114) k=esc_seq(ev.shiftKey ? "28~" : "[C"); // F3
|
|
else if (kc==115) k=esc_seq(ev.shiftKey ? "29~" : "[D"); // F4
|
|
else if (kc==116) k=esc_seq(ev.shiftKey ? "31~" : "[E"); // F5
|
|
else if (kc==117) k=esc_seq(ev.shiftKey ? "32~" : "17~"); // F6
|
|
else if (kc==118) k=esc_seq(ev.shiftKey ? "33~" : "18~"); // F7
|
|
else if (kc==119) k=esc_seq(ev.shiftKey ? "34~" : "19~"); // F8
|
|
else if (kc==120) k=esc_seq("20~"); // F9
|
|
else if (kc==121) k=esc_seq("21~"); // F10
|
|
else if (kc==122) k=esc_seq("23~"); // F11
|
|
else if (kc==123) k=esc_seq("24~"); // F12
|
|
|
|
else {
|
|
|
|
// For most keys we'll stop now and let the subsequent keypress event
|
|
// process the key. This has the advantage that auto-repeat will work.
|
|
// But we'll carry on here for control keys.
|
|
// Note that when altgr is pressed, the event reports ctrl and alt being
|
|
// pressed because it doesn't have a separate field for altgr. We'll
|
|
// handle altgr in the keypress handler.
|
|
if (!ev.ctrlKey // ctrl not pressed
|
|
|| (ev.ctrlKey && ev.altKey) // altgr pressed
|
|
|| (ev.keyCode==17)) { // I think that if you press shift-control,
|
|
// you'll get an event with !ctrlKey && keyCode==17.
|
|
key_ev_supress(ev);
|
|
return; // Note that we don't "return false" here, as we want the
|
|
// keypress handler to be invoked.
|
|
}
|
|
|
|
// OK, so now we're handling a ctrl key combination.
|
|
|
|
// There are some assumptions below about whether these symbols are shifted
|
|
// or not; does this work with different keyboards?
|
|
if (ev.shiftKey) {
|
|
if (kc==50) k=String.fromCharCode(0); // Ctrl-@
|
|
else if (kc==54) k=String.fromCharCode(30); // Ctrl-^, doesn't work
|
|
else if (kc==94) k=String.fromCharCode(30); // Ctrl-^, doesn't work
|
|
else if (kc==109) k=String.fromCharCode(31); // Ctrl-_
|
|
else {
|
|
key_ev_supress(ev);
|
|
return;
|
|
}
|
|
} else {
|
|
if (kc>=65 && kc<=90) k=String.fromCharCode(kc-64); // Ctrl-A..Z
|
|
else if (kc==219) k=String.fromCharCode(27); // Ctrl-[
|
|
else if (kc==220) k=String.fromCharCode(28); // Ctrl-\ .
|
|
else if (kc==221) k=String.fromCharCode(29); // Ctrl-]
|
|
else if (kc==190) k=String.fromCharCode(30); // Since ctrl-^ doesn't work, map
|
|
// ctrl-. to its code.
|
|
else if (kc==32) k=String.fromCharCode(0); // Ctrl-space sends 0, like ctrl-@.
|
|
else {
|
|
key_ev_supress(ev);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// alert("keydown keyCode="+ev.keyCode+" which="+ev.which+
|
|
// " shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey);
|
|
|
|
process_key(k);
|
|
|
|
key_ev_stop(ev);
|
|
return false;
|
|
}
|
|
|
|
|
|
// Open, close and initialisation:
|
|
|
|
function open_term(rows,cols,p,charset,scrollback) {
|
|
var params = "a=open&rows="+rows+"&cols="+cols;
|
|
if (p) {
|
|
params += "&p="+p;
|
|
}
|
|
if (charset) {
|
|
params += "&ch="+charset;
|
|
}
|
|
if (scrollback) {
|
|
if (scrollback>1000) {
|
|
alert("The maximum scrollback is currently limited to 1000 lines. "
|
|
+"Please choose a smaller value and try again.");
|
|
return;
|
|
}
|
|
params += "&sb="+scrollback;
|
|
}
|
|
params += cachebust();
|
|
var resp = sync_load(url_prefix+"anyterm-module",params);
|
|
|
|
if (handle_resp_error(resp)) {
|
|
return;
|
|
}
|
|
|
|
open=true;
|
|
session=resp;
|
|
}
|
|
|
|
function close_term() {
|
|
if (!open) {
|
|
alert("Connection is not open");
|
|
return;
|
|
}
|
|
open=false;
|
|
var resp = sync_load(url_prefix+"anyterm-module","a=close&s="+session+cachebust());
|
|
handle_resp_error(resp); // If we get an error, we still close everything.
|
|
document.onkeypress=null;
|
|
document.onkeydown=null;
|
|
window.onbeforeunload=null;
|
|
var e;
|
|
while (e=frame.firstChild) {
|
|
frame.removeChild(e);
|
|
}
|
|
frame.className="";
|
|
if (on_close_goto_url) {
|
|
document.location = on_close_goto_url;
|
|
}
|
|
}
|
|
|
|
|
|
function get_anyterm_version() {
|
|
var svn_url="$URL: file:///var/lib/svn/anyterm/tags/releases/1.1/1.1.29/browser/anyterm.js $";
|
|
var re = /releases\/[0-9]+\.[0-9]+\/([0-9\.]+)/;
|
|
var match = re.exec(svn_url);
|
|
if (match) {
|
|
return match[1];
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function substitute_variables(s) {
|
|
var version = get_anyterm_version();
|
|
if (version!="") {
|
|
version="-"+version;
|
|
}
|
|
var hostname=document.location.host;
|
|
return s.replace(/%v/g,version).replace(/%h/g,hostname);
|
|
}
|
|
|
|
|
|
// Copying
|
|
|
|
function copy_ie_clipboard() {
|
|
try {
|
|
window.document.execCommand("copy",false,null);
|
|
} catch (err) {
|
|
return undefined;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
function copy_mozilla_clipboard() {
|
|
// Thanks to Simon Wissinger for this function.
|
|
|
|
try {
|
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
|
} catch (err) {
|
|
return undefined;
|
|
}
|
|
|
|
var sel=window.getSelection();
|
|
var copytext=sel.toString();
|
|
|
|
var str=Components.classes["@mozilla.org/supports-string;1"]
|
|
.createInstance(Components.interfaces.nsISupportsString);
|
|
if (!str) return undefined;
|
|
|
|
str.data=copytext;
|
|
|
|
var trans=Components.classes["@mozilla.org/widget/transferable;1"]
|
|
.createInstance(Components.interfaces.nsITransferable);
|
|
if (!trans) return undefined;
|
|
|
|
trans.addDataFlavor("text/unicode");
|
|
trans.setTransferData("text/unicode", str, copytext.length * 2);
|
|
|
|
var clipid=Components.interfaces.nsIClipboard;
|
|
|
|
var clip=Components.classes["@mozilla.org/widget/clipboard;1"].getService(clipid);
|
|
if (!clip) return undefined;
|
|
|
|
clip.setData(trans, null, clipid.kGlobalClipboard);
|
|
|
|
return 1;
|
|
}
|
|
|
|
function copy_to_clipboard() {
|
|
var r=copy_ie_clipboard();
|
|
if (r==undefined) {
|
|
r=copy_mozilla_clipboard();
|
|
}
|
|
if (r==undefined) {
|
|
alert("Copy seems to be disabled; maybe you need to change your security settings?"
|
|
+"\n(Copy on the Edit menu will probably work)");
|
|
}
|
|
}
|
|
|
|
|
|
// Pasting
|
|
|
|
function get_mozilla_clipboard() {
|
|
// This function is taken from
|
|
// http://www.nomorepasting.com/paste.php?action=getpaste&pasteID=41974&PHPSESSID=e6565dcf5de07256345e562b97ac9f46
|
|
// which does not indicate any particular copyright conditions. It
|
|
// is a public forum, so one might conclude that it is public
|
|
// domain.
|
|
|
|
// IMHO it's disgraceful that Mozilla makes us use these 30 lines of
|
|
// undocumented gobledegook to do what IE does, and documents, with
|
|
// just 'window.clipboardData.getData("Text")'. What on earth were
|
|
// they thinking?
|
|
|
|
try {
|
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
|
} catch (err) {
|
|
return undefined;
|
|
}
|
|
|
|
var clip = Components.classes["@mozilla.org/widget/clipboard;1"]
|
|
.createInstance(Components.interfaces.nsIClipboard);
|
|
if (!clip) {
|
|
return undefined;
|
|
}
|
|
|
|
var trans = Components.classes["@mozilla.org/widget/transferable;1"]
|
|
.createInstance(Components.interfaces.nsITransferable);
|
|
if (!trans) {
|
|
return undefined;
|
|
}
|
|
|
|
trans.addDataFlavor("text/unicode");
|
|
clip.getData(trans,clip.kGlobalClipboard);
|
|
|
|
var str=new Object();
|
|
var strLength=new Object();
|
|
|
|
try {
|
|
trans.getTransferData("text/unicode",str,strLength);
|
|
} catch(err) {
|
|
// One reason for getting here seems to be that nothing is selected
|
|
return "";
|
|
}
|
|
|
|
if (str) {
|
|
str=str.value.QueryInterface(Components.interfaces.nsISupportsString);
|
|
}
|
|
|
|
if (str) {
|
|
return str.data.substring(0,strLength.value / 2);
|
|
} else {
|
|
return ""; // ? is this "clipboard empty" or "cannot access"?
|
|
}
|
|
}
|
|
|
|
function get_ie_clipboard() {
|
|
if (window.clipboardData) {
|
|
return window.clipboardData.getData("Text");
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function get_default_clipboard() {
|
|
return prompt("Paste into this box and press OK:","");
|
|
}
|
|
|
|
function paste_from_clipboard() {
|
|
var p = get_ie_clipboard();
|
|
if (p==undefined) {
|
|
p = get_mozilla_clipboard();
|
|
}
|
|
if (p==undefined) {
|
|
p = get_default_clipboard();
|
|
if (p) {
|
|
process_key(p);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (p=="") {
|
|
alert("The clipboard seems to be empty");
|
|
return;
|
|
}
|
|
|
|
if (confirm('Click OK to "type" the following into the terminal:\n'+p)) {
|
|
process_key(p);
|
|
}
|
|
}
|
|
|
|
|
|
function create_button(label,fn) {
|
|
var button=document.createElement("A");
|
|
var button_t=document.createTextNode("["+label+"] ");
|
|
button.appendChild(button_t);
|
|
button.onclick=fn;
|
|
return button;
|
|
}
|
|
|
|
function create_img_button(imgfn,label,fn) {
|
|
var button=document.createElement("A");
|
|
var button_img=document.createElement("IMG");
|
|
var class_attr=document.createAttribute("CLASS");
|
|
class_attr.value="button";
|
|
button_img.setAttributeNode(class_attr);
|
|
var src_attr=document.createAttribute("SRC");
|
|
src_attr.value=imgfn;
|
|
button_img.setAttributeNode(src_attr);
|
|
var alt_attr=document.createAttribute("ALT");
|
|
alt_attr.value="["+label+"] ";
|
|
button_img.setAttributeNode(alt_attr);
|
|
var title_attr=document.createAttribute("TITLE");
|
|
title_attr.value=label;
|
|
button_img.setAttributeNode(title_attr);
|
|
button.appendChild(button_img);
|
|
button.onclick=fn;
|
|
return button;
|
|
}
|
|
|
|
function create_term(elem_id,title,rows,cols,p,charset,scrollback) {
|
|
if (open) {
|
|
alert("Terminal is already open");
|
|
return;
|
|
}
|
|
title=substitute_variables(title);
|
|
frame=document.getElementById(elem_id);
|
|
if (!frame) {
|
|
alert("There is no element named '"+elem_id+"' in which to build a terminal");
|
|
return;
|
|
}
|
|
frame.className="termframe";
|
|
var title_p=document.createElement("P");
|
|
title_p.appendChild(create_img_button("copy.gif","Copy",copy_to_clipboard));
|
|
title_p.appendChild(create_img_button("paste.gif","Paste",paste_from_clipboard));
|
|
title_p.appendChild(create_ctrlkey_menu());
|
|
var title_t=document.createTextNode(" "+title+" ");
|
|
title_p.appendChild(title_t);
|
|
// title_p.appendChild(create_button("close",close_term));
|
|
frame.appendChild(title_p);
|
|
term=document.createElement("PRE");
|
|
frame.appendChild(term);
|
|
term.className="term a p";
|
|
var termbody=document.createTextNode("");
|
|
term.appendChild(termbody);
|
|
visible_height_frac=Number(rows)/(Number(rows)+Number(scrollback));
|
|
if (scrollback>0) {
|
|
term.style.overflowY="scroll";
|
|
}
|
|
document.onhelp = function() { return false; };
|
|
document.onkeypress=keypress;
|
|
document.onkeydown=keydown;
|
|
open_term(rows,cols,p,charset,scrollback);
|
|
if (open) {
|
|
window.onbeforeunload=warn_unload;
|
|
get();
|
|
maybe_send();
|
|
}
|
|
}
|
|
|
|
|
|
function warn_unload() {
|
|
if (open) {
|
|
return "Leaving this page will close the terminal.";
|
|
}
|
|
}
|
|
|
|
|
|
function create_ctrlkey_menu() {
|
|
var sel=document.createElement("SELECT");
|
|
create_ctrlkey_menu_entry(sel,"Control keys...",-1);
|
|
create_ctrlkey_menu_entry(sel,"Ctrl-@",0);
|
|
for (var code=1; code<27; code++) {
|
|
var letter=String.fromCharCode(64+code);
|
|
create_ctrlkey_menu_entry(sel,"Ctrl-"+letter,code);
|
|
}
|
|
create_ctrlkey_menu_entry(sel,"Ctrl-[",27);
|
|
create_ctrlkey_menu_entry(sel,"Ctrl-\\",28);
|
|
create_ctrlkey_menu_entry(sel,"Ctrl-]",29);
|
|
create_ctrlkey_menu_entry(sel,"Ctrl-^",30);
|
|
create_ctrlkey_menu_entry(sel,"Ctrl-_",31);
|
|
sel.onchange=function() {
|
|
var code = sel.options[sel.selectedIndex].value;
|
|
if (code>=0) {
|
|
process_key(String.fromCharCode(code));
|
|
}
|
|
};
|
|
return sel;
|
|
}
|
|
|
|
function create_ctrlkey_menu_entry(sel,name,code) {
|
|
var opt=document.createElement("OPTION");
|
|
opt.appendChild(document.createTextNode(name));
|
|
var value_attr=document.createAttribute("VALUE");
|
|
value_attr.value=code;
|
|
opt.setAttributeNode(value_attr);
|
|
sel.appendChild(opt);
|
|
}
|
|
|