]> git.donarmstrong.com Git - dactyl.git/blob - common/modules/base.jsm
Import 1.0b7.1 supporting Firefox up to 8.*
[dactyl.git] / common / modules / base.jsm
1 // Copyright (c) 2009-2011 by Kris Maglione <maglione.k@gmail.com>
2 //
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
5 "use strict";
6
7 var Cc = Components.classes;
8 var Ci = Components.interfaces;
9 var Cr = Components.results;
10 var Cu = Components.utils;
11
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
13 try {
14     var ctypes;
15     Components.utils.import("resource://gre/modules/ctypes.jsm");
16 }
17 catch (e) {}
18
19 let objproto = Object.prototype;
20 let { __lookupGetter__, __lookupSetter__, hasOwnProperty, propertyIsEnumerable } = objproto;
21
22 if (typeof XPCSafeJSObjectWrapper === "undefined")
23     this.XPCSafeJSObjectWrapper = XPCNativeWrapper;
24
25 if (!XPCNativeWrapper.unwrap)
26     XPCNativeWrapper.unwrap = function unwrap(obj) {
27         if (hasOwnProperty.call(obj, "wrappedJSObject"))
28             return obj.wrappedJSObject;
29         return obj;
30     };
31 if (!Object.create)
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]);
36         return obj;
37     };
38 if (!Object.defineProperty)
39     Object.defineProperty = function defineProperty(obj, prop, desc) {
40         try {
41             let value = desc.value;
42             if ("value" in desc)
43                 if (desc.writable && !__lookupGetter__.call(obj, prop)
44                                   && !__lookupSetter__.call(obj, prop))
45                     try {
46                         obj[prop] = value;
47                     }
48                     catch (e if e instanceof TypeError) {}
49                 else {
50                     objproto.__defineGetter__.call(obj, prop, function () value);
51                     if (desc.writable)
52                         objproto.__defineSetter__.call(obj, prop, function (val) { value = val; });
53                 }
54
55             if ("get" in desc)
56                 objproto.__defineGetter__.call(obj, prop, desc.get);
57             if ("set" in desc)
58                 objproto.__defineSetter__.call(obj, prop, desc.set);
59         }
60         catch (e) {
61             throw e.stack ? e : Error(e);
62         }
63     };
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);
68     };
69 if (!Object.freeze)
70     Object.freeze = function freeze(obj) {};
71 if (!Object.getPropertyDescriptor)
72     Object.getPropertyDescriptor = function getPropertyDescriptor(obj, prop) {
73         try {
74             let desc = {
75                 configurable: true,
76                 enumerable: propertyIsEnumerable.call(obj, prop)
77             };
78             var get = __lookupGetter__.call(obj, prop),
79                 set = __lookupSetter__.call(obj, prop);
80             if (!get && !set) {
81                 desc.value = obj[prop];
82                 desc.writable = true;
83             }
84             if (get)
85                 desc.get = get;
86             if (set)
87                 desc.set = set;
88             return desc;
89         }
90         catch (e) {
91             throw e.stack ? e : Error(e);
92         }
93     };
94 if (!Object.getOwnPropertyDescriptor)
95     Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
96         if (hasOwnProperty.call(obj, prop))
97             return Object.getPropertyDescriptor(obj, prop);
98     };
99 if (!Object.getOwnPropertyNames)
100     Object.getOwnPropertyNames = function getOwnPropertyNames(obj, _debugger) {
101         try {
102             // This is an ugly and unfortunately necessary hack.
103             if (hasOwnProperty.call(obj, "__iterator__")) {
104                 var oldIter = obj.__iterator__;
105                 delete obj.__iterator__;
106             }
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__");
111             }
112             return res;
113         }
114         catch (e) {
115             throw e.stack ? e : Error(e);
116         }
117     };
118 if (!Object.getPrototypeOf)
119     Object.getPrototypeOf = function getPrototypeOf(obj) obj.__proto__;
120 if (!Object.keys)
121     Object.keys = function keys(obj)
122         Object.getOwnPropertyNames(obj).filter(function (k) propertyIsEnumerable.call(obj, k));
123
124 let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__;
125
126 let use = {};
127 let loaded = {};
128 let currentModule;
129 let global = this;
130 function defineModule(name, params, module) {
131     if (!module)
132         module = getGlobalForObject(params);
133
134     module.NAME = name;
135     module.EXPORTED_SYMBOLS = params.exports || [];
136     defineModule.loadLog.push("defineModule " + name);
137     for (let [, mod] in Iterator(params.require || []))
138         require(module, mod);
139
140     for (let [, mod] in Iterator(params.use || []))
141         if (loaded.hasOwnProperty(mod))
142             require(module, mod, "use");
143         else {
144             use[mod] = use[mod] || [];
145             use[mod].push(module);
146         }
147     currentModule = module;
148     module.startTime = Date.now();
149 }
150
151 defineModule.loadLog = [];
152 Object.defineProperty(defineModule.loadLog, "push", {
153     value: function (val) {
154         if (false)
155             defineModule.dump(val + "\n");
156         this[this.length] = Date.now() + " " + val;
157     }
158 });
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);
163         return msg;
164     }).join(", ");
165     let name = loaded.config ? config.name : "dactyl";
166     dump(String.replace(msg, /\n?$/, "\n")
167                .replace(/^./gm, name + ": $&"));
168 }
169 defineModule.modules = [];
170 defineModule.time = function time(major, minor, func, self) {
171     let time = Date.now();
172     if (typeof func !== "function")
173         func = self[func];
174
175     try {
176         var res = func.apply(self, Array.slice(arguments, 4));
177     }
178     catch (e) {
179         loaded.util && util.reportError(e);
180     }
181
182     JSMLoader.times.add(major, minor, Date.now() - time);
183     return res;
184 }
185
186 function endModule() {
187     defineModule.loadLog.push("endModule " + currentModule.NAME);
188
189     for (let [, mod] in Iterator(use[currentModule.NAME] || []))
190         require(mod, currentModule.NAME, "use");
191
192     loaded[currentModule.NAME] = 1;
193 }
194
195 function require(obj, name, from) {
196     try {
197         if (arguments.length === 1)
198             [obj, name] = [{}, obj];
199
200         let caller = Components.stack.caller;
201
202         if (!loaded[name])
203             defineModule.loadLog.push((from || "require") + ": loading " + name + " into " + (obj.NAME || caller.filename + ":" + caller.lineNumber));
204
205         JSMLoader.load(name + ".jsm", obj);
206         return obj;
207     }
208     catch (e) {
209         defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
210         if (loaded.util)
211             util.reportError(e);
212         else
213             defineModule.dump("    " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e + "\n");
214     }
215 }
216
217 defineModule("base", {
218     // sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/   "\2",/p' base.jsm | sort | fmt
219     exports: [
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"
226     ],
227     use: ["config", "services", "util"]
228 }, this);
229
230 function Runnable(self, func, args) {
231     return {
232         __proto__: Runnable.prototype,
233         run: function () { func.apply(self, args || []); }
234     };
235 }
236 Runnable.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIRunnable]);
237
238 /**
239  * Returns a list of all of the top-level properties of an object, by
240  * way of the debugger.
241  *
242  * @param {object} obj
243  * @returns [jsdIProperty]
244  */
245 function debuggerProperties(obj) {
246     if (loaded.services && services.debugger.isOn) {
247         let res = {};
248         services.debugger.wrapValue(obj).getProperties(res, {});
249         return res.value;
250     }
251 }
252
253 /**
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.
257  *
258  * @param {object} obj The object to inspect.
259  * @param {boolean} properties Whether to inspect the prototype chain
260  * @default false
261  * @returns {Generator}
262  */
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));
268
269 function properties(obj, prototypes, debugger_) {
270     let orig = obj;
271     let seen = { dactylPropertyNames: true };
272
273     try {
274         if ("dactylPropertyNames" in obj && !prototypes)
275             for (let key in values(obj.dactylPropertyNames))
276                 if (key in obj && !Set.add(seen, key))
277                     yield key;
278     }
279     catch (e) {}
280
281     for (; obj; obj = prototypes && prototype(obj)) {
282         try {
283             if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn)
284                 var iter = values(Object.getOwnPropertyNames(obj));
285         }
286         catch (e) {}
287         if (!iter)
288             iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
289
290         for (let key in iter)
291             if (!prototypes || !Set.add(seen, key) && obj != orig)
292                 yield key;
293     }
294 }
295
296 function iterOwnProperties(obj) {
297     for (let prop in properties(obj))
298         yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
299 }
300
301 function deprecated(alternative, fn) {
302     if (isObject(fn))
303         return Class.Property(iter(fn).map(function ([k, v]) [k, callable(v) ? deprecated(alternative, v) : v])
304                                       .toObject());
305
306     let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
307
308     function deprecatedMethod() {
309         let obj = !this                      ? "" :
310                   this.className             ? this.className + "#" :
311                   this.constructor.className ? this.constructor.className + "#" :
312                       "";
313
314         deprecated.warn(func, obj + (fn.name || name), alternative);
315         return func.apply(this, arguments);
316     }
317
318     return callable(fn) ? deprecatedMethod : Class.Property({
319         get: function () deprecatedMethod,
320         init: function (prop) { name = prop; }
321     });
322 }
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"
328         ]);
329
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));
335 }
336
337 /**
338  * Iterates over all of the top-level, iterable property names of an
339  * object.
340  *
341  * @param {object} obj The object to inspect.
342  * @returns {Generator}
343  */
344 function keys(obj) iter(function keys() {
345     for (var k in obj)
346         if (hasOwnProperty.call(obj, k))
347             yield k;
348 }());
349 /**
350  * Iterates over all of the top-level, iterable property values of an
351  * object.
352  *
353  * @param {object} obj The object to inspect.
354  * @returns {Generator}
355  */
356 function values(obj) iter(function values() {
357     if (isinstance(obj, ["Generator", "Iterator"]))
358         for (let k in obj)
359             yield k;
360     else
361         for (var k in obj)
362             if (hasOwnProperty.call(obj, k))
363                 yield obj[k];
364 }());
365
366 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
367 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
368
369 /**
370  * Utility for managing sets of strings. Given an array, returns an
371  * object with one key for each value thereof.
372  *
373  * @param {[string]} ary @optional
374  * @returns {object}
375  */
376 function Set(ary) {
377     let obj = {};
378     if (ary)
379         for (let val in values(ary))
380             obj[val] = true;
381     return obj;
382 }
383 /**
384  * Adds an element to a set and returns true if the element was
385  * previously contained.
386  *
387  * @param {object} set The set.
388  * @param {string} key The key to add.
389  * @returns boolean
390  */
391 Set.add = curry(function set_add(set, key) {
392     let res = this.has(set, key);
393     set[key] = true;
394     return res;
395 });
396 /**
397  * Returns true if the given set contains the given key.
398  *
399  * @param {object} set The set.
400  * @param {string} key The key to check.
401  * @returns {boolean}
402  */
403 Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
404                                            propertyIsEnumerable.call(set, key));
405 /**
406  * Returns a new set containing the members of the first argument which
407  * do not exist in any of the other given arguments.
408  *
409  * @param {object} set The set.
410  * @returns {object}
411  */
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]))
416             delete set[k];
417     return set;
418 };
419 /**
420  * Removes an element from a set and returns true if the element was
421  * previously contained.
422  *
423  * @param {object} set The set.
424  * @param {string} key The key to remove.
425  * @returns boolean
426  */
427 Set.remove = curry(function set_remove(set, key) {
428     let res = set.has(set, key);
429     delete set[key];
430     return res;
431 });
432
433 function set() {
434     deprecated.warn(set, "set", "Set");
435     return Set.apply(this, arguments);
436 }
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);
441     };
442 });
443
444 /**
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.
450  *
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";
455  *
456  * @param {function} fn The function to curry.
457  * @param {integer} length The number of arguments expected.
458  *     @default fn.length
459  *     @optional
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
462  *     preserved.
463  *     @optional
464  */
465 function curry(fn, length, self, acc) {
466     if (length == null)
467         length = fn.length;
468     if (length == 0)
469         return fn;
470
471     // Close over function with 'this'
472     function close(self, fn) function () fn.apply(self, Array.slice(arguments));
473
474     if (acc == null)
475         acc = [];
476
477     return function curried() {
478         let args = acc.concat(Array.slice(arguments));
479
480         // The curried result should preserve 'this'
481         if (arguments.length == 0)
482             return close(self || this, curried);
483
484         if (args.length >= length)
485             return fn.apply(self || this, args);
486
487         return curry(fn, length, self || this, args);
488     };
489 }
490
491 if (curry.bind)
492     var bind = function bind(meth, self) let (func = callable(meth) ? meth : self[meth])
493         func.bind.apply(func, Array.slice(arguments, 1));
494 else
495     var bind = function bind(func, self) {
496         if (!callable(func))
497             func = self[func];
498
499         let args = Array.slice(arguments, bind.length);
500         return function bound() func.apply(self, args.concat(Array.slice(arguments)));
501     };
502
503 /**
504  * Returns true if both arguments are functions and
505  * (targ() instanceof src) would also return true.
506  *
507  * @param {function} targ
508  * @param {function} src
509  * @returns {boolean}
510  */
511 function isSubclass(targ, src) {
512     return src === targ ||
513         targ && typeof targ === "function" && targ.prototype instanceof src;
514 }
515
516 /**
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>]").
524  *
525  * @param {object} object The object to check.
526  * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
527  * @returns {boolean}
528  */
529 var isinstance_types = {
530     boolean: Boolean,
531     string: String,
532     function: Function,
533     number: Number
534 };
535 function isinstance(object, interfaces) {
536     if (object == null)
537         return false;
538
539     return Array.concat(interfaces).some(function isinstance_some(iface) {
540         if (typeof iface === "string") {
541             if (objproto.toString.call(object) === "[object " + iface + "]")
542                 return true;
543         }
544         else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
545             if (object.isinstance(iface))
546                 return true;
547         }
548         else {
549             if (object instanceof iface)
550                 return true;
551             var type = isinstance_types[typeof object];
552             if (type && isSubclass(iface, type))
553                 return true;
554         }
555         return false;
556     });
557 }
558
559 /**
560  * Returns true if obj is a non-null object.
561  */
562 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
563
564 /**
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).
569  */
570 var isArray =
571     Array.isArray
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]";
575
576 /**
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)).
581  */
582 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
583
584 /**
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").
590  */
591 function isString(val) objproto.toString.call(val) == "[object String]";
592
593 /**
594  * Returns true if and only if its sole argument may be called
595  * as a function. This includes classes and function objects.
596  */
597 function callable(val) typeof val === "function";
598
599 function call(fn) {
600     fn.apply(arguments[1], Array.slice(arguments, 2));
601     return fn;
602 }
603
604 /**
605  * Memoizes an object property value.
606  *
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.
611  */
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);
617             if (get)
618                 memoize(obj, prop, get);
619         }
620         return obj;
621     }
622
623     try {
624         Object.defineProperty(obj, key, {
625             configurable: true,
626             enumerable: true,
627
628             get: function g_replaceProperty() (
629                 Class.replaceProperty(this.instance || this, key, null),
630                 Class.replaceProperty(this.instance || this, key, getter.call(this, key))),
631
632             set: function s_replaceProperty(val)
633                 Class.replaceProperty(this.instance || this, key, val)
634         });
635     }
636     catch (e) {
637         obj[key] = getter.call(obj, key);
638     }
639 }
640
641 let sandbox = Cu.Sandbox(this);
642 sandbox.__proto__ = this;
643 /**
644  * Wraps a function so that when called, the global object of the caller
645  * is prepended to its arguments.
646  */
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()
651             fn.apply(this,
652                      [Class.objectGlobal(withCallerGlobal_wrapped.caller)]
653                         .concat(Array.slice(arguments))))
654 ]]>, Cu.Sandbox(this), "1.8");
655
656 /**
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
661  * prototype.
662  *
663  *    let a = { foo: function (arg) "bar " + arg }
664  *    let b = { __proto__: a }
665  *    update(b, { foo: function foo() foo.supercall(this, "baz") });
666  *
667  *    a.foo("foo") -> "bar foo"
668  *    b.foo()      -> "bar baz"
669  *
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.
674  */
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;
682
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));
692                 }
693             }
694             try {
695                 Object.defineProperty(target, k, desc);
696             }
697             catch (e) {}
698         });
699     }
700     return target;
701 }
702
703 /**
704  * @constructor Class
705  *
706  * Constructs a new Class. Arguments marked as optional must be
707  * either entirely elided, or they must have the exact type
708  * specified.
709  *
710  * @param {string} name The class's as it will appear when toString
711  *     is called, as well as in stack traces.
712  *     @optional
713  * @param {function} base The base class for this module. May be any
714  *     callable object.
715  *     @optional
716  *     @default Class
717  * @param {Object} prototype The prototype for instances of this
718  *     object. The object itself is copied and not used as a prototype
719  *     directly.
720  * @param {Object} classProperties The class properties for the new
721  *     module constructor. More than one may be provided.
722  *     @optional
723  *
724  * @returns {function} The constructor for the resulting class.
725  */
726 function Class() {
727
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();
734
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 },
739             });
740             self.instance = self;
741             var res = self.init.apply(self, arguments);
742             return res !== undefined ? res : self;
743         };
744     else
745         var Constructor = eval(String.replace(<![CDATA[
746             (function constructor(PARAMS) {
747                 var self = Object.create(Constructor.prototype, {
748                     constructor: { value: Constructor },
749                 });
750                 self.instance = self;
751                 var res = self.init.apply(self, arguments);
752                 return res !== undefined ? res : self;
753             })]]>,
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_")));
757
758     Constructor.className = name || superclass.className || superclass.name;
759
760     if ("init" in superclass.prototype)
761         Constructor.__proto__ = superclass;
762     else {
763         let superc = superclass;
764         superclass = function Shim() {};
765         Class.extend(superclass, superc, {
766             init: superc
767         });
768         superclass.__proto__ = superc;
769     }
770
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) {
776         if (callable(obj))
777             obj = obj.prototype;
778         update(Constructor.prototype, obj);
779     });
780     return Constructor;
781 }
782
783 if (Cu.getGlobalForObject)
784     Class.objectGlobal = function (object) {
785         try {
786             return Cu.getGlobalForObject(object);
787         }
788         catch (e) {
789             return null;
790         }
791     };
792 else
793     Class.objectGlobal = function (object) {
794         while (object.__parent__)
795             object = object.__parent__;
796         return object;
797     };
798
799 /**
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
805  * assigned.
806  *
807  * @param {Object} desc The property descriptor.
808  */
809 Class.Property = function Property(desc) update(
810     Object.create(Property.prototype), desc || { configurable: true, writable: true });
811 Class.Property.prototype.init = function () {};
812 /**
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*.
817  *
818  * @param {function} subclass
819  * @param {function} superclass
820  * @param {Object} overrides @optional
821  */
822 Class.extend = function extend(subclass, superclass, overrides) {
823     subclass.superclass = superclass;
824
825     subclass.prototype = Object.create(superclass.prototype);
826     update(subclass.prototype, overrides);
827     subclass.prototype.constructor = subclass;
828     subclass.prototype._class_ = subclass;
829
830     if (superclass.prototype.constructor === objproto.constructor)
831         superclass.prototype.constructor = superclass;
832 }
833
834 /**
835  * Memoizes the value of a class property to the value returned by
836  * the passed function the first time the property is accessed.
837  *
838  * @param {function(string)} getter The function which returns the
839  *      property's value.
840  * @returns {Class.Property}
841  */
842 Class.memoize = function memoize(getter, wait)
843     Class.Property({
844         configurable: true,
845         enumerable: true,
846         init: function (key) {
847             let done = false;
848
849             if (wait)
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);
856                             return this[key];
857                         }
858                     });
859
860                     util.yieldable(function () {
861                         let wait;
862                         for (var res in getter.call(obj)) {
863                             if (wait !== undefined)
864                                 yield wait;
865                             wait = res;
866                         }
867                         Class.replaceProperty(obj, key, res);
868                         done = true;
869                     })();
870
871                     return this[key];
872                 };
873             else
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));
878                 };
879
880             this.set = function replace(val) Class.replaceProperty(this.instance || this, val);
881         }
882     });
883
884 Class.replaceProperty = function replaceProperty(obj, prop, value) {
885     Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
886     return value;
887 };
888 Class.toString = function toString() "[class " + this.className + "]";
889 Class.prototype = {
890     /**
891      * Initializes new instances of this class. Called automatically
892      * when new instances are created.
893      */
894     init: function c_init() {},
895
896     withSavedValues: function withSavedValues(names, callback, self) {
897         let vals = names.map(function (name) this[name], this);
898         try {
899             return callback.call(self || this);
900         }
901         finally {
902             names.forEach(function (name, i) this[name] = vals[i], this);
903         }
904     },
905
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))
910                                    .join(", ") + ")";
911         return "[instance " + this.constructor.className + (params || "") + "]";
912     },
913
914     /**
915      * Executes *callback* after *timeout* milliseconds. The value of
916      * 'this' is preserved in the invocation of *callback*.
917      *
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.
922      */
923     timeout: function timeout(callback, timeout) {
924         const self = this;
925         function timeout_notify(timer) {
926             if (self.stale ||
927                     util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
928                 return;
929             util.trapErrors(callback, self);
930         }
931         return services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
932     },
933
934     /**
935      * Updates this instance with the properties of the given objects.
936      * Like the update function, but with special semantics for
937      * localized properties.
938      */
939     update: function update() {
940         let self = this;
941         // XXX: Duplication.
942
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;
949
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));
959                     }
960                 }
961
962                 try {
963                     if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties))
964                         this[k] = desc.value;
965                     else
966                         Object.defineProperty(this, k, desc);
967                 }
968                 catch (e) {}
969             }, this);
970         }
971     },
972
973     magicalProperties: {}
974 };
975 Class.makeClosure = function makeClosure() {
976     const self = this;
977     function closure(fn) {
978         function _closure() {
979             try {
980                 return fn.apply(self, arguments);
981             }
982             catch (e if !(e instanceof FailedAssertion)) {
983                 util.reportError(e);
984                 throw e.stack ? e : Error(e);
985             }
986         }
987         _closure.wrapped = fn;
988         return _closure;
989     }
990
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, {
996                 configurable: true,
997                 enumerable: true,
998                 get: function get_proxy() self[k],
999                 set: function set_proxy(val) self[k] = val,
1000             });
1001     }, this);
1002
1003     return closure;
1004 };
1005 memoize(Class.prototype, "closure", Class.makeClosure);
1006
1007 /**
1008  * A base class generator for classes which implement XPCOM interfaces.
1009  *
1010  * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
1011  *      implements.
1012  * @param {Class} superClass A super class. @optional
1013  * @returns {Class}
1014  */
1015 function XPCOM(interfaces, superClass) {
1016     interfaces = Array.concat(interfaces);
1017
1018     let shim = interfaces.reduce(function (shim, iface) shim.QueryInterface(iface),
1019                                  Cc["@dactyl.googlecode.com/base/xpc-interface-shim"].createInstance());
1020
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;
1026     return res;
1027 }
1028
1029 /**
1030  * An abstract base class for classes that wish to inherit from Error.
1031  */
1032 var ErrorBase = Class("ErrorBase", Error, {
1033     level: 2,
1034     init: function EB_init(message, level) {
1035         level = level || 0;
1036         let error = Error(message);
1037         update(this, error)
1038         this.stack = error.stack;
1039         this.message = message;
1040
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/, "");
1045         }
1046         this.fileName = frame.filename;
1047         this.lineNumber = frame.lineNumber;
1048     },
1049     toString: function () String(this.message)
1050 });
1051
1052 /**
1053  * Constructs a new Module class and instantiates an instance into the current
1054  * module global object.
1055  *
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.
1059  * @returns {Class}
1060  */
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();
1066
1067     instance.INIT = update(Object.create(Module.INIT),
1068                            arguments[init] || {});
1069
1070     currentModule[module.className] = instance;
1071     defineModule.modules.push(instance);
1072     return module;
1073 }
1074 Module.INIT = {
1075     init: function Module_INIT_init(dactyl, modules, window) {
1076         let args = arguments;
1077
1078         let locals = [];
1079         for (let local = this.Local; local; local = local.super)
1080             locals.push(local);
1081
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;
1087             }
1088             module.isLocalModule = true;
1089
1090             modules.jsmodules[this.constructor.className] = module;
1091             locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args)))
1092
1093             memoize(module, "closure", Class.makeClosure);
1094             module.instance = module;
1095             module.init();
1096
1097             if (module.signals)
1098                 modules.dactyl.registerObservers(module);
1099         }
1100     }
1101 }
1102
1103 /**
1104  * @class Struct
1105  *
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.
1112  *
1113  *     const Point = Struct("x", "y", "z");
1114  *     let p1 = Point(x, y, z);
1115  *
1116  * @returns {function} The constructor for the new Struct.
1117  */
1118 function Struct() {
1119     if (!/^[A-Z]/.test(arguments[0]))
1120         var args = Array.slice(arguments, 0);
1121     else {
1122         var className = arguments[0];
1123         args = Array.slice(arguments, 1);
1124     }
1125
1126     const Struct = Class(className || "Struct", StructBase, {
1127         length: args.length,
1128         members: array.toObject(args.map(function (v, k) [v, k]))
1129     });
1130     args.forEach(function (name, i) {
1131         Struct.prototype.__defineGetter__(name, function () this[i]);
1132         Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1133     });
1134     return Struct;
1135 }
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];
1141     },
1142
1143     clone: function struct_clone() this.constructor.apply(null, this.slice()),
1144
1145     closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1146
1147     get: function struct_get(key, val) this[this.members[key]],
1148     set: function struct_set(key, val) this[this.members[key]] = val,
1149
1150     toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1151
1152     // Iterator over our named members
1153     __iterator__: function struct__iterator__() {
1154         let self = this;
1155         return ([k, self[k]] for (k in keys(self.members)))
1156     }
1157 }, {
1158     fromArray: function fromArray(ary) {
1159         if (!(ary instanceof this))
1160             ary.__proto__ = this.prototype;
1161         return ary;
1162     },
1163
1164     /**
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.
1168      *
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.
1173      */
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));
1179         return this;
1180     },
1181
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));
1185         return this;
1186     }
1187 });
1188
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;
1196         this.doneAt = 0;
1197         this.latest = 0;
1198     },
1199
1200     notify: function notify(timer, force) {
1201         try {
1202             if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1203                 return;
1204
1205             this._timer.cancel();
1206             this.latest = 0;
1207             // minInterval is the time between the completion of the command and the next firing
1208             this.doneAt = Date.now() + this.minInterval;
1209
1210             this.callback.call(this.self, this.arg);
1211         }
1212         catch (e) {
1213             if (typeof util === "undefined")
1214                 dump("dactyl: " + e + "\n" + (e.stack || Error().stack));
1215             else
1216                 util.reportError(e);
1217         }
1218         finally {
1219             this.doneAt = Date.now() + this.minInterval;
1220         }
1221     },
1222
1223     tell: function tell(arg) {
1224         if (arguments.length > 0)
1225             this.arg = arg;
1226
1227         let now = Date.now();
1228         if (this.doneAt == -1)
1229             this._timer.cancel();
1230
1231         let timeout = this.minInterval;
1232         if (now > this.doneAt && this.doneAt > -1)
1233             timeout = 0;
1234         else if (this.latest)
1235             timeout = Math.min(timeout, this.latest - now);
1236         else
1237             this.latest = now + this.maxInterval;
1238
1239         this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1240         this.doneAt = -1;
1241     },
1242
1243     reset: function reset() {
1244         this._timer.cancel();
1245         this.doneAt = 0;
1246     },
1247
1248     flush: function flush(force) {
1249         if (this.doneAt == -1 || force)
1250             this.notify(null, true);
1251     }
1252 });
1253
1254 /**
1255  * Idempotent function which returns the UTF-8 encoded value of an
1256  * improperly-decoded string.
1257  *
1258  * @param {string} str
1259  * @returns {string}
1260  */
1261 function UTF8(str) {
1262     try {
1263         return decodeURIComponent(escape(str));
1264     }
1265     catch (e) {
1266         return str;
1267     }
1268 }
1269
1270 function octal(decimal) parseInt(decimal, 8);
1271
1272 /**
1273  * Iterates over an arbitrary object. The following iterator types are
1274  * supported, and work as a user would expect:
1275  *
1276  *  â€¢ nsIDOMNodeIterator
1277  *  â€¢ mozIStorageStatement
1278  *
1279  * Additionally, the following array-like objects yield a tuple of the
1280  * form [index, element] for each contained element:
1281  *
1282  *  â€¢ nsIDOMHTMLCollection
1283  *  â€¢ nsIDOMNodeList
1284  *
1285  * and the following likewise yield one element of the form
1286  * [name, element] for each contained element:
1287  *
1288  *  â€¢ nsIDOMNamedNodeMap
1289  *
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.
1295  *
1296  * For all other cases, this function behaves exactly like the Iterator
1297  * function.
1298  *
1299  * @param {object} obj
1300  * @returns {Generator}
1301  */
1302 function iter(obj) {
1303     let args = arguments;
1304     let res = Iterator(obj);
1305
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]))
1310                     yield j;
1311         })();
1312     else if (isinstance(obj, ["Iterator", "Generator"]))
1313         ;
1314     else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1315         while (obj.constructor instanceof ctypes.PointerType)
1316             obj = obj.contents;
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]];
1323             })();
1324         else
1325             return iter({});
1326     }
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];
1333         })();
1334     else if (obj instanceof Ci.mozIStorageStatement)
1335         res = (function (obj) {
1336             while (obj.executeStep())
1337                 yield obj.row;
1338             obj.reset();
1339         })(obj);
1340     else if ("getNext" in obj) {
1341         if ("hasMoreElements" in obj)
1342             res = (function () {
1343                 while (obj.hasMoreElements())
1344                     yield obj.getNext();
1345             })();
1346         else if ("hasMore" in obj)
1347             res = (function () {
1348                 while (obj.hasMore())
1349                     yield obj.getNext();
1350             })();
1351     }
1352     else if ("enumerator" in obj) {
1353         if (callable(obj.enumerator))
1354             return iter(obj.enumerator());
1355         return iter(obj.enumerator);
1356     }
1357     res.__noSuchMethod__ = function __noSuchMethod__(meth, args) {
1358         if (meth in iter)
1359             var res = iter[meth].apply(iter, [this].concat(args));
1360         else
1361             res = let (ary = array(this))
1362                 ary[meth] ? ary[meth].apply(ary, args) : ary.__noSuchMethod__(meth, args);
1363         if (isinstance(res, ["Iterator", "Generator"]))
1364             return iter(res);
1365         return res;
1366     };
1367     return res;
1368 }
1369 update(iter, {
1370     toArray: function toArray(iter) array(iter).array,
1371
1372     // See array.prototype for API docs.
1373     toObject: function toObject(iter) {
1374         let obj = {};
1375         for (let [k, v] in iter)
1376             if (v instanceof Class.Property)
1377                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1378             else
1379                 obj[k] = v;
1380         return obj;
1381     },
1382
1383     compact: function compact(iter) (item for (item in iter) if (item != null)),
1384
1385     every: function every(iter, pred, self) {
1386         pred = pred || util.identity;
1387         for (let elem in iter)
1388             if (!pred.call(self, elem))
1389                 return false;
1390         return true;
1391     },
1392     some: function every(iter, pred, self) {
1393         pred = pred || util.identity;
1394         for (let elem in iter)
1395             if (pred.call(self, elem))
1396                 return true;
1397         return false;
1398     },
1399
1400     filter: function filter(iter, pred, self) {
1401         for (let elem in iter)
1402             if (pred.call(self, elem))
1403                 yield elem;
1404     },
1405
1406     /**
1407      * Iterates over an iterable object and calls a callback for each
1408      * element.
1409      *
1410      * @param {object} iter The iterator.
1411      * @param {function} fn The callback.
1412      * @param {object} self The this object for *fn*.
1413      */
1414     forEach: function forEach(iter, func, self) {
1415         for (let val in iter)
1416             func.call(self, val);
1417     },
1418
1419     indexOf: function indexOf(iter, elem) {
1420         let i = 0;
1421         for (let item in iter) {
1422             if (item == elem)
1423                 return i;
1424             i++;
1425         }
1426     },
1427
1428     /**
1429      * Returns the array that results from applying *func* to each property of
1430      * *obj*.
1431      *
1432      * @param {Object} obj
1433      * @param {function} func
1434      * @returns {Array}
1435      */
1436     map: function map(iter, func, self) {
1437         for (let i in iter)
1438             yield func.call(self, i);
1439     },
1440
1441     /**
1442      * Returns the nth member of the given array that matches the
1443      * given predicate.
1444      */
1445     nth: function nth(iter, pred, n, self) {
1446         if (typeof pred === "number")
1447             [pred, n] = [function () true, pred]; // Hack.
1448
1449         for (let elem in iter)
1450             if (pred.call(self, elem) && n-- === 0)
1451                 return elem;
1452         return undefined;
1453     },
1454
1455     sort: function sort(iter, fn, self)
1456         array(this.toArray(iter).sort(fn, self)),
1457
1458     uniq: function uniq(iter) {
1459         let seen = {};
1460         for (let item in iter)
1461             if (!Set.add(seen, item))
1462                 yield item;
1463     },
1464
1465     /**
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.
1468      *
1469      * @param {Array} ary1
1470      * @param {Array} ary2
1471      * @returns {Array}
1472      */
1473     zip: function zip(iter1, iter2) {
1474         try {
1475             yield [iter1.next(), iter2.next()];
1476         }
1477         catch (e if e instanceof StopIteration) {}
1478     }
1479 });
1480
1481 /**
1482  * Array utility methods.
1483  */
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);
1490
1491         return {
1492             __proto__: ary,
1493             __iterator__: function () this.iterItems(),
1494             __noSuchMethod__: function (meth, args) {
1495                 var res = array[meth].apply(null, [this.array].concat(args));
1496                 if (isArray(res))
1497                     return array(res);
1498                 if (isinstance(res, ["Iterator", "Generator"]))
1499                     return iter(res);
1500                 return res;
1501             },
1502             array: ary,
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))
1507         };
1508     }
1509 }, {
1510     /**
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,
1513      * as such:
1514      *    [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1515      *
1516      * @param {[Array]} assoc
1517      * @... {string} 0 - Key
1518      * @...          1 - Value
1519      */
1520     toObject: function toObject(assoc) {
1521         let obj = {};
1522         assoc.forEach(function ([k, v]) {
1523             if (v instanceof Class.Property)
1524                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1525             else
1526                 obj[k] = v;
1527         });
1528         return obj;
1529     },
1530
1531     /**
1532      * Compacts an array, removing all elements that are null or undefined:
1533      *    ["foo", null, "bar", undefined] -> ["foo", "bar"]
1534      *
1535      * @param {Array} ary
1536      * @returns {Array}
1537      */
1538     compact: function compact(ary) ary.filter(function (item) item != null),
1539
1540     /**
1541      * Returns true if each element of ary1 is equal to the
1542      * corresponding element in ary2.
1543      *
1544      * @param {Array} ary1
1545      * @param {Array} ary2
1546      * @returns {boolean}
1547      */
1548     equals: function (ary1, ary2)
1549         ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1550
1551     /**
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"]
1555      *
1556      * @param {Array} ary
1557      * @returns {Array}
1558      */
1559     flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1560
1561     /**
1562      * Returns an Iterator for an array's values.
1563      *
1564      * @param {Array} ary
1565      * @returns {Iterator(Object)}
1566      */
1567     iterValues: function iterValues(ary) {
1568         for (let i = 0; i < ary.length; i++)
1569             yield ary[i];
1570     },
1571
1572     /**
1573      * Returns an Iterator for an array's indices and values.
1574      *
1575      * @param {Array} ary
1576      * @returns {Iterator([{number}, {Object}])}
1577      */
1578     iterItems: function iterItems(ary) {
1579         let length = ary.length;
1580         for (let i = 0; i < length; i++)
1581             yield [i, ary[i]];
1582     },
1583
1584     /**
1585      * Returns the nth member of the given array that matches the
1586      * given predicate.
1587      */
1588     nth: function nth(ary, pred, n, self) {
1589         for (let elem in values(ary))
1590             if (pred.call(self, elem) && n-- === 0)
1591                 return elem;
1592         return undefined;
1593     },
1594
1595     /**
1596      * Filters out all duplicates from an array. If *unsorted* is false, the
1597      * array is sorted before duplicates are removed.
1598      *
1599      * @param {Array} ary
1600      * @param {boolean} unsorted
1601      * @returns {Array}
1602      */
1603     uniq: function uniq(ary, unsorted) {
1604         let res = [];
1605         if (unsorted) {
1606             for (let item in values(ary))
1607                 if (res.indexOf(item) == -1)
1608                     res.push(item);
1609         }
1610         else {
1611             for (let [, item] in Iterator(ary.sort())) {
1612                 if (item != last || !res.length)
1613                     res.push(item);
1614                 var last = item;
1615             }
1616         }
1617         return res;
1618     },
1619
1620     /**
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.
1623      *
1624      * @param {Array} ary1
1625      * @param {Array} ary2
1626      * @returns {Array}
1627      */
1628     zip: function zip(ary1, ary2) {
1629         let res = [];
1630         for (let [i, item] in Iterator(ary1))
1631             res.push([item, i in ary2 ? ary2[i] : ""]);
1632         return res;
1633     }
1634 });
1635
1636 endModule();
1637
1638 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1639
1640 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: