1 /*
  2 Script:
  3 	Deluge.OptionsManager.js
  4 
  5 Copyright:
  6 	(C) Damien Churchill 2009 <damoxc@gmail.com>
  7 	This program is free software; you can redistribute it and/or modify
  8 	it under the terms of the GNU General Public License as published by
  9 	the Free Software Foundation; either version 3, or (at your option)
 10 	any later version.
 11 
 12 	This program is distributed in the hope that it will be useful,
 13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15 	GNU General Public License for more details.
 16 
 17 	You should have received a copy of the GNU General Public License
 18 	along with this program.  If not, write to:
 19 		The Free Software Foundation, Inc.,
 20 		51 Franklin Street, Fifth Floor
 21 		Boston, MA  02110-1301, USA.
 22 
 23     In addition, as a special exception, the copyright holders give
 24     permission to link the code of portions of this program with the OpenSSL
 25     library.
 26     You must obey the GNU General Public License in all respects for all of
 27     the code used other than OpenSSL. If you modify file(s) with this
 28     exception, you may extend this exception to your version of the file(s),
 29     but you are not obligated to do so. If you do not wish to do so, delete
 30     this exception statement from your version. If you delete this exception
 31     statement from all source files in the program, then also delete it here.
 32 */
 33 
 34 /**
 35  * @description A class that can be used to manage options throughout the ui.
 36  * @namespace Deluge
 37  * @class Deluge.OptionsManager
 38  */
 39 Deluge.OptionsManager = Ext.extend(Ext.util.Observable, {
 40 	
 41 	constructor: function(config) {
 42 		this.binds = {};
 43 		this.changed = {};
 44 		this.defaults = config['defaults'] || {};
 45 		this.options = {};
 46 		this.currentId = null;
 47 		
 48 		this.addEvents({
 49 			'add': true,
 50 			'changed': true,
 51 			'reset': true
 52 		});
 53 		this.on('changed', this.onChange, this);
 54 		
 55 		Deluge.OptionsManager.superclass.constructor.call(this);
 56 	},
 57 
 58 	/**
 59 	 * Add a set of default options and values to the options manager
 60 	 * @param {String} id
 61 	 * @param {Object} options The default options.
 62 	 */
 63 	addOptions: function(id, options) {
 64 		this.options[id] = options;
 65 	},
 66 	
 67 	/**
 68 	 * Binds a form field to the specified option.
 69 	 * @param {String} option
 70 	 * @param {Ext.form.Field} field
 71 	 */
 72 	bind: function(option, field) {
 73 		this.binds[option] = field;
 74 		this.binds[field] = option;
 75 		
 76 		switch (field.getXType()) {
 77 			case 'checkbox':
 78 			case 'radiogroup':
 79 				field.on('check', this.onFieldChange, this);
 80 				break;
 81 			case 'uxspinner':
 82 				field.on('spin', this.onFieldChange, this);
 83 				field.on('keypress', this.onFieldChange, this);
 84 				break;
 85 			default:
 86 				break;
 87 		}
 88 	},
 89 	
 90 	/**
 91 	 * Changes bound fields to use the specified id.
 92 	 * @param {String} id
 93 	 */
 94 	changeId: function(id) {
 95 		this.currentId = id;
 96 		for (var option in this.defaults) {
 97 			if (!this.binds[option]) continue;
 98 			this.binds[option].setValue(this.get(id, option));
 99 		}
100 	},
101 	
102 	/**
103 	 * Get the value for an option
104 	 * @param {String} id
105 	 * @param {String|Array} [option] A single option or an array of options to return.
106 	 * @returns {Object} the options value.
107 	 */
108 	get: function(id, option) {
109 		if (!option) {
110 			var values = {};
111 			for (var key in this.defaults) {
112 				values[key] = this.get(id, key);
113 			}
114 			return values;
115 		} else {
116 			return (this.hasChanged(id, option)) ? this.changed[id][option] : this.getDefault(id, option);
117 		}
118 	},
119 	
120 	/**
121 	 * Returns the changed values.
122 	 * @param {String} id
123 	 * @returns {Object} the changed options
124 	 */
125 	getChanged: function(id) {
126 		return (this.changed[id]) ? this.changed[id] : {};
127 	},
128 	
129 	/**
130 	 * Get the default value for an option.
131 	 * @param {String} id
132 	 * @param {String|Array} [option] A single option or an array of options to return.
133 	 * @returns {Object} the value of the option
134 	 */
135 	getDefault: function(id, option) {
136 		return (this.hasOption(id, option)) ? this.options[id][option] : this.defaults[option];
137 	},
138 	
139 	/**
140 	 * Check to see if the option has been changed.
141 	 * @param {String} id
142 	 * @param {String} option
143 	 * @returns {Boolean} true if the option has been changed, else false.
144 	 */
145 	hasChanged: function(id, option) {
146 		return (this.changed[id] && !Ext.isEmpty(this.changed[id][option]));
147 	},
148 	
149 	/**
150 	 * Check to see if an id has had an option set to something other than the
151 	 * default value.
152 	 * @param {String} id
153 	 * @param {String} option
154 	 * @returns {Boolean} true if the id has an option, else false.
155 	 */
156 	hasOption: function(id, option) {
157 		return (this.options[id] && !Ext.isEmpty(this.options[id][option]));
158 	},
159 
160 	/**
161 	 * Reset the options back to the default values for the specified id.
162 	 * @param {String} id
163 	 */
164 	reset: function(id) {
165 		if (!this.changed[id]) return;
166 		delete this.changed[id];
167 	},
168 	
169 	/**
170 	 * Sets the value of specified option for the passed in id.
171 	 * @param {String} id
172 	 * @param {String} option
173 	 * @param {Object} value The value for the option
174 	 */
175 	set: function(id, option, value) {
176 		if (typeof value === undefined) {
177 			for (var key in option) {
178 				this.set(id, key, option[key]);
179 			}
180 		} else {
181 			if (!this.options[id]) this.options[id] = {};
182 			this.options[id][option] = value;
183 		}
184 	},
185 	
186 	/**
187 	 * Update the value for the specified option and id.
188 	 * @param {String} id
189 	 * @param {String|Object} option or options to update
190 	 * @param {Object} [value];
191 	 */
192 	update: function(id, option, value) {
193 		if (typeof value === undefined) {
194 			for (var key in option) {
195 				this.update(id, key, option[key]);
196 			}
197 		} else {
198 			if (!this.changed[id]) this.changed[id] = {};
199 			
200 			var oldValue = this.get(id, option);
201 			if (oldValue == value) return;
202 			
203 			var defaultValue = this.getDefault(id, option);
204 			if (defaultValue == value) {
205 				if (this.hasChanged(id, option)) delete this.changed[id][option];
206 				this.fireEvent('changed', id, option, value, oldValue);
207 				return;
208 			}
209 			
210 			if (Ext.type(defaultValue) != Ext.type(value)) {
211 				switch (Ext.type(defaultValue)) {
212 					case 'string':
213 						value = String(value);
214 						break;
215 					case 'number':
216 						value = Number(value);
217 						break;
218 					case 'boolean':
219 						value = Boolean(value);
220 						break;
221 				}
222 			}
223 	
224 			this.changed[id][option] = value;
225 			this.fireEvent('changed', id, option, value, oldValue);
226 		}
227 	},
228 	
229 	/* Event Handlers */
230 	
231 	/**
232 	 * Stops a form fields value from being blocked by the change functions
233 	 * @param {Ext.form.Field} field
234 	 * @private
235 	 */
236 	onFieldChange: function(field) {
237 		var option = this.binds[field];
238 		this.update(this.currentId, option, field.getValue());
239 	},
240 	
241 	onChange: function(id, option, newValue, oldValue) {
242 		// If we don't have a bind there's nothing to do.
243 		if (Ext.isEmpty(this.binds[option])) return;
244 		
245 		// Set the form field to the new value.
246 		this.binds[option].setValue(newValue);
247 	}
248 });
249