diff --git a/common/content/abbreviations.js b/common/content/abbreviations.js index e264666a..fac98e93 100644 --- a/common/content/abbreviations.js +++ b/common/content/abbreviations.js @@ -366,23 +366,21 @@ var Abbreviations = Module("abbreviations", { } ], serialize: function () { - return Ary(abbreviations.userHives) + return abbreviations.userHives .filter(h => h.persist) - .map(hive => [ - { - command: this.name, - arguments: [abbr.lhs], - literalArg: abbr.rhs, - options: { - "-group": hive.name == "user" ? undefined : hive.name, - "-javascript": callable(abbr.rhs) ? null : undefined - } - } - for (abbr of hive.merged) - if (abbr.modesEqual(modes)) - ]). - flatten().array; - } + .flatMap(hive => + hive.merged + .filter(abbr => abbr.modesEqual(modes)) + .map(abbr => ({ + command: this.name, + arguments: [abbr.lhs], + literalArg: abbr.rhs, + options: { + "-group": hive.name == "user" ? undefined : hive.name, + "-javascript": callable(abbr.rhs) ? null : undefined + } + }))); + }, }); commands.add([ch + "una[bbreviate]"], diff --git a/common/content/bookmarks.js b/common/content/bookmarks.js index aa7f7f9d..ff1768e6 100644 --- a/common/content/bookmarks.js +++ b/common/content/bookmarks.js @@ -436,10 +436,9 @@ var Bookmarks = Module("bookmarks", { names: ["-tags", "-T"], description: "A comma-separated list of tags", completer: function tags(context) { - context.generate = () => Ary(b.tags - for (b of bookmarkcache) - if (b.tags)) - .flatten().uniq().array; + context.generate = () => new RealSet(Array.from(bookmarkcache) + .flatMap(b.tags)); + context.keys = { text: identity, description: identity }; }, type: CommandOption.LIST diff --git a/common/content/browser.js b/common/content/browser.js index 966073c6..100c88f6 100644 --- a/common/content/browser.js +++ b/common/content/browser.js @@ -243,12 +243,12 @@ var Browser = Module("browser", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { argCount: "0" }); }, mappings: function initMappings(dactyl, modules, window) { - let openModes = Ary.toObject([ - [dactyl.CURRENT_TAB, ""], - [dactyl.NEW_TAB, "tab"], - [dactyl.NEW_BACKGROUND_TAB, "background tab"], - [dactyl.NEW_WINDOW, "win"] - ]); + let openModes = { + [dactyl.CURRENT_TAB]: "", + [dactyl.NEW_TAB]: "tab", + [dactyl.NEW_BACKGROUND_TAB]: "background tab", + [dactyl.NEW_WINDOW]: "win", + }; function open(mode, args) { if (dactyl.forceTarget in openModes) diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 346d659b..fefb8109 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -240,8 +240,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { let name = commands.add(params.name, params.description, function (args) { - let results = Ary(params.iterate(args)) - .sort((a, b) => String.localeCompare(a.name, b.name)); + let results = Array.from(params.iterate(args)) + .sort((a, b) => String.localeCompare(a.name, b.name)); let filters = args.map(arg => { let re = util.regexp.escape(arg); @@ -261,22 +261,24 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { return seen[this.text] + /*L*/" matching items"; }; context.ignoreCase = true; + let seen = {}; - context.completions = Ary(keys(item).join(" ").toLowerCase().split(/[()\s]+/) - for (item of params.iterate(args))) - .flatten() - .map(function (k) { - seen[k] = (seen[k] || 0) + 1; - return k; - }).uniq(); + context.completions = new RealSet( + Array.from(params.iterate(args)) + .flatMap(item => Object.keys(item).join(" ") + .toLowerCase().split(/[()\s]+/)) + .map(k => { + seen[k] = (seen[k] || 0) + 1; + return k; + })); }, options: params.options || [] }); if (params.index) this.indices[params.index] = function* () { - let results = Ary((params.iterateIndex || params.iterate).call(params, commands.get(name).newArgs())) - .array.sort((a, b) => String.localeCompare(a.name, b.name)); + let results = Array.from((params.iterateIndex || params.iterate).call(params, commands.get(name).newArgs())) + .sort((a, b) => String.localeCompare(a.name, b.name)); for (let obj of results) { let res = dactyl.generateHelp(obj, null, null, true); @@ -646,7 +648,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * Initialize the help system. */ initHelp: function initHelp() { - util.dumpStack('INIT HELP'); if ("noscriptOverlay" in window) window.noscriptOverlay.safeAllow("dactyl:", true, false); @@ -1415,11 +1416,13 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { // FIXME: cleanup cleanupValue: config.cleanups.guioptions || - "rb" + [k for ([k, v] of iter(groups[1].opts)) - if (!Dactyl.isToolbarHidden(document.getElementById(v[1][0])))].join(""), + "rb" + Object.entries(groups[1].opts) + .filter(([k, v]) => !Dactyl.isToolbarHidden(document.getElementById(v[1][0]))) + .map(([k]) => k) + .join(""), - values: Ary(groups).map(g => [[k, v[0]] for ([k, v] of iter(g.opts))]) - .flatten(), + values: groups.flatMap(g => Object.entries(g.opts) + .map(([k, v]) => [k, v[0]])), setter: function (value) { for (let group of groups) diff --git a/common/content/events.js b/common/content/events.js index 2eb58227..86391f4e 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -23,10 +23,10 @@ var EventHive = Class("EventHive", Contexts.Hive, { }, _events: function _events(event, callback) { - if (!isObject(event)) - var [self, events] = [null, Ary.toObject([[event, callback]])]; + if (isObject(event)) + var [self, events] = [event, event[callback || "events"]]; else - [self, events] = [event, event[callback || "events"]]; + [self, events] = [null, { [event]: callback }]; if (hasOwnProp(events, "input") && !hasOwnProp(events, "dactyl-input")) events["dactyl-input"] = events.input; @@ -1141,7 +1141,7 @@ var Events = Module("events", { return this.value.filter(f => f(buffer.documentURI)); }); memoize(this, "pass", function () { - return new RealSet(Ary.flatten(this.filters.map(f => f.keys))); + return new RealSet(this.filters.flatMap(f => f.keys)); }); memoize(this, "commandHive", function hive() { return Hive(this.filters, "command"); diff --git a/common/content/hints.js b/common/content/hints.js index c2ce1a2b..5a7f71a8 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -1379,7 +1379,8 @@ var Hints = Module("hints", { }, validator: function (value) { let values = DOM.Event.parse(value).map(DOM.Event.bound.stringify); - return Option.validIf(Ary.uniq(values).length === values.length && values.length > 1, + + return Option.validIf(new RealSet(values).size === values.length && values.length > 1, _("option.hintkeys.duplicate")); } }); diff --git a/common/content/history.js b/common/content/history.js index 3bedb20d..39259ad3 100644 --- a/common/content/history.js +++ b/common/content/history.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2014 Kris Maglione +// Copyright (c) 2008-2015 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -290,7 +290,7 @@ var History = Module("history", { description: "The sort order of the results", completer: function (context) { context.compare = CompletionContext.Sort.unsorted; - return Ary.flatten([ + return [ "annotation", "date", "date added", @@ -300,10 +300,10 @@ var History = Module("history", { "title", "uri", "visitcount" - ].map(order => [ - ["+" + order.replace(" ", ""), /*L*/"Sort by " + order + " ascending"], - ["-" + order.replace(" ", ""), /*L*/"Sort by " + order + " descending"] - ])); + ].flatMap(order => [ + ["+" + order.replace(" ", ""), /*L*/"Sort by " + order + " ascending"], + ["-" + order.replace(" ", ""), /*L*/"Sort by " + order + " descending"] + ]); } } ], diff --git a/common/content/key-processors.js b/common/content/key-processors.js index 551053a5..ac1bbd44 100644 --- a/common/content/key-processors.js +++ b/common/content/key-processors.js @@ -17,7 +17,9 @@ var ProcessorStack = Class("ProcessorStack", { events.dbg("STACK " + mode); let main = { __proto__: mode.main, params: mode.params }; - this.modes = Ary([mode.params.keyModes, main, mode.main.allBases.slice(1)]).flatten().compact(); + this.modes = Ary([mode.params.keyModes, + main, + mode.main.allBases.slice(1)]).flatten().compact(); if (builtin) hives = hives.filter(h => h.name === "builtin"); diff --git a/common/content/mappings.js b/common/content/mappings.js index 9e4c7e20..95c4428a 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -116,7 +116,7 @@ var Map = Class("Map", { return this.keys.indexOf(name) >= 0; }, - get keys() { return Ary.flatten(this.names.map(mappings.bound.expand)); }, + get keys() { return this.names.flatMap(mappings.bound.expand); }, /** * Execute the action for this mapping. @@ -404,7 +404,7 @@ var Mappings = Module("mappings", { iterate: function* (mode) { let seen = new RealSet; for (let hive of this.hives.iterValues()) - for (let map of Ary.iterValues(hive.getStack(mode))) + for (let map of hive.getStack(mode)) if (!seen.add(map.name)) yield map; }, @@ -646,26 +646,24 @@ var Mappings = Module("mappings", { if (this.name != "map") return []; else - return Ary(mappings.userHives) + return mappings.userHives .filter(h => h.persist) - .map(hive => [ - { - command: "map", - options: { - "-count": map.count ? null : undefined, - "-description": map.description, - "-group": hive.name == "user" ? undefined : hive.name, - "-modes": uniqueModes(map.modes), - "-silent": map.silent ? null : undefined - }, - arguments: [map.names[0]], - literalArg: map.rhs, - ignoreDefaults: true - } - for (map of userMappings(hive)) - if (map.persist) - ]) - .flatten().array; + .flatMap(hive => + Array.from(userMappings(hive)) + .filter(map => map.persist) + .map(map => ({ + command: "map", + options: { + "-count": map.count ? null : undefined, + "-description": map.description, + "-group": hive.name == "user" ? undefined : hive.name, + "-modes": uniqueModes(map.modes), + "-silent": map.silent ? null : undefined + }, + arguments: [map.names[0]], + literalArg: map.rhs, + ignoreDefaults: true + }))); } }; function* userMappings(hive) { @@ -731,9 +729,11 @@ var Mappings = Module("mappings", { return Array.concat(value).every(findMode); }, completer: function () { - return [[Ary.compact([mode.name.toLowerCase().replace(/_/g, "-"), mode.char]), mode.description] - for (mode of modes.all) - if (!mode.hidden)]; + return modes.all.filter(mode => !mode.hidden) + .map( + mode => [Ary.compact([mode.name.toLowerCase().replace(/_/g, "-"), + mode.char]), + mode.description]); } }; diff --git a/common/content/modes.js b/common/content/modes.js index f6f6e4b2..c3cf2d34 100644 --- a/common/content/modes.js +++ b/common/content/modes.js @@ -685,8 +685,10 @@ var Modes = Module("modes", { }, get values() { - return Ary.toObject([[m.name.toLowerCase(), m.description] - for (m of modes._modes) if (!m.hidden)]); + return Ary.toObject( + modes._modes.filter(mode => !mode.hidden) + .map(mode => [mode.name.toLowerCase(), + mode.description])); } }; diff --git a/common/modules/base.jsm b/common/modules/base.jsm index 860eed80..409004b0 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -186,7 +186,6 @@ defineModule("base", { "Set", "Struct", "StructBase", - "Symbol", "TextEncoder", "TextDecoder", "Timer", @@ -217,6 +216,7 @@ defineModule("base", { "iterOwnProperties", "keys", "literal", + "loadPolyfill", "memoize", "module", "octal", @@ -228,6 +228,12 @@ defineModule("base", { ] }); +function loadPolyfill(obj) { + JSMLoader.loadSubScript("resource://dactyl/polyfill.jsm", + obj); +} +loadPolyfill(this); + this.lazyRequire("cache", ["cache"]); this.lazyRequire("config", ["config"]); this.lazyRequire("messages", ["_", "Messages"]); @@ -236,11 +242,6 @@ this.lazyRequire("services", ["services"]); this.lazyRequire("storage", ["File"]); this.lazyRequire("util", ["FailedAssertion", "util"]); -if (typeof Symbol == "undefined") - this.Symbol = { - iterator: "@@iterator" - }; - let literal_ = function literal(comment) { if (comment) return /^function.*?\/\*([^]*)\*\/(?:\/\* use strict \*\/)\s*\S$/.exec(comment)[1]; @@ -1203,34 +1204,19 @@ for (let name of properties(Class.prototype)) { } var closureHooks = { - get: function closure_get(target, prop) { - if (hasOwnProp(target._closureCache, prop)) + get(target, prop) { + if (prop in target._closureCache) return target._closureCache[prop]; let p = target[prop]; if (callable(p)) return target._closureCache[prop] = p.bind(target); return p; - } - - /* - getOwnPropertyNames: function getOwnPropertyNames(target) { - return [k for (k in properties(target, true))]; }, - - getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, prop) { - let self = this; - return { - configurable: false, - writable: false, - get value() { return self.get(target, prop); } - } - } - */ }; Class.makeClosure = function makeClosure() { - this._closureCache = {}; + this._closureCache = Object.create(null); return new Proxy(this, closureHooks); }; @@ -1402,7 +1388,7 @@ function Struct(...args) { const Struct = Class(className || "Struct", StructBase, { length: args.length, - members: Ary(args).map((v, k) => [v, k]).toObject() + members: Ary.toObject(args.map((v, k) => [v, k])), }); args.forEach(function (name, i) { Struct.prototype.__defineGetter__(name, function () { @@ -1617,12 +1603,12 @@ function iter(obj, iface) { else if (Symbol.iterator in obj) res = obj[Symbol.iterator](); else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList])) - res = Ary.iterItems(obj); + res = Array.from(obj).entries(); else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) { while (obj.constructor instanceof ctypes.PointerType) obj = obj.contents; if (obj.constructor instanceof ctypes.ArrayType) - res = Ary.iterItems(obj); + res = Array.from(obj).entries(); else if (obj.constructor instanceof ctypes.StructType) res = (function* () { for (let prop of obj.constructor.fields) { @@ -1664,14 +1650,14 @@ function iter(obj, iface) { } if (res === undefined) - res = Iterator(obj)[Symbol.iterator](); + res = Object.entries(obj).values(); return Iter(res); } update(iter, { - toArray: function toArray(iter) { - return Ary(iter).array; - }, + toArray: deprecated("Array.from", function toArray(iter) { + return Array.from(iter); + }), // See Ary.prototype for API docs. toObject: function toObject(iter) { @@ -1939,10 +1925,10 @@ var Ary = Class("Ary", Array, { * @param {Array} ary * @returns {Iterator(Object)} */ - iterValues: function* iterValues(ary) { + iterValues: deprecated("Array#values", function* iterValues(ary) { for (let i = 0; i < ary.length; i++) yield ary[i]; - }, + }), /** * Returns an Iterator for an array's indices and values. @@ -1950,11 +1936,11 @@ var Ary = Class("Ary", Array, { * @param {Array} ary * @returns {Iterator([{number}, {Object}])} */ - iterItems: function* iterItems(ary) { + iterItems: deprecated("Array#entries", function* iterItems(ary) { let length = ary.length; for (let i = 0; i < length; i++) yield [i, ary[i]]; - }, + }), /** * Returns the nth member of the given array that matches the diff --git a/common/modules/bookmarkcache.jsm b/common/modules/bookmarkcache.jsm index f495a8e9..dcbcb301 100644 --- a/common/modules/bookmarkcache.jsm +++ b/common/modules/bookmarkcache.jsm @@ -94,9 +94,12 @@ var BookmarkCache = Module("BookmarkCache", XPCOM(Ci.nsINavBookmarkObserver), { bookmarks: Class.Memoize(function () { return this.load(); }), keywords: Class.Memoize(function () { - return Ary.toObject([[b.keyword, b] - for (b of this) - if (b.keyword)]); + let res = {}; + for (let bookmark of this) + if (bookmark.keyword) + res[bookmark.keyword] = res; + + return res; }), rootFolders: ["toolbarFolder", "bookmarksMenuFolder", "unfiledBookmarksFolder"] diff --git a/common/modules/buffer.jsm b/common/modules/buffer.jsm index 6190d28f..72e09314 100644 --- a/common/modules/buffer.jsm +++ b/common/modules/buffer.jsm @@ -58,8 +58,8 @@ var Buffer = Module("Buffer", { * buffer. Only returns style sheets for the 'screen' media type. */ get alternateStyleSheets() { - let stylesheets = Ary.flatten( - this.allFrames().map(w => Array.slice(w.document.styleSheets))); + let stylesheets = this.allFrames() + .flatMap(w => w.document.styleSheets); return stylesheets.filter( s => /^(screen|all|)$/i.test(s.media.mediaText) && !/^\s*$/.test(s.title) @@ -415,11 +415,12 @@ var Buffer = Module("Buffer", { * Returns a list of all frames in the given window or current buffer. */ allFrames: function allFrames(win=this.win, focusedFirst) { - let frames = iter(util.iterFrames(win)).toArray(); + let frames = Array.from(util.iterFrames(win)); - if (focusedFirst) - return frames.filter(f => f === this.focusedFrame).concat( - frames.filter(f => f !== this.focusedFrame)); + if (focusedFirst) { + let focused = this.focusedFrame; + return frames.sort((a, b) => (b === focused) - (a === focused)); + } return frames; }, @@ -2415,8 +2416,10 @@ var Buffer = Module("Buffer", { let frames = buffer.allFrames(undefined, true); - let elements = Ary.flatten(frames.map(win => [m for (m of DOM.XPath(xpath, win.document))])) - .filter(function (elem) { + let elements = Array.from(frames) + .flatMap(win => DOM.XPath(xpath, win.document)) + .filter(elem => { + if (isinstance(elem, [Ci.nsIDOMHTMLFrameElement, Ci.nsIDOMHTMLIFrameElement])) return Editor.getEditor(elem.contentWindow); @@ -2428,9 +2431,11 @@ var Buffer = Module("Buffer", { let style = elem.style; let rect = elem.rect; + return elem.isVisible && - (elem[0] instanceof Ci.nsIDOMXULTextBoxElement || style.MozUserFocus != "ignore") && - rect.width && rect.height; + (elem[0] instanceof Ci.nsIDOMXULTextBoxElement || + style.MozUserFocus != "ignore") && + rect.width && rect.height; }); dactyl.assert(elements.length > 0); diff --git a/common/modules/commands.jsm b/common/modules/commands.jsm index 24035957..96f8f1af 100644 --- a/common/modules/commands.jsm +++ b/common/modules/commands.jsm @@ -251,7 +251,7 @@ var Command = Class("Command", { /** @property {[string]} All of this command's long and short names. */ names: Class.Memoize(function () { - return this.names = Ary.flatten(this.parsedSpecs); + return this.parsedSpecs.flatMap(); }), /** @property {string} This command's description, as shown in :listcommands */ @@ -330,9 +330,9 @@ var Command = Class("Command", { _options: [], optionMap: Class.Memoize(function () { - return Ary(this.options) - .map(opt => opt.names.map(name => [name, opt])) - .flatten().toObject(); + return Ary.toObject( + Array.from(this.options) + .flatMap(opt => opt.names.map(name => [name, opt]))); }), newArgs: function newArgs(base) { @@ -456,7 +456,7 @@ var Command = Class("Command", { * @returns {Array} */ parseSpecs: function parseSpecs(specs) { - return specs.map(function (spec) { + return specs.map(spec => { let [, head, tail] = /([^[]+)(?:\[(.*)])?/.exec(spec); return tail ? [head + tail, head] : [head]; }); @@ -498,8 +498,9 @@ var Ex = Module("Ex", { args = Array.slice(args); let res = cmd.newArgs({ context: this.context }); + if (isObject(args[0])) - for (let [k, v] of iter(args.shift())) + for (let [k, v] of Object.entries(args.shift())) if (k == "!") res.bang = v; else if (k == "#") @@ -515,8 +516,9 @@ var Ex = Module("Ex", { res.explicitOpts[opt.names[0]] = val; } - for (let [i, val] of Ary.iterItems(args)) + for (let [i, val] of args.entries()) res[i] = String(val); + return res; }, @@ -593,7 +595,9 @@ var CommandHive = Class("CommandHive", Contexts.Hive, { if (this.cached) this.modules.initDependencies("commands"); this.cached = false; - return Ary.iterValues(this._list.sort((a, b) => a.name > b.name)); + + return this._list.sort((a, b) => String.localeCompare(a.name, b.name)) + .values(); }, /** @property {string} The last executed Ex command line. */ @@ -623,7 +627,7 @@ var CommandHive = Class("CommandHive", Contexts.Hive, { extra.hive = this; extra.parsedSpecs = Command.parseSpecs(specs); - let names = Ary.flatten(extra.parsedSpecs); + let names = extra.parsedSpecs.flatMap(); let name = names[0]; if (this.name != "builtin") { @@ -934,10 +938,10 @@ var Commands = Module("commands", { let defaults = {}; if (args.ignoreDefaults) - defaults = Ary(this.options).map(opt => [opt.names[0], opt.default]) - .toObject(); + defaults = Ary.toObject(Array.from(this.options, + opt => [opt.names[0], opt.default])); - for (let [opt, val] of iter(args.options || {})) { + for (let [opt, val] of Object.entries(args.options || {})) { if (val === undefined) continue; if (val != null && defaults[opt] === val) @@ -1788,28 +1792,32 @@ var Commands = Module("commands", { literal: 1, serialize: function () { - return Ary(commands.userHives) + return commands.userHives .filter(h => h.persist) - .map(hive => [ - { + .flatMap(hive => + Array.from(hive) + .filter(cmd => cmd.persist) + .map(cmd => + ({ command: this.name, bang: true, - options: iter([v, typeof cmd[k] == "boolean" ? null : cmd[k]] - // FIXME: this map is expressed multiple times - for ([k, v] of iter({ - argCount: "-nargs", - bang: "-bang", - count: "-count", - description: "-description" - })) - if (cmd[k])).toObject(), + options: Ary.toObject( + Object.entries({ + argCount: "-nargs", + bang: "-bang", + count: "-count", + description: "-description", + }) + .filter(([k, v]) => cmd[k]) + .map(([k, v]) => [ + v, + typeof cmd[k] == "boolean" ? null : cmd[k] + ])), arguments: [cmd.name], literalArg: cmd.action, ignoreDefaults: true - } - for (cmd of hive) if (cmd.persist) - ]) - .flatten().array; + })) + ); } }); diff --git a/common/modules/completion.jsm b/common/modules/completion.jsm index 6bb12d1f..27a6b998 100644 --- a/common/modules/completion.jsm +++ b/common/modules/completion.jsm @@ -284,17 +284,17 @@ var CompletionContext = Class("CompletionContext", { get longestSubstring() { return self.longestAllSubstring; }, get items() { - return Ary.flatten(self.activeContexts.map(function m(context) { + return self.activeContexts.flatMap(context => { let prefix = self.value.substring(minStart, context.offset); - return context.items.map(function m(item) { + return context.items.map(item => { return { text: prefix + item.text, result: prefix + item.result, __proto__: item }; }); - })); + }); } }); @@ -323,24 +323,23 @@ var CompletionContext = Class("CompletionContext", { * Possibly. */ let substrings = lists.reduce( - function r(res, list) { - return res.filter(function f(str) { - return list.some(function s_(s) { - return s.substr(0, str.length) == str; - }); + (res, list) => { + return res.filter(str => { + return list.some(s => s.substr(0, str.length) == str); }); }, lists.pop()); if (!substrings) // FIXME: How is this undefined? return []; + return Ary.uniq(Array.slice(substrings)); }, // Temporary get longestAllSubstring() { return this.allSubstrings.reduce(function r(a, b) { - return a.length > b.length ? a : b, ""; - }); + return a.length > b.length ? a : b; + }, ""); }, get caret() { return this._caret - this.offset; }, diff --git a/common/modules/config.jsm b/common/modules/config.jsm index aa8d842e..c73cd0e5 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -235,20 +235,19 @@ var ConfigBase = Class("ConfigBase", { let uri = "resource://dactyl-locale/"; let jar = io.isJarURL(uri); + let res; if (jar) { let prefix = getDir(jar.JAREntry); - var res = iter(s.slice(prefix.length).replace(/\/.*/, "") - for (s of io.listJar(jar.JARFile, prefix))) - .toArray(); + res = Array.from(io.listJar(jar.JARFile, prefix), + s => slice(prefix.length).replace(/\/.*/, "")); } else { - res = Ary(f.leafName - // Fails on FF3: for (f of util.getFile(uri).iterDirectory()) - for (f of util.getFile(uri).readDirectory()) - if (f.isDirectory())).array; + res = Array.from(util.getFile(uri).readDirectory()) + .filter(f => f.isDirectory()) + .map(f => f.leafName); } - let exists = function exists(pkg) { + let exists = pkg => { return services["resource:"].hasSubstitution("dactyl-locale-" + pkg); }; @@ -265,9 +264,14 @@ var ConfigBase = Class("ConfigBase", { * @returns {string} */ bestLocale: function (list) { - return values([this.appLocale, this.appLocale.replace(/-.*/, ""), - "en", "en-US", list[0]]) - .find(bind("has", new RealSet(list))); + let candidates = [this.appLocale, + this.appLocale.replace(/-.*/, ""), + "en", + "en-US", + list[0]]; + + list = new RealSet(list); + return candidates.find(locale => list.has(locale)); }, /** diff --git a/common/modules/dom.jsm b/common/modules/dom.jsm index 58aedc89..eccfb85a 100644 --- a/common/modules/dom.jsm +++ b/common/modules/dom.jsm @@ -723,17 +723,17 @@ var DOM = Class("DOM", { return this[0].getAttributeNS(ns, key); }, - css: update(function css(key, val) { + css: update(function css(props, val = undefined) { if (val !== undefined) - key = Ary.toObject([[key, val]]); + props = { [props]: val }; - if (isObject(key)) - return this.each(function (elem) { - for (let [k, v] of iter(key)) + if (isObject(props)) + return this.each(elem => { + for (let [k, v] of Object.entries(props)) elem.style[css.property(k)] = v; }); - return this[0].style[css.property(key)]; + return this[0].style[css.property(props)]; }, { name: function (property) { return property.replace(/[A-Z]/g, m0 => "-" + m0.toLowerCase()); @@ -895,13 +895,13 @@ var DOM = Class("DOM", { if (isObject(event)) capture = listener; else - event = Ary.toObject([[event, listener]]); + event = { [event]: listener }; - for (let [evt, callback] of iter(event)) + for (let [evt, callback] of Object.entries(event)) event[evt] = util.wrapCallback(callback, true); - return this.each(function (elem) { - for (let [evt, callback] of iter(event)) + return this.each(elem => { + for (let [evt, callback] of Object.entries(event)) elem.addEventListener(evt, callback, capture); }); }, @@ -909,30 +909,31 @@ var DOM = Class("DOM", { if (isObject(event)) capture = listener; else - event = Ary.toObject([[event, listener]]); + event = { [event]: listener }; - return this.each(function (elem) { - for (let [k, v] of iter(event)) - elem.removeEventListener(k, v.wrapper || v, capture); + return this.each(elem => { + for (let [event, callback] of Object.entries(event)) + elem.removeEventListener(event, callback.wrapper || v, capture); }); }, once: function once(event, listener, capture) { if (isObject(event)) capture = listener; else - event = Ary.toObject([[event, listener]]); + event = { [event]: listener }; for (let pair of iter(event)) { let [evt, callback] = pair; event[evt] = util.wrapCallback(function wrapper(event) { this.removeEventListener(evt, wrapper.wrapper, capture); + return callback.apply(this, arguments); }, true); } - return this.each(function (elem) { - for (let [k, v] of iter(event)) - elem.addEventListener(k, v, capture); + return this.each(elem => { + for (let [event, callback] of Object.entries(event)) + elem.addEventListener(event, callback, capture); }); }, @@ -940,6 +941,7 @@ var DOM = Class("DOM", { this.canceled = false; return this.each(function (elem) { let evt = DOM.Event(this.document, event, params, elem); + if (!DOM.Event.dispatch(elem, evt, extraProps)) this.canceled = true; }, this); @@ -1231,7 +1233,7 @@ var DOM = Class("DOM", { */ parse: function parse(input, unknownOk=true) { if (isArray(input)) - return Ary.flatten(input.map(k => this.parse(k, unknownOk))); + return input.flatMap(k => this.parse(k, unknownOk)); let out = []; for (let match of util.regexp.iterate(/<.*?>?>|[^<]|<(?!.*>)/g, input)) { @@ -1971,11 +1973,11 @@ var DOM = Class("DOM", { * @returns {string} */ makeXPath: function makeXPath(nodes) { - return Ary(nodes).map(util.debrace).flatten() - .map(node => /^[a-z]+:/.test(node) ? node - : [node, "xhtml:" + node]) - .flatten() - .map(node => "//" + node).join(" | "); + return nodes.flatMap(util.debrace) + .flatMap(node => /^[a-z]+:/.test(node) ? node + : [node, "xhtml:" + node]) + .map(node => "//" + node) + .join(" | "); }, namespaces: { diff --git a/common/modules/help.jsm b/common/modules/help.jsm index a34a3cfb..c2a2dffd 100644 --- a/common/modules/help.jsm +++ b/common/modules/help.jsm @@ -27,16 +27,16 @@ var HelpBuilder = Class("HelpBuilder", { // Scrape the list of help files from all.xml this.tags["all"] = this.tags["all.xml"] = "all"; - let files = this.findHelpFile("all").map(doc => - [f.value for (f of DOM.XPath("//dactyl:include/@href", doc))]); + let files = this.findHelpFile("all").flatMap( + doc => Array.from(DOM.XPath("//dactyl:include/@href", doc), + f => f.value)); // Scrape the tags from the rest of the help files. - Ary.flatten(files).forEach(function (file) { + for (let file of files) { this.tags[file + ".xml"] = file; - this.findHelpFile(file).forEach(function (doc) { + for (let doc of this.findHelpFile(file)) this.addTags(file, doc); - }, this); - }, this); + } } finally { delete help._data; @@ -129,10 +129,12 @@ var Help = Module("Help", { | (?: ^ [^\S\n]* \n) + `), "gmxy"); - let betas = util.regexp(/\[((?:b|rc)\d)\]/, "gx"); + let matchBetas = util.regexp(/\[((?:b|rc)\d)\]/, "gx"); - let beta = Ary(betas.iterate(NEWS)) - .map(m => m[1]).uniq().slice(-1)[0]; + let betas = Array.from(betas.iterate(NEWS), + match => match[1]); + + let beta = betas.sort().pop(); function rec(text, level, li) { let res = []; @@ -154,7 +156,9 @@ var Help = Module("Help", { else if (match.par) { let [, par, tags] = /([^]*?)\s*((?:\[[^\]]+\])*)\n*$/.exec(match.par); let t = tags; - tags = Ary(betas.iterate(tags)).map(m => m[1]); + + tags = Array.from(matchBetas.iterate(tags), + match => match[1]); let group = !tags.length ? "" : !tags.some(t => t == beta) ? "HelpNewsOld" : "HelpNewsNew"; diff --git a/common/modules/highlight.jsm b/common/modules/highlight.jsm index 8c7693fa..25ebca4b 100644 --- a/common/modules/highlight.jsm +++ b/common/modules/highlight.jsm @@ -456,13 +456,13 @@ var Highlights = Module("Highlight", { context.keys = { text: f => f.leafName.replace(extRe, ""), description: ".parent.path" }; context.completions = - Ary.flatten( - io.getRuntimeDirectories("colors").map( - dir => dir.readDirectory() - .filter(file => extRe.test(file.leafName)))) - .concat([ - { leafName: "default", parent: { path: /*L*/"Revert to builtin colorscheme" } } - ]); + io.getRuntimeDirectories("colors") + .flatMap(dir => dir.readDirectory() + .filter(file => extRe.test(file.leafName))) + .concat([ + { leafName: "default", + parent: { path: /*L*/"Revert to builtin colorscheme" } } + ]); }; diff --git a/common/modules/io.jsm b/common/modules/io.jsm index b2c8a243..dfeaee06 100644 --- a/common/modules/io.jsm +++ b/common/modules/io.jsm @@ -589,11 +589,13 @@ var IO = Module("io", { * otherwise, the return value of *func*. */ withTempFiles: function withTempFiles(func, self, checked, ext, label) { - let args = Ary(util.range(0, func.length)) - .map(bind("createTempFile", this, ext, label)).array; + let args = Array.from(util.range(0, func.length), + () => this.createTempFile(ext, label)); + try { if (!args.every(identity)) return false; + var res = func.apply(self || this, args); } finally { @@ -679,11 +681,10 @@ var IO = Module("io", { dactyl.assert(!file.exists() || args.bang, _("io.exists", JSON.stringify(file.path))); // TODO: Use a set/specifiable list here: - let lines = [cmd.serialize().map(commands.commandToString, cmd) - for (cmd of commands.iterator()) - if (cmd.serialize)]; - - lines = Ary.flatten(lines); + let lines = Array.from(commands.iterator()) + .filter(cmd => cmd.serialize) + .flatMap(cmd => cmd.serialize() + .map(commands.commandToString, cmd)); lines.unshift('"' + config.version + "\n"); lines.push("\n\" vim: set ft=" + config.name + ":"); @@ -840,7 +841,7 @@ let &cpo = s:cpo_save unlet s:cpo_save " vim: tw=130 et ts=8 sts=4 sw=4: -`;//}}} +`;//}}} " const { options } = modules; @@ -876,15 +877,21 @@ unlet s:cpo_save appname: config.appName, fileext: config.fileExtension, maintainer: "Doug Kearns ", + autocommands: wrap("syn keyword " + config.name + "AutoEvent ", - keys(config.autocommands)), + Object.keys(config.autocommands)), + commands: wrap("syn keyword " + config.name + "Command ", - Ary(c.specs for (c of commands.iterator())).flatten()), + Array.from(commands.iterator()).flatMap(cmd => cmd.specs)), + options: wrap("syn keyword " + config.name + "Option ", - Ary(o.names for (o of options) if (o.type != "boolean")).flatten()), + Array.from(options).filter(opt => opt.type != "boolean") + .flatMap(opt => opt.names)), + toggleoptions: wrap("let s:toggleOptions = [", - Ary(o.realNames for (o of options) if (o.type == "boolean")) - .flatten().map(JSON.stringify), + Array.from(options).filter(opt => opt.type == "boolean") + .flatMap(opt => opt.realNames) + .map(JSON.stringify), ", ") + "]" }; //}}} @@ -1103,16 +1110,14 @@ unlet s:cpo_save context.title = ["Shell Command", "Path"]; context.generate = () => { let dirNames = services.environment.get("PATH").split(config.OS.pathListSep); - let commands = []; - for (let dirName of dirNames) { + return dirNames.flatMap(dirName => { let dir = io.File(dirName); if (dir.exists() && dir.isDirectory()) - commands.push([[file.leafName, dir.path] for (file of iter(dir.directoryEntries)) - if (file.isFile() && file.isExecutable())]); - } - - return Ary.flatten(commands); + return Array.from(dir.directoryEntries) + .filter(file => file.isFile() && file.isExecutable()) + .map(file => [file.leafName, dir.path]) + }); }; }; diff --git a/common/modules/javascript.jsm b/common/modules/javascript.jsm index 77d6c2e1..387aed55 100644 --- a/common/modules/javascript.jsm +++ b/common/modules/javascript.jsm @@ -110,9 +110,10 @@ var JavaScript = Module("javascript", { if (isPrototypeOf.call(this.toplevel, obj) && !toplevel) return []; - let completions = [k for (k of this.iter(obj, toplevel))]; + let completions = Array.from(this.iter(obj, toplevel)); if (obj === this.modules) // Hack. - completions = Ary.uniq(completions.concat([k for (k of this.iter(this.modules.jsmodules, toplevel))])); + completions = [...new RealSet([...completions, + ...this.iter(this.modules.jsmodules, toplevel)])]; return completions; }, @@ -640,7 +641,9 @@ var JavaScript = Module("javascript", { * enumerable by any standard method. */ globalNames: Class.Memoize(function () { - return Ary.uniq([ + let interfaces = Object.keys(Ci); + + let names = new RealSet([ "Array", "ArrayBuffer", "AttributeName", "Audio", "Boolean", "Components", "CSSFontFaceStyleDecl", "CSSGroupRuleRuleList", "CSSNameSpaceRule", "CSSRGBColor", "CSSRect", "ComputedCSSStyleDeclaration", "Date", "Error", @@ -655,11 +658,14 @@ var JavaScript = Module("javascript", { "XMLList", "XMLSerializer", "XPCNativeWrapper", "XULControllers", "constructor", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "eval", "isFinite", "isNaN", - "isXMLName", "parseFloat", "parseInt", "undefined", "unescape", "uneval" - ].concat([k.substr(6) for (k of keys(Ci)) if (/^nsIDOM/.test(k))]) - .concat([k.substr(3) for (k of keys(Ci)) if (/^nsI/.test(k))]) - .concat(this.magicalNames) - .filter(k => k in this.window)); + "isXMLName", "parseFloat", "parseInt", "undefined", "unescape", "uneval", + + ...interfaces.filter(key => /^nsIDOM/.test(key)).map(key => k.substr(6)), + ...interfaces.filter(key => /^nsI/.test(key)).map(key => k.substr(3)), + ...this.magicalNames, + ]); + + return [...names].filter(key => key in this.window); }) }, { EVAL_TMP: "__dactyl_eval_tmp", diff --git a/common/modules/main.jsm b/common/modules/main.jsm index 8784dd85..2b6ae6ee 100644 --- a/common/modules/main.jsm +++ b/common/modules/main.jsm @@ -104,6 +104,8 @@ var Modules = function Modules(window) { wantXrays: true, metadata: { addonID: config.addon.id } }); + loadPolyfill(sandbox); + // Hack: // sandbox.Object = jsmodules.Object; sandbox.File = global.File; diff --git a/common/modules/messages.jsm b/common/modules/messages.jsm index 01bb4469..866d7e52 100644 --- a/common/modules/messages.jsm +++ b/common/modules/messages.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2014 Kris Maglione +// Copyright (c) 2011-2015 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -40,25 +40,24 @@ var Messages = Module("messages", { }, bundles: Class.Memoize(function () { - let urls = [ + let urls = new RealSet([ JSMLoader.getTarget("dactyl://locale/"), JSMLoader.getTarget("dactyl://locale-local/"), "resource://dactyl-locale/en-US/", "resource://dactyl-locale-local/en-US/" - ].map(url => url + this.name + ".properties"); + ].map(url => url + this.name + ".properties")); - return Ary.uniq(urls, true) - .map(services.stringBundle.createBundle) - .filter(bundle => { - try { - bundle.getSimpleEnumeration(); - return true; - } - catch (e) { - return false; - } - }); - }), + return Array.from(urls, services.stringBundle.createBundle) + .filter(bundle => { + try { + bundle.getSimpleEnumeration(); + return true; + } + catch (e) { + return false; + } + }); + }), iterate: function* () { let seen = new RealSet; @@ -111,12 +110,16 @@ var Messages = Module("messages", { file = io.File(file); function properties(base, iter_, prop="description") { - return iter(function* _properties() { + return Array.from(function* _properties() { function key(...args) { - return [base, obj.identifier || obj.name].concat(args).join(".").replace(/[\\:=]/g, "\\$&"); + return [base, + obj.identifier || obj.name, + ...args] + .join(".") + .replace(/[\\:=]/g, "\\$&"); } - for (var obj of iter_) { + for (let obj of iter_) { if (!obj.hive || obj.hive.name !== "user") { yield key(prop) + " = " + obj[prop]; @@ -135,20 +138,22 @@ var Messages = Module("messages", { yield key("deprecated") + " = " + obj.deprecated; } } - }()).toArray(); + }()); } - file.write( - Ary(commands.allHives.map(h => properties("command", h))) - .concat(modes.all.map(m => - properties("map", values(mappings.builtin.getStack(m) - .filter(map => map.modes[0] == m))))) - .concat(properties("mode", values(modes.all.filter(m => !m.hidden)))) - .concat(properties("option", options)) - .concat(properties("hintmode", values(hints.modes), "prompt")) - .concat(properties("pageinfo", values(Buffer.pageInfo), "title")) - .concat(properties("sanitizeitem", values(sanitizer.itemMap))) - .flatten().uniq().join("\n")); + let allMessages = [ + ...commands.allHives.map(h => properties("command", h)), + ...modes.all.flatMap(m => + properties("map", values(mappings.builtin.getStack(m) + .filter(map => map.modes[0] == m)))), + ...properties("mode", values(modes.all.filter(m => !m.hidden))), + ...properties("option", options), + ...properties("hintmode", values(hints.modes), "prompt"), + ...properties("pageinfo", values(Buffer.pageInfo), "title"), + ...properties("sanitizeitem", values(sanitizer.itemMap)), + ]; + + file.write(allMessages.join("\n")); } }, { Localized: Class("Localized", Class.Property, { diff --git a/common/modules/options.jsm b/common/modules/options.jsm index 63ddec28..359962d5 100644 --- a/common/modules/options.jsm +++ b/common/modules/options.jsm @@ -611,7 +611,7 @@ var Option = Class("Option", { list: function list(value, parse) { let prev = null; return Ary.compact(Option.splitList(value, true) - .map(function (v) { + .map(v => { let [count, filter, quote] = Commands.parseArg(v, /:/, true); let val = v.substr(count + 1); @@ -624,7 +624,7 @@ var Option = Class("Option", { util.assert(prev, _("error.syntaxError"), false); prev.result += "," + v; } - }, this)); + })); }, }, @@ -886,7 +886,7 @@ var Option = Class("Option", { update(BooleanOption.prototype, { names: Class.Memoize(function () { - return Ary.flatten([[name, "no" + name] for (name of this.realNames)]); + return this.realNames.flatMap(name => [name, "no" + name]); }) }); @@ -1504,7 +1504,7 @@ var Options = Module("options", { bang: true, completer: setCompleter, domains: function domains(args) { - return Ary.flatten(args.map(function (spec) { + return args.flatMap(spec => { try { let opt = modules.options.parseOpt(spec); if (opt.option && opt.option.domains) @@ -1514,17 +1514,17 @@ var Options = Module("options", { util.reportError(e); } return []; - })); + }); }, keepQuotes: true, - privateData: function privateData(args) { - return args.some(function (spec) { - let opt = modules.options.parseOpt(spec); - return opt.option && opt.option.privateData && - (!callable(opt.option.privateData) || - opt.option.privateData(opt.values)); - }); - } + privateData: args => args.some(spec => { + let opt = modules.options.parseOpt(spec); + + return (opt.option && + opt.option.privateData && + (!callable(opt.option.privateData) || + opt.option.privateData(opt.values))); + }), }, params.extra || {})); }); diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm index bd42c1c1..0a2ba60e 100644 --- a/common/modules/overlay.jsm +++ b/common/modules/overlay.jsm @@ -71,10 +71,10 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen let doc = target.ownerDocument || target.document || target; let listeners = this.getData(doc, "listeners"); - if (!isObject(event)) - var [self, events] = [null, Ary.toObject([[event, callback]])]; + if (isObject(event)) + var [self, events] = [event, event[callback || "events"]]; else - [self, events] = [event, event[callback || "events"]]; + [self, events] = [null, { [event]: callback }]; for (let [event, callback] of iter(events)) { let args = [Cu.getWeakReference(target), diff --git a/common/modules/polyfill.jsm b/common/modules/polyfill.jsm new file mode 100644 index 00000000..097b9262 --- /dev/null +++ b/common/modules/polyfill.jsm @@ -0,0 +1,70 @@ +// Copyright (c) 2015 Kris Maglione +// +// This work is licensed for reuse under an MIT license. Details are +// given in the LICENSE.txt file included with this file. +"use strict"; + +// Implementations for some ES6 and ES7 features that aren't in Firefox +// mainline yet. + +{ + // This is similar to the same-named function in self-hosted JS, but + // somewhat less efficient. + // + // It helps dodge some of the pitfalls of using the `call` method of the + // function you're intending to call, which might have been replaced, + // and can fail for other reasons (on CPOWs, most notably). + let callFunction = Function.call.bind(Function.call); + + let identity = x => x; + + if (!Object.entries) + Object.entries = function (obj) { + let result = []; + + for (let key of Object.keys(obj)) { + // The check is necessary, since keys may be removed during + // iteration. + if (key in obj) + result.push([key, obj[val]]); + } + + return result; + }; + + if (!Object.values) + Object.values = function (obj) { + let result = []; + + for (let key of Object.keys(obj)) { + // The check is necessary, since keys may be removed during + // iteration. + if (key in obj) + result.push(obj[val]); + } + + return result; + }; + + if (!Array.prototype.flatMap) + Array.prototype.flatMap = function (fn = identity, self = null) { + let result = []; + + for (let [i, value] of Array.prototype.entries.call(this)) { + let res = callFunction(fn, self, value, i); + + if (isObject(res) && Symbol.iterator in res) + result.push(...res); + else if (res !== undefined) + result.push(res); + } + + return result; + }; + + if (!Array.prototype.values) + Array.prototype.values = function* () { + for (let [i, value] of this.entries()) + yield value; + }; +} diff --git a/common/modules/styles.jsm b/common/modules/styles.jsm index aaba1097..e7ef5a20 100644 --- a/common/modules/styles.jsm +++ b/common/modules/styles.jsm @@ -125,9 +125,9 @@ var Hive = Class("Hive", { }, get sites() { - return Ary(this.sheets).map(s => s.sites) - .flatten() - .uniq().array; + let sites = this.sheets.flatMap(s => s.sites); + + return [...new RealSet(sites)]; }, /** @@ -557,6 +557,7 @@ var Styles = Module("Styles", { ' (?:[^\\']|\\.)* (?:'|$) ) `, "gx", this); + // " Vim. }, get token() { @@ -617,7 +618,11 @@ var Styles = Module("Styles", { let [filter, css] = args; if (!css) - styles.list(window.content, filter ? filter.split(",") : null, args["-name"], args.explicitOpts["-group"] ? [args["-group"]] : null); + styles.list(window.content, + filter ? filter.split(",") : null, + args["-name"], + args.explicitOpts["-group"] ? [args["-group"]] + : null); else { util.assert(args["-group"].modifiable && args["-group"].hive.modifiable, _("group.cantChangeBuiltin", _("style.styles"))); @@ -660,9 +665,9 @@ var Styles = Module("Styles", { { names: ["-nopersist", "-N"], description: "Do not save this style to an auto-generated RC file" } ], serialize: function () { - return Ary(styles.hives) + return styles.hives .filter(hive => hive.persist) - .map(hive => + .flatMap(hive => hive.sheets.filter(style => style.persist) .sort((a, b) => String.localeCompare(a.name || "", b.name || "")) @@ -674,8 +679,7 @@ var Styles = Module("Styles", { "-group": hive.name == "user" ? undefined : hive.name, "-name": style.name || undefined } - }))) - .flatten().array; + }))); } }); diff --git a/common/modules/util.jsm b/common/modules/util.jsm index 7f4bbda3..4099f220 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -1778,9 +1778,9 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * @returns {Generator(nsIDOMWindow)} */ iterFrames: function* iterFrames(win) { - yield win; - for (let i = 0; i < win.frames.length; i++) - yield* iterFrames(win.frames[i]); + yield win; + for (let i = 0; i < win.frames.length; i++) + yield* iterFrames(win.frames[i]); }, /**