ActionScript 2.0 Configuration Class
2nd June, 2008 – 2:19 pmThis is an update to my original Actionscript 2.0 Configuration Class which I wrote back in February. It features a new access method for reading and writing values and allows you to load in external XML configuration documents with an easy to understand Schema. All comments welcome.
This class allows simple loading of Configuration values into your ActionScript 2.0 Flash application – you can either defined your values in an external XML file (which is easy to read and edit – even by XML novices) or by using flashVars attached to the root of the stage.
Once you have loaded in your configuration values you can easily access or alter them by using the static method Configure.read(’key’) or Configure.write(’key’, ‘value’).
This class follows the Singleton Design Pattern (or Anti-Pattern depending on what side of the fence you sit on) – what this means, in a nutshell is that you can access the values stored in the Configuration from anywhere in your code – it also means that if you update the values using Configure.write() they will be updated globally.
You can download a .zip file of the Class complete with a detailed example of how to use it in your application.
Any questions – feel free to post in the comments below.
import mx.utils.Delegate;
/**
* ActionScript 2.0 Configuration Class
* John Reves, http://www.jonnyreeves.co.uk/
* June 2008
*
* Singleton configuration class with class notation value access and XML
* import.
*/
class co.uk.jonnyreeves.Configure
{
// Event Listener
private var dispatchEvent:Function;
public var addEventListener:Function;
public var removeEventListener:Function;
/**
* Singleton Instance of this Object.
*/
private static var _INSTANCE:Configure;
/**
* Dynamic Object containing all configuration values
* @see Configure::read()
*/
private var _values:Object;
/**
* Interval used to handle Timeouts when retrieving XML Documents.
*/
private var _xmlTimeoutInterval:Number
/**
* Indicates if the XML document timed out whilst loading.
*/
private var _hasTimedOut:Boolean
/**
* Constructor
* Private, Access via getInstance() only.
*/
private function Configure(Void)
{
this._values = new Object();
EventDispatcher.initialize(this);
}
/**
* Return a singleton instance of Configure.
*
* @return Configure instance
*/
static public function getInstance(Void):Configure
{
if (_INSTANCE == undefined) {
_INSTANCE = new Configure();
}
return _INSTANCE;
}
/**
* Attempts to load in Configuration from an XML File, will dispatch
* an Event depending on the result of this method.
*
* @param xmlFile Location of XML File containing Configuration Values
* @return Void
*/
static public function loadXML(xmlFile:String):Void
{
var _this:Configure = getInstance();
var parser:XML = new XML();
parser.ignoreWhite = true;
// Handles XML Document Retreval.
parser.onLoad = function(success:Boolean):Void
{
if (success && _this.__validates(this)) {
_this.__clearXMLTimeout();
_this.__parseXML(this.childNodes[0], _this._values);
_this.__onXMLLoadSuccess();
}
else {
_this.__onXMLLoadError();
}
}
// Create an Interval to handle timeouts.
_this.__createXMLTimeout();
parser.load(xmlFile);
}
/**
* Attemps to load all flashVars from _root which exist in the ‘config’
* namespace. flashVars should be written using dot notation, but with
* underscores replacing the dots.
*
* For example:
* _root.config_testMessage = ‘hello world!’
* _root.config_shapes_rectangle_width’ = ‘250′
*
* Or using SWFObject notation in the HTML
* fo.AddVariable(’config_testMessage’, ‘hello world!’);
* fo.AddVariable(’config_shapes_rectangle_width’, ‘250′);
*/
static public function loadFlashVars():Void
{
for (var flashVar:String in _root) {
if (flashVar.substr(0, 7) == ‘config_’) {
Configure.write(flashVar.substr(7).split(‘_’).join(‘.’), unescape(_root[flashVar]));
}
}
}
/**
* Read a Variable from the Loaded Configuration. Access to Conifg Groups
* is handled in {dot} notation. So to access _config[Omniture][tracking][dc]
* you would pass in the String ‘omniture.tracking.dc’
*
* @param key Variable to obtain in . notation
* @return String value of Configure::_values[key]
*/
static public function read(key:String):String
{
// Turn the . syntax notation into AS2.0 Array access notation.
var keys:Array = key.split(‘.’);
var thisGroup:Object = getInstance()._values
while (keys.length) {
thisGroup = thisGroup[keys.shift()];
}
// Debugging.
if (thisGroup.toString() == undefined) {
trace(‘Configure::read – Returning Undefined Value for: ‘ + arguments[0]);
}
return thisGroup.toString();
}
/**
* Write a Key, Value pair to the Config Object at Run Time. The key
* should be supplied in {dot} notation. The return value could slow
* down performance on writes.
*
* @param key Varible to write in . notation
* @param value Value to write to @param key as a String
* @return indicates write was succesfull.
*/
static public function write(key:String, value:String):Boolean
{
// Helper method used for recursion
getInstance().__recurseWrite(key.split(‘.’), value, getInstance()._values);
return (read(key) == value);
}
/**
* Helper method used by Configure::write() when dealing with "deep"
* values inside the _values object. Method will recurse through the
* input String (x), and write value to the current configObj node.
*
* @param keys Array contianing the "path" of _values object to write to
* @param value String value to write to ConfigObject.
* @param configObj Current Node of the Configuration Values _values object
* @return Void
*/
private function __recurseWrite(keys:Array, value:String, configObj:Object):Void
{
// Write if that was the last key index.
if (keys.length == 1) {
configObj[keys.shift()] = value;
}
// Otherwise Recurse again, create new object if needed.
else {
var thisKey = keys.shift();
if (configObj[thisKey] == undefined) {
configObj[thisKey] = new Object();
}
this.__recurseWrite(keys, value, configObj[thisKey]);
}
}
/**
* Callback to handle failed attempts to Load the configuration XML.
*
* @param Void
* @return Void
*/
private function __onXMLLoadError(Void):Void
{
this.__clearXMLTimeout();
var eventObject:Object = new Object();
eventObject.target = this;
eventObject.type = ‘configureEvent’;
eventObject.name = ‘Configure.onXMLLoadError’
this.dispatchEvent(eventObject);
}
/**
* XML has been successfully loaded and is ready to be interacted with.
*
* @param Void
* @return Void
*/
private function __onXMLLoadSuccess(Void):Void
{
var eventObject:Object = new Object();
eventObject.target = this;
eventObject.type = ‘configureEvent’;
eventObject.name = ‘Configure.onXMLLoaded’
this.dispatchEvent(eventObject);
}
/**
* Validates the loaded XML Document
*
* @param xml XMLObject loaded by Configure::loadXML()
* @return Boolean false on validation errors
*/
private function __validates(xml:XML):Boolean
{
// Abort if we have already timed out.
if (this._hasTimedOut) {
trace (‘Configure::__validateXML – Refusing to Parse XML, timed out’);
return false;
}
// Check the first node is
if (String(xml.childNodes[0].nodeName) !== "configuration") {
this.__onInvalidXML();
return false;
}
return true;
}
/**
* Parses the Loaded XML Documents, populating this object’s _values prop.
*
* @param xml XMLObject loaded by Configure::loadXML()
* @param config Internal Configuration Object to write values to
* @return Void
*/
private function __parseXML(xml:XMLNode, config:Object):Void
{
// loop through all Children of this Node.
var nodeCount:Number = xml.childNodes.length;
for (var i:Number = 0; i < nodeCount; i++)
{
var thisNode:XMLNode = xml.childNodes[i];
// Recurse into Config Groups (tricky because values are considered to have Length.
if (thisNode.childNodes.length > 0 && thisNode.childNodes[0].nodeValue == null)
{
// Create new Object off of this._values
config[thisNode.nodeName] = new Object();
this.__parseXML(thisNode, config[thisNode.nodeName]);
}
// Write Value
else {
config[thisNode.nodeName] = thisNode.childNodes[0].nodeValue.toString();
}
}
}
/**
* Creates an Interval used by Configure::loadXML to handle XML documents
* which timeout.
*
* @param Void
* @return Void
*/
private function __createXMLTimeout(Void):Void
{
this._xmlTimeoutInterval = _global.setTimeout(this, ‘__onXMLTimeout’, 1000);
this._hasTimedOut = false;
}
/**
* Triggered when the timeout in createXMLTimeout() has expired, indicates
* that the XML document failed to load.
*
* @param Void
* @return Void
*/
private function __onXMLTimeout(Void):Void
{
trace (‘Configure::__onXMLTimeout’);
this._hasTimedOut = true;
this.__clearXMLTimeout();
this.__onXMLLoadError();
}
/**
* Gets called when the Parser fails to parse the incoming configuration
* XML.
* @param Void
* @return Void
*/
private function __onInvalidXML(Void):Void
{
trace(‘Configure::__onInvalidXML’);
}
/**
* Clears the Interval, tidies up.
*/
private function __clearXMLTimeout()
{
clearInterval(this._xmlTimeoutInterval);
}
}
3 Responses to “ActionScript 2.0 Configuration Class”
Hello, I am trying to run your ActionScript 2.0 Configuration Class. I have the Configure file, an xml file and your test pr example_configure file. I run it and it does not see the xml file. I am following the instructions to a tee and I cannot for the life of me get the xml file to load. Any suggestions?? It says that this is for ActionScript 2.0, and that is what I am using, but when I went into your Singleton design pattern, it said that Singleton doesn’t work in AS2 because this version doesn’t use private functions. I have looked up how true that is, but I have some hope that you can help me. If you need any other information, feel free to email me and ask. thanks and have a nice day:
rick
By Rick on Feb 23, 2009
Hi Rick,
The Wikip[edia article on the Singleton Design Pattern notes that ActionScript 3.0 does not support – this is true, but does not affect the code above as it’s ActionScript 2.0 (which does support private constructors.)
The code listed above should, in theory execute correctly. If you are having trouble loading your XML configuration file, you should ensure that you are calling the Configure.loadXML() correctly (ie: the path to the XML file is correct and that your flash player permissions and can read the file).
Jonny.
By Jonny on Feb 24, 2009