diff --git a/common/content/commandline.js b/common/content/commandline.js
index b72ef0c3..5e2b9bae 100644
--- a/common/content/commandline.js
+++ b/common/content/commandline.js
@@ -14,56 +14,45 @@ var CommandWidgets = Class("CommandWidgets", {
init: function init() {
let s = "dactyl-statusline-field-";
- XML.ignoreWhitespace = true;
overlay.overlayWindow(window, {
objects: {
eventTarget: commandline
},
- append:
-
-
-
-
+ append: [
+ ["vbox", { id: config.ids.commandContainer, xmlns: "xul" },
+ ["vbox", { class: "dactyl-container", hidden: "false", collapsed: "true" },
+ ["iframe", { class: "dactyl-completions", id: "dactyl-completions-dactyl-commandline",
+ src: "dactyl://content/buffer.xhtml", contextmenu: "dactyl-contextmenu",
+ flex: "1", hidden: "false", collapsed: "false",
+ highlight: "Events", events: "mowEvents" }]],
-
-
-
-
+ ["stack", { orient: "horizontal", align: "stretch", class: "dactyl-container",
+ id: "dactyl-container", highlight: "CmdLine CmdCmdLine" },
+ ["textbox", { class: "plain", id: "dactyl-strut", flex: "1", crop: "end", collapsed: "true" }],
+ ["textbox", { class: "plain", id: "dactyl-mode", flex: "1", crop: "end" }],
+ ["textbox", { class: "plain", id: "dactyl-message", flex: "1", readonly: "true" }],
-
-
-
-
-
+ ["hbox", { id: "dactyl-commandline", hidden: "false", foo: "bar", class: "dactyl-container", highlight: "Normal CmdNormal", collapsed: "true" },
+ ["label", { id: "dactyl-commandline-prompt", class: "dactyl-commandline-prompt plain", flex: "0", crop: "end", value: "", collapsed: "true" }],
+ ["textbox", { id: "dactyl-commandline-command", class: "dactyl-commandline-command plain", flex: "1", type: "input", timeout: "100",
+ highlight: "Events" }]]],
-
-
-
-
+ ["vbox", { class: "dactyl-container", hidden: "false", collapsed: "false", highlight: "CmdLine" },
+ ["textbox", { id: "dactyl-multiline-input", class: "plain", flex: "1", rows: "1", hidden: "false", collapsed: "true",
+ multiline: "true", highlight: "Normal Events", events: "multilineInputEvents" }]]],
-
-
-
-
-
-
- .elements(),
+ ["stack", { id: "dactyl-statusline-stack", xmlns: "xul" },
+ ["hbox", { id: s + "commandline", hidden: "false", class: "dactyl-container", highlight: "Normal StatusNormal", collapsed: "true" },
+ ["label", { id: s + "commandline-prompt", class: "dactyl-commandline-prompt plain", flex: "0", crop: "end", value: "", collapsed: "true" }],
+ ["textbox", { id: s + "commandline-command", class: "dactyl-commandline-command plain", flex: "1", type: "text", timeout: "100",
+ highlight: "Events", }]]]],
- before:
-
-
-
-
-
- .elements()
+ before: [
+ ["toolbar", { id: statusline.statusBar.id, xmlns: "xul" },
+ ["vbox", { id: "dactyl-completions-" + s + "commandline-container", class: "dactyl-container", hidden: "false", collapsed: "true" },
+ ["iframe", { class: "dactyl-completions", id: "dactyl-completions-" + s + "commandline", src: "dactyl://content/buffer.xhtml",
+ contextmenu: "dactyl-contextmenu", flex: "1", hidden: "false", collapsed: "false", highlight: "Events",
+ events: "mowEvents" }]]]],
});
this.elements = {};
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index 179975f7..e4012eab 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -304,20 +304,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
bell: document.getElementById("dactyl-bell"),
strut: document.getElementById("dactyl-bell-strut")
};
- XML.ignoreWhitespace = true;
if (!elems.bell)
overlay.overlayWindow(window, {
objects: elems,
- prepend: <>
-
-
-
- >,
- append: <>
-
-
-
- >
+ prepend: [
+ ["window", { id: document.documentElement.id, xmlns: "xul" },
+ ["hbox", { style: "display: none", highlight: "Bell", id: "dactyl-bell", key: "bell" }]]],
+ append: [
+ ["window", { id: document.documentElement.id, xmlns: "xul" },
+ ["hbox", { style: "display: none", highlight: "Bell", id: "dactyl-bell-strut", key: "strut" }]]]
}, elems);
elems.bell.style.height = window.innerHeight + "px";
diff --git a/common/content/events.js b/common/content/events.js
index b003a0f2..5408391d 100644
--- a/common/content/events.js
+++ b/common/content/events.js
@@ -4,7 +4,7 @@
//
// 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";
/** @scope modules */
@@ -102,17 +102,14 @@ var Events = Module("events", {
init: function () {
this.keyEvents = [];
- XML.ignoreWhitespace = true;
overlay.overlayWindow(window, {
- append:
-
-
-
-
-
- .elements()
+ append: [
+ ["window", { id: document.documentElement.id, xmlns: "xul" },
+ // http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands
+ ["commandset", { id: "dactyl-onfocus", commandupdater: "true", events: "focus",
+ commandupdate: this.closure.onFocusChange }],
+ ["commandset", { id: "dactyl-onselect", commandupdater: "true", events: "select",
+ commandupdate: this.closure.onSelectionChange }]]]
});
this._fullscreen = window.fullScreen;
diff --git a/common/content/mow.js b/common/content/mow.js
index afebff6b..36d271c9 100644
--- a/common/content/mow.js
+++ b/common/content/mow.js
@@ -41,38 +41,28 @@ var MOW = Module("mow", {
html|html > xul|scrollbar { visibility: collapse !important; }",
true);
- XML.ignoreWhitespace = true;
overlay.overlayWindow(window, {
objects: {
eventTarget: this
},
- append:
-
-
-
-
-
-
-
-
-
-
-
+ append: [
+ ["window", { id: document.documentElement.id, xmlns: "xul" },
+ ["popupset", {},
+ ["menupopup", { id: "dactyl-contextmenu", highlight: "Events", events: "contextEvents" },
+ ["menuitem", { id: "dactyl-context-copylink", label: _("mow.contextMenu.copyLink"),
+ "dactyl:group": "link", oncommand: "goDoCommand('cmd_copyLink');" }],
+ ["menuitem", { id: "dactyl-context-copypath", label: _("mow.contextMenu.copyPath"),
+ "dactyl:group": "link path", oncommand: "dactyl.clipboardWrite(document.popupNode.getAttribute('path'));" }],
+ ["menuitem", { id: "dactyl-context-copy", label: _("mow.contextMenu.copy"),
+ "dactyl:group": "selection", command: "cmd_copy" }],
+ ["menuitem", { id: "dactyl-context-selectall", label: _("mow.contextMenu.selectAll"),
+ command: "cmd_selectAll" }]]]],
+
+ ["vbox", { id: config.ids.commandContainer, xmlns: "xul" },
+ ["vbox", { class: "dactyl-container", id: "dactyl-multiline-output-container", hidden: "false", collapsed: "true" },
+ ["iframe", { id: "dactyl-multiline-output", src: "dactyl://content/buffer.xhtml",
+ flex: "1", hidden: "false", collapsed: "false",
+ contextmenu: "dactyl-contextmenu", highlight: "Events" }]]]]
});
},
@@ -125,7 +115,7 @@ var MOW = Module("mow", {
if (isObject(data) && !isinstance(data, _)) {
this.lastOutput = null;
- var output = DOM(
,
+ var output = DOM(["div", { style: "white-space: nowrap", highlight: highlightGroup }],
this.document);
data.document = this.document;
try {
diff --git a/common/content/statusline.js b/common/content/statusline.js
index 5b7d2a88..4152f8cf 100644
--- a/common/content/statusline.js
+++ b/common/content/statusline.js
@@ -22,7 +22,10 @@ var StatusLine = Module("statusline", {
#addon-bar > xul|toolbarspring { visibility: collapse; }
]]>);
- overlay.overlayWindow(window, { append: <>> });
+ overlay.overlayWindow(window, {
+ append: [
+ ["statusbar", { id: "status-bar", ordinal: "0" }]]
+ });
highlight.loadCSS(util.compileMacro(
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ;
+ let prepend = [
+ ["button", { id: "appmenu-button", label: "", image: "chrome://branding/content/icon16.png", highlight: "AppmenuButton", xmlns: "xul" }],
+ ["toolbarbutton", { id: "appmenu-toolbar-button", label: "", image: "chrome://branding/content/icon16.png" }],
+ ["statusbar", { id: "status-bar", highlight: "StatusLine", xmlns: "xul" },
+ //
+ ["hbox", { key: "container", hidden: "false", align: "center", flex: "1" },
+ ["stack", { orient: "horizontal", align: "stretch", flex: "1", highlight: "CmdLine StatusCmdLine", class: "dactyl-container" },
+ ["hbox", { highlight: "CmdLine StatusCmdLine", class: "dactyl-container" },
+ ["label", { key: "mode", crop: "end", class: "plain", collapsed: "true" }],
+ ["stack", { id: "dactyl-statusline-stack", flex: "1", highlight: "CmdLine StatusCmdLine", class: "dactyl-container" },
+ ["textbox", { key: "url", crop: "end", flex: "1", style: "background: transparent;", class: "plain dactyl-status-field-url",
+ readonly: "true" }],
+ ["textbox", { key: "message", crop: "end", flex: "1", highlight: "Normal StatusNormal", class: "plain",
+ readonly: "true" }]]]],
+ ["label", { class: "plain", key: "inputbuffer", flex: "0" }],
+ ["label", { class: "plain", key: "progress", flex: "0" }],
+ ["label", { class: "plain", key: "tabcount", flex: "0" }],
+ ["label", { class: "plain", key: "bufferposition", flex: "0" }],
+ ["label", { class: "plain", key: "zoomlevel", flex: "0" }]],
+ // just hide them since other elements expect them
+ ["statusbarpanel", { id: "statusbar-display", hidden: "true" }],
+ ["statusbarpanel", { id: "statusbar-progresspanel", hidden: "true" }]]];
- for each (let attr in prepend..@key)
- attr.parent().@id = "dactyl-statusline-field-" + attr;
+ (function rec(ary) {
+ ary.forEach(function (elem) {
+ if ("key" in elem[1])
+ elem[1].id = "dactyl-statusline-field-" + elem[1].key;
+ if (elem.length > 2)
+ rec(elem.slice(2));
+ });
+ })(prepend);
overlay.overlayWindow(window, {
objects: this.widgets = { get status() this.container },
- prepend: prepend.elements()
+ prepend: prepend
});
try {
diff --git a/common/modules/base.jsm b/common/modules/base.jsm
index 32de7015..89829b1b 100644
--- a/common/modules/base.jsm
+++ b/common/modules/base.jsm
@@ -144,7 +144,7 @@ defineModule("base", {
"Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils",
"XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry",
"debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray",
- "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "iter", "iterAll",
+ "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "isXML", "iter", "iterAll",
"iterOwnProperties", "keys", "memoize", "octal", "properties", "require", "set", "update",
"values", "withCallerGlobal"
]
@@ -482,6 +482,12 @@ function isinstance(object, interfaces) {
*/
function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
+/**
+ * Returns true if obje is an E4X XML object.
+ * @deprecated
+ */
+function isXML(obj) typeof obj === "xml";
+
/**
* Returns true if and only if its sole argument is an
* instance of the builtin Array type. The array may come from
diff --git a/common/modules/dom.jsm b/common/modules/dom.jsm
index 9276b30e..54545b70 100644
--- a/common/modules/dom.jsm
+++ b/common/modules/dom.jsm
@@ -55,6 +55,12 @@ var DOM = Class("DOM", {
;
else if (typeof val == "xml" && context instanceof Ci.nsIDOMDocument)
this[length++] = DOM.fromXML(val, context, this.nodes);
+ else if (DOM.isJSONXML(val)) {
+ if (context instanceof Ci.nsIDOMDocument)
+ this[length++] = DOM.fromJSON(val, context, this.nodes);
+ else
+ this[length++] = val;
+ }
else if (val instanceof Ci.nsIDOMNode || val instanceof Ci.nsIDOMWindow)
this[length++] = val;
else if ("__iterator__" in val || isinstance(val, ["Iterator", "Generator"]))
@@ -1377,6 +1383,12 @@ var DOM = Class("DOM", {
? function (elem, dir) services.dactyl.getScrollable(elem) & (dir ? services.dactyl["DIRECTION_" + dir.toUpperCase()] : ~0)
: function (elem, dir) true),
+ isJSONXML: function isJSONXML(val) isArray(val) && isString(val[0]) || isObject(val) && "toDOM" in val,
+
+ DOMString: function (val) ({
+ toDOM: function toDOM(doc) doc.createTextNode(val)
+ }),
+
/**
* The set of input element type attribute values that mark the element as
* an editable field.
@@ -1536,6 +1548,93 @@ var DOM = Class("DOM", {
}
},
+ fromJSON: update(function fromJSON(xml, doc, nodes, namespaces) {
+ if (!doc)
+ doc = document;
+
+ function tag(args, namespaces) {
+ let _namespaces = namespaces;
+
+ let [name, attr] = args;
+
+ if (Array.isArray(name) || args.length == 0) {
+ var frag = doc.createDocumentFragment();
+ Array.forEach(args, function (arg) {
+ if (!Array.isArray(arg[0]))
+ frag.appendChild(tag(arg, namespaces));
+ else
+ arg.forEach(function (arg) {
+ frag.appendChild(tag(arg, namespaces));
+ });
+ });
+ return frag;
+ }
+
+ if (isObject(name) && "toDOM" in name)
+ return name.toDOM(doc, namespaces, nodes);
+
+ function parseNamespace(name) {
+ var m = /^(?:(.*):)?(.*)$/.exec(name);
+ return [namespaces[m[1]], m[2]];
+ }
+
+ // FIXME: Surely we can do better.
+ for (var key in attr) {
+ if (/^xmlns(?:$|:)/.test(key)) {
+ if (_namespaces === namespaces)
+ namespaces = update({}, namespaces);
+
+ namespaces[key.substr(6)] = namespaces[attr[key]] || attr[key];
+ }}
+
+ var args = Array.slice(args, 2);
+ var vals = parseNamespace(name);
+ var elem = doc.createElementNS(vals[0] || namespaces[""],
+ vals[1]);
+
+ for (var key in attr)
+ if (!/^xmlns(?:$|:)/.test(key)) {
+ var val = attr[key];
+ if (nodes && key == "key")
+ nodes[val] = elem;
+
+ vals = parseNamespace(key);
+ if (typeof val == "function")
+ elem.addEventListener(key.replace(/^on/, ""), val, false);
+ else if (vals[0])
+ elem.setAttributeNS(vals[0], key, val);
+ else if (key != "highlight")
+ elem.setAttribute(key, val);
+ }
+ args.forEach(function(e) {
+ elem.appendChild(typeof e == "object" ? tag(e, namespaces) :
+ e instanceof Node ? e : doc.createTextNode(e));
+ });
+
+ if ("highlight" in attr)
+ highlight.highlightNode(elem, attr.highlight, nodes || true);
+ return elem;
+ }
+
+ if (namespaces)
+ namespaces = update({}, fromJSON.namespaces, namespaces);
+ else
+ namespaces = fromJSON.namespaces;
+
+ return tag(xml, namespaces)
+ }, {
+ namespaces: {
+ "": "http://www.w3.org/1999/xhtml",
+ html: "http://www.w3.org/1999/xhtml",
+ xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ }
+ }),
+
+ parseNamespace: function parseNamespace(name) {
+ var m = /^(?:(.*):)?(.*)$/.exec(name);
+ return [DOM.fromJSON.namespaces[m[1]], m[2]];
+ },
+
/**
* Evaluates an XPath expression in the current or provided
* document. It provides the xhtml, xhtml2 and dactyl XML
diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm
index 69f0cad9..44c5a2ee 100644
--- a/common/modules/overlay.jsm
+++ b/common/modules/overlay.jsm
@@ -215,32 +215,65 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
_loadOverlay: function _loadOverlay(window, obj) {
let doc = window.document;
- let elems = this.getData(doc, "overlayElements");
- let attrs = this.getData(doc, "overlayAttributes");
+ let savedElems = this.getData(doc, "overlayElements");
+ let savedAttrs = this.getData(doc, "overlayAttributes");
function insert(key, fn) {
if (obj[key]) {
let iterator = Iterator(obj[key]);
if (!isObject(obj[key]))
- iterator = ([elem.@id, elem.elements(), elem.@*::*.(function::name() != "id")] for each (elem in obj[key]));
+ iterator = ([elem.@id, elem.elements(), elem.@*::*.(function::name() != "id")]
+ for each (elem in obj[key]));
+ else if (isArray(obj[key])) {
+ iterator = ([elem[1].id, elem.slice(2), elem[1]]
+ for each (elem in obj[key]))
+ }
- for (let [elem, xml, attr] in iterator) {
+ for (let [elem, xml, attrs] in iterator) {
if (elem = doc.getElementById(String(elem))) {
- let node = DOM.fromXML(xml, doc, obj.objects);
+ // Urgh. Hack.
+ let namespaces;
+ if (attrs && !isXML(attrs))
+ namespaces = iter([k.slice(6), DOM.fromJSON.namespaces[v] || v]
+ for ([k, v] in Iterator(attrs))
+ if (/^xmlns(?:$|:)/.test(k))).toObject();
+
+ let node;
+ if (isXML(xml))
+ node = DOM.fromXML(xml, doc, obj.objects);
+ else
+ node = DOM.fromJSON(xml, doc, obj.objects, namespaces);
+
if (!(node instanceof Ci.nsIDOMDocumentFragment))
- elems.push(node);
+ savedElems.push(node);
else
for (let n in array.iterValues(node.childNodes))
- elems.push(n);
+ savedElems.push(n);
fn(elem, node);
- for each (let attr in attr || []) {
- let ns = attr.namespace(), name = attr.localName();
- attrs.push([elem, ns, name, getAttr(elem, ns, name), String(attr)]);
- if (attr.name() != "highlight")
- elem.setAttributeNS(ns, name, String(attr));
+
+ if (isXML(attrs))
+ // Evilness and such.
+ let (oldAttrs = attrs) {
+ attrs = (attr for each (attr in oldAttrs));
+ }
+
+ for (let attr in attrs || []) {
+ let ns, name, localName, val;
+ if (isXML(attr))
+ ns = attr.namespace(), localName = attr.localName(),
+ name = attr.name(), val = String(attr);
else
- highlight.highlightNode(elem, String(attr));
+ [ns, localName] = DOM.parseNamespace(attr),
+ name = attr, val = attrs[attr];
+
+ savedAttrs.push([elem, ns, name, getAttr(elem, ns, name), val]);
+ if (name === "highlight")
+ highlight.highlightNode(elem, val);
+ else if (ns)
+ elem.setAttributeNS(ns, name, val);
+ else
+ elem.setAttribute(name, val);
}
}
}