5 _win, // a top-level context
14 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
15 var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
16 getService(Components.interfaces.nsIWindowMediator);
17 var enumerator = wm.getEnumerator('eg_menu');
18 while(enumerator.hasMoreElements()) {
19 targetwindow = enumerator.getNext();
20 list.push(targetwindow);
25 function get_tab(a,b) {
30 if (typeof b == 'undefined') {
38 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
39 var tabs = win.document.getElementById('main_tabs');
40 var panels = win.document.getElementById('main_panels');
41 return { 'name' : tabs.childNodes[idx].getAttribute('label'), 'content' : panels.childNodes[idx].firstChild.contentWindow };
46 _in.blur(); // Needed for Mozilla to scroll correctly.
52 _in = document.getElementById("input");
53 _out = document.getElementById("output");
57 if (opener && !opener.closed)
59 println("Using bookmarklet version of shell: commands will run in opener's context.", "message");
65 recalculateInputHeight();
72 _win.print = shellCommands.print;
76 // Unless the user is selected something, refocus the textbox.
77 // (requested by caillon, brendan, asa)
78 function keepFocusInTextbox(e)
80 var g = e.srcElement ? e.srcElement : e.target; // IE vs. standard
84 var t = g.tagName.toUpperCase();
85 if (t=="A" || t=="INPUT")
88 if (window.getSelection) {
90 if (String(window.getSelection()))
93 else if (document.getSelection) {
95 if (document.getSelection())
100 if ( document.selection.createRange().text )
107 function inputKeydown(e) {
108 // Use onkeydown because IE doesn't support onkeypress for arrow keys
110 //alert(e.keyCode + " ^ " + e.keycode);
112 if (e.shiftKey && e.keyCode == 13) { // shift-enter
113 // don't do anything; allow the shift-enter to insert a line break as normal
114 } else if (e.keyCode == 13) { // enter
115 // execute the input on enter
116 try { go(); } catch(er) { alert(er); };
117 setTimeout(function() { _in.value = ""; }, 0); // can't preventDefault on input, so clear it later
118 } else if (e.keyCode == 38) { // up
119 // go up in history if at top or ctrl-up
120 if (e.ctrlKey || caretInFirstLine(_in))
122 } else if (e.keyCode == 40) { // down
123 // go down in history if at end or ctrl-down
124 if (e.ctrlKey || caretInLastLine(_in))
126 } else if (e.keyCode == 9) { // tab
128 setTimeout(function() { refocus(); }, 0); // refocus because tab was hit
131 setTimeout(recalculateInputHeight, 0);
136 function caretInFirstLine(textbox)
138 // IE doesn't support selectionStart/selectionEnd
139 if (textbox.selectionStart == undefined)
142 var firstLineBreak = textbox.value.indexOf("\n");
144 return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak));
147 function caretInLastLine(textbox)
149 // IE doesn't support selectionStart/selectionEnd
150 if (textbox.selectionEnd == undefined)
153 var lastLineBreak = textbox.value.lastIndexOf("\n");
155 return (textbox.selectionEnd > lastLineBreak);
158 function recalculateInputHeight()
160 var rows = _in.value.split(/\n/).length
161 + 1 // prevent scrollbar flickering in Mozilla
162 + (window.opera ? 1 : 0); // leave room for scrollbar in Opera
164 if (_in.rows != rows) // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0.
168 function println(s, type)
172 var newdiv = document.createElement("div");
173 newdiv.appendChild(document.createTextNode(s));
174 newdiv.className = type;
175 _out.appendChild(newdiv);
180 function printWithRunin(h, s, type)
182 var div = println(s, type);
183 var head = document.createElement("strong");
184 head.appendChild(document.createTextNode(h + ": "));
185 div.insertBefore(head, div.firstChild);
191 load : function load(url)
193 var s = _win.document.createElement("script");
194 s.type = "text/javascript";
196 _win.document.getElementsByTagName("head")[0].appendChild(s);
197 println("Loading " + url + "...", "message");
200 clear : function clear()
202 var CHILDREN_TO_PRESERVE = 3;
203 while (_out.childNodes[CHILDREN_TO_PRESERVE])
204 _out.removeChild(_out.childNodes[CHILDREN_TO_PRESERVE]);
207 print : function print(s) { println(s, "print"); },
209 // the normal function, "print", shouldn't return a value
210 // (suggested by brendan; later noticed it was a problem when showing others)
213 shellCommands.print(s); // need to specify shellCommands so it doesn't try window.print()!
217 props : function props(e, onePerLine)
220 println("props called with null argument", "error");
224 if (e === undefined) {
225 println("props called with undefined argument", "error");
229 var ns = ["Methods", "Fields", "Unreachables"];
230 var as = [[], [], []]; // array of (empty) arrays of arrays!
231 var p, j, i; // loop variables, several used multiple times
235 for (p = e; p; p = p.__proto__)
237 for (i=0; i<ns.length; ++i)
238 as[i][protoLevels] = [];
244 // Shortcoming: doesn't check that VALUES are the same in object and prototype.
249 for (p = e; p && (a in p); p = p.__proto__)
252 catch(er) { protoLevel = 0; } // "in" operator throws when param to props() is a string
257 if ((typeof e[a]) == "function")
260 catch (er) { type = 2; }
262 as[type][protoLevel].push(a);
265 function times(s, n) { return n ? s + times(s, n-1) : ""; }
267 for (j=0; j<protoLevels; ++j)
268 for (i=0;i<ns.length;++i)
271 ns[i] + times(" of prototype", j),
272 (onePerLine ? "\n\n" : "") + as[i][j].sort().join(onePerLine ? "\n" : ", ") + (onePerLine ? "\n\n" : ""),
277 blink : function blink(node)
279 if (!node) throw("blink: argument is null or undefined.");
280 if (node.nodeType == null) throw("blink: argument must be a node.");
281 if (node.nodeType == 3) throw("blink: argument must not be a text node");
282 if (node.documentElement) throw("blink: argument must not be the document object");
284 function setOutline(o) {
286 if (node.style.outline != node.style.bogusProperty) {
287 // browser supports outline (Firefox 1.1 and newer, CSS3, Opera 8).
288 node.style.outline = o;
290 else if (node.style.MozOutline != node.style.bogusProperty) {
291 // browser supports MozOutline (Firefox 1.0.x and older)
292 node.style.MozOutline = o;
295 // browser only supports border (IE). border is a fallback because it moves things around.
296 node.style.border = o;
301 function focusIt(a) {
307 if (node.ownerDocument) {
308 var windowToFocusNow = (node.ownerDocument.defaultView || node.ownerDocument.parentWindow); // Moz vs. IE
309 if (windowToFocusNow)
310 setTimeout(focusIt(windowToFocusNow.top), 0);
314 setTimeout(setOutline((i%2)?'3px solid red':'none'), i*100);
316 setTimeout(focusIt(window), 800);
317 setTimeout(focusIt(_in), 810);
320 scope : function scope(sc)
324 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");
327 mathHelp : function mathHelp()
329 printWithRunin("Math constants", "E, LN2, LN10, LOG2E, LOG10E, PI, SQRT1_2, SQRT2", "propList");
330 printWithRunin("Math methods", "abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random, round, sin, sqrt, tan", "propList");
339 // histList[0] = first command entered, [1] = second, etc.
340 // type something, press up --> thing typed is now in "limbo"
341 // (last item in histList) and should be reachable by pressing
344 var L = histList.length;
353 // Save this entry in case the user hits the down key.
354 histList[histPos] = _in.value;
360 // Use a timeout to prevent up from moving cursor within new text
361 // Set to nothing first for the same reason
365 _in.value = histList[histPos];
366 var caretPos = _in.value.length;
367 if (_in.setSelectionRange)
368 _in.setSelectionRange(caretPos, caretPos);
379 _in.value = histList[histPos];
381 else if (histPos == L-1)
383 // Already on the current entry: clear but save
386 histList[histPos] = _in.value;
394 function tabcomplete()
397 * Working backwards from s[from], find the spot
398 * where this expression starts. It will scan
399 * until it hits a mismatched ( or a space,
400 * but it skips over quoted strings.
401 * If stopAtDot is true, stop at a '.'
403 function findbeginning(s, from, stopAtDot)
406 * Complicated function.
408 * Return true if s[i] == q BUT ONLY IF
409 * s[i-1] is not a backslash.
411 function equalButNotEscaped(s,i,q)
413 if(s.charAt(i) != q) // not equal go no further
416 if(i==0) // beginning of string
419 if(s.charAt(i-1) == '\\') // escaped?
427 for(i=from; i>=0; i--)
429 if(s.charAt(i) == ' ')
432 if(stopAtDot && s.charAt(i) == '.')
435 if(s.charAt(i) == ')')
437 else if(s.charAt(i) == '(')
443 // skip quoted strings
444 if(s.charAt(i) == '\'' || s.charAt(i) == '\"')
446 //dump("skipping quoted chars: ");
447 var quot = s.charAt(i);
449 while(i >= 0 && !equalButNotEscaped(s,i,quot)) {
459 // XXX should be used more consistently (instead of using selectionStart/selectionEnd throughout code)
460 // XXX doesn't work in IE, even though it contains IE-specific code
461 function getcaretpos(inp)
463 if(inp.selectionEnd != null)
464 return inp.selectionEnd;
466 if(inp.createTextRange)
468 var docrange = _win.Shell.document.selection.createRange();
469 var inprange = inp.createTextRange();
470 if (inprange.setEndPoint)
472 inprange.setEndPoint('EndToStart', docrange);
473 return inprange.text.length;
477 return inp.value.length; // sucks, punt
480 function setselectionto(inp,pos)
482 if(inp.selectionStart) {
483 inp.selectionStart = inp.selectionEnd = pos;
485 else if(inp.createTextRange) {
486 var docrange = _win.Shell.document.selection.createRange();
487 var inprange = inp.createTextRange();
488 inprange.move('character',pos);
494 if(_win.Shell.document.getSelection())
495 _win.Shell.document.getSelection() = "";
499 // get position of cursor within the input box
500 var caret = getcaretpos(_in);
504 var dotpos, spacepos, complete, obj;
505 //dump("caret pos: " + caret + "\n");
506 // see if there's a dot before here
507 dotpos = findbeginning(_in.value, caret-1, true);
508 //dump("dot pos: " + dotpos + "\n");
509 if(dotpos == -1 || _in.value.charAt(dotpos) != '.') {
511 //dump("changed dot pos: " + dotpos + "\n");
514 // look backwards for a non-variable-name character
515 spacepos = findbeginning(_in.value, dotpos-1, false);
516 //dump("space pos: " + spacepos + "\n");
517 // get the object we're trying to complete on
518 if(spacepos == dotpos || spacepos+1 == dotpos || dotpos == caret)
520 // try completing function args
521 if(_in.value.charAt(dotpos) == '(' ||
522 (_in.value.charAt(spacepos) == '(' && (spacepos+1) == dotpos))
525 var from = (_in.value.charAt(dotpos) == '(') ? dotpos : spacepos;
526 spacepos = findbeginning(_in.value, from-1, false);
528 fname = _in.value.substr(spacepos+1,from-(spacepos+1));
529 //dump("fname: " + fname + "\n");
531 with(_win.Shell._scope)
533 with(Shell.shellCommands)
537 //dump('fn is not a valid object\n');
540 if(fn == undefined) {
541 //dump('fn is undefined');
544 if(fn instanceof Function)
546 // Print function definition, including argument names, but not function body
547 if(!fn.toString().match(/function .+?\(\) +\{\n +\[native code\]\n\}/))
548 println(fn.toString().match(/function .+?\(.*?\)/), "tabcomplete");
558 var objname = _in.value.substr(spacepos+1,dotpos-(spacepos+1));
559 //dump("objname: |" + objname + "|\n");
561 with(_win.Shell._scope)
569 if(obj == undefined) {
570 // sometimes this is tabcomplete's fault, so don't print it :(
571 // e.g. completing from "print(document.getElements"
572 // println("Can't complete from null or undefined expression " + objname, "error");
576 //dump("obj: " + obj + "\n");
577 // get the thing we're trying to complete
580 if(spacepos+1 == dotpos || spacepos == dotpos)
582 // nothing to complete
583 //dump("nothing to complete\n");
587 complete = _in.value.substr(spacepos+1,dotpos-(spacepos+1));
590 complete = _in.value.substr(dotpos+1,caret-(dotpos+1));
592 //dump("complete: " + complete + "\n");
593 // ok, now look at all the props/methods of this obj
594 // and find ones starting with 'complete'
596 var bestmatch = null;
600 //XXX: making it lowercase could help some cases,
601 // but screws up my general logic.
602 if(a.substr(0,complete.length) == complete) {
604 ////dump("match: " + a + "\n");
605 // if no best match, this is the best match
606 if(bestmatch == null)
611 // the best match is the longest common string
612 function min(a,b){ return ((a<b)?a:b); }
614 for(i=0; i< min(bestmatch.length, a.length); i++)
616 if(bestmatch.charAt(i) != a.charAt(i))
619 bestmatch = bestmatch.substr(0,i);
620 ////dump("bestmatch len: " + i + "\n");
622 ////dump("bestmatch: " + bestmatch + "\n");
625 bestmatch = (bestmatch || "");
626 ////dump("matches: " + matches + "\n");
627 var objAndComplete = (objname || obj) + "." + bestmatch;
628 //dump("matches.length: " + matches.length + ", tooManyMatches: " + tooManyMatches + ", objAndComplete: " + objAndComplete + "\n");
629 if(matches.length > 1 && (tooManyMatches == objAndComplete || matches.length <= 10)) {
631 printWithRunin("Matches: ", matches.join(', '), "tabcomplete");
632 tooManyMatches = null;
634 else if(matches.length > 10)
636 println(matches.length + " matches. Press tab again to see them all", "tabcomplete");
637 tooManyMatches = objAndComplete;
640 tooManyMatches = null;
645 if(dotpos == caret) {
651 _in.value = _in.value.substr(0, sstart)
653 + _in.value.substr(caret);
654 setselectionto(_in,caret + (bestmatch.length - complete.length));
659 function printQuestion(q)
664 function printAnswer(a)
666 if (a !== undefined) {
667 println(a, "normalOutput");
668 shellCommands.ans = a;
672 function printError(er)
674 var lineNumberString;
676 lastError = er; // for debugging the shell
679 // lineNumberString should not be "", to avoid a very wacky bug in IE 6.
680 lineNumberString = (er.lineNumber != undefined) ? (" on line " + er.lineNumber + ": ") : ": ";
681 println(er.name + lineNumberString + er.message, "error"); // Because IE doesn't have error.toString.
684 println(er, "error"); // Because security errors in Moz /only/ have toString.
689 _in.value = question = s ? s : _in.value;
694 histList[histList.length-1] = question;
695 histList[histList.length] = "";
696 histPos = histList.length - 1;
698 // Unfortunately, this has to happen *before* the JavaScript is run, so that
699 // print() output will go in the right place.
701 recalculateInputHeight();
702 printQuestion(question);
705 printError("Target window has been closed.");
709 try { ("Shell" in _win) }
711 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.");
715 if (!("Shell" in _win))
716 initTarget(); // silent
718 // Evaluate Shell.question using _win's eval (this is why eval isn't in the |with|, IIRC).
719 _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";