diff --git a/common/content/commandline.js b/common/content/commandline.js index 4b0d104e..4144f368 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -1582,7 +1582,7 @@ var CommandLine = Module("commandline", { else if (callable(arg)) arg = String.replace(arg, "/* use strict */ \n", "/* use strict */ "); else if (!isString(arg) && useColor) - arg = template.highlight(arg); + arg = template_.highlight(arg); return arg; } }, { diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 12fa5836..232dba67 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -256,7 +256,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { results = results.filter(function (item) filters.every(function (re) keys(item).some(re.closure.test))); commandline.commandOutput( - template.usage(results, params.format)); + template_.usage(results, params.format)); }, { argCount: "*", @@ -1166,7 +1166,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { error.message = prefix + error.message; if (error.message) - dactyl.echoerr(template.linkifyHelp(error.message)); + dactyl.echoerr(template_.linkifyHelp(error.message)); else dactyl.beep(); diff --git a/common/content/mappings.js b/common/content/mappings.js index 8f7b298b..9b9efbf7 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -765,14 +765,13 @@ var Mappings = Module("mappings", { } }, format: { - description: function (map) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <> - {options.get("passkeys").has(map.name) - ? - ({template.linkifyHelp(_("option.passkeys.passedBy"))}) - - : <>} - {template.linkifyHelp(map.description + (map.rhs ? ": " + map.rhs : ""))} - ), + description: function (map) [ + options.get("passkeys").has(map.name) + ? ["span", { highlight: "URLExtra" }, + "(", template_.linkifyHelp(_("option.passkeys.passedBy")), ")"] + : [], + template_.linkifyHelp(map.description + (map.rhs ? ": " + map.rhs : "")) + ], help: function (map) let (char = array.compact(map.modes.map(function (m) m.char))[0]) char === "n" ? map.name : char ? char + "_" + map.name : "", headings: ["Command", "Mode", "Group", "Description"] diff --git a/common/content/marks.js b/common/content/marks.js index d179cd5a..e616608a 100644 --- a/common/content/marks.js +++ b/common/content/marks.js @@ -265,7 +265,7 @@ var Marks = Module("marks", { } commandline.commandOutput( - template.tabular( + template_.tabular( ["Mark", "HPos", "VPos", "File"], ["", "text-align: right", "text-align: right", "color: green"], ([name, diff --git a/common/content/quickmarks.js b/common/content/quickmarks.js index c4f694f8..f91a6b4a 100644 --- a/common/content/quickmarks.js +++ b/common/content/quickmarks.js @@ -113,7 +113,7 @@ var QuickMarks = Module("quickmarks", { dactyl.assert(marks.length >= 0, _("quickmark.noMatching", filter.quote())); } - commandline.commandOutput(template.tabular(["QuickMark", "URL"], [], + commandline.commandOutput(template_.tabular(["QuickMark", "URL"], [], ([mark, quickmarks._qmarks.get(mark)] for ([k, mark] in Iterator(marks))))); } }, { diff --git a/common/content/tabs.js b/common/content/tabs.js index e6fc862a..a377678e 100644 --- a/common/content/tabs.js +++ b/common/content/tabs.js @@ -35,9 +35,9 @@ var Tabs = Module("tabs", { tabs.switchTo(event.originalTarget.getAttribute("identifier")); }; - this.tabBinding = styles.system.add("tab-binding", "chrome://browser/content/browser.xul", String.replace(literal(/* + this.tabBinding = styles.system.add("tab-binding", "chrome://browser/content/browser.xul", literal(/* xul|tab { -moz-binding: url(chrome://dactyl/content/bindings.xml#tab) !important; } - */), /tab-./g, function (m) config.OS.isMacOSX ? "tab-mac" : m), + */).replace(/tab-./g, function (m) config.OS.isMacOSX ? "tab-mac" : m), false, true); this.timeout(function () { diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm index cc5b805b..8ff78dc7 100644 --- a/common/modules/addons.jsm +++ b/common/modules/addons.jsm @@ -13,7 +13,7 @@ defineModule("addons", { }); this.lazyRequire("completion", ["completion"]); -lazyRequire("template", ["template"]); +lazyRequire("template", ["template", "template_"]); var callResult = function callResult(method) { let args = Array.slice(arguments, 1); @@ -220,7 +220,7 @@ var Addon = Class("Addon", { DOM(node).append(isArray(xml) || isXML(xml) ? xml : DOM.DOMString(xml)); } - update("name", template.icon({ icon: this.iconURL }, this.name)); + update("name", template_.icon({ icon: this.iconURL }, this.name)); this.nodes.version.textContent = this.version; update("status", this.statusInfo); this.nodes.description.textContent = this.description; diff --git a/common/modules/buffer.jsm b/common/modules/buffer.jsm index ffc839f1..348c4fea 100644 --- a/common/modules/buffer.jsm +++ b/common/modules/buffer.jsm @@ -908,11 +908,11 @@ var Buffer = Module("Buffer", { let file = this.win.location.pathname.split("/").pop() || _("buffer.noName"); let title = this.win.document.title || _("buffer.noTitle"); - let info = template.map( + let info = template_.map( (sections || options["pageinfo"]) .map(function (opt) Buffer.pageInfo[opt].action.call(self)), function (res) res && iter(res).join(", ") || undefined, - ", "); + ", ").join(""); if (bookmarkcache.isBookmarked(this.URL)) info += ", " + _("buffer.bookmarked"); @@ -2478,11 +2478,11 @@ Buffer.addPageInfoSection("g", "General Info", function (verbose) { } yield ["Title", doc.title]; - yield ["URL", template.highlightURL(doc.location.href, true)]; + yield ["URL", template_.highlightURL(doc.location.href, true)]; let ref = "referrer" in doc && doc.referrer; if (ref) - yield ["Referrer", template.highlightURL(ref, true)]; + yield ["Referrer", template_.highlightURL(ref, true)]; if (pageSize[0]) yield ["File Size", pageSize[1] ? pageSize[1] + " (" + pageSize[0] + ")" @@ -2502,7 +2502,8 @@ Buffer.addPageInfoSection("m", "Meta Tags", function (verbose) { // get meta tag data, sort and put into pageMeta[] let metaNodes = this.focusedFrame.document.getElementsByTagName("meta"); - return Array.map(metaNodes, function (node) [(node.name || node.httpEquiv), template.highlightURL(node.content)]) + return Array.map(metaNodes, function (node) [(node.name || node.httpEquiv), + template_.highlightURL(node.content)]) .sort(function (a, b) util.compareIgnoreCase(a[0], b[0])); }); diff --git a/common/modules/commands.jsm b/common/modules/commands.jsm index 8fdd4b43..8fd8534f 100644 --- a/common/modules/commands.jsm +++ b/common/modules/commands.jsm @@ -1704,7 +1704,8 @@ var Commands = Module("commands", { iterate: function (args) commands.iterator().map(function (cmd) ({ __proto__: cmd, columns: [ - cmd.hive == commands.builtin ? "" : {cmd.hive.name} + cmd.hive == commands.builtin ? "" : ["span", { highlight: "Object", style: "padding-right: 1em;" }, + cmd.hive.name] ] })), iterateIndex: function (args) let (tags = help.tags) diff --git a/common/modules/dom.jsm b/common/modules/dom.jsm index ae52068f..c1ce2148 100644 --- a/common/modules/dom.jsm +++ b/common/modules/dom.jsm @@ -1390,7 +1390,9 @@ var DOM = Class("DOM", { DOMString: function DOMString(val) ({ __proto__: DOMString.prototype, - toDOM: function toDOM(doc) doc.createTextNode(val) + toDOM: function toDOM(doc) doc.createTextNode(val), + + toString: function () val }), /** @@ -1563,9 +1565,12 @@ var DOM = Class("DOM", { return doc.createTextNode(args); if (isXML(args)) return DOM.fromXML(args, doc, nodes); + if (isObject(args) && "toDOM" in args) + return args.toDOM(doc, namespaces, nodes); + if (args instanceof Ci.nsIDOMNode) + return args; let [name, attr] = args; - attr = attr || {}; if (!isString(name) || args.length == 0 || name === "") { var frag = doc.createDocumentFragment(); @@ -1579,8 +1584,7 @@ var DOM = Class("DOM", { return frag; } - if (isObject(name) && "toDOM" in name) - return name.toDOM(doc, namespaces, nodes); + attr = attr || {}; function parseNamespace(name) { var m = /^(?:(.*):)?(.*)$/.exec(name); @@ -1616,7 +1620,7 @@ var DOM = Class("DOM", { elem.setAttribute(key, val); } args.forEach(function(e) { - elem.appendChild(e instanceof Ci.nsIDOMNode ? e : tag(e, namespaces)); + elem.appendChild(tag(e, namespaces)); }); if ("highlight" in attr) diff --git a/common/modules/highlight.jsm b/common/modules/highlight.jsm index 3c5e4999..097f8c51 100644 --- a/common/modules/highlight.jsm +++ b/common/modules/highlight.jsm @@ -241,6 +241,7 @@ var Highlights = Module("Highlight", { \s* (?P .*) $ */), "x"), + // /** * Bulk loads new CSS rules, in the format of, @@ -350,7 +351,7 @@ var Highlights = Module("Highlight", { "text-align: center"], ([h.class, ["span", { style: "text-align: center; line-height: 1em;" + h.value + style }, "XXX"], - template_.map(h.extends, function (s) template.highlight(s), ","), + template_.map(h.extends, function (s) template_.highlight(s), ","), template_.highlightRegexp(h.value, /\b[-\w]+(?=:)|\/\*.*?\*\//g, function (match) ["span", { highlight: match[0] == "/" ? "Comment" : "Key" }, match]) ] diff --git a/common/modules/io.jsm b/common/modules/io.jsm index 5095b520..da0ddf05 100644 --- a/common/modules/io.jsm +++ b/common/modules/io.jsm @@ -18,7 +18,7 @@ lazyRequire("config", ["config"]); lazyRequire("contexts", ["Contexts", "contexts"]); lazyRequire("storage", ["File", "storage"]); lazyRequire("styles", ["styles"]); -lazyRequire("template", ["template"]); +lazyRequire("template", ["template", "template_"]); // TODO: why are we passing around strings rather than file objects? /** @@ -867,7 +867,7 @@ unlet s:cpo_save dactyl.echomsg(_("command.scriptnames.none")); else modules.commandline.commandOutput( - template.tabular(["", "Filename"], ["text-align: right; padding-right: 1em;"], + template_.tabular(["", "Filename"], ["text-align: right; padding-right: 1em;"], ([i + 1, file] for ([i, file] in Iterator(names))))); }, diff --git a/common/modules/options.jsm b/common/modules/options.jsm index d4929805..aaef95b7 100644 --- a/common/modules/options.jsm +++ b/common/modules/options.jsm @@ -19,7 +19,7 @@ lazyRequire("commands", ["Commands"]); lazyRequire("completion", ["CompletionContext"]); lazyRequire("prefs", ["prefs"]); lazyRequire("styles", ["Styles"]); -lazyRequire("template", ["template"]); +lazyRequire("template", ["template", "template_"]); /** @scope modules */ @@ -859,7 +859,7 @@ var Options = Module("options", { isDefault: opt.isDefault, default: opt.stringDefaultValue, pre: "\u00a0\u00a0", // Unicode nonbreaking space. - value: <> + value: [] }; if (filter && !filter(opt)) @@ -873,16 +873,19 @@ var Options = Module("options", { option.default = (opt.defaultValue ? "" : "no") + opt.name; } else if (isArray(opt.value) && opt.type != "charlist") - option.value = <>={template.map(opt.value, - function (v) template.highlight(String(v)), - <>, )}; + option.value = ["", "=", + template_.map(opt.value, + function (v) template_.highlight(String(v)), + ["", ",", + ["span", { style: "width: 0; display: inline-block" }, " "]])]; else - option.value = <>={template.highlight(opt.stringValue)}; + option.value = ["", "=", template_.highlight(opt.stringValue)]; yield option; } }; - modules.commandline.commandOutput(template.options("Options", opts.call(this), this["verbose"] > 0)); + modules.commandline.commandOutput( + template_.options("Options", opts.call(this), this["verbose"] > 0)); }, cleanup: function cleanup() { @@ -1074,11 +1077,13 @@ var Options = Module("options", { index: "option", iterate: function (args) options, format: { - description: function (opt) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <> - {opt.scope == Option.SCOPE_LOCAL - ? ({_("option.bufferLocal")}) : ""} - {template.linkifyHelp(opt.description)} - ), + description: function (opt) [ + opt.scope == Option.SCOPE_LOCAL + ? ["span", { highlight: "URLExtra" }, + "(" + _("option.bufferLocal") + ")"] + : "", + template_.linkifyHelp(opt.description) + ], help: function (opt) "'" + opt.name + "'" } }); diff --git a/common/modules/prefs.jsm b/common/modules/prefs.jsm index 6467de59..5f99ed62 100644 --- a/common/modules/prefs.jsm +++ b/common/modules/prefs.jsm @@ -169,7 +169,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) if (saved == null && curval != defval || saved != null && curval != saved) { let msg = _("pref.safeSet.warnChanged", name); if (message) - msg = template.linkifyHelp(msg + " " + message); + msg = template_.linkifyHelp(msg + " " + message); util.dactyl.warn(msg); } }, @@ -404,7 +404,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) let option = { isDefault: !userValue, default: this.defaults.get(pref, null), - value: ["", "=", template.highlight(value, true, 100)], + value: ["", "=", template_.highlight(value, true, 100)], name: pref, pre: "\u00a0\u00a0" // Unicode nonbreaking space. }; diff --git a/common/modules/template.jsm b/common/modules/template.jsm index e2045438..3e350167 100644 --- a/common/modules/template.jsm +++ b/common/modules/template.jsm @@ -572,6 +572,132 @@ var Template_ = Module("Template_", { }, + helpLink: function (token, text, type) { + if (!help.initialized) + util.dactyl.initHelp(); + + let topic = token; // FIXME: Evil duplication! + if (/^\[.*\]$/.test(topic)) + topic = topic.slice(1, -1); + else if (/^n_/.test(topic)) + topic = topic.slice(2); + + if (help.initialized && !Set.has(help.tags, topic)) + return {text || token}; + + type = type || (/^'.*'$/.test(token) ? "HelpOpt" : + /^\[.*\]$|^E\d{3}$/.test(token) ? "HelpTopic" : + /^:\w/.test(token) ? "HelpEx" : "HelpKey"); + + return ["a", { highlight: "InlineHelpLink " + type, tag: topic, + href: "dactyl://help-tag/" + topic, + "dactyl:command": "dactyl.help" }, + text || topic]; + }, + HelpLink: function (token) { + if (!help.initialized) + util.dactyl.initHelp(); + + let topic = token; // FIXME: Evil duplication! + if (/^\[.*\]$/.test(topic)) + topic = topic.slice(1, -1); + else if (/^n_/.test(topic)) + topic = topic.slice(2); + + if (help.initialized && !Set.has(help.tags, topic)) + return token; + + let tag = (/^'.*'$/.test(token) ? "o" : + /^\[.*\]$|^E\d{3}$/.test(token) ? "t" : + /^:\w/.test(token) ? "ex" : "k"); + + topic = topic.replace(/^'(.*)'$/, "$1"); + return [tag, { xmlns: "dactyl" }, topic]; + }, + linkifyHelp: function linkifyHelp(str, help) { + let re = util.regexp(literal(/* + (?P
 [/\s]|^)
+            (?P '[\w-]+' | :(?:[\w-]+!?|!) | (?:._)?<[\w-]+>\w* | \b[a-zA-Z]_(?:[\w[\]]+|.) | \[[\w-;]+\] | E\d{3} )
+            (?=      [[\)!,:;./\s]|$)
+        */), "gx");
+        return this.highlightSubstrings(str, (function () {
+            for (let res in re.iterate(str))
+                yield [res.index + res.pre.length, res.tag.length];
+        })(), template[help ? "HelpLink" : "helpLink"]);
+    },
+
+
+    // Fixes some strange stack rewinds on NS_ERROR_OUT_OF_MEMORY
+    // exceptions that we can't catch.
+    stringify: function stringify(arg) {
+        if (!callable(arg))
+            return String(arg);
+
+        try {
+            this._sandbox.arg = arg;
+            return Cu.evalInSandbox("String(arg)", this._sandbox);
+        }
+        finally {
+            this._sandbox.arg = null;
+        }
+    },
+
+    _sandbox: Class.Memoize(function () Cu.Sandbox(Cu.getGlobalForObject(global),
+                                                   { wantXrays: false })),
+
+    // if "processStrings" is true, any passed strings will be surrounded by " and
+    // any line breaks are displayed as \n
+    highlight: function highlight(arg, processStrings, clip, bw) {
+        // some objects like window.JSON or getBrowsers()._browsers need the try/catch
+        try {
+            let str = this.stringify(arg);
+            if (clip)
+                str = util.clip(str, clip);
+            switch (arg == null ? "undefined" : typeof arg) {
+            case "number":
+                return ["span", { highlight: "Number" }, str];
+            case "string":
+                if (processStrings)
+                    str = str.quote();
+                return ["span", { highlight: "String" }, str];
+            case "boolean":
+                return ["span", { highlight: "Boolean" }, str];
+            case "function":
+                if (arg instanceof Ci.nsIDOMElement) // wtf?
+                    return util.objectToString(arg, !bw);
+
+                str = str.replace("/* use strict */ \n", "/* use strict */ ");
+                if (processStrings)
+                    return ["span", { highlight: "Function" },
+                                str.replace(/\{(.|\n)*(?:)/g, "{ ... }")];
+                arg = String(arg).replace("/* use strict */ \n", "/* use strict */ ");
+                return arg;
+            case "undefined":
+                return ["span", { highlight: "Null" }, arg];
+            case "object":
+                if (arg instanceof Ci.nsIDOMElement)
+                    return util.objectToString(arg, !bw);
+
+                // for java packages value.toString() would crash so badly
+                // that we cannot even try/catch it
+                if (/^\[JavaPackage.*\]$/.test(arg))
+                    return "[JavaPackage]";
+                if (processStrings && false)
+                    str = template._highlightFilter(str, "\n",
+                                                    function () ["span", { highlight: "NonText" },
+                                                                     "^J"]);
+                return ["span", { highlight: "Object" }, str];
+            case "xml":
+                return arg;
+            default:
+                return "";
+            }
+        }
+        catch (e) {
+            return "";
+        }
+    },
+
     highlightFilter: function highlightFilter(str, filter, highlight, isURI) {
         if (isURI)
             str = util.losslessDecodeURI(str);
@@ -621,6 +747,42 @@ var Template_ = Module("Template_", {
         return s;
     },
 
+    highlightURL: function highlightURL(str, force) {
+        if (force || /^[a-zA-Z]+:\/\//.test(str))
+            return ["a", { highlight: "URL", href: str },
+                        util.losslessDecodeURI(str)];
+        else
+            return str;
+    },
+
+    icon: function (item, text) [
+        ["span", { highlight: "CompIcon" },
+            item.icon ? ["img", { src: item.icon }] : []],
+        ["span", { class: "td-strut" }],
+        text
+    ],
+
+    jumps: function jumps(index, elems) {
+        return ["table", {},
+                ["tr", { style: "text-align: left;", highlight: "Title" },
+                    ["th", { colspan: "2" }, _("title.Jump")],
+                    ["th", {}, _("title.HPos")],
+                    ["th", {}, _("title.VPos")],
+                    ["th", {}, _("title.Title")],
+                    ["th", {}, _("title.URI")]],
+                this.map(Iterator(elems), function ([idx, val])
+                    ["tr", {},
+                        ["td", { class: "indicator" }, idx == index ? ">" : ""],
+                        ["td", {}, Math.abs(idx - index)],
+                        ["td", {}, val.offset ? val.offset.x : ""],
+                        ["td", {}, val.offset ? val.offset.y : ""],
+                        ["td", { style: "width: 250px; max-width: 500px; overflow: hidden;" }, val.title],
+                        ["td", {},
+                            ["a", { href: val.URI.spec, highlight: "URL jump-list" },
+                                util.losslessDecodeURI(val.URI.spec)]]])];
+    },
+
+
     options: function options(title, opts, verbose) {
         return ["table", {},
                 ["tr", { highlight: "Title", align: "left" },
@@ -638,6 +800,16 @@ var Template_ = Module("Template_", {
                                                          template.sourceLink(opt.setFrom)] : ""]])];
     },
 
+    sourceLink: function (frame) {
+        let url = util.fixURI(frame.filename || "unknown");
+        let path = util.urlPath(url);
+
+        return ["a", { "dactyl:command": "buffer.viewSource",
+                        href: url, path: path, line: frame.lineNumber,
+                        highlight: "URL" },
+            path + ":" + frame.lineNumber];
+    },
+
     table: function table(title, data, indent) {
         let table = ["table", {},
             ["tr", { highlight: "Title", align: "left" },
@@ -663,6 +835,43 @@ var Template_ = Module("Template_", {
                     self.map(Iterator(row), function ([i, d])
                         ["td", { style: style[i] || "" }, d])])];
     },
+
+    usage: function usage(iter, format) {
+        let self = this;
+
+        format = format || {};
+        let desc = format.description || function (item) self.linkifyHelp(item.description);
+        let help = format.help || function (item) item.name;
+        function sourceLink(frame) {
+            let source = self.sourceLink(frame);
+            source[1]["dactyl:hint"] = source[2];
+            return source;
+        }
+        return ["table", {},
+            format.headings ?
+                ["thead", { highlight: "UsageHead" },
+                    ["tr", { highlight: "Title", align: "left" },
+                        this.map(format.headings, function (h) ["th", {}, h])]] :
+                [],
+            format.columns ?
+                ["colgroup", {},
+                    this.map(format.columns, function (c) ["col", { style: c }])] :
+                [],
+            ["tbody", { highlight: "UsageBody" },
+                this.map(iter, function (item)
+                    // Urgh.
+                    let (name = item.name || item.names[0], frame = item.definedAt)
+                        ["tr", { highlight: "UsageItem" },
+                            ["td", { style: "padding-right: 2em;" },
+                                ["span", { highlight: "Usage Link" },
+                                    !frame ? name :
+                                        [self.helpLink(help(item), name, "Title"),
+                                         ["span", { highlight: "LinkInfo" },
+                                            _("io.definedAt"), " ",
+                                            sourceLink(frame)]]]],
+                            item.columns ? self.map(item.columns, function (c) ["td", {}, c]) : [],
+                            ["td", {}, desc(item)]])]]
+    }
 });
 
 endModule();