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 { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
9 Cu.import("resource://dactyl/bootstrap.jsm");
10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
13 Cu.import("resource://gre/modules/ctypes.jsm");
17 let objproto = Object.prototype;
18 let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__,
19 hasOwnProperty, propertyIsEnumerable } = objproto;
21 if (typeof XPCSafeJSObjectWrapper === "undefined")
22 this.XPCSafeJSObjectWrapper = XPCNativeWrapper;
24 if (!XPCNativeWrapper.unwrap)
25 XPCNativeWrapper.unwrap = function unwrap(obj) {
26 if (hasOwnProperty.call(obj, "wrappedJSObject"))
27 return obj.wrappedJSObject;
31 Object.create = function create(proto, props) {
32 let obj = { __proto__: proto };
33 for (let k in properties(props || {}))
34 Object.defineProperty(obj, k, props[k]);
37 if (!Object.defineProperty)
38 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 __defineGetter__.call(obj, prop, function () value);
51 __defineSetter__.call(obj, prop, function (val) { value = val; });
55 __defineGetter__.call(obj, prop, desc.get);
57 __defineSetter__.call(obj, prop, desc.set);
60 throw e.stack ? e : Error(e);
63 if (!Object.defineProperties)
64 Object.defineProperties = function defineProperties(obj, props) {
65 for (let [k, v] in Iterator(props))
66 Object.defineProperty(obj, k, v);
69 Object.freeze = function freeze(obj) {};
70 if (!Object.getPropertyDescriptor)
71 Object.getPropertyDescriptor = function getPropertyDescriptor(obj, prop) {
75 enumerable: propertyIsEnumerable.call(obj, prop)
77 var get = __lookupGetter__.call(obj, prop),
78 set = __lookupSetter__.call(obj, prop);
80 desc.value = obj[prop];
90 throw e.stack ? e : Error(e);
93 if (!Object.getOwnPropertyDescriptor)
94 Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
95 if (hasOwnProperty.call(obj, prop))
96 return Object.getPropertyDescriptor(obj, prop);
98 if (!Object.getOwnPropertyNames)
99 Object.getOwnPropertyNames = function getOwnPropertyNames(obj, _debugger) {
101 // This is an ugly and unfortunately necessary hack.
102 if (hasOwnProperty.call(obj, "__iterator__")) {
103 var oldIter = obj.__iterator__;
104 delete obj.__iterator__;
106 let res = [k for (k in obj) if (hasOwnProperty.call(obj, k))];
107 if (oldIter !== undefined) {
108 obj.__iterator__ = oldIter;
109 res.push("__iterator__");
114 throw e.stack ? e : Error(e);
117 if (!Object.getPrototypeOf)
118 Object.getPrototypeOf = function getPrototypeOf(obj) obj.__proto__;
120 Object.keys = function keys(obj)
121 Object.getOwnPropertyNames(obj).filter(function (k) propertyIsEnumerable.call(obj, k));
123 let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__;
126 lazyRequire: function lazyRequire(module, names, target) {
127 for each (let name in names)
128 memoize(target || this, name, function (name) require(module)[name]);
131 jsmodules.jsmodules = jsmodules;
137 function defineModule(name, params, module) {
139 module = getGlobalForObject(params);
142 module.EXPORTED_SYMBOLS = params.exports || [];
143 if (!~module.EXPORTED_SYMBOLS.indexOf("File"))
146 defineModule.loadLog.push("[Begin " + name + "]");
147 defineModule.prefix += " ";
149 for (let [, mod] in Iterator(params.require || []))
150 require(module, mod, null, name);
151 module.__proto__ = jsmodules;
153 module._lastModule = currentModule;
154 currentModule = module;
155 module.startTime = Date.now();
158 defineModule.loadLog = [];
159 Object.defineProperty(defineModule.loadLog, "push", {
160 value: function (val) {
161 val = defineModule.prefix + val;
163 defineModule.dump(val + "\n");
164 this[this.length] = Date.now() + " " + val;
167 defineModule.prefix = "";
168 defineModule.dump = function dump_() {
169 let msg = Array.map(arguments, function (msg) {
170 if (loaded.util && typeof msg == "object")
171 msg = util.objectToString(msg);
174 dump(String.replace(msg, /\n?$/, "\n")
175 .replace(/^./gm, JSMLoader.name + ": $&"));
177 defineModule.modules = [];
178 defineModule.time = function time(major, minor, func, self) {
179 let time = Date.now();
180 if (typeof func !== "function")
184 var res = func.apply(self, Array.slice(arguments, 4));
187 loaded.util && util.reportError(e);
190 JSMLoader.times.add(major, minor, Date.now() - time);
194 function endModule() {
195 defineModule.prefix = defineModule.prefix.slice(0, -2);
196 defineModule.loadLog.push("(End " + currentModule.NAME + ")");
198 loaded[currentModule.NAME] = 1;
199 require(jsmodules, currentModule.NAME);
200 currentModule = currentModule._lastModule;
203 function require(obj, name, from, targetName) {
205 if (arguments.length === 1)
206 [obj, name] = [{}, obj];
208 let caller = Components.stack.caller;
211 defineModule.loadLog.push((from || "require") + ": loading " + name +
212 " into " + (targetName || obj.NAME || caller.filename + ":" + caller.lineNumber));
214 JSMLoader.load(name + ".jsm", obj);
216 if (!loaded[name] && obj != jsmodules)
217 JSMLoader.load(name + ".jsm", jsmodules);
222 defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
226 defineModule.dump(" " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e + "\n");
230 defineModule("base", {
231 // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt
233 "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Module", "JSMLoader", "Object",
234 "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils",
235 "XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry",
236 "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray",
237 "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "iter", "iterAll",
238 "iterOwnProperties", "keys", "memoize", "octal", "properties", "require", "set", "update",
239 "values", "withCallerGlobal"
243 this.lazyRequire("messages", ["_", "Messages"]);
244 this.lazyRequire("util", ["util"]);
247 * Returns a list of all of the top-level properties of an object, by
248 * way of the debugger.
250 * @param {object} obj
251 * @returns [jsdIProperty]
253 function debuggerProperties(obj) {
254 if (loaded.services && services.debugger.isOn) {
256 services.debugger.wrapValue(obj).getProperties(res, {});
262 * Iterates over the names of all of the top-level properties of an
263 * object or, if prototypes is given, all of the properties in the
264 * prototype chain below the top. Uses the debugger if possible.
266 * @param {object} obj The object to inspect.
267 * @param {boolean} properties Whether to inspect the prototype chain
269 * @returns {Generator}
271 function prototype(obj)
272 /* Temporary hack: */ typeof obj === "xml" || obj.__proto__ !== obj.__proto__ ? null :
273 obj.__proto__ || Object.getPrototypeOf(obj) ||
274 XPCNativeWrapper.unwrap(obj).__proto__ ||
275 Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
277 function properties(obj, prototypes, debugger_) {
279 let seen = { dactylPropertyNames: true };
282 if ("dactylPropertyNames" in obj && !prototypes)
283 for (let key in values(obj.dactylPropertyNames))
284 if (key in obj && !Set.add(seen, key))
289 for (; obj; obj = prototypes && prototype(obj)) {
291 if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn)
292 var iter = (v for each (v in Object.getOwnPropertyNames(obj)));
296 iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
298 for (let key in iter)
299 if (!prototypes || !Set.add(seen, key) && obj != orig)
304 function iterOwnProperties(obj) {
305 for (let prop in properties(obj))
306 yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
309 function deprecated(alternative, fn) {
311 return Class.Property(iter(fn).map(function ([k, v]) [k, callable(v) ? deprecated(alternative, v) : v])
314 let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
316 function deprecatedMethod() {
317 let obj = !this ? "" :
318 this.className ? this.className + "#" :
319 this.constructor.className ? this.constructor.className + "#" :
322 deprecated.warn(func, obj + (fn.name || name), alternative);
323 return func.apply(this, arguments);
326 return callable(fn) ? deprecatedMethod : Class.Property({
327 get: function () deprecatedMethod,
328 init: function (prop) { name = prop; }
331 deprecated.warn = function warn(func, name, alternative, frame) {
332 if (!func.seenCaller)
333 func.seenCaller = Set([
334 "resource://dactyl" + JSMLoader.suffix + "/javascript.jsm",
335 "resource://dactyl" + JSMLoader.suffix + "/util.jsm"
338 frame = frame || Components.stack.caller.caller;
339 let filename = util.fixURI(frame.filename || "unknown");
340 if (!Set.add(func.seenCaller, filename))
341 util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":")
342 + _("warn.deprecated", name, alternative));
346 * Iterates over all of the top-level, iterable property names of an
349 * @param {object} obj The object to inspect.
350 * @returns {Generator}
352 function keys(obj) iter(function keys() {
354 if (hasOwnProperty.call(obj, k))
359 * Iterates over all of the top-level, iterable property values of an
362 * @param {object} obj The object to inspect.
363 * @returns {Generator}
365 function values(obj) iter(function values() {
366 if (isinstance(obj, ["Generator", "Iterator", Iter]))
371 if (hasOwnProperty.call(obj, k))
375 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
376 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
379 * Utility for managing sets of strings. Given an array, returns an
380 * object with one key for each value thereof.
382 * @param {[string]} ary @optional
388 for (let val in values(ary))
393 * Adds an element to a set and returns true if the element was
394 * previously contained.
396 * @param {object} set The set.
397 * @param {string} key The key to add.
400 Set.add = curry(function set_add(set, key) {
401 let res = this.has(set, key);
406 * Returns true if the given set contains the given key.
408 * @param {object} set The set.
409 * @param {string} key The key to check.
412 Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
413 propertyIsEnumerable.call(set, key));
415 * Returns a new set containing the members of the first argument which
416 * do not exist in any of the other given arguments.
418 * @param {object} set The set.
421 Set.subtract = function set_subtract(set) {
422 set = update({}, set);
423 for (let i = 1; i < arguments.length; i++)
424 for (let k in keys(arguments[i]))
429 * Removes an element from a set and returns true if the element was
430 * previously contained.
432 * @param {object} set The set.
433 * @param {string} key The key to remove.
436 Set.remove = curry(function set_remove(set, key) {
437 let res = set.has(set, key);
443 deprecated.warn(set, "set", "Set");
444 return Set.apply(this, arguments);
446 Object.keys(Set).forEach(function (meth) {
447 set[meth] = function proxy() {
448 deprecated.warn(proxy, "set." + meth, "Set." + meth);
449 return Set[meth].apply(Set, arguments);
454 * Curries a function to the given number of arguments. Each
455 * call of the resulting function returns a new function. When
456 * a call does not contain enough arguments to satisfy the
457 * required number, the resulting function is another curried
458 * function with previous arguments accumulated.
460 * function foo(a, b, c) [a, b, c].join(" ");
461 * curry(foo)(1, 2, 3) -> "1 2 3";
462 * curry(foo)(4)(5, 6) -> "4 5 6";
463 * curry(foo)(7)(8)(9) -> "7 8 9";
465 * @param {function} fn The function to curry.
466 * @param {integer} length The number of arguments expected.
469 * @param {object} self The 'this' value for the returned function. When
470 * omitted, the value of 'this' from the first call to the function is
474 function curry(fn, length, self, acc) {
480 // Close over function with 'this'
481 function close(self, fn) function () fn.apply(self, Array.slice(arguments));
486 return function curried() {
487 let args = acc.concat(Array.slice(arguments));
489 // The curried result should preserve 'this'
490 if (arguments.length == 0)
491 return close(self || this, curried);
493 if (args.length >= length)
494 return fn.apply(self || this, args);
496 return curry(fn, length, self || this, args);
501 var bind = function bind(meth, self) let (func = callable(meth) ? meth : self[meth])
502 func.bind.apply(func, Array.slice(arguments, 1));
504 var bind = function bind(func, self) {
508 let args = Array.slice(arguments, bind.length);
509 return function bound() func.apply(self, args.concat(Array.slice(arguments)));
513 * Returns true if both arguments are functions and
514 * (targ() instanceof src) would also return true.
516 * @param {function} targ
517 * @param {function} src
520 function isSubclass(targ, src) {
521 return src === targ ||
522 targ && typeof targ === "function" && targ.prototype instanceof src;
526 * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
527 * an array, returns true if *object* is an instance of any element of
528 * *interfaces*. If *interfaces* is the object form of a primitive type,
529 * returns true if *object* is a non-boxed version of the type, i.e., if
530 * (typeof object == "string"), isinstance(object, String) is true. Finally, if
531 * *interfaces* is a string, returns true if ({}.toString.call(object) ==
532 * "[object <interfaces>]").
534 * @param {object} object The object to check.
535 * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
538 var isinstance_types = {
544 function isinstance(object, interfaces) {
548 return Array.concat(interfaces).some(function isinstance_some(iface) {
549 if (typeof iface === "string") {
550 if (objproto.toString.call(object) === "[object " + iface + "]")
553 else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
554 if (object.isinstance(iface))
558 if (object instanceof iface)
560 var type = isinstance_types[typeof object];
561 if (type && isSubclass(iface, type))
569 * Returns true if obj is a non-null object.
571 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
574 * Returns true if and only if its sole argument is an
575 * instance of the builtin Array type. The array may come from
576 * any window, frame, namespace, or execution context, which
577 * is not the case when using (obj instanceof Array).
581 // This is bloody stupid.
582 ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
583 : function isArray(val) objproto.toString.call(val) == "[object Array]";
586 * Returns true if and only if its sole argument is an
587 * instance of the builtin Generator type. This includes
588 * functions containing the 'yield' statement and generator
589 * statements such as (x for (x in obj)).
591 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
594 * Returns true if and only if its sole argument is a String,
595 * as defined by the builtin type. May be constructed via
596 * String(foo) or new String(foo) from any window, frame,
597 * namespace, or execution context, which is not the case when
598 * using (obj instanceof String) or (typeof obj == "string").
600 function isString(val) objproto.toString.call(val) == "[object String]";
603 * Returns true if and only if its sole argument may be called
604 * as a function. This includes classes and function objects.
606 function callable(val) typeof val === "function" && !(val instanceof Ci.nsIDOMElement);
609 fn.apply(arguments[1], Array.slice(arguments, 2));
614 * Memoizes an object property value.
616 * @param {object} obj The object to add the property to.
617 * @param {string} key The property name.
618 * @param {function} getter The function which will return the initial
619 * value of the property.
621 function memoize(obj, key, getter) {
622 if (arguments.length == 1) {
623 let res = update(Object.create(obj), obj);
624 for each (let prop in Object.getOwnPropertyNames(obj)) {
625 let get = __lookupGetter__.call(obj, prop);
627 memoize(res, prop, get);
633 Object.defineProperty(obj, key, {
637 get: function g_replaceProperty() {
639 Class.replaceProperty(this.instance || this, key, null);
640 return Class.replaceProperty(this.instance || this, key, getter.call(this, key));
647 set: function s_replaceProperty(val)
648 Class.replaceProperty(this.instance || this, key, val)
652 obj[key] = getter.call(obj, key);
656 let sandbox = Cu.Sandbox(this);
657 sandbox.__proto__ = this;
659 * Wraps a function so that when called, the global object of the caller
660 * is prepended to its arguments.
662 // Hack to get around lack of access to caller in strict mode.
663 var withCallerGlobal = Cu.evalInSandbox(<![CDATA[
664 (function withCallerGlobal(fn)
665 function withCallerGlobal_wrapped()
667 [Class.objectGlobal(withCallerGlobal_wrapped.caller)]
668 .concat(Array.slice(arguments))))
669 ]]>, Cu.Sandbox(this), "1.8");
672 * Updates an object with the properties of another object. Getters
673 * and setters are copied as expected. Moreover, any function
674 * properties receive new 'supercall' and 'superapply' properties,
675 * which will call the identically named function in target's
678 * let a = { foo: function (arg) "bar " + arg }
679 * let b = { __proto__: a }
680 * update(b, { foo: function foo() foo.supercall(this, "baz") });
682 * a.foo("foo") -> "bar foo"
683 * b.foo() -> "bar baz"
685 * @param {Object} target The object to update.
686 * @param {Object} src The source object from which to update target.
687 * May be provided multiple times.
688 * @returns {Object} Returns its updated first argument.
690 function update(target) {
691 for (let i = 1; i < arguments.length; i++) {
692 let src = arguments[i];
693 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
694 let desc = Object.getOwnPropertyDescriptor(src, k);
695 if (desc.value instanceof Class.Property)
696 desc = desc.value.init(k, target) || desc.value;
699 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
700 let func = desc.value.wrapped || desc.value;
701 if (!func.superapply) {
702 func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
703 func.superapply = function superapply(self, args)
704 let (meth = Object.getPrototypeOf(target)[k])
705 meth && meth.apply(self, args);
706 func.supercall = function supercall(self)
707 func.superapply(self, Array.slice(arguments, 1));
710 Object.defineProperty(target, k, desc);
721 * Constructs a new Class. Arguments marked as optional must be
722 * either entirely elided, or they must have the exact type
725 * @param {string} name The class's as it will appear when toString
726 * is called, as well as in stack traces.
728 * @param {function} base The base class for this module. May be any
732 * @param {Object} prototype The prototype for instances of this
733 * object. The object itself is copied and not used as a prototype
735 * @param {Object} classProperties The class properties for the new
736 * module constructor. More than one may be provided.
739 * @returns {function} The constructor for the resulting class.
743 var args = Array.slice(arguments);
744 if (isString(args[0]))
745 var name = args.shift();
746 var superclass = Class;
747 if (callable(args[0]))
748 superclass = args.shift();
750 if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418.
751 var Constructor = function Constructor() {
752 var self = Object.create(Constructor.prototype);
753 self.instance = self;
755 if ("_metaInit_" in self && self._metaInit_)
756 self._metaInit_.apply(self, arguments);
758 var res = self.init.apply(self, arguments);
759 return res !== undefined ? res : self;
762 var Constructor = eval(String.replace(<![CDATA[
763 (function constructor(PARAMS) {
764 var self = Object.create(Constructor.prototype);
765 self.instance = self;
767 if ("_metaInit_" in self && self._metaInit_)
768 self._metaInit_.apply(self, arguments);
770 var res = self.init.apply(self, arguments);
771 return res !== undefined ? res : self;
773 "constructor", (name || superclass.className).replace(/\W/g, "_"))
774 .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
775 .replace(/\b(self|res|Constructor)\b/g, "$1_")));
777 Constructor.className = name || superclass.className || superclass.name;
779 if ("init" in superclass.prototype)
780 Constructor.__proto__ = superclass;
782 let superc = superclass;
783 superclass = function Shim() {};
784 Class.extend(superclass, superc, {
787 superclass.__proto__ = superc;
790 Class.extend(Constructor, superclass, args[0]);
791 memoize(Constructor, "closure", Class.makeClosure);
792 update(Constructor, args[1]);
794 Constructor.__proto__ = superclass;
796 args.slice(2).forEach(function (obj) {
799 update(Constructor.prototype, obj);
804 if (Cu.getGlobalForObject)
805 Class.objectGlobal = function (object) {
807 return Cu.getGlobalForObject(object);
814 Class.objectGlobal = function (object) {
815 while (object.__parent__)
816 object = object.__parent__;
821 * @class Class.Property
822 * A class which, when assigned to a property in a Class's prototype
823 * or class property object, defines that property's descriptor
824 * rather than its value. If the desc object has an init property, it
825 * will be called with the property's name before the descriptor is
828 * @param {Object} desc The property descriptor.
830 Class.Property = function Property(desc) update(
831 Object.create(Property.prototype), desc || { configurable: true, writable: true });
832 Class.Property.prototype.init = function () {};
834 * Extends a subclass with a superclass. The subclass's
835 * prototype is replaced with a new object, which inherits
836 * from the superclass's prototype, {@see update}d with the
837 * members of *overrides*.
839 * @param {function} subclass
840 * @param {function} superclass
841 * @param {Object} overrides @optional
843 Class.extend = function extend(subclass, superclass, overrides) {
844 subclass.superclass = superclass;
846 subclass.prototype = Object.create(superclass.prototype);
847 update(subclass.prototype, overrides);
848 subclass.prototype.constructor = subclass;
849 subclass.prototype._class_ = subclass;
851 if (superclass.prototype.constructor === objproto.constructor)
852 superclass.prototype.constructor = superclass;
856 * Memoizes the value of a class property to the value returned by
857 * the passed function the first time the property is accessed.
859 * @param {function(string)} getter The function which returns the
861 * @returns {Class.Property}
863 Class.Memoize = function Memoize(getter, wait)
867 init: function (key) {
871 // Crazy, yeah, I know. -- Kris
872 this.get = function replace() {
873 let obj = this.instance || this;
874 Object.defineProperty(obj, key, {
875 configurable: true, enumerable: false,
876 get: function get() {
877 util.waitFor(function () done);
882 util.yieldable(function () {
884 for (var res in getter.call(obj)) {
885 if (wait !== undefined)
889 Class.replaceProperty(obj, key, res);
896 this.get = function g_Memoize() {
897 let obj = this.instance || this;
899 Class.replaceProperty(obj, key, null);
900 return Class.replaceProperty(obj, key, getter.call(this, key));
907 this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val);
911 Class.memoize = deprecated("Class.Memoize", function memoize() Class.Memoize.apply(this, arguments));
914 * Updates the given object with the object in the target class's
917 Class.Update = function Update(obj)
922 init: function (key, target) {
923 this.value = update({}, target[key], obj);
927 Class.replaceProperty = function replaceProperty(obj, prop, value) {
928 Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
931 Class.toString = function toString() "[class " + this.className + "]";
934 * Initializes new instances of this class. Called automatically
935 * when new instances are created.
937 init: function c_init() {},
940 set instance(val) Class.replaceProperty(this, "instance", val),
942 withSavedValues: function withSavedValues(names, callback, self) {
943 let vals = names.map(function (name) this[name], this);
945 return callback.call(self || this);
948 names.forEach(function (name, i) this[name] = vals[i], this);
952 toString: function C_toString() {
953 if (this.toStringParams)
954 var params = "(" + this.toStringParams.map(function (m) isArray(m) ? "[" + m + "]" :
955 isString(m) ? m.quote() : String(m))
957 return "[instance " + this.constructor.className + (params || "") + "]";
961 * Executes *callback* after *timeout* milliseconds. The value of
962 * 'this' is preserved in the invocation of *callback*.
964 * @param {function} callback The function to call after *timeout*
965 * @param {number} timeout The time, in milliseconds, to wait
966 * before calling *callback*.
967 * @returns {nsITimer} The timer which backs this timeout.
969 timeout: function timeout(callback, timeout) {
971 function timeout_notify(timer) {
973 util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
975 self.timeouts.splice(self.timeouts.indexOf(timer), 1);
976 util.trapErrors(callback, self);
978 let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
979 this.timeouts.push(timer);
985 * Updates this instance with the properties of the given objects.
986 * Like the update function, but with special semantics for
987 * localized properties.
989 update: function update() {
993 for (let i = 0; i < arguments.length; i++) {
994 let src = arguments[i];
995 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
996 let desc = Object.getOwnPropertyDescriptor(src, k);
997 if (desc.value instanceof Class.Property)
998 desc = desc.value.init(k, this) || desc.value;
1000 if (typeof desc.value === "function") {
1001 let func = desc.value.wrapped || desc.value;
1002 if (!func.superapply) {
1003 func.__defineGetter__("super", function () Object.getPrototypeOf(self)[k]);
1004 func.superapply = function superapply(self, args)
1005 let (meth = Object.getPrototypeOf(self)[k])
1006 meth && meth.apply(self, args);
1007 func.supercall = function supercall(self)
1008 func.superapply(self, Array.slice(arguments, 1));
1013 if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties))
1014 this[k] = desc.value;
1016 Object.defineProperty(this, k, desc);
1024 localizedProperties: {},
1025 magicalProperties: {}
1027 for (let name in properties(Class.prototype)) {
1028 let desc = Object.getOwnPropertyDescriptor(Class.prototype, name);
1029 desc.enumerable = false;
1030 Object.defineProperty(Class.prototype, name, desc);
1033 Class.makeClosure = function makeClosure() {
1035 function closure(fn) {
1036 function _closure() {
1038 return fn.apply(self, arguments);
1040 catch (e if !(e instanceof FailedAssertion)) {
1041 util.reportError(e);
1042 throw e.stack ? e : Error(e);
1045 _closure.wrapped = fn;
1049 iter(properties(this), properties(this, true)).forEach(function (k) {
1050 if (!__lookupGetter__.call(this, k) && callable(this[k]))
1051 closure[k] = closure(this[k]);
1052 else if (!(k in closure))
1053 Object.defineProperty(closure, k, {
1056 get: function get_proxy() self[k],
1057 set: function set_proxy(val) self[k] = val,
1063 memoize(Class.prototype, "closure", Class.makeClosure);
1066 * A base class generator for classes which implement XPCOM interfaces.
1068 * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
1070 * @param {Class} superClass A super class. @optional
1073 function XPCOM(interfaces, superClass) {
1074 interfaces = Array.concat(interfaces);
1076 let shim = XPCOMShim(interfaces);
1078 let res = Class("XPCOM(" + interfaces + ")", superClass || Class,
1080 v === undefined || callable(v) ? stub : v]
1081 for ([k, v] in Iterator(shim))).toObject(),
1082 { QueryInterface: XPCOMUtils.generateQI(interfaces) }));
1086 function XPCOMShim(interfaces) {
1087 let ip = services.InterfacePointer({
1088 QueryInterface: function (iid) {
1089 if (iid.equals(Ci.nsISecurityCheckedComponent))
1090 throw Cr.NS_ERROR_NO_INTERFACE;
1093 getHelperForLanguage: function () null,
1094 getInterfaces: function (count) { count.value = 0; }
1096 return (interfaces || []).reduce(function (shim, iface) shim.QueryInterface(Ci[iface]),
1099 let stub = Class.Property({
1102 value: function stub() null,
1107 * An abstract base class for classes that wish to inherit from Error.
1109 var ErrorBase = Class("ErrorBase", Error, {
1111 init: function EB_init(message, level) {
1113 let error = Error(message);
1115 this.stack = error.stack;
1116 this.message = message;
1118 let frame = Components.stack;
1119 for (let i = 0; i < this.level + level; i++) {
1120 frame = frame.caller;
1121 this.stack = this.stack.replace(/^.*\n/, "");
1123 this.fileName = frame.filename;
1124 this.lineNumber = frame.lineNumber;
1126 toString: function () String(this.message)
1130 * Constructs a new Module class and instantiates an instance into the current
1131 * module global object.
1133 * @param {string} name The name of the instance.
1134 * @param {Object} prototype The instance prototype.
1135 * @param {Object} classProperties Properties to be applied to the class constructor.
1138 function Module(name, prototype) {
1140 let init = callable(prototype) ? 4 : 3;
1141 let proto = arguments[callable(prototype) ? 2 : 1];
1143 proto._metaInit_ = function () {
1144 delete module.prototype._metaInit_;
1145 currentModule[name.toLowerCase()] = this;
1148 const module = Class.apply(Class, Array.slice(arguments, 0, init));
1149 let instance = module();
1150 module.className = name.toLowerCase();
1152 instance.INIT = update(Object.create(Module.INIT),
1153 arguments[init] || {});
1155 currentModule[module.className] = instance;
1156 defineModule.modules.push(instance);
1160 if (typeof e === "string")
1163 dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack));
1167 init: function Module_INIT_init(dactyl, modules, window) {
1168 let args = arguments;
1171 for (let local = this.Local; local; local = local.super)
1174 if (locals.length) {
1175 let module = this, objs = {};
1176 for (let i in locals) {
1177 module = objs[i] = Object.create(module);
1178 module.modules = modules;
1180 module.isLocalModule = true;
1182 modules.jsmodules[this.constructor.className] = module;
1183 locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args)))
1185 memoize(module, "closure", Class.makeClosure);
1186 module.instance = module;
1190 modules.dactyl.registerObservers(module);
1198 * Creates a new Struct constructor, used for creating objects with
1199 * a fixed set of named members. Each argument should be the name of
1200 * a member in the resulting objects. These names will correspond to
1201 * the arguments passed to the resultant constructor. Instances of
1202 * the new struct may be treated very much like arrays, and provide
1203 * many of the same methods.
1205 * const Point = Struct("x", "y", "z");
1206 * let p1 = Point(x, y, z);
1208 * @returns {function} The constructor for the new Struct.
1211 if (!/^[A-Z]/.test(arguments[0]))
1212 var args = Array.slice(arguments, 0);
1214 var className = arguments[0];
1215 args = Array.slice(arguments, 1);
1218 const Struct = Class(className || "Struct", StructBase, {
1219 length: args.length,
1220 members: array.toObject(args.map(function (v, k) [v, k]))
1222 args.forEach(function (name, i) {
1223 Struct.prototype.__defineGetter__(name, function () this[i]);
1224 Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1228 var StructBase = Class("StructBase", Array, {
1229 init: function struct_init() {
1230 for (let i = 0; i < arguments.length; i++)
1231 if (arguments[i] != undefined)
1232 this[i] = arguments[i];
1235 get toStringParams() this,
1237 clone: function struct_clone() this.constructor.apply(null, this.slice()),
1239 closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1241 get: function struct_get(key, val) this[this.members[key]],
1242 set: function struct_set(key, val) this[this.members[key]] = val,
1244 toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1246 // Iterator over our named members
1247 __iterator__: function struct__iterator__() {
1249 return ([k, self[k]] for (k in keys(self.members)))
1252 fromArray: function fromArray(ary) {
1253 if (!(ary instanceof this))
1254 ary.__proto__ = this.prototype;
1259 * Sets a lazily constructed default value for a member of
1260 * the struct. The value is constructed once, the first time
1261 * it is accessed and memoized thereafter.
1263 * @param {string} key The name of the member for which to
1264 * provide the default value.
1265 * @param {function} val The function which is to generate
1266 * the default value.
1268 defaultValue: function defaultValue(key, val) {
1269 let i = this.prototype.members[key];
1270 this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1271 this.prototype.__defineSetter__(i, function (value)
1272 Class.replaceProperty(this, i, value));
1276 localize: function localize(key, defaultValue) {
1277 let i = this.prototype.members[key];
1278 Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1283 var Timer = Class("Timer", {
1284 init: function init(minInterval, maxInterval, callback, self) {
1285 this._timer = services.Timer();
1286 this.callback = callback;
1287 this.self = self || this;
1288 this.minInterval = minInterval;
1289 this.maxInterval = maxInterval;
1294 notify: function notify(timer, force) {
1296 if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1299 this._timer.cancel();
1301 // minInterval is the time between the completion of the command and the next firing
1302 this.doneAt = Date.now() + this.minInterval;
1304 this.callback.call(this.self, this.arg);
1307 if (typeof util === "undefined")
1308 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1310 util.reportError(e);
1313 this.doneAt = Date.now() + this.minInterval;
1317 tell: function tell(arg) {
1318 if (arguments.length > 0)
1321 let now = Date.now();
1322 if (this.doneAt == -1)
1323 this._timer.cancel();
1325 let timeout = this.minInterval;
1326 if (now > this.doneAt && this.doneAt > -1)
1328 else if (this.latest)
1329 timeout = Math.min(timeout, this.latest - now);
1331 this.latest = now + this.maxInterval;
1333 this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1337 reset: function reset() {
1338 this._timer.cancel();
1342 flush: function flush(force) {
1343 if (this.doneAt == -1 || force)
1344 this.notify(null, true);
1349 * Idempotent function which returns the UTF-8 encoded value of an
1350 * improperly-decoded string.
1352 * @param {string} str
1355 function UTF8(str) {
1357 return decodeURIComponent(escape(str));
1364 function octal(decimal) parseInt(decimal, 8);
1367 * Iterates over an arbitrary object. The following iterator types are
1368 * supported, and work as a user would expect:
1370 * • nsIDOMNodeIterator
1371 * • mozIStorageStatement
1373 * Additionally, the following array-like objects yield a tuple of the
1374 * form [index, element] for each contained element:
1376 * • nsIDOMHTMLCollection
1377 * • nsIDOMNodeList
1379 * and the following likewise yield one element of the form
1380 * [name, element] for each contained element:
1382 * • nsIDOMNamedNodeMap
1384 * Duck typing is implemented for any other type. If the object
1385 * contains the "enumerator" property, iter is called on that. If the
1386 * property is a function, it is called first. If it contains the
1387 * property "getNext" along with either "hasMoreItems" or "hasMore", it
1388 * is iterated over appropriately.
1390 * For all other cases, this function behaves exactly like the Iterator
1393 * @param {object} obj
1394 * @param {nsIJSIID} iface The interface to which to query all elements.
1395 * @returns {Generator}
1397 function iter(obj, iface) {
1398 if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1399 return iter(obj).map(function (item) item.QueryInterface(iface));
1401 let args = arguments;
1402 let res = Iterator(obj);
1404 if (args.length > 1)
1405 res = (function () {
1406 for (let i = 0; i < args.length; i++)
1407 for (let j in iter(args[i]))
1410 else if (isinstance(obj, ["Iterator", "Generator"]))
1412 else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1413 while (obj.constructor instanceof ctypes.PointerType)
1415 if (obj.constructor instanceof ctypes.ArrayType)
1416 res = array.iterItems(obj);
1417 else if (obj.constructor instanceof ctypes.StructType)
1418 res = (function () {
1419 for (let prop in values(obj.constructor.fields))
1420 yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1425 else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1426 res = array.iterItems(obj);
1427 else if (obj instanceof Ci.nsIDOMNamedNodeMap)
1428 res = (function () {
1429 for (let i = 0; i < obj.length; i++)
1430 yield [obj.name, obj];
1432 else if (obj instanceof Ci.mozIStorageStatement)
1433 res = (function (obj) {
1434 while (obj.executeStep())
1438 else if ("getNext" in obj) {
1439 if ("hasMoreElements" in obj)
1440 res = (function () {
1441 while (obj.hasMoreElements())
1442 yield obj.getNext();
1444 else if ("hasMore" in obj)
1445 res = (function () {
1446 while (obj.hasMore())
1447 yield obj.getNext();
1450 else if ("enumerator" in obj) {
1451 if (callable(obj.enumerator))
1452 return iter(obj.enumerator());
1453 return iter(obj.enumerator);
1458 toArray: function toArray(iter) array(iter).array,
1460 // See array.prototype for API docs.
1461 toObject: function toObject(iter) {
1463 for (let [k, v] in iter)
1464 if (v instanceof Class.Property)
1465 Object.defineProperty(obj, k, v.init(k, obj) || v);
1471 compact: function compact(iter) (item for (item in iter) if (item != null)),
1473 every: function every(iter, pred, self) {
1474 pred = pred || util.identity;
1475 for (let elem in iter)
1476 if (!pred.call(self, elem))
1480 some: function every(iter, pred, self) {
1481 pred = pred || util.identity;
1482 for (let elem in iter)
1483 if (pred.call(self, elem))
1488 filter: function filter(iter, pred, self) {
1489 for (let elem in iter)
1490 if (pred.call(self, elem))
1495 * Iterates over an iterable object and calls a callback for each
1498 * @param {object} iter The iterator.
1499 * @param {function} fn The callback.
1500 * @param {object} self The this object for *fn*.
1502 forEach: function forEach(iter, func, self) {
1503 for (let val in iter)
1504 func.call(self, val);
1507 indexOf: function indexOf(iter, elem) {
1509 for (let item in iter) {
1517 * Returns the array that results from applying *func* to each property of
1520 * @param {Object} obj
1521 * @param {function} func
1524 map: function map(iter, func, self) {
1526 yield func.call(self, i);
1530 * Returns the nth member of the given array that matches the
1533 nth: function nth(iter, pred, n, self) {
1534 if (typeof pred === "number")
1535 [pred, n] = [function () true, pred]; // Hack.
1537 for (let elem in iter)
1538 if (pred.call(self, elem) && n-- === 0)
1543 sort: function sort(iter, fn, self)
1544 array(this.toArray(iter).sort(fn, self)),
1546 uniq: function uniq(iter) {
1548 for (let item in iter)
1549 if (!Set.add(seen, item))
1554 * Zips the contents of two arrays. The resulting array is the length of
1555 * ary1, with any shortcomings of ary2 replaced with null strings.
1557 * @param {Array} ary1
1558 * @param {Array} ary2
1561 zip: function zip(iter1, iter2) {
1563 yield [iter1.next(), iter2.next()];
1565 catch (e if e instanceof StopIteration) {}
1569 const Iter = Class("Iter", {
1570 init: function init(iter) {
1572 if ("__iterator__" in iter)
1573 this.iter = iter.__iterator__();
1575 if (this.iter.finalize)
1576 this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1579 next: function next() this.iter.next(),
1581 send: function send() this.iter.send.apply(this.iter, arguments),
1583 __iterator__: function () this.iter
1587 * Array utility methods.
1589 var array = Class("array", Array, {
1590 init: function (ary) {
1591 if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1592 ary = [k for (k in ary)];
1593 else if (ary.length)
1594 ary = Array.slice(ary);
1598 __iterator__: function () this.iterItems(),
1599 __noSuchMethod__: function (meth, args) {
1600 var res = array[meth].apply(null, [this.array].concat(args));
1603 if (isinstance(res, ["Iterator", "Generator"]))
1608 toString: function () this.array.toString(),
1609 concat: function () this.__noSuchMethod__("concat", Array.slice(arguments)),
1610 filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)),
1611 map: function () this.__noSuchMethod__("map", Array.slice(arguments))
1616 * Converts an array to an object. As in lisp, an assoc is an
1617 * array of key-value pairs, which maps directly to an object,
1619 * [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1621 * @param {[Array]} assoc
1622 * @... {string} 0 - Key
1625 toObject: function toObject(assoc) {
1627 assoc.forEach(function ([k, v]) {
1628 if (v instanceof Class.Property)
1629 Object.defineProperty(obj, k, v.init(k, obj) || v);
1637 * Compacts an array, removing all elements that are null or undefined:
1638 * ["foo", null, "bar", undefined] -> ["foo", "bar"]
1640 * @param {Array} ary
1643 compact: function compact(ary) ary.filter(function (item) item != null),
1646 * Returns true if each element of ary1 is equal to the
1647 * corresponding element in ary2.
1649 * @param {Array} ary1
1650 * @param {Array} ary2
1651 * @returns {boolean}
1653 equals: function (ary1, ary2)
1654 ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1657 * Flattens an array, such that all elements of the array are
1658 * joined into a single array:
1659 * [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1661 * @param {Array} ary
1664 flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1667 * Returns an Iterator for an array's values.
1669 * @param {Array} ary
1670 * @returns {Iterator(Object)}
1672 iterValues: function iterValues(ary) {
1673 for (let i = 0; i < ary.length; i++)
1678 * Returns an Iterator for an array's indices and values.
1680 * @param {Array} ary
1681 * @returns {Iterator([{number}, {Object}])}
1683 iterItems: function iterItems(ary) {
1684 let length = ary.length;
1685 for (let i = 0; i < length; i++)
1690 * Returns the nth member of the given array that matches the
1693 nth: function nth(ary, pred, n, self) {
1694 for (let elem in values(ary))
1695 if (pred.call(self, elem) && n-- === 0)
1701 * Filters out all duplicates from an array. If *unsorted* is false, the
1702 * array is sorted before duplicates are removed.
1704 * @param {Array} ary
1705 * @param {boolean} unsorted
1708 uniq: function uniq(ary, unsorted) {
1711 for (let item in values(ary))
1712 if (res.indexOf(item) == -1)
1716 for (let [, item] in Iterator(ary.sort())) {
1717 if (item != last || !res.length)
1726 * Zips the contents of two arrays. The resulting array is the length of
1727 * ary1, with any shortcomings of ary2 replaced with null strings.
1729 * @param {Array} ary1
1730 * @param {Array} ary2
1733 zip: function zip(ary1, ary2) {
1735 for (let [i, item] in Iterator(ary1))
1736 res.push([item, i in ary2 ? ary2[i] : ""]);
1741 /* Make Minefield not explode, because Minefield exploding is not fun. */
1742 let iterProto = Iter.prototype;
1743 Object.keys(iter).forEach(function (k) {
1744 iterProto[k] = function () {
1745 let res = iter[k].apply(iter, [this].concat(Array.slice(arguments)));
1746 if (isinstance(res, ["Iterator", "Generator"]))
1752 Object.keys(array).forEach(function (k) {
1753 if (!(k in iterProto))
1754 iterProto[k] = function () {
1755 let res = array[k].apply(array, [this.toArray()].concat(Array.slice(arguments)));
1756 if (isinstance(res, ["Iterator", "Generator"]))
1764 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1765 if (!(k in iterProto) && callable(Array.prototype[k]))
1766 iterProto[k] = function () {
1767 let ary = iter(this).toArray();
1768 let res = ary[k].apply(ary, arguments);
1777 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1779 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: