package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.enum.FrustumCulling;
	import aerys.minko.type.math.Ray;

	use namespace minko_scene;

	/**
	 * Mesh objects are a visible instance of a Geometry rendered using a specific
	 * Effect with specific rendering properties.
	 *
	 *

	 * Those rendering properties are stored in a DataBindings object so they can
	 * be directly used by the shaders in the rendering API.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Mesh extends AbstractVisibleSceneNode implements ITaggable
	{
		public static const DEFAULT_MATERIAL	: Material	= new BasicMaterial();

		private var _ctrl				: MeshController;
		private var _properties			: DataProvider;
		private var _material			: Material;
		private var _bindings			: DataBindings;

		private var _visibility			: MeshVisibilityController;

		private var _cloned				: Signal;

		private var _tag				: uint = 1;

		/**
		 * A DataProvider object already bound to the Mesh bindings.
		 *
		 *

		 * // set the "diffuseColor" property to 0x0000ffff
		 * mesh.properties.diffuseColor = 0x0000ffff;
		 *
		 * // animate the "diffuseColor" property
		 * mesh.addController(
		 *   new AnimationController(
		 * 	  new <ITimeline>[new ColorTimeline(
		 * 	    "dataProvider.diffuseColor",
		 * 	    5000,
		 * 	    new <uint>[0xffffffff, 0xffffff00, 0xffffffff]
		 *    )]
		 *   )
		 * );
		 *

		 *
		 * @return
		 *
		 */
		public function get properties() : DataProvider
		{
			return _properties;
		}

		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get material() : Material
		{
			return _material;
		}
		public function set material(value : Material) : void
		{
			if (_material != value)
			{
				if (_material)
					_bindings.removeProvider(_material);

				var oldMaterial : Material = _material;

				_material = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		/**
		 * The rendering properties provided to the shaders to customize
		 * how the mesh will appear on screen.
		 *
		 * @return
		 *
		 */
		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The Geometry of the mesh.
		 * @return
		 *
		 */
		public function get geometry() : Geometry
		{
			return _ctrl.geometry;
		}

		public function set geometry(value : Geometry) : void
		{
			_ctrl.geometry = value;
		}

		public function get tag() : uint
		{
			return _tag;
		}

		public function set tag(value : uint) : void
		{
			if (_tag != value)
			{
				_tag = value;
				_properties.setProperty('tag', value);
			}
		}

        /**
         * Whether the mesh in inside the camera frustum or not.
         * @return
         *
         */
        public function get insideFrustum() : Boolean
        {
            return _visibility.insideFrustum;
        }

        override public function get computedVisibility() : Boolean
        {
            return scene
				? super.computedVisibility && _visibility.computedVisibility
				: super.computedVisibility;
        }

		public function get frustumCulling() : uint
		{
			return _visibility.frustumCulling;
		}
		public function set frustumCulling(value : uint) : void
		{
			_visibility.frustumCulling = value;
		}

		public function get cloned() : Signal
		{
			return _cloned;
		}

		public function get frame() : uint
		{
			return _ctrl.frame;
		}
		public function set frame(value : uint) : void
		{
			_ctrl.frame = value;
		}

		public function Mesh(geometry	: Geometry	= null,
							 material	: Material	= null,
							 name		: String	= null,
							 tag		: uint		= 1)
		{
			super();

			initializeMesh(geometry, material, name, tag);
		}

		override protected function initialize() : void
		{
			_bindings = new DataBindings(this);
			this.properties = new DataProvider(
				properties,
				'meshProperties',
				DataProviderUsage.EXCLUSIVE
			);

			super.initialize();
		}

		override protected function initializeSignals() : void
		{
			super.initializeSignals();

			_cloned = new Signal('Mesh.cloned');
		}

		override protected function initializeContollers() : void
		{
			super.initializeContollers();

			_ctrl = new MeshController();
			addController(_ctrl);

			_visibility = new MeshVisibilityController();
			_visibility.frustumCulling = FrustumCulling.ENABLED;
			addController(_visibility);
		}

		protected function initializeMesh(geometry	: Geometry,
										  material	: Material,
										  name		: String,
										  tag		: uint) : void
		{
			if (name)
				this.name = name;

			this.geometry	= geometry;
			this.material	= material || DEFAULT_MATERIAL;
			_tag			= tag;
		}

		public function cast(ray            : Ray,
                             maxDistance    : Number    = Number.POSITIVE_INFINITY,
                             tag            : uint      = 1) : Number
		{
			if (!(_tag & tag))
				return -1;

			return _ctrl.geometry.boundingBox.testRay(
				ray,
				getWorldToLocalTransform(),
				maxDistance
			);
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Mesh = new Mesh();

			clone.name 		= name;
			clone.geometry 	= _ctrl.geometry;

			clone.properties = DataProvider(_properties.clone());
			clone._bindings.copySharedProvidersFrom(_bindings);

			clone.transform.copyFrom(transform);
			clone.material = _material;

			this.cloned.execute(this, clone);

			return clone;
		}
	}
}

package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.enum.FrustumCulling;
	import aerys.minko.type.math.Ray;

	use namespace minko_scene;

	/**
	 * Mesh objects are a visible instance of a Geometry rendered using a specific
	 * Effect with specific rendering properties.
	 *
	 *

	 * Those rendering properties are stored in a DataBindings object so they can
	 * be directly used by the shaders in the rendering API.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Mesh extends AbstractVisibleSceneNode implements ITaggable
	{
		private var _ctrl				: MeshController;
		private var _properties			: DataProvider;
		private var _material			: Material;
		private var _bindings			: DataBindings;

		private var _visibility			: MeshVisibilityController;

		private var _cloned				: Signal;

		private var _tag				: uint = 1;

		/**
		 * A DataProvider object already bound to the Mesh bindings.
		 *
		 *

		 * // set the "diffuseColor" property to 0x0000ffff
		 * mesh.properties.diffuseColor = 0x0000ffff;
		 *
		 * // animate the "diffuseColor" property
		 * mesh.addController(
		 *   new AnimationController(
		 * 	  new <ITimeline>[new ColorTimeline(
		 * 	    "dataProvider.diffuseColor",
		 * 	    5000,
		 * 	    new <uint>[0xffffffff, 0xffffff00, 0xffffffff]
		 *    )]
		 *   )
		 * );
		 *

		 *
		 * @return
		 *
		 */
		public function get properties() : DataProvider
		{
			return _properties;
		}

		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get material() : Material
		{
			return _material;
		}
		public function set material(value : Material) : void
		{
			if (_material != value)
			{
				if (_material)
					_bindings.removeProvider(_material);

				var oldMaterial : Material = _material;

				_material = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		/**
		 * The rendering properties provided to the shaders to customize
		 * how the mesh will appear on screen.
		 *
		 * @return
		 *
		 */
		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The Geometry of the mesh.
		 * @return
		 *
		 */
		public function get geometry() : Geometry
		{
			return _ctrl.geometry;
		}

		public function set geometry(value : Geometry) : void
		{
			_ctrl.geometry = value;
		}

		public function get tag() : uint
		{
			return _tag;
		}

		public function set tag(value : uint) : void
		{
			if (_tag != value)
			{
				_tag = value;
				_properties.setProperty('tag', value);
			}
		}

        /**
         * Whether the mesh in inside the camera frustum or not.
         * @return
         *
         */
        public function get insideFrustum() : Boolean
        {
            return _visibility.insideFrustum;
        }

        override public function get computedVisibility() : Boolean
        {
            return scene
				? super.computedVisibility && _visibility.computedVisibility
				: super.computedVisibility;
        }

		public function get frustumCulling() : uint
		{
			return _visibility.frustumCulling;
		}
		public function set frustumCulling(value : uint) : void
		{
			_visibility.frustumCulling = value;
		}

		public function get cloned() : Signal
		{
			return _cloned;
		}

		public function get frame() : uint
		{
			return _ctrl.frame;
		}
		public function set frame(value : uint) : void
		{
			_ctrl.frame = value;
		}

		public function Mesh(geometry	: Geometry	= null,
							 material	: Material	= null,
							 name		: String	= null,
							 tag		: uint		= 1)
		{
			super();

			initializeMesh(geometry, material, name, tag);
		}

		override protected function initialize() : void
		{
			_bindings = new DataBindings(this);
			this.properties = new DataProvider(
				properties,
				'meshProperties',
				DataProviderUsage.EXCLUSIVE
			);

			super.initialize();
		}

		override protected function initializeSignals() : void
		{
			super.initializeSignals();

			_cloned = new Signal('Mesh.cloned');
		}

		override protected function initializeContollers() : void
		{
			super.initializeContollers();

			_ctrl = new MeshController();
			addController(_ctrl);

			_visibility = new MeshVisibilityController();
			_visibility.frustumCulling = FrustumCulling.ENABLED;
			addController(_visibility);
		}

		protected function initializeMesh(geometry	: Geometry,
										  material	: Material,
										  name		: String,
										  tag		: uint) : void
		{
			if (name)
				this.name = name;

			this.geometry	= geometry;
			this.material	= material;
			_tag			= tag;
		}

		public function cast(ray            : Ray,
                             maxDistance    : Number    = Number.POSITIVE_INFINITY,
                             tag            : uint      = 1) : Number
		{
			if (!(_tag & tag))
				return -1;

			return _ctrl.geometry.boundingBox.testRay(
				ray,
				getWorldToLocalTransform(),
				maxDistance
			);
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Mesh = new Mesh();

			clone.name 		= name;
			clone.geometry 	= _ctrl.geometry;

			clone.properties = DataProvider(_properties.clone());
			clone._bindings.copySharedProvidersFrom(_bindings);

			clone.transform.copyFrom(transform);
			clone.material = _material;

			this.cloned.execute(this, clone);

			return clone;
		}
	}
}

fix Mesh.initializeMesh() to stop using static reference to a default material to avoid memory leak: by default Mesh will now have a null material if none is provided


////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.core
{
	import org.apache.flex.events.Event;
	import org.apache.flex.events.EventDispatcher;

    /**
     *  The SimpleCSSStyles class contains CSS style
     *  properties supported by SimpleCSSValuesImpl.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SimpleCSSStyles
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SimpleCSSStyles()
		{
			super();
		}

		private var _object:IStyleableObject;

        /**
         *  The object to which these styles apply.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get object():IStyleableObject
		{
			return _object;
		}

        /**
         *  @private
         */
		public function set object(value:IStyleableObject):void
		{
			if (_object != value)
			{
                _object = value;
			}
		}

        public var top:*;
        public var bottom:*;
        public var left:*;
        public var right:*;
        public var padding:*;
		public var paddingLeft:*;
        public var paddingRight:*;
        public var paddingTop:*;
        public var paddingBottom:*;
        public var horizontalCenter:*;
        public var verticalCenter:*;
        public var horizontalAlign:*;
        public var verticalAlign:*;
        public var fontFamily:*;
        public var fontSize:*;
        public var color:*;
        public var backgroundColor:*;
        public var backgroundImage:*;
        public var borderColor:*;
        public var borderStyle:*;
        public var borderRadius:*;
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.core
{
	import org.apache.flex.events.Event;
	import org.apache.flex.events.EventDispatcher;

    /**
     *  The SimpleCSSStyles class contains CSS style
     *  properties supported by SimpleCSSValuesImpl.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SimpleCSSStyles
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SimpleCSSStyles()
		{
			super();
		}

		private var _object:IStyleableObject;

        /**
         *  The object to which these styles apply.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get object():IStyleableObject
		{
			return _object;
		}

        /**
         *  @private
         */
		public function set object(value:IStyleableObject):void
		{
			if (_object != value)
			{
                _object = value;
			}
		}

        public var top:*;
        public var bottom:*;
        public var left:*;
        public var right:*;
        public var padding:*;
		public var paddingLeft:*;
        public var paddingRight:*;
        public var paddingTop:*;
        public var paddingBottom:*;
        public var horizontalCenter:*;
        public var verticalCenter:*;
        public var horizontalAlign:*;
        public var verticalAlign:*;
        public var fontFamily:*;
        public var fontSize:*;
        public var color:*;
        public var backgroundAlpha:*;
        public var backgroundColor:*;
        public var backgroundImage:*;
        public var borderColor:*;
        public var borderStyle:*;
        public var borderRadius:*;
	}
}

add more style props

add more style props


package aerys.minko.type
{
	public final class Signal
	{
		private var _name			: String			= null;

		private var _callbacks		: Array				= [];
		private var _numCallbacks	: uint				= 0;

		private var _executed		: Boolean			= false;
		private var _numAdded		: uint				= 0;
		private var _toAdd			: Array				= null;
		private var _numRemoved		: uint				= 0;
		private var _toRemove		: Array				= null;

		public function get numCallbacks() : uint
		{
			return _numCallbacks;
		}

		public function Signal(name : String)
		{
			_name = name;
		}

		public function removeAllCallback() : void
		{
			for (var callbackIndex : int = _numCallbacks - 1; callbackIndex >= 0; --callbackIndex)
				remove(_callbacks[callbackIndex]);
		}

		public function add(callback : Function) : void
		{
			if (_executed)
			{
				if (_toAdd)
					_toAdd.push(callback);
				else
					_toAdd = [callback];
				++_numAdded;

				return ;
			}

			_callbacks[_numCallbacks] = callback;
			++_numCallbacks;
		}

		public function remove(callback : Function) : void
		{
			if (_executed)
			{
				if (_toRemove)
					_toRemove.push(callback);
				else
					_toRemove = [callback];
				++_numRemoved;

				return ;
			}

			var index : int = _callbacks.indexOf(callback);

			if (index < 0)
				throw new Error("This callback does not exist.");

			--_numCallbacks;
			_callbacks[index] = _callbacks[_numCallbacks];
			_callbacks.length = _numCallbacks;
		}

		public function hasCallback(callback : Function) : Boolean
		{
			return _callbacks.indexOf(callback) >= 0;
		}

		public function execute(...params) : void
		{
			_executed = true;
			for (var i : uint = 0; i < _numCallbacks; ++i)
				(_callbacks[i] as Function).apply(null, params);
			_executed = false;

			for (i = 0; i < _numAdded; ++i)
				add(_toAdd[i]);
			_numAdded = 0;
			_toAdd = null;

			for (i = 0; i < _numRemoved; ++i)
				remove(_toRemove[i]);
			_numRemoved = 0;
			_toRemove = null;
		}
	}
}

package aerys.minko.type
{
	public final class Signal
	{
		private var _name			: String			= null;

		private var _callbacks		: Array				= [];
		private var _numCallbacks	: uint				= 0;

		private var _executed		: Boolean			= false;
		private var _numAdded		: uint				= 0;
		private var _toAdd			: Array				= null;
		private var _numRemoved		: uint				= 0;
		private var _toRemove		: Array				= null;

		public function get numCallbacks() : uint
		{
			return _numCallbacks;
		}

		public function Signal(name : String)
		{
			_name = name;
		}

		public function add(callback : Function) : void
		{
			if (_executed)
			{
				if (_toAdd)
					_toAdd.push(callback);
				else
					_toAdd = [callback];
				++_numAdded;

				return ;
			}

			_callbacks[_numCallbacks] = callback;
			++_numCallbacks;
		}

		public function remove(callback : Function) : void
		{
			if (_executed)
			{
				if (_toRemove)
					_toRemove.push(callback);
				else
					_toRemove = [callback];
				++_numRemoved;

				return ;
			}

			var index : int = _callbacks.indexOf(callback);

			if (index < 0)
				throw new Error("This callback does not exist.");

			--_numCallbacks;
			_callbacks[index] = _callbacks[_numCallbacks];
			_callbacks.length = _numCallbacks;
		}

		public function hasCallback(callback : Function) : Boolean
		{
			return _callbacks.indexOf(callback) >= 0;
		}

		public function execute(...params) : void
		{
			_executed = true;
			for (var i : uint = 0; i < _numCallbacks; ++i)
				(_callbacks[i] as Function).apply(null, params);
			_executed = false;

			for (i = 0; i < _numAdded; ++i)
				add(_toAdd[i]);
			_numAdded = 0;
			_toAdd = null;

			for (i = 0; i < _numRemoved; ++i)
				remove(_toRemove[i]);
			_numRemoved = 0;
			_toRemove = null;
		}
	}
}

Remove 'removeAllCallback' function from signal file


package com.freshplanet.nativeExtensions
{
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

    public class PushNotification extends EventDispatcher
	{

		public static const RECURRENCE_NONE:int   = 0;
		public static const RECURRENCE_DAILY:int  = 1;
		public static const RECURRENCE_WEEK:int   = 2;
		public static const RECURRENCE_MONTH:int  = 3;
		public static const RECURRENCE_YEAR:int   = 4;

		public static const DEFAULT_LOCAL_NOTIFICATION_ID:int = 0;

		private static var extCtx:ExtensionContext = null;

        private static var _instance:PushNotification;

        public function PushNotification()
		{
			if (!_instance)
			{
				if (this.isPushNotificationSupported)
				{

					extCtx = ExtensionContext.createExtensionContext("com.freshplanet.AirPushNotification", null);

					if (extCtx != null)
					{
						extCtx.addEventListener(StatusEvent.STATUS, onStatus);
					} else
					{
						trace('extCtx is null.');
					}
				}
				_instance = this;
			}
			else
			{
				throw Error( 'This is a singleton, use getInstance, do not call the constructor directly');
			}
		}

		public function get isPushNotificationSupported():Boolean
		{
			var result:Boolean = (Capabilities.manufacturer.search('iOS') > -1 || Capabilities.manufacturer.search('Android') > -1);
			return result;
		}

		/**
		 *  return true if notifs are enabled for this app in device settings
		 *  If iOS < 8 or android < 4.1 this isn't available, so will always return true.
		 */
		public function get notificationsEnabled():Boolean
		{
			if(!isPushNotificationSupported) {
				return false;
			}
			return extCtx.call("getNotificationsEnabled");
		}

		/**
		 * return true if OS permits sending user to settings (iOS 8, Android
		 */
		public function get canSendUserToSettings():Boolean
		{
			if(!isPushNotificationSupported) {
				return false;
			}
			return extCtx.call("getCanSendUserToSettings");
		}

		public function openDeviceNotificationSettions():void
		{
			if(isPushNotificationSupported) {
				extCtx.call("openDeviceNotificationSettings");
			}
		}

		public static function getInstance() : PushNotification
		{
			return _instance ? _instance : new PushNotification();
		}

        /**
		 * Needs Project id for Android Notifications.
		 * The project id is the one the developer used to register to gcm.
		 * @param projectId: project id to use
		 */
		public function registerForPushNotification(projectId:String = null) : void
		{
			if (this.isPushNotificationSupported)
			{
				extCtx.call("registerPush", projectId);
			}
		}

		public function setBadgeNumberValue(value:int):void
		{
			if (this.isPushNotificationSupported)
			{
				extCtx.call("setBadgeNb", value);
			}
		}

		/**
		 * Sends a local notification to the device.
		 * @param message the local notification text displayed
		 * @param timestamp when the local notification should appear (in sec)
		 * @param title (Android Only) Title of the local notification
		 * @param recurrenceType
		 *
		 */
		public function sendLocalNotification(message:String, timestamp:int, title:String, recurrenceType:int = RECURRENCE_NONE,  notificationId:int = DEFAULT_LOCAL_NOTIFICATION_ID):void
		{
			if (this.isPushNotificationSupported)
			{
				if (notificationId == DEFAULT_LOCAL_NOTIFICATION_ID)
       			{
           			extCtx.call("sendLocalNotification", message, timestamp, title, recurrenceType);
         		} else
         		{
           			extCtx.call("sendLocalNotification", message, timestamp, title, recurrenceType, notificationId);
         		}
			}
		}

		/**
      	* Not implemented on Android for now.
      	* @param notificationId
      	*
	  	*/
	    public function cancelLocalNotification(notificationId:int = DEFAULT_LOCAL_NOTIFICATION_ID):void
	    {
	       	if (this.isPushNotificationSupported)
	       	{
	         	if (notificationId == DEFAULT_LOCAL_NOTIFICATION_ID)
	         	{
	           		extCtx.call("cancelLocalNotification");
	         	} else
	         	{
	           		extCtx.call("cancelLocalNotification", notificationId);
	         	}
	       	}
	    }

		public function cancelAllLocalNotifications():void
		{
			if(this.isPushNotificationSupported)
			{
				extCtx.call("cancelAllLocalNotifications");
			}
		}

		public function setIsAppInForeground(value:Boolean):void
		{
			if (this.isPushNotificationSupported)
			{
				extCtx.call("setIsAppInForeground", value);
			}
		}

		public function addListenerForStarterNotifications(listener:Function):void
		{
			if (this.isPushNotificationSupported)
			{
				this.addEventListener(PushNotificationEvent.APP_STARTING_FROM_NOTIFICATION_EVENT, listener);
				extCtx.call("fetchStarterNotification");
			}
		}

        // onStatus()
        // Event handler for the event that the native implementation dispatches.
        //
        private function onStatus(e:StatusEvent):void
		{
			if (this.isPushNotificationSupported)
			{

				var event : PushNotificationEvent;
				var data:String = e.level;
				switch (e.code)
				{
					case "TOKEN_SUCCESS":
						event = new PushNotificationEvent( PushNotificationEvent.PERMISSION_GIVEN_WITH_TOKEN_EVENT );
						event.token = e.level;
						break;
					case "TOKEN_FAIL":
						event = new PushNotificationEvent( PushNotificationEvent.PERMISSION_REFUSED_EVENT );
						event.errorCode = "NativeCodeError";
						event.errorMessage = e.level;
						break;
					case "COMING_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.COMING_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "APP_STARTING_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.APP_STARTING_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "APP_BROUGHT_TO_FOREGROUND_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.APP_BROUGHT_TO_FOREGROUND_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "APP_STARTED_IN_BACKGROUND_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.APP_STARTED_IN_BACKGROUND_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "NOTIFICATION_RECEIVED_WHEN_IN_FOREGROUND":
						event = new PushNotificationEvent( PushNotificationEvent.NOTIFICATION_RECEIVED_WHEN_IN_FOREGROUND_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "NOTIFICATION_SETTINGS_ENABLED":
						event = new PushNotificationEvent(PushNotificationEvent.NOTIFICATION_SETTINGS_ENABLED);
						break;
					case "NOTIFICATION_SETTINGS_DISABLED":
						event = new PushNotificationEvent(PushNotificationEvent.NOTIFICATION_SETTINGS_DISABLED);
						break;

					case "LOGGING":
						trace(e, e.level);
						break;
				}

				if (event != null)
				{
					this.dispatchEvent( event );
				}
			}
		}

	}
}

package com.freshplanet.nativeExtensions
{
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

    public class PushNotification extends EventDispatcher
	{

		public static const RECURRENCE_NONE:int   = 0;
		public static const RECURRENCE_DAILY:int  = 1;
		public static const RECURRENCE_WEEK:int   = 2;
		public static const RECURRENCE_MONTH:int  = 3;
		public static const RECURRENCE_YEAR:int   = 4;

		public static const DEFAULT_LOCAL_NOTIFICATION_ID:int = 0;

		private static var extCtx:ExtensionContext = null;

        private static var _instance:PushNotification;

        public function PushNotification()
		{
			if (!_instance)
			{
				if (this.isPushNotificationSupported)
				{

					extCtx = ExtensionContext.createExtensionContext("com.freshplanet.AirPushNotification", null);

					if (extCtx != null)
					{
						extCtx.addEventListener(StatusEvent.STATUS, onStatus);
					} else
					{
						trace('extCtx is null.');
					}
				}
				_instance = this;
			}
			else
			{
				throw Error( 'This is a singleton, use getInstance, do not call the constructor directly');
			}
		}

		public function get isPushNotificationSupported():Boolean
		{
			var result:Boolean = (Capabilities.manufacturer.search('iOS') > -1 || Capabilities.manufacturer.search('Android') > -1);
			return result;
		}

		/**
		 *  return true if notifs are enabled for this app in device settings
		 *  If iOS < 8 or android < 4.1 this isn't available, so will always return true.
		 */
		public function get notificationsEnabled():Boolean
		{
			if(!isPushNotificationSupported) {
				return false;
			}
			return extCtx.call("getNotificationsEnabled");
		}

		/**
		 * return true if OS permits sending user to settings (iOS 8, Android
		 */
		public function get canSendUserToSettings():Boolean
		{
			if(!isPushNotificationSupported) {
				return false;
			}
			return extCtx.call("getCanSendUserToSettings");
		}

		public function openDeviceNotificationSettings():void
		{
			if(isPushNotificationSupported) {
				extCtx.call("openDeviceNotificationSettings");
			}
		}

		public static function getInstance() : PushNotification
		{
			return _instance ? _instance : new PushNotification();
		}

        /**
		 * Needs Project id for Android Notifications.
		 * The project id is the one the developer used to register to gcm.
		 * @param projectId: project id to use
		 */
		public function registerForPushNotification(projectId:String = null) : void
		{
			if (this.isPushNotificationSupported)
			{
				extCtx.call("registerPush", projectId);
			}
		}

		public function setBadgeNumberValue(value:int):void
		{
			if (this.isPushNotificationSupported)
			{
				extCtx.call("setBadgeNb", value);
			}
		}

		/**
		 * Sends a local notification to the device.
		 * @param message the local notification text displayed
		 * @param timestamp when the local notification should appear (in sec)
		 * @param title (Android Only) Title of the local notification
		 * @param recurrenceType
		 *
		 */
		public function sendLocalNotification(message:String, timestamp:int, title:String, recurrenceType:int = RECURRENCE_NONE,  notificationId:int = DEFAULT_LOCAL_NOTIFICATION_ID):void
		{
			if (this.isPushNotificationSupported)
			{
				if (notificationId == DEFAULT_LOCAL_NOTIFICATION_ID)
       			{
           			extCtx.call("sendLocalNotification", message, timestamp, title, recurrenceType);
         		} else
         		{
           			extCtx.call("sendLocalNotification", message, timestamp, title, recurrenceType, notificationId);
         		}
			}
		}

		/**
      	* Not implemented on Android for now.
      	* @param notificationId
      	*
	  	*/
	    public function cancelLocalNotification(notificationId:int = DEFAULT_LOCAL_NOTIFICATION_ID):void
	    {
	       	if (this.isPushNotificationSupported)
	       	{
	         	if (notificationId == DEFAULT_LOCAL_NOTIFICATION_ID)
	         	{
	           		extCtx.call("cancelLocalNotification");
	         	} else
	         	{
	           		extCtx.call("cancelLocalNotification", notificationId);
	         	}
	       	}
	    }

		public function cancelAllLocalNotifications():void
		{
			if(this.isPushNotificationSupported)
			{
				extCtx.call("cancelAllLocalNotifications");
			}
		}

		public function setIsAppInForeground(value:Boolean):void
		{
			if (this.isPushNotificationSupported)
			{
				extCtx.call("setIsAppInForeground", value);
			}
		}

		public function addListenerForStarterNotifications(listener:Function):void
		{
			if (this.isPushNotificationSupported)
			{
				this.addEventListener(PushNotificationEvent.APP_STARTING_FROM_NOTIFICATION_EVENT, listener);
				extCtx.call("fetchStarterNotification");
			}
		}

        // onStatus()
        // Event handler for the event that the native implementation dispatches.
        //
        private function onStatus(e:StatusEvent):void
		{
			if (this.isPushNotificationSupported)
			{

				var event : PushNotificationEvent;
				var data:String = e.level;
				switch (e.code)
				{
					case "TOKEN_SUCCESS":
						event = new PushNotificationEvent( PushNotificationEvent.PERMISSION_GIVEN_WITH_TOKEN_EVENT );
						event.token = e.level;
						break;
					case "TOKEN_FAIL":
						event = new PushNotificationEvent( PushNotificationEvent.PERMISSION_REFUSED_EVENT );
						event.errorCode = "NativeCodeError";
						event.errorMessage = e.level;
						break;
					case "COMING_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.COMING_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "APP_STARTING_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.APP_STARTING_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "APP_BROUGHT_TO_FOREGROUND_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.APP_BROUGHT_TO_FOREGROUND_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "APP_STARTED_IN_BACKGROUND_FROM_NOTIFICATION":
						event = new PushNotificationEvent( PushNotificationEvent.APP_STARTED_IN_BACKGROUND_FROM_NOTIFICATION_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "NOTIFICATION_RECEIVED_WHEN_IN_FOREGROUND":
						event = new PushNotificationEvent( PushNotificationEvent.NOTIFICATION_RECEIVED_WHEN_IN_FOREGROUND_EVENT );
						if (data != null)
						{
							try
							{
								event.parameters = JSON.parse(data);
							} catch (error:Error)
							{
								trace("[PushNotification Error]", "cannot parse the params string", data);
							}
						}
						break;
					case "NOTIFICATION_SETTINGS_ENABLED":
						event = new PushNotificationEvent(PushNotificationEvent.NOTIFICATION_SETTINGS_ENABLED);
						break;
					case "NOTIFICATION_SETTINGS_DISABLED":
						event = new PushNotificationEvent(PushNotificationEvent.NOTIFICATION_SETTINGS_DISABLED);
						break;

					case "LOGGING":
						trace(e, e.level);
						break;
				}

				if (event != null)
				{
					this.dispatchEvent( event );
				}
			}
		}

	}
}

fix typo

fix typo


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;
    public final class HLSSettings extends Object {
        /**
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;

        /**
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;

        // // // // // ///////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // ///////////////////////////////////
        /**
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;
        /**
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 60.
         */
        public static var maxBufferLength : Number = 60;
        /**
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3.
         */
        public static var lowBufferLength : Number = 3;
        /**
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *      HLSSeekMode.SEGMENT_SEEK - segment based seeking (seek to beginning of segment containing requested seek position)
         *
         * Default is HLSSeekMode.ACCURATE_SEEK.
         */
        public static var seekMode : String = HLSSeekMode.ACCURATE_SEEK;
        /** max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var keyLoadMaxRetry : int = -1;

        /** keyLoadMaxRetryTimeout

         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;
        /** max nb of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var fragmentLoadMaxRetry : int = -1;

        /** fragmentLoadMaxRetryTimeout

         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 64000;
        /**
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;
        /** max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = -1;
        /** manifestLoadMaxRetryTimeout

         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;

        /**
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         */
        public static var startFromBitrate : Number = -1;

        /** start level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         */
        public static var startFromLevel : Number = -1;

        /** seek level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         */
        public static var seekFromLevel : Number = -1;

        /** use hardware video decoder :
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         */
        public static var useHardwareVideoDecoder : Boolean = true;

        /**
         * Defines whether INFO level log messages will will appear in the console
         * Default is true.
         */
        public static var logInfo : Boolean = true;
        /**
         * Defines whether DEBUG level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug : Boolean = false;
        /**
         * Defines whether DEBUG2 level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug2 : Boolean = false;
        /**
         * Defines whether WARN level log messages will will appear in the console
         * Default is true.
         */
        public static var logWarn : Boolean = true;
        /**
         * Defines whether ERROR level log messages will will appear in the console
         * Default is true.
         */
        public static var logError : Boolean = true;
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;
    public final class HLSSettings extends Object {
        /**
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;

        /**
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;

        // // // // // ///////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // ///////////////////////////////////
        /**
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;
        /**
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 60.
         */
        public static var maxBufferLength : Number = 60;
        /**
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3.
         */
        public static var lowBufferLength : Number = 3;
        /**
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *      HLSSeekMode.SEGMENT_SEEK - segment based seeking (seek to beginning of segment containing requested seek position)
         *
         * Default is HLSSeekMode.ACCURATE_SEEK.
         */
        public static var seekMode : String = HLSSeekMode.ACCURATE_SEEK;
        /** max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var keyLoadMaxRetry : int = -1;

        /** keyLoadMaxRetryTimeout

         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;
        /** max nb of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var fragmentLoadMaxRetry : int = -1;

        /** fragmentLoadMaxRetryTimeout

         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 64000;
        /**
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;
        /** max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = -1;
        /** manifestLoadMaxRetryTimeout

         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;

        /**
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         */
        public static var startFromBitrate : Number = -1;

        /** start level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         */
        public static var startFromLevel : Number = -1;

        /** seek level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         */
        public static var seekFromLevel : Number = -1;

        /** use hardware video decoder :
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         */
        public static var useHardwareVideoDecoder : Boolean = false;

        /**
         * Defines whether INFO level log messages will will appear in the console
         * Default is true.
         */
        public static var logInfo : Boolean = true;
        /**
         * Defines whether DEBUG level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug : Boolean = false;
        /**
         * Defines whether DEBUG2 level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug2 : Boolean = false;
        /**
         * Defines whether WARN level log messages will will appear in the console
         * Default is true.
         */
        public static var logWarn : Boolean = true;
        /**
         * Defines whether ERROR level log messages will will appear in the console
         * Default is true.
         */
        public static var logError : Boolean = true;
    }
}

disable video hardware acceleration by default causing too much trouble :-(


package aerys.minko.type.data
{
	import aerys.minko.type.Signal;

	import flash.utils.Dictionary;

	public class DataBindings
	{
		private var _bindingNames					: Vector.<String>	= new Vector.<String>();

		private var _bindingNameToValue				: Object			= {};
		private var _bindingNameToChangedSignal		: Object			= {};

		private var _providerToBindingNames			: Dictionary		= new Dictionary(); // dic[Vector.<String>[]]
		private var _attributeToProviders			: Dictionary		= new Dictionary(); // dic[Vector.<IDataProvider>[]]
		private var _attributeToProvidersAttrNames	: Dictionary		= new Dictionary(); // dic[Vector.<String>[]]

		private var _numProviders					: uint 					= 0;
		private var _providers						: Vector.<DataProvider> = new <DataProvider>[];

		public function get numProviders() : uint
		{
			return _numProviders;
		}

		public function get numProperties() : uint
		{
			return _bindingNames.length;
		}

		public function DataBindings()
		{
		}

		public function add(provider : IDataProvider) : void
		{
			if (_providerToBindingNames[provider])
				throw new Error('This provider is already binded');

			var providerBindingNames	: Vector.<String>	= new Vector.<String>();
			var dataDescriptor			: Object			= provider.dataDescriptor;

			provider.changed.add(onProviderChange);

			for (var attrName : String in dataDescriptor)
			{
				// if this provider attribute is also a dataprovider, let's also bind it
				var bindingName	: String		= dataDescriptor[attrName];
				var attribute	: Object		= provider[attrName]
				var dpAttribute	: IDataProvider	= attribute as IDataProvider;

				if (_bindingNames.indexOf(bindingName) != -1)
					throw new Error('Another Dataprovider is already declaring "' + bindingName + '". Cannot overwrite.');

				if (dpAttribute != null)
				{
					dpAttribute.changed.add(onProviderAttributeChange);

					if (!_attributeToProviders[dpAttribute])
						_attributeToProviders[dpAttribute] = new <IDataProvider>[];

					if (!_attributeToProvidersAttrNames[dpAttribute])
						_attributeToProvidersAttrNames[dpAttribute] = new <String>[];

					_attributeToProviders[dpAttribute].push(provider);
					_attributeToProvidersAttrNames[dpAttribute].push(attrName);
				}

				_bindingNameToValue[bindingName] = attribute;

				providerBindingNames.push(bindingName);
				_bindingNames.push(bindingName);

				getPropertyChangedSignal(bindingName).execute(this, bindingName, attribute);
			}

			_providerToBindingNames[provider] = providerBindingNames;
			_providers.push(provider);
			++_numProviders;
		}

		public function remove(provider : IDataProvider) : void
		{
			var bindingNames : Vector.<String> = _providerToBindingNames[provider];

			if (bindingNames == null)
				throw new ArgumentError('No such provider was binded');

			for each (var bindingName : String in bindingNames)
			{
				var indexOf : int = _bindingNames.indexOf(bindingName);

				_bindingNames.splice(indexOf, 1);

				if (_bindingNameToValue[bindingName] is IDataProvider)
					IDataProvider(_bindingNameToValue[bindingName]).changed.remove(onProviderAttributeChange);

				delete _bindingNameToValue[bindingName];
			}

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

			for (var attribute : Object in _attributeToProviders)
			{
				var providers		: Vector.<IDataProvider>	= _attributeToProviders[attribute];
				var attrNames		: Vector.<String>			= _attributeToProvidersAttrNames[attribute];
				var indexOfProvider	: int						= providers.indexOf(provider);

				if (indexOfProvider != -1)
				{
					providers.splice(indexOfProvider, 1);
					attrNames.splice(indexOfProvider, 1);
				}

				if (providers.length == 0)
					attributesToDelete.push(attribute);
			}

			for (var attributeToDelete : Object in attributesToDelete)
			{
				delete _attributeToProviders[attributeToDelete];
				delete _attributeToProvidersAttrNames[attributeToDelete];
			}

			provider.changed.remove(onProviderChange);

			--_numProviders;
			_providers.splice(_providers.indexOf(provider), 1);

			delete _providerToBindingNames[provider];

			for each (bindingName in bindingNames)
				getPropertyChangedSignal(bindingName).execute(this, bindingName, null);
		}

		public function getDataProviderAt(index : uint) : DataProvider
		{
			if (index >= _numProviders)
				throw new ArgumentError('Index out of bounds.');

			return _providers[index];
		}

		public function getPropertyChangedSignal(bindingName : String) : Signal
		{
			if (!_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName] = new Signal('DataBindings.changed[' + bindingName + ']');

			return _bindingNameToChangedSignal[bindingName];
		}

		public function propertyExists(bindingName : String) : Boolean
		{
			return _bindingNameToValue.hasOwnProperty(bindingName);
		}

		public function getProperty(bindingName : String) : *
		{
			return _bindingNameToValue[bindingName];
		}

		public function getPropertyName(bindingIndex : uint) : String
		{
			if (bindingIndex > numProperties)
				throw new ArgumentError('No such binding');

			return _bindingNames[bindingIndex];
		}

		/**
		 * A provider attribute has changed, and the provider tells us.
		 * For example, camera.fov has changed, the camera dispatches a 'changed' signal with 'fov' as attributeName.
		 *
		 * It could also be that camera.localToWorld has been replaced by another matrix instance.
		 */
		private function onProviderChange(source : IDataProvider, attributeName : String) : void
		{
			if (attributeName == null)
			{
				throw new Error('DataProviders must change one property at a time.');
			}
			else if (attributeName == 'dataDescriptor')
			{
				remove(source);
				add(source);
			}
			else
			{
				var bindingName : String		= source.dataDescriptor[attributeName];
				var oldDpValue	: IDataProvider	= _bindingNameToValue[bindingName] as IDataProvider;
				var newValue	: Object		= source[attributeName];
				var newDpValue	: IDataProvider	= newValue as IDataProvider;

				// we are replacing a data provider. We must remove listeners and related mapping keys
				if (oldDpValue != null)
				{
					oldDpValue.changed.remove(onProviderAttributeChange);

					var providers	: Vector.<IDataProvider>	= _attributeToProviders[oldDpValue];
					var attrNames	: Vector.<String>			= _attributeToProvidersAttrNames[oldDpValue];

					if (providers.length == 1)
					{
						delete _attributeToProviders[oldDpValue];
						delete _attributeToProvidersAttrNames[oldDpValue];
					}
					else
					{
						var index : uint = providers.indexOf(source);
						providers.splice(index, 1);
						attrNames.splice(index, 1);
					}
				}

				// the new value for this key is a dataprovider, we must listen changes.
				if (newDpValue != null)
				{
					newDpValue.changed.add(onProviderAttributeChange);

					if (!_attributeToProviders[newDpValue])
						_attributeToProviders[newDpValue] = new <IDataProvider>[];

					if (!_attributeToProvidersAttrNames[newDpValue])
						_attributeToProvidersAttrNames[newDpValue] = new <String>[];

					_attributeToProviders[newDpValue].push(source);
					_attributeToProvidersAttrNames[newDpValue].push(attributeName);
				}

				_bindingNameToValue[bindingName] = newValue;

				getPropertyChangedSignal(bindingName).execute(this, bindingName, newValue);
			}
		}

		/**
		 * A provider attribute has changed, and the attribute tells us.
		 * For example, camera.localToWorld has been updated.
		 */
		private function onProviderAttributeChange(source : IDataProvider, key : String) : void
		{
			var providers		: Vector.<IDataProvider>	= _attributeToProviders[source];
			var attrNames		: Vector.<String>			= _attributeToProvidersAttrNames[source];
			var numProviders	: uint						= providers.length;

			for (var providerId : uint = 0; providerId < numProviders; ++providerId)
			{
				var provider	: IDataProvider	= providers[providerId];
				var attrName	: String		= attrNames[providerId];
				var bindingName : String		= provider.dataDescriptor[attrName];

				getPropertyChangedSignal(bindingName).execute(this, bindingName, source);
			}
		}
	}
}

package aerys.minko.type.data
{
	import aerys.minko.type.Signal;

	import flash.utils.Dictionary;

	public class DataBindings
	{
		private var _bindingNames					: Vector.<String>	= new Vector.<String>();

		private var _bindingNameToValue				: Object			= {};
		private var _bindingNameToChangedSignal		: Object			= {};

		private var _providerToBindingNames			: Dictionary		= new Dictionary(); // dic[Vector.<String>[]]
		private var _attributeToProviders			: Dictionary		= new Dictionary(); // dic[Vector.<IDataProvider>[]]
		private var _attributeToProvidersAttrNames	: Dictionary		= new Dictionary(); // dic[Vector.<String>[]]

		private var _numProviders					: uint 						= 0;
		private var _providers						: Vector.<IDataProvider> 	= new <IDataProvider>[];

		public function get numProviders() : uint
		{
			return _numProviders;
		}

		public function get numProperties() : uint
		{
			return _bindingNames.length;
		}

		public function DataBindings()
		{
		}

		public function add(provider : IDataProvider) : void
		{
			if (_providerToBindingNames[provider])
				throw new Error('This provider is already binded');

			var providerBindingNames	: Vector.<String>	= new Vector.<String>();
			var dataDescriptor			: Object			= provider.dataDescriptor;

			provider.changed.add(onProviderChange);

			for (var attrName : String in dataDescriptor)
			{
				// if this provider attribute is also a dataprovider, let's also bind it
				var bindingName	: String		= dataDescriptor[attrName];
				var attribute	: Object		= provider[attrName]
				var dpAttribute	: IDataProvider	= attribute as IDataProvider;

				if (_bindingNames.indexOf(bindingName) != -1)
					throw new Error('Another Dataprovider is already declaring "' + bindingName + '". Cannot overwrite.');

				if (dpAttribute != null)
				{
					dpAttribute.changed.add(onProviderAttributeChange);

					if (!_attributeToProviders[dpAttribute])
						_attributeToProviders[dpAttribute] = new <IDataProvider>[];

					if (!_attributeToProvidersAttrNames[dpAttribute])
						_attributeToProvidersAttrNames[dpAttribute] = new <String>[];

					_attributeToProviders[dpAttribute].push(provider);
					_attributeToProvidersAttrNames[dpAttribute].push(attrName);
				}

				_bindingNameToValue[bindingName] = attribute;

				providerBindingNames.push(bindingName);
				_bindingNames.push(bindingName);

				getPropertyChangedSignal(bindingName).execute(this, bindingName, attribute);
			}

			_providerToBindingNames[provider] = providerBindingNames;
			_providers.push(provider);
			++_numProviders;
		}

		public function remove(provider : IDataProvider) : void
		{
			var bindingNames : Vector.<String> = _providerToBindingNames[provider];

			if (bindingNames == null)
				throw new ArgumentError('No such provider was binded');

			for each (var bindingName : String in bindingNames)
			{
				var indexOf : int = _bindingNames.indexOf(bindingName);

				_bindingNames.splice(indexOf, 1);

				if (_bindingNameToValue[bindingName] is IDataProvider)
					IDataProvider(_bindingNameToValue[bindingName]).changed.remove(onProviderAttributeChange);

				delete _bindingNameToValue[bindingName];
			}

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

			for (var attribute : Object in _attributeToProviders)
			{
				var providers		: Vector.<IDataProvider>	= _attributeToProviders[attribute];
				var attrNames		: Vector.<String>			= _attributeToProvidersAttrNames[attribute];
				var indexOfProvider	: int						= providers.indexOf(provider);

				if (indexOfProvider != -1)
				{
					providers.splice(indexOfProvider, 1);
					attrNames.splice(indexOfProvider, 1);
				}

				if (providers.length == 0)
					attributesToDelete.push(attribute);
			}

			for (var attributeToDelete : Object in attributesToDelete)
			{
				delete _attributeToProviders[attributeToDelete];
				delete _attributeToProvidersAttrNames[attributeToDelete];
			}

			provider.changed.remove(onProviderChange);

			--_numProviders;
			_providers.splice(_providers.indexOf(provider), 1);

			delete _providerToBindingNames[provider];

			for each (bindingName in bindingNames)
				getPropertyChangedSignal(bindingName).execute(this, bindingName, null);
		}

		public function getDataProviderAt(index : uint) : IDataProvider
		{
			if (index >= _numProviders)
				throw new ArgumentError('Index out of bounds.');

			return _providers[index];
		}

		public function getPropertyChangedSignal(bindingName : String) : Signal
		{
			if (!_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName] = new Signal('DataBindings.changed[' + bindingName + ']');

			return _bindingNameToChangedSignal[bindingName];
		}

		public function propertyExists(bindingName : String) : Boolean
		{
			return _bindingNameToValue.hasOwnProperty(bindingName);
		}

		public function getProperty(bindingName : String) : *
		{
			return _bindingNameToValue[bindingName];
		}

		public function getPropertyName(bindingIndex : uint) : String
		{
			if (bindingIndex > numProperties)
				throw new ArgumentError('No such binding');

			return _bindingNames[bindingIndex];
		}

		/**
		 * A provider attribute has changed, and the provider tells us.
		 * For example, camera.fov has changed, the camera dispatches a 'changed' signal with 'fov' as attributeName.
		 *
		 * It could also be that camera.localToWorld has been replaced by another matrix instance.
		 */
		private function onProviderChange(source : IDataProvider, attributeName : String) : void
		{
			if (attributeName == null)
			{
				throw new Error('DataProviders must change one property at a time.');
			}
			else if (attributeName == 'dataDescriptor')
			{
				remove(source);
				add(source);
			}
			else
			{
				var bindingName : String		= source.dataDescriptor[attributeName];
				var oldDpValue	: IDataProvider	= _bindingNameToValue[bindingName] as IDataProvider;
				var newValue	: Object		= source[attributeName];
				var newDpValue	: IDataProvider	= newValue as IDataProvider;

				// we are replacing a data provider. We must remove listeners and related mapping keys
				if (oldDpValue != null)
				{
					oldDpValue.changed.remove(onProviderAttributeChange);

					var providers	: Vector.<IDataProvider>	= _attributeToProviders[oldDpValue];
					var attrNames	: Vector.<String>			= _attributeToProvidersAttrNames[oldDpValue];

					if (providers.length == 1)
					{
						delete _attributeToProviders[oldDpValue];
						delete _attributeToProvidersAttrNames[oldDpValue];
					}
					else
					{
						var index : uint = providers.indexOf(source);
						providers.splice(index, 1);
						attrNames.splice(index, 1);
					}
				}

				// the new value for this key is a dataprovider, we must listen changes.
				if (newDpValue != null)
				{
					newDpValue.changed.add(onProviderAttributeChange);

					if (!_attributeToProviders[newDpValue])
						_attributeToProviders[newDpValue] = new <IDataProvider>[];

					if (!_attributeToProvidersAttrNames[newDpValue])
						_attributeToProvidersAttrNames[newDpValue] = new <String>[];

					_attributeToProviders[newDpValue].push(source);
					_attributeToProvidersAttrNames[newDpValue].push(attributeName);
				}

				_bindingNameToValue[bindingName] = newValue;

				getPropertyChangedSignal(bindingName).execute(this, bindingName, newValue);
			}
		}

		/**
		 * A provider attribute has changed, and the attribute tells us.
		 * For example, camera.localToWorld has been updated.
		 */
		private function onProviderAttributeChange(source : IDataProvider, key : String) : void
		{
			var providers		: Vector.<IDataProvider>	= _attributeToProviders[source];
			var attrNames		: Vector.<String>			= _attributeToProvidersAttrNames[source];
			var numProviders	: uint						= providers.length;

			for (var providerId : uint = 0; providerId < numProviders; ++providerId)
			{
				var provider	: IDataProvider	= providers[providerId];
				var attrName	: String		= attrNames[providerId];
				var bindingName : String		= provider.dataDescriptor[attrName];

				getPropertyChangedSignal(bindingName).execute(this, bindingName, source);
			}
		}
	}
}

Fix getDataProviderAt

Fix getDataProviderAt

M Change _providers type from vector.<DataProvider> into
vector.<IDataProvider>


/*
 *  FRESteamWorks.h
 *  This file is part of FRESteamWorks.
 *
 *  Created by David ´Oldes´ Oliva on 3/29/12.
 *  Contributors: Ventero <http://github.com/Ventero>
 *  Copyright (c) 2012 Amanita Design. All rights reserved.
 *  Copyright (c) 2012-2013 Level Up Labs, LLC. All rights reserved.
 */

package
{
	import com.amanitadesign.steam.FRESteamWorks;
	import com.amanitadesign.steam.SteamConstants;
	import com.amanitadesign.steam.SteamEvent;
	import com.amanitadesign.steam.WorkshopConstants;

	import flash.desktop.NativeApplication;
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.display.StageDisplayState;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.utils.ByteArray;

	public class FRESteamWorksTest extends Sprite
	{
		public var Steamworks:FRESteamWorks = new FRESteamWorks();
		public var tf:TextField;

		private var _buttonPos:int = 5;
		private var _appId:uint;

		public function FRESteamWorksTest()
		{
			tf = new TextField();
			tf.x = 160;
			tf.width = stage.stageWidth - tf.x;
			tf.height = stage.stageHeight;
			addChild(tf);

			addButton("Check stats/achievements", checkAchievements);
			addButton("Toggle achievement", toggleAchievement);
			addButton("Toggle cloud enabled", toggleCloudEnabled);
			addButton("Toggle file", toggleFile);
			addButton("Publish file", publishFile);
			addButton("Toggle fullscreen", toggleFullscreen);
			addButton("Show Friends overlay", activateOverlay);
			addButton("List subscribed files", enumerateSubscribedFiles);
			addButton("List shared files", enumerateSharedFiles);
			addButton("List workshop files", enumerateWorkshopFiles);

			Steamworks.addEventListener(SteamEvent.STEAM_RESPONSE, onSteamResponse);
			Steamworks.addOverlayWorkaround(stage, true);
			NativeApplication.nativeApplication.addEventListener(Event.EXITING, onExit);
			try {
				//Steamworks.useCrashHandler(480, "1.0", "Feb 20 2013", "21:42:20");
				if(!Steamworks.init()){
					log("STEAMWORKS API is NOT available");
					return;
				}

				log("STEAMWORKS API is available\n");
				log("User ID: " + Steamworks.getUserID());
				_appId = Steamworks.getAppID();
				log("App ID: " + _appId);
				log("Persona name: " + Steamworks.getPersonaName());
				log("isCloudEnabledForApp() == "+Steamworks.isCloudEnabledForApp());
				log("getFileCount() == "+Steamworks.getFileCount());
				log("fileExists('test.txt') == "+Steamworks.fileExists('test.txt'));
				log("getDLCCount() == " + Steamworks.getDLCCount());

				Steamworks.resetAllStats(true);
			} catch(e:Error) {
				log("*** ERROR ***");
				log(e.message);
				log(e.getStackTrace());
			}

		}
		private function log(value:String):void{
			tf.appendText(value+"\n");
			tf.scrollV = tf.maxScrollV;
		}

		public function writeFileToCloud(fileName:String, data:String):Boolean {
			var dataOut:ByteArray = new ByteArray();
			dataOut.writeUTFBytes(data);
			return Steamworks.fileWrite(fileName, dataOut);
		}

		public function readFileFromCloud(fileName:String):String {
			var dataIn:ByteArray = new ByteArray();
			var result:String;
			dataIn.position = 0;
			dataIn.length = Steamworks.getFileSize(fileName);

			if(dataIn.length>0 && Steamworks.fileRead(fileName,dataIn)){
				result = dataIn.readUTFBytes(dataIn.length);
			}
			return result;
		}

		private function checkAchievements(e:Event = null):void {
			if(!Steamworks.isReady) return;

			// current stats and achievement ids are from steam example app
			log("isAchievement('ACH_WIN_ONE_GAME') == "+Steamworks.isAchievement("ACH_WIN_ONE_GAME"));
			log("isAchievement('ACH_TRAVEL_FAR_SINGLE') == "+Steamworks.isAchievement("ACH_TRAVEL_FAR_SINGLE"));
			log("setStatFloat('FeetTraveled') == "+Steamworks.setStatFloat('FeetTraveled', 21.3));
			log("setStatInt('NumGames', 2) == "+Steamworks.setStatInt('NumGames', 2));
			Steamworks.storeStats();
			log("getStatInt('NumGames') == "+Steamworks.getStatInt('NumGames'));
			log("getStatFloat('FeetTraveled') == "+Steamworks.getStatFloat('FeetTraveled'));
		}

		private function toggleAchievement(e:Event = null):void{
			if(!Steamworks.isReady) return;

			var result:Boolean = Steamworks.isAchievement("ACH_WIN_ONE_GAME");
			log("isAchievement('ACH_WIN_ONE_GAME') == " + result);
			if(!result) {
				log("setAchievement('ACH_WIN_ONE_GAME') == "+Steamworks.setAchievement("ACH_WIN_ONE_GAME"));
			} else {
				log("clearAchievement('ACH_WIN_ONE_GAME') == "+Steamworks.clearAchievement("ACH_WIN_ONE_GAME"));
			}
		}

		private function toggleCloudEnabled(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var enabled:Boolean = Steamworks.isCloudEnabledForApp();
			log("isCloudEnabledForApp() == " + enabled);
			log("setCloudEnabledForApp(" + !enabled + ") == " + Steamworks.setCloudEnabledForApp(!enabled));
			log("isCloudEnabledForApp() == " + Steamworks.isCloudEnabledForApp());
		}

		private function toggleFile(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var result:Boolean = Steamworks.fileExists('test.txt');
			log("fileExists('test.txt') == " + result);
			if(result){
				log("readFileFromCloud('test.txt') == "+readFileFromCloud('test.txt') );
				log("fileDelete('test.txt') == "+Steamworks.fileDelete('test.txt'));
			} else {
				log("writeFileToCloud('test.txt','click') == "+writeFileToCloud('test.txt','click'));
			}
		}

		private function publishFile(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var res:Boolean = Steamworks.publishWorkshopFile("test.txt", "", _appId,
				"Test.txt", "Test.txt", WorkshopConstants.VISIBILITY_Private,
				["TestTag"], WorkshopConstants.FILETYPE_Community);
			log("publishWorkshopFile('test.txt' ...) == " + res);
		}

		private function toggleFullscreen(e:Event = null):void {
			if(stage.displayState == StageDisplayState.NORMAL)
				stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
			else
				stage.displayState = StageDisplayState.NORMAL;
		}

		private function activateOverlay(e:Event = null):void {
			if(!Steamworks.isReady) return;

			log("isOverlayEnabled() == " + Steamworks.isOverlayEnabled());
			log("activateGameOverlay('Friends') == " + Steamworks.activateGameOverlay("Friends"));
			log("isOverlayEnabled() == " + Steamworks.isOverlayEnabled());
		}

		private function enumerateSubscribedFiles(e:Event = null):void {
			if(!Steamworks.isReady) return;

			log("enumerateUserSubscribedFiles(0) == " + Steamworks.enumerateUserSubscribedFiles(0));
		}

		private function enumerateSharedFiles(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var userID:String = Steamworks.getUserID();
			var res:Boolean = Steamworks.enumerateUserSharedWorkshopFiles(userID, 0, [], []);
			log("enumerateSharedFiles(" + userID + ", 0, [], []) == " + res);
		}

		private function enumerateWorkshopFiles(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var res:Boolean = Steamworks.enumeratePublishedWorkshopFiles(
				WorkshopConstants.ENUMTYPE_RankedByVote, 0, 10, 0, [], []);
			log("enumerateSharedFiles(...) == " + res);
		}

		private var id:String;
		private var handle:String;
		private function onSteamResponse(e:SteamEvent):void{
			var apiCall:Boolean;
			var i:int;
			switch(e.req_type){
				case SteamConstants.RESPONSE_OnUserStatsStored:
					log("RESPONSE_OnUserStatsStored: "+e.response);
					break;
				case SteamConstants.RESPONSE_OnUserStatsReceived:
					log("RESPONSE_OnUserStatsReceived: "+e.response);
					break;
				case SteamConstants.RESPONSE_OnAchievementStored:
					log("RESPONSE_OnAchievementStored: "+e.response);
					break;
				case SteamConstants.RESPONSE_OnPublishWorkshopFile:
					log("RESPONSE_OnPublishWorkshopFile: " + e.response);
					var file:String = Steamworks.publishWorkshopFileResult();
					log("File published as " + file);
					log("subscribePublishedFile(...) == " + Steamworks.subscribePublishedFile(file));
					break;
				case SteamConstants.RESPONSE_OnEnumerateUserSubscribedFiles:
					log("RESPONSE_OnEnumerateUserSubscribedFiles: " + e.response);
					var subRes:SubscribedFilesResult = Steamworks.enumerateUserSubscribedFilesResult();
					log("User subscribed files: " + subRes.resultsReturned + "/" + subRes.totalResults);
					for(i = 0; i < subRes.resultsReturned; i++) {
						id = subRes.publishedFileId[i];
						apiCall = Steamworks.getPublishedFileDetails(subRes.publishedFileId[i]);
						log(i + ": " + subRes.publishedFileId[i] + " (" + subRes.timeSubscribed[i] + ") - " + apiCall);
					}
					break;
				case SteamConstants.RESPONSE_OnEnumerateUserSharedWorkshopFiles:
					log("RESPONSE_OnEnumerateUserSharedWorkshopFiles: " + e.response);
					var userRes:UserFilesResult = Steamworks.enumerateUserSharedWorkshopFilesResult();
					log("User shared files: " + userRes.resultsReturned + "/" + userRes.totalResults);
					for(i = 0; i < userRes.resultsReturned; i++) {
						log(i + ": " + userRes.publishedFileId[i]);
					}
					break;
				case SteamConstants.RESPONSE_OnEnumeratePublishedWorkshopFiles:
					log("RESPONSE_OnEnumeratePublishedWorkshopFiles: " + e.response);
					var fileRes:WorkshopFilesResult = Steamworks.enumeratePublishedWorkshopFilesResult();
					log("Workshop files: " + fileRes.resultsReturned + "/" + fileRes.totalResults);
					for(i = 0; i < fileRes.resultsReturned; i++) {
						log(i + ": " + fileRes.publishedFileId[i] + " - " + fileRes.score[i]);
					}
					break;
				case SteamConstants.RESPONSE_OnGetPublishedFileDetails:
					log("RESPONSE_OnGetPublishedFileDetails: " + e.response);
					var res:FileDetailsResult = Steamworks.getPublishedFileDetailsResult(id);
					log("Result for " + id + ": " + res);
					if(res) {
						log("File: " + res.fileName + ", handle: " + res.fileHandle);
						handle = res.fileHandle;
						apiCall = Steamworks.UGCDownload(res.fileHandle, 0);
						log("UGCDownload(...) == " + apiCall);
					}
					break;
				case SteamConstants.RESPONSE_OnUGCDownload:
					log("RESPONSE_OnUGCDownload: " + e.response);
					var ugcResult:DownloadUGCResult = Steamworks.getUGCDownloadResult(handle);
					log("Result for " + handle + ": " + ugcResult);
					if(ugcResult) {
						log("File: " + ugcResult.fileName + ", handle: " + ugcResult.fileHandle + ", size: " + ugcResult.size);
						var ba:ByteArray = new ByteArray();
						ba.length = ugcResult.size;
						apiCall = Steamworks.UGCRead(ugcResult.fileHandle, ugcResult.size, 0, ba);
						log("UGCRead(...) == " + apiCall);
						if(apiCall) {
							log("Result length: " + ba.position + "//" + ba.length);
							log("Result: " + ba.readUTFBytes(ugcResult.size));
						}
					}
					break;
				default:
					log("STEAMresponse type:"+e.req_type+" response:"+e.response);
			}
		}

		private function onExit(e:Event):void{
			log("Exiting application, cleaning up");
			Steamworks.dispose();
		}

		private function addButton(label:String, callback:Function):void {
			var button:Sprite = new Sprite();
			button.graphics.beginFill(0xaaaaaa);
			button.graphics.drawRoundRect(0, 0, 150, 30, 5, 5);
			button.graphics.endFill();
			button.buttonMode = true;
			button.useHandCursor = true;
			button.addEventListener(MouseEvent.CLICK, callback);
			button.x = 5;
			button.y = _buttonPos;
			_buttonPos += button.height + 5;

			button.addEventListener(MouseEvent.MOUSE_OVER, function(e:MouseEvent):void {
				button.graphics.clear();
				button.graphics.beginFill(0xccccccc);
				button.graphics.drawRoundRect(0, 0, 150, 30, 5, 5);
				button.graphics.endFill();
			});

			button.addEventListener(MouseEvent.MOUSE_OUT, function(e:MouseEvent):void {
				button.graphics.clear();
				button.graphics.beginFill(0xaaaaaa);
				button.graphics.drawRoundRect(0, 0, 150, 30, 5, 5);
				button.graphics.endFill();
			});

			var text:TextField = new TextField();
			text.text = label;
			text.width = 140;
			text.height = 25;
			text.x = 5;
			text.y = 5;
			text.mouseEnabled = false;

			button.addChild(text);
			addChild(button);
		}
	}
}

/*
 *  FRESteamWorks.h
 *  This file is part of FRESteamWorks.
 *
 *  Created by David ´Oldes´ Oliva on 3/29/12.
 *  Contributors: Ventero <http://github.com/Ventero>
 *  Copyright (c) 2012 Amanita Design. All rights reserved.
 *  Copyright (c) 2012-2013 Level Up Labs, LLC. All rights reserved.
 */

package
{
	import com.amanitadesign.steam.FRESteamWorks;
	import com.amanitadesign.steam.SteamConstants;
	import com.amanitadesign.steam.SteamEvent;
	import com.amanitadesign.steam.WorkshopConstants;

	import flash.desktop.NativeApplication;
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.display.StageDisplayState;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.utils.ByteArray;

	public class FRESteamWorksTest extends Sprite
	{
		public var Steamworks:FRESteamWorks = new FRESteamWorks();
		public var tf:TextField;

		private var _buttonPos:int = 5;
		private var _appId:uint;

		public function FRESteamWorksTest()
		{
			tf = new TextField();
			tf.x = 160;
			tf.width = stage.stageWidth - tf.x;
			tf.height = stage.stageHeight;
			addChild(tf);

			addButton("Check stats/achievements", checkAchievements);
			addButton("Toggle achievement", toggleAchievement);
			addButton("Toggle cloud enabled", toggleCloudEnabled);
			addButton("Toggle file", toggleFile);
			addButton("Publish file", publishFile);
			addButton("Toggle fullscreen", toggleFullscreen);
			addButton("Show Friends overlay", activateOverlay);
			addButton("List subscribed files", enumerateSubscribedFiles);
			addButton("List shared files", enumerateSharedFiles);
			addButton("List workshop files", enumerateWorkshopFiles);

			Steamworks.addEventListener(SteamEvent.STEAM_RESPONSE, onSteamResponse);
			Steamworks.addOverlayWorkaround(stage, true);
			NativeApplication.nativeApplication.addEventListener(Event.EXITING, onExit);
			try {
				//Steamworks.useCrashHandler(480, "1.0", "Feb 20 2013", "21:42:20");
				if(!Steamworks.init()){
					log("STEAMWORKS API is NOT available");
					return;
				}

				log("STEAMWORKS API is available\n");
				log("User ID: " + Steamworks.getUserID());
				_appId = Steamworks.getAppID();
				log("App ID: " + _appId);
				log("Persona name: " + Steamworks.getPersonaName());
				log("isCloudEnabledForApp() == "+Steamworks.isCloudEnabledForApp());
				log("getFileCount() == "+Steamworks.getFileCount());
				log("fileExists('test.txt') == "+Steamworks.fileExists('test.txt'));
				log("getDLCCount() == " + Steamworks.getDLCCount());

				Steamworks.resetAllStats(true);
			} catch(e:Error) {
				log("*** ERROR ***");
				log(e.message);
				log(e.getStackTrace());
			}

		}
		private function log(value:String):void{
			tf.appendText(value+"\n");
			tf.scrollV = tf.maxScrollV;
		}

		public function writeFileToCloud(fileName:String, data:String):Boolean {
			var dataOut:ByteArray = new ByteArray();
			dataOut.writeUTFBytes(data);
			return Steamworks.fileWrite(fileName, dataOut);
		}

		public function readFileFromCloud(fileName:String):String {
			var dataIn:ByteArray = new ByteArray();
			var result:String;
			dataIn.position = 0;
			dataIn.length = Steamworks.getFileSize(fileName);

			if(dataIn.length>0 && Steamworks.fileRead(fileName,dataIn)){
				result = dataIn.readUTFBytes(dataIn.length);
			}
			return result;
		}

		private function checkAchievements(e:Event = null):void {
			if(!Steamworks.isReady) return;

			// current stats and achievement ids are from steam example app
			log("isAchievement('ACH_WIN_ONE_GAME') == "+Steamworks.isAchievement("ACH_WIN_ONE_GAME"));
			log("isAchievement('ACH_TRAVEL_FAR_SINGLE') == "+Steamworks.isAchievement("ACH_TRAVEL_FAR_SINGLE"));
			log("setStatFloat('FeetTraveled') == "+Steamworks.setStatFloat('FeetTraveled', 21.3));
			log("setStatInt('NumGames', 2) == "+Steamworks.setStatInt('NumGames', 2));
			Steamworks.storeStats();
			log("getStatInt('NumGames') == "+Steamworks.getStatInt('NumGames'));
			log("getStatFloat('FeetTraveled') == "+Steamworks.getStatFloat('FeetTraveled'));
		}

		private function toggleAchievement(e:Event = null):void{
			if(!Steamworks.isReady) return;

			var result:Boolean = Steamworks.isAchievement("ACH_WIN_ONE_GAME");
			log("isAchievement('ACH_WIN_ONE_GAME') == " + result);
			if(!result) {
				log("setAchievement('ACH_WIN_ONE_GAME') == "+Steamworks.setAchievement("ACH_WIN_ONE_GAME"));
			} else {
				log("clearAchievement('ACH_WIN_ONE_GAME') == "+Steamworks.clearAchievement("ACH_WIN_ONE_GAME"));
			}
		}

		private function toggleCloudEnabled(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var enabled:Boolean = Steamworks.isCloudEnabledForApp();
			log("isCloudEnabledForApp() == " + enabled);
			log("setCloudEnabledForApp(" + !enabled + ") == " + Steamworks.setCloudEnabledForApp(!enabled));
			log("isCloudEnabledForApp() == " + Steamworks.isCloudEnabledForApp());
		}

		private function toggleFile(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var result:Boolean = Steamworks.fileExists('test.txt');
			log("fileExists('test.txt') == " + result);
			if(result){
				log("readFileFromCloud('test.txt') == "+readFileFromCloud('test.txt') );
				log("fileDelete('test.txt') == "+Steamworks.fileDelete('test.txt'));
			} else {
				log("writeFileToCloud('test.txt','click') == "+writeFileToCloud('test.txt','click'));
			}
		}

		private function publishFile(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var res:Boolean = Steamworks.publishWorkshopFile("test.txt", "", _appId,
				"Test.txt", "Test.txt", WorkshopConstants.VISIBILITY_Private,
				["TestTag"], WorkshopConstants.FILETYPE_Community);
			log("publishWorkshopFile('test.txt' ...) == " + res);
		}

		private function toggleFullscreen(e:Event = null):void {
			if(stage.displayState == StageDisplayState.NORMAL)
				stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
			else
				stage.displayState = StageDisplayState.NORMAL;
		}

		private function activateOverlay(e:Event = null):void {
			if(!Steamworks.isReady) return;

			log("isOverlayEnabled() == " + Steamworks.isOverlayEnabled());
			log("activateGameOverlay('Friends') == " + Steamworks.activateGameOverlay("Friends"));
			log("isOverlayEnabled() == " + Steamworks.isOverlayEnabled());
		}

		private function enumerateSubscribedFiles(e:Event = null):void {
			if(!Steamworks.isReady) return;

			log("enumerateUserSubscribedFiles(0) == " + Steamworks.enumerateUserSubscribedFiles(0));
		}

		private function enumerateSharedFiles(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var userID:String = Steamworks.getUserID();
			var res:Boolean = Steamworks.enumerateUserSharedWorkshopFiles(userID, 0, [], []);
			log("enumerateSharedFiles(" + userID + ", 0, [], []) == " + res);
		}

		private function enumerateWorkshopFiles(e:Event = null):void {
			if(!Steamworks.isReady) return;

			var res:Boolean = Steamworks.enumeratePublishedWorkshopFiles(
				WorkshopConstants.ENUMTYPE_RankedByVote, 0, 10, 0, [], []);
			log("enumerateSharedFiles(...) == " + res);
		}

		private var id:String;
		private var handle:String;
		private function onSteamResponse(e:SteamEvent):void{
			var apiCall:Boolean;
			var i:int;
			var file:String;
			switch(e.req_type){
				case SteamConstants.RESPONSE_OnUserStatsStored:
					log("RESPONSE_OnUserStatsStored: "+e.response);
					break;
				case SteamConstants.RESPONSE_OnUserStatsReceived:
					log("RESPONSE_OnUserStatsReceived: "+e.response);
					break;
				case SteamConstants.RESPONSE_OnAchievementStored:
					log("RESPONSE_OnAchievementStored: "+e.response);
					break;
				case SteamConstants.RESPONSE_OnPublishWorkshopFile:
					log("RESPONSE_OnPublishWorkshopFile: " + e.response);
					file = Steamworks.publishWorkshopFileResult();
					log("File published as " + file);
					log("subscribePublishedFile(...) == " + Steamworks.subscribePublishedFile(file));
					break;
				case SteamConstants.RESPONSE_OnEnumerateUserSubscribedFiles:
					log("RESPONSE_OnEnumerateUserSubscribedFiles: " + e.response);
					var subRes:SubscribedFilesResult = Steamworks.enumerateUserSubscribedFilesResult();
					log("User subscribed files: " + subRes.resultsReturned + "/" + subRes.totalResults);
					for(i = 0; i < subRes.resultsReturned; i++) {
						id = subRes.publishedFileId[i];
						apiCall = Steamworks.getPublishedFileDetails(subRes.publishedFileId[i]);
						log(i + ": " + subRes.publishedFileId[i] + " (" + subRes.timeSubscribed[i] + ") - " + apiCall);
					}
					break;
				case SteamConstants.RESPONSE_OnEnumerateUserSharedWorkshopFiles:
					log("RESPONSE_OnEnumerateUserSharedWorkshopFiles: " + e.response);
					var userRes:UserFilesResult = Steamworks.enumerateUserSharedWorkshopFilesResult();
					log("User shared files: " + userRes.resultsReturned + "/" + userRes.totalResults);
					for(i = 0; i < userRes.resultsReturned; i++) {
						log(i + ": " + userRes.publishedFileId[i]);
					}
					break;
				case SteamConstants.RESPONSE_OnEnumeratePublishedWorkshopFiles:
					log("RESPONSE_OnEnumeratePublishedWorkshopFiles: " + e.response);
					var fileRes:WorkshopFilesResult = Steamworks.enumeratePublishedWorkshopFilesResult();
					log("Workshop files: " + fileRes.resultsReturned + "/" + fileRes.totalResults);
					for(i = 0; i < fileRes.resultsReturned; i++) {
						log(i + ": " + fileRes.publishedFileId[i] + " - " + fileRes.score[i]);
					}
					if(fileRes.resultsReturned > 0) {
						file = fileRes.publishedFileId[0];
						log("updateUserPublishedItemVote(" + file + ", true)");
						apiCall = Steamworks.updateUserPublishedItemVote(file, true);
						log("updateUserPublishedItemVote(" + file + ", true) == " + apiCall);
					}
					break;
				case SteamConstants.RESPONSE_OnGetPublishedFileDetails:
					log("RESPONSE_OnGetPublishedFileDetails: " + e.response);
					var res:FileDetailsResult = Steamworks.getPublishedFileDetailsResult(id);
					log("Result for " + id + ": " + res);
					if(res) {
						log("File: " + res.fileName + ", handle: " + res.fileHandle);
						handle = res.fileHandle;
						apiCall = Steamworks.UGCDownload(res.fileHandle, 0);
						log("UGCDownload(...) == " + apiCall);
					}
					break;
				case SteamConstants.RESPONSE_OnUGCDownload:
					log("RESPONSE_OnUGCDownload: " + e.response);
					var ugcResult:DownloadUGCResult = Steamworks.getUGCDownloadResult(handle);
					log("Result for " + handle + ": " + ugcResult);
					if(ugcResult) {
						log("File: " + ugcResult.fileName + ", handle: " + ugcResult.fileHandle + ", size: " + ugcResult.size);
						var ba:ByteArray = new ByteArray();
						ba.length = ugcResult.size;
						apiCall = Steamworks.UGCRead(ugcResult.fileHandle, ugcResult.size, 0, ba);
						log("UGCRead(...) == " + apiCall);
						if(apiCall) {
							log("Result length: " + ba.position + "//" + ba.length);
							log("Result: " + ba.readUTFBytes(ugcResult.size));
						}
					}
					break;
				default:
					log("STEAMresponse type:"+e.req_type+" response:"+e.response);
			}
		}

		private function onExit(e:Event):void{
			log("Exiting application, cleaning up");
			Steamworks.dispose();
		}

		private function addButton(label:String, callback:Function):void {
			var button:Sprite = new Sprite();
			button.graphics.beginFill(0xaaaaaa);
			button.graphics.drawRoundRect(0, 0, 150, 30, 5, 5);
			button.graphics.endFill();
			button.buttonMode = true;
			button.useHandCursor = true;
			button.addEventListener(MouseEvent.CLICK, callback);
			button.x = 5;
			button.y = _buttonPos;
			_buttonPos += button.height + 5;

			button.addEventListener(MouseEvent.MOUSE_OVER, function(e:MouseEvent):void {
				button.graphics.clear();
				button.graphics.beginFill(0xccccccc);
				button.graphics.drawRoundRect(0, 0, 150, 30, 5, 5);
				button.graphics.endFill();
			});

			button.addEventListener(MouseEvent.MOUSE_OUT, function(e:MouseEvent):void {
				button.graphics.clear();
				button.graphics.beginFill(0xaaaaaa);
				button.graphics.drawRoundRect(0, 0, 150, 30, 5, 5);
				button.graphics.endFill();
			});

			var text:TextField = new TextField();
			text.text = label;
			text.width = 140;
			text.height = 25;
			text.x = 5;
			text.y = 5;
			text.mouseEnabled = false;

			button.addChild(text);
			addChild(button);
		}
	}
}

Add test for updateUserPublishedItemVote