/** * MinimalConfigurator.as * Keith Peters * version 0.9.10 * * A class for parsing xml layout code to create minimal components declaratively. * * Copyright (c) 2011 Keith Peters * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.bit101.utils { // usually don't use * but we really are importing everything here. import com.bit101.components.*; import flash.display.DisplayObjectContainer; import flash.events.Event; import flash.events.EventDispatcher; import flash.net.URLLoader; import flash.net.URLRequest; import flash.utils.getDefinitionByName; /** * Creates and lays out minimal components based on a simple xml format. */ public class MinimalConfigurator extends EventDispatcher { protected var loader:URLLoader; protected var parent:DisplayObjectContainer; protected var idMap:Object; /** * Constructor. * @param parent The display object container on which to create components and look for ids and event handlers. */ public function MinimalConfigurator(parent:DisplayObjectContainer) { this.parent = parent; idMap = new Object(); } /** * Loads an xml file from the specified url and attempts to parse it as a layout format for this class. * @param url The location of the xml file. */ public function loadXML(url:String):void { loader = new URLLoader(); loader.addEventListener(Event.COMPLETE, onLoadComplete); loader.load(new URLRequest(url)); } /** * Called when the xml has loaded. Will attempt to parse the loaded data as xml. */ private function onLoadComplete(event:Event):void { parseXMLString(loader.data as String); } /** * Parses a string as xml. * @param string The xml string to parse. */ public function parseXMLString(string:String):void { try { var xml:XML = new XML(string); parseXML(xml); } catch(e:Error) { } dispatchEvent(new Event(Event.COMPLETE)); } /** * Parses xml and creates componetns based on it. * @param xml The xml to parse. */ public function parseXML(xml:XML):void { // root tag should contain one or more component tags // each tag's name should be the base name of a component, i.e. "PushButton" // package is assumed "com.bit101.components" for(var i:int = 0; i < xml.children().length(); i++) { var comp:XML = xml.children()[i]; var compInst:Component = parseComp(comp); if(compInst != null) { parent.addChild(compInst); } } } /** * Parses a single component's xml. * @param xml The xml definition for this component. * @return A component instance. */ private function parseComp(xml:XML):Component { var compInst:Object; var specialProps:Object = {}; try { var classRef:Class = getDefinitionByName("com.bit101.components." + xml.name()) as Class; compInst = new classRef(); // id is special case, maps to name as well. var id:String = trim(xml.@id.toString()); if(id != "") { compInst.name = id; idMap[id] = compInst; // if id exists on parent as a public property, assign this component to it. if(parent.hasOwnProperty(id)) { parent[id] = compInst; } } // event is another special case if(xml.@event.toString() != "") { // events are in the format: event="eventName:eventHandler" // i.e. event="click:onClick" var parts:Array = xml.@event.split(":"); var eventName:String = trim(parts[0]); var handler:String = trim(parts[1]); if(parent.hasOwnProperty(handler)) { // if event handler exists on parent as a public method, assign it as a handler for the event. compInst.addEventListener(eventName, parent[handler]); } } // every other attribute handled essentially the same for each(var attrib:XML in xml.attributes()) { var prop:String = attrib.name().toString(); // if the property exists on the component, assign it. if(compInst.hasOwnProperty(prop)) { // special handling to correctly parse booleans if(compInst[prop] is Boolean) { compInst[prop] = attrib == "true"; } // special handling - these values should be set last. else if(prop == "value" || prop == "lowValue" || prop == "highValue" || prop == "choice") { specialProps[prop] = attrib; } else { compInst[prop] = attrib; } } } // now handle special props for(prop in specialProps) { compInst[prop] = specialProps[prop]; } // child nodes will be added as children to the instance just created. for(var j:int = 0; j < xml.children().length(); j++) { var child:Component = parseComp(xml.children()[j]); if(child != null) { compInst.addChild(child); } } } catch(e:Error) { } return compInst as Component; } /** * Returns the componet with the given id, if it exists. * @param id The id of the component you want. * @return The component with that id, if it exists. */ public function getCompById(id:String):Component { return idMap[id]; } /** * Trims a string. * @param s The string to trim. * @return The trimmed string. */ private function trim(s:String):String { // http://jeffchannell.com/ActionScript-3/as3-trim.html return s.replace(/^\s+|\s+$/gs, ''); } /** * We need to include all component classes in the swf. */ Accordion; Calendar; CheckBox; ColorChooser; ComboBox; FPSMeter; HBox; HRangeSlider; HScrollBar; HSlider; HUISlider; IndicatorLight; InputText; Knob; Label; List; ListItem; Meter; NumericStepper; Panel; ProgressBar; PushButton; RadioButton; RangeSlider; RotarySelector; ScrollBar; ScrollPane; Slider; Style; Text; TextArea; UISlider; VBox; VRangeSlider; VScrollBar; VSlider; VUISlider; WheelMenu; Window; } }