1 // Copyright (c) 2009-2011 by Kris Maglione <maglione.k@gmail.com>
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
7 var Cc = Components.classes;
8 var Ci = Components.interfaces;
9 var Cr = Components.results;
10 var Cu = Components.utils;
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
15 Components.utils.import("resource://gre/modules/ctypes.jsm");
19 let objproto = Object.prototype;
20 let { __lookupGetter__, __lookupSetter__, hasOwnProperty, propertyIsEnumerable } = objproto;
22 if (typeof XPCSafeJSObjectWrapper === "undefined")
23 this.XPCSafeJSObjectWrapper = XPCNativeWrapper;
25 if (!XPCNativeWrapper.unwrap)
26 XPCNativeWrapper.unwrap = function unwrap(obj) {
27 if (hasOwnProperty.call(obj, "wrappedJSObject"))
28 return obj.wrappedJSObject;
32 Object.create = function create(proto, props) {
33 let obj = { __proto__: proto };
34 for (let k in properties(props || {}))
35 Object.defineProperty(obj, k, props[k]);
38 if (!Object.defineProperty)
39 Object.defineProperty = function defineProperty(obj, prop, desc) {
41 let value = desc.value;
43 if (desc.writable && !__lookupGetter__.call(obj, prop)
44 && !__lookupSetter__.call(obj, prop))
48 catch (e if e instanceof TypeError) {}
50 objproto.__defineGetter__.call(obj, prop, function () value);
52 objproto.__defineSetter__.call(obj, prop, function (val) { value = val; });
56 objproto.__defineGetter__.call(obj, prop, desc.get);
58 objproto.__defineSetter__.call(obj, prop, desc.set);
61 throw e.stack ? e : Error(e);
64 if (!Object.defineProperties)
65 Object.defineProperties = function defineProperties(obj, props) {
66 for (let [k, v] in Iterator(props))
67 Object.defineProperty(obj, k, v);
70 Object.freeze = function freeze(obj) {};
71 if (!Object.getPropertyDescriptor)
72 Object.getPropertyDescriptor = function getPropertyDescriptor(obj, prop) {
76 enumerable: propertyIsEnumerable.call(obj, prop)
78 var get = __lookupGetter__.call(obj, prop),
79 set = __lookupSetter__.call(obj, prop);
81 desc.value = obj[prop];
91 throw e.stack ? e : Error(e);
94 if (!Object.getOwnPropertyDescriptor)
95 Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
96 if (hasOwnProperty.call(obj, prop))
97 return Object.getPropertyDescriptor(obj, prop);
99 if (!Object.getOwnPropertyNames)
100 Object.getOwnPropertyNames = function getOwnPropertyNames(obj, _debugger) {
102 // This is an ugly and unfortunately necessary hack.
103 if (hasOwnProperty.call(obj, "__iterator__")) {
104 var oldIter = obj.__iterator__;
105 delete obj.__iterator__;
107 let res = [k for (k in obj) if (hasOwnProperty.call(obj, k))];
108 if (oldIter !== undefined) {
109 obj.__iterator__ = oldIter;
110 res.push("__iterator__");
115 throw e.stack ? e : Error(e);
118 if (!Object.getPrototypeOf)
119 Object.getPrototypeOf = function getPrototypeOf(obj) obj.__proto__;
121 Object.keys = function keys(obj)
122 Object.getOwnPropertyNames(obj).filter(function (k) propertyIsEnumerable.call(obj, k));
124 let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__;
130 function defineModule(name, params, module) {
132 module = getGlobalForObject(params);
135 module.EXPORTED_SYMBOLS = params.exports || [];
136 defineModule.loadLog.push("defineModule " + name);
137 for (let [, mod] in Iterator(params.require || []))
138 require(module, mod);
140 for (let [, mod] in Iterator(params.use || []))
141 if (loaded.hasOwnProperty(mod))
142 require(module, mod, "use");
144 use[mod] = use[mod] || [];
145 use[mod].push(module);
147 currentModule = module;
148 module.startTime = Date.now();
151 defineModule.loadLog = [];
152 Object.defineProperty(defineModule.loadLog, "push", {
153 value: function (val) {
155 defineModule.dump(val + "\n");
156 this[this.length] = Date.now() + " " + val;
159 defineModule.dump = function dump_() {
160 let msg = Array.map(arguments, function (msg) {
161 if (loaded.util && typeof msg == "object")
162 msg = util.objectToString(msg);
165 let name = loaded.config ? config.name : "dactyl";
166 dump(String.replace(msg, /\n?$/, "\n")
167 .replace(/^./gm, name + ": $&"));
169 defineModule.modules = [];
170 defineModule.time = function time(major, minor, func, self) {
171 let time = Date.now();
172 if (typeof func !== "function")
176 var res = func.apply(self, Array.slice(arguments, 4));
179 loaded.util && util.reportError(e);
182 JSMLoader.times.add(major, minor, Date.now() - time);
186 function endModule() {
187 defineModule.loadLog.push("endModule " + currentModule.NAME);
189 for (let [, mod] in Iterator(use[currentModule.NAME] || []))
190 require(mod, currentModule.NAME, "use");
192 loaded[currentModule.NAME] = 1;
195 function require(obj, name, from) {
197 if (arguments.length === 1)
198 [obj, name] = [{}, obj];
200 let caller = Components.stack.caller;
203 defineModule.loadLog.push((from || "require") + ": loading " + name + " into " + (obj.NAME || caller.filename + ":" + caller.lineNumber));
205 JSMLoader.load(name + ".jsm", obj);
209 defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
213 defineModule.dump(" " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e + "\n");
217 defineModule("base", {
218 // sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt
220 "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Module", "JSMLoader", "Object", "Runnable",
221 "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMUtils", "XPCSafeJSObjectWrapper",
222 "array", "bind", "call", "callable", "ctypes", "curry", "debuggerProperties", "defineModule",
223 "deprecated", "endModule", "forEach", "isArray", "isGenerator", "isinstance", "isObject",
224 "isString", "isSubclass", "iter", "iterAll", "iterOwnProperties", "keys", "memoize", "octal",
225 "properties", "require", "set", "update", "values", "withCallerGlobal"
227 use: ["config", "services", "util"]
230 function Runnable(self, func, args) {
232 __proto__: Runnable.prototype,
233 run: function () { func.apply(self, args || []); }
236 Runnable.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIRunnable]);
239 * Returns a list of all of the top-level properties of an object, by
240 * way of the debugger.
242 * @param {object} obj
243 * @returns [jsdIProperty]
245 function debuggerProperties(obj) {
246 if (loaded.services && services.debugger.isOn) {
248 services.debugger.wrapValue(obj).getProperties(res, {});
254 * Iterates over the names of all of the top-level properties of an
255 * object or, if prototypes is given, all of the properties in the
256 * prototype chain below the top. Uses the debugger if possible.
258 * @param {object} obj The object to inspect.
259 * @param {boolean} properties Whether to inspect the prototype chain
261 * @returns {Generator}
263 function prototype(obj)
264 /* Temporary hack: */ typeof obj === "xml" || obj.__proto__ !== obj.__proto__ ? null :
265 obj.__proto__ || Object.getPrototypeOf(obj) ||
266 XPCNativeWrapper.unwrap(obj).__proto__ ||
267 Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
269 function properties(obj, prototypes, debugger_) {
271 let seen = { dactylPropertyNames: true };
274 if ("dactylPropertyNames" in obj && !prototypes)
275 for (let key in values(obj.dactylPropertyNames))
276 if (key in obj && !Set.add(seen, key))
281 for (; obj; obj = prototypes && prototype(obj)) {
283 if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn)
284 var iter = values(Object.getOwnPropertyNames(obj));
288 iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
290 for (let key in iter)
291 if (!prototypes || !Set.add(seen, key) && obj != orig)
296 function iterOwnProperties(obj) {
297 for (let prop in properties(obj))
298 yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
301 function deprecated(alternative, fn) {
303 return Class.Property(iter(fn).map(function ([k, v]) [k, callable(v) ? deprecated(alternative, v) : v])
306 let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
308 function deprecatedMethod() {
309 let obj = !this ? "" :
310 this.className ? this.className + "#" :
311 this.constructor.className ? this.constructor.className + "#" :
314 deprecated.warn(func, obj + (fn.name || name), alternative);
315 return func.apply(this, arguments);
318 return callable(fn) ? deprecatedMethod : Class.Property({
319 get: function () deprecatedMethod,
320 init: function (prop) { name = prop; }
323 deprecated.warn = function warn(func, name, alternative, frame) {
324 if (!func.seenCaller)
325 func.seenCaller = Set([
326 "resource://dactyl" + JSMLoader.suffix + "/javascript.jsm",
327 "resource://dactyl" + JSMLoader.suffix + "/util.jsm"
330 frame = frame || Components.stack.caller.caller;
331 let filename = util.fixURI(frame.filename || "unknown");
332 if (!Set.add(func.seenCaller, filename))
333 util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":")
334 + require("messages")._("warn.deprecated", name, alternative));
338 * Iterates over all of the top-level, iterable property names of an
341 * @param {object} obj The object to inspect.
342 * @returns {Generator}
344 function keys(obj) iter(function keys() {
346 if (hasOwnProperty.call(obj, k))
350 * Iterates over all of the top-level, iterable property values of an
353 * @param {object} obj The object to inspect.
354 * @returns {Generator}
356 function values(obj) iter(function values() {
357 if (isinstance(obj, ["Generator", "Iterator"]))
362 if (hasOwnProperty.call(obj, k))
366 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
367 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
370 * Utility for managing sets of strings. Given an array, returns an
371 * object with one key for each value thereof.
373 * @param {[string]} ary @optional
379 for (let val in values(ary))
384 * Adds an element to a set and returns true if the element was
385 * previously contained.
387 * @param {object} set The set.
388 * @param {string} key The key to add.
391 Set.add = curry(function set_add(set, key) {
392 let res = this.has(set, key);
397 * Returns true if the given set contains the given key.
399 * @param {object} set The set.
400 * @param {string} key The key to check.
403 Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
404 propertyIsEnumerable.call(set, key));
406 * Returns a new set containing the members of the first argument which
407 * do not exist in any of the other given arguments.
409 * @param {object} set The set.
412 Set.subtract = function set_subtract(set) {
413 set = update({}, set);
414 for (let i = 1; i < arguments.length; i++)
415 for (let k in keys(arguments[i]))
420 * Removes an element from a set and returns true if the element was
421 * previously contained.
423 * @param {object} set The set.
424 * @param {string} key The key to remove.
427 Set.remove = curry(function set_remove(set, key) {
428 let res = set.has(set, key);
434 deprecated.warn(set, "set", "Set");
435 return Set.apply(this, arguments);
437 Object.keys(Set).forEach(function (meth) {
438 set[meth] = function proxy() {
439 deprecated.warn(proxy, "set." + meth, "Set." + meth);
440 return Set[meth].apply(Set, arguments);
445 * Curries a function to the given number of arguments. Each
446 * call of the resulting function returns a new function. When
447 * a call does not contain enough arguments to satisfy the
448 * required number, the resulting function is another curried
449 * function with previous arguments accumulated.
451 * function foo(a, b, c) [a, b, c].join(" ");
452 * curry(foo)(1, 2, 3) -> "1 2 3";
453 * curry(foo)(4)(5, 6) -> "4 5 6";
454 * curry(foo)(7)(8)(9) -> "7 8 9";
456 * @param {function} fn The function to curry.
457 * @param {integer} length The number of arguments expected.
460 * @param {object} self The 'this' value for the returned function. When
461 * omitted, the value of 'this' from the first call to the function is
465 function curry(fn, length, self, acc) {
471 // Close over function with 'this'
472 function close(self, fn) function () fn.apply(self, Array.slice(arguments));
477 return function curried() {
478 let args = acc.concat(Array.slice(arguments));
480 // The curried result should preserve 'this'
481 if (arguments.length == 0)
482 return close(self || this, curried);
484 if (args.length >= length)
485 return fn.apply(self || this, args);
487 return curry(fn, length, self || this, args);
492 var bind = function bind(meth, self) let (func = callable(meth) ? meth : self[meth])
493 func.bind.apply(func, Array.slice(arguments, 1));
495 var bind = function bind(func, self) {
499 let args = Array.slice(arguments, bind.length);
500 return function bound() func.apply(self, args.concat(Array.slice(arguments)));
504 * Returns true if both arguments are functions and
505 * (targ() instanceof src) would also return true.
507 * @param {function} targ
508 * @param {function} src
511 function isSubclass(targ, src) {
512 return src === targ ||
513 targ && typeof targ === "function" && targ.prototype instanceof src;
517 * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
518 * an array, returns true if *object* is an instance of any element of
519 * *interfaces*. If *interfaces* is the object form of a primitive type,
520 * returns true if *object* is a non-boxed version of the type, i.e., if
521 * (typeof object == "string"), isinstance(object, String) is true. Finally, if
522 * *interfaces* is a string, returns true if ({}.toString.call(object) ==
523 * "[object <interfaces>]").
525 * @param {object} object The object to check.
526 * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
529 var isinstance_types = {
535 function isinstance(object, interfaces) {
539 return Array.concat(interfaces).some(function isinstance_some(iface) {
540 if (typeof iface === "string") {
541 if (objproto.toString.call(object) === "[object " + iface + "]")
544 else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
545 if (object.isinstance(iface))
549 if (object instanceof iface)
551 var type = isinstance_types[typeof object];
552 if (type && isSubclass(iface, type))
560 * Returns true if obj is a non-null object.
562 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
565 * Returns true if and only if its sole argument is an
566 * instance of the builtin Array type. The array may come from
567 * any window, frame, namespace, or execution context, which
568 * is not the case when using (obj instanceof Array).
572 // This is bloody stupid.
573 ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
574 : function isArray(val) objproto.toString.call(val) == "[object Array]";
577 * Returns true if and only if its sole argument is an
578 * instance of the builtin Generator type. This includes
579 * functions containing the 'yield' statement and generator
580 * statements such as (x for (x in obj)).
582 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
585 * Returns true if and only if its sole argument is a String,
586 * as defined by the builtin type. May be constructed via
587 * String(foo) or new String(foo) from any window, frame,
588 * namespace, or execution context, which is not the case when
589 * using (obj instanceof String) or (typeof obj == "string").
591 function isString(val) objproto.toString.call(val) == "[object String]";
594 * Returns true if and only if its sole argument may be called
595 * as a function. This includes classes and function objects.
597 function callable(val) typeof val === "function";
600 fn.apply(arguments[1], Array.slice(arguments, 2));
605 * Memoizes an object property value.
607 * @param {object} obj The object to add the property to.
608 * @param {string} key The property name.
609 * @param {function} getter The function which will return the initial
610 * value of the property.
612 function memoize(obj, key, getter) {
613 if (arguments.length == 1) {
614 obj = update({ __proto__: obj.__proto__ }, obj);
615 for (let prop in Object.getOwnPropertyNames(obj)) {
616 let get = __lookupGetter__.call(obj, prop);
618 memoize(obj, prop, get);
624 Object.defineProperty(obj, key, {
628 get: function g_replaceProperty() (
629 Class.replaceProperty(this.instance || this, key, null),
630 Class.replaceProperty(this.instance || this, key, getter.call(this, key))),
632 set: function s_replaceProperty(val)
633 Class.replaceProperty(this.instance || this, key, val)
637 obj[key] = getter.call(obj, key);
641 let sandbox = Cu.Sandbox(this);
642 sandbox.__proto__ = this;
644 * Wraps a function so that when called, the global object of the caller
645 * is prepended to its arguments.
647 // Hack to get around lack of access to caller in strict mode.
648 var withCallerGlobal = Cu.evalInSandbox(<![CDATA[
649 (function withCallerGlobal(fn)
650 function withCallerGlobal_wrapped()
652 [Class.objectGlobal(withCallerGlobal_wrapped.caller)]
653 .concat(Array.slice(arguments))))
654 ]]>, Cu.Sandbox(this), "1.8");
657 * Updates an object with the properties of another object. Getters
658 * and setters are copied as expected. Moreover, any function
659 * properties receive new 'supercall' and 'superapply' properties,
660 * which will call the identically named function in target's
663 * let a = { foo: function (arg) "bar " + arg }
664 * let b = { __proto__: a }
665 * update(b, { foo: function foo() foo.supercall(this, "baz") });
667 * a.foo("foo") -> "bar foo"
668 * b.foo() -> "bar baz"
670 * @param {Object} target The object to update.
671 * @param {Object} src The source object from which to update target.
672 * May be provided multiple times.
673 * @returns {Object} Returns its updated first argument.
675 function update(target) {
676 for (let i = 1; i < arguments.length; i++) {
677 let src = arguments[i];
678 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
679 let desc = Object.getOwnPropertyDescriptor(src, k);
680 if (desc.value instanceof Class.Property)
681 desc = desc.value.init(k, target) || desc.value;
683 if (typeof desc.value === "function" && target.__proto__) {
684 let func = desc.value.wrapped || desc.value;
685 if (!func.superapply) {
686 func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
687 func.superapply = function superapply(self, args)
688 let (meth = Object.getPrototypeOf(target)[k])
689 meth && meth.apply(self, args);
690 func.supercall = function supercall(self)
691 func.superapply(self, Array.slice(arguments, 1));
695 Object.defineProperty(target, k, desc);
706 * Constructs a new Class. Arguments marked as optional must be
707 * either entirely elided, or they must have the exact type
710 * @param {string} name The class's as it will appear when toString
711 * is called, as well as in stack traces.
713 * @param {function} base The base class for this module. May be any
717 * @param {Object} prototype The prototype for instances of this
718 * object. The object itself is copied and not used as a prototype
720 * @param {Object} classProperties The class properties for the new
721 * module constructor. More than one may be provided.
724 * @returns {function} The constructor for the resulting class.
728 var args = Array.slice(arguments);
729 if (isString(args[0]))
730 var name = args.shift();
731 var superclass = Class;
732 if (callable(args[0]))
733 superclass = args.shift();
735 if (loaded.util && util.haveGecko("6.0a1")) // Bug 657418.
736 var Constructor = function Constructor() {
737 var self = Object.create(Constructor.prototype, {
738 constructor: { value: Constructor },
740 self.instance = self;
741 var res = self.init.apply(self, arguments);
742 return res !== undefined ? res : self;
745 var Constructor = eval(String.replace(<![CDATA[
746 (function constructor(PARAMS) {
747 var self = Object.create(Constructor.prototype, {
748 constructor: { value: Constructor },
750 self.instance = self;
751 var res = self.init.apply(self, arguments);
752 return res !== undefined ? res : self;
754 "constructor", (name || superclass.className).replace(/\W/g, "_"))
755 .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
756 .replace(/\b(self|res|Constructor)\b/g, "$1_")));
758 Constructor.className = name || superclass.className || superclass.name;
760 if ("init" in superclass.prototype)
761 Constructor.__proto__ = superclass;
763 let superc = superclass;
764 superclass = function Shim() {};
765 Class.extend(superclass, superc, {
768 superclass.__proto__ = superc;
771 Class.extend(Constructor, superclass, args[0]);
772 update(Constructor, args[1]);
773 Constructor.__proto__ = superclass;
774 args = args.slice(2);
775 Array.forEach(args, function (obj) {
778 update(Constructor.prototype, obj);
783 if (Cu.getGlobalForObject)
784 Class.objectGlobal = function (object) {
786 return Cu.getGlobalForObject(object);
793 Class.objectGlobal = function (object) {
794 while (object.__parent__)
795 object = object.__parent__;
800 * @class Class.Property
801 * A class which, when assigned to a property in a Class's prototype
802 * or class property object, defines that property's descriptor
803 * rather than its value. If the desc object has an init property, it
804 * will be called with the property's name before the descriptor is
807 * @param {Object} desc The property descriptor.
809 Class.Property = function Property(desc) update(
810 Object.create(Property.prototype), desc || { configurable: true, writable: true });
811 Class.Property.prototype.init = function () {};
813 * Extends a subclass with a superclass. The subclass's
814 * prototype is replaced with a new object, which inherits
815 * from the superclass's prototype, {@see update}d with the
816 * members of *overrides*.
818 * @param {function} subclass
819 * @param {function} superclass
820 * @param {Object} overrides @optional
822 Class.extend = function extend(subclass, superclass, overrides) {
823 subclass.superclass = superclass;
825 subclass.prototype = Object.create(superclass.prototype);
826 update(subclass.prototype, overrides);
827 subclass.prototype.constructor = subclass;
828 subclass.prototype._class_ = subclass;
830 if (superclass.prototype.constructor === objproto.constructor)
831 superclass.prototype.constructor = superclass;
835 * Memoizes the value of a class property to the value returned by
836 * the passed function the first time the property is accessed.
838 * @param {function(string)} getter The function which returns the
840 * @returns {Class.Property}
842 Class.memoize = function memoize(getter, wait)
846 init: function (key) {
850 this.get = function replace() {
851 let obj = this.instance || this;
852 Object.defineProperty(obj, key, {
853 configurable: true, enumerable: false,
854 get: function get() {
855 util.waitFor(function () done);
860 util.yieldable(function () {
862 for (var res in getter.call(obj)) {
863 if (wait !== undefined)
867 Class.replaceProperty(obj, key, res);
874 this.get = function replace() {
875 let obj = this.instance || this;
876 Class.replaceProperty(obj, key, null);
877 return Class.replaceProperty(obj, key, getter.call(this, key));
880 this.set = function replace(val) Class.replaceProperty(this.instance || this, val);
884 Class.replaceProperty = function replaceProperty(obj, prop, value) {
885 Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
888 Class.toString = function toString() "[class " + this.className + "]";
891 * Initializes new instances of this class. Called automatically
892 * when new instances are created.
894 init: function c_init() {},
896 withSavedValues: function withSavedValues(names, callback, self) {
897 let vals = names.map(function (name) this[name], this);
899 return callback.call(self || this);
902 names.forEach(function (name, i) this[name] = vals[i], this);
906 toString: function C_toString() {
907 if (this.toStringParams)
908 var params = "(" + this.toStringParams.map(function (m) isArray(m) ? "[" + m + "]" :
909 isString(m) ? m.quote() : String(m))
911 return "[instance " + this.constructor.className + (params || "") + "]";
915 * Executes *callback* after *timeout* milliseconds. The value of
916 * 'this' is preserved in the invocation of *callback*.
918 * @param {function} callback The function to call after *timeout*
919 * @param {number} timeout The time, in milliseconds, to wait
920 * before calling *callback*.
921 * @returns {nsITimer} The timer which backs this timeout.
923 timeout: function timeout(callback, timeout) {
925 function timeout_notify(timer) {
927 util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
929 util.trapErrors(callback, self);
931 return services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
935 * Updates this instance with the properties of the given objects.
936 * Like the update function, but with special semantics for
937 * localized properties.
939 update: function update() {
943 for (let i = 0; i < arguments.length; i++) {
944 let src = arguments[i];
945 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
946 let desc = Object.getOwnPropertyDescriptor(src, k);
947 if (desc.value instanceof Class.Property)
948 desc = desc.value.init(k, this) || desc.value;
950 if (typeof desc.value === "function") {
951 let func = desc.value.wrapped || desc.value;
952 if (!func.superapply) {
953 func.__defineGetter__("super", function () Object.getPrototypeOf(self)[k]);
954 func.superapply = function superapply(self, args)
955 let (meth = Object.getPrototypeOf(self)[k])
956 meth && meth.apply(self, args);
957 func.supercall = function supercall(self)
958 func.superapply(self, Array.slice(arguments, 1));
963 if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties))
964 this[k] = desc.value;
966 Object.defineProperty(this, k, desc);
973 magicalProperties: {}
975 Class.makeClosure = function makeClosure() {
977 function closure(fn) {
978 function _closure() {
980 return fn.apply(self, arguments);
982 catch (e if !(e instanceof FailedAssertion)) {
984 throw e.stack ? e : Error(e);
987 _closure.wrapped = fn;
991 iter(properties(this), properties(this, true)).forEach(function (k) {
992 if (!__lookupGetter__.call(this, k) && callable(this[k]))
993 closure[k] = closure(this[k]);
994 else if (!(k in closure))
995 Object.defineProperty(closure, k, {
998 get: function get_proxy() self[k],
999 set: function set_proxy(val) self[k] = val,
1005 memoize(Class.prototype, "closure", Class.makeClosure);
1008 * A base class generator for classes which implement XPCOM interfaces.
1010 * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
1012 * @param {Class} superClass A super class. @optional
1015 function XPCOM(interfaces, superClass) {
1016 interfaces = Array.concat(interfaces);
1018 let shim = interfaces.reduce(function (shim, iface) shim.QueryInterface(iface),
1019 Cc["@dactyl.googlecode.com/base/xpc-interface-shim"].createInstance());
1021 let res = Class("XPCOM(" + interfaces + ")", superClass || Class, update(
1022 iter.toObject([k, v === undefined || callable(v) ? function stub() null : v]
1023 for ([k, v] in Iterator(shim))),
1024 { QueryInterface: XPCOMUtils.generateQI(interfaces) }));
1025 shim = interfaces = null;
1030 * An abstract base class for classes that wish to inherit from Error.
1032 var ErrorBase = Class("ErrorBase", Error, {
1034 init: function EB_init(message, level) {
1036 let error = Error(message);
1038 this.stack = error.stack;
1039 this.message = message;
1041 let frame = Components.stack;
1042 for (let i = 0; i < this.level + level; i++) {
1043 frame = frame.caller;
1044 this.stack = this.stack.replace(/^.*\n/, "");
1046 this.fileName = frame.filename;
1047 this.lineNumber = frame.lineNumber;
1049 toString: function () String(this.message)
1053 * Constructs a new Module class and instantiates an instance into the current
1054 * module global object.
1056 * @param {string} name The name of the instance.
1057 * @param {Object} prototype The instance prototype.
1058 * @param {Object} classProperties Properties to be applied to the class constructor.
1061 function Module(name, prototype) {
1062 let init = callable(prototype) ? 4 : 3;
1063 const module = Class.apply(Class, Array.slice(arguments, 0, init));
1064 let instance = module();
1065 module.className = name.toLowerCase();
1067 instance.INIT = update(Object.create(Module.INIT),
1068 arguments[init] || {});
1070 currentModule[module.className] = instance;
1071 defineModule.modules.push(instance);
1075 init: function Module_INIT_init(dactyl, modules, window) {
1076 let args = arguments;
1079 for (let local = this.Local; local; local = local.super)
1082 if (locals.length) {
1083 let module = this, objs = {};
1084 for (let i in locals) {
1085 module = objs[i] = Object.create(module);
1086 module.modules = modules;
1088 module.isLocalModule = true;
1090 modules.jsmodules[this.constructor.className] = module;
1091 locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args)))
1093 memoize(module, "closure", Class.makeClosure);
1094 module.instance = module;
1098 modules.dactyl.registerObservers(module);
1106 * Creates a new Struct constructor, used for creating objects with
1107 * a fixed set of named members. Each argument should be the name of
1108 * a member in the resulting objects. These names will correspond to
1109 * the arguments passed to the resultant constructor. Instances of
1110 * the new struct may be treated very much like arrays, and provide
1111 * many of the same methods.
1113 * const Point = Struct("x", "y", "z");
1114 * let p1 = Point(x, y, z);
1116 * @returns {function} The constructor for the new Struct.
1119 if (!/^[A-Z]/.test(arguments[0]))
1120 var args = Array.slice(arguments, 0);
1122 var className = arguments[0];
1123 args = Array.slice(arguments, 1);
1126 const Struct = Class(className || "Struct", StructBase, {
1127 length: args.length,
1128 members: array.toObject(args.map(function (v, k) [v, k]))
1130 args.forEach(function (name, i) {
1131 Struct.prototype.__defineGetter__(name, function () this[i]);
1132 Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1136 let StructBase = Class("StructBase", Array, {
1137 init: function struct_init() {
1138 for (let i = 0; i < arguments.length; i++)
1139 if (arguments[i] != undefined)
1140 this[i] = arguments[i];
1143 clone: function struct_clone() this.constructor.apply(null, this.slice()),
1145 closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1147 get: function struct_get(key, val) this[this.members[key]],
1148 set: function struct_set(key, val) this[this.members[key]] = val,
1150 toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1152 // Iterator over our named members
1153 __iterator__: function struct__iterator__() {
1155 return ([k, self[k]] for (k in keys(self.members)))
1158 fromArray: function fromArray(ary) {
1159 if (!(ary instanceof this))
1160 ary.__proto__ = this.prototype;
1165 * Sets a lazily constructed default value for a member of
1166 * the struct. The value is constructed once, the first time
1167 * it is accessed and memoized thereafter.
1169 * @param {string} key The name of the member for which to
1170 * provide the default value.
1171 * @param {function} val The function which is to generate
1172 * the default value.
1174 defaultValue: function defaultValue(key, val) {
1175 let i = this.prototype.members[key];
1176 this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1177 this.prototype.__defineSetter__(i, function (value)
1178 Class.replaceProperty(this, i, value));
1182 localize: function localize(key, defaultValue) {
1183 let i = this.prototype.members[key];
1184 Object.defineProperty(this.prototype, i, require("messages").Messages.Localized(defaultValue).init(key, this.prototype));
1189 var Timer = Class("Timer", {
1190 init: function init(minInterval, maxInterval, callback, self) {
1191 this._timer = services.Timer();
1192 this.callback = callback;
1193 this.self = self || this;
1194 this.minInterval = minInterval;
1195 this.maxInterval = maxInterval;
1200 notify: function notify(timer, force) {
1202 if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1205 this._timer.cancel();
1207 // minInterval is the time between the completion of the command and the next firing
1208 this.doneAt = Date.now() + this.minInterval;
1210 this.callback.call(this.self, this.arg);
1213 if (typeof util === "undefined")
1214 dump("dactyl: " + e + "\n" + (e.stack || Error().stack));
1216 util.reportError(e);
1219 this.doneAt = Date.now() + this.minInterval;
1223 tell: function tell(arg) {
1224 if (arguments.length > 0)
1227 let now = Date.now();
1228 if (this.doneAt == -1)
1229 this._timer.cancel();
1231 let timeout = this.minInterval;
1232 if (now > this.doneAt && this.doneAt > -1)
1234 else if (this.latest)
1235 timeout = Math.min(timeout, this.latest - now);
1237 this.latest = now + this.maxInterval;
1239 this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1243 reset: function reset() {
1244 this._timer.cancel();
1248 flush: function flush(force) {
1249 if (this.doneAt == -1 || force)
1250 this.notify(null, true);
1255 * Idempotent function which returns the UTF-8 encoded value of an
1256 * improperly-decoded string.
1258 * @param {string} str
1261 function UTF8(str) {
1263 return decodeURIComponent(escape(str));
1270 function octal(decimal) parseInt(decimal, 8);
1273 * Iterates over an arbitrary object. The following iterator types are
1274 * supported, and work as a user would expect:
1276 * • nsIDOMNodeIterator
1277 * • mozIStorageStatement
1279 * Additionally, the following array-like objects yield a tuple of the
1280 * form [index, element] for each contained element:
1282 * • nsIDOMHTMLCollection
1283 * • nsIDOMNodeList
1285 * and the following likewise yield one element of the form
1286 * [name, element] for each contained element:
1288 * • nsIDOMNamedNodeMap
1290 * Duck typing is implemented for any other type. If the object
1291 * contains the "enumerator" property, iter is called on that. If the
1292 * property is a function, it is called first. If it contains the
1293 * property "getNext" along with either "hasMoreItems" or "hasMore", it
1294 * is iterated over appropriately.
1296 * For all other cases, this function behaves exactly like the Iterator
1299 * @param {object} obj
1300 * @returns {Generator}
1302 function iter(obj) {
1303 let args = arguments;
1304 let res = Iterator(obj);
1306 if (args.length > 1)
1307 res = (function () {
1308 for (let i = 0; i < args.length; i++)
1309 for (let j in iter(args[i]))
1312 else if (isinstance(obj, ["Iterator", "Generator"]))
1314 else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1315 while (obj.constructor instanceof ctypes.PointerType)
1317 if (obj.constructor instanceof ctypes.ArrayType)
1318 res = array.iterItems(obj);
1319 else if (obj.constructor instanceof ctypes.StructType)
1320 res = (function () {
1321 for (let prop in values(obj.constructor.fields))
1322 yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1327 else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1328 res = array.iterItems(obj);
1329 else if (obj instanceof Ci.nsIDOMNamedNodeMap)
1330 res = (function () {
1331 for (let i = 0; i < obj.length; i++)
1332 yield [obj.name, obj];
1334 else if (obj instanceof Ci.mozIStorageStatement)
1335 res = (function (obj) {
1336 while (obj.executeStep())
1340 else if ("getNext" in obj) {
1341 if ("hasMoreElements" in obj)
1342 res = (function () {
1343 while (obj.hasMoreElements())
1344 yield obj.getNext();
1346 else if ("hasMore" in obj)
1347 res = (function () {
1348 while (obj.hasMore())
1349 yield obj.getNext();
1352 else if ("enumerator" in obj) {
1353 if (callable(obj.enumerator))
1354 return iter(obj.enumerator());
1355 return iter(obj.enumerator);
1357 res.__noSuchMethod__ = function __noSuchMethod__(meth, args) {
1359 var res = iter[meth].apply(iter, [this].concat(args));
1361 res = let (ary = array(this))
1362 ary[meth] ? ary[meth].apply(ary, args) : ary.__noSuchMethod__(meth, args);
1363 if (isinstance(res, ["Iterator", "Generator"]))
1370 toArray: function toArray(iter) array(iter).array,
1372 // See array.prototype for API docs.
1373 toObject: function toObject(iter) {
1375 for (let [k, v] in iter)
1376 if (v instanceof Class.Property)
1377 Object.defineProperty(obj, k, v.init(k, obj) || v);
1383 compact: function compact(iter) (item for (item in iter) if (item != null)),
1385 every: function every(iter, pred, self) {
1386 pred = pred || util.identity;
1387 for (let elem in iter)
1388 if (!pred.call(self, elem))
1392 some: function every(iter, pred, self) {
1393 pred = pred || util.identity;
1394 for (let elem in iter)
1395 if (pred.call(self, elem))
1400 filter: function filter(iter, pred, self) {
1401 for (let elem in iter)
1402 if (pred.call(self, elem))
1407 * Iterates over an iterable object and calls a callback for each
1410 * @param {object} iter The iterator.
1411 * @param {function} fn The callback.
1412 * @param {object} self The this object for *fn*.
1414 forEach: function forEach(iter, func, self) {
1415 for (let val in iter)
1416 func.call(self, val);
1419 indexOf: function indexOf(iter, elem) {
1421 for (let item in iter) {
1429 * Returns the array that results from applying *func* to each property of
1432 * @param {Object} obj
1433 * @param {function} func
1436 map: function map(iter, func, self) {
1438 yield func.call(self, i);
1442 * Returns the nth member of the given array that matches the
1445 nth: function nth(iter, pred, n, self) {
1446 if (typeof pred === "number")
1447 [pred, n] = [function () true, pred]; // Hack.
1449 for (let elem in iter)
1450 if (pred.call(self, elem) && n-- === 0)
1455 sort: function sort(iter, fn, self)
1456 array(this.toArray(iter).sort(fn, self)),
1458 uniq: function uniq(iter) {
1460 for (let item in iter)
1461 if (!Set.add(seen, item))
1466 * Zips the contents of two arrays. The resulting array is the length of
1467 * ary1, with any shortcomings of ary2 replaced with null strings.
1469 * @param {Array} ary1
1470 * @param {Array} ary2
1473 zip: function zip(iter1, iter2) {
1475 yield [iter1.next(), iter2.next()];
1477 catch (e if e instanceof StopIteration) {}
1482 * Array utility methods.
1484 var array = Class("array", Array, {
1485 init: function (ary) {
1486 if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1487 ary = [k for (k in ary)];
1488 else if (ary.length)
1489 ary = Array.slice(ary);
1493 __iterator__: function () this.iterItems(),
1494 __noSuchMethod__: function (meth, args) {
1495 var res = array[meth].apply(null, [this.array].concat(args));
1498 if (isinstance(res, ["Iterator", "Generator"]))
1503 toString: function () this.array.toString(),
1504 concat: function () this.__noSuchMethod__("concat", Array.slice(arguments)),
1505 filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)),
1506 map: function () this.__noSuchMethod__("map", Array.slice(arguments))
1511 * Converts an array to an object. As in lisp, an assoc is an
1512 * array of key-value pairs, which maps directly to an object,
1514 * [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1516 * @param {[Array]} assoc
1517 * @... {string} 0 - Key
1520 toObject: function toObject(assoc) {
1522 assoc.forEach(function ([k, v]) {
1523 if (v instanceof Class.Property)
1524 Object.defineProperty(obj, k, v.init(k, obj) || v);
1532 * Compacts an array, removing all elements that are null or undefined:
1533 * ["foo", null, "bar", undefined] -> ["foo", "bar"]
1535 * @param {Array} ary
1538 compact: function compact(ary) ary.filter(function (item) item != null),
1541 * Returns true if each element of ary1 is equal to the
1542 * corresponding element in ary2.
1544 * @param {Array} ary1
1545 * @param {Array} ary2
1546 * @returns {boolean}
1548 equals: function (ary1, ary2)
1549 ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1552 * Flattens an array, such that all elements of the array are
1553 * joined into a single array:
1554 * [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1556 * @param {Array} ary
1559 flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1562 * Returns an Iterator for an array's values.
1564 * @param {Array} ary
1565 * @returns {Iterator(Object)}
1567 iterValues: function iterValues(ary) {
1568 for (let i = 0; i < ary.length; i++)
1573 * Returns an Iterator for an array's indices and values.
1575 * @param {Array} ary
1576 * @returns {Iterator([{number}, {Object}])}
1578 iterItems: function iterItems(ary) {
1579 let length = ary.length;
1580 for (let i = 0; i < length; i++)
1585 * Returns the nth member of the given array that matches the
1588 nth: function nth(ary, pred, n, self) {
1589 for (let elem in values(ary))
1590 if (pred.call(self, elem) && n-- === 0)
1596 * Filters out all duplicates from an array. If *unsorted* is false, the
1597 * array is sorted before duplicates are removed.
1599 * @param {Array} ary
1600 * @param {boolean} unsorted
1603 uniq: function uniq(ary, unsorted) {
1606 for (let item in values(ary))
1607 if (res.indexOf(item) == -1)
1611 for (let [, item] in Iterator(ary.sort())) {
1612 if (item != last || !res.length)
1621 * Zips the contents of two arrays. The resulting array is the length of
1622 * ary1, with any shortcomings of ary2 replaced with null strings.
1624 * @param {Array} ary1
1625 * @param {Array} ary2
1628 zip: function zip(ary1, ary2) {
1630 for (let [i, item] in Iterator(ary1))
1631 res.push([item, i in ary2 ? ary2[i] : ""]);
1638 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1640 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: