814 lines
28 KiB
JavaScript
814 lines
28 KiB
JavaScript
// JAM Shell Wrapper Version 1.1.2
|
|
|
|
function winShell(opts) {
|
|
opts = opts || {};
|
|
var options = {
|
|
cmd : null,
|
|
name : "jam",
|
|
nameopts : {length:8, memorable:true, lowercase:true},
|
|
Nameopts : {length:8, memorable:true, uppercase:true},
|
|
log : { Time: true, Network: false },
|
|
output : printAnswer,
|
|
server : true,
|
|
}
|
|
|
|
// http://www.squarefree.com/shell/shell.html
|
|
var
|
|
histList = [""],
|
|
histPos = 0,
|
|
_scope = {},
|
|
_win, // a top-level context
|
|
question,
|
|
_in,
|
|
_out,
|
|
_scroll,
|
|
tooManyMatches = null,
|
|
lastError = null;
|
|
|
|
function refocus() {
|
|
if (!_in) return;
|
|
_in.blur(); // Needed for Mozilla to scroll correctly.
|
|
_in.focus();
|
|
}
|
|
|
|
function scrollToBottom(id){
|
|
var div = document.getElementById(id);
|
|
div.scrollTop = div.scrollHeight - div.clientHeight;
|
|
}
|
|
|
|
|
|
function setup () {
|
|
}
|
|
|
|
function init(input, output, scroll) {
|
|
if (input) _in = document.getElementById(input);
|
|
_out = document.getElementById(output);
|
|
_scroll = scroll;
|
|
_win = window;
|
|
|
|
if (opener && !opener.closed) {
|
|
console.log("Using bookmarklet version of shell: commands will run in opener's context.", "message");
|
|
_win = opener;
|
|
}
|
|
|
|
initTarget();
|
|
|
|
recalculateInputHeight();
|
|
refocus();
|
|
|
|
if (options.shell == undefined) {
|
|
console.log('winshell: Creating jamsh ..')
|
|
options.shell = SHELL(options);
|
|
options.cmd = options.shell.init().cmd();
|
|
options.name = options.cmd.name('node');
|
|
} else {
|
|
// It is an agent monitor - attach to shell
|
|
options.cmd = options.shell.cmd();
|
|
options.cmd.config({printAgent:printAnswer});
|
|
options.name = options.cmd.name('node');
|
|
}
|
|
}
|
|
|
|
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 if (document.getSelection) {
|
|
// Opera? Netscape 4?
|
|
if (document.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() {
|
|
if (!_in) return;
|
|
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 loadScript(url, callback) {
|
|
|
|
var script = document.createElement("script")
|
|
script.type = "text/javascript";
|
|
|
|
if (script.readyState) { //IE
|
|
script.onreadystatechange = function() {
|
|
if (script.readyState == "loaded" ||
|
|
script.readyState == "complete") {
|
|
script.onreadystatechange = null;
|
|
callback();
|
|
}
|
|
};
|
|
} else { //Others
|
|
script.onload = function() {
|
|
callback();
|
|
};
|
|
}
|
|
|
|
script.src = url;
|
|
document.getElementsByTagName("head")[0].appendChild(script);
|
|
}
|
|
|
|
function get(url, callback) {
|
|
// read text from URL location
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.open('GET', url, true);
|
|
request.send(null);
|
|
request.onreadystatechange = function() {
|
|
if (request.status==0) {
|
|
println('No connection to LUAOS.');
|
|
if (_listener) clearInterval(_listener);
|
|
} else if (request.readyState === 4) {
|
|
var type = request.getResponseHeader('Content-Type');
|
|
if (type && type.indexOf("text") !== 1) {
|
|
callback(request.responseText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function put(url, data, callback) {
|
|
// write text to URL location
|
|
var request = new XMLHttpRequest();
|
|
request.open('POST', url, true);
|
|
request.send(data);
|
|
request.onreadystatechange = function(err) {
|
|
if (request.status==0) {
|
|
println('No connection to LUAOS.');
|
|
if (_listener) clearInterval(_listener);
|
|
} else if (request.readyState === 4 && request.status === 200) {
|
|
callback(request.responseText);
|
|
}
|
|
}
|
|
}
|
|
|
|
function println(s, type, time) {
|
|
if ((s = String(s))) {
|
|
var newdiv = document.createElement("div");
|
|
newdiv.innerHTML=
|
|
(time?'<span style="color:green">'+SHELL.Io.Time()+'</span> ':'')+
|
|
s.replace(/&/g,'&')
|
|
.replace(/\t/g,' ')
|
|
.replace(/\s/g,' ')
|
|
.replace(/</g,'<')
|
|
.replace(/>/g,'>');
|
|
newdiv.className = type;
|
|
_out.appendChild(newdiv);
|
|
if (_scroll && _scroll.scrollAuto) {
|
|
var scPos = _scroll.getScrollState();
|
|
_scroll.scrollTo(0,scPos.y+500);
|
|
}
|
|
return newdiv;
|
|
}
|
|
}
|
|
|
|
function printWithRunin(h, s, type) {
|
|
var div = println(s, type);
|
|
var head = document.createElement("strong");
|
|
head.appendChild(document.createTextNode(h + ": "));
|
|
div.insertBefore(head, div.firstChild);
|
|
}
|
|
|
|
function iter(a, func) {
|
|
for (var p in a) func(a[p])
|
|
}
|
|
|
|
|
|
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 = 0;
|
|
while (_out.childNodes[CHILDREN_TO_PRESERVE])
|
|
_out.removeChild(_out.childNodes[CHILDREN_TO_PRESERVE]);
|
|
},
|
|
|
|
code: function code(index) {
|
|
if (location.href.split('?').length == 1) return;
|
|
var params = location.href.split('?')[1].split('&');
|
|
var data = {},
|
|
i = 1,
|
|
file;
|
|
|
|
for (var x in params) {
|
|
data[params[x].split('=')[0]] = params[x].split('=')[1];
|
|
if (i == index) file = data[params[x].split('=')[0]];
|
|
i++;
|
|
}
|
|
if (file) {
|
|
get('code/' + file, function(text) {
|
|
go(text)
|
|
});
|
|
}
|
|
},
|
|
|
|
codes: function codes() {
|
|
if (location.href.split('?').length == 1) return;
|
|
var params = location.href.split('?')[1].split('&');
|
|
var data = {};
|
|
|
|
for (var x in params) {
|
|
data[params[x].split('=')[0]] = params[x].split('=')[1];
|
|
}
|
|
return data;
|
|
},
|
|
|
|
getResult: function getResult() {
|
|
get(URL, function(reply) {
|
|
printAnswer(reply);
|
|
});
|
|
},
|
|
|
|
help: function help(topic) {
|
|
|
|
|
|
printWithRunin("Features", "autocompletion of property names with Tab,");
|
|
printWithRunin("Features", "multiline input with Shift+Enter, input history with (Ctrl+) Up/Down");
|
|
|
|
},
|
|
|
|
print: function print(s) {
|
|
println(s, "print");
|
|
},
|
|
|
|
reset: function reset() {
|
|
put(URL, ':reset');
|
|
},
|
|
|
|
// 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);
|
|
if (_in) setTimeout(focusIt(_in), 810);
|
|
},
|
|
|
|
|
|
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(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) {
|
|
a.split('\n').forEach(function (line,index) {
|
|
if (opts.cmd && line[0]=='@') {
|
|
opts.cmd(line);
|
|
} else {
|
|
println(line, "normalOutput", options.log.Time && index==0)
|
|
}
|
|
});
|
|
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.
|
|
}
|
|
|
|
function go(s) {
|
|
_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 question using _win's eval (this is why eval isn't in the |with|, IIRC).
|
|
// _win.location.href = "javascript:try{ Shell.printAnswer(eval('with(Shell._scope) with(Shell.shellCommands) {' + Shell.question + String.fromCharCode(10) + '}')); } catch(er) { Shell.printError(er); }; setTimeout(Shell.refocus, 0); void 0";
|
|
options.cmd.exec(question)
|
|
}
|
|
|
|
function ask(cmd,data,callback) {
|
|
options.cmd.exec(cmd)
|
|
}
|
|
|
|
function cmd () {
|
|
return options.cmd
|
|
}
|
|
|
|
// API
|
|
return {
|
|
ask:ask,
|
|
cmd:cmd,
|
|
commands: shellCommands,
|
|
init: init,
|
|
inputKeydown: inputKeydown,
|
|
options:options,
|
|
print:printAnswer,
|
|
setup:setup
|
|
}
|
|
};
|