mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 20:47:44 +00:00
771 lines
22 KiB
HTML
771 lines
22 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
|
|
|
<html onclick="keepFocusInTextbox(event)">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
<title>JavaScript Shell 1.4</title>
|
|
|
|
<script type="text/javascript">
|
|
var
|
|
histList = [""],
|
|
histPos = 0,
|
|
_scope = {},
|
|
_win, // a top-level context
|
|
question, // {String} the input command that's being evaluated. Accessed via
|
|
// |Shell.question| from the target window for evaluation.
|
|
_in, // {HTMLTextAreaElement} the textarea containing the input
|
|
_out, // {HTMLDivElement} the output is printed to this element
|
|
tooManyMatches = null,
|
|
lastError = null,
|
|
_jsVer = 0 // determines the way to execute the commands, see run()
|
|
;
|
|
|
|
function refocus()
|
|
{
|
|
_in.blur(); // Needed for Mozilla to scroll correctly.
|
|
_in.focus();
|
|
}
|
|
|
|
function init()
|
|
{
|
|
_in = document.getElementById("input");
|
|
_out = document.getElementById("output");
|
|
|
|
_win = window;
|
|
|
|
if (opener && !opener.closed)
|
|
{
|
|
println("Using bookmarklet version of shell: commands will run in opener's context.", "message");
|
|
_win = opener;
|
|
}
|
|
|
|
/* Run a series of (potentially async, but quick) tests to determine the
|
|
* way to run code in this browser (for run()). Sets window._jsVer based
|
|
* on the tests. */
|
|
_jsVer = 0;
|
|
for (var jsVerToTry=19; jsVerToTry>=15; jsVerToTry--) {
|
|
run(window, "if(_jsVer < " + jsVerToTry + ") { " +
|
|
"_jsVer=" + jsVerToTry + "; }", jsVerToTry);
|
|
}
|
|
|
|
initTarget();
|
|
|
|
recalculateInputHeight();
|
|
refocus();
|
|
}
|
|
|
|
/**
|
|
* Runs |code| in |_win|'s context.
|
|
* @param overridenJSVer {int} (optional) overrides the default (specified by _jsVer)
|
|
* way to run the code.
|
|
*/
|
|
function run(_win, code, overridenJSVer) {
|
|
var jsVerToUse = overridenJSVer ? overridenJSVer : _jsVer;
|
|
if (jsVerToUse <= 15) {
|
|
_win.location.href = "javascript:" + code + "; void 0";
|
|
} else {
|
|
var sc = _win.document.createElement("script");
|
|
sc.type="application/javascript;version=" + jsVerToUse/10;
|
|
sc.src="data:application/x-javascript," + code;
|
|
_win.document.body.appendChild(sc); // runs the script asynchronously
|
|
}
|
|
}
|
|
|
|
function initTarget()
|
|
{
|
|
_win.Shell = window;
|
|
_win.print = shellCommands.print;
|
|
}
|
|
|
|
|
|
// Unless the user is selected something, refocus the textbox.
|
|
// (requested by caillon, brendan, asa)
|
|
function keepFocusInTextbox(e)
|
|
{
|
|
var g = e.srcElement ? e.srcElement : e.target; // IE vs. standard
|
|
|
|
while (!g.tagName)
|
|
g = g.parentNode;
|
|
var t = g.tagName.toUpperCase();
|
|
if (t=="A" || t=="INPUT")
|
|
return;
|
|
|
|
if (window.getSelection) {
|
|
// Mozilla
|
|
if (String(window.getSelection()))
|
|
return;
|
|
}
|
|
else {
|
|
// IE
|
|
if ( document.selection.createRange().text )
|
|
return;
|
|
}
|
|
|
|
refocus();
|
|
}
|
|
|
|
function inputKeydown(e) {
|
|
// Use onkeydown because IE doesn't support onkeypress for arrow keys
|
|
|
|
//alert(e.keyCode + " ^ " + e.keycode);
|
|
|
|
if (e.shiftKey && e.keyCode == 13) { // shift-enter
|
|
// don't do anything; allow the shift-enter to insert a line break as normal
|
|
} else if (e.keyCode == 13) { // enter
|
|
// execute the input on enter
|
|
try { go(); } catch(er) { alert(er); };
|
|
setTimeout(function() { _in.value = ""; }, 0); // can't preventDefault on input, so clear it later
|
|
} else if (e.keyCode == 38) { // up
|
|
// go up in history if at top or ctrl-up
|
|
if (e.ctrlKey || caretInFirstLine(_in))
|
|
hist(true);
|
|
} else if (e.keyCode == 40) { // down
|
|
// go down in history if at end or ctrl-down
|
|
if (e.ctrlKey || caretInLastLine(_in))
|
|
hist(false);
|
|
} else if (e.keyCode == 9) { // tab
|
|
tabcomplete();
|
|
setTimeout(function() { refocus(); }, 0); // refocus because tab was hit
|
|
} else { }
|
|
|
|
setTimeout(recalculateInputHeight, 0);
|
|
|
|
//return true;
|
|
};
|
|
|
|
function caretInFirstLine(textbox)
|
|
{
|
|
// IE doesn't support selectionStart/selectionEnd
|
|
if (textbox.selectionStart == undefined)
|
|
return true;
|
|
|
|
var firstLineBreak = textbox.value.indexOf("\n");
|
|
|
|
return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak));
|
|
}
|
|
|
|
function caretInLastLine(textbox)
|
|
{
|
|
// IE doesn't support selectionStart/selectionEnd
|
|
if (textbox.selectionEnd == undefined)
|
|
return true;
|
|
|
|
var lastLineBreak = textbox.value.lastIndexOf("\n");
|
|
|
|
return (textbox.selectionEnd > lastLineBreak);
|
|
}
|
|
|
|
function recalculateInputHeight()
|
|
{
|
|
var rows = _in.value.split(/\n/).length
|
|
+ 1 // prevent scrollbar flickering in Mozilla
|
|
+ (window.opera ? 1 : 0); // leave room for scrollbar in Opera
|
|
|
|
if (_in.rows != rows) // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0.
|
|
_in.rows = rows;
|
|
}
|
|
|
|
function println(s, type)
|
|
{
|
|
if((s=String(s)))
|
|
{
|
|
var newdiv = document.createElement("div");
|
|
newdiv.appendChild(document.createTextNode(s));
|
|
newdiv.className = type;
|
|
_out.appendChild(newdiv);
|
|
return newdiv;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function printWithRunin(h, s, type)
|
|
{
|
|
var div = println(s, type);
|
|
if (div) {
|
|
var head = document.createElement("strong");
|
|
head.appendChild(document.createTextNode(h + ": "));
|
|
div.insertBefore(head, div.firstChild);
|
|
}
|
|
}
|
|
|
|
|
|
var shellCommands =
|
|
{
|
|
load : function load(url)
|
|
{
|
|
var s = _win.document.createElement("script");
|
|
s.type = "text/javascript";
|
|
s.src = url;
|
|
_win.document.getElementsByTagName("head")[0].appendChild(s);
|
|
println("Loading " + url + "...", "message");
|
|
},
|
|
|
|
clear : function clear()
|
|
{
|
|
var CHILDREN_TO_PRESERVE = 3;
|
|
while (_out.childNodes[CHILDREN_TO_PRESERVE])
|
|
_out.removeChild(_out.childNodes[CHILDREN_TO_PRESERVE]);
|
|
},
|
|
|
|
print : function print(s) { println(s, "print"); },
|
|
|
|
// the normal function, "print", shouldn't return a value
|
|
// (suggested by brendan; later noticed it was a problem when showing others)
|
|
pr : function pr(s)
|
|
{
|
|
shellCommands.print(s); // need to specify shellCommands so it doesn't try window.print()!
|
|
return s;
|
|
},
|
|
|
|
props : function props(e, onePerLine)
|
|
{
|
|
if (e === null) {
|
|
println("props called with null argument", "error");
|
|
return;
|
|
}
|
|
|
|
if (e === undefined) {
|
|
println("props called with undefined argument", "error");
|
|
return;
|
|
}
|
|
|
|
var ns = ["Methods", "Fields", "Unreachables"];
|
|
var as = [[], [], []]; // array of (empty) arrays of arrays!
|
|
var p, j, i; // loop variables, several used multiple times
|
|
|
|
var protoLevels = 0;
|
|
|
|
for (p = e; p; p = p.__proto__)
|
|
{
|
|
for (i=0; i<ns.length; ++i)
|
|
as[i][protoLevels] = [];
|
|
++protoLevels;
|
|
}
|
|
|
|
for(var a in e)
|
|
{
|
|
// Shortcoming: doesn't check that VALUES are the same in object and prototype.
|
|
|
|
var protoLevel = -1;
|
|
try
|
|
{
|
|
for (p = e; p && (a in p); p = p.__proto__)
|
|
++protoLevel;
|
|
}
|
|
catch(er) { protoLevel = 0; } // "in" operator throws when param to props() is a string
|
|
|
|
var type = 1;
|
|
try
|
|
{
|
|
if ((typeof e[a]) == "function")
|
|
type = 0;
|
|
}
|
|
catch (er) { type = 2; }
|
|
|
|
as[type][protoLevel].push(a);
|
|
}
|
|
|
|
function times(s, n) { return n ? s + times(s, n-1) : ""; }
|
|
|
|
for (j=0; j<protoLevels; ++j)
|
|
for (i=0;i<ns.length;++i)
|
|
if (as[i][j].length)
|
|
printWithRunin(
|
|
ns[i] + times(" of prototype", j),
|
|
(onePerLine ? "\n\n" : "") + as[i][j].sort().join(onePerLine ? "\n" : ", ") + (onePerLine ? "\n\n" : ""),
|
|
"propList"
|
|
);
|
|
},
|
|
|
|
blink : function blink(node)
|
|
{
|
|
if (!node) throw("blink: argument is null or undefined.");
|
|
if (node.nodeType == null) throw("blink: argument must be a node.");
|
|
if (node.nodeType == 3) throw("blink: argument must not be a text node");
|
|
if (node.documentElement) throw("blink: argument must not be the document object");
|
|
|
|
function setOutline(o) {
|
|
return function() {
|
|
if (node.style.outline != node.style.bogusProperty) {
|
|
// browser supports outline (Firefox 1.1 and newer, CSS3, Opera 8).
|
|
node.style.outline = o;
|
|
}
|
|
else if (node.style.MozOutline != node.style.bogusProperty) {
|
|
// browser supports MozOutline (Firefox 1.0.x and older)
|
|
node.style.MozOutline = o;
|
|
}
|
|
else {
|
|
// browser only supports border (IE). border is a fallback because it moves things around.
|
|
node.style.border = o;
|
|
}
|
|
}
|
|
}
|
|
|
|
function focusIt(a) {
|
|
return function() {
|
|
a.focus();
|
|
}
|
|
}
|
|
|
|
if (node.ownerDocument) {
|
|
var windowToFocusNow = (node.ownerDocument.defaultView || node.ownerDocument.parentWindow); // Moz vs. IE
|
|
if (windowToFocusNow)
|
|
setTimeout(focusIt(windowToFocusNow.top), 0);
|
|
}
|
|
|
|
for(var i=1;i<7;++i)
|
|
setTimeout(setOutline((i%2)?'3px solid red':'none'), i*100);
|
|
|
|
setTimeout(focusIt(window), 800);
|
|
setTimeout(focusIt(_in), 810);
|
|
},
|
|
|
|
scope : function scope(sc)
|
|
{
|
|
if (!sc) sc = {};
|
|
_scope = sc;
|
|
println("Scope is now " + sc + ". If a variable is not found in this scope, window will also be searched. New variables will still go on window.", "message");
|
|
},
|
|
|
|
mathHelp : function mathHelp()
|
|
{
|
|
printWithRunin("Math constants", "E, LN2, LN10, LOG2E, LOG10E, PI, SQRT1_2, SQRT2", "propList");
|
|
printWithRunin("Math methods", "abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random, round, sin, sqrt, tan", "propList");
|
|
},
|
|
|
|
ans : undefined
|
|
};
|
|
|
|
|
|
function hist(up)
|
|
{
|
|
// histList[0] = first command entered, [1] = second, etc.
|
|
// type something, press up --> thing typed is now in "limbo"
|
|
// (last item in histList) and should be reachable by pressing
|
|
// down again.
|
|
|
|
var L = histList.length;
|
|
|
|
if (L == 1)
|
|
return;
|
|
|
|
if (up)
|
|
{
|
|
if (histPos == L-1)
|
|
{
|
|
// Save this entry in case the user hits the down key.
|
|
histList[histPos] = _in.value;
|
|
}
|
|
|
|
if (histPos > 0)
|
|
{
|
|
histPos--;
|
|
// Use a timeout to prevent up from moving cursor within new text
|
|
// Set to nothing first for the same reason
|
|
setTimeout(
|
|
function() {
|
|
_in.value = '';
|
|
_in.value = histList[histPos];
|
|
var caretPos = _in.value.length;
|
|
if (_in.setSelectionRange)
|
|
_in.setSelectionRange(caretPos, caretPos);
|
|
},
|
|
0
|
|
);
|
|
}
|
|
}
|
|
else // down
|
|
{
|
|
if (histPos < L-1)
|
|
{
|
|
histPos++;
|
|
_in.value = histList[histPos];
|
|
}
|
|
else if (histPos == L-1)
|
|
{
|
|
// Already on the current entry: clear but save
|
|
if (_in.value)
|
|
{
|
|
histList[histPos] = _in.value;
|
|
++histPos;
|
|
_in.value = "";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function tabcomplete()
|
|
{
|
|
/*
|
|
* Working backwards from s[from], find the spot
|
|
* where this expression starts. It will scan
|
|
* until it hits a mismatched ( or a space,
|
|
* but it skips over quoted strings.
|
|
* If stopAtDot is true, stop at a '.'
|
|
*/
|
|
function findbeginning(s, from, stopAtDot)
|
|
{
|
|
/*
|
|
* Complicated function.
|
|
*
|
|
* Return true if s[i] == q BUT ONLY IF
|
|
* s[i-1] is not a backslash.
|
|
*/
|
|
function equalButNotEscaped(s,i,q)
|
|
{
|
|
if(s.charAt(i) != q) // not equal go no further
|
|
return false;
|
|
|
|
if(i==0) // beginning of string
|
|
return true;
|
|
|
|
if(s.charAt(i-1) == '\\') // escaped?
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
var nparens = 0;
|
|
var i;
|
|
for(i=from; i>=0; i--)
|
|
{
|
|
if(s.charAt(i) == ' ')
|
|
break;
|
|
|
|
if(stopAtDot && s.charAt(i) == '.')
|
|
break;
|
|
|
|
if(s.charAt(i) == ')')
|
|
nparens++;
|
|
else if(s.charAt(i) == '(')
|
|
nparens--;
|
|
|
|
if(nparens < 0)
|
|
break;
|
|
|
|
// skip quoted strings
|
|
if(s.charAt(i) == '\'' || s.charAt(i) == '\"')
|
|
{
|
|
//dump("skipping quoted chars: ");
|
|
var quot = s.charAt(i);
|
|
i--;
|
|
while(i >= 0 && !equalButNotEscaped(s,i,quot)) {
|
|
//dump(s.charAt(i));
|
|
i--;
|
|
}
|
|
//dump("\n");
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
// XXX should be used more consistently (instead of using selectionStart/selectionEnd throughout code)
|
|
// XXX doesn't work in IE, even though it contains IE-specific code
|
|
function getcaretpos(inp)
|
|
{
|
|
if(inp.selectionEnd != null)
|
|
return inp.selectionEnd;
|
|
|
|
if(inp.createTextRange)
|
|
{
|
|
var docrange = _win.Shell.document.selection.createRange();
|
|
var inprange = inp.createTextRange();
|
|
if (inprange.setEndPoint)
|
|
{
|
|
inprange.setEndPoint('EndToStart', docrange);
|
|
return inprange.text.length;
|
|
}
|
|
}
|
|
|
|
return inp.value.length; // sucks, punt
|
|
}
|
|
|
|
function setselectionto(inp,pos)
|
|
{
|
|
if(inp.selectionStart) {
|
|
inp.selectionStart = inp.selectionEnd = pos;
|
|
}
|
|
else if(inp.createTextRange) {
|
|
var docrange = _win.Shell.document.selection.createRange();
|
|
var inprange = inp.createTextRange();
|
|
inprange.move('character',pos);
|
|
inprange.select();
|
|
}
|
|
else { // err...
|
|
/*
|
|
inp.select();
|
|
if(_win.Shell.document.getSelection())
|
|
_win.Shell.document.getSelection() = "";
|
|
*/
|
|
}
|
|
}
|
|
// get position of cursor within the input box
|
|
var caret = getcaretpos(_in);
|
|
|
|
if(caret) {
|
|
//dump("----\n");
|
|
var dotpos, spacepos, complete, obj;
|
|
//dump("caret pos: " + caret + "\n");
|
|
// see if there's a dot before here
|
|
dotpos = findbeginning(_in.value, caret-1, true);
|
|
//dump("dot pos: " + dotpos + "\n");
|
|
if(dotpos == -1 || _in.value.charAt(dotpos) != '.') {
|
|
dotpos = caret;
|
|
//dump("changed dot pos: " + dotpos + "\n");
|
|
}
|
|
|
|
// look backwards for a non-variable-name character
|
|
spacepos = findbeginning(_in.value, dotpos-1, false);
|
|
//dump("space pos: " + spacepos + "\n");
|
|
// get the object we're trying to complete on
|
|
if(spacepos == dotpos || spacepos+1 == dotpos || dotpos == caret)
|
|
{
|
|
// try completing function args
|
|
if(_in.value.charAt(dotpos) == '(' ||
|
|
(_in.value.charAt(spacepos) == '(' && (spacepos+1) == dotpos))
|
|
{
|
|
var fn,fname;
|
|
var from = (_in.value.charAt(dotpos) == '(') ? dotpos : spacepos;
|
|
spacepos = findbeginning(_in.value, from-1, false);
|
|
|
|
fname = _in.value.substr(spacepos+1,from-(spacepos+1));
|
|
//dump("fname: " + fname + "\n");
|
|
try {
|
|
with(_win.Shell._scope)
|
|
with(_win)
|
|
with(Shell.shellCommands)
|
|
fn = eval(fname);
|
|
}
|
|
catch(er) {
|
|
//dump('fn is not a valid object\n');
|
|
return;
|
|
}
|
|
if(fn == undefined) {
|
|
//dump('fn is undefined');
|
|
return;
|
|
}
|
|
if(fn instanceof Function)
|
|
{
|
|
// Print function definition, including argument names, but not function body
|
|
if(!fn.toString().match(/function .+?\(\) +\{\n +\[native code\]\n\}/))
|
|
println(fn.toString().match(/function .+?\(.*?\)/), "tabcomplete");
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
obj = _win;
|
|
}
|
|
else
|
|
{
|
|
var objname = _in.value.substr(spacepos+1,dotpos-(spacepos+1));
|
|
//dump("objname: |" + objname + "|\n");
|
|
try {
|
|
with(_win.Shell._scope)
|
|
with(_win)
|
|
obj = eval(objname);
|
|
}
|
|
catch(er) {
|
|
printError(er);
|
|
return;
|
|
}
|
|
if(obj == undefined) {
|
|
// sometimes this is tabcomplete's fault, so don't print it :(
|
|
// e.g. completing from "print(document.getElements"
|
|
// println("Can't complete from null or undefined expression " + objname, "error");
|
|
return;
|
|
}
|
|
}
|
|
//dump("obj: " + obj + "\n");
|
|
// get the thing we're trying to complete
|
|
if(dotpos == caret)
|
|
{
|
|
if(spacepos+1 == dotpos || spacepos == dotpos)
|
|
{
|
|
// nothing to complete
|
|
//dump("nothing to complete\n");
|
|
return;
|
|
}
|
|
|
|
complete = _in.value.substr(spacepos+1,dotpos-(spacepos+1));
|
|
}
|
|
else {
|
|
complete = _in.value.substr(dotpos+1,caret-(dotpos+1));
|
|
}
|
|
//dump("complete: " + complete + "\n");
|
|
// ok, now look at all the props/methods of this obj
|
|
// and find ones starting with 'complete'
|
|
var matches = [];
|
|
var bestmatch = null;
|
|
for(var a in obj)
|
|
{
|
|
//a = a.toString();
|
|
//XXX: making it lowercase could help some cases,
|
|
// but screws up my general logic.
|
|
if(a.substr(0,complete.length) == complete) {
|
|
matches.push(a);
|
|
////dump("match: " + a + "\n");
|
|
// if no best match, this is the best match
|
|
if(bestmatch == null)
|
|
{
|
|
bestmatch = a;
|
|
}
|
|
else {
|
|
// the best match is the longest common string
|
|
function min(a,b){ return ((a<b)?a:b); }
|
|
var i;
|
|
for(i=0; i< min(bestmatch.length, a.length); i++)
|
|
{
|
|
if(bestmatch.charAt(i) != a.charAt(i))
|
|
break;
|
|
}
|
|
bestmatch = bestmatch.substr(0,i);
|
|
////dump("bestmatch len: " + i + "\n");
|
|
}
|
|
////dump("bestmatch: " + bestmatch + "\n");
|
|
}
|
|
}
|
|
bestmatch = (bestmatch || "");
|
|
////dump("matches: " + matches + "\n");
|
|
var objAndComplete = (objname || obj) + "." + bestmatch;
|
|
//dump("matches.length: " + matches.length + ", tooManyMatches: " + tooManyMatches + ", objAndComplete: " + objAndComplete + "\n");
|
|
if(matches.length > 1 && (tooManyMatches == objAndComplete || matches.length <= 10)) {
|
|
|
|
printWithRunin("Matches: ", matches.join(', '), "tabcomplete");
|
|
tooManyMatches = null;
|
|
}
|
|
else if(matches.length > 10)
|
|
{
|
|
println(matches.length + " matches. Press tab again to see them all", "tabcomplete");
|
|
tooManyMatches = objAndComplete;
|
|
}
|
|
else {
|
|
tooManyMatches = null;
|
|
}
|
|
if(bestmatch != "")
|
|
{
|
|
var sstart;
|
|
if(dotpos == caret) {
|
|
sstart = spacepos+1;
|
|
}
|
|
else {
|
|
sstart = dotpos+1;
|
|
}
|
|
_in.value = _in.value.substr(0, sstart)
|
|
+ bestmatch
|
|
+ _in.value.substr(caret);
|
|
setselectionto(_in,caret + (bestmatch.length - complete.length));
|
|
}
|
|
}
|
|
}
|
|
|
|
function printQuestion(q)
|
|
{
|
|
println(q, "input");
|
|
}
|
|
|
|
function printAnswer(a)
|
|
{
|
|
if (a !== undefined) {
|
|
println(a, "normalOutput");
|
|
shellCommands.ans = a;
|
|
}
|
|
}
|
|
|
|
function printError(er)
|
|
{
|
|
var lineNumberString;
|
|
|
|
lastError = er; // for debugging the shell
|
|
if (er.name)
|
|
{
|
|
// lineNumberString should not be "", to avoid a very wacky bug in IE 6.
|
|
lineNumberString = (er.lineNumber != undefined) ? (" on line " + er.lineNumber + ": ") : ": ";
|
|
println(er.name + lineNumberString + er.message, "error"); // Because IE doesn't have error.toString.
|
|
}
|
|
else
|
|
println(er, "error"); // Because security errors in Moz /only/ have toString.
|
|
}
|
|
|
|
/**
|
|
* Evaluates |s| or current input (_in.value) in the previously set up context.
|
|
* @param {String} s - (optional) command to evaluate.
|
|
*/
|
|
function go(s)
|
|
{
|
|
// save the command to eval in |question|, so that the target window can access
|
|
// it when evaluating.
|
|
_in.value = question = s ? s : _in.value;
|
|
|
|
if (question == "")
|
|
return;
|
|
|
|
histList[histList.length-1] = question;
|
|
histList[histList.length] = "";
|
|
histPos = histList.length - 1;
|
|
|
|
// Unfortunately, this has to happen *before* the JavaScript is run, so that
|
|
// print() output will go in the right place.
|
|
_in.value='';
|
|
recalculateInputHeight();
|
|
printQuestion(question);
|
|
|
|
if (_win.closed) {
|
|
printError("Target window has been closed.");
|
|
return;
|
|
}
|
|
|
|
try { ("Shell" in _win) }
|
|
catch(er) {
|
|
printError("The JavaScript Shell cannot access variables in the target window. The most likely reason is that the target window now has a different page loaded and that page has a different hostname than the original page.");
|
|
return;
|
|
}
|
|
|
|
if (!("Shell" in _win))
|
|
initTarget(); // silent
|
|
|
|
// Evaluate Shell.question using _win's eval (this is why eval isn't in the |with|, IIRC).
|
|
run(_win, "try{ Shell.printAnswer(eval('with(Shell._scope) with(Shell.shellCommands) {' + Shell.question + String.fromCharCode(10) + '}')); } catch(er) { Shell.printError(er); }; setTimeout(Shell.refocus, 0);");
|
|
}
|
|
|
|
</script>
|
|
|
|
<!-- for http://ted.mielczarek.org/code/mozilla/extensiondev/ -->
|
|
<script type="text/javascript" src="chrome://extensiondev/content/chromeShellExtras.js"></script>
|
|
|
|
<style type="text/css">
|
|
body { background: white; color: black; }
|
|
|
|
#output {
|
|
/* Preserve line breaks, but wrap too if browser supports it */
|
|
white-space: pre;
|
|
white-space: -moz-pre-wrap;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
h3 { margin-top: 0; margin-bottom: 0em; }
|
|
h3 + div { margin: 0; }
|
|
|
|
form { margin: 0; padding: 0; }
|
|
#input { width: 100%; border: none; padding: 0; overflow: auto; }
|
|
|
|
.input { color: blue; background: white; font: inherit; font-weight: bold; margin-top: .5em; /* background: #E6E6FF; */ }
|
|
.normalOutput { color: black; background: white; }
|
|
.print { color: brown; background: white; }
|
|
.error { color: red; background: white; }
|
|
.propList { color: green; background: white; }
|
|
.message { color: green; background: white; }
|
|
.tabcomplete { color: purple; background: white; }
|
|
</style>
|
|
</head>
|
|
|
|
<body onload="init()">
|
|
|
|
<div id="output"><h3>JavaScript Shell 1.4</h3><div>Features: autocompletion of property names with Tab, multiline input with Shift+Enter, input history with (Ctrl+) Up/Down, <a accesskey="M" href="javascript:go('scope(Math); mathHelp();');" title="Accesskey: M">Math</a>, <a accesskey="H" href="http://www.squarefree.com/shell/?ignoreReferrerFrom=shell1.4" title="Accesskey: H">help</a></div><div>Values and functions: ans, print(string), <a accesskey="P" href="javascript:go('props(ans)')" title="Accesskey: P">props(object)</a>, <a accesskey="B" href="javascript:go('blink(ans)')" title="Accesskey: B">blink(node)</a>, <a accesskey="C" href="javascript:go('clear()')" title="Accesskey: C">clear()</a>, load(scriptURL), scope(object)</div></div>
|
|
|
|
<div><textarea id="input" class="input" wrap="off" spellcheck="false" onkeydown="inputKeydown(event)" rows="1"></textarea></div>
|
|
|
|
</body>
|
|
|
|
</html>
|