]> git.donarmstrong.com Git - dactyl.git/commitdiff
Imported Upstream version 1.1+hg7904 upstream upstream/1.1+hg7904
authorDon Armstrong <don@debian.org>
Fri, 13 Jun 2014 19:55:01 +0000 (12:55 -0700)
committerDon Armstrong <don@debian.org>
Fri, 13 Jun 2014 19:55:01 +0000 (12:55 -0700)
62 files changed:
.hg_archival.txt
common/Makefile
common/bootstrap.js
common/chrome.manifest
common/content/abbreviations.js
common/content/autocommands.js
common/content/bookmarks.js
common/content/browser.js
common/content/commandline.js
common/content/dactyl.js
common/content/disable-acr.jsm [deleted file]
common/content/editor.js
common/content/events.js
common/content/hints.js
common/content/history.js
common/content/key-processors.js
common/content/mappings.js
common/content/marks.js
common/content/modes.js
common/content/mow.js
common/content/statusline.js
common/content/tabs.js
common/locale/en-US/messages.properties
common/locale/en-US/options.xml
common/locale/en-US/print.xml
common/locale/en-US/repeat.xml
common/modules/addons.jsm
common/modules/base.jsm
common/modules/bookmarkcache.jsm
common/modules/bootstrap.jsm
common/modules/buffer.jsm
common/modules/cache.jsm
common/modules/commands.jsm
common/modules/completion.jsm
common/modules/config.jsm
common/modules/contexts.jsm
common/modules/dom-e4x.jsm [deleted file]
common/modules/dom.jsm
common/modules/downloads.jsm
common/modules/finder.jsm
common/modules/help.jsm
common/modules/highlight.jsm
common/modules/io.jsm
common/modules/javascript.jsm
common/modules/main.jsm
common/modules/messages.jsm
common/modules/options.jsm
common/modules/overlay.jsm
common/modules/prefs.jsm
common/modules/promises.jsm [new file with mode: 0644]
common/modules/protocol.jsm
common/modules/sanitizer.jsm
common/modules/services.jsm
common/modules/storage.jsm
common/modules/styles.jsm
common/modules/template.jsm
common/modules/util.jsm
common/process_config.awk [new file with mode: 0644]
common/process_manifest.awk
common/skin/dactyl.css
pentadactyl/config.json
pentadactyl/install.rdf

index 3e94fb38298a78aa97f1bf13b604a45f64a221dd..870f741b3f4b58e3bdc1c4e838344e9a3a3681e8 100644 (file)
@@ -1,5 +1,5 @@
 repo: 373f1649c80dea9be7b5bc9c57e8395f94f93ab1
-node: a34a77b2caffe9b07ee32821a66342c9ec6e9e09
+node: 025b18cc985df652eb7ec1cb9d8920ba312de985
 branch: default
-latesttag: pentadactyl-1.0rc1
-latesttagdistance: 247
+latesttag: pentadactyl-1.1
+latesttagdistance: 22
index 0251c4ab3ce210e14c1b17c5665c456052a80f88..d0985841968bb35436a072ef7631f61f102cc697 100644 (file)
@@ -1,11 +1,17 @@
 #### configuration
 
 
+_SH       := $(shell if which dash >/dev/null 2>&1; \
+                    then echo dash; \
+                    else echo sh;   \
+                    fi)
+SH        ?= $(_SH)
 AWK       ?= awk
 B64ENCODE ?= base64
 CURL      ?= curl
 SED       := $(shell if [ "xoo" = x$$(echo foo | sed -E 's/f(o)/\1/' 2>/dev/null) ]; \
-                    then echo sed -E; else echo sed -r;                             \
+                    then echo sed -E; \
+                    else echo sed -r; \
                     fi)
 
 TOP           = $(shell pwd)
@@ -28,7 +34,7 @@ LOCALEDIR     = locale
 DOC_FILES     = $(wildcard $(LOCALEDIR)/*/*.xml)
 
 export VERSION BUILD_DATE
-MAKE_JAR      = sh $(BASE)/make_jar.sh
+MAKE_JAR      = $(SH) $(BASE)/make_jar.sh
 
 # TODO: specify source files manually?
 JAR_BASES     = $(TOP) $(BASE)
@@ -90,26 +96,6 @@ info:
 
 jar: $(JAR)
 
-# This is not for you!
-dist: $(XPI)
-       @echo DIST $(XPI) $(GOOGLE)
-       set -e;                                                                 \
-                                                                               \
-       proj=$$(echo -n $(NAME) | sed 's/\(.\).*/\1/' | tr a-z A-Z);            \
-       proj="$$proj$$(echo $(NAME) | sed 's/.//')";                            \
-       [ -z "$$summary" ] && summary="$$proj $(VERSION) Release";              \
-       labels="Project-$$proj,$(labels)";                                      \
-       [ -n "$(featured)" ] && labels="$$labels,Featured";                     \
-                                                                               \
-       IFS=,; for l in $$labels; do                                            \
-               set -- "$$@" --form-string "label=$$l";                         \
-       done;                                                                   \
-       auth=$$(echo -n "$(GOOGLE_USER):$(GOOGLE_PASS)" | $(B64ENCODE));        \
-       $(CURL) "$$@" --form-string "summary=$$summary"                         \
-               -F "filename=@$(XPI)"                                           \
-               -H "Authorization: Basic $$auth"                                \
-               -i "$(GOOGLE)" | sed -n '/^Location/{p;q;}'
-
 install:
        export dir;                                                             \
        for dir in $(PROFILEPATHS); do                                          \
@@ -177,6 +163,10 @@ xpi: $(CHROME)
               -f $(BASE)/process_manifest.awk \
               "$(TOP)/chrome.manifest" >"$(XPI_PATH)/chrome.manifest"
 
+       $(AWK) -v 'name=$(NAME)' -v 'suffix=$(MANGLE)' \
+              -f $(BASE)/process_config.awk \
+              "$(TOP)/config.json" >"$(XPI_PATH)/config.json"
+
        version="$(VERSION)";                   \
        hg root >/dev/null 2>&1 &&              \
        case "$$version" in                     \
index 4d5dcca634a9e863b5afeebe2ab5fb48e325b6e5..a5b25deb4d640ea7d1432f9993385e81caf76530 100755 (executable)
@@ -1,4 +1,4 @@
-// Copyright (c) 2010-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2010-2014 by Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -21,6 +21,8 @@ var { AddonManager } = module("resource://gre/modules/AddonManager.jsm");
 var { XPCOMUtils }   = module("resource://gre/modules/XPCOMUtils.jsm");
 var { Services }     = module("resource://gre/modules/Services.jsm");
 
+var Timer = Components.Constructor("@mozilla.org/timer;1", "nsITimer", "initWithCallback");
+
 const resourceProto = Services.io.getProtocolHandler("resource")
                               .QueryInterface(Ci.nsIResProtocolHandler);
 const categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
@@ -57,13 +59,11 @@ let addonData = null;
 let basePath = null;
 let bootstrap;
 let bootstrap_jsm;
-let categories = [];
 let components = {};
-let resources = [];
 let getURI = null;
 
 let JSMLoader = {
-    SANDBOX: Cu.nukeSandbox && false,
+    SANDBOX: Cu.nukeSandbox,
 
     get addon() addon,
 
@@ -134,7 +134,7 @@ let JSMLoader = {
                     return this.modules[name] = this.globals[uri];
 
                 this.globals[uri] = this.modules[name];
-                bootstrap_jsm.loadSubScript(url, this.modules[name]);
+                bootstrap_jsm.loadSubScript(url, this.modules[name], "UTF-8");
                 return;
             }
             catch (e) {
@@ -172,7 +172,17 @@ let JSMLoader = {
         let module = this.modules[name];
         if (target)
             for each (let symbol in module.EXPORTED_SYMBOLS)
-                target[symbol] = module[symbol];
+                try {
+                    Object.defineProperty(target, symbol, {
+                        configurable: true,
+                        enumerable: true,
+                        writable: true,
+                        value: module[symbol]
+                    });
+                }
+                catch (e) {
+                    target[symbol] = module[symbol];
+                }
 
         return module;
     },
@@ -180,8 +190,8 @@ let JSMLoader = {
     // Cuts down on stupid, fscking url mangling.
     get loadSubScript() bootstrap_jsm.loadSubScript,
 
-    cleanup: function unregister() {
-        for each (let factory in this.factories.splice(0))
+    cleanup: function cleanup() {
+        for (let factory of this.factories.splice(0))
             manager.unregisterFactory(factory.classID, factory);
     },
 
@@ -215,35 +225,29 @@ let JSMLoader = {
 function init() {
     debug("bootstrap: init");
 
-    let manifestURI = getURI("chrome.manifest");
-    let manifest = httpGet(manifestURI)
-            .responseText
-            .replace(/#(resource)#/g, "$1")
-            .replace(/^\s*|\s*$|#.*/g, "")
-            .replace(/^\s*\n/gm, "");
-
-    for each (let line in manifest.split("\n")) {
-        let fields = line.split(/\s+/);
-        switch (fields[0]) {
-        case "category":
-            categoryManager.addCategoryEntry(fields[1], fields[2], fields[3], false, true);
-            categories.push([fields[1], fields[2]]);
-            break;
-        case "component":
-            components[fields[1]] = new FactoryProxy(getURI(fields[2]).spec, fields[1]);
-            break;
-        case "contract":
-            components[fields[2]].contractID = fields[1];
-            break;
-
-        case "resource":
-            moduleName = moduleName || fields[1];
-            resources.push(fields[1]);
-            resourceProto.setSubstitution(fields[1], getURI(fields[2]));
-        }
+    let manifest = JSON.parse(httpGet(getURI("config.json"))
+                                .responseText);
+
+    if (!manifest.categories)
+        manifest.categories = [];
+
+    for (let [classID, { contract, path, categories }] of Iterator(manifest.components || {})) {
+        components[classID] = new FactoryProxy(getURI(path).spec, classID, contract);
+        if (categories)
+            for (let [category, id] in Iterator(categories))
+                manifest.categories.push([category, id, contract]);
+    }
+
+    for (let [category, id, value] of manifest.categories)
+        categoryManager.addCategoryEntry(category, id, value,
+                                         false, true);
+
+    for (let [pkg, path] in Iterator(manifest.resources || {})) {
+        moduleName = moduleName || pkg;
+        resourceProto.setSubstitution(pkg, getURI(path));
     }
 
-    JSMLoader.config = JSON.parse(httpGet("resource://dactyl-local/config.json").responseText);
+    JSMLoader.config = manifest;
 
     bootstrap_jsm = module(BOOTSTRAP);
     if (!JSMLoader.SANDBOX)
@@ -255,22 +259,13 @@ function init() {
     }
     bootstrap.require = JSMLoader.load("base").require;
 
-    // Flush the cache if necessary, just to be paranoid
     let pref = "extensions.dactyl.cacheFlushCheck";
     let val  = addon.version;
     if (!Services.prefs.prefHasUserValue(pref) || Services.prefs.getCharPref(pref) != val) {
         var cacheFlush = true;
-        Services.obs.notifyObservers(null, "startupcache-invalidate", "");
         Services.prefs.setCharPref(pref, val);
     }
 
-    try {
-        //JSMLoader.load("disable-acr").init(addon.id);
-    }
-    catch (e) {
-        reportError(e);
-    }
-
     Services.obs.notifyObservers(null, "dactyl-rehash", null);
 
     JSMLoader.bootstrap = global;
@@ -326,7 +321,9 @@ function updateVersion() {
         // Disable automatic updates when switching to nightlies,
         // restore the default action when switching to stable.
         if (!config.lastVersion || isDev(config.lastVersion) != isDev(addon.version))
-            addon.applyBackgroundUpdates = AddonManager[isDev(addon.version) ? "AUTOUPDATE_DISABLE" : "AUTOUPDATE_DEFAULT"];
+            addon.applyBackgroundUpdates =
+                AddonManager[isDev(addon.version) ? "AUTOUPDATE_DISABLE"
+                                                  : "AUTOUPDATE_DEFAULT"];
     }
     catch (e) {
         reportError(e);
@@ -381,9 +378,10 @@ function startup(data, reason) {
  * @param {string} url The URL of the module housing the real factory.
  * @param {string} classID The CID of the class this factory represents.
  */
-function FactoryProxy(url, classID) {
+function FactoryProxy(url, classID, contractID) {
     this.url = url;
     this.classID = Components.ID(classID);
+    this.contractID = contractID;
 }
 FactoryProxy.prototype = {
     QueryInterface: XPCOMUtils.generateQI(Ci.nsIFactory),
@@ -405,18 +403,12 @@ FactoryProxy.prototype = {
     }
 }
 
+var timer;
 function shutdown(data, reason) {
     let strReason = reasonToString(reason);
     debug("bootstrap: shutdown " + strReason);
 
     if (reason != APP_SHUTDOWN) {
-        try {
-            //JSMLoader.load("disable-acr").cleanup(addon.id);
-        }
-        catch (e) {
-            reportError(e);
-        }
-
         if (~[ADDON_UPGRADE, ADDON_DOWNGRADE, ADDON_UNINSTALL].indexOf(reason))
             Services.obs.notifyObservers(null, "dactyl-purge", null);
 
@@ -426,17 +418,20 @@ function shutdown(data, reason) {
         JSMLoader.atexit(strReason);
         JSMLoader.cleanup(strReason);
 
-        if (JSMLoader.SANDBOX)
-            Cu.nukeSandbox(bootstrap);
-        bootstrap_jsm.require = null;
-        Cu.unload(BOOTSTRAP);
-        bootstrap = null;
-        bootstrap_jsm = null;
-
-        for each (let [category, entry] in categories)
+        for each (let [category, entry] in JSMLoader.config.categories)
             categoryManager.deleteCategoryEntry(category, entry, false);
-        for each (let resource in resources)
+        for (let resource in JSMLoader.config.resources)
             resourceProto.setSubstitution(resource, null);
+
+        timer = Timer(() => {
+            bootstrap_jsm.require = null;
+            if (JSMLoader.SANDBOX)
+                Cu.nukeSandbox(bootstrap);
+            else
+                Cu.unload(BOOTSTRAP);
+            bootstrap = null;
+            bootstrap_jsm = null;
+        }, 5000, Ci.nsITimer.TYPE_ONE_SHOT);
     }
 }
 
index fbf9096256ba21ed66b6216319c10de8431520b6..b24abc9bc172497c996756046986772fcaaf974e 100644 (file)
@@ -1,17 +1 @@
-resource dactyl             ../common/modules/
-resource dactyl-common      ../common/
-resource dactyl-content     ../common/content/
-resource dactyl-skin        ../common/skin/
-resource dactyl-locale      ../common/locale/
-
-resource dactyl-local         ./
-resource dactyl-local-content content/
-resource dactyl-local-skin    skin/
-resource dactyl-local-locale  locale/
-
 content  dactyl  ../common/content/
-
-component {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69} components/commandline-handler.js
-contract  @mozilla.org/commandlinehandler/general-startup;1?type=dactyl {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69}
-category  command-line-handler m-dactyl @mozilla.org/commandlinehandler/general-startup;1?type=dactyl
-
index f0cd06d17747011ec4441a5505354031505086cc..7ceb5e9afe57e8a994e96982f8c48610085a6552 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2010 by anekos <anekos@snca.net>
-// Copyright (c) 2010-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2010-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -105,7 +105,7 @@ var AbbrevHive = Class("AbbrevHive", Contexts.Hive, {
     },
 
     /** @property {boolean} True if there are no abbreviations. */
-    get empty() !values(this._store).nth(util.identity, 0),
+    get empty() !values(this._store).find(util.identity),
 
     /**
      * Adds a new abbreviation.
@@ -132,7 +132,8 @@ var AbbrevHive = Class("AbbrevHive", Contexts.Hive, {
      */
     get: function (mode, lhs) {
         let abbrevs = this._store[mode];
-        return abbrevs && Set.has(abbrevs, lhs) ? abbrevs[lhs] : null;
+        return abbrevs && hasOwnProperty(abbrevs, lhs) ? abbrevs[lhs]
+                                                       : null;
     },
 
     /**
@@ -232,10 +233,10 @@ var Abbreviations = Module("abbreviations", {
 
     get userHives() this.allHives.filter(h => h !== this.builtin),
 
-    get: deprecated("group.abbrevs.get", { get: function get() this.user.closure.get }),
-    set: deprecated("group.abbrevs.set", { get: function set() this.user.closure.set }),
-    remove: deprecated("group.abbrevs.remove", { get: function remove() this.user.closure.remove }),
-    removeAll: deprecated("group.abbrevs.clear", { get: function removeAll() this.user.closure.clear }),
+    get: deprecated("group.abbrevs.get", { get: function get() this.user.bound.get }),
+    set: deprecated("group.abbrevs.set", { get: function set() this.user.bound.set }),
+    remove: deprecated("group.abbrevs.remove", { get: function remove() this.user.bound.remove }),
+    removeAll: deprecated("group.abbrevs.clear", { get: function removeAll() this.user.bound.clear }),
 
     /**
      * Returns the abbreviation for the given *mode* if *text* matches the
@@ -250,7 +251,7 @@ var Abbreviations = Module("abbreviations", {
         let match = this._match.exec(text);
         if (match)
             return this.hives.map(h => h.get(mode, match[2] || match[4] || match[6]))
-                       .nth(util.identity, 0);
+                       .find(util.identity);
         return null;
     },
 
@@ -266,7 +267,7 @@ var Abbreviations = Module("abbreviations", {
         let hives = (hives || this.userHives).filter(h => !h.empty);
 
         function abbrevs(hive)
-            hive.merged.filter(ab => (ab.inModes(modes) && ab.lhs.indexOf(lhs) == 0));
+            hive.merged.filter(ab => (ab.inModes(modes) && ab.lhs.startsWith(lhs)));
 
         let list = ["table", {},
                 ["tr", { highlight: "Title" },
index 582337ce8bf3a63777b0d6fbdc0aab8cb4731479..7bb23472efb0e28a5b5b86420d8ef0f1edffd08d 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -71,13 +71,15 @@ var AutoCmdHive = Class("AutoCmdHive", Contexts.Hive, {
  */
 var AutoCommands = Module("autocommands", {
     init: function () {
+        if (!config.haveGecko("26"))
+            delete config.autocommands.DownloadPost; // FIXME
     },
 
     get activeHives() contexts.allGroups.autocmd.filter(h => h._store.length),
 
-    add: deprecated("group.autocmd.add", { get: function add() autocommands.user.closure.add }),
-    get: deprecated("group.autocmd.get", { get: function get() autocommands.user.closure.get }),
-    remove: deprecated("group.autocmd.remove", { get: function remove() autocommands.user.closure.remove }),
+    add: deprecated("group.autocmd.add", { get: function add() autocommands.user.bound.add }),
+    get: deprecated("group.autocmd.get", { get: function get() autocommands.user.bound.get }),
+    remove: deprecated("group.autocmd.remove", { get: function remove() autocommands.user.bound.remove }),
 
     /**
      * Lists all autocommands with a matching *event*, *regexp* and optionally
index 09f088c54fef59759a0c14206954ec51c9d4adca..dd64e545cc8e0a763f5c3f5079f4b77d6692a1f5 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -73,7 +73,7 @@ var Bookmarks = Module("bookmarks", {
         if (id != null)
             var bmark = bookmarkcache.bookmarks[id];
         else if (!force) {
-            if (keyword && Set.has(bookmarkcache.keywords, keyword))
+            if (keyword && hasOwnProperty(bookmarkcache.keywords, keyword))
                 bmark = bookmarkcache.keywords[keyword];
             else if (bookmarkcache.isBookmarked(uri))
                 for (bmark in bookmarkcache)
@@ -172,7 +172,7 @@ var Bookmarks = Module("bookmarks", {
         }
     },
 
-    isBookmarked: deprecated("bookmarkcache.isBookmarked", { get: function isBookmarked() bookmarkcache.closure.isBookmarked }),
+    isBookmarked: deprecated("bookmarkcache.isBookmarked", { get: function isBookmarked() bookmarkcache.bound.isBookmarked }),
 
     /**
      * Remove a bookmark or bookmarks. If *ids* is an array, removes the
@@ -189,7 +189,7 @@ var Bookmarks = Module("bookmarks", {
                 let uri = util.newURI(ids);
                 ids = services.bookmarks
                               .getBookmarkIdsForURI(uri, {})
-                              .filter(bookmarkcache.closure.isRegularBookmark);
+                              .filter(bookmarkcache.bound.isRegularBookmark);
             }
             ids.forEach(function (id) {
                 let bmark = bookmarkcache.bookmarks[id];
@@ -223,7 +223,7 @@ var Bookmarks = Module("bookmarks", {
             if (!alias)
                 alias = "search"; // for search engines which we can't find a suitable alias
 
-            if (Set.has(aliases, alias))
+            if (hasOwnProperty(aliases, alias))
                 alias += ++aliases[alias];
             else
                 aliases[alias] = 0;
@@ -232,6 +232,31 @@ var Bookmarks = Module("bookmarks", {
         }).toObject();
     },
 
+    /**
+     * Returns true if the given search engine provides suggestions.
+     * engine based on the given *query*. The results are always in the
+     * form of an array of strings. If *callback* is provided, the
+     * request is executed asynchronously and *callback* is called on
+     * completion. Otherwise, the request is executed synchronously and
+     * the results are returned.
+     *
+     * @param {string} engineName The name of the search engine from
+     *      which to request suggestions.
+     * @returns {boolean}
+     */
+    hasSuggestions: function hasSuggestions(engineName, query, callback) {
+        const responseType = "application/x-suggestions+json";
+
+        if (hasOwnProperty(this.suggestionProviders, engineName))
+            return true;
+
+        let engine = hasOwnProperty(this.searchEngines, engineName) && this.searchEngines[engineName];
+        if (engine && engine.supportsResponseType(responseType))
+            return true;
+
+        return false;
+    },
+
     /**
      * Retrieves a list of search suggestions from the named search
      * engine based on the given *query*. The results are always in the
@@ -251,14 +276,15 @@ var Bookmarks = Module("bookmarks", {
     getSuggestions: function getSuggestions(engineName, query, callback) {
         const responseType = "application/x-suggestions+json";
 
-        if (Set.has(this.suggestionProviders, engineName))
+        if (hasOwnProperty(this.suggestionProviders, engineName))
             return this.suggestionProviders[engineName](query, callback);
 
-        let engine = Set.has(this.searchEngines, engineName) && this.searchEngines[engineName];
+        let engine = hasOwnProperty(this.searchEngines, engineName) && this.searchEngines[engineName];
         if (engine && engine.supportsResponseType(responseType))
             var queryURI = engine.getSubmission(query, responseType).uri.spec;
+
         if (!queryURI)
-            return (callback || util.identity)([]);
+            return promises.fail();
 
         function parse(req) JSON.parse(req.responseText)[1].filter(isString);
         return this.makeSuggestions(queryURI, parse, callback);
@@ -271,25 +297,25 @@ var Bookmarks = Module("bookmarks", {
      * @param {string} url The URL to fetch.
      * @param {function(XMLHttpRequest):[string]} parser The function which
      *      parses the response.
+     * @returns {Promise<Array>}
      */
-    makeSuggestions: function makeSuggestions(url, parser, callback) {
-        function process(req) {
+    makeSuggestions: function makeSuggestions(url, parser) {
+        let deferred = Promise.defer();
+
+        let req = util.fetchUrl(url);
+        req.then(function process(req) {
             let results = [];
             try {
                 results = parser(req);
             }
             catch (e) {
-                util.reportError(e);
+                return deferred.reject(e);
             }
-            if (callback)
-                return callback(results);
-            return results;
-        }
+            deferred.resolve(results);
+        }, Cu.reportError);
 
-        let req = util.httpGet(url, callback && process);
-        if (callback)
-            return req;
-        return process(req);
+        promises.oncancel(deferred, r => promises.cancel(req, reason));
+        return deferred.promise;
     },
 
     suggestionProviders: {},
@@ -320,7 +346,7 @@ var Bookmarks = Module("bookmarks", {
             param = query.substr(offset + 1);
         }
 
-        var engine = Set.has(bookmarks.searchEngines, keyword) && bookmarks.searchEngines[keyword];
+        var engine = hasOwnProperty(bookmarks.searchEngines, keyword) && bookmarks.searchEngines[keyword];
         if (engine) {
             if (engine.searchForm && !param)
                 return engine.searchForm;
@@ -536,7 +562,7 @@ var Bookmarks = Module("bookmarks", {
             "Delete a bookmark",
             function (args) {
                 if (args.bang)
-                    commandline.input(_("bookmark.prompt.deleteAll") + " ",
+                    commandline.input(_("bookmark.prompt.deleteAll") + " ").then(
                         function (resp) {
                             if (resp && resp.match(/^y(es)?$/i)) {
                                 bookmarks.remove(Object.keys(bookmarkcache.bookmarks));
@@ -628,7 +654,7 @@ var Bookmarks = Module("bookmarks", {
     },
 
     completion: function initCompletion() {
-        completion.bookmark = function bookmark(context, tags, extra = {}) {
+        completion.bookmark = function bookmark(context, tags, extra={}) {
             context.title = ["Bookmark", "Title"];
             context.format = bookmarks.format;
             iter(extra).forEach(function ([k, v]) {
@@ -655,7 +681,7 @@ var Bookmarks = Module("bookmarks", {
                          keyword, true);
 
             let item = keywords[keyword];
-            if (item && item.url.indexOf("%s") > -1)
+            if (item && item.url.contains("%s"))
                 context.fork("keyword/" + keyword, keyword.length + space.length, null, function (context) {
                     context.format = history.format;
                     context.title = [/*L*/keyword + " Quick Search"];
@@ -668,7 +694,7 @@ var Bookmarks = Module("bookmarks", {
                         return history.get({ uri: util.newURI(begin), uriIsPrefix: true }).map(function (item) {
                             let rest = item.url.length - end.length;
                             let query = item.url.substring(begin.length, rest);
-                            if (item.url.substr(rest) == end && query.indexOf("&") == -1)
+                            if (item.url.substr(rest) == end && query.contains("&"))
                                 try {
                                     item.url = decodeURIComponent(query.replace(/#.*/, "").replace(/\+/g, " "));
                                     return item;
@@ -696,16 +722,19 @@ var Bookmarks = Module("bookmarks", {
             let engineList = (engineAliases || options["suggestengines"].join(",") || "google").split(",");
 
             engineList.forEach(function (name) {
+                if (!bookmarks.hasSuggestions(name))
+                    return;
+
                 var desc = name;
                 let engine = bookmarks.searchEngines[name];
                 if (engine)
-                    var desc = engine.description;
-                else if (!Set.has(bookmarks.suggestionProviders, name))
-                    return;
+                    desc = engine.description;
+
 
                 let [, word] = /^\s*(\S+)/.exec(context.filter) || [];
                 if (!kludge && word == name) // FIXME: Check for matching keywords
                     return;
+
                 let ctxt = context.fork(name, 0);
 
                 ctxt.title = [/*L*/desc + " Suggestions"];
@@ -717,14 +746,20 @@ var Bookmarks = Module("bookmarks", {
                     return;
 
                 let words = ctxt.filter.toLowerCase().split(/\s+/g);
-                ctxt.completions = ctxt.completions.filter(i => words.every(w => i.toLowerCase().indexOf(w) >= 0));
+                ctxt.completions = ctxt.completions.filter(i => words.every(w => i.toLowerCase().contains(w)));
 
                 ctxt.hasItems = ctxt.completions.length;
                 ctxt.incomplete = true;
-                ctxt.cache.request = bookmarks.getSuggestions(name, ctxt.filter, function (compl) {
+                ctxt.cache.request = bookmarks.getSuggestions(name, ctxt.filter);
+                ctxt.cache.request.then(function (compl) {
                     ctxt.incomplete = false;
-                    ctxt.completions = array.uniq(ctxt.completions.filter(c => compl.indexOf(c) >= 0)
+                    ctxt.completions = array.uniq(ctxt.completions.filter(c => compl.contains(c))
                                                       .concat(compl), true);
+                }, function (e) {
+                    ctxt.incomplete = false;
+                    ctxt.completions = [];
+                    if (e)
+                        Cu.reportError(e);
                 });
             });
         };
index 95c1fd4a8e9586a3dbe3c49399496c0864d460f0..73736298120117cba328afac2553641a8227d166 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -16,11 +16,17 @@ var Browser = Module("browser", XPCOM(Ci.nsISupportsWeakReference, ModuleBase),
         this.cleanupProgressListener = overlay.overlayObject(window.XULBrowserWindow,
                                                              this.progressListener);
         util.addObserver(this);
+
+        this._unoverlay = overlay.overlayObject(FullZoom, {
+            get siteSpecific() false,
+            set siteSpecific(val) {}
+        });
     },
 
     destroy: function () {
         this.cleanupProgressListener();
         this.observe.unregister();
+        this._unoverlay();
     },
 
     observers: {
index de8d9c3c79d6e9607c12dfa9a7167df11322e0a3..9e66e533247f20e2024beafbb20444db92c92818 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -263,17 +263,7 @@ var CommandWidgets = Class("CommandWidgets", {
         }
         [this.commandbar.container, this.statusbar.container].forEach(check);
 
-        // Work around a redrawing bug.
-        if (changed && config.haveGecko("16", "20")) {
-            util.delay(function () {
-                // Urgh.
-                statusline.statusBar.style.paddingRight = "1px";
-                DOM(statusline.statusBar).rect; // Force reflow.
-                statusline.statusBar.style.paddingRight = "";
-            }, 0);
-        }
-
-        if (this.initialized && loaded.mow && mow.visible)
+        if (this.initialized && loaded.has("mow") && mow.visible)
             mow.resize(false);
     },
 
@@ -351,7 +341,7 @@ var CommandMode = Class("CommandMode", {
                       false);
 
         this.messageCount = commandline.messageCount;
-        modes.push(this.mode, this.extendedMode, this.closure);
+        modes.push(this.mode, this.extendedMode, this.bound);
 
         this.widgets.active.commandline.collapsed = false;
         this.widgets.prompt = this.prompt;
@@ -694,7 +684,7 @@ var CommandLine = Module("commandline", {
      * Displays the multi-line output of a command, preceded by the last
      * executed ex command string.
      *
-     * @param {XML} xml The output as an E4X XML object.
+     * @param {object} xml The output as a JSON XML object.
      */
     commandOutput: function commandOutput(xml) {
         if (!this.command)
@@ -801,7 +791,7 @@ var CommandLine = Module("commandline", {
         let action = this._echoLine;
 
         if ((flags & this.FORCE_MULTILINE) || (/\n/.test(data) || !isinstance(data, [_, "String"])) && !(flags & this.FORCE_SINGLELINE))
-            action = mow.closure.echo;
+            action = mow.bound.echo;
 
         let checkSingleLine = () => action == this._echoLine;
 
@@ -813,7 +803,7 @@ var CommandLine = Module("commandline", {
             // So complicated...
             if (checkSingleLine() && !this.widgets.mowContainer.collapsed) {
                 highlightGroup += " Message";
-                action = mow.closure.echo;
+                action = mow.bound.echo;
             }
             else if (!checkSingleLine() && this.widgets.mowContainer.collapsed) {
                 if (this._lastEcho && this.widgets.message && this.widgets.message[1] == this._lastEcho.msg) {
@@ -855,7 +845,6 @@ var CommandLine = Module("commandline", {
      * pop at any time to close the prompt.
      *
      * @param {string} prompt The input prompt to use.
-     * @param {function(string)} callback
      * @param {Object} extra
      * @... {function} onChange - A function to be called with the current
      *     input every time it changes.
@@ -866,15 +855,16 @@ var CommandLine = Module("commandline", {
      * @... {string} default - The initial value that will be returned
      *     if the user presses <CR> straightaway. @default ""
      */
-    input: function _input(prompt, callback, extra = {}) {
-        CommandPromptMode(prompt, update({ onSubmit: callback }, extra)).open();
-    },
+    input: promises.withCallbacks(function _input([callback, reject], prompt, extra={}, thing={}) {
+        if (callable(extra))
+            // Deprecated.
+            [callback, extra] = [extra, thing];
+
+        CommandPromptMode(prompt, update({ onSubmit: callback, onCancel: reject }, extra)).open();
+    }),
 
     readHeredoc: function readHeredoc(end) {
-        let args;
-        commandline.inputMultiline(end, function (res) { args = res; });
-        util.waitFor(() => args !== undefined);
-        return args;
+        return util.waitFor(commandline.inputMultiline(end));
     },
 
     /**
@@ -883,10 +873,10 @@ var CommandLine = Module("commandline", {
      * callback with that string as a parameter.
      *
      * @param {string} end
-     * @param {function(string)} callback
+     * @returns {Promise<string>}
      */
     // FIXME: Buggy, especially when pasting.
-    inputMultiline: function inputMultiline(end, callback) {
+    inputMultiline: promises.withCallbacks(function inputMultiline([callback], end) {
         let cmd = this.command;
         let self = {
             end: "\n" + end + "\n",
@@ -912,7 +902,7 @@ var CommandLine = Module("commandline", {
         this._autosizeMultilineInputWidget();
 
         this.timeout(function () { dactyl.focus(this.widgets.multilineInput); }, 10);
-    },
+    }),
 
     get commandMode() this.commandSession && isinstance(modes.main, modes.COMMAND_LINE),
 
@@ -1010,11 +1000,10 @@ var CommandLine = Module("commandline", {
             if (privateData == "never-save")
                 return;
 
-            this.store = this.store.filter(line => (line.value || line) != str);
-            dactyl.trapErrors(function () {
-                this.store.push({ value: str, timestamp: Date.now() * 1000, privateData: privateData });
-            }, this);
-            this.store = this.store.slice(Math.max(0, this.store.length - options["history"]));
+            let store = Array.filter(this.store, line => (line.value || line) != str);
+            dactyl.trapErrors(
+                () => store.push({ value: str, timestamp: Date.now() * 1000, privateData: privateData }));
+            this.store = store.slice(Math.max(0, store.length - options["history"]));
         },
         /**
          * @property {function} Returns whether a data item should be
@@ -1124,7 +1113,7 @@ var CommandLine = Module("commandline", {
             this.itemList = commandline.completionList;
             this.itemList.open(this.context);
 
-            dactyl.registerObserver("events.doneFeeding", this.closure.onDoneFeeding, true);
+            dactyl.registerObserver("events.doneFeeding", this.bound.onDoneFeeding, true);
 
             this.autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) {
                 if (events.feedingKeys && !tabPressed)
@@ -1257,7 +1246,7 @@ var CommandLine = Module("commandline", {
          * called.
          */
         cleanup: function cleanup() {
-            dactyl.unregisterObserver("events.doneFeeding", this.closure.onDoneFeeding);
+            dactyl.unregisterObserver("events.doneFeeding", this.bound.onDoneFeeding);
             this.previewClear();
 
             this.tabTimer.reset();
@@ -1394,7 +1383,7 @@ var CommandLine = Module("commandline", {
          *      @default {@link #selected}
          * @returns {object}
          */
-        getItem: function getItem(tuple = this.selected)
+        getItem: function getItem(tuple=this.selected)
             tuple && tuple[0] && tuple[0].items[tuple[1]],
 
         /**
@@ -1509,7 +1498,7 @@ var CommandLine = Module("commandline", {
          *      @default false
          *      @private
          */
-        select: function select(idx, count = 1, fromTab = false) {
+        select: function select(idx, count=1, fromTab=false) {
             switch (idx) {
             case this.UP:
             case this.DOWN:
@@ -1956,7 +1945,7 @@ var ItemList = Class("ItemList", {
                 this.resize(flags);
         }, this);
 
-        DOM(this.win).resize(this._onResize.closure.tell);
+        DOM(this.win).resize(this._onResize.bound.tell);
     },
 
     get rootXML()
@@ -2029,7 +2018,7 @@ var ItemList = Class("ItemList", {
         if (start < 0 || start >= this.itemCount)
             return null;
 
-        group = array.nth(groups, g => let (i = start - g.offsets.start) i >= 0 && i < g.itemCount, 0);
+        group = groups.find(g => let (i = start - g.offsets.start) i >= 0 && i < g.itemCount);
         return [group.context, start - group.offsets.start];
     },
 
@@ -2146,7 +2135,8 @@ var ItemList = Class("ItemList", {
 
         // We need to collect all of the rescrolling functions in
         // one go, as the height calculation that they need to do
-        // would force a reflow after each DOM modification.
+        // would force an expensive reflow after each call due to
+        // DOM modifications, otherwise.
         this.activeGroups.filter(g => !g.collapsed)
             .map(g => g.rescrollFunc)
             .forEach(call);
@@ -2280,7 +2270,7 @@ var ItemList = Class("ItemList", {
     getGroup: function getGroup(context)
         context instanceof ItemList.Group ? context
                                           : context && context.getCache("itemlist-group",
-                                                                        bind("Group", ItemList, this, context)),
+                                                                        () => ItemList.Group(this, context)),
 
     getOffset: function getOffset(tuple) tuple && this.getGroup(tuple[0]).getOffset(tuple[1])
 }, {
@@ -2407,8 +2397,8 @@ var ItemList = Class("ItemList", {
                         first = row;
 
                 let container = DOM(this.nodes.items);
-                let before    = first ? DOM(first).closure.before
-                                      : DOM(this.nodes.items).closure.append;
+                let before    = first ? DOM(first).bound.before
+                                      : DOM(this.nodes.items).bound.append;
 
                 for (let [i, row] in this.context.getRows(range.start, range.end,
                                                           this.doc)) {
index 6ad9e5534ca899160a439e8b5014974c20d58f20..752b17ddd1516af998dcf30231e4c083f741bfcf 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -136,14 +136,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                     && !item.hidden
                     && !/rdf:http:/.test(item.getAttribute("label"))) { // FIXME
                     item.dactylPath = parent + item.getAttribute("label");
-                    if (!targetPath || targetPath.indexOf(item.dactylPath) == 0)
+                    if (!targetPath || targetPath.startsWith(item.dactylPath))
                         items.push(item);
                 }
                 else {
                     let path = parent;
                     if (item.localName == "menu")
                         path += item.getAttribute("label") + ".";
-                    if (!targetPath || targetPath.indexOf(path) == 0)
+                    if (!targetPath || targetPath.startsWith(path))
                         addChildren(item, path);
                 }
             }
@@ -206,7 +206,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
 
     registerObservers: function registerObservers(obj, prop) {
         for (let [signal, func] in Iterator(obj[prop || "signals"]))
-            this.registerObserver(signal, obj.closure(func), false);
+            this.registerObserver(signal, func.bind(obj), false);
     },
 
     unregisterObserver: function unregisterObserver(type, callback) {
@@ -216,20 +216,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
 
     applyTriggerObserver: function triggerObserver(type, args) {
         if (type in this._observers)
-            this._observers[type] = this._observers[type].filter(function (callback) {
-                if (callback.get()) {
-                    try {
-                        try {
-                            callback.get().apply(null, args);
-                        }
-                        catch (e if e.message == "can't wrap XML objects") {
-                            // Horrible kludge.
-                            callback.get().apply(null, [String(args[0])].concat(args.slice(1)));
-                        }
-                    }
-                    catch (e) {
-                        dactyl.reportError(e);
-                    }
+            this._observers[type] = this._observers[type]
+                                        .filter(callback => {
+                callback = callback.get();
+                if (callback) {
+                    util.trapErrors(() => callback.apply(null, args));
                     return true;
                 }
             });
@@ -250,7 +241,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 let filters = args.map(arg => let (re = util.regexp.escape(arg))
                                         util.regexp("\\b" + re + "\\b|(?:^|[()\\s])" + re + "(?:$|[()\\s])", "i"));
                 if (filters.length)
-                    results = results.filter(item => filters.every(re => keys(item).some(re.closure.test)));
+                    results = results.filter(item => filters.every(re => keys(item).some(re.bound.test)));
 
                 commandline.commandOutput(
                     template.usage(results, params.format));
@@ -278,10 +269,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 let results = array((params.iterateIndex || params.iterate).call(params, commands.get(name).newArgs()))
                         .array.sort((a, b) => String.localeCompare(a.name, b.name));
 
-                let haveTag = Set.has(help.tags);
                 for (let obj in values(results)) {
                     let res = dactyl.generateHelp(obj, null, null, true);
-                    if (!haveTag(obj.helpTag))
+                    if (!hasOwnProperty(help.tags, obj.helpTag))
                         res[0][1].tag = obj.helpTag;
 
                     yield res;
@@ -387,9 +377,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
     },
 
     dump: deprecated("util.dump",
-                     { get: function dump() util.closure.dump }),
+                     { get: function dump() util.bound.dump }),
     dumpStack: deprecated("util.dumpStack",
-                          { get: function dumpStack() util.closure.dumpStack }),
+                          { get: function dumpStack() util.bound.dumpStack }),
 
     /**
      * Outputs a plain message to the command line.
@@ -468,16 +458,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      *     should be loaded.
      */
     loadScript: function loadScript(uri, context) {
+        let prefix = "literal:" + uri + ":";
+        cache.flush(s => s.startsWith(prefix));
+        delete literal.files[uri];
         JSMLoader.loadSubScript(uri, context, File.defaultEncoding);
     },
 
     userEval: function userEval(str, context, fileName, lineNumber) {
-        let ctxt;
-        if (jsmodules.__proto__ != window && jsmodules.__proto__ != XPCNativeWrapper(window) &&
-                jsmodules.isPrototypeOf(context))
-            str = "with (window) { with (modules) { (this.eval || eval)(" + str.quote() + ") } }";
+        let ctxt,
+            info = contexts.context;
 
-        let info = contexts.context;
         if (fileName == null)
             if (info)
                 ({ file: fileName, line: lineNumber, context: ctxt }) = info;
@@ -485,14 +475,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         if (fileName && fileName[0] == "[")
             fileName = "dactyl://command-line/";
         else if (!context)
-            context = ctxt || _userContext;
-
-        if (isinstance(context, ["Sandbox"]))
-            return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber);
+            context = ctxt || userContext;
 
         if (!context)
             context = userContext || ctxt;
 
+        if (isinstance(context, ["Sandbox"]))
+            return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber);
+
         if (services.has("dactyl") && services.dactyl.evalInContext)
             return services.dactyl.evalInContext(str, context, fileName, lineNumber);
 
@@ -538,7 +528,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @param {boolean} silent Whether the command should be echoed on the
      *     command line.
      */
-    execute: function execute(str, modifiers = {}, silent = false) {
+    execute: function execute(str, modifiers={}, silent=false) {
         // skip comments and blank lines
         if (/^\s*("|$)/.test(str))
             return;
@@ -620,7 +610,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @param {string} feature The feature name.
      * @returns {boolean}
      */
-    has: function has(feature) Set.has(config.features, feature),
+    has: function has(feature) config.has(feature),
 
     /**
      * @private
@@ -637,8 +627,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         }
     },
 
-    help: deprecated("help.help", { get: function help() modules.help.closure.help }),
-    findHelp: deprecated("help.findHelp", { get: function findHelp() help.closure.findHelp }),
+    help: deprecated("help.help", { get: function help() modules.help.bound.help }),
+    findHelp: deprecated("help.findHelp", { get: function findHelp() help.bound.findHelp }),
 
     /**
      * @private
@@ -734,7 +724,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         }
 
         if (obj.completer && false)
-            add(completion._runCompleter(obj.closure.completer, "", null, args).items
+            add(completion._runCompleter(obj.bound.completer, "", null, args).items
                           .map(i => [i.text, i.description]));
 
         if (obj.options && obj.options.some(o => o.description) && false)
@@ -832,6 +822,20 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
     },
 
     events: {
+        beforecustomization: function onbeforecustomization(event) {
+            // Show navigation bar on Australis, where it's not supposed
+            // to be collapsible, and is therefore not handled by
+            // builtin code.
+            if ("CustomizableUI" in window)
+                this.setNodeVisible(document.getElementById("nav-bar"),
+                                    true);
+        },
+
+        aftercustomization: function onaftercustomization(event) {
+            // Restore toolbar states.
+            options["guioptions"] = options["guioptions"];
+        },
+
         click: function onClick(event) {
             let elem = event.originalTarget;
 
@@ -890,13 +894,13 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      *     tabs.
      * @returns {boolean}
      */
-    open: function open(urls, params = {}, force = false) {
+    open: function open(urls, params={}, force=false) {
         if (typeof urls == "string")
             urls = dactyl.parseURLs(urls);
 
         if (urls.length > prefs.get("browser.tabs.maxOpenBeforeWarn", 20) && !force)
-            return commandline.input(_("dactyl.prompt.openMany", urls.length) + " ",
-                function (resp) {
+            return commandline.input(_("dactyl.prompt.openMany", urls.length) + " ")
+                .then(function (resp) {
                     if (resp && resp.match(/^y(es)?$/i))
                         dactyl.open(urls, params, true);
                 });
@@ -1143,7 +1147,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         if (error instanceof FailedAssertion && error.noTrace || error.message === "Interrupted") {
             let context = contexts.context;
             let prefix = context ? context.file + ":" + context.line + ": " : "";
-            if (error.message && error.message.indexOf(prefix) !== 0 &&
+            if (error.message && !error.message.startsWith(prefix) &&
                     prefix != "[Command Line]:1: ")
                 error.message = prefix + error.message;
 
@@ -1184,7 +1188,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             return [];
         }
     },
-    wrapCallback: function wrapCallback(callback, self = this) {
+    wrapCallback: function wrapCallback(callback, self=this) {
         let save = ["forceOpen"];
         let saved = save.map(p => dactyl[p]);
         return function wrappedCallback() {
@@ -1205,10 +1209,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @property {[Window]} Returns an array of all the host application's
      *     open windows.
      */
-    get windows() [win for (win in iter(services.windowMediator.getEnumerator("navigator:browser"))) if (win.dactyl)],
+    get windows() [w for (w of overlay.windows)]
 
 }, {
-    toolbarHidden: function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true"
+    toolbarHidden: function toolbarHidden(elem) "true" == (elem.getAttribute("autohide") ||
+                                                           elem.getAttribute("collapsed"))
 }, {
     cache: function initCache() {
         cache.register("help/plugins.xml", function () {
@@ -1219,19 +1224,23 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 try {
                     let info = contexts.getDocs(context);
                     if (DOM.isJSONXML(info)) {
-                        let langs = info.slice(2).filter(e => isArray(e) && isObject(e[1]) && e[1].lang);
+                        let langs = info.slice(2)
+                                        .filter(e => isArray(e) && isObject(e[1]) && e[1].lang);
                         if (langs) {
-                            let lang = config.bestLocale(l[1].lang for each (l in langs));
+                            let lang = config.bestLocale(langs.map(l => l[1].lang));
 
                             info = info.slice(0, 2).concat(
                                 info.slice(2).filter(e => !isArray(e)
                                                        || !isObject(e[1])
                                                        || e[1].lang == lang));
 
-                            for each (let elem in info.slice(2).filter(e => isArray(e) && e[0] == "info" && isObject(e[1])))
-                                for (let attr in values(["name", "summary", "href"]))
+                            info.slice(2)
+                                .filter(e => isArray(e) && e[0] == "info" && isObject(e[1]))
+                                .forEach(elem => {
+                                for (let attr of ["name", "summary", "href"])
                                     if (attr in elem[1])
                                         info[attr] = elem[1][attr];
+                            });
                         }
                         body.push(["h2", { xmlns: "dactyl", tag: info[1].name + '-plugin' },
                                        String(info[1].summary)]);
@@ -1251,7 +1260,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                            ["toc", { start: "2" }],
 
                            body]);
-        });
+        }, true);
 
         cache.register("help/index.xml", function () {
             return '<?xml version="1.0"?>\n' +
@@ -1260,7 +1269,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                            ["dl", { insertafter: name + "-index" },
                                template.map(iter(), util.identity)],
                            "\n\n")]);
-        });
+        }, true);
 
         cache.register("help/gui.xml", function () {
             return '<?xml version="1.0"?>\n' +
@@ -1272,7 +1281,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                                       ["dd", {}, val[0]]]
                                    : undefined,
                                "\n")]]);
-        });
+        }, true);
 
         cache.register("help/privacy.xml", function () {
             return '<?xml version="1.0"?>\n' +
@@ -1285,7 +1294,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                                [["dt", {}, name],
                                 ["dd", {}, template.linkifyHelp(description, true)]],
                                "\n")]]);
-        });
+        }, true);
     },
     events: function initEvents() {
         events.listen(window, dactyl, "events", true);
@@ -1315,7 +1324,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                     M: ["Always show messages outside of the status line"]
                 },
                 setter: function (opts) {
-                    if (loaded.commandline || ~opts.indexOf("c"))
+                    if (loaded.has("commandline") || ~opts.indexOf("c"))
                         commandline.widgets.updateVisibility();
                 }
             },
@@ -1367,10 +1376,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                     styles.system.add("taboptions", "chrome://*",
                                       classes.length ? classes.join(",") + "{ display: none; }" : "");
 
-                    if (!dactyl.has("Gecko2")) {
-                        tabs.tabBinding.enabled = Array.some(opts, k => k in this.opts);
-                        tabs.updateTabCount();
-                    }
                     if (config.tabbrowser.tabContainer._positionPinnedTabs)
                         config.tabbrowser.tabContainer._positionPinnedTabs();
                 },
@@ -1639,7 +1644,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 if (args["+purgecaches"])
                     cache.flush();
 
-                util.delay(function () { util.rehash(args) });
+                util.delay(() => { util.rehash(args) });
             },
             {
                 argCount: "0", // FIXME
@@ -1889,6 +1894,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             });
         }
 
+        if (config.has("default-theme") && "CustomizableUI" in window)
+            overlay.overlayWindow(window, {
+                append: [
+                    ["window", { id: document.documentElement.id, "dactyl-australis": "true", xmlns: "xul" }]]
+            });
+
         dactyl.timeout(function () {
             try {
                 var args = config.prefs.get("commandline-args")
@@ -1927,7 +1938,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             // dactyl.hideGUI();
 
             if (dactyl.userEval("typeof document", null, "test.js") === "undefined")
-                jsmodules.__proto__ = XPCSafeJSObjectWrapper(window);
+                jsmodules.__proto__ = window;
 
             if (dactyl.commandLineOptions.preCommands)
                 dactyl.commandLineOptions.preCommands.forEach(function (cmd) {
diff --git a/common/content/disable-acr.jsm b/common/content/disable-acr.jsm
deleted file mode 100644 (file)
index 86e55c4..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-// By Kris Maglione. Public Domain.
-// Please feel free to copy and use at will.
-
-var ADDON_ID;
-
-const OVERLAY_URLS = [
-    "about:addons",
-    "chrome://mozapps/content/extensions/extensions.xul"
-];
-
-let { interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const TOPIC = "chrome-document-global-created";
-
-function observe(window, topic, url) {
-    if (topic === TOPIC)
-        checkDocument(window.document);
-}
-function init(id) {
-    if (id)
-        ADDON_ID = id;
-
-    Services.obs[id ? "addObserver" : "removeObserver"](observe, TOPIC, false);
-    for (let doc in chromeDocuments())
-        checkDocument(doc, !id);
-}
-function cleanup() { init(null); }
-
-function checkPopup(event) {
-    let doc = event.originalTarget.ownerDocument;
-    let binding = doc.getBindingParent(event.originalTarget);
-    if (binding && binding.addon && binding.addon.guid == ADDON_ID && !binding.addon.compatible) {
-        let elem = doc.getAnonymousElementByAttribute(binding, "anonid", "stillworks");
-        if (elem && elem.nextSibling) {
-            elem.nextSibling.disabled = true;
-            elem.nextSibling.setAttribute("tooltiptext", "Developer has opted out of incompatibility reports\n"+
-                                                         "Development versions are available with updated support");
-        }
-    }
-}
-
-function checkDocument(doc, disable, force) {
-    if (["interactive", "complete"].indexOf(doc.readyState) >= 0 || force && doc.readyState === "uninitialized") {
-        if (OVERLAY_URLS.indexOf(doc.documentURI) >= 0)
-            doc[disable ? "removeEventListener" : "addEventListener"]("popupshowing", checkPopup, false);
-    }
-    else {
-        doc.addEventListener("DOMContentLoaded", function listener() {
-            doc.removeEventListener("DOMContentLoaded", listener, false);
-            checkDocument(doc, disable, true);
-        }, false);
-    }
-}
-
-function chromeDocuments() {
-    let windows = Services.wm.getXULWindowEnumerator(null);
-    while (windows.hasMoreElements()) {
-        let window = windows.getNext().QueryInterface(Ci.nsIXULWindow);
-        for each (let type in ["typeChrome", "typeContent"]) {
-            let docShells = window.docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem[type],
-                                                                  Ci.nsIDocShell.ENUMERATE_FORWARDS);
-            while (docShells.hasMoreElements())
-                try {
-                    yield docShells.getNext().QueryInterface(Ci.nsIDocShell).contentViewer.DOMDocument;
-                }
-                catch (e) {}
-        }
-    }
-}
-
-var EXPORTED_SYMBOLS = ["cleanup", "init"];
-
-// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript:
index d98fa950da6b4f8ad99cc7431beaee07762cd034..38153e48fae087863ca8516a4746385aabb04b82 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 // Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
 //
 // This work is licensed for reuse under an MIT license. Details are
@@ -46,7 +46,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
         }, this);
     },
 
-    defaultRegister: "*",
+    defaultRegister: "*+",
 
     selectionRegisters: {
         "*": "selection",
@@ -64,11 +64,12 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
         if (name == null)
             name = editor.currentRegister || editor.defaultRegister;
 
+        name = String(name)[0];
         if (name == '"')
             name = 0;
         if (name == "_")
             var res = null;
-        else if (Set.has(this.selectionRegisters, name))
+        else if (hasOwnProperty(this.selectionRegisters, name))
             res = { text: dactyl.clipboardRead(this.selectionRegisters[name]) || "" };
         else if (!/^[0-9]$/.test(name))
             res = this.registers.get(name);
@@ -101,17 +102,19 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
             value = DOM.stringify(value);
         value = { text: value, isLine: modes.extended & modes.LINE, timestamp: Date.now() * 1000 };
 
-        if (name == '"')
-            name = 0;
-        if (name == "_")
-            ;
-        else if (Set.has(this.selectionRegisters, name))
-            dactyl.clipboardWrite(value.text, verbose, this.selectionRegisters[name]);
-        else if (!/^[0-9]$/.test(name))
-            this.registers.set(name, value);
-        else {
-            this.registerRing.insert(value, name);
-            this.registerRing.truncate(10);
+        for (let n of String(name)) {
+            if (n == '"')
+                n = 0;
+            if (n == "_")
+                ;
+            else if (hasOwnProperty(this.selectionRegisters, n))
+                dactyl.clipboardWrite(value.text, verbose, this.selectionRegisters[n]);
+            else if (!/^[0-9]$/.test(n))
+                this.registers.set(n, value);
+            else {
+                this.registerRing.insert(value, n);
+                this.registerRing.truncate(10);
+            }
         }
     },
 
@@ -386,8 +389,8 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
         let keepFocus = modes.stack.some(m => isinstance(m.main, modes.COMMAND_LINE));
 
         if (!forceEditing && textBox && textBox.type == "password") {
-            commandline.input(_("editor.prompt.editPassword") + " ",
-                function (resp) {
+            commandline.input(_("editor.prompt.editPassword") + " ")
+                .then(function (resp) {
                     if (resp && resp.match(/^y(es)?$/i))
                         editor.editFieldExternally(true);
                 });
@@ -424,7 +427,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
         column = 1 + pre.replace(/[^]*\n/, "").length;
 
         let origGroup = DOM(textBox).highlight.toString();
-        let cleanup = util.yieldable(function cleanup(error) {
+        let cleanup = promises.task(function cleanup(error) {
             if (timer)
                 timer.cancel();
 
@@ -443,9 +446,11 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
                 DOM(textBox).highlight.remove("EditorEditing");
                 if (!keepFocus)
                     dactyl.focus(textBox);
+
                 for (let group in values(blink.concat(blink, ""))) {
                     highlight.highlightNode(textBox, origGroup + " " + group);
-                    yield 100;
+
+                    yield promises.sleep(100);
                 }
             }
         });
@@ -760,9 +765,9 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
     completion: function initCompletion() {
         completion.register = function complete_register(context) {
             context = context.fork("registers");
-            context.keys = { text: util.identity, description: editor.closure.getRegister };
+            context.keys = { text: util.identity, description: editor.bound.getRegister };
 
-            context.match = function (r) !this.filter || ~this.filter.indexOf(r);
+            context.match = function (r) !this.filter || this.filter.contains(r);
 
             context.fork("clipboard", 0, this, function (ctxt) {
                 ctxt.match = context.match;
@@ -1363,11 +1368,12 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), {
                         args.push(obj["file"]);
                     return args;
                 },
-                has: function (key) Set.has(util.compileMacro(this.value).seen, key),
+                has: function (key) util.compileMacro(this.value).seen.has(key),
                 validator: function (value) {
                     this.format({}, value);
-                    return Object.keys(util.compileMacro(value).seen)
-                                 .every(k => ["column", "file", "line"].indexOf(k) >= 0);
+                    let allowed = RealSet(["column", "file", "line"]);
+                    return [k for (k of util.compileMacro(value).seen)]
+                                .every(k => allowed.has(k));
                 }
             });
 
index 8006c4cdb422a0aa203159400d69d58ed011c922..844a5b51216f471621adb15da5caa5b0b6e6167a 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -28,7 +28,7 @@ var EventHive = Class("EventHive", Contexts.Hive, {
         else
             [self, events] = [event, event[callback || "events"]];
 
-        if (Set.has(events, "input") && !Set.has(events, "dactyl-input"))
+        if (hasOwnProperty(events, "input") && !hasOwnProperty(events, "dactyl-input"))
             events["dactyl-input"] = events.input;
 
         return [self, events];
@@ -79,7 +79,7 @@ var EventHive = Class("EventHive", Contexts.Hive, {
             let elem = args[0].get();
             if (target == null || elem == target
                                && self == args[1].get()
-                               && Set.has(events, args[2])
+                               && hasOwnProperty(events, args[2])
                                && args[3].wrapped == events[args[2]]
                                && args[4] == capture) {
 
@@ -90,7 +90,7 @@ var EventHive = Class("EventHive", Contexts.Hive, {
         });
     },
 
-    get wrapListener() events.closure.wrapListener
+    get wrapListener() events.bound.wrapListener
 });
 
 /**
@@ -107,9 +107,9 @@ var Events = Module("events", {
                 ["window", { id: document.documentElement.id, xmlns: "xul" },
                     // http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands
                     ["commandset", { id: "dactyl-onfocus", commandupdater: "true", events: "focus",
-                                     commandupdate: this.closure.onFocusChange }],
+                                     commandupdate: this.bound.onFocusChange }],
                     ["commandset", { id: "dactyl-onselect", commandupdater: "true", events: "select",
-                                     commandupdate: this.closure.onSelectionChange }]]]
+                                     commandupdate: this.bound.onSelectionChange }]]]
         });
 
         this._fullscreen = window.fullScreen;
@@ -188,6 +188,11 @@ var Events = Module("events", {
 
         this.listen(window, this, "events", true);
         this.listen(window, this.popups, "events", true);
+
+        this.grabFocus = 0;
+        this.grabFocusTimer = Timer(100, 10000, () => {
+            this.grabFocus = 0;
+        });
     },
 
     cleanup: function cleanup() {
@@ -206,13 +211,13 @@ var Events = Module("events", {
         }
     },
 
-    get listen() this.builtin.closure.listen,
+    get listen() this.builtin.bound.listen,
     addSessionListener: deprecated("events.listen", { get: function addSessionListener() this.listen }),
 
     /**
      * Wraps an event listener to ensure that errors are reported.
      */
-    wrapListener: function wrapListener(method, self = this) {
+    wrapListener: function wrapListener(method, self=this) {
         method.wrapper = wrappedListener;
         wrappedListener.wrapped = method;
         function wrappedListener(event) {
@@ -419,11 +424,11 @@ var Events = Module("events", {
         return true;
     },
 
-    canonicalKeys: deprecated("DOM.Event.canonicalKeys", { get: function canonicalKeys() DOM.Event.closure.canonicalKeys }),
+    canonicalKeys: deprecated("DOM.Event.canonicalKeys", { get: function canonicalKeys() DOM.Event.bound.canonicalKeys }),
     create:        deprecated("DOM.Event", function create() DOM.Event.apply(null, arguments)),
     dispatch:      deprecated("DOM.Event.dispatch", function dispatch() DOM.Event.dispatch.apply(DOM.Event, arguments)),
-    fromString:    deprecated("DOM.Event.parse", { get: function fromString() DOM.Event.closure.parse }),
-    iterKeys:      deprecated("DOM.Event.iterKeys", { get: function iterKeys() DOM.Event.closure.iterKeys }),
+    fromString:    deprecated("DOM.Event.parse", { get: function fromString() DOM.Event.bound.parse }),
+    iterKeys:      deprecated("DOM.Event.iterKeys", { get: function iterKeys() DOM.Event.bound.iterKeys }),
 
     toString: function toString() {
         if (!arguments.length)
@@ -571,7 +576,7 @@ var Events = Module("events", {
     events: {
         blur: function onBlur(event) {
             let elem = event.originalTarget;
-            if (DOM(elem).isEditable)
+            if (DOM(elem).editor)
                 util.trapErrors("removeEditActionListener",
                                 DOM(elem).editor, editor);
 
@@ -595,7 +600,7 @@ var Events = Module("events", {
         // TODO: Merge with onFocusChange
         focus: function onFocus(event) {
             let elem = event.originalTarget;
-            if (DOM(elem).isEditable)
+            if (DOM(elem).editor)
                 util.trapErrors("addEditActionListener",
                                 DOM(elem).editor, editor);
 
@@ -616,11 +621,15 @@ var Events = Module("events", {
                                      Ci.nsIDOMHTMLSelectElement,
                                      Ci.nsIDOMHTMLTextAreaElement,
                                      Ci.nsIDOMWindow])) {
-
-                if (elem.frameElement)
-                    dactyl.focusContent(true);
-                else if (!(elem instanceof Window) || Editor.getEditor(elem))
-                    dactyl.focus(window);
+                if (this.grabFocus++ > 5)
+                    ; // Something is fighting us. Give up.
+                else {
+                    this.grabFocusTimer.tell();
+                    if (elem.frameElement)
+                        dactyl.focusContent(true);
+                    else if (!(elem instanceof Window) || Editor.getEditor(elem))
+                        dactyl.focus(window);
+                }
             }
 
             if (elem instanceof Element)
@@ -711,7 +720,10 @@ var Events = Module("events", {
                     return Events.kill(event);
                 }
 
-                if (!this.processor) {
+                if (this.processor)
+                    events.dbg("ON KEYPRESS " + key + " processor: " + this.processor,
+                               event.originalTarget instanceof Element ? event.originalTarget : String(event.originalTarget));
+                else {
                     let mode = modes.getStack(0);
                     if (event.dactylMode)
                         mode = Modes.StackElement(event.dactylMode);
@@ -875,19 +887,23 @@ var Events = Module("events", {
             let haveInput = modes.stack.some(m => m.main.input);
 
             if (DOM(elem || win).isEditable) {
-                if (!haveInput)
-                    if (!isinstance(modes.main, [modes.INPUT, modes.TEXT_EDIT, modes.VISUAL]))
-                        if (options["insertmode"])
-                            modes.push(modes.INSERT);
-                        else {
-                            modes.push(modes.TEXT_EDIT);
-                            if (elem.selectionEnd - elem.selectionStart > 0)
-                                modes.push(modes.VISUAL);
-                        }
-
-                if (hasHTMLDocument(win))
-                    buffer.lastInputField = elem || win;
-                return;
+                let e = elem || win;
+                if (!(e instanceof Ci.nsIDOMWindow &&
+                        DOM(e.document.activeElement).style.MozUserModify != "read-write")) {
+                    if (!haveInput)
+                        if (!isinstance(modes.main, [modes.INPUT, modes.TEXT_EDIT, modes.VISUAL]))
+                            if (options["insertmode"])
+                                modes.push(modes.INSERT);
+                            else {
+                                modes.push(modes.TEXT_EDIT);
+                                if (elem.selectionEnd - elem.selectionStart > 0)
+                                    modes.push(modes.VISUAL);
+                            }
+
+                    if (hasHTMLDocument(win))
+                        buffer.lastInputField = elem || win;
+                    return;
+                }
             }
 
             if (elem && Events.isInputElement(elem)) {
@@ -969,7 +985,7 @@ var Events = Module("events", {
     },
 
     isInputElement: function isInputElement(elem) {
-        return DOM(elem).isEditable ||
+        return elem instanceof Ci.nsIDOMElement && DOM(elem).isEditable ||
                isinstance(elem, [Ci.nsIDOMHTMLEmbedElement,
                                  Ci.nsIDOMHTMLObjectElement,
                                  Ci.nsIDOMHTMLSelectElement]);
@@ -1124,12 +1140,12 @@ var Events = Module("events", {
             "sitemap", "", {
                 flush: function flush() {
                     memoize(this, "filters", function () this.value.filter(function (f) f(buffer.documentURI)));
-                    memoize(this, "pass", function () Set(array.flatten(this.filters.map(function (f) f.keys))));
+                    memoize(this, "pass", function () RealSet(array.flatten(this.filters.map(function (f) f.keys))));
                     memoize(this, "commandHive", function hive() Hive(this.filters, "command"));
                     memoize(this, "inputHive", function hive() Hive(this.filters, "input"));
                 },
 
-                has: function (key) Set.has(this.pass, key) || Set.has(this.commandHive.stack.mappings, key),
+                has: function (key) this.pass.has(key) || hasOwnProperty(this.commandHive.stack.mappings, key),
 
                 get pass() (this.flush(), this.pass),
 
@@ -1137,9 +1153,9 @@ var Events = Module("events", {
                     let value = parse.superapply(this, arguments);
                     value.forEach(function (filter) {
                         let vals = Option.splitList(filter.result);
-                        filter.keys = DOM.Event.parse(vals[0]).map(DOM.Event.closure.stringify);
+                        filter.keys = DOM.Event.parse(vals[0]).map(DOM.Event.bound.stringify);
 
-                        filter.commandKeys = vals.slice(1).map(DOM.Event.closure.canonicalKeys);
+                        filter.commandKeys = vals.slice(1).map(DOM.Event.bound.canonicalKeys);
                         filter.inputKeys = filter.commandKeys.filter(bind("test", /^<[ACM]-/));
                     });
                     return value;
index af09ad4dd499498ef16561971e67bb3a09af3c68..035f821327f9a9320fb257bfa734c6dbbafbb32c 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -12,7 +12,7 @@
 var HintSession = Class("HintSession", CommandMode, {
     get extendedMode() modes.HINTS,
 
-    init: function init(mode, opts = {}) {
+    init: function init(mode, opts={}) {
         init.supercall(this);
 
         if (!opts.window)
@@ -24,7 +24,7 @@ var HintSession = Class("HintSession", CommandMode, {
         this.activeTimeout = null; // needed for hinttimeout > 0
         this.continue = Boolean(opts.continue);
         this.docs = [];
-        this.hintKeys = DOM.Event.parse(options["hintkeys"]).map(DOM.Event.closure.stringify);
+        this.hintKeys = DOM.Event.parse(options["hintkeys"]).map(DOM.Event.bound.stringify);
         this.hintNumber = 0;
         this.hintString = opts.filter || "";
         this.pageHints = [];
@@ -36,8 +36,8 @@ var HintSession = Class("HintSession", CommandMode, {
         this.open();
 
         this.top = opts.window || content;
-        this.top.addEventListener("resize", this.closure._onResize, true);
-        this.top.addEventListener("dactyl-commandupdate", this.closure._onResize, false, true);
+        this.top.addEventListener("resize", this.bound._onResize, true);
+        this.top.addEventListener("dactyl-commandupdate", this.bound._onResize, false, true);
 
         this.generate();
 
@@ -101,8 +101,8 @@ var HintSession = Class("HintSession", CommandMode, {
             if (hints.hintSession == this)
                 hints.hintSession = null;
             if (this.top) {
-                this.top.removeEventListener("resize", this.closure._onResize, true);
-                this.top.removeEventListener("dactyl-commandupdate", this.closure._onResize, true);
+                this.top.removeEventListener("resize", this.bound._onResize, true);
+                this.top.removeEventListener("dactyl-commandupdate", this.bound._onResize, true);
             }
 
             this.removeHints(0);
@@ -305,6 +305,8 @@ var HintSession = Class("HintSession", CommandMode, {
                         return false;
 
             let computedStyle = doc.defaultView.getComputedStyle(elem, null);
+            if (!computedStyle)
+                return false;
             if (computedStyle.visibility != "visible" || computedStyle.display == "none")
                 return false;
             return true;
@@ -332,7 +334,7 @@ var HintSession = Class("HintSession", CommandMode, {
                         __proto__: this.Hint
                     });
 
-            for (let hint in values(_hints)) {
+            for (let hint of _hints) {
                 let { elem, rect } = hint;
 
                 if (elem.hasAttributeNS(NS, "hint"))
@@ -695,7 +697,7 @@ var HintSession = Class("HintSession", CommandMode, {
     updateValidNumbers: function updateValidNumbers(always) {
         let string = this.getHintString(this.hintNumber);
         for (let hint in values(this.validHints))
-            hint.valid = always || hint.span.getAttribute("number").indexOf(string) == 0;
+            hint.valid = always || hint.span.getAttribute("number").startsWith(string);
     },
 
     tab: function tab(previous) {
@@ -748,19 +750,29 @@ var Hints = Module("hints", {
 
         let appContent = document.getElementById("appcontent");
         if (appContent)
-            events.listen(appContent, "scroll", this.resizeTimer.closure.tell, false);
+            events.listen(appContent, "scroll", this.resizeTimer.bound.tell, false);
 
         const Mode = Hints.Mode;
         Mode.prototype.__defineGetter__("matcher", function ()
             options.get("extendedhinttags").getKey(this.name, options.get("hinttags").matcher));
 
+        function cleanLoc(loc) {
+            try {
+                let uri = util.newURI(loc);
+                if (uri.scheme == "mailto" && !~uri.path.indexOf("?"))
+                    return uri.path;
+            }
+            catch (e) {}
+            return loc;
+        }
+
         this.modes = {};
-        this.addMode(";", "Focus hint",                           buffer.closure.focusElement);
+        this.addMode(";", "Focus hint",                           buffer.bound.focusElement);
         this.addMode("?", "Show information for hint",            elem => buffer.showElementInfo(elem));
         // TODO: allow for ! override to overwrite existing paths -- where? --djk
         this.addMode("s", "Save hint",                            elem => buffer.saveLink(elem, false));
         this.addMode("f", "Focus frame",                          elem => dactyl.focus(elem.ownerDocument.defaultView));
-        this.addMode("F", "Focus frame or pseudo-frame",          buffer.closure.focusElement, isScrollable);
+        this.addMode("F", "Focus frame or pseudo-frame",          buffer.bound.focusElement, isScrollable);
         this.addMode("o", "Follow hint",                          elem => buffer.followLink(elem, dactyl.CURRENT_TAB));
         this.addMode("t", "Follow hint in a new tab",             elem => buffer.followLink(elem, dactyl.NEW_TAB));
         this.addMode("b", "Follow hint in a background tab",      elem => buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB));
@@ -772,7 +784,7 @@ var Hints = Module("hints", {
         this.addMode("S", "Add a search keyword",                 elem => bookmarks.addSearchKeyword(elem));
         this.addMode("v", "View hint source",                     (elem, loc) => buffer.viewSource(loc, false));
         this.addMode("V", "View hint source in external editor",  (elem, loc) => buffer.viewSource(loc, true));
-        this.addMode("y", "Yank hint location",                   (elem, loc) => editor.setRegister(null, loc, true));
+        this.addMode("y", "Yank hint location",                   (elem, loc) => editor.setRegister(null, cleanLoc(loc), true));
         this.addMode("Y", "Yank hint description",                elem => editor.setRegister(null, elem.textContent || "", true));
         this.addMode("A", "Yank hint anchor url",                 function (elem) {
             let uri = elem.ownerDocument.documentURIObject.clone();
@@ -1042,7 +1054,7 @@ var Hints = Module("hints", {
 
         let indexOf = String.indexOf;
         if (options.get("hintmatching").has("transliterated"))
-            indexOf = Hints.closure.indexOf;
+            indexOf = Hints.bound.indexOf;
 
         switch (options["hintmatching"][0]) {
         case "contains"      : return containsMatcher(hintString);
@@ -1054,24 +1066,24 @@ var Hints = Module("hints", {
         return null;
     }, //}}}
 
-    open: function open(mode, opts = {}) {
+    open: function open(mode, opts={}) {
         this._extendedhintCount = opts.count;
 
         mappings.pushCommand();
-        commandline.input(["Normal", mode], null, {
+        commandline.input(["Normal", mode], {
             autocomplete: false,
             completer: function (context) {
                 context.compare = () => 0;
                 context.completions = [[k, v.prompt] for ([k, v] in Iterator(hints.modes))];
             },
-            onCancel: mappings.closure.popCommand,
+            onCancel: mappings.bound.popCommand,
             onSubmit: function (arg) {
                 if (arg)
                     hints.show(arg, opts);
                 mappings.popCommand();
             },
             onChange: function (arg) {
-                if (Object.keys(hints.modes).some(m => m != arg && m.indexOf(arg) == 0))
+                if (Object.keys(hints.modes).some(m => m != arg && m.startsWith(arg)))
                     return;
 
                 this.accepted = true;
@@ -1299,23 +1311,29 @@ var Hints = Module("hints", {
             },
             {
                 keepQuotes: true,
+
                 getKey: function (val, default_)
-                    let (res = array.nth(this.value, re => let (match = re.exec(val)) match && match[0] == val, 0))
-                        res ? res.matcher : default_,
+                    let (res = this.value.find(re => let (match = re.exec(val)) match && match[0] == val))
+                        res ? res.matcher
+                            : default_,
+
                 parse: function parse(val) {
                     let vals = parse.supercall(this, val);
                     for (let value in values(vals))
                         value.matcher = DOM.compileMatcher(Option.splitList(value.result));
                     return vals;
                 },
+
                 testValues: function testValues(vals, validator) vals.every(re => Option.splitList(re).every(validator)),
+
                 validator: DOM.validateMatcher
             });
 
         options.add(["hinttags", "ht"],
             "XPath or CSS selector strings of hintable elements for Hints mode",
             // Make sure to update the docs when you change this.
-            "stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]),label[for],select,textarea," +
+            "stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]):not([disabled])," +
+                          "label[for],select,textarea," +
                           "[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," +
                           "[tabindex],[role=link],[role=button],[contenteditable=true]",
             {
@@ -1335,7 +1353,7 @@ var Hints = Module("hints", {
                     "asdfg;lkjh": "Home Row"
                 },
                 validator: function (value) {
-                    let values = DOM.Event.parse(value).map(DOM.Event.closure.stringify);
+                    let values = DOM.Event.parse(value).map(DOM.Event.bound.stringify);
                     return Option.validIf(array.uniq(values).length === values.length && values.length > 1,
                                           _("option.hintkeys.duplicate"));
                 }
index 62aacf1bb3a0692fb5dd33e529bf989ffe63aaa4..5b2c6d3f5f5a2268a1dfc764bd6735781a89aa20 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -13,7 +13,7 @@ var History = Module("history", {
 
     get service() services.history,
 
-    get: function get(filter, maxItems, sort = this.SORT_DEFAULT) {
+    get: function get(filter, maxItems, sort=this.SORT_DEFAULT) {
         if (isString(filter))
             filter = { searchTerms: filter };
 
index 305964392cf4818c34f9dbf1eccaac875090e8f6..d1a51d2185169ef01918bb72911e5d579b744c4f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -107,8 +107,8 @@ var ProcessorStack = Class("ProcessorStack", {
                 this.timer = services.Timer(this, options["timeoutlen"], services.Timer.TYPE_ONE_SHOT);
         }
         else if (result !== Events.KILL && !this.actions.length &&
-                 !(this.events[0].isReplay || this.passUnknown
-                   || this.modes.some(function (m) m.passEvent(this), this.events[0]))) {
+                 !(this.events[0].isReplay || this.passUnknown ||
+                   this.modes.some(function (m) m.passEvent(this), this.events[0]))) {
             // No patching processors, this isn't a fake, pass-through
             // event, we're not in pass-through mode, and we're not
             // choosing to pass unknown keys. Kill the event and beep.
@@ -142,9 +142,9 @@ var ProcessorStack = Class("ProcessorStack", {
             let list = this.events.filter(e => e.defaultPrevented && !e.dactylDefaultPrevented);
 
             if (result === Events.PASS)
-                events.dbg("PASS THROUGH: " + list.slice(0, length).filter(e => e.type === "keypress").map(DOM.Event.closure.stringify));
+                events.dbg("PASS THROUGH: " + list.slice(0, length).filter(e => e.type === "keypress").map(DOM.Event.bound.stringify));
             if (list.length > length)
-                events.dbg("REFEED: " + list.slice(length).filter(e => e.type === "keypress").map(DOM.Event.closure.stringify));
+                events.dbg("REFEED: " + list.slice(length).filter(e => e.type === "keypress").map(DOM.Event.bound.stringify));
 
             if (result === Events.PASS)
                 events.feedevents(null, list.slice(0, length), { skipmap: true, isMacro: true, isReplay: true });
index fa113b4d4de0229bd2ca5e24b202c2ffc39f7438..5a1d6b269452d6c5e6439560b404591cc375335a 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -39,7 +39,7 @@ var Map = Class("Map", {
         Object.freeze(this.modes);
 
         if (info) {
-            if (Set.has(Map.types, info.type))
+            if (hasOwnProperty(Map.types, info.type))
                 this.update(Map.types[info.type]);
             this.update(info);
         }
@@ -107,7 +107,7 @@ var Map = Class("Map", {
      */
     hasName: function (name) this.keys.indexOf(name) >= 0,
 
-    get keys() array.flatten(this.names.map(mappings.closure.expand)),
+    get keys() array.flatten(this.names.map(mappings.bound.expand)),
 
     /**
      * Execute the action for this mapping.
@@ -171,7 +171,7 @@ var MapHive = Class("MapHive", Contexts.Hive, {
      * @param {[Modes.Mode]} modes The modes for which to return mappings.
      */
     iterate: function (modes) {
-        let stacks = Array.concat(modes).map(this.closure.getStack);
+        let stacks = Array.concat(modes).map(this.bound.getStack);
         return values(stacks.shift().sort((m1, m2) => String.localeCompare(m1.name, m2.name))
             .filter((map) => map.rhs &&
                 stacks.every(stack => stack.some(m => m.rhs && m.rhs === map.rhs && m.name === map.name))));
@@ -187,7 +187,7 @@ var MapHive = Class("MapHive", Contexts.Hive, {
      * @param {Object} extra An optional extra configuration hash.
      * @optional
      */
-    add: function (modes, keys, description, action, extra = {}) {
+    add: function (modes, keys, description, action, extra={}) {
         modes = Array.concat(modes);
         if (!modes.every(util.identity))
             throw TypeError(/*L*/"Invalid modes: " + modes);
@@ -380,10 +380,10 @@ var Mappings = Module("mappings", {
     },
 
     iterate: function (mode) {
-        let seen = {};
+        let seen = RealSet();
         for (let hive in this.hives.iterValues())
             for (let map in array(hive.getStack(mode)).iterValues())
-                if (!Set.add(seen, map.name))
+                if (!seen.add(map.name))
                     yield map;
     },
 
@@ -638,10 +638,10 @@ var Mappings = Module("mappings", {
                 }
             };
             function userMappings(hive) {
-                let seen = {};
+                let seen = RealSet();
                 for (let stack in values(hive.stacks))
                     for (let map in array.iterValues(stack))
-                        if (!Set.add(seen, map.id))
+                        if (!seen.add(map.id))
                             yield map;
             }
 
@@ -714,6 +714,7 @@ var Mappings = Module("mappings", {
         function uniqueModes(modes) {
             let chars = [k for ([k, v] in Iterator(modules.modes.modeChars))
                          if (v.every(mode => modes.indexOf(mode) >= 0))];
+
             return array.uniq(modes.filter(m => chars.indexOf(m.char) < 0)
                                    .map(m => m.name.toLowerCase())
                                    .concat(chars));
@@ -748,13 +749,13 @@ var Mappings = Module("mappings", {
                 if (!mainOnly)
                     modes = modes[0].allBases;
 
-                let seen = {};
+                let seen = RealSet();
                 // Bloody hell. --Kris
                 for (let [i, mode] in Iterator(modes))
                     for (let hive in mappings.hives.iterValues())
                         for (let map in array.iterValues(hive.getStack(mode)))
                             for (let name in values(map.names))
-                                if (!Set.add(seen, name)) {
+                                if (!seen.add(name))
                                     yield {
                                         name: name,
                                         columns: [
@@ -765,7 +766,6 @@ var Mappings = Module("mappings", {
                                         ],
                                         __proto__: map
                                     };
-                                }
             },
             format: {
                 description: function (map) [
@@ -800,7 +800,7 @@ var Mappings = Module("mappings", {
                     name: [mode.char + "listk[eys]", mode.char + "lk"],
                     iterateIndex: function (args)
                             let (self = this, prefix = /^[bCmn]$/.test(mode.char) ? "" : mode.char + "_",
-                                 haveTag = Set.has(help.tags))
+                                 haveTag = k => hasOwnProperty(help.tags, k))
                                     ({ helpTag: prefix + map.name, __proto__: map }
                                      for (map in self.iterate(args, true))
                                      if (map.hive === mappings.builtin || haveTag(prefix + map.name))),
@@ -812,7 +812,7 @@ var Mappings = Module("mappings", {
         });
     },
     completion: function initCompletion(dactyl, modules, window) {
-        completion.userMapping = function userMapping(context, modes_ = [modes.NORMAL], hive = mappings.user) {
+        completion.userMapping = function userMapping(context, modes_=[modes.NORMAL], hive=mappings.user) {
             context.keys = { text: function (m) m.names[0],
                              description: function (m) m.description + ": " + m.action };
             context.completions = hive.iterate(modes_);
index cfa27bf30b90ee25346974e3bc79851aa8a40aef..7f6e256e245c6414d43a0a449e5292076a6f234b 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -34,7 +34,7 @@ var Marks = Module("marks", {
 
     get localURI() buffer.focusedFrame.document.documentURI.replace(/#.*/, ""),
 
-    Mark: function Mark(params = {}) {
+    Mark: function Mark(params={}) {
         let win = buffer.focusedFrame;
         let doc = win.document;
 
@@ -63,7 +63,8 @@ var Marks = Module("marks", {
         let mark = this.Mark();
 
         if (Marks.isURLMark(name)) {
-            mark.tab = util.weakReference(tabs.getTab());
+            // FIXME: Disabled due to cross-compartment magic.
+            // mark.tab = util.weakReference(tabs.getTab());
             this._urlMarks.set(name, mark);
             var message = "mark.addURL";
         }
@@ -310,7 +311,7 @@ var Marks = Module("marks", {
     events: function () {
         let appContent = document.getElementById("appcontent");
         if (appContent)
-            events.listen(appContent, "load", marks.closure._onPageLoad, true);
+            events.listen(appContent, "load", marks.bound._onPageLoad, true);
     },
     mappings: function () {
         var myModes = config.browserModes;
index 838eab65d1186d67f684389d1e49ab79929fdb3d..d4cd27399cc9f6a7794167ab9391c8746b9f18f9 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -21,13 +21,7 @@ var Modes = Module("modes", {
         this._recording = false;
         this._replaying = false; // playing a macro
 
-        this._modeStack = update([], {
-            pop: function pop() {
-                if (this.length <= 1)
-                    throw Error("Trying to pop last element in mode stack");
-                return pop.superapply(this, arguments);
-            }
-        });
+        this._modeStack = Modes.ModeStack([]);
 
         this._modes = [];
         this._mainModes = [];
@@ -262,20 +256,21 @@ var Modes = Module("modes", {
 
     // show the current mode string in the command line
     show: function show() {
-        if (!loaded.modes)
+        if (!loaded.has("modes"))
             return;
 
         let msg = this._getModeMessage();
 
-        if (msg || loaded.commandline)
+        if (msg || loaded.has("commandline"))
             commandline.widgets.mode = msg || null;
     },
 
     remove: function remove(mode, covert) {
         if (covert && this.topOfStack.main != mode) {
             util.assert(mode != this.NORMAL);
-            for (let m; m = array.nth(this.modeStack, m => m.main == mode, 0);)
-                this._modeStack.splice(this._modeStack.indexOf(m));
+
+            this._modeStack = Modes.ModeStack(
+                this._modeStack.filter(m => m.main != mode));
         }
         else if (this.stack.some(m => m.main == mode)) {
             this.pop(mode);
@@ -334,10 +329,11 @@ var Modes = Module("modes", {
                                       { push: push }, push);
 
                 for (let [id, { obj, prop, test }] in Iterator(this.boundProperties)) {
-                    if (!obj.get())
+                    obj = obj.get();
+                    if (!obj)
                         delete this.boundProperties[id];
                     else
-                        this.topOfStack.saved[id] = { obj: obj.get(), prop: prop, value: obj.get()[prop], test: test };
+                        this.topOfStack.saved[id] = { obj: obj, prop: prop, value: obj[prop], test: test };
                 }
             }
 
@@ -446,9 +442,11 @@ var Modes = Module("modes", {
             this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
 
         allBases: Class.Memoize(function () {
-            let seen = {}, res = [], queue = [this].concat(this.bases);
+            let seen = RealSet(),
+                res = [],
+                queue = [this].concat(this.bases);
             for (let mode in array.iterValues(queue))
-                if (!Set.add(seen, mode)) {
+                if (!seen.add(mode)) {
                     res.push(mode);
                     queue.push.apply(queue, mode.bases);
                 }
@@ -485,13 +483,21 @@ var Modes = Module("modes", {
     }, {
         _id: 0
     }),
+    ModeStack: function ModeStack(array)
+        update(array, {
+            pop: function pop() {
+                if (this.length <= 1)
+                    throw Error("Trying to pop last element in mode stack");
+                return pop.superapply(this, arguments);
+            }
+        }),
     StackElement: (function () {
         const StackElement = Struct("main", "extended", "params", "saved");
         StackElement.className = "Modes.StackElement";
         StackElement.defaultValue("params", function () this.main.params);
 
         update(StackElement.prototype, {
-            get toStringParams() !loaded.modes ? this.main.name : [
+            get toStringParams() !loaded.has("modes") ? [this.main.name] : [
                 this.main.name,
                 ["(", modes.all.filter(m => this.extended & m)
                                .map(m => m.name)
@@ -502,7 +508,7 @@ var Modes = Module("modes", {
         return StackElement;
     })(),
     cacheId: 0,
-    boundProperty: function BoundProperty(desc = {}) {
+    boundProperty: function BoundProperty(desc={}) {
         let id = this.cacheId++;
         let value;
 
@@ -560,9 +566,10 @@ var Modes = Module("modes", {
             return rec(roots);
         }
 
-        cache.register("modes.dtd", () =>
-            util.makeDTD(iter({ "modes.tree": makeTree() },
-                              config.dtd)));
+        cache.register("modes.dtd",
+            () => util.makeDTD(iter({ "modes.tree": makeTree() },
+                                    config.dtd)),
+            true);
     },
     mappings: function initMappings() {
         mappings.add([modes.BASE, modes.NORMAL],
@@ -601,10 +608,10 @@ var Modes = Module("modes", {
 
             getKey: function getKey(val, default_) {
                 if (isArray(val))
-                    return (array.nth(this.value, v => val.some(m => m.name === v.mode), 0)
+                    return (this.value.find(v => val.some(m => m.name === v.mode))
                                 || { result: default_ }).result;
 
-                return Set.has(this.valueMap, val) ? this.valueMap[val] : default_;
+                return hasOwnProperty(this.valueMap, val) ? this.valueMap[val] : default_;
             },
 
             setter: function (vals) {
@@ -622,9 +629,10 @@ var Modes = Module("modes", {
             },
 
             validator: function validator(vals) vals.map(v => v.replace(/^!/, ""))
-                                                    .every(Set.has(this.values)),
+                                                    .every(k => hasOwnProperty(this.values, k)),
 
-            get values() array.toObject([[m.name.toLowerCase(), m.description] for (m in values(modes._modes)) if (!m.hidden)])
+            get values() array.toObject([[m.name.toLowerCase(), m.description]
+                                         for (m in values(modes._modes)) if (!m.hidden)])
         };
 
         options.add(["passunknown", "pu"],
index 7149f9cba230ad1a01c1c3ae8a1d91e193b985e0..e2ad98f5d459f512954d8da5ee283f9509e991da 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -90,14 +90,14 @@ var MOW = Module("mow", {
 
         if (modes.main != modes.OUTPUT_MULTILINE) {
             modes.push(modes.OUTPUT_MULTILINE, null, {
-                onKeyPress: this.closure.onKeyPress,
+                onKeyPress: this.bound.onKeyPress,
 
-                leave: this.closure(function leave(stack) {
+                leave: stack => {
                     if (stack.pop)
                         for (let message in values(this.messages))
                             if (message.leave)
                                 message.leave(stack);
-                }),
+                },
 
                 window: this.window
             });
index 7b0cd48d59b65da6d38df8b1de4d8e08f1198f96..caf0390f0bf335d25bfa93ae5047c2a2ec6ccab9 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -12,32 +12,55 @@ var StatusLine = Module("statusline", {
     init: function init() {
         this._statusLine = document.getElementById("status-bar");
         this.statusBar = document.getElementById("addon-bar") || this._statusLine;
+
         this.baseGroup = this.statusBar == this._statusLine ? "StatusLine " : "";
 
+        if (this.statusBar.localName == "toolbar" &&
+            this.statusBar.parentNode.id != "browser-bottombox")
+            overlay.overlayWindow(window, {
+                objects: this,
+                append: [
+                    ["vbox", { id: "browser-bottombox", xmlns: "xul" },
+                        ["toolbar", { id: "dactyl-addon-bar",
+                                      customizable: true,
+                                      defaultset: "",
+                                      toolboxid: "navigator-toolbox",
+                                      toolbarname: /*L*/ "Add-on Bar",
+                                      class: "toolbar-primary chromeclass-toolbar",
+                                      mode: "icons",
+                                      iconsize: "small", defaulticonsize: "small",
+                                      key: "statusBar" },
+                            ["statusbar", { id: "dactyl-status-bar", key: "_statusLine" }]]]
+                ]
+            });
+
         if (config.haveGecko("25"))
             config.tabbrowser.getStatusPanel().hidden = true;
 
         if (this.statusBar.localName == "toolbar") {
             styles.system.add("addon-bar", config.styleableChrome, literal(/*
-                #status-bar { margin-top: 0 !important; }
-                #addon-bar > statusbar { -moz-box-flex: 1 }
+                #status-bar, #dactyl-status-bar { margin-top: 0 !important; }
+                #dactyl-status-bar { min-height: 0 !important; }
+                :-moz-any(#addon-bar, #dactyl-addon-bar) > statusbar { -moz-box-flex: 1 }
+                :-moz-any(#addon-bar, #dactyl-addon-bar) > xul|toolbarspring { visibility: collapse; }
                 #addon-bar > #addonbar-closebutton { visibility: collapse; }
-                #addon-bar > xul|toolbarspring { visibility: collapse; }
             */));
 
             overlay.overlayWindow(window, {
                 append: [
-                    ["statusbar", { id: "status-bar", ordinal: "0" }]]
+                    ["statusbar", { id: this._statusLine.id, ordinal: "0" }]]
             });
 
             highlight.loadCSS(util.compileMacro(literal(/*
-                !AddonBar;#addon-bar {
+                !AddonBar;#addon-bar,#dactyl-addon-bar {
                     padding-left: 0 !important;
+                    padding-top: 0 !important;
+                    padding-bottom: 0 !important;
                     min-height: 18px !important;
                     -moz-appearance: none !important;
                     <padding>
                 }
-                !AddonButton;#addon-bar xul|toolbarbutton {
+                !AddonButton;,:-moz-any(#addon-bar, #dactyl-addon-bar) xul|toolbarbutton {
                     -moz-appearance: none !important;
                     padding: 0 !important;
                     border-width: 0px !important;
@@ -57,7 +80,7 @@ var StatusLine = Module("statusline", {
         let prepend = [
             ["button", { id: "appmenu-button", label: "", image: "chrome://branding/content/icon16.png", highlight: "AppmenuButton", xmlns: "xul" }],
             ["toolbarbutton", { id: "appmenu-toolbar-button", label: "", image: "chrome://branding/content/icon16.png" }],
-            ["statusbar", { id: "status-bar", highlight: "StatusLine", xmlns: "xul" },
+            ["statusbar", { id: this._statusLine.id, highlight: "StatusLine", xmlns: "xul" },
                 // <!-- insertbefore="dactyl.statusBefore;" insertafter="dactyl.statusAfter;" -->
                 ["hbox", { key: "container", hidden: "false", align: "center",  flex: "1" },
                     ["stack", { orient: "horizontal",       align: "stretch", flex: "1", highlight: "CmdLine StatusCmdLine", class: "dactyl-container" },
@@ -99,6 +122,11 @@ var StatusLine = Module("statusline", {
         catch (e) {}
     },
 
+    cleanup: function cleanup(reason) {
+        if (reason != "unload" && "CustomizableUI" in window)
+            CustomizableUI.unregisterArea(this.statusBar.id, false);
+    },
+
     get visible() !this.statusBar.collapsed && !this.statusBar.hidden,
 
     signals: {
index a7f038e3603b6c4004ed26b3d22aeabb2fa37f05..9bf588b92f54391400a58bd65092ba78e581a3b5 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -27,7 +27,8 @@ var Tabs = Module("tabs", {
             config.tabStrip.collapsed = true;
 
         this.tabStyle = styles.system.add("tab-strip-hiding", config.styleableChrome,
-                                          (config.tabStrip.id ? "#" + config.tabStrip.id : ".tabbrowser-strip") +
+                                          (config.tabStrip.id ? "#" + config.tabStrip.id
+                                                              : ".tabbrowser-strip") +
                                               "{ visibility: collapse; }",
                                           false, true);
 
@@ -73,28 +74,26 @@ var Tabs = Module("tabs", {
 
     updateTabCount: function updateTabCount() {
         for (let [i, tab] in Iterator(this.visibleTabs)) {
-            if (dactyl.has("Gecko2")) {
-                let node = function node(class_) document.getAnonymousElementByAttribute(tab, "class", class_);
-                if (!node("dactyl-tab-number")) {
-                    let img = node("tab-icon-image");
-                    if (img) {
-                        let dom = DOM([
-                            ["xul:hbox", { highlight: "tab-number" },
-                                ["xul:label", { key: "icon", align: "center", highlight: "TabIconNumber",
-                                                class: "dactyl-tab-icon-number" }]],
-                            ["xul:hbox", { highlight: "tab-number" },
-                                ["html:div", { key: "label", highlight: "TabNumber",
-                                               class: "dactyl-tab-number" }]]],
-                            document).appendTo(img.parentNode);
-
-                        update(tab, {
-                            get dactylOrdinal() Number(dom.nodes.icon.value),
-                            set dactylOrdinal(i) {
-                                dom.nodes.icon.value = dom.nodes.label.textContent = i;
-                                this.setAttribute("dactylOrdinal", i);
-                            }
-                        });
-                    }
+            let node = function node(class_) document.getAnonymousElementByAttribute(tab, "class", class_);
+            if (!node("dactyl-tab-number")) {
+                let img = node("tab-icon-image");
+                if (img) {
+                    let dom = DOM([
+                        ["xul:hbox", { highlight: "tab-number" },
+                            ["xul:label", { key: "icon", align: "center", highlight: "TabIconNumber",
+                                            class: "dactyl-tab-icon-number" }]],
+                        ["xul:hbox", { highlight: "tab-number" },
+                            ["html:div", { key: "label", highlight: "TabNumber",
+                                           class: "dactyl-tab-number" }]]],
+                        document).appendTo(img.parentNode);
+
+                    update(tab, {
+                        get dactylOrdinal() Number(dom.nodes.icon.value),
+                        set dactylOrdinal(i) {
+                            dom.nodes.icon.value = dom.nodes.label.textContent = i;
+                            this.setAttribute("dactylOrdinal", i);
+                        }
+                    });
                 }
             }
             tab.dactylOrdinal = i + 1;
@@ -408,7 +407,7 @@ var Tabs = Module("tabs", {
      * @param {number} count How many tabs to remove.
      * @param {boolean} focusLeftTab Focus the tab to the left of the removed tab.
      */
-    remove: function remove(tab, count = 1, focusLeftTab = false) {
+    remove: function remove(tab, count=1, focusLeftTab=false) {
         let res = this.count > count;
 
         let tabs = this.visibleTabs;
@@ -427,11 +426,11 @@ var Tabs = Module("tabs", {
         if (focusLeftTab)
             tabs.slice(Math.max(0, index + 1 - count),
                        index + 1)
-                .forEach(config.closure.removeTab);
+                .forEach(config.bound.removeTab);
         else
             tabs.slice(index,
                        index + count)
-                .forEach(config.closure.removeTab);
+                .forEach(config.bound.removeTab);
         return res;
     },
 
@@ -551,7 +550,7 @@ var Tabs = Module("tabs", {
         if (matches)
             return tabs.select(this.allTabs[parseInt(matches[1], 10) - 1], false);
 
-        matches = array.nth(tabs.allTabs, t => (t.linkedBrowser.lastURI || {}).spec === buffer, 0);
+        matches = tabs.allTabs.find(t => (t.linkedBrowser.lastURI || {}).spec === buffer);
         if (matches)
             return tabs.select(matches, false);
 
@@ -1038,7 +1037,7 @@ var Tabs = Module("tabs", {
             tabs.getGroups();
             tabs[visible ? "visibleTabs" : "allTabs"].forEach(function (tab, i) {
                 let group = (tab.tabItem || tab._tabViewTabItem || defItem).parent || defItem.parent;
-                if (!Set.has(tabGroups, group.id))
+                if (!hasOwnProperty(tabGroups, group.id))
                     tabGroups[group.id] = [group.getTitle(), []];
 
                 group = tabGroups[group.id];
@@ -1114,7 +1113,7 @@ var Tabs = Module("tabs", {
         }
         for (let event in values(["TabMove", "TabOpen", "TabClose"]))
             events.listen(tabContainer, event, callback, false);
-        events.listen(tabContainer, "TabSelect", tabs.closure._onTabSelect, false);
+        events.listen(tabContainer, "TabSelect", tabs.bound._onTabSelect, false);
     },
     mappings: function initMappings() {
 
@@ -1224,7 +1223,7 @@ var Tabs = Module("tabs", {
                         tabs.tabStyle.enabled = false;
                     }
 
-                    if (value !== "multitab" || !dactyl.has("Gecko2"))
+                    if (value !== "multitab")
                         if (tabs.xulTabs)
                             tabs.xulTabs.visible = value !== "never";
                         else
@@ -1263,11 +1262,11 @@ var Tabs = Module("tabs", {
                     values: activateGroups,
                     has: Option.has.toggleAll,
                     setter: function (newValues) {
-                        let valueSet = Set(newValues);
+                        let valueSet = RealSet(newValues);
                         for (let group in values(activateGroups))
                             if (group[2])
                                 prefs.safeSet("browser.tabs." + group[2],
-                                              !(valueSet["all"] ^ valueSet[group[0]]),
+                                              !(valueSet.has("all") ^ valueSet.has(group[0])),
                                               _("option.safeSet", "activate"));
                         return newValues;
                     }
index fc69e8fa40286fb8178497daab471f2c361a1d1b..da27f0b1feae88b0f971770f5c16dc851e5eef61 100644 (file)
@@ -162,13 +162,11 @@ download.nActive-1 = %S active
 download.almostDone = ~1 second
 download.unknown = Unknown
 
-download.action.Cancel = Cancel
 download.action.Clear = Clear
 download.action.Delete = Delete
-download.action.Pause = Pause
+download.action.Stop = Stop
 download.action.Remove = Remove
 download.action.Resume = Resume
-download.action.Retry = Retry
 
 editor.prompt.editPassword = Editing a password field externally will reveal the password. Would you like to continue? (yes/[no]):
 
index eed009c12da3a513b4e2954a9f63144e97bfbcbf..64de86061dd013ed0054758fff61923a97911b58 100644 (file)
             <dt>time</dt>     <dd>Time remaining</dd>
             <dt>url</dt>      <dd>Source URL</dd>
         </dl>
+        <note>
+            This option is currently unavailable in &dactyl.host; 26 and greater.
+        </note>
     </description>
 </item>
 
index bd2ec45f01b98bde5be88f793f43201d1f905017..f527f955b8023233f80b6c71bed6147805a5f026 100644 (file)
@@ -28,8 +28,6 @@
     <spec>:ha<oa>rdcopy</oa><oa>!</oa> ><a>filename</a></spec>
     <description>
         <p>As above, but write the output to <a>filename</a>.</p>
-
-        <note>Not available on Windows.</note>
     </description>
 </item>
 
index 3aaaa4d9e194d585b41016cf51ed1240529d49dd..b4cf84847521ec0330869f66936f19002ecab201 100644 (file)
 <item>
     <tags>&lt;record-macro> q</tags>
     <strut/>
-    <spec>q<a>0-9a-zA-Z</a></spec>
+    <spec>q<a>a-zA-Z</a></spec>
     <description>
         <p>
             Record a key sequence as a macro. Available macros are
-            <a>0-9a-zA-Z</a>. If the macro is an uppercase letter, the
+            <a>a-zA-Z</a>. If the macro is an uppercase letter, the
             recorded keys are appended to the lowercase macro of the same
             name. Typing <k>q</k> again stops the recording.
         </p>
 
 <item>
     <tags>&lt;play-macro> @</tags>
-    <spec><oa>count</oa>@<a>a-z0-9</a></spec>
+    <spec><oa>count</oa>@<a>a-z</a></spec>
     <description>
         <p>
-            Plays the contents of macro with name <a>a-z0-9</a> <oa>count</oa>
+            Plays the contents of macro with name <a>a-z</a> <oa>count</oa>
             times.
         </p>
     </description>
index fb1e59616a57e86d8bca74d159e32dfd852512b2..b42db8d7c4e416ad594ae3949e6ad06ea7bbba0f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2009-2014 Kris Maglione <maglione.k@gmail.com>
 // Copyright (c) 2009-2010 by Doug Kearns <dougkearns@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
@@ -110,7 +110,6 @@ var actions = {
         name: "extr[ehash]",
         description: "Reload an extension",
         action: function (addon) {
-            util.assert(config.haveGecko("2b"), _("command.notUseful", config.host));
             util.flushCache();
             util.timeout(function () {
                 addon.userDisabled = true;
@@ -165,7 +164,8 @@ var Addon = Class("Addon", {
     },
 
     commandAllowed: function commandAllowed(cmd) {
-        util.assert(Set.has(actions, cmd), _("addon.unknownCommand"));
+        util.assert(hasOwnProperty(actions, cmd),
+                    _("addon.unknownCommand"));
 
         let action = actions[cmd];
         if ("perm" in action && !(this.permissions & AddonManager["PERM_CAN_" + action.perm.toUpperCase()]))
@@ -258,14 +258,14 @@ var AddonList = Class("AddonList", {
         this.modules = modules;
         this.filter = filter && filter.toLowerCase();
         this.nodes = {};
-        this.addons = [];
+        this.addons = {};
         this.ready = false;
 
-        AddonManager.getAddonsByTypes(types, this.closure(function (addons) {
+        AddonManager.getAddonsByTypes(types, addons => {
             this._addons = addons;
             if (this.document)
                 this._init();
-        }));
+        });
         AddonManager.addAddonListener(this);
     },
     cleanup: function cleanup() {
@@ -273,7 +273,7 @@ var AddonList = Class("AddonList", {
     },
 
     _init: function _init() {
-        this._addons.forEach(this.closure.addAddon);
+        this._addons.forEach(this.bound.addAddon);
         this.ready = true;
         this.update();
     },
index e0f0eaad1bff85036755b1c98f512ce5bc81e733..b581186ea703f0418ef196f4562d0a5bffdf4abd 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2009-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -6,10 +6,19 @@
 
 var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+var Cs = new Proxy(Components.stack, {
+    get: function Cs_get(target, prop) Components.stack.caller[prop]
+});
+
+function module(url) {
+    let obj = {};
+    Cu.import(url, obj);
+    return obj;
+}
+
+var { XPCOMUtils } = module("resource://gre/modules/XPCOMUtils.jsm");
 try {
-    var ctypes;
-    Cu.import("resource://gre/modules/ctypes.jsm");
+    var ctypes = module("resource://gre/modules/ctypes.jsm");
 }
 catch (e) {}
 
@@ -17,15 +26,41 @@ let objproto = Object.prototype;
 let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__,
       hasOwnProperty, propertyIsEnumerable } = objproto;
 
-if (typeof XPCSafeJSObjectWrapper === "undefined")
-    this.XPCSafeJSObjectWrapper = XPCNativeWrapper;
+hasOwnProperty = Function.call.bind(hasOwnProperty);
+propertyIsEnumerable = Function.call.bind(propertyIsEnumerable);
 
-let getGlobalForObject = Cu.getGlobalForObject || (obj => obj.__parent__);
+// Gecko 24.
+if (!("find" in Array.prototype))
+    Object.defineProperty(Array.prototype, "find", {
+        configurable: true,
+        writable: true,
+        value: function Array_find(pred, self) {
+            for (let [i, elem] in Iterator(this))
+                if (pred.call(self, elem, i, this))
+                    return elem;
+        }
+    });
 
-function require(module, target) JSMLoader.load(module, target);
+if (!("findIndex" in Array.prototype))
+    Object.defineProperty(Array.prototype, "findIndex", {
+        configurable: true,
+        writable: true,
+        value: function Array_findIndex(pred, self) {
+            for (let [i, elem] in Iterator(this))
+                if (pred.call(self, elem, i, this))
+                    return i;
+            return -1;
+        }
+    });
+
+function require(module_, target) {
+    if (/^[A-Za-z0-9]+:/.test(module_))
+        return module(module_);
+    return JSMLoader.load(module_, target);
+}
 
 function lazyRequire(module, names, target) {
-    for each (let name in names)
+    for (let name of names)
         memoize(target || this, name, name => require(module)[name]);
 }
 
@@ -133,41 +168,78 @@ function require_(obj, name, from, targetName) {
 defineModule("base", {
     // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/      "\2",/p' base.jsm | sort | fmt
     exports: [
-        "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Finished", "Module", "JSMLoader",
-        "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils",
-        "XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry",
-        "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray",
-        "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "isXML", "iter",
-        "iterAll", "iterOwnProperties", "keys", "literal", "memoize", "octal", "properties",
-        "require", "set", "update", "values", "update_"
+        "Cc",
+        "Ci",
+        "Class",
+        "Cr",
+        "Cs",
+        "Cu",
+        "ErrorBase",
+        "Finished",
+        "JSMLoader",
+        "Module",
+        "RealSet",
+        "Set",
+        "Struct",
+        "StructBase",
+        "Timer",
+        "UTF8",
+        "XPCOM",
+        "XPCOMShim",
+        "XPCOMUtils",
+        "array",
+        "bind",
+        "call",
+        "callable",
+        "ctypes",
+        "curry",
+        "defineModule",
+        "deprecated",
+        "endModule",
+        "forEach",
+        "hasOwnProperty",
+        "isArray",
+        "isGenerator",
+        "isObject",
+        "isString",
+        "isSubclass",
+        "isinstance",
+        "iter",
+        "iterAll",
+        "iterOwnProperties",
+        "keys",
+        "literal",
+        "memoize",
+        "modujle",
+        "octal",
+        "properties",
+        "require",
+        "set",
+        "update",
+        "values",
     ]
 });
 
 this.lazyRequire("cache", ["cache"]);
 this.lazyRequire("config", ["config"]);
 this.lazyRequire("messages", ["_", "Messages"]);
+this.lazyRequire("promises", ["Task", "promises"]);
 this.lazyRequire("services", ["services"]);
 this.lazyRequire("storage", ["File"]);
 this.lazyRequire("util", ["FailedAssertion", "util"]);
 
+literal.files = {};
+literal.locations = {};
 function literal(/* comment */) {
     let { caller } = Components.stack;
     while (caller && caller.language != 2)
         caller = caller.caller;
 
     let file = caller.filename.replace(/.* -> /, "");
-    let key = "literal:" + file + ":" + caller.line;
-
-    let source = File.readURL(file);
-
-    let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" +
-                       ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source);
-    return match[1];
-
-    // Later...
-    return cache.get(key, function () {
-        let source = cache.get("literal:" + file,
-                               () => util.httpGet(file).responseText);
+    let key = "literal:" + file + ":" + caller.lineNumber;
+    return cache.get(key, function() {
+        let source = literal.files[file] || File.readURL(file);
+        literal.files[file] = source;
 
         let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" +
                            ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source);
@@ -175,25 +247,10 @@ function literal(/* comment */) {
     });
 }
 
-/**
- * Returns a list of all of the top-level properties of an object, by
- * way of the debugger.
- *
- * @param {object} obj
- * @returns [jsdIProperty]
- */
-function debuggerProperties(obj) {
-    if (loaded.services && services.debugger.isOn) {
-        let res = {};
-        services.debugger.wrapValue(obj).getProperties(res, {});
-        return res.value;
-    }
-}
-
 /**
  * Iterates over the names of all of the top-level properties of an
  * object or, if prototypes is given, all of the properties in the
- * prototype chain below the top. Uses the debugger if possible.
+ * prototype chain below the top.
  *
  * @param {object} obj The object to inspect.
  * @param {boolean} properties Whether to inspect the prototype chain
@@ -205,14 +262,14 @@ function prototype(obj)
     XPCNativeWrapper.unwrap(obj).__proto__ ||
     Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
 
-function properties(obj, prototypes, debugger_) {
+function properties(obj, prototypes) {
     let orig = obj;
-    let seen = { dactylPropertyNames: true };
+    let seen = RealSet(["dactylPropertyNames"]);
 
     try {
         if ("dactylPropertyNames" in obj && !prototypes)
             for (let key in values(obj.dactylPropertyNames))
-                if (key in obj && !Set.add(seen, key))
+                if (key in obj && !seen.add(key))
                     yield key;
     }
     catch (e) {}
@@ -250,16 +307,8 @@ function properties(obj, prototypes, debugger_) {
     }
 
     for (; obj; obj = prototypes && prototype(obj)) {
-        try {
-            if (!debugger_ || !services.debugger.isOn)
-                var iter = (v for each (v in props(obj)));
-        }
-        catch (e) {}
-        if (!iter)
-            iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
-
-        for (let key in iter)
-            if (!prototypes || !Set.add(seen, key) && obj != orig)
+        for (let key of props(obj))
+            if (!prototypes || !seen.add(key) && obj != orig)
                 yield key;
     }
 }
@@ -271,10 +320,14 @@ function iterOwnProperties(obj) {
 
 function deprecated(alternative, fn) {
     if (isObject(fn))
-        return Class.Property(iter(fn).map(([k, v]) => [k, callable(v) ? deprecated(alternative, v) : v])
+        return Class.Property(iter(fn).map(([k, v]) => [k,
+                                                        callable(v) ? deprecated(alternative, v)
+                                                                    : v])
                                       .toObject());
 
-    let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
+    let name,
+        func = callable(fn) ? fn
+                            : function () this[fn].apply(this, arguments);
 
     function deprecatedMethod() {
         let obj = !this                      ? "" :
@@ -282,9 +335,13 @@ function deprecated(alternative, fn) {
                   this.constructor.className ? this.constructor.className + "#" :
                       "";
 
-        deprecated.warn(func, obj + (fn.name || name), alternative);
+        deprecated.warn(func,
+                        obj + (fn.realName || fn.name || name || "").replace(/__/g, "."),
+                        alternative);
         return func.apply(this, arguments);
     }
+    if (func.name)
+        deprecatedMethod.realName = func.name;
 
     return callable(fn) ? deprecatedMethod : Class.Property({
         get: function () deprecatedMethod,
@@ -293,14 +350,20 @@ function deprecated(alternative, fn) {
 }
 deprecated.warn = function warn(func, name, alternative, frame) {
     if (!func.seenCaller)
-        func.seenCaller = Set([
+        func.seenCaller = RealSet([
             "resource://dactyl/javascript.jsm",
             "resource://dactyl/util.jsm"
         ]);
 
+    if (!(loaded.util && util && loaded.config && config.protocolLoaded)) {
+        dump("DACTYL: deprecated method called too early [" + [name, alternative] + "]:\n" + Error().stack + "\n\n");
+        return;
+    }
+
     frame = frame || Components.stack.caller.caller;
+
     let filename = util.fixURI(frame.filename || "unknown");
-    if (!Set.add(func.seenCaller, filename))
+    if (!func.seenCaller.add(filename))
         util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":")
                                    + _("warn.deprecated", name, alternative));
 }
@@ -313,9 +376,13 @@ deprecated.warn = function warn(func, name, alternative, frame) {
  * @returns {Generator}
  */
 function keys(obj) iter(function keys() {
-    for (var k in obj)
-        if (hasOwnProperty.call(obj, k))
+    if (isinstance(obj, ["Map"]))
+        for (let [k, v] of obj)
             yield k;
+    else
+        for (var k in obj)
+            if (hasOwnProperty(obj, k))
+                yield k;
 }());
 
 /**
@@ -326,18 +393,47 @@ function keys(obj) iter(function keys() {
  * @returns {Generator}
  */
 function values(obj) iter(function values() {
-    if (isinstance(obj, ["Generator", "Iterator", Iter]))
+    if (isinstance(obj, ["Map"]))
+        for (let [k, v] of obj)
+            yield v;
+    else if (isinstance(obj, ["Generator", "Iterator", Iter]))
         for (let k in obj)
             yield k;
+    else if (iter.iteratorProp in obj)
+        for (let v of obj)
+            yield v;
     else
         for (var k in obj)
-            if (hasOwnProperty.call(obj, k))
+            if (hasOwnProperty(obj, k))
                 yield obj[k];
 }());
 
 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
 
+var RealSet = Set;
+let Set_add = RealSet.prototype.add;
+RealSet.prototype.add = function RealSet_add(val) {
+    let res = this.has(val);
+    Set_add.apply(this, arguments);
+    return res;
+};
+
+RealSet.prototype.difference = function RealSet_difference(set) {
+    return RealSet(i for (i of this) if (!set.has(i)));
+};
+
+RealSet.prototype.intersection = function RealSet_intersection(set) {
+    return RealSet(i for (i of this) if (set.has(i)));
+};
+
+RealSet.prototype.union = function RealSet_union(set) {
+    let res = RealSet(this);
+    for (let item of set)
+        res.add(item);
+    return res;
+};
+
 /**
  * Utility for managing sets of strings. Given an array, returns an
  * object with one key for each value thereof.
@@ -345,13 +441,13 @@ var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments))
  * @param {[string]} ary @optional
  * @returns {object}
  */
-function Set(ary) {
+this.Set = deprecated("RealSet", function Set(ary) {
     let obj = {};
     if (ary)
         for (let val in values(ary))
             obj[val] = true;
     return obj;
-}
+});
 /**
  * Adds an element to a set and returns true if the element was
  * previously contained.
@@ -360,11 +456,18 @@ function Set(ary) {
  * @param {string} key The key to add.
  * @returns boolean
  */
-Set.add = curry(function set_add(set, key) {
-    let res = this.has(set, key);
-    set[key] = true;
-    return res;
-});
+Set.add = deprecated("RealSet#add",
+    curry(function Set__add(set, key) {
+        if (isinstance(set, ["Set"])) {
+            let res = set.has(key);
+            set.add(key);
+            return res;
+        }
+
+        let res = this.has(set, key);
+        set[key] = true;
+        return res;
+    }));
 /**
  * Returns true if the given set contains the given key.
  *
@@ -372,8 +475,14 @@ Set.add = curry(function set_add(set, key) {
  * @param {string} key The key to check.
  * @returns {boolean}
  */
-Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
-                                           propertyIsEnumerable.call(set, key));
+Set.has = deprecated("hasOwnProperty or Set#has",
+    curry(function Set__has(set, key) {
+        if (isinstance(set, ["Set"]))
+            return set.has(key);
+
+        return hasOwnProperty(set, key) &&
+               propertyIsEnumerable(set, key);
+    }));
 /**
  * Returns a new set containing the members of the first argument which
  * do not exist in any of the other given arguments.
@@ -381,13 +490,15 @@ Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
  * @param {object} set The set.
  * @returns {object}
  */
-Set.subtract = function set_subtract(set) {
-    set = update({}, set);
-    for (let i = 1; i < arguments.length; i++)
-        for (let k in keys(arguments[i]))
-            delete set[k];
-    return set;
-};
+Set.subtract = deprecated("RealSet#difference",
+    function set_subtract(set) {
+        set = update({}, set);
+        for (let i = 1; i < arguments.length; i++)
+            for (let k in keys(arguments[i]))
+                delete set[k];
+        return set;
+    });
+
 /**
  * Removes an element from a set and returns true if the element was
  * previously contained.
@@ -396,11 +507,15 @@ Set.subtract = function set_subtract(set) {
  * @param {string} key The key to remove.
  * @returns boolean
  */
-Set.remove = curry(function set_remove(set, key) {
-    let res = set.has(set, key);
-    delete set[key];
-    return res;
-});
+Set.remove = deprecated("RealSet#delete",
+    curry(function Set__remove(set, key) {
+        if (isinstance(set, ["Set"]))
+            return set.delete(key);
+
+        let res = set.has(set, key);
+        delete set[key];
+        return res;
+    }));
 
 function set() {
     deprecated.warn(set, "set", "Set");
@@ -446,7 +561,7 @@ function curry(fn, length, self, acc) {
     if (acc == null)
         acc = [];
 
-    return function curried(...args) {
+    function curried(...args) {
         // The curried result should preserve 'this'
         if (args.length == 0)
             return close(self || this, curried);
@@ -458,18 +573,13 @@ function curry(fn, length, self, acc) {
 
         return curry(fn, length, self || this, args);
     };
+    curried.realName = fn.realName || fn.name;
+    return curried;
 }
 
-if (curry.bind)
-    var bind = function bind(meth, self, ...args) let (func = callable(meth) ? meth : self[meth])
+var bind = function bind(meth, self, ...args)
+    let (func = callable(meth) ? meth : self[meth])
         func.bind.apply(func, [self].concat(args));
-else
-    var bind = function bind(func, self, ...args) {
-        if (!callable(func))
-            func = self[func];
-
-        return function bound(...args2) func.apply(self, args.concat(args2));
-    };
 
 /**
  * Returns true if both arguments are functions and
@@ -532,12 +642,6 @@ function isinstance(object, interfaces) {
  */
 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
 
-/**
- * Returns true if obje is an E4X XML object.
- * @deprecated
- */
-function isXML(obj) typeof obj === "xml";
-
 /**
  * Returns true if and only if its sole argument is an
  * instance of the builtin Array type. The array may come from
@@ -545,10 +649,8 @@ function isXML(obj) typeof obj === "xml";
  * is not the case when using (obj instanceof Array).
  */
 var isArray =
-    Array.isArray
-        // This is bloody stupid.
-        ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
-        : function isArray(val) objproto.toString.call(val) == "[object Array]";
+    // This is bloody stupid.
+    function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array";
 
 /**
  * Returns true if and only if its sole argument is an
@@ -589,7 +691,7 @@ function call(fn, self, ...args) {
 function memoize(obj, key, getter) {
     if (arguments.length == 1) {
         let res = update(Object.create(obj), obj);
-        for each (let prop in Object.getOwnPropertyNames(obj)) {
+        for (let prop of Object.getOwnPropertyNames(obj)) {
             let get = __lookupGetter__.call(obj, prop);
             if (get)
                 memoize(res, prop, get);
@@ -670,33 +772,6 @@ function update(target) {
     }
     return target;
 }
-function update_(target) {
-    for (let i = 1; i < arguments.length; i++) {
-        let src = arguments[i];
-        Object.getOwnPropertyNames(src || {}).forEach(function (k) {
-            let desc = Object.getOwnPropertyDescriptor(src, k);
-            if (desc.value instanceof Class.Property)
-                desc = desc.value.init(k, target) || desc.value;
-
-            try {
-                if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
-                    let func = desc.value.wrapped || desc.value;
-                    if (!func.superapply) {
-                        func.__defineGetter__("super", function get_super_() Object.getPrototypeOf(target)[k]);
-                        func.superapply = function super_apply(self, args)
-                            let (meth = Object.getPrototypeOf(target)[k])
-                                meth && meth.apply(self, args);
-                        func.supercall = function super_call(self, ...args)
-                            func.superapply(self, args);
-                    }
-                }
-                Object.defineProperty(target, k, desc);
-            }
-            catch (e) {}
-        });
-    }
-    return target;
-}
 
 /**
  * @constructor Class
@@ -729,34 +804,23 @@ function Class(...args) {
     if (callable(args[0]))
         superclass = args.shift();
 
-    if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418.
-        var Constructor = function Constructor() {
-            var self = Object.create(Constructor.prototype);
-            self.instance = self;
-            self.globalInstance = self;
-
-            if ("_metaInit_" in self && self._metaInit_)
-                self._metaInit_.apply(self, arguments);
-
-            var res = self.init.apply(self, arguments);
-            return res !== undefined ? res : self;
-        };
-    else
-        var Constructor = eval(String.replace('\n\
-            (function constructor(PARAMS) {                      \n\
-                var self = Object.create(Constructor.prototype); \n\
-                self.instance = self;                            \n\
-                self.globalInstance = self;                      \n\
-                                                                 \n\
-                if ("_metaInit_" in self && self._metaInit_)     \n\
-                    self._metaInit_.apply(self, arguments);      \n\
-                                                                 \n\
-                var res = self.init.apply(self, arguments);      \n\
-                return res !== undefined ? res : self;           \n\
-            })',
-            "constructor", (name || superclass.className).replace(/\W/g, "_"))
-                .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
-                                                           .replace(/\b(self|res|Constructor)\b/g, "$1_")));
+    var Constructor = eval(String.replace('\n\
+        (function constructor(PARAMS) {                      \n\
+            var self = Object.create(Constructor.prototype); \n\
+            self.instance = self;                            \n\
+            self.globalInstance = self;                      \n\
+                                                             \n\
+            if ("_metaInit_" in self && self._metaInit_)     \n\
+                self._metaInit_.apply(self, arguments);      \n\
+                                                             \n\
+            var res = self.init.apply(self, arguments);      \n\
+            return res !== undefined ? res : self;           \n\
+        })',
+        "constructor", (name || superclass.className).replace(/\W/g, "_"))
+            .replace("PARAMS",
+                     /^function .*?\((.*?)\)/
+                        .exec(args[0] && args[0].init || Class.prototype.init)[1]
+                        .replace(/\b(self|res|Constructor)\b/g, "$1_")));
 
     Constructor.className = name || superclass.className || superclass.name;
 
@@ -772,7 +836,10 @@ function Class(...args) {
     }
 
     Class.extend(Constructor, superclass, args[0]);
-    memoize(Constructor, "closure", Class.makeClosure);
+    memoize(Constructor, "bound", Class.makeClosure);
+    if (Iter && array) // Hack. :/
+        Object.defineProperty(Constructor, "closure",
+                              deprecated("bound", { get: function closure() this.bound }));
     update(Constructor, args[1]);
 
     Constructor.__proto__ = superclass;
@@ -785,21 +852,14 @@ function Class(...args) {
     return Constructor;
 }
 
-if (Cu.getGlobalForObject)
-    Class.objectGlobal = function (object) {
-        try {
-            return Cu.getGlobalForObject(object);
-        }
-        catch (e) {
-            return null;
-        }
-    };
-else
-    Class.objectGlobal = function (object) {
-        while (object.__parent__)
-            object = object.__parent__;
-        return object;
-    };
+Class.objectGlobal = function (object) {
+    try {
+        return Cu.getGlobalForObject(object);
+    }
+    catch (e) {
+        return null;
+    }
+};
 
 /**
  * @class Class.Property
@@ -863,16 +923,16 @@ Class.Memoize = function Memoize(getter, wait)
                         }
                     });
 
-                    util.yieldable(function () {
+                    Task.spawn(function () {
                         let wait;
                         for (var res in getter.call(obj)) {
                             if (wait !== undefined)
-                                yield wait;
+                                yield promises.sleep(wait);
                             wait = res;
                         }
                         Class.replaceProperty(obj, key, res);
                         done = true;
-                    })();
+                    });
 
                     return this[key];
                 };
@@ -956,8 +1016,21 @@ Class.prototype = {
                     util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
                 return;
             this.timeouts.splice(this.timeouts.indexOf(timer), 1);
-            util.trapErrors(callback, this);
+            try {
+                callback.call(this);
+            }
+            catch (e) {
+                try {
+                    util.dump("Error invoking timer callback registered at " +
+                              [frame.filename, frame.lineNumber, ""].join(":"));
+                    util.reportError(e);
+                }
+                catch (e) {
+                    Cu.reportError(e);
+                }
+            }
         };
+        let frame = Components.stack.caller;
         let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
         this.timeouts.push(timer);
         return timer;
@@ -996,7 +1069,7 @@ Class.prototype = {
                 }
 
                 try {
-                    if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties))
+                    if ("value" in desc && (this.localizedProperties.has(k) || this.magicalProperties.has(k)))
                         this[k] = desc.value;
                     else
                         Object.defineProperty(this, k, desc);
@@ -1007,8 +1080,8 @@ Class.prototype = {
         return this;
     },
 
-    localizedProperties: {},
-    magicalProperties: {}
+    localizedProperties: RealSet(),
+    magicalProperties: RealSet()
 };
 for (let name in properties(Class.prototype)) {
     let desc = Object.getOwnPropertyDescriptor(Class.prototype, name);
@@ -1016,37 +1089,39 @@ for (let name in properties(Class.prototype)) {
     Object.defineProperty(Class.prototype, name, desc);
 }
 
-Class.makeClosure = function makeClosure() {
-    const self = this;
-    function closure(fn) {
-        function _closure() {
-            try {
-                return fn.apply(self, arguments);
-            }
-            catch (e if !(e instanceof FailedAssertion)) {
-                util.reportError(e);
-                throw e.stack ? e : Error(e);
-            }
+var closureHooks = {
+    get: function closure_get(target, prop) {
+        if (hasOwnProperty(target._closureCache, prop))
+            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() self.get(target, prop)
         }
-        _closure.wrapped = fn;
-        return _closure;
     }
+    */
+};
 
-    iter(properties(this), properties(this, true)).forEach(function (k) {
-        if (!__lookupGetter__.call(this, k) && callable(this[k]))
-            closure[k] = closure(this[k]);
-        else if (!(k in closure))
-            Object.defineProperty(closure, k, {
-                configurable: true,
-                enumerable: true,
-                get: function get_proxy() self[k],
-                set: function set_proxy(val) self[k] = val,
-            });
-    }, this);
-
-    return closure;
+Class.makeClosure = function makeClosure() {
+    this._closureCache = {};
+
+    return new Proxy(this, closureHooks);
 };
-memoize(Class.prototype, "closure", Class.makeClosure);
+memoize(Class.prototype, "bound", Class.makeClosure);
 
 /**
  * A base class generator for classes which implement XPCOM interfaces.
@@ -1094,7 +1169,7 @@ let stub = Class.Property({
  */
 var ErrorBase = Class("ErrorBase", Error, {
     level: 2,
-    init: function EB_init(message, level = 0) {
+    init: function EB_init(message, level=0) {
         let error = Error(message);
         update(this, error);
         this.stack = error.stack;
@@ -1132,7 +1207,7 @@ function Module(name, prototype, ...args) {
         let proto = callable(prototype) ? args[0] : prototype;
 
         proto._metaInit_ = function () {
-            delete module.prototype._metaInit_;
+            module.prototype._metaInit_ = null;
             currentModule[name.toLowerCase()] = this;
         };
 
@@ -1223,6 +1298,7 @@ var StructBase = Class("StructBase", Array, {
 
     clone: function struct_clone() this.constructor.apply(null, this.slice()),
 
+    bound: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")),
     closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
 
     get: function struct_get(key, val) this[this.members[key]],
@@ -1268,7 +1344,7 @@ var StructBase = Class("StructBase", Array, {
 });
 
 var Timer = Class("Timer", {
-    init: function init(minInterval, maxInterval, callback, self = this) {
+    init: function init(minInterval, maxInterval, callback, self=this) {
         this._timer = services.Timer();
         this.callback = callback;
         this.self = self;
@@ -1381,6 +1457,7 @@ function octal(decimal) parseInt(decimal, 8);
  * @param {nsIJSIID} iface The interface to which to query all elements.
  * @returns {Generator}
  */
+iter.iteratorProp = "@@iterator" in [] ? "@@iterator" : "iterator";
 function iter(obj, iface) {
     if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
         return iter(obj).map(item => item.QueryInterface(iface));
@@ -1394,8 +1471,12 @@ function iter(obj, iface) {
                 for (let j in iter(args[i]))
                     yield j;
         })();
-    else if (isinstance(obj, ["Iterator", "Generator"]))
+    else if (isinstance(obj, ["Iterator", "Generator", "Array"]))
         ;
+    else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
+        res = array.iterItems(obj);
+    else if (iter.iteratorProp in obj && callable(obj[iter.iteratorProp]) && !("__iterator__" in obj))
+        res = (x for (x of obj));
     else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
         while (obj.constructor instanceof ctypes.PointerType)
             obj = obj.contents;
@@ -1409,8 +1490,6 @@ function iter(obj, iface) {
         else
             return iter({});
     }
-    else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
-        res = array.iterItems(obj);
     else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
              Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
         res = (function () {
@@ -1460,21 +1539,21 @@ update(iter, {
 
     every: function every(iter, pred, self) {
         pred = pred || util.identity;
-        for (let elem in iter)
+        for (let elem of iter)
             if (!pred.call(self, elem))
                 return false;
         return true;
     },
     some: function every(iter, pred, self) {
         pred = pred || util.identity;
-        for (let elem in iter)
+        for (let elem of iter)
             if (pred.call(self, elem))
                 return true;
         return false;
     },
 
     filter: function filter(iter, pred, self) {
-        for (let elem in iter)
+        for (let elem of iter)
             if (pred.call(self, elem))
                 yield elem;
     },
@@ -1488,13 +1567,13 @@ update(iter, {
      * @param {object} self The this object for *fn*.
      */
     forEach: function forEach(iter, func, self) {
-        for (let val in iter)
+        for (let val of iter)
             func.call(self, val);
     },
 
     indexOf: function indexOf(iter, elem) {
         let i = 0;
-        for (let item in iter) {
+        for (let item of iter) {
             if (item == elem)
                 return i;
             i++;
@@ -1510,7 +1589,7 @@ update(iter, {
      * @returns {Array}
      */
     map: function map(iter, func, self) {
-        for (let i in iter)
+        for (let i of iter)
             yield func.call(self, i);
     },
 
@@ -1522,19 +1601,30 @@ update(iter, {
         if (typeof pred === "number")
             [pred, n] = [() => true, pred]; // Hack.
 
-        for (let elem in iter)
+        for (let elem of iter)
             if (pred.call(self, elem) && n-- === 0)
                 return elem;
         return undefined;
     },
 
+    /**
+     * Analog of Array.find method. Returns the first item in the
+     * iterator for which `pred` returns true.
+     */
+    find: function find(iter, pred, self) {
+        for (let elem of iter)
+            if (pred.call(self, elem))
+                return elem;
+        return undefined;
+    },
+
     sort: function sort(iter, fn, self)
         array(this.toArray(iter).sort(fn, self)),
 
     uniq: function uniq(iter) {
-        let seen = {};
-        for (let item in iter)
-            if (!Set.add(seen, item))
+        let seen = RealSet();
+        for (let item of iter)
+            if (!seen.add(item))
                 yield item;
     },
 
@@ -1570,6 +1660,20 @@ const Iter = Class("Iter", {
 
     __iterator__: function () this.iter
 });
+iter.Iter = Iter;
+
+function arrayWrap(fn) {
+    function wrapper() {
+        let res = fn.apply(this, arguments);
+        if (isArray(res))
+            return array(res);
+        if (isinstance(res, ["Iterator", "Generator"]))
+            return iter(res);
+        return res;
+    }
+    wrapper.wrapped = fn;
+    return wrapper;
+}
 
 /**
  * Array utility methods.
@@ -1581,23 +1685,25 @@ var array = Class("array", Array, {
         else if (ary.length)
             ary = Array.slice(ary);
 
-        return {
-            __proto__: ary,
-            __iterator__: function () this.iterItems(),
-            __noSuchMethod__: function (meth, args) {
-                var res = array[meth].apply(null, [this.array].concat(args));
-                if (isArray(res))
-                    return array(res);
-                if (isinstance(res, ["Iterator", "Generator"]))
-                    return iter(res);
-                return res;
-            },
-            array: ary,
-            toString: function () this.array.toString(),
-            concat: function (...args) this.__noSuchMethod__("concat", args),
-            filter: function (...args) this.__noSuchMethod__("filter", args),
-            map: function (...args) this.__noSuchMethod__("map", args)
-        };
+        let self = this;
+        return new Proxy(ary, {
+            get: function array_get(target, prop) {
+                if (prop in array && callable(array[prop]))
+                    return arrayWrap(array[prop].bind(array, target));
+
+                if (prop == "array")
+                    return target;
+
+                let p = target[prop];
+                if (!/^\d+$/.test(prop) &&
+                    prop != "toString" &&
+                    prop != "toSource" &&
+                    callable(p))
+                    return arrayWrap(p);
+
+                return p;
+            }
+        });
     }
 }, {
     /**
@@ -1760,6 +1866,9 @@ Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
         };
 });
 
+Object.defineProperty(Class.prototype, "closure",
+                      deprecated("bound", { get: function closure() this.bound }));
+
 endModule();
 
 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
index 031618941e915c2e2d6121f7cad5344085be185a..66d056c84975f6a31253f9fa364a62524f4192dd 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright ©2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright ©2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -160,7 +160,7 @@ var BookmarkCache = Module("BookmarkCache", XPCOM(Ci.nsINavBookmarkObserver), {
         try {
             return services.bookmarks
                            .getBookmarkIdsForURI(uri, {})
-                           .some(this.closure.isRegularBookmark);
+                           .some(this.bound.isRegularBookmark);
         }
         catch (e) {
             return false;
index 4206133a8249b922b5cff8befbe73190ac3f0813..1325f2940564d56ef6c9fd4a91df05fefec65198 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2012 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2011-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -6,11 +6,11 @@
 
 var EXPORTED_SYMBOLS = ["require"];
 
-// Deal with cross-compartment XML passing issues.
 function create(proto) Object.create(proto);
+
 this["import"] = function import_(obj) {
     let res = {};
-    for each (let key in Object.getOwnPropertyNames(obj))
+    for (let key of Object.getOwnPropertyNames(obj))
         Object.defineProperty(res, key, Object.getOwnPropertyDescriptor(obj, key));
     return res;
 }
index 97b37b25787e0e88a1ba67e5efcb81b06c02d615..97398efa920803e5a35375396ddf893eb8fcc43f 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -16,6 +16,7 @@ lazyRequire("contexts", ["Group"]);
 lazyRequire("io", ["io"]);
 lazyRequire("finder", ["RangeFind"]);
 lazyRequire("overlay", ["overlay"]);
+lazyRequire("promises", ["Promise", "promises"]);
 lazyRequire("sanitizer", ["sanitizer"]);
 lazyRequire("storage", ["File", "storage"]);
 lazyRequire("template", ["template"]);
@@ -45,7 +46,7 @@ var Buffer = Module("Buffer", {
             this.win = win;
     },
 
-    get addPageInfoSection() Buffer.closure.addPageInfoSection,
+    get addPageInfoSection() Buffer.bound.addPageInfoSection,
 
     get pageInfo() Buffer.pageInfo,
 
@@ -68,6 +69,71 @@ var Buffer = Module("Buffer", {
         );
     },
 
+    /**
+     * The load context of the window bound to this buffer.
+     */
+    get loadContext() sanitizer.getContext(this.win),
+
+    /**
+     * Content preference methods.
+     */
+    prefs: Class.Memoize(function ()
+        let (self = this) ({
+            /**
+             * Returns a promise for the given preference name.
+             *
+             * @param {string} pref The name of the preference to return.
+             * @returns {Promise<*>}
+             */
+            get: promises.withCallbacks(function get([resolve, reject], pref) {
+                let val = services.contentPrefs.getCachedByDomainAndName(
+                    self.uri.spec, pref, self.loadContext);
+
+                let found = false;
+                if (val)
+                    resolve(val.value);
+                else
+                    services.contentPrefs.getByDomainAndName(
+                        self.uri.spec, pref, self.loadContext,
+                        { handleCompletion: () => {
+                              if (!found)
+                                  resolve(undefined);
+                          },
+                          handleResult: (pref) => {
+                              found = true;
+                              resolve(pref.value);
+                          },
+                          handleError: reject });
+            }),
+
+            /**
+             * Sets a content preference for the given buffer.
+             *
+             * @param {string} pref The preference to set.
+             * @param {string} value The value to store.
+             */
+            set: promises.withCallbacks(function set([resolve, reject], pref, value) {
+                services.contentPrefs.set(
+                    self.uri.spec, pref, value, self.loadContext,
+                    { handleCompletion: () => {},
+                      handleResult: resolve,
+                      handleError: reject });
+            }),
+
+            /**
+             * Clear a content preference for the given buffer.
+             *
+             * @param {string} pref The preference to clear.
+             */
+            clear: promises.withCallbacks(function clear([resolve, reject], pref) {
+                services.contentPrefs.removeByDomainAndName(
+                    self.uri.spec, pref, self.loadContext,
+                    { handleCompletion: () => {},
+                      handleResult: resolve,
+                      handleError: reject });
+            })
+        })),
+
     /**
      * Gets a content preference for the given buffer.
      *
@@ -77,14 +143,10 @@ var Buffer = Module("Buffer", {
      * @returns {string|number|boolean} The value of the preference, if
      *      callback is not provided.
      */
-    getPref: function getPref(pref, callback) {
-        // God damn it.
-        if (config.haveGecko("19.0a1"))
-            services.contentPrefs.getPref(this.uri, pref,
-                                          sanitizer.getContext(this.win), callback);
-        else
-            services.contentPrefs.getPref(this.uri, pref, callback);
-    },
+    getPref: deprecated("prefs.get", function getPref(pref, callback) {
+        services.contentPrefs.getPref(this.uri, pref,
+                                      this.loadContext, callback);
+    }),
 
     /**
      * Sets a content preference for the given buffer.
@@ -92,20 +154,20 @@ var Buffer = Module("Buffer", {
      * @param {string} pref The preference to set.
      * @param {string} value The value to store.
      */
-    setPref: function setPref(pref, value) {
+    setPref: deprecated("prefs.set", function setPref(pref, value) {
         services.contentPrefs.setPref(
-            this.uri, pref, value, sanitizer.getContext(this.win));
-    },
+            this.uri, pref, value, this.loadContext);
+    }),
 
     /**
      * Clear a content preference for the given buffer.
      *
      * @param {string} pref The preference to clear.
      */
-    clearPref: function clearPref(pref) {
+    clearPref: deprecated("prefs.clear", function clearPref(pref) {
         services.contentPrefs.removePref(
-            this.uri, pref, sanitizer.getContext(this.win));
-    },
+            this.uri, pref, this.loadContext);
+    }),
 
     climbUrlPath: function climbUrlPath(count) {
         let { dactyl } = this.modules;
@@ -525,8 +587,6 @@ var Buffer = Module("Buffer", {
             };
 
             DOM(elem).mousedown(params).mouseup(params);
-            if (!config.haveGecko("2b"))
-                DOM(elem).click(params);
 
             let sel = util.selectionController(win);
             sel.getSelection(sel.SELECTION_FOCUS_REGION).collapseToStart();
@@ -633,7 +693,7 @@ var Buffer = Module("Buffer", {
             return newURI.spec;
         }
 
-        for each (let shortener in Buffer.uriShorteners)
+        for (let shortener of Buffer.uriShorteners)
             try {
                 let shortened = shortener(uri, doc);
                 if (shortened)
@@ -644,9 +704,10 @@ var Buffer = Module("Buffer", {
             }
 
         let link = DOM("link[href][rev=canonical], \
-                        link[href][rel=shortlink]", doc);
-        if (link.length)
-            return hashify(link.attr("href"));
+                        link[href][rel=shortlink]", doc)
+                       .attr("href");
+        if (link)
+            return hashify(link);
 
         return null;
     },
@@ -793,7 +854,7 @@ var Buffer = Module("Buffer", {
      * @param {number} count The multiple of 'scroll' lines to scroll.
      * @optional
      */
-    scrollByScrollSize: function scrollByScrollSize(direction, count = 1) {
+    scrollByScrollSize: function scrollByScrollSize(direction, count=1) {
         let { options } = this.modules;
 
         direction = direction ? 1 : -1;
@@ -1104,7 +1165,7 @@ var Buffer = Module("Buffer", {
             else {
                 let url = loc || doc.location.href;
                 const PREFIX = "view-source:";
-                if (url.indexOf(PREFIX) == 0)
+                if (url.startsWith(PREFIX))
                     url = url.substr(PREFIX.length);
                 else
                     url = PREFIX + url;
@@ -1249,12 +1310,12 @@ var Buffer = Module("Buffer", {
         if (prefs.get("browser.zoom.siteSpecific")) {
             var privacy = sanitizer.getContext(this.win);
             if (value == 1) {
-                this.clearPref("browser.content.full-zoom");
-                this.clearPref("dactyl.content.full-zoom");
+                this.prefs.clear("browser.content.full-zoom");
+                this.prefs.clear("dactyl.content.full-zoom");
             }
             else {
-                this.setPref("browser.content.full-zoom", value);
-                this.setPref("dactyl.content.full-zoom", fullZoom);
+                this.prefs.set("browser.content.full-zoom", value);
+                this.prefs.set("dactyl.content.full-zoom", fullZoom);
             }
         }
 
@@ -1264,15 +1325,15 @@ var Buffer = Module("Buffer", {
     /**
      * Updates the zoom level of this buffer from a content preference.
      */
-    updateZoom: util.wrapCallback(function updateZoom() {
+    updateZoom: promises.task(function updateZoom() {
         let uri = this.uri;
 
         if (prefs.get("browser.zoom.siteSpecific")) {
-            this.getPref("dactyl.content.full-zoom", (val) => {
-                if (val != null && uri.equals(this.uri) && val != prefs.get("browser.zoom.full"))
-                    [this.contentViewer.textZoom, this.contentViewer.fullZoom] =
-                        [this.contentViewer.fullZoom, this.contentViewer.textZoom];
-            });
+            let val = yield this.prefs.get("dactyl.content.full-zoom");
+
+            if (val != null && uri.equals(this.uri) && val != prefs.get("browser.zoom.full"))
+                [this.contentViewer.textZoom, this.contentViewer.fullZoom] =
+                    [this.contentViewer.fullZoom, this.contentViewer.textZoom];
         }
     }),
 
@@ -1751,7 +1812,7 @@ var Buffer = Module("Buffer", {
                 let arg = args[0];
 
                 // FIXME: arg handling is a bit of a mess, check for filename
-                dactyl.assert(!arg || arg[0] == ">" && !config.OS.isWindows,
+                dactyl.assert(!arg || arg[0] == ">",
                               _("error.trailingCharacters"));
 
                 const PRINTER  = "PostScript/default";
@@ -1761,26 +1822,25 @@ var Buffer = Module("Buffer", {
                     BRANCHES.forEach(function (branch) { prefs.set(branch + pref, value); });
                 }
 
-                prefs.withContext(function () {
-                    if (arg) {
-                        prefs.set("print.print_printer", PRINTER);
-
-                        let { path } = io.File(arg.substr(1));
-                        set("print_to_file", true);
-                        set("print_to_filename", path);
-                        prefs.set("print_to_filename", path);
+                let settings = services.printSettings.newPrintSettings;
+                settings.printSilent = args.bang;
+                if (arg) {
+                    settings.printToFile = true;
+                    settings.toFileName = io.File(arg.substr(1)).path;
+                    settings.outputFormat = settings.kOutputFormatPDF;
 
-                        dactyl.echomsg(_("print.toFile", arg.substr(1)));
-                    }
-                    else
-                        dactyl.echomsg(_("print.sending"));
+                    dactyl.echomsg(_("print.toFile", arg.substr(1)));
+                }
+                else {
+                    dactyl.echomsg(_("print.sending"));
 
-                    prefs.set("print.always_print_silent", args.bang);
                     if (false)
                         prefs.set("print.show_print_progress", !args.bang);
+                }
 
-                    config.browser.contentWindow.print();
-                });
+                config.browser.contentWindow
+                      .QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIWebBrowserPrint).print(settings, null);
 
                 dactyl.echomsg(_("print.sent"));
             },
@@ -2009,7 +2069,7 @@ var Buffer = Module("Buffer", {
     events: function initEvents(dactyl, modules, window) {
         let { buffer, config, events } = modules;
 
-        events.listen(config.browser, "scroll", buffer.closure._updateBufferPosition, false);
+        events.listen(config.browser, "scroll", buffer.bound._updateBufferPosition, false);
     },
     mappings: function initMappings(dactyl, modules, window) {
         let { Editor, Events, buffer, editor, events, ex, mappings, modes, options, tabs } = modules;
@@ -2230,7 +2290,7 @@ var Buffer = Module("Buffer", {
 
                         elem = DOM(elem);
 
-                        if (elem[0].readOnly || !DOM(elem).isEditable)
+                        if (elem[0].readOnly || elem[0].disabled || !DOM(elem).isEditable)
                             return false;
 
                         let style = elem.style;
@@ -2428,10 +2488,9 @@ var Buffer = Module("Buffer", {
                             if (/^func:/.test(filter.result))
                                 var res = dactyl.userEval("(" + Option.dequote(filter.result.substr(5)) + ")")(doc, line);
                             else
-                                res = iter.nth(filter.matcher(doc),
-                                               elem => ((elem.nodeValue || elem.textContent).trim() == line &&
-                                                        DOM(elem).display != "none"),
-                                               0)
+                                res = iter.find(filter.matcher(doc),
+                                                elem => ((elem.nodeValue || elem.textContent).trim() == line &&
+                                                         DOM(elem).display != "none"))
                                    || iter.nth(filter.matcher(doc), util.identity, line - 1);
                             if (res)
                                 break;
index 3f8e2f1ce75d4436d6d199b1d0e14b266949041d..fc0459547e7de6bb264327ec0d6ae848b6626941 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2011-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -10,33 +10,41 @@ defineModule("cache", {
 });
 
 lazyRequire("overlay", ["overlay"]);
-lazyRequire("storage", ["File"]);
+lazyRequire("storage", ["File", "storage"]);
 
 var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
     init: function init() {
         this.queue = [];
-        this.cache = {};
+        this.storage = storage.newMap("cache", { store: true });
         this.providers = {};
         this.globalProviders = this.providers;
-        this.providing = {};
-        this.localProviders = {};
+        this.providing = RealSet();
+        this.localProviders = RealSet();
 
         if (JSMLoader.cacheFlush)
             this.flush();
 
         update(services["dactyl:"].providers, {
-            "cache": function (uri, path) {
+            "cache": (uri, path) => {
                 let contentType = "text/plain";
                 try {
                     contentType = services.mime.getTypeFromURI(uri);
                 }
                 catch (e) {}
 
-                if (!cache.cacheReader || !cache.cacheReader.hasEntry(path))
-                    return [contentType, cache.force(path)];
+                if (this.storage.has(path) ||
+                    !this.cacheReader ||
+                    !this.cacheReader.hasEntry(path))
+                    return [contentType, this.force(path)];
 
                 let channel = services.StreamChannel(uri);
-                channel.contentStream = cache.cacheReader.getInputStream(path);
+                try {
+                    channel.contentStream = this.cacheReader.getInputStream(path);
+                }
+                catch (e if e.result = Cr.NS_ERROR_FILE_CORRUPTED) {
+                    this.flushDiskCache();
+                    throw e;
+                }
                 channel.contentType = contentType;
                 channel.contentCharset = "UTF-8";
                 return channel;
@@ -54,7 +62,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
     }),
 
     parse: function parse(str) {
-        if (~'{['.indexOf(str[0]))
+        if ('{['.contains(str[0]))
             return JSON.parse(str);
         return str;
     },
@@ -83,8 +91,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
             }
             catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) {
                 util.reportError(e);
-                this.closeWriter();
-                this.cacheFile.remove(false);
+                this.flushDiskCache();
             }
 
         return this._cacheReader;
@@ -114,7 +121,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
     closeReader: function closeReader() {
         if (cache._cacheReader) {
             this.cacheReader.close();
-            delete cache._cacheReader;
+            cache._cacheReader = null;
         }
     },
 
@@ -123,7 +130,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
 
         if (this._cacheWriter) {
             this._cacheWriter.close();
-            delete cache._cacheWriter;
+            cache._cacheWriter = null;
 
             // ZipWriter bug.
             if (this.cacheFile.fileSize <= 22)
@@ -131,10 +138,20 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
         }
     }),
 
-    flush: function flush() {
-        cache.cache = {};
+    flush: function flush(filter) {
+        if (filter) {
+            this.storage.keys().filter(filter)
+                .forEach(bind("remove", this.storage));
+        }
+        else {
+            this.storage.clear();
+            this.flushDiskCache();
+        }
+    },
+
+    flushDiskCache: function flushDiskCache() {
         if (this.cacheFile.exists()) {
-            this.closeReader();
+            this.closeWriter();
 
             this.flushJAR(this.cacheFile);
             this.cacheFile.remove(false);
@@ -155,7 +172,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
             cache.processQueue();
         }
 
-        delete this.cache[name];
+        this.storage.remove(name);
     },
 
     flushJAR: function flushJAR(file) {
@@ -167,65 +184,80 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
     },
 
     force: function force(name, localOnly) {
+        if (this.storage.has(name))
+            return this.storage.get(name);
+
         util.waitFor(() => !this.inQueue);
 
         if (this.cacheReader && this.cacheReader.hasEntry(name)) {
-            return this.parse(File.readStream(
-                this.cacheReader.getInputStream(name)));
+            try {
+                return this.parse(File.readStream(
+                    this.cacheReader.getInputStream(name)));
+            }
+            catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) {
+                this.flushDiskCache();
+            }
         }
 
-        if (Set.has(this.localProviders, name) && !this.isLocal) {
-            for each (let { cache } in overlay.modules)
+        if (this.localProviders.has(name) && !this.isLocal) {
+            for (let { cache } of overlay.modules)
                 if (cache._has(name))
                     return cache.force(name, true);
         }
 
-        if (Set.has(this.providers, name)) {
-            util.assert(!Set.add(this.providing, name),
+        if (hasOwnProperty(this.providers, name)) {
+            util.assert(!this.providing.add(name),
                         "Already generating cache for " + name,
                         false);
+
+            let [func, long] = this.providers[name];
             try {
-                let [func, self] = this.providers[name];
-                this.cache[name] = func.call(self || this, name);
+                var value = func.call(this, name);
             }
             finally {
-                delete this.providing[name];
+                this.providing.delete(name);
             }
 
-            cache.queue.push([Date.now(), name]);
-            cache.processQueue();
+            if (!long)
+                this.storage.set(name, value);
+            else {
+                cache.queue.push([Date.now(), name, value]);
+                cache.processQueue();
+            }
 
-            return this.cache[name];
+            return value;
         }
 
         if (this.isLocal && !localOnly)
             return cache.force(name);
     },
 
-    get: function get(name, callback, self) {
-        if (!Set.has(this.cache, name)) {
-            if (callback && !(Set.has(this.providers, name) ||
-                              Set.has(this.localProviders, name)))
-                this.register(name, callback, self);
+    get: function get(name, callback, long) {
+        if (this.storage.has(name))
+            return this.storage.get(name);
 
-            this.cache[name] = this.force(name);
-            util.assert(this.cache[name] !== undefined,
-                        "No such cache key", false);
-        }
+        if (callback && !(hasOwnProperty(this.providers, name) ||
+                          this.localProviders.has(name)))
+            this.register(name, callback, long);
 
-        return this.cache[name];
+        var result = this.force(name);
+        util.assert(result !== undefined, "No such cache key", false);
+
+        return result;
     },
 
-    _has: function _has(name) Set.has(this.providers, name) || set.has(this.cache, name),
+    _has: function _has(name) hasOwnProperty(this.providers, name)
+                           || this.storage.has(name),
 
-    has: function has(name) [this.globalProviders, this.cache, this.localProviders]
-            .some(obj => Set.has(obj, name)),
+    has: function has(name) [this.globalProviders, this.localProviders]
+            .some(obj => isinstance(obj, ["Set"]) ? obj.has(name)
+                                                  : hasOwnProperty(obj, name)),
 
-    register: function register(name, callback, self) {
+    register: function register(name, callback, long) {
         if (this.isLocal)
-            Set.add(this.localProviders, name);
+            this.localProviders.add(name);
 
-        this.providers[name] = [callback, self];
+        this.providers[name] = [callback, long];
     },
 
     processQueue: function processQueue() {
@@ -235,18 +267,20 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), {
         if (this.queue.length && !this.inQueue) {
             // removeEntry does not work properly with queues.
             let removed = 0;
-            for each (let [, entry] in this.queue)
+            for (let [, entry] of this.queue)
                 if (this.getCacheWriter().hasEntry(entry)) {
                     this.getCacheWriter().removeEntry(entry, false);
                     removed++;
                 }
-            if (removed)
+            if (removed) {
                 this.closeWriter();
+                util.flushCache(this.cacheFile);
+            }
 
-            this.queue.splice(0).forEach(function ([time, entry]) {
-                if (time && Set.has(this.cache, entry)) {
+            this.queue.splice(0).forEach(function ([time, entry, value]) {
+                if (time && value != null) {
                     let stream = services.CharsetConv("UTF-8")
-                                         .convertToInputStream(this.stringify(this.cache[entry]));
+                                         .convertToInputStream(this.stringify(value));
 
                     this.getCacheWriter().addEntryStream(entry, time * 1000,
                                                          this.compression, stream,
index 0e0d58fc8b6468f2961eea1c7e50d6f96feef2e5..0fab7bcbe4d4ecbe8160ecff6d6b949226e0c1f8 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -155,7 +155,7 @@ var Command = Class("Command", {
      * @param {Args} args The Args object passed to {@link #action}.
      * @param {Object} modifiers Any modifiers to be passed to {@link #action}.
      */
-    execute: function execute(args, modifiers = {}) {
+    execute: function execute(args, modifiers={}) {
         const { dactyl } = this.modules;
 
         let context = args.context;
@@ -210,7 +210,7 @@ var Command = Class("Command", {
         extra: extra
     }),
 
-    complained: Class.Memoize(function () ({})),
+    complained: Class.Memoize(function () RealSet()),
 
     /**
      * @property {[string]} All of this command's name specs. e.g., "com[mand]"
@@ -325,7 +325,8 @@ var Command = Class("Command", {
 
                 explicitOpts: Class.Memoize(function () ({})),
 
-                has: function AP_has(opt) Set.has(this.explicitOpts, opt) || typeof opt === "number" && Set.has(this, opt),
+                has: function AP_has(opt) hasOwnProperty(this.explicitOpts, opt)
+                                       || typeof opt === "number" && hasOwnProperty(this, opt),
 
                 get literalArg() this.command.literal != null && this[this.command.literal] || "",
 
@@ -402,7 +403,9 @@ var Command = Class("Command", {
     warn: function warn(context, type, message) {
         let loc = !context ? "" : [context.file, context.line, " "].join(":");
 
-        if (!Set.add(this.complained, type + ":" + (context ? context.file : "[Command Line]")))
+        let key = type + ":" + (context ? context.file : "[Command Line]");
+
+        if (!this.complained.add(key))
             this.modules.dactyl.warn(loc + message);
     }
 }, {
@@ -498,22 +501,21 @@ var CommandHive = Class("CommandHive", Contexts.Hive, {
      */
 
     cache: function cache() {
-        let self = this;
         let { cache } = this.modules;
         this.cached = true;
 
-        let cached = cache.get(this.cacheKey, function () {
-            self.cached = false;
+        let cached = cache.get(this.cacheKey, () => {
+            this.cached = false;
             this.modules.moduleManager.initDependencies("commands");
 
             let map = {};
-            for (let [name, cmd] in Iterator(self._map))
+            for (let [name, cmd] in Iterator(this._map))
                 if (cmd.sourceModule)
                     map[name] = { sourceModule: cmd.sourceModule, isPlaceholder: true };
 
             let specs = [];
-            for (let cmd in values(self._list))
-                for each (let spec in cmd.parsedSpecs)
+            for (let cmd of this._list)
+                for (let spec of cmd.parsedSpecs)
                     specs.push(spec.concat(cmd.name));
 
             return { map: map, specs: specs };
@@ -553,7 +555,7 @@ var CommandHive = Class("CommandHive", Contexts.Hive, {
      * @param {boolean} replace Replace an existing command of the same name.
      *     @optional
      */
-    add: function add(specs, description, action, extra = {}, replace = false) {
+    add: function add(specs, description, action, extra={}, replace=false) {
         const { commands, contexts } = this.modules;
 
         if (!extra.definedAt)
@@ -592,7 +594,7 @@ var CommandHive = Class("CommandHive", Contexts.Hive, {
         return name;
     },
 
-    _add: function _add(names, description, action, extra = {}, replace = false) {
+    _add: function _add(names, description, action, extra={}, replace=false) {
         const { contexts } = this.modules;
         extra.definedAt = contexts.getCaller(Components.stack.caller.caller);
         return this.add.apply(this, arguments);
@@ -619,11 +621,12 @@ var CommandHive = Class("CommandHive", Contexts.Hive, {
      */
     get: function get(name, full) {
         let cmd = this._map[name]
-               || !full && array.nth(this._list, cmd => cmd.hasName(name), 0)
+               || !full && this._list.find(cmd => cmd.hasName(name))
                || null;
 
         if (!cmd && full) {
-            let name = array.nth(this.specs, spec => Command.hasName(spec, name), 0);
+            // Hrm. This is wrong. -Kris
+            let name = this._specs.find(spec => Command.hasName(spec, name));
             return name && this.get(name);
         }
 
@@ -756,11 +759,11 @@ var Commands = Module("commands", {
             const { commandline, completion } = this.modules;
             function completerToString(completer) {
                 if (completer)
-                    return [k for ([k, v] in Iterator(config.completers)) if (completer == completion.closure[v])][0] || "custom";
+                    return [k for ([k, v] in Iterator(config.completers)) if (completer == completion.bound[v])][0] || "custom";
                 return "";
             }
             // TODO: allow matching of aliases?
-            function cmds(hive) hive._list.filter(cmd => cmd.name.indexOf(filter || "") == 0)
+            function cmds(hive) hive._list.filter(cmd => cmd.name.startsWith(filter || ""))
 
             let hives = (hives || this.userHives).map(h => [h, cmds(h)])
                                                  .filter(([h, c]) => c.length);
@@ -828,9 +831,9 @@ var Commands = Module("commands", {
 
         return group._add.apply(group, arguments);
     },
-    addUserCommand: deprecated("group.commands.add", { get: function addUserCommand() this.user.closure._add }),
+    addUserCommand: deprecated("group.commands.add", { get: function addUserCommand() this.user.bound._add }),
     getUserCommands: deprecated("iter(group.commands)", function getUserCommands() iter(this.user).toArray()),
-    removeUserCommand: deprecated("group.commands.remove", { get: function removeUserCommand() this.user.closure.remove }),
+    removeUserCommand: deprecated("group.commands.remove", { get: function removeUserCommand() this.user.bound.remove }),
 
     /**
      * Returns the specified command invocation object serialized to
@@ -880,7 +883,7 @@ var Commands = Module("commands", {
      * @returns {Command}
      */
     get: function get(name, full) iter(this.hives).map(([i, hive]) => hive.get(name, full))
-                                                  .nth(util.identity, 0),
+                                                  .find(util.identity),
 
     /**
      * Returns true if a command invocation contains a URL referring to the
@@ -964,7 +967,7 @@ var Commands = Module("commands", {
      *     Args object.
      * @returns {Args}
      */
-    parseArgs: function parseArgs(str, params = {}) {
+    parseArgs: function parseArgs(str, params={}) {
         const self = this;
 
         function getNextArg(str, _keepQuotes=keepQuotes) {
@@ -1011,7 +1014,7 @@ var Commands = Module("commands", {
             let matchOpts = function matchOpts(arg) {
                 // Push possible option matches into completions
                 if (complete && !onlyArgumentsRemaining)
-                    completeOpts = options.filter(opt => (opt.multiple || !Set.has(args, opt.names[0])));
+                    completeOpts = options.filter(opt => (opt.multiple || !hasOwnProperty(args, opt.names[0])));
             };
             let resetCompletions = function resetCompletions() {
                 completeOpts = null;
@@ -1063,7 +1066,7 @@ var Commands = Module("commands", {
                 if (!onlyArgumentsRemaining) {
                     for (let [, opt] in Iterator(options)) {
                         for (let [, optname] in Iterator(opt.names)) {
-                            if (sub.indexOf(optname) == 0) {
+                            if (sub.startsWith(optname)) {
                                 let count = 0;
                                 let invalid = false;
                                 let arg, quote, quoted;
@@ -1110,7 +1113,7 @@ var Commands = Module("commands", {
 
                                             if (arg == null || (typeof arg == "number" && isNaN(arg))) {
                                                 if (!complete || orig != "" || args.completeStart != str.length)
-                                                    fail(_("command.invalidOptTypeArg", opt.type.description, optname, argString));
+                                                    fail(_("command.invalidOptTypeArg", opt.type.description, optname, quoted));
                                                 if (complete)
                                                     complete.highlight(args.completeStart, count - 1, "SPELLCHECK");
                                             }
@@ -1119,7 +1122,7 @@ var Commands = Module("commands", {
                                         // we have a validator function
                                         if (typeof opt.validator == "function") {
                                             if (opt.validator(arg, quoted) == false && (arg || !complete)) {
-                                                fail(_("command.invalidOptArg", optname, argString));
+                                                fail(_("command.invalidOptArg", optname, quoted));
                                                 if (complete) // Always true.
                                                     complete.highlight(args.completeStart, count - 1, "SPELLCHECK");
                                             }
@@ -1543,7 +1546,7 @@ var Commands = Module("commands", {
             function (args) {
                 let cmd = args[0];
 
-                util.assert(!cmd || cmd.split(",").every(commands.validName.closure.test),
+                util.assert(!cmd || cmd.split(",").every(commands.validName.bound.test),
                             _("command.invalidName", cmd));
 
                 if (args.length <= 1)
@@ -1573,7 +1576,7 @@ var Commands = Module("commands", {
                             };
                         }
                         else
-                            completerFunc = context => modules.completion.closure[config.completers[completer]](context);
+                            completerFunc = context => modules.completion.bound[config.completers[completer]](context);
                     }
 
                     let added = args["-group"].add(cmd.split(","),
@@ -1721,7 +1724,7 @@ var Commands = Module("commands", {
                 ]
             })),
             iterateIndex: function (args) let (tags = help.tags)
-                this.iterate(args).filter(cmd => (cmd.hive === commands.builtin || Set.has(tags, cmd.helpTag))),
+                this.iterate(args).filter(cmd => (cmd.hive === commands.builtin || hasOwnProperty(tags, cmd.helpTag))),
             format: {
                 headings: ["Command", "Group", "Description"],
                 description: function (cmd) template.linkifyHelp(cmd.description + (cmd.replacementText ? ": " + cmd.action : "")),
@@ -1772,7 +1775,7 @@ var Commands = Module("commands", {
     }
 });
 
-let quote = function quote(q, list, map = Commands.quoteMap) {
+let quote = function quote(q, list, map=Commands.quoteMap) {
     let re = RegExp("[" + list + "]", "g");
     function quote(str) (q + String.replace(str, re, $0 => ($0 in map ? map[$0] : ("\\" + $0)))
                            + q);
index 001b267b7d1d84f0b63b5322c9eed5a6eab9362c..7d2b6c11da5777f10acaa8be551ee9563c682648 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -33,7 +33,7 @@ lazyRequire("template", ["template"]);
  * @constructor
  */
 var CompletionContext = Class("CompletionContext", {
-    init: function cc_init(editor, name = "", offset = 0) {
+    init: function cc_init(editor, name="", offset=0) {
         let self = this;
         if (editor instanceof this.constructor) {
             let parent = editor;
@@ -512,7 +512,7 @@ var CompletionContext = Class("CompletionContext", {
                 filtered.sort(this.compare);
                 if (!this.anchored) {
                     let filter = this.filter;
-                    filtered.sort(function s(a, b) (b.text.indexOf(filter) == 0) - (a.text.indexOf(filter) == 0));
+                    filtered.sort(function s(a, b) b.text.startsWith(filter) - a.text.startsWith(filter));
                 }
             }
 
@@ -549,7 +549,7 @@ var CompletionContext = Class("CompletionContext", {
             var substrings = [text];
         }
         else {
-            var compare = function compare(text, s) text.indexOf(s) >= 0;
+            var compare = function compare(text, s) text.contains(s);
             var substrings = [];
             let start = 0;
             let idx;
@@ -733,7 +733,7 @@ var CompletionContext = Class("CompletionContext", {
         let alias = (prop) => {
             context.__defineGetter__(prop, () => this[prop]);
             context.__defineSetter__(prop, (val) => this[prop] = val);
-        }
+        };
         alias("_cache");
         alias("_completions");
         alias("_generate");
@@ -843,7 +843,7 @@ var CompletionContext = Class("CompletionContext", {
         }
         this.waitingForTab = false;
         this.runCount++;
-        for each (let context in this.contextList)
+        for (let context of this.contextList)
             context.lastActivated = this.runCount;
         this.contextList = [];
     },
@@ -970,7 +970,7 @@ var Completion = Module("completion", {
                 context.generate = function generate_() {
                     return [[k.substr(services.ABOUT.length), ""]
                             for (k in Cc)
-                            if (k.indexOf(services.ABOUT) == 0)];
+                            if (k.startsWith(services.ABOUT))];
                 };
             });
 
@@ -1056,7 +1056,7 @@ var Completion = Module("completion", {
         let contains = String.indexOf;
         if (context.ignoreCase) {
             compare = util.compareIgnoreCase;
-            contains = function contains_(a, b) a && a.toLowerCase().indexOf(b.toLowerCase()) > -1;
+            contains = function contains_(a, b) a && a.toLowerCase().contains(b.toLowerCase());
         }
 
         if (tags)
@@ -1180,12 +1180,13 @@ var Completion = Module("completion", {
                                 .concat([let (name = k.substr(services.AUTOCOMPLETE.length))
                                             ["native:" + name, _("autocomplete.description", name)]
                                          for (k in Cc)
-                                         if (k.indexOf(services.AUTOCOMPLETE) == 0)]),
+                                         if (k.startsWith(services.AUTOCOMPLETE))]),
 
                 setter: function setter(values) {
-                    if (values.length == 1 && !Set.has(values[0], this.values)
-                            && Array.every(values[0], Set.has(this.valueMap)))
-                        return Array.map(values[0], function m(v) this[v], this.valueMap);
+                    if (values.length == 1 && !hasOwnProperty(values[0], this.values)
+                            && Array.every(values[0], v => hasOwnProperty(this.valueMap, v)))
+                        return Array.map(values[0], v => this.valueMap[v]);
+
                     return values;
                 },
 
index d009e1a4ca602024eae72ffccabb8836abaa9c71..dfae6a549bd318de2160b173c988e3cbba39606b 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -9,16 +9,19 @@
 let global = this;
 defineModule("config", {
     exports: ["ConfigBase", "Config", "config"],
-    require: ["dom", "io", "protocol", "services", "util", "template"]
+    require: ["io", "protocol", "services"]
 });
 
 lazyRequire("addons", ["AddonManager"]);
 lazyRequire("cache", ["cache"]);
+lazyRequire("dom", ["DOM"]);
 lazyRequire("highlight", ["highlight"]);
 lazyRequire("messages", ["_"]);
 lazyRequire("prefs", ["localPrefs", "prefs"]);
 lazyRequire("storage", ["storage", "File"]);
 lazyRequire("styles", ["Styles"]);
+lazyRequire("template", ["template"]);
+lazyRequire("util", ["util"]);
 
 function AboutHandler() {}
 AboutHandler.prototype = {
@@ -45,19 +48,30 @@ var ConfigBase = Class("ConfigBase", {
      * initialization code. Must call superclass's init function.
      */
     init: function init() {
-        this.loadConfig();
+        if (!config.haveGecko("26"))
+            this.modules.global = this.modules.global.filter(m => m != "downloads"); // FIXME
 
-        this.features.push = deprecated("Set.add", function push(feature) Set.add(this, feature));
-        if (this.haveGecko("2b"))
-            Set.add(this.features, "Gecko2");
+        this.loadConfig();
 
-        JSMLoader.registerFactory(JSMLoader.Factory(AboutHandler));
-        JSMLoader.registerFactory(JSMLoader.Factory(
-            Protocol("dactyl", "{9c8f2530-51c8-4d41-b356-319e0b155c44}",
-                     "resource://dactyl-content/")));
+        util.trapErrors(() => {
+            JSMLoader.registerFactory(JSMLoader.Factory(AboutHandler));
+        });
+        util.withProperErrors(() => {
+            JSMLoader.registerFactory(JSMLoader.Factory(
+                Protocol("dactyl", "{9c8f2530-51c8-4d41-b356-319e0b155c44}",
+                         "resource://dactyl-content/")));
+        });
 
+        this.protocolLoaded = true;
         this.timeout(function () {
-            cache.register("config.dtd", () => util.makeDTD(config.dtd));
+            cache.register("config.dtd", () => util.makeDTD(config.dtd),
+                           true);
+        });
+
+        // FIXME: May not be ready before first window opens.
+        AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", a => {
+            if (!a.isActive)
+                config.features.delete("default-theme");
         });
 
         services["dactyl:"].pages["dtd"] = () => [null, cache.get("config.dtd")];
@@ -70,7 +84,7 @@ var ConfigBase = Class("ConfigBase", {
 
     get prefs() localPrefs,
 
-    get has() Set.has(this.features),
+    has: function (feature) this.features.has(feature),
 
     configFiles: [
         "resource://dactyl-common/config.json",
@@ -81,7 +95,7 @@ var ConfigBase = Class("ConfigBase", {
 
     loadConfig: function loadConfig(documentURL) {
 
-        for each (let config in this.configs) {
+        for (let config of this.configs) {
             if (documentURL)
                 config = config.overlays && config.overlays[documentURL] || {};
 
@@ -90,6 +104,9 @@ var ConfigBase = Class("ConfigBase", {
 
                 if (isArray(this[prop]))
                     this[prop] = [].concat(this[prop], value);
+                else if (isinstance(this[prop], ["Set"]))
+                    for (let key of value)
+                        this[prop].add(key);
                 else if (isObject(this[prop])) {
                     if (isArray(value))
                         value = Set(value);
@@ -126,6 +143,7 @@ var ConfigBase = Class("ConfigBase", {
                  "options",
                  "overlay",
                  "prefs",
+                 ["promises", "Promise", "Task", "promises"],
                  "protocol",
                  "sanitizer",
                  "services",
@@ -157,17 +175,6 @@ var ConfigBase = Class("ConfigBase", {
         highlight.loadCSS(this.helpCSS.replace(/__MSG_(.*?)__/g,
                                                (m0, m1) => _(m1)));
 
-        if (!this.haveGecko("2b"))
-            highlight.loadCSS(literal(/*
-                !TabNumber               font-weight: bold; margin: 0px; padding-right: .8ex;
-                !TabIconNumber  {
-                    font-weight: bold;
-                    color: white;
-                    text-align: center;
-                    text-shadow: black -1px 0 1px, black 0 1px 1px, black 1px 0 1px, black 0 -1px 1px;
-                }
-            */));
-
         let hl = highlight.set("Find", "");
         hl.onChange = function () {
             function hex(val) ("#" + util.regexp.iterate(/\d+/g, val)
@@ -246,7 +253,7 @@ var ConfigBase = Class("ConfigBase", {
     bestLocale: function (list) {
         return values([this.appLocale, this.appLocale.replace(/-.*/, ""),
                        "en", "en-US", list[0]])
-            .nth(Set.has(Set(list)), 0);
+            .find(bind("has", RealSet(list)));
     },
 
     /**
@@ -256,7 +263,7 @@ var ConfigBase = Class("ConfigBase", {
         // Horrible hack.
         let res = {};
         function process(manifest) {
-            for each (let line in manifest.split(/\n+/)) {
+            for (let line of manifest.split(/\n+/)) {
                 let match = /^\s*(content|skin|locale|resource)\s+([^\s#]+)\s/.exec(line);
                 if (match)
                     res[match[2]] = true;
@@ -274,7 +281,7 @@ var ConfigBase = Class("ConfigBase", {
                 }
         }
 
-        for each (let dir in ["UChrm", "AChrom"]) {
+        for (let dir of ["UChrm", "AChrom"]) {
             dir = File(services.directory.get(dir, Ci.nsIFile));
             if (dir.exists() && dir.isDirectory())
                 for (let file in dir.iterDirectory())
@@ -401,7 +408,7 @@ var ConfigBase = Class("ConfigBase", {
     dtd: Class.Memoize(function ()
         iter(this.dtdExtra,
              (["dactyl." + k, v] for ([k, v] in iter(config.dtdDactyl))),
-             (["dactyl." + s, config[s]] for each (s in config.dtdStrings)))
+             (["dactyl." + s, config[s]] for (s of config.dtdStrings)))
             .toObject()),
 
     dtdDactyl: memoize({
@@ -458,7 +465,7 @@ var ConfigBase = Class("ConfigBase", {
                     ["menupopup", { id: "viewSidebarMenu", xmlns: "xul" }],
                     ["broadcasterset", { id: "mainBroadcasterSet", xmlns: "xul" }]];
 
-            for each (let [id, [name, key, uri]] in Iterator(this.sidebars)) {
+            for (let [id, [name, key, uri]] in Iterator(this.sidebars)) {
                 append[0].push(
                         ["menuitem", { observes: "pentadactyl-" + id + "Sidebar", label: name,
                                        accesskey: key }]);
@@ -539,7 +546,7 @@ var ConfigBase = Class("ConfigBase", {
      *    dactyl.has(feature) to check for a feature's presence
      *    in this array.
      */
-    features: {},
+    features: RealSet(["default-theme"]),
 
     /**
      * @property {string} The file extension used for command script files.
@@ -586,7 +593,7 @@ config.INIT = update(Object.create(config.INIT), config.INIT, {
     init: function init(dactyl, modules, window) {
         init.superapply(this, arguments);
 
-        let img = window.Image();
+        let img = new window.Image;
         img.src = this.logo || "resource://dactyl-local-content/logo.png";
         img.onload = util.wrapCallback(function () {
             highlight.loadCSS(literal(/*
index 3c0ca3a94c39ea4ee79ae07addef934043fe1862..2a6e46ddf9c257cf84065121047074928aa0e56d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2010-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2010-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -43,14 +43,14 @@ var Group = Class("Group", {
             delete this[hive];
 
         if (reason != "shutdown")
-            this.children.splice(0).forEach(this.contexts.closure.removeGroup);
+            this.children.splice(0).forEach(this.contexts.bound.removeGroup);
     },
     destroy: function destroy(reason) {
         for (let hive in values(this.hives))
             util.trapErrors("destroy", hive);
 
         if (reason != "shutdown")
-            this.children.splice(0).forEach(this.contexts.closure.removeGroup);
+            this.children.splice(0).forEach(this.contexts.bound.removeGroup);
     },
 
     argsExtra: function argsExtra() ({}),
@@ -67,8 +67,9 @@ var Group = Class("Group", {
 }, {
     compileFilter: function (patterns, default_=false) {
         function siteFilter(uri)
-            let (match = array.nth(siteFilter.filters, f => f(uri), 0))
-                match ? match.result : default_;
+            let (match = siteFilter.filters.find(f => f(uri)))
+                match ? match.result
+                      : default_;
 
         return update(siteFilter, {
             toString: function () this.filters.join(","),
@@ -138,22 +139,26 @@ var Contexts = Module("contexts", {
                 completer: function (context) modules.completion.group(context)
             });
 
-            memoize(modules, "userContext",  () => contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules, true]));
-            memoize(modules, "_userContext", () => contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules.userContext]));
+            memoize(modules, "userContext",  () => contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules, false]));
+            memoize(modules, "_userContext", () => modules.userContext);
         },
 
         cleanup: function () {
-            for each (let hive in this.groupList.slice())
+            for (let hive of this.groupList.slice())
                 util.trapErrors("cleanup", hive, "shutdown");
         },
 
         destroy: function () {
-            for each (let hive in values(this.groupList.slice()))
+            for (let hive of values(this.groupList.slice()))
                 util.trapErrors("destroy", hive, "shutdown");
 
-            for (let [name, plugin] in iter(this.modules.plugins.contexts))
+            for each (let plugin in this.modules.plugins.contexts) {
                 if (plugin && "onUnload" in plugin && callable(plugin.onUnload))
                     util.trapErrors("onUnload", plugin);
+
+                if (isinstance(plugin, ["Sandbox"]))
+                    util.trapErrors("nukeSandbox", Cu, plugin);
+            }
         },
 
         signals: {
@@ -189,7 +194,9 @@ var Contexts = Module("contexts", {
                                                           { _hive: { value: name } })));
 
                 memoize(contexts.groupsProto, name,
-                        function () [group[name] for (group in values(this.groups)) if (Set.has(group, name))]);
+                        function () [group[name]
+                                     for (group in values(this.groups))
+                                     if (hasOwnProperty(group, name))]);
             },
 
             get toStringParams() [this.name, this.Hive]
@@ -199,25 +206,23 @@ var Contexts = Module("contexts", {
     Context: function Context(file, group, args) {
         const { contexts, io, newContext, plugins, userContext } = this.modules;
 
-        let isPlugin  = array.nth(io.getRuntimeDirectories("plugins"),
-                                  dir => dir.contains(file, true),
-                                  0);
-        let isRuntime = array.nth(io.getRuntimeDirectories(""),
-                                  dir => dir.contains(file, true),
-                                  0);
+        let isPlugin  = io.getRuntimeDirectories("plugins")
+                          .find(dir => dir.contains(file, true));
+        let isRuntime = io.getRuntimeDirectories("")
+                          .find(dir => dir.contains(file, true));
 
         let name = isPlugin ? file.getRelativeDescriptor(isPlugin).replace(File.PATH_SEP, "-")
                             : file.leafName;
         let id   = util.camelCase(name.replace(/\.[^.]*$/, ""));
 
         let contextPath = file.path;
-        let self = Set.has(plugins, contextPath) && plugins.contexts[contextPath];
+        let self = hasOwnProperty(plugins, contextPath) && plugins.contexts[contextPath];
 
         if (!self && isPlugin && false)
-            self = Set.has(plugins, id) && plugins[id];
+            self = hasOwnProperty(plugins, id) && plugins[id];
 
         if (self) {
-            if (Set.has(self, "onUnload"))
+            if (hasOwnProperty(self, "onUnload"))
                 util.trapErrors("onUnload", self);
         }
         else {
@@ -302,15 +307,14 @@ var Contexts = Module("contexts", {
         if (uri instanceof Ci.nsIFileURL)
             var file = File(uri.file);
 
-        let isPlugin = array.nth(io.getRuntimeDirectories("plugins"),
-                                 dir => dir.contains(file, true),
-                                 0);
+        let isPlugin = io.getRuntimeDirectories("plugins")
+                         .find(dir => dir.contains(file, true));
 
         let name = isPlugin && file && file.getRelativeDescriptor(isPlugin)
                                            .replace(File.PATH_SEP, "-");
         let id   = util.camelCase(name.replace(/\.[^.]*$/, ""));
 
-        let self = Set.has(this.pluginModules, canonical) && this.pluginModules[canonical];
+        let self = hasOwnProperty(this.pluginModules, canonical) && this.pluginModules[canonical];
 
         if (!self) {
             self = Object.create(jsmodules);
@@ -336,7 +340,7 @@ var Contexts = Module("contexts", {
                         delete contexts.pluginModules[canonical];
                     }
 
-                    for each (let { plugins } in overlay.modules)
+                    for (let { plugins } of overlay.modules)
                         if (plugins[this.NAME] == this)
                             delete plugins[this.name];
                 })
@@ -411,7 +415,7 @@ var Contexts = Module("contexts", {
 
     initializedGroups: function (hive)
         let (need = hive ? [hive] : Object.keys(this.hives))
-            this.groupList.filter(group => need.some(Set.has(group))),
+            this.groupList.filter(group => need.some(hasOwnProperty.bind(null, group))),
 
     addGroup: function addGroup(name, description, filter, persist, replace) {
         let group = this.getGroup(name);
@@ -468,7 +472,7 @@ var Contexts = Module("contexts", {
     getGroup: function getGroup(name, hive) {
         if (name === "default")
             var group = this.context && this.context.context && this.context.context.GROUP;
-        else if (Set.has(this.groupMap, name))
+        else if (hasOwnProperty(this.groupMap, name))
             group = this.groupMap[name];
 
         if (group && hive)
@@ -478,14 +482,8 @@ var Contexts = Module("contexts", {
 
     getDocs: function getDocs(context) {
         try {
-            if (isinstance(context, ["Sandbox"])) {
-                let info = "INFO" in context && Cu.evalInSandbox("this.INFO instanceof XML ? INFO.toXMLString() : this.INFO", context);
-                return /^</.test(info) ? XML(info) : info;
-            }
             if (DOM.isJSONXML(context.INFO))
                 return context.INFO;
-            if (typeof context.INFO == "xml" && config.haveGecko(null, "14.*"))
-                return context.INFO;
         }
         catch (e) {}
         return null;
@@ -644,7 +642,7 @@ var Contexts = Module("contexts", {
 
                 util.assert(!group.builtin ||
                                 !["-description", "-locations", "-nopersist"]
-                                    .some(Set.has(args.explicitOpts)),
+                                    .some(hasOwnProperty.bind(null, args.explicitOpts)),
                             _("group.cantModifyBuiltin"));
             },
             {
diff --git a/common/modules/dom-e4x.jsm b/common/modules/dom-e4x.jsm
deleted file mode 100644 (file)
index c83810b..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2012 Kris Maglione <maglione.k@gmail.com>
-//
-// This work is licensed for reuse under an MIT license. Details are
-// given in the LICENSE.txt file included with this file.
-/* use strict */
-
-defineModule("dom", {
-    exports: ["fromXML"]
-});
-
-lazyRequire("highlight", ["highlight"]);
-
-var XBL = Namespace("xbl", "http://www.mozilla.org/xbl");
-var XHTML = Namespace("html", "http://www.w3.org/1999/xhtml");
-var XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-var NS = Namespace("dactyl", "http://vimperator.org/namespaces/liberator");
-
-function fromXML(node, doc, nodes) {
-    XML.ignoreWhitespace = XML.prettyPrinting = false;
-    if (typeof node === "string") // Sandboxes can't currently pass us XML objects.
-        node = XML(node);
-
-    if (node.length() != 1) {
-        let domnode = doc.createDocumentFragment();
-        for each (let child in node)
-            domnode.appendChild(fromXML(child, doc, nodes));
-        return domnode;
-    }
-
-    switch (node.nodeKind()) {
-    case "text":
-        return doc.createTextNode(String(node));
-    case "element":
-        let domnode = doc.createElementNS(node.namespace(), node.localName());
-
-        for each (let attr in node.@*::*)
-            if (attr.name() != "highlight")
-                domnode.setAttributeNS(attr.namespace(), attr.localName(), String(attr));
-
-        for each (let child in node.*::*)
-            domnode.appendChild(fromXML(child, doc, nodes));
-        if (nodes && node.@key)
-            nodes[node.@key] = domnode;
-
-        if ("@highlight" in node)
-            highlight.highlightNode(domnode, String(node.@highlight), nodes || true);
-        return domnode;
-    default:
-        return null;
-    }
-}
-
-// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript:
index 6831bd3e83b9a35fc3a56bf0594d9e6b435aea7b..72d80135751b7b1779436cfdf877c59baf7f985a 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -55,8 +55,6 @@ var DOM = Class("DOM", {
 
         if (val == null)
             ;
-        else if (typeof val == "xml" && context instanceof Ci.nsIDOMDocument)
-            this[length++] = DOM.fromXML(val, context, this.nodes);
         else if (DOM.isJSONXML(val)) {
             if (context instanceof Ci.nsIDOMDocument)
                 this[length++] = DOM.fromJSON(val, context, this.nodes);
@@ -476,7 +474,7 @@ var DOM = Class("DOM", {
 
         let charset = doc.characterSet;
         let converter = services.CharsetConv(charset);
-        for each (let cs in form.acceptCharset.split(/\s*,\s*|\s+/)) {
+        for (let cs of form.acceptCharset.split(/\s*,\s*|\s+/)) {
             let c = services.CharsetConv(cs);
             if (c) {
                 converter = services.CharsetConv(cs);
@@ -627,7 +625,7 @@ var DOM = Class("DOM", {
                     if (callable(v))
                         v = v.call(this, elem, i);
 
-                    if (Set.has(hooks, k) && hooks[k].set)
+                    if (hasOwnProperty(hooks, k) && hooks[k].set)
                         hooks[k].set.call(this, elem, v, k);
                     else if (v == null)
                         elem.removeAttributeNS(ns, k);
@@ -639,7 +637,7 @@ var DOM = Class("DOM", {
         if (!this.length)
             return null;
 
-        if (Set.has(hooks, key) && hooks[key].get)
+        if (hasOwnProperty(hooks, key) && hooks[key].get)
             return hooks[key].get.call(this, this[0], key);
 
         if (!this[0].hasAttributeNS(ns, key))
@@ -1071,7 +1069,7 @@ var DOM = Class("DOM", {
         keyTable:       Class.Memoize(function (prop) this.init()[prop]),
         key_code:       Class.Memoize(function (prop) this.init()[prop]),
         key_key:        Class.Memoize(function (prop) this.init()[prop]),
-        pseudoKeys:     Set(["count", "leader", "nop", "pass"]),
+        pseudoKeys:     RealSet(["count", "leader", "nop", "pass"]),
 
         /**
          * Converts a user-input string of keys into a canonical
@@ -1094,7 +1092,7 @@ var DOM = Class("DOM", {
          * @returns {string} Canonical form.
          */
         canonicalKeys: function canonicalKeys(keys, unknownOk=true) {
-            return this.parse(keys, unknownOk).map(this.closure.stringify).join("");
+            return this.parse(keys, unknownOk).map(this.bound.stringify).join("");
         },
 
         iterKeys: function iterKeys(keys) iter(function () {
@@ -1141,19 +1139,19 @@ var DOM = Class("DOM", {
                 }
                 else {
                     let [match, modifier, keyname] = evt_str.match(/^<((?:[*12CASM⌘]-)*)(.+?)>$/i) || [false, '', ''];
-                    modifier = Set(modifier.toUpperCase());
+                    modifier = RealSet(modifier.toUpperCase());
                     keyname = keyname.toLowerCase();
                     evt_obj.dactylKeyname = keyname;
                     if (/^u[0-9a-f]+$/.test(keyname))
                         keyname = String.fromCharCode(parseInt(keyname.substr(1), 16));
 
                     if (keyname && (unknownOk || keyname.length == 1 || /mouse$/.test(keyname) ||
-                                    this.key_code[keyname] || Set.has(this.pseudoKeys, keyname))) {
-                        evt_obj.globKey  ="*" in modifier;
-                        evt_obj.ctrlKey  ="C" in modifier;
-                        evt_obj.altKey   ="A" in modifier;
-                        evt_obj.shiftKey ="S" in modifier;
-                        evt_obj.metaKey  ="M" in modifier || "⌘" in modifier;
+                                    this.key_code[keyname] || this.pseudoKeys.has(keyname))) {
+                        evt_obj.globKey  = modifier.has("*");
+                        evt_obj.ctrlKey  = modifier.has("C");
+                        evt_obj.altKey   = modifier.has("A");
+                        evt_obj.shiftKey = modifier.has("S");
+                        evt_obj.metaKey  = modifier.has("M") || modifier.has("⌘");
                         evt_obj.dactylShift = evt_obj.shiftKey;
 
                         if (keyname.length == 1) { // normal characters
@@ -1164,11 +1162,11 @@ var DOM = Class("DOM", {
                             evt_obj.charCode = keyname.charCodeAt(0);
                             evt_obj.keyCode = this.key_code[keyname.toLowerCase()];
                         }
-                        else if (Set.has(this.pseudoKeys, keyname)) {
+                        else if (this.pseudoKeys.has(keyname)) {
                             evt_obj.dactylString = "<" + this.key_key[keyname] + ">";
                         }
                         else if (/mouse$/.test(keyname)) { // mouse events
-                            evt_obj.type = (/2-/.test(modifier) ? "dblclick" : "click");
+                            evt_obj.type = (modifier.has("2") ? "dblclick" : "click");
                             evt_obj.button = ["leftmouse", "middlemouse", "rightmouse"].indexOf(keyname);
                             delete evt_obj.keyCode;
                             delete evt_obj.charCode;
@@ -1354,38 +1352,26 @@ var DOM = Class("DOM", {
          * @param {Node} target The DOM node to which to dispatch the event.
          * @param {Event} event The event to dispatch.
          */
-        dispatch: Class.Memoize(function ()
-            config.haveGecko("2b")
-                ? function dispatch(target, event, extra) {
-                    try {
-                        this.feedingEvent = extra;
-
-                        if (target instanceof Ci.nsIDOMElement)
-                            // This causes a crash on Gecko<2.0, it seems.
-                            return (target.ownerDocument || target.document || target).defaultView
-                                   .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
-                                   .dispatchDOMEventViaPresShell(target, event, true);
-                        else {
-                            target.dispatchEvent(event);
-                            return !event.defaultPrevented;
-                        }
-                    }
-                    catch (e) {
-                        util.reportError(e);
-                    }
-                    finally {
-                        this.feedingEvent = null;
-                    }
+        dispatch: function dispatch(target, event, extra) {
+            try {
+                this.feedingEvent = extra;
+
+                if (target instanceof Ci.nsIDOMElement)
+                    return (target.ownerDocument || target.document || target).defaultView
+                           .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
+                           .dispatchDOMEventViaPresShell(target, event, true);
+                else {
+                    target.dispatchEvent(event);
+                    return !event.defaultPrevented;
                 }
-                : function dispatch(target, event, extra) {
-                    try {
-                        this.feedingEvent = extra;
-                        target.dispatchEvent(update(event, extra));
-                    }
-                    finally {
-                        this.feedingEvent = null;
-                    }
-                })
+            }
+            catch (e) {
+                util.reportError(e);
+            }
+            finally {
+                this.feedingEvent = null;
+            }
+        }
     }),
 
     createContents: Class.Memoize(() => services.has("dactyl") && services.dactyl.createContents
@@ -1410,9 +1396,9 @@ var DOM = Class("DOM", {
      * The set of input element type attribute values that mark the element as
      * an editable field.
      */
-    editableInputs: Set(["date", "datetime", "datetime-local", "email", "file",
-                         "month", "number", "password", "range", "search",
-                         "tel", "text", "time", "url", "week"]),
+    editableInputs: RealSet(["date", "datetime", "datetime-local", "email", "file",
+                             "month", "number", "password", "range", "search",
+                             "tel", "text", "time", "url", "week"]),
 
     /**
      * Converts a given DOM Node, Range, or Selection to a string. If
@@ -1494,7 +1480,7 @@ var DOM = Class("DOM", {
      * @returns {boolean} True when the patterns are all valid.
      */
     validateMatcher: function validateMatcher(list) {
-        return this.testValues(list, DOM.closure.testMatcher);
+        return this.testValues(list, DOM.bound.testMatcher);
     },
 
     testMatcher: function testMatcher(value) {
@@ -1522,21 +1508,6 @@ var DOM = Class("DOM", {
         return str.replace(regexp, m => map[m]);
     },
 
-    /**
-     * Converts an E4X XML literal to a DOM node. Any attribute named
-     * highlight is present, it is transformed into dactyl:highlight,
-     * and the named highlight groups are guaranteed to be loaded.
-     *
-     * @param {Node} node
-     * @param {Document} doc
-     * @param {Object} nodes If present, nodes with the "key" attribute are
-     *     stored here, keyed to the value thereof.
-     * @returns {Node}
-     */
-    fromXML: deprecated("DOM.fromJSON", { get: function fromXML()
-               prefs.get("javascript.options.xml.chrome") !== false
-            && require("dom-e4x").fromXML }),
-
     fromJSON: update(function fromJSON(xml, doc, nodes, namespaces) {
         if (!doc)
             doc = document;
@@ -1552,8 +1523,6 @@ var DOM = Class("DOM", {
 
             if (isinstance(args, ["String", "Number", "Boolean", _]))
                 return doc.createTextNode(args);
-            if (isXML(args))
-                return DOM.fromXML(args, doc, nodes);
             if (isObject(args) && "toDOM" in args)
                 return args.toDOM(doc, namespaces, nodes);
             if (args instanceof Ci.nsIDOMNode)
@@ -1645,7 +1614,7 @@ var DOM = Class("DOM", {
     toPrettyXML: function toPrettyXML(xml, asXML, indent, namespaces) {
         const INDENT = indent || "    ";
 
-        const EMPTY = Set("area base basefont br col frame hr img input isindex link meta param"
+        const EMPTY = RealSet("area base basefont br col frame hr img input isindex link meta param"
                             .split(" "));
 
         function namespaced(namespaces, namespace, localName) {
@@ -1678,11 +1647,6 @@ var DOM = Class("DOM", {
                 return indent +
                        DOM.escapeHTML(String(args), true);
 
-            if (isXML(args))
-                return indent +
-                       args.toXMLString()
-                           .replace(/^/m, indent);
-
             if (isObject(args) && "toDOM" in args)
                 return indent +
                        services.XMLSerializer()
@@ -1754,7 +1718,7 @@ var DOM = Class("DOM", {
             let res = [indent, "<", name];
 
             for (let [key, val] in Iterator(attr)) {
-                if (Set.has(skipAttr, key))
+                if (hasOwnProperty(skipAttr, key))
                     continue;
 
                 let vals = parseNamespace(key);
@@ -1770,7 +1734,7 @@ var DOM = Class("DOM", {
                              '="', DOM.escapeHTML(val), '"');
             }
 
-            if ((vals[0] || namespaces[""]) == String(XHTML) && Set.has(EMPTY, vals[1])
+            if ((vals[0] || namespaces[""]) == String(XHTML) && EMPTY.has(vals[1])
                     || asXML && !args.length)
                 res.push("/>");
             else {
@@ -1893,7 +1857,7 @@ var DOM = Class("DOM", {
 
 Object.keys(DOM.Event.types).forEach(function (event) {
     let name = event.replace(/-(.)/g, (m, m1) => m1.toUpperCase());
-    if (!Set.has(DOM.prototype, name))
+    if (!hasOwnProperty(DOM.prototype, name))
         DOM.prototype[name] =
             function _event(arg, extra) {
                 return this[callable(arg) ? "listen" : "dispatch"](event, arg, extra);
index cbcd9a6e4fcaf4ff3ffccc4ae1bb3e35213c4850..3f4a3501384d6c10baf8b0ab0ccd8cc0892070db 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2011-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -10,20 +10,22 @@ defineModule("downloads", {
 });
 
 lazyRequire("overlay", ["overlay"]);
+lazyRequire("promises", ["Task", "promises"]);
 
-Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
+lazyRequire("resource://gre/modules/Downloads.jsm", ["Downloads"]);
+lazyRequire("resource://gre/modules/DownloadUtils.jsm", ["DownloadUtils"]);
 
 var MAX_LOAD_TIME = 10 * 1000;
 
 let prefix = "DOWNLOAD_";
 var states = iter([v, k.slice(prefix.length).toLowerCase()]
                   for ([k, v] in Iterator(Ci.nsIDownloadManager))
-                  if (k.indexOf(prefix) == 0))
+                  if (k.startsWith(prefix)))
                 .toObject();
 
 var Download = Class("Download", {
-    init: function init(id, list) {
-        this.download = services.downloadManager.getDownload(id);
+    init: function init(download, list) {
+        this.download = download;
         this.list = list;
 
         this.nodes = {
@@ -39,11 +41,9 @@ var Download = Class("Download", {
                             this.targetFile.path]]],
                 ["td", { highlight: "DownloadState", key: "state" }],
                 ["td", { highlight: "DownloadButtons Buttons" },
-                    ["a", { highlight: "Button", href: "javascript:0", key: "pause" }, _("download.action.Pause")],
+                    ["a", { highlight: "Button", href: "javascript:0", key: "stop"   }, _("download.action.Stop")],
                     ["a", { highlight: "Button", href: "javascript:0", key: "remove" }, _("download.action.Remove")],
                     ["a", { highlight: "Button", href: "javascript:0", key: "resume" }, _("download.action.Resume")],
-                    ["a", { highlight: "Button", href: "javascript:0", key: "retry" }, _("download.action.Retry")],
-                    ["a", { highlight: "Button", href: "javascript:0", key: "cancel" }, _("download.action.Cancel")],
                     ["a", { highlight: "Button", href: "javascript:0", key: "delete" }, _("download.action.Delete")]],
                 ["td", { highlight: "DownloadProgress", key: "progress" },
                     ["span", { highlight: "DownloadProgressHave", key: "progressHave" }],
@@ -53,8 +53,8 @@ var Download = Class("Download", {
                 ["td", { highlight: "DownloadSpeed", key: "speed" }],
                 ["td", { highlight: "DownloadTime", key: "time" }],
                 ["td", {},
-                    ["a", { highlight: "DownloadSource", key: "source", href: this.source.spec },
-                        this.source.spec]]],
+                    ["a", { highlight: "DownloadSource", key: "source", href: this.source.url },
+                        this.source.url]]],
             this.list.document, this.nodes);
 
         this.nodes.launch.addEventListener("click", (event) => {
@@ -68,38 +68,40 @@ var Download = Class("Download", {
         return this;
     },
 
+    get active() !this.stopped,
+
+    get targetFile() File(this.download.target.path),
+
+    get displayName() this.targetFile.leafName,
+
     get status() states[this.state],
 
     inState: function inState(states) states.indexOf(this.status) >= 0,
 
-    get alive() this.inState(["downloading", "notstarted", "paused", "queued", "scanning"]),
-
     allowedCommands: Class.Memoize(function () let (self = this) ({
-        get cancel() self.cancelable && self.inState(["downloading", "paused", "starting"]),
-        get delete() !this.cancel && self.targetFile.exists(),
-        get launch() self.targetFile.exists() && self.inState(["finished"]),
-        get pause() self.inState(["downloading"]),
-        get remove() self.inState(["blocked_parental", "blocked_policy",
-                                   "canceled", "dirty", "failed", "finished"]),
-        get resume() self.resumable && self.inState(["paused"]),
-        get retry() self.inState(["canceled", "failed"])
+        get delete() !self.active && (self.targetFile.exists() || self.hasPartialData),
+        get launch() self.targetFile.exists() && self.succeeded,
+        get stop()  self.active,
+        get remove() !self.active,
+        get resume() self.canceled
     })),
 
     command: function command(name) {
-        util.assert(Set.has(this.allowedCommands, name), _("download.unknownCommand"));
+        util.assert(hasOwnProperty(this.allowedCommands, name), _("download.unknownCommand"));
         util.assert(this.allowedCommands[name], _("download.commandNotAllowed"));
 
-        if (Set.has(this.commands, name))
+        if (hasOwnProperty(this.commands, name))
             this.commands[name].call(this);
-        else
-            services.downloadManager[name + "Download"](this.id);
     },
 
     commands: {
-        delete: function delete_() {
-            this.targetFile.remove(false);
+        delete: promises.task(function delete_() {
+            if (this.hasPartialData)
+                yield this.removePartialData();
+            else if (this.targetFile.exists())
+                this.targetFile.remove(false);
             this.updateStatus();
-        },
+        }),
         launch: function launch() {
             // Behavior mimics that of the builtin Download Manager.
             function action() {
@@ -127,42 +129,52 @@ var Download = Class("Download", {
                     });
             else
                 action.call(this);
-        }
+        },
+        resume: function resume() {
+            this.download.start();
+        },
+        remove: promises.task(function remove() {
+            yield this.list.list.remove(this.download);
+            yield this.download.finalize(true);
+        }),
+        stop: function stop() {
+            this.download.cancel();
+        },
     },
 
     _compare: {
-        active: (a, b) => a.alive - b.alive,
+        active:   (a, b) => a.active - b.active,
         complete: (a, b) => a.percentComplete - b.percentComplete,
-        date: (a, b) => a.startTime - b.startTime,
+        date:     (a, b) => a.startTime - b.startTime,
         filename: (a, b) => String.localeCompare(a.targetFile.leafName, b.targetFile.leafName),
-        size: (a, b) => a.size - b.size,
-        speed: (a, b) => a.speed - b.speed,
-        time: (a, b) => a.timeRemaining - b.timeRemaining,
-        url: (a, b) => String.localeCompare(a.source.spec, b.source.spec)
+        size:     (a, b) => a.totalBytes - b.totalBytes,
+        speed:    (a, b) => a.speed - b.speed,
+        time:     (a, b) => a.timeRemaining - b.timeRemaining,
+        url:      (a, b) => String.localeCompare(a.source.url, b.source.url)
     },
 
     compare: function compare(other) values(this.list.sortOrder).map(function (order) {
         let val = this._compare[order.substr(1)](this, other);
 
         return (order[0] == "-") ? -val : val;
-    }, this).nth(util.identity, 0) || 0,
+    }, this).find(util.identity) || 0,
 
     timeRemaining: Infinity,
 
     updateProgress: function updateProgress() {
         let self = this.__proto__;
 
-        if (this.amountTransferred === this.size) {
+        if (!this.active) {
             this.nodes.speed.textContent = "";
             this.nodes.time.textContent = "";
         }
         else {
             this.nodes.speed.textContent = util.formatBytes(this.speed, 1, true) + "/s";
 
-            if (this.speed == 0 || this.size == 0)
+            if (this.speed == 0 || !this.hasProgress)
                 this.nodes.time.textContent = _("download.unknown");
             else {
-                let seconds = (this.size - this.amountTransferred) / this.speed;
+                let seconds = (this.totalBytes - this.currentBytes) / this.speed;
                 [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining);
                 if (this.timeRemaining)
                     this.nodes.time.textContent = util.formatSeconds(this.timeRemaining);
@@ -171,17 +183,20 @@ var Download = Class("Download", {
             }
         }
 
-        let total = this.nodes.progressTotal.textContent = this.size || !this.nActive ? util.formatBytes(this.size, 1, true)
-                                                                                      : _("download.unknown");
+        let total = this.nodes.progressTotal.textContent =
+            this.hasProgress && (this.totalBytes || !this.nActive)
+                ? util.formatBytes(this.totalBytes, 1, true)
+                : _("download.unknown");
+
         let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$");
-        this.nodes.progressHave.textContent = util.formatBytes(this.amountTransferred, 1, true).replace(suffix, "");
+        this.nodes.progressHave.textContent = util.formatBytes(this.currentBytes, 1, true).replace(suffix, "");
 
-        this.nodes.percent.textContent = this.size ? Math.round(this.amountTransferred * 100 / this.size) + "%" : "";
+        this.nodes.percent.textContent = this.hasProgress ? this.progress + "%" : "";
     },
 
     updateStatus: function updateStatus() {
 
-        this.nodes.row[this.alive ? "setAttribute" : "removeAttribute"]("active", "true");
+        this.nodes.row[this.active ? "setAttribute" : "removeAttribute"]("active", "true");
 
         this.nodes.row.setAttribute("status", this.status);
         this.nodes.state.textContent = util.capitalize(this.status);
@@ -193,14 +208,6 @@ var Download = Class("Download", {
         this.updateProgress();
     }
 });
-Object.keys(XPCOMShim([Ci.nsIDownload])).forEach(function (key) {
-    if (!(key in Download.prototype))
-        Object.defineProperty(Download.prototype, key, {
-            get: function get() this.download[key],
-            set: function set(val) this.download[key] = val,
-            configurable: true
-        });
-});
 
 var DownloadList = Class("DownloadList",
                          XPCOM([Ci.nsIDownloadProgressListener,
@@ -213,12 +220,13 @@ var DownloadList = Class("DownloadList",
         this.nodes = {
             commandTarget: this
         };
-        this.downloads = {};
+        this.downloads = Map();
     },
 
     cleanup: function cleanup() {
-        this.observe.unregister();
-        services.downloadManager.removeListener(this);
+        if (this.list)
+            this.list.removeView(this);
+        this.dead = true;
     },
 
     message: Class.Memoize(function () {
@@ -258,40 +266,44 @@ var DownloadList = Class("DownloadList",
         this.index = Array.indexOf(this.nodes.list.childNodes,
                                    this.nodes.head);
 
-        let start = Date.now();
-        for (let row in iter(services.downloadManager.DBConnection
-                                     .createStatement("SELECT id FROM moz_downloads"))) {
-            if (Date.now() - start > MAX_LOAD_TIME) {
-                util.dactyl.warn(_("download.givingUpAfter", (Date.now() - start) / 1000));
-                break;
+        Task.spawn(function () {
+            this.list = yield Downloads.getList(Downloads.ALL);
+
+            let start = Date.now();
+            for (let download of yield this.list.getAll()) {
+                if (Date.now() - start > MAX_LOAD_TIME) {
+                    util.dactyl.warn(_("download.givingUpAfter", (Date.now() - start) / 1000));
+                    break;
+                }
+                this.addDownload(download);
             }
-            this.addDownload(row.id);
-        }
-        this.update();
+            this.update();
 
-        util.addObserver(this);
-        services.downloadManager.addListener(this);
+            if (!this.dead)
+                this.list.addView(this);
+        }.bind(this));
         return this.nodes.list;
     }),
 
-    addDownload: function addDownload(id) {
-        if (!(id in this.downloads)) {
-            let download = Download(id, this);
-            if (this.filter && download.displayName.indexOf(this.filter) === -1)
+    addDownload: function addDownload(download) {
+        if (!this.downloads.has(download)) {
+            download = Download(download, this);
+            if (this.filter && !download.displayName.contains(this.filter))
                 return;
 
-            this.downloads[id] = download;
-            let index = values(this.downloads).sort((a, b) => a.compare(b))
-                                              .indexOf(download);
+            this.downloads.set(download.download, download);
+            let index = values(this.downloads).toArray()
+                            .sort((a, b) => a.compare(b))
+                            .indexOf(download);
 
             this.nodes.list.insertBefore(download.nodes.row,
                                          this.nodes.list.childNodes[index + this.index + 1]);
         }
     },
-    removeDownload: function removeDownload(id) {
-        if (id in this.downloads) {
-            this.nodes.list.removeChild(this.downloads[id].nodes.row);
-            delete this.downloads[id];
+    removeDownload: function removeDownload(download) {
+        if (this.downloads.has(download)) {
+            this.nodes.list.removeChild(this.downloads.get(download).nodes.row);
+            delete this.downloads.delete(download);
         }
     },
 
@@ -301,17 +313,17 @@ var DownloadList = Class("DownloadList",
     },
 
     allowedCommands: Class.Memoize(function () let (self = this) ({
-        get clear() values(self.downloads).some(dl => dl.allowedCommands.remove)
+        get clear() iter(self.downloads.values()).some(dl => dl.allowedCommands.remove)
     })),
 
     commands: {
         clear: function () {
-            services.downloadManager.cleanUp();
+            this.list.removeFinished();
         }
     },
 
     sort: function sort() {
-        let list = values(this.downloads).sort((a, b) => a.compare(b));
+        let list = iter(this.downloads.values()).sort((a, b) => a.compare(b));
 
         for (let [i, download] in iter(list))
             if (this.nodes.list.childNodes[i + 1] != download.nodes.row)
@@ -335,16 +347,19 @@ var DownloadList = Class("DownloadList",
     timeRemaining: Infinity,
 
     updateProgress: function updateProgress() {
-        let downloads = values(this.downloads).toArray();
-        let active    = downloads.filter(d => d.alive);
+        let downloads = iter(this.downloads.values()).toArray();
+        let active    = downloads.filter(d => d.active);
 
         let self = Object.create(this);
-        for (let prop in values(["amountTransferred", "size", "speed", "timeRemaining"]))
+        for (let prop in values(["currentBytes", "totalBytes", "speed", "timeRemaining"]))
             this[prop] = active.reduce((acc, dl) => dl[prop] + acc, 0);
 
+        this.hasProgress = active.every(d => d.hasProgress);
+        this.progress = Math.round((this.currentBytes / this.totalBytes) * 100);
+        this.nActive = active.length;
+
         Download.prototype.updateProgress.call(self);
 
-        this.nActive = active.length;
         if (active.length)
             this.nodes.total.textContent = _("download.nActive", active.length);
         else for (let key in values(["total", "percent", "speed", "time"]))
@@ -354,68 +369,87 @@ var DownloadList = Class("DownloadList",
             this.sort();
     },
 
-    observers: {
-        "download-manager-remove-download": function (id) {
-            if (id == null)
-                id = [k for ([k, dl] in iter(this.downloads)) if (dl.allowedCommands.remove)];
-            else
-                id = [id.QueryInterface(Ci.nsISupportsPRUint32).data];
+    onDownloadAdded: function onDownloadAdded(download) {
+        this.addDownload(download);
 
-            Array.concat(id).map(this.closure.removeDownload);
-            this.update();
-        }
+        this.modules.mow.resize(false);
+        this.nodes.list.scrollIntoView(false);
     },
 
-    onDownloadStateChange: function (state, download) {
-        try {
-            if (download.id in this.downloads)
-                this.downloads[download.id].updateStatus();
-            else {
-                this.addDownload(download.id);
+    onDownloadRemoved: function onDownloadRemoved(download) {
+        this.removeDownload(download);
+    },
+
+    onDownloadChanged: function onDownloadChanged(download) {
+        if (this.downloads.has(download)) {
+            download = this.downloads.get(download)
+
+            download.updateStatus();
+            download.updateProgress();
 
-                this.modules.mow.resize(false);
-                this.nodes.list.scrollIntoView(false);
-            }
             this.update();
 
             if (this.shouldSort("active"))
                 this.sort();
         }
-        catch (e) {
-            util.reportError(e);
-        }
-    },
-
-    onProgressChange: function (webProgress, request,
-                                curProgress, maxProgress,
-                                curTotalProgress, maxTotalProgress,
-                                download) {
-        try {
-            if (download.id in this.downloads)
-                this.downloads[download.id].updateProgress();
-            this.updateProgress();
-        }
-        catch (e) {
-            util.reportError(e);
-        }
     }
 });
+["canceled",
+ "contentType",
+ "currentBytes",
+ "error",
+ "hasPartialData",
+ "hasProgress",
+ "launchWhenSucceeded",
+ "launcherPath",
+ "progress",
+ "saver",
+ "source",
+ "speed",
+ "startTime",
+ "stopped",
+ "succeeded",
+ "target",
+ "totalBytes",
+ "tryToKeepPartialData"].forEach(key => {
+    if (!(key in Download.prototype))
+        Object.defineProperty(Download.prototype, key, {
+            get: function get() this.download[key],
+            set: function set(val) this.download[key] = val,
+            configurable: true
+        });
+});
+
 
-var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
+var Downloads_ = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
     init: function () {
-        services.downloadManager.addListener(this);
+        Downloads.getList(Downloads.ALL).then(list => {
+            this.list = list;
+            if (!this.dead)
+                this.list.addView(this);
+        });
     },
 
     cleanup: function destroy() {
-        services.downloadManager.removeListener(this);
+        if (this.list)
+            this.list.removeView(this);
+        this.dead = true;
+    },
+
+    onDownloadAdded: function onDownloadAdded(download) {
     },
 
-    onDownloadStateChange: function (state, download) {
-        if (download.state == services.downloadManager.DOWNLOAD_FINISHED) {
-            let url   = download.source.spec;
-            let title = download.displayName;
-            let file  = download.targetFile.path;
-            let size  = download.size;
+    onDownloadRemoved: function onDownloadRemoved(download) {
+    },
+
+    onDownloadChanged: function onDownloadChanged(download) {
+        if (download.succeeded) {
+            let target = File(download.target.path);
+
+            let url   = download.source.url;
+            let title = target.leafName;
+            let file  = target.path;
+            let size  = download.totalBytes;
 
             overlay.modules.forEach(function (modules) {
                 modules.dactyl.echomsg({ domains: [util.getHost(url)], message: _("io.downloadFinished", title, file) },
@@ -451,7 +485,7 @@ var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
 
         commands.add(["dlc[lear]"],
             "Clear completed downloads",
-            function (args) { services.downloadManager.cleanUp(); });
+            function (args) { downloads.list.removeFinished(); });
     },
     options: function initOptions(dactyl, modules, window) {
         const { options } = modules;
@@ -489,9 +523,9 @@ var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
                 },
 
                 completer: function (context, extra) {
-                    let seen = Set.has(Set(extra.values.map(val => val.substr(1))));
+                    let seen = RealSet(extra.values.map(val => val.substr(1)));
 
-                    context.completions = iter(this.values).filter(([k, v]) => !seen(k))
+                    context.completions = iter(this.values).filter(([k, v]) => !seen.has(k))
                                                            .map(([k, v]) => [["+" + k, [v, " (", _("sort.ascending"), ")"].join("")],
                                                                              ["-" + k, [v, " (", _("sort.descending"), ")"].join("")]])
                                                            .flatten().array;
@@ -500,9 +534,9 @@ var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
                 has: function () Array.some(arguments, val => this.value.some(v => v.substr(1) == val)),
 
                 validator: function (value) {
-                    let seen = {};
-                    return value.every(val => /^[+-]/.test(val) && Set.has(this.values, val.substr(1))
-                                                                && !Set.add(seen, val.substr(1)))
+                    let seen = RealSet();
+                    return value.every(val => /^[+-]/.test(val) && hasOwnProperty(this.values, val.substr(1))
+                                                                && !seen.add(val.substr(1)))
                         && value.length;
                 }
             });
index 738b17772bf4835fe5def2d718c3d6f4b26ad4f3..2f263ba04ba0745f66196fe4da25895c29021391 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -253,10 +253,10 @@ var RangeFinder = Module("rangefinder", {
 
             get prompt() this.mode === modules.modes.FIND_BACKWARD ? "?" : "/",
 
-            get onCancel() modules.rangefinder.closure.onCancel,
-            get onChange() modules.rangefinder.closure.onChange,
-            get onHistory() modules.rangefinder.closure.onHistory,
-            get onSubmit() modules.rangefinder.closure.onSubmit
+            get onCancel()  modules.rangefinder.bound.onCancel,
+            get onChange()  modules.rangefinder.bound.onChange,
+            get onHistory() modules.rangefinder.bound.onHistory,
+            get onSubmit()  modules.rangefinder.bound.onSubmit
         });
     },
     mappings: function initMappings(dactyl, modules, window) {
@@ -626,7 +626,7 @@ var RangeFind = Class("RangeFind", {
         if (!this.matchCase)
             pattern = pattern.toLowerCase();
 
-        if (!again && (pattern === "" || pattern.indexOf(this.lastString) !== 0 || this.backward)) {
+        if (!again && (pattern === "" || !pattern.startsWith(this.lastString) || this.backward)) {
             if (!private_)
                 this.range.deselect();
             if (pattern === "")
@@ -707,12 +707,12 @@ var RangeFind = Class("RangeFind", {
 
     addListeners: function addListeners() {
         for (let range in array.iterValues(this.ranges))
-            range.window.addEventListener("unload", this.closure.onUnload, true);
+            range.window.addEventListener("unload", this.bound.onUnload, true);
     },
     purgeListeners: function purgeListeners() {
         for (let range in array.iterValues(this.ranges))
             try {
-                range.window.removeEventListener("unload", this.closure.onUnload, true);
+                range.window.removeEventListener("unload", this.bound.onUnload, true);
             }
             catch (e if e.result === Cr.NS_ERROR_FAILURE) {}
     },
index f477411ed4b9acae873f5792a36b995f6cc14b04..cc66a595144dcb99b1222d3676457d28e4b1017c 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -201,7 +201,7 @@ var Help = Module("Help", {
                        ["toc", { start: "2" }],
 
                        body]);
-        });
+        }, true);
     },
 
     initialize: function initialize() {
@@ -237,7 +237,7 @@ var Help = Module("Help", {
          * @returns {string}
          */
         findHelp: function (topic, consolidated) {
-            if (!consolidated && Set.has(help.files, topic))
+            if (!consolidated && hasOwnProperty(help.files, topic))
                 return topic;
             let items = modules.completion._runCompleter("help", topic, null, !!consolidated).items;
             let partialMatch = null;
@@ -268,7 +268,7 @@ var Help = Module("Help", {
             if (!topic) {
                 let helpFile = consolidated ? "all" : modules.options["helpfile"];
 
-                if (Set.has(help.files, helpFile))
+                if (hasOwnProperty(help.files, helpFile))
                     dactyl.open("dactyl://help/" + helpFile, { from: "help" });
                 else
                     dactyl.echomsg(_("help.noFile", helpFile.quote()));
@@ -304,7 +304,7 @@ var Help = Module("Help", {
                     addURIEntry(file, "data:text/plain;charset=UTF-8," + encodeURI(data));
             }
 
-            let empty = Set("area base basefont br col frame hr img input isindex link meta param"
+            let empty = RealSet("area base basefont br col frame hr img input isindex link meta param"
                                 .split(" "));
             function fix(node) {
                 switch (node.nodeType) {
@@ -319,13 +319,13 @@ var Help = Module("Help", {
 
                     for (let { name, value } in array.iterValues(node.attributes)) {
                         if (name == "dactyl:highlight") {
-                            Set.add(styles, value);
+                            styles.add(value);
                             name = "class";
                             value = "hl-" + value;
                         }
                         if (name == "href") {
                             value = node.href || value;
-                            if (value.indexOf("dactyl://help-tag/") == 0) {
+                            if (value.startsWith("dactyl://help-tag/")) {
                                 try {
                                     let uri = services.io.newChannel(value, null, null).originalURI;
                                     value = uri.spec == value ? "javascript:;" : uri.path.substr(1);
@@ -345,7 +345,7 @@ var Help = Module("Help", {
 
                         data.push(" ", name, '="', DOM.escapeHTML(value), '"');
                     }
-                    if (node.localName in empty)
+                    if (empty.has(node.localName))
                         data.push(" />");
                     else {
                         data.push(">");
@@ -362,7 +362,7 @@ var Help = Module("Help", {
 
             let { buffer, content, events } = modules;
             let chromeFiles = {};
-            let styles = {};
+            let styles = RealSet();
 
             for (let [file, ] in Iterator(help.files)) {
                 let url = "dactyl://help/" + file;
@@ -380,7 +380,7 @@ var Help = Module("Help", {
                 addDataEntry(file + ".xhtml", data.join(""));
             }
 
-            let data = [h for (h in highlight) if (Set.has(styles, h.class) || /^Help/.test(h.class))]
+            let data = [h for (h in highlight) if (styles.has(h.class) || /^Help/.test(h.class))]
                 .map(h => h.selector
                            .replace(/^\[.*?=(.*?)\]/, ".hl-$1")
                            .replace(/html\|/g, "") + "\t" + "{" + h.cssText + "}")
index e25b7cc1f5d71adee0103944a26321b8326be465..501edde272d2406b3f2043df60678caa0ce2f3e5 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -125,7 +125,7 @@ var Highlights = Module("Highlight", {
         if (/^[[>+: ]/.test(args[1]))
             obj.selector = this.selector(obj.class) + args[1];
         else if (args[1])
-            obj.selector = this.selector(args[1]);
+            obj.selector = this.selector(args[1].replace(/^,/, ""));
 
         if (old && old.value != old.defaultValue)
             obj.value = old.value;
@@ -134,8 +134,12 @@ var Highlights = Module("Highlight", {
             obj.style.enabled = true;
         else
             this.loaded.__defineSetter__(obj.class, function () {
-                delete this[obj.class];
-                this[obj.class] = true;
+                Object.defineProperty(this, obj.class, {
+                    value: true,
+                    configurable: true,
+                    enumerable: true,
+                    writable: true
+                });
 
                 if (obj.class === obj.baseClass)
                     for (let h in highlight)
@@ -200,11 +204,11 @@ var Highlights = Module("Highlight", {
         node.setAttributeNS(NS, "highlight", group);
 
         let groups = group.split(" ");
-        for each (let group in groups)
+        for (let group of groups)
             this.loaded[group] = true;
 
         if (applyBindings)
-            for each (let group in groups) {
+            for (let group of groups) {
                 if (applyBindings.bindings && group in applyBindings.bindings)
                     applyBindings.bindings[group](node, applyBindings);
                 else if (group in template.bindings)
@@ -275,7 +279,7 @@ var Highlights = Module("Highlight", {
      * If Star is provided, the style is applied as an agent sheet.
      *
      * The new styles are lazily activated unless Bang or *eager* is
-     * provided. See {@link Util#xmlToDom}.
+     * provided.
      *
      * @param {string} css The rules to load. See {@link Highlights#css}.
      * @param {boolean} eager When true, load all provided rules immediately.
index 4a61d67313b74d7e1c3a2092a44ef7ea027c1125..9de045500a4f2a7b9202335847ed0bce541faa35 100644 (file)
@@ -1,7 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2012 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
-// Some code based on Venkman
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -16,6 +15,7 @@ defineModule("io", {
 
 lazyRequire("config", ["config"]);
 lazyRequire("contexts", ["Contexts", "contexts"]);
+lazyRequire("promises", ["Promise"]);
 lazyRequire("storage", ["File", "storage"]);
 lazyRequire("styles", ["styles"]);
 lazyRequire("template", ["template"]);
@@ -42,7 +42,7 @@ var IO = Module("io", {
             this._oldcwd = null;
 
             this._lastRunCommand = ""; // updated whenever the users runs a command with :!
-            this._scriptNames = [];
+            this._scriptNames = RealSet();
         },
 
         CommandFileMode: Class("CommandFileMode", modules.CommandMode, {
@@ -156,7 +156,7 @@ var IO = Module("io", {
                     else if (/\.js$/.test(filename)) {
                         try {
                             var context = contexts.Script(file, params.group);
-                            if (Set.has(this._scriptNames, file.path))
+                            if (this._scriptNames.has(file.path))
                                 util.flushCache();
 
                             dactyl.loadScript(uri.spec, context);
@@ -196,7 +196,7 @@ var IO = Module("io", {
                         dactyl.triggerObserver("io.source", context, file, file.lastModifiedTime);
                     }
 
-                    Set.add(this._scriptNames, file.path);
+                    this._scriptNames.add(file.path);
 
                     dactyl.echomsg(_("io.sourcingEnd", filename.quote()), 2);
                     dactyl.log(_("dactyl.sourced", filename), 3);
@@ -319,7 +319,7 @@ var IO = Module("io", {
      *     @default ""
      * @returns {File}
      */
-    createTempFile: function createTempFile(ext = "txt", label = "") {
+    createTempFile: function createTempFile(ext="txt", label="") {
         let file = services.directory.get("TmpD", Ci.nsIFile);
         file.append(config.name + label + "." + ext);
         file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666));
@@ -447,26 +447,29 @@ var IO = Module("io", {
 
         let process = services.Process(file.file);
         process.run(false, args.map(String), args.length);
-        try {
-            if (callable(blocking))
-                var timer = services.Timer(
-                    function () {
-                        if (!process.isRunning) {
-                            timer.cancel();
-                            util.trapErrors(blocking, self, process.exitValue);
-                        }
-                    },
-                    100, services.Timer.TYPE_REPEATING_SLACK);
-            else if (blocking)
-                while (process.isRunning)
-                    util.threadYield(false, true);
-        }
-        catch (e) {
-            process.kill();
-            throw e;
+
+        let deferred = Promise.defer();
+
+        if (callable(blocking))
+            // Deprecated.
+            deferred.promise.then(blocking);
+        else if (blocking) {
+            // Deprecated?
+            while (process.isRunning)
+                util.threadYield(false, true);
+            return process.exitValue;
         }
 
-        return process.exitValue;
+        let timer = services.Timer(
+            function () {
+                if (!process.isRunning) {
+                    timer.cancel();
+                    deferred.resolve(process.exitValue);
+                }
+            },
+            100, services.Timer.TYPE_REPEATING_SLACK);
+
+        return deferred.promise;
     },
 
     // TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is
@@ -486,7 +489,7 @@ var IO = Module("io", {
     system: function system(command, input, callback) {
         util.dactyl.echomsg(_("io.callingShell", command), 4);
 
-        let { shellEscape } = util.closure;
+        let { shellEscape } = util.bound;
 
         return this.withTempFiles(function (stdin, stdout, cmd) {
             if (input instanceof File)
@@ -865,7 +868,7 @@ unlet s:cpo_save
         commands.add(["scrip[tnames]"],
             "List all sourced script names",
             function () {
-                let names = Object.keys(io._scriptNames);
+                let names = [k for (k of io._scriptNames)];
                 if (!names.length)
                     dactyl.echomsg(_("command.scriptnames.none"));
                 else
index dcf206bfacce9b93c66e2e10131ef28c86748369..8555cdf2624ab1112bcd21903775a24dc73ca695 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -55,7 +55,8 @@ var JavaScript = Module("javascript", {
 
     lazyInit: true,
 
-    newContext: function () this.modules.newContext(this.modules.userContext, true, "Dactyl JS Temp Context"),
+    newContext: function () this.modules.newContext(this.modules.userContext, false,
+                                                    "Dactyl JS Temp Context"),
 
     completers: Class.Memoize(() => Object.create(JavaScript.completers)),
 
@@ -72,22 +73,23 @@ var JavaScript = Module("javascript", {
         if (obj == null)
             return;
 
-        let seen = isinstance(obj, ["Sandbox"]) ? Set(JavaScript.magicalNames) : {};
+        let seen = RealSet(isinstance(obj, ["Sandbox"]) ? JavaScript.magicalNames : []);
         let globals = values(toplevel && this.window === obj ? this.globalNames : []);
 
         if (toplevel && isObject(obj) && "wrappedJSObject" in obj)
-            if (!Set.add(seen, "wrappedJSObject"))
+            if (!seen.add("wrappedJSObject"))
                 yield "wrappedJSObject";
 
-        for (let key in iter(globals, properties(obj, !toplevel, true)))
-            if (!Set.add(seen, key))
+        for (let key in iter(globals, properties(obj, !toplevel)))
+            if (!seen.add(key))
                 yield key;
 
         // Properties aren't visible in an XPCNativeWrapper until
         // they're accessed.
-        for (let key in properties(this.getKey(obj, "wrappedJSObject"), !toplevel, true))
+        for (let key in properties(this.getKey(obj, "wrappedJSObject"),
+                                   !toplevel))
             try {
-                if (key in obj && !Set.has(seen, key))
+                if (key in obj && !seen.has(key))
                     yield key;
             }
             catch (e) {}
@@ -336,9 +338,6 @@ var JavaScript = Module("javascript", {
     _complete: function (objects, key, compl, string, last) {
         const self = this;
 
-        if (!getOwnPropertyNames && !services.debugger.isOn && !this.context.message)
-            this.context.message = /*L*/"For better completion data, please enable the JavaScript debugger (:set jsdebugger)";
-
         let base = this.context.fork("js", this._top.offset);
         base.forceAnchored = true;
         base.filter = last == null ? key : string;
@@ -349,7 +348,7 @@ var JavaScript = Module("javascript", {
         else {
             base.quote = [last, text => util.escapeString(text, ""), last];
             if (prefix)
-                base.filters.push(item => item.item.indexOf(prefix) === 0);
+                base.filters.push(item => item.item.startsWith(prefix));
         }
 
         if (!compl) {
@@ -464,7 +463,7 @@ var JavaScript = Module("javascript", {
         }
 
         this.context.getCache("evalled", Object);
-        this.context.getCache("evalContext", this.closure.newContext);
+        this.context.getCache("evalContext", this.bound.newContext);
 
         // Okay, have parse stack. Figure out what we're completing.
 
@@ -635,7 +634,7 @@ var JavaScript = Module("javascript", {
         "ROCSSPrimitiveValue", "RangeError", "ReferenceError", "RegExp",
         "StopIteration", "String", "SyntaxError", "TypeError", "URIError",
         "Uint16Array", "Uint32Array", "Uint8Array", "XML", "XMLHttpProgressEvent",
-        "XMLList", "XMLSerializer", "XPCNativeWrapper", "XPCSafeJSWrapper",
+        "XMLList", "XMLSerializer", "XPCNativeWrapper",
         "XULControllers", "constructor", "decodeURI", "decodeURIComponent",
         "encodeURI", "encodeURIComponent", "escape", "eval", "isFinite", "isNaN",
         "isXMLName", "parseFloat", "parseInt", "undefined", "unescape", "uneval"
@@ -694,7 +693,7 @@ var JavaScript = Module("javascript", {
     completion: function (dactyl, modules, window) {
         const { completion } = modules;
         update(modules.completion, {
-            get javascript() modules.javascript.closure.complete,
+            get javascript() modules.javascript.bound.complete,
             javascriptCompleter: JavaScript // Backwards compatibility
         });
     },
@@ -822,8 +821,10 @@ var JavaScript = Module("javascript", {
 
             leave: function leave(params) {
                 leave.superapply(this, arguments);
-                if (!params.push)
+                if (!params.push) {
                     modes.delay(function () { modes.pop(); });
+                    Cu.nukeSandbox(this.context);
+                }
             },
 
             updatePrompt: function updatePrompt() {
index 19e36b46a11574f9bd27e16ec7a69e24c867ee3a..80e64a797bfce4f3e6e4e8fe61b2545fd377dea4 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2009-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -13,6 +13,8 @@ defineModule("main", {
 
 var BASE = "resource://dactyl-content/";
 
+var global = this;
+
 /**
  * @class ModuleBase
  * The base class for all modules.
@@ -89,21 +91,89 @@ var Modules = function Modules(window) {
     Module.list = [];
     Module.constructors = {};
 
-    const create = window.Object.create.bind(window.Object);
+    function newContext(proto, normal, name) {
+        if (normal)
+            return create(proto);
+
+        sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules,
+                                                     sandboxName: name || ("Dactyl Sandbox " + ++_id),
+                                                     wantXrays: true });
+
+        // Hack:
+        // sandbox.Object = jsmodules.Object;
+        sandbox.File = global.File;
+        sandbox.Math = global.Math;
+        sandbox.Set  = global.Set;
+        return sandbox;
+    };
+
 
     const BASES = [BASE, "resource://dactyl-local-content/"];
 
-    jsmodules = Cu.createObjectIn(window);
+    let proxyCache = {};
+    if (config.haveGecko(29))
+        var proxy = new Proxy(window, {
+            get: function window_get(target, prop) {
+                // `in`, not `hasOwnProperty`, because we want to return
+                // unbound methods in `Object.prototype`
+                if (prop in proxyCache)
+                    return proxyCache[prop];
+
+                let p = target[prop];
+                if (callable(p))
+                    return proxyCache[prop] = p.bind(target);
+
+                return p;
+            },
+
+            set: function window_set(target, prop, val) {
+                return target[prop] = val;
+            }
+        });
+    else {
+        // Bug 814892
+        let o = {};
+        // Oh, the brokenness... See bug 793210
+        Object.preventExtensions(o);
+        proxy = new Proxy(o, {
+            get: function window_get(target, prop) {
+                // `in`, not `hasOwnProperty`, because we want to return
+                // unbound methods in `Object.prototype`
+                if (prop in proxyCache)
+                    return proxyCache[prop];
+
+                let p = window[prop];
+                if (callable(p))
+                    return proxyCache[prop] = p.bind(window);
+
+                return p;
+            },
+
+            set: function window_set(target, prop, val) {
+                return window[prop] = val;
+            },
+
+            getOwnPropertyDescriptor: function (target, prop) Object.getOwnPropertyDescriptor(window, prop),
+            getOwnPropertyNames: function (target, prop) Object.getOwnPropertyNames(window),
+            defineProperty: function (target, prop, desc) Object.defineProperty(window, prop, desc),
+            deleteProperty: function (target, prop) { delete window[prop]; },
+            has: function (target, prop) prop in window,
+            hasOwn: function (target, prop) hasOwnProperty(window, prop),
+            enumerate: function (target) (p for (p in window)),
+            iterate: function (target) (p for (p of window))
+        });
+    }
+
+    var jsmodules = newContext(proxy, false, "Dactyl `jsmodules`");
     jsmodules.NAME = "jsmodules";
+
+    const create = bind("create", jsmodules.Object);
+
     const modules = update(create(jsmodules), {
         yes_i_know_i_should_not_report_errors_in_these_branches_thanks: [],
 
         jsmodules: jsmodules,
 
-        get content() this.config.browser.contentWindow || window.content,
-
-        window: window,
-
         Module: Module,
 
         load: function load(script) {
@@ -128,24 +198,7 @@ var Modules = function Modules(window) {
             }
         },
 
-        newContext: function newContext(proto, normal, name) {
-            if (normal)
-                return create(proto);
-
-            if (services.has("dactyl") && services.dactyl.createGlobal)
-                var sandbox = services.dactyl.createGlobal();
-            else
-                sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules,
-                                                             sandboxName: name || ("Dactyl Sandbox " + ++_id),
-                                                             wantXrays: false });
-
-            // Hack:
-            // sandbox.Object = jsmodules.Object;
-            sandbox.File = jsmodules.File;
-            sandbox.Math = jsmodules.Math;
-            sandbox.__proto__ = proto || modules;
-            return sandbox;
-        },
+        newContext: newContext,
 
         get ownPropertyValues() array.compact(
                 Object.getOwnPropertyNames(this)
@@ -167,6 +220,7 @@ overlay.overlayWindow(Object.keys(config.overlays),
         const modules = Modules(window);
         modules.moduleManager = this;
         this.modules = modules;
+        this.jsmodules = modules.jsmodules;
 
         window.dactyl = { modules: modules };
 
@@ -192,8 +246,8 @@ overlay.overlayWindow(Object.keys(config.overlays),
 
         this.startTime = Date.now();
         this.deferredInit = { load: {} };
-        this.seen = {};
-        this.loaded = {};
+        this.seen = RealSet();
+        this.loaded = RealSet();
         modules.loaded = this.loaded;
 
         this.modules = modules;
@@ -225,11 +279,13 @@ overlay.overlayWindow(Object.keys(config.overlays),
     },
 
     cleanup: function cleanup(window) {
-        overlay.windows = overlay.windows.filter(w => w != window);
+        overlay.windows.delete(window);
+
+        Cu.nukeSandbox(this.jsmodules);
     },
 
     unload: function unload(window) {
-        for each (let mod in this.modules.moduleList.reverse()) {
+        for (let mod of this.modules.moduleList.reverse()) {
             mod.stale = true;
 
             if ("destroy" in mod)
@@ -244,7 +300,7 @@ overlay.overlayWindow(Object.keys(config.overlays),
 
         defineModule.loadLog.push("Loaded in " + (Date.now() - this.startTime) + "ms");
 
-        overlay.windows = array.uniq(overlay.windows.concat(window), true);
+        overlay.windows.add(window);
     },
 
     loadModule: function loadModule(module, prereq, frame) {
@@ -258,10 +314,10 @@ overlay.overlayWindow(Object.keys(config.overlays),
         }
 
         try {
-            if (Set.has(loaded, module.className))
+            if (loaded.has(module.className))
                 return;
 
-            if (Set.add(seen, module.className))
+            if (seen.add(module.className))
                 throw Error("Module dependency loop.");
 
             for (let dep in values(module.requires))
@@ -277,9 +333,9 @@ overlay.overlayWindow(Object.keys(config.overlays),
             let obj = defineModule.time(module.className, "init", module);
             Class.replaceProperty(modules, module.className, obj);
 
-            Set.add(loaded, module.className);
+            loaded.add(module.className);
 
-            if (loaded.dactyl && obj.signals)
+            if (loaded.has("dactyl") && obj.signals)
                 modules.dactyl.registerObservers(obj);
 
             if (!module.lazyDepends)
@@ -300,7 +356,7 @@ overlay.overlayWindow(Object.keys(config.overlays),
 
         let className = mod.className || mod.constructor.className;
 
-        if (!Set.has(init, className)) {
+        if (!hasOwnProperty(init, className)) {
             init[className] = function callee() {
                 function finish() {
                     this.currentDependency = className;
@@ -324,11 +380,12 @@ overlay.overlayWindow(Object.keys(config.overlays),
         let { Module, modules } = this.modules;
 
         defineModule.modules.forEach((mod) => {
-            let names = Set(Object.keys(mod.INIT));
+            let names = RealSet(Object.keys(mod.INIT));
             if ("init" in mod.INIT)
-                Set.add(names, "init");
+                names.add("init");
 
-            keys(names).forEach((name) => { this.deferInit(name, mod.INIT, mod); });
+            for (let name of names)
+                this.deferInit(name, mod.INIT, mod);
         });
 
         Module.list.forEach((mod) => {
index 36fead2c19abd57265800d202bf0e6bfbb85ae4d..82c0f3c6f261f27905bc91817941187bed923841 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2011-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -11,7 +11,7 @@ defineModule("messages", {
 
 var Messages = Module("messages", {
 
-    init: function init(name = "messages") {
+    init: function init(name="messages") {
         let self = this;
         this.name = name;
 
@@ -45,13 +45,21 @@ var Messages = Module("messages", {
                     "resource://dactyl-locale-local/en-US/" + this.name + ".properties"],
                    true)
              .map(services.stringBundle.createBundle)
-             .filter(function (bundle) { try { bundle.getSimpleEnumeration(); return true; } catch (e) { return false; } })),
+             .filter(function (bundle) {
+                 try {
+                     bundle.getSimpleEnumeration();
+                     return true;
+                 }
+                 catch (e) {
+                     return false;
+                 }
+             })),
 
     iterate: function () {
-        let seen = {};
+        let seen = RealSet();
         for (let bundle in values(this.bundles))
             for (let { key, value } in iter(bundle.getSimpleEnumeration(), Ci.nsIPropertyElement))
-                if (!Set.add(seen, key))
+                if (!seen.add(key))
                     yield [key, value];
     },
 
@@ -97,7 +105,7 @@ var Messages = Module("messages", {
         let { Buffer, commands, hints, io, mappings, modes, options, sanitizer } = overlay.activeModules;
         file = io.File(file);
 
-        function properties(base, iter_, prop = "description") iter(function _properties() {
+        function properties(base, iter_, prop="description") iter(function _properties() {
             function key(...args) [base, obj.identifier || obj.name].concat(args).join(".").replace(/[\\:=]/g, "\\$&");
 
             for (var obj in iter_) {
@@ -139,9 +147,9 @@ var Messages = Module("messages", {
                     return { configurable: true, enumerable: true, value: this.default, writable: true };
                 */
 
-                if (!Set.has(obj, "localizedProperties"))
-                    obj.localizedProperties = { __proto__: obj.localizedProperties };
-                obj.localizedProperties[prop] = true;
+                if (!hasOwnProperty(obj, "localizedProperties"))
+                    obj.localizedProperties = RealSet(obj.localizedProperties);
+                obj.localizedProperties.add(prop);
 
                 obj[_prop] = this.default;
                 return {
index 77439afe11273f8c0c7a6513efd7d1ed204d45f9..302a1d3400abd40c711470f66dfa84262083566e 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 by Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -65,7 +65,7 @@ var Option = Class("Option", {
             this.globalValue = this.defaultValue;
     },
 
-    magicalProperties: Set(["cleanupValue"]),
+    magicalProperties: RealSet(["cleanupValue"]),
 
     /**
      * @property {string} This option's description, as shown in :listoptions.
@@ -95,7 +95,9 @@ var Option = Class("Option", {
         return this.globalValue = this.defaultValue;
     },
     set globalValue(val) {
-        options.store.set(this.name, { value: val, time: Date.now() });
+        options.store.set(this.name,
+                          { value: this.parse(this.stringify(val)),
+                            time: Date.now() });
     },
 
     /**
@@ -107,6 +109,8 @@ var Option = Class("Option", {
      */
     parse: function parse(value) Option.dequote(value),
 
+    parseKey: function parseKey(value) value,
+
     /**
      * Returns *values* packed in the appropriate format for the option type.
      *
@@ -140,6 +144,9 @@ var Option = Class("Option", {
         if ((scope & Option.SCOPE_GLOBAL) && (values == undefined))
             values = this.globalValue;
 
+        if (hasOwnProperty(this, "_value"))
+            values = this._value;
+
         if (this.getter)
             return util.trapErrors(this.getter, this, values);
 
@@ -169,6 +176,7 @@ var Option = Class("Option", {
         */
         if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal)
             this.globalValue = newValues;
+        this._value = newValues;
 
         this.hasChanged = true;
         this.setFrom = null;
@@ -329,7 +337,7 @@ var Option = Class("Option", {
         let defaultValue = this._defaultValue;
         delete this._defaultValue;
 
-        if (Set.has(this.modules.config.optionDefaults, this.name))
+        if (hasOwnProperty(this.modules.config.optionDefaults, this.name))
             defaultValue = this.modules.config.optionDefaults[this.name];
 
         if (defaultValue == null && this.getter)
@@ -558,6 +566,11 @@ var Option = Class("Option", {
             }, this))
     },
 
+    parseKey: {
+        number: Number,
+        boolean: function boolean(value) value == "true" || value == true ? true : false,
+    },
+
     testValues: {
         regexpmap:  function regexpmap(vals, validator) vals.every(re => validator(re.result)),
         get sitemap() this.regexpmap,
@@ -671,8 +684,8 @@ var Option = Class("Option", {
             values = Array.concat(values);
 
             function uniq(ary) {
-                let seen = {};
-                return ary.filter(elem => !Set.add(seen, elem));
+                let seen = RealSet();
+                return ary.filter(elem => !seen.add(elem));
             }
 
             switch (operator) {
@@ -682,11 +695,11 @@ var Option = Class("Option", {
                 // NOTE: Vim doesn't prepend if there's a match in the current value
                 return uniq(Array.concat(values, this.value), true);
             case "-":
-                return this.value.filter(function (item) !Set.has(this, item), Set(values));
+                return this.value.filter(function (item) !this.has(item), RealSet(values));
             case "=":
                 if (invert) {
-                    let keepValues = this.value.filter(function (item) !Set.has(this, item), Set(values));
-                    let addValues  = values.filter(function (item) !Set.has(this, item), Set(this.value));
+                    let keepValues = this.value.filter(function (item) !this.has(item), RealSet(values));
+                    let addValues  = values.filter(function (item) !this.has(item), RealSet(this.value));
                     return addValues.concat(keepValues);
                 }
                 return values;
@@ -723,7 +736,9 @@ var Option = Class("Option", {
         if (isObject(vals) && !isArray(vals)) {
             let k = values(completions.call(this, { values: {} })).toObject();
             let v = values(completions.call(this, { value: "" })).toObject();
-            return Object.keys(vals).every(Set.has(k)) && values(vals).every(Set.has(v));
+
+            return Object.keys(vals).every(hasOwnProperty.bind(null, k)) &&
+                   values(vals).every(hasOwnProperty.bind(null, v));
         }
 
         if (this.values)
@@ -732,12 +747,15 @@ var Option = Class("Option", {
             acceptable = completions.call(this);
 
         if (isArray(acceptable))
-            acceptable = Set(acceptable.map(([k]) => (k)));
+            acceptable = RealSet(acceptable.map(([k]) => k));
+        else
+            acceptable = RealSet(this.parseKey(k)
+                                 for (k of Object.keys(acceptable)));
 
         if (this.type === "regexpmap" || this.type === "sitemap")
-            return Array.concat(vals).every(re => Set.has(acceptable, re.result));
+            return Array.concat(vals).every(re => acceptable.has(re.result));
 
-        return Array.concat(vals).every(Set.has(acceptable));
+        return Array.concat(vals).every(v => acceptable.has(v));
     },
 
     types: {}
@@ -766,6 +784,9 @@ var Option = Class("Option", {
     if (type in Option.parse)
         class_.prototype.parse = Option.parse[type];
 
+    if (type in Option.parseKey)
+        class_.prototype.parseKey = Option.parse[type];
+
     if (type in Option.stringify)
         class_.prototype.stringify = Option.stringify[type];
 
@@ -789,7 +810,7 @@ var OptionHive = Class("OptionHive", Contexts.Hive, {
     init: function init(group) {
         init.supercall(this, group);
         this.values = {};
-        this.has = Set.has(this.values);
+        this.has = v => hasOwnProperty(this.values, v);
     },
 
     add: function add(names, description, type, defaultValue, extraInfo) {
@@ -822,17 +843,18 @@ var Options = Module("options", {
                     opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true);
             }, window);
 
-            modules.cache.register("options.dtd", () =>
-                util.makeDTD(
-                    iter(([["option", o.name, "default"].join("."),
-                           o.type === "string" ? o.defaultValue.replace(/'/g, "''") :
-                           o.defaultValue === true  ? "on"  :
-                           o.defaultValue === false ? "off" : o.stringDefaultValue]
-                          for (o in self)),
+            modules.cache.register("options.dtd",
+                () => util.makeDTD(
+                        iter(([["option", o.name, "default"].join("."),
+                               o.type === "string" ? o.defaultValue.replace(/'/g, "''") :
+                               o.defaultValue === true  ? "on"  :
+                               o.defaultValue === false ? "off" : o.stringDefaultValue]
+                              for (o in self)),
 
-                         ([["option", o.name, "type"].join("."), o.type] for (o in self)),
+                             ([["option", o.name, "type"].join("."), o.type] for (o in self)),
 
-                         config.dtd)));
+                             config.dtd)),
+                true);
         },
 
         signals: {
@@ -1098,13 +1120,13 @@ var Options = Module("options", {
 
             let list = [];
             function flushList() {
-                let names = Set(list.map(opt => opt.option ? opt.option.name : ""));
+                let names = RealSet(list.map(opt => opt.option ? opt.option.name : ""));
                 if (list.length)
                     if (list.some(opt => opt.all))
                         options.list(opt => !(list[0].onlyNonDefault && opt.isDefault),
                                      list[0].scope);
                     else
-                        options.list(opt => Set.has(names, opt.name),
+                        options.list(opt => names.has(opt.name),
                                      list[0].scope);
                 list = [];
             }
@@ -1279,12 +1301,12 @@ var Options = Module("options", {
 
             // Fill in the current values if we're removing
             if (opt.operator == "-" && isArray(opt.values)) {
-                let have = Set([i.text for (i in values(context.allItems.items))]);
+                let have = RealSet((i.text for (i in values(context.allItems.items))));
                 context = context.fork("current-values", 0);
                 context.anchored = optcontext.anchored;
                 context.maxItems = optcontext.maxItems;
 
-                context.filters.push(i => !Set.has(have, i.text));
+                context.filters.push(i => !have.has(i.text));
                 modules.completion.optionValue(context, opt.name, opt.operator, null,
                                        function (context) {
                                            context.generate = () => option.value.map(o => [o, ""]);
@@ -1313,7 +1335,7 @@ var Options = Module("options", {
 
                     util.assert(scope == "g:" || scope == null,
                                 _("command.let.illegalVar", scope + name));
-                    util.assert(Set.has(globalVariables, name) || (expr && !op),
+                    util.assert(hasOwnProperty(globalVariables, name) || (expr && !op),
                                 _("command.let.undefinedVar", fullName));
 
                     if (!expr)
@@ -1411,7 +1433,7 @@ var Options = Module("options", {
             function (args) {
                 for (let [, name] in args) {
                     name = name.replace(/^g:/, ""); // throw away the scope prefix
-                    if (!Set.has(dactyl._globalVariables, name)) {
+                    if (!hasOwnProperty(dactyl._globalVariables, name)) {
                         if (!args.bang)
                             dactyl.echoerr(_("command.let.noSuch", name));
                         return;
@@ -1493,11 +1515,10 @@ var Options = Module("options", {
 
             function val(obj) {
                 if (isArray(opt.defaultValue)) {
-                    let val = array.nth(obj, re => (re.key == extra.key),
-                                        0);
+                    let val = [].find.call(obj, re => (re.key == extra.key));
                     return val && val.result;
                 }
-                if (Set.has(opt.defaultValue, extra.key))
+                if (hasOwnProperty(opt.defaultValue, extra.key))
                     return obj[extra.key];
             }
 
index c52ba4ad95a9fceb4d599fa9131b302854be3a4f..f52a23ebaaec4f843f9b55321e8c3b9d4acef4a3 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2009-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -103,16 +103,20 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
             target = null;
 
         this.setData(doc, "listeners", listeners.filter(function (args) {
-            if (target == null || args[0].get() == target && args[1] == event && args[2].wrapped == callback && args[3] == capture) {
-                args[0].get().removeEventListener.apply(args[0].get(), args.slice(1));
+            let elem = args[0].get();
+            if (target == null || elem == target && args[1] == event && args[2].wrapped == callback && args[3] == capture) {
+                elem.removeEventListener.apply(elem, args.slice(1));
                 return false;
             }
-            return !args[0].get();
+            return elem;
         }));
     },
 
     cleanup: function cleanup(reason) {
         for (let doc in util.iterDocuments()) {
+            for (let callback in values(this.getData(doc, "cleanup")))
+                util.trapErrors(callback, doc, reason);
+
             for (let elem in values(this.getData(doc, "overlayElements")))
                 if (elem.parentNode)
                     elem.parentNode.removeChild(elem);
@@ -121,9 +125,6 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
                 if (getAttr(elem, ns, name) === value)
                     setAttr(elem, ns, name, orig);
 
-            for (let callback in values(this.getData(doc, "cleanup")))
-                util.trapErrors(callback, doc, reason);
-
             this.unlisten(doc, true);
 
             delete doc[this.id];
@@ -185,8 +186,10 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
 
     setData: function setData(obj, key, val) {
         let data = this.getData(obj);
+        if (val !== undefined)
+            return data[key] = val;
 
-        return data[key] = val;
+        delete data[key];
     },
 
     overlayWindow: function overlayWindow(url, fn) {
@@ -215,7 +218,7 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
     _loadOverlays: function _loadOverlays(window) {
         let overlays = this.getData(window, "overlays");
 
-        for each (let obj in overlay.overlays[window.document.documentURI] || []) {
+        for (let obj of overlay.overlays[window.document.documentURI] || []) {
             if (~overlays.indexOf(obj))
                 continue;
             overlays.push(obj);
@@ -240,16 +243,12 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
                     if (elem = doc.getElementById(String(elem))) {
                         // Urgh. Hack.
                         let namespaces;
-                        if (attrs && !isXML(attrs))
+                        if (attrs)
                             namespaces = iter([k.slice(6), DOM.fromJSON.namespaces[v] || v]
                                               for ([k, v] in Iterator(attrs))
                                               if (/^xmlns(?:$|:)/.test(k))).toObject();
 
-                        let node;
-                        if (isXML(xml))
-                            node = DOM.fromXML(xml, doc, obj.objects);
-                        else
-                            node = DOM.fromJSON(xml, doc, obj.objects, namespaces);
+                        let node = DOM.fromJSON(xml, doc, obj.objects, namespaces);
 
                         if (!(node instanceof Ci.nsIDOMDocumentFragment))
                             savedElems.push(node);
@@ -259,12 +258,6 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
 
                         fn(elem, node);
 
-                        if (isXML(attrs))
-                            // Evilness and such.
-                            let (oldAttrs = attrs) {
-                                attrs = (attr for each (attr in oldAttrs));
-                            }
-
                         for (let attr in attrs || []) {
                             let [ns, localName] = DOM.parseNamespace(attr);
                             let name = attr;
@@ -343,18 +336,23 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
         overrides = update(Object.create(original), overrides);
 
         Object.getOwnPropertyNames(overrides).forEach(function (k) {
-            let orig, desc = Object.getOwnPropertyDescriptor(overrides, k);
+            let desc = Object.getOwnPropertyDescriptor(overrides, k);
+
             if (desc.value instanceof Class.Property)
                 desc = desc.value.init(k) || desc.value;
 
             if (k in object) {
-                for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj))
-                    if (orig = Object.getOwnPropertyDescriptor(obj, k))
+                for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj)) {
+                    var orig = Object.getOwnPropertyDescriptor(obj, k);
+                    if (orig)
                         Object.defineProperty(original, k, orig);
+                }
 
-                if (!orig)
-                    if (orig = Object.getPropertyDescriptor(object, k))
+                if (!orig) {
+                    orig = Object.getPropertyDescriptor(object, k);
+                    if (orig)
                         Object.defineProperty(original, k, orig);
+                }
             }
 
             // Guard against horrible add-ons that use eval-based monkey
@@ -366,7 +364,7 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
                 delete desc.writable;
                 desc.get = function get() value;
                 desc.set = function set(val) {
-                    if (!callable(val) || Function.prototype.toString(val).indexOf(sentinel) < 0)
+                    if (!callable(val) || !Function.prototype.toString(val).contains(sentinel))
                         Class.replaceProperty(this, k, val);
                     else {
                         let package_ = util.newURI(Components.stack.caller.filename).host;
@@ -398,7 +396,7 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
         }, this);
 
         return function unwrap() {
-            for each (let k in Object.getOwnPropertyNames(original))
+            for (let k of Object.getOwnPropertyNames(original))
                 if (Object.getOwnPropertyDescriptor(object, k).configurable)
                     Object.defineProperty(object, k, Object.getOwnPropertyDescriptor(original, k));
                 else {
@@ -412,19 +410,22 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen
 
     get activeModules() this.activeWindow && this.activeWindow.dactyl.modules,
 
-    get modules() this.windows.map(w => w.dactyl.modules),
+    get modules() [w.dactyl.modules for (w of this.windows)],
 
     /**
      * The most recently active dactyl window.
      */
-    get activeWindow() this.windows[0],
+    get activeWindow() {
+        let win = this._activeWindow && this._activeWindow.get();
+        return this.windows.has(win) && win;
+    },
 
-    set activeWindow(win) this.windows = [win].concat(this.windows.filter(w => w != win)),
+    set activeWindow(win) this._activeWindow = util.weakReference(win),
 
     /**
      * A list of extant dactyl windows.
      */
-    windows: Class.Memoize(() => [])
+    windows: Class.Memoize(() => RealSet())
 });
 
 endModule();
index 47fec508aafeb08c2fb4d63c998e8bf9ffa4d46b..5cdfc9b4e14f1e33cbf52f837bc96e7641bdbd27 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -291,7 +291,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
      * @param {string} branch The preference name. @optional
      */
     resetBranch: function resetBranch(branch) {
-        this.getNames(branch).forEach(this.closure.reset);
+        this.getNames(branch).forEach(this.bound.reset);
     },
 
     /**
@@ -351,9 +351,10 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
             if (observers) {
                 let value = this.get(data, false);
                 this._observers[data] = observers.filter(function (callback) {
-                    if (!callback.get())
+                    callback = callback.get();
+                    if (!callback)
                         return false;
-                    util.trapErrors(callback.get(), null, value);
+                    util.trapErrors(callback, null, value);
                     return true;
                 });
             }
@@ -398,7 +399,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
         function prefs() {
             for (let [, pref] in Iterator(prefArray)) {
                 let userValue = services.pref.prefHasUserValue(pref);
-                if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1)
+                if (onlyNonDefault && !userValue || !pref.contains(filter))
                     continue;
 
                 let value = this.get(pref);
diff --git a/common/modules/promises.jsm b/common/modules/promises.jsm
new file mode 100644 (file)
index 0000000..a1b832f
--- /dev/null
@@ -0,0 +1,148 @@
+// Copyright (c) 2014 Kris Maglione <maglione.k at Gmail>
+//
+// This work is licensed for reuse under an MIT license. Details are
+// given in the LICENSE.txt file included with this file.
+"use strict";
+
+defineModule("promises", {
+    exports: ["Promise", "Task", "promises"],
+    require: []
+});
+
+lazyRequire("services", ["services"]);
+
+lazyRequire("resource://gre/modules/Promise.jsm", ["Promise"]);
+lazyRequire("resource://gre/modules/Task.jsm", ["Task"]);
+
+function withCallbacks(fn) {
+    return function wrapper(...args) {
+        let deferred = Promise.defer();
+        function resolve(arg) { deferred.resolve(arg); }
+        function reject(arg)  { deferred.reject(arg); }
+        fn.apply(this, [[resolve, reject, deferred]].concat(args));
+        return deferred.promise;
+    }
+}
+
+var Promises = Module("Promises", {
+    _cancel: WeakMap(),
+
+    /**
+     * Allows promises to be canceled..
+     *
+     * @param {Promise} promise The promise to cancel.
+     * @param {*} arg Argument to be passed to the cancellation
+     * function.
+     */
+    cancel: function cancel(promise, reason) {
+        let cleanup = this._cancel.get(promise);
+        if (cleanup) {
+            cleanup[0](promise);
+            cleanup[1].reject(reason);
+        }
+        this._cancel.delete(promise);
+    },
+
+    /**
+     * Registers a cleanup function for the given deferred promise.
+     *
+     * @param {Deferred} promise The promise to cancel.
+     * @param {function} fn The cleanup function.
+     */
+    oncancel: function oncancel(deferred, fn) {
+        this._cancel.set(deferred.promise, [fn, deferred]);
+    },
+
+    /**
+     * Returns a promise which resolves after a brief delay.
+     */
+    delay: withCallbacks(function delay([accept]) {
+        let { mainThread } = services.threading;
+        mainThread.dispatch(accept, mainThread.DISPATCH_NORMAL);
+    }),
+
+    /**
+     * Returns a promise which resolves with the given argument.
+     */
+    accept: function fail(arg) {
+        let deferred = Promise.defer();
+        deferred.resolve(arg);
+        return deferred.promise;
+    },
+
+    /**
+     * Returns a promise which fails with the given argument.
+     */
+    fail: function fail(arg) {
+        let deferred = Promise.defer();
+        deferred.reject(arg);
+        return deferred.promise;
+    },
+
+    /**
+     * Returns a promise which resolves after the given number of
+     * milliseconds.
+     *
+     * @param {number} delay The number of milliseconds to wait.
+     */
+    sleep: withCallbacks(function sleep([callback], delay) {
+        this.timeout(callback, delay);
+    }),
+
+    /**
+     * Wraps the given function so that each call spawns a Task.
+     *
+     * @param {function} fn The function to wrap.
+     * @returns {function}
+     */
+    task: function task(fn) {
+        return function task_(...args) {
+            return Task.spawn(fn.bind.apply(fn, [this].concat(args)));
+        }
+    },
+
+    /**
+     * Returns a promise which resolves when the function *test* returns
+     * true, or *timeout* milliseconds have expired.
+     *
+     * @param {function} test The predicate on which to wait.
+     * @param {Number} timeout The maximum number of milliseconds to
+     *      wait.
+     *      @optional
+     * @param {number} pollInterval The poll interval, in milliseconds.
+     *      @default 10
+     */
+    waitFor: withCallbacks(function waitFor([accept, reject], test, timeout=null, pollInterval=10) {
+        let end = timeout && Date.now() + timeout, result;
+
+        let timer = services.Timer(
+            () => {
+                try {
+                    var result = test();
+                }
+                catch (e) {
+                    timer.cancel();
+                    reject(e);
+                }
+                if (result) {
+                    timer.cancel();
+                    accept(result);
+                }
+            },
+            pollInterval, services.Timer.TYPE_REPEATING_SLACK);
+    }),
+
+    /**
+     * Wraps the given function so that its first argument is an array
+     * of success and failure callbacks which, when called, resolve the
+     * returned promise.
+     *
+     * @param {function} fn The function to wrap.
+     * @returns {Promise}
+     */
+    withCallbacks: withCallbacks,
+});
+
+endModule();
+
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript:
index f73e209247102989d14f113b4518ba3d51ce752e..d5e6d1a379ca20b28b03223064a3c22db9430363 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2012 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -143,8 +143,8 @@ ProtocolBase.prototype = {
 };
 
 function LocaleChannel(pkg, locale, path, orig) {
-    for each (let locale in [locale, "en-US"])
-        for each (let sep in "-/") {
+    for (let locale of [locale, "en-US"])
+        for (let sep of "-/") {
             var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true, true);
             if (channel)
                 return channel;
index 2db1dfe54d1455ce9faebf3933e788649b8f84bd..8f08a12738ff37d401cbac29757c663b473a9186 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright (c) 2009 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2009-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2009-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -151,11 +151,11 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                     // "Allow this site to open popups" ...
                     services.permissions.removeAll();
                     // Zoom level, ...
-                    services.contentPrefs.removeGroupedPrefs();
+                    services.contentPrefs.removeAllDomains(null);
                 }
 
                 // "Never remember passwords" ...
-                for each (let domain in services.loginManager.getAllDisabledHosts())
+                for (let domain of services.loginManager.getAllDisabledHosts())
                     if (!host || util.isSubdomain(domain, host))
                         services.loginManager.setLoginSavingEnabled(host, true);
             },
@@ -247,14 +247,19 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             if (!("value" in prop) || !callable(prop.value) && !(k in item))
                 Object.defineProperty(item, k, prop);
 
-        let names = Set([name].concat(params.contains || []).map(e => "clear-" + e));
+        function getWindow(obj) {
+            obj = Class.objectGlobal(obj);
+            return obj.window || obj;
+        }
+
+        let names = RealSet([name].concat(params.contains || []).map(e => "clear-" + e));
         if (params.action)
             storage.addObserver("sanitizer",
                 function (key, event, arg) {
-                    if (event in names)
+                    if (names.has(event))
                         params.action.apply(params, arg);
                 },
-                Class.objectGlobal(params.action));
+                getWindow(params.action));
 
         if (params.privateEnter || params.privateLeave)
             storage.addObserver("private-mode",
@@ -263,7 +268,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                     if (meth)
                         meth.call(params);
                 },
-                Class.objectGlobal(params.action));
+                getWindow(params.privateEnter || params.privateLeave));
     },
 
     observers: {
@@ -373,12 +378,12 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
     UNPERMS: Class.Memoize(function () iter(this.PERMS).map(Array.reverse).toObject()),
 
     COMMANDS: {
-        unset:   /*L*/"Unset",
-        allow:   /*L*/"Allowed",
-        deny:    /*L*/"Denied",
-        session: /*L*/"Allowed for the current session",
-        list:    /*L*/"List all cookies for domain",
-        clear:   /*L*/"Clear all cookies for domain",
+        "unset":   /*L*/"Unset",
+        "allow":   /*L*/"Allowed",
+        "deny":    /*L*/"Denied",
+        "session": /*L*/"Allowed for the current session",
+        "list":    /*L*/"List all cookies for domain",
+        "clear":   /*L*/"Clear all cookies for domain",
         "clear-persistent": /*L*/"Clear all persistent cookies for domain",
         "clear-session":    /*L*/"Clear all session cookies for domain"
     },
@@ -394,7 +399,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
         for (let c in iter(services.cookies, Ci.nsICookie2))
             if (!host || util.isSubdomain(c.rawHost, host) ||
                     c.host[0] == "." && c.host.length < host.length
-                        && host.indexOf(c.host) == host.length - c.host.length)
+                        && host.endsWith(c.host))
                 yield c;
 
     },
@@ -446,11 +451,14 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                 if (args.bang)
                     dactyl.assert(args.length == 0, _("error.trailingCharacters"));
                 else {
+                    dactyl.assert(args.length, _("error.argumentRequired"));
                     dactyl.assert(opt.validator(args), _("error.invalidArgument"));
                     opt = { __proto__: opt, value: args.slice() };
                 }
 
-                let items = Object.keys(sanitizer.itemMap).slice(1).filter(opt.has, opt);
+                let items = Object.keys(sanitizer.itemMap)
+                                  .slice(1)
+                                  .filter(opt.has, opt);
 
                 function sanitize(items) {
                     sanitizer.range = range.native;
@@ -466,7 +474,10 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                         sanitizer.sanitize(items, range);
                 }
 
-                if (array.nth(opt.value, i => i == "all" || /^!/.test(i), 0) == "all" && !args["-host"])
+                if ("all" == opt.value.find(i => (i == "all" ||
+                                                  /^!/.test(i)))
+                    && !args["-host"])
+
                     modules.commandline.input(_("sanitize.prompt.deleteAll") + " ",
                         function (resp) {
                             if (resp.match(/^y(es)?$/i)) {
@@ -586,7 +597,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
     completion: function initCompletion(dactyl, modules, window) {
         modules.completion.visibleHosts = function completeHosts(context) {
             let res = util.visibleHosts(window.content);
-            if (context.filter && !res.some(host => host.indexOf(context.filter) >= 0))
+            if (context.filter && !res.some(host => host.contains(context.filter)))
                 res.push(context.filter);
 
             context.title = ["Domain"];
@@ -612,12 +623,11 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                 },
 
                 has: function has(val)
-                    let (res = array.nth(this.value, v => (v == "all" || v.replace(/^!/, "") == val),
-                                         0))
+                    let (res = this.value.find(v => (v == "all" || v.replace(/^!/, "") == val)))
                         res && !/^!/.test(res),
 
                 validator: function (values) values.length &&
-                    values.every(val => (val === "all" || Set.has(sanitizer.itemMap, val.replace(/^!/, ""))))
+                    values.every(val => (val === "all" || hasOwnProperty(sanitizer.itemMap, val.replace(/^!/, ""))))
             });
 
         options.add(["sanitizeshutdown", "ss"],
@@ -635,10 +645,10 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                         sanitizer.runAtShutdown = false;
                     else {
                         sanitizer.runAtShutdown = true;
-                        let have = Set(value);
+                        let have = RealSet(value);
                         for (let item in values(sanitizer.itemMap))
                             prefs.set(item.shutdownPref,
-                                      Boolean(Set.has(have, item.name) ^ Set.has(have, "all")));
+                                      Boolean(have.has(item.name) ^ have.has("all")));
                     }
                     return value;
                 }
index 315f95189531ef9bd5098dde994faa6693a8967f..b586139418817f24e150305627303d096b5c024d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -39,7 +39,8 @@ var Services = Module("Services", {
         this.add("clipboardHelper",     "@mozilla.org/widget/clipboardhelper;1",            "nsIClipboardHelper");
         this.add("commandLineHandler",  "@mozilla.org/commandlinehandler/general-startup;1?type=dactyl");
         this.add("console",             "@mozilla.org/consoleservice;1",                    "nsIConsoleService");
-        this.add("contentPrefs",        "@mozilla.org/content-pref/service;1",              "nsIContentPrefService");
+        this.add("contentPrefs",        "@mozilla.org/content-pref/service;1",              ["nsIContentPrefService",
+                                                                                             "nsIContentPrefService2"]);
         this.add("dactyl",              "@dactyl.googlecode.com/extra/utils",               "dactylIUtils");
         this.add("dactyl:",             this.PROTOCOL + "dactyl");
         this.add("debugger",            "@mozilla.org/js/jsd/debugger-service;1",           "jsdIDebuggerService");
@@ -62,6 +63,7 @@ var Services = Module("Services", {
         this.add("mime",                "@mozilla.org/mime;1",                              "nsIMIMEService");
         this.add("observer",            "@mozilla.org/observer-service;1",                  "nsIObserverService");
         this.add("pref",                "@mozilla.org/preferences-service;1",               ["nsIPrefBranch2", "nsIPrefService"]);
+        this.add("printSettings",       "@mozilla.org/gfx/printsettings-service;1",         "nsIPrintSettingsService");
         this.add("privateBrowsing",     "@mozilla.org/privatebrowsing;1",                   "nsIPrivateBrowsingService");
         this.add("profile",             "@mozilla.org/toolkit/profile-service;1",           "nsIToolkitProfileService");
         this.add("resource:",           this.PROTOCOL + "resource",                         ["nsIProtocolHandler", "nsIResProtocolHandler"]);
@@ -183,7 +185,7 @@ var Services = Module("Services", {
                     function () callable(XPCOMShim(this.interfaces)[this.init]));
 
         this[name] = (function Create() this._create(name, arguments)).bind(this);
-        update.apply(null, [this[name]].concat([Ci[i] for each (i in Array.concat(ifaces))]));
+        update.apply(null, [this[name]].concat([Ci[i] for (i of Array.concat(ifaces))]));
         return this[name];
     },
 
@@ -206,7 +208,7 @@ var Services = Module("Services", {
      *
      * @param {string} name The service's cache key.
      */
-    has: function has(name) Set.has(this.services, name) && this.services[name].class in Cc &&
+    has: function has(name) hasOwnProperty(this.services, name) && this.services[name].class in Cc &&
         this.services[name].interfaces.every(iface => iface in Ci)
 });
 
index 6cc704f0040c477242a61d3ee7705160621a9961..5ff94561bde8e2ba2e97c259cb2424bad9d7dbbc 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -6,16 +6,20 @@
 
 defineModule("storage", {
     exports: ["File", "Storage", "storage"],
-    require: ["services", "util"]
+    require: ["promises", "services", "util"]
 });
 
 lazyRequire("config", ["config"]);
 lazyRequire("io", ["IO"]);
 lazyRequire("overlay", ["overlay"]);
 
+lazyRequire("resource://gre/modules/osfile.jsm", ["OS"]);
+
 var win32 = /^win(32|nt)$/i.test(services.runtime.OS);
 var myObject = JSON.parse("{}").constructor;
 
+var global = Cu.getGlobalForObject(this);
+
 var StoreBase = Class("StoreBase", {
     OPTIONS: ["privateData", "replacer"],
 
@@ -23,7 +27,7 @@ var StoreBase = Class("StoreBase", {
 
     get serial() JSON.stringify(this._object, this.replacer),
 
-    init: function (name, store, load, options) {
+    init: function init(name, store, load, options) {
         this._load = load;
         this._options = options;
 
@@ -35,13 +39,21 @@ var StoreBase = Class("StoreBase", {
         this.reload();
     },
 
-    clone: function (storage) {
+    clone: function clone(storage) {
         let store = storage.privateMode ? false : this.store;
         let res = this.constructor(this.name, store, this._load, this._options);
         res.storage = storage;
         return res;
     },
 
+    makeOwn: function makeOwn(val) {
+        if (typeof val != "object")
+            return val;
+        if (Cu.getGlobalForObject(val) == global)
+            return val;
+        return JSON.parse(JSON.stringify(val, this.replacer));
+    },
+
     changed: function () { this.timer && this.timer.tell(); },
 
     reload: function reload() {
@@ -52,7 +64,8 @@ var StoreBase = Class("StoreBase", {
     delete: function delete_() {
         delete storage.keys[this.name];
         delete storage[this.name];
-        storage.infoPath.child(this.name).remove(false);
+        return OS.File.remove(
+            storage.infoPath.child(this.name).path);
     },
 
     save: function () { (self.storage || storage)._saveData(this); },
@@ -67,7 +80,7 @@ var ArrayStore = Class("ArrayStore", StoreBase, {
 
     set: function set(index, value, quiet) {
         var orig = this._object[index];
-        this._object[index] = value;
+        this._object[index] = this.makeOwn(value);
         if (!quiet)
             this.fireEvent("change", index);
 
@@ -75,7 +88,7 @@ var ArrayStore = Class("ArrayStore", StoreBase, {
     },
 
     push: function push(value) {
-        this._object.push(value);
+        this._object.push(this.makeOwn(value));
         this.fireEvent("push", this._object.length);
     },
 
@@ -96,6 +109,7 @@ var ArrayStore = Class("ArrayStore", StoreBase, {
     },
 
     insert: function insert(value, ord) {
+        value = this.makeOwn(value);
         if (ord == 0)
             this._object.unshift(value);
         else
@@ -120,7 +134,8 @@ var ArrayStore = Class("ArrayStore", StoreBase, {
     mutate: function mutate(funcName) {
         var _funcName = funcName;
         arguments[0] = this._object;
-        this._object = Array[_funcName].apply(Array, arguments);
+        this._object = Array[_funcName].apply(Array, arguments)
+                                       .map(this.makeOwn.bind(this));
         this.fireEvent("change", null);
     },
 
@@ -136,11 +151,13 @@ var ObjectStore = Class("ObjectStore", StoreBase, {
     },
 
     get: function get(key, default_) {
-        return key in this._object  ? this._object[key] :
+        return this.has(key)        ? this._object[key] :
                arguments.length > 1 ? this.set(key, default_) :
                                       undefined;
     },
 
+    has: function has(key) hasOwnProperty(this._object, key),
+
     keys: function keys() Object.keys(this._object),
 
     remove: function remove(key) {
@@ -153,7 +170,7 @@ var ObjectStore = Class("ObjectStore", StoreBase, {
     set: function set(key, val) {
         var defined = key in this._object;
         var orig = this._object[key];
-        this._object[key] = val;
+        this._object[key] = this.makeOwn(val);
         if (!defined)
             this.fireEvent("add", key);
         else if (orig != val)
@@ -210,12 +227,18 @@ var Storage = Module("Storage", {
         }
     },
 
-    _saveData: function saveData(obj) {
+    _saveData: promises.task(function saveData(obj) {
         if (obj.privateData && storage.privateMode)
             return;
-        if (obj.store && storage.infoPath)
-            storage.infoPath.child(obj.name).write(obj.serial);
-    },
+        if (obj.store && storage.infoPath) {
+            var { path } = storage.infoPath.child(obj.name);
+            yield OS.File.makeDir(storage.infoPath.path,
+                                  { ignoreExisting: true });
+            yield OS.File.writeAtomic(
+                path, obj.serial,
+                { tmpPath: path + ".part" });
+        }
+    }),
 
     storeForSession: function storeForSession(key, val) {
         if (val)
@@ -236,11 +259,12 @@ var Storage = Module("Storage", {
                 this[key].timer.flush();
             delete this[key];
             delete this.keys[key];
-            this.infoPath.child(key).remove(false);
+            return OS.File.remove(
+                this.infoPath.child(key).path);
         }
     },
 
-    newObject: function newObject(key, constructor, params) {
+    newObject: function newObject(key, constructor, params={}) {
         if (params == null || !isObject(params))
             throw Error("Invalid argument type");
 
@@ -269,68 +293,41 @@ var Storage = Module("Storage", {
         return this.keys[key];
     },
 
-    newMap: function newMap(key, options) {
+    newMap: function newMap(key, options={}) {
         return this.newObject(key, ObjectStore, options);
     },
 
-    newArray: function newArray(key, options) {
+    newArray: function newArray(key, options={}) {
         return this.newObject(key, ArrayStore, update({ type: Array }, options));
     },
 
-    addObserver: function addObserver(key, callback, ref) {
-        if (ref) {
-            let refs = overlay.getData(ref, "storage-refs");
-            refs.push(callback);
-            var callbackRef = util.weakReference(callback);
-        }
-        else {
-            callbackRef = { get: function () callback };
-        }
-
-        this.removeDeadObservers();
-
-        if (!(key in this.observers))
-            this.observers[key] = [];
-
-        if (!this.observers[key].some(o => o.callback.get() == callback))
-            this.observers[key].push({ ref: ref && Cu.getWeakReference(ref),
-                                       callback: callbackRef });
+    get observerMaps() {
+        yield this.observers;
+        for (let window of overlay.windows)
+            yield overlay.getData(window, "storage-observers", Object);
     },
 
-    removeObserver: function (key, callback) {
-        this.removeDeadObservers();
+    addObserver: function addObserver(key, callback, window) {
+        var { observers } = this;
+        if (window instanceof Ci.nsIDOMWindow)
+            observers = overlay.getData(window, "storage-observers", Object);
 
-        if (!(key in this.observers))
-            return;
+        if (!hasOwnProperty(observers, key))
+            observers[key] = RealSet();
 
-        this.observers[key] = this.observers[key].filter(elem => elem.callback.get() != callback);
-        if (this.observers[key].length == 0)
-            delete obsevers[key];
+        observers[key].add(callback);
     },
 
-    removeDeadObservers: function () {
-        function filter(o) {
-            if (!o.callback.get())
-                return false;
-
-            let ref = o.ref && o.ref.get();
-            return ref && !ref.closed && overlay.getData(ref, "storage-refs", null);
-        }
-
-        for (let [key, ary] in Iterator(this.observers)) {
-            this.observers[key] = ary = ary.filter(filter);
-            if (!ary.length)
-                delete this.observers[key];
-        }
+    removeObserver: function (key, callback) {
+        for (let observers in this.observerMaps)
+            if (key in observers)
+                observers[key].remove(callback);
     },
 
     fireEvent: function fireEvent(key, event, arg) {
-        this.removeDeadObservers();
-
-        if (key in this.observers)
-            // Safe, since we have our own Array object here.
-            for each (let observer in this.observers[key])
-                observer.callback.get()(key, event, arg);
+        for (let observers in this.observerMaps)
+            for (let observer of observers[key] || [])
+                observer(key, event, arg);
 
         if (key in this.keys && this.keys[key].timer)
             this[key].timer.tell();
@@ -382,8 +379,7 @@ var Storage = Module("Storage", {
     }
 }, {
     cleanup: function (dactyl, modules, window) {
-        overlay.setData(window, "storage-refs", null);
-        this.removeDeadObservers();
+        overlay.setData(window, "storage-callbacks", undefined);
     }
 });
 
@@ -458,7 +454,7 @@ var File = Class("File", {
     child: function child() {
         let f = this.constructor(this);
         for (let [, name] in Iterator(arguments))
-            for each (let elem in name.split(File.pathSplit))
+            for (let elem of name.split(File.pathSplit))
                 f.append(elem);
         return f;
     },
index 6584a7ef704482fa9c6efd0c1b5697d8569ea023..ee635c20bbd3476ff11f97b44449e3d2ec70eabd 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -111,8 +111,10 @@ var Hive = Class("Hive", {
     },
 
     cleanup: function cleanup() {
-        for (let sheet in values(this.sheets))
-            sheet.enabled = false;
+        for (let sheet of this.sheets)
+            util.trapErrors(() => {
+                sheet.enabled = false;
+            });
     },
 
     __iterator__: function () Iterator(this.sheets),
@@ -137,8 +139,12 @@ var Hive = Class("Hive", {
      */
     add: function add(name, filter, css, agent, lazy) {
 
-        if (!isArray(filter))
+        if (isArray(filter))
+            // Need an array from the same compartment.
+            filter = Array.slice(filter);
+        else
             filter = filter.split(",");
+
         if (name && name in this.names) {
             var sheet = this.names[name];
             sheet.agent = agent;
@@ -212,7 +218,7 @@ var Hive = Class("Hive", {
             name = null;
         }
 
-        if (filter && filter.indexOf(",") > -1)
+        if (filter && filter.contains(","))
             return filter.split(",").reduce(
                 (n, f) => n + this.removeSheet(name, f, index), 0);
 
@@ -261,7 +267,7 @@ var Styles = Module("Styles", {
         update(services["dactyl:"].providers, {
             "style": function styleProvider(uri, path) {
                 let id = parseInt(path);
-                if (Set.has(styles.allSheets, id))
+                if (hasOwnProperty(styles.allSheets, id))
                     return ["text/css", styles.allSheets[id].fullCSS];
                 return null;
             }
@@ -269,7 +275,7 @@ var Styles = Module("Styles", {
     },
 
     cleanup: function cleanup() {
-        for each (let hive in this.hives)
+        for (let hive of this.hives || [])
             util.trapErrors("cleanup", hive);
         this.hives = [];
         this.user = this.addHive("user", this, true);
@@ -277,8 +283,7 @@ var Styles = Module("Styles", {
     },
 
     addHive: function addHive(name, ref, persist) {
-        let hive = array.nth(this.hives, h => h.name === name,
-                             0);
+        let hive = this.hives.find(h => h.name === name);
         if (!hive) {
             hive = Hive(name, persist);
             this.hives.push(hive);
@@ -369,7 +374,7 @@ var Styles = Module("Styles", {
 }, {
     append: function (dest, src, sort) {
         let props = {};
-        for each (let str in [dest, src])
+        for (let str of [dest, src])
             for (let prop in Styles.propertyIter(str))
                 props[prop.name] = prop.value;
 
@@ -382,7 +387,7 @@ var Styles = Module("Styles", {
         return val;
     },
 
-    completeSite: function (context, content, group = styles.user) {
+    completeSite: function (context, content, group=styles.user) {
         context.anchored = false;
         try {
             context.fork("current", 0, this, function (context) {
@@ -553,7 +558,7 @@ var Styles = Module("Styles", {
             let uris = util.visibleURIs(window.content);
             context.compare = modules.CompletionContext.Sort.number;
             context.generate = () => args["-group"].sheets;
-            context.keys.active = sheet => uris.some(sheet.closure.match);
+            context.keys.active = sheet => uris.some(sheet.bound.match);
             context.keys.description = sheet => [sheet.formatSites(uris), ": ", sheet.css.replace("\n", "\\n")];
             if (filter)
                 context.filters.push(({ item }) => filter(item));
index 5b89f187524e336eb95e7c2899c01a1c686d996d..93b600831f2049d7c20bdee09a290beabe2a98f7 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k at Gmail>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -106,7 +106,7 @@ var Template = Module("Template", {
                 "click": function onClick(event) {
                     event.preventDefault();
                     if (this.commandAllowed) {
-                        if (Set.has(this.target.commands || {}, this.command))
+                        if (hasOwnProperty(this.target.commands || {}, this.command))
                             this.target.commands[this.command].call(this.target);
                         else
                             this.target.command(this.command);
@@ -115,7 +115,7 @@ var Template = Module("Template", {
             },
 
             get commandAllowed() {
-                if (Set.has(this.target.allowedCommands || {}, this.command))
+                if (hasOwnProperty(this.target.allowedCommands || {}, this.command))
                     return this.target.allowedCommands[this.command];
                 if ("commandAllowed" in this.target)
                     return this.target.commandAllowed(this.command);
@@ -140,11 +140,11 @@ var Template = Module("Template", {
 
                 let obj = params.eventTarget;
                 let events = obj[this.getAttribute("events") || "events"];
-                if (Set.has(events, "input"))
+                if (hasOwnProperty(events, "input"))
                     events["dactyl-input"] = events["input"];
 
                 for (let [event, handler] in Iterator(events))
-                    node.addEventListener(event, util.wrapCallback(obj.closure(handler), true), false);
+                    node.addEventListener(event, util.wrapCallback(handler.bind(obj), true), false);
             }
         })
     },
@@ -155,7 +155,7 @@ var Template = Module("Template", {
 
         let res = [];
         let n = 0;
-        for each (let i in Iterator(iter)) {
+        for (let i in Iterator(iter)) {
             let val = func(i, n);
             if (val == undefined)
                 continue;
@@ -219,7 +219,7 @@ var Template = Module("Template", {
         else if (/^n_/.test(topic))
             topic = topic.slice(2);
 
-        if (help.initialized && !Set.has(help.tags, topic))
+        if (help.initialized && !hasOwnProperty(help.tags, topic))
             return ["span", { highlight: type || ""}, text || token];
 
         type = type || (/^'.*'$/.test(token)   ? "HelpOpt" :
@@ -241,7 +241,7 @@ var Template = Module("Template", {
         else if (/^n_/.test(topic))
             topic = topic.slice(2);
 
-        if (help.initialized && !Set.has(help.tags, topic))
+        if (help.initialized && !hasOwnProperty(help.tags, topic))
             return token;
 
         let tag = (/^'.*'$/.test(token)            ? "o" :
@@ -263,30 +263,12 @@ var Template = Module("Template", {
         })(), this[help ? "HelpLink" : "helpLink"]);
     },
 
-    // Fixes some strange stack rewinds on NS_ERROR_OUT_OF_MEMORY
-    // exceptions that we can't catch.
-    stringify: function stringify(arg) {
-        if (!callable(arg))
-            return String(arg);
-
-        try {
-            this._sandbox.arg = arg;
-            return Cu.evalInSandbox("String(arg)", this._sandbox);
-        }
-        finally {
-            this._sandbox.arg = null;
-        }
-    },
-
-    _sandbox: Class.Memoize(() => Cu.Sandbox(Cu.getGlobalForObject(global),
-                                             { wantXrays: false })),
-
     // if "processStrings" is true, any passed strings will be surrounded by " and
     // any line breaks are displayed as \n
     highlight: function highlight(arg, processStrings, clip, bw) {
         // some objects like window.JSON or getBrowsers()._browsers need the try/catch
         try {
-            let str = this.stringify(arg);
+            let str = String(arg);
             if (clip)
                 str = util.clip(str, clip);
             switch (arg == null ? "undefined" : typeof arg) {
@@ -467,7 +449,7 @@ var Template = Module("Template", {
                         ["td", { style: style[i] || "" }, d])])];
     },
 
-    usage: function usage(iter, format = {}) {
+    usage: function usage(iter, format={}) {
         let desc = format.description || (item => this.linkifyHelp(item.description));
         let help = format.help || (item => item.name);
         let sourceLink = (frame) => {
index d6eaf80ceaa1d0430d6886b9c8d6b860bd750bb8..a0d0f090f2b06df38c674cce543b1170e1e46901 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2014 Kris Maglione <maglione.k@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
@@ -10,7 +10,7 @@ try {
 
 defineModule("util", {
     exports: ["DOM", "$", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"],
-    require: ["dom", "services"]
+    require: ["dom", "promises", "services"]
 });
 
 lazyRequire("overlay", ["overlay"]);
@@ -72,8 +72,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
     },
 
     activeWindow: deprecated("overlay.activeWindow", { get: function activeWindow() overlay.activeWindow }),
-    overlayObject: deprecated("overlay.overlayObject", { get: function overlayObject() overlay.closure.overlayObject }),
-    overlayWindow: deprecated("overlay.overlayWindow", { get: function overlayWindow() overlay.closure.overlayWindow }),
+    overlayObject: deprecated("overlay.overlayObject", { get: function overlayObject() overlay.bound.overlayObject }),
+    overlayWindow: deprecated("overlay.overlayWindow", { get: function overlayWindow() overlay.bound.overlayWindow }),
 
     compileMatcher: deprecated("DOM.compileMatcher", { get: function compileMatcher() DOM.compileMatcher }),
     computedStyle: deprecated("DOM#style", function computedStyle(elem) DOM(elem).style),
@@ -89,14 +89,13 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
     parseForm: deprecated("DOM#formData", function parseForm(elem) values(DOM(elem).formData).toArray()),
     scrollIntoView: deprecated("DOM#scrollIntoView", function scrollIntoView(elem, alignWithTop) DOM(elem).scrollIntoView(alignWithTop)),
     validateMatcher: deprecated("DOM.validateMatcher", { get: function validateMatcher() DOM.validateMatcher }),
-    xmlToDom: deprecated("DOM.fromJSON", function xmlToDom() DOM.fromXML.apply(DOM, arguments)),
 
     map: deprecated("iter.map", function map(obj, fn, self) iter(obj).map(fn, self).toArray()),
     writeToClipboard: deprecated("dactyl.clipboardWrite", function writeToClipboard(str, verbose) util.dactyl.clipboardWrite(str, verbose)),
     readFromClipboard: deprecated("dactyl.clipboardRead", function readFromClipboard() util.dactyl.clipboardRead(false)),
 
     chromePackages: deprecated("config.chromePackages", { get: function chromePackages() config.chromePackages }),
-    haveGecko: deprecated("config.haveGecko", { get: function haveGecko() config.closure.haveGecko }),
+    haveGecko: deprecated("config.haveGecko", { get: function haveGecko() config.bound.haveGecko }),
     OS: deprecated("config.OS", { get: function OS() config.OS }),
 
     dactyl: update(function dactyl(obj) {
@@ -138,7 +137,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         let cleanup = ["dactyl-cleanup-modules", "quit-application"];
 
         function register(meth) {
-            for (let target in Set(cleanup.concat(Object.keys(obj.observers))))
+            for (let target of RealSet(cleanup.concat(Object.keys(obj.observers))))
                 try {
                     services.observer[meth](obj, target, true);
                 }
@@ -354,7 +353,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                     : "",
             {
                 elements: [],
-                seen: {},
+                seen: RealSet(),
                 valid: function valid(obj) this.elements.every(e => (!e.test || e.test(obj)))
             });
 
@@ -387,15 +386,15 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
             }
             else {
                 let [, flags, name] = /^((?:[a-z]-)*)(.*)/.exec(macro);
-                flags = Set(flags);
+                flags = RealSet(flags);
 
                 let quote = util.identity;
-                if (flags.q)
+                if (flags.has("q"))
                     quote = function quote(obj) typeof obj === "number" ? obj : String.quote(obj);
-                if (flags.e)
+                if (flags.has("e"))
                     quote = function quote(obj) "";
 
-                if (Set.has(defaults, name))
+                if (hasOwnProperty(defaults, name))
                     stack.top.elements.push(quote(defaults[name]));
                 else {
                     let index = idx;
@@ -403,7 +402,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                         idx = Number(idx) - 1;
                         stack.top.elements.push(update(
                             obj => obj[name] != null && idx in obj[name] ? quote(obj[name][idx])
-                                                                                 : Set.has(obj, name) ? "" : unknown(full),
+                                                                         : hasOwnProperty(obj, name) ? "" : unknown(full),
                             {
                                 test: function test(obj) obj[name] != null && idx in obj[name]
                                                       && obj[name][idx] !== false
@@ -413,7 +412,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                     else {
                         stack.top.elements.push(update(
                             obj => obj[name] != null ? quote(obj[name])
-                                                             : Set.has(obj, name) ? "" : unknown(full),
+                                                     : hasOwnProperty(obj, name) ? "" : unknown(full),
                             {
                                 test: function test(obj) obj[name] != null
                                                       && obj[name] !== false
@@ -422,7 +421,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                     }
 
                     for (let elem in array.iterValues(stack))
-                        elem.seen[name] = true;
+                        elem.seen.add(name);
                 }
             }
         }
@@ -483,7 +482,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                 return obj.res;
             }
 
-            if (pattern.indexOf("{") == -1)
+            if (!pattern.contains("{"))
                 return [pattern];
 
             let res = [];
@@ -517,7 +516,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
             rec([]);
             return res;
         }
-        catch (e if e.message && ~e.message.indexOf("res is undefined")) {
+        catch (e if e.message && e.message.contains("res is undefined")) {
             // prefs.safeSet() would be reset on :rehash
             prefs.set("javascript.options.methodjit.chrome", false);
             util.dactyl.warn(_(UTF8("error.damnYouJägermonkey")));
@@ -545,7 +544,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      * @returns {string}
      */
     dequote: function dequote(pattern, chars)
-        pattern.replace(/\\(.)/, (m0, m1) => chars.indexOf(m1) >= 0 ? m1 : m0),
+        pattern.replace(/\\(.)/, (m0, m1) => chars.contains(m1) ? m1 : m0),
 
     /**
      * Returns the nsIDocShell for the given window.
@@ -750,19 +749,19 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      *
      * @returns {XMLHttpRequest}
      */
-    httpGet: function httpGet(url, callback, self) {
-        let params = callback;
-        if (!isObject(params))
-            params = { callback: params && ((...args) => callback.apply(self, args)) };
+    httpGet: function httpGet(url, params={}, self) {
+        if (callable(params))
+            // Deprecated.
+            params = { callback: params.bind(self) };
 
         try {
             let xmlhttp = services.Xmlhttp();
-            xmlhttp.mozBackgroundRequest = Set.has(params, "background") ? params.background : true;
+            xmlhttp.mozBackgroundRequest = hasOwnProperty(params, "background") ? params.background : true;
 
             let async = params.callback || params.onload || params.onerror;
             if (async) {
-                xmlhttp.addEventListener("load",  function handler(event) { util.trapErrors(params.onload  || params.callback, params, xmlhttp, event); }, false);
-                xmlhttp.addEventListener("error", function handler(event) { util.trapErrors(params.onerror || params.callback, params, xmlhttp, event); }, false);
+                xmlhttp.addEventListener("load",  event => { util.trapErrors(params.onload  || params.callback, params, xmlhttp, event); }, false);
+                xmlhttp.addEventListener("error", event => { util.trapErrors(params.onerror || params.callback, params, xmlhttp, event); }, false);
             }
 
             if (isObject(params.params)) {
@@ -810,6 +809,22 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         }
     },
 
+    /**
+     * Like #httpGet, but returns a promise rather than accepting
+     * callbacks.
+     *
+     * @param {string} url The URL to fetch.
+     * @param {object} params Parameter object, as in #httpGet.
+     */
+    fetchUrl: promises.withCallbacks(function fetchUrl([accept, reject, deferred], url, params) {
+        params = update({}, params);
+        params.onload = accept;
+        params.onerror = reject;
+
+        let req = this.httpGet(url, params);
+        promises.oncancel(deferred, req.cancel);
+    }),
+
     /**
      * The identity function.
      *
@@ -880,7 +895,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         let windows = services.windowMediator.getXULWindowEnumerator(null);
         while (windows.hasMoreElements()) {
             let window = windows.getNext().QueryInterface(Ci.nsIXULWindow);
-            for each (let type in types) {
+            for (let type of types) {
                 let docShells = window.docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem[type],
                                                                       Ci.nsIDocShell.ENUMERATE_FORWARDS);
                 while (docShells.hasMoreElements())
@@ -1018,12 +1033,18 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         // window.content often does not want to be queried with "var i in object"
         try {
             let hasValue = !("__iterator__" in object || isinstance(object, ["Generator", "Iterator"]));
+
             if (object.dactyl && object.modules && object.modules.modules == object.modules) {
                 object = Iterator(object);
                 hasValue = false;
             }
+
             let keyIter = object;
-            if ("__iterator__" in object && !callable(object.__iterator__))
+            if (iter.iteratorProp in object) {
+                keyIter = (k for (k of object));
+                hasValue = false;
+            }
+            else if ("__iterator__" in object && !callable(object.__iterator__))
                 keyIter = keys(object);
 
             for (let i in keyIter) {
@@ -1032,6 +1053,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                     value = object[i];
                 }
                 catch (e) {}
+
                 if (!hasValue) {
                     if (isArray(i) && i.length == 2)
                         [i, value] = i;
@@ -1088,9 +1110,9 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
 
         function rec(data, level, seen) {
             if (isObject(data)) {
-                if (~seen.indexOf(data))
+                seen = RealSet(seen);
+                if (seen.add(data))
                     throw Error("Recursive object passed");
-                seen = seen.concat([data]);
             }
 
             let prefix = level + INDENT;
@@ -1138,7 +1160,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         }
 
         let res = [];
-        rec(data, "", []);
+        rec(data, "", RealSet());
         return res.join("");
     },
 
@@ -1151,28 +1173,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                     util.dump("cleanup: " + module.constructor.className);
                     util.trapErrors(module.cleanup, module, reason);
                 }
-
-            JSMLoader.cleanup();
-
-            if (!this.rehashing)
-                services.observer.addObserver(this, "dactyl-rehash", true);
-        },
-        "dactyl-rehash": function dactylRehash() {
-            services.observer.removeObserver(this, "dactyl-rehash");
-
-            defineModule.loadLog.push("dactyl: util: observe: dactyl-rehash");
-            if (!this.rehashing)
-                for (let module in values(defineModule.modules)) {
-                    defineModule.loadLog.push("dactyl: util: init(" + module + ")");
-                    if (module.reinit)
-                        module.reinit();
-                    else
-                        module.init();
-                }
-        },
-        "dactyl-purge": function dactylPurge() {
-            this.rehashing = 1;
-        },
+        }
     },
 
     /**
@@ -1230,7 +1231,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      *
      * This is similar to Perl's extended regular expression format.
      *
-     * @param {string|XML} expr The expression to compile into a RegExp.
+     * @param {string} expr The expression to compile into a RegExp.
      * @param {string} flags Flags to apply to the new RegExp.
      * @param {object} tokens The tokens to substitute. @optional
      * @returns {RegExp} A custom regexp object.
@@ -1255,10 +1256,10 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         // Replace replacement <tokens>.
         if (tokens)
             expr = String.replace(expr, /(\(?P)?<(\w+)>/g,
-                                  (m, n1, n2) => !n1 && Set.has(tokens, n2) ?    tokens[n2].dactylSource
-                                                                              || tokens[n2].source
-                                                                              || tokens[n2]
-                                                                            : m);
+                                  (m, n1, n2) => !n1 && hasOwnProperty(tokens, n2) ?    tokens[n2].dactylSource
+                                                                                     || tokens[n2].source
+                                                                                     || tokens[n2]
+                                                                                   : m);
 
         // Strip comments and white space.
         if (/x/.test(flags))
@@ -1279,7 +1280,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         }
 
         let res = update(RegExp(expr, flags.replace("x", "")), {
-            closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
+            bound: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")),
+            closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")),
             dactylPropertyNames: ["exec", "match", "test", "toSource", "toString", "global", "ignoreCase", "lastIndex", "multiLine", "source", "sticky"],
             iterate: function iterate(str, idx) util.regexp.iterate(this, str, idx)
         });
@@ -1350,7 +1352,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         storage.storeForSession("commandlineArgs", args);
         this.timeout(function () {
             this.flushCache();
-            this.rehashing = true;
+            cache.flush(bind("test", /^literal:/));
             let addon = config.addon;
             addon.userDisabled = true;
             addon.userDisabled = false;
@@ -1555,7 +1557,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      * Waits for the function *test* to return true, or *timeout*
      * milliseconds to expire.
      *
-     * @param {function} test The predicate on which to wait.
+     * @param {function|Promise} test The predicate on which to wait.
      * @param {object} self The 'this' object for *test*.
      * @param {Number} timeout The maximum number of milliseconds to
      *      wait.
@@ -1565,6 +1567,15 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      *      thrown.
      */
     waitFor: function waitFor(test, self, timeout, interruptable) {
+        if (!callable(test)) {
+            let done = false;
+            var promise = test,
+                retVal;
+            promise.then((arg) => { retVal = arg; done = true; },
+                         (arg) => { retVal = arg; done = true; });
+            test = () => done;
+        }
+
         let end = timeout && Date.now() + timeout, result;
 
         let timer = services.Timer(function () {}, 10, services.Timer.TYPE_REPEATING_SLACK);
@@ -1575,7 +1586,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
         finally {
             timer.cancel();
         }
-        return result;
+        return promise ? retVal: result;
     },
 
     /**
@@ -1595,7 +1606,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      * @returns {function} A new function which may not execute
      *      synchronously.
      */
-    yieldable: function yieldable(func)
+    yieldable: deprecated("Task.spawn", function yieldable(func)
         function magic() {
             let gen = func.apply(this, arguments);
             (function next() {
@@ -1604,7 +1615,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
                 }
                 catch (e if e instanceof StopIteration) {};
             })();
-        },
+        }),
 
     /**
      * Wraps a callback function such that its errors are not lost. This
@@ -1673,7 +1684,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      * @returns {[string]} The visible domains.
      */
     visibleHosts: function visibleHosts(win) {
-        let res = [], seen = {};
+        let res = [],
+            seen = RealSet();
         (function rec(frame) {
             try {
                 if (frame.location.hostname)
@@ -1682,7 +1694,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
             catch (e) {}
             Array.forEach(frame.frames, rec);
         })(win);
-        return res.filter(h => !Set.add(seen, h));
+        return res.filter(h => !seen.add(h));
     },
 
     /**
@@ -1693,7 +1705,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
      * @returns {[nsIURI]} The visible URIs.
      */
     visibleURIs: function visibleURIs(win) {
-        let res = [], seen = {};
+        let res = [],
+            seen = RealSet();
         (function rec(frame) {
             try {
                 res = res.concat(util.newURI(frame.location.href));
@@ -1701,7 +1714,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
             catch (e) {}
             Array.forEach(frame.frames, rec);
         })(win);
-        return res.filter(h => !Set.add(seen, h.spec));
+        return res.filter(h => !seen.add(h.spec));
     },
 
     /**
diff --git a/common/process_config.awk b/common/process_config.awk
new file mode 100644 (file)
index 0000000..4582f7b
--- /dev/null
@@ -0,0 +1,22 @@
+BEGIN {
+    chrome = "chrome"
+    if (suffix)
+        chrome = suffix
+}
+/^    \}/              { on = 0 }
+
+on && $NF ~ /^"([a-z]|\.\/)/ {
+    $NF = "\"/" name "/" substr($NF, 2)
+}
+/./ && on {
+    sub(/^"\.\./, "\"", $NF);
+    $NF = "\"" chrome substr($NF, 2)
+}
+/./ && on {
+    gsub(/\/\.\//, "/")
+    sub(/^\"\.\.\/common\//, "\"", $NF)
+    $0 = "        " $0
+}
+//
+
+/^    "resources": \{/ { on = 1 }
index 4abfceefa0c549488f3471a4ce8a77fc2307439a..3844834447908052febfaf9e2f78ef137c7cc942 100644 (file)
@@ -3,8 +3,12 @@ BEGIN {
     if (suffix)
         chrome = suffix
 }
+
 { content = $1 ~ /^(content|skin|locale|resource)$/ }
-content && $NF ~ /^[a-z]|^\.\// { $NF = "/" name "/" $NF }
+
+content && $NF ~ /^([a-z]|\.\/)/ {
+    $NF = "/" name "/" $NF
+}
 content {
     sub(/^\.\./, "", $NF);
     if (isjar)
index 53d3dc29ba6a260ae6e078fab52df58c70caf1ce..43aef7f47b515c69dbb0aa2d3938efa47a20a5dc 100644 (file)
@@ -168,6 +168,23 @@ statusbarpanel {
     visibility: collapse;
 }
 
+:-moz-any(#addon-bar, #dactyl-addon-bar) .toolbarbutton-1 > xul|dropmarker {
+    margin-left: 0 !important;
+    margin-right: 0 !important;
+}
+
+#dactyl-addon-bar .toolbarbutton-1 > xul|dropmarker::after {
+    content: "▾";
+    color: white;
+    font-size: 18px;
+    line-height: 18px;
+}
+
+#nav-bar {
+    padding-top: 0px !important;
+    padding-bottom: 0px !important;
+}
+
 .dactyl-commandline-prompt {
     /* background-color: inherit; */
     margin: 0px;
@@ -207,4 +224,60 @@ statusbarpanel {
 
 }
 
+@-moz-document url(chrome://browser/content/browser.xul) {
+
+/* Fix ginormous Australis tabs. */
+[dactyl-australis=true] xul|tab.tabbrowser-tab .tab-background > * {
+    min-height: 24px !important;
+    max-height: 24px !important;
+}
+
+[dactyl-australis=true] xul|tab.tabbrowser-tab
+    .tab-background > :-moz-any(.tab-background-start, .tab-background-end)::after {
+    background-size: 30px 24px !important;
+    max-height: 24px !important;
+    min-height: 24px !important;
+}
+
+[dactyl-australis=true] .tabbrowser-tabs {
+    min-height: 0 !important;
+}
+
+/* Fix stupid line... */
+[dactyl-australis=true] #navigator-toolbox::after {
+    height: 0 !important;
+}
+
+#nav-bar-customization-target > .toolbarbutton-1 {
+    margin-top: -5px !important;
+    margin-bottom: -5px !important;
+}
+
+/*
+#PanelUI-button,
+#PanelUI-menu-button,
+#nav-bar-customization-target > .toolbarbutton-1,
+#nav-bar-customization-target > .toolbarbutton-1 > xul|toolbarbutton {
+    padding: 0 !important;
+}
+
+#nav-bar-customization-target > xul|toolbaritem > .toolbarbutton-1 {
+    padding-top: 0 !important;
+    padding-bottom: 0 !important;
+}
+
+#nav-bar-customization-target > .toolbarbutton-1,
+#nav-bar-customization-target > .toolbarbutton-1 > xul|dropmarker > xul|image {
+    margin-top: 0 !important;
+    margin-bottom: 0 !important;
+    max-height: 24px !important;
+}
+
+#nav-bar-customization-target #urlbar {
+    margin-bottom: 0 !important;
+}
+*/
+
+}
+
 /* vim: set fdm=marker sw=4 ts=4 et: */
index 5ba3bdd9875a89b91c21032873741a5124fc2290..dc1e8ec66e51dcdec6d6c3820361fec6fd8d0641 100644 (file)
@@ -9,6 +9,29 @@
         "resource://dactyl/"
     ],
 
+    "resources": {
+        "dactyl":             "../common/modules/",
+        "dactyl-common":      "../common/",
+        "dactyl-content":     "../common/content/",
+        "dactyl-skin":        "../common/skin/",
+        "dactyl-locale":      "../common/locale/",
+
+        "dactyl-local":         "./",
+        "dactyl-local-content": "content/",
+        "dactyl-local-skin":    "skin/",
+        "dactyl-local-locale":  "locale/"
+    },
+
+    "components": {
+        "{16dc34f7-6d22-4aa4-a67f-2921fb5dcb69}": {
+            "path": "components/commandline-handler.js",
+            "contract": "@mozilla.org/commandlinehandler/general-startup;1?type=dactyl",
+            "categories": {
+                "command-line-handler": "m-dactyl"
+            }
+        }
+    },
+
     "autocommands": {
         "BookmarkAdd":      "Triggered after a page is bookmarked",
         "BookmarkChange":   "Triggered after a page's bookmark is changed",
index d7060288ae357cc855e53ec0b638ae3e55460a00..bd50bda5cdebc8f135ba1e0ee1b67338d3c3daf6 100644 (file)
@@ -5,7 +5,7 @@
         em:id="pentadactyl@dactyl.googlecode.com"
         em:type="2"
         em:name="Pentadactyl"
-        em:version="1.1pre"
+        em:version="1.2pre"
         em:description="Firefox for Vim and Links addicts"
         em:homepageURL="http://5digits.org/pentadactyl"
         em:bootstrap="true"
@@ -31,8 +31,8 @@
         <em:targetApplication>
             <Description
                 em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
-                em:minVersion="24.0"
-                em:maxVersion="25.*"/>
+                em:minVersion="25.0"
+                em:maxVersion="31.*"/>
         </em:targetApplication>
     </Description>
 </RDF>