]> git.donarmstrong.com Git - dactyl.git/blob - common/modules/base.jsm
13bd9a03e93fcd3b88a5400ba38061e5b25bf08a
[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         let value = desc.value;
41         if ("value" in desc)
42             if (desc.writable && !__lookupGetter__.call(obj, prop)
43                               && !__lookupSetter__.call(obj, prop))
44                 try {
45                     obj[prop] = value;
46                 }
47                 catch (e if e instanceof TypeError) {}
48             else {
49                 objproto.__defineGetter__.call(obj, prop, function () value);
50                 if (desc.writable)
51                     objproto.__defineSetter__.call(obj, prop, function (val) { value = val; });
52             }
53
54         if ("get" in desc)
55             objproto.__defineGetter__.call(obj, prop, desc.get);
56         if ("set" in desc)
57             objproto.__defineSetter__.call(obj, prop, desc.set);
58     };
59 if (!Object.defineProperties)
60     Object.defineProperties = function defineProperties(obj, props) {
61         for (let [k, v] in Iterator(props))
62             Object.defineProperty(obj, k, v);
63     }
64 if (!Object.freeze)
65     Object.freeze = function freeze(obj) {};
66 if (!Object.getOwnPropertyDescriptor)
67     Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
68         if (!hasOwnProperty.call(obj, prop))
69             return undefined;
70         let desc = {
71             configurable: true,
72             enumerable: propertyIsEnumerable.call(obj, prop)
73         };
74         var get = __lookupGetter__.call(obj, prop),
75             set = __lookupSetter__.call(obj, prop);
76         if (!get && !set) {
77             desc.value = obj[prop];
78             desc.writable = true;
79         }
80         if (get)
81             desc.get = get;
82         if (set)
83             desc.set = set;
84         return desc;
85     };
86 if (!Object.getOwnPropertyNames)
87     Object.getOwnPropertyNames = function getOwnPropertyNames(obj, _debugger) {
88         // This is an ugly and unfortunately necessary hack.
89         if (hasOwnProperty.call(obj, "__iterator__")) {
90             var oldIter = obj.__iterator__;
91             delete obj.__iterator__;
92         }
93         let res = [k for (k in obj) if (hasOwnProperty.call(obj, k))];
94         if (oldIter !== undefined) {
95             obj.__iterator__ = oldIter;
96             res.push("__iterator__");
97         }
98         return res;
99     };
100 if (!Object.getPrototypeOf)
101     Object.getPrototypeOf = function getPrototypeOf(obj) obj.__proto__;
102 if (!Object.keys)
103     Object.keys = function keys(obj)
104         Object.getOwnPropertyNames(obj).filter(function (k) propertyIsEnumerable.call(obj, k));
105
106 let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__;
107
108 let use = {};
109 let loaded = {};
110 let currentModule;
111 let global = this;
112 function defineModule(name, params, module) {
113     if (!module)
114         module = getGlobalForObject(params);
115
116     module.NAME = name;
117     module.EXPORTED_SYMBOLS = params.exports || [];
118     defineModule.loadLog.push("defineModule " + name);
119     for (let [, mod] in Iterator(params.require || []))
120         require(module, mod);
121
122     for (let [, mod] in Iterator(params.use || []))
123         if (loaded.hasOwnProperty(mod))
124             require(module, mod, "use");
125         else {
126             use[mod] = use[mod] || [];
127             use[mod].push(module);
128         }
129     currentModule = module;
130 }
131
132 defineModule.loadLog = [];
133 Object.defineProperty(defineModule.loadLog, "push", {
134     value: function (val) {
135         if (false)
136             defineModule.dump(val + "\n");
137         this[this.length] = Date.now() + " " + val;
138     }
139 });
140 defineModule.dump = function dump_() {
141     let msg = Array.map(arguments, function (msg) {
142         if (loaded.util && typeof msg == "object")
143             msg = util.objectToString(msg);
144         return msg;
145     }).join(", ");
146     let name = loaded.config ? config.name : "dactyl";
147     dump(String.replace(msg, /\n?$/, "\n")
148                .replace(/^./gm, name + ": $&"));
149 }
150 defineModule.modules = [];
151 defineModule.times = { all: 0 };
152 defineModule.time = function time(major, minor, func, self) {
153     let time = Date.now();
154     if (typeof func !== "function")
155         func = self[func];
156
157     try {
158         var res = func.apply(self, Array.slice(arguments, 4));
159     }
160     catch (e) {
161         loaded.util && util.reportError(e);
162     }
163
164     let delta = Date.now() - time;
165     defineModule.times.all += delta;
166     defineModule.times[major] = (defineModule.times[major] || 0) + delta;
167     if (minor) {
168         defineModule.times[":" + minor] = (defineModule.times[":" + minor] || 0) + delta;
169         defineModule.times[major + ":" + minor] = (defineModule.times[major + ":" + minor] || 0) + delta;
170     }
171     return res;
172 }
173
174 function endModule() {
175     defineModule.loadLog.push("endModule " + currentModule.NAME);
176
177     for (let [, mod] in Iterator(use[currentModule.NAME] || []))
178         require(mod, currentModule.NAME, "use");
179
180     loaded[currentModule.NAME] = 1;
181 }
182
183 function require(obj, name, from) {
184     try {
185         if (arguments.length === 1)
186             [obj, name] = [{}, obj];
187
188         if (!loaded[name])
189             defineModule.loadLog.push((from || "require") + ": loading " + name + (obj.NAME ? " into " + obj.NAME : ""));
190
191         JSMLoader.load(name + ".jsm", obj);
192         return obj;
193     }
194     catch (e) {
195         defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
196         if (loaded.util)
197             util.reportError(e);
198         else
199             defineModule.dump("    " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e +"\n");
200     }
201 }
202
203 defineModule("base", {
204     // sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/   "\2",/p' base.jsm | sort | fmt
205     exports: [
206         "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Module", "JSMLoader", "Object", "Runnable",
207         "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMUtils", "XPCSafeJSObjectWrapper",
208         "array", "bind", "call", "callable", "ctypes", "curry", "debuggerProperties", "defineModule",
209         "deprecated", "endModule", "forEach", "isArray", "isGenerator", "isinstance", "isObject",
210         "isString", "isSubclass", "iter", "iterAll", "iterOwnProperties","keys", "memoize", "octal",
211         "properties", "require", "set", "update", "values", "withCallerGlobal"
212     ],
213     use: ["config", "services", "util"]
214 }, this);
215
216 function Runnable(self, func, args) {
217     return {
218         __proto__: Runnable.prototype,
219         run: function () { func.apply(self, args || []); }
220     };
221 }
222 Runnable.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIRunnable]);
223
224 /**
225  * Returns a list of all of the top-level properties of an object, by
226  * way of the debugger.
227  *
228  * @param {object} obj
229  * @returns [jsdIProperty]
230  */
231 function debuggerProperties(obj) {
232     if (loaded.services && services.debugger.isOn) {
233         let res = {};
234         services.debugger.wrapValue(obj).getProperties(res, {});
235         return res.value;
236     }
237 }
238
239 /**
240  * Iterates over the names of all of the top-level properties of an
241  * object or, if prototypes is given, all of the properties in the
242  * prototype chain below the top. Uses the debugger if possible.
243  *
244  * @param {object} obj The object to inspect.
245  * @param {boolean} properties Whether to inspect the prototype chain
246  * @default false
247  * @returns {Generator}
248  */
249 function prototype(obj)
250     /* Temporary hack: */ typeof obj === "xml" || obj.__proto__ !== obj.__proto__ ? null :
251     obj.__proto__ || Object.getPrototypeOf(obj) ||
252     XPCNativeWrapper.unwrap(obj).__proto__ ||
253     Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
254
255 function properties(obj, prototypes, debugger_) {
256     let orig = obj;
257     let seen = { dactylPropertyNames: true };
258
259     try {
260         if ("dactylPropertyNames" in obj && !prototypes)
261             for (let key in values(obj.dactylPropertyNames))
262                 if (key in obj && !set.add(seen, key))
263                     yield key;
264     }
265     catch (e) {}
266
267     for (; obj; obj = prototypes && prototype(obj)) {
268         try {
269             if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn)
270                 var iter = values(Object.getOwnPropertyNames(obj));
271         }
272         catch (e) {}
273         if (!iter)
274             iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
275
276         for (let key in iter)
277             if (!prototypes || !set.add(seen, key) && obj != orig)
278                 yield key;
279     }
280 }
281
282 function iterOwnProperties(obj) {
283     for (let prop in properties(obj))
284         yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
285 }
286
287 function deprecated(alternative, fn) {
288     if (isObject(fn))
289         return Class.Property(iter(fn).map(function ([k, v]) [k, callable(v) ? deprecated(alternative, v) : v])
290                                       .toObject());
291
292     let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
293
294     function deprecatedMethod() {
295         let obj = this.className             ? this.className + "#" :
296                   this.constructor.className ? this.constructor.className + "#" :
297                       "";
298
299         deprecated.warn(func, obj + (fn.name || name), alternative);
300         return func.apply(this, arguments);
301     }
302
303     return callable(fn) ? deprecatedMethod : Class.Property({
304         get: function () deprecatedMethod,
305         init: function (prop) { name = prop; }
306     });
307 }
308 deprecated.warn = function warn(func, name, alternative, frame) {
309     if (!func.seenCaller)
310         func.seenCaller = set([
311             "resource://dactyl" + JSMLoader.suffix + "/javascript.jsm",
312             "resource://dactyl" + JSMLoader.suffix + "/util.jsm"
313         ]);
314
315     frame = frame || Components.stack.caller.caller;
316     let filename = util.fixURI(frame.filename || "unknown");
317     if (!set.add(func.seenCaller, filename))
318         util.dactyl(func).warn(
319             util.urlPath(filename) + ":" + frame.lineNumber + ": " +
320             name + " is deprecated: Please use " + alternative + " instead");
321 }
322
323 /**
324  * Iterates over all of the top-level, iterable property names of an
325  * object.
326  *
327  * @param {object} obj The object to inspect.
328  * @returns {Generator}
329  */
330 function keys(obj) iter(function keys() {
331     for (var k in obj)
332         if (hasOwnProperty.call(obj, k))
333             yield k;
334 }());
335 /**
336  * Iterates over all of the top-level, iterable property values of an
337  * object.
338  *
339  * @param {object} obj The object to inspect.
340  * @returns {Generator}
341  */
342 function values(obj) iter(function values() {
343     if (isinstance(obj, ["Generator", "Iterator"]))
344         for (let k in obj)
345             yield k;
346     else
347         for (var k in obj)
348             if (hasOwnProperty.call(obj, k))
349                 yield obj[k];
350 }());
351
352 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
353 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
354
355 /**
356  * Utility for managing sets of strings. Given an array, returns an
357  * object with one key for each value thereof.
358  *
359  * @param {[string]} ary @optional
360  * @returns {object}
361  */
362 function set(ary) {
363     let obj = {};
364     if (ary)
365         for (let val in values(ary))
366             obj[val] = true;
367     return obj;
368 }
369 /**
370  * Adds an element to a set and returns true if the element was
371  * previously contained.
372  *
373  * @param {object} set The set.
374  * @param {string} key The key to add.
375  * @returns boolean
376  */
377 set.add = curry(function set_add(set, key) {
378     let res = this.has(set, key);
379     set[key] = true;
380     return res;
381 });
382 /**
383  * Returns true if the given set contains the given key.
384  *
385  * @param {object} set The set.
386  * @param {string} key The key to check.
387  * @returns {boolean}
388  */
389 set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
390                                            propertyIsEnumerable.call(set, key));
391 /**
392  * Returns a new set containing the members of the first argument which
393  * do not exist in any of the other given arguments.
394  *
395  * @param {object} set The set.
396  * @returns {object}
397  */
398 set.subtract = function set_subtract(set) {
399     set = update({}, set);
400     for (let i = 1; i < arguments.length; i++)
401         for (let k in keys(arguments[i]))
402             delete set[k];
403     return set;
404 };
405 /**
406  * Removes an element from a set and returns true if the element was
407  * previously contained.
408  *
409  * @param {object} set The set.
410  * @param {string} key The key to remove.
411  * @returns boolean
412  */
413 set.remove = curry(function set_remove(set, key) {
414     let res = set.has(set, key);
415     delete set[key];
416     return res;
417 });
418
419 /**
420  * Curries a function to the given number of arguments. Each
421  * call of the resulting function returns a new function. When
422  * a call does not contain enough arguments to satisfy the
423  * required number, the resulting function is another curried
424  * function with previous arguments accumulated.
425  *
426  *     function foo(a, b, c) [a, b, c].join(" ");
427  *     curry(foo)(1, 2, 3) -> "1 2 3";
428  *     curry(foo)(4)(5, 6) -> "4 5 6";
429  *     curry(foo)(7)(8)(9) -> "7 8 9";
430  *
431  * @param {function} fn The function to curry.
432  * @param {integer} length The number of arguments expected.
433  *     @default fn.length
434  *     @optional
435  * @param {object} self The 'this' value for the returned function. When
436  *     omitted, the value of 'this' from the first call to the function is
437  *     preserved.
438  *     @optional
439  */
440 function curry(fn, length, self, acc) {
441     if (length == null)
442         length = fn.length;
443     if (length == 0)
444         return fn;
445
446     // Close over function with 'this'
447     function close(self, fn) function () fn.apply(self, Array.slice(arguments));
448
449     if (acc == null)
450         acc = [];
451
452     return function curried() {
453         let args = acc.concat(Array.slice(arguments));
454
455         // The curried result should preserve 'this'
456         if (arguments.length == 0)
457             return close(self || this, curried);
458
459         if (args.length >= length)
460             return fn.apply(self || this, args);
461
462         return curry(fn, length, self || this, args);
463     };
464 }
465
466 if (curry.bind)
467     var bind = function bind(func) func.bind.apply(func, Array.slice(arguments, bind.length));
468 else
469     var bind = function bind(func, self) {
470         let args = Array.slice(arguments, bind.length);
471         return function bound() func.apply(self, args.concat(Array.slice(arguments)));
472     };
473
474 /**
475  * Returns true if both arguments are functions and
476  * (targ() instanceof src) would also return true.
477  *
478  * @param {function} targ
479  * @param {function} src
480  * @returns {boolean}
481  */
482 function isSubclass(targ, src) {
483     return src === targ ||
484         targ && typeof targ === "function" && targ.prototype instanceof src;
485 }
486
487 /**
488  * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
489  * an array, returns true if *object* is an instance of any element of
490  * *interfaces*. If *interfaces* is the object form of a primitive type,
491  * returns true if *object* is a non-boxed version of the type, i.e., if
492  * (typeof object == "string"), isinstance(object, String) is true. Finally, if
493  * *interfaces* is a string, returns true if ({}.toString.call(object) ==
494  * "[object <interfaces>]").
495  *
496  * @param {object} object The object to check.
497  * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
498  * @returns {boolean}
499  */
500 var isinstance_types = {
501     boolean: Boolean,
502     string: String,
503     function: Function,
504     number: Number
505 };
506 function isinstance(object, interfaces) {
507     if (object == null)
508         return false;
509
510     return Array.concat(interfaces).some(function isinstance_some(iface) {
511         if (typeof iface === "string") {
512             if (objproto.toString.call(object) === "[object " + iface + "]")
513                 return true;
514         }
515         else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
516             if (object.isinstance(iface))
517                 return true;
518         }
519         else {
520             if (object instanceof iface)
521                 return true;
522             var type = isinstance_types[typeof object];
523             if (type && isSubclass(iface, type))
524                 return true;
525         }
526         return false;
527     });
528 }
529
530 /**
531  * Returns true if obj is a non-null object.
532  */
533 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
534
535 /**
536  * Returns true if and only if its sole argument is an
537  * instance of the builtin Array type. The array may come from
538  * any window, frame, namespace, or execution context, which
539  * is not the case when using (obj instanceof Array).
540  */
541 var isArray =
542     Array.isArray
543         // This is bloody stupid.
544         ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
545         : function isArray(val) objproto.toString.call(val) == "[object Array]";
546
547 /**
548  * Returns true if and only if its sole argument is an
549  * instance of the builtin Generator type. This includes
550  * functions containing the 'yield' statement and generator
551  * statements such as (x for (x in obj)).
552  */
553 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
554
555 /**
556  * Returns true if and only if its sole argument is a String,
557  * as defined by the builtin type. May be constructed via
558  * String(foo) or new String(foo) from any window, frame,
559  * namespace, or execution context, which is not the case when
560  * using (obj instanceof String) or (typeof obj == "string").
561  */
562 function isString(val) objproto.toString.call(val) == "[object String]";
563
564 /**
565  * Returns true if and only if its sole argument may be called
566  * as a function. This includes classes and function objects.
567  */
568 function callable(val) typeof val === "function";
569
570 function call(fn) {
571     fn.apply(arguments[1], Array.slice(arguments, 2));
572     return fn;
573 }
574
575 /**
576  * Memoizes an object property value.
577  *
578  * @param {object} obj The object to add the property to.
579  * @param {string} key The property name.
580  * @param {function} getter The function which will return the initial
581  * value of the property.
582  */
583 function memoize(obj, key, getter) {
584     if (arguments.length == 1) {
585         obj = update({}, obj);
586         for (let prop in Object.getOwnPropertyNames(obj)) {
587             let get = __lookupGetter__.call(obj, prop);
588             if (get)
589                 memoize(obj, prop, get);
590         }
591         return obj;
592     }
593
594     Object.defineProperty(obj, key, {
595         configurable: true,
596         enumerable: true,
597
598         get: function g_replaceProperty() (
599             Class.replaceProperty(this.instance || this, key, null),
600             Class.replaceProperty(this.instance || this, key, getter.call(this, key))),
601
602         set: function s_replaceProperty(val)
603             Class.replaceProperty(this.instance || this, key, val)
604     });
605 }
606
607 let sandbox = Cu.Sandbox(this);
608 sandbox.__proto__ = this;
609 /**
610  * Wraps a function so that when called, the global object of the caller
611  * is prepended to its arguments.
612  */
613 // Hack to get around lack of access to caller in strict mode.
614 var withCallerGlobal = Cu.evalInSandbox(<![CDATA[
615     (function withCallerGlobal(fn)
616         function withCallerGlobal_wrapped()
617             fn.apply(this,
618                      [Class.objectGlobal(withCallerGlobal_wrapped.caller)]
619                         .concat(Array.slice(arguments))))
620 ]]>, Cu.Sandbox(this), "1.8");
621
622 /**
623  * Updates an object with the properties of another object. Getters
624  * and setters are copied as expected. Moreover, any function
625  * properties receive new 'supercall' and 'superapply' properties,
626  * which will call the identically named function in target's
627  * prototype.
628  *
629  *    let a = { foo: function (arg) "bar " + arg }
630  *    let b = { __proto__: a }
631  *    update(b, { foo: function foo() foo.supercall(this, "baz") });
632  *
633  *    a.foo("foo") -> "bar foo"
634  *    b.foo()      -> "bar baz"
635  *
636  * @param {Object} target The object to update.
637  * @param {Object} src The source object from which to update target.
638  *    May be provided multiple times.
639  * @returns {Object} Returns its updated first argument.
640  */
641 function update(target) {
642     for (let i = 1; i < arguments.length; i++) {
643         let src = arguments[i];
644         Object.getOwnPropertyNames(src || {}).forEach(function (k) {
645             let desc = Object.getOwnPropertyDescriptor(src, k);
646             if (desc.value instanceof Class.Property)
647                 desc = desc.value.init(k, target) || desc.value;
648
649             if (typeof desc.value === "function" && target.__proto__) {
650                 let func = desc.value.wrapped || desc.value;
651                 func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]);
652                 func.superapply = function superapply(self, args)
653                     let (meth = Object.getPrototypeOf(target)[k])
654                         meth && meth.apply(self, args);
655                 func.supercall = function supercall(self)
656                     func.superapply(self, Array.slice(arguments, 1));
657             }
658             try {
659                 Object.defineProperty(target, k, desc);
660             }
661             catch (e) {}
662         });
663     }
664     return target;
665 }
666
667 /**
668  * @constructor Class
669  *
670  * Constructs a new Class. Arguments marked as optional must be
671  * either entirely elided, or they must have the exact type
672  * specified.
673  *
674  * @param {string} name The class's as it will appear when toString
675  *     is called, as well as in stack traces.
676  *     @optional
677  * @param {function} base The base class for this module. May be any
678  *     callable object.
679  *     @optional
680  *     @default Class
681  * @param {Object} prototype The prototype for instances of this
682  *     object. The object itself is copied and not used as a prototype
683  *     directly.
684  * @param {Object} classProperties The class properties for the new
685  *     module constructor. More than one may be provided.
686  *     @optional
687  *
688  * @returns {function} The constructor for the resulting class.
689  */
690 function Class() {
691
692     var args = Array.slice(arguments);
693     if (isString(args[0]))
694         var name = args.shift();
695     var superclass = Class;
696     if (callable(args[0]))
697         superclass = args.shift();
698
699     var Constructor = eval(String.replace(<![CDATA[
700         (function constructor(PARAMS) {
701             var self = Object.create(Constructor.prototype, {
702                 constructor: { value: Constructor },
703             });
704             self.instance = self;
705             var res = self.init.apply(self, arguments);
706             return res !== undefined ? res : self;
707         })]]>,
708         "constructor", (name || superclass.className).replace(/\W/g, "_"))
709             .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
710                                                        .replace(/\b(self|res|Constructor)\b/g, "$1_")));
711
712     Constructor.className = name || superclass.className || superclass.name;
713
714     if ("init" in superclass.prototype)
715         Constructor.__proto__ = superclass;
716     else {
717         let superc = superclass;
718         superclass = function Shim() {};
719         Class.extend(superclass, superc, {
720             init: superc
721         });
722         superclass.__proto__ = superc;
723     }
724
725     Class.extend(Constructor, superclass, args[0]);
726     update(Constructor, args[1]);
727     Constructor.__proto__ = superclass;
728     args = args.slice(2);
729     Array.forEach(args, function (obj) {
730         if (callable(obj))
731             obj = obj.prototype;
732         update(Constructor.prototype, obj);
733     });
734     return Constructor;
735 }
736
737 if (Cu.getGlobalForObject)
738     Class.objectGlobal = function (caller) {
739         try {
740             return Cu.getGlobalForObject(caller);
741         }
742         catch (e) {
743             return null;
744         }
745     };
746 else
747     Class.objectGlobal = function (caller) {
748         while (caller.__parent__)
749             caller = caller.__parent__;
750         return caller;
751     };
752
753 /**
754  * @class Class.Property
755  * A class which, when assigned to a property in a Class's prototype
756  * or class property object, defines that property's descriptor
757  * rather than its value. If the desc object has an init property, it
758  * will be called with the property's name before the descriptor is
759  * assigned.
760  *
761  * @param {Object} desc The property descriptor.
762  */
763 Class.Property = function Property(desc) update(
764     Object.create(Property.prototype), desc || { configurable: true, writable: true });
765 Class.Property.prototype.init = function () {};
766 /**
767  * Extends a subclass with a superclass. The subclass's
768  * prototype is replaced with a new object, which inherits
769  * from the superclass's prototype, {@see update}d with the
770  * members of *overrides*.
771  *
772  * @param {function} subclass
773  * @param {function} superclass
774  * @param {Object} overrides @optional
775  */
776 Class.extend = function extend(subclass, superclass, overrides) {
777     subclass.superclass = superclass;
778
779     subclass.prototype = Object.create(superclass.prototype);
780     update(subclass.prototype, overrides);
781     subclass.prototype.constructor = subclass;
782     subclass.prototype._class_ = subclass;
783
784     if (superclass.prototype.constructor === objproto.constructor)
785         superclass.prototype.constructor = superclass;
786 }
787
788 /**
789  * Memoizes the value of a class property to the value returned by
790  * the passed function the first time the property is accessed.
791  *
792  * @param {function(string)} getter The function which returns the
793  *      property's value.
794  * @return {Class.Property}
795  */
796 Class.memoize = function memoize(getter, wait)
797     Class.Property({
798         configurable: true,
799         enumerable: true,
800         init: function (key) {
801             let done = false;
802
803             if (wait)
804                 this.get = function replace() {
805                     let obj = this.instance || this;
806                     Object.defineProperty(obj, key,  {
807                         configurable: true, enumerable: false,
808                         get: function get() {
809                             util.waitFor(function () done);
810                             return this[key];
811                         }
812                     });
813
814                     util.yieldable(function () {
815                         let wait;
816                         for (var res in getter.call(obj)) {
817                             if (wait !== undefined)
818                                 yield wait;
819                             wait = res;
820                         }
821                         Class.replaceProperty(obj, key, res);
822                         done = true;
823                     })();
824
825                     return this[key];
826                 };
827             else
828                 this.get = function replace() {
829                     let obj = this.instance || this;
830                     Class.replaceProperty(obj, key, null);
831                     return Class.replaceProperty(obj, key, getter.call(this, key));
832                 };
833
834             this.set = function replace(val) Class.replaceProperty(this.instance || this, val);
835         }
836     });
837
838 Class.replaceProperty = function replaceProperty(obj, prop, value) {
839     Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
840     return value;
841 };
842 Class.toString = function toString() "[class " + this.className + "]";
843 Class.prototype = {
844     /**
845      * Initializes new instances of this class. Called automatically
846      * when new instances are created.
847      */
848     init: function c_init() {},
849
850     withSavedValues: function withSavedValues(names, callback, self) {
851         let vals = names.map(function (name) this[name], this);
852         try {
853             return callback.call(self || this);
854         }
855         finally {
856             names.forEach(function (name, i) this[name] = vals[i], this);
857         }
858     },
859
860     toString: function C_toString() {
861         if (this.toStringParams)
862             var params = "(" + this.toStringParams.map(function (m) isArray(m)  ? "[" + m + "]" :
863                                                                     isString(m) ? m.quote() : String(m))
864                                    .join(", ") + ")";
865         return "[instance " + this.constructor.className + (params || "") + "]";
866     },
867
868     /**
869      * Executes *callback* after *timeout* milliseconds. The value of
870      * 'this' is preserved in the invocation of *callback*.
871      *
872      * @param {function} callback The function to call after *timeout*
873      * @param {number} timeout The time, in milliseconds, to wait
874      *     before calling *callback*.
875      * @returns {nsITimer} The timer which backs this timeout.
876      */
877     timeout: function timeout(callback, timeout) {
878         const self = this;
879         function timeout_notify(timer) {
880             if (self.stale ||
881                     util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
882                 return;
883             util.trapErrors(callback, self);
884         }
885         return services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
886     }
887 };
888 Class.makeClosure = function makeClosure() {
889     const self = this;
890     function closure(fn) {
891         function _closure() {
892             try {
893                 return fn.apply(self, arguments);
894             }
895             catch (e if !(e instanceof FailedAssertion)) {
896                 util.reportError(e);
897                 throw e.stack ? e : Error(e);
898             }
899         }
900         _closure.wrapped = fn;
901         return _closure;
902     }
903
904     iter(properties(this), properties(this, true)).forEach(function (k) {
905         if (!__lookupGetter__.call(this, k) && callable(this[k]))
906             closure[k] = closure(this[k]);
907         else if (!(k in closure))
908             Object.defineProperty(closure, k, {
909                 configurable: true,
910                 enumerable: true,
911                 get: function get_proxy() self[k],
912                 set: function set_proxy(val) self[k] = val,
913             });
914     }, this);
915
916     return closure;
917 };
918 memoize(Class.prototype, "closure", Class.makeClosure);
919
920 /**
921  * A base class generator for classes which implement XPCOM interfaces.
922  *
923  * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
924  *      implements.
925  * @param {Class} superClass A super class. @optional
926  * @returns {Class}
927  */
928 function XPCOM(interfaces, superClass) {
929     interfaces = Array.concat(interfaces);
930
931     let shim = interfaces.reduce(function (shim, iface) shim.QueryInterface(iface),
932                                  Cc["@dactyl.googlecode.com/base/xpc-interface-shim"].createInstance());
933
934     let res = Class("XPCOM(" + interfaces + ")", superClass || Class, update(
935         iter.toObject([k, v === undefined || callable(v) ? function stub() null : v]
936                       for ([k, v] in Iterator(shim))),
937         { QueryInterface: XPCOMUtils.generateQI(interfaces) }));
938     shim = interfaces = null;
939     return res;
940 }
941
942 /**
943  * An abstract base class for classes that wish to inherit from Error.
944  */
945 var ErrorBase = Class("ErrorBase", Error, {
946     level: 2,
947     init: function EB_init(message, level) {
948         level = level || 0;
949         update(this, Error(message))
950         this.message = message;
951
952         let frame = Components.stack;
953         for (let i = 0; i < this.level + level; i++) {
954             frame = frame.caller;
955             this.stack = this.stack.replace(/^.*\n/, "");
956         }
957         this.fileName = frame.filename;
958         this.lineNumber = frame.lineNumber;
959     }
960 });
961
962 /**
963  * Constructs a new Module class and instantiates an instance into the current
964  * module global object.
965  *
966  * @param {string} name The name of the instance.
967  * @param {Object} prototype The instance prototype.
968  * @param {Object} classProperties Properties to be applied to the class constructor.
969  * @returns {Class}
970  */
971 function Module(name, prototype) {
972     let init = callable(prototype) ? 4 : 3;
973     const module = Class.apply(Class, Array.slice(arguments, 0, init));
974     let instance = module();
975     module.className = name.toLowerCase();
976
977     instance.INIT = update(Object.create(Module.INIT),
978                            arguments[init] || {});
979
980     currentModule[module.className] = instance;
981     defineModule.modules.push(instance);
982     return module;
983 }
984 Module.INIT = {
985     init: function Module_INIT_init(dactyl, modules, window) {
986         let args = arguments;
987
988         let locals = [];
989         for (let local = this.Local; local; local = local.super)
990             locals.push(local);
991
992         if (locals.length) {
993             let module = this, objs = {};
994             for (let i in locals) {
995                 module = objs[i] = Object.create(module);
996                 module.modules = modules;
997             }
998             module.isLocalModule = true;
999
1000             modules.jsmodules[this.constructor.className] = module;
1001             locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args)))
1002
1003             memoize(module, "closure", Class.makeClosure);
1004             module.instance = module;
1005             module.init();
1006
1007             if (module.signals)
1008                 modules.dactyl.registerObservers(module);
1009         }
1010     }
1011 }
1012
1013 /**
1014  * @class Struct
1015  *
1016  * Creates a new Struct constructor, used for creating objects with
1017  * a fixed set of named members. Each argument should be the name of
1018  * a member in the resulting objects. These names will correspond to
1019  * the arguments passed to the resultant constructor. Instances of
1020  * the new struct may be treated very much like arrays, and provide
1021  * many of the same methods.
1022  *
1023  *     const Point = Struct("x", "y", "z");
1024  *     let p1 = Point(x, y, z);
1025  *
1026  * @returns {function} The constructor for the new Struct.
1027  */
1028 function Struct() {
1029     if (!/^[A-Z]/.test(arguments[0]))
1030         var args = Array.slice(arguments, 0);
1031     else {
1032         var className = arguments[0];
1033         args = Array.slice(arguments, 1);
1034     }
1035
1036     const Struct = Class(className || "Struct", StructBase, {
1037         length: args.length,
1038         members: array.toObject(args.map(function (v, k) [v, k]))
1039     });
1040     args.forEach(function (name, i) {
1041         Struct.prototype.__defineGetter__(name, function () this[i]);
1042         Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1043     });
1044     return Struct;
1045 }
1046 let StructBase = Class("StructBase", Array, {
1047     init: function struct_init() {
1048         for (let i = 0; i < arguments.length; i++)
1049             if (arguments[i] != undefined)
1050                 this[i] = arguments[i];
1051     },
1052
1053     clone: function struct_clone() this.constructor.apply(null, this.slice()),
1054
1055     closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1056
1057     get: function struct_get(key, val) this[this.members[key]],
1058     set: function struct_set(key, val) this[this.members[key]] = val,
1059
1060     toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1061
1062     // Iterator over our named members
1063     __iterator__: function struct__iterator__() {
1064         let self = this;
1065         return ([k, self[k]] for (k in keys(self.members)))
1066     }
1067 }, {
1068     fromArray: function fromArray(ary) {
1069         if (!(ary instanceof this))
1070             ary.__proto__ = this.prototype;
1071         return ary;
1072     },
1073
1074     /**
1075      * Sets a lazily constructed default value for a member of
1076      * the struct. The value is constructed once, the first time
1077      * it is accessed and memoized thereafter.
1078      *
1079      * @param {string} key The name of the member for which to
1080      *     provide the default value.
1081      * @param {function} val The function which is to generate
1082      *     the default value.
1083      */
1084     defaultValue: function defaultValue(key, val) {
1085         let i = this.prototype.members[key];
1086         this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1087         this.prototype.__defineSetter__(i, function (value)
1088             Class.replaceProperty(this, i, value));
1089         return this;
1090     },
1091
1092     localize: function localize(key, defaultValue) {
1093         let i = this.prototype.members[key];
1094         Object.defineProperty(this.prototype, i, require("messages").Messages.Localized(defaultValue).init(key, this.prototype));
1095         return this;
1096     }
1097 });
1098
1099 var Timer = Class("Timer", {
1100     init: function init(minInterval, maxInterval, callback, self) {
1101         this._timer = services.Timer();
1102         this.callback = callback;
1103         this.self = self || this;
1104         this.minInterval = minInterval;
1105         this.maxInterval = maxInterval;
1106         this.doneAt = 0;
1107         this.latest = 0;
1108     },
1109
1110     notify: function notify(timer, force) {
1111         try {
1112             if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1113                 return;
1114
1115             this._timer.cancel();
1116             this.latest = 0;
1117             // minInterval is the time between the completion of the command and the next firing
1118             this.doneAt = Date.now() + this.minInterval;
1119
1120             this.callback.call(this.self, this.arg);
1121         }
1122         catch (e) {
1123             if (typeof util === "undefined")
1124                 dump("dactyl: " + e + "\n" + (e.stack || Error().stack));
1125             else
1126                 util.reportError(e);
1127         }
1128         finally {
1129             this.doneAt = Date.now() + this.minInterval;
1130         }
1131     },
1132
1133     tell: function tell(arg) {
1134         if (arguments.length > 0)
1135             this.arg = arg;
1136
1137         let now = Date.now();
1138         if (this.doneAt == -1)
1139             this._timer.cancel();
1140
1141         let timeout = this.minInterval;
1142         if (now > this.doneAt && this.doneAt > -1)
1143             timeout = 0;
1144         else if (this.latest)
1145             timeout = Math.min(timeout, this.latest - now);
1146         else
1147             this.latest = now + this.maxInterval;
1148
1149         this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1150         this.doneAt = -1;
1151     },
1152
1153     reset: function reset() {
1154         this._timer.cancel();
1155         this.doneAt = 0;
1156     },
1157
1158     flush: function flush(force) {
1159         if (this.doneAt == -1 || force)
1160             this.notify(null, true);
1161     }
1162 });
1163
1164 /**
1165  * Idempotent function which returns the UTF-8 encoded value of an
1166  * improperly-decoded string.
1167  *
1168  * @param {string} str
1169  * @returns {string}
1170  */
1171 function UTF8(str) {
1172     try {
1173         return decodeURIComponent(escape(str));
1174     }
1175     catch (e) {
1176         return str;
1177     }
1178 }
1179
1180 function octal(decimal) parseInt(decimal, 8);
1181
1182 /**
1183  * Iterates over an arbitrary object. The following iterator types are
1184  * supported, and work as a user would expect:
1185  *
1186  *  â€¢ nsIDOMNodeIterator
1187  *  â€¢ mozIStorageStatement
1188  *
1189  * Additionally, the following array-like objects yield a tuple of the
1190  * form [index, element] for each contained element:
1191  *
1192  *  â€¢ nsIDOMHTMLCollection
1193  *  â€¢ nsIDOMNodeList
1194  *
1195  * and the following likewise yield one element of the form
1196  * [name, element] for each contained element:
1197  *
1198  *  â€¢ nsIDOMNamedNodeMap
1199  *
1200  * Duck typing is implemented for any other type. If the object
1201  * contains the "enumerator" property, iter is called on that. If the
1202  * property is a function, it is called first. If it contains the
1203  * property "getNext" along with either "hasMoreItems" or "hasMore", it
1204  * is iterated over appropriately.
1205  *
1206  * For all other cases, this function behaves exactly like the Iterator
1207  * function.
1208  *
1209  * @param {object} obj
1210  * @returns {Generator}
1211  */
1212 function iter(obj) {
1213     let args = arguments;
1214     let res = Iterator(obj);
1215
1216     if (args.length > 1)
1217         res = (function () {
1218             for (let i = 0; i < args.length; i++)
1219                 for (let j in iter(args[i]))
1220                     yield j;
1221         })();
1222     else if (isinstance(obj, ["Iterator", "Generator"]))
1223         ;
1224     else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1225         while (obj.constructor instanceof ctypes.PointerType)
1226             obj = obj.contents;
1227         if (obj.constructor instanceof ctypes.ArrayType)
1228             res = array.iterItems(obj);
1229         else if (obj.constructor instanceof ctypes.StructType)
1230             res = (function () {
1231                 for (let prop in values(obj.constructor.fields))
1232                     yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1233             })();
1234         else
1235             return iter({});
1236     }
1237     else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1238         res = array.iterItems(obj);
1239     else if (obj instanceof Ci.nsIDOMNamedNodeMap)
1240         res = (function () {
1241             for (let i = 0; i < obj.length; i++)
1242                 yield [obj.name, obj];
1243         })();
1244     else if (obj instanceof Ci.mozIStorageStatement)
1245         res = (function (obj) {
1246             while (obj.executeStep())
1247                 yield obj.row;
1248             obj.reset();
1249         })(obj);
1250     else if ("getNext" in obj) {
1251         if ("hasMoreElements" in obj)
1252             res = (function () {
1253                 while (obj.hasMoreElements())
1254                     yield obj.getNext();
1255             })();
1256         else if ("hasMore" in obj)
1257             res = (function () {
1258                 while (obj.hasMore())
1259                     yield obj.getNext();
1260             })();
1261     }
1262     else if ("enumerator" in obj) {
1263         if (callable(obj.enumerator))
1264             return iter(obj.enumerator());
1265         return iter(obj.enumerator);
1266     }
1267     res.__noSuchMethod__ = function __noSuchMethod__(meth, args) {
1268         if (meth in iter)
1269             var res = iter[meth].apply(iter, [this].concat(args));
1270         else
1271             res = let (ary = array(this))
1272                 ary[meth] ? ary[meth].apply(ary, args) : ary.__noSuchMethod__(meth, args);
1273         if (isinstance(res, ["Iterator", "Generator"]))
1274             return iter(res);
1275         return res;
1276     };
1277     return res;
1278 }
1279 update(iter, {
1280     toArray: function toArray(iter) array(iter).array,
1281
1282     // See array.prototype for API docs.
1283     toObject: function toObject(iter) {
1284         let obj = {};
1285         for (let [k, v] in iter)
1286             if (v instanceof Class.Property)
1287                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1288             else
1289                 obj[k] = v;
1290         return obj;
1291     },
1292
1293     compact: function compact(iter) (item for (item in iter) if (item != null)),
1294
1295     every: function every(iter, pred, self) {
1296         pred = pred || util.identity;
1297         for (let elem in iter)
1298             if (!pred.call(self, elem))
1299                 return false;
1300         return true;
1301     },
1302     some: function every(iter, pred, self) {
1303         pred = pred || util.identity;
1304         for (let elem in iter)
1305             if (pred.call(self, elem))
1306                 return true;
1307         return false;
1308     },
1309
1310     filter: function filter(iter, pred, self) {
1311         for (let elem in iter)
1312             if (pred.call(self, elem))
1313                 yield elem;
1314     },
1315
1316     /**
1317      * Iterates over an iterable object and calls a callback for each
1318      * element.
1319      *
1320      * @param {object} iter The iterator.
1321      * @param {function} fn The callback.
1322      * @param {object} self The this object for *fn*.
1323      */
1324     forEach: function forEach(iter, func, self) {
1325         for (let val in iter)
1326             func.call(self, val);
1327     },
1328
1329     indexOf: function indexOf(iter, elem) {
1330         let i = 0;
1331         for (let item in iter) {
1332             if (item == elem)
1333                 return i;
1334             i++;
1335         }
1336     },
1337
1338     /**
1339      * Returns the array that results from applying *func* to each property of
1340      * *obj*.
1341      *
1342      * @param {Object} obj
1343      * @param {function} func
1344      * @returns {Array}
1345      */
1346     map: function map(iter, func, self) {
1347         for (let i in iter)
1348             yield func.call(self, i);
1349     },
1350
1351     /**
1352      * Returns the nth member of the given array that matches the
1353      * given predicate.
1354      */
1355     nth: function nth(iter, pred, n, self) {
1356         if (typeof pred === "number")
1357             [pred, n] = [function () true, pred]; // Hack.
1358
1359         for (let elem in iter)
1360             if (pred.call(self, elem) && n-- === 0)
1361                 return elem;
1362         return undefined;
1363     },
1364
1365     sort: function sort(iter, fn, self)
1366         array(this.toArray(iter).sort(fn, self)),
1367
1368     uniq: function uniq(iter) {
1369         let seen = {};
1370         for (let item in iter)
1371             if (!set.add(seen, item))
1372                 yield item;
1373     },
1374
1375     /**
1376      * Zips the contents of two arrays. The resulting array is the length of
1377      * ary1, with any shortcomings of ary2 replaced with null strings.
1378      *
1379      * @param {Array} ary1
1380      * @param {Array} ary2
1381      * @returns {Array}
1382      */
1383     zip: function zip(iter1, iter2) {
1384         try {
1385             yield [iter1.next(), iter2.next()];
1386         }
1387         catch (e if e instanceof StopIteration) {}
1388     }
1389 });
1390
1391 /**
1392  * Array utility methods.
1393  */
1394 var array = Class("array", Array, {
1395     init: function (ary) {
1396         if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1397             ary = [k for (k in ary)];
1398         else if (ary.length)
1399             ary = Array.slice(ary);
1400
1401         return {
1402             __proto__: ary,
1403             __iterator__: function () this.iterItems(),
1404             __noSuchMethod__: function (meth, args) {
1405                 var res = array[meth].apply(null, [this.array].concat(args));
1406                 if (isArray(res))
1407                     return array(res);
1408                 if (isinstance(res, ["Iterator", "Generator"]))
1409                     return iter(res);
1410                 return res;
1411             },
1412             array: ary,
1413             toString: function () this.array.toString(),
1414             concat: function () this.__noSuchMethod__("concat", Array.slice(arguments)),
1415             filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)),
1416             map: function () this.__noSuchMethod__("map", Array.slice(arguments))
1417         };
1418     }
1419 }, {
1420     /**
1421      * Converts an array to an object. As in lisp, an assoc is an
1422      * array of key-value pairs, which maps directly to an object,
1423      * as such:
1424      *    [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1425      *
1426      * @param {Array[]} assoc
1427      * @... {string} 0 - Key
1428      * @...          1 - Value
1429      */
1430     toObject: function toObject(assoc) {
1431         let obj = {};
1432         assoc.forEach(function ([k, v]) {
1433             if (v instanceof Class.Property)
1434                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1435             else
1436                 obj[k] = v;
1437         });
1438         return obj;
1439     },
1440
1441     /**
1442      * Compacts an array, removing all elements that are null or undefined:
1443      *    ["foo", null, "bar", undefined] -> ["foo", "bar"]
1444      *
1445      * @param {Array} ary
1446      * @returns {Array}
1447      */
1448     compact: function compact(ary) ary.filter(function (item) item != null),
1449
1450     /**
1451      * Returns true if each element of ary1 is equal to the
1452      * corresponding element in ary2.
1453      *
1454      * @param {Array} ary1
1455      * @param {Array} ary2
1456      * @returns {boolean}
1457      */
1458     equals: function (ary1, ary2)
1459         ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1460
1461     /**
1462      * Flattens an array, such that all elements of the array are
1463      * joined into a single array:
1464      *    [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1465      *
1466      * @param {Array} ary
1467      * @returns {Array}
1468      */
1469     flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1470
1471     /**
1472      * Returns an Iterator for an array's values.
1473      *
1474      * @param {Array} ary
1475      * @returns {Iterator(Object)}
1476      */
1477     iterValues: function iterValues(ary) {
1478         for (let i = 0; i < ary.length; i++)
1479             yield ary[i];
1480     },
1481
1482     /**
1483      * Returns an Iterator for an array's indices and values.
1484      *
1485      * @param {Array} ary
1486      * @returns {Iterator([{number}, {Object}])}
1487      */
1488     iterItems: function iterItems(ary) {
1489         let length = ary.length;
1490         for (let i = 0; i < length; i++)
1491             yield [i, ary[i]];
1492     },
1493
1494     /**
1495      * Returns the nth member of the given array that matches the
1496      * given predicate.
1497      */
1498     nth: function nth(ary, pred, n, self) {
1499         for (let elem in values(ary))
1500             if (pred.call(self, elem) && n-- === 0)
1501                 return elem;
1502         return undefined;
1503     },
1504
1505     /**
1506      * Filters out all duplicates from an array. If *unsorted* is false, the
1507      * array is sorted before duplicates are removed.
1508      *
1509      * @param {Array} ary
1510      * @param {boolean} unsorted
1511      * @returns {Array}
1512      */
1513     uniq: function uniq(ary, unsorted) {
1514         let res = [];
1515         if (unsorted) {
1516             for (let item in values(ary))
1517                 if (res.indexOf(item) == -1)
1518                     res.push(item);
1519         }
1520         else {
1521             for (let [, item] in Iterator(ary.sort())) {
1522                 if (item != last || !res.length)
1523                     res.push(item);
1524                 var last = item;
1525             }
1526         }
1527         return res;
1528     },
1529
1530     /**
1531      * Zips the contents of two arrays. The resulting array is the length of
1532      * ary1, with any shortcomings of ary2 replaced with null strings.
1533      *
1534      * @param {Array} ary1
1535      * @param {Array} ary2
1536      * @returns {Array}
1537      */
1538     zip: function zip(ary1, ary2) {
1539         let res = [];
1540         for (let [i, item] in Iterator(ary1))
1541             res.push([item, i in ary2 ? ary2[i] : ""]);
1542         return res;
1543     }
1544 });
1545
1546 endModule();
1547
1548 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1549
1550 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: