////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You 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.
//
////////////////////////////////////////////////////////////////////////////////

package
{

internal class MobileThemeClasses
{

/**
 *  @private
 *  This class is used to link additional classes into mobile.swc
 *  beyond those that are found by dependecy analysis starting
 *  from the classes specified in manifest.xml.
 */
import spark.skins.mobile.ActionBarSkin; ActionBarSkin;
import spark.skins.mobile.BeveledActionButtonSkin; BeveledActionButtonSkin;
import spark.skins.mobile.BeveledBackButtonSkin; BeveledBackButtonSkin;
import spark.skins.mobile.ButtonBarSkin; spark.skins.mobile.ButtonBarSkin;
import spark.skins.mobile.ButtonSkin; spark.skins.mobile.ButtonSkin;
import spark.skins.mobile.CalloutSkin; spark.skins.mobile.CalloutSkin;
 import spark.skins.mobile.supportClasses.CalloutArrow;  spark.skins.mobile.supportClasses.CalloutArrow;
    import spark.skins.mobile.CalloutActionBarSkin; spark.skins.mobile.CalloutActionBarSkin;
import spark.skins.mobile.CalloutViewNavigatorSkin; spark.skins.mobile.CalloutViewNavigatorSkin;
import spark.skins.mobile.CheckBoxSkin; spark.skins.mobile.CheckBoxSkin;
import spark.skins.mobile.DefaultBeveledActionButtonSkin; spark.skins.mobile.DefaultBeveledActionButtonSkin;
import spark.skins.mobile.DefaultBeveledBackButtonSkin; spark.skins.mobile.DefaultBeveledBackButtonSkin;
import spark.skins.mobile.DefaultButtonSkin; spark.skins.mobile.DefaultButtonSkin;
import spark.skins.mobile.DefaultTransparentActionButtonSkin; spark.skins.mobile.DefaultTransparentActionButtonSkin;
import spark.skins.mobile.DefaultTransparentNavigationButtonSkin; spark.skins.mobile.DefaultTransparentNavigationButtonSkin;
import spark.skins.mobile.DateSpinnerSkin; spark.skins.mobile.DateSpinnerSkin;
import spark.skins.mobile.HScrollBarSkin; spark.skins.mobile.HScrollBarSkin;
import spark.skins.mobile.HSliderSkin; spark.skins.mobile.HSliderSkin;
import spark.skins.mobile.ImageSkin; spark.skins.mobile.ImageSkin;
import spark.skins.mobile.ListSkin; spark.skins.mobile.ListSkin;
import spark.skins.mobile.RadioButtonSkin; spark.skins.mobile.RadioButtonSkin;
import spark.skins.mobile.SpinnerListContainerSkin; SpinnerListContainerSkin;
import spark.skins.mobile.SpinnerListScrollerSkin; SpinnerListScrollerSkin;
import spark.skins.mobile.SpinnerListSkin; SpinnerListSkin;
import spark.skins.mobile.SkinnableContainerSkin; SkinnableContainerSkin;
import spark.skins.mobile.SplitViewNavigatorSkin; SplitViewNavigatorSkin;
import spark.skins.mobile.StageTextAreaSkin; StageTextAreaSkin;
import spark.skins.mobile.StageTextInputSkin; StageTextInputSkin;
 import spark.skins.mobile.ScrollingStageTextInputSkin; ScrollingStageTextInputSkin;
import spark.skins.mobile.ScrollingStageTextAreaSkin; ScrollingStageTextAreaSkin
import spark.skins.mobile.TabbedViewNavigatorApplicationSkin; TabbedViewNavigatorApplicationSkin;
import spark.skins.mobile.TabbedViewNavigatorSkin; TabbedViewNavigatorSkin;
import spark.skins.mobile.TabbedViewNavigatorTabBarSkin; TabbedViewNavigatorTabBarSkin;
import spark.skins.mobile.TextAreaSkin; TextAreaSkin;
import spark.skins.mobile.TextAreaHScrollBarSkin; TextAreaHScrollBarSkin;
import spark.skins.mobile.TextAreaVScrollBarSkin; TextAreaVScrollBarSkin;
import spark.skins.mobile.TextInputSkin; TextInputSkin;
import spark.skins.mobile.ToggleSwitchSkin; ToggleSwitchSkin;
import spark.skins.mobile.TransparentActionButtonSkin; TransparentActionButtonSkin;
import spark.skins.mobile.TransparentNavigationButtonSkin; TransparentNavigationButtonSkin;
import spark.skins.mobile.ViewMenuItemSkin; ViewMenuItemSkin;
import spark.skins.mobile.ViewMenuSkin; ViewMenuSkin;
import spark.skins.mobile.ViewNavigatorApplicationSkin; ViewNavigatorApplicationSkin;
import spark.skins.mobile.ViewNavigatorSkin; ViewNavigatorSkin;
import spark.skins.mobile.VScrollBarSkin; spark.skins.mobile.VScrollBarSkin;
}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You 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.
//
////////////////////////////////////////////////////////////////////////////////

package
{

	internal class MobileThemeClasses
	{

		/**
		 *  @private
		 *  This class is used to link additional classes into mobile.swc
		 *  beyond those that are found by dependecy analysis starting
		 *  from the classes specified in manifest.xml.
		 */
		import spark.skins.mobile.ActionBarSkin; spark.skins.mobile.ActionBarSkin;
		import spark.skins.mobile.BeveledActionButtonSkin; spark.skins.mobile.BeveledActionButtonSkin;
		import spark.skins.mobile.BeveledBackButtonSkin; spark.skins.mobile.BeveledBackButtonSkin;
		import spark.skins.mobile.ButtonBarSkin; spark.skins.mobile.ButtonBarSkin;
		import spark.skins.mobile.ButtonSkin; spark.skins.mobile.ButtonSkin;
		import spark.skins.mobile.CalloutSkin; spark.skins.mobile.CalloutSkin;
		import spark.skins.mobile.supportClasses.CalloutArrow;  spark.skins.mobile.supportClasses.CalloutArrow;
		import spark.skins.mobile.CalloutActionBarSkin; spark.skins.mobile.CalloutActionBarSkin;
		import spark.skins.mobile.CalloutViewNavigatorSkin; spark.skins.mobile.CalloutViewNavigatorSkin;
		import spark.skins.mobile.CheckBoxSkin; spark.skins.mobile.CheckBoxSkin;
		import spark.skins.mobile.DefaultBeveledActionButtonSkin; spark.skins.mobile.DefaultBeveledActionButtonSkin;
		import spark.skins.mobile.DefaultBeveledBackButtonSkin; spark.skins.mobile.DefaultBeveledBackButtonSkin;
		import spark.skins.mobile.DefaultButtonSkin; spark.skins.mobile.DefaultButtonSkin;
		import spark.skins.mobile.DefaultTransparentActionButtonSkin; spark.skins.mobile.DefaultTransparentActionButtonSkin;
		import spark.skins.mobile.DefaultTransparentNavigationButtonSkin; spark.skins.mobile.DefaultTransparentNavigationButtonSkin;
		import spark.skins.mobile.DateSpinnerSkin; spark.skins.mobile.DateSpinnerSkin;
		import spark.skins.mobile.HScrollBarSkin; spark.skins.mobile.HScrollBarSkin;
		import spark.skins.mobile.HSliderSkin; spark.skins.mobile.HSliderSkin;
		import spark.skins.mobile.ImageSkin; spark.skins.mobile.ImageSkin;
		import spark.skins.mobile.ListSkin; spark.skins.mobile.ListSkin;
		import spark.skins.mobile.RadioButtonSkin; spark.skins.mobile.RadioButtonSkin;
		import spark.skins.mobile.SpinnerListContainerSkin; spark.skins.mobile.SpinnerListContainerSkin;
		import spark.skins.mobile.SpinnerListScrollerSkin; spark.skins.mobile.SpinnerListScrollerSkin;
		import spark.skins.mobile.SpinnerListSkin; spark.skins.mobile.SpinnerListSkin;
		import spark.skins.mobile.SkinnableContainerSkin; spark.skins.mobile.SkinnableContainerSkin;
		import spark.skins.mobile.SplitViewNavigatorSkin; spark.skins.mobile.SplitViewNavigatorSkin;
		import spark.skins.mobile.StageTextAreaSkin; spark.skins.mobile.StageTextAreaSkin;
		import spark.skins.mobile.StageTextInputSkin; spark.skins.mobile.StageTextInputSkin;
		import spark.skins.mobile.ScrollingStageTextInputSkin; spark.skins.mobile.ScrollingStageTextInputSkin;
		import spark.skins.mobile.ScrollingStageTextAreaSkin; spark.skins.mobile.ScrollingStageTextAreaSkin
		import spark.skins.mobile.TabbedViewNavigatorApplicationSkin; spark.skins.mobile.TabbedViewNavigatorApplicationSkin;
		import spark.skins.mobile.TabbedViewNavigatorSkin; spark.skins.mobile.TabbedViewNavigatorSkin;
		import spark.skins.mobile.TabbedViewNavigatorTabBarSkin; spark.skins.mobile.TabbedViewNavigatorTabBarSkin;
		import spark.skins.mobile.TextAreaSkin; spark.skins.mobile.TextAreaSkin;
		import spark.skins.mobile.TextAreaHScrollBarSkin; spark.skins.mobile.TextAreaHScrollBarSkin;
		import spark.skins.mobile.TextAreaVScrollBarSkin; spark.skins.mobile.TextAreaVScrollBarSkin;
		import spark.skins.mobile.TextInputSkin; spark.skins.mobile.TextInputSkin;
		import spark.skins.mobile.ToggleSwitchSkin; spark.skins.mobile.ToggleSwitchSkin;
		import spark.skins.mobile.TransparentActionButtonSkin; spark.skins.mobile.TransparentActionButtonSkin;
		import spark.skins.mobile.TransparentNavigationButtonSkin; spark.skins.mobile.TransparentNavigationButtonSkin;
		import spark.skins.mobile.ViewMenuItemSkin; spark.skins.mobile.ViewMenuItemSkin;
		import spark.skins.mobile.ViewMenuSkin; spark.skins.mobile.ViewMenuSkin;
		import spark.skins.mobile.ViewNavigatorApplicationSkin; spark.skins.mobile.ViewNavigatorApplicationSkin;
		import spark.skins.mobile.ViewNavigatorSkin; spark.skins.mobile.ViewNavigatorSkin;
		import spark.skins.mobile.VScrollBarSkin; spark.skins.mobile.VScrollBarSkin;

		//Android skins
		import spark.skins.android4.ActionBarSkin; spark.skins.android4.ActionBarSkin;
		import spark.skins.android4.BusyIndicatorSkin; spark.skins.android4.BusyIndicatorSkin;
		import spark.skins.android4.ButtonBarFirstButtonSkin; spark.skins.android4.ButtonBarFirstButtonSkin;
		import spark.skins.android4.ButtonBarMiddleButtonSkin; spark.skins.android4.ButtonBarMiddleButtonSkin;
		import spark.skins.android4.ButtonBarSkin; spark.skins.android4.ButtonBarSkin;
		import spark.skins.android4.ButtonSkin; spark.skins.android4.ButtonSkin;
		import spark.skins.android4.CalloutSkin; spark.skins.android4.CalloutSkin;
		import spark.skins.android4.CheckBoxSkin; spark.skins.android4.CheckBoxSkin;
		import spark.skins.android4.HScrollBarSkin; spark.skins.android4.HScrollBarSkin;
		import spark.skins.android4.HScrollBarThumbSkin; spark.skins.android4.HScrollBarThumbSkin;
		import spark.skins.android4.HSliderSkin; spark.skins.android4.HSliderSkin;
		import spark.skins.android4.HSliderThumbSkin; spark.skins.android4.HSliderThumbSkin;
		import spark.skins.android4.HSliderTrackSkin; spark.skins.android4.HSliderTrackSkin;
		import spark.skins.android4.RadioButtonSkin; spark.skins.android4.RadioButtonSkin;
		import spark.skins.android4.SpinnerListContainerSkin; spark.skins.android4.SpinnerListContainerSkin;
		import spark.skins.android4.SpinnerListScrollerSkin; spark.skins.android4.SpinnerListScrollerSkin;
		import spark.skins.android4.SpinnerListSkin; spark.skins.android4.SpinnerListSkin;
		import spark.skins.android4.StageTextAreaSkin; spark.skins.android4.StageTextAreaSkin;
		import spark.skins.android4.StageTextInputSkin; spark.skins.android4.StageTextInputSkin;
		import spark.skins.android4.TabbedViewNavigatorTabBarSkin; spark.skins.android4.TabbedViewNavigatorTabBarSkin;
		import spark.skins.android4.TextAreaSkin; spark.skins.android4.TextAreaSkin;
		import spark.skins.android4.TextInputSkin; spark.skins.android4.TextInputSkin;
		import spark.skins.android4.ToggleSwitchSkin; spark.skins.android4.ToggleSwitchSkin;
		import spark.skins.android4.TransparentActionButtonSkin; spark.skins.android4.TransparentActionButtonSkin;
		import spark.skins.android4.TransparentNavigationButtonSkin; spark.skins.android4.TransparentNavigationButtonSkin;
		import spark.skins.android4.ViewMenuItemSkin; spark.skins.android4.ViewMenuItemSkin;
		import spark.skins.android4.ViewMenuSkin; spark.skins.android4.ViewMenuSkin;
		import spark.skins.android4.VScrollBarSkin; spark.skins.android4.VScrollBarSkin;
		import spark.skins.android4.VScrollBarThumbSkin; spark.skins.android4.VScrollBarThumbSkin;
		import spark.skins.android4.supportClasses.CalloutArrow; spark.skins.android4.supportClasses.CalloutArrow;

	}
}

Include all Android skins so that they will get compiled into mobiletheme.swc


/*
 * Copyright (c) 2009 the original author or authors
 *
 * Permission is hereby granted to use, modify, and distribute this file
 * in accordance with the terms of the license agreement accompanying it.
 */

package org.swiftsuspenders.injectionpoints
{
	import flash.utils.describeType;

	import org.swiftsuspenders.Injector;

	public class ConstructorInjectionPoint extends MethodInjectionPoint
	{
		/*******************************************************************************************
		*								public methods											   *
		*******************************************************************************************/
		public function ConstructorInjectionPoint(node : XML, clazz : Class, injector : Injector)
		{
			/*
			  In many cases, the flash player doesn't give us type information for constructors until
			  the class has been instantiated at least once. Therefore, we do just that if we don't get
			  type information for at least one parameter.
			 */
			if (node.parameter.(@type == '*').length() == node.parameter.@type.length())
			{
				createDummyInstance(node, clazz);
			}
			super(node, injector);
		}

		override public function applyInjection(target : Object, injector : Injector) : Object
		{
			var p : Array = gatherParameterValues(target, injector);
			//the only way to implement ctor injections, really!
			switch (p.length)
			{
				case 0 : return (new target());
				case 1 : return (new target(p[0]));
				case 2 : return (new target(p[0], p[1]));
				case 3 : return (new target(p[0], p[1], p[2]));
				case 4 : return (new target(p[0], p[1], p[2], p[3]));
				case 5 : return (new target(p[0], p[1], p[2], p[3], p[4]));
				case 6 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5]));
				case 7 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6]));
				case 8 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
				case 9 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]));
				case 10 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]));
			}
			return null;
		}

		/*******************************************************************************************
		*								protected methods										   *
		*******************************************************************************************/
		override protected function initializeInjection(node : XML, injector : Injector) : void
		{
			var nameArgs : XMLList = node.parent().metadata.(@name == 'Inject').arg.(@key == 'name');
			methodName = 'constructor';

			gatherParameters(node, nameArgs, injector);
		}

		/*******************************************************************************************
		*								private methods											   *
		*******************************************************************************************/
		private function createDummyInstance(constructorNode : XML, clazz : Class) : void
		{
			try
			{
				switch (constructorNode.children().length())
				{
					case 0 : (new clazz()); break;
					case 1 : (new clazz(null)); break;
					case 2 : (new clazz(null, null)); break;
					case 3 : (new clazz(null, null, null)); break;
					case 4 : (new clazz(null, null, null, null)); break;
					case 5 : (new clazz(null, null, null, null, null)); break;
					case 6 : (new clazz(null, null, null, null, null, null)); break;
					case 7 : (new clazz(null, null, null, null, null, null, null)); break;
					case 8 : (new clazz(null, null, null, null, null, null, null, null)); break;
					case 9 : (new clazz(null, null, null, null, null, null, null, null, null)); break;
					case 10 : (new clazz(null, null, null, null, null, null, null, null, null, null)); break;
				}
			}
			catch (error : Error)
			{
				trace(error);
			}
			constructorNode.setChildren(describeType(clazz).factory.constructor[0].children());
		}
	}
}

/*
 * Copyright (c) 2009 the original author or authors
 *
 * Permission is hereby granted to use, modify, and distribute this file
 * in accordance with the terms of the license agreement accompanying it.
 */

package org.swiftsuspenders.injectionpoints
{
	import flash.utils.describeType;

	import org.swiftsuspenders.Injector;

	public class ConstructorInjectionPoint extends MethodInjectionPoint
	{
		/*******************************************************************************************
		*								public methods											   *
		*******************************************************************************************/
		public function ConstructorInjectionPoint(node : XML, clazz : Class, injector : Injector)
		{
			/*
			  In many cases, the flash player doesn't give us type information for constructors until
			  the class has been instantiated at least once. Therefore, we do just that if we don't get
			  type information for at least one parameter.
			 */
			if (node.parameter.(@type == '*').length() == node.parameter.@type.length())
			{
				createDummyInstance(node, clazz);
			}
			super(node, injector);
		}

		override public function applyInjection(target : Object, injector : Injector) : Object
		{
			var p : Array = gatherParameterValues(target, injector);
			//the only way to implement ctor injections, really!
			switch (p.length)
			{
				case 0 : return (new target());
				case 1 : return (new target(p[0]));
				case 2 : return (new target(p[0], p[1]));
				case 3 : return (new target(p[0], p[1], p[2]));
				case 4 : return (new target(p[0], p[1], p[2], p[3]));
				case 5 : return (new target(p[0], p[1], p[2], p[3], p[4]));
				case 6 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5]));
				case 7 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6]));
				case 8 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
				case 9 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]));
				case 10 : return (new target(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]));
			}
			return null;
		}

		/*******************************************************************************************
		*								protected methods										   *
		*******************************************************************************************/
		override protected function initializeInjection(node : XML, injector : Injector) : void
		{
			var nameArgs : XMLList = node.parent().metadata.(@name == 'Inject').arg.(@key == 'name');
			methodName = 'constructor';

			gatherParameters(node, nameArgs, injector);
		}

		/*******************************************************************************************
		*								private methods											   *
		*******************************************************************************************/
		private function createDummyInstance(constructorNode : XML, clazz : Class) : void
		{
			try
			{
				switch (constructorNode.children().length())
				{
					case 0 : (new clazz()); break;
					case 1 : (new clazz(null)); break;
					case 2 : (new clazz(null, null)); break;
					case 3 : (new clazz(null, null, null)); break;
					case 4 : (new clazz(null, null, null, null)); break;
					case 5 : (new clazz(null, null, null, null, null)); break;
					case 6 : (new clazz(null, null, null, null, null, null)); break;
					case 7 : (new clazz(null, null, null, null, null, null, null)); break;
					case 8 : (new clazz(null, null, null, null, null, null, null, null)); break;
					case 9 : (new clazz(null, null, null, null, null, null, null, null, null)); break;
					case 10 : (new clazz(null, null, null, null, null, null, null, null, null, null)); break;
				}
			}
			catch (error : Error)
			{
				trace('Exception caught while trying to create dummy instance for constructor ' +
						'injection. It\'s almost certainly ok to ignore this exception, but you ' +
						'might want to restructure your constructor to prevent errors from ' +
						'happening. See the SwiftSuspenders documentation for more details. ' +
						'The caught exception was:\n' + error);
			}
			constructorNode.setChildren(describeType(clazz).factory.constructor[0].children());
		}
	}
}

Add meaningful trace to createDummyInstance error handling

Closes #30


/*
 * Copyright (C) 2003-2007
 * Nick Velloff <nick.velloff@gmail.com>
 * Derrick Grigg <dgrigg@rogers.com>
 * Sean Voisen <sean@voisen.org>
 * Sean Treadway <seant@oncotype.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package org.jivesoftware.xiff.core
{
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;

	import mx.logging.ILogger;

	import org.jivesoftware.xiff.events.*;
	import org.jivesoftware.xiff.logging.LoggerFactory;
	import org.jivesoftware.xiff.util.SocketConn;
	import org.jivesoftware.xiff.util.SocketDataEvent;

	/**
	 * A child of XMPPConnection, this class makes use of the
	 * Flash AVM2 binary socket instead of the older XMLSocket.
	 * This gets rid of issues related to the XMLSocket's appending
	 * of a null-byte to all outgoing data.
	 *
	 * @see org.jivesoftware.xiff.core.XMPPConnection
	 */
	public class XMPPSocketConnection extends XMPPConnection
	{
		private static const logger:ILogger = LoggerFactory.getLogger("org.jivesoftware.xiff.core.XMPPSocketConnection");

		private var _incompleteRawXML: String = '';

		protected var binarySocket:SocketConn;

		public function XMPPSocketConnection()
		{
			super();
			configureSocket();
		}

		private function configureSocket():void {
			binarySocket = new SocketConn();

	        binarySocket.addEventListener(Event.CLOSE, socketClosed);
	        binarySocket.addEventListener(Event.CONNECT, socketConnected);
	        binarySocket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
	        binarySocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
	        binarySocket.addEventListener(SocketDataEvent.SOCKET_DATA_RECEIVED, bSocketReceivedData);
	    }

	    override protected function sendXML( someData:* ):void
		{
			logger.info("OUTGOING: {0}", someData);
			// Data is untyped because it could be a string or XML
	        binarySocket.sendString(someData);

			var event:OutgoingDataEvent = new OutgoingDataEvent();
			event.data = someData;
			dispatchEvent( event );
		}

		override public function disconnect():void
		{
			if( isActive() ) {
				sendXML( closingStreamTag );
				binarySocket.close();
				active = false;
				loggedIn = false;
				var event:DisconnectionEvent = new DisconnectionEvent();
				dispatchEvent(event);
			}
		}

		override public function connect( streamType:String = "terminatedStandard" ):Boolean
		{
			active = false;
			loggedIn = false;

			// Stream type lets user set opening/closing tag - some servers (jadc2s) prefer <stream:flash> to the standard
			// <stream:stream>
			switch( streamType ) {
				case "flash":
					openingStreamTag = "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\">";
					closingStreamTag = "</flash:stream>";
					break;

				case "terminatedFlash":
					openingStreamTag = "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\" />";
					closingStreamTag = "</flash:stream>";
					break;

				case "standard":
					openingStreamTag = "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">";
					closingStreamTag = "</stream:stream>";
					break;

				case "terminatedStandard":
				default:
					openingStreamTag = "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" />";
					closingStreamTag = "</stream:stream>";
					break;
			}
			binarySocket.connect( server, port );
			return true;
		}

		protected function bSocketReceivedData( ev:SocketDataEvent ):void
		{
			var rawXML:String = _incompleteRawXML + ev.data as String;

			logger.info("INCOMING: {0}", rawXML);
			// parseXML is more strict in AS3 so we must check for the presence of flash:stream
			// the unterminated tag should be in the first string of xml data retured from the server
			if (!_expireTagSearch)
			{
				var pattern:RegExp = new RegExp("<flash:stream");
				var resultObj:Object = pattern.exec(rawXML);
				if (resultObj != null) // stop searching for unterminated node
				{
					rawXML = rawXML.concat("</flash:stream>");
					_expireTagSearch = true;
				}
			}

			if (!_expireTagSearch)
			{
				var pattern2:RegExp = new RegExp("<stream:stream");
				var resultObj2:Object = pattern2.exec(rawXML);
				if (resultObj2 != null) // stop searching for unterminated node
				{
					rawXML = rawXML.concat("</stream:stream>");
					_expireTagSearch = true;
				}
			}

			var xmlData:XMLDocument = new XMLDocument();
			xmlData.ignoreWhite = this.ignoreWhite;

			//error handling to catch incomplete xml strings that have
			//been truncated by the socket
			try{
				var isComplete: Boolean = true;
				xmlData.parseXML( rawXML );
				_incompleteRawXML = '';
			}
			catch(err:Error){
				isComplete = false;
				_incompleteRawXML += ev.data as String;//concatenate the raw xml to the previous xml
			}

			if (isComplete){

				var event:IncomingDataEvent = new IncomingDataEvent();
				event.data = xmlData;
				dispatchEvent( event );

				for (var i:int = 0; i<xmlData.childNodes.length; i++)
				{
					// Read the data and send it to the appropriate parser
					var currentNode:XMLNode = xmlData.childNodes[i];
					var nodeName:String = currentNode.nodeName.toLowerCase();

					switch( nodeName )
					{
						case "stream:stream":
							_expireTagSearch = false;
							handleStream( currentNode );
							break;
						case "flash:stream":
							_expireTagSearch = false;
							handleStream( currentNode );
							break;

						case "stream:error":
							handleStreamError( currentNode );
							break;

						case "iq":
							handleIQ( currentNode );
							break;

						case "message":
							handleMessage( currentNode );
							break;

						case "presence":
							handlePresence( currentNode );
							break;

						default:
							dispatchError( "undefined-condition", "Unknown Error", "modify", 500 );
							break;
					}
				}
			}
		}
	}
}

/*
 * Copyright (C) 2003-2007
 * Nick Velloff <nick.velloff@gmail.com>
 * Derrick Grigg <dgrigg@rogers.com>
 * Sean Voisen <sean@voisen.org>
 * Sean Treadway <seant@oncotype.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package org.jivesoftware.xiff.core
{
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;

	import mx.logging.ILogger;

	import org.jivesoftware.xiff.events.*;
	import org.jivesoftware.xiff.logging.LoggerFactory;
	import org.jivesoftware.xiff.util.SocketConn;
	import org.jivesoftware.xiff.util.SocketDataEvent;

	/**
	 * A child of XMPPConnection, this class makes use of the
	 * Flash AVM2 binary socket instead of the older XMLSocket.
	 * This gets rid of issues related to the XMLSocket's appending
	 * of a null-byte to all outgoing data.
	 *
	 * @see org.jivesoftware.xiff.core.XMPPConnection
	 */
	public class XMPPSocketConnection extends XMPPConnection
	{
		private static const logger:ILogger = LoggerFactory.getLogger("org.jivesoftware.xiff.core.XMPPSocketConnection");

		private var _incompleteRawXML: String = '';

		protected var binarySocket:SocketConn;

		public function XMPPSocketConnection()
		{
			super();
			configureSocket();
		}

		private function configureSocket():void {
			binarySocket = new SocketConn();

	        binarySocket.addEventListener(Event.CLOSE, socketClosed);
	        binarySocket.addEventListener(Event.CONNECT, socketConnected);
	        binarySocket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
	        binarySocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
	        binarySocket.addEventListener(SocketDataEvent.SOCKET_DATA_RECEIVED, bSocketReceivedData);
	    }

	    override protected function sendXML( someData:* ):void
		{
			logger.info("OUTGOING: {0}", someData);
			// Data is untyped because it could be a string or XML
	        binarySocket.sendString(someData);

			var event:OutgoingDataEvent = new OutgoingDataEvent();
			event.data = someData;
			dispatchEvent( event );
		}

		override public function disconnect():void
		{
			if( isActive() ) {
				sendXML( closingStreamTag );
				binarySocket.close();
				active = false;
				loggedIn = false;
				var event:DisconnectionEvent = new DisconnectionEvent();
				dispatchEvent(event);
			}
		}

		override public function connect( streamType:String = "terminatedStandard" ):Boolean
		{
			active = false;
			loggedIn = false;

			// Stream type lets user set opening/closing tag - some servers (jadc2s) prefer <stream:flash> to the standard
			// <stream:stream>
			switch( streamType ) {
				case "flash":
					openingStreamTag = "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\">";
					closingStreamTag = "</flash:stream>";
					break;

				case "terminatedFlash":
					openingStreamTag = "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\" />";
					closingStreamTag = "</flash:stream>";
					break;

				case "standard":
					openingStreamTag = "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">";
					closingStreamTag = "</stream:stream>";
					break;

				case "terminatedStandard":
				default:
					openingStreamTag = "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" />";
					closingStreamTag = "</stream:stream>";
					break;
			}
			binarySocket.connect( server, port );
			return true;
		}

		protected function bSocketReceivedData( ev:SocketDataEvent ):void
		{
			var rawXML:String = _incompleteRawXML + ev.data as String;

			logger.info("INCOMING: {0}", rawXML);
			// parseXML is more strict in AS3 so we must check for the presence of flash:stream
			// the unterminated tag should be in the first string of xml data retured from the server
			if (!_expireTagSearch)
			{
				var pattern:RegExp = new RegExp("<flash:stream");
				var resultObj:Object = pattern.exec(rawXML);
				if (resultObj != null) // stop searching for unterminated node
				{
					rawXML = rawXML.concat("</flash:stream>");
					_expireTagSearch = true;
				}
			}

			if (!_expireTagSearch)
			{
				var pattern2:RegExp = new RegExp("<stream:stream");
				var resultObj2:Object = pattern2.exec(rawXML);
				if (resultObj2 != null) // stop searching for unterminated node
				{
					rawXML = rawXML.concat("</stream:stream>");
					_expireTagSearch = true;
				}
			}

			var xmlData:XMLDocument = new XMLDocument();
			xmlData.ignoreWhite = this.ignoreWhite;

			//error handling to catch incomplete xml strings that have
			//been truncated by the socket
			try{
				var isComplete: Boolean = true;
				xmlData.parseXML( rawXML );
				_incompleteRawXML = '';
			}
			catch(err:Error){
				isComplete = false;
				_incompleteRawXML += ev.data as String;//concatenate the raw xml to the previous xml
			}

			if (isComplete){

				var event:IncomingDataEvent = new IncomingDataEvent();
				event.data = xmlData;
				dispatchEvent( event );

				for (var i:int = 0; i<xmlData.childNodes.length; i++)
				{
					// Read the data and send it to the appropriate parser
					var currentNode:XMLNode = xmlData.childNodes[i];
					var nodeName:String = currentNode.nodeName.toLowerCase();

					switch( nodeName )
					{
						case "stream:stream":
							_expireTagSearch = false;
							handleStream( currentNode );
							break;
						case "flash:stream":
							_expireTagSearch = false;
							handleStream( currentNode );
							break;

						case "stream:error":
							handleStreamError( currentNode );
							break;

						case "iq":
							handleIQ( currentNode );
							break;

						case "message":
							handleMessage( currentNode );
							break;

						case "presence":
							handlePresence( currentNode );
							break;

						case "stream:features":
							handleStreamFeatures( currentNode );
							break;

						case "success":
							handleAuthentication( currentNode );
							break;

						case "failure":
							handleAuthentication( currentNode );
							break;

						default:
							dispatchError( "undefined-condition", "Unknown Error", "modify", 500 );
							break;
					}
				}
			}
		}
	}
}

Allow XMPPSocketConnection to handle the stream:features packet.


package aerys.minko
{
	import aerys.common.Factory;
	import aerys.common.IVersionnable;
	import aerys.minko.asset.MinkoLogo;
	import aerys.minko.ns.minko;
	import aerys.minko.query.RenderingQuery;
	import aerys.minko.render.DirectRenderer3D;
	import aerys.minko.render.IRenderer3D;
	import aerys.minko.scene.IScene3D;

	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DRenderMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.utils.getTimer;

	/**
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Viewport3D extends Sprite implements IVersionnable
	{
		use namespace minko;

		private var _logo			: MovieClip			= new MinkoLogo();

		private var _width			: Number			= 0.;
		private var _height			: Number			= 0.;

		private var _version		: uint				= 0;

		private var _query			: RenderingQuery	= null;
		private var _time			: int				= 0;
		private var _sceneSize		: uint				= 0;
		private var _drawTime		: int				= 0;

		private var _rendererClass	: Class				= null;
		private var _renderer		: IRenderer3D		= null;
		private var _context		: Context3D			= null;

		private var _aa				: int				= 0;

		public function get version() : uint
		{
			return _version;
		}

		/**
		 * Indicates the width of the viewport.
		 * @return The width of the viewport.
		 *
		 */
		override public function get width()	: Number
		{
			return _width;
		}

		override public function set width(value : Number) : void
		{
			if (value != _width)
			{
				_width = value;
				++_version;
			}
		}

		public function get sceneSize() : uint
		{
			return _sceneSize;
		}

		/**
		 * Indicates the height of the viewport.
		 * @return The height of the viewport.
		 *
		 */
		override public function get height() : Number
		{
			return _height;
		}

		override public function set height(value : Number) : void
		{
			if (value != _height)
			{
				_height = value;
				++_version;
			}
		}

		public function get antiAliasing() : int
		{
			return _aa;
		}

		public function set antiAliasing(value : int) : void
		{
			if (value != _aa)
			{
				_aa = value;
				++_version;

				resetContext3D();
			}
		}

		public function get numTriangles() : uint
		{
			return _query ? _query.numTriangles
							: 0;
		}

		public function get renderingTime() : uint
		{
			return _time;
		}

		public function get drawingTime() : int
		{
			return _drawTime;
		}

		public function get renderMode() : String
		{
			if (_context)
				return _context.driverInfo.split(/^(\w+) Description=(.*) Driver=.*$/gs)[1];

			return null;
		}

		public function get driver() : String
		{
			if (_context)
				return _context.driverInfo.split(/^(\w+) Description=(.*) Driver=.*$/gs)[2];

			return null;
		}

		/**
		 * Creates a new Viewport object.
		 *
		 * @param width The width of the viewport.
		 * @param height The height of the viewport.
		 */
		public function Viewport3D(width		: Number,
								   height		: Number,
								   antiAliasing	: int	= 0,
								   rendererType	: Class = null)
		{
			this.width = width;
			this.height = height;

			_aa = antiAliasing;
			_rendererClass = rendererType || DirectRenderer3D;
			_logo.addEventListener(MouseEvent.CLICK, logoClickHandler);
		}

		public static function setupOnStage(stage : Stage, antiAliasing : int = 0) : Viewport3D
		{
			var vp : Viewport3D = new Viewport3D(stage.stageWidth, stage.stageHeight, antiAliasing);

			vp.setupOnStage(stage);

			return vp;
		}

		private function setupOnStage(stage : Stage, autoResize : Boolean = true) : void
		{
			stage.addChild(this);

			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, contextCreatedHandler);
			stage.stage3Ds[0].viewPort = new Rectangle(0, 0, _width, _height);
			stage.stage3Ds[0].requestContext3D(Context3DRenderMode.AUTO);

			if (autoResize)
				stage.addEventListener(Event.RESIZE, stageResizeHandler);
		}

		private function stageResizeHandler(event : Event) : void
		{
			var stage : Stage = event.target as Stage;

			if (stage.stageWidth)
				width = stage.stageWidth;
			if (stage.stageHeight)
				height = stage.stageHeight;

			resetContext3D();
		}

		private function contextCreatedHandler(event : Event) : void
		{
			_context = stage.stage3Ds[0].context3D;
			resetContext3D();
		}

		private function resetContext3D(event : Event = null) : void
		{
			if (_context)
			{
				stage.stage3Ds[0].viewPort = new Rectangle(0, 0, _width, _height);

				_context.configureBackBuffer(_width, _height, _aa, true);
				_context.setDepthTest(true, Context3DCompareMode.LESS_EQUAL);

				_renderer = new DirectRenderer3D(this, _context);
				_query = new RenderingQuery(_renderer);
			}
		}

		/**
		 * Render the specified scene.
		 * @param scene
		 */
		public function render(scene : IScene3D) : void
		{
			if (_query)
			{
				var time : Number = getTimer();

				_query.reset();
				_query.query(scene);
				_renderer.present();

				_sceneSize = _query.numNodes;

				Factory.sweep();

				_time = getTimer() - time;
				_drawTime = _query.drawingTime;
			}
			else
			{
				_time = 0;
				_drawTime = 0;
			}

			//showLogo();
		}

		public function showLogo() : void
		{
			addChild(_logo);

			_logo.visible = true;
			_logo.useHandCursor = true;
			_logo.buttonMode = true;
			_logo.x = stage.stageWidth - _logo.width - 10;
			_logo.y = stage.stageHeight - _logo.height - 10;
		}

		private function logoClickHandler(event : Event) : void
		{
			navigateToURL(new URLRequest(Minko.URL), "_blank");
		}
	}
}

package aerys.minko
{
	import aerys.common.Factory;
	import aerys.common.IVersionnable;
	import aerys.minko.asset.MinkoLogo;
	import aerys.minko.ns.minko;
	import aerys.minko.query.RenderingQuery;
	import aerys.minko.render.DirectRenderer3D;
	import aerys.minko.render.IRenderer3D;
	import aerys.minko.scene.IScene3D;

	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DRenderMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.utils.getTimer;

	/**
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Viewport3D extends Sprite implements IVersionnable
	{
		use namespace minko;

		private var _logo			: MovieClip			= new MinkoLogo();

		private var _width			: Number			= 0.;
		private var _height			: Number			= 0.;

		private var _version		: uint				= 0;

		private var _query			: RenderingQuery	= null;
		private var _time			: int				= 0;
		private var _sceneSize		: uint				= 0;
		private var _drawTime		: int				= 0;

		private var _rendererClass	: Class				= null;
		private var _renderer		: IRenderer3D		= null;
		private var _context		: Context3D			= null;

		private var _aa				: int				= 0;

		public function get version() : uint
		{
			return _version;
		}

		/**
		 * Indicates the width of the viewport.
		 * @return The width of the viewport.
		 *
		 */
		override public function get width()	: Number
		{
			return _width;
		}

		override public function set width(value : Number) : void
		{
			if (value != _width)
			{
				_width = value;
				++_version;
			}
		}

		public function get sceneSize() : uint
		{
			return _sceneSize;
		}

		/**
		 * Indicates the height of the viewport.
		 * @return The height of the viewport.
		 *
		 */
		override public function get height() : Number
		{
			return _height;
		}

		override public function set height(value : Number) : void
		{
			if (value != _height)
			{
				_height = value;
				++_version;
			}
		}

		public function get antiAliasing() : int
		{
			return _aa;
		}

		public function set antiAliasing(value : int) : void
		{
			if (value != _aa)
			{
				_aa = value;
				++_version;

				resetContext3D();
			}
		}

		public function get numTriangles() : uint
		{
			return _query ? _query.numTriangles
							: 0;
		}

		public function get renderingTime() : uint
		{
			return _time;
		}

		public function get drawingTime() : int
		{
			return _drawTime;
		}

		public function get renderMode() : String
		{
			return _context ? _context.driverInfo : null;
		}

		/**
		 * Creates a new Viewport object.
		 *
		 * @param width The width of the viewport.
		 * @param height The height of the viewport.
		 */
		public function Viewport3D(width		: Number,
								   height		: Number,
								   antiAliasing	: int	= 0,
								   rendererType	: Class = null)
		{
			this.width = width;
			this.height = height;

			_aa = antiAliasing;
			_rendererClass = rendererType || DirectRenderer3D;
			_logo.addEventListener(MouseEvent.CLICK, logoClickHandler);
		}

		public static function setupOnStage(stage : Stage, antiAliasing : int = 0) : Viewport3D
		{
			var vp : Viewport3D = new Viewport3D(stage.stageWidth, stage.stageHeight, antiAliasing);

			vp.setupOnStage(stage);

			return vp;
		}

		private function setupOnStage(stage : Stage, autoResize : Boolean = true) : void
		{
			stage.addChild(this);

			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, contextCreatedHandler);
			stage.stage3Ds[0].viewPort = new Rectangle(0, 0, _width, _height);
			stage.stage3Ds[0].requestContext3D(Context3DRenderMode.AUTO);

			if (autoResize)
				stage.addEventListener(Event.RESIZE, stageResizeHandler);
		}

		private function stageResizeHandler(event : Event) : void
		{
			var stage : Stage = event.target as Stage;

			if (stage.stageWidth)
				width = stage.stageWidth;
			if (stage.stageHeight)
				height = stage.stageHeight;

			resetContext3D();
		}

		private function contextCreatedHandler(event : Event) : void
		{
			_context = stage.stage3Ds[0].context3D;
			resetContext3D();
		}

		private function resetContext3D(event : Event = null) : void
		{
			if (_context)
			{
				stage.stage3Ds[0].viewPort = new Rectangle(0, 0, _width, _height);

				_context.configureBackBuffer(_width, _height, _aa, true);
				_context.setDepthTest(true, Context3DCompareMode.LESS_EQUAL);

				_renderer = new DirectRenderer3D(this, _context);
				_query = new RenderingQuery(_renderer);
			}
		}

		/**
		 * Render the specified scene.
		 * @param scene
		 */
		public function render(scene : IScene3D) : void
		{
			if (_query)
			{
				var time : Number = getTimer();

				_query.reset();
				_query.query(scene);
				_renderer.present();

				_sceneSize = _query.numNodes;

				Factory.sweep();

				_time = getTimer() - time;
				_drawTime = _query.drawingTime;
			}
			else
			{
				_time = 0;
				_drawTime = 0;
			}

			//showLogo();
		}

		public function showLogo() : void
		{
			addChild(_logo);

			_logo.visible = true;
			_logo.useHandCursor = true;
			_logo.buttonMode = true;
			_logo.x = stage.stageWidth - _logo.width - 10;
			_logo.y = stage.stageHeight - _logo.height - 10;
		}

		private function logoClickHandler(event : Event) : void
		{
			navigateToURL(new URLRequest(Minko.URL), "_blank");
		}
	}
}

Fix driver info reading.

Fix driver info reading.


package krewfw.builtin_actor.ui {

    import flash.display.Stage;
    import flash.events.KeyboardEvent;
    import flash.geom.Point;
    import flash.ui.Keyboard;
    import flash.utils.Dictionary;

    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.Quad;
    import starling.events.Touch;
    import starling.events.TouchEvent;
    import starling.events.TouchPhase;

    import krewfw.NativeStageAccessor;
    import krewfw.core.KrewActor;

    /**
     * いわゆるバーチャルジョイスティック。
     *
     * maxFingerMove に 100 を指定すると、中心から 100 ピクセル指を動かした際に
     * スティックの傾きが最大となる。maxImageMove はこのときのスティック画像の
     * 中心からの移動量である。
     */
    //------------------------------------------------------------
    public class SimpleVirtualJoystick extends KrewActor {

        /**
         * ジョイスティックに対して触れた・動かした・離した際にこのイベントが投げられる。
         * イベントの引数は {velocityX:Number, velocityY:Number}.
         * ジョイスティックの傾きの x, y 成分が [0, 1] の値で渡される。
         * 離した際のイベントでは velocityX, velocityY は共に 0 となる。
         */
        public static const UPDATE_JOYSTICK:String = "krew.updateJoystick";

        public var maxFingerMove:Number = 60;
        public var maxImageMove:Number  = 40;

        private var _stickImage:DisplayObject;

        private var _keyDowns:Dictionary = new Dictionary();
        private var _numKeyDown:int = 0;

        //------------------------------------------------------------
        public function SimpleVirtualJoystick(holderImage:Image=null,
                                              stickImage:Image=null,
                                              touchSize:Number=130)
        {
            touchable = true;

            // default display object
            if (holderImage == null) {
                var defaultHolder:Quad = new Quad(100, 100, 0x777777);
                var defaultStick :Quad = new Quad( 50,  50, 0xee4444);
                _setCenterPivot(defaultHolder);
                _setCenterPivot(defaultStick);
                addChild(defaultHolder);
                addChild(defaultStick);
            } else {
                addImage(holderImage);
                addImage(stickImage);
            }

            _stickImage = stickImage;

            super.addTouchMarginNode(touchSize, touchSize);
            addEventListener(TouchEvent.TOUCH, _onTouch);

            _initKeyboardEventListener();
        }

        private function _setCenterPivot(dispObj:DisplayObject):void {
            dispObj.pivotX = dispObj.width  / 2;
            dispObj.pivotY = dispObj.height / 2;
        }

        protected override function onDispose():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.removeEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.removeEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onTouch(event:TouchEvent):void {
            var touchBegan:Touch = event.getTouch(this, TouchPhase.BEGAN);
            if (touchBegan) { _onTouchStart(touchBegan); }

            var touchMoved:Touch = event.getTouch(this, TouchPhase.MOVED);
            if (touchMoved) { _onTouchMove(touchMoved); }

            var touchEnded:Touch = event.getTouch(this, TouchPhase.ENDED);
            if (touchEnded) { _onTouchEnd(touchEnded); }
        }

        private function _onTouchStart(touchBegan:Touch):void {
            _onTouchMove(touchBegan);
        }

        private function _onTouchMove(touchMoved:Touch):void {
            var localPos:Point = touchMoved.getLocation(this);
            var stickX:Number = localPos.x;
            var stickY:Number = localPos.y;

            var fingerDistance:Number = krew.getDistance(0, 0, stickX, stickY);
            var scaleToFingerLimit:Number = fingerDistance / maxFingerMove;
            if (scaleToFingerLimit > 1) {
                stickX /= scaleToFingerLimit;
                stickY /= scaleToFingerLimit;
            }

            var moveScale:Number = maxImageMove / maxFingerMove;
            _stickImage.x = stickX * moveScale;
            _stickImage.y = stickY * moveScale;

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: _stickImage.x / maxImageMove,
                velocityY: _stickImage.y / maxImageMove
            });
        }

        private function _onTouchEnd(touchEnded:Touch):void {
            _stickImage.x = 0;
            _stickImage.y = 0;
            sendMessage(UPDATE_JOYSTICK, {velocityX: 0, velocityY: 0});
        }

        public override function onUpdate(passedTime:Number):void {
            _onKeyUpdate();
        }

        //------------------------------------------------------------
        // Keyboard Event
        //------------------------------------------------------------

        private function _initKeyboardEventListener():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onKeyDown(event:KeyboardEvent):void {
            if (!_keyDowns[event.keyCode]) { ++_numKeyDown; }
            _keyDowns[event.keyCode] = true;
        }

        private function _onKeyUp(event:KeyboardEvent):void {
            if (_keyDowns[event.keyCode]) { --_numKeyDown; }
            _keyDowns[event.keyCode] = false;

            // 全部離したタイミング
            if (_numKeyDown == 0) {
                sendMessage(UPDATE_JOYSTICK, {
                    velocityX: 0,
                    velocityY: 0
                });
            }
        }

        /**
         * PC 用にキーボードでの操作でもメッセージを投げる。
         * マウスでジョイスティックが動かされていた場合はそちらを優先
         */
        private function _onKeyUpdate():void {
            if (_numKeyDown == 0) { return; }
            if (!(_stickImage.x == 0  &&  _stickImage.y == 0)) { return; }

            var keyVelocityX:Number = 0;
            var keyVelocityY:Number = 0;
            if (_keyDowns[Keyboard.LEFT ]) { keyVelocityX -= 1.0; }
            if (_keyDowns[Keyboard.RIGHT]) { keyVelocityX += 1.0; }
            if (_keyDowns[Keyboard.UP   ]) { keyVelocityY -= 1.0; }
            if (_keyDowns[Keyboard.DOWN ]) { keyVelocityY += 1.0; }

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: keyVelocityX,
                velocityY: keyVelocityY
            });
        }

    }
}

package krewfw.builtin_actor.ui {

    import flash.display.Stage;
    import flash.events.KeyboardEvent;
    import flash.geom.Point;
    import flash.ui.Keyboard;
    import flash.utils.Dictionary;

    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.Quad;
    import starling.events.Touch;
    import starling.events.TouchEvent;
    import starling.events.TouchPhase;

    import krewfw.NativeStageAccessor;
    import krewfw.core.KrewActor;

    /**
     * いわゆるバーチャルジョイスティック。
     *
     * maxFingerMove に 100 を指定すると、中心から 100 ピクセル指を動かした際に
     * スティックの傾きが最大となる。maxImageMove はこのときのスティック画像の
     * 中心からの移動量である。
     */
    //------------------------------------------------------------
    public class SimpleVirtualJoystick extends KrewActor {

        /**
         * ジョイスティックに対して触れた・動かした・離した際にこのイベントが投げられる。
         * イベントの引数は {velocityX:Number, velocityY:Number}.
         * ジョイスティックの傾きの x, y 成分が [0, 1] の値で渡される。
         * 離した際のイベントでは velocityX, velocityY は共に 0 となる。
         */
        public static const UPDATE_JOYSTICK:String = "krew.updateJoystick";

        public var maxFingerMove:Number = 60;
        public var maxImageMove:Number  = 40;

        private var _stickImage:DisplayObject;

        private var _keyDowns:Dictionary = new Dictionary();
        private var _numKeyDown:int = 0;

        //------------------------------------------------------------
        public function SimpleVirtualJoystick(holderImage:Image=null,
                                              stickImage:Image=null,
                                              touchSize:Number=130)
        {
            touchable = true;

            // default display object
            if (holderImage == null) {
                var defaultHolder:Quad = new Quad(100, 100, 0x777777);
                var defaultStick :Quad = new Quad( 50,  50, 0xee4444);
                _setCenterPivot(defaultHolder);
                _setCenterPivot(defaultStick);
                addChild(defaultHolder);
                addChild(defaultStick);
                _stickImage = defaultStick;
            } else {
                addImage(holderImage);
                addImage(stickImage);
                _stickImage = stickImage;
            }

            super.addTouchMarginNode(touchSize, touchSize);
            addEventListener(TouchEvent.TOUCH, _onTouch);

            _initKeyboardEventListener();
        }

        private function _setCenterPivot(dispObj:DisplayObject):void {
            dispObj.pivotX = dispObj.width  / 2;
            dispObj.pivotY = dispObj.height / 2;
        }

        protected override function onDispose():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.removeEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.removeEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onTouch(event:TouchEvent):void {
            var touchBegan:Touch = event.getTouch(this, TouchPhase.BEGAN);
            if (touchBegan) { _onTouchStart(touchBegan); }

            var touchMoved:Touch = event.getTouch(this, TouchPhase.MOVED);
            if (touchMoved) { _onTouchMove(touchMoved); }

            var touchEnded:Touch = event.getTouch(this, TouchPhase.ENDED);
            if (touchEnded) { _onTouchEnd(touchEnded); }
        }

        private function _onTouchStart(touchBegan:Touch):void {
            _onTouchMove(touchBegan);
        }

        private function _onTouchMove(touchMoved:Touch):void {
            var localPos:Point = touchMoved.getLocation(this);
            var stickX:Number = localPos.x;
            var stickY:Number = localPos.y;

            var fingerDistance:Number = krew.getDistance(0, 0, stickX, stickY);
            var scaleToFingerLimit:Number = fingerDistance / maxFingerMove;
            if (scaleToFingerLimit > 1) {
                stickX /= scaleToFingerLimit;
                stickY /= scaleToFingerLimit;
            }

            var moveScale:Number = maxImageMove / maxFingerMove;
            _stickImage.x = stickX * moveScale;
            _stickImage.y = stickY * moveScale;

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: _stickImage.x / maxImageMove,
                velocityY: _stickImage.y / maxImageMove
            });
        }

        private function _onTouchEnd(touchEnded:Touch):void {
            _stickImage.x = 0;
            _stickImage.y = 0;
            sendMessage(UPDATE_JOYSTICK, {velocityX: 0, velocityY: 0});
        }

        public override function onUpdate(passedTime:Number):void {
            _onKeyUpdate();
        }

        //------------------------------------------------------------
        // Keyboard Event
        //------------------------------------------------------------

        private function _initKeyboardEventListener():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onKeyDown(event:KeyboardEvent):void {
            if (!_keyDowns[event.keyCode]) { ++_numKeyDown; }
            _keyDowns[event.keyCode] = true;
        }

        private function _onKeyUp(event:KeyboardEvent):void {
            if (_keyDowns[event.keyCode]) { --_numKeyDown; }
            _keyDowns[event.keyCode] = false;

            // 全部離したタイミング
            if (_numKeyDown == 0) {
                sendMessage(UPDATE_JOYSTICK, {
                    velocityX: 0,
                    velocityY: 0
                });
            }
        }

        /**
         * PC 用にキーボードでの操作でもメッセージを投げる。
         * マウスでジョイスティックが動かされていた場合はそちらを優先
         */
        private function _onKeyUpdate():void {
            if (_numKeyDown == 0) { return; }
            if (!(_stickImage.x == 0  &&  _stickImage.y == 0)) { return; }

            var keyVelocityX:Number = 0;
            var keyVelocityY:Number = 0;
            if (_keyDowns[Keyboard.LEFT ]) { keyVelocityX -= 1.0; }
            if (_keyDowns[Keyboard.RIGHT]) { keyVelocityX += 1.0; }
            if (_keyDowns[Keyboard.UP   ]) { keyVelocityY -= 1.0; }
            if (_keyDowns[Keyboard.DOWN ]) { keyVelocityY += 1.0; }

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: keyVelocityX,
                velocityY: keyVelocityY
            });
        }

    }
}

Modify SimpleVirtualJoystick

Modify SimpleVirtualJoystick


package aerys.minko.scene.controller.camera
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.data.CameraDataProvider;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.scene.node.camera.AbstractCamera;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.IDataProvider;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	public final class CameraController extends AbstractController
	{
		use namespace minko_scene;

		private var _data	: CameraDataProvider;
		private var _camera	: AbstractCamera;
		private var _ortho 	: Boolean;

		public function get cameraData() : CameraDataProvider
		{
			return _data;
		}

		public function get orthographic() : Boolean
		{
			return _ortho;
		}
		public function set orthographic(value : Boolean) : void
		{
			_ortho = value;
		}

		public function CameraController()
		{
			super(AbstractCamera);

			initialize();
		}

		private function initialize() : void
		{
			_data = new CameraDataProvider();

			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function targetAddedHandler(controller	: CameraController,
											target		: AbstractCamera) : void
		{
			if (_camera != null)
				throw new Error('The CameraController can target only one Camera object.');

			_camera = target;
			_camera.added.add(addedHandler);
			_camera.removed.add(removedHandler);
		}

		private function targetRemovedHandler(controller	: CameraController,
											  target		: AbstractCamera) : void
		{
			_camera.added.remove(addedHandler);
			_camera.removed.remove(removedHandler);
			_camera = null;
		}

		private function addedHandler(camera : AbstractCamera, ancestor : Group) : void
		{
			var scene : Scene = camera.scene;

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

            if (camera.enabled)
                sceneBindings.addProvider(_data);

			camera.localToWorldTransformChanged.add(localToWorldChangedHandler);
			camera.activated.add(cameraActivatedHandler);
			camera.deactivated.add(cameraDeactivatedHandler);

			_data.changed.add(cameraPropertyChangedHandler);

			sceneBindings.addCallback('viewportWidth', viewportSizeChanged);
			sceneBindings.addCallback('viewportHeight', viewportSizeChanged);

			updateProjection();
			localToWorldChangedHandler(camera, camera.getLocalToWorldTransform());
		}

		private function removedHandler(camera : AbstractCamera, ancestor : Group) : void
		{
			var scene : Scene = ancestor.scene;

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

			if (camera.enabled)
				sceneBindings.removeProvider(_data);

			camera.localToWorldTransformChanged.remove(localToWorldChangedHandler);
			camera.activated.remove(cameraActivatedHandler);
			camera.deactivated.remove(cameraDeactivatedHandler);

			_data.changed.remove(cameraPropertyChangedHandler);

			sceneBindings.removeCallback('viewportWidth', viewportSizeChanged);
			sceneBindings.removeCallback('viewportHeight', viewportSizeChanged);
		}

		private function localToWorldChangedHandler(camera 			: AbstractCamera,
													localToWorld 	: Matrix4x4) : void
		{
			var cameraData		: CameraDataProvider	= _data;
			var worldToView		: Matrix4x4				= cameraData.worldToView;
			var worldToScreen	: Matrix4x4				= cameraData.worldToScreen;

			localToWorld.deltaTransformVector(Vector4.Z_AXIS, cameraData.direction);
			localToWorld.transformVector(Vector4.ZERO, cameraData.position);

			worldToView.lock()
				.copyFrom(localToWorld)
				.invert()
				.unlock();

			worldToScreen.lock()
				.copyFrom(worldToView)
				.append(cameraData.projection)
				.unlock();

			cameraData.frustum.updateFromMatrix(worldToScreen);
		}

		private function viewportSizeChanged(bindings 	: DataBindings,
											 key 		: String,
											 oldValue 	: Object,
											 newValue 	: Object) : void
		{
			updateProjection();
		}

		private function cameraPropertyChangedHandler(provider : IDataProvider,
                                                      property : String) : void
		{
			if (property == 'zFar' || property == 'zNear' || property == 'fieldOfView'
                || property == 'zoom')
				updateProjection();
		}

		private function updateProjection() : void
		{
			var sceneBindings	: DataBindings	= Scene(_camera.root).bindings;

			if (sceneBindings.propertyExists('viewportWidth')
				&& sceneBindings.propertyExists('viewportHeight'))
			{
				var cameraData		: CameraDataProvider	= _data;
				var viewportWidth	: Number				= sceneBindings.getProperty('viewportWidth');
				var viewportHeight	: Number				= sceneBindings.getProperty('viewportHeight');
				var ratio			: Number				= viewportWidth / viewportHeight;

				var projection		: Matrix4x4				= cameraData.projection;
				var worldToScreen	: Matrix4x4				= cameraData.worldToScreen;

				projection.lock();
				worldToScreen.lock();

				if (_ortho)
				{
					var cameraZoom : Number = cameraData.zoom;

					projection.ortho(
						viewportWidth / cameraZoom,
						viewportHeight / cameraZoom,
						cameraData.zNear,
						cameraData.zFar
					);
				}
				else
					projection.perspectiveFoV(
						cameraData.fieldOfView,
						ratio,
						cameraData.zNear,
						cameraData.zFar
					);

				worldToScreen.copyFrom(_camera.getWorldToLocalTransform()).append(projection);

				cameraData.frustum.updateFromMatrix(worldToScreen);

				projection.unlock();
				worldToScreen.unlock();
			}
		}

		private function cameraActivatedHandler(camera : AbstractCamera) : void
		{
			var scene : Scene = camera.root as Scene;

			resetSceneCamera(scene);
			scene.bindings.addProvider(_data);
		}

		private function cameraDeactivatedHandler(camera : AbstractCamera) : void
		{
			var scene 	: Scene	= camera.root as Scene;

			resetSceneCamera(scene);
			scene.bindings.removeProvider(_data);
		}

		private function resetSceneCamera(scene : Scene) : void
		{
			var cameras 	: Vector.<ISceneNode> 	= scene.getDescendantsByType(AbstractCamera);
			var numCameras 	: uint 					= cameras.length;
			var cameraId 	: uint 					= 0;
			var camera 		: AbstractCamera 		= null;

			if (_camera.enabled)
			{
				scene._camera = _camera;
				for (cameraId; cameraId < numCameras; ++cameraId)
				{
					camera = cameras[cameraId] as AbstractCamera;
					camera.enabled = camera == _camera;
				}
			}
			else
			{
				scene._camera = null;
				for (cameraId; cameraId < numCameras; ++cameraId)
				{
					camera = cameras[cameraId] as AbstractCamera;
					if (camera.enabled)
						scene._camera = camera;
				}
			}
		}
	}
}

package aerys.minko.scene.controller.camera
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.data.CameraDataProvider;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.scene.node.camera.AbstractCamera;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.IDataProvider;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	public final class CameraController extends AbstractController
	{
		use namespace minko_scene;

		private var _data	: CameraDataProvider;
		private var _camera	: AbstractCamera;
		private var _ortho 	: Boolean;

		public function get cameraData() : CameraDataProvider
		{
			return _data;
		}

		public function get orthographic() : Boolean
		{
			return _ortho;
		}
		public function set orthographic(value : Boolean) : void
		{
			_ortho = value;
		}

		public function CameraController()
		{
			super(AbstractCamera);

			initialize();
		}

		private function initialize() : void
		{
			_data = new CameraDataProvider();

			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function targetAddedHandler(controller	: CameraController,
											target		: AbstractCamera) : void
		{
			if (_camera != null)
				throw new Error('The CameraController can target only one Camera object.');

			_camera = target;
			_camera.added.add(addedHandler);
			_camera.removed.add(removedHandler);
		}

		private function targetRemovedHandler(controller	: CameraController,
											  target		: AbstractCamera) : void
		{
			_camera.added.remove(addedHandler);
			_camera.removed.remove(removedHandler);
			_camera = null;
		}

		private function addedHandler(camera : AbstractCamera, ancestor : Group) : void
		{
			var scene : Scene = camera.scene;

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

            if (camera.enabled)
                sceneBindings.addProvider(_data);

			camera.localToWorldTransformChanged.add(localToWorldChangedHandler);
			camera.activated.add(cameraActivatedHandler);
			camera.deactivated.add(cameraDeactivatedHandler);

			_data.changed.add(cameraPropertyChangedHandler);

			sceneBindings.addCallback('viewportWidth', viewportSizeChanged);
			sceneBindings.addCallback('viewportHeight', viewportSizeChanged);

			updateProjection();
			localToWorldChangedHandler(camera, camera.getLocalToWorldTransform());
		}

		private function removedHandler(camera : AbstractCamera, ancestor : Group) : void
		{
			var scene : Scene = ancestor.scene;

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

			if (camera.enabled)
				sceneBindings.removeProvider(_data);

			camera.localToWorldTransformChanged.remove(localToWorldChangedHandler);
			camera.activated.remove(cameraActivatedHandler);
			camera.deactivated.remove(cameraDeactivatedHandler);

			_data.changed.remove(cameraPropertyChangedHandler);

			sceneBindings.removeCallback('viewportWidth', viewportSizeChanged);
			sceneBindings.removeCallback('viewportHeight', viewportSizeChanged);
		}

		private function localToWorldChangedHandler(camera 			: AbstractCamera,
													localToWorld 	: Matrix4x4) : void
		{
			var cameraData		: CameraDataProvider	= _data;
			var worldToView		: Matrix4x4				= cameraData.worldToView;
			var worldToScreen	: Matrix4x4				= cameraData.worldToScreen;

			localToWorld.deltaTransformVector(Vector4.Z_AXIS, cameraData.direction);
			localToWorld.transformVector(Vector4.ZERO, cameraData.position);

            worldToView.lock();
            worldToScreen.lock();

			worldToView
				.copyFrom(localToWorld)
				.invert()

			worldToScreen
				.copyFrom(worldToView)
				.append(cameraData.projection)

			cameraData.frustum.updateFromMatrix(worldToScreen);

            worldToView.unlock();
            worldToScreen.unlock();
		}

		private function viewportSizeChanged(bindings 	: DataBindings,
											 key 		: String,
											 oldValue 	: Object,
											 newValue 	: Object) : void
		{
			updateProjection();
		}

		private function cameraPropertyChangedHandler(provider : IDataProvider,
                                                      property : String) : void
		{
			if (property == 'zFar' || property == 'zNear' || property == 'fieldOfView'
                || property == 'zoom')
				updateProjection();
		}

		private function updateProjection() : void
		{
			var sceneBindings	: DataBindings	= Scene(_camera.root).bindings;

			if (sceneBindings.propertyExists('viewportWidth')
				&& sceneBindings.propertyExists('viewportHeight'))
			{
				var cameraData		: CameraDataProvider	= _data;
				var viewportWidth	: Number				= sceneBindings.getProperty('viewportWidth');
				var viewportHeight	: Number				= sceneBindings.getProperty('viewportHeight');
				var ratio			: Number				= viewportWidth / viewportHeight;

				var projection		: Matrix4x4				= cameraData.projection;
				var worldToScreen	: Matrix4x4				= cameraData.worldToScreen;

				projection.lock();
				worldToScreen.lock();

				if (_ortho)
				{
					var cameraZoom : Number = cameraData.zoom;

					projection.ortho(
						viewportWidth / cameraZoom,
						viewportHeight / cameraZoom,
						cameraData.zNear,
						cameraData.zFar
					);
				}
				else
					projection.perspectiveFoV(
						cameraData.fieldOfView,
						ratio,
						cameraData.zNear,
						cameraData.zFar
					);

				worldToScreen.copyFrom(_camera.getWorldToLocalTransform()).append(projection);

				cameraData.frustum.updateFromMatrix(worldToScreen);

				projection.unlock();
				worldToScreen.unlock();
			}
		}

		private function cameraActivatedHandler(camera : AbstractCamera) : void
		{
			var scene : Scene = camera.root as Scene;

			resetSceneCamera(scene);
			scene.bindings.addProvider(_data);
		}

		private function cameraDeactivatedHandler(camera : AbstractCamera) : void
		{
			var scene 	: Scene	= camera.root as Scene;

			resetSceneCamera(scene);
			scene.bindings.removeProvider(_data);
		}

		private function resetSceneCamera(scene : Scene) : void
		{
			var cameras 	: Vector.<ISceneNode> 	= scene.getDescendantsByType(AbstractCamera);
			var numCameras 	: uint 					= cameras.length;
			var cameraId 	: uint 					= 0;
			var camera 		: AbstractCamera 		= null;

			if (_camera.enabled)
			{
				scene._camera = _camera;
				for (cameraId; cameraId < numCameras; ++cameraId)
				{
					camera = cameras[cameraId] as AbstractCamera;
					camera.enabled = camera == _camera;
				}
			}
			else
			{
				scene._camera = null;
				for (cameraId; cameraId < numCameras; ++cameraId)
				{
					camera = cameras[cameraId] as AbstractCamera;
					if (camera.enabled)
						scene._camera = camera;
				}
			}
		}
	}
}

fix CameraController.localToWorldChangedHandler() to unlock worldToScreen (and worldToView) after the frustum has been updated in order to avoid 1-frame late frustum culling bug


/**
 * Copyright 2017 FreshPlanet
 * 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.
 */
package com.freshplanet.ane.AirInAppPurchase {

	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

    /**
     *
     */
	public class InAppPurchase extends EventDispatcher {

        // --------------------------------------------------------------------------------------//
        //																						 //
        // 									   PUBLIC API										 //
        // 																						 //
        // --------------------------------------------------------------------------------------//

        /**
         * If true, logs will be displayed at the ActionScript level.
         */
        public static var logEnabled:Boolean = false;

        /**
         *
         */
        public static function get isSupported():Boolean {
            return _isIOS() || _isAndroid();
        }

        /**
         *
         */
        public static function get instance():InAppPurchase {
            return _instance ? _instance : new InAppPurchase();
        }

        /**
         * INIT_SUCCESSFUL
         * INIT_ERROR
         * @param googlePlayKey
         * @param debug
         */
        public function init(googlePlayKey:String, debug:Boolean = false):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.INIT_ERROR, "InAppPurchase not supported");
            else
                _context.call("initLib", googlePlayKey, debug);
        }

        /**
         * PURCHASE_SUCCESSFUL
         * PURCHASE_ERROR
         * @param productId
         */
        public function makePurchase(productId:String):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported");
            else
                _context.call("makePurchase", productId);
        }

        /**
         * PURCHASE_SUCCESSFUL
         * PURCHASE_ERROR
         * @param productId
         * @param oldProductId used on Android when upgrading/downgrading subscription - pass in the productId of current user subscription
         * @param prorationMode used on Android when upgrading/downgrading subscription
         */
        public function makeSubscription(productId:String, oldProductId:String = null, prorationMode:InAppPurchaseProrationMode = null):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported");
            else
                _context.call("makeSubscription", productId, oldProductId ? oldProductId : "", prorationMode ? prorationMode.value : -1);
        }

        /**
         * CONSUME_SUCCESSFUL
         * CONSUME_ERROR
         * @param productId
         * @param receipt
         * @param developerPayload used on Android
         */
        public function removePurchaseFromQueue(productId:String, receipt:String, developerPayload:String = null):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.CONSUME_ERROR, "InAppPurchase not supported");
            else {

                _context.call("removePurchaseFromQueue", productId, receipt, developerPayload ? developerPayload : "");

                if (_isIOS()) {

                    var filterPurchase:Function = function(jsonPurchase:String, index:int, purchases:Vector.<Object>):Boolean {

                        try {

                            var purchase:Object = JSON.parse(jsonPurchase);
                            return JSON.stringify(purchase.receipt) != receipt;
                        }
                        catch (error:Error) {
                            _log("ERROR", "couldn't parse purchase: " + jsonPurchase);
                        }

                        return false;
                    };

                    _iosPendingPurchases = _iosPendingPurchases.filter(filterPurchase);
                }
            }
        }

        /**
         * PRODUCT_INFO_RECEIVED
         * PRODUCT_INFO_ERROR
         * @param productsIds
         * @param subscriptionIds
         */
        public function getProductsInfo(productsIds:Array, subscriptionIds:Array):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.PRODUCT_INFO_ERROR, "InAppPurchase not supported");
            else {

                productsIds ||= [];
                subscriptionIds ||= [];
                _context.call("getProductsInfo", productsIds, subscriptionIds);
            }
        }

        /**
         * RESTORE_INFO_RECEIVED
         * RESTORE_INFO_ERROR
         */
        public function restoreTransactions(restoreIOSHistory:Boolean=false):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.RESTORE_INFO_ERROR, "InAppPurchase not supported");
            else if (_isAndroid() || restoreIOSHistory)
                _context.call("restoreTransaction");
            else if (_isIOS()) {

                var jsonPurchases:String = "[" + _iosPendingPurchases.join(",") + "]";
                var jsonData:String = "{ \"purchases\": " + jsonPurchases + "}";

                _dispatchEvent(InAppPurchaseEvent.RESTORE_INFO_RECEIVED, jsonData);
            }
        }

        public function clearTransactions():void
        {
            _iosPendingPurchases = new Vector.<Object>();
            if (!isSupported || _isAndroid()) {
                _dispatchEvent("CLEAR_TRANSACTIONS_ERROR", "clear transactions not supported");
            } else if (_isIOS()) {
                _context.call("clearTransactions");
            }
        }

        // --------------------------------------------------------------------------------------//
        //																						 //
        // 									 	PRIVATE API										 //
        // 																						 //
        // --------------------------------------------------------------------------------------//

        private static const EXTENSION_ID:String = "com.freshplanet.ane.AirInAppPurchase";
        private static var _instance:InAppPurchase = null;
		private var _context:ExtensionContext = null;

        private var _iosPendingPurchases:Vector.<Object> = new Vector.<Object>();

        /**
         * "private" singleton constructor
         */
		public function InAppPurchase() {

            super();

            if (_instance)
                throw Error("This is a singleton, use getInstance(), do not call the constructor directly.");

            _instance = this;

            _context = ExtensionContext.createExtensionContext(EXTENSION_ID, null);

            if (!_context)
                _log("ERROR", "Extension context is null. Please check if extension.xml is setup correctly.");
            else
                _context.addEventListener(StatusEvent.STATUS, _onStatus);
		}

        /**
         *
         * @param type
         * @param eventData
         */
        private function _dispatchEvent(type:String, eventData:String):void {
            this.dispatchEvent(new InAppPurchaseEvent(type, eventData))
        }

        /**
         *
         * @param event
         */
		private function _onStatus(event:StatusEvent):void {

            if (event.code == InAppPurchaseEvent.PURCHASE_SUCCESSFUL && _isIOS())
                _iosPendingPurchases.push(event.level);
            else if(event.code == "DEBUG")
                _log("DEBUG", event.level);

            _dispatchEvent(event.code, event.level);
		}

        /**
         *
         * @param strings
         */
        private function _log(...strings):void {

            if (logEnabled) {

                strings.unshift(EXTENSION_ID);
                trace.apply(null, strings);
            }
        }

        /**
         *
         * @return
         */
        private static function _isIOS():Boolean {
			return Capabilities.manufacturer.indexOf("iOS") > -1 && Capabilities.os.indexOf("x86_64") < 0 && Capabilities.os.indexOf("i386") < 0;
        }

        /**
         *
         * @return
         */
        private static function _isAndroid():Boolean {
            return Capabilities.manufacturer.indexOf("Android") > -1;
        }
	}
}

/**
 * Copyright 2017 FreshPlanet
 * 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.
 */
package com.freshplanet.ane.AirInAppPurchase {

	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

    /**
     *
     */
	public class InAppPurchase extends EventDispatcher {

        // --------------------------------------------------------------------------------------//
        //																						 //
        // 									   PUBLIC API										 //
        // 																						 //
        // --------------------------------------------------------------------------------------//

        /**
         * If true, logs will be displayed at the ActionScript level.
         */
        public static var logEnabled:Boolean = false;

        /**
         *
         */
        public static function get isSupported():Boolean {
            return _isIOSOrMacOS() || _isAndroid();
        }

        /**
         *
         */
        public static function get instance():InAppPurchase {
            return _instance ? _instance : new InAppPurchase();
        }

        /**
         * INIT_SUCCESSFUL
         * INIT_ERROR
         * @param googlePlayKey
         * @param debug
         */
        public function init(googlePlayKey:String, debug:Boolean = false):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.INIT_ERROR, "InAppPurchase not supported");
            else
                _context.call("initLib", googlePlayKey, debug);
        }

        /**
         * PURCHASE_SUCCESSFUL
         * PURCHASE_ERROR
         * @param productId
         */
        public function makePurchase(productId:String):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported");
            else
                _context.call("makePurchase", productId);
        }

        /**
         * PURCHASE_SUCCESSFUL
         * PURCHASE_ERROR
         * @param productId
         * @param oldProductId used on Android when upgrading/downgrading subscription - pass in the productId of current user subscription
         * @param prorationMode used on Android when upgrading/downgrading subscription
         */
        public function makeSubscription(productId:String, oldProductId:String = null, prorationMode:InAppPurchaseProrationMode = null):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported");
            else
                _context.call("makeSubscription", productId, oldProductId ? oldProductId : "", prorationMode ? prorationMode.value : -1);
        }

        /**
         * CONSUME_SUCCESSFUL
         * CONSUME_ERROR
         * @param productId
         * @param receipt
         * @param developerPayload used on Android
         */
        public function removePurchaseFromQueue(productId:String, receipt:String, developerPayload:String = null):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.CONSUME_ERROR, "InAppPurchase not supported");
            else {

                _context.call("removePurchaseFromQueue", productId, receipt, developerPayload ? developerPayload : "");

                if (_isIOSOrMacOS()) {

                    var filterPurchase:Function = function(jsonPurchase:String, index:int, purchases:Vector.<Object>):Boolean {

                        try {

                            var purchase:Object = JSON.parse(jsonPurchase);
                            return JSON.stringify(purchase.receipt) != receipt;
                        }
                        catch (error:Error) {
                            _log("ERROR", "couldn't parse purchase: " + jsonPurchase);
                        }

                        return false;
                    };

                    _iosPendingPurchases = _iosPendingPurchases.filter(filterPurchase);
                }
            }
        }

        /**
         * PRODUCT_INFO_RECEIVED
         * PRODUCT_INFO_ERROR
         * @param productsIds
         * @param subscriptionIds
         */
        public function getProductsInfo(productsIds:Array, subscriptionIds:Array):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.PRODUCT_INFO_ERROR, "InAppPurchase not supported");
            else {

                productsIds ||= [];
                subscriptionIds ||= [];
                _context.call("getProductsInfo", productsIds, subscriptionIds);
            }
        }

        /**
         * RESTORE_INFO_RECEIVED
         * RESTORE_INFO_ERROR
         */
        public function restoreTransactions(restoreIOSHistory:Boolean=false):void {

            if (!isSupported)
                _dispatchEvent(InAppPurchaseEvent.RESTORE_INFO_ERROR, "InAppPurchase not supported");
            else if (_isAndroid() || restoreIOSHistory)
                _context.call("restoreTransaction");
            else if (_isIOSOrMacOS()) {

                var jsonPurchases:String = "[" + _iosPendingPurchases.join(",") + "]";
                var jsonData:String = "{ \"purchases\": " + jsonPurchases + "}";

                _dispatchEvent(InAppPurchaseEvent.RESTORE_INFO_RECEIVED, jsonData);
            }
        }

        public function clearTransactions():void
        {
            _iosPendingPurchases = new Vector.<Object>();
            if (!isSupported || _isAndroid()) {
                _dispatchEvent("CLEAR_TRANSACTIONS_ERROR", "clear transactions not supported");
            } else if (_isIOSOrMacOS()) {
                _context.call("clearTransactions");
            }
        }

        // --------------------------------------------------------------------------------------//
        //																						 //
        // 									 	PRIVATE API										 //
        // 																						 //
        // --------------------------------------------------------------------------------------//

        private static const EXTENSION_ID:String = "com.freshplanet.ane.AirInAppPurchase";
        private static var _instance:InAppPurchase = null;
		private var _context:ExtensionContext = null;

        private var _iosPendingPurchases:Vector.<Object> = new Vector.<Object>();

        /**
         * "private" singleton constructor
         */
		public function InAppPurchase() {

            super();

            if (_instance)
                throw Error("This is a singleton, use getInstance(), do not call the constructor directly.");

            _instance = this;

            _context = ExtensionContext.createExtensionContext(EXTENSION_ID, null);

            if (!_context)
                _log("ERROR", "Extension context is null. Please check if extension.xml is setup correctly.");
            else
                _context.addEventListener(StatusEvent.STATUS, _onStatus);
		}

        /**
         *
         * @param type
         * @param eventData
         */
        private function _dispatchEvent(type:String, eventData:String):void {
            this.dispatchEvent(new InAppPurchaseEvent(type, eventData))
        }

        /**
         *
         * @param event
         */
		private function _onStatus(event:StatusEvent):void {

            if (event.code == InAppPurchaseEvent.PURCHASE_SUCCESSFUL && _isIOSOrMacOS())
                _iosPendingPurchases.push(event.level);
            else if(event.code == "DEBUG")
                _log("DEBUG", event.level);

            _dispatchEvent(event.code, event.level);
		}

        /**
         *
         * @param strings
         */
        private function _log(...strings):void {

            if (logEnabled) {

                strings.unshift(EXTENSION_ID);
                trace.apply(null, strings);
            }
        }

        /**
         *
         * @return
         */
        private static function _isIOSOrMacOS():Boolean {
			return Capabilities.os.indexOf("Mac OS") > -1 || (Capabilities.manufacturer.indexOf("iOS") > -1 && Capabilities.os.indexOf("x86_64") < 0 && Capabilities.os.indexOf("i386") < 0);
        }

        /**
         *
         * @return
         */
        private static function _isAndroid():Boolean {
            return Capabilities.manufacturer.indexOf("Android") > -1;
        }
	}
}

adjust as3 to support macos

adjust as3 to support macos


package goplayer
{
  import flash.display.Sprite
  import flash.events.Event
  import flash.events.MouseEvent
  import flash.utils.getTimer

  public class SkinnedPlayerView extends Component
    implements PlayerVideoUpdateListener, SkinBackend
  {
    private const IDLE_TIME_MS : uint = 2000

    private const missingSkinParts : Array = []

    private var skin : Skin
    private var video : PlayerVideo
    private var player : Player

    private var lastInteractionTime : uint = 0

    public function SkinnedPlayerView
      (skin : Skin, video : PlayerVideo, player : Player)
    {
      this.skin = skin
      this.video = video
      this.player = player

      skin.backend = this

      addChild(video)
      addChild(skin.frontend)

      video.addUpdateListener(this)

      addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage)
    }

    private function handleAddedToStage(event : Event) : void
    {
      stage.addEventListener(MouseEvent.MOUSE_MOVE, handleStageMouseMove)
      stage.addEventListener(Event.MOUSE_LEAVE, handleStageMouseLeave)
    }

    private function handleStageMouseMove(event : MouseEvent) : void
    { registerInteraction() }

    private function registerInteraction() : void
    { lastInteractionTime = getTimer() }

    private function handleStageMouseLeave(event : Event) : void
    { lastInteractionTime = 0 }

    public function handlePlayerVideoUpdated() : void
    {
      if (!player.playing)
        registerInteraction()

      skin.update()
    }

    public function get showControls() : Boolean
    { return !player.playing || !userIdle }

    private function get userIdle() : Boolean
    { return getTimer() - lastInteractionTime > IDLE_TIME_MS }

    // -----------------------------------------------------

    public function handleUserPlay() : void
    {
      if (player.finished)
        player.rewind()
      else if (player.started)
        player.paused = false
      else
        player.start()
    }

    public function handleUserPause() : void
    { player.paused = true }

    public function handleUserSeek(ratio : Number) : void
    { player.playheadRatio = ratio }

    public function handleUserMute() : void
    { player.mute() }

    public function handleUserUnmute() : void
    { player.unmute() }

    public function handleUserToggleFullscreen() : void
    { video.toggleFullscreen() }

    // -----------------------------------------------------

    public function get skinWidth() : Number
    { return video.dimensions.width }

    public function get skinHeight() : Number
    { return video.dimensions.height }

    public function get skinScale() : Number
    { return video.modernFullscreenEnabled ? 2 : 1 }

    public function get running() : Boolean
    { return player.running }

    public function get streamLengthSeconds() : Number
    { return player.streamLength.seconds }

    public function get playheadRatio() : Number
    { return player.playheadRatio }

    public function get bufferRatio() : Number
    { return player.bufferRatio }

    public function get bufferFillRatio() : Number
    { return player.bufferFillRatio }

    public function get playing() : Boolean
    { return player.playing }

    public function get buffering() : Boolean
    { return player.buffering }

    public function get volume() : Number
    { return player.volume }

    // -----------------------------------------------------

    public function handleSkinPartMissing(name : String) : void
    {
      if (missingSkinParts.indexOf(name) == -1)
        $handleSkinPartMissing(name)
    }

    private function $handleSkinPartMissing(name : String) : void
    {
      debug("Error: Skin part missing: " + name)
      missingSkinParts.push(name)
    }
  }
}

package goplayer
{
  import flash.display.Sprite
  import flash.events.Event
  import flash.events.MouseEvent
  import flash.ui.Mouse
  import flash.utils.getTimer

  public class SkinnedPlayerView extends Component
    implements PlayerVideoUpdateListener, SkinBackend
  {
    private const IDLE_TIME_MS : uint = 2000

    private const missingSkinParts : Array = []

    private var skin : Skin
    private var video : PlayerVideo
    private var player : Player

    private var lastInteractionTime : uint = 0

    public function SkinnedPlayerView
      (skin : Skin, video : PlayerVideo, player : Player)
    {
      this.skin = skin
      this.video = video
      this.player = player

      skin.backend = this

      addChild(video)
      addChild(skin.frontend)

      video.addUpdateListener(this)

      addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage)
    }

    private function handleAddedToStage(event : Event) : void
    {
      stage.addEventListener(MouseEvent.MOUSE_MOVE, handleStageMouseMove)
      stage.addEventListener(Event.MOUSE_LEAVE, handleStageMouseLeave)
    }

    private function handleStageMouseMove(event : MouseEvent) : void
    { registerInteraction() }

    private function registerInteraction() : void
    { lastInteractionTime = getTimer() }

    private function handleStageMouseLeave(event : Event) : void
    { lastInteractionTime = 0 }

    public function handlePlayerVideoUpdated() : void
    {
      if (!player.playing)
        registerInteraction()

      if (showControls)
        Mouse.show()
      else
        Mouse.hide()

      skin.update()
    }

    public function get showControls() : Boolean
    { return !player.playing || !userIdle }

    private function get userIdle() : Boolean
    { return getTimer() - lastInteractionTime > IDLE_TIME_MS }

    // -----------------------------------------------------

    public function handleUserPlay() : void
    {
      if (player.finished)
        player.rewind()
      else if (player.started)
        player.paused = false
      else
        player.start()
    }

    public function handleUserPause() : void
    { player.paused = true }

    public function handleUserSeek(ratio : Number) : void
    { player.playheadRatio = ratio }

    public function handleUserMute() : void
    { player.mute() }

    public function handleUserUnmute() : void
    { player.unmute() }

    public function handleUserToggleFullscreen() : void
    { video.toggleFullscreen() }

    // -----------------------------------------------------

    public function get skinWidth() : Number
    { return video.dimensions.width }

    public function get skinHeight() : Number
    { return video.dimensions.height }

    public function get skinScale() : Number
    { return video.modernFullscreenEnabled ? 2 : 1 }

    public function get running() : Boolean
    { return player.running }

    public function get streamLengthSeconds() : Number
    { return player.streamLength.seconds }

    public function get playheadRatio() : Number
    { return player.playheadRatio }

    public function get bufferRatio() : Number
    { return player.bufferRatio }

    public function get bufferFillRatio() : Number
    { return player.bufferFillRatio }

    public function get playing() : Boolean
    { return player.playing }

    public function get buffering() : Boolean
    { return player.buffering }

    public function get volume() : Number
    { return player.volume }

    // -----------------------------------------------------

    public function handleSkinPartMissing(name : String) : void
    {
      if (missingSkinParts.indexOf(name) == -1)
        $handleSkinPartMissing(name)
    }

    private function $handleSkinPartMissing(name : String) : void
    {
      debug("Error: Skin part missing: " + name)
      missingSkinParts.push(name)
    }
  }
}

Hide mouse cursor when idle.

Hide mouse cursor when idle.