diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index c3eee956..763e07d5 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -402,7 +402,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
if (info && info.file[0] !== "[")
({ file: fileName, line: lineNumber, context: ctxt }) = info;
- if (!context)
+ if (!context && fileName && fileName[0] !== "[")
context = _userContext || ctxt;
if (isinstance(context, ["Sandbox"]))
diff --git a/common/locale/en-US/eval.xml b/common/locale/en-US/eval.xml
index 40b059bc..a29e8f62 100644
--- a/common/locale/en-US/eval.xml
+++ b/common/locale/en-US/eval.xml
@@ -90,7 +90,6 @@
:js :javas :javascript
:javascript cmd
:javascript <<endpattern\ncmd\nendpattern
- :javascript!
Evaluates the given cmd as JavaScript. Behaves exactly as
@@ -137,6 +136,32 @@
+-
+ REPL
+ :javascript! context
+
+
+ Starts the JavaScript Read Eval Print Loop, where JavaScript
+ statements are entered and evaluated, their results printed, and the
+ input modified and entered again. Within the REPL, the results of a
+ given evaluation are available as variables named for the given
+ prompt.
+
+
+
+ If context is given, then statements are executed in that
+ global context.
+
+
+
+js1> ({ foo: bar })[object Object]::
+foo: "bar"js2> js1.foobar
+
+
+
Global Variables
-
diff --git a/common/modules/javascript.jsm b/common/modules/javascript.jsm
index e8318182..b6abdb4d 100644
--- a/common/modules/javascript.jsm
+++ b/common/modules/javascript.jsm
@@ -12,6 +12,8 @@ defineModule("javascript", {
use: ["services", "template", "util"]
}, this);
+let isPrototypeOf = Object.prototype.isPrototypeOf;
+
// TODO: Clean this up.
var JavaScript = Module("javascript", {
@@ -40,6 +42,14 @@ var JavaScript = Module("javascript", {
},
}),
+ globals: Class.memoize(function () [
+ [this.modules.userContext, "Global Variables"],
+ [this.modules, "modules"],
+ [this.window, "window"]
+ ]),
+
+ toplevel: Class.memoize(function () this.modules.jsmodules),
+
lazyInit: true,
newContext: function () this.modules.newContext(this.modules.userContext),
@@ -83,7 +93,7 @@ var JavaScript = Module("javascript", {
return [];
if (isinstance(obj, ["Sandbox"]) && !toplevel) // Temporary hack.
return [];
- if (this.modules.jsmodules.isPrototypeOf(obj) && !toplevel)
+ if (isPrototypeOf.call(this.toplevel, obj) && !toplevel)
return [];
let completions = [k for (k in this.iter(obj, toplevel))];
@@ -310,11 +320,7 @@ var JavaScript = Module("javascript", {
let end = (frame == -1 ? this._lastIdx : this._get(frame + 1).offset);
this._cacheKey = null;
- let obj = [[this.cache.evalContext, "Local Variables"],
- [this.replContext, "REPL Variables"],
- [this.modules.userContext, "Global Variables"],
- [this.modules, "modules"],
- [this.window, "window"]]; // Default objects;
+ let obj = [[this.cache.evalContext, "Local Variables"]].concat(this.globals);
// Is this an object dereference?
if (dot < statement) // No.
dot = statement - 1;
@@ -751,15 +757,34 @@ var JavaScript = Module("javascript", {
});
modules.CommandREPLMode = Class("CommandREPLMode", modules.CommandMode, {
- open: function open() {
- let self = this;
+ init: function init(context) {
+ init.supercall(this);
- this.context = modules.newContext(modules.userContext, true);
+ let self = this;
+ let sandbox = isinstance(context, ["Sandbox"]);
+
+ this.context = modules.newContext(context, !sandbox);
this.js = modules.JavaScript();
this.js.replContext = this.context;
- this.js.newContext = function newContext() modules.newContext(self.context, true);
- this.repl = REPL(this.context);
+ this.js.newContext = function newContext() modules.newContext(self.context, !sandbox);
+ this.js.globals = [
+ [this.context, "REPL Variables"],
+ [context, "REPL Global"]
+ ].concat(this.js.globals.filter(function ([global]) isPrototypeOf.call(global, context)));
+
+ if (!isPrototypeOf.call(modules.jsmodules, context))
+ this.js.toplevel = context;
+
+ if (!isPrototypeOf.call(window, context))
+ this.js.window = context;
+
+ if (this.js.globals.slice(2).some(function ([global]) global === context))
+ this.js.globals.splice(1);
+
+ this.repl = REPL(this.context);
+ },
+ open: function open(context) {
this.updatePrompt();
modules.mow.echo(this.repl);
@@ -803,14 +828,14 @@ var JavaScript = Module("javascript", {
commands.add(["javas[cript]", "js"],
"Evaluate a JavaScript string",
function (args) {
- if (args.bang) // open JavaScript console
- dactyl.open("chrome://global/content/console.xul",
- { from: "javascript" });
- else if (args[0])
+ modules.commandline;
+
+ if (args[0] && !args.bang)
dactyl.userEval(args[0]);
else {
modules.commandline;
- modules.CommandREPLMode().open();
+ modules.CommandREPLMode(args[0] ? dactyl.userEval(args[0]) : modules.userContext)
+ .open();
}
}, {
bang: true,
diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS
index b5bfedd0..e8d2ca69 100644
--- a/pentadactyl/NEWS
+++ b/pentadactyl/NEWS
@@ -86,6 +86,7 @@
and linking to source code locations). [b4]
- :downloads now opens a download list in the multi-line output
buffer. [b6]
+ - :javascript! now opens a Read Eval Print Loop. [b6]
- Added -arg flag to :map. [b6]
- :extensions has been replaced with a more powerful :addons.
- Added -literal flag to :command. [b6]