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

package
{
    import library.ASTUce.Runner;

    import com.google.analytics.AllTests;
    import flash.display.Sprite;

    /* note:
       Run the Google Analytics unit tests
    */
    [SWF(width="400", height="400", backgroundColor='0xffffff', frameRate='24', pageTitle='testrunner', scriptRecursionLimit='1000', scriptTimeLimit='60')]
    [ExcludeClass]
    public class TestRunner extends Sprite
    {
        public function TestRunner()
        {
            Runner.main( com.google.analytics.AllTests.suite( ) );
        }

    }
}

update comment

update comment


package{
	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.utils.ByteArray;

	[SWF(width="400", height="400")]
	public class flashtest extends Sprite {
		private var tf:TextField;
		public function flashtest() {
			tf = new TextField();
			tf.width = tf.height = 400;
			tf.wordWrap = true;
			addChild(tf);

			var b:ByteArray = new ByteArray();
			var v:Vector.<int> = new Vector.<int>();
			for(var z:int = 0; z < 3; ++z)
				v.push(z);
			b.writeObject(v);

			dumpByteArray(b);

			var data:Array = [
				0x0d, 0x07, 0x00,
				0x00, 0x00, 0x00, 0x01,
				0x00, 0x00, 0x00, 0x02,
				0x00, 0x00, 0x00, 0x03
			];
			b = createByteArray(data);

			var o:* = b.readObject();
			tf.appendText(o + "\n");
			var a:Vector.<int> =  o as Vector.<int>;
			if(a != null) {
				tf.appendText("Vector with " + a.length + " elements\n");
				for(var j:int = 0; j < a.length; ++j)
					tf.appendText(j + ": " + a[j] + "\n");
			} else {
				tf.appendText("Vector is null\n");
			}
		}

		private function dumpByteArray(b:ByteArray):void {
			b.position = 0;
			tf.appendText(b.bytesAvailable + "\n");
			var cnt:int = 0;
			while(b.bytesAvailable) {
				var c:String = b.readUnsignedByte().toString(16);
				if(c.length < 2) c = "0" + c;
				tf.appendText("0x" + c);
				if (b.bytesAvailable == 0) break;
				if (++cnt == 12) {
					cnt = 0;
					tf.appendText(",\n");
				} else {
					tf.appendText(", ");
				}
			}
			tf.appendText("\n");
		}

		private function createByteArray(data:Array):ByteArray {
			var b:ByteArray = new ByteArray();

			for(var i:int = 0; i < data.length; ++i)
				b.writeByte(data[i]);

			b.position = 0;
			return b;
		}
	}
}

package{
	import flash.display.Sprite;
	import flash.net.registerClassAlias;
	import flash.text.TextField;
	import flash.utils.ByteArray;

	[SWF(width="400", height="400")]
	public class flashtest extends Sprite {
		private var tf:TextField;
		public function flashtest() {
			tf = new TextField();
			tf.width = tf.height = 400;
			tf.wordWrap = true;
			addChild(tf);

			dumpValues();
		}

		private function dumpValues():void {
			tf.text = "";
			var b:ByteArray = new ByteArray();
			var a:Array = [undefined, null, [], "foo"];
			a.foo = "qux";
			a.bar = [];
			b.writeObject(a);
			dumpByteArray(b);
		}

		private function readValues():void {
			var data:Array = [
				0x0d, 0x07, 0x00,
				0x00, 0x00, 0x00, 0x01,
				0x00, 0x00, 0x00, 0x02,
				0x00, 0x00, 0x00, 0x03
			];
			var b:ByteArray = createByteArray(data);

			var o:* = b.readObject();
			tf.appendText(o + "\n");
			var a:Vector.<int> =  o as Vector.<int>;
			if(a != null) {
				tf.appendText("Vector with " + a.length + " elements\n");
				for(var j:int = 0; j < a.length; ++j)
					tf.appendText(j + ": " + a[j] + "\n");
			} else {
				tf.appendText("Vector is null\n");
			}
		}

		private function testExternalizable():void {
			tf.text = "";
			registerClassAlias("Foo", Foo);
			var b:ByteArray = new ByteArray();
			var f:Foo = new Foo("asd");
			var g:Foo = new Foo("bar");
			var a:Array = [f, f, g];
			b.writeObject(a);
			dumpByteArray(b);
			b.position = 0;
			var a2:Array = b.readObject();
			tf.appendText((a[0] as Foo).foo);
			tf.appendText((a[1] as Foo).foo);
			tf.appendText((a[2] as Foo).foo);
		}

		private function dumpByteArray(b:ByteArray):void {
			b.position = 0;
			tf.appendText(b.bytesAvailable + "\n");
			var cnt:int = 0;
			while(b.bytesAvailable) {
				var c:String = b.readUnsignedByte().toString(16);
				if(c.length < 2) c = "0" + c;
				tf.appendText("0x" + c);
				if (b.bytesAvailable == 0) break;
				if (++cnt == 12) {
					cnt = 0;
					tf.appendText(",\n");
				} else {
					tf.appendText(", ");
				}
			}
			tf.appendText("\n");
		}

		private function createByteArray(data:Array):ByteArray {
			var b:ByteArray = new ByteArray();

			for(var i:int = 0; i < data.length; ++i)
				b.writeByte(data[i]);

			b.position = 0;
			return b;
		}
	}
}

import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.IExternalizable;

class Foo implements IExternalizable {
	public var foo:String;

	public function Foo(s:String = "default") {
		foo = s;
	}

	public function writeExternal(output:IDataOutput):void {
		output.writeUTF(foo);
	}

	public function readExternal(input:IDataInput):void {
		foo = input.readUTF();
	}
}

Add externalizable object to Flash test.


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

package com.freshplanet.ane.AirCapabilities {

import com.freshplanet.ane.AirCapabilities.events.AirCapabilitiesLowMemoryEvent;
import com.freshplanet.ane.AirCapabilities.events.AirCapabilitiesOpenURLEvent;
	import flash.display.BitmapData;
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

	public class AirCapabilities extends EventDispatcher {

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

		/**
		 * Is the ANE supported on the current platform
		 */
        static public function get isSupported():Boolean {
            return (Capabilities.manufacturer.indexOf("iOS") > -1 && Capabilities.os.indexOf("x86_64") < 0 && Capabilities.os.indexOf("i386") < 0) || Capabilities.manufacturer.indexOf("Android") > -1;
        }

		/**
		 * If true, logs will be displayed at the ActionScript and native level.
		 */
		public function setLogging(value:Boolean):void {

			_doLogging = value;
			if (isSupported)
				_extContext.call("setLogging", value);
		}

		public function get nativeLogger():ILogger {
            return _logger;
        }

        /**
         * AirCapabilities instance
         */
        static public function get instance():AirCapabilities {
			return _instance != null ? _instance : new AirCapabilities()
		}

        /**
         * Is SMS available
         * @return
         */
		public function hasSmsEnabled():Boolean {

			if (isSupported)
				return _extContext.call("hasSMS");

			return false;
		}

        /**
         * Is Twitter available
         * @return
         */
		public function hasTwitterEnabled():Boolean {

			if (isSupported)
				return _extContext.call("hasTwitter");

			return false;
		}

        /**
         * Sends an SMS message
         * @param message to send
         * @param recipient phonenumber
         */
		public function sendMsgWithSms(message:String, recipient:String = null):void {

			if (isSupported)
				_extContext.call("sendWithSms", message, recipient);
		}

        /**
         * Sends a Twitter message
         * @param message
         */
		public function sendMsgWithTwitter(message:String):void {

			if (isSupported)
				_extContext.call("sendWithTwitter", message);
		}

        /**
         * Redirect user to Rating page
         * @param appId
         * @param appName
         */
		public function redirecToRating(appId:String, appName:String):void {

			if (isSupported)
				_extContext.call("redirectToRating", appId, appName);
		}

        /**
         * Model of the device
         * @return
         */
		public function getDeviceModel():String {

			if (isSupported)
				return _extContext.call("getDeviceModel") as String;

            return "";
		}

        /**
         * Name of the machine
         * @return
         */
		public function getMachineName():String {

			if (isSupported)
				return _extContext.call("getMachineName") as String;

            return "";
		}

        /**
         * Opens the referral link URL
         * @param url
         */
		public function processReferralLink(url:String):void {

			if (isSupported)
				_extContext.call("processReferralLink", url);
		}

        /**
         * Opens Facebook page
         * @param pageId id of the facebook page
         */
		public function redirectToPageId(pageId:String):void {

			if (isSupported)
				_extContext.call("redirectToPageId", pageId);
		}

        /**
         * Opens Twitter account
         * @param twitterAccount
         */
		public function redirectToTwitterAccount(twitterAccount:String):void {

			if (isSupported)
				_extContext.call("redirectToTwitterAccount", twitterAccount);
		}

        /**
         * Is posting pictures on Twitter enabled
         * @return
         */
		public function canPostPictureOnTwitter():Boolean {

			if (isSupported)
				return _extContext.call("canPostPictureOnTwitter");

			return false;
		}

        /**
         * Post new picture on Twitter
         * @param message
         * @param bitmapData
         */
		public function postPictureOnTwitter(message:String, bitmapData:BitmapData):void {

			if (isSupported)
				_extContext.call("postPictureOnTwitter", message, bitmapData);
		}

        /**
         * Is Instagram enabled
         * @return
         */
		public function hasInstagramEnabled():Boolean {

			if (isSupported)
				return _extContext.call("hasInstagramEnabled");

			return false;
		}

        /**
         * Post new picture on Instagram
         * @param message
         * @param bitmapData
         * @param x
         * @param y
         * @param width
         * @param height
         */
		public function postPictureOnInstagram(message:String, bitmapData:BitmapData,
                                               x:int, y:int, width:int, height:int):void {

			if (isSupported)
				_extContext.call("postPictureOnInstagram", message, bitmapData, x, y, width, height);
		}

		/**
		 * Open an application (if installed on the Device) or send the player to the appstore.  iOS Only
		 * @param schemes 		List of schemes (String) that the application accepts.  Examples : @"sms://", @"twit://".  You can find schemes in http://handleopenurl.com/
		 * @param appStoreURL 	(optional) Link to the AppStore page for the Application for the player to download. URL can be generated via Apple's linkmaker (itunes.apple.com/linkmaker?)
		 */
		public function openExternalApplication(schemes:Array, appStoreURL:String = null):void {

			if (isSupported && Capabilities.manufacturer.indexOf("Android") == -1)
				_extContext.call("openExternalApplication", schemes, appStoreURL);
		}

        /**
         * Is opening URLs available
         * @param url
         * @return
         */
		public function canOpenURL(url:String):Boolean {
			return isSupported ? _extContext.call("canOpenURL", url) as Boolean : false;
		}

        /**
         * Open URL
         * @param url
         */
		public function openURL(url:String):void {

			if (canOpenURL(url))
				_extContext.call("openURL", url);
		}

		/**
		 * Opens modal app store. Available on iOS only
		 * @param appStoreId	id of the app to open a modal view to (do not include the "id" at the beginning of the number)
		 */
		public function openModalAppStoreIOS(appStoreId:String):void {

			if (Capabilities.manufacturer.indexOf("iOS") > -1)
				_extContext.call("openModalAppStore", appStoreId);
		}

        /**
         * Version of the operating system
         * @return
         */
        public function getOSVersion():String {

            if (isSupported)
                return _extContext.call("getOSVersion") as String;

            return "";
        }

        /**
         * @return  current amount of RAM being used in bytes
         */
        public function getCurrentMem():Number {

			if (!isSupported)
				return -1;

            var ret:Object = _extContext.call("getCurrentMem");
            if (ret is Error)
                throw ret;
            else
                return ret ? ret as Number : 0;
        }

        /**
         * @return  amount of RAM used by the VM
         */
        public function getCurrentVirtualMem():Number {

			if(!isSupported)
				return -1;

            if (Capabilities.manufacturer.indexOf("iOS") == -1)
                return -1;

            var ret:Object = _extContext.call("getCurrentVirtualMem");
            if (ret is Error)
                throw ret;
            else
                return ret ? ret as Number : 0;
        }

		/**
		 * @return  is requesting review available. Available on iOS only
		 */
		public function canRequestReview():Boolean {

			if (!isSupported)
				return false;

			if (Capabilities.manufacturer.indexOf("iOS") == -1)
				return false;

			return _extContext.call("canRequestReview");
		}

		/**
		 * Request AppStore review. Available on iOS only
		 */
		public function requestReview():void {

			if (!canRequestReview())
				return;

			_extContext.call("requestReview");
		}
		/**
		 * Generate haptic feedback - iOS only
		 */
		public function generateHapticFeedback(feedbackType:AirCapabilitiesHapticFeedbackType):void {

			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return;

			if(!feedbackType)
				return

			_extContext.call("generateHapticFeedback", feedbackType.value);
		}

		public function getNativeScale():Number
		{
			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return 1;
			return _extContext.call("getNativeScale") as Number;
		}

		public function openAdSettings():void
		{
			if (Capabilities.manufacturer.indexOf("Android") < 0)
				return;

			_extContext.call("openAdSettings");
		}

		public function getTopInset():Number
		{
			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return 0;
			return _extContext.call("getTopInset") as Number;
		}

		public function getBottomInset():Number
		{
			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return 0;
			return _extContext.call("getBottomInset") as Number;
		}

		public function get iOSAppOnMac():Boolean
		{
			if(Capabilities.os.toLowerCase().indexOf("mac os") < 0 && Capabilities.manufacturer.indexOf("iOS") < 0)
				return false;

			return _extContext.call("iOSAppOnMac") as Boolean;
		}

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

		static private const EXTENSION_ID:String = "com.freshplanet.ane.AirCapabilities";

		static private var _instance:AirCapabilities = null;

		private var _extContext:ExtensionContext = null;
		private var _doLogging:Boolean = false;
		private var _logger:ILogger;

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

			super();

			if (_instance)
				throw new Error("singleton class, use .instance");

			_extContext = ExtensionContext.createExtensionContext(EXTENSION_ID, null);
			_extContext.addEventListener(StatusEvent.STATUS, _handleStatusEvent);

			if (isSupported)
				_logger = new NativeLogger(_extContext);
			else
				_logger = new DefaultLogger();

			_instance = this;
		}

        private function _handleStatusEvent(event:StatusEvent):void {

            if (event.code == "log")
	            _doLogging && trace("[AirCapabilities] " + event.level);
            else if (event.code == "OPEN_URL")
                this.dispatchEvent(new AirCapabilitiesOpenURLEvent(AirCapabilitiesOpenURLEvent.OPEN_URL_SUCCESS, event.level));
            else if (event.code == AirCapabilitiesLowMemoryEvent.LOW_MEMORY_WARNING) {
	            var memory:Number = Number(event.level);
	            this.dispatchEvent(new AirCapabilitiesLowMemoryEvent(event.code, memory));
            }
            else
                this.dispatchEvent(event);
        }
	}
}

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

package com.freshplanet.ane.AirCapabilities {

import com.freshplanet.ane.AirCapabilities.events.AirCapabilitiesLowMemoryEvent;
import com.freshplanet.ane.AirCapabilities.events.AirCapabilitiesOpenURLEvent;
	import flash.display.BitmapData;
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

	public class AirCapabilities extends EventDispatcher {

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

		/**
		 * Is the ANE supported on the current platform
		 */
        static public function get isSupported():Boolean {
            return (Capabilities.manufacturer.indexOf("iOS") > -1 && Capabilities.os.indexOf("x86_64") < 0 && Capabilities.os.indexOf("i386") < 0) || Capabilities.manufacturer.indexOf("Android") > -1;
        }

		/**
		 * If true, logs will be displayed at the ActionScript and native level.
		 */
		public function setLogging(value:Boolean):void {

			_doLogging = value;
			if (isSupported)
				_extContext.call("setLogging", value);
		}

		public function get nativeLogger():ILogger {
            return _logger;
        }

        /**
         * AirCapabilities instance
         */
        static public function get instance():AirCapabilities {
			return _instance != null ? _instance : new AirCapabilities()
		}

        /**
         * Is SMS available
         * @return
         */
		public function hasSmsEnabled():Boolean {

			if (isSupported)
				return _extContext.call("hasSMS");

			return false;
		}

        /**
         * Is Twitter available
         * @return
         */
		public function hasTwitterEnabled():Boolean {

			if (isSupported)
				return _extContext.call("hasTwitter");

			return false;
		}

        /**
         * Sends an SMS message
         * @param message to send
         * @param recipient phonenumber
         */
		public function sendMsgWithSms(message:String, recipient:String = null):void {

			if (isSupported)
				_extContext.call("sendWithSms", message, recipient);
		}

        /**
         * Sends a Twitter message
         * @param message
         */
		public function sendMsgWithTwitter(message:String):void {

			if (isSupported)
				_extContext.call("sendWithTwitter", message);
		}

        /**
         * Redirect user to Rating page
         * @param appId
         * @param appName
         */
		public function redirecToRating(appId:String, appName:String):void {

			if (isSupported)
				_extContext.call("redirectToRating", appId, appName);
		}

        /**
         * Model of the device
         * @return
         */
		public function getDeviceModel():String {

			if (isSupported)
				return _extContext.call("getDeviceModel") as String;

            return "";
		}

        /**
         * Name of the machine
         * @return
         */
		public function getMachineName():String {

			if (isSupported)
				return _extContext.call("getMachineName") as String;

            return "";
		}

        /**
         * Opens the referral link URL
         * @param url
         */
		public function processReferralLink(url:String):void {

			if (isSupported)
				_extContext.call("processReferralLink", url);
		}

        /**
         * Opens Facebook page
         * @param pageId id of the facebook page
         */
		public function redirectToPageId(pageId:String):void {

			if (isSupported)
				_extContext.call("redirectToPageId", pageId);
		}

        /**
         * Opens Twitter account
         * @param twitterAccount
         */
		public function redirectToTwitterAccount(twitterAccount:String):void {

			if (isSupported)
				_extContext.call("redirectToTwitterAccount", twitterAccount);
		}

        /**
         * Is posting pictures on Twitter enabled
         * @return
         */
		public function canPostPictureOnTwitter():Boolean {

			if (isSupported)
				return _extContext.call("canPostPictureOnTwitter");

			return false;
		}

        /**
         * Post new picture on Twitter
         * @param message
         * @param bitmapData
         */
		public function postPictureOnTwitter(message:String, bitmapData:BitmapData):void {

			if (isSupported)
				_extContext.call("postPictureOnTwitter", message, bitmapData);
		}

        /**
         * Is Instagram enabled
         * @return
         */
		public function hasInstagramEnabled():Boolean {

			if (isSupported)
				return _extContext.call("hasInstagramEnabled");

			return false;
		}

        /**
         * Post new picture on Instagram
         * @param message
         * @param bitmapData
         * @param x
         * @param y
         * @param width
         * @param height
         */
		public function postPictureOnInstagram(message:String, bitmapData:BitmapData,
                                               x:int, y:int, width:int, height:int):void {

			if (isSupported)
				_extContext.call("postPictureOnInstagram", message, bitmapData, x, y, width, height);
		}

		/**
		 * Open an application (if installed on the Device) or send the player to the appstore.  iOS Only
		 * @param schemes 		List of schemes (String) that the application accepts.  Examples : @"sms://", @"twit://".  You can find schemes in http://handleopenurl.com/
		 * @param appStoreURL 	(optional) Link to the AppStore page for the Application for the player to download. URL can be generated via Apple's linkmaker (itunes.apple.com/linkmaker?)
		 */
		public function openExternalApplication(schemes:Array, appStoreURL:String = null):void {

			if (isSupported && Capabilities.manufacturer.indexOf("Android") == -1)
				_extContext.call("openExternalApplication", schemes, appStoreURL);
		}

        /**
         * Is opening URLs available
         * @param url
         * @return
         */
		public function canOpenURL(url:String):Boolean {
			return isSupported ? _extContext.call("canOpenURL", url) as Boolean : false;
		}

        /**
         * Open URL
         * @param url
         */
		public function openURL(url:String):void {

			if (canOpenURL(url))
				_extContext.call("openURL", url);
		}

		/**
		 * Opens modal app store. Available on iOS only
		 * @param appStoreId	id of the app to open a modal view to (do not include the "id" at the beginning of the number)
		 */
		public function openModalAppStoreIOS(appStoreId:String):void {

			if (Capabilities.manufacturer.indexOf("iOS") > -1)
				_extContext.call("openModalAppStore", appStoreId);
		}

        /**
         * Version of the operating system
         * @return
         */
        public function getOSVersion():String {

            if (isSupported)
                return _extContext.call("getOSVersion") as String;

            return "";
        }

        /**
         * @return  current amount of RAM being used in bytes
         */
        public function getCurrentMem():Number {

			if (!isSupported)
				return -1;

            var ret:Object = _extContext.call("getCurrentMem");
            if (ret is Error)
                throw ret;
            else
                return ret ? ret as Number : 0;
        }

        /**
         * @return  amount of RAM used by the VM
         */
        public function getCurrentVirtualMem():Number {

			if(!isSupported)
				return -1;

            if (Capabilities.manufacturer.indexOf("iOS") == -1)
                return -1;

            var ret:Object = _extContext.call("getCurrentVirtualMem");
            if (ret is Error)
                throw ret;
            else
                return ret ? ret as Number : 0;
        }

		/**
		 * @return  is requesting review available. Available on iOS only
		 */
		public function canRequestReview():Boolean {

			if (!isSupported)
				return false;

			if (Capabilities.manufacturer.indexOf("iOS") == -1)
				return false;

			return _extContext.call("canRequestReview");
		}

		/**
		 * Request AppStore review. Available on iOS only
		 */
		public function requestReview():void {

			if (!canRequestReview())
				return;

			_extContext.call("requestReview");
		}
		/**
		 * Generate haptic feedback - iOS only
		 */
		public function generateHapticFeedback(feedbackType:AirCapabilitiesHapticFeedbackType):void {

			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return;

			if(!feedbackType)
				return

			_extContext.call("generateHapticFeedback", feedbackType.value);
		}

		public function getNativeScale():Number
		{
			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return 1;
			return _extContext.call("getNativeScale") as Number;
		}

		public function openAdSettings():void
		{
			if (Capabilities.manufacturer.indexOf("Android") < 0)
				return;

			_extContext.call("openAdSettings");
		}

		public function getTopInset():Number
		{
			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return 0;
			return _extContext.call("getTopInset") as Number;
		}

		public function getBottomInset():Number
		{
			if (Capabilities.manufacturer.indexOf("iOS") < 0)
				return 0;
			return _extContext.call("getBottomInset") as Number;
		}

		public function get iOSAppOnMac():Boolean
		{
			if(Capabilities.manufacturer.indexOf("iOS") < 0)
				return false;

			return _extContext.call("iOSAppOnMac") as Boolean;
		}

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

		static private const EXTENSION_ID:String = "com.freshplanet.ane.AirCapabilities";

		static private var _instance:AirCapabilities = null;

		private var _extContext:ExtensionContext = null;
		private var _doLogging:Boolean = false;
		private var _logger:ILogger;

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

			super();

			if (_instance)
				throw new Error("singleton class, use .instance");

			_extContext = ExtensionContext.createExtensionContext(EXTENSION_ID, null);
			_extContext.addEventListener(StatusEvent.STATUS, _handleStatusEvent);

			if (isSupported)
				_logger = new NativeLogger(_extContext);
			else
				_logger = new DefaultLogger();

			_instance = this;
		}

        private function _handleStatusEvent(event:StatusEvent):void {

            if (event.code == "log")
	            _doLogging && trace("[AirCapabilities] " + event.level);
            else if (event.code == "OPEN_URL")
                this.dispatchEvent(new AirCapabilitiesOpenURLEvent(AirCapabilitiesOpenURLEvent.OPEN_URL_SUCCESS, event.level));
            else if (event.code == AirCapabilitiesLowMemoryEvent.LOW_MEMORY_WARNING) {
	            var memory:Number = Number(event.level);
	            this.dispatchEvent(new AirCapabilitiesLowMemoryEvent(event.code, memory));
            }
            else
                this.dispatchEvent(event);
        }
	}
}

check just for iOS

check just for iOS


////////////////////////////////////////////////////////////////////////////////
//
//  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.html.staticControls.beads
{
	import flash.events.Event;

	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IInitSkin;
	import org.apache.flex.core.IItemRenderer;
	import org.apache.flex.core.IItemRendererParent;
	import org.apache.flex.core.ISelectionModel;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.UIBase;
	import org.apache.flex.html.staticControls.beads.controllers.VScrollBarMouseController;
	import org.apache.flex.html.staticControls.beads.layouts.VScrollBarLayout;
	import org.apache.flex.html.staticControls.beads.models.ScrollBarModel;
	import org.apache.flex.html.staticControls.beads.models.SingleLineBorderModel;
	import org.apache.flex.html.staticControls.supportClasses.Border;
	import org.apache.flex.html.staticControls.supportClasses.NonVirtualDataGroup;
	import org.apache.flex.html.staticControls.supportClasses.ScrollBar;

	public class ListBead implements IBead, IInitSkin, IStrand, IListBead
	{
		public function ListBead()
		{
		}

		private var listModel:ISelectionModel;

        private var _border:Border;

        public function get border():Border
        {
            return _border;
        }

        private var _dataGroup:IItemRendererParent;

		public function get dataGroup():IItemRendererParent
		{
			return _dataGroup;
		}

		private var _vScrollBar:ScrollBar;

		public function get vScrollBar():ScrollBar
		{
            if (!_vScrollBar)
                _vScrollBar = createScrollBar();
			return _vScrollBar;
		}

		private var _strand:IStrand;

		public function get strand():IStrand
		{
			return _strand;
		}
		public function set strand(value:IStrand):void
		{
			_strand = value;
            _border = new Border();
            border.addToParent(UIBase(_strand));
            _border.model = new SingleLineBorderModel();
            _border.addBead(new SingleLineBorderBead());
			_dataGroup = new NonVirtualDataGroup();
			UIBase(_dataGroup).addToParent(UIBase(_strand));
			listModel = value.getBeadByType(ISelectionModel) as ISelectionModel;
			listModel.addEventListener("selectedIndexChanged", selectionChangeHandler);
		}

		private var lastSelectedIndex:int = -1;

		private function selectionChangeHandler(event:Event):void
		{
			if (lastSelectedIndex != -1)
			{
				var ir:IItemRenderer = dataGroup.getItemRendererForIndex(lastSelectedIndex) as IItemRenderer;
                ir.selected = false;
			}
            ir = dataGroup.getItemRendererForIndex(listModel.selectedIndex);
            ir.selected = true;
            lastSelectedIndex = listModel.selectedIndex;
		}

		public function initSkin():void
		{
		}

		private function createScrollBar():ScrollBar
		{
			var vsb:ScrollBar;
			vsb = new ScrollBar();
			vsb.addToParent(UIBase(_strand));
			var vsbm:ScrollBarModel = new ScrollBarModel();
			vsbm.maximum = 100;
			vsbm.minimum = 0;
			vsbm.pageSize = 10;
			vsbm.pageStepSize = 10;
			vsbm.snapInterval = 1;
			vsbm.stepSize = 1;
			vsbm.value = 0;
			vsb.model = vsbm;
			vsb.initModel();
			vsb.width = 16;
			var vsbb:ScrollBarBead = new ScrollBarBead();
			vsbb.initSkin();
			vsb.addBead(vsbb);
			var vsbl:VScrollBarLayout = new VScrollBarLayout();
			vsbb.addBead(vsbl);
			var vsbc:VScrollBarMouseController = new VScrollBarMouseController();
			vsb.addBead(vsbc);
			return vsb;
		}

		// beads declared in MXML are added to the strand.
		// from AS, just call addBead()
		public var beads:Array;

		private var _beads:Vector.<IBead>;
		public function addBead(bead:IBead):void
		{
			if (!_beads)
				_beads = new Vector.<IBead>;
			_beads.push(bead);
			bead.strand = this;
		}

		public function getBeadByType(classOrInterface:Class):IBead
		{
			for each (var bead:IBead in _beads)
			{
				if (bead is classOrInterface)
					return bead;
			}
			return null;
		}

		public function removeBead(value:IBead):IBead
		{
			var n:int = _beads.length;
			for (var i:int = 0; i < n; i++)
			{
				var bead:IBead = _beads[i];
				if (bead == value)
				{
					_beads.splice(i, 1);
					return bead;
				}
			}
			return null;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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.html.staticControls.beads
{
	import flash.events.Event;

	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IInitSkin;
	import org.apache.flex.core.IItemRenderer;
	import org.apache.flex.core.IItemRendererParent;
	import org.apache.flex.core.ISelectionModel;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.UIBase;
	import org.apache.flex.html.staticControls.beads.controllers.VScrollBarMouseController;
	import org.apache.flex.html.staticControls.beads.layouts.VScrollBarLayout;
	import org.apache.flex.html.staticControls.beads.models.ScrollBarModel;
	import org.apache.flex.html.staticControls.beads.models.SingleLineBorderModel;
	import org.apache.flex.html.staticControls.supportClasses.Border;
	import org.apache.flex.html.staticControls.supportClasses.NonVirtualDataGroup;
	import org.apache.flex.html.staticControls.supportClasses.ScrollBar;

	public class ListBead implements IBead, IInitSkin, IStrand, IListBead
	{
		public function ListBead()
		{
		}

		private var listModel:ISelectionModel;

        private var _border:Border;

        public function get border():Border
        {
            return _border;
        }

        private var _dataGroup:IItemRendererParent;

		public function get dataGroup():IItemRendererParent
		{
			return _dataGroup;
		}

		private var _vScrollBar:ScrollBar;

		public function get vScrollBar():ScrollBar
		{
            if (!_vScrollBar)
                _vScrollBar = createScrollBar();
			return _vScrollBar;
		}

		private var _strand:IStrand;

		public function get strand():IStrand
		{
			return _strand;
		}
		public function set strand(value:IStrand):void
		{
			_strand = value;
            _border = new Border();
            border.addToParent(UIBase(_strand));
            _border.model = new SingleLineBorderModel();
            _border.addBead(new SingleLineBorderBead());
			_dataGroup = new NonVirtualDataGroup();
			UIBase(_dataGroup).addToParent(UIBase(_strand));
			listModel = value.getBeadByType(ISelectionModel) as ISelectionModel;
			listModel.addEventListener("selectedIndexChanged", selectionChangeHandler);
		}

		private var lastSelectedIndex:int = -1;

		private function selectionChangeHandler(event:Event):void
		{
			if (lastSelectedIndex != -1)
			{
				var ir:IItemRenderer = dataGroup.getItemRendererForIndex(lastSelectedIndex) as IItemRenderer;
                ir.selected = false;
			}
			if (listModel.selectedIndex != -1)
			{
	            ir = dataGroup.getItemRendererForIndex(listModel.selectedIndex);
	            ir.selected = true;
			}
            lastSelectedIndex = listModel.selectedIndex;
		}

		public function initSkin():void
		{
		}

		private function createScrollBar():ScrollBar
		{
			var vsb:ScrollBar;
			vsb = new ScrollBar();
			vsb.addToParent(UIBase(_strand));
			var vsbm:ScrollBarModel = new ScrollBarModel();
			vsbm.maximum = 100;
			vsbm.minimum = 0;
			vsbm.pageSize = 10;
			vsbm.pageStepSize = 10;
			vsbm.snapInterval = 1;
			vsbm.stepSize = 1;
			vsbm.value = 0;
			vsb.model = vsbm;
			vsb.initModel();
			vsb.width = 16;
			var vsbb:ScrollBarBead = new ScrollBarBead();
			vsbb.initSkin();
			vsb.addBead(vsbb);
			var vsbl:VScrollBarLayout = new VScrollBarLayout();
			vsbb.addBead(vsbl);
			var vsbc:VScrollBarMouseController = new VScrollBarMouseController();
			vsb.addBead(vsbc);
			return vsb;
		}

		// beads declared in MXML are added to the strand.
		// from AS, just call addBead()
		public var beads:Array;

		private var _beads:Vector.<IBead>;
		public function addBead(bead:IBead):void
		{
			if (!_beads)
				_beads = new Vector.<IBead>;
			_beads.push(bead);
			bead.strand = this;
		}

		public function getBeadByType(classOrInterface:Class):IBead
		{
			for each (var bead:IBead in _beads)
			{
				if (bead is classOrInterface)
					return bead;
			}
			return null;
		}

		public function removeBead(value:IBead):IBead
		{
			var n:int = _beads.length;
			for (var i:int = 0; i < n; i++)
			{
				var bead:IBead = _beads[i];
				if (bead == value)
				{
					_beads.splice(i, 1);
					return bead;
				}
			}
			return null;
		}
	}
}

fix bug when selectedIndex == -1

fix bug when selectedIndex == -1


package battlecode.serial {
    import battlecode.events.ParseEvent;
    import battlecode.util.GZIP;

    import flash.errors.MemoryError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.events.TimerEvent;
    import flash.net.URLRequest;
    import flash.net.URLStream;
    import flash.system.System;
    import flash.utils.ByteArray;
    import flash.utils.Timer;
    import flash.utils.getTimer;

    import mx.controls.Alert;

    [Event(name="parseProgress", type="battlecode.events.ParseEvent")]
    [Event(name="parseComplete", type="battlecode.events.ParseEvent")]
    [Event(name="progress", type="flash.events.ProgressEvent")]
    [Event(name="complete", type="flash.events.Event")]
    public class MatchLoader extends EventDispatcher {
        private var matchPath:String;
        private var stream:URLStream;
        private var matches:Vector.<Match>;
        private var numMatches:uint = 0;
        private var builder:MatchBuilder;
        private var nameA:String, nameB:String;

        private var gamesXML:XMLList;
        private var currentIndex:uint = 0;
        private var gameTimer:Timer;

        public function MatchLoader() {
            this.stream = new URLStream();
            this.stream.addEventListener(ProgressEvent.PROGRESS, onDownloadProgress, false, 0, true);
            this.stream.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
            this.stream.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
            this.stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError, false, 0, true);
        }

        public function load(file:String):void {
            matchPath = file;
            this.stream.load(new URLRequest(file));
        }

        public function loadData(matchBytes:ByteArray):void {
            // decompress the bytes (they are GZIP'd)
            try {
                matchBytes = GZIP.decompress(matchBytes);
            } catch (e:Error) {
            }

            trace("--- DECOMPRESSION ---");
            trace("MEM USAGE: " + Math.round(System.totalMemory / 1024 / 1024) + "MB");

            try {
                // parse the xml from the decompressed byte stream
                var xmlStr:String = matchBytes.readUTFBytes(matchBytes.bytesAvailable);
                var xml:XML = new XML(xmlStr);
            } catch (e:MemoryError) {
                Alert.show("Memory allocation error: " + e.message);
                return;
            } catch (e:TypeError) {
                trace("TypeError: " + e.message);
                Alert.show("Unauthorized: " + e.message);
                return;
            }

            // clear match bytes
            matchBytes.clear();

            // generate new matches
            numMatches = parseInt(xml.child("ser.MatchHeader").length());
            matches = new Vector.<Match>();

            gamesXML = xml.children();

            gameTimer = new Timer(30);
            gameTimer.addEventListener(TimerEvent.TIMER, onTimerTick);
            gameTimer.start();

            trace("--- XML PARSE ---");
            trace("MEM USAGE: " + Math.round(System.totalMemory / 1000 / 1000) + "MB");

            dispatchEvent(new Event(Event.COMPLETE));
        }

        public function setTeamNames(nameA:String, nameB:String):void {
            this.nameA = nameA;
            this.nameB = nameB;
        }

        public function getMatches():Vector.<Match> {
            return matches;
        }

        public function getNumMatches():uint {
            return numMatches;
        }

        private function onDownloadProgress(e:ProgressEvent):void {
            dispatchEvent(e);
        }

        private function onComplete(e:Event):void {
            var matchBytes:ByteArray = new ByteArray();
            stream.readBytes(matchBytes);
            stream.close();
            stream = null;

            loadData(matchBytes);
        }

        private function onIOError(e:IOErrorEvent):void {
            trace("match load failed: " + e.toString());
            Alert.show("Could not load the specified match file: File " + matchPath + " not found");
        }

        private function onSecurityError(e:SecurityError):void {
            trace("match load failed: " + e.toString());
            Alert.show("Could not load the specified match file: Security error");
        }

        private function onTimerTick(e:TimerEvent):void {
            var start:Number = getTimer();
            while(getTimer() - start < 30 && currentIndex < gamesXML.length()) {
                var node:XML = gamesXML[currentIndex++];
                var nodeName:String = node.name().toString();
                switch (nodeName) {
                    case "ser.StoredConstants":
                        StoredConstantsLoader.loadConstants(node);
                        break;
                    case "ser.MatchHeader":
                        builder = new MatchBuilder();
                        builder.setHeader(node);
                        builder.setTeamNames(nameA, nameB);
                        break;
                    case "ser.MatchFooter":
                        builder.setFooter(node);
                        matches.push(builder.build());
                        builder = null;
                        trace("--- MATCH "+matches.length+" ---");
                        trace("MEM USAGE: " + Math.round(System.totalMemory / 1024 / 1024) + "MB");
                        break;
                    case "ser.ExtensibleMetadata":
                        builder.setExtensibleMetadata(node);
                        break;
                    case "ser.GameStats":
                        builder.setGameStats(node);
                        break;
                    case "ser.RoundDelta":
                        builder.addRoundDelta(node);
                        break;
                    case "ser.RoundStats":
                        builder.addRoundStats(node);
                        break;
                    default:
                        trace("Unknown node: " + node.name());
                        break;
                }
            }

            var progressEvent:ParseEvent;
            if (matches.length == numMatches) {
                progressEvent = new ParseEvent(ParseEvent.COMPLETE);
                gameTimer.stop();
            } else {
                progressEvent = new ParseEvent(ParseEvent.PROGRESS);
            }
            progressEvent.rowsParsed = currentIndex;
            progressEvent.rowsTotal = gamesXML.length();
            progressEvent.matchesParsed = matches.length;
            progressEvent.matchesTotal = numMatches;
            dispatchEvent(progressEvent);
        }
    }
}

package battlecode.serial {
    import battlecode.events.ParseEvent;
    import battlecode.util.GZIP;

    import flash.errors.MemoryError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.events.TimerEvent;
    import flash.net.URLRequest;
    import flash.net.URLStream;
    import flash.system.System;
    import flash.utils.ByteArray;
    import flash.utils.Timer;
    import flash.utils.getTimer;

    import mx.controls.Alert;

    [Event(name="parseProgress", type="battlecode.events.ParseEvent")]
    [Event(name="parseComplete", type="battlecode.events.ParseEvent")]
    [Event(name="progress", type="flash.events.ProgressEvent")]
    [Event(name="complete", type="flash.events.Event")]
    public class MatchLoader extends EventDispatcher {
        private var matchPath:String;
        private var stream:URLStream;
        private var matches:Vector.<Match>;
        private var numMatches:uint = 0;
        private var builder:MatchBuilder;
        private var nameA:String, nameB:String;

        private var gamesXML:XMLList;
        private var currentIndex:uint = 0;
        private var gameTimer:Timer;

        public function MatchLoader() {
            this.stream = new URLStream();
            this.stream.addEventListener(ProgressEvent.PROGRESS, onDownloadProgress, false, 0, true);
            this.stream.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
            this.stream.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
            this.stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError, false, 0, true);
        }

        public function load(file:String):void {
            matchPath = file;
            this.stream.load(new URLRequest(file));
        }

        public function loadData(matchBytes:ByteArray):void {
            // decompress the bytes (they are GZIP'd)
            try {
                matchBytes = GZIP.decompress(matchBytes);
            } catch (e:Error) {
            }

            trace("--- DECOMPRESSION ---");
            trace("MEM USAGE: " + Math.round(System.totalMemory / 1024 / 1024) + "MB");

            try {
                // parse the xml from the decompressed byte stream
                var xmlStr:String = matchBytes.readUTFBytes(matchBytes.bytesAvailable);
                var xml:XML = new XML(xmlStr);
            } catch (e:MemoryError) {
                Alert.show("Memory allocation error: " + e.message);
                return;
            } catch (e:TypeError) {
                trace("TypeError: " + e.message);
                Alert.show("Unauthorized: " + e.message);
                return;
            }

            // clear match bytes
            matchBytes.clear();

            // generate new matches
            numMatches = parseInt(xml.child("ser.MatchHeader").length());
            matches = new Vector.<Match>();

            gamesXML = xml.children();

            gameTimer = new Timer(30);
            gameTimer.addEventListener(TimerEvent.TIMER, onTimerTick);
            gameTimer.start();

            trace("--- XML PARSE ---");
            trace("MEM USAGE: " + Math.round(System.totalMemory / 1000 / 1000) + "MB");

            dispatchEvent(new Event(Event.COMPLETE));
        }

        public function setTeamNames(nameA:String, nameB:String):void {
            this.nameA = nameA;
            this.nameB = nameB;
        }

        public function getMatches():Vector.<Match> {
            return matches;
        }

        public function getNumMatches():uint {
            return numMatches;
        }

        private function onDownloadProgress(e:ProgressEvent):void {
            dispatchEvent(e);
        }

        private function onComplete(e:Event):void {
            var matchBytes:ByteArray = new ByteArray();
            stream.readBytes(matchBytes);
            stream.close();
            stream = null;

            loadData(matchBytes);
        }

        private function onIOError(e:IOErrorEvent):void {
            trace("match load failed: " + e.toString());
            Alert.show("Could not load the specified match file: File " + matchPath + " not found");
        }

        private function onSecurityError(e:SecurityErrorEvent):void {
            trace("match load failed: " + e.toString());
            Alert.show("Could not load the specified match file: Security error");
        }

        private function onTimerTick(e:TimerEvent):void {
            var start:Number = getTimer();
            while(getTimer() - start < 30 && currentIndex < gamesXML.length()) {
                var node:XML = gamesXML[currentIndex++];
                var nodeName:String = node.name().toString();
                switch (nodeName) {
                    case "ser.StoredConstants":
                        StoredConstantsLoader.loadConstants(node);
                        break;
                    case "ser.MatchHeader":
                        builder = new MatchBuilder();
                        builder.setHeader(node);
                        builder.setTeamNames(nameA, nameB);
                        break;
                    case "ser.MatchFooter":
                        builder.setFooter(node);
                        matches.push(builder.build());
                        builder = null;
                        trace("--- MATCH "+matches.length+" ---");
                        trace("MEM USAGE: " + Math.round(System.totalMemory / 1024 / 1024) + "MB");
                        break;
                    case "ser.ExtensibleMetadata":
                        builder.setExtensibleMetadata(node);
                        break;
                    case "ser.GameStats":
                        builder.setGameStats(node);
                        break;
                    case "ser.RoundDelta":
                        builder.addRoundDelta(node);
                        break;
                    case "ser.RoundStats":
                        builder.addRoundStats(node);
                        break;
                    default:
                        trace("Unknown node: " + node.name());
                        break;
                }
            }

            var progressEvent:ParseEvent;
            if (matches.length == numMatches) {
                progressEvent = new ParseEvent(ParseEvent.COMPLETE);
                gameTimer.stop();
            } else {
                progressEvent = new ParseEvent(ParseEvent.PROGRESS);
            }
            progressEvent.rowsParsed = currentIndex;
            progressEvent.rowsTotal = gamesXML.length();
            progressEvent.matchesParsed = matches.length;
            progressEvent.matchesTotal = numMatches;
            dispatchEvent(progressEvent);
        }
    }
}

fix event type

fix event type


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

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

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

use namespace dolly_internal;

public class CloningOfCompositeCloneableClassTest {

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

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

		compositeCloneableClassType = Type.forInstance(compositeCloneableClass);
	}

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

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

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

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

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

		const arrayList:ArrayList = clone.arrayList;

		assertNotNull(arrayList);
		assertFalse(clone.arrayList == compositeCloneableClass.arrayList);
		assertEquals(arrayList.length, 5);
		assertEquals(arrayList.getItemAt(0), 1);
		assertEquals(arrayList.getItemAt(1), 2);
		assertEquals(arrayList.getItemAt(2), 3);
		assertEquals(arrayList.getItemAt(3), 4);
		assertEquals(arrayList.getItemAt(4), 5);
	}

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

		const arrayCollection:ArrayCollection = clone.arrayCollection;

		assertNotNull(arrayCollection);
		assertFalse(clone.arrayCollection == compositeCloneableClass.arrayCollection);
		assertEquals(arrayCollection.length, 5);
		assertEquals(arrayCollection.getItemAt(0), 1);
		assertEquals(arrayCollection.getItemAt(1), 2);
		assertEquals(arrayCollection.getItemAt(2), 3);
		assertEquals(arrayCollection.getItemAt(3), 4);
		assertEquals(arrayCollection.getItemAt(4), 5);
	}
}
}

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

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

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

use namespace dolly_internal;

public class CloningOfCompositeCloneableClassTest {

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

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

		const nestedCloneable:CloneableClass = new CloneableClass();
		nestedCloneable.property1 = "property1 value";
		nestedCloneable.writableField1 = "writableField1 value";

		const nestedCompositeCloneable:CompositeCloneableClass = new CompositeCloneableClass();
		nestedCompositeCloneable.compositeCloneable = compositeCloneableClass;
		nestedCompositeCloneable.array = [1, 2, 3];
		nestedCompositeCloneable.arrayList = new ArrayList([1, 2, 3]);
		nestedCompositeCloneable.arrayCollection = new ArrayCollection([1, 2, 3]);

		compositeCloneableClass.cloneable = nestedCloneable;
		compositeCloneableClass.compositeCloneable = nestedCompositeCloneable;
		compositeCloneableClass.array = [1, 2, 3, 4, 5];
		compositeCloneableClass.arrayList = new ArrayList([1, 2, 3, 4, 5]);
		compositeCloneableClass.arrayCollection = new ArrayCollection([1, 2, 3, 4, 5]);

		compositeCloneableClassType = Type.forInstance(compositeCloneableClass);
	}

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

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

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

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

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

		const arrayList:ArrayList = clone.arrayList;

		assertNotNull(arrayList);
		assertFalse(clone.arrayList == compositeCloneableClass.arrayList);
		assertEquals(arrayList.length, 5);
		assertEquals(arrayList.getItemAt(0), 1);
		assertEquals(arrayList.getItemAt(1), 2);
		assertEquals(arrayList.getItemAt(2), 3);
		assertEquals(arrayList.getItemAt(3), 4);
		assertEquals(arrayList.getItemAt(4), 5);
	}

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

		const arrayCollection:ArrayCollection = clone.arrayCollection;

		assertNotNull(arrayCollection);
		assertFalse(clone.arrayCollection == compositeCloneableClass.arrayCollection);
		assertEquals(arrayCollection.length, 5);
		assertEquals(arrayCollection.getItemAt(0), 1);
		assertEquals(arrayCollection.getItemAt(1), 2);
		assertEquals(arrayCollection.getItemAt(2), 3);
		assertEquals(arrayCollection.getItemAt(3), 4);
		assertEquals(arrayCollection.getItemAt(4), 5);
	}
}
}

Initialize props "cloneable" and "compositeCloneable" of CompositeCloneableClass in tests.


package com.kaltura.kdpfl.plugin.component
{
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.EnableType;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.types.KalturaMediaType;
	import com.kaltura.vo.KalturaMediaEntry;

	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.external.ExternalInterface;
	import flash.net.SharedObject;
	import flash.net.URLRequest;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;

	public class BitrateDetectionMediator extends Mediator
	{
		public static var NAME:String = "bitrateDetectionMediator";
		/**
		 * Flag indicating autoPlay flashvar was true
		 */
		private var _wasAutoPlay:Boolean = false;
		/**
		 * Flag indicating singleAutoPlay was true
		 */
		private var _wasSingleAutoPlay:Boolean = false;
		/**
		 * The url loader
		 */
		private var _loader:Loader;
		/**
		 * average download speed
		 */
		private var _avgSpeed:Number = 0;
		/**
		 * counter for progress events, will be used to calculate average speed
		 */
		private var _progressCount:int;
		/**
		 * timer for the download process
		 */
		private var _downloadTimer:Timer;
		/**
		 * startTime of the download process
		 */
		private var _startTime:Number;
		/**
		 * Indicating if player has already played
		 */
		private var _playedPlayed:Boolean = false;

		private var _prevTime:Number;
		private var _prevBytesLoaded:int;
		private var _forceBitrate:int;

		public function BitrateDetectionMediator(viewComponentObject:Object = null , forceBitrate:int = 0)
		{
			_forceBitrate = forceBitrate;
			super(NAME, viewComponentObject);
		}

		override public function listNotificationInterests():Array
		{
			return [
				NotificationType.DO_SWITCH,
				NotificationType.KDP_EMPTY,
				NotificationType.KDP_READY,
				NotificationType.PLAYER_PLAYED,
				NotificationType.MEDIA_READY
				];
		}

		private var _bandwidth:Number = 0;
		private var _bandwidthByUser:Number = 0;

		override public function handleNotification(notification:INotification):void
		{
			//check bitrate only before player played
/*			if (_playedPlayed)
			{
				return;
			}*/

			switch (notification.getName())
			{
				//in case the user switched the flavor manually
				case NotificationType.DO_SWITCH:
					_bandwidthByUser = int(notification.getBody())
					break;
				case NotificationType.MEDIA_READY:
					if(_bandwidthByUser)
					{
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidthByUser});
						return;
					}
					if(_bandwidth)
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidth});
					break;
				case NotificationType.KDP_EMPTY:
				case NotificationType.KDP_READY:
					var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
					if (!mediaProxy.vo.entry ||
						((mediaProxy.vo.entry is KalturaMediaEntry) && (int(mediaProxy.vo.entry.mediaType)==KalturaMediaType.IMAGE)))
						break;
					trace("bitrate detection:", notification.getName());
					var configProxy:ConfigProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
					if ((viewComponent as bitrateDetectionPluginCode).useFlavorCookie)
					{
						var flavorCookie : SharedObject;
						try
						{
							//Check to see if we have a cookie
							flavorCookie = SharedObject.getLocal("kaltura");
						}
						catch (e: Error)
						{
							//if not just start download
							trace ("no permissions to access partner's file system");
							startDownload();
							return;
						}
						if (flavorCookie && flavorCookie.data)
						{
							//If we are in Auto Switch or the first time we run it - do the download test
							if(!flavorCookie.data.preferedFlavorBR || (flavorCookie.data.preferedFlavorBR == -1))
							{
								startDownload();
							}
						}
					}
					//disable bitrate cookie--> start detection
					else
					{
						startDownload();
					}
					break;
				case NotificationType.PLAYER_PLAYED:
					if(_bandwidth)
						return;
					_playedPlayed = true;
					break;
			}
		}

		/**
		 * Start a download process to find the preferred bitrate
		 *
		 */
		public function startDownload() : void
		{
			trace ("bitrate detection: start download");
			if (!(viewComponent as bitrateDetectionPluginCode).downloadUrl)
				return;

			var configProxy:ConfigProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
			var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			//disable autoPlay - will change it back once the bitrate detection will finish
			if (configProxy.vo.flashvars.autoPlay == "true")
			{
				trace ("bitrate detection: was auto play");
				configProxy.vo.flashvars.autoPlay = "false";
				_wasAutoPlay = true;
			}
			if (mediaProxy.vo.singleAutoPlay)
			{
				mediaProxy.vo.singleAutoPlay = false;
				_wasSingleAutoPlay = true;
			}
			_startTime = ( new Date( ) ).getTime( );
			_progressCount = 0;
			_prevTime = _startTime;
			_prevBytesLoaded = 0;
			_loader = new Loader( );
			_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, downloadComplete );
			_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
			_loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			_downloadTimer = new Timer ((viewComponent as bitrateDetectionPluginCode).downloadTimeoutMS, 1);
			_downloadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: false, enableType: EnableType.FULL});
			sendNotification( NotificationType.SWITCHING_CHANGE_STARTED, {newIndex: -2,  newBitrate: null});
			_loader.load( new URLRequest( (viewComponent as bitrateDetectionPluginCode).downloadUrl + "?" + ( Math.random( ) * 100000 )) );
			_downloadTimer.start();
		}

		/**
		 * on download progress- calculate average speed
		 * @param event
		 *
		 */
		private function onProgress (event:ProgressEvent):void
		{
			var curTime:Number = ( new Date( ) ).getTime( );
			_progressCount++;
			if (event.bytesLoaded!=0)
			{
				var totalTime:Number =( curTime - _startTime ) / 1000;
				var totalKB:Number = event.bytesLoaded / 1024;
				_avgSpeed = totalKB / totalTime;
			}
			_prevTime = curTime;
			_prevBytesLoaded = event.bytesLoaded;
		}

		/**
		 *  download complete handler - set the preferred bitrate, enable GUI
		 *
		 */
		private function downloadComplete( e:Event = null):void
		{
			_downloadTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			_loader.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, onProgress );

			var bitrateVal:int = _avgSpeed * 8;
			trace("*** preferred bitrate for bitrate detection plugin:", bitrateVal);

			//for debugging - force a bitrate value to override the calculated one
			if(_forceBitrate > 0)
			{
				bitrateVal = _forceBitrate;
			}

			//for testing - expose the # via JS
			try
			{
				ExternalInterface.call('bitrateValue' , bitrateVal);
			}
			catch(error:Error)
			{

			}

			sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: bitrateVal});

			_bandwidth = bitrateVal;

			//write to cookie
			var flavorCookie : SharedObject;
			try
			{
				flavorCookie = SharedObject.getLocal("kaltura");
			}
			catch (e : Error)
			{
				trace("No access to user's file system");
			}
			if (flavorCookie && flavorCookie.data)
			{
				flavorCookie.data.preferedFlavorBR = bitrateVal;
				flavorCookie.flush();
			}

			finishDownloadProcess();
		}

		/**
		 * default timer time has passed, stop listening to progress event and continue the flow
		 * @param event
		 *
		 */
		private function onTimerComplete (event:TimerEvent):void {
			downloadComplete();
		}

		/**
		 * I/O error getting the sample file, release the UI
		 * @param event
		 *
		 */
		private function ioErrorHandler(event:IOErrorEvent) : void
		{
			//Bypass: ignore #2124 error (loaded file is an unknown type)
			if (!event.text || event.text.indexOf("Error #2124")==-1)
			{
				trace ("bitrate detection i/o error:", event.text);
				finishDownloadProcess();
			}
		}

		/**
		 * enable back the GUI.
		 * call Do_Play if we were in auto play and set back relevant flashvars
		 *
		 */
		private function finishDownloadProcess():void {
			//if we changed variables, set them back
			if (_wasAutoPlay || _wasSingleAutoPlay) {
				var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
				var sequenceProxy:SequenceProxy = facade.retrieveProxy(SequenceProxy.NAME) as SequenceProxy;
				if (_wasAutoPlay)
				{
					var configProxy:ConfigProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
					configProxy.vo.flashvars.autoPlay = "true";
				}
				else
				{
					mediaProxy.vo.singleAutoPlay = true;
				}
				//play if we should have played
				if (!_playedPlayed && !mediaProxy.vo.isMediaDisabled && !sequenceProxy.vo.isInSequence)
				{
					sendNotification(NotificationType.DO_PLAY);
				}
			}

			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: true, enableType: EnableType.FULL});
		}

	}

}

package com.kaltura.kdpfl.plugin.component
{
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.EnableType;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.types.KalturaMediaType;
	import com.kaltura.vo.KalturaMediaEntry;

	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.external.ExternalInterface;
	import flash.net.SharedObject;
	import flash.net.URLRequest;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;

	public class BitrateDetectionMediator extends Mediator
	{
		public static var NAME:String = "bitrateDetectionMediator";
		/**
		 * Flag indicating autoPlay flashvar was true
		 */
		private var _wasAutoPlay:Boolean = false;
		/**
		 * Flag indicating singleAutoPlay was true
		 */
		private var _wasSingleAutoPlay:Boolean = false;
		/**
		 * The url loader
		 */
		private var _loader:Loader;
		/**
		 * average download speed
		 */
		private var _avgSpeed:Number = 0;
		/**
		 * counter for progress events, will be used to calculate average speed
		 */
		private var _progressCount:int;
		/**
		 * timer for the download process
		 */
		private var _downloadTimer:Timer;
		/**
		 * startTime of the download process
		 */
		private var _startTime:Number;
		/**
		 * Indicating if player has already played
		 */
		private var _playedPlayed:Boolean = false;

		private var _configProxy:ConfigProxy;

		private var _prevTime:Number;
		private var _prevBytesLoaded:int;
		private var _forceBitrate:int;

		public function BitrateDetectionMediator(viewComponentObject:Object = null , forceBitrate:int = 0)
		{
			_forceBitrate = forceBitrate;
			super(NAME, viewComponentObject);
		}

		override public function listNotificationInterests():Array
		{
			return [
				NotificationType.DO_SWITCH,
				NotificationType.KDP_EMPTY,
				NotificationType.KDP_READY,
				NotificationType.PLAYER_PLAYED,
				NotificationType.MEDIA_READY
				];
		}

		private var _bandwidth:Number = 0;
		private var _bandwidthByUser:Number = 0;

		override public function handleNotification(notification:INotification):void
		{
			//check bitrate only before player played
/*			if (_playedPlayed)
			{
				return;
			}*/

			switch (notification.getName())
			{
				//in case the user switched the flavor manually
				case NotificationType.DO_SWITCH:
					_bandwidthByUser = int(notification.getBody())
					break;
				case NotificationType.MEDIA_READY:
					if(_bandwidthByUser)
					{
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidthByUser});
						return;
					}
					if(_bandwidth)
						sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: _bandwidth});
					break;
				case NotificationType.KDP_EMPTY:
				case NotificationType.KDP_READY:
					var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
					if (!mediaProxy.vo.entry ||
						((mediaProxy.vo.entry is KalturaMediaEntry) && (int(mediaProxy.vo.entry.mediaType)==KalturaMediaType.IMAGE)))
						break;
					trace("bitrate detection:", notification.getName());
					_configProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
					if ((viewComponent as bitrateDetectionPluginCode).useFlavorCookie)
					{
						var flavorCookie : SharedObject;
						try
						{
							//Check to see if we have a cookie
							flavorCookie = SharedObject.getLocal("kaltura");
						}
						catch (e: Error)
						{
							//if not just start download
							trace ("no permissions to access partner's file system");
							startDownload();
							return;
						}
						if (flavorCookie && flavorCookie.data)
						{
							//If we are in Auto Switch or the first time we run it - do the download test
							if(!flavorCookie.data.preferedFlavorBR || (flavorCookie.data.preferedFlavorBR == -1))
							{
								startDownload();
							}
						}
					}
					//disable bitrate cookie--> start detection
					else
					{
						startDownload();
					}
					break;
				case NotificationType.PLAYER_PLAYED:
					if(_bandwidth)
						return;
					_playedPlayed = true;
					break;
			}
		}

		/**
		 * Start a download process to find the preferred bitrate
		 *
		 */
		public function startDownload() : void
		{
			trace ("bitrate detection: start download");
			if (!(viewComponent as bitrateDetectionPluginCode).downloadUrl)
				return;

			var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			//disable autoPlay - will change it back once the bitrate detection will finish
			if (_configProxy.vo.flashvars.autoPlay == "true")
			{
				trace ("bitrate detection: was auto play");
				_configProxy.vo.flashvars.autoPlay = "false";
				_wasAutoPlay = true;
			}
			if (mediaProxy.vo.singleAutoPlay)
			{
				mediaProxy.vo.singleAutoPlay = false;
				_wasSingleAutoPlay = true;
			}
			_startTime = ( new Date( ) ).getTime( );
			_progressCount = 0;
			_prevTime = _startTime;
			_prevBytesLoaded = 0;
			_loader = new Loader( );
			_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, downloadComplete );
			_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
			_loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			_downloadTimer = new Timer ((viewComponent as bitrateDetectionPluginCode).downloadTimeoutMS, 1);
			_downloadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: false, enableType: EnableType.FULL});
			sendNotification( NotificationType.SWITCHING_CHANGE_STARTED, {newIndex: -2,  newBitrate: null});
			_loader.load( new URLRequest( (viewComponent as bitrateDetectionPluginCode).downloadUrl + "?" + ( Math.random( ) * 100000 )) );
			_downloadTimer.start();
		}

		/**
		 * on download progress- calculate average speed
		 * @param event
		 *
		 */
		private function onProgress (event:ProgressEvent):void
		{
			var curTime:Number = ( new Date( ) ).getTime( );
			_progressCount++;
			if (event.bytesLoaded!=0)
			{
				var totalTime:Number =( curTime - _startTime ) / 1000;
				var totalKB:Number = event.bytesLoaded / 1024;
				_avgSpeed = totalKB / totalTime;
			}
			_prevTime = curTime;
			_prevBytesLoaded = event.bytesLoaded;
		}

		/**
		 *  download complete handler - set the preferred bitrate, enable GUI
		 *
		 */
		private function downloadComplete( e:Event = null):void
		{
			_downloadTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			_loader.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, onProgress );

			var bitrateVal:int = _avgSpeed * 8;
			trace("*** preferred bitrate for bitrate detection plugin:", bitrateVal);

			//for debugging - force a bitrate value to override the calculated one
			if(_forceBitrate > 0)
			{
				bitrateVal = _forceBitrate;
			}

			//for testing - expose the # via JS
			try
			{
				ExternalInterface.call('bitrateValue' , bitrateVal);
			}
			catch(error:Error)
			{

			}

			sendNotification(NotificationType.CHANGE_PREFERRED_BITRATE, {bitrate: bitrateVal});

			_bandwidth = bitrateVal;

			//write to cookie
			if (_configProxy.vo.flashvars.allowCookies=="true")
			{
				var flavorCookie : SharedObject;
				try
				{
					flavorCookie = SharedObject.getLocal("kaltura");
				}
				catch (e : Error)
				{
					trace("No access to user's file system");
				}
				if (flavorCookie && flavorCookie.data)
				{
					flavorCookie.data.preferedFlavorBR = bitrateVal;
					flavorCookie.flush();
				}
			}

			finishDownloadProcess();
		}

		/**
		 * default timer time has passed, stop listening to progress event and continue the flow
		 * @param event
		 *
		 */
		private function onTimerComplete (event:TimerEvent):void {
			downloadComplete();
		}

		/**
		 * I/O error getting the sample file, release the UI
		 * @param event
		 *
		 */
		private function ioErrorHandler(event:IOErrorEvent) : void
		{
			//Bypass: ignore #2124 error (loaded file is an unknown type)
			if (!event.text || event.text.indexOf("Error #2124")==-1)
			{
				trace ("bitrate detection i/o error:", event.text);
				finishDownloadProcess();
			}
		}

		/**
		 * enable back the GUI.
		 * call Do_Play if we were in auto play and set back relevant flashvars
		 *
		 */
		private function finishDownloadProcess():void {
			//if we changed variables, set them back
			if (_wasAutoPlay || _wasSingleAutoPlay) {
				var mediaProxy:MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
				var sequenceProxy:SequenceProxy = facade.retrieveProxy(SequenceProxy.NAME) as SequenceProxy;
				if (_wasAutoPlay)
				{
					_configProxy.vo.flashvars.autoPlay = "true";
				}
				else
				{
					mediaProxy.vo.singleAutoPlay = true;
				}
				//play if we should have played
				if (!_playedPlayed && !mediaProxy.vo.isMediaDisabled && !sequenceProxy.vo.isInSequence)
				{
					sendNotification(NotificationType.DO_PLAY);
				}
			}

			sendNotification(NotificationType.ENABLE_GUI, {guiEnabled: true, enableType: EnableType.FULL});
		}

	}

}

support in "allowCookies" flashvar

qnd: support in "allowCookies" flashvar

git-svn-id: 3f608e5a9a704dd448217c0a64c508e7f145cfa1@85787 6b8eccd3-e8c5-4e7d-8186-e12b5326b719