/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
Version 0.12.2
*/
/**
* 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);
}
};
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,
/**
* 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) {
if (typeof val == 'boolean') {
return true;
} else {
return false;
}
},
/**
* 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) {
if (isNaN(val)) {
return false;
} else {
return true;
}
}
};
/**
* 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;
/**
* Object reference to the owner of this Config Object
* @event configChangedEvent
*/
this.configChangedEvent = new YAHOO.util.CustomEvent("configChanged");
this.queueInProgress = false;
/* Private Members */
/**
* Maintains the local collection of configuration property objects and their specified values
* @property config
* @private
* @type Object
*/
var config = {};
/**
* 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
*/
var initialConfig = {};
/**
* Maintains the local, normalized CustomEvent queue
* @property eventQueue
* @private
* @type Object
*/
var eventQueue = [];
/**
* 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
*/
var fireEvent = function( key, value ) {
key = key.toLowerCase();
var property = config[key];
if (typeof property != 'undefined' && property.event) {
property.event.fire(value);
}
};
/* End Private Members */
/**
* 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
*/
this.addProperty = function( key, propertyObject ) {
key = key.toLowerCase();
config[key] = propertyObject;
propertyObject.event = new YAHOO.util.CustomEvent(key);
propertyObject.key = key;
if (propertyObject.handler) {
propertyObject.event.subscribe(propertyObject.handler, this.owner, true);
}
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
*/
this.getConfig = function() {
var cfg = {};
for (var prop in config) {
var property = config[prop];
if (typeof property != 'undefined' && 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
*/
this.getProperty = function(key) {
key = key.toLowerCase();
var property = config[key];
if (typeof property != 'undefined' && 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
*/
this.resetProperty = function(key) {
key = key.toLowerCase();
var property = config[key];
if (typeof property != 'undefined' && property.event) {
if (initialConfig[key] && initialConfig[key] != 'undefined') {
this.setProperty(key, 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.
*/
this.setProperty = function(key, value, silent) {
key = key.toLowerCase();
if (this.queueInProgress && ! silent) {
this.queueProperty(key,value); // Currently running through a queue...
return true;
} else {
var property = config[key];
if (typeof property != 'undefined' && property.event) {
if (property.validator && ! property.validator(value)) { // validator
return false;
} else {
property.value = value;
if (! silent) {
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.
*/
this.queueProperty = function(key, value) {
key = key.toLowerCase();
var property = config[key];
if (typeof property != 'undefined' && property.event) {
if (typeof value != 'undefined' && property.validator && ! property.validator(value)) { // validator
return false;
} else {
if (typeof value != 'undefined') {
property.value = value;
} else {
value = property.value;
}
var foundDuplicate = false;
for (var i=0;i<eventQueue.length;i++) {
var queueItem = eventQueue[i];
if (queueItem) {
var queueItemKey = queueItem[0];
var queueItemValue = queueItem[1];
if (queueItemKey.toLowerCase() == key) {
// found a dupe... push to end of queue, null current item, and break
eventQueue[i] = null;
eventQueue.push([key, (typeof value != 'undefined' ? value : queueItemValue)]);
foundDuplicate = true;
break;
}
}
}
if (! foundDuplicate && typeof value != 'undefined') { // this is a refire, or a new property in the queue
eventQueue.push([key, value]);
}
}
if (property.supercedes) {
for (var s=0;s<property.supercedes.length;s++) {
var supercedesCheck = property.supercedes[s];
for (var q=0;q<eventQueue.length;q++) {
var queueItemCheck = eventQueue[q];
if (queueItemCheck) {
var queueItemCheckKey = queueItemCheck[0];
var queueItemCheckValue = queueItemCheck[1];
if ( queueItemCheckKey.toLowerCase() == supercedesCheck.toLowerCase() ) {
eventQueue.push([queueItemCheckKey, queueItemCheckValue]);
eventQueue[q] = null;
break;
}
}
}
}
}
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
*/
this.refireEvent = function(key) {
key = key.toLowerCase();
var property = config[key];
if (typeof property != 'undefined' && property.event && typeof property.value != 'undefined') {
if (this.queueInProgress) {
this.queueProperty(key);
} else {
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.
*/
this.applyConfig = function(userConfig, init) {
if (init) {
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
*/
this.refresh = function() {
for (var prop in config) {
this.refireEvent(prop);
}
};
/**
* Fires the normalized list of queued property change events
* @method fireQueue
*/
this.fireQueue = function() {
this.queueInProgress = true;
for (var i=0;i<eventQueue.length;i++) {
var queueItem = eventQueue[i];
if (queueItem) {
var key = queueItem[0];
var value = queueItem[1];
var property = config[key];
property.value = value;
fireEvent(key,value);
}
}
this.queueInProgress = false;
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.
*/
this.subscribeToConfigEvent = function(key, handler, obj, override) {
key = key.toLowerCase();
var property = config[key];
if (typeof property != 'undefined' && 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.
*/
this.unsubscribeFromConfigEvent = function(key, handler, obj) {
key = key.toLowerCase();
var property = config[key];
if (typeof property != 'undefined' && 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.
*/
this.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
*/
this.outputEventQueue = function() {
var output = "";
for (var q=0;q<eventQueue.length;q++) {
var queueItem = eventQueue[q];
if (queueItem) {
output += queueItem[0] + "=" + queueItem[1] + ", ";
}
}
return output;
};
};
/**
* 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;
};