# Yahoo! UI Library

### Container 2.2.2

Yahoo! UI Library > container > Config.js (source view)

    /**
    * Config is a utility used within an Object to allow the implementer to maintain a list of local configuration properties and listen for changes to those properties dynamically using CustomEvent. The initial values are also maintained so that the configuration can be reset at any given point to its initial state.
    * @namespace YAHOO.util
    * @class Config
    * @constructor
    * @param {Object}	owner	The owner Object to which this Config Object belongs
    */
    YAHOO.util.Config = function(owner) {
    	if (owner) {
    		this.init(owner);
    	}
    	if (!owner) { YAHOO.log("No owner specified for Config object", "error"); }
    };

    /**
     * Constant representing the CustomEvent type for the config changed event.
     * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
     * @private
     * @static
     * @final
     */
    YAHOO.util.Config.CONFIG_CHANGED_EVENT = "configChanged";

    /**
     * Constant representing the boolean type string
     * @property YAHOO.util.Config.BOOLEAN_TYPE
     * @private
     * @static
     * @final
     */
    YAHOO.util.Config.BOOLEAN_TYPE = "boolean";

    YAHOO.util.Config.prototype = {

    	/**
    	* Object reference to the owner of this Config Object
    	* @property owner
    	* @type Object
    	*/
    	owner : null,

    	/**
    	* Boolean flag that specifies whether a queue is currently being executed
    	* @property queueInProgress
    	* @type Boolean
    	*/
    	queueInProgress : false,

    	/**
    	* Maintains the local collection of configuration property objects and their specified values
    	* @property config
    	* @private
    	* @type Object
    	*/
    	config : null,

    	/**
    	* Maintains the local collection of configuration property objects as they were initially applied.
    	* This object is used when resetting a property.
    	* @property initialConfig
    	* @private
    	* @type Object
    	*/
    	initialConfig : null,

    	/**
    	* Maintains the local, normalized CustomEvent queue
    	* @property eventQueue
    	* @private
    	* @type Object
    	*/
    	eventQueue : null,

    	/**
    	* Custom Event, notifying subscribers when Config properties are set (setProperty is called without the silent flag
    	* @event configChangedEvent
    	*/
    	configChangedEvent : null,

    	/**
    	* Validates that the value passed in is a Boolean.
    	* @method checkBoolean
    	* @param	{Object}	val	The value to validate
    	* @return	{Boolean}	true, if the value is valid
    	*/
    	checkBoolean: function(val) {
    		return (typeof val == YAHOO.util.Config.BOOLEAN_TYPE);
    	},

    	/**
    	* Validates that the value passed in is a number.
    	* @method checkNumber
    	* @param	{Object}	val	The value to validate
    	* @return	{Boolean}	true, if the value is valid
    	*/
    	checkNumber: function(val) {
    		return (!isNaN(val));
    	},

    	/**
    	* Fires a configuration property event using the specified value.
    	* @method fireEvent
    	* @private
    	* @param {String}	key			The configuration property's name
    	* @param {value}	Object		The value of the correct type for the property
    	*/
    	fireEvent : function( key, value ) {
    		YAHOO.log("Firing Config event: " + key + "=" + value, "info");
    		var property = this.config[key];

    		if (property && property.event) {
    			property.event.fire(value);
    		}
    	},

    	/**
    	* Adds a property to the Config Object's private config hash.
    	* @method addProperty
    	* @param {String}	key	The configuration property's name
    	* @param {Object}	propertyObject	The Object containing all of this property's arguments
    	*/
    	addProperty : function( key, propertyObject ) {
    		key = key.toLowerCase();
    		YAHOO.log("Added property: " + key, "info");

    		this.config[key] = propertyObject;

    		propertyObject.event = new YAHOO.util.CustomEvent(key, this.owner);
    		propertyObject.key = key;

    		if (propertyObject.handler) {
    			propertyObject.event.subscribe(propertyObject.handler, this.owner);
    		}

    		this.setProperty(key, propertyObject.value, true);

    		if (! propertyObject.suppressEvent) {
    			this.queueProperty(key, propertyObject.value);
    		}

    	},

    	/**
    	* Returns a key-value configuration map of the values currently set in the Config Object.
    	* @method getConfig
    	* @return {Object} The current config, represented in a key-value map
    	*/
    	getConfig : function() {
    		var cfg = {};

    		for (var prop in this.config) {
    			var property = this.config[prop];
    			if (property && property.event) {
    				cfg[prop] = property.value;
    			}
    		}

    		return cfg;
    	},

    	/**
    	* Returns the value of specified property.
    	* @method getProperty
    	* @param {String} key	The name of the property
    	* @return {Object}		The value of the specified property
    	*/
    	getProperty : function(key) {
    		var property = this.config[key.toLowerCase()];
    		if (property && property.event) {
    			return property.value;
    		} else {
    			return undefined;
    		}
    	},

    	/**
    	* Resets the specified property's value to its initial value.
    	* @method resetProperty
    	* @param {String} key	The name of the property
    	* @return {Boolean} True is the property was reset, false if not
    	*/
    	resetProperty : function(key) {
    		key = key.toLowerCase();

    		var property = this.config[key];
    		if (property && property.event) {
    			if (this.initialConfig[key] && !YAHOO.lang.isUndefined(this.initialConfig[key]))	{
    				this.setProperty(key, this.initialConfig[key]);
    			}
    			return true;
    		} else {
    			return false;
    		}
    	},

    	/**
    	* Sets the value of a property. If the silent property is passed as true, the property's event will not be fired.
    	* @method setProperty
    	* @param {String} key		The name of the property
    	* @param {String} value		The value to set the property to
    	* @param {Boolean} silent	Whether the value should be set silently, without firing the property event.
    	* @return {Boolean}			True, if the set was successful, false if it failed.
    	*/
    	setProperty : function(key, value, silent) {
    		key = key.toLowerCase();
    		YAHOO.log("setProperty: " + key + "=" + value, "info");

    		if (this.queueInProgress && ! silent) {
    			this.queueProperty(key,value); // Currently running through a queue...
    			return true;
    		} else {
    			var property = this.config[key];
    			if (property && property.event) {
    				if (property.validator && ! property.validator(value)) { // validator
    					return false;
    				} else {
    					property.value = value;
    					if (! silent) {
    						this.fireEvent(key, value);
    						this.configChangedEvent.fire([key, value]);
    					}
    					return true;
    				}
    			} else {
    				return false;
    			}
    		}
    	},

    	/**
    	* Sets the value of a property and queues its event to execute. If the event is already scheduled to execute, it is
    	* moved from its current position to the end of the queue.
    	* @method queueProperty
    	* @param {String} key	The name of the property
    	* @param {String} value	The value to set the property to
    	* @return {Boolean}		true, if the set was successful, false if it failed.
    	*/
    	queueProperty : function(key, value) {
    		key = key.toLowerCase();
    		YAHOO.log("queueProperty: " + key + "=" + value, "info");

    		var property = this.config[key];

    		if (property && property.event) {
    			if (!YAHOO.lang.isUndefined(value) && property.validator && ! property.validator(value)) { // validator
    				return false;
    			} else {

    				if (!YAHOO.lang.isUndefined(value)) {
    					property.value = value;
    				} else {
    					value = property.value;
    				}

    				var foundDuplicate = false;
    				var iLen = this.eventQueue.length;
    				for (var i=0; i < iLen; i++) {
    					var queueItem = this.eventQueue[i];

    					if (queueItem) {
    						var queueItemKey = queueItem[0];
    						var queueItemValue = queueItem[1];

    						if (queueItemKey == key) {
    							// found a dupe... push to end of queue, null current item, and break
    							this.eventQueue[i] = null;
    							this.eventQueue.push([key, (!YAHOO.lang.isUndefined(value) ? value : queueItemValue)]);
    							foundDuplicate = true;
    							break;
    						}
    					}
    				}

    				if (! foundDuplicate && !YAHOO.lang.isUndefined(value)) { // this is a refire, or a new property in the queue
    					this.eventQueue.push([key, value]);
    				}
    			}

    			if (property.supercedes) {
    				var sLen = property.supercedes.length;
    				for (var s=0; s < sLen; s++) {
    					var supercedesCheck = property.supercedes[s];
    					var qLen = this.eventQueue.length;
    					for (var q=0; q < qLen; q++) {
    						var queueItemCheck = this.eventQueue[q];

    						if (queueItemCheck) {
    							var queueItemCheckKey = queueItemCheck[0];
    							var queueItemCheckValue = queueItemCheck[1];

    							if ( queueItemCheckKey == supercedesCheck.toLowerCase() ) {
    								this.eventQueue.push([queueItemCheckKey, queueItemCheckValue]);
    								this.eventQueue[q] = null;
    								break;
    							}
    						}
    					}
    				}
    			}
    			YAHOO.log("Config event queue: " + this.outputEventQueue(), "info");

    			return true;
    		} else {
    			return false;
    		}
    	},

    	/**
    	* Fires the event for a property using the property's current value.
    	* @method refireEvent
    	* @param {String} key	The name of the property
    	*/
    	refireEvent : function(key) {
    		key = key.toLowerCase();

    		var property = this.config[key];
    		if (property && property.event && !YAHOO.lang.isUndefined(property.value)) {
    			if (this.queueInProgress) {
    				this.queueProperty(key);
    			} else {
    				this.fireEvent(key, property.value);
    			}
    		}
    	},

    	/**
    	* Applies a key-value Object literal to the configuration, replacing any existing values, and queueing the property events.
    	* Although the values will be set, fireQueue() must be called for their associated events to execute.
    	* @method applyConfig
    	* @param {Object}	userConfig	The configuration Object literal
    	* @param {Boolean}	init		When set to true, the initialConfig will be set to the userConfig passed in, so that calling a reset will reset the properties to the passed values.
    	*/
    	applyConfig : function(userConfig, init) {
    		if (init) {
    			this.initialConfig = userConfig;
    		}
    		for (var prop in userConfig) {
    			this.queueProperty(prop, userConfig[prop]);
    		}
    	},

    	/**
    	* Refires the events for all configuration properties using their current values.
    	* @method refresh
    	*/
    	refresh : function() {
    		for (var prop in this.config) {
    			this.refireEvent(prop);
    		}
    	},

    	/**
    	* Fires the normalized list of queued property change events
    	* @method fireQueue
    	*/
    	fireQueue : function() {
    		this.queueInProgress = true;
    		for (var i=0;i<this.eventQueue.length;i++) {
    			var queueItem = this.eventQueue[i];
    			if (queueItem) {
    				var key = queueItem[0];
    				var value = queueItem[1];

    				var property = this.config[key];
    				property.value = value;

    				this.fireEvent(key,value);
    			}
    		}

    		this.queueInProgress = false;
    		this.eventQueue = [];
    	},

    	/**
    	* Subscribes an external handler to the change event for any given property.
    	* @method subscribeToConfigEvent
    	* @param {String}	key			The property name
    	* @param {Function}	handler		The handler function to use subscribe to the property's event
    	* @param {Object}	obj			The Object to use for scoping the event handler (see CustomEvent documentation)
    	* @param {Boolean}	override	Optional. If true, will override "this" within the handler to map to the scope Object passed into the method.
    	* @return {Boolean}				True, if the subscription was successful, otherwise false.
    	*/
    	subscribeToConfigEvent : function(key, handler, obj, override) {
    		var property = this.config[key.toLowerCase()];
    		if (property && property.event) {
    			if (! YAHOO.util.Config.alreadySubscribed(property.event, handler, obj)) {
    				property.event.subscribe(handler, obj, override);
    			}
    			return true;
    		} else {
    			return false;
    		}
    	},

    	/**
    	* Unsubscribes an external handler from the change event for any given property.
    	* @method unsubscribeFromConfigEvent
    	* @param {String}	key			The property name
    	* @param {Function}	handler		The handler function to use subscribe to the property's event
    	* @param {Object}	obj			The Object to use for scoping the event handler (see CustomEvent documentation)
    	* @return {Boolean}				True, if the unsubscription was successful, otherwise false.
    	*/
    	unsubscribeFromConfigEvent : function(key, handler, obj) {
    		var property = this.config[key.toLowerCase()];
    		if (property && property.event) {
    			return property.event.unsubscribe(handler, obj);
    		} else {
    			return false;
    		}
    	},

    	/**
    	* Returns a string representation of the Config object
    	* @method toString
    	* @return {String}	The Config object in string format.
    	*/
    	toString : function() {
    		var output = "Config";
    		if (this.owner) {
    			output += " [" + this.owner.toString() + "]";
    		}
    		return output;
    	},

    	/**
    	* Returns a string representation of the Config object's current CustomEvent queue
    	* @method outputEventQueue
    	* @return {String}	The string list of CustomEvents currently queued for execution
    	*/
    	outputEventQueue : function() {
    		var output = "";
    		for (var q=0;q<this.eventQueue.length;q++) {
    			var queueItem = this.eventQueue[q];
    			if (queueItem) {
    				output += queueItem[0] + "=" + queueItem[1] + ", ";
    			}
    		}
    		return output;
    	}
    };

    /**
    * Initializes the configuration Object and all of its local members.
    * @method init
    * @param {Object}	owner	The owner Object to which this Config Object belongs
    */
    YAHOO.util.Config.prototype.init = function(owner) {
    	this.owner = owner;
    	this.configChangedEvent = new YAHOO.util.CustomEvent(YAHOO.util.CONFIG_CHANGED_EVENT, this);
    	this.queueInProgress = false;
    	this.config = {};
    	this.initialConfig = {};
    	this.eventQueue = [];
    };

    /**
    * Checks to determine if a particular function/Object pair are already subscribed to the specified CustomEvent
    * @method YAHOO.util.Config.alreadySubscribed
    * @static
    * @param {YAHOO.util.CustomEvent} evt	The CustomEvent for which to check the subscriptions
    * @param {Function}	fn	The function to look for in the subscribers list
    * @param {Object}	obj	The execution scope Object for the subscription
    * @return {Boolean}	true, if the function/Object pair is already subscribed to the CustomEvent passed in
    */
    YAHOO.util.Config.alreadySubscribed = function(evt, fn, obj) {
    	for (var e=0;e<evt.subscribers.length;e++) {
    		var subsc = evt.subscribers[e];
    		if (subsc && subsc.obj == obj && subsc.fn == fn) {
    			return true;
    		}
    	}
    	return false;
    };

Copyright © 2007 Yahoo! Inc. All rights reserved.