]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/modules/template.jsm
Import r6923 from upstream hg supporting Firefox up to 22.0a1
[dactyl.git] / common / modules / template.jsm
index 09300d0e888a419ac729d90439ed92d1b53bb29b..47fd94db2214804890fd76f9f85f26bdd788570b 100644 (file)
@@ -1,17 +1,16 @@
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2012 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.
-/* use strict */
+"use strict";
 
 let global = this;
-Components.utils.import("resource://dactyl/bootstrap.jsm");
 defineModule("template", {
     exports: ["Binding", "Template", "template"],
     require: ["util"]
-}, this);
+});
 
-default xml namespace = XHTML;
+lazyRequire("help", ["help"]);
 
 var Binding = Class("Binding", {
     init: function (node, nodes) {
@@ -92,27 +91,6 @@ var Binding = Class("Binding", {
 });
 
 var Template = Module("Template", {
-    add: function add(a, b) a + b,
-    join: function join(c) function (a, b) a + c + b,
-
-    map: function map(iter, func, sep, interruptable) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        if (typeof iter.length == "number") // FIXME: Kludge?
-            iter = array.iterValues(iter);
-        let res = <></>;
-        let n = 0;
-        for each (let i in Iterator(iter)) {
-            let val = func(i, n);
-            if (val == undefined)
-                continue;
-            if (n++ && sep)
-                res += sep;
-            if (interruptable && n % interruptable == 0)
-                util.threadYield(true, true);
-            res += val;
-        }
-        return res;
-    },
 
     bindings: {
         Button: Class("Button", Binding, {
@@ -171,22 +149,42 @@ var Template = Module("Template", {
         })
     },
 
-    bookmarkDescription: function (item, text)
-    <>
-        {
-            !(item.extra && item.extra.length) ? "" :
-            <span highlight="URLExtra">
-                ({
-                    template.map(item.extra, function (e)
-                    <>{e[0]}: <span highlight={e[2]}>{e[1]}</span></>,
-                    <>&#xa0;</>)
-                })&#xa0;</span>
+    map: function map(iter, func, sep, interruptable) {
+        if (typeof iter.length == "number") // FIXME: Kludge?
+            iter = array.iterValues(iter);
+
+        let res = [];
+        let n = 0;
+        for each (let i in Iterator(iter)) {
+            let val = func(i, n);
+            if (val == undefined)
+                continue;
+            if (n++ && sep)
+                res.push(sep);
+            if (interruptable && n % interruptable == 0)
+                util.threadYield(true, true);
+            res.push(val);
         }
-        <a xmlns:dactyl={NS} identifier={item.id == null ? "" : item.id} dactyl:command={item.command || ""}
-           href={item.item.url} highlight="URL">{text || ""}</a>
-    </>,
+        return res;
+    },
+
+
+    bookmarkDescription: function (item, text) [
+        !(item.extra && item.extra.length) ? [] :
+        ["span", { highlight: "URLExtra" },
+            " (",
+            template.map(item.extra, function (e)
+                ["", e[0], ": ",
+                 ["span", { highlight: e[2] }, e[1]]],
+                "\u00a0"),
+            ")\u00a0"],
+        ["a", { identifier: item.id == null ? "" : item.id,
+                "dactyl:command": item.command || "",
+                href: item.item.url, highlight: "URL" },
+            text || ""]
+    ],
 
-    filter: function (str) <span highlight="Filter">{str}</span>,
+    filter: function (str) ["span", { highlight: "Filter" }, str],
 
     completionRow: function completionRow(item, highlightGroup) {
         if (typeof icon == "function")
@@ -201,17 +199,15 @@ var Template = Module("Template", {
             var desc = this.processor[1].call(this, item, item.description);
         }
 
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        // <e4x>
-        return <div highlight={highlightGroup || "CompItem"} style="white-space: nowrap">
-                   <!-- The non-breaking spaces prevent empty elements
-                      - from pushing the baseline down and enlarging
-                      - the row.
-                      -->
-                   <li highlight={"CompResult " + item.highlight}>{text}&#xa0;</li>
-                   <li highlight="CompDesc">{desc}&#xa0;</li>
-               </div>;
-        // </e4x>
+        return ["div", { highlight: highlightGroup || "CompItem", style: "white-space: nowrap" },
+                   /* The non-breaking spaces prevent empty elements
+                    * from pushing the baseline down and enlarging
+                    * the row.
+                    */
+                   ["li", { highlight: "CompResult " + item.highlight },
+                       text, "\u00a0"],
+                   ["li", { highlight: "CompDesc" },
+                       desc, "\u00a0"]];
     },
 
     helpLink: function (token, text, type) {
@@ -225,14 +221,16 @@ var Template = Module("Template", {
             topic = topic.slice(2);
 
         if (help.initialized && !Set.has(help.tags, topic))
-            return <span highlight={type || ""}>{text || token}</span>;
+            return ["span", { highlight: type || ""}, text || token];
 
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
         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" xmlns:dactyl={NS}>{text || topic}</a>;
+        return ["a", { highlight: "InlineHelpLink " + type, tag: topic,
+                       href: "dactyl://help-tag/" + topic,
+                       "dactyl:command": "dactyl.help" },
+                    text || topic];
     },
     HelpLink: function (token) {
         if (!help.initialized)
@@ -245,28 +243,28 @@ var Template = Module("Template", {
             topic = topic.slice(2);
 
         if (help.initialized && !Set.has(help.tags, topic))
-            return <>{token}</>;
+            return token;
 
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
         let tag = (/^'.*'$/.test(token)            ? "o" :
                    /^\[.*\]$|^E\d{3}$/.test(token) ? "t" :
                    /^:\w/.test(token)              ? "ex"  : "k");
 
         topic = topic.replace(/^'(.*)'$/, "$1");
-        return <{tag} xmlns={NS}>{topic}</{tag}>;
+        return [tag, { xmlns: "dactyl" }, topic];
     },
     linkifyHelp: function linkifyHelp(str, help) {
-        let re = util.regexp(<![CDATA[
+        let re = util.regexp(literal(/*
             (?P<pre> [/\s]|^)
             (?P<tag> '[\w-]+' | :(?:[\w-]+!?|!) | (?:._)?<[\w-]+>\w* | \b[a-zA-Z]_(?:[\w[\]]+|.) | \[[\w-;]+\] | E\d{3} )
             (?=      [[\)!,:;./\s]|$)
-        ]]>, "gx");
+        */), "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"]);
+        })(), this[help ? "HelpLink" : "helpLink"]);
     },
 
+
     // Fixes some strange stack rewinds on NS_ERROR_OUT_OF_MEMORY
     // exceptions that we can't catch.
     stringify: function stringify(arg) {
@@ -282,12 +280,12 @@ var Template = Module("Template", {
         }
     },
 
-    _sandbox: Class.Memoize(function () Cu.Sandbox(global, { wantXrays: false })),
+    _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) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
         // some objects like window.JSON or getBrowsers()._browsers need the try/catch
         try {
             let str = this.stringify(arg);
@@ -295,44 +293,44 @@ var Template = Module("Template", {
                 str = util.clip(str, clip);
             switch (arg == null ? "undefined" : typeof arg) {
             case "number":
-                return <span highlight="Number">{str}</span>;
+                return ["span", { highlight: "Number" }, str];
             case "string":
                 if (processStrings)
                     str = str.quote();
-                return <span highlight="String">{str}</span>;
+                return ["span", { highlight: "String" }, str];
             case "boolean":
-                return <span highlight="Boolean">{str}</span>;
+                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, "{ ... }")}</span>;
-                    <>}</>; /* Vim */
+                    return ["span", { highlight: "Function" },
+                                str.replace(/\{(.|\n)*(?:)/g, "{ ... }")];
                 arg = String(arg).replace("/* use strict */ \n", "/* use strict */ ");
-                return <>{arg}</>;
+                return arg;
             case "undefined":
-                return <span highlight="Null">{arg}</span>;
+                return ["span", { highlight: "Null" }, "undefined"];
             case "object":
                 if (arg instanceof Ci.nsIDOMElement)
                     return util.objectToString(arg, !bw);
+                if (arg instanceof util.Magic)
+                    return String(arg);
 
-                // 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</span>);
-                return <span highlight="Object">{str}</span>;
+                    str = template._highlightFilter(str, "\n",
+                                                    function () ["span", { highlight: "NonText" },
+                                                                     "^J"]);
+                return ["span", { highlight: "Object" }, str];
             case "xml":
                 return arg;
             default:
-                return <![CDATA[<unknown type>]]>;
+                return "<unknown type>";
             }
         }
         catch (e) {
-            return <![CDATA[<unknown>]]>;
+            return "<error: " + e + ">";
         }
     },
 
@@ -344,7 +342,6 @@ var Template = Module("Template", {
             if (filter.length == 0)
                 return;
 
-            XML.ignoreWhitespace = XML.prettyPrinting = false;
             let lcstr = String.toLowerCase(str);
             let lcfilter = filter.toLowerCase();
             let start = 0;
@@ -363,14 +360,13 @@ var Template = Module("Template", {
     },
 
     highlightSubstrings: function highlightSubstrings(str, iter, highlight) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        if (typeof str == "xml")
+        if (!isString(str))
             return str;
+
         if (str == "")
-            return <>{str}</>;
+            return DOM.DOMString(str);
 
-        str = String(str).replace(" ", "\u00a0");
-        let s = <></>;
+        let s = [""];
         let start = 0;
         let n = 0, _i;
         for (let [i, length, args] in iter) {
@@ -378,176 +374,138 @@ var Template = Module("Template", {
                 break;
             _i = i;
 
-            XML.ignoreWhitespace = false;
-            s += <>{str.substring(start, i)}</>;
-            s += highlight.apply(this, Array.concat(args || str.substr(i, length)));
+            s.push(str.substring(start, i),
+                   highlight.apply(this, Array.concat(args || str.substr(i, length))));
             start = i + length;
         }
-        return s + <>{str.substr(start)}</>;
+        s.push(str.substr(start));
+        return s;
     },
 
     highlightURL: function highlightURL(str, force) {
         if (force || /^[a-zA-Z]+:\/\//.test(str))
-            return <a highlight="URL" href={str}>{util.losslessDecodeURI(str)}</a>;
+            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><span class="td-strut"/>{text}
-    </>,
+    icon: function (item, text) [
+        ["span", { highlight: "CompIcon" },
+            item.icon ? ["img", { src: item.icon }] : []],
+        ["span", { class: "td-strut" }],
+        text
+    ],
 
     jumps: function jumps(index, elems) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        // <e4x>
-        return <table>
-                <tr style="text-align: left;" highlight="Title">
-                    <th colspan="2">{_("title.Jump")}</th>
-                    <th>{_("title.HPos")}</th>
-                    <th>{_("title.VPos")}</th>
-                    <th>{_("title.Title")}</th>
-                    <th>{_("title.URI")}</th>
-                </tr>
-                {
-                    this.map(Iterator(elems), function ([idx, val])
-                    <tr>
-                        <td class="indicator">{idx == index ? ">" : ""}</td>
-                        <td>{Math.abs(idx - index)}</td>
-                        <td>{val.offset ? val.offset.x : ""}</td>
-                        <td>{val.offset ? val.offset.y : ""}</td>
-                        <td style="width: 250px; max-width: 500px; overflow: hidden;">{val.title}</td>
-                        <td><a href={val.URI.spec} highlight="URL jump-list">{util.losslessDecodeURI(val.URI.spec)}</a></td>
-                    </tr>)
-                }
-            </table>;
-        // </e4x>
+        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) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        // <e4x>
-        return <table>
-                <tr highlight="Title" align="left">
-                    <th>--- {title} ---</th>
-                </tr>
-                {
-                    this.map(opts, function (opt)
-                    <tr>
-                        <td>
-                            <div highlight="Message"
-                            ><span style={opt.isDefault ? "" : "font-weight: bold"}>{opt.pre}{opt.name}</span><span>{opt.value}</span>{
-                                opt.isDefault || opt.default == null ? "" : <span class="extra-info"> (default: {opt.default})</span>
-                            }</div>{
-                                verbose && opt.setFrom ? <div highlight="Message">       Last set from {template.sourceLink(opt.setFrom)}</div> : <></>
-                            }
-                        </td>
-                    </tr>)
-                }
-            </table>;
-        // </e4x>
+        return ["table", {},
+                ["tr", { highlight: "Title", align: "left" },
+                    ["th", {}, "--- " + title + " ---"]],
+                this.map(opts, function (opt)
+                    ["tr", {},
+                        ["td", {},
+                            ["div", { highlight: "Message" },
+                                ["span", { style: opt.isDefault ? "" : "font-weight: bold" },
+                                    opt.pre, opt.name],
+                                ["span", {}, opt.value],
+                                opt.isDefault || opt.default == null ? "" : ["span", { class: "extra-info" }, " (default: ", opt.default, ")"]],
+                            verbose && opt.setFrom ? ["div", { highlight: "Message" },
+                                                         "       Last set from ",
+                                                         template.sourceLink(opt.setFrom)] : ""]])];
     },
 
     sourceLink: function (frame) {
         let url = util.fixURI(frame.filename || "unknown");
         let path = util.urlPath(url);
 
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        return <a xmlns:dactyl={NS} dactyl:command="buffer.viewSource"
-            href={url} path={path} line={frame.lineNumber}
-            highlight="URL">{
-            path + ":" + frame.lineNumber
-        }</a>;
+        return ["a", { "dactyl:command": "buffer.viewSource",
+                        href: url, path: path, line: frame.lineNumber,
+                        highlight: "URL" },
+            path + ":" + frame.lineNumber];
     },
 
     table: function table(title, data, indent) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        let table = // <e4x>
-            <table>
-                <tr highlight="Title" align="left">
-                    <th colspan="2">{title}</th>
-                </tr>
-                {
-                    this.map(data, function (datum)
-                    <tr>
-                       <td style={"font-weight: bold; min-width: 150px; padding-left: " + (indent || "2ex")}>{datum[0]}</td>
-                       <td>{datum[1]}</td>
-                    </tr>)
-                }
-            </table>;
-        // </e4x>
-        if (table.tr.length() > 1)
+        let table = ["table", {},
+            ["tr", { highlight: "Title", align: "left" },
+                ["th", { colspan: "2" }, title]],
+            this.map(data, function (datum)
+                ["tr", {},
+                    ["td", { style: "font-weight: bold; min-width: 150px; padding-left: " + (indent || "2ex") }, datum[0]],
+                    ["td", {}, datum[1]]])];
+
+        if (table[3].length)
             return table;
     },
 
     tabular: function tabular(headings, style, iter) {
+        let self = this;
         // TODO: This might be mind-bogglingly slow. We'll see.
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        // <e4x>
-        return <table>
-                <tr highlight="Title" align="left">
-                {
-                    this.map(headings, function (h)
-                    <th>{h}</th>)
-                }
-                </tr>
-                {
-                    this.map(iter, function (row)
-                    <tr>
-                    {
-                        template.map(Iterator(row), function ([i, d])
-                        <td style={style[i] || ""}>{d}</td>)
-                    }
-                    </tr>)
-                }
-            </table>;
-        // </e4x>
+        return ["table", {},
+            ["tr", { highlight: "Title", align: "left" },
+                this.map(headings, function (h)
+                    ["th", {}, h])],
+            this.map(iter, function (row)
+                ["tr", {},
+                    self.map(Iterator(row), function ([i, d])
+                        ["td", { style: style[i] || "" }, d])])];
     },
 
     usage: function usage(iter, format) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
+        let self = this;
+
         format = format || {};
-        let desc = format.description || function (item) template.linkifyHelp(item.description);
+        let desc = format.description || function (item) self.linkifyHelp(item.description);
         let help = format.help || function (item) item.name;
         function sourceLink(frame) {
-            let source = template.sourceLink(frame);
-            source.@NS::hint = source.text();
+            let source = self.sourceLink(frame);
+            source[1]["dactyl:hint"] = source[2];
             return source;
         }
-        // <e4x>
-        return <table>
-            { format.headings ?
-                <thead highlight="UsageHead">
-                    <tr highlight="Title" align="left">
-                    {
-                        this.map(format.headings, function (h) <th>{h}</th>)
-                    }
-                    </tr>
-                </thead> : ""
-            }
-            { format.columns ?
-                <colgroup>
-                {
-                    this.map(format.columns, function (c) <col style={c}/>)
-                }
-                </colgroup> : ""
-            }
-            <tbody highlight="UsageBody">{
+        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)
-                <tr highlight="UsageItem">
-                    <td style="padding-right: 2em;">
-                        <span highlight="Usage Link">{
-                            let (name = item.name || item.names[0], frame = item.definedAt)
-                                !frame ? name :
-                                    template.helpLink(help(item), name, "Title") +
-                                    <span highlight="LinkInfo" xmlns:dactyl={NS}>{_("io.definedAt")} {sourceLink(frame)}</span>
-                        }</span>
-                    </td>
-                    { item.columns ? template.map(item.columns, function (c) <td>{c}</td>) : "" }
-                    <td>{desc(item)}</td>
-                </tr>)
-            }</tbody>
-        </table>;
-        // </e4x>
+                    // 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)]])]]
     }
 });