|
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
|
2 |
|
3 <html onclick="keepFocusInTextbox(event)"> |
|
4 <head> |
|
5 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
|
6 <title>JavaScript Shell 1.4</title> |
|
7 |
|
8 <script type="text/javascript"> |
|
9 var |
|
10 histList = [""], |
|
11 histPos = 0, |
|
12 _scope = {}, |
|
13 _win, // a top-level context |
|
14 question, // {String} the input command that's being evaluated. Accessed via |
|
15 // |Shell.question| from the target window for evaluation. |
|
16 _in, // {HTMLTextAreaElement} the textarea containing the input |
|
17 _out, // {HTMLDivElement} the output is printed to this element |
|
18 tooManyMatches = null, |
|
19 lastError = null, |
|
20 _jsVer = 0 // determines the way to execute the commands, see run() |
|
21 ; |
|
22 |
|
23 function refocus() |
|
24 { |
|
25 _in.blur(); // Needed for Mozilla to scroll correctly. |
|
26 _in.focus(); |
|
27 } |
|
28 |
|
29 function init() |
|
30 { |
|
31 _in = document.getElementById("input"); |
|
32 _out = document.getElementById("output"); |
|
33 |
|
34 _win = window; |
|
35 |
|
36 if (opener && !opener.closed) |
|
37 { |
|
38 println("Using bookmarklet version of shell: commands will run in opener's context.", "message"); |
|
39 _win = opener; |
|
40 } |
|
41 |
|
42 /* Run a series of (potentially async, but quick) tests to determine the |
|
43 * way to run code in this browser (for run()). Sets window._jsVer based |
|
44 * on the tests. */ |
|
45 _jsVer = 0; |
|
46 for (var jsVerToTry=19; jsVerToTry>=15; jsVerToTry--) { |
|
47 run(window, "if(_jsVer < " + jsVerToTry + ") { " + |
|
48 "_jsVer=" + jsVerToTry + "; }", jsVerToTry); |
|
49 } |
|
50 |
|
51 initTarget(); |
|
52 |
|
53 recalculateInputHeight(); |
|
54 refocus(); |
|
55 } |
|
56 |
|
57 /** |
|
58 * Runs |code| in |_win|'s context. |
|
59 * @param overridenJSVer {int} (optional) overrides the default (specified by _jsVer) |
|
60 * way to run the code. |
|
61 */ |
|
62 function run(_win, code, overridenJSVer) { |
|
63 var jsVerToUse = overridenJSVer ? overridenJSVer : _jsVer; |
|
64 if (jsVerToUse <= 15) { |
|
65 _win.location.href = "javascript:" + code + "; void 0"; |
|
66 } else { |
|
67 var sc = _win.document.createElement("script"); |
|
68 sc.type="application/javascript;version=" + jsVerToUse/10; |
|
69 sc.src="data:application/x-javascript," + code; |
|
70 _win.document.body.appendChild(sc); // runs the script asynchronously |
|
71 } |
|
72 } |
|
73 |
|
74 function initTarget() |
|
75 { |
|
76 _win.Shell = window; |
|
77 _win.print = shellCommands.print; |
|
78 } |
|
79 |
|
80 |
|
81 // Unless the user is selected something, refocus the textbox. |
|
82 // (requested by caillon, brendan, asa) |
|
83 function keepFocusInTextbox(e) |
|
84 { |
|
85 var g = e.srcElement ? e.srcElement : e.target; // IE vs. standard |
|
86 |
|
87 while (!g.tagName) |
|
88 g = g.parentNode; |
|
89 var t = g.tagName.toUpperCase(); |
|
90 if (t=="A" || t=="INPUT") |
|
91 return; |
|
92 |
|
93 if (window.getSelection) { |
|
94 // Mozilla |
|
95 if (String(window.getSelection())) |
|
96 return; |
|
97 } |
|
98 else { |
|
99 // IE |
|
100 if ( document.selection.createRange().text ) |
|
101 return; |
|
102 } |
|
103 |
|
104 refocus(); |
|
105 } |
|
106 |
|
107 function inputKeydown(e) { |
|
108 // Use onkeydown because IE doesn't support onkeypress for arrow keys |
|
109 |
|
110 //alert(e.keyCode + " ^ " + e.keycode); |
|
111 |
|
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)) |
|
121 hist(true); |
|
122 } else if (e.keyCode == 40) { // down |
|
123 // go down in history if at end or ctrl-down |
|
124 if (e.ctrlKey || caretInLastLine(_in)) |
|
125 hist(false); |
|
126 } else if (e.keyCode == 9) { // tab |
|
127 tabcomplete(); |
|
128 setTimeout(function() { refocus(); }, 0); // refocus because tab was hit |
|
129 } else { } |
|
130 |
|
131 setTimeout(recalculateInputHeight, 0); |
|
132 |
|
133 //return true; |
|
134 }; |
|
135 |
|
136 function caretInFirstLine(textbox) |
|
137 { |
|
138 // IE doesn't support selectionStart/selectionEnd |
|
139 if (textbox.selectionStart == undefined) |
|
140 return true; |
|
141 |
|
142 var firstLineBreak = textbox.value.indexOf("\n"); |
|
143 |
|
144 return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak)); |
|
145 } |
|
146 |
|
147 function caretInLastLine(textbox) |
|
148 { |
|
149 // IE doesn't support selectionStart/selectionEnd |
|
150 if (textbox.selectionEnd == undefined) |
|
151 return true; |
|
152 |
|
153 var lastLineBreak = textbox.value.lastIndexOf("\n"); |
|
154 |
|
155 return (textbox.selectionEnd > lastLineBreak); |
|
156 } |
|
157 |
|
158 function recalculateInputHeight() |
|
159 { |
|
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 |
|
163 |
|
164 if (_in.rows != rows) // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0. |
|
165 _in.rows = rows; |
|
166 } |
|
167 |
|
168 function println(s, type) |
|
169 { |
|
170 if((s=String(s))) |
|
171 { |
|
172 var newdiv = document.createElement("div"); |
|
173 newdiv.appendChild(document.createTextNode(s)); |
|
174 newdiv.className = type; |
|
175 _out.appendChild(newdiv); |
|
176 return newdiv; |
|
177 } |
|
178 return false; |
|
179 } |
|
180 |
|
181 function printWithRunin(h, s, type) |
|
182 { |
|
183 var div = println(s, type); |
|
184 if (div) { |
|
185 var head = document.createElement("strong"); |
|
186 head.appendChild(document.createTextNode(h + ": ")); |
|
187 div.insertBefore(head, div.firstChild); |
|
188 } |
|
189 } |
|
190 |
|
191 |
|
192 var shellCommands = |
|
193 { |
|
194 load : function load(url) |
|
195 { |
|
196 var s = _win.document.createElement("script"); |
|
197 s.type = "text/javascript"; |
|
198 s.src = url; |
|
199 _win.document.getElementsByTagName("head")[0].appendChild(s); |
|
200 println("Loading " + url + "...", "message"); |
|
201 }, |
|
202 |
|
203 clear : function clear() |
|
204 { |
|
205 var CHILDREN_TO_PRESERVE = 3; |
|
206 while (_out.childNodes[CHILDREN_TO_PRESERVE]) |
|
207 _out.removeChild(_out.childNodes[CHILDREN_TO_PRESERVE]); |
|
208 }, |
|
209 |
|
210 print : function print(s) { println(s, "print"); }, |
|
211 |
|
212 // the normal function, "print", shouldn't return a value |
|
213 // (suggested by brendan; later noticed it was a problem when showing others) |
|
214 pr : function pr(s) |
|
215 { |
|
216 shellCommands.print(s); // need to specify shellCommands so it doesn't try window.print()! |
|
217 return s; |
|
218 }, |
|
219 |
|
220 props : function props(e, onePerLine) |
|
221 { |
|
222 if (e === null) { |
|
223 println("props called with null argument", "error"); |
|
224 return; |
|
225 } |
|
226 |
|
227 if (e === undefined) { |
|
228 println("props called with undefined argument", "error"); |
|
229 return; |
|
230 } |
|
231 |
|
232 var ns = ["Methods", "Fields", "Unreachables"]; |
|
233 var as = [[], [], []]; // array of (empty) arrays of arrays! |
|
234 var p, j, i; // loop variables, several used multiple times |
|
235 |
|
236 var protoLevels = 0; |
|
237 |
|
238 for (p = e; p; p = p.__proto__) |
|
239 { |
|
240 for (i=0; i<ns.length; ++i) |
|
241 as[i][protoLevels] = []; |
|
242 ++protoLevels; |
|
243 } |
|
244 |
|
245 for(var a in e) |
|
246 { |
|
247 // Shortcoming: doesn't check that VALUES are the same in object and prototype. |
|
248 |
|
249 var protoLevel = -1; |
|
250 try |
|
251 { |
|
252 for (p = e; p && (a in p); p = p.__proto__) |
|
253 ++protoLevel; |
|
254 } |
|
255 catch(er) { protoLevel = 0; } // "in" operator throws when param to props() is a string |
|
256 |
|
257 var type = 1; |
|
258 try |
|
259 { |
|
260 if ((typeof e[a]) == "function") |
|
261 type = 0; |
|
262 } |
|
263 catch (er) { type = 2; } |
|
264 |
|
265 as[type][protoLevel].push(a); |
|
266 } |
|
267 |
|
268 function times(s, n) { return n ? s + times(s, n-1) : ""; } |
|
269 |
|
270 for (j=0; j<protoLevels; ++j) |
|
271 for (i=0;i<ns.length;++i) |
|
272 if (as[i][j].length) |
|
273 printWithRunin( |
|
274 ns[i] + times(" of prototype", j), |
|
275 (onePerLine ? "\n\n" : "") + as[i][j].sort().join(onePerLine ? "\n" : ", ") + (onePerLine ? "\n\n" : ""), |
|
276 "propList" |
|
277 ); |
|
278 }, |
|
279 |
|
280 blink : function blink(node) |
|
281 { |
|
282 if (!node) throw("blink: argument is null or undefined."); |
|
283 if (node.nodeType == null) throw("blink: argument must be a node."); |
|
284 if (node.nodeType == 3) throw("blink: argument must not be a text node"); |
|
285 if (node.documentElement) throw("blink: argument must not be the document object"); |
|
286 |
|
287 function setOutline(o) { |
|
288 return function() { |
|
289 if (node.style.outline != node.style.bogusProperty) { |
|
290 // browser supports outline (Firefox 1.1 and newer, CSS3, Opera 8). |
|
291 node.style.outline = o; |
|
292 } |
|
293 else if (node.style.MozOutline != node.style.bogusProperty) { |
|
294 // browser supports MozOutline (Firefox 1.0.x and older) |
|
295 node.style.MozOutline = o; |
|
296 } |
|
297 else { |
|
298 // browser only supports border (IE). border is a fallback because it moves things around. |
|
299 node.style.border = o; |
|
300 } |
|
301 } |
|
302 } |
|
303 |
|
304 function focusIt(a) { |
|
305 return function() { |
|
306 a.focus(); |
|
307 } |
|
308 } |
|
309 |
|
310 if (node.ownerDocument) { |
|
311 var windowToFocusNow = (node.ownerDocument.defaultView || node.ownerDocument.parentWindow); // Moz vs. IE |
|
312 if (windowToFocusNow) |
|
313 setTimeout(focusIt(windowToFocusNow.top), 0); |
|
314 } |
|
315 |
|
316 for(var i=1;i<7;++i) |
|
317 setTimeout(setOutline((i%2)?'3px solid red':'none'), i*100); |
|
318 |
|
319 setTimeout(focusIt(window), 800); |
|
320 setTimeout(focusIt(_in), 810); |
|
321 }, |
|
322 |
|
323 scope : function scope(sc) |
|
324 { |
|
325 if (!sc) sc = {}; |
|
326 _scope = sc; |
|
327 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"); |
|
328 }, |
|
329 |
|
330 mathHelp : function mathHelp() |
|
331 { |
|
332 printWithRunin("Math constants", "E, LN2, LN10, LOG2E, LOG10E, PI, SQRT1_2, SQRT2", "propList"); |
|
333 printWithRunin("Math methods", "abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random, round, sin, sqrt, tan", "propList"); |
|
334 }, |
|
335 |
|
336 ans : undefined |
|
337 }; |
|
338 |
|
339 |
|
340 function hist(up) |
|
341 { |
|
342 // histList[0] = first command entered, [1] = second, etc. |
|
343 // type something, press up --> thing typed is now in "limbo" |
|
344 // (last item in histList) and should be reachable by pressing |
|
345 // down again. |
|
346 |
|
347 var L = histList.length; |
|
348 |
|
349 if (L == 1) |
|
350 return; |
|
351 |
|
352 if (up) |
|
353 { |
|
354 if (histPos == L-1) |
|
355 { |
|
356 // Save this entry in case the user hits the down key. |
|
357 histList[histPos] = _in.value; |
|
358 } |
|
359 |
|
360 if (histPos > 0) |
|
361 { |
|
362 histPos--; |
|
363 // Use a timeout to prevent up from moving cursor within new text |
|
364 // Set to nothing first for the same reason |
|
365 setTimeout( |
|
366 function() { |
|
367 _in.value = ''; |
|
368 _in.value = histList[histPos]; |
|
369 var caretPos = _in.value.length; |
|
370 if (_in.setSelectionRange) |
|
371 _in.setSelectionRange(caretPos, caretPos); |
|
372 }, |
|
373 0 |
|
374 ); |
|
375 } |
|
376 } |
|
377 else // down |
|
378 { |
|
379 if (histPos < L-1) |
|
380 { |
|
381 histPos++; |
|
382 _in.value = histList[histPos]; |
|
383 } |
|
384 else if (histPos == L-1) |
|
385 { |
|
386 // Already on the current entry: clear but save |
|
387 if (_in.value) |
|
388 { |
|
389 histList[histPos] = _in.value; |
|
390 ++histPos; |
|
391 _in.value = ""; |
|
392 } |
|
393 } |
|
394 } |
|
395 } |
|
396 |
|
397 function tabcomplete() |
|
398 { |
|
399 /* |
|
400 * Working backwards from s[from], find the spot |
|
401 * where this expression starts. It will scan |
|
402 * until it hits a mismatched ( or a space, |
|
403 * but it skips over quoted strings. |
|
404 * If stopAtDot is true, stop at a '.' |
|
405 */ |
|
406 function findbeginning(s, from, stopAtDot) |
|
407 { |
|
408 /* |
|
409 * Complicated function. |
|
410 * |
|
411 * Return true if s[i] == q BUT ONLY IF |
|
412 * s[i-1] is not a backslash. |
|
413 */ |
|
414 function equalButNotEscaped(s,i,q) |
|
415 { |
|
416 if(s.charAt(i) != q) // not equal go no further |
|
417 return false; |
|
418 |
|
419 if(i==0) // beginning of string |
|
420 return true; |
|
421 |
|
422 if(s.charAt(i-1) == '\\') // escaped? |
|
423 return false; |
|
424 |
|
425 return true; |
|
426 } |
|
427 |
|
428 var nparens = 0; |
|
429 var i; |
|
430 for(i=from; i>=0; i--) |
|
431 { |
|
432 if(s.charAt(i) == ' ') |
|
433 break; |
|
434 |
|
435 if(stopAtDot && s.charAt(i) == '.') |
|
436 break; |
|
437 |
|
438 if(s.charAt(i) == ')') |
|
439 nparens++; |
|
440 else if(s.charAt(i) == '(') |
|
441 nparens--; |
|
442 |
|
443 if(nparens < 0) |
|
444 break; |
|
445 |
|
446 // skip quoted strings |
|
447 if(s.charAt(i) == '\'' || s.charAt(i) == '\"') |
|
448 { |
|
449 //dump("skipping quoted chars: "); |
|
450 var quot = s.charAt(i); |
|
451 i--; |
|
452 while(i >= 0 && !equalButNotEscaped(s,i,quot)) { |
|
453 //dump(s.charAt(i)); |
|
454 i--; |
|
455 } |
|
456 //dump("\n"); |
|
457 } |
|
458 } |
|
459 return i; |
|
460 } |
|
461 |
|
462 // XXX should be used more consistently (instead of using selectionStart/selectionEnd throughout code) |
|
463 // XXX doesn't work in IE, even though it contains IE-specific code |
|
464 function getcaretpos(inp) |
|
465 { |
|
466 if(inp.selectionEnd != null) |
|
467 return inp.selectionEnd; |
|
468 |
|
469 if(inp.createTextRange) |
|
470 { |
|
471 var docrange = _win.Shell.document.selection.createRange(); |
|
472 var inprange = inp.createTextRange(); |
|
473 if (inprange.setEndPoint) |
|
474 { |
|
475 inprange.setEndPoint('EndToStart', docrange); |
|
476 return inprange.text.length; |
|
477 } |
|
478 } |
|
479 |
|
480 return inp.value.length; // sucks, punt |
|
481 } |
|
482 |
|
483 function setselectionto(inp,pos) |
|
484 { |
|
485 if(inp.selectionStart) { |
|
486 inp.selectionStart = inp.selectionEnd = pos; |
|
487 } |
|
488 else if(inp.createTextRange) { |
|
489 var docrange = _win.Shell.document.selection.createRange(); |
|
490 var inprange = inp.createTextRange(); |
|
491 inprange.move('character',pos); |
|
492 inprange.select(); |
|
493 } |
|
494 else { // err... |
|
495 /* |
|
496 inp.select(); |
|
497 if(_win.Shell.document.getSelection()) |
|
498 _win.Shell.document.getSelection() = ""; |
|
499 */ |
|
500 } |
|
501 } |
|
502 // get position of cursor within the input box |
|
503 var caret = getcaretpos(_in); |
|
504 |
|
505 if(caret) { |
|
506 //dump("----\n"); |
|
507 var dotpos, spacepos, complete, obj; |
|
508 //dump("caret pos: " + caret + "\n"); |
|
509 // see if there's a dot before here |
|
510 dotpos = findbeginning(_in.value, caret-1, true); |
|
511 //dump("dot pos: " + dotpos + "\n"); |
|
512 if(dotpos == -1 || _in.value.charAt(dotpos) != '.') { |
|
513 dotpos = caret; |
|
514 //dump("changed dot pos: " + dotpos + "\n"); |
|
515 } |
|
516 |
|
517 // look backwards for a non-variable-name character |
|
518 spacepos = findbeginning(_in.value, dotpos-1, false); |
|
519 //dump("space pos: " + spacepos + "\n"); |
|
520 // get the object we're trying to complete on |
|
521 if(spacepos == dotpos || spacepos+1 == dotpos || dotpos == caret) |
|
522 { |
|
523 // try completing function args |
|
524 if(_in.value.charAt(dotpos) == '(' || |
|
525 (_in.value.charAt(spacepos) == '(' && (spacepos+1) == dotpos)) |
|
526 { |
|
527 var fn,fname; |
|
528 var from = (_in.value.charAt(dotpos) == '(') ? dotpos : spacepos; |
|
529 spacepos = findbeginning(_in.value, from-1, false); |
|
530 |
|
531 fname = _in.value.substr(spacepos+1,from-(spacepos+1)); |
|
532 //dump("fname: " + fname + "\n"); |
|
533 try { |
|
534 with(_win.Shell._scope) |
|
535 with(_win) |
|
536 with(Shell.shellCommands) |
|
537 fn = eval(fname); |
|
538 } |
|
539 catch(er) { |
|
540 //dump('fn is not a valid object\n'); |
|
541 return; |
|
542 } |
|
543 if(fn == undefined) { |
|
544 //dump('fn is undefined'); |
|
545 return; |
|
546 } |
|
547 if(fn instanceof Function) |
|
548 { |
|
549 // Print function definition, including argument names, but not function body |
|
550 if(!fn.toString().match(/function .+?\(\) +\{\n +\[native code\]\n\}/)) |
|
551 println(fn.toString().match(/function .+?\(.*?\)/), "tabcomplete"); |
|
552 } |
|
553 |
|
554 return; |
|
555 } |
|
556 else |
|
557 obj = _win; |
|
558 } |
|
559 else |
|
560 { |
|
561 var objname = _in.value.substr(spacepos+1,dotpos-(spacepos+1)); |
|
562 //dump("objname: |" + objname + "|\n"); |
|
563 try { |
|
564 with(_win.Shell._scope) |
|
565 with(_win) |
|
566 obj = eval(objname); |
|
567 } |
|
568 catch(er) { |
|
569 printError(er); |
|
570 return; |
|
571 } |
|
572 if(obj == undefined) { |
|
573 // sometimes this is tabcomplete's fault, so don't print it :( |
|
574 // e.g. completing from "print(document.getElements" |
|
575 // println("Can't complete from null or undefined expression " + objname, "error"); |
|
576 return; |
|
577 } |
|
578 } |
|
579 //dump("obj: " + obj + "\n"); |
|
580 // get the thing we're trying to complete |
|
581 if(dotpos == caret) |
|
582 { |
|
583 if(spacepos+1 == dotpos || spacepos == dotpos) |
|
584 { |
|
585 // nothing to complete |
|
586 //dump("nothing to complete\n"); |
|
587 return; |
|
588 } |
|
589 |
|
590 complete = _in.value.substr(spacepos+1,dotpos-(spacepos+1)); |
|
591 } |
|
592 else { |
|
593 complete = _in.value.substr(dotpos+1,caret-(dotpos+1)); |
|
594 } |
|
595 //dump("complete: " + complete + "\n"); |
|
596 // ok, now look at all the props/methods of this obj |
|
597 // and find ones starting with 'complete' |
|
598 var matches = []; |
|
599 var bestmatch = null; |
|
600 for(var a in obj) |
|
601 { |
|
602 //a = a.toString(); |
|
603 //XXX: making it lowercase could help some cases, |
|
604 // but screws up my general logic. |
|
605 if(a.substr(0,complete.length) == complete) { |
|
606 matches.push(a); |
|
607 ////dump("match: " + a + "\n"); |
|
608 // if no best match, this is the best match |
|
609 if(bestmatch == null) |
|
610 { |
|
611 bestmatch = a; |
|
612 } |
|
613 else { |
|
614 // the best match is the longest common string |
|
615 function min(a,b){ return ((a<b)?a:b); } |
|
616 var i; |
|
617 for(i=0; i< min(bestmatch.length, a.length); i++) |
|
618 { |
|
619 if(bestmatch.charAt(i) != a.charAt(i)) |
|
620 break; |
|
621 } |
|
622 bestmatch = bestmatch.substr(0,i); |
|
623 ////dump("bestmatch len: " + i + "\n"); |
|
624 } |
|
625 ////dump("bestmatch: " + bestmatch + "\n"); |
|
626 } |
|
627 } |
|
628 bestmatch = (bestmatch || ""); |
|
629 ////dump("matches: " + matches + "\n"); |
|
630 var objAndComplete = (objname || obj) + "." + bestmatch; |
|
631 //dump("matches.length: " + matches.length + ", tooManyMatches: " + tooManyMatches + ", objAndComplete: " + objAndComplete + "\n"); |
|
632 if(matches.length > 1 && (tooManyMatches == objAndComplete || matches.length <= 10)) { |
|
633 |
|
634 printWithRunin("Matches: ", matches.join(', '), "tabcomplete"); |
|
635 tooManyMatches = null; |
|
636 } |
|
637 else if(matches.length > 10) |
|
638 { |
|
639 println(matches.length + " matches. Press tab again to see them all", "tabcomplete"); |
|
640 tooManyMatches = objAndComplete; |
|
641 } |
|
642 else { |
|
643 tooManyMatches = null; |
|
644 } |
|
645 if(bestmatch != "") |
|
646 { |
|
647 var sstart; |
|
648 if(dotpos == caret) { |
|
649 sstart = spacepos+1; |
|
650 } |
|
651 else { |
|
652 sstart = dotpos+1; |
|
653 } |
|
654 _in.value = _in.value.substr(0, sstart) |
|
655 + bestmatch |
|
656 + _in.value.substr(caret); |
|
657 setselectionto(_in,caret + (bestmatch.length - complete.length)); |
|
658 } |
|
659 } |
|
660 } |
|
661 |
|
662 function printQuestion(q) |
|
663 { |
|
664 println(q, "input"); |
|
665 } |
|
666 |
|
667 function printAnswer(a) |
|
668 { |
|
669 if (a !== undefined) { |
|
670 println(a, "normalOutput"); |
|
671 shellCommands.ans = a; |
|
672 } |
|
673 } |
|
674 |
|
675 function printError(er) |
|
676 { |
|
677 var lineNumberString; |
|
678 |
|
679 lastError = er; // for debugging the shell |
|
680 if (er.name) |
|
681 { |
|
682 // lineNumberString should not be "", to avoid a very wacky bug in IE 6. |
|
683 lineNumberString = (er.lineNumber != undefined) ? (" on line " + er.lineNumber + ": ") : ": "; |
|
684 println(er.name + lineNumberString + er.message, "error"); // Because IE doesn't have error.toString. |
|
685 } |
|
686 else |
|
687 println(er, "error"); // Because security errors in Moz /only/ have toString. |
|
688 } |
|
689 |
|
690 /** |
|
691 * Evaluates |s| or current input (_in.value) in the previously set up context. |
|
692 * @param {String} s - (optional) command to evaluate. |
|
693 */ |
|
694 function go(s) |
|
695 { |
|
696 // save the command to eval in |question|, so that the target window can access |
|
697 // it when evaluating. |
|
698 _in.value = question = s ? s : _in.value; |
|
699 |
|
700 if (question == "") |
|
701 return; |
|
702 |
|
703 histList[histList.length-1] = question; |
|
704 histList[histList.length] = ""; |
|
705 histPos = histList.length - 1; |
|
706 |
|
707 // Unfortunately, this has to happen *before* the JavaScript is run, so that |
|
708 // print() output will go in the right place. |
|
709 _in.value=''; |
|
710 recalculateInputHeight(); |
|
711 printQuestion(question); |
|
712 |
|
713 if (_win.closed) { |
|
714 printError("Target window has been closed."); |
|
715 return; |
|
716 } |
|
717 |
|
718 try { ("Shell" in _win) } |
|
719 catch(er) { |
|
720 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."); |
|
721 return; |
|
722 } |
|
723 |
|
724 if (!("Shell" in _win)) |
|
725 initTarget(); // silent |
|
726 |
|
727 // Evaluate Shell.question using _win's eval (this is why eval isn't in the |with|, IIRC). |
|
728 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);"); |
|
729 } |
|
730 |
|
731 </script> |
|
732 |
|
733 <!-- for http://ted.mielczarek.org/code/mozilla/extensiondev/ --> |
|
734 <script type="text/javascript" src="chrome://extensiondev/content/chromeShellExtras.js"></script> |
|
735 |
|
736 <style type="text/css"> |
|
737 body { background: white; color: black; } |
|
738 |
|
739 #output { |
|
740 /* Preserve line breaks, but wrap too if browser supports it */ |
|
741 white-space: pre; |
|
742 white-space: -moz-pre-wrap; |
|
743 white-space: pre-wrap; |
|
744 } |
|
745 |
|
746 h3 { margin-top: 0; margin-bottom: 0em; } |
|
747 h3 + div { margin: 0; } |
|
748 |
|
749 form { margin: 0; padding: 0; } |
|
750 #input { width: 100%; border: none; padding: 0; overflow: auto; } |
|
751 |
|
752 .input { color: blue; background: white; font: inherit; font-weight: bold; margin-top: .5em; /* background: #E6E6FF; */ } |
|
753 .normalOutput { color: black; background: white; } |
|
754 .print { color: brown; background: white; } |
|
755 .error { color: red; background: white; } |
|
756 .propList { color: green; background: white; } |
|
757 .message { color: green; background: white; } |
|
758 .tabcomplete { color: purple; background: white; } |
|
759 </style> |
|
760 </head> |
|
761 |
|
762 <body onload="init()"> |
|
763 |
|
764 <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> |
|
765 |
|
766 <div><textarea id="input" class="input" wrap="off" spellcheck="false" onkeydown="inputKeydown(event)" rows="1"></textarea></div> |
|
767 |
|
768 </body> |
|
769 |
|
770 </html> |