]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/modules/javascript.jsm
Import r6976 from upstream hg supporting Firefox up to 25.*
[dactyl.git] / common / modules / javascript.jsm
index 0f78615cc5b204b924716042538f1bae4c41101b..dcf206bfacce9b93c66e2e10131ef28c86748369 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -8,11 +8,12 @@ let { getOwnPropertyNames } = Object;
 
 try {
 
-Components.utils.import("resource://dactyl/bootstrap.jsm");
 defineModule("javascript", {
     exports: ["JavaScript", "javascript"],
-    use: ["services", "template", "util"]
-}, this);
+    require: ["util"]
+});
+
+lazyRequire("template", ["template"]);
 
 let isPrototypeOf = Object.prototype.isPrototypeOf;
 
@@ -41,22 +42,22 @@ var JavaScript = Module("javascript", {
             this.window = window;
 
             init.supercall(this);
-        },
+        }
     }),
 
-    globals: Class.memoize(function () [
-       [this.modules.userContext, "Global Variables"],
+    globals: Class.Memoize(function () [
+       [this.modules.userContext, /*L*/"Global Variables"],
        [this.modules, "modules"],
        [this.window, "window"]
     ]),
 
-    toplevel: Class.memoize(function () this.modules.jsmodules),
+    toplevel: Class.Memoize(function () this.modules.jsmodules),
 
     lazyInit: true,
 
-    newContext: function () this.modules.newContext(this.modules.userContext),
+    newContext: function () this.modules.newContext(this.modules.userContext, true, "Dactyl JS Temp Context"),
 
-    get completers() JavaScript.completers, // For backward compatibility
+    completers: Class.Memoize(() => Object.create(JavaScript.completers)),
 
     // Some object members are only accessible as function calls
     getKey: function (obj, key) {
@@ -71,22 +72,22 @@ var JavaScript = Module("javascript", {
         if (obj == null)
             return;
 
-        let seen = isinstance(obj, ["Sandbox"]) ? set(JavaScript.magicalNames) : {};
+        let seen = isinstance(obj, ["Sandbox"]) ? Set(JavaScript.magicalNames) : {};
         let globals = values(toplevel && this.window === obj ? this.globalNames : []);
 
         if (toplevel && isObject(obj) && "wrappedJSObject" in obj)
-            if (!set.add(seen, "wrappedJSObject"))
+            if (!Set.add(seen, "wrappedJSObject"))
                 yield "wrappedJSObject";
 
         for (let key in iter(globals, properties(obj, !toplevel, true)))
-            if (!set.add(seen, key))
+            if (!Set.add(seen, key))
                 yield key;
 
         // Properties aren't visible in an XPCNativeWrapper until
         // they're accessed.
         for (let key in properties(this.getKey(obj, "wrappedJSObject"), !toplevel, true))
             try {
-                if (key in obj && !set.has(seen, key))
+                if (key in obj && !Set.has(seen, key))
                     yield key;
             }
             catch (e) {}
@@ -103,7 +104,7 @@ var JavaScript = Module("javascript", {
 
         let completions = [k for (k in this.iter(obj, toplevel))];
         if (obj === this.modules) // Hack.
-            completions = completions.concat([k for (k in this.iter(this.modules.jsmodules, toplevel))]);
+            completions = array.uniq(completions.concat([k for (k in this.iter(this.modules.jsmodules, toplevel))]));
         return completions;
     },
 
@@ -117,18 +118,15 @@ var JavaScript = Module("javascript", {
             return cache[key];
 
         context[JavaScript.EVAL_TMP] = tmp;
-        context[JavaScript.EVAL_EXPORT] = function export_(obj) cache[key] = obj;
         try {
-            if (tmp != null) // Temporary hack until bug 609949 is fixed.
-                this.modules.dactyl.userEval(JavaScript.EVAL_EXPORT + "(" + arg + ")", context, "[Command Line Completion]", 1);
-            else
-                cache[key] = this.modules.dactyl.userEval(arg, context, "[Command Line Completion]", 1);
+            cache[key] = this.modules.dactyl.userEval(arg, context,
+                                                      /*L*/"[Command Line Completion]", 1);
 
             return cache[key];
         }
         catch (e) {
             util.reportError(e);
-            this.context.message = "Error: " + e;
+            this.context.message = _("error.error", e);
             return null;
         }
         finally {
@@ -170,7 +168,7 @@ var JavaScript = Module("javascript", {
 
         if (this._top.char != arg) {
             this.context.highlight(this._top.offset, this._i - this._top.offset, "SPELLCHECK");
-            throw Error("Invalid JS");
+            throw Error(/*L*/"Invalid JS");
         }
 
         // The closing character of this stack frame will have pushed a new
@@ -275,7 +273,7 @@ var JavaScript = Module("javascript", {
 
     // Don't eval any function calls unless the user presses tab.
     _checkFunction: function (start, end, key) {
-        let res = this._functions.some(function (idx) idx >= start && idx < end);
+        let res = this._functions.some(idx => (idx >= start && idx < end));
         if (!res || this.context.tabPressed || key in this.cache.evalled)
             return false;
         this.context.waitingForTab = true;
@@ -308,7 +306,7 @@ var JavaScript = Module("javascript", {
             if (this._checkFunction(prev, dot, cacheKey))
                 return [];
             if (prev != statement && obj == null) {
-                this.context.message = "Error: " + cacheKey.quote() + " is " + String(obj);
+                this.context.message = /*L*/"Error: " + cacheKey.quote() + " is " + String(obj);
                 return [];
             }
 
@@ -324,7 +322,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"]].concat(this.globals);
+        let obj = [[this.cache.evalContext, /*L*/"Local Variables"]].concat(this.globals);
         // Is this an object dereference?
         if (dot < statement) // No.
             dot = statement - 1;
@@ -339,7 +337,7 @@ var JavaScript = Module("javascript", {
         const self = this;
 
         if (!getOwnPropertyNames && !services.debugger.isOn && !this.context.message)
-            this.context.message = "For better completion data, please enable the JavaScript debugger (:set jsdebugger)";
+            this.context.message = /*L*/"For better completion data, please enable the JavaScript debugger (:set jsdebugger)";
 
         let base = this.context.fork("js", this._top.offset);
         base.forceAnchored = true;
@@ -347,11 +345,11 @@ var JavaScript = Module("javascript", {
         let prefix  = last != null ? key : "";
 
         if (last == null) // We're not looking for a quoted string, so filter out anything that's not a valid identifier
-            base.filters.push(function (item) /^[a-zA-Z_$][\w$]*$/.test(item.text));
+            base.filters.push(item => /^[a-zA-Z_$][\w$]*$/.test(item.text));
         else {
-            base.quote = [last, function (text) util.escapeString(text, ""), last];
+            base.quote = [last, text => util.escapeString(text, ""), last];
             if (prefix)
-                base.filters.push(function (item) item.item.indexOf(prefix) === 0);
+                base.filters.push(item => item.item.indexOf(prefix) === 0);
         }
 
         if (!compl) {
@@ -373,7 +371,8 @@ var JavaScript = Module("javascript", {
             };
 
             base.keys = {
-                text: prefix ? function (text) text.substr(prefix.length) : util.identity,
+                text: prefix ? text => text.substr(prefix.length)
+                             : text => text,
                 description: function (item) self.getKey(this.obj, item),
                 key: function (item) {
                     if (!isNaN(key))
@@ -391,7 +390,7 @@ var JavaScript = Module("javascript", {
         objects.forEach(function (obj) {
             let context = base.fork(obj[1]);
             context.title = [obj[1]];
-            context.keys.obj = function () obj[0];
+            context.keys.obj = () => obj[0];
             context.key = obj[1] + last;
             if (obj[0] == this.cache.evalContext)
                 context.regenerate = true;
@@ -399,8 +398,8 @@ var JavaScript = Module("javascript", {
             obj.ctxt_t = context.fork("toplevel");
             if (!compl) {
                 obj.ctxt_p = context.fork("prototypes");
-                obj.ctxt_t.generate = function () self.objectKeys(obj[0], true);
-                obj.ctxt_p.generate = function () self.objectKeys(obj[0], false);
+                obj.ctxt_t.generate = () => self.objectKeys(obj[0], true);
+                obj.ctxt_p.generate = () => self.objectKeys(obj[0], false);
             }
         }, this);
 
@@ -419,14 +418,14 @@ var JavaScript = Module("javascript", {
         objects.forEach(function (obj) {
             obj.ctxt_p.split(obj[1] + "/anchored", this, function (context) {
                 context.anchored = true;
-                context.title[0] += " (prototypes)";
+                context.title[0] += /*L*/" (prototypes)";
             });
         });
 
         objects.forEach(function (obj) {
             obj.ctxt_t.split(obj[1] + "/unanchored", this, function (context) {
                 context.anchored = false;
-                context.title[0] += " (substrings)";
+                context.title[0] += /*L*/" (substrings)";
                 context.filters.push(unanchored);
             });
         });
@@ -434,7 +433,7 @@ var JavaScript = Module("javascript", {
         objects.forEach(function (obj) {
             obj.ctxt_p.split(obj[1] + "/unanchored", this, function (context) {
                 context.anchored = false;
-                context.title[0] += " (prototype substrings)";
+                context.title[0] += /*L*/" (prototype substrings)";
                 context.filters.push(unanchored);
             });
         });
@@ -493,9 +492,9 @@ var JavaScript = Module("javascript", {
                         let [, prefix, args] = /^(function .*?)\((.*?)\)/.exec(Function.prototype.toString.call(func));
                         let n = this._get(i).comma.length;
                         args = template.map(Iterator(args.split(", ")),
-                            function ([i, arg]) <span highlight={i == n ? "Filter" : ""}>{arg}</span>,
-                            <>,&#xa0;</>);
-                        this.context.message = <>{prefix}({args})</>;
+                            ([i, arg]) => ["span", { highlight: i == n ? "Filter" : "" }, arg],
+                            ",\u00a0");
+                        this.context.message = ["", prefix + "(", args, ")"];
                     }
                 }
             }
@@ -555,7 +554,7 @@ var JavaScript = Module("javascript", {
                 }
                 catch (e) {}
                 if (!completer)
-                    completer = JavaScript.completers[funcName];
+                    completer = this.completers[funcName];
                 if (!completer)
                     return null;
 
@@ -565,7 +564,7 @@ var JavaScript = Module("javascript", {
                 for (let [i, idx] in Iterator(this._get(-2).comma)) {
                     let arg = this._str.substring(prev + 1, idx);
                     prev = idx;
-                    memoize(args, i, function () self.evalled(arg));
+                    memoize(args, i, () => self.evalled(arg));
                 }
                 let key = this._getKey();
                 args.push(key + string);
@@ -597,8 +596,10 @@ var JavaScript = Module("javascript", {
 
         // Wait for a keypress before completing when there's no key
         if (!this.context.tabPressed && key == "" && obj.length > 1) {
+            let message = this.context.message || "";
             this.context.waitingForTab = true;
-            this.context.message = "Waiting for key press";
+            this.context.message = ["", message, "\n",
+                                    _("completion.waitingForKeyPress")];
             return null;
         }
 
@@ -616,37 +617,35 @@ var JavaScript = Module("javascript", {
         return null;
     },
 
-    magicalNames: Class.memoize(function () Object.getOwnPropertyNames(Cu.Sandbox(this.window), true).sort()),
+    magicalNames: Class.Memoize(function () Object.getOwnPropertyNames(Cu.Sandbox(this.window), true).sort()),
 
     /**
      * A list of properties of the global object which are not
      * enumerable by any standard method.
      */
-    globalNames: Class.memoize(function () let (self = this) array.uniq([
-        "Array", "ArrayBuffer", "AttributeName", "Boolean", "Components",
+    globalNames: Class.Memoize(function () let (self = this) array.uniq([
+        "Array", "ArrayBuffer", "AttributeName", "Audio", "Boolean", "Components",
         "CSSFontFaceStyleDecl", "CSSGroupRuleRuleList", "CSSNameSpaceRule",
-        "CSSRGBColor", "CSSRect", "ComputedCSSStyleDeclaration", "Date",
-        "Error", "EvalError", "Float32Array", "Float64Array", "Function",
+        "CSSRGBColor", "CSSRect", "ComputedCSSStyleDeclaration", "Date", "Error",
+        "EvalError", "File", "Float32Array", "Float64Array", "Function",
         "HTMLDelElement", "HTMLInsElement", "HTMLSpanElement", "Infinity",
         "InnerModalContentWindow", "InnerWindow", "Int16Array", "Int32Array",
         "Int8Array", "InternalError", "Iterator", "JSON", "KeyboardEvent",
         "Math", "NaN", "Namespace", "Number", "Object", "Proxy", "QName",
         "ROCSSPrimitiveValue", "RangeError", "ReferenceError", "RegExp",
         "StopIteration", "String", "SyntaxError", "TypeError", "URIError",
-        "Uint16Array", "Uint32Array", "Uint8Array", "XML",
-        "XMLHttpProgressEvent", "XMLList", "XMLSerializer", "XPCNativeWrapper",
-        "XPCSafeJSWrapper", "XULControllers", "constructor", "decodeURI",
-        "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape",
-        "eval", "isFinite", "isNaN", "isXMLName", "parseFloat", "parseInt",
-        "undefined", "unescape", "uneval"
+        "Uint16Array", "Uint32Array", "Uint8Array", "XML", "XMLHttpProgressEvent",
+        "XMLList", "XMLSerializer", "XPCNativeWrapper", "XPCSafeJSWrapper",
+        "XULControllers", "constructor", "decodeURI", "decodeURIComponent",
+        "encodeURI", "encodeURIComponent", "escape", "eval", "isFinite", "isNaN",
+        "isXMLName", "parseFloat", "parseInt", "undefined", "unescape", "uneval"
     ].concat([k.substr(6) for (k in keys(Ci)) if (/^nsIDOM/.test(k))])
      .concat([k.substr(3) for (k in keys(Ci)) if (/^nsI/.test(k))])
      .concat(this.magicalNames)
-     .filter(function (k) k in self.window))),
+     .filter(k => k in self.window))),
 
 }, {
     EVAL_TMP: "__dactyl_eval_tmp",
-    EVAL_EXPORT: "__dactyl_eval_export",
 
     /**
      * A map of argument completion functions for named methods. The
@@ -670,9 +669,9 @@ var JavaScript = Module("javascript", {
      * time they are accessed, so they should be accessed
      * judiciously.
      *
-     * @param {function|function[]} funcs The functions for which to
+     * @param {function|[function]} funcs The functions for which to
      *      install the completers.
-     * @param {function[]} completers An array of completer
+     * @param {[function]} completers An array of completer
      *      functions.
      */
     setCompleter: function (funcs, completers) {
@@ -705,7 +704,8 @@ var JavaScript = Module("javascript", {
 
         modes.addMode("REPL", {
             description: "JavaScript Read Eval Print Loop",
-            bases: [modes.COMMAND_LINE]
+            bases: [modes.COMMAND_LINE],
+            displayName: Class.Memoize(function () this.name)
         });
     },
     commandline: function initCommandLine(dactyl, modules, window) {
@@ -718,12 +718,11 @@ var JavaScript = Module("javascript", {
             },
 
             addOutput: function addOutput(js) {
-                default xml namespace = XHTML;
                 this.count++;
 
                 try {
                     var result = dactyl.userEval(js, this.context);
-                    var xml = util.objectToString(result, true);
+                    var xml = result === undefined ? "" : util.objectToString(result, true);
                 }
                 catch (e) {
                     util.reportError(e);
@@ -731,19 +730,21 @@ var JavaScript = Module("javascript", {
 
                     if (e.fileName)
                         e = util.fixURI(e.fileName) + ":" + e.lineNumber + ": " + e;
-                    xml = <span highlight="ErrorMsg">{e}</span>;
+                    xml = ["span", { highlight: "ErrorMsg" }, e];
                 }
 
                 let prompt = "js" + this.count;
                 Class.replaceProperty(this.context, prompt, result);
 
-                XML.ignoreWhitespace = XML.prettyPrinting = false;
                 let nodes = {};
                 this.rootNode.appendChild(
-                    util.xmlToDom(<e4x>
-                        <div highlight="REPL-E" key="e"><span highlight="REPL-R">{prompt}></span> {js}</div>
-                        <div highlight="REPL-P" key="p">{xml}</div>
-                    </e4x>.elements(), this.document, nodes));
+                    DOM.fromJSON(
+                        [["div", { highlight: "REPL-E", key: "e" },
+                            ["span", { highlight: "REPL-R" },
+                                prompt, ">"], " ", js],
+                         ["div", { highlight: "REPL-P", key: "p" },
+                            xml]],
+                        this.document, nodes));
 
                 this.rootNode.scrollTop += nodes.e.getBoundingClientRect().top
                                          - this.rootNode.getBoundingClientRect().top;
@@ -751,9 +752,8 @@ var JavaScript = Module("javascript", {
 
             count: 0,
 
-            message: Class.memoize(function () {
-                default xml namespace = XHTML;
-                util.xmlToDom(<div highlight="REPL" key="rootNode"/>,
+            message: Class.Memoize(function () {
+                DOM.fromJSON(["div", { highlight: "REPL", key: "rootNode" }],
                               this.document, this);
 
                 return this.rootNode;
@@ -766,18 +766,17 @@ var JavaScript = Module("javascript", {
             init: function init(context) {
                 init.supercall(this);
 
-                let self = this;
-                let sandbox = isinstance(context, ["Sandbox"]);
+                let sandbox = true || isinstance(context, ["Sandbox"]);
 
-                this.context = modules.newContext(context, !sandbox);
+                this.context = modules.newContext(context, !sandbox, "Dactyl REPL Context");
                 this.js = modules.JavaScript();
                 this.js.replContext = this.context;
-                this.js.newContext = function newContext() modules.newContext(self.context, !sandbox);
+                this.js.newContext = () => modules.newContext(this.context, !sandbox, "Dactyl REPL Temp Context");
 
                 this.js.globals = [
-                   [this.context, "REPL Variables"],
-                   [context, "REPL Global"]
-                ].concat(this.js.globals.filter(function ([global]) isPrototypeOf.call(global, context)));
+                   [this.context, /*L*/"REPL Variables"],
+                   [context, /*L*/"REPL Global"]
+                ].concat(this.js.globals.filter(([global]) => isPrototypeOf.call(global, context)));
 
                 if (!isPrototypeOf.call(modules.jsmodules, context))
                     this.js.toplevel = context;
@@ -785,18 +784,19 @@ var JavaScript = Module("javascript", {
                 if (!isPrototypeOf.call(window, context))
                     this.js.window = context;
 
-                if (this.js.globals.slice(2).some(function ([global]) global === context))
+                if (this.js.globals.slice(2).some(([global]) => global === context))
                     this.js.globals.splice(1);
 
                 this.repl = REPL(this.context);
             },
+
             open: function open(context) {
-                this.updatePrompt();
 
                 modules.mow.echo(this.repl);
                 this.widgets.message = null;
 
                 open.superapply(this, arguments);
+                this.updatePrompt();
             },
 
             complete: function complete(context) {
@@ -807,8 +807,10 @@ var JavaScript = Module("javascript", {
 
             mode: modes.REPL,
 
+            get completionList() this.widgets.statusbar.commandline.id,
+
             accept: function accept() {
-                dactyl.trapErrors(function () { this.repl.addOutput(this.command) }, this);
+                dactyl.trapErrors(function () { this.repl.addOutput(this.command); }, this);
 
                 this.completions.cleanup();
                 this.history.save();
@@ -836,8 +838,6 @@ var JavaScript = Module("javascript", {
         commands.add(["javas[cript]", "js"],
             "Evaluate a JavaScript string",
             function (args) {
-                modules.commandline;
-
                 if (args[0] && !args.bang)
                     dactyl.userEval(args[0]);
                 else {
@@ -846,6 +846,7 @@ var JavaScript = Module("javascript", {
                            .open();
                 }
             }, {
+                argCount: "?",
                 bang: true,
                 completer: function (context) modules.completion.javascript(context),
                 hereDoc: true,
@@ -855,8 +856,7 @@ var JavaScript = Module("javascript", {
     mappings: function initMappings(dactyl, modules, window) {
         const { mappings, modes } = modules;
 
-        function bind() mappings.add.apply(mappings,
-                                           [[modes.REPL]].concat(Array.slice(arguments)))
+        function bind(...args) mappings.add.apply(mappings, [[modes.REPL]].concat(args))
 
         bind(["<Return>"], "Accept the current input",
              function ({ self }) { self.accept(); });
@@ -879,7 +879,7 @@ var JavaScript = Module("javascript", {
         bind(["<C-b>", "<PageUp>"], "Scroll up half a page",
              function ({ self }) { self.repl.scrollVertical("pages", -1); });
     },
-    options: function (dactyl, modules, window) {
+    options: function initOptions(dactyl, modules, window) {
         modules.options.add(["jsdebugger", "jsd"],
             "Enable the JavaScript debugger service for use in JavaScript completion",
             "boolean", false, {
@@ -899,4 +899,4 @@ endModule();
 
 } catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
 
-// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: