// 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=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); }