/* vim:ts=4:sts=4:sw=4: * ***** 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) * Irakli Gozalishvili (http://jeditoolkit.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 ***** */ define(function(require, exports, module) { /** * This plug-in manages settings. */ var console = require('pilot/console'); var oop = require('pilot/oop'); var types = require('pilot/types'); var EventEmitter = require('pilot/event_emitter').EventEmitter; var catalog = require('pilot/catalog'); var settingExtensionSpec = { name: 'setting', description: 'A setting is something that the application offers as a ' + 'way to customize how it works', register: 'env.settings.addSetting', indexOn: 'name' }; exports.startup = function(data, reason) { catalog.addExtensionSpec(settingExtensionSpec); }; exports.shutdown = function(data, reason) { catalog.removeExtensionSpec(settingExtensionSpec); }; /** * Create a new setting. * @param settingSpec An object literal that looks like this: * { * name: 'thing', * description: 'Thing is an example setting', * type: 'string', * defaultValue: 'something' * } */ function Setting(settingSpec, settings) { this._settings = settings; Object.keys(settingSpec).forEach(function(key) { this[key] = settingSpec[key]; }, this); this.type = types.getType(this.type); if (this.type == null) { throw new Error('In ' + this.name + ': can\'t find type for: ' + JSON.stringify(settingSpec.type)); } if (!this.name) { throw new Error('Setting.name == undefined. Ignoring.', this); } if (!this.defaultValue === undefined) { throw new Error('Setting.defaultValue == undefined', this); } if (this.onChange) { this.on('change', this.onChange.bind(this)) } this.set(this.defaultValue); } Setting.prototype = { get: function() { return this.value; }, set: function(value) { if (this.value === value) { return; } this.value = value; if (this._settings.persister) { this._settings.persister.persistValue(this._settings, this.name, value); } this._dispatchEvent('change', { setting: this, value: value }); }, /** * Reset the value of the key setting to it's default */ resetValue: function() { this.set(this.defaultValue); }, toString: function () { return this.name; } }; oop.implement(Setting.prototype, EventEmitter); /** * 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 active settings this._settings = {}; // We often want sorted setting names. Cache this._settingNames = []; if (persister) { this.setPersister(persister); } }; 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) { var setting = new Setting(settingSpec, this); this._settings[setting.name] = setting; this._settingNames.push(setting.name); this._settingNames.sort(); }, addSettings: function addSettings(settings) { Object.keys(settings).forEach(function (name) { var setting = settings[name]; if (!('name' in setting)) setting.name = name; this.addSetting(setting); }, this); }, removeSetting: function(setting) { var name = (typeof setting === 'string' ? setting : setting.name); setting = this._settings[name]; delete this._settings[name]; util.arrayRemove(this._settingNames, name); settings.removeAllListeners('change'); }, removeSettings: function removeSettings(settings) { Object.keys(settings).forEach(function(name) { var setting = settings[name]; if (!('name' in setting)) setting.name = name; this.removeSettings(setting); }, this); }, getSettingNames: function() { return this._settingNames; }, getSetting: function(name) { return this._settings[name]; }, /** * 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); } }, resetAll: function() { this.getSettingNames().forEach(function(key) { this.resetValue(key); }, 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.getSetting(setting).get() }); }, 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.parse(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.stringify(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(); /** * 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; });