// Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
//
// 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";
-Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("dom", {
exports: ["$", "DOM", "NS", "XBL", "XHTML", "XUL"]
-}, this);
+});
+
+lazyRequire("highlight", ["highlight"]);
+lazyRequire("messages", ["_"]);
+lazyRequire("overlay", ["overlay"]);
+lazyRequire("prefs", ["prefs"]);
+lazyRequire("template", ["template"]);
-var XBL = Namespace("xbl", "http://www.mozilla.org/xbl");
-var XHTML = Namespace("html", "http://www.w3.org/1999/xhtml");
-var XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-var NS = Namespace("dactyl", "http://vimperator.org/namespaces/liberator");
-default xml namespace = XHTML;
+var XBL = "http://www.mozilla.org/xbl";
+var XHTML = "http://www.w3.org/1999/xhtml";
+var XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+var NS = "http://vimperator.org/namespaces/liberator";
function BooleanAttribute(attr) ({
get: function (elem) elem.getAttribute(attr) == "true",
;
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 ("length" in val)
- for (let i = 0; i < val.length; i++)
- this[length++] = val[i];
else if ("__iterator__" in val || isinstance(val, ["Iterator", "Generator"]))
for (let elem in val)
this[length++] = elem;
+ else if ("length" in val)
+ for (let i = 0; i < val.length; i++)
+ this[length++] = val[i];
else
this[length++] = val;
href: { get: function (elem) elem.href || elem.getAttribute("href") },
src: { get: function (elem) elem.src || elem.getAttribute("src") },
checked: { get: function (elem) elem.hasAttribute("checked") ? elem.getAttribute("checked") == "true" : elem.checked,
- set: function (elem, val) { elem.setAttribute("checked", !!val); elem.checked = val } },
+ set: function (elem, val) { elem.setAttribute("checked", !!val); elem.checked = val; } },
collapsed: BooleanAttribute("collapsed"),
disabled: BooleanAttribute("disabled"),
hidden: BooleanAttribute("hidden"),
}]
]),
- matcher: function matcher(sel) function (elem) elem.mozMatchesSelector && elem.mozMatchesSelector(sel),
+ matcher: function matcher(sel) elem => (elem.mozMatchesSelector && elem.mozMatchesSelector(sel)),
each: function each(fn, self) {
let obj = self || this.Empty();
},
eachDOM: function eachDOM(val, fn, self) {
- XML.prettyPrinting = XML.ignoreWhitespace = false;
- if (isString(val))
- val = XML(val);
-
- if (typeof val == "xml")
- return this.each(function (elem, i) {
- fn.call(this, DOM.fromXML(val, elem.ownerDocument), elem, i);
- }, self || this);
-
let dom = this;
function munge(val, container, idx) {
if (val instanceof Ci.nsIDOMRange)
if (val instanceof Ci.nsIDOMNode)
return val;
- if (typeof val == "xml") {
+ if (DOM.isJSONXML(val)) {
val = dom.constructor(val, dom.document);
if (container)
container[idx] = val[0];
return val;
}
+ if (DOM.isJSONXML(val))
+ val = (function () this).bind(val);
+
if (callable(val))
return this.each(function (elem, i) {
util.withProperErrors(fn, this, munge(val.call(this, elem, i)), elem, i);
},
find: function find(val) {
- return this.map(function (elem) elem.querySelectorAll(val));
+ return this.map(elem => elem.querySelectorAll(val));
},
findAnon: function findAnon(attr, val) {
- return this.map(function (elem) elem.ownerDocument.getAnonymousElementByAttribute(elem, attr, val));
+ return this.map(elem => elem.ownerDocument.getAnonymousElementByAttribute(elem, attr, val));
},
filter: function filter(val, self) {
let res = this.Empty();
this.each(function (elem) {
- while(true) {
- elem = fn.call(this, elem)
- if (elem instanceof Ci.nsIDOMElement)
+ while (true) {
+ elem = fn.call(this, elem);
+ if (elem instanceof Ci.nsIDOMNode)
res[res.length++] = elem;
else if (elem && "length" in elem)
- for (let i = 0; i < tmp.length; i++)
- res[res.length++] = tmp[j];
+ for (let i = 0; i < elem.length; i++)
+ res[res.length++] = elem[j];
else
break;
}
for (let i = 0; i < this.length; i++) {
let tmp = fn.call(self || update(obj, [this[i]]), this[i], i);
- if (isObject(tmp) && "length" in tmp)
+ if (isObject(tmp) && !(tmp instanceof Ci.nsIDOMNode) && "length" in tmp)
for (let j = 0; j < tmp.length; j++)
res[res.length++] = tmp[j];
else if (tmp != null)
return false;
},
- get parent() this.map(function (elem) elem.parentNode, this),
+ get parent() this.map(elem => elem.parentNode, this),
get offsetParent() this.map(function (elem) {
do {
while (parent);
}, this),
- get ancestors() this.all(function (elem) elem.parentNode),
+ get ancestors() this.all(elem => elem.parentNode),
- get children() this.map(function (elem) Array.filter(elem.childNodes,
- function (e) e instanceof Ci.nsIDOMElement),
+ get children() this.map(elem => Array.filter(elem.childNodes,
+ e => e instanceof Ci.nsIDOMElement),
this),
- get contents() this.map(function (elem) elem.childNodes, this),
+ get contents() this.map(elem => elem.childNodes, this),
- get siblings() this.map(function (elem) Array.filter(elem.parentNode.childNodes,
- function (e) e != elem && e instanceof Ci.nsIDOMElement),
+ get siblings() this.map(elem => Array.filter(elem.parentNode.childNodes,
+ e => e != elem && e instanceof Ci.nsIDOMElement),
this),
- get siblingsBefore() this.all(function (elem) elem.previousElementSibling),
- get siblingsAfter() this.all(function (elem) elem.nextElementSibling),
+ get siblingsBefore() this.all(elem => elem.previousElementSibling),
+ get siblingsAfter() this.all(elem => elem.nextElementSibling),
+
+ get allSiblingsBefore() this.all(elem => elem.previousSibling),
+ get allSiblingsAfter() this.all(elem => elem.nextSibling),
get class() let (self = this) ({
toString: function () self[0].className,
each: function each(meth, arg) {
return self.each(function (elem) {
elem.classList[meth](arg);
- })
+ });
},
add: function add(cls) this.each("add", cls),
}),
remove: function remove(hl) self.each(function () {
- this.highlight.list = this.highlight.list.filter(function (h) h != hl);
+ this.highlight.list = this.highlight.list.filter(h => h != hl);
}),
toggle: function toggle(hl, val, thisObj) self.each(function (elem, i) {
let { highlight } = this;
let v = callable(val) ? val.call(thisObj || this, elem, i) : val;
- highlight[(v == null ? highlight.has(hl) : !v) ? "remove" : "add"](hl)
+ highlight[(v == null ? highlight.has(hl) : !v) ? "remove" : "add"](hl);
}),
}),
this[0] ? this[0].getBoundingClientRect() : {},
get viewport() {
- if (this[0] instanceof Ci.nsIDOMWindow)
+ let node = this[0];
+ if (node instanceof Ci.nsIDOMDocument)
+ node = node.defaultView;
+
+ if (node instanceof Ci.nsIDOMWindow)
return {
get width() this.right - this.left,
get height() this.bottom - this.top,
- bottom: this[0].innerHeight,
- right: this[0].innerWidth,
+ bottom: node.innerHeight,
+ right: node.innerWidth,
top: 0, left: 0
};
let r = this.rect;
return {
- width: this[0].clientWidth,
- height: this[0].clientHeight,
- top: r.top + this[0].clientTop,
+ width: node.clientWidth,
+ height: node.clientHeight,
+ top: r.top + node.clientTop,
get bottom() this.top + this.height,
- left: r.left + this[0].clientLeft,
+ left: r.left + node.clientLeft,
get right() this.left + this.width
- }
+ };
},
scrollPos: function scrollPos(left, top) {
return editor;
},
- get isEditable() !!this.editor,
+ get isEditable() !!this.editor || this[0] instanceof Ci.nsIDOMElement && this.style.MozUserModify == "read-write",
get isInput() isinstance(this[0], [Ci.nsIDOMHTMLInputElement,
Ci.nsIDOMHTMLTextAreaElement,
if (DOM(elem).isInput
|| /^(?:hidden|textarea)$/.test(elem.type)
|| elem.type == "submit" && elem == field
- || elem.checked && /^(?:checkbox|radio)$/.test(elem.type))
- elems.push(encode(elem.name, elem.value, elem === field));
+ || elem.checked && /^(?:checkbox|radio)$/.test(elem.type)) {
+
+ if (elem !== field)
+ elems.push(encode(elem.name, elem.value));
+ else if (overlay.getData(elem, "had-focus"))
+ elems.push(encode(elem.name, elem.value, true));
+ else
+ elems.push(encode(elem.name, "", true));
+ }
else if (elem instanceof Ci.nsIDOMHTMLSelectElement) {
for (let [, opt] in Iterator(elem.options))
if (opt.selected)
* representation of this node.
*/
repr: function repr(color) {
- XML.ignoreWhitespace = XML.prettyPrinting = false;
-
function namespaced(node) {
- var ns = DOM.namespaceNames[node.namespaceURI] || /^(?:(.*?):)?/.exec(node.name)[0];
+ var ns = DOM.namespaceNames[node.namespaceURI] || /^(?:(.*?):)?/.exec(node.name)[1];
if (!ns)
return node.localName;
if (color)
- return <><span highlight="HelpXMLNamespace">{ns}</span>{node.localName}</>
+ return [["span", { highlight: "HelpXMLNamespace" }, ns],
+ node.localName];
return ns + ":" + node.localName;
}
let res = [];
this.each(function (elem) {
try {
- let hasChildren = elem.firstChild && (!/^\s*$/.test(elem.firstChild) || elem.firstChild.nextSibling)
+ let hasChildren = elem.firstChild && (!/^\s*$/.test(elem.firstChild) || elem.firstChild.nextSibling);
if (color)
- res.push(<span highlight="HelpXML"><span highlight="HelpXMLTagStart"><{
- namespaced(elem)} {
- template.map(array.iterValues(elem.attributes),
- function (attr)
- <span highlight="HelpXMLAttribute">{namespaced(attr)}</span> +
- <span highlight="HelpXMLString">{attr.value}</span>,
- <> </>)
- }{ !hasChildren ? "/>" : ">"
- }</span>{ !hasChildren ? "" : <>...</> +
- <span highlight="HtmlTagEnd"><{namespaced(elem)}></span>
- }</span>);
+ res.push(["span", { highlight: "HelpXML" },
+ ["span", { highlight: "HelpXMLTagStart" },
+ "<", namespaced(elem), " ",
+ template.map(array.iterValues(elem.attributes),
+ attr => [
+ ["span", { highlight: "HelpXMLAttribute" }, namespaced(attr)],
+ ["span", { highlight: "HelpXMLString" }, attr.value]
+ ],
+ " "),
+ !hasChildren ? "/>" : ">",
+ ],
+ !hasChildren ? "" :
+ ["", "...",
+ ["span", { highlight: "HtmlTagEnd" }, "<", namespaced(elem), ">"]]
+ ]);
else {
let tag = "<" + [namespaced(elem)].concat(
- [namespaced(a) + "=" + template.highlight(a.value, true)
+ [namespaced(a) + '="' + String.replace(a.value, /["<]/, DOM.escapeHTML) + '"'
for ([i, a] in array.iterItems(elem.attributes))]).join(" ");
res.push(tag + (!hasChildren ? "/>" : ">...</" + namespaced(elem) + ">"));
res.push({}.toString.call(elem));
}
}, this);
- return template.map(res, util.identity, <>,</>);
+ res = template.map(res, util.identity, ",");
+ return color ? res : res.join("");
},
attr: function attr(key, val) {
return this[0].style[css.property(key)];
}, {
- name: function (property) property.replace(/[A-Z]/g, function (m0) "-" + m0.toLowerCase()),
+ name: function (property) property.replace(/[A-Z]/g, m0 => "-" + m0.toLowerCase()),
- property: function (name) name.replace(/-(.)/g, function (m0, m1) m1.toUpperCase())
+ property: function (name) name.replace(/-(.)/g, (m0, m1) => m1.toUpperCase())
}),
append: function append(val) {
}, this);
},
+ fragment: function fragment() {
+ let frag = this.document.createDocumentFragment();
+ this.appendTo(frag);
+ return this;
+ },
+
+ clone: function clone(deep)
+ this.map(elem => elem.cloneNode(deep)),
+
toggle: function toggle(val, self) {
if (callable(val))
return this.each(function (elem, i) {
if (arguments.length)
return this[val ? "show" : "hide"]();
- let hidden = this.map(function (elem) elem.style.display == "none");
+ let hidden = this.map(elem => elem.style.display == "none");
return this.each(function (elem, i) {
this[hidden[i] ? "show" : "hide"]();
});
let [fn, self] = args;
if (!callable(fn))
- fn = function () args[0];
+ fn = () => args[0];
return this.each(function (elem, i) {
set.call(this, elem, fn.call(self || this, elem, i));
html: function html(txt, self) {
return this.getSet(arguments,
- function (elem) elem.innerHTML,
- function (elem, val) { elem.innerHTML = val });
+ elem => elem.innerHTML,
+ util.wrapCallback((elem, val) => { elem.innerHTML = val; }));
},
text: function text(txt, self) {
return this.getSet(arguments,
- function (elem) elem.textContent,
- function (elem, val) { elem.textContent = val });
+ elem => elem.textContent,
+ (elem, val) => { elem.textContent = val; });
},
val: function val(txt) {
return this.getSet(arguments,
- function (elem) elem.value,
- function (elem, val) { elem.value = val == null ? "" : val });
+ elem => elem.value,
+ (elem, val) => { elem.value = val == null ? "" : val; });
},
listen: function listen(event, listener, capture) {
else
event = array.toObject([[event, listener]]);
- for (let [k, v] in Iterator(event))
- event[k] = util.wrapCallback(v, true);
+ for (let [evt, callback] in Iterator(event))
+ event[evt] = util.wrapCallback(callback, true);
return this.each(function (elem) {
- for (let [k, v] in Iterator(event))
- elem.addEventListener(k, v, capture);
+ for (let [evt, callback] in Iterator(event))
+ elem.addEventListener(evt, callback, capture);
});
},
unlisten: function unlisten(event, listener, capture) {
if (isObject(event))
capture = listener;
else
- event = array.toObject([[key, val]]);
+ event = array.toObject([[event, listener]]);
return this.each(function (elem) {
for (let [k, v] in Iterator(event))
elem.removeEventListener(k, v.wrapper || v, capture);
});
},
+ once: function once(event, listener, capture) {
+ if (isObject(event))
+ capture = listener;
+ else
+ event = array.toObject([[event, listener]]);
+
+ for (let pair in Iterator(event)) {
+ let [evt, callback] = pair;
+ event[evt] = util.wrapCallback(function wrapper(event) {
+ this.removeEventListener(evt, wrapper.wrapper, capture);
+ return callback.apply(this, arguments);
+ }, true);
+ }
+
+ return this.each(function (elem) {
+ for (let [k, v] in Iterator(event))
+ elem.addEventListener(k, v, capture);
+ });
+ },
dispatch: function dispatch(event, params, extraProps) {
this.canceled = false;
return true;
if (rect.top > viewport.bottom)
return false;
- return Math.abs(rect.top) < Math.abs(viewport.bottom - rect.bottom)
+ return Math.abs(rect.top) < Math.abs(viewport.bottom - rect.bottom);
}
let rect;
update(params, this.constructor.defaults[type],
iter.toObject([k, opts[k]] for (k in opts) if (k in params)));
- evt["init" + t + "Event"].apply(evt, args.map(function (k) params[k]));
+ evt["init" + t + "Event"].apply(evt, args.map(k => params[k]));
return evt;
}
}, {
// want to refer to within dactyl's source code for
// comparisons like if (key == "<Esc>") { ... }
this.keyTable = {
- add: ["Plus", "Add"],
+ add: ["+", "Plus", "Add"],
+ back_quote: ["`"],
+ back_slash: ["\\"],
back_space: ["BS"],
+ comma: [","],
count: ["count"],
+ close_bracket: ["]"],
delete: ["Del"],
+ equals: ["="],
escape: ["Esc", "Escape"],
insert: ["Insert", "Ins"],
leader: ["Leader"],
left_shift: ["LT", "<"],
nop: ["Nop"],
+ open_bracket: ["["],
pass: ["Pass"],
+ period: ["."],
+ quote: ["'"],
return: ["Return", "CR", "Enter"],
right_shift: [">"],
+ semicolon: [";"],
slash: ["/"],
space: ["Space", " "],
- subtract: ["Minus", "Subtract"]
+ subtract: ["-", "Minus", "Subtract"]
};
this.key_key = {};
}
for (let [k, v] in Iterator(Ci.nsIDOMKeyEvent)) {
+ if (!/^DOM_VK_/.test(k))
+ continue;
+
this.code_nativeKey[v] = k.substr(4);
k = k.substr(7).toLowerCase();
- let names = [k.replace(/(^|_)(.)/g, function (m, n1, n2) n2.toUpperCase())
+ let names = [k.replace(/(^|_)(.)/g, (m, n1, n2) => n2.toUpperCase())
.replace(/^NUMPAD/, "k")];
if (names[0].length == 1)
return this;
},
-
code_key: Class.Memoize(function (prop) this.init()[prop]),
code_nativeKey: Class.Memoize(function (prop) this.init()[prop]),
keyTable: Class.Memoize(function (prop) this.init()[prop]),
* @param {string} keys Messy form.
* @param {boolean} unknownOk Whether unknown keys are passed
* through rather than being converted to <lt>keyname>.
- * @default false
+ * @default true
* @returns {string} Canonical form.
*/
- canonicalKeys: function canonicalKeys(keys, unknownOk) {
- if (arguments.length === 1)
- unknownOk = true;
+ canonicalKeys: function canonicalKeys(keys, unknownOk=true) {
return this.parse(keys, unknownOk).map(this.closure.stringify).join("");
},
* @param {string} keys The string to parse.
* @param {boolean} unknownOk Whether unknown keys are passed
* through rather than being converted to <lt>keyname>.
- * @default false
+ * @default true
* @returns {Array[Object]}
*/
- parse: function parse(input, unknownOk) {
+ parse: function parse(input, unknownOk=true) {
if (isArray(input))
- return array.flatten(input.map(function (k) this.parse(k, unknownOk), this));
-
- if (arguments.length === 1)
- unknownOk = true;
+ return array.flatten(input.map(k => this.parse(k, unknownOk)));
let out = [];
for (let match in util.regexp.iterate(/<.*?>?>|[^<]|<(?!.*>)/g, input)) {
*/
stringify: function stringify(event) {
if (isArray(event))
- return event.map(function (e) this.stringify(e), this).join("");
+ return event.map(e => this.stringify(e)).join("");
if (event.dactylString)
return event.dactylString;
if (event.keyCode in this.code_key) {
key = this.code_key[event.keyCode];
- if (event.shiftKey && (key.length > 1 || event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
+ if (event.shiftKey && (key.length > 1 || key.toUpperCase() == key.toLowerCase()
+ || event.ctrlKey || event.altKey || event.metaKey)
+ || event.dactylShift)
modifier += "S-";
else if (!modifier && key.length === 1)
if (event.shiftKey)
return "<" + modifier + key + ">";
},
-
defaults: {
load: { bubbles: false },
submit: { cancelable: true }
},
- types: Class.Memoize(function () iter(
+ types: Class.Memoize(() => iter(
{
Mouse: "click mousedown mouseout mouseover mouseup dblclick " +
"hover " +
"load unload pageshow pagehide DOMContentLoaded " +
"resize scroll"
}
- ).map(function ([k, v]) v.split(" ").map(function (v) [v, k]))
+ ).map(([k, v]) => v.split(" ").map(v => [v, k]))
.flatten()
.toObject()),
.dispatchDOMEventViaPresShell(target, event, true);
else {
target.dispatchEvent(event);
- return !event.getPreventDefault();
+ return !event.defaultPrevented;
}
}
catch (e) {
})
}),
- createContents: Class.Memoize(function () services.has("dactyl") && services.dactyl.createContents
- || function (elem) {}),
+ createContents: Class.Memoize(() => services.has("dactyl") && services.dactyl.createContents
+ || (elem => {})),
+
+ isScrollable: Class.Memoize(() => services.has("dactyl") && services.dactyl.getScrollable
+ ? (elem, dir) => services.dactyl.getScrollable(elem) & (dir ? services.dactyl["DIRECTION_" + dir.toUpperCase()] : ~0)
+ : (elem, dir) => true),
- isScrollable: Class.Memoize(function () services.has("dactyl") && services.dactyl.getScrollable
- ? function (elem, dir) services.dactyl.getScrollable(elem) & (dir ? services.dactyl["DIRECTION_" + dir.toUpperCase()] : ~0)
- : function (elem, dir) true),
+ isJSONXML: function isJSONXML(val) isArray(val) && isinstance(val[0], ["String", "Array", "XML", DOM.DOMString])
+ || isObject(val) && "toDOM" in val,
+
+ DOMString: function DOMString(val) ({
+ __proto__: DOMString.prototype,
+
+ toDOM: function toDOM(doc) doc.createTextNode(val),
+
+ toString: function () val
+ }),
/**
* The set of input element type attribute values that mark the element as
* entities.
*
* @param {string} str
+ * @param {boolean} simple If true, only escape for the simple case
+ * of text nodes.
* @returns {string}
*/
- escapeHTML: function escapeHTML(str) {
+ escapeHTML: function escapeHTML(str, simple) {
let map = { "'": "'", '"': """, "%": "%", "&": "&", "<": "<", ">": ">" };
- return str.replace(/['"&<>]/g, function (m) map[m]);
+ let regexp = simple ? /[<>]/g : /['"&<>]/g;
+ return str.replace(regexp, m => map[m]);
},
/**
* stored here, keyed to the value thereof.
* @returns {Node}
*/
- fromXML: function fromXML(node, doc, nodes) {
- XML.ignoreWhitespace = XML.prettyPrinting = false;
- if (typeof node === "string") // Sandboxes can't currently pass us XML objects.
- node = XML(node);
-
- if (node.length() != 1) {
- let domnode = doc.createDocumentFragment();
- for each (let child in node)
- domnode.appendChild(fromXML(child, doc, nodes));
- return domnode;
+ fromXML: deprecated("DOM.fromJSON", { get: function fromXML()
+ prefs.get("javascript.options.xml.chrome") !== false
+ && require("dom-e4x").fromXML }),
+
+ fromJSON: update(function fromJSON(xml, doc, nodes, namespaces) {
+ if (!doc)
+ doc = document;
+
+ function tag(args, namespaces) {
+ let _namespaces = namespaces;
+
+ // Deal with common error case
+ if (args == null) {
+ util.reportError(Error("Unexpected null when processing XML."));
+ args = ["html:i", {}, "[NULL]"];
+ }
+
+ if (isinstance(args, ["String", "Number", "Boolean", _]))
+ 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;
+ if (args instanceof DOM)
+ return args.fragment();
+ if ("toJSONXML" in args)
+ args = args.toJSONXML();
+
+ let [name, attr] = args;
+
+ if (!isString(name) || args.length == 0 || name === "") {
+ var frag = doc.createDocumentFragment();
+ Array.forEach(args, function (arg) {
+ if (!isArray(arg[0]))
+ arg = [arg];
+ arg.forEach(function (arg) {
+ frag.appendChild(tag(arg, namespaces));
+ });
+ });
+ return frag;
+ }
+
+ attr = attr || {};
+
+ function parseNamespace(name) DOM.parseNamespace(name, namespaces);
+
+ // FIXME: Surely we can do better.
+ for (var key in attr) {
+ if (/^xmlns(?:$|:)/.test(key)) {
+ if (_namespaces === namespaces)
+ namespaces = Object.create(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[""],
+ name);
+
+ for (var key in attr)
+ if (!/^xmlns(?:$|:)/.test(key)) {
+ var val = attr[key];
+ if (nodes && key == "key")
+ nodes[val] = elem;
+
+ vals = parseNamespace(key);
+ if (key == "highlight")
+ ;
+ else if (typeof val == "function")
+ elem.addEventListener(key.replace(/^on/, ""), val, false);
+ else
+ elem.setAttributeNS(vals[0] || "", key, val);
+ }
+ args.forEach(function (e) {
+ elem.appendChild(tag(e, namespaces));
+ });
+
+ if ("highlight" in attr)
+ highlight.highlightNode(elem, attr.highlight, nodes || true);
+ return elem;
}
- switch (node.nodeKind()) {
- case "text":
- return doc.createTextNode(String(node));
- case "element":
- let domnode = doc.createElementNS(node.namespace(), node.localName());
-
- for each (let attr in node.@*::*)
- if (attr.name() != "highlight")
- domnode.setAttributeNS(attr.namespace(), attr.localName(), String(attr));
-
- for each (let child in node.*::*)
- domnode.appendChild(fromXML(child, doc, nodes));
- if (nodes && node.@key)
- nodes[node.@key] = domnode;
-
- if ("@highlight" in node)
- highlight.highlightNode(domnode, String(node.@highlight), nodes || true);
- return domnode;
- default:
- return null;
+ if (namespaces)
+ namespaces = update({}, fromJSON.namespaces, namespaces);
+ else
+ namespaces = fromJSON.namespaces;
+
+ return tag(xml, namespaces);
+ }, {
+ namespaces: {
+ "": "http://www.w3.org/1999/xhtml",
+ dactyl: String(NS),
+ html: "http://www.w3.org/1999/xhtml",
+ xmlns: "http://www.w3.org/2000/xmlns/",
+ xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ }
+ }),
+
+ toXML: function toXML(xml) {
+ // Meh. For now.
+ let doc = services.XMLDocument();
+ let node = this.fromJSON(xml, doc);
+ return services.XMLSerializer()
+ .serializeToString(node);
+ },
+
+ toPrettyXML: function toPrettyXML(xml, asXML, indent, namespaces) {
+ const INDENT = indent || " ";
+
+ const EMPTY = Set("area base basefont br col frame hr img input isindex link meta param"
+ .split(" "));
+
+ function namespaced(namespaces, namespace, localName) {
+ for (let [k, v] in Iterator(namespaces))
+ if (v == namespace)
+ return (k ? k + ":" + localName : localName);
+
+ throw Error("No such namespace");
+ }
+
+ function isFragment(args) !isString(args[0]) || args.length == 0 || args[0] === "";
+
+ function hasString(args) {
+ return args.some(a => (isString(a) || isFragment(a) && hasString(a)));
+ }
+
+ function isStrings(args) {
+ if (!isArray(args))
+ return util.dump("ARGS: " + {}.toString.call(args) + " " + args), false;
+ return args.every(a => (isinstance(a, ["String", DOM.DOMString]) || isFragment(a) && isStrings(a)));
+ }
+
+ function tag(args, namespaces, indent) {
+ let _namespaces = namespaces;
+
+ if (args == "")
+ return "";
+
+ if (isinstance(args, ["String", "Number", "Boolean", _, DOM.DOMString]))
+ return indent +
+ DOM.escapeHTML(String(args), true);
+
+ if (isXML(args))
+ return indent +
+ args.toXMLString()
+ .replace(/^/m, indent);
+
+ if (isObject(args) && "toDOM" in args)
+ return indent +
+ services.XMLSerializer()
+ .serializeToString(args.toDOM(services.XMLDocument()))
+ .replace(/^/m, indent);
+
+ if (args instanceof Ci.nsIDOMNode)
+ return indent +
+ services.XMLSerializer()
+ .serializeToString(args)
+ .replace(/^/m, indent);
+
+ if ("toJSONXML" in args)
+ args = args.toJSONXML();
+
+ // Deal with common error case
+ if (args == null) {
+ util.reportError(Error("Unexpected null when processing XML."));
+ return "[NULL]";
+ }
+
+ let [name, attr] = args;
+
+ if (isFragment(args)) {
+ let res = [];
+ let join = isArray(args) && isStrings(args) ? "" : "\n";
+ Array.forEach(args, function (arg) {
+ if (!isArray(arg[0]))
+ arg = [arg];
+
+ let contents = [];
+ arg.forEach(function (arg) {
+ let string = tag(arg, namespaces, indent);
+ if (string)
+ contents.push(string);
+ });
+ if (contents.length)
+ res.push(contents.join("\n"), join);
+ });
+ if (res[res.length - 1] == join)
+ res.pop();
+ return res.join("");
+ }
+
+ attr = attr || {};
+
+ function parseNamespace(name) {
+ var m = /^(?:(.*):)?(.*)$/.exec(name);
+ return [namespaces[m[1]], m[2]];
+ }
+
+ // FIXME: Surely we can do better.
+ let skipAttr = {};
+ for (var key in attr) {
+ if (/^xmlns(?:$|:)/.test(key)) {
+ if (_namespaces === namespaces)
+ namespaces = update({}, namespaces);
+
+ let ns = namespaces[attr[key]] || attr[key];
+ if (ns == namespaces[key.substr(6)])
+ skipAttr[key] = true;
+
+ attr[key] = namespaces[key.substr(6)] = ns;
+ }}
+
+ var args = Array.slice(args, 2);
+ var vals = parseNamespace(name);
+
+ let res = [indent, "<", name];
+
+ for (let [key, val] in Iterator(attr)) {
+ if (Set.has(skipAttr, key))
+ continue;
+
+ let vals = parseNamespace(key);
+ if (typeof val == "function") {
+ key = key.replace(/^(?:on)?/, "on");
+ val = val.toSource() + "(event)";
+ }
+
+ if (key != "highlight" || vals[0] == String(NS))
+ res.push(" ", key, '="', DOM.escapeHTML(val), '"');
+ else
+ res.push(" ", namespaced(namespaces, String(NS), "highlight"),
+ '="', DOM.escapeHTML(val), '"');
+ }
+
+ if ((vals[0] || namespaces[""]) == String(XHTML) && Set.has(EMPTY, vals[1])
+ || asXML && !args.length)
+ res.push("/>");
+ else {
+ res.push(">");
+
+ if (isStrings(args))
+ res.push(args.map(e => tag(e, namespaces, "")).join(""),
+ "</", name, ">");
+ else {
+ let contents = [];
+ args.forEach(function (e) {
+ let string = tag(e, namespaces, indent + INDENT);
+ if (string)
+ contents.push(string);
+ });
+
+ res.push("\n", contents.join("\n"), "\n", indent, "</", name, ">");
+ }
+ }
+
+ return res.join("");
}
+
+ if (namespaces)
+ namespaces = update({}, DOM.fromJSON.namespaces, namespaces);
+ else
+ namespaces = DOM.fromJSON.namespaces;
+
+ return tag(xml, namespaces, "");
+ },
+
+ parseNamespace: function parseNamespace(name, namespaces) {
+ if (name == "xmlns")
+ return [DOM.fromJSON.namespaces.xmlns, "xmlns"];
+
+ var m = /^(?:(.*):)?(.*)$/.exec(name);
+ return [(namespaces || DOM.fromJSON.namespaces)[m[1]],
+ m[2]];
},
/**
let resolver = XPath.resolver;
if (namespaces) {
namespaces = update({}, DOM.namespaces, namespaces);
- resolver = function (prefix) namespaces[prefix] || null;
+ resolver = prefix => namespaces[prefix] || null;
}
let result = doc.evaluate(expression, elem,
null
);
- return Object.create(result, {
- __iterator__: {
- value: asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
- : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); }
- }
- });
+ let res = {
+ iterateNext: function () result.iterateNext(),
+ get resultType() result.resultType,
+ get snapshotLength() result.snapshotLength,
+ snapshotItem: function (i) result.snapshotItem(i),
+ __iterator__:
+ asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
+ : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); }
+ };
+ return res;
}
catch (e) {
throw e.stack ? e : Error(e);
*/
makeXPath: function makeXPath(nodes) {
return array(nodes).map(util.debrace).flatten()
- .map(function (node) /^[a-z]+:/.test(node) ? node : [node, "xhtml:" + node]).flatten()
- .map(function (node) "//" + node).join(" | ");
+ .map(node => /^[a-z]+:/.test(node) ? node
+ : [node, "xhtml:" + node])
+ .flatten()
+ .map(node => "//" + node).join(" | ");
},
namespaces: {
- xul: XUL.uri,
- xhtml: XHTML.uri,
- html: XHTML.uri,
+ xul: XUL,
+ xhtml: XHTML,
+ html: XHTML,
xhtml2: "http://www.w3.org/2002/06/xhtml2",
- dactyl: NS.uri
+ dactyl: NS
},
namespaceNames: Class.Memoize(function ()
- iter(this.namespaces).map(function ([k, v]) [v, k]).toObject()),
+ iter(this.namespaces).map(([k, v]) => ([v, k])).toObject()),
});
Object.keys(DOM.Event.types).forEach(function (event) {
- let name = event.replace(/-(.)/g, function (m, m1) m1.toUpperCase());
+ let name = event.replace(/-(.)/g, (m, m1) => m1.toUpperCase());
if (!Set.has(DOM.prototype, name))
DOM.prototype[name] =
function _event(arg, extra) {
// catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
-// vim: set sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: