]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/modules/prefs.jsm
Imported Upstream version 1.1+hg7904
[dactyl.git] / common / modules / prefs.jsm
index 13500685dbb10318a2925b33be7ced0a96db680f..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-2011 by 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.
@@ -8,12 +8,13 @@
 
 try {
 
-Components.utils.import("resource://dactyl/bootstrap.jsm");
 defineModule("prefs", {
     exports: ["Prefs", "localPrefs", "prefs"],
-    require: ["services", "util"],
-    use: ["config", "messages", "template"]
-}, this);
+    require: ["services", "util"]
+});
+
+lazyRequire("messages", ["_"]);
+lazyRequire("template", ["template"]);
 
 var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), {
     ORIGINAL: "extensions.dactyl.original.",
@@ -25,7 +26,8 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
         this._prefContexts = [];
 
         this.branch = services.pref[defaults ? "getDefaultBranch" : "getBranch"](branch || "");
-        this.branch instanceof Ci.nsIPrefBranch2;
+        if ("nsIPrefBranch2" in Ci)
+            this.branch instanceof Ci.nsIPrefBranch2;
 
         this.defaults = defaults ? this : this.constructor(branch, true);
 
@@ -44,7 +46,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
 
     cleanup: function cleanup(reason) {
         if (this.defaults != this)
-            this.defaults.cleanup();
+            this.defaults.cleanup(reason);
 
         this._observers = {};
         if (this.observe) {
@@ -62,118 +64,58 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
                 this.branches.saved.resetBranch();
             }
 
-            if (reason == "uninstall" && this == prefs)
+            if (reason == "uninstall")
                 localPrefs.resetBranch();
         }
     },
 
-    /**
-     * Returns the full name of this object's preference branch.
-     */
-    get root() this.branch.root,
-
     /**
      * Returns a new Prefs instance for the sub-branch *branch* of this
      * branch.
      *
-     * @param {string} branch The branch to branch to.
+     * @param {string} branch The sub-branch to branch to.
      * @returns {Prefs}
      */
     Branch: function Branch(branch) Prefs(this.root + branch),
 
-    observe: null,
-    observers: {
-        "nsPref:changed": function (subject, data) {
-            let observers = this._observers[data];
-            if (observers) {
-                let value = this.get(data, false);
-                this._observers[data] = observers.filter(function (callback) {
-                    if (!callback.get())
-                        return false;
-                    util.trapErrors(callback.get(), null, value);
-                    return true;
-                });
-            }
-        }
-    },
-
     /**
-     * Adds a new preference observer for the given preference.
+     * Clears the entire branch.
      *
-     * @param {string} pref The preference to observe.
-     * @param {function(object)} callback The callback, called with the
-     *    new value of the preference whenever it changes.
+     * @param {string} name The name of the preference branch to delete.
      */
-    watch: function watch(pref, callback, strong) {
-        if (!this.observe) {
-            util.addObserver(this);
-            this.branch.addObserver("", this, false);
-        }
-
-        if (!this._observers[pref])
-            this._observers[pref] = [];
-        this._observers[pref].push(!strong ? Cu.getWeakReference(callback) : { get: function () callback });
+    clear: function clear(branch) {
+        this.branch.deleteBranch(branch || "");
     },
 
     /**
-     * Lists all preferences matching *filter* or only those with changed
-     * values if *onlyNonDefault* is specified.
-     *
-     * @param {boolean} onlyNonDefault Limit the list to prefs with a
-     *     non-default value.
-     * @param {string} filter The list filter. A null filter lists all
-     *     prefs.
-     * @optional
+     * Returns the full name of this object's preference branch.
      */
-    list: function list(onlyNonDefault, filter) {
-        if (!filter)
-            filter = "";
-
-        let prefArray = this.getNames();
-        prefArray.sort();
-        function prefs() {
-            for (let [, pref] in Iterator(prefArray)) {
-                let userValue = services.pref.prefHasUserValue(pref);
-                if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1)
-                    continue;
-
-                let value = this.get(pref);
-
-                let option = {
-                    isDefault: !userValue,
-                    default:   this.defaults.get(pref, null),
-                    value:     <>={template.highlight(value, true, 100)}</>,
-                    name:      pref,
-                    pre:       "\u00a0\u00a0" // Unicode nonbreaking space.
-                };
-
-                yield option;
-            }
-        };
-
-        return template.options(_("pref.hostPreferences", config.host), prefs.call(this));
-    },
+    get root() this.branch.root,
 
     /**
-     * Returns the value of a preference.
+     * Returns the value of the preference *name*, or *defaultValue* if
+     * the preference does not exist.
      *
-     * @param {string} name The preference name.
-     * @param {value} defaultValue The value to return if the preference
-     *     is unset.
+     * @param {string} name The name of the preference to return.
+     * @param {*} defaultValue The value to return if the preference has no value.
+     * @optional
      */
     get: function get(name, defaultValue) {
         if (defaultValue == null)
             defaultValue = null;
+        if (isArray(name))
+            name = name.join(".");
 
         let type = this.branch.getPrefType(name);
         try {
             switch (type) {
             case Ci.nsIPrefBranch.PREF_STRING:
                 let value = this.branch.getComplexValue(name, Ci.nsISupportsString).data;
-                // try in case it's a localized string (will throw an exception if not)
-                if (!this.branch.prefIsLocked(name) && !this.branch.prefHasUserValue(name) &&
-                    RegExp("chrome://.+/locale/.+\\.properties").test(value))
-                        value = this.branch.getComplexValue(name, Ci.nsIPrefLocalizedString).data;
+                try {
+                    if (/^[a-z0-9-]+:/i.test(value))
+                    value = this.branch.getComplexValue(name, Ci.nsIPrefLocalizedString).data;
+                }
+                catch (e) {}
                 return value;
             case Ci.nsIPrefBranch.PREF_INT:
                 return this.branch.getIntPref(name);
@@ -191,17 +133,32 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
     getDefault: deprecated("Prefs#defaults.get", function getDefault(name, defaultValue) this.defaults.get(name, defaultValue)),
 
     /**
-     * Returns the names of all preferences.
+     * Returns an array of all preference names in this branch or the
+     * given sub-branch.
      *
-     * @param {string} branch The branch in which to search preferences.
-     *     @default ""
+     * @param {string} branch The sub-branch for which to return preferences.
+     * @optional
      */
     getNames: function getNames(branch) this.branch.getChildList(branch || "", { value: 0 }),
 
+    /**
+     * Returns true if the given preference exists in this branch.
+     *
+     * @param {string} name The name of the preference to check.
+     */
+    has: function has(name) this.branch.getPrefType(name) !== 0,
+
+    /**
+     * Returns true if the given preference is set to its default value.
+     *
+     * @param {string} name The name of the preference to check.
+     */
+    isDefault: function isDefault(name) !this.branch.prefHasUserValue(name),
+
     _checkSafe: function _checkSafe(name, message, value) {
         let curval = this.get(name, null);
 
-        if (this.branches.original.get(name) == null)
+        if (this.branches.original.get(name) == null && !this.branches.saved.has(name))
             this.branches.original.set(name, curval, true);
 
         if (arguments.length > 2 && curval === value)
@@ -247,10 +204,11 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
     },
 
     /**
-     * Sets the preference *name* to *value*.
+     * Sets the preference *name* to *value*. If the preference already
+     * exists, it must have the same type as the given value.
      *
-     * @param {string} name The preference name.
-     * @param {value} value The new preference value.
+     * @param {name} name The name of the preference to change.
+     * @param {string|number|boolean} value The value to set.
      * @param {boolean} silent Ignore errors.
      */
     set: function set(name, value, silent) {
@@ -320,13 +278,11 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
     /**
      * Resets the preference *name* to its default value.
      *
-     * @param {string} name The preference name.
+     * @param {string} name The name of the preference to reset.
      */
     reset: function reset(name) {
-        try {
+        if (this.branch.prefHasUserValue(name))
             this.branch.clearUserPref(name);
-        }
-        catch (e) {} // ignore - thrown if not a user set value
     },
 
     /**
@@ -335,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);
     },
 
     /**
@@ -386,20 +342,96 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
         finally {
             this.popContext();
         }
-    }
+    },
+
+    observe: null,
+    observers: {
+        "nsPref:changed": function (subject, data) {
+            let observers = this._observers[data];
+            if (observers) {
+                let value = this.get(data, false);
+                this._observers[data] = observers.filter(function (callback) {
+                    callback = callback.get();
+                    if (!callback)
+                        return false;
+                    util.trapErrors(callback, null, value);
+                    return true;
+                });
+            }
+        }
+    },
+
+    /**
+     * Adds a new preference observer for the given preference.
+     *
+     * @param {string} pref The preference to observe.
+     * @param {function(object)} callback The callback, called with the
+     *    new value of the preference whenever it changes.
+     */
+    watch: function watch(pref, callback, strong) {
+        if (!this.observe) {
+            util.addObserver(this);
+            this.branch.addObserver("", this, false);
+        }
+
+        if (!this._observers[pref])
+            this._observers[pref] = [];
+        this._observers[pref].push(!strong ? util.weakReference(callback)
+                                           : { get: function () callback });
+    },
+
+    /**
+     * Lists all preferences matching *filter* or only those with changed
+     * values if *onlyNonDefault* is specified.
+     *
+     * @param {boolean} onlyNonDefault Limit the list to prefs with a
+     *     non-default value.
+     * @param {string} filter The list filter. A null filter lists all
+     *     prefs.
+     * @optional
+     */
+    list: function list(onlyNonDefault, filter) {
+        if (!filter)
+            filter = "";
+
+        let prefArray = this.getNames();
+        prefArray.sort();
+        function prefs() {
+            for (let [, pref] in Iterator(prefArray)) {
+                let userValue = services.pref.prefHasUserValue(pref);
+                if (onlyNonDefault && !userValue || !pref.contains(filter))
+                    continue;
+
+                let value = this.get(pref);
+
+                let option = {
+                    isDefault: !userValue,
+                    default:   this.defaults.get(pref, null),
+                    value:     ["", "=", template.highlight(value, true, 100)],
+                    name:      pref,
+                    pre:       "\u00a0\u00a0" // Unicode nonbreaking space.
+                };
+
+                yield option;
+            }
+        };
+
+        return template.options(_("pref.hostPreferences", config.host), prefs.call(this));
+    },
 }, {
 }, {
     completion: function init_completion(dactyl, modules) {
         modules.completion.preference = function preference(context) {
             context.anchored = false;
             context.title = [config.host + " Preference", "Value"];
-            context.keys = { text: function (item) item, description: function (item) prefs.get(item) };
+            context.keys = { text: function (item) item,
+                             description: function (item) prefs.get(item) };
             context.completions = prefs.getNames();
         };
     },
     javascript: function init_javascript(dactyl, modules) {
         modules.JavaScript.setCompleter([this.get, this.safeSet, this.set, this.reset, this.toggle],
-                [function (context) (context.anchored=false, this.getNames().map(function (pref) [pref, ""]))]);
+                [function (context) (context.anchored=false, this.getNames().map(pref => [pref, ""]))]);
     }
 });
 
@@ -410,4 +442,4 @@ endModule();
 
 } catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
 
-// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: