diff --git a/plugins/pilot/lib/commands/settings.js b/plugins/pilot/lib/commands/settings.js
new file mode 100644
index 00000000..3f781839
--- /dev/null
+++ b/plugins/pilot/lib/commands/settings.js
@@ -0,0 +1,104 @@
+require.def(['require', 'exports', 'module',
+ 'skywriter/plugins',
+ 'settings/environment',
+ 'settings/settings'
+], function(require, exports, module,
+ plugins,
+ environment,
+ settingsMod
+) {
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Skywriter Team (skywriter@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var catalog = plugins.catalog;
+var env = environment.env;
+
+var settings = settingsMod.settings;
+
+/**
+ * 'set' command
+ */
+exports.setCommand = function(args, request) {
+ var html;
+
+ if (!args.setting) {
+ var settingsList = settings._list();
+ html = '';
+ // first sort the settingsList based on the key
+ settingsList.sort(function(a, b) {
+ if (a.key < b.key) {
+ return -1;
+ } else if (a.key == b.key) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+
+ settingsList.forEach(function(setting) {
+ html += '' +
+ setting.key +
+ ' = ' +
+ setting.value +
+ '
';
+ });
+ } else {
+ if (args.value === undefined) {
+ html = '' + args.setting + ' = ' + settings.get(args.setting);
+ } else {
+ html = 'Setting: ' + args.setting + ' = ' + args.value;
+ settings.set(args.setting, args.value);
+ }
+ }
+
+ request.done(html);
+};
+
+/**
+ * 'unset' command
+ */
+exports.unsetCommand = function(args, request) {
+ settings.resetValue(args.setting);
+ request.done('Reset ' + args.setting + ' to default: ' + settings.get(args.setting));
+};
+
+});
diff --git a/plugins/pilot/lib/settings.js b/plugins/pilot/lib/settings.js
new file mode 100644
index 00000000..9f6af921
--- /dev/null
+++ b/plugins/pilot/lib/settings.js
@@ -0,0 +1,351 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ * Julian Viereck (jviereck@mozilla.com)
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This plug-in manages settings.
+ */
+
+define(function(require, exports, module) {
+
+var console = require("util/console");
+var types = require("types");
+var Event = require("events").Event;
+
+exports.startup = function(data, reason) {
+ // TODO add extension point in new style
+ // catalog.addExtensionPoint("setting", {
+ // "description":
+ // "A setting is something that the application offers as a way to customize how it works",
+ // "register": "index#addSetting",
+ // "indexOn": "name"
+ // });
+ // catalog.addExtensionPoint("settingChange", {
+ // "description":
+ // "A settingChange is a way to be notified of changes to a setting"
+ // });
+
+ // TODO add commands in new style
+ // catalog.connect("command", module.id, {
+ // "name": "set",
+ // "params": [
+ // {
+ // "name": "setting",
+ // "type": {
+ // "name": "selection",
+ // "pointer": "settings:index#getSettings"
+ // },
+ // "description": "The name of the setting to display or alter",
+ // "defaultValue": null
+ // },
+ // {
+ // "name": "value",
+ // "type": {
+ // "name": "deferred",
+ // "pointer": "settings:index#getTypeSpecFromAssignment"
+ // },
+ // "description": "The new value for the chosen setting",
+ // "defaultValue": null
+ // }
+ // ],
+ // "description": "define and show settings",
+ // "pointer": "commands#setCommand"
+ // });
+ // catalog.connect("command", module.id, {
+ // "name": "unset",
+ // "params": [
+ // {
+ // "name": "setting",
+ // "type": {
+ // "name": "selection",
+ // "pointer": "settings:index#getSettings"
+ // },
+ // "description": "The name of the setting to return to defaults"
+ // }
+ // ],
+ // "description": "unset a setting entirely",
+ // "pointer": "commands#unsetCommand"
+ // });
+};
+
+exports.shutdown = function(data, reason) {
+};
+
+
+/**
+ * A base class for all the various methods of storing settings.
+ *
Usage: + *
+ * // Create manually, or require 'settings' from the container.
+ * // This is the manual version:
+ * var settings = plugins.catalog.getObject('settings');
+ * // Add a new setting
+ * settings.addSetting({ name:'foo', ... });
+ * // Display the default value
+ * alert(settings.get('foo'));
+ * // Alter the value, which also publishes the change etc.
+ * settings.set('foo', 'bar');
+ * // Reset the value to the default
+ * settings.resetValue('foo');
+ *
+ * @constructor
+ */
+function Settings(persister) {
+ /**
+ * Storage for deactivated values
+ */
+ this._deactivated = {};
+
+ /**
+ * Storage for the setting values
+ */
+ this._values = {};
+
+ this._settings = {};
+
+ if (persister) {
+ this.setPersister(persister);
+ }
+
+ /**
+ * Event that tells people when a setting has changed.
+ */
+ // TODO: Fix events
+ // this.settingChange = new Event({ keyElement: 0 });
+};
+
+Settings.prototype = {
+ /**
+ * Function to add to the list of available settings.
+ * Example usage: + *
+ * var settings = plugins.catalog.getObject('settings');
+ * settings.addSetting({
+ * name: 'tabsize', // For use in settings.get('X')
+ * type: 'number', // To allow value checking.
+ * defaultValue: 4 // Default value for use when none is directly set
+ * });
+ *
+ * @param {object} settingSpec Object containing name/type/defaultValue members.
+ */
+ addSetting: function(settingSpec) {
+ if (!settingSpec.name) {
+ console.error('Setting.name == undefined. Ignoring.', settingSpec);
+ return;
+ }
+
+ if (!settingSpec.defaultValue === undefined) {
+ console.error('Setting.defaultValue == undefined', settingSpec);
+ return;
+ }
+
+ var type = types.getType(settingSpec.type);
+ if (!type) {
+ console.error('Missing type', settingSpec);
+ return;
+ }
+
+ /*
+ // The value can be
+ // 1) the value of a setting that is not activated at the moment
+ // OR
+ // 2) the defaultValue of the setting.
+ var value = this._deactivated[settingSpec.name] ||
+ settingSpec.defaultValue;
+ */
+
+ var setting = { type: type, spec: settingSpec };
+ this._settings[settingSpec.name] = setting;
+ },
+
+ removeSetting: function(name) {
+ delete this._settings[name];
+ },
+
+ getSettingNames: function() {
+ return Object.keys(this._settings);
+ },
+
+ /**
+ * A Persister is able to store settings. It is an object that defines
+ * two functions:
+ * loadInitialValues(settings) and persistValue(settings, key, value).
+ */
+ setPersister: function(persister) {
+ this._persister = persister;
+ if (persister) {
+ persister.loadInitialValues(this);
+ }
+ },
+
+ /**
+ * Read accessor
+ */
+ get: function(key) {
+ return this._values[key];
+ },
+
+ /**
+ * Override observable.set(key, value) to provide type conversion and
+ * validation.
+ */
+ set: function(key, value) {
+ var setting = this._settings[key];
+ if (!setting) {
+ console.warn('Setting not defined: ', key, value);
+ }
+
+ this._values[key] = value;
+ if (persister) {
+ persister.persistValue(this, key, value);
+ }
+
+ // Inform subscriptions of the change
+ // TODO: fix events
+ // this.settingChange(key, converted);
+ return this;
+ },
+
+ /**
+ * Reset the value of the key setting to it's default
+ */
+ resetValue: function(key) {
+ var setting = this._settings[key];
+ if (setting) {
+ this.set(key, setting.spec.defaultValue);
+ } else {
+ console.log('ignore resetValue on ', key);
+ }
+ },
+
+ resetAll: function() {
+ this.getSettingNames().forEach(function(key) {
+ this.resetValue(key);
+ }.bind(this));
+ },
+
+ /**
+ * Retrieve a list of the known settings and their values
+ */
+ _list: function() {
+ var reply = [];
+ this.getSettingNames().forEach(function(setting) {
+ reply.push({
+ 'key': setting,
+ 'value': this.get(setting)
+ });
+ }.bind(this));
+ return reply;
+ },
+
+ /**
+ * Prime the local cache with the defaults.
+ */
+ _loadDefaultValues: function() {
+ this._loadFromObject(this._getDefaultValues());
+ },
+
+ /**
+ * Utility to load settings from an object
+ */
+ _loadFromObject: function(data) {
+ // We iterate over data rather than keys so we don't forget values
+ // which don't have a setting yet.
+ for (var key in data) {
+ if (data.hasOwnProperty(key)) {
+ var setting = this._settings[key];
+ if (setting) {
+ var value = setting.type.fromString(data[key]);
+ this.set(key, value);
+ } else {
+ this.set(key, data[key]);
+ }
+ }
+ }
+ },
+
+ /**
+ * Utility to grab all the settings and export them into an object
+ */
+ _saveToObject: function() {
+ return this.getSettingNames().map(function(key) {
+ return this._settings[key].type.toString(this.get(key));
+ }.bind(this));
+ },
+
+ /**
+ * The default initial settings
+ */
+ _getDefaultValues: function() {
+ return this.getSettingNames().map(function(key) {
+ return this._settings[key].spec.defaultValue;
+ }.bind(this));
+ }
+};
+
+exports.settings = new Settings(new MemoryPersister());
+
+/**
+ * Save the settings in a cookie
+ * This code has not been tested since reboot
+ * @constructor
+ */
+function CookiePersister() {
+};
+
+CookiePersister.prototype = {
+ loadInitialValues: function(settings) {
+ settings._loadDefaultValues();
+ var data = cookie.get('settings');
+ settings._loadFromObject(JSON.parse(data));
+ },
+
+ persistValue: function(settings, key, value) {
+ try {
+ var stringData = JSON.stringify(settings._saveToObject());
+ cookie.set('settings', stringData);
+ } catch (ex) {
+ console.error('Unable to JSONify the settings! ' + ex);
+ return;
+ }
+ }
+};
+
+exports.CookiePersister = CookiePersister;
+
+});