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