]> git.donarmstrong.com Git - dactyl.git/blob - common/modules/base.jsm
Import r6923 from upstream hg supporting Firefox up to 22.0a1
[dactyl.git] / common / modules / base.jsm
1 // Copyright (c) 2009-2012 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_() {
72     let msg = Array.map(arguments, 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) {
82     let time = Date.now();
83     if (typeof func !== "function")
84         func = self[func];
85
86     try {
87         var res = func.apply(self, Array.slice(arguments, 4));
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, Array.slice(arguments));
445
446     if (acc == null)
447         acc = [];
448
449     return function curried() {
450         let args = acc.concat(Array.slice(arguments));
451
452         // The curried result should preserve 'this'
453         if (arguments.length == 0)
454             return close(self || this, curried);
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) let (func = callable(meth) ? meth : self[meth])
465         func.bind.apply(func, Array.slice(arguments, 1));
466 else
467     var bind = function bind(func, self) {
468         if (!callable(func))
469             func = self[func];
470
471         let args = Array.slice(arguments, bind.length);
472         return function bound() func.apply(self, args.concat(Array.slice(arguments)));
473     };
474
475 /**
476  * Returns true if both arguments are functions and
477  * (targ() instanceof src) would also return true.
478  *
479  * @param {function} targ
480  * @param {function} src
481  * @returns {boolean}
482  */
483 function isSubclass(targ, src) {
484     return src === targ ||
485         targ && typeof targ === "function" && targ.prototype instanceof src;
486 }
487
488 /**
489  * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
490  * an array, returns true if *object* is an instance of any element of
491  * *interfaces*. If *interfaces* is the object form of a primitive type,
492  * returns true if *object* is a non-boxed version of the type, i.e., if
493  * (typeof object == "string"), isinstance(object, String) is true. Finally, if
494  * *interfaces* is a string, returns true if ({}.toString.call(object) ==
495  * "[object <interfaces>]").
496  *
497  * @param {object} object The object to check.
498  * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
499  * @returns {boolean}
500  */
501 var isinstance_types = {
502     boolean: Boolean,
503     string: String,
504     function: Function,
505     number: Number
506 };
507 function isinstance(object, interfaces) {
508     if (object == null)
509         return false;
510
511     return Array.concat(interfaces).some(function isinstance_some(iface) {
512         if (typeof iface === "string") {
513             if (objproto.toString.call(object) === "[object " + iface + "]")
514                 return true;
515         }
516         else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
517             if (object.isinstance(iface))
518                 return true;
519         }
520         else {
521             if (object instanceof iface)
522                 return true;
523             var type = isinstance_types[typeof object];
524             if (type && isSubclass(iface, type))
525                 return true;
526         }
527         return false;
528     });
529 }
530
531 /**
532  * Returns true if obj is a non-null object.
533  */
534 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
535
536 /**
537  * Returns true if obje is an E4X XML object.
538  * @deprecated
539  */
540 function isXML(obj) typeof obj === "xml";
541
542 /**
543  * Returns true if and only if its sole argument is an
544  * instance of the builtin Array type. The array may come from
545  * any window, frame, namespace, or execution context, which
546  * is not the case when using (obj instanceof Array).
547  */
548 var isArray =
549     Array.isArray
550         // This is bloody stupid.
551         ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
552         : function isArray(val) objproto.toString.call(val) == "[object Array]";
553
554 /**
555  * Returns true if and only if its sole argument is an
556  * instance of the builtin Generator type. This includes
557  * functions containing the 'yield' statement and generator
558  * statements such as (x for (x in obj)).
559  */
560 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
561
562 /**
563  * Returns true if and only if its sole argument is a String,
564  * as defined by the builtin type. May be constructed via
565  * String(foo) or new String(foo) from any window, frame,
566  * namespace, or execution context, which is not the case when
567  * using (obj instanceof String) or (typeof obj == "string").
568  */
569 function isString(val) objproto.toString.call(val) == "[object String]";
570
571 /**
572  * Returns true if and only if its sole argument may be called
573  * as a function. This includes classes and function objects.
574  */
575 function callable(val) typeof val === "function" && !(val instanceof Ci.nsIDOMElement);
576
577 function call(fn) {
578     fn.apply(arguments[1], Array.slice(arguments, 2));
579     return fn;
580 }
581
582 /**
583  * Memoizes an object property value.
584  *
585  * @param {object} obj The object to add the property to.
586  * @param {string} key The property name.
587  * @param {function} getter The function which will return the initial
588  * value of the property.
589  */
590 function memoize(obj, key, getter) {
591     if (arguments.length == 1) {
592         let res = update(Object.create(obj), obj);
593         for each (let prop in Object.getOwnPropertyNames(obj)) {
594             let get = __lookupGetter__.call(obj, prop);
595             if (get)
596                 memoize(res, prop, get);
597         }
598         return res;
599     }
600
601     try {
602         Object.defineProperty(obj, key, {
603             configurable: true,
604             enumerable: true,
605
606             get: function g_replaceProperty() {
607                 try {
608                     Class.replaceProperty(this.instance || this, key, null);
609                     return Class.replaceProperty(this.instance || this, key, getter.call(this, key));
610                 }
611                 catch (e) {
612                     util.reportError(e);
613                 }
614             },
615
616             set: function s_replaceProperty(val)
617                 Class.replaceProperty(this.instance || this, key, val)
618         });
619     }
620     catch (e) {
621         obj[key] = getter.call(obj, key);
622     }
623 }
624
625 let sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance());
626 sandbox.__proto__ = this;
627
628 /**
629  * Updates an object with the properties of another object. Getters
630  * and setters are copied as expected. Moreover, any function
631  * properties receive new 'supercall' and 'superapply' properties,
632  * which will call the identically named function in target's
633  * prototype.
634  *
635  *    let a = { foo: function (arg) "bar " + arg }
636  *    let b = { __proto__: a }
637  *    update(b, { foo: function foo() foo.supercall(this, "baz") });
638  *
639  *    a.foo("foo") -> "bar foo"
640  *    b.foo()      -> "bar baz"
641  *
642  * @param {Object} target The object to update.
643  * @param {Object} src The source object from which to update target.
644  *    May be provided multiple times.
645  * @returns {Object} Returns its updated first argument.
646  */
647 function update(target) {
648     for (let i = 1; i < arguments.length; i++) {
649         let src = arguments[i];
650         Object.getOwnPropertyNames(src || {}).forEach(function (k) {
651             let desc = Object.getOwnPropertyDescriptor(src, k);
652             if (desc.value instanceof Class.Property)
653                 desc = desc.value.init(k, target) || desc.value;
654
655             try {
656                 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
657                     let func = desc.value.wrapped || desc.value;
658                     if (!func.superapply) {
659                         func.__defineGetter__("super", function get_super() Object.getPrototypeOf(target)[k]);
660                         func.superapply = function superapply(self, args)
661                             let (meth = Object.getPrototypeOf(target)[k])
662                                 meth && meth.apply(self, args);
663                         func.supercall = function supercall(self)
664                             func.superapply(self, Array.slice(arguments, 1));
665                     }
666                 }
667                 Object.defineProperty(target, k, desc);
668             }
669             catch (e) {}
670         });
671     }
672     return target;
673 }
674 function update_(target) {
675     for (let i = 1; i < arguments.length; i++) {
676         let src = arguments[i];
677         Object.getOwnPropertyNames(src || {}).forEach(function (k) {
678             let desc = Object.getOwnPropertyDescriptor(src, k);
679             if (desc.value instanceof Class.Property)
680                 desc = desc.value.init(k, target) || desc.value;
681
682             try {
683                 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
684                     let func = desc.value.wrapped || desc.value;
685                     if (!func.superapply) {
686                         func.__defineGetter__("super", function get_super_() Object.getPrototypeOf(target)[k]);
687                         func.superapply = function super_apply(self, args)
688                             let (meth = Object.getPrototypeOf(target)[k])
689                                 meth && meth.apply(self, args);
690                         func.supercall = function super_call(self)
691                             func.superapply(self, Array.slice(arguments, 1));
692                     }
693                 }
694                 Object.defineProperty(target, k, desc);
695             }
696             catch (e) {}
697         });
698     }
699     return target;
700 }
701
702 /**
703  * @constructor Class
704  *
705  * Constructs a new Class. Arguments marked as optional must be
706  * either entirely elided, or they must have the exact type
707  * specified.
708  *
709  * @param {string} name The class's as it will appear when toString
710  *     is called, as well as in stack traces.
711  *     @optional
712  * @param {function} base The base class for this module. May be any
713  *     callable object.
714  *     @optional
715  *     @default Class
716  * @param {Object} prototype The prototype for instances of this
717  *     object. The object itself is copied and not used as a prototype
718  *     directly.
719  * @param {Object} classProperties The class properties for the new
720  *     module constructor. More than one may be provided.
721  *     @optional
722  *
723  * @returns {function} The constructor for the resulting class.
724  */
725 function Class() {
726
727     var args = Array.slice(arguments);
728     if (isString(args[0]))
729         var name = args.shift();
730     var superclass = Class;
731     if (callable(args[0]))
732         superclass = args.shift();
733
734     if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418.
735         var Constructor = function Constructor() {
736             var self = Object.create(Constructor.prototype);
737             self.instance = self;
738             self.globalInstance = self;
739
740             if ("_metaInit_" in self && self._metaInit_)
741                 self._metaInit_.apply(self, arguments);
742
743             var res = self.init.apply(self, arguments);
744             return res !== undefined ? res : self;
745         };
746     else
747         var Constructor = eval(String.replace('\n\
748             (function constructor(PARAMS) {                      \n\
749                 var self = Object.create(Constructor.prototype); \n\
750                 self.instance = self;                            \n\
751                 self.globalInstance = self;                      \n\
752                                                                  \n\
753                 if ("_metaInit_" in self && self._metaInit_)     \n\
754                     self._metaInit_.apply(self, arguments);      \n\
755                                                                  \n\
756                 var res = self.init.apply(self, arguments);      \n\
757                 return res !== undefined ? res : self;           \n\
758             })',
759             "constructor", (name || superclass.className).replace(/\W/g, "_"))
760                 .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
761                                                            .replace(/\b(self|res|Constructor)\b/g, "$1_")));
762
763     Constructor.className = name || superclass.className || superclass.name;
764
765     if ("init" in superclass.prototype)
766         Constructor.__proto__ = superclass;
767     else {
768         let superc = superclass;
769         superclass = function Shim() {};
770         Class.extend(superclass, superc, {
771             init: superc
772         });
773         superclass.__proto__ = superc;
774     }
775
776     Class.extend(Constructor, superclass, args[0]);
777     memoize(Constructor, "closure", Class.makeClosure);
778     update(Constructor, args[1]);
779
780     Constructor.__proto__ = superclass;
781
782     args.slice(2).forEach(function (obj) {
783         if (callable(obj))
784             obj = obj.prototype;
785         update(Constructor.prototype, obj);
786     });
787     return Constructor;
788 }
789
790 if (Cu.getGlobalForObject)
791     Class.objectGlobal = function (object) {
792         try {
793             return Cu.getGlobalForObject(object);
794         }
795         catch (e) {
796             return null;
797         }
798     };
799 else
800     Class.objectGlobal = function (object) {
801         while (object.__parent__)
802             object = object.__parent__;
803         return object;
804     };
805
806 /**
807  * @class Class.Property
808  * A class which, when assigned to a property in a Class's prototype
809  * or class property object, defines that property's descriptor
810  * rather than its value. If the desc object has an init property, it
811  * will be called with the property's name before the descriptor is
812  * assigned.
813  *
814  * @param {Object} desc The property descriptor.
815  */
816 Class.Property = function Property(desc) update(
817     Object.create(Property.prototype), desc || { configurable: true, writable: true });
818 Class.Property.prototype.init = function () {};
819 /**
820  * Extends a subclass with a superclass. The subclass's
821  * prototype is replaced with a new object, which inherits
822  * from the superclass's prototype, {@see update}d with the
823  * members of *overrides*.
824  *
825  * @param {function} subclass
826  * @param {function} superclass
827  * @param {Object} overrides @optional
828  */
829 Class.extend = function extend(subclass, superclass, overrides) {
830     subclass.superclass = superclass;
831
832     subclass.prototype = Object.create(superclass.prototype);
833     update(subclass.prototype, overrides);
834     subclass.prototype.constructor = subclass;
835     subclass.prototype._class_ = subclass;
836
837     if (superclass.prototype.constructor === objproto.constructor)
838         superclass.prototype.constructor = superclass;
839 }
840
841 /**
842  * Memoizes the value of a class property to the value returned by
843  * the passed function the first time the property is accessed.
844  *
845  * @param {function(string)} getter The function which returns the
846  *      property's value.
847  * @returns {Class.Property}
848  */
849 Class.Memoize = function Memoize(getter, wait)
850     Class.Property({
851         configurable: true,
852         enumerable: true,
853         init: function (key) {
854             let done = false;
855
856             if (wait)
857                 // Crazy, yeah, I know. -- Kris
858                 this.get = function replace() {
859                     let obj = this.instance || this;
860                     Object.defineProperty(obj, key,  {
861                         configurable: true, enumerable: false,
862                         get: function get() {
863                             util.waitFor(function () done);
864                             return this[key];
865                         }
866                     });
867
868                     util.yieldable(function () {
869                         let wait;
870                         for (var res in getter.call(obj)) {
871                             if (wait !== undefined)
872                                 yield wait;
873                             wait = res;
874                         }
875                         Class.replaceProperty(obj, key, res);
876                         done = true;
877                     })();
878
879                     return this[key];
880                 };
881             else
882                 this.get = function g_Memoize() {
883                     let obj = this.instance || this;
884                     try {
885                         Class.replaceProperty(obj, key, null);
886                         return Class.replaceProperty(obj, key, getter.call(this, key));
887                     }
888                     catch (e) {
889                         util.reportError(e);
890                     }
891                 };
892
893             this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val);
894         }
895     });
896
897 Class.memoize = deprecated("Class.Memoize", function memoize() Class.Memoize.apply(this, arguments));
898
899 /**
900  * Updates the given object with the object in the target class's
901  * prototype.
902  */
903 Class.Update = function Update(obj)
904     Class.Property({
905         configurable: true,
906         enumerable: true,
907         writable: true,
908         init: function (key, target) {
909             this.value = update({}, target[key], obj);
910         }
911     });
912
913 Class.replaceProperty = function replaceProperty(obj, prop, value) {
914     Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
915     return value;
916 };
917 Class.toString = function toString() "[class " + this.className + "]";
918 Class.prototype = {
919     /**
920      * Initializes new instances of this class. Called automatically
921      * when new instances are created.
922      */
923     init: function c_init() {},
924
925     get instance() ({}),
926     set instance(val) Class.replaceProperty(this, "instance", val),
927
928     withSavedValues: function withSavedValues(names, callback, self) {
929         let vals = names.map(function (name) this[name], this);
930         try {
931             return callback.call(self || this);
932         }
933         finally {
934             names.forEach(function (name, i) this[name] = vals[i], this);
935         }
936     },
937
938     toString: function C_toString() {
939         if (this.toStringParams)
940             var params = "(" + this.toStringParams.map(function (m) isArray(m)  ? "[" + m + "]" :
941                                                                     isString(m) ? m.quote() : String(m))
942                                    .join(", ") + ")";
943         return "[instance " + this.constructor.className + (params || "") + "]";
944     },
945
946     /**
947      * Executes *callback* after *timeout* milliseconds. The value of
948      * 'this' is preserved in the invocation of *callback*.
949      *
950      * @param {function} callback The function to call after *timeout*
951      * @param {number} timeout The time, in milliseconds, to wait
952      *     before calling *callback*.
953      * @returns {nsITimer} The timer which backs this timeout.
954      */
955     timeout: function timeout(callback, timeout) {
956         const self = this;
957         function timeout_notify(timer) {
958             if (self.stale ||
959                     util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
960                 return;
961             self.timeouts.splice(self.timeouts.indexOf(timer), 1);
962             util.trapErrors(callback, self);
963         }
964         let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
965         this.timeouts.push(timer);
966         return timer;
967     },
968     timeouts: [],
969
970     /**
971      * Updates this instance with the properties of the given objects.
972      * Like the update function, but with special semantics for
973      * localized properties.
974      */
975     update: function update() {
976         let self = this;
977         // XXX: Duplication.
978
979         for (let i = 0; i < arguments.length; i++) {
980             let src = arguments[i];
981             Object.getOwnPropertyNames(src || {}).forEach(function (k) {
982                 let desc = Object.getOwnPropertyDescriptor(src, k);
983                 if (desc.value instanceof Class.Property)
984                     desc = desc.value.init(k, this) || desc.value;
985
986                 if (typeof desc.value === "function") {
987                     let func = desc.value.wrapped || desc.value;
988                     if (!func.superapply) {
989                         func.__defineGetter__("super", function () Object.getPrototypeOf(self)[k]);
990                         func.superapply = function superapply(self, args)
991                             let (meth = Object.getPrototypeOf(self)[k])
992                                 meth && meth.apply(self, args);
993                         func.supercall = function supercall(self)
994                             func.superapply(self, Array.slice(arguments, 1));
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) {
1131     try {
1132         let init = callable(prototype) ? 4 : 3;
1133         let proto = arguments[callable(prototype) ? 2 : 1];
1134
1135         proto._metaInit_ = function () {
1136             delete module.prototype._metaInit_;
1137             currentModule[name.toLowerCase()] = this;
1138         };
1139
1140         const module = Class.apply(Class, Array.slice(arguments, 0, init));
1141         let instance = module();
1142         module.className = name.toLowerCase();
1143
1144         instance.INIT = update(Object.create(Module.INIT),
1145                                arguments[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() {
1203     if (!/^[A-Z]/.test(arguments[0]))
1204         var args = Array.slice(arguments, 0);
1205     else {
1206         var className = arguments[0];
1207         args = Array.slice(arguments, 1);
1208     }
1209
1210     const Struct = Class(className || "Struct", StructBase, {
1211         length: args.length,
1212         members: array.toObject(args.map(function (v, k) [v, k]))
1213     });
1214     args.forEach(function (name, i) {
1215         Struct.prototype.__defineGetter__(name, function () this[i]);
1216         Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1217     });
1218     return Struct;
1219 }
1220 var StructBase = Class("StructBase", Array, {
1221     init: function struct_init() {
1222         for (let i = 0; i < arguments.length; i++)
1223             if (arguments[i] != undefined)
1224                 this[i] = arguments[i];
1225     },
1226
1227     get toStringParams() this,
1228
1229     clone: function struct_clone() this.constructor.apply(null, this.slice()),
1230
1231     closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1232
1233     get: function struct_get(key, val) this[this.members[key]],
1234     set: function struct_set(key, val) this[this.members[key]] = val,
1235
1236     toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1237
1238     // Iterator over our named members
1239     __iterator__: function struct__iterator__() {
1240         let self = this;
1241         return ([k, self[k]] for (k in keys(self.members)))
1242     }
1243 }, {
1244     fromArray: function fromArray(ary) {
1245         if (!(ary instanceof this))
1246             ary.__proto__ = this.prototype;
1247         return ary;
1248     },
1249
1250     /**
1251      * Sets a lazily constructed default value for a member of
1252      * the struct. The value is constructed once, the first time
1253      * it is accessed and memoized thereafter.
1254      *
1255      * @param {string} key The name of the member for which to
1256      *     provide the default value.
1257      * @param {function} val The function which is to generate
1258      *     the default value.
1259      */
1260     defaultValue: function defaultValue(key, val) {
1261         let i = this.prototype.members[key];
1262         this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1263         this.prototype.__defineSetter__(i, function (value)
1264             Class.replaceProperty(this, i, value));
1265         return this;
1266     },
1267
1268     localize: function localize(key, defaultValue) {
1269         let i = this.prototype.members[key];
1270         Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1271         return this;
1272     }
1273 });
1274
1275 var Timer = Class("Timer", {
1276     init: function init(minInterval, maxInterval, callback, self) {
1277         this._timer = services.Timer();
1278         this.callback = callback;
1279         this.self = self || this;
1280         this.minInterval = minInterval;
1281         this.maxInterval = maxInterval;
1282         this.doneAt = 0;
1283         this.latest = 0;
1284     },
1285
1286     notify: function notify(timer, force) {
1287         try {
1288             if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1289                 return;
1290
1291             this._timer.cancel();
1292             this.latest = 0;
1293             // minInterval is the time between the completion of the command and the next firing
1294             this.doneAt = Date.now() + this.minInterval;
1295
1296             this.callback.call(this.self, this.arg);
1297         }
1298         catch (e) {
1299             if (typeof util === "undefined")
1300                 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1301             else
1302                 util.reportError(e);
1303         }
1304         finally {
1305             this.doneAt = Date.now() + this.minInterval;
1306         }
1307     },
1308
1309     tell: function tell(arg) {
1310         if (arguments.length > 0)
1311             this.arg = arg;
1312
1313         let now = Date.now();
1314         if (this.doneAt == -1)
1315             this._timer.cancel();
1316
1317         let timeout = this.minInterval;
1318         if (now > this.doneAt && this.doneAt > -1)
1319             timeout = 0;
1320         else if (this.latest)
1321             timeout = Math.min(timeout, this.latest - now);
1322         else
1323             this.latest = now + this.maxInterval;
1324
1325         this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1326         this.doneAt = -1;
1327     },
1328
1329     reset: function reset() {
1330         this._timer.cancel();
1331         this.doneAt = 0;
1332     },
1333
1334     flush: function flush(force) {
1335         if (this.doneAt == -1 || force)
1336             this.notify(null, true);
1337     }
1338 });
1339
1340 /**
1341  * Idempotent function which returns the UTF-8 encoded value of an
1342  * improperly-decoded string.
1343  *
1344  * @param {string} str
1345  * @returns {string}
1346  */
1347 function UTF8(str) {
1348     try {
1349         return decodeURIComponent(escape(str));
1350     }
1351     catch (e) {
1352         return str;
1353     }
1354 }
1355
1356 function octal(decimal) parseInt(decimal, 8);
1357
1358 /**
1359  * Iterates over an arbitrary object. The following iterator types are
1360  * supported, and work as a user would expect:
1361  *
1362  *  â€¢ nsIDOMNodeIterator
1363  *  â€¢ mozIStorageStatement
1364  *
1365  * Additionally, the following array-like objects yield a tuple of the
1366  * form [index, element] for each contained element:
1367  *
1368  *  â€¢ nsIDOMHTMLCollection
1369  *  â€¢ nsIDOMNodeList
1370  *
1371  * and the following likewise yield one element of the form
1372  * [name, element] for each contained element:
1373  *
1374  *  â€¢ nsIDOMNamedNodeMap
1375  *
1376  * Duck typing is implemented for any other type. If the object
1377  * contains the "enumerator" property, iter is called on that. If the
1378  * property is a function, it is called first. If it contains the
1379  * property "getNext" along with either "hasMoreItems" or "hasMore", it
1380  * is iterated over appropriately.
1381  *
1382  * For all other cases, this function behaves exactly like the Iterator
1383  * function.
1384  *
1385  * @param {object} obj
1386  * @param {nsIJSIID} iface The interface to which to query all elements.
1387  * @returns {Generator}
1388  */
1389 function iter(obj, iface) {
1390     if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1391         return iter(obj).map(function (item) item.QueryInterface(iface));
1392
1393     let args = arguments;
1394     let res = Iterator(obj);
1395
1396     if (args.length > 1)
1397         res = (function () {
1398             for (let i = 0; i < args.length; i++)
1399                 for (let j in iter(args[i]))
1400                     yield j;
1401         })();
1402     else if (isinstance(obj, ["Iterator", "Generator"]))
1403         ;
1404     else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1405         while (obj.constructor instanceof ctypes.PointerType)
1406             obj = obj.contents;
1407         if (obj.constructor instanceof ctypes.ArrayType)
1408             res = array.iterItems(obj);
1409         else if (obj.constructor instanceof ctypes.StructType)
1410             res = (function () {
1411                 for (let prop in values(obj.constructor.fields))
1412                     yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1413             })();
1414         else
1415             return iter({});
1416     }
1417     else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1418         res = array.iterItems(obj);
1419     else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
1420              Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
1421         res = (function () {
1422             for (let i = 0; i < obj.length; i++)
1423                 yield [obj.name, obj];
1424         })();
1425     else if (obj instanceof Ci.mozIStorageStatement)
1426         res = (function (obj) {
1427             while (obj.executeStep())
1428                 yield obj.row;
1429             obj.reset();
1430         })(obj);
1431     else if ("getNext" in obj) {
1432         if ("hasMoreElements" in obj)
1433             res = (function () {
1434                 while (obj.hasMoreElements())
1435                     yield obj.getNext();
1436             })();
1437         else if ("hasMore" in obj)
1438             res = (function () {
1439                 while (obj.hasMore())
1440                     yield obj.getNext();
1441             })();
1442     }
1443     else if ("enumerator" in obj) {
1444         if (callable(obj.enumerator))
1445             return iter(obj.enumerator());
1446         return iter(obj.enumerator);
1447     }
1448     return Iter(res);
1449 }
1450 update(iter, {
1451     toArray: function toArray(iter) array(iter).array,
1452
1453     // See array.prototype for API docs.
1454     toObject: function toObject(iter) {
1455         let obj = {};
1456         for (let [k, v] in iter)
1457             if (v instanceof Class.Property)
1458                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1459             else
1460                 obj[k] = v;
1461         return obj;
1462     },
1463
1464     compact: function compact(iter) (item for (item in iter) if (item != null)),
1465
1466     every: function every(iter, pred, self) {
1467         pred = pred || util.identity;
1468         for (let elem in iter)
1469             if (!pred.call(self, elem))
1470                 return false;
1471         return true;
1472     },
1473     some: function every(iter, pred, self) {
1474         pred = pred || util.identity;
1475         for (let elem in iter)
1476             if (pred.call(self, elem))
1477                 return true;
1478         return false;
1479     },
1480
1481     filter: function filter(iter, pred, self) {
1482         for (let elem in iter)
1483             if (pred.call(self, elem))
1484                 yield elem;
1485     },
1486
1487     /**
1488      * Iterates over an iterable object and calls a callback for each
1489      * element.
1490      *
1491      * @param {object} iter The iterator.
1492      * @param {function} fn The callback.
1493      * @param {object} self The this object for *fn*.
1494      */
1495     forEach: function forEach(iter, func, self) {
1496         for (let val in iter)
1497             func.call(self, val);
1498     },
1499
1500     indexOf: function indexOf(iter, elem) {
1501         let i = 0;
1502         for (let item in iter) {
1503             if (item == elem)
1504                 return i;
1505             i++;
1506         }
1507     },
1508
1509     /**
1510      * Returns the array that results from applying *func* to each property of
1511      * *obj*.
1512      *
1513      * @param {Object} obj
1514      * @param {function} func
1515      * @returns {Array}
1516      */
1517     map: function map(iter, func, self) {
1518         for (let i in iter)
1519             yield func.call(self, i);
1520     },
1521
1522     /**
1523      * Returns the nth member of the given array that matches the
1524      * given predicate.
1525      */
1526     nth: function nth(iter, pred, n, self) {
1527         if (typeof pred === "number")
1528             [pred, n] = [function () true, pred]; // Hack.
1529
1530         for (let elem in iter)
1531             if (pred.call(self, elem) && n-- === 0)
1532                 return elem;
1533         return undefined;
1534     },
1535
1536     sort: function sort(iter, fn, self)
1537         array(this.toArray(iter).sort(fn, self)),
1538
1539     uniq: function uniq(iter) {
1540         let seen = {};
1541         for (let item in iter)
1542             if (!Set.add(seen, item))
1543                 yield item;
1544     },
1545
1546     /**
1547      * Zips the contents of two arrays. The resulting array is the length of
1548      * ary1, with any shortcomings of ary2 replaced with null strings.
1549      *
1550      * @param {Array} ary1
1551      * @param {Array} ary2
1552      * @returns {Array}
1553      */
1554     zip: function zip(iter1, iter2) {
1555         try {
1556             yield [iter1.next(), iter2.next()];
1557         }
1558         catch (e if e instanceof StopIteration) {}
1559     }
1560 });
1561
1562 const Iter = Class("Iter", {
1563     init: function init(iter) {
1564         this.iter = iter;
1565         if ("__iterator__" in iter)
1566             this.iter = iter.__iterator__();
1567
1568         if (this.iter.finalize)
1569             this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1570     },
1571
1572     next: function next() this.iter.next(),
1573
1574     send: function send() this.iter.send.apply(this.iter, arguments),
1575
1576     __iterator__: function () this.iter
1577 });
1578
1579 /**
1580  * Array utility methods.
1581  */
1582 var array = Class("array", Array, {
1583     init: function (ary) {
1584         if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1585             ary = [k for (k in ary)];
1586         else if (ary.length)
1587             ary = Array.slice(ary);
1588
1589         return {
1590             __proto__: ary,
1591             __iterator__: function () this.iterItems(),
1592             __noSuchMethod__: function (meth, args) {
1593                 var res = array[meth].apply(null, [this.array].concat(args));
1594                 if (isArray(res))
1595                     return array(res);
1596                 if (isinstance(res, ["Iterator", "Generator"]))
1597                     return iter(res);
1598                 return res;
1599             },
1600             array: ary,
1601             toString: function () this.array.toString(),
1602             concat: function () this.__noSuchMethod__("concat", Array.slice(arguments)),
1603             filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)),
1604             map: function () this.__noSuchMethod__("map", Array.slice(arguments))
1605         };
1606     }
1607 }, {
1608     /**
1609      * Converts an array to an object. As in lisp, an assoc is an
1610      * array of key-value pairs, which maps directly to an object,
1611      * as such:
1612      *    [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1613      *
1614      * @param {[Array]} assoc
1615      * @... {string} 0 - Key
1616      * @...          1 - Value
1617      */
1618     toObject: function toObject(assoc) {
1619         let obj = {};
1620         assoc.forEach(function ([k, v]) {
1621             if (v instanceof Class.Property)
1622                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1623             else
1624                 obj[k] = v;
1625         });
1626         return obj;
1627     },
1628
1629     /**
1630      * Compacts an array, removing all elements that are null or undefined:
1631      *    ["foo", null, "bar", undefined] -> ["foo", "bar"]
1632      *
1633      * @param {Array} ary
1634      * @returns {Array}
1635      */
1636     compact: function compact(ary) ary.filter(function (item) item != null),
1637
1638     /**
1639      * Returns true if each element of ary1 is equal to the
1640      * corresponding element in ary2.
1641      *
1642      * @param {Array} ary1
1643      * @param {Array} ary2
1644      * @returns {boolean}
1645      */
1646     equals: function (ary1, ary2)
1647         ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1648
1649     /**
1650      * Flattens an array, such that all elements of the array are
1651      * joined into a single array:
1652      *    [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1653      *
1654      * @param {Array} ary
1655      * @returns {Array}
1656      */
1657     flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1658
1659     /**
1660      * Returns an Iterator for an array's values.
1661      *
1662      * @param {Array} ary
1663      * @returns {Iterator(Object)}
1664      */
1665     iterValues: function iterValues(ary) {
1666         for (let i = 0; i < ary.length; i++)
1667             yield ary[i];
1668     },
1669
1670     /**
1671      * Returns an Iterator for an array's indices and values.
1672      *
1673      * @param {Array} ary
1674      * @returns {Iterator([{number}, {Object}])}
1675      */
1676     iterItems: function iterItems(ary) {
1677         let length = ary.length;
1678         for (let i = 0; i < length; i++)
1679             yield [i, ary[i]];
1680     },
1681
1682     /**
1683      * Returns the nth member of the given array that matches the
1684      * given predicate.
1685      */
1686     nth: function nth(ary, pred, n, self) {
1687         for (let elem in values(ary))
1688             if (pred.call(self, elem) && n-- === 0)
1689                 return elem;
1690         return undefined;
1691     },
1692
1693     /**
1694      * Filters out all duplicates from an array. If *unsorted* is false, the
1695      * array is sorted before duplicates are removed.
1696      *
1697      * @param {Array} ary
1698      * @param {boolean} unsorted
1699      * @returns {Array}
1700      */
1701     uniq: function uniq(ary, unsorted) {
1702         let res = [];
1703         if (unsorted) {
1704             for (let item in values(ary))
1705                 if (res.indexOf(item) == -1)
1706                     res.push(item);
1707         }
1708         else {
1709             for (let [, item] in Iterator(ary.sort())) {
1710                 if (item != last || !res.length)
1711                     res.push(item);
1712                 var last = item;
1713             }
1714         }
1715         return res;
1716     },
1717
1718     /**
1719      * Zips the contents of two arrays. The resulting array is the length of
1720      * ary1, with any shortcomings of ary2 replaced with null strings.
1721      *
1722      * @param {Array} ary1
1723      * @param {Array} ary2
1724      * @returns {Array}
1725      */
1726     zip: function zip(ary1, ary2) {
1727         let res = [];
1728         for (let [i, item] in Iterator(ary1))
1729             res.push([item, i in ary2 ? ary2[i] : ""]);
1730         return res;
1731     }
1732 });
1733
1734 /* Make Minefield not explode, because Minefield exploding is not fun. */
1735 let iterProto = Iter.prototype;
1736 Object.keys(iter).forEach(function (k) {
1737     iterProto[k] = function () {
1738         let res = iter[k].apply(iter, [this].concat(Array.slice(arguments)));
1739         if (isinstance(res, ["Iterator", "Generator"]))
1740             return Iter(res);
1741         return res;
1742     };
1743 });
1744
1745 Object.keys(array).forEach(function (k) {
1746     if (!(k in iterProto))
1747         iterProto[k] = function () {
1748             let res = array[k].apply(array, [this.toArray()].concat(Array.slice(arguments)));
1749             if (isinstance(res, ["Iterator", "Generator"]))
1750                 return Iter(res);
1751             if (isArray(res))
1752                 return array(res);
1753             return res;
1754         };
1755 });
1756
1757 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1758     if (!(k in iterProto) && callable(Array.prototype[k]))
1759         iterProto[k] = function () {
1760             let ary = iter(this).toArray();
1761             let res = ary[k].apply(ary, arguments);
1762             if (isArray(res))
1763                 return array(res);
1764             return res;
1765         };
1766 });
1767
1768 endModule();
1769
1770 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1771
1772 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: