]> git.donarmstrong.com Git - dactyl.git/blob - common/modules/base.jsm
Import r6976 from upstream hg supporting Firefox up to 25.*
[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 || (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, 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                                () => 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(([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) (...args) => fn.apply(self, args);
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(() => 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(name => this[name]);
928         try {
929             return callback.call(self || this);
930         }
931         finally {
932             names.forEach((name, i) => { this[name] = vals[i]; });
933         }
934     },
935
936     toString: function C_toString() {
937         if (this.toStringParams)
938             var params = "(" + this.toStringParams.map(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((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 = 0) {
1098         let error = Error(message);
1099         update(this, error);
1100         this.stack = error.stack;
1101         this.message = message;
1102
1103         let frame = Components.stack;
1104         for (let i = 0; i < this.level + level; i++) {
1105             frame = frame.caller;
1106             this.stack = this.stack.replace(/^.*\n/, "");
1107         }
1108         this.fileName = frame.filename;
1109         this.lineNumber = frame.lineNumber;
1110     },
1111     toString: function () String(this.message)
1112 });
1113
1114 /**
1115  * An Error subclass to throw in order to stop sourcing a plugin without
1116  * printing a stack trace.
1117  */
1118 var Finished = Class("Finished", ErrorBase);
1119
1120 /**
1121  * Constructs a new Module class and instantiates an instance into the current
1122  * module global object.
1123  *
1124  * @param {string} name The name of the instance.
1125  * @param {Object} prototype The instance prototype.
1126  * @param {Object} classProperties Properties to be applied to the class constructor.
1127  * @returns {Class}
1128  */
1129 function Module(name, prototype, ...args) {
1130     try {
1131         let init = callable(prototype) ? 2 : 1;
1132         let proto = callable(prototype) ? args[0] : prototype;
1133
1134         proto._metaInit_ = function () {
1135             delete module.prototype._metaInit_;
1136             currentModule[name.toLowerCase()] = this;
1137         };
1138
1139         const module = Class.apply(Class, [name, prototype, ...args.slice(0, init)]);
1140         let instance = module();
1141         module.className = name.toLowerCase();
1142
1143         instance.INIT = update(Object.create(Module.INIT),
1144                                args[init] || {});
1145
1146         currentModule[module.className] = instance;
1147         defineModule.modules.push(instance);
1148         return module;
1149     }
1150     catch (e) {
1151         if (typeof e === "string")
1152             e = Error(e);
1153
1154         dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack));
1155     }
1156 }
1157 Module.INIT = {
1158     init: function Module_INIT_init(dactyl, modules, window) {
1159         let args = arguments;
1160
1161         let locals = [];
1162         for (let local = this.Local; local; local = local.super)
1163             locals.push(local);
1164
1165         if (locals.length) {
1166             let module = this, objs = {};
1167             for (let i in locals) {
1168                 module = objs[i] = Object.create(module);
1169                 module.modules = modules;
1170             }
1171             module.isLocalModule = true;
1172
1173             modules.jsmodules[this.constructor.className] = module;
1174             locals.reverse().forEach((fn, i) => { update(objs[i], fn.apply(module, args)); });
1175
1176             memoize(module, "closure", Class.makeClosure);
1177             module.instance = module;
1178             module.init();
1179
1180             if (module.signals)
1181                 modules.dactyl.registerObservers(module);
1182         }
1183     }
1184 }
1185
1186 /**
1187  * @class Struct
1188  *
1189  * Creates a new Struct constructor, used for creating objects with
1190  * a fixed set of named members. Each argument should be the name of
1191  * a member in the resulting objects. These names will correspond to
1192  * the arguments passed to the resultant constructor. Instances of
1193  * the new struct may be treated very much like arrays, and provide
1194  * many of the same methods.
1195  *
1196  *     const Point = Struct("x", "y", "z");
1197  *     let p1 = Point(x, y, z);
1198  *
1199  * @returns {function} The constructor for the new Struct.
1200  */
1201 function Struct(...args) {
1202     if (/^[A-Z]/.test(args[0]))
1203         var className = args.shift();
1204
1205     const Struct = Class(className || "Struct", StructBase, {
1206         length: args.length,
1207         members: array.toObject(args.map((v, k) => [v, k]))
1208     });
1209     args.forEach(function (name, i) {
1210         Struct.prototype.__defineGetter__(name, function () this[i]);
1211         Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1212     });
1213     return Struct;
1214 }
1215 var StructBase = Class("StructBase", Array, {
1216     init: function struct_init() {
1217         for (let i = 0; i < arguments.length; i++)
1218             if (arguments[i] != undefined)
1219                 this[i] = arguments[i];
1220     },
1221
1222     get toStringParams() this,
1223
1224     clone: function struct_clone() this.constructor.apply(null, this.slice()),
1225
1226     closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1227
1228     get: function struct_get(key, val) this[this.members[key]],
1229     set: function struct_set(key, val) this[this.members[key]] = val,
1230
1231     toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1232
1233     // Iterator over our named members
1234     __iterator__: function struct__iterator__() {
1235         let self = this;
1236         return ([k, self[k]] for (k in keys(self.members)))
1237     }
1238 }, {
1239     fromArray: function fromArray(ary) {
1240         if (!(ary instanceof this))
1241             ary.__proto__ = this.prototype;
1242         return ary;
1243     },
1244
1245     /**
1246      * Sets a lazily constructed default value for a member of
1247      * the struct. The value is constructed once, the first time
1248      * it is accessed and memoized thereafter.
1249      *
1250      * @param {string} key The name of the member for which to
1251      *     provide the default value.
1252      * @param {function} val The function which is to generate
1253      *     the default value.
1254      */
1255     defaultValue: function defaultValue(key, val) {
1256         let i = this.prototype.members[key];
1257         this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1258         this.prototype.__defineSetter__(i, function (value)
1259             Class.replaceProperty(this, i, value));
1260         return this;
1261     },
1262
1263     localize: function localize(key, defaultValue) {
1264         let i = this.prototype.members[key];
1265         Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1266         return this;
1267     }
1268 });
1269
1270 var Timer = Class("Timer", {
1271     init: function init(minInterval, maxInterval, callback, self = this) {
1272         this._timer = services.Timer();
1273         this.callback = callback;
1274         this.self = self;
1275         this.minInterval = minInterval;
1276         this.maxInterval = maxInterval;
1277         this.doneAt = 0;
1278         this.latest = 0;
1279     },
1280
1281     notify: function notify(timer, force) {
1282         try {
1283             if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1284                 return;
1285
1286             this._timer.cancel();
1287             this.latest = 0;
1288             // minInterval is the time between the completion of the command and the next firing
1289             this.doneAt = Date.now() + this.minInterval;
1290
1291             this.callback.call(this.self, this.arg);
1292         }
1293         catch (e) {
1294             if (typeof util === "undefined")
1295                 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1296             else
1297                 util.reportError(e);
1298         }
1299         finally {
1300             this.doneAt = Date.now() + this.minInterval;
1301         }
1302     },
1303
1304     tell: function tell(arg) {
1305         if (arguments.length > 0)
1306             this.arg = arg;
1307
1308         let now = Date.now();
1309         if (this.doneAt == -1)
1310             this._timer.cancel();
1311
1312         let timeout = this.minInterval;
1313         if (now > this.doneAt && this.doneAt > -1)
1314             timeout = 0;
1315         else if (this.latest)
1316             timeout = Math.min(timeout, this.latest - now);
1317         else
1318             this.latest = now + this.maxInterval;
1319
1320         this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1321         this.doneAt = -1;
1322     },
1323
1324     reset: function reset() {
1325         this._timer.cancel();
1326         this.doneAt = 0;
1327     },
1328
1329     flush: function flush(force) {
1330         if (this.doneAt == -1 || force)
1331             this.notify(null, true);
1332     }
1333 });
1334
1335 /**
1336  * Idempotent function which returns the UTF-8 encoded value of an
1337  * improperly-decoded string.
1338  *
1339  * @param {string} str
1340  * @returns {string}
1341  */
1342 function UTF8(str) {
1343     try {
1344         return decodeURIComponent(escape(str));
1345     }
1346     catch (e) {
1347         return str;
1348     }
1349 }
1350
1351 function octal(decimal) parseInt(decimal, 8);
1352
1353 /**
1354  * Iterates over an arbitrary object. The following iterator types are
1355  * supported, and work as a user would expect:
1356  *
1357  *  â€¢ nsIDOMNodeIterator
1358  *  â€¢ mozIStorageStatement
1359  *
1360  * Additionally, the following array-like objects yield a tuple of the
1361  * form [index, element] for each contained element:
1362  *
1363  *  â€¢ nsIDOMHTMLCollection
1364  *  â€¢ nsIDOMNodeList
1365  *
1366  * and the following likewise yield one element of the form
1367  * [name, element] for each contained element:
1368  *
1369  *  â€¢ nsIDOMNamedNodeMap
1370  *
1371  * Duck typing is implemented for any other type. If the object
1372  * contains the "enumerator" property, iter is called on that. If the
1373  * property is a function, it is called first. If it contains the
1374  * property "getNext" along with either "hasMoreItems" or "hasMore", it
1375  * is iterated over appropriately.
1376  *
1377  * For all other cases, this function behaves exactly like the Iterator
1378  * function.
1379  *
1380  * @param {object} obj
1381  * @param {nsIJSIID} iface The interface to which to query all elements.
1382  * @returns {Generator}
1383  */
1384 function iter(obj, iface) {
1385     if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1386         return iter(obj).map(item => item.QueryInterface(iface));
1387
1388     let args = arguments;
1389     let res = Iterator(obj);
1390
1391     if (args.length > 1)
1392         res = (function () {
1393             for (let i = 0; i < args.length; i++)
1394                 for (let j in iter(args[i]))
1395                     yield j;
1396         })();
1397     else if (isinstance(obj, ["Iterator", "Generator"]))
1398         ;
1399     else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1400         while (obj.constructor instanceof ctypes.PointerType)
1401             obj = obj.contents;
1402         if (obj.constructor instanceof ctypes.ArrayType)
1403             res = array.iterItems(obj);
1404         else if (obj.constructor instanceof ctypes.StructType)
1405             res = (function () {
1406                 for (let prop in values(obj.constructor.fields))
1407                     yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1408             })();
1409         else
1410             return iter({});
1411     }
1412     else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1413         res = array.iterItems(obj);
1414     else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
1415              Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
1416         res = (function () {
1417             for (let i = 0; i < obj.length; i++)
1418                 yield [obj.name, obj];
1419         })();
1420     else if (obj instanceof Ci.mozIStorageStatement)
1421         res = (function (obj) {
1422             while (obj.executeStep())
1423                 yield obj.row;
1424             obj.reset();
1425         })(obj);
1426     else if ("getNext" in obj) {
1427         if ("hasMoreElements" in obj)
1428             res = (function () {
1429                 while (obj.hasMoreElements())
1430                     yield obj.getNext();
1431             })();
1432         else if ("hasMore" in obj)
1433             res = (function () {
1434                 while (obj.hasMore())
1435                     yield obj.getNext();
1436             })();
1437     }
1438     else if ("enumerator" in obj) {
1439         if (callable(obj.enumerator))
1440             return iter(obj.enumerator());
1441         return iter(obj.enumerator);
1442     }
1443     return Iter(res);
1444 }
1445 update(iter, {
1446     toArray: function toArray(iter) array(iter).array,
1447
1448     // See array.prototype for API docs.
1449     toObject: function toObject(iter) {
1450         let obj = {};
1451         for (let [k, v] in iter)
1452             if (v instanceof Class.Property)
1453                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1454             else
1455                 obj[k] = v;
1456         return obj;
1457     },
1458
1459     compact: function compact(iter) (item for (item in iter) if (item != null)),
1460
1461     every: function every(iter, pred, self) {
1462         pred = pred || util.identity;
1463         for (let elem in iter)
1464             if (!pred.call(self, elem))
1465                 return false;
1466         return true;
1467     },
1468     some: function every(iter, pred, self) {
1469         pred = pred || util.identity;
1470         for (let elem in iter)
1471             if (pred.call(self, elem))
1472                 return true;
1473         return false;
1474     },
1475
1476     filter: function filter(iter, pred, self) {
1477         for (let elem in iter)
1478             if (pred.call(self, elem))
1479                 yield elem;
1480     },
1481
1482     /**
1483      * Iterates over an iterable object and calls a callback for each
1484      * element.
1485      *
1486      * @param {object} iter The iterator.
1487      * @param {function} fn The callback.
1488      * @param {object} self The this object for *fn*.
1489      */
1490     forEach: function forEach(iter, func, self) {
1491         for (let val in iter)
1492             func.call(self, val);
1493     },
1494
1495     indexOf: function indexOf(iter, elem) {
1496         let i = 0;
1497         for (let item in iter) {
1498             if (item == elem)
1499                 return i;
1500             i++;
1501         }
1502     },
1503
1504     /**
1505      * Returns the array that results from applying *func* to each property of
1506      * *obj*.
1507      *
1508      * @param {Object} obj
1509      * @param {function} func
1510      * @returns {Array}
1511      */
1512     map: function map(iter, func, self) {
1513         for (let i in iter)
1514             yield func.call(self, i);
1515     },
1516
1517     /**
1518      * Returns the nth member of the given array that matches the
1519      * given predicate.
1520      */
1521     nth: function nth(iter, pred, n, self) {
1522         if (typeof pred === "number")
1523             [pred, n] = [() => true, pred]; // Hack.
1524
1525         for (let elem in iter)
1526             if (pred.call(self, elem) && n-- === 0)
1527                 return elem;
1528         return undefined;
1529     },
1530
1531     sort: function sort(iter, fn, self)
1532         array(this.toArray(iter).sort(fn, self)),
1533
1534     uniq: function uniq(iter) {
1535         let seen = {};
1536         for (let item in iter)
1537             if (!Set.add(seen, item))
1538                 yield item;
1539     },
1540
1541     /**
1542      * Zips the contents of two arrays. The resulting array is the length of
1543      * ary1, with any shortcomings of ary2 replaced with null strings.
1544      *
1545      * @param {Array} ary1
1546      * @param {Array} ary2
1547      * @returns {Array}
1548      */
1549     zip: function zip(iter1, iter2) {
1550         try {
1551             yield [iter1.next(), iter2.next()];
1552         }
1553         catch (e if e instanceof StopIteration) {}
1554     }
1555 });
1556
1557 const Iter = Class("Iter", {
1558     init: function init(iter) {
1559         this.iter = iter;
1560         if ("__iterator__" in iter)
1561             this.iter = iter.__iterator__();
1562
1563         if (this.iter.finalize)
1564             this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1565     },
1566
1567     next: function next() this.iter.next(),
1568
1569     send: function send() this.iter.send.apply(this.iter, arguments),
1570
1571     __iterator__: function () this.iter
1572 });
1573
1574 /**
1575  * Array utility methods.
1576  */
1577 var array = Class("array", Array, {
1578     init: function (ary) {
1579         if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1580             ary = [k for (k in ary)];
1581         else if (ary.length)
1582             ary = Array.slice(ary);
1583
1584         return {
1585             __proto__: ary,
1586             __iterator__: function () this.iterItems(),
1587             __noSuchMethod__: function (meth, args) {
1588                 var res = array[meth].apply(null, [this.array].concat(args));
1589                 if (isArray(res))
1590                     return array(res);
1591                 if (isinstance(res, ["Iterator", "Generator"]))
1592                     return iter(res);
1593                 return res;
1594             },
1595             array: ary,
1596             toString: function () this.array.toString(),
1597             concat: function (...args) this.__noSuchMethod__("concat", args),
1598             filter: function (...args) this.__noSuchMethod__("filter", args),
1599             map: function (...args) this.__noSuchMethod__("map", args)
1600         };
1601     }
1602 }, {
1603     /**
1604      * Converts an array to an object. As in lisp, an assoc is an
1605      * array of key-value pairs, which maps directly to an object,
1606      * as such:
1607      *    [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1608      *
1609      * @param {[Array]} assoc
1610      * @... {string} 0 - Key
1611      * @...          1 - Value
1612      */
1613     toObject: function toObject(assoc) {
1614         let obj = {};
1615         assoc.forEach(function ([k, v]) {
1616             if (v instanceof Class.Property)
1617                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1618             else
1619                 obj[k] = v;
1620         });
1621         return obj;
1622     },
1623
1624     /**
1625      * Compacts an array, removing all elements that are null or undefined:
1626      *    ["foo", null, "bar", undefined] -> ["foo", "bar"]
1627      *
1628      * @param {Array} ary
1629      * @returns {Array}
1630      */
1631     compact: function compact(ary) ary.filter(item => item != null),
1632
1633     /**
1634      * Returns true if each element of ary1 is equal to the
1635      * corresponding element in ary2.
1636      *
1637      * @param {Array} ary1
1638      * @param {Array} ary2
1639      * @returns {boolean}
1640      */
1641     equals: function (ary1, ary2)
1642         ary1.length === ary2.length && Array.every(ary1, (e, i) => e === ary2[i]),
1643
1644     /**
1645      * Flattens an array, such that all elements of the array are
1646      * joined into a single array:
1647      *    [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1648      *
1649      * @param {Array} ary
1650      * @returns {Array}
1651      */
1652     flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1653
1654     /**
1655      * Returns an Iterator for an array's values.
1656      *
1657      * @param {Array} ary
1658      * @returns {Iterator(Object)}
1659      */
1660     iterValues: function iterValues(ary) {
1661         for (let i = 0; i < ary.length; i++)
1662             yield ary[i];
1663     },
1664
1665     /**
1666      * Returns an Iterator for an array's indices and values.
1667      *
1668      * @param {Array} ary
1669      * @returns {Iterator([{number}, {Object}])}
1670      */
1671     iterItems: function iterItems(ary) {
1672         let length = ary.length;
1673         for (let i = 0; i < length; i++)
1674             yield [i, ary[i]];
1675     },
1676
1677     /**
1678      * Returns the nth member of the given array that matches the
1679      * given predicate.
1680      */
1681     nth: function nth(ary, pred, n, self) {
1682         for (let elem in values(ary))
1683             if (pred.call(self, elem) && n-- === 0)
1684                 return elem;
1685         return undefined;
1686     },
1687
1688     /**
1689      * Filters out all duplicates from an array. If *unsorted* is false, the
1690      * array is sorted before duplicates are removed.
1691      *
1692      * @param {Array} ary
1693      * @param {boolean} unsorted
1694      * @returns {Array}
1695      */
1696     uniq: function uniq(ary, unsorted) {
1697         let res = [];
1698         if (unsorted) {
1699             for (let item in values(ary))
1700                 if (res.indexOf(item) == -1)
1701                     res.push(item);
1702         }
1703         else {
1704             for (let [, item] in Iterator(ary.sort())) {
1705                 if (item != last || !res.length)
1706                     res.push(item);
1707                 var last = item;
1708             }
1709         }
1710         return res;
1711     },
1712
1713     /**
1714      * Zips the contents of two arrays. The resulting array is the length of
1715      * ary1, with any shortcomings of ary2 replaced with null strings.
1716      *
1717      * @param {Array} ary1
1718      * @param {Array} ary2
1719      * @returns {Array}
1720      */
1721     zip: function zip(ary1, ary2) {
1722         let res = [];
1723         for (let [i, item] in Iterator(ary1))
1724             res.push([item, i in ary2 ? ary2[i] : ""]);
1725         return res;
1726     }
1727 });
1728
1729 /* Make Minefield not explode, because Minefield exploding is not fun. */
1730 let iterProto = Iter.prototype;
1731 Object.keys(iter).forEach(function (k) {
1732     iterProto[k] = function (...args) {
1733         let res = iter[k].apply(iter, [this].concat(args));
1734         if (isinstance(res, ["Iterator", "Generator"]))
1735             return Iter(res);
1736         return res;
1737     };
1738 });
1739
1740 Object.keys(array).forEach(function (k) {
1741     if (!(k in iterProto))
1742         iterProto[k] = function (...args) {
1743             let res = array[k].apply(array, [this.toArray()].concat(args));
1744             if (isinstance(res, ["Iterator", "Generator"]))
1745                 return Iter(res);
1746             if (isArray(res))
1747                 return array(res);
1748             return res;
1749         };
1750 });
1751
1752 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1753     if (!(k in iterProto) && callable(Array.prototype[k]))
1754         iterProto[k] = function () {
1755             let ary = iter(this).toArray();
1756             let res = ary[k].apply(ary, arguments);
1757             if (isArray(res))
1758                 return array(res);
1759             return res;
1760         };
1761 });
1762
1763 endModule();
1764
1765 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1766
1767 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: