//
// $Id$

package com.threerings.flex {

import flash.display.DisplayObjectContainer;

import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TextEvent;

import flash.ui.Keyboard;

import mx.containers.HBox;

import mx.core.Application;

import mx.controls.TextInput;

import mx.events.FlexEvent;

import com.threerings.util.ArrayUtil;
import com.threerings.util.StringUtil;

import com.threerings.flex.CommandButton;
import com.threerings.flex.CommandMenu;

import com.threerings.crowd.chat.client.ChatDirector;
import com.threerings.crowd.chat.data.ChatCodes;

import com.threerings.crowd.util.CrowdContext;

/**
 * The chat control widget.
 */
public class ChatControl extends HBox
{
    /**
     * Request focus for the oldest ChatControl.
     */
    public static function grabFocus () :void
    {
        if (_controls.length > 0) {
            (_controls[0] as ChatControl).setFocus();
        }
    }

    public function ChatControl (
        ctx :CrowdContext, sendButtonLabel :String = "send",
        height :Number = NaN, controlHeight :Number = NaN)
    {
        _ctx = ctx;
        _chatDtr = _ctx.getChatDirector();

        this.height = height;
        styleName = "chatControl";

        addChild(_txt = new ChatInput());
        _txt.addEventListener(FlexEvent.ENTER, sendChat, false, 0, true);

        _but = new CommandButton(sendButtonLabel, sendChat);
        addChild(_but);

        if (!isNaN(controlHeight)) {
            _txt.height = controlHeight;
            _but.height = controlHeight;
        }

        addEventListener(Event.ADDED_TO_STAGE, handleAddRemove);
        addEventListener(Event.REMOVED_FROM_STAGE, handleAddRemove);
    }

    /**
     * Request focus to this chat control.
     */
    override public function setFocus () :void
    {
        _txt.setFocus();
    }

    /**
     * Enables or disables our chat input.
     */
    public function setEnabled (enabled :Boolean) :void
    {
        _txt.enabled = enabled;
        _but.enabled = enabled;
    }

    /**
     * Configures the chat director to which we should send our chat. Pass null to restore our
     * default chat director.
     */
    public function setChatDirector (chatDtr :ChatDirector) :void
    {
        _chatDtr = (chatDtr == null) ? _ctx.getChatDirector() : chatDtr;
    }

    /**
     * Configures the background color of the text entry area.
     */
    public function setChatColor (color :int) :void
    {
        _txt.setStyle("backgroundColor", color);
    }

    /**
     * Handles FlexEvent.ENTER and the action from the send button.
     */
    protected function sendChat (... ignored) :void
    {
        var message :String = StringUtil.trim(_txt.text);
        if ("" == message) {
            return;
        }

        var result :String = _chatDtr.requestChat(null, message, true);
        if (result != ChatCodes.SUCCESS) {
            _chatDtr.displayFeedback(null, result);
            return;
        }

        // if there was no error, clear the entry area in prep for the next entry event
        _txt.text = "";
    }

    /**
     * Handles Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE.
     */
    protected function handleAddRemove (event :Event) :void
    {
        if (event.type == Event.ADDED_TO_STAGE) {
            // set up any already-configured text
            _txt.text = _curLine;

            // request focus
            callLater(_txt.setFocus);
            _controls.push(this);

        } else {
            _curLine = _txt.text;
            ArrayUtil.removeAll(_controls, this);
        }
    }

    /** Our client-side context. */
    protected var _ctx :CrowdContext;

    /** The director to which we are sending chat requests. */
    protected var _chatDtr :ChatDirector;

    /** The place where the user may enter chat. */
    protected var _txt :ChatInput;

    /** The button for sending chat. */
    protected var _but :CommandButton;

    /** An array of the currently shown-controls. */
    protected static var _controls :Array = [];

    /** The preserved current line of text when traversing history or carried between instances of
     * ChatControl. */
    protected static var _curLine :String;
}
}

//
// $Id$

package com.threerings.flex {

import flash.display.DisplayObjectContainer;

import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TextEvent;

import flash.ui.Keyboard;

import mx.containers.HBox;

import mx.core.Application;

import mx.controls.TextInput;

import mx.events.FlexEvent;

import com.threerings.util.ArrayUtil;
import com.threerings.util.StringUtil;

import com.threerings.flex.CommandButton;
import com.threerings.flex.CommandMenu;

import com.threerings.crowd.chat.client.ChatDirector;
import com.threerings.crowd.chat.data.ChatCodes;

import com.threerings.crowd.util.CrowdContext;

/**
 * The chat control widget.
 */
public class ChatControl extends HBox
{
    /**
     * Request focus for the oldest ChatControl.
     */
    public static function grabFocus () :void
    {
        if (_controls.length > 0) {
            (_controls[0] as ChatControl).setFocus();
        }
    }

    public function ChatControl (
        ctx :CrowdContext, sendButtonLabel :String = "send",
        height :Number = NaN, controlHeight :Number = NaN)
    {
        _ctx = ctx;
        _chatDtr = _ctx.getChatDirector();

        this.height = height;
        styleName = "chatControl";

        addChild(_txt = new ChatInput());
        _txt.addEventListener(FlexEvent.ENTER, sendChat, false, 0, true);

        _but = new CommandButton(sendButtonLabel, sendChat);
        addChild(_but);

        if (!isNaN(controlHeight)) {
            _txt.height = controlHeight;
            _but.height = controlHeight;
        }

        addEventListener(Event.ADDED_TO_STAGE, handleAddRemove);
        addEventListener(Event.REMOVED_FROM_STAGE, handleAddRemove);
    }

    /**
     * Provides access to the text field we use to accept chat.
     */
    public function get chatInput () :ChatInput
    {
        return _txt;
    }

    /**
     * Request focus to this chat control.
     */
    override public function setFocus () :void
    {
        _txt.setFocus();
    }

    /**
     * Enables or disables our chat input.
     */
    public function setEnabled (enabled :Boolean) :void
    {
        _txt.enabled = enabled;
        _but.enabled = enabled;
    }

    /**
     * Configures the chat director to which we should send our chat. Pass null to restore our
     * default chat director.
     */
    public function setChatDirector (chatDtr :ChatDirector) :void
    {
        _chatDtr = (chatDtr == null) ? _ctx.getChatDirector() : chatDtr;
    }

    /**
     * Configures the background color of the text entry area.
     */
    public function setChatColor (color :int) :void
    {
        _txt.setStyle("backgroundColor", color);
    }

    /**
     * Handles FlexEvent.ENTER and the action from the send button.
     */
    protected function sendChat (... ignored) :void
    {
        var message :String = StringUtil.trim(_txt.text);
        if ("" == message) {
            return;
        }

        var result :String = _chatDtr.requestChat(null, message, true);
        if (result != ChatCodes.SUCCESS) {
            _chatDtr.displayFeedback(null, result);
            return;
        }

        // if there was no error, clear the entry area in prep for the next entry event
        _txt.text = "";
    }

    /**
     * Handles Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE.
     */
    protected function handleAddRemove (event :Event) :void
    {
        if (event.type == Event.ADDED_TO_STAGE) {
            // set up any already-configured text
            _txt.text = _curLine;

            // request focus
            callLater(_txt.setFocus);
            _controls.push(this);

        } else {
            _curLine = _txt.text;
            ArrayUtil.removeAll(_controls, this);
        }
    }

    /** Our client-side context. */
    protected var _ctx :CrowdContext;

    /** The director to which we are sending chat requests. */
    protected var _chatDtr :ChatDirector;

    /** The place where the user may enter chat. */
    protected var _txt :ChatInput;

    /** The button for sending chat. */
    protected var _but :CommandButton;

    /** An array of the currently shown-controls. */
    protected static var _controls :Array = [];

    /** The preserved current line of text when traversing history or carried between instances of
     * ChatControl. */
    protected static var _curLine :String;
}
}

Add an accessor for our chatInput field.

git-svn-id: b675b909355d5cf946977f44a8ec5a6ceb3782e4@446 ed5b42cb-e716-0410-a449-f6a68f950b19


/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 */

package com.google.analytics.core
{
	import library.ASTUce.framework.TestCase;

    import com.google.analytics.utils.Environment;
    import com.google.analytics.utils.FakeEnvironment;
    import com.google.analytics.utils.Variables;
    import com.google.analytics.utils.Version;
    import com.google.analytics.v4.Configuration;

    public class BrowserInfoTest extends TestCase
    {
        private var _config:Configuration;
        private var _browserInfo0:BrowserInfo;
        private var _env0:Environment;

        public function BrowserInfoTest(name:String="")
        {
            super(name);
        }

        public function testHitId():void {
        	assertEquals(1,1);

        }

        public function setUp():void
        {
            _config = new Configuration();
            _env0 = new FakeEnvironment("",null,"","","","","","",new Version(9,0,115,0),"en-GB","UTF-8","","","",null,800,600,"24");
            _browserInfo0 = new BrowserInfo( _config, _env0 );
        }

        public function testFlashVersion():void
        {
            assertEquals( "9.0 r115", _browserInfo0.utmfl );
        }

        public function testScreenInfo():void
        {
            assertEquals( "800x600", _browserInfo0.utmsr );
            assertEquals( "24-bit", _browserInfo0.utmsc );
        }

        public function testLangInfo():void
        {
            assertEquals( "en-gb", _browserInfo0.utmul );
            assertEquals( "UTF-8", _browserInfo0.utmcs );
        }

        public function testToVariables():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            assertEquals( "UTF-8",    vars.utmcs );
            assertEquals( "800x600",  vars.utmsr );
            assertEquals( "24-bit",   vars.utmsc );
            assertEquals( "en-gb",    vars.utmul );
            assertEquals( "0",        vars.utmje );
            assertEquals( "9.0 r115", vars.utmfl );
        }

        public function testToURLString():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            var varsA:Variables = new Variables();
                varsA.URIencode = true;
                varsA.utmcs = vars.utmcs;
            assertEquals( "utmcs=UTF-8", varsA.toString() );

            var varsB:Variables = new Variables();
                varsB.URIencode = true;
                varsB.utmsr = vars.utmsr;
            assertEquals( "utmsr=800x600", varsB.toString() );

            var varsC:Variables = new Variables();
                varsC.URIencode = true;
                varsC.utmsc = vars.utmsc;
            assertEquals( "utmsc=24-bit", varsC.toString() );

            var varsD:Variables = new Variables();
                varsD.URIencode = true;
                varsD.utmul = vars.utmul;
            assertEquals( "utmul=en-gb", varsD.toString() );

            var varsE:Variables = new Variables();
                varsE.URIencode = true;
                varsE.utmje = vars.utmje;
            assertEquals( "utmje=0", varsE.toString() );

            var varsF:Variables = new Variables();
                varsF.URIencode = true;
                varsF.utmfl = vars.utmfl;
            assertEquals( "utmfl=9.0%20r115", varsF.toString() );
        }

    }
}

/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 */

package com.google.analytics.core
{
	import com.google.analytics.utils.Environment;
	import com.google.analytics.utils.FakeEnvironment;
	import com.google.analytics.utils.Variables;
	import com.google.analytics.v4.Configuration;

	import core.version;

	import library.ASTUce.framework.TestCase;

    public class BrowserInfoTest extends TestCase
    {
        private var _config:Configuration;
        private var _browserInfo0:BrowserInfo;
        private var _env0:Environment;

        public function BrowserInfoTest(name:String="")
        {
            super(name);
        }

        public function testHitId():void {
        	assertEquals(1,1);

        }

        public function setUp():void
        {
            _config = new Configuration();
            _env0 = new FakeEnvironment("",null,"","","","","","",new version(9,0,115,0),"en-GB","UTF-8","","","",null,800,600,"24");
            _browserInfo0 = new BrowserInfo( _config, _env0 );
        }

        public function testFlashVersion():void
        {
            assertEquals( "9.0 r115", _browserInfo0.utmfl );
        }

        public function testScreenInfo():void
        {
            assertEquals( "800x600", _browserInfo0.utmsr );
            assertEquals( "24-bit", _browserInfo0.utmsc );
        }

        public function testLangInfo():void
        {
            assertEquals( "en-gb", _browserInfo0.utmul );
            assertEquals( "UTF-8", _browserInfo0.utmcs );
        }

        public function testToVariables():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            assertEquals( "UTF-8",    vars.utmcs );
            assertEquals( "800x600",  vars.utmsr );
            assertEquals( "24-bit",   vars.utmsc );
            assertEquals( "en-gb",    vars.utmul );
            assertEquals( "0",        vars.utmje );
            assertEquals( "9.0 r115", vars.utmfl );
        }

        public function testToURLString():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            var varsA:Variables = new Variables();
                varsA.URIencode = true;
                varsA.utmcs = vars.utmcs;
            assertEquals( "utmcs=UTF-8", varsA.toString() );

            var varsB:Variables = new Variables();
                varsB.URIencode = true;
                varsB.utmsr = vars.utmsr;
            assertEquals( "utmsr=800x600", varsB.toString() );

            var varsC:Variables = new Variables();
                varsC.URIencode = true;
                varsC.utmsc = vars.utmsc;
            assertEquals( "utmsc=24-bit", varsC.toString() );

            var varsD:Variables = new Variables();
                varsD.URIencode = true;
                varsD.utmul = vars.utmul;
            assertEquals( "utmul=en-gb", varsD.toString() );

            var varsE:Variables = new Variables();
                varsE.URIencode = true;
                varsE.utmje = vars.utmje;
            assertEquals( "utmje=0", varsE.toString() );

            var varsF:Variables = new Variables();
                varsF.URIencode = true;
                varsF.utmfl = vars.utmfl;
            assertEquals( "utmfl=9.0%20r115", varsF.toString() );
        }

    }
}

replace class Version by core.version

replace class Version by core.version


/*
Copyright (c) 2007 FlexLib Contributors.  See:
    http://code.google.com/p/flexlib/wiki/ProjectContributors

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 flexlib.containers {
	import flash.events.MouseEvent;

	import mx.controls.Button;
	import mx.core.EdgeMetrics;
	import mx.core.IFactory;
	import mx.core.LayoutContainer;
	import mx.core.ScrollPolicy;
	import mx.effects.Resize;
    import mx.effects.effectClasses.ResizeInstance;
    import mx.events.EffectEvent;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	import mx.utils.StringUtil;

    /**
     * This is the icon displayed on the headerButton when the WindowShade is in the open state.
     */
    [Style(name="openIcon", type="Class", inherit="no")]

    /**
     * This is the icon displayed on the headerButton when the WindowShade is in the closed state.
     */
    [Style(name="closeIcon", type="Class", inherit="no")]

    /**
     * The duration of the WindowShade opening transition, in milliseconds. The value 0 specifies no transition.
     *
     * @default 250
     */
    [Style(name="openDuration", type="Number", format="Time", inherit="no")]

    /**
     * The duration of the WindowShade closing transition, in milliseconds. The value 0 specifies no transition.
     *
     * @default 250
     */
    [Style(name="closeDuration", type="Number", format="Time", inherit="no")]

    /**
     * The class from which the headerButton will be instantiated. Must be mx.controls.Button
     * or a subclass.
     *
     * @default mx.controls.Button
     */
    [Style(name="headerClass", type="Class", inherit="no")]

    /**
     * Name of CSS style declaration that specifies styles for the headerButton.
     */
    [Style(name="headerStyleName", type="String", inherit="no")]

    /**
     * Alignment of text on the headerButton. The value set for this style is used as
     * the textAlign style on the headerButton. Valid values are "left", "center" and "right".
     *
     * @default "right"
     */
     [Style(name="headerTextAlign", type="String", inherit="no")]

     /**
      * If true, the value of the headerButton's toggle property will be set to true;
      * otherwise the toggle property will be left in its default state.
      *
      * @default false
      */
     [Style(name="toggleHeader", type="Boolean", inherit="no")]

    /**
     * This control displays a button, which when clicked, will cause a panel to "unroll" beneath
     * it like a windowshade being pulled down; or if the panel is already displayed it
     * will be "rolled up" like a windowshade being rolled up. When multiple WindowShades are stacked
     * in a VBox, the result will be similar to an mx.containers.Accordian container, except that multiple
     * WindowShades can be opened simultaneously whereas an Accordian acts like a tab navigator, with only
     * one panel visible at a time.
     */
    public class WindowShade extends LayoutContainer {

		[Embed (source="../assets/assets.swf", symbol="right_arrow")]
		private static var DEFAULT_CLOSE_ICON:Class;

		[Embed (source="../assets/assets.swf", symbol="down_arrow")]
		private static var DEFAULT_OPEN_ICON:Class;

        private static var styleDefaults:Object = {
             openDuration:250
            ,closeDuration:250
            ,paddingTop:10
            ,headerClass:Button
            ,headerTextAlign:"left"
            ,toggleHeader:false
            ,headerStyleName:null
            ,closeIcon:DEFAULT_CLOSE_ICON
            ,openIcon:DEFAULT_OPEN_ICON
        };

        private static var classConstructed:Boolean = constructClass();

        private static function constructClass():Boolean {

            var css:CSSStyleDeclaration = StyleManager.getStyleDeclaration("WindowShade")
            var changed:Boolean = false;
            if(!css) {
                // If there is no CSS definition for WindowShade,
                // then create one and set the default value.
                css = new CSSStyleDeclaration();
                changed = true;
            }

            // make sure we have a valid values for each style. If not, set the defaults.
            for(var styleProp:String in styleDefaults) {
                if(!StyleManager.isValidStyleValue(css.getStyle(styleProp))) {
                    css.setStyle(styleProp, styleDefaults[styleProp]);
                    changed = true;
                }
            }

            if(changed) {
                StyleManager.setStyleDeclaration("WindowShade", css, true);
            }

            return true;
        }

		/**
		 * @private
		 * A reference to the Button that will be used for the header. Must always be a Button or subclass of Button.
		 */
        private var _headerButton:Button = null;

        private var headerChanged:Boolean;

        /**
        * @private
        * The header renderer factory that will get used to create the header.
        */
        private var _headerRenderer:IFactory;

        /**
        * To control the header used on the WindowShade component you can either set the headerClass or the
        * headerRenderer. The headerRenderer works similar to the itemRenderer of a List control.
        * You can set this using MXML using any Button control. This would let you customize things like button skin. You could
        * even combine this with the CanvasButton component to make complex headers.
        */
        public function set headerRenderer(value:IFactory):void {
        	_headerRenderer = value;

        	headerChanged = true;
        	invalidateProperties();
        }

        public function get headerRenderer():IFactory {
        	return _headerRenderer;
        }

        /**
        * @private
        * Boolean dirty flag to let us know if we need to change the icon in the commitProperties method.
        */
        private var _openedChanged:Boolean = false;

        public function WindowShade() {
            super();

            //default scroll policies are off
            this.verticalScrollPolicy = ScrollPolicy.OFF;
            this.horizontalScrollPolicy = ScrollPolicy.OFF;

            addEventListener(EffectEvent.EFFECT_END, onEffectEnd);
        }

        protected function createOrReplaceHeaderButton():void {
           if(_headerButton) {
                _headerButton.removeEventListener(MouseEvent.CLICK, headerButton_clickHandler);

                if(rawChildren.contains(_headerButton)) {
                    rawChildren.removeChild(_headerButton);
                }
            }

            if(_headerRenderer) {
            	_headerButton = _headerRenderer.newInstance() as Button;
            }
            else {
            	var headerClass:Class = getStyle("headerClass");
          	 	_headerButton = new headerClass();
            }

            applyHeaderButtonStyles(_headerButton);

            _headerButton.addEventListener(MouseEvent.CLICK, headerButton_clickHandler);

            rawChildren.addChild(_headerButton);
        }

        protected function applyHeaderButtonStyles(button:Button):void {
            button.setStyle("textAlign", getStyle("headerTextAlign"));

            var headerStyleName:String = getStyle("headerStyleName");
            if(headerStyleName) {
            	headerStyleName = StringUtil.trim(headerStyleName);
            	button.styleName = headerStyleName;
            }

            button.toggle = getStyle("toggleHeader");
            button.label = label;

            if(_opened) {
                button.setStyle('icon', getStyle("openIcon"));
            }
            else {
                button.setStyle('icon', getStyle("closeIcon"));
            }

            if(button.toggle) {
                button.selected = _opened;
            }
        }

        /**
         * @private
         */
        override public function set label(value:String):void {
            super.label = value;

            if(_headerButton) _headerButton.label = value;
        }

		/**
		 * @private
		 */
		private var _opened:Boolean = true;

        /**
         * Sets or gets the state of this WindowShade, either opened (true) or closed (false).
         */
        public function get opened():Boolean {
            return _opened;
        }

        private var _headerLocation:String = "top";

		[Bindable]
		[Inspectable(enumeration="top,bottom", defaultValue="top")]
		/**
		 * Specifies where the header button is placed relative tot he content of this WindowShade. Possible
		 * values are top and bottom.
		 */
		public function set headerLocation(value:String):void {
			_headerLocation = value;
			invalidateSize();
			invalidateDisplayList();
		}

		public function get headerLocation():String {
			return _headerLocation;
		}

        /**
         * @private
         */
         [Bindable]
        public function set opened(value:Boolean):void {
            var old:Boolean = _opened;

            _opened = value;
            _openedChanged = _openedChanged || old != _opened;

            if(_openedChanged && initialized) {
                measure();
               	runResizeEffect();

                invalidateProperties();
            }
        }

        /**
         * @private
         */
        override public function styleChanged(styleProp:String):void {
            super.styleChanged(styleProp);

            if(styleProp == "headerClass") {
                headerChanged = true;
                invalidateProperties();
            }
            else if(styleProp == "headerStyleName" || styleProp == "headerTextAlign" || styleProp == "toggleHeader"
            	|| styleProp == "openIcon" || styleProp == "closeIcon") {
                applyHeaderButtonStyles(_headerButton);
            }

            invalidateDisplayList();
        }

        /**
         * @private
         */
        override protected function createChildren():void {
            super.createChildren();

            createOrReplaceHeaderButton();
        }

        /**
         * @private
         */
        override protected function commitProperties():void {

			super.commitProperties();

			if(headerChanged) {
				createOrReplaceHeaderButton();
				headerChanged = false;
			}

            if(_openedChanged) {

                if(_opened) {
                    _headerButton.setStyle('icon', getStyle("openIcon"));
                }
                else {
                    _headerButton.setStyle('icon', getStyle("closeIcon"));
                }

                _openedChanged = false;
            }
        }

        /**
         * @private
         */
        override protected function updateDisplayList(w:Number, h:Number):void {
            super.updateDisplayList(w, h);

            if(_headerLocation == "top") {
            	_headerButton.move(0,0);
            }
            else if(_headerLocation == "bottom") {
            	_headerButton.move(0,h - _headerButton.getExplicitOrMeasuredHeight());
            }

			_headerButton.setActualSize(w, _headerButton.getExplicitOrMeasuredHeight());
        }

		/**
		 * @private
		 */
		private var _viewMetrics:EdgeMetrics;

		override public function get viewMetrics():EdgeMetrics
    	{
    		// The getViewMetrics function needs to return its own object.
	        // Rather than allocating a new one each time, we'll allocate
	        // one once and then hold a pointer to it.
	        if (!_viewMetrics)
	            _viewMetrics = new EdgeMetrics(0, 0, 0, 0);

	        var vm:EdgeMetrics = _viewMetrics;

	        var o:EdgeMetrics = super.viewMetrics;

	        vm.left = o.left;
	        vm.top = o.top;
	        vm.right = o.right;
	        vm.bottom = o.bottom;

	        var hHeight:Number = _headerButton.getExplicitOrMeasuredHeight();
	        if (!isNaN(hHeight)) {
	        	if(_headerLocation == "top") {
	        		 vm.top += hHeight;
	        	}
	        	else if(_headerLocation == "bottom") {
	        		 vm.bottom += hHeight;
	        	}
	        }

	        return vm;
    	}

        /**
         * @private
         */
        override protected function measure():void {
            super.measure();

            if(_opened) {
            	//if this WindowShade is opened then we have to include the height of the header button
                //measuredHeight += _headerButton.getExplicitOrMeasuredHeight();
            }
            else {
            	//if the WindowShade is closed then the height is only the height of the header button
            	measuredHeight = _headerButton.getExplicitOrMeasuredHeight();
            }
        }

		/**
		 * @private
		 */
		private var resize:Resize;

        /**
         * @private
         */
        private var resizeInstance:ResizeInstance;

        /**
         * @private
         */
        private var resetExplicitHeight:Boolean;

        /**
         * @private
         */
        protected function runResizeEffect():void {
			if(resize && resize.isPlaying) {
                // before the call to end() returns, the onEffectEnd method will have been called
                // for the currently playing resize.
				resize.end();
			}

            var duration:Number = _opened ? getStyle("openDuration") : getStyle("closeDuration");
            if(duration == 0) {
            	this.setActualSize(getExplicitOrMeasuredWidth(), measuredHeight);

            	invalidateSize();
            	invalidateDisplayList();

            	return;
            }

            resize = new Resize(this);

            // If this WindowShade currently has no explicit height set, we want to
            // restore that state when the resize effect is finished, in the onEffectEnd method.
            // If it does, then the final height set by the effect will be retained.
            resetExplicitHeight = isNaN(explicitHeight);

            resize.heightTo = Math.min(maxHeight, measuredHeight);

            resize.duration = duration;

            var instances:Array = resize.play();
            if(instances && instances.length) {
                resizeInstance = instances[0];
            }
        }

        /**
         * @private
         */
        protected function onEffectEnd(evt:EffectEvent):void {
            // Make sure this is our effect ending
            if(evt.effectInstance == resizeInstance) {
                if(resetExplicitHeight) explicitHeight = NaN;
                resizeInstance = null;
            }
        }

        /**
         * @private
         */
        protected function headerButton_clickHandler(event:MouseEvent):void {
            opened = !_opened;
        }
    }
}

/*
Copyright (c) 2007 FlexLib Contributors.  See:
    http://code.google.com/p/flexlib/wiki/ProjectContributors

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 flexlib.containers {
	import flash.events.MouseEvent;

	import mx.controls.Button;
	import mx.core.EdgeMetrics;
	import mx.core.IFactory;
	import mx.core.LayoutContainer;
	import mx.core.ScrollPolicy;
	import mx.effects.Resize;
    import mx.effects.effectClasses.ResizeInstance;
    import mx.events.EffectEvent;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	import mx.utils.StringUtil;

    /**
     * This is the icon displayed on the headerButton when the WindowShade is in the open state.
     */
    [Style(name="openIcon", type="Class", inherit="no")]

    /**
     * This is the icon displayed on the headerButton when the WindowShade is in the closed state.
     */
    [Style(name="closeIcon", type="Class", inherit="no")]

    /**
     * The duration of the WindowShade opening transition, in milliseconds. The value 0 specifies no transition.
     *
     * @default 250
     */
    [Style(name="openDuration", type="Number", format="Time", inherit="no")]

    /**
     * The duration of the WindowShade closing transition, in milliseconds. The value 0 specifies no transition.
     *
     * @default 250
     */
    [Style(name="closeDuration", type="Number", format="Time", inherit="no")]

    /**
     * The class from which the headerButton will be instantiated. Must be mx.controls.Button
     * or a subclass.
     *
     * @default mx.controls.Button
     */
    [Style(name="headerClass", type="Class", inherit="no")]

    /**
     * Name of CSS style declaration that specifies styles for the headerButton.
     */
    [Style(name="headerStyleName", type="String", inherit="no")]

    /**
     * Alignment of text on the headerButton. The value set for this style is used as
     * the textAlign style on the headerButton. Valid values are "left", "center" and "right".
     *
     * @default "right"
     */
     [Style(name="headerTextAlign", type="String", inherit="no")]

     /**
      * If true, the value of the headerButton's toggle property will be set to true;
      * otherwise the toggle property will be left in its default state.
      *
      * @default false
      */
     [Style(name="toggleHeader", type="Boolean", inherit="no")]

    /**
     * This control displays a button, which when clicked, will cause a panel to "unroll" beneath
     * it like a windowshade being pulled down; or if the panel is already displayed it
     * will be "rolled up" like a windowshade being rolled up. When multiple WindowShades are stacked
     * in a VBox, the result will be similar to an mx.containers.Accordian container, except that multiple
     * WindowShades can be opened simultaneously whereas an Accordian acts like a tab navigator, with only
     * one panel visible at a time.
     */
    public class WindowShade extends LayoutContainer {

		[Embed (source="../assets/assets.swf", symbol="right_arrow")]
		private static var DEFAULT_CLOSE_ICON:Class;

		[Embed (source="../assets/assets.swf", symbol="down_arrow")]
		private static var DEFAULT_OPEN_ICON:Class;

        private static var styleDefaults:Object = {
             openDuration:250
            ,closeDuration:250
            ,paddingTop:10
            ,headerClass:Button
            ,headerTextAlign:"left"
            ,toggleHeader:false
            ,headerStyleName:null
            ,closeIcon:DEFAULT_CLOSE_ICON
            ,openIcon:DEFAULT_OPEN_ICON
        };

        private static var classConstructed:Boolean = constructClass();

        private static function constructClass():Boolean {

            var css:CSSStyleDeclaration = StyleManager.getStyleDeclaration("WindowShade")
            var changed:Boolean = false;
            if(!css) {
                // If there is no CSS definition for WindowShade,
                // then create one and set the default value.
                css = new CSSStyleDeclaration();
                changed = true;
            }

            // make sure we have a valid values for each style. If not, set the defaults.
            for(var styleProp:String in styleDefaults) {
                if(!StyleManager.isValidStyleValue(css.getStyle(styleProp))) {
                    css.setStyle(styleProp, styleDefaults[styleProp]);
                    changed = true;
                }
            }

            if(changed) {
                StyleManager.setStyleDeclaration("WindowShade", css, true);
            }

            return true;
        }

		/**
		 * @private
		 * A reference to the Button that will be used for the header. Must always be a Button or subclass of Button.
		 */
        private var _headerButton:Button = null;

        private var headerChanged:Boolean;

        /**
        * @private
        * The header renderer factory that will get used to create the header.
        */
        private var _headerRenderer:IFactory;

        /**
        * To control the header used on the WindowShade component you can either set the headerClass or the
        * headerRenderer. The headerRenderer works similar to the itemRenderer of a List control.
        * You can set this using MXML using any Button control. This would let you customize things like button skin. You could
        * even combine this with the CanvasButton component to make complex headers.
        */
        public function set headerRenderer(value:IFactory):void {
        	_headerRenderer = value;

        	headerChanged = true;
        	invalidateProperties();
        }

        public function get headerRenderer():IFactory {
        	return _headerRenderer;
        }

        /**
        * @private
        * Boolean dirty flag to let us know if we need to change the icon in the commitProperties method.
        */
        private var _openedChanged:Boolean = false;

        public function WindowShade() {
            super();

            //default scroll policies are off
            this.verticalScrollPolicy = ScrollPolicy.OFF;
            this.horizontalScrollPolicy = ScrollPolicy.OFF;

            addEventListener(EffectEvent.EFFECT_END, onEffectEnd);
        }

        protected function createOrReplaceHeaderButton():void {
           if(_headerButton) {
                _headerButton.removeEventListener(MouseEvent.CLICK, headerButton_clickHandler);

                if(rawChildren.contains(_headerButton)) {
                    rawChildren.removeChild(_headerButton);
                }
            }

            if(_headerRenderer) {
            	_headerButton = _headerRenderer.newInstance() as Button;
            }
            else {
            	var headerClass:Class = getStyle("headerClass");
          	 	_headerButton = new headerClass();
            }

            applyHeaderButtonStyles(_headerButton);

            _headerButton.addEventListener(MouseEvent.CLICK, headerButton_clickHandler);

            rawChildren.addChild(_headerButton);

            // Fix for Issue #85
            _headerButton.tabEnabled = false;
        }

        protected function applyHeaderButtonStyles(button:Button):void {
            button.setStyle("textAlign", getStyle("headerTextAlign"));

            var headerStyleName:String = getStyle("headerStyleName");
            if(headerStyleName) {
            	headerStyleName = StringUtil.trim(headerStyleName);
            	button.styleName = headerStyleName;
            }

            button.toggle = getStyle("toggleHeader");
            button.label = label;

            if(_opened) {
                button.setStyle('icon', getStyle("openIcon"));
            }
            else {
                button.setStyle('icon', getStyle("closeIcon"));
            }

            if(button.toggle) {
                button.selected = _opened;
            }
        }

        /**
         * @private
         */
        override public function set label(value:String):void {
            super.label = value;

            if(_headerButton) _headerButton.label = value;
        }

		/**
		 * @private
		 */
		private var _opened:Boolean = true;

        /**
         * Sets or gets the state of this WindowShade, either opened (true) or closed (false).
         */
        public function get opened():Boolean {
            return _opened;
        }

        private var _headerLocation:String = "top";

		[Bindable]
		[Inspectable(enumeration="top,bottom", defaultValue="top")]
		/**
		 * Specifies where the header button is placed relative tot he content of this WindowShade. Possible
		 * values are top and bottom.
		 */
		public function set headerLocation(value:String):void {
			_headerLocation = value;
			invalidateSize();
			invalidateDisplayList();
		}

		public function get headerLocation():String {
			return _headerLocation;
		}

        /**
         * @private
         */
         [Bindable]
        public function set opened(value:Boolean):void {
            var old:Boolean = _opened;

            _opened = value;
            _openedChanged = _openedChanged || old != _opened;

            if(_openedChanged && initialized) {
                measure();
               	runResizeEffect();

                invalidateProperties();
            }
        }

        /**
         * @private
         */
        override public function styleChanged(styleProp:String):void {
            super.styleChanged(styleProp);

            if(styleProp == "headerClass") {
                headerChanged = true;
                invalidateProperties();
            }
            else if(styleProp == "headerStyleName" || styleProp == "headerTextAlign" || styleProp == "toggleHeader"
            	|| styleProp == "openIcon" || styleProp == "closeIcon") {
                applyHeaderButtonStyles(_headerButton);
            }

            invalidateDisplayList();
        }

        /**
         * @private
         */
        override protected function createChildren():void {
            super.createChildren();

            createOrReplaceHeaderButton();
        }

        /**
         * @private
         */
        override protected function commitProperties():void {

			super.commitProperties();

			if(headerChanged) {
				createOrReplaceHeaderButton();
				headerChanged = false;
			}

            if(_openedChanged) {

                if(_opened) {
                    _headerButton.setStyle('icon', getStyle("openIcon"));
                }
                else {
                    _headerButton.setStyle('icon', getStyle("closeIcon"));
                }

                _openedChanged = false;
            }
        }

        /**
         * @private
         */
        override protected function updateDisplayList(w:Number, h:Number):void {
            super.updateDisplayList(w, h);

            if(_headerLocation == "top") {
            	_headerButton.move(0,0);
            }
            else if(_headerLocation == "bottom") {
            	_headerButton.move(0,h - _headerButton.getExplicitOrMeasuredHeight());
            }

			_headerButton.setActualSize(w, _headerButton.getExplicitOrMeasuredHeight());
        }

		/**
		 * @private
		 */
		private var _viewMetrics:EdgeMetrics;

		override public function get viewMetrics():EdgeMetrics
    	{
    		// The getViewMetrics function needs to return its own object.
	        // Rather than allocating a new one each time, we'll allocate
	        // one once and then hold a pointer to it.
	        if (!_viewMetrics)
	            _viewMetrics = new EdgeMetrics(0, 0, 0, 0);

	        var vm:EdgeMetrics = _viewMetrics;

	        var o:EdgeMetrics = super.viewMetrics;

	        vm.left = o.left;
	        vm.top = o.top;
	        vm.right = o.right;
	        vm.bottom = o.bottom;

	        var hHeight:Number = _headerButton.getExplicitOrMeasuredHeight();
	        if (!isNaN(hHeight)) {
	        	if(_headerLocation == "top") {
	        		 vm.top += hHeight;
	        	}
	        	else if(_headerLocation == "bottom") {
	        		 vm.bottom += hHeight;
	        	}
	        }

	        return vm;
    	}

        /**
         * @private
         */
        override protected function measure():void {
            super.measure();

            if(_opened) {
            	//if this WindowShade is opened then we have to include the height of the header button
                //measuredHeight += _headerButton.getExplicitOrMeasuredHeight();
            }
            else {
            	//if the WindowShade is closed then the height is only the height of the header button
            	measuredHeight = _headerButton.getExplicitOrMeasuredHeight();
            }
        }

		/**
		 * @private
		 */
		private var resize:Resize;

        /**
         * @private
         */
        private var resizeInstance:ResizeInstance;

        /**
         * @private
         */
        private var resetExplicitHeight:Boolean;

        /**
         * @private
         */
        protected function runResizeEffect():void {
			if(resize && resize.isPlaying) {
                // before the call to end() returns, the onEffectEnd method will have been called
                // for the currently playing resize.
				resize.end();
			}

            var duration:Number = _opened ? getStyle("openDuration") : getStyle("closeDuration");
            if(duration == 0) {
            	this.setActualSize(getExplicitOrMeasuredWidth(), measuredHeight);

            	invalidateSize();
            	invalidateDisplayList();

            	return;
            }

            resize = new Resize(this);

            // If this WindowShade currently has no explicit height set, we want to
            // restore that state when the resize effect is finished, in the onEffectEnd method.
            // If it does, then the final height set by the effect will be retained.
            resetExplicitHeight = isNaN(explicitHeight);

            resize.heightTo = Math.min(maxHeight, measuredHeight);

            resize.duration = duration;

            var instances:Array = resize.play();
            if(instances && instances.length) {
                resizeInstance = instances[0];
            }
        }

        /**
         * @private
         */
        protected function onEffectEnd(evt:EffectEvent):void {
            // Make sure this is our effect ending
            if(evt.effectInstance == resizeInstance) {
                if(resetExplicitHeight) explicitHeight = NaN;
                resizeInstance = null;
            }
        }

        /**
         * @private
         */
        protected function headerButton_clickHandler(event:MouseEvent):void {
            opened = !_opened;
        }
    }
}

Fix for Issue #85.

Fix for Issue #85.


/**
 * User: MerlinDS
 * Date: 16.07.2014
 * Time: 22:22
 */
package com.merlinds.miracle_tool.controllers.converters {
	import com.merlinds.debug.log;
	import com.merlinds.miracle_tool.events.ActionEvent;
	import com.merlinds.miracle_tool.events.EditorEvent;
	import com.merlinds.miracle_tool.models.ProjectModel;
	import com.merlinds.miracle_tool.models.vo.AnimSourcesVO;
	import com.merlinds.miracle_tool.models.vo.AnimationVO;
	import com.merlinds.miracle_tool.models.vo.FrameVO;
	import com.merlinds.miracle_tool.models.vo.SourceVO;
	import com.merlinds.miracle_tool.models.vo.TimelineVO;
	import com.merlinds.miracle_tool.services.ActionService;
	import com.merlinds.miracle_tool.utils.XMLConverters;

	import flash.debugger.enterDebugger;

	import flash.geom.Matrix;
	import flash.geom.Point;

	import org.robotlegs.mvcs.Command;

	public class AnimationConverterCommand extends Command {

		[Inject]
		public var projectModel:ProjectModel;
		[Inject]
		public var actionService:ActionService;
		[Inject]
		public var event:ActionEvent;

		private var _inputLayers:Array;
		private var _animation:AnimationVO;

		private var _namespace:Namespace;
		//==============================================================================
		//{region							PUBLIC METHODS
		public function AnimationConverterCommand() {
			super();
		}

		override public function execute():void {
			log(this, "execute");
			//search for animation in file
			var data:AnimSourcesVO = this.event.body as AnimSourcesVO;
			var source:SourceVO = this.projectModel.selected;
			if(source == null)source = this.projectModel.inProgress;
			var n:int = source.animations.length;
			for(var i:int = 0; i < n; i++){
				var animation:AnimationVO = source.animations[i];
				for(var sourceName:String in data){
					if(animation.name + '.xml' == sourceName){
						_animation = animation;
						_animation.name = animation.name;
						_animation.file = this.projectModel.tempFile;
						_animation.added = true;
						this.prepareData4Animation(data[sourceName] as XML);
						this.convert2Animation();
					}
				}
			}
			this.dispatch(new EditorEvent(EditorEvent.ANIMATION_ATTACHED, source.name));
		}

		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		private function prepareData4Animation(xml:XML):void {
			_namespace = new Namespace(xml.inScopeNamespaces()[0],
					xml.inScopeNamespaces()[1]);
			default xml namespace = _namespace;
			xml.normalize();
			var layers:XMLList = xml.timeline.DOMTimeline.layers.children();
			var n:int = layers.length();
			//get all layers and prepare for converting
			_inputLayers = [];
			for(var i:int = 0; i < n; i++){
				var layer:XML = layers[i];
				if(layer.@layerType != "folder"){
					//exclude Folders from list
					_inputLayers = _inputLayers.concat( this.readSourceLayer(layer) );
				}
			}
		}

		private function convert2Animation():void {
			var n:int = _inputLayers.length;
			_animation.timelines.length = n;
			for(var i:int = 0; i < n; i++){
				var layer:Array = _inputLayers[i];
				var timeline:TimelineVO = new TimelineVO();
				var m:int = layer.length;
				timeline.frames.length = m;
				for(var j:int = 0; j < m; j++){
					timeline.frames[j] = layer[j];
				}
				_animation.timelines[i] = timeline;
			}
		}

		private function readSourceLayer(layer:XML):Array {
			default xml namespace =  _namespace;
			/*
			 * This array will contains layer that was separated from current source layer
			 * Will contains minimum one layer.
			 * In case when source layer contains more than one symbol,
			 * this layer will be separated to N output layers, where N - count of symbols on source layer
			 */
			//get all frames of the source layer
			var frames:XMLList = layer.frames.DOMFrame;
			var separatedLayer:Array = this.createEmptyLayers(frames);
			var m:int = separatedLayer.length;
			var n:int = frames.length();
			for(var i:int = 0; i < n; i++){
				var frame:XML = frames[i];
				//read all symbols on the frame
				var symbols:XMLList = frame.elements.DOMSymbolInstance;
				var o:int = symbols.length();//number of symbols
				for(var j:int = 0; j < m; j++){
					var frameVO:FrameVO = new FrameVO(frame.@index, frame.@duration);
					if(j < o){
						var symbol:XML = symbols[j];
						//convert symbol to frame object and save it to sublayer
						frameVO.type = frame.@tweenType;
						this.parseSymbols(symbol, frameVO);
					}
					separatedLayer[j][i] = frameVO;
				}
			}
			return separatedLayer;
		}

		private function createEmptyLayers(frames:XMLList):Array {
			default xml namespace =  _namespace;
			var separatedLayer:Array = [];
			var n:int = frames.length();
			for(var i:int = 0; i < n; i++){
				var frame:XML = frames[i];
				//read all symbols on the frame
				var symbols:XMLList = frame.elements.DOMSymbolInstance;
				var m:int = symbols.length();
				for(var j:int = 0; j < m; j++){
					if(j + 1 > separatedLayer.length){//Sublayer does not exit in separatedLayer
						separatedLayer[j] = [];
						separatedLayer[j].length = n;
					}
				}
			}
			return separatedLayer;
		}

		private function parseSymbols(elements:XML, frameVO:FrameVO):void {
			default xml namespace =  _namespace;

			var n:int = elements.length();
			for(var i:int = 0; i < n; i++) {
				var element:XML = elements[i];
				frameVO.name = element.@libraryItemName;
				//get element matrix
				frameVO.matrix = XMLConverters.convertToObject(element.matrix.Matrix, Matrix);
				frameVO.transformationPoint = XMLConverters.convertToObject(
						element.transformationPoint.Point, Point);
				frameVO.color = new XMLColorConverter(element.color.Color);
			}
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

/**
 * User: MerlinDS
 * Date: 16.07.2014
 * Time: 22:22
 */
package com.merlinds.miracle_tool.controllers.converters {
	import com.merlinds.debug.log;
	import com.merlinds.miracle_tool.events.ActionEvent;
	import com.merlinds.miracle_tool.events.EditorEvent;
	import com.merlinds.miracle_tool.models.ProjectModel;
	import com.merlinds.miracle_tool.models.vo.AnimSourcesVO;
	import com.merlinds.miracle_tool.models.vo.AnimationVO;
	import com.merlinds.miracle_tool.models.vo.FrameVO;
	import com.merlinds.miracle_tool.models.vo.SourceVO;
	import com.merlinds.miracle_tool.models.vo.TimelineVO;
	import com.merlinds.miracle_tool.services.ActionService;
	import com.merlinds.miracle_tool.utils.XMLConverters;

	import flash.geom.Matrix;
	import flash.geom.Point;

	import org.robotlegs.mvcs.Command;

	public class AnimationConverterCommand extends Command {

		[Inject]
		public var projectModel:ProjectModel;
		[Inject]
		public var actionService:ActionService;
		[Inject]
		public var event:ActionEvent;

		private var _inputLayers:Array;
		private var _animation:AnimationVO;

		private var _namespace:Namespace;
		//==============================================================================
		//{region							PUBLIC METHODS
		public function AnimationConverterCommand() {
			super();
		}

		override public function execute():void {
			log(this, "execute");
			//search for animation in file
			var data:AnimSourcesVO = this.event.body as AnimSourcesVO;
			var source:SourceVO = this.projectModel.selected;
			if(source == null)source = this.projectModel.inProgress;
			var n:int = source.animations.length;
			for(var i:int = 0; i < n; i++){
				var animation:AnimationVO = source.animations[i];
				for(var sourceName:String in data){
					if(animation.name + '.xml' == sourceName){
						_animation = animation;
						_animation.name = animation.name;
						_animation.file = this.projectModel.tempFile;
						_animation.added = true;
						this.prepareData4Animation(data[sourceName] as XML);
						this.convert2Animation();
					}
				}
			}
			this.dispatch(new EditorEvent(EditorEvent.ANIMATION_ATTACHED, source.name));
		}

		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		private function prepareData4Animation(xml:XML):void {
			_namespace = new Namespace(xml.inScopeNamespaces()[0],
					xml.inScopeNamespaces()[1]);
			default xml namespace = _namespace;
			xml.normalize();
			var layers:XMLList = xml.timeline.DOMTimeline.layers.children();
			var n:int = layers.length();
			//get all layers and prepare for converting
			_inputLayers = [];
			for(var i:int = 0; i < n; i++){
				var layer:XML = layers[i];
				if(layer.@layerType != "folder"){
					//exclude Folders from list
					_inputLayers = _inputLayers.concat( this.readSourceLayer(layer) );
				}
			}
		}

		private function convert2Animation():void {
			var n:int = _inputLayers.length;
			_animation.timelines.length = n;
			for(var i:int = 0; i < n; i++){
				var layer:Array = _inputLayers[i];
				var timeline:TimelineVO = new TimelineVO();
				var m:int = layer.length;
				timeline.frames.length = m;
				for(var j:int = 0; j < m; j++){
					timeline.frames[j] = layer[j];
				}
				_animation.timelines[i] = timeline;
			}
		}

		private function readSourceLayer(layer:XML):Array {
			default xml namespace =  _namespace;
			/*
			 * This array will contains layer that was separated from current source layer
			 * Will contains minimum one layer.
			 * In case when source layer contains more than one symbol,
			 * this layer will be separated to N output layers, where N - count of symbols on source layer
			 */
			//get all frames of the source layer
			var frames:XMLList = layer.frames.DOMFrame;
			var separatedLayer:Array = this.createEmptyLayers(frames);
			var m:int = separatedLayer.length;
			var n:int = frames.length();
			for(var i:int = 0; i < n; i++){
				var frame:XML = frames[i];
				//read all symbols on the frame
				var symbols:XMLList = frame.elements.DOMSymbolInstance;
				var o:int = symbols.length();//number of symbols
				for(var j:int = 0; j < m; j++){
					var frameVO:FrameVO = new FrameVO(frame.@index, frame.@duration);
					if(j < o){
						var symbol:XML = symbols[j];
						//convert symbol to frame object and save it to sublayer
						frameVO.type = frame.@tweenType;
						this.parseSymbols(symbol, frameVO);
					}
					separatedLayer[j][i] = frameVO;
				}
			}
			return separatedLayer;
		}

		private function createEmptyLayers(frames:XMLList):Array {
			default xml namespace =  _namespace;
			var separatedLayer:Array = [];
			var n:int = frames.length();
			for(var i:int = 0; i < n; i++){
				var frame:XML = frames[i];
				//read all symbols on the frame
				var symbols:XMLList = frame.elements.DOMSymbolInstance;
				var m:int = symbols.length();
				for(var j:int = 0; j < m; j++){
					if(j + 1 > separatedLayer.length){//Sublayer does not exit in separatedLayer
						separatedLayer[j] = [];
						separatedLayer[j].length = n;
					}
				}
			}
			return separatedLayer;
		}

		private function parseSymbols(elements:XML, frameVO:FrameVO):void {
			default xml namespace =  _namespace;

			var n:int = elements.length();
			for(var i:int = 0; i < n; i++) {
				var element:XML = elements[i];
				frameVO.name = element.@libraryItemName;
				//get element matrix
				frameVO.matrix = XMLConverters.convertToObject(element.matrix.Matrix, Matrix);
				frameVO.transformationPoint = XMLConverters.convertToObject(
						element.transformationPoint.Point, Point);
				frameVO.color = new XMLColorConverter(element.color.Color);
			}
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

remove excess imports

remove excess imports


package dolly {
import dolly.core.dolly_internal;
import dolly.data.CompositeCloneableClass;

import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.hamcrest.assertThat;
import org.hamcrest.collection.array;
import org.hamcrest.collection.arrayWithSize;
import org.hamcrest.collection.everyItem;
import org.hamcrest.core.isA;
import org.hamcrest.object.equalTo;

use namespace dolly_internal;

public class CloningOfCompositeCloneableClassTest {

	private var compositeCloneableClass:CompositeCloneableClass;
	private var compositeCloneableClassType:Type;

	[Before]
	public function before():void {
		compositeCloneableClass = new CompositeCloneableClass();

		compositeCloneableClassType = Type.forInstance(compositeCloneableClass);
	}

	[After]
	public function after():void {
		compositeCloneableClass = null;
		compositeCloneableClassType = null;
	}

	[Test]
	public function findingAllWritableFieldsForType():void {
		const writableFields:Vector.<Field> = Cloner.findAllWritableFieldsForType(compositeCloneableClassType);
		assertNotNull(writableFields);
		assertEquals(4, writableFields.length);
	}

	[Test]
	public function cloningOfArray():void {
		const clone:CompositeCloneableClass = Cloner.clone(compositeCloneableClass);

		assertNotNull(clone.array);
		assertThat(clone.array, arrayWithSize(5));
		assertThat(clone.array, compositeCloneableClass.array);
		assertFalse(clone.array == compositeCloneableClass.array);
		assertThat(clone.array, everyItem(isA(Number)));
		assertThat(clone.array, array(equalTo(0), equalTo(1), equalTo(2), equalTo(3), equalTo(4)));
	}
}
}

package dolly {
import dolly.core.dolly_internal;
import dolly.data.CompositeCloneableClass;

import mx.collections.ArrayCollection;
import mx.collections.ArrayList;

import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.hamcrest.assertThat;
import org.hamcrest.collection.array;
import org.hamcrest.collection.arrayWithSize;
import org.hamcrest.collection.everyItem;
import org.hamcrest.core.isA;
import org.hamcrest.object.equalTo;

use namespace dolly_internal;

public class CloningOfCompositeCloneableClassTest {

	private var compositeCloneableClass:CompositeCloneableClass;
	private var compositeCloneableClassType:Type;

	[Before]
	public function before():void {
		compositeCloneableClass = new CompositeCloneableClass();
		compositeCloneableClass.array = [1, 2, 3, 4, 5];
		compositeCloneableClass.arrayList = new ArrayList([1, 2, 3, 4, 5]);
		compositeCloneableClass.arrayCollection = new ArrayCollection([1, 2, 3, 4, 5]);

		compositeCloneableClassType = Type.forInstance(compositeCloneableClass);
	}

	[After]
	public function after():void {
		compositeCloneableClass = null;
		compositeCloneableClassType = null;
	}

	[Test]
	public function findingAllWritableFieldsForType():void {
		const writableFields:Vector.<Field> = Cloner.findAllWritableFieldsForType(compositeCloneableClassType);
		assertNotNull(writableFields);
		assertEquals(4, writableFields.length);
	}

	[Test]
	public function cloningOfArray():void {
		const clone:CompositeCloneableClass = Cloner.clone(compositeCloneableClass);

		assertNotNull(clone.array);
		assertThat(clone.array, arrayWithSize(5));
		assertThat(clone.array, compositeCloneableClass.array);
		assertFalse(clone.array == compositeCloneableClass.array);
		assertThat(clone.array, everyItem(isA(Number)));
		assertThat(clone.array, array(equalTo(1), equalTo(2), equalTo(3), equalTo(4), equalTo(5)));
	}
}
}

Edit values of test objects.

Edit values of test objects.