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