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) {
40 let value = desc.value;
42 if (desc.writable && !__lookupGetter__.call(obj, prop)
43 && !__lookupSetter__.call(obj, prop))
47 catch (e if e instanceof TypeError) {}
49 objproto.__defineGetter__.call(obj, prop, function () value);
51 objproto.__defineSetter__.call(obj, prop, function (val) { value = val; });
55 objproto.__defineGetter__.call(obj, prop, desc.get);
57 objproto.__defineSetter__.call(obj, prop, desc.set);
59 if (!Object.defineProperties)
60 Object.defineProperties = function defineProperties(obj, props) {
61 for (let [k, v] in Iterator(props))
62 Object.defineProperty(obj, k, v);
65 Object.freeze = function freeze(obj) {};
66 if (!Object.getOwnPropertyDescriptor)
67 Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
68 if (!hasOwnProperty.call(obj, prop))
72 enumerable: propertyIsEnumerable.call(obj, prop)
74 var get = __lookupGetter__.call(obj, prop),
75 set = __lookupSetter__.call(obj, prop);
77 desc.value = obj[prop];
86 if (!Object.getOwnPropertyNames)
87 Object.getOwnPropertyNames = function getOwnPropertyNames(obj, _debugger) {
88 // This is an ugly and unfortunately necessary hack.
89 if (hasOwnProperty.call(obj, "__iterator__")) {
90 var oldIter = obj.__iterator__;
91 delete obj.__iterator__;
93 let res = [k for (k in obj) if (hasOwnProperty.call(obj, k))];
94 if (oldIter !== undefined) {
95 obj.__iterator__ = oldIter;
96 res.push("__iterator__");
100 if (!Object.getPrototypeOf)
101 Object.getPrototypeOf = function getPrototypeOf(obj) obj.__proto__;
103 Object.keys = function keys(obj)
104 Object.getOwnPropertyNames(obj).filter(function (k) propertyIsEnumerable.call(obj, k));
106 let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__;
112 function defineModule(name, params, module) {
114 module = getGlobalForObject(params);
117 module.EXPORTED_SYMBOLS = params.exports || [];
118 defineModule.loadLog.push("defineModule " + name);
119 for (let [, mod] in Iterator(params.require || []))
120 require(module, mod);
122 for (let [, mod] in Iterator(params.use || []))
123 if (loaded.hasOwnProperty(mod))
124 require(module, mod, "use");
126 use[mod] = use[mod] || [];
127 use[mod].push(module);
129 currentModule = module;
132 defineModule.loadLog = [];
133 Object.defineProperty(defineModule.loadLog, "push", {
134 value: function (val) {
136 defineModule.dump(val + "\n");
137 this[this.length] = Date.now() + " " + val;
140 defineModule.dump = function dump_() {
141 let msg = Array.map(arguments, function (msg) {
142 if (loaded.util && typeof msg == "object")
143 msg = util.objectToString(msg);
146 let name = loaded.config ? config.name : "dactyl";
147 dump(String.replace(msg, /\n?$/, "\n")
148 .replace(/^./gm, name + ": $&"));
150 defineModule.modules = [];
151 defineModule.times = { all: 0 };
152 defineModule.time = function time(major, minor, func, self) {
153 let time = Date.now();
154 if (typeof func !== "function")
158 var res = func.apply(self, Array.slice(arguments, 4));
161 loaded.util && util.reportError(e);
164 let delta = Date.now() - time;
165 defineModule.times.all += delta;
166 defineModule.times[major] = (defineModule.times[major] || 0) + delta;
168 defineModule.times[":" + minor] = (defineModule.times[":" + minor] || 0) + delta;
169 defineModule.times[major + ":" + minor] = (defineModule.times[major + ":" + minor] || 0) + delta;
174 function endModule() {
175 defineModule.loadLog.push("endModule " + currentModule.NAME);
177 for (let [, mod] in Iterator(use[currentModule.NAME] || []))
178 require(mod, currentModule.NAME, "use");
180 loaded[currentModule.NAME] = 1;
183 function require(obj, name, from) {
185 if (arguments.length === 1)
186 [obj, name] = [{}, obj];
189 defineModule.loadLog.push((from || "require") + ": loading " + name + (obj.NAME ? " into " + obj.NAME : ""));
191 JSMLoader.load(name + ".jsm", obj);
195 defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
199 defineModule.dump(" " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e +"\n");
203 defineModule("base", {
204 // sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt
206 "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Module", "JSMLoader", "Object", "Runnable",
207 "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMUtils", "XPCSafeJSObjectWrapper",
208 "array", "bind", "call", "callable", "ctypes", "curry", "debuggerProperties", "defineModule",
209 "deprecated", "endModule", "forEach", "isArray", "isGenerator", "isinstance", "isObject",
210 "isString", "isSubclass", "iter", "iterAll", "iterOwnProperties","keys", "memoize", "octal",
211 "properties", "require", "set", "update", "values", "withCallerGlobal"
213 use: ["config", "services", "util"]
216 function Runnable(self, func, args) {
218 __proto__: Runnable.prototype,
219 run: function () { func.apply(self, args || []); }
222 Runnable.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIRunnable]);
225 * Returns a list of all of the top-level properties of an object, by
226 * way of the debugger.
228 * @param {object} obj
229 * @returns [jsdIProperty]
231 function debuggerProperties(obj) {
232 if (loaded.services && services.debugger.isOn) {
234 services.debugger.wrapValue(obj).getProperties(res, {});
240 * Iterates over the names of all of the top-level properties of an
241 * object or, if prototypes is given, all of the properties in the
242 * prototype chain below the top. Uses the debugger if possible.
244 * @param {object} obj The object to inspect.
245 * @param {boolean} properties Whether to inspect the prototype chain
247 * @returns {Generator}
249 function prototype(obj)
250 /* Temporary hack: */ typeof obj === "xml" || obj.__proto__ !== obj.__proto__ ? null :
251 obj.__proto__ || Object.getPrototypeOf(obj) ||
252 XPCNativeWrapper.unwrap(obj).__proto__ ||
253 Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
255 function properties(obj, prototypes, debugger_) {
257 let seen = { dactylPropertyNames: true };
260 if ("dactylPropertyNames" in obj && !prototypes)
261 for (let key in values(obj.dactylPropertyNames))
262 if (key in obj && !set.add(seen, key))
267 for (; obj; obj = prototypes && prototype(obj)) {
269 if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn)
270 var iter = values(Object.getOwnPropertyNames(obj));
274 iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
276 for (let key in iter)
277 if (!prototypes || !set.add(seen, key) && obj != orig)
282 function iterOwnProperties(obj) {
283 for (let prop in properties(obj))
284 yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
287 function deprecated(alternative, fn) {
289 return Class.Property(iter(fn).map(function ([k, v]) [k, callable(v) ? deprecated(alternative, v) : v])
292 let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
294 function deprecatedMethod() {
295 let obj = this.className ? this.className + "#" :
296 this.constructor.className ? this.constructor.className + "#" :
299 deprecated.warn(func, obj + (fn.name || name), alternative);
300 return func.apply(this, arguments);
303 return callable(fn) ? deprecatedMethod : Class.Property({
304 get: function () deprecatedMethod,
305 init: function (prop) { name = prop; }
308 deprecated.warn = function warn(func, name, alternative, frame) {
309 if (!func.seenCaller)
310 func.seenCaller = set([
311 "resource://dactyl" + JSMLoader.suffix + "/javascript.jsm",
312 "resource://dactyl" + JSMLoader.suffix + "/util.jsm"
315 frame = frame || Components.stack.caller.caller;
316 let filename = util.fixURI(frame.filename || "unknown");
317 if (!set.add(func.seenCaller, filename))
318 util.dactyl(func).warn(
319 util.urlPath(filename) + ":" + frame.lineNumber + ": " +
320 name + " is deprecated: Please use " + alternative + " instead");
324 * Iterates over all of the top-level, iterable property names of an
327 * @param {object} obj The object to inspect.
328 * @returns {Generator}
330 function keys(obj) iter(function keys() {
332 if (hasOwnProperty.call(obj, k))
336 * Iterates over all of the top-level, iterable property values of an
339 * @param {object} obj The object to inspect.
340 * @returns {Generator}
342 function values(obj) iter(function values() {
343 if (isinstance(obj, ["Generator", "Iterator"]))
348 if (hasOwnProperty.call(obj, k))
352 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
353 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
356 * Utility for managing sets of strings. Given an array, returns an
357 * object with one key for each value thereof.
359 * @param {[string]} ary @optional
365 for (let val in values(ary))
370 * Adds an element to a set and returns true if the element was
371 * previously contained.
373 * @param {object} set The set.
374 * @param {string} key The key to add.
377 set.add = curry(function set_add(set, key) {
378 let res = this.has(set, key);
383 * Returns true if the given set contains the given key.
385 * @param {object} set The set.
386 * @param {string} key The key to check.
389 set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
390 propertyIsEnumerable.call(set, key));
392 * Returns a new set containing the members of the first argument which
393 * do not exist in any of the other given arguments.
395 * @param {object} set The set.
398 set.subtract = function set_subtract(set) {
399 set = update({}, set);
400 for (let i = 1; i < arguments.length; i++)
401 for (let k in keys(arguments[i]))
406 * Removes an element from a set and returns true if the element was
407 * previously contained.
409 * @param {object} set The set.
410 * @param {string} key The key to remove.
413 set.remove = curry(function set_remove(set, key) {
414 let res = set.has(set, key);
420 * Curries a function to the given number of arguments. Each
421 * call of the resulting function returns a new function. When
422 * a call does not contain enough arguments to satisfy the
423 * required number, the resulting function is another curried
424 * function with previous arguments accumulated.
426 * function foo(a, b, c) [a, b, c].join(" ");
427 * curry(foo)(1, 2, 3) -> "1 2 3";
428 * curry(foo)(4)(5, 6) -> "4 5 6";
429 * curry(foo)(7)(8)(9) -> "7 8 9";
431 * @param {function} fn The function to curry.
432 * @param {integer} length The number of arguments expected.
435 * @param {object} self The 'this' value for the returned function. When
436 * omitted, the value of 'this' from the first call to the function is
440 function curry(fn, length, self, acc) {
446 // Close over function with 'this'
447 function close(self, fn) function () fn.apply(self, Array.slice(arguments));
452 return function curried() {
453 let args = acc.concat(Array.slice(arguments));
455 // The curried result should preserve 'this'
456 if (arguments.length == 0)
457 return close(self || this, curried);
459 if (args.length >= length)
460 return fn.apply(self || this, args);
462 return curry(fn, length, self || this, args);
467 var bind = function bind(func) func.bind.apply(func, Array.slice(arguments, bind.length));
469 var bind = function bind(func, self) {
470 let args = Array.slice(arguments, bind.length);
471 return function bound() func.apply(self, args.concat(Array.slice(arguments)));
475 * Returns true if both arguments are functions and
476 * (targ() instanceof src) would also return true.
478 * @param {function} targ
479 * @param {function} src
482 function isSubclass(targ, src) {
483 return src === targ ||
484 targ && typeof targ === "function" && targ.prototype instanceof src;
488 * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
489 * an array, returns true if *object* is an instance of any element of
490 * *interfaces*. If *interfaces* is the object form of a primitive type,
491 * returns true if *object* is a non-boxed version of the type, i.e., if
492 * (typeof object == "string"), isinstance(object, String) is true. Finally, if
493 * *interfaces* is a string, returns true if ({}.toString.call(object) ==
494 * "[object <interfaces>]").
496 * @param {object} object The object to check.
497 * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
500 var isinstance_types = {
506 function isinstance(object, interfaces) {
510 return Array.concat(interfaces).some(function isinstance_some(iface) {
511 if (typeof iface === "string") {
512 if (objproto.toString.call(object) === "[object " + iface + "]")
515 else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
516 if (object.isinstance(iface))
520 if (object instanceof iface)
522 var type = isinstance_types[typeof object];
523 if (type && isSubclass(iface, type))
531 * Returns true if obj is a non-null object.
533 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
536 * Returns true if and only if its sole argument is an
537 * instance of the builtin Array type. The array may come from
538 * any window, frame, namespace, or execution context, which
539 * is not the case when using (obj instanceof Array).
543 // This is bloody stupid.
544 ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
545 : function isArray(val) objproto.toString.call(val) == "[object Array]";
548 * Returns true if and only if its sole argument is an
549 * instance of the builtin Generator type. This includes
550 * functions containing the 'yield' statement and generator
551 * statements such as (x for (x in obj)).
553 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
556 * Returns true if and only if its sole argument is a String,
557 * as defined by the builtin type. May be constructed via
558 * String(foo) or new String(foo) from any window, frame,
559 * namespace, or execution context, which is not the case when
560 * using (obj instanceof String) or (typeof obj == "string").
562 function isString(val) objproto.toString.call(val) == "[object String]";
565 * Returns true if and only if its sole argument may be called
566 * as a function. This includes classes and function objects.
568 function callable(val) typeof val === "function";
571 fn.apply(arguments[1], Array.slice(arguments, 2));
576 * Memoizes an object property value.
578 * @param {object} obj The object to add the property to.
579 * @param {string} key The property name.
580 * @param {function} getter The function which will return the initial
581 * value of the property.
583 function memoize(obj, key, getter) {
584 if (arguments.length == 1) {
585 obj = update({}, obj);
586 for (let prop in Object.getOwnPropertyNames(obj)) {
587 let get = __lookupGetter__.call(obj, prop);
589 memoize(obj, prop, get);
594 Object.defineProperty(obj, key, {
598 get: function g_replaceProperty() (
599 Class.replaceProperty(this.instance || this, key, null),
600 Class.replaceProperty(this.instance || this, key, getter.call(this, key))),
602 set: function s_replaceProperty(val)
603 Class.replaceProperty(this.instance || this, key, val)
607 let sandbox = Cu.Sandbox(this);
608 sandbox.__proto__ = this;
610 * Wraps a function so that when called, the global object of the caller
611 * is prepended to its arguments.
613 // Hack to get around lack of access to caller in strict mode.
614 var withCallerGlobal = Cu.evalInSandbox(<![CDATA[
615 (function withCallerGlobal(fn)
616 function withCallerGlobal_wrapped()
618 [Class.objectGlobal(withCallerGlobal_wrapped.caller)]
619 .concat(Array.slice(arguments))))
620 ]]>, Cu.Sandbox(this), "1.8");
623 * Updates an object with the properties of another object. Getters
624 * and setters are copied as expected. Moreover, any function
625 * properties receive new 'supercall' and 'superapply' properties,
626 * which will call the identically named function in target's
629 * let a = { foo: function (arg) "bar " + arg }
630 * let b = { __proto__: a }
631 * update(b, { foo: function foo() foo.supercall(this, "baz") });
633 * a.foo("foo") -> "bar foo"
634 * b.foo() -> "bar baz"
636 * @param {Object} target The object to update.
637 * @param {Object} src The source object from which to update target.
638 * May be provided multiple times.
639 * @returns {Object} Returns its updated first argument.
641 function update(target) {
642 for (let i = 1; i < arguments.length; i++) {
643 let src = arguments[i];
644 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
645 let desc = Object.getOwnPropertyDescriptor(src, k);
646 if (desc.value instanceof Class.Property)
647 desc = desc.value.init(k, target) || desc.value;
649 if (typeof desc.value === "function" && target.__proto__) {
650 let func = desc.value.wrapped || desc.value;
651 func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
652 func.superapply = function superapply(self, args)
653 let (meth = Object.getPrototypeOf(target)[k])
654 meth && meth.apply(self, args);
655 func.supercall = function supercall(self)
656 func.superapply(self, Array.slice(arguments, 1));
659 Object.defineProperty(target, k, desc);
670 * Constructs a new Class. Arguments marked as optional must be
671 * either entirely elided, or they must have the exact type
674 * @param {string} name The class's as it will appear when toString
675 * is called, as well as in stack traces.
677 * @param {function} base The base class for this module. May be any
681 * @param {Object} prototype The prototype for instances of this
682 * object. The object itself is copied and not used as a prototype
684 * @param {Object} classProperties The class properties for the new
685 * module constructor. More than one may be provided.
688 * @returns {function} The constructor for the resulting class.
692 var args = Array.slice(arguments);
693 if (isString(args[0]))
694 var name = args.shift();
695 var superclass = Class;
696 if (callable(args[0]))
697 superclass = args.shift();
699 var Constructor = eval(String.replace(<![CDATA[
700 (function constructor(PARAMS) {
701 var self = Object.create(Constructor.prototype, {
702 constructor: { value: Constructor },
704 self.instance = self;
705 var res = self.init.apply(self, arguments);
706 return res !== undefined ? res : self;
708 "constructor", (name || superclass.className).replace(/\W/g, "_"))
709 .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
710 .replace(/\b(self|res|Constructor)\b/g, "$1_")));
712 Constructor.className = name || superclass.className || superclass.name;
714 if ("init" in superclass.prototype)
715 Constructor.__proto__ = superclass;
717 let superc = superclass;
718 superclass = function Shim() {};
719 Class.extend(superclass, superc, {
722 superclass.__proto__ = superc;
725 Class.extend(Constructor, superclass, args[0]);
726 update(Constructor, args[1]);
727 Constructor.__proto__ = superclass;
728 args = args.slice(2);
729 Array.forEach(args, function (obj) {
732 update(Constructor.prototype, obj);
737 if (Cu.getGlobalForObject)
738 Class.objectGlobal = function (caller) {
740 return Cu.getGlobalForObject(caller);
747 Class.objectGlobal = function (caller) {
748 while (caller.__parent__)
749 caller = caller.__parent__;
754 * @class Class.Property
755 * A class which, when assigned to a property in a Class's prototype
756 * or class property object, defines that property's descriptor
757 * rather than its value. If the desc object has an init property, it
758 * will be called with the property's name before the descriptor is
761 * @param {Object} desc The property descriptor.
763 Class.Property = function Property(desc) update(
764 Object.create(Property.prototype), desc || { configurable: true, writable: true });
765 Class.Property.prototype.init = function () {};
767 * Extends a subclass with a superclass. The subclass's
768 * prototype is replaced with a new object, which inherits
769 * from the superclass's prototype, {@see update}d with the
770 * members of *overrides*.
772 * @param {function} subclass
773 * @param {function} superclass
774 * @param {Object} overrides @optional
776 Class.extend = function extend(subclass, superclass, overrides) {
777 subclass.superclass = superclass;
779 subclass.prototype = Object.create(superclass.prototype);
780 update(subclass.prototype, overrides);
781 subclass.prototype.constructor = subclass;
782 subclass.prototype._class_ = subclass;
784 if (superclass.prototype.constructor === objproto.constructor)
785 superclass.prototype.constructor = superclass;
789 * Memoizes the value of a class property to the value returned by
790 * the passed function the first time the property is accessed.
792 * @param {function(string)} getter The function which returns the
794 * @return {Class.Property}
796 Class.memoize = function memoize(getter, wait)
800 init: function (key) {
804 this.get = function replace() {
805 let obj = this.instance || this;
806 Object.defineProperty(obj, key, {
807 configurable: true, enumerable: false,
808 get: function get() {
809 util.waitFor(function () done);
814 util.yieldable(function () {
816 for (var res in getter.call(obj)) {
817 if (wait !== undefined)
821 Class.replaceProperty(obj, key, res);
828 this.get = function replace() {
829 let obj = this.instance || this;
830 Class.replaceProperty(obj, key, null);
831 return Class.replaceProperty(obj, key, getter.call(this, key));
834 this.set = function replace(val) Class.replaceProperty(this.instance || this, val);
838 Class.replaceProperty = function replaceProperty(obj, prop, value) {
839 Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
842 Class.toString = function toString() "[class " + this.className + "]";
845 * Initializes new instances of this class. Called automatically
846 * when new instances are created.
848 init: function c_init() {},
850 withSavedValues: function withSavedValues(names, callback, self) {
851 let vals = names.map(function (name) this[name], this);
853 return callback.call(self || this);
856 names.forEach(function (name, i) this[name] = vals[i], this);
860 toString: function C_toString() {
861 if (this.toStringParams)
862 var params = "(" + this.toStringParams.map(function (m) isArray(m) ? "[" + m + "]" :
863 isString(m) ? m.quote() : String(m))
865 return "[instance " + this.constructor.className + (params || "") + "]";
869 * Executes *callback* after *timeout* milliseconds. The value of
870 * 'this' is preserved in the invocation of *callback*.
872 * @param {function} callback The function to call after *timeout*
873 * @param {number} timeout The time, in milliseconds, to wait
874 * before calling *callback*.
875 * @returns {nsITimer} The timer which backs this timeout.
877 timeout: function timeout(callback, timeout) {
879 function timeout_notify(timer) {
881 util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
883 util.trapErrors(callback, self);
885 return services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
888 Class.makeClosure = function makeClosure() {
890 function closure(fn) {
891 function _closure() {
893 return fn.apply(self, arguments);
895 catch (e if !(e instanceof FailedAssertion)) {
897 throw e.stack ? e : Error(e);
900 _closure.wrapped = fn;
904 iter(properties(this), properties(this, true)).forEach(function (k) {
905 if (!__lookupGetter__.call(this, k) && callable(this[k]))
906 closure[k] = closure(this[k]);
907 else if (!(k in closure))
908 Object.defineProperty(closure, k, {
911 get: function get_proxy() self[k],
912 set: function set_proxy(val) self[k] = val,
918 memoize(Class.prototype, "closure", Class.makeClosure);
921 * A base class generator for classes which implement XPCOM interfaces.
923 * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
925 * @param {Class} superClass A super class. @optional
928 function XPCOM(interfaces, superClass) {
929 interfaces = Array.concat(interfaces);
931 let shim = interfaces.reduce(function (shim, iface) shim.QueryInterface(iface),
932 Cc["@dactyl.googlecode.com/base/xpc-interface-shim"].createInstance());
934 let res = Class("XPCOM(" + interfaces + ")", superClass || Class, update(
935 iter.toObject([k, v === undefined || callable(v) ? function stub() null : v]
936 for ([k, v] in Iterator(shim))),
937 { QueryInterface: XPCOMUtils.generateQI(interfaces) }));
938 shim = interfaces = null;
943 * An abstract base class for classes that wish to inherit from Error.
945 var ErrorBase = Class("ErrorBase", Error, {
947 init: function EB_init(message, level) {
949 update(this, Error(message))
950 this.message = message;
952 let frame = Components.stack;
953 for (let i = 0; i < this.level + level; i++) {
954 frame = frame.caller;
955 this.stack = this.stack.replace(/^.*\n/, "");
957 this.fileName = frame.filename;
958 this.lineNumber = frame.lineNumber;
963 * Constructs a new Module class and instantiates an instance into the current
964 * module global object.
966 * @param {string} name The name of the instance.
967 * @param {Object} prototype The instance prototype.
968 * @param {Object} classProperties Properties to be applied to the class constructor.
971 function Module(name, prototype) {
972 let init = callable(prototype) ? 4 : 3;
973 const module = Class.apply(Class, Array.slice(arguments, 0, init));
974 let instance = module();
975 module.className = name.toLowerCase();
977 instance.INIT = update(Object.create(Module.INIT),
978 arguments[init] || {});
980 currentModule[module.className] = instance;
981 defineModule.modules.push(instance);
985 init: function Module_INIT_init(dactyl, modules, window) {
986 let args = arguments;
989 for (let local = this.Local; local; local = local.super)
993 let module = this, objs = {};
994 for (let i in locals) {
995 module = objs[i] = Object.create(module);
996 module.modules = modules;
998 module.isLocalModule = true;
1000 modules.jsmodules[this.constructor.className] = module;
1001 locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args)))
1003 memoize(module, "closure", Class.makeClosure);
1004 module.instance = module;
1008 modules.dactyl.registerObservers(module);
1016 * Creates a new Struct constructor, used for creating objects with
1017 * a fixed set of named members. Each argument should be the name of
1018 * a member in the resulting objects. These names will correspond to
1019 * the arguments passed to the resultant constructor. Instances of
1020 * the new struct may be treated very much like arrays, and provide
1021 * many of the same methods.
1023 * const Point = Struct("x", "y", "z");
1024 * let p1 = Point(x, y, z);
1026 * @returns {function} The constructor for the new Struct.
1029 if (!/^[A-Z]/.test(arguments[0]))
1030 var args = Array.slice(arguments, 0);
1032 var className = arguments[0];
1033 args = Array.slice(arguments, 1);
1036 const Struct = Class(className || "Struct", StructBase, {
1037 length: args.length,
1038 members: array.toObject(args.map(function (v, k) [v, k]))
1040 args.forEach(function (name, i) {
1041 Struct.prototype.__defineGetter__(name, function () this[i]);
1042 Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1046 let StructBase = Class("StructBase", Array, {
1047 init: function struct_init() {
1048 for (let i = 0; i < arguments.length; i++)
1049 if (arguments[i] != undefined)
1050 this[i] = arguments[i];
1053 clone: function struct_clone() this.constructor.apply(null, this.slice()),
1055 closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1057 get: function struct_get(key, val) this[this.members[key]],
1058 set: function struct_set(key, val) this[this.members[key]] = val,
1060 toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1062 // Iterator over our named members
1063 __iterator__: function struct__iterator__() {
1065 return ([k, self[k]] for (k in keys(self.members)))
1068 fromArray: function fromArray(ary) {
1069 if (!(ary instanceof this))
1070 ary.__proto__ = this.prototype;
1075 * Sets a lazily constructed default value for a member of
1076 * the struct. The value is constructed once, the first time
1077 * it is accessed and memoized thereafter.
1079 * @param {string} key The name of the member for which to
1080 * provide the default value.
1081 * @param {function} val The function which is to generate
1082 * the default value.
1084 defaultValue: function defaultValue(key, val) {
1085 let i = this.prototype.members[key];
1086 this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1087 this.prototype.__defineSetter__(i, function (value)
1088 Class.replaceProperty(this, i, value));
1092 localize: function localize(key, defaultValue) {
1093 let i = this.prototype.members[key];
1094 Object.defineProperty(this.prototype, i, require("messages").Messages.Localized(defaultValue).init(key, this.prototype));
1099 var Timer = Class("Timer", {
1100 init: function init(minInterval, maxInterval, callback, self) {
1101 this._timer = services.Timer();
1102 this.callback = callback;
1103 this.self = self || this;
1104 this.minInterval = minInterval;
1105 this.maxInterval = maxInterval;
1110 notify: function notify(timer, force) {
1112 if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1115 this._timer.cancel();
1117 // minInterval is the time between the completion of the command and the next firing
1118 this.doneAt = Date.now() + this.minInterval;
1120 this.callback.call(this.self, this.arg);
1123 if (typeof util === "undefined")
1124 dump("dactyl: " + e + "\n" + (e.stack || Error().stack));
1126 util.reportError(e);
1129 this.doneAt = Date.now() + this.minInterval;
1133 tell: function tell(arg) {
1134 if (arguments.length > 0)
1137 let now = Date.now();
1138 if (this.doneAt == -1)
1139 this._timer.cancel();
1141 let timeout = this.minInterval;
1142 if (now > this.doneAt && this.doneAt > -1)
1144 else if (this.latest)
1145 timeout = Math.min(timeout, this.latest - now);
1147 this.latest = now + this.maxInterval;
1149 this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1153 reset: function reset() {
1154 this._timer.cancel();
1158 flush: function flush(force) {
1159 if (this.doneAt == -1 || force)
1160 this.notify(null, true);
1165 * Idempotent function which returns the UTF-8 encoded value of an
1166 * improperly-decoded string.
1168 * @param {string} str
1171 function UTF8(str) {
1173 return decodeURIComponent(escape(str));
1180 function octal(decimal) parseInt(decimal, 8);
1183 * Iterates over an arbitrary object. The following iterator types are
1184 * supported, and work as a user would expect:
1186 * • nsIDOMNodeIterator
1187 * • mozIStorageStatement
1189 * Additionally, the following array-like objects yield a tuple of the
1190 * form [index, element] for each contained element:
1192 * • nsIDOMHTMLCollection
1193 * • nsIDOMNodeList
1195 * and the following likewise yield one element of the form
1196 * [name, element] for each contained element:
1198 * • nsIDOMNamedNodeMap
1200 * Duck typing is implemented for any other type. If the object
1201 * contains the "enumerator" property, iter is called on that. If the
1202 * property is a function, it is called first. If it contains the
1203 * property "getNext" along with either "hasMoreItems" or "hasMore", it
1204 * is iterated over appropriately.
1206 * For all other cases, this function behaves exactly like the Iterator
1209 * @param {object} obj
1210 * @returns {Generator}
1212 function iter(obj) {
1213 let args = arguments;
1214 let res = Iterator(obj);
1216 if (args.length > 1)
1217 res = (function () {
1218 for (let i = 0; i < args.length; i++)
1219 for (let j in iter(args[i]))
1222 else if (isinstance(obj, ["Iterator", "Generator"]))
1224 else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1225 while (obj.constructor instanceof ctypes.PointerType)
1227 if (obj.constructor instanceof ctypes.ArrayType)
1228 res = array.iterItems(obj);
1229 else if (obj.constructor instanceof ctypes.StructType)
1230 res = (function () {
1231 for (let prop in values(obj.constructor.fields))
1232 yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1237 else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1238 res = array.iterItems(obj);
1239 else if (obj instanceof Ci.nsIDOMNamedNodeMap)
1240 res = (function () {
1241 for (let i = 0; i < obj.length; i++)
1242 yield [obj.name, obj];
1244 else if (obj instanceof Ci.mozIStorageStatement)
1245 res = (function (obj) {
1246 while (obj.executeStep())
1250 else if ("getNext" in obj) {
1251 if ("hasMoreElements" in obj)
1252 res = (function () {
1253 while (obj.hasMoreElements())
1254 yield obj.getNext();
1256 else if ("hasMore" in obj)
1257 res = (function () {
1258 while (obj.hasMore())
1259 yield obj.getNext();
1262 else if ("enumerator" in obj) {
1263 if (callable(obj.enumerator))
1264 return iter(obj.enumerator());
1265 return iter(obj.enumerator);
1267 res.__noSuchMethod__ = function __noSuchMethod__(meth, args) {
1269 var res = iter[meth].apply(iter, [this].concat(args));
1271 res = let (ary = array(this))
1272 ary[meth] ? ary[meth].apply(ary, args) : ary.__noSuchMethod__(meth, args);
1273 if (isinstance(res, ["Iterator", "Generator"]))
1280 toArray: function toArray(iter) array(iter).array,
1282 // See array.prototype for API docs.
1283 toObject: function toObject(iter) {
1285 for (let [k, v] in iter)
1286 if (v instanceof Class.Property)
1287 Object.defineProperty(obj, k, v.init(k, obj) || v);
1293 compact: function compact(iter) (item for (item in iter) if (item != null)),
1295 every: function every(iter, pred, self) {
1296 pred = pred || util.identity;
1297 for (let elem in iter)
1298 if (!pred.call(self, elem))
1302 some: function every(iter, pred, self) {
1303 pred = pred || util.identity;
1304 for (let elem in iter)
1305 if (pred.call(self, elem))
1310 filter: function filter(iter, pred, self) {
1311 for (let elem in iter)
1312 if (pred.call(self, elem))
1317 * Iterates over an iterable object and calls a callback for each
1320 * @param {object} iter The iterator.
1321 * @param {function} fn The callback.
1322 * @param {object} self The this object for *fn*.
1324 forEach: function forEach(iter, func, self) {
1325 for (let val in iter)
1326 func.call(self, val);
1329 indexOf: function indexOf(iter, elem) {
1331 for (let item in iter) {
1339 * Returns the array that results from applying *func* to each property of
1342 * @param {Object} obj
1343 * @param {function} func
1346 map: function map(iter, func, self) {
1348 yield func.call(self, i);
1352 * Returns the nth member of the given array that matches the
1355 nth: function nth(iter, pred, n, self) {
1356 if (typeof pred === "number")
1357 [pred, n] = [function () true, pred]; // Hack.
1359 for (let elem in iter)
1360 if (pred.call(self, elem) && n-- === 0)
1365 sort: function sort(iter, fn, self)
1366 array(this.toArray(iter).sort(fn, self)),
1368 uniq: function uniq(iter) {
1370 for (let item in iter)
1371 if (!set.add(seen, item))
1376 * Zips the contents of two arrays. The resulting array is the length of
1377 * ary1, with any shortcomings of ary2 replaced with null strings.
1379 * @param {Array} ary1
1380 * @param {Array} ary2
1383 zip: function zip(iter1, iter2) {
1385 yield [iter1.next(), iter2.next()];
1387 catch (e if e instanceof StopIteration) {}
1392 * Array utility methods.
1394 var array = Class("array", Array, {
1395 init: function (ary) {
1396 if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1397 ary = [k for (k in ary)];
1398 else if (ary.length)
1399 ary = Array.slice(ary);
1403 __iterator__: function () this.iterItems(),
1404 __noSuchMethod__: function (meth, args) {
1405 var res = array[meth].apply(null, [this.array].concat(args));
1408 if (isinstance(res, ["Iterator", "Generator"]))
1413 toString: function () this.array.toString(),
1414 concat: function () this.__noSuchMethod__("concat", Array.slice(arguments)),
1415 filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)),
1416 map: function () this.__noSuchMethod__("map", Array.slice(arguments))
1421 * Converts an array to an object. As in lisp, an assoc is an
1422 * array of key-value pairs, which maps directly to an object,
1424 * [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1426 * @param {Array[]} assoc
1427 * @... {string} 0 - Key
1430 toObject: function toObject(assoc) {
1432 assoc.forEach(function ([k, v]) {
1433 if (v instanceof Class.Property)
1434 Object.defineProperty(obj, k, v.init(k, obj) || v);
1442 * Compacts an array, removing all elements that are null or undefined:
1443 * ["foo", null, "bar", undefined] -> ["foo", "bar"]
1445 * @param {Array} ary
1448 compact: function compact(ary) ary.filter(function (item) item != null),
1451 * Returns true if each element of ary1 is equal to the
1452 * corresponding element in ary2.
1454 * @param {Array} ary1
1455 * @param {Array} ary2
1456 * @returns {boolean}
1458 equals: function (ary1, ary2)
1459 ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1462 * Flattens an array, such that all elements of the array are
1463 * joined into a single array:
1464 * [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1466 * @param {Array} ary
1469 flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1472 * Returns an Iterator for an array's values.
1474 * @param {Array} ary
1475 * @returns {Iterator(Object)}
1477 iterValues: function iterValues(ary) {
1478 for (let i = 0; i < ary.length; i++)
1483 * Returns an Iterator for an array's indices and values.
1485 * @param {Array} ary
1486 * @returns {Iterator([{number}, {Object}])}
1488 iterItems: function iterItems(ary) {
1489 let length = ary.length;
1490 for (let i = 0; i < length; i++)
1495 * Returns the nth member of the given array that matches the
1498 nth: function nth(ary, pred, n, self) {
1499 for (let elem in values(ary))
1500 if (pred.call(self, elem) && n-- === 0)
1506 * Filters out all duplicates from an array. If *unsorted* is false, the
1507 * array is sorted before duplicates are removed.
1509 * @param {Array} ary
1510 * @param {boolean} unsorted
1513 uniq: function uniq(ary, unsorted) {
1516 for (let item in values(ary))
1517 if (res.indexOf(item) == -1)
1521 for (let [, item] in Iterator(ary.sort())) {
1522 if (item != last || !res.length)
1531 * Zips the contents of two arrays. The resulting array is the length of
1532 * ary1, with any shortcomings of ary2 replaced with null strings.
1534 * @param {Array} ary1
1535 * @param {Array} ary2
1538 zip: function zip(ary1, ary2) {
1540 for (let [i, item] in Iterator(ary1))
1541 res.push([item, i in ary2 ? ary2[i] : ""]);
1548 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1550 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: