-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 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) {
})
});
-var Template = Module("Template", {
- add: function add(a, b) a + b,
- join: function join(c) function (a, b) a + c + b,
+["appendChild", "getAttribute", "insertBefore", "setAttribute"].forEach(function (key) {
+ Object.defineProperty(Binding.prototype, key, {
+ configurable: true,
+ enumerable: false,
+ value: function () this.node[key].apply(this.node, arguments),
+ writable: true
+ });
+});
- map: function map(iter, func, sep, interruptable) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
- if (iter.length) // 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;
- },
+var Template = Module("Template", {
bindings: {
Button: Class("Button", Binding, {
"click": function onClick(event) {
event.preventDefault();
if (this.commandAllowed) {
- if (Set.has(this.target.commands || {}, this.command))
+ if (hasOwnProperty(this.target.commands || {}, this.command))
this.target.commands[this.command].call(this.target);
else
this.target.command(this.command);
},
get commandAllowed() {
- if (Set.has(this.target.allowedCommands || {}, this.command))
+ if (hasOwnProperty(this.target.allowedCommands || {}, this.command))
return this.target.allowedCommands[this.command];
if ("commandAllowed" in this.target)
return this.target.commandAllowed(this.command);
let obj = params.eventTarget;
let events = obj[this.getAttribute("events") || "events"];
- if (Set.has(events, "input"))
+ if (hasOwnProperty(events, "input"))
events["dactyl-input"] = events["input"];
for (let [event, handler] in Iterator(events))
- node.addEventListener(event, util.wrapCallback(obj.closure(handler), true), false);
+ node.addEventListener(event, util.wrapCallback(handler.bind(obj), true), false);
}
})
},
- 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></>,
- <> </>)
- }) </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 (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;
+ },
- filter: function (str) <span highlight="Filter">{str}</span>,
+ bookmarkDescription: function (item, text) [
+ !(item.extra && item.extra.length) ? [] :
+ ["span", { highlight: "URLExtra" },
+ " (",
+ template.map(item.extra, 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],
completionRow: function completionRow(item, highlightGroup) {
if (typeof icon == "function")
var desc = this.processor[1].call(this, item, item.description);
}
- XML.ignoreWhitespace = false; 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} </li>
- <li highlight="CompDesc">{desc} </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) {
else if (/^n_/.test(topic))
topic = topic.slice(2);
- if (help.initialized && !Set.has(help.tags, topic))
- return <span highlight={type || ""}>{text || token}</span>;
+ if (help.initialized && !hasOwnProperty(help.tags, topic))
+ return ["span", { highlight: type || ""}, text || token];
- XML.ignoreWhitespace = false; 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)
else if (/^n_/.test(topic))
topic = topic.slice(2);
- if (help.initialized && !Set.has(help.tags, topic))
- return <>{token}</>;
+ if (help.initialized && !hasOwnProperty(help.tags, topic))
+ return token;
- XML.ignoreWhitespace = false; 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} )
+ (?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) {
- 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(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 = false; XML.prettyPrinting = false;
// some objects like window.JSON or getBrowsers()._browsers need the try/catch
try {
- let str = this.stringify(arg);
+ let str = String(arg);
if (clip)
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 + ">";
}
},
return this.highlightSubstrings(str, (function () {
if (filter.length == 0)
return;
+
let lcstr = String.toLowerCase(str);
let lcfilter = filter.toLowerCase();
let start = 0;
},
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) {
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 = false; 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), ([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 = false; 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, 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 = false; 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 = false; 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, 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) {
// TODO: This might be mind-bogglingly slow. We'll see.
- XML.ignoreWhitespace = false; 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, (row) =>
+ ["tr", {},
+ this.map(Iterator(row), ([i, d]) =>
+ ["td", { style: style[i] || "" }, d])])];
},
- usage: function usage(iter, format) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
- format = format || {};
- let desc = format.description || function (item) template.linkifyHelp(item.description);
- let help = format.help || function (item) item.name;
- function sourceLink(frame) {
- let source = template.sourceLink(frame);
- source.@NS::hint = source.text();
+ usage: function usage(iter, format={}) {
+ let desc = format.description || (item => this.linkifyHelp(item.description));
+ let help = format.help || (item => item.name);
+ let sourceLink = (frame) => {
+ let source = this.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">{
- 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>
+ return ["table", {},
+ format.headings ?
+ ["thead", { highlight: "UsageHead" },
+ ["tr", { highlight: "Title", align: "left" },
+ this.map(format.headings, (h) => ["th", {}, h])]] :
+ [],
+ format.columns ?
+ ["colgroup", {},
+ this.map(format.columns, (c) => ["col", { style: c }])] :
+ [],
+ ["tbody", { highlight: "UsageBody" },
+ this.map(iter, (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 :
+ [this.helpLink(help(item), name, "Title"),
+ ["span", { highlight: "LinkInfo" },
+ _("io.definedAt"), " ",
+ sourceLink(frame)]]]],
+ item.columns ? this.map(item.columns, (c) => ["td", {}, c]) : [],
+ ["td", {}, desc(item)]])]];
}
});
endModule();
-// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: