browser/metro/base/content/jsshell/shell.html

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial