-// Copyright (c) 2009-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2009-2014 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";
try {
-Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("overlay", {
exports: ["overlay"],
require: ["util"]
-}, this);
+});
+
+lazyRequire("highlight", ["highlight"]);
var getAttr = function getAttr(elem, ns, name)
elem.hasAttributeNS(ns, name) ? elem.getAttributeNS(ns, name) : null;
elem.removeAttributeNS(ns, name);
else
elem.setAttributeNS(ns, name, val);
-}
+};
var Overlay = Class("Overlay", {
init: function init(window) {
this.window = window;
},
- cleanups: Class.Memoize(function () []),
- objects: Class.Memoize(function () ({})),
+ cleanups: Class.Memoize(() => []),
+ objects: Class.Memoize(() => ({})),
get doc() this.window.document,
}
});
-
var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), {
init: function init() {
util.addObserver(this);
this.overlays = {};
+ this.weakMap = WeakMap();
+
this.onWindowVisible = [];
},
- id: Class.Memoize(function () config.addon.id),
+ id: Class.Memoize(() => config.addon.id),
/**
* Adds an event listener for this session and removes it on
target = null;
this.setData(doc, "listeners", listeners.filter(function (args) {
- if (target == null || args[0].get() == target && args[1] == event && args[2].wrapped == callback && args[3] == capture) {
- args[0].get().removeEventListener.apply(args[0].get(), args.slice(1));
+ let elem = args[0].get();
+ if (target == null || elem == target && args[1] == event && args[2].wrapped == callback && args[3] == capture) {
+ elem.removeEventListener.apply(elem, args.slice(1));
return false;
}
- return !args[0].get();
+ return elem;
}));
},
cleanup: function cleanup(reason) {
for (let doc in util.iterDocuments()) {
+ for (let callback in values(this.getData(doc, "cleanup")))
+ util.trapErrors(callback, doc, reason);
+
for (let elem in values(this.getData(doc, "overlayElements")))
if (elem.parentNode)
elem.parentNode.removeChild(elem);
if (getAttr(elem, ns, name) === value)
setAttr(elem, ns, name, orig);
- for (let callback in values(this.getData(doc, "cleanup")))
- util.trapErrors(callback, doc, reason);
-
this.unlisten(doc, true);
delete doc[this.id];
"content-document-global-created": function (window, uri) { this.observe(window, "toplevel-window-ready", null); },
"xul-window-visible": function () {
if (this.onWindowVisible)
- this.onWindowVisible.forEach(function (f) f.call(this), this);
+ this.onWindowVisible.forEach(f => { f.call(this); });
this.onWindowVisible = null;
}
},
getData: function getData(obj, key, constructor) {
- let { id } = this;
- if (!(id in obj && obj[id]))
- obj[id] = {};
+ if (!this.weakMap.has(obj))
+ try {
+ this.weakMap.set(obj, {});
+ }
+ catch (e if e instanceof TypeError) {
+ // util.dump("Bad WeakMap key: " + obj + " " + Components.stack.caller);
+ let { id } = this;
+
+ if (!(id in obj && obj[id]))
+ obj[id] = {};
+
+ var data = obj[id];
+ }
+
+ data = data || this.weakMap.get(obj);
if (arguments.length == 1)
- return obj[id];
+ return data;
- if (obj[id][key] === undefined)
+ if (data[key] === undefined)
if (constructor === undefined || callable(constructor))
- obj[id][key] = (constructor || Array)();
+ data[key] = (constructor || Array)();
else
- obj[id][key] = constructor;
+ data[key] = constructor;
- return obj[id][key];
+ return data[key];
},
setData: function setData(obj, key, val) {
- let { id } = this;
-
- if (!(id in obj))
- obj[id] = {};
+ let data = this.getData(obj);
+ if (val !== undefined)
+ return data[key] = val;
- return obj[id][key] = val;
+ delete data[key];
},
- overlayWindow: function (url, fn) {
+ overlayWindow: function overlayWindow(url, fn) {
if (url instanceof Ci.nsIDOMWindow)
overlay._loadOverlay(url, fn);
else {
_loadOverlays: function _loadOverlays(window) {
let overlays = this.getData(window, "overlays");
- for each (let obj in overlay.overlays[window.document.documentURI] || []) {
+ for (let obj of overlay.overlays[window.document.documentURI] || []) {
if (~overlays.indexOf(obj))
continue;
overlays.push(obj);
_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]));
+ if (isArray(obj[key])) {
+ iterator = ([elem[1].id, elem.slice(2), elem[1]]
+ for each (elem in obj[key]));
+ }
+
+ for (let [elem, xml, attrs] in iterator) {
+ if (elem = doc.getElementById(String(elem))) {
+ // Urgh. Hack.
+ let namespaces;
+ if (attrs)
+ namespaces = iter([k.slice(6), DOM.fromJSON.namespaces[v] || v]
+ for ([k, v] in Iterator(attrs))
+ if (/^xmlns(?:$|:)/.test(k))).toObject();
+
+ let node = DOM.fromJSON(xml, doc, obj.objects, namespaces);
- for (let [elem, xml, attr] in iterator) {
- if (elem = doc.getElementById(elem)) {
- let node = DOM.fromXML(xml, doc, obj.objects);
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));
+
+ for (let attr in attrs || []) {
+ let [ns, localName] = DOM.parseNamespace(attr);
+ let name = attr;
+ let val = attrs[attr];
+
+ savedAttrs.push([elem, ns, name, getAttr(elem, ns, name), val]);
+ if (name === "highlight")
+ highlight.highlightNode(elem, val);
else
- highlight.highlightNode(elem, String(attr));
+ elem.setAttributeNS(ns || "", name, val);
}
}
}
}
}
- insert("before", function (elem, dom) elem.parentNode.insertBefore(dom, elem));
- insert("after", function (elem, dom) elem.parentNode.insertBefore(dom, elem.nextSibling));
- insert("append", function (elem, dom) elem.appendChild(dom));
- insert("prepend", function (elem, dom) elem.insertBefore(dom, elem.firstChild));
+ insert("before", (elem, dom) => elem.parentNode.insertBefore(dom, elem));
+ insert("after", (elem, dom) => elem.parentNode.insertBefore(dom, elem.nextSibling));
+ insert("append", (elem, dom) => elem.appendChild(dom));
+ insert("prepend", (elem, dom) => elem.insertBefore(dom, elem.firstChild));
if (obj.ready)
util.trapErrors("ready", obj, window);
overrides = update(Object.create(original), overrides);
Object.getOwnPropertyNames(overrides).forEach(function (k) {
- let orig, desc = Object.getOwnPropertyDescriptor(overrides, k);
+ let desc = Object.getOwnPropertyDescriptor(overrides, k);
+
if (desc.value instanceof Class.Property)
desc = desc.value.init(k) || desc.value;
if (k in object) {
- for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj))
- if (orig = Object.getOwnPropertyDescriptor(obj, k))
+ for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj)) {
+ var orig = Object.getOwnPropertyDescriptor(obj, k);
+ if (orig)
Object.defineProperty(original, k, orig);
+ }
- if (!orig)
- if (orig = Object.getPropertyDescriptor(object, k))
+ if (!orig) {
+ orig = Object.getPropertyDescriptor(object, k);
+ if (orig)
Object.defineProperty(original, k, orig);
+ }
}
// Guard against horrible add-ons that use eval-based monkey
delete desc.writable;
desc.get = function get() value;
desc.set = function set(val) {
- if (!callable(val) || Function.prototype.toString(val).indexOf(sentinel) < 0)
+ if (!callable(val) || !Function.prototype.toString(val).contains(sentinel))
Class.replaceProperty(this, k, val);
else {
let package_ = util.newURI(Components.stack.caller.filename).host;
Object.defineProperty(object, k, desc);
if (callable(value)) {
- var sentinel = "(function DactylOverlay() {}())"
+ var sentinel = "(function DactylOverlay() {}())";
value.toString = function toString() toString.toString.call(this).replace(/\}?$/, sentinel + "; $&");
value.toSource = function toSource() toSource.toSource.call(this).replace(/\}?$/, sentinel + "; $&");
}
}, this);
return function unwrap() {
- for each (let k in Object.getOwnPropertyNames(original))
+ for (let k of Object.getOwnPropertyNames(original))
if (Object.getOwnPropertyDescriptor(object, k).configurable)
Object.defineProperty(object, k, Object.getOwnPropertyDescriptor(original, k));
else {
get activeModules() this.activeWindow && this.activeWindow.dactyl.modules,
- get modules() this.windows.map(function (w) w.dactyl.modules),
+ get modules() [w.dactyl.modules for (w of this.windows)],
/**
* The most recently active dactyl window.
*/
- get activeWindow() this.windows[0],
+ get activeWindow() {
+ let win = this._activeWindow && this._activeWindow.get();
+ return this.windows.has(win) && win;
+ },
- set activeWindow(win) this.windows = [win].concat(this.windows.filter(function (w) w != win)),
+ set activeWindow(win) this._activeWindow = util.weakReference(win),
/**
* A list of extant dactyl windows.
*/
- windows: Class.Memoize(function () [])
+ windows: Class.Memoize(() => RealSet())
});
endModule();
} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
-// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: