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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
     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>
     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   ;
    23 function refocus()
    24 {
    25   _in.blur(); // Needed for Mozilla to scroll correctly.
    26   _in.focus();
    27 }
    29 function init()
    30 {
    31   _in = document.getElementById("input");
    32   _out = document.getElementById("output");
    34   _win = window;
    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   }
    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   }
    51   initTarget();
    53   recalculateInputHeight();
    54   refocus();
    55 }
    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 }
    74 function initTarget()
    75 {
    76   _win.Shell = window;
    77   _win.print = shellCommands.print;
    78 }
    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
    87   while (!g.tagName)
    88     g = g.parentNode;
    89   var t = g.tagName.toUpperCase();
    90   if (t=="A" || t=="INPUT")
    91     return;
    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   }
   104   refocus();
   105 }
   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))
   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 { }
   131   setTimeout(recalculateInputHeight, 0);
   133   //return true;
   134 };
   136 function caretInFirstLine(textbox)
   137 {
   138   // IE doesn't support selectionStart/selectionEnd
   139   if (textbox.selectionStart == undefined)
   140     return true;
   142   var firstLineBreak = textbox.value.indexOf("\n");
   144   return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak));
   145 }
   147 function caretInLastLine(textbox)
   148 {
   149   // IE doesn't support selectionStart/selectionEnd
   150   if (textbox.selectionEnd == undefined)
   151     return true;
   153   var lastLineBreak = textbox.value.lastIndexOf("\n");
   155   return (textbox.selectionEnd > lastLineBreak);
   156 }
   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
   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 }
   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 }
   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 }
   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 },
   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 },
   210 print : function print(s) { println(s, "print"); },
   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 },
   220 props : function props(e, onePerLine)
   221 {
   222   if (e === null) {
   223     println("props called with null argument", "error");
   224     return;
   225   }
   227   if (e === undefined) {
   228     println("props called with undefined argument", "error");
   229     return;
   230   }
   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
   236   var protoLevels = 0;
   238   for (p = e; p; p = p.__proto__)
   239   {
   240     for (i=0; i<ns.length; ++i)
   241       as[i][protoLevels] = [];
   242     ++protoLevels;
   243   }
   245   for(var a in e)
   246   {
   247     // Shortcoming: doesn't check that VALUES are the same in object and prototype.
   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
   257     var type = 1;
   258     try
   259     {
   260       if ((typeof e[a]) == "function")
   261         type = 0;
   262     }
   263     catch (er) { type = 2; }
   265     as[type][protoLevel].push(a);
   266   }
   268   function times(s, n) { return n ? s + times(s, n-1) : ""; }
   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 },
   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");
   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   } 
   304   function focusIt(a) {
   305     return function() {
   306       a.focus(); 
   307     }
   308   }
   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   }
   316   for(var i=1;i<7;++i)
   317     setTimeout(setOutline((i%2)?'3px solid red':'none'), i*100);
   319   setTimeout(focusIt(window), 800);
   320   setTimeout(focusIt(_in), 810);
   321 },
   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 },
   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 },
   336 ans : undefined
   337 };
   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.
   347   var L = histList.length;
   349   if (L == 1)
   350     return;
   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     }
   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 }
   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;
   419       if(i==0) // beginning of string
   420         return true;
   422       if(s.charAt(i-1) == '\\') // escaped?
   423         return false;
   425       return true;
   426     }
   428     var nparens = 0;
   429     var i;
   430     for(i=from; i>=0; i--)
   431     {
   432       if(s.charAt(i) == ' ')
   433         break;
   435       if(stopAtDot && s.charAt(i) == '.')
   436         break;
   438       if(s.charAt(i) == ')')
   439         nparens++;
   440       else if(s.charAt(i) == '(')
   441         nparens--;
   443       if(nparens < 0)
   444         break;
   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   }
   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;
   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     }
   480     return inp.value.length; // sucks, punt
   481   }
   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);
   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       }
   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);
   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           }
   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         }
   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)) {
   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 }
   662 function printQuestion(q)
   663 {
   664   println(q, "input");
   665 }
   667 function printAnswer(a)
   668 {
   669   if (a !== undefined) {
   670     println(a, "normalOutput");
   671     shellCommands.ans = a;
   672   }
   673 }
   675 function printError(er)
   676 { 
   677   var lineNumberString;
   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 }
   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;
   700   if (question == "")
   701     return;
   703   histList[histList.length-1] = question;
   704   histList[histList.length] = "";
   705   histPos = histList.length - 1;
   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);
   713   if (_win.closed) {
   714     printError("Target window has been closed.");
   715     return;
   716   }
   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   }
   724   if (!("Shell" in _win))
   725     initTarget(); // silent
   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 }
   731 </script>
   733 <!-- for http://ted.mielczarek.org/code/mozilla/extensiondev/ -->
   734 <script type="text/javascript" src="chrome://extensiondev/content/chromeShellExtras.js"></script>
   736 <style type="text/css">
   737   body { background: white; color: black; }
   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   }
   746   h3 { margin-top: 0; margin-bottom: 0em; }
   747   h3 + div { margin: 0; }
   749   form { margin: 0; padding: 0; }
   750   #input { width: 100%; border: none; padding: 0; overflow: auto; }
   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>
   762 <body onload="init()">
   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>
   766 <div><textarea id="input" class="input" wrap="off" spellcheck="false" onkeydown="inputKeydown(event)" rows="1"></textarea></div>
   768 </body>
   770 </html>

mercurial