package com.kaltura.hls.manifest
{
	import com.hurlant.util.Hex;
	import com.kaltura.hls.crypto.FastAESKey;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;

	CONFIG::LOGGING
	{
		import org.osmf.logging.Logger;
        import org.osmf.logging.Log;
	}

	/**
	 * HLSManifestEncryptionKey is used to parse AES decryption key data from a m3u8
	 * manifest, load the specified key file into memory, and use the key data to decrypt
	 * audio and video streams. It supports AES-128 encryption with #PKCS7 padding, and uses
	 * explicitly passed in Initialization Vectors.
	 */

	public class HLSManifestEncryptionKey extends EventDispatcher
	{
        CONFIG::LOGGING
        {
            private static const logger:Logger = Log.getLogger("com.kaltura.hls.manifest.HLSManifestEncryptionKey");
        }

		private static const LOADER_CACHE:Dictionary = new Dictionary();
		private var _key:FastAESKey;
		public var usePadding:Boolean = false;
		public var iv:String = "";
		public var url:String = "";
		private var iv0 : uint;
		private var iv1 : uint;
		private var iv2 : uint;
		private var iv3 : uint;

		// Keep track of the segments this key applies to
		public var startSegmentId:uint = 0;
		public var endSegmentId:uint = uint.MAX_VALUE;

		private var _keyData:ByteArray;

		public var isLoading:Boolean = false;

		public function HLSManifestEncryptionKey()
		{

		}

		public function get isLoaded():Boolean { return _keyData != null; }

		public override function toString():String
		{
			return "[HLSManifestEncryptionKey start=" + startSegmentId +", end=" + endSegmentId + ", iv=" + iv + ", url=" + url + ", loaded=" + isLoaded + "]";
		}

		public static function fromParams( params:String ):HLSManifestEncryptionKey
		{
			var result:HLSManifestEncryptionKey = new HLSManifestEncryptionKey();

			var tokens:Array = KeyParamParser.parseParams( params );
			var tokenCount:int = tokens.length;

			for ( var i:int = 0; i < tokenCount; i += 2 )
			{
				var name:String = tokens[ i ];
				var value:String = tokens[ i + 1 ];

				switch ( name )
				{
					case "URI" :
						result.url = value;
						break;

					case "IV" :
						result.iv = value;
						break;
				}

			}

			return result;
		}

		/**
		 * Creates an initialization vector from the passed in uint ID, usually
		 * a segment ID.
		 */
		public static function createIVFromID( id:uint ):ByteArray
		{
			var result:ByteArray = new ByteArray();
			result.writeUnsignedInt( 0 );
			result.writeUnsignedInt( 0 );
			result.writeUnsignedInt( 0 );
			result.writeUnsignedInt( id );
			return result;
		}

		public static function clearLoaderCache():void
		{
			for ( var key:String in LOADER_CACHE ) delete LOADER_CACHE[ key ];
		}

		/**
		 * Decrypts a video or audio stream using AES-128 with the provided initialization vector.
		 */

		public function decrypt( data:ByteArray, iv:ByteArray ):ByteArray
		{
			//logger.debug("got " + data.length + " bytes");
			if(data.length == 0)
				return data;

			var startTime:uint = getTimer();
			_key = new FastAESKey(_keyData);
			iv.position = 0;
			iv0 = iv.readUnsignedInt();
			iv1 = iv.readUnsignedInt();
			iv2 = iv.readUnsignedInt();
			iv3 = iv.readUnsignedInt();
			data.position = 0;
			data = _decryptCBC(data,data.length);
			if ( usePadding ){
				data = unpad( data );
			}
		//	logger.debug( "DECRYPTION OF " + data.length + " BYTES TOOK " + ( getTimer() - startTime ) + " MS" );
			return data;
		}

		/* Cypher Block Chaining Decryption, refer to
		* http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_
		* for algorithm description
		*/
		private function _decryptCBC(crypt : ByteArray, len : uint) : ByteArray {
			var src : Vector.<uint> = new Vector.<uint>(4);
			var dst : Vector.<uint> = new Vector.<uint>(4);
			var decrypt : ByteArray = new ByteArray();
			decrypt.length = len;

			for (var i : uint = 0; i < len / 16; i++) {
				// read src byte array
				src[0] = crypt.readUnsignedInt();
				src[1] = crypt.readUnsignedInt();
				src[2] = crypt.readUnsignedInt();
				src[3] = crypt.readUnsignedInt();

				// AES decrypt src vector into dst vector
				_key.decrypt128(src, dst);

				// CBC : write output = XOR(decrypted,IV)
				decrypt.writeUnsignedInt(dst[0] ^ iv0);
				decrypt.writeUnsignedInt(dst[1] ^ iv1);
				decrypt.writeUnsignedInt(dst[2] ^ iv2);
				decrypt.writeUnsignedInt(dst[3] ^ iv3);

				// CBC : next IV = (input)
				iv0 = src[0];
				iv1 = src[1];
				iv2 = src[2];
				iv3 = src[3];
			}
			return decrypt;
		}
		public function unpad(bytesToUnpad : ByteArray) : ByteArray {
			if ((bytesToUnpad.length % 16) != 0)
			{
				throw new Error("PKCS#5::unpad: ByteArray.length isn't a multiple of the blockSize");
				return a;
			}

			const paddingValue:int = bytesToUnpad[bytesToUnpad.length - 1];

			if (paddingValue > 15)
			{
				//Return early, since any values bigger than 15 aren't actually padding.
				return bytesTounpad;
			}
			var doUnpad:Boolean = true;
			for (var i:int = 0; i<paddingValue; i++)
			{
				var readValue:int = bytesToUnpad[bytesToUnpad.length - (1 + i)];
				if (paddingValue != readValue)
				{
					doUnpad = false;
					//Break since we know we are not dealing with padding
					break;
				}
			}

			if(doUnpad)
			{
				//subtract paddingValue to remove the padding from the block
				bytesToUnpad.length -= paddingValue;
			}
			return bytesToUnpad;
		}

		public function retrieveStoredIV():ByteArray
		{
			CONFIG::LOGGING
			{
				logger.debug("IV of " + iv + " for " + url + ", key=" + Hex.fromArray(_keyData));
			}
			return Hex.toArray( iv );
		}

		private static function getLoader( url:String ):URLLoader
		{
			if ( LOADER_CACHE[ url ] != null ) return LOADER_CACHE[ url ] as URLLoader;
			var newLoader:URLLoader = new URLLoader();
			newLoader.dataFormat = URLLoaderDataFormat.BINARY;
			newLoader.load( new URLRequest( url ) );
			LOADER_CACHE[ url ] = newLoader;
			return newLoader;
		}

		public function load():void
		{
			isLoading = true;

			if ( isLoaded ) throw new Error( "Already loaded!" );
			var loader:URLLoader = getLoader( url );
			if ( loader.bytesTotal > 0 && loader.bytesLoaded == loader.bytesTotal ) onLoad();
			else loader.addEventListener( Event.COMPLETE, onLoad );
		}

		private function onLoad( e:Event = null ):void
		{
			isLoading = false;

			CONFIG::LOGGING
			{
				logger.debug("KEY LOADED! " + url);
			}

			var loader:URLLoader = getLoader( url );
			_keyData = loader.data as ByteArray;
			loader.removeEventListener( Event.COMPLETE, onLoad );
			dispatchEvent( new Event( Event.COMPLETE ) );
		}
	}
}

class KeyParamParser
{
	private static const STATE_PARSE_NAME:String = "ParseName";
	private static const STATE_BEGIN_PARSE_VALUE:String = "BeginParseValue";
	private static const STATE_PARSE_VALUE:String = "ParseValue";

	public static function parseParams( paramString:String ):Array
	{
		var result:Array = [];
		var cursor:int = 0;
		var state:String = STATE_PARSE_NAME;
		var accum:String = "";
		var usingQuotes:Boolean = false;

		while ( cursor < paramString.length )
		{
			var char:String = paramString.charAt( cursor );
			switch ( state )
			{
				case STATE_PARSE_NAME:

					if ( char == '=' )
					{
						result.push( accum );
						accum = "";
						state = STATE_BEGIN_PARSE_VALUE;
					}
					else accum += char;
					break;

				case STATE_BEGIN_PARSE_VALUE:

					if ( char == '"' ) usingQuotes = true;
					else accum += char;
					state = STATE_PARSE_VALUE;
					break;

				case STATE_PARSE_VALUE:

					if ( !usingQuotes && char == ',' )
					{
						result.push( accum );
						accum = "";
						state = STATE_PARSE_NAME;
						break;
					}

					if ( usingQuotes && char == '"' )
					{
						usingQuotes = false;
						break;
					}

					accum += char;
					break;
			}

			cursor++;

			if ( cursor == paramString.length ) result.push( accum );
		}

		return result;
	}
}

package com.kaltura.hls.manifest
{
	import com.hurlant.util.Hex;
	import com.kaltura.hls.crypto.FastAESKey;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;

	CONFIG::LOGGING
	{
		import org.osmf.logging.Logger;
        import org.osmf.logging.Log;
	}

	/**
	 * HLSManifestEncryptionKey is used to parse AES decryption key data from a m3u8
	 * manifest, load the specified key file into memory, and use the key data to decrypt
	 * audio and video streams. It supports AES-128 encryption with #PKCS7 padding, and uses
	 * explicitly passed in Initialization Vectors.
	 */

	public class HLSManifestEncryptionKey extends EventDispatcher
	{
        CONFIG::LOGGING
        {
            private static const logger:Logger = Log.getLogger("com.kaltura.hls.manifest.HLSManifestEncryptionKey");
        }

		private static const LOADER_CACHE:Dictionary = new Dictionary();
		private var _key:FastAESKey;
		public var usePadding:Boolean = false;
		public var iv:String = "";
		public var url:String = "";
		private var iv0 : uint;
		private var iv1 : uint;
		private var iv2 : uint;
		private var iv3 : uint;

		// Keep track of the segments this key applies to
		public var startSegmentId:uint = 0;
		public var endSegmentId:uint = uint.MAX_VALUE;

		private var _keyData:ByteArray;

		public var isLoading:Boolean = false;

		public function HLSManifestEncryptionKey()
		{

		}

		public function get isLoaded():Boolean { return _keyData != null; }

		public override function toString():String
		{
			return "[HLSManifestEncryptionKey start=" + startSegmentId +", end=" + endSegmentId + ", iv=" + iv + ", url=" + url + ", loaded=" + isLoaded + "]";
		}

		public static function fromParams( params:String ):HLSManifestEncryptionKey
		{
			var result:HLSManifestEncryptionKey = new HLSManifestEncryptionKey();

			var tokens:Array = KeyParamParser.parseParams( params );
			var tokenCount:int = tokens.length;

			for ( var i:int = 0; i < tokenCount; i += 2 )
			{
				var name:String = tokens[ i ];
				var value:String = tokens[ i + 1 ];

				switch ( name )
				{
					case "URI" :
						result.url = value;
						break;

					case "IV" :
						result.iv = value;
						break;
				}

			}

			return result;
		}

		/**
		 * Creates an initialization vector from the passed in uint ID, usually
		 * a segment ID.
		 */
		public static function createIVFromID( id:uint ):ByteArray
		{
			var result:ByteArray = new ByteArray();
			result.writeUnsignedInt( 0 );
			result.writeUnsignedInt( 0 );
			result.writeUnsignedInt( 0 );
			result.writeUnsignedInt( id );
			return result;
		}

		public static function clearLoaderCache():void
		{
			for ( var key:String in LOADER_CACHE ) delete LOADER_CACHE[ key ];
		}

		/**
		 * Decrypts a video or audio stream using AES-128 with the provided initialization vector.
		 */

		public function decrypt( data:ByteArray, iv:ByteArray ):ByteArray
		{
			//logger.debug("got " + data.length + " bytes");
			if(data.length == 0)
				return data;

			var startTime:uint = getTimer();
			_key = new FastAESKey(_keyData);
			iv.position = 0;
			iv0 = iv.readUnsignedInt();
			iv1 = iv.readUnsignedInt();
			iv2 = iv.readUnsignedInt();
			iv3 = iv.readUnsignedInt();
			data.position = 0;
			data = _decryptCBC(data,data.length);
			if ( usePadding ){
				data = unpad( data );
			}
		//	logger.debug( "DECRYPTION OF " + data.length + " BYTES TOOK " + ( getTimer() - startTime ) + " MS" );
			return data;
		}

		/* Cypher Block Chaining Decryption, refer to
		* http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_
		* for algorithm description
		*/
		private function _decryptCBC(crypt : ByteArray, len : uint) : ByteArray {
			var src : Vector.<uint> = new Vector.<uint>(4);
			var dst : Vector.<uint> = new Vector.<uint>(4);
			var decrypt : ByteArray = new ByteArray();
			decrypt.length = len;

			for (var i : uint = 0; i < len / 16; i++) {
				// read src byte array
				src[0] = crypt.readUnsignedInt();
				src[1] = crypt.readUnsignedInt();
				src[2] = crypt.readUnsignedInt();
				src[3] = crypt.readUnsignedInt();

				// AES decrypt src vector into dst vector
				_key.decrypt128(src, dst);

				// CBC : write output = XOR(decrypted,IV)
				decrypt.writeUnsignedInt(dst[0] ^ iv0);
				decrypt.writeUnsignedInt(dst[1] ^ iv1);
				decrypt.writeUnsignedInt(dst[2] ^ iv2);
				decrypt.writeUnsignedInt(dst[3] ^ iv3);

				// CBC : next IV = (input)
				iv0 = src[0];
				iv1 = src[1];
				iv2 = src[2];
				iv3 = src[3];
			}
			return decrypt;
		}
		public function unpad(bytesToUnpad : ByteArray) : ByteArray {
			if ((bytesToUnpad.length % 16) != 0)
			{
				throw new Error("PKCS#5::unpad: ByteArray.length isn't a multiple of the blockSize");
				return a;
			}

			const paddingValue:int = bytesToUnpad[bytesToUnpad.length - 1];

			if (paddingValue > 15)
			{
				//Return early, since any values bigger than 15 aren't actually padding.
				return bytesToUnpad;
			}
			var doUnpad:Boolean = true;
			for (var i:int = 0; i<paddingValue; i++)
			{
				var readValue:int = bytesToUnpad[bytesToUnpad.length - (1 + i)];
				if (paddingValue != readValue)
				{
					doUnpad = false;
					//Break since we know we are not dealing with padding
					break;
				}
			}

			if(doUnpad)
			{
				//subtract paddingValue to remove the padding from the block
				bytesToUnpad.length -= paddingValue;
			}
			return bytesToUnpad;
		}

		public function retrieveStoredIV():ByteArray
		{
			CONFIG::LOGGING
			{
				logger.debug("IV of " + iv + " for " + url + ", key=" + Hex.fromArray(_keyData));
			}
			return Hex.toArray( iv );
		}

		private static function getLoader( url:String ):URLLoader
		{
			if ( LOADER_CACHE[ url ] != null ) return LOADER_CACHE[ url ] as URLLoader;
			var newLoader:URLLoader = new URLLoader();
			newLoader.dataFormat = URLLoaderDataFormat.BINARY;
			newLoader.load( new URLRequest( url ) );
			LOADER_CACHE[ url ] = newLoader;
			return newLoader;
		}

		public function load():void
		{
			isLoading = true;

			if ( isLoaded ) throw new Error( "Already loaded!" );
			var loader:URLLoader = getLoader( url );
			if ( loader.bytesTotal > 0 && loader.bytesLoaded == loader.bytesTotal ) onLoad();
			else loader.addEventListener( Event.COMPLETE, onLoad );
		}

		private function onLoad( e:Event = null ):void
		{
			isLoading = false;

			CONFIG::LOGGING
			{
				logger.debug("KEY LOADED! " + url);
			}

			var loader:URLLoader = getLoader( url );
			_keyData = loader.data as ByteArray;
			loader.removeEventListener( Event.COMPLETE, onLoad );
			dispatchEvent( new Event( Event.COMPLETE ) );
		}
	}
}

class KeyParamParser
{
	private static const STATE_PARSE_NAME:String = "ParseName";
	private static const STATE_BEGIN_PARSE_VALUE:String = "BeginParseValue";
	private static const STATE_PARSE_VALUE:String = "ParseValue";

	public static function parseParams( paramString:String ):Array
	{
		var result:Array = [];
		var cursor:int = 0;
		var state:String = STATE_PARSE_NAME;
		var accum:String = "";
		var usingQuotes:Boolean = false;

		while ( cursor < paramString.length )
		{
			var char:String = paramString.charAt( cursor );
			switch ( state )
			{
				case STATE_PARSE_NAME:

					if ( char == '=' )
					{
						result.push( accum );
						accum = "";
						state = STATE_BEGIN_PARSE_VALUE;
					}
					else accum += char;
					break;

				case STATE_BEGIN_PARSE_VALUE:

					if ( char == '"' ) usingQuotes = true;
					else accum += char;
					state = STATE_PARSE_VALUE;
					break;

				case STATE_PARSE_VALUE:

					if ( !usingQuotes && char == ',' )
					{
						result.push( accum );
						accum = "";
						state = STATE_PARSE_NAME;
						break;
					}

					if ( usingQuotes && char == '"' )
					{
						usingQuotes = false;
						break;
					}

					accum += char;
					break;
			}

			cursor++;

			if ( cursor == paramString.length ) result.push( accum );
		}

		return result;
	}
}

Update HLSManifestEncryptionKey.as

Update HLSManifestEncryptionKey.as

Case correction.


/**
  * LzMouseKernel.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic AS2
  */

// Receives mouse events from the runtime
class LzMouseKernel  {
    #passthrough (toplevel:true) {
    import flash.display.*;
    import flash.events.*;
    import flash.utils.*;
    import flash.ui.*;
    }#

    // sends mouse events to the callback
    static function __sendEvent(view, eventname) {
        if (__callback) __scope[__callback](eventname, view);
        //Debug.write('LzMouseKernel event', eventname);
    }
    static var __callback = null;
    static var __scope = null;
    static var __lastMouseDown = null;
    static var __mouseLeft:Boolean = false;
    static var __listeneradded:Boolean = false;

    /**
     * Shows or hides the hand cursor for all clickable views.
     */
    static var showhandcursor:Boolean = true;

    static function setCallback (scope, funcname) {
        __scope = scope;
        __callback = funcname;
        if (__listeneradded == false) {
            /* TODO [hqm 2008-01] Do we want to do anything with other
            * events, like click?
            stage.addEventListener(MouseEvent.CLICK, reportClick);
            */

            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_WHEEL, __mouseWheelHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, __mouseLeavesHandler);
            __listeneradded = true;
        }
    }

    // Handles global mouse events
    static function __mouseHandler(event:MouseEvent):void {
        var eventname = 'on' + event.type.toLowerCase();
        if (eventname == 'onmouseup' && __lastMouseDown != null) {
            // call mouseup on the sprite that got the last mouse down
            __lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            if (__mouseLeft && event.buttonDown) {
                __mouseLeft = false;
                __mouseUpOutsideHandler();
            }
            __sendEvent(null, eventname);
        }
    }

    // sends mouseup and calls __globalmouseup when the mouse goes up outside the app - see LPP-7724
    static function __mouseUpOutsideHandler():void {
        if (__lastMouseDown != null) {
            var ev = new MouseEvent('mouseup');
            __lastMouseDown.__globalmouseup(ev);
            __lastMouseDown = null;
        }
    }

    // handles MOUSE_LEAVE event
    static function __mouseLeavesHandler(event:Event):void {
        var eventname = 'on' + event.type.toLowerCase();
        __mouseLeft = true;
        __sendEvent(null, eventname);
    }

    static function __mouseWheelHandler(event:MouseEvent):void {
        lz.Keys.__mousewheelEvent(event.delta);
    }

    /**
    * Shows or hides the hand cursor for all clickable views.
    * @param Boolean show: true shows the hand cursor for buttons, false hides it
    */
    static function showHandCursor (show) {
        showhandcursor = show;
    }

    static var __amLocked:Boolean = false;
    static var cursorSprite:Sprite = null;
    static var globalCursorResource:String = null;
    static var lastCursorResource:String = null;

    /**
    * Sets the cursor to a resource
    * @param String what: The resource to use as the cursor.
    */
    static function setCursorGlobal ( what:String ){
        globalCursorResource = what;
        setCursorLocal(what);
    }

    static function setCursorLocal ( what:String ) {
        if ( __amLocked ) { return; }
        Mouse.hide();
        cursorSprite.x = LFCApplication.stage.mouseX;
        cursorSprite.y = LFCApplication.stage.mouseY;
        LFCApplication.setChildIndex(cursorSprite, LFCApplication._sprite.numChildren-1);
        if (lastCursorResource != what) {
            if (cursorSprite.numChildren > 0) {
                cursorSprite.removeChildAt(0);
            }
            var resourceSprite = getCursorResource(what);
            cursorSprite.addChild( resourceSprite );
            lastCursorResource = what;
        }
        // respond to mouse move events
        cursorSprite.startDrag();
        LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
        cursorSprite.visible = true;
    }

    static function mouseLeaveHandler(evt:Event):void {
        cursorSprite.visible = false;
    }

    static function getCursorResource (resource:String):Sprite {
          var resinfo = LzResourceLibrary[resource];
          var assetclass;
          var frames = resinfo.frames;
          var asset:Sprite;
          // single frame resources get an entry in LzResourceLibrary which has
          // 'assetclass' pointing to the resource Class object.
          if (resinfo.assetclass is Class) {
              assetclass = resinfo.assetclass;
          } else {
              // Multiframe resources have an array of Class objects in frames[]
              assetclass = frames[0];
          }

          if (! assetclass) return null;
          asset = new assetclass();
          asset.scaleX = 1.0;
          asset.scaleY = 1.0;
          //Debug.write('cursor asset', asset);
          return asset;
    }

    /**
    * This function restores the default cursor if there is no locked cursor on
    * the screen.
    *
    * @access private
    */
    static function restoreCursor ( ){
        if ( __amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        globalCursorResource = null;
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal ( ){
        if ( __amLocked ) { return; }
        if (globalCursorResource == null) {
            // Restore to system default pointer
            restoreCursor();
        } else {
            // Restore to the last value set by setCursorGlobal
            setCursorLocal(globalCursorResource);
        }
    }

    /**
    * Prevents the cursor from being changed until unlock is called.
    *
    */
    static function lock (){
        __amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock (){
        __amLocked = false;
        restoreCursor();
    }

    static function initCursor () {
        cursorSprite = new Sprite();
        cursorSprite.mouseChildren = false;
        cursorSprite.mouseEnabled = false;
        // Add the cursor DisplayObject to the root sprite
        LFCApplication.addChild(cursorSprite);
        cursorSprite.x = -10000;
        cursorSprite.y = -10000;
    }
}

/**
  * LzMouseKernel.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic AS2
  */

// Receives mouse events from the runtime
class LzMouseKernel  {
    #passthrough (toplevel:true) {
    import flash.display.*;
    import flash.events.*;
    import flash.utils.*;
    import flash.ui.*;
    }#

    // sends mouse events to the callback
    static function __sendEvent(view, eventname) {
        if (__callback) __scope[__callback](eventname, view);
        //Debug.write('LzMouseKernel event', eventname);
    }
    static var __callback = null;
    static var __scope = null;
    static var __lastMouseDown = null;
    static var __mouseLeft:Boolean = false;
    static var __listeneradded:Boolean = false;

    /**
     * Shows or hides the hand cursor for all clickable views.
     */
    static var showhandcursor:Boolean = true;

    static function setCallback (scope, funcname) {
        __scope = scope;
        __callback = funcname;
        if (__listeneradded == false) {
            /* TODO [hqm 2008-01] Do we want to do anything with other
            * events, like click?
            stage.addEventListener(MouseEvent.CLICK, reportClick);
            */

            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_WHEEL, __mouseWheelHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, __mouseLeaveHandler);
            __listeneradded = true;
        }
    }

    // Handles global mouse events
    static function __mouseHandler(event:MouseEvent):void {
        var eventname = 'on' + event.type.toLowerCase();
        if (eventname == 'onmouseup' && __lastMouseDown != null) {
            // call mouseup on the sprite that got the last mouse down
            __lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            if (__mouseLeft) {
                __mouseLeft = false;
                if (event.buttonDown) __mouseUpOutsideHandler();
            }
            __sendEvent(null, eventname);
        }
    }

    // sends mouseup and calls __globalmouseup when the mouse goes up outside the app - see LPP-7724
    static function __mouseUpOutsideHandler():void {
        if (__lastMouseDown != null) {
            var ev = new MouseEvent('mouseup');
            __lastMouseDown.__globalmouseup(ev);
            __lastMouseDown = null;
        }
    }

    // handles MOUSE_LEAVE event
    static function __mouseLeaveHandler(event:Event = null):void {
        __mouseLeft = true;
        __sendEvent(null, 'onmouseleave');
    }

    static function __mouseWheelHandler(event:MouseEvent):void {
        lz.Keys.__mousewheelEvent(event.delta);
    }

    /**
    * Shows or hides the hand cursor for all clickable views.
    * @param Boolean show: true shows the hand cursor for buttons, false hides it
    */
    static function showHandCursor (show) {
        showhandcursor = show;
    }

    static var __amLocked:Boolean = false;
    static var cursorSprite:Sprite = null;
    static var globalCursorResource:String = null;
    static var lastCursorResource:String = null;

    /**
    * Sets the cursor to a resource
    * @param String what: The resource to use as the cursor.
    */
    static function setCursorGlobal ( what:String ){
        globalCursorResource = what;
        setCursorLocal(what);
    }

    static function setCursorLocal ( what:String ) {
        if ( __amLocked ) { return; }
        Mouse.hide();
        cursorSprite.x = LFCApplication.stage.mouseX;
        cursorSprite.y = LFCApplication.stage.mouseY;
        LFCApplication.setChildIndex(cursorSprite, LFCApplication._sprite.numChildren-1);
        if (lastCursorResource != what) {
            if (cursorSprite.numChildren > 0) {
                cursorSprite.removeChildAt(0);
            }
            var resourceSprite = getCursorResource(what);
            cursorSprite.addChild( resourceSprite );
            lastCursorResource = what;
        }
        // respond to mouse move events
        cursorSprite.startDrag();
        LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
        cursorSprite.visible = true;
    }

    static function mouseLeaveHandler(evt:Event):void {
        cursorSprite.visible = false;
    }

    static function getCursorResource (resource:String):Sprite {
          var resinfo = LzResourceLibrary[resource];
          var assetclass;
          var frames = resinfo.frames;
          var asset:Sprite;
          // single frame resources get an entry in LzResourceLibrary which has
          // 'assetclass' pointing to the resource Class object.
          if (resinfo.assetclass is Class) {
              assetclass = resinfo.assetclass;
          } else {
              // Multiframe resources have an array of Class objects in frames[]
              assetclass = frames[0];
          }

          if (! assetclass) return null;
          asset = new assetclass();
          asset.scaleX = 1.0;
          asset.scaleY = 1.0;
          //Debug.write('cursor asset', asset);
          return asset;
    }

    /**
    * This function restores the default cursor if there is no locked cursor on
    * the screen.
    *
    * @access private
    */
    static function restoreCursor ( ){
        if ( __amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        globalCursorResource = null;
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal ( ){
        if ( __amLocked ) { return; }
        if (globalCursorResource == null) {
            // Restore to system default pointer
            restoreCursor();
        } else {
            // Restore to the last value set by setCursorGlobal
            setCursorLocal(globalCursorResource);
        }
    }

    /**
    * Prevents the cursor from being changed until unlock is called.
    *
    */
    static function lock (){
        __amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock (){
        __amLocked = false;
        restoreCursor();
    }

    static function initCursor () {
        cursorSprite = new Sprite();
        cursorSprite.mouseChildren = false;
        cursorSprite.mouseEnabled = false;
        // Add the cursor DisplayObject to the root sprite
        LFCApplication.addChild(cursorSprite);
        cursorSprite.x = -10000;
        cursorSprite.y = -10000;
    }
}

Change 20090212-maxcarlson-9 by maxcarlson@Bank.lan on 2009-02-12 23:59:18 PST
    in /Users/maxcarlson/openlaszlo/trunk-clean
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: Improve mouseupoutside behavior outside windows

Bugs Fixed: LPP-7724 - Mouse events behave wrong in opaque mode (FireFox)

Technical Reviewer: hminsky
QA Reviewer: promanik

Details: Rename __mouseLeaveHandler -> __mouseLeaveHandler.  Clear __mouseLeft if set, regardless of button state.  Make argument to __mouseLeaveHandler() optional so it can be called from external JavaScript.

Tests: See updated testcase at LPP-7724 - test app.lzx?lzr=swf9, em.html, emOp.html and emIf.html in firefox 3 windows and mac.

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@12851 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


package aerys.minko.type.stream
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.VertexBuffer3DResource;
	import aerys.minko.type.IVersionable;
	import aerys.minko.type.stream.format.VertexComponent;
	import aerys.minko.type.stream.format.VertexComponentType;
	import aerys.minko.type.stream.format.VertexFormat;

	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	public class VertexStream implements IVersionable, IVertexStream
	{
		use namespace minko_stream;

		public static const DEFAULT_FORMAT	: VertexFormat	= VertexFormat.XYZ_UV;

		minko_stream var _data			: Vector.<Number>			= null;

		private var _dynamic			: Boolean					= false;
		private var _version			: uint						= 0;

		private var _format				: VertexFormat				= null;
		private var _resource			: VertexBuffer3DResource	= null;
		private var _length				: uint						= 0;
		private var _componentToStream	: Dictionary				= new Dictionary(true);

		public function get format()	: VertexFormat				{ return _format; }
		public function get version()	: uint						{ return _version; }
		public function get isDynamic()	: Boolean					{ return _dynamic; }
		public function get resource()	: VertexBuffer3DResource	{ return _resource; }
		public function get length()	: uint						{ return _length; }

		protected function get data() : Vector.<Number>
		{
			return _data;
		}

		protected function set data(value : Vector.<Number>) : void
		{
			_data = value;
			invalidate();
		}

		public function VertexStream(data 		: Vector.<Number>	= null,
									 format		: VertexFormat 		= null,
									 isDynamic	: Boolean			= false)
		{
			super();

			initialize(data, format, isDynamic);
		}

		private function initialize(data 	: Vector.<Number>	= null,
									format	: VertexFormat 		= null,
									isDynamic	: Boolean			= false) : void
		{
			_resource = new VertexBuffer3DResource(this);
			_format = format || DEFAULT_FORMAT;

			if (data && data.length && data.length % _format.dwordsPerVertex)
				throw new Error("Incompatible vertex format: the data length does not match.");

			_data = data ? data.concat() : new Vector.<Number>();
			_dynamic = isDynamic;

			invalidate();
		}

		public function deleteVertexByIndex(index : uint) : Boolean
		{
			if (index > length)
				return false;

			_data.splice(index, _format.dwordsPerVertex);

			invalidate();

			return true;
		}

		public function getSubStreamByComponent(vertexComponent : VertexComponent) : VertexStream
		{
			return _format.hasComponent(vertexComponent) ? this : null;
		}

		public function get(i : int) : Number
		{
			return _data[i];
		}

		public function set(i : int, value : Number) : void
		{
			_data[i] = value;
			invalidate();
		}

		public function push(data : Vector.<Number>) : void
		{
			var dataLength : int = data.length;

			if (dataLength % _format.dwordsPerVertex)
				throw new Error("Invalid data length.");

			for (var i : int = 0; i < dataLength; i++)
				_data.push(data[i]);

			invalidate();
		}

		public function disposeLocalData() : void
		{
			if (length != resource.numVertices)
				throw new Error("Unable to dispose local data: "
								+ "some vertices have not been uploaded.");

			_data = null;
			_dynamic = false;
		}

		protected function invalidate() : void
		{
			_length = _data.length / _format.dwordsPerVertex;
			++_version;
		}

		minko_stream function invalidate() : void
		{
			protected::invalidate();
		}

		public static function fromPositionsAndUVs(positions 	: Vector.<Number>,
												   uvs		 	: Vector.<Number> 	= null,
												   isDynamic		: Boolean			= false) : VertexStream
		{
			var numVertices : int 				= positions.length / 3;
			var stride 		: int 				= uvs ? 5 : 3;
			var data 		: Vector.<Number> 	= new Vector.<Number>(numVertices * stride, true);

			for (var i : int = 0; i < numVertices; ++i)
			{
				var offset : int = i * stride;

				data[offset] = positions[int(i * 3)];
				data[int(offset + 1)] = positions[int(i * 3 + 1)];
				data[int(offset + 2)] = positions[int(i * 3 + 2)];

				if (uvs)
				{
					data[int(offset + 3)] = uvs[int(i * 2)];
					data[int(offset + 4)] = uvs[int(i * 2 + 1)];
				}
			}

			return new VertexStream(data,
									uvs ? VertexFormat.XYZ_UV : VertexFormat.XYZ,
									isDynamic);
		}

		public static function extractSubStream(source			: IVertexStream,
										 		vertexFormat 	: VertexFormat	= null) : VertexStream
		{
			vertexFormat ||= source.format;

			var newVertexStreamData			: Vector.<Number>			= new Vector.<Number>();

			var components					: Vector.<VertexComponent>	= vertexFormat.components;
			var numComponents				: uint						= components.length;
			var componentOffsets			: Vector.<uint>				= new Vector.<uint>(numComponents, true);
			var componentSizes				: Vector.<uint>				= new Vector.<uint>(numComponents, true);
			var componentDwordsPerVertex	: Vector.<uint>				= new Vector.<uint>(numComponents, true);
			var componentDatas				: Vector.<Vector.<Number>>	= new Vector.<Vector.<Number>>(numComponents, true);

			var totalVertices				: int						= 0;
			var totalIndices				: int						= 0;

			// cache get offsets, sizes, and buffers for each components
			for (var k : int = 0; k < numComponents; ++k)
			{
				var vertexComponent	: VertexComponent	= components[k];
				var subVertexStream	: VertexStream		= source.getSubStreamByComponent(vertexComponent);
				var subvertexFormat	: VertexFormat		= subVertexStream.format;

				componentOffsets[k]			= subvertexFormat.getOffsetForComponent(vertexComponent);
				componentDwordsPerVertex[k]	= subvertexFormat.dwordsPerVertex;
				componentSizes[k]			= vertexComponent.dwords;
				componentDatas[k]			= subVertexStream._data;
			}

			// push vertex data into the new buffer.
			var numVertices : uint 	= source.length;

			for (var vertexId : uint = 0; vertexId < numVertices; ++vertexId)
			{
				for (var componentId : int = 0; componentId < numComponents; ++componentId)
				{
					var vertexData		: Vector.<Number>	= componentDatas[componentId];
					var componentSize	: uint				= componentSizes[componentId];
					var componentOffset	: uint				= componentOffsets[componentId]
															  + vertexId * componentDwordsPerVertex[componentId];
					var componentLimit	: uint				= componentSize + componentOffset;

					for (var n : int = componentOffset; n < componentLimit; ++n, ++totalVertices)
						newVertexStreamData[totalVertices] = vertexData[n];
				}
			}

			// avoid copying data vectors
			var newVertexStream		: VertexStream	= new VertexStream(null, vertexFormat);

			newVertexStream.data = newVertexStreamData;

			return newVertexStream;
		}

		public static function concat(streams : Vector.<IVertexStream>) : VertexStream
		{
			var format		: VertexFormat	= streams[0].format;
			var numStreams 	: int			= streams.length;

			for (var i : int = 0; i < numStreams; ++i)
				if (!streams[i].format.equals(format))
					throw new Error("All vertex streams must have the same format.");

			// a bit expensive... but hey, it works :)
			var stream	: VertexStream	= extractSubStream(streams[0], format);

			for  (i = 1; i < numStreams; ++i)
				stream.data = stream.data.concat(extractSubStream(streams[i], format).data);

			return stream;
		}

		public static function fromByteArray(bytes 		: ByteArray,
											 count		: int,
											 formatIn	: VertexFormat,
											 formatOut	: VertexFormat	= null,
											 isDynamic	: Boolean		= false,
											 reader 	: Function 		= null,
											 dwordSize	: uint			= 4) : VertexStream
		{
			formatOut ||= formatIn;
			reader ||= bytes.readFloat;

			var dataLength		: int						= 0;
			var data			: Vector.<Number>			= null;
			var stream			: VertexStream				= new VertexStream(null, formatOut, isDynamic);
			var start			: int						= bytes.position;
			var componentsOut	: Vector.<VertexComponent>	= formatOut.components;
			var numComponents	: int						= componentsOut.length;
			var nativeFormats	: Vector.<int>				= new Vector.<int>(numComponents, true);

			for (var k : int = 0; k < numComponents; k++)
				nativeFormats[k] = componentsOut[k].nativeFormat;

			data = new Vector.<Number>(formatOut.dwordsPerVertex * count, true);
			for (var vertexId : int = 0; vertexId < count; ++vertexId)
			{
				for (var componentId : int = 0; componentId < numComponents; ++componentId)
				{
					bytes.position = start + formatIn.dwordsPerVertex * vertexId * dwordSize
									+ formatIn.getOffsetForComponent(componentsOut[componentId]) * dwordSize;

					switch (nativeFormats[componentId])
					{
						case VertexComponentType.FLOAT_4 :
							data[int(dataLength++)] = reader();
						case VertexComponentType.FLOAT_3 :
							data[int(dataLength++)] = reader();
						case VertexComponentType.FLOAT_2 :
							data[int(dataLength++)] = reader();
						case VertexComponentType.FLOAT_1 :
							data[int(dataLength++)] = reader();
							break ;
					}
				}
			}
			// make sure the ByteArray position is at the end of the buffer
			bytes.position = start + formatIn.dwordsPerVertex * count * dwordSize;

			stream.data = data;

			return stream;
		}

	}
}

package aerys.minko.type.stream
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.VertexBuffer3DResource;
	import aerys.minko.type.IVersionable;
	import aerys.minko.type.stream.format.VertexComponent;
	import aerys.minko.type.stream.format.VertexComponentType;
	import aerys.minko.type.stream.format.VertexFormat;

	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	public class VertexStream implements IVersionable, IVertexStream
	{
		use namespace minko_stream;

		public static const DEFAULT_FORMAT	: VertexFormat	= VertexFormat.XYZ_UV;

		minko_stream var _data			: Vector.<Number>			= null;

		private var _dynamic			: Boolean					= false;
		private var _version			: uint						= 0;

		private var _format				: VertexFormat				= null;
		private var _resource			: VertexBuffer3DResource	= null;
		private var _length				: uint						= 0;
		private var _componentToStream	: Dictionary				= new Dictionary(true);

		public function get format()	: VertexFormat				{ return _format; }
		public function get version()	: uint						{ return _version; }
		public function get isDynamic()	: Boolean					{ return _dynamic; }
		public function get resource()	: VertexBuffer3DResource	{ return _resource; }
		public function get length()	: uint						{ return _length; }

		protected function get data() : Vector.<Number>
		{
			return _data;
		}

		protected function set data(value : Vector.<Number>) : void
		{
			_data = value;
			invalidate();
		}

		public function VertexStream(data 		: Vector.<Number>	= null,
									 format		: VertexFormat 		= null,
									 isDynamic	: Boolean			= false)
		{
			super();

			initialize(data, format, isDynamic);
		}

		private function initialize(data 	: Vector.<Number>	= null,
									format	: VertexFormat 		= null,
									isDynamic	: Boolean			= false) : void
		{
			_resource = new VertexBuffer3DResource(this);
			_format = format || DEFAULT_FORMAT;

			if (data && data.length && data.length % _format.dwordsPerVertex)
				throw new Error("Incompatible vertex format: the data length does not match.");

			_data = data ? data.concat() : new Vector.<Number>();
			_dynamic = isDynamic;

			invalidate();
		}

		public function deleteVertexByIndex(index : uint) : Boolean
		{
			if (index > length)
				return false;

			_data.splice(index, _format.dwordsPerVertex);

			invalidate();

			return true;
		}

		public function getSubStreamByComponent(vertexComponent : VertexComponent) : VertexStream
		{
			return _format.hasComponent(vertexComponent) ? this : null;
		}

		public function get(i : int) : Number
		{
			return _data[i];
		}

		public function set(i : int, value : Number) : void
		{
			_data[i] = value;
			invalidate();
		}

		public function push(data : Vector.<Number>) : void
		{
			var dataLength : int = data.length;

			if (dataLength % _format.dwordsPerVertex)
				throw new Error("Invalid data length.");

			for (var i : int = 0; i < dataLength; i++)
				_data.push(data[i]);

			invalidate();
		}

		public function disposeLocalData() : void
		{
			if (length != resource.numVertices)
				throw new Error("Unable to dispose local data: "
								+ "some vertices have not been uploaded.");

			_data = null;
			_dynamic = false;
		}

		protected function invalidate() : void
		{
			_length = _data.length / _format.dwordsPerVertex;
			++_version;
		}

		minko_stream function invalidate() : void
		{
			protected::invalidate();
		}

		public static function fromPositionsAndUVs(positions 	: Vector.<Number>,
												   uvs		 	: Vector.<Number> 	= null,
												   isDynamic		: Boolean			= false) : VertexStream
		{
			var numVertices : int 				= positions.length / 3;
			var stride 		: int 				= uvs ? 5 : 3;
			var data 		: Vector.<Number> 	= new Vector.<Number>(numVertices * stride, true);

			for (var i : int = 0; i < numVertices; ++i)
			{
				var offset : int = i * stride;

				data[offset] = positions[int(i * 3)];
				data[int(offset + 1)] = positions[int(i * 3 + 1)];
				data[int(offset + 2)] = positions[int(i * 3 + 2)];

				if (uvs)
				{
					data[int(offset + 3)] = uvs[int(i * 2)];
					data[int(offset + 4)] = uvs[int(i * 2 + 1)];
				}
			}

			return new VertexStream(data,
									uvs ? VertexFormat.XYZ_UV : VertexFormat.XYZ,
									isDynamic);
		}

		public static function extractSubStream(source			: IVertexStream,
										 		vertexFormat 	: VertexFormat	= null) : VertexStream
		{
			vertexFormat ||= source.format;

			var newVertexStreamData			: Vector.<Number>			= new Vector.<Number>();

			var components					: Vector.<VertexComponent>	= vertexFormat.components;
			var numComponents				: uint						= components.length;
			var componentOffsets			: Vector.<uint>				= new Vector.<uint>(numComponents, true);
			var componentSizes				: Vector.<uint>				= new Vector.<uint>(numComponents, true);
			var componentDwordsPerVertex	: Vector.<uint>				= new Vector.<uint>(numComponents, true);
			var componentDatas				: Vector.<Vector.<Number>>	= new Vector.<Vector.<Number>>(numComponents, true);

			var totalVertices				: int						= 0;
			var totalIndices				: int						= 0;

			// cache get offsets, sizes, and buffers for each components
			for (var k : int = 0; k < numComponents; ++k)
			{
				var vertexComponent	: VertexComponent	= components[k];
				var subVertexStream	: VertexStream		= source.getSubStreamByComponent(vertexComponent);
				var subvertexFormat	: VertexFormat		= subVertexStream.format;

				componentOffsets[k]			= subvertexFormat.getOffsetForComponent(vertexComponent);
				componentDwordsPerVertex[k]	= subvertexFormat.dwordsPerVertex;
				componentSizes[k]			= vertexComponent.dwords;
				componentDatas[k]			= subVertexStream._data;
			}

			// push vertex data into the new buffer.
			var numVertices : uint 	= source.length;

			for (var vertexId : uint = 0; vertexId < numVertices; ++vertexId)
			{
				for (var componentId : int = 0; componentId < numComponents; ++componentId)
				{
					var vertexData		: Vector.<Number>	= componentDatas[componentId];
					var componentSize	: uint				= componentSizes[componentId];
					var componentOffset	: uint				= componentOffsets[componentId]
															  + vertexId * componentDwordsPerVertex[componentId];
					var componentLimit	: uint				= componentSize + componentOffset;

					for (var n : int = componentOffset; n < componentLimit; ++n, ++totalVertices)
						newVertexStreamData[totalVertices] = vertexData[n];
				}
			}

			// avoid copying data vectors
			var newVertexStream		: VertexStream	= new VertexStream(null, vertexFormat);

			newVertexStream.data = newVertexStreamData;

			return newVertexStream;
		}

		public static function concat(streams : Vector.<IVertexStream>) : VertexStream
		{
			var format		: VertexFormat	= streams[0].format;
			var numStreams 	: int			= streams.length;

			for (var i : int = 0; i < numStreams; ++i)
				if (!streams[i].format.equals(format))
					throw new Error("All vertex streams must have the same format.");

			// a bit expensive... but hey, it works :)
			var stream	: VertexStream	= extractSubStream(streams[0], format);

			for  (i = 1; i < numStreams; ++i)
				stream.data = stream.data.concat(extractSubStream(streams[i], format).data);

			return stream;
		}

		public static function fromByteArray(bytes 			: ByteArray,
											 count			: int,
											 formatIn		: VertexFormat,
											 formatOut		: VertexFormat	= null,
											 isDynamic		: Boolean		= false,
											 functionReader : Dictionary	= null,
											 dwordSize		: uint			= 4) : VertexStream
		{
			formatOut ||= formatIn;

			var dataLength		: int						= 0;
			var data			: Vector.<Number>			= null;
			var stream			: VertexStream				= new VertexStream(null, formatOut, isDynamic);
			var start			: int						= bytes.position;
			var componentsOut	: Vector.<VertexComponent>	= formatOut.components;
			var numComponents	: int						= componentsOut.length;
			var nativeFormats	: Vector.<int>				= new Vector.<int>(numComponents, true);

			for (var k : int = 0; k < numComponents; k++)
				nativeFormats[k] = componentsOut[k].nativeFormat;

			data = new Vector.<Number>(formatOut.dwordsPerVertex * count, true);
			for (var vertexId : int = 0; vertexId < count; ++vertexId)
			{
				for (var componentId : int = 0; componentId < numComponents; ++componentId)
				{
					bytes.position = start + formatIn.dwordsPerVertex * vertexId * dwordSize
									+ formatIn.getOffsetForComponent(componentsOut[componentId]) * dwordSize;

					var reader : Function = null;

					if (functionReader[componentsOut[componentId].nativeFormatString])
						reader = functionReader[componentsOut[componentId].nativeFormatString];
					else if (functionReader["defaut"])
						reader = functionReader["defaut"];
					else
						reader = bytes.readFloat;

					switch (nativeFormats[componentId])
					{
						case VertexComponentType.FLOAT_4 :
							data[int(dataLength++)] = reader();
						case VertexComponentType.FLOAT_3 :
							data[int(dataLength++)] = reader();
						case VertexComponentType.FLOAT_2 :
							data[int(dataLength++)] = reader();
						case VertexComponentType.FLOAT_1 :
							data[int(dataLength++)] = reader();
							break ;
					}
				}
			}
			// make sure the ByteArray position is at the end of the buffer
			bytes.position = start + formatIn.dwordsPerVertex * count * dwordSize;

			stream.data = data;

			return stream;
		}

	}
}

Read function feature

Read function feature

M The reader function use to transform ByteArray into VertexStream is now a collection indexed by the components name


package aerys.minko.type.loader.parser
{
	import aerys.minko.render.Effect;
	import aerys.minko.render.material.Material;
	import aerys.minko.scene.node.Mesh;
	import aerys.minko.type.animation.SkinningMethod;
	import aerys.minko.type.loader.ILoader;
	import aerys.minko.type.loader.SceneLoader;
	import aerys.minko.type.loader.TextureLoader;

	import flash.net.URLRequest;

	/**
	 * ParserOptions objects provide properties and function references
	 * to customize how a LoaderGroup object will load and interpret
	 * content.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class ParserOptions
	{
		private var _dependencyLoaderFunction	: Function;
        private var _loadSkin                   : Boolean;
		private var _skinningMethod				: uint;
		private var _mipmapTextures				: Boolean;
		private var _meshEffect					: Effect;
		private var _material					: Material;
		private var _vertexStreamUsage			: uint;
		private var _indexStreamUsage			: uint;
		private var _parser						: Class;

		public function get skinningMethod() : uint
		{
			return _skinningMethod;
		}

		public function set skinningMethod(value : uint) : void
		{
			_skinningMethod = value;
		}

		public function get parser() : Class
		{
			return _parser;
		}

		public function set parser(value : Class) : void
		{
			_parser = value;
		}

		public function get indexStreamUsage() : uint
		{
			return _indexStreamUsage;
		}

		public function set indexStreamUsage(value : uint) : void
		{
			_indexStreamUsage = value;
		}

		public function get vertexStreamUsage() : uint
		{
			return _vertexStreamUsage;
		}

		public function set vertexStreamUsage(value : uint) : void
		{
			_vertexStreamUsage = value;
		}

		public function get material():Material
		{
			return _material;
		}

		public function set material(value:Material):void
		{
			_material = value;
		}

		public function get mipmapTextures() : Boolean
		{
			return _mipmapTextures;
		}

		public function set mipmapTextures(value : Boolean) : void
		{
			_mipmapTextures = value;
		}

		public function get dependencyLoaderFunction() : Function
		{
			return _dependencyLoaderFunction;
		}

		public function set dependencyLoaderFunction(value : Function) : void
		{
			_dependencyLoaderFunction = value;
		}

        public function get loadSkin() : Boolean
        {
            return _loadSkin;
        }

        public function set loadSkin(value : Boolean) : void
        {
            _loadSkin = value;
        }

		public function clone() : ParserOptions
		{
			return new ParserOptions(
				_dependencyLoaderFunction,
                _loadSkin,
				_skinningMethod,
				_mipmapTextures,
				_material,
				_vertexStreamUsage,
				_indexStreamUsage,
				_parser
			);
		}

		public function ParserOptions(dependencyLoaderFunction	: Function 	= null,
                                      loadSkin                  : Boolean 	= true,
									  skinningMethod			: uint		= 2,
									  mipmapTextures			: Boolean 	= true,
									  material					: Material 	= null,
									  vertexStreamUsage			: uint 		= 0,
									  indexStreamUsage			: uint 		= 0,
									  parser					: Class 	= null)
		{
			_dependencyLoaderFunction	= dependencyLoaderFunction || defaultDependencyLoaderFunction;
            _loadSkin                   = loadSkin;
			_skinningMethod				= skinningMethod;
			_mipmapTextures				= mipmapTextures;
			_material					= _material || new Material();
			_vertexStreamUsage			= vertexStreamUsage;
			_indexStreamUsage			= indexStreamUsage;
			_parser						= parser;
		}

		private function defaultDependencyLoaderFunction(dependencyPath	: String,
														 isTexture		: Boolean,
														 options		: ParserOptions) : ILoader
		{
			var loader : ILoader;

			if (isTexture)
			{
				loader = new TextureLoader(true);
				loader.load(new URLRequest(dependencyPath));
			}
			else
			{
				loader = new SceneLoader(options);
				loader.load(new URLRequest(dependencyPath));
			}

			return loader;
		}

    }
}

package aerys.minko.type.loader.parser
{
	import aerys.minko.render.Effect;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.scene.node.Mesh;
	import aerys.minko.type.animation.SkinningMethod;
	import aerys.minko.type.loader.ILoader;
	import aerys.minko.type.loader.SceneLoader;
	import aerys.minko.type.loader.TextureLoader;

	import flash.net.URLRequest;

	/**
	 * ParserOptions objects provide properties and function references
	 * to customize how a LoaderGroup object will load and interpret
	 * content.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class ParserOptions
	{
		private var _dependencyLoaderFunction	: Function;
        private var _loadSkin                   : Boolean;
		private var _skinningMethod				: uint;
		private var _mipmapTextures				: Boolean;
		private var _meshEffect					: Effect;
		private var _material					: Material;
		private var _vertexStreamUsage			: uint;
		private var _indexStreamUsage			: uint;
		private var _parser						: Class;

		public function get skinningMethod() : uint
		{
			return _skinningMethod;
		}

		public function set skinningMethod(value : uint) : void
		{
			_skinningMethod = value;
		}

		public function get parser() : Class
		{
			return _parser;
		}

		public function set parser(value : Class) : void
		{
			_parser = value;
		}

		public function get indexStreamUsage() : uint
		{
			return _indexStreamUsage;
		}

		public function set indexStreamUsage(value : uint) : void
		{
			_indexStreamUsage = value;
		}

		public function get vertexStreamUsage() : uint
		{
			return _vertexStreamUsage;
		}

		public function set vertexStreamUsage(value : uint) : void
		{
			_vertexStreamUsage = value;
		}

		public function get material():Material
		{
			return _material;
		}

		public function set material(value:Material):void
		{
			_material = value;
		}

		public function get mipmapTextures() : Boolean
		{
			return _mipmapTextures;
		}

		public function set mipmapTextures(value : Boolean) : void
		{
			_mipmapTextures = value;
		}

		public function get dependencyLoaderFunction() : Function
		{
			return _dependencyLoaderFunction;
		}

		public function set dependencyLoaderFunction(value : Function) : void
		{
			_dependencyLoaderFunction = value;
		}

        public function get loadSkin() : Boolean
        {
            return _loadSkin;
        }

        public function set loadSkin(value : Boolean) : void
        {
            _loadSkin = value;
        }

		public function clone() : ParserOptions
		{
			return new ParserOptions(
				_dependencyLoaderFunction,
                _loadSkin,
				_skinningMethod,
				_mipmapTextures,
				_material,
				_vertexStreamUsage,
				_indexStreamUsage,
				_parser
			);
		}

		public function ParserOptions(dependencyLoaderFunction	: Function 	= null,
                                      loadSkin                  : Boolean 	= true,
									  skinningMethod			: uint		= 2,
									  mipmapTextures			: Boolean 	= true,
									  material					: Material 	= null,
									  vertexStreamUsage			: uint 		= 0,
									  indexStreamUsage			: uint 		= 0,
									  parser					: Class 	= null)
		{
			_dependencyLoaderFunction	= dependencyLoaderFunction || defaultDependencyLoaderFunction;
            _loadSkin                   = loadSkin;
			_skinningMethod				= skinningMethod;
			_mipmapTextures				= mipmapTextures;
			_material					= _material || new BasicMaterial();
			_vertexStreamUsage			= vertexStreamUsage;
			_indexStreamUsage			= indexStreamUsage;
			_parser						= parser;
		}

		private function defaultDependencyLoaderFunction(dependencyPath	: String,
														 isTexture		: Boolean,
														 options		: ParserOptions) : ILoader
		{
			var loader : ILoader;

			if (isTexture)
			{
				loader = new TextureLoader(true);
				loader.load(new URLRequest(dependencyPath));
			}
			else
			{
				loader = new SceneLoader(options);
				loader.load(new URLRequest(dependencyPath));
			}

			return loader;
		}

    }
}

set default value of ParserOptions.material to be a BasicMaterial


/**
 * Created with IntelliJ IDEA.
 * User: mobitile
 * Date: 4/21/13
 * Time: 1:37 PM
 * To change this template use File | Settings | File Templates.
 */
package skein.core
{
import skein.binding.core.BindingContext;
import skein.binding.core.PropertyDestination;
import skein.binding.core.Source;

public class PropertySetter
{
    public static function set(site:Object, property:String, value:Object):void
    {
        if (value is Source)
        {
            var ctx:BindingContext = new BindingContext();

            ctx.bind(Source(value), new PropertyDestination(site, property));
        }
        else
        {
            site[property] = value;
        }
    }
}
}

/**
 * Created with IntelliJ IDEA.
 * User: mobitile
 * Date: 4/21/13
 * Time: 1:37 PM
 * To change this template use File | Settings | File Templates.
 */
package skein.core
{
import flash.utils.getDefinitionByName;

public class PropertySetter
{
    public static function set(site:Object, property:String, value:Object):void
    {
        var SourceClass:Class = getDefinitionByName("skein.binding.core::Source") as Class;
        var BindingContextClass:Class = getDefinitionByName("skein.binding.core::BindingContext") as Class;

        if (SourceClass && BindingContextClass && value is SourceClass)
        {
            BindingContextClass["bindProperty"](site, property, value);
        }
        else
        {
            site[property] = value;
        }
    }
}
}

Remove dependency on skein.binding

[core] Remove dependency on skein.binding


package dolly.utils {
import dolly.core.dolly_internal;

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

use namespace dolly_internal;

public class PropertyUtilTests {

	private var sourceObj:Object;
	private var targetObj:Object;

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

		targetObj = {};
	}

	[After]
	public function after():void {
		sourceObj = targetObj = null;
	}
}
}

package dolly.utils {
import dolly.core.dolly_internal;

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

import org.flexunit.assertThat;
import org.flexunit.asserts.assertTrue;
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 PropertyUtilTests {

	private var sourceObj:Object;
	private var targetObj:Object;

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

		targetObj = {};
	}

	[After]
	public function after():void {
		sourceObj = targetObj = null;
	}

	[Test]
	public function copyingOfArray():void {
		PropertyUtil.copyArray(targetObj, "array", sourceObj.array);

		assertThat(targetObj.array, arrayWithSize(5));
		assertThat(targetObj.array, sourceObj.array);
		assertTrue(targetObj.array != sourceObj.array);
		assertThat(targetObj.array, everyItem(isA(Number)));
		assertThat(targetObj.array, array(equalTo(0), equalTo(1), equalTo(2), equalTo(3), equalTo(4)));
	}
}
}

Test for PropertyUtil.copyArray() method.


package {
  import flash.display.Sprite;
  import flash.external.ExternalInterface;
  import flash.system.Security;
  import flash.net.NetStream;
  import flash.events.*;
  import flash.utils.setTimeout;

  import org.osmf.containers.MediaContainer;
  import org.osmf.elements.VideoElement;
  import org.osmf.net.NetStreamLoadTrait;

  import org.osmf.media.DefaultMediaFactory;
  import org.osmf.media.MediaElement;
  import org.osmf.media.MediaPlayer;
  import org.osmf.media.URLResource;

  import org.osmf.events.TimeEvent;
  import org.osmf.events.BufferEvent;
  import org.osmf.events.MediaElementEvent;
  import org.osmf.events.LoadEvent;

  import org.osmf.traits.MediaTraitType;

  [SWF(width="640", height="360")]
  public class RTMP extends Sprite {
    private var playbackId:String;
    private var mediaFactory:DefaultMediaFactory;
    private var mediaContainer:MediaContainer;
    private var mediaPlayer:MediaPlayer;
    private var netStream:NetStream;
    private var mediaElement:MediaElement;
    private var netStreamLoadTrait:NetStreamLoadTrait;
    private var playbackState:String = "IDLE";

    public function RTMP() {
      Security.allowDomain('*');
      Security.allowInsecureDomain('*');
      playbackId = this.root.loaderInfo.parameters.playbackId;
      mediaFactory = new DefaultMediaFactory();
      mediaContainer = new MediaContainer();
      setupCallbacks();
      setupGetters();
      ExternalInterface.call('console.log', 'clappr rtmp 0.7-alpha');
      _triggerEvent('flashready');
    }

    private function setupCallbacks():void {
      ExternalInterface.addCallback("playerPlay", playerPlay);
      ExternalInterface.addCallback("playerResume", playerPlay);
      ExternalInterface.addCallback("playerPause", playerPause);
      ExternalInterface.addCallback("playerStop", playerStop);
      ExternalInterface.addCallback("playerSeek", playerSeek);
      ExternalInterface.addCallback("playerVolume", playerVolume);
    }

    private function setupGetters():void {
      ExternalInterface.addCallback("getState", getState);
      ExternalInterface.addCallback("getPosition", getPosition);
      ExternalInterface.addCallback("getDuration", getDuration);
    }

    private function onTraitAdd(event:MediaElementEvent):void {
      if (mediaElement.hasTrait(MediaTraitType.LOAD)) {
        netStreamLoadTrait = mediaElement.getTrait(MediaTraitType.LOAD) as NetStreamLoadTrait;
        netStreamLoadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onLoaded);
      }
    }

    private function onLoaded(event:LoadEvent):void {
      netStream = netStreamLoadTrait.netStream;
      netStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
      mediaPlayer.play();
    }

    private function netStatusHandler(event:NetStatusEvent):void {
      ExternalInterface.call('console.log', '-> ' + event.info.code);
      if (playbackState == "ENDED") {
        return;
      } else if (event.info.code == "NetStream.Buffer.Full") {
        playbackState = "PLAYING";
        _triggerEvent('statechanged');
      } else if (event.info.code == "NetStream.Buffer.Empty" || event.info.code == "NetStream.Seek.Notify") {
        playbackState = "PLAYING_BUFFERING";
        _triggerEvent('statechanged');
      }
    }

    private function playerPlay(url:String):void {
      if (!mediaElement) {
        ExternalInterface.call('console.log', 'player play, no mediaelement');
        playbackState = "PLAYING_BUFFERING";
        _triggerEvent('statechanged');
        mediaElement = mediaFactory.createMediaElement(new URLResource(url));
        mediaElement.addEventListener(MediaElementEvent.TRAIT_ADD, onTraitAdd);
        mediaPlayer = new MediaPlayer(mediaElement);
        mediaPlayer.autoPlay = false;
        mediaPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, onTimeUpdated);
        mediaPlayer.addEventListener(TimeEvent.DURATION_CHANGE, onTimeUpdated);
        mediaPlayer.addEventListener(TimeEvent.COMPLETE, onFinish);
        mediaContainer.addMediaElement(mediaElement);
        addChild(mediaContainer);
      } else {
        ExternalInterface.call('console.log', 'playing with media element');
        mediaPlayer.play();
      }
    }

    private function playerPause():void {
      ExternalInterface.call('console.log', 'player pause');
      mediaPlayer.pause();
    }

    private function playerSeek(seconds:Number):void {
      mediaPlayer.seek(seconds);
    }

    private function playerStop():void {
      mediaPlayer.stop();
    }

    private function playerVolume(level:Number):void {
      mediaPlayer.volume = level/100;
    }

   private function getState():String {
      return playbackState;
    }

    private function getPosition():Number {
      return mediaPlayer.currentTime;
    }

    private function getDuration():Number {
      return mediaPlayer.duration;
    }

    private function onTimeUpdated(event:TimeEvent):void {
      _triggerEvent('progress');
      _triggerEvent('timeupdate');
    }

    private function onFinish(event:TimeEvent):void {
      mediaPlayer.stop();
      ExternalInterface.call('console.log', 'ended');
      playbackState = 'ENDED';
      _triggerEvent('statechanged');
    }

    private function _triggerEvent(name: String):void {
      ExternalInterface.call('Clappr.Mediator.trigger("' + playbackId + ':' + name +'")');
    }
  }
}

package {
  import flash.display.Sprite;
  import flash.external.ExternalInterface;
  import flash.system.Security;
  import flash.net.NetStream;
  import flash.events.*;
  import flash.utils.setTimeout;

  import org.osmf.containers.MediaContainer;
  import org.osmf.elements.VideoElement;
  import org.osmf.net.NetStreamLoadTrait;

  import org.osmf.media.DefaultMediaFactory;
  import org.osmf.media.MediaElement;
  import org.osmf.media.MediaPlayer;
  import org.osmf.media.URLResource;

  import org.osmf.events.TimeEvent;
  import org.osmf.events.BufferEvent;
  import org.osmf.events.MediaElementEvent;
  import org.osmf.events.LoadEvent;

  import org.osmf.traits.MediaTraitType;

  [SWF(width="640", height="360")]
  public class RTMP extends Sprite {
    private var playbackId:String;
    private var mediaFactory:DefaultMediaFactory;
    private var mediaContainer:MediaContainer;
    private var mediaPlayer:MediaPlayer;
    private var netStream:NetStream;
    private var mediaElement:MediaElement;
    private var netStreamLoadTrait:NetStreamLoadTrait;
    private var playbackState:String = "IDLE";

    public function RTMP() {
      Security.allowDomain('*');
      Security.allowInsecureDomain('*');
      playbackId = this.root.loaderInfo.parameters.playbackId;
      mediaFactory = new DefaultMediaFactory();
      mediaContainer = new MediaContainer();
      setupCallbacks();
      setupGetters();
      ExternalInterface.call('console.log', 'clappr rtmp 0.8-alpha');
      _triggerEvent('flashready');
    }

    private function setupCallbacks():void {
      ExternalInterface.addCallback("playerPlay", playerPlay);
      ExternalInterface.addCallback("playerResume", playerPlay);
      ExternalInterface.addCallback("playerPause", playerPause);
      ExternalInterface.addCallback("playerStop", playerStop);
      ExternalInterface.addCallback("playerSeek", playerSeek);
      ExternalInterface.addCallback("playerVolume", playerVolume);
    }

    private function setupGetters():void {
      ExternalInterface.addCallback("getState", getState);
      ExternalInterface.addCallback("getPosition", getPosition);
      ExternalInterface.addCallback("getDuration", getDuration);
    }

    private function onTraitAdd(event:MediaElementEvent):void {
      if (mediaElement.hasTrait(MediaTraitType.LOAD)) {
        netStreamLoadTrait = mediaElement.getTrait(MediaTraitType.LOAD) as NetStreamLoadTrait;
        netStreamLoadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onLoaded);
      }
    }

    private function onLoaded(event:LoadEvent):void {
      netStream = netStreamLoadTrait.netStream;
      netStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
      mediaPlayer.play();
    }

    private function netStatusHandler(event:NetStatusEvent):void {
      if (playbackState == "ENDED") {
        return;
      } else if (event.info.code == "NetStream.Buffer.Full") {
        playbackState = "PLAYING";
        _triggerEvent('statechanged');
      } else if (event.info.code == "NetStream.Buffer.Empty" || event.info.code == "NetStream.Seek.Notify") {
        playbackState = "PLAYING_BUFFERING";
        _triggerEvent('statechanged');
      }
    }

    private function playerPlay(url:String=null):void {
      if (!mediaElement) {
        playbackState = "PLAYING_BUFFERING";
        _triggerEvent('statechanged');
        mediaElement = mediaFactory.createMediaElement(new URLResource(url));
        mediaElement.addEventListener(MediaElementEvent.TRAIT_ADD, onTraitAdd);
        mediaPlayer = new MediaPlayer(mediaElement);
        mediaPlayer.autoPlay = false;
        mediaPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, onTimeUpdated);
        mediaPlayer.addEventListener(TimeEvent.DURATION_CHANGE, onTimeUpdated);
        mediaPlayer.addEventListener(TimeEvent.COMPLETE, onFinish);
        mediaContainer.addMediaElement(mediaElement);
        addChild(mediaContainer);
      } else {
        ExternalInterface.call('console.log', 'playing with media element');
        mediaPlayer.play();
      }
    }

    private function playerPause():void {
      ExternalInterface.call('console.log', 'player pause');
      mediaPlayer.pause();
      playbackState = "PAUSED";
    }

    private function playerSeek(seconds:Number):void {
      mediaPlayer.seek(seconds);
    }

    private function playerStop():void {
      mediaPlayer.stop();
      playbackState = "IDLE";
    }

    private function playerVolume(level:Number):void {
      mediaPlayer.volume = level/100;
    }

   private function getState():String {
      return playbackState;
    }

    private function getPosition():Number {
      return mediaPlayer.currentTime;
    }

    private function getDuration():Number {
      return mediaPlayer.duration;
    }

    private function onTimeUpdated(event:TimeEvent):void {
      _triggerEvent('progress');
      _triggerEvent('timeupdate');
    }

    private function onFinish(event:TimeEvent):void {
      mediaPlayer.stop();
      playbackState = 'ENDED';
      _triggerEvent('statechanged');
    }

    private function _triggerEvent(name: String):void {
      ExternalInterface.call('Clappr.Mediator.trigger("' + playbackId + ':' + name +'")');
    }
  }
}

RTMP.as: change playbackState on pause/stop (fix #5)


package com.kaltura.kdpfl
{
	import com.kaltura.kdpfl.controller.InitMacroCommand;
	import com.kaltura.kdpfl.controller.LayoutReadyCommand;
	import com.kaltura.kdpfl.controller.PlaybackCompleteCommand;
	import com.kaltura.kdpfl.controller.SequenceItemPlayEndCommand;
	import com.kaltura.kdpfl.controller.SequenceSkipNextCommand;
	import com.kaltura.kdpfl.controller.StartupCommand;
	import com.kaltura.kdpfl.controller.media.InitChangeMediaMacroCommand;
	import com.kaltura.kdpfl.controller.media.LiveStreamCommand;
	import com.kaltura.kdpfl.controller.media.MediaReadyCommand;
	import com.kaltura.kdpfl.events.DynamicEvent;
	import com.kaltura.kdpfl.model.ExternalInterfaceProxy;
	import com.kaltura.kdpfl.model.type.DebugLevel;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.kdpfl.view.media.KMediaPlayerMediator;
	import com.kaltura.puremvc.as3.core.KView;

	import flash.display.DisplayObject;

	import mx.utils.ObjectProxy;

	import org.osmf.media.MediaPlayerState;
	import org.puremvc.as3.interfaces.IFacade;
	import org.puremvc.as3.interfaces.IMediator;
	import org.puremvc.as3.interfaces.IProxy;
	import org.puremvc.as3.patterns.facade.Facade;

	public class ApplicationFacade extends Facade implements IFacade
	{
		/**
		 * The current version of the KDP.
		 */
		public var kdpVersion : String = "v3.9.6";

		/**
		 * save any mediator name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _mediatorNameArray : Array = new Array();
		/**
		 * save any proxy name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _proxyNameArray : Array = new Array();
		/**
		 * save any notification that create a command name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _commandNameArray : Array = new Array();

		/**
		 * The bindObject create dynamicly all attributes that need to be binded on it
		 */
		public var bindObject:ObjectProxy = new ObjectProxy();
		/**
		 * a reference to KDP3 main application
		 */
		private var _app : DisplayObject;

		/**
		 * the url of the kdp
		 */
		public var appFolder : String;
		public var debugMode : Boolean = false;
		/**
		 *
		 * will affect the traces that will be sent. See DebugLevel enum
		 */
		public var debugLevel:int;

		private var externalInterface : ExternalInterfaceProxy;

		/**
		 * return the one and only instance of the ApplicationFacade,
		 * if not exist it will be created.
		 * @return
		 *
		 */
		public static function getInstance() : ApplicationFacade
		{
			if (instance == null) instance = new ApplicationFacade();
			return instance as ApplicationFacade;
		}

		/**
		 * All this simply does is fire a notification which is routed to
		 * "StartupCommand" via the "registerCommand" handlers
		 * @param app
		 *
		 */
		public function start(app:DisplayObject):void
		{
			CONFIG::isSDK46 {
				kdpVersion += ".sdk46";
			}

			_app = app;
			appFolder = app.root.loaderInfo.url;
			appFolder = appFolder.substr(0, appFolder.lastIndexOf('/') + 1);

			sendNotification(NotificationType.STARTUP, app);
			externalInterface = retrieveProxy(ExternalInterfaceProxy.NAME) as ExternalInterfaceProxy;
		}

		/**
		 * Created to trace all notifications for debug
		 * @param notificationName
		 * @param body
		 * @param type
		 *
		 */
		override public function sendNotification(notificationName:String, body:Object = null, type:String = null):void {
			if (debugMode) {
				var s:String;
				if ((notificationName == NotificationType.BYTES_DOWNLOADED_CHANGE && debugLevel == DebugLevel.HIGH) ||
					(notificationName == NotificationType.PLAYER_UPDATE_PLAYHEAD && (debugLevel == DebugLevel.HIGH || debugLevel == DebugLevel.MEDIUM)) ||
					(notificationName != NotificationType.PLAYER_UPDATE_PLAYHEAD && notificationName != NotificationType.BYTES_DOWNLOADED_CHANGE))
				 {
					if (notificationName == NotificationType.PLAYER_STATE_CHANGE)
						s = 'Sent ' + notificationName + ' ==> ' + body.toString();
					else {
						s = 'Sent ' + notificationName;
						if (body) {
							var found:Boolean = false;
							for (var o:* in body) {
								s += ", " + o + ":" + body[o];
								found = true;
							}
							if (!found) {
								s += ", " + body.toString();
							}
						}
					}
				}

				if (s && s != "null") {
					var curTime:Number = 0;
					var kmediaPlayerMediator:KMediaPlayerMediator = retrieveMediator(KMediaPlayerMediator.NAME) as KMediaPlayerMediator;
					if (kmediaPlayerMediator &&
						kmediaPlayerMediator.player &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.LOADING &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.UNINITIALIZED &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.PLAYBACK_ERROR)
					{
						curTime = kmediaPlayerMediator.getCurrentTime();
					}
					var date:Date = new Date();

					KTrace.getInstance().log(date.toTimeString(), ":", s, ", playhead position:", curTime);
				}

			}

			super.sendNotification(notificationName, body, type);
			//For external Flash/Flex application application listening to the KDP events
			_app.dispatchEvent(new DynamicEvent(notificationName, body));

			//For external Javascript application listening to the KDP events
			if (externalInterface)
				externalInterface.notifyJs(notificationName, body);

		}

		/**
		 * controls the routing of notifications to our controllers,
		 * we all know them as commands
		 *
		 */
		override protected function initializeController():void
		{
			super.initializeController();
			registerCommand(NotificationType.STARTUP, StartupCommand);

			// we do both init start KDP life cycle, and start to load the entry simultaneously
			registerCommand(NotificationType.INITIATE_APP, InitMacroCommand);

			//There are several things that need to be done as soon as the layout of the kdp is ready.
			registerCommand(NotificationType.LAYOUT_READY, LayoutReadyCommand );
			//when one call change media we fire the load media command again
			registerCommand(NotificationType.CHANGE_MEDIA, InitChangeMediaMacroCommand);

			registerCommand (NotificationType.LIVE_ENTRY, LiveStreamCommand );

			registerCommand (NotificationType.MEDIA_LOADED, MediaReadyCommand );

			registerCommand (NotificationType.PLAYBACK_COMPLETE, PlaybackCompleteCommand);

			registerCommand(NotificationType.SEQUENCE_ITEM_PLAY_END, SequenceItemPlayEndCommand);

			registerCommand(NotificationType.SEQUENCE_SKIP_NEXT, SequenceSkipNextCommand);
		}

		override protected function initializeView():void
		{
			if ( view != null ) return;
			view = KView.getInstance();
		}

		override protected function initializeFacade():void
		{
			initializeView();
			initializeModel();
			initializeController();
		}

		/**
		 * Help to remove all commands, mediator, proxies from the facade
		 *
		 */
		public function dispose() : void
		{
			var i:int =0;

			for(i=0; i<_commandNameArray.length; i++)
				this.removeCommand( _commandNameArray[i] );

			for(i=0; i<_mediatorNameArray.length; i++)
				this.removeMediator( _mediatorNameArray[i] );

			for(i=0; i<_proxyNameArray.length; i++)
				this.removeProxy( _proxyNameArray[i] );

			_commandNameArray = new Array();
			_mediatorNameArray = new Array();
			_proxyNameArray = new Array();
		}

		/**
		 * after registartion add the notification name to a
		 * @param notificationName
		 * @param commandClassRef
		 *
		 */
		override public function registerCommand(notificationName:String, commandClassRef:Class):void
		{
			super.registerCommand(notificationName, commandClassRef);

			//save the notification name so we can delete it later
			_commandNameArray.push( notificationName );
		}

		override public function registerMediator(mediator:IMediator):void
		{
			super.registerMediator(mediator);

			//save the mediator name so we can delete it later
			_mediatorNameArray.push( mediator.getMediatorName() );
		}

		override public function registerProxy(proxy:IProxy):void
		{
			bindObject[proxy.getProxyName()] = proxy.getData();

			super.registerProxy(proxy);

			//save the proxy name so we can delete it later
			_proxyNameArray.push( proxy.getProxyName() );
		}

		/**
		 * add notification observer for a given mediator
		 * @param notification the notification to listen for
		 * @param notificationHandler handler to be called
		 * @param mediator
		 *
		 */
		public function addNotificationInterest(notification:String, notificationHandler:Function, mediator:IMediator):void {
			(view as KView).addNotificationInterest(notification, notificationHandler, mediator);
		}

		public function get app () : DisplayObject
		{
			return _app;
		}

		public function set app (disObj : DisplayObject) : void
		{
			_app = disObj;
		}

	}
}

package com.kaltura.kdpfl
{
	import com.kaltura.kdpfl.controller.InitMacroCommand;
	import com.kaltura.kdpfl.controller.LayoutReadyCommand;
	import com.kaltura.kdpfl.controller.PlaybackCompleteCommand;
	import com.kaltura.kdpfl.controller.SequenceItemPlayEndCommand;
	import com.kaltura.kdpfl.controller.SequenceSkipNextCommand;
	import com.kaltura.kdpfl.controller.StartupCommand;
	import com.kaltura.kdpfl.controller.media.InitChangeMediaMacroCommand;
	import com.kaltura.kdpfl.controller.media.LiveStreamCommand;
	import com.kaltura.kdpfl.controller.media.MediaReadyCommand;
	import com.kaltura.kdpfl.events.DynamicEvent;
	import com.kaltura.kdpfl.model.ExternalInterfaceProxy;
	import com.kaltura.kdpfl.model.type.DebugLevel;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.kdpfl.view.media.KMediaPlayerMediator;
	import com.kaltura.puremvc.as3.core.KView;

	import flash.display.DisplayObject;

	import mx.utils.ObjectProxy;

	import org.osmf.media.MediaPlayerState;
	import org.puremvc.as3.interfaces.IFacade;
	import org.puremvc.as3.interfaces.IMediator;
	import org.puremvc.as3.interfaces.IProxy;
	import org.puremvc.as3.patterns.facade.Facade;

	public class ApplicationFacade extends Facade implements IFacade
	{
		/**
		 * The current version of the KDP.
		 */
		public var kdpVersion : String = "v3.9.7";

		/**
		 * save any mediator name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _mediatorNameArray : Array = new Array();
		/**
		 * save any proxy name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _proxyNameArray : Array = new Array();
		/**
		 * save any notification that create a command name that is registered to this array in order to delete at any time
		 * (the pure mvc don't support it as built-in function)
		 */
		private var _commandNameArray : Array = new Array();

		/**
		 * The bindObject create dynamicly all attributes that need to be binded on it
		 */
		public var bindObject:ObjectProxy = new ObjectProxy();
		/**
		 * a reference to KDP3 main application
		 */
		private var _app : DisplayObject;

		/**
		 * the url of the kdp
		 */
		public var appFolder : String;
		public var debugMode : Boolean = false;
		/**
		 *
		 * will affect the traces that will be sent. See DebugLevel enum
		 */
		public var debugLevel:int;

		private var externalInterface : ExternalInterfaceProxy;

		/**
		 * return the one and only instance of the ApplicationFacade,
		 * if not exist it will be created.
		 * @return
		 *
		 */
		public static function getInstance() : ApplicationFacade
		{
			if (instance == null) instance = new ApplicationFacade();
			return instance as ApplicationFacade;
		}

		/**
		 * All this simply does is fire a notification which is routed to
		 * "StartupCommand" via the "registerCommand" handlers
		 * @param app
		 *
		 */
		public function start(app:DisplayObject):void
		{
			CONFIG::isSDK46 {
				kdpVersion += ".sdk46";
			}

			_app = app;
			appFolder = app.root.loaderInfo.url;
			appFolder = appFolder.substr(0, appFolder.lastIndexOf('/') + 1);

			sendNotification(NotificationType.STARTUP, app);
			externalInterface = retrieveProxy(ExternalInterfaceProxy.NAME) as ExternalInterfaceProxy;
		}

		/**
		 * Created to trace all notifications for debug
		 * @param notificationName
		 * @param body
		 * @param type
		 *
		 */
		override public function sendNotification(notificationName:String, body:Object = null, type:String = null):void {
			if (debugMode) {
				var s:String;
				if ((notificationName == NotificationType.BYTES_DOWNLOADED_CHANGE && debugLevel == DebugLevel.HIGH) ||
					(notificationName == NotificationType.PLAYER_UPDATE_PLAYHEAD && (debugLevel == DebugLevel.HIGH || debugLevel == DebugLevel.MEDIUM)) ||
					(notificationName != NotificationType.PLAYER_UPDATE_PLAYHEAD && notificationName != NotificationType.BYTES_DOWNLOADED_CHANGE))
				 {
					if (notificationName == NotificationType.PLAYER_STATE_CHANGE)
						s = 'Sent ' + notificationName + ' ==> ' + body.toString();
					else {
						s = 'Sent ' + notificationName;
						if (body) {
							var found:Boolean = false;
							for (var o:* in body) {
								s += ", " + o + ":" + body[o];
								found = true;
							}
							if (!found) {
								s += ", " + body.toString();
							}
						}
					}
				}

				if (s && s != "null") {
					var curTime:Number = 0;
					var kmediaPlayerMediator:KMediaPlayerMediator = retrieveMediator(KMediaPlayerMediator.NAME) as KMediaPlayerMediator;
					if (kmediaPlayerMediator &&
						kmediaPlayerMediator.player &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.LOADING &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.UNINITIALIZED &&
						kmediaPlayerMediator.player.state!=MediaPlayerState.PLAYBACK_ERROR)
					{
						curTime = kmediaPlayerMediator.getCurrentTime();
					}
					var date:Date = new Date();

					KTrace.getInstance().log(date.toTimeString(), ":", s, ", playhead position:", curTime);
				}

			}

			super.sendNotification(notificationName, body, type);
			//For external Flash/Flex application application listening to the KDP events
			_app.dispatchEvent(new DynamicEvent(notificationName, body));

			//For external Javascript application listening to the KDP events
			if (externalInterface)
				externalInterface.notifyJs(notificationName, body);

		}

		/**
		 * controls the routing of notifications to our controllers,
		 * we all know them as commands
		 *
		 */
		override protected function initializeController():void
		{
			super.initializeController();
			registerCommand(NotificationType.STARTUP, StartupCommand);

			// we do both init start KDP life cycle, and start to load the entry simultaneously
			registerCommand(NotificationType.INITIATE_APP, InitMacroCommand);

			//There are several things that need to be done as soon as the layout of the kdp is ready.
			registerCommand(NotificationType.LAYOUT_READY, LayoutReadyCommand );
			//when one call change media we fire the load media command again
			registerCommand(NotificationType.CHANGE_MEDIA, InitChangeMediaMacroCommand);

			registerCommand (NotificationType.LIVE_ENTRY, LiveStreamCommand );

			registerCommand (NotificationType.MEDIA_LOADED, MediaReadyCommand );

			registerCommand (NotificationType.PLAYBACK_COMPLETE, PlaybackCompleteCommand);

			registerCommand(NotificationType.SEQUENCE_ITEM_PLAY_END, SequenceItemPlayEndCommand);

			registerCommand(NotificationType.SEQUENCE_SKIP_NEXT, SequenceSkipNextCommand);
		}

		override protected function initializeView():void
		{
			if ( view != null ) return;
			view = KView.getInstance();
		}

		override protected function initializeFacade():void
		{
			initializeView();
			initializeModel();
			initializeController();
		}

		/**
		 * Help to remove all commands, mediator, proxies from the facade
		 *
		 */
		public function dispose() : void
		{
			var i:int =0;

			for(i=0; i<_commandNameArray.length; i++)
				this.removeCommand( _commandNameArray[i] );

			for(i=0; i<_mediatorNameArray.length; i++)
				this.removeMediator( _mediatorNameArray[i] );

			for(i=0; i<_proxyNameArray.length; i++)
				this.removeProxy( _proxyNameArray[i] );

			_commandNameArray = new Array();
			_mediatorNameArray = new Array();
			_proxyNameArray = new Array();
		}

		/**
		 * after registartion add the notification name to a
		 * @param notificationName
		 * @param commandClassRef
		 *
		 */
		override public function registerCommand(notificationName:String, commandClassRef:Class):void
		{
			super.registerCommand(notificationName, commandClassRef);

			//save the notification name so we can delete it later
			_commandNameArray.push( notificationName );
		}

		override public function registerMediator(mediator:IMediator):void
		{
			super.registerMediator(mediator);

			//save the mediator name so we can delete it later
			_mediatorNameArray.push( mediator.getMediatorName() );
		}

		override public function registerProxy(proxy:IProxy):void
		{
			bindObject[proxy.getProxyName()] = proxy.getData();

			super.registerProxy(proxy);

			//save the proxy name so we can delete it later
			_proxyNameArray.push( proxy.getProxyName() );
		}

		/**
		 * add notification observer for a given mediator
		 * @param notification the notification to listen for
		 * @param notificationHandler handler to be called
		 * @param mediator
		 *
		 */
		public function addNotificationInterest(notification:String, notificationHandler:Function, mediator:IMediator):void {
			(view as KView).addNotificationInterest(notification, notificationHandler, mediator);
		}

		public function get app () : DisplayObject
		{
			return _app;
		}

		public function set app (disObj : DisplayObject) : void
		{
			_app = disObj;
		}

	}
}

bump version 3.9.7

bump version 3.9.7


package aerys.minko.type.binding
{
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.enum.DataProviderUsage;

	import flash.utils.Dictionary;

	public final class DataBindings
	{
		private var _owner						: ISceneNode;

		private var _providers					: Vector.<IDataProvider>;
		private var _bindingNames				: Vector.<String>;

		private var _bindingNameToValue			: Object;
		private var _bindingNameToChangedSignal	: Object;

		private var _bindingNameToProvider		: Object;
		private var _providerToBindingNames		: Dictionary;

		public function get owner() : ISceneNode
		{
			return _owner;
		}

		public function get numProviders() : uint
		{
			return _providers.length;
		}

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

		public function DataBindings(owner : ISceneNode)
		{
			_owner = owner;

			_providers = new <IDataProvider>[];
			_bindingNames = new <String>[];
			_bindingNameToValue = {};
			_bindingNameToChangedSignal = {};
			_bindingNameToProvider = {};
			_providerToBindingNames = new Dictionary(true);
		}

		public function contains(dataProvider : IDataProvider) : Boolean
		{
			return _providers.indexOf(dataProvider) != -1;
		}

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

			var dataDescriptor	: Object	= provider.dataDescriptor;

			provider.propertyChanged.add(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.add(addBinding);
				dynamicProvider.propertyRemoved.add(removeBinding);
			}

			_providerToBindingNames[provider] = new <String>[];
			_providers.push(provider);

			for (var propertyName : String in dataDescriptor)
				addBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		private function addBinding(provider		: IDataProvider,
									propertyName	: String,
									bindingName		: String,
									value			: Object) : void
		{
			var providerBindingNames	: Vector.<String> 	= _providerToBindingNames[provider];

			if (_bindingNames.indexOf(bindingName) != -1)
				throw new Error(
					'Another data provider is already declaring the \'' + bindingName
					+ '\' property.'
				);

			_bindingNameToProvider[bindingName] = provider;
			_bindingNameToValue[bindingName] = value;

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

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(this, bindingName, null, value);
		}

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

			if (providerBindingsNames == null)
				throw new ArgumentError('Unknown provider.');

			var numProviders : uint = _providers.length - 1;

			provider.propertyChanged.remove(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.remove(addBinding);
				dynamicProvider.propertyRemoved.remove(removeBinding);
			}

			_providers[_providers.indexOf(provider)] = _providers[numProviders];
			_providers.length = numProviders;
			delete _providerToBindingNames[provider];

			var dataDescriptor : Object = provider.dataDescriptor;

			for (var propertyName : String in dataDescriptor)
				removeBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		public function removeBinding(provider		: IDataProvider,
									  propertyName	: String,
									  bindingName	: String,
									  value			: Object) : void
		{
			var numBindings 	: uint 		= _bindingNames.length - 1;
			var changedSignal 	: Signal 	= _bindingNameToChangedSignal[bindingName] as Signal;

			delete _bindingNameToValue[bindingName];
			delete _bindingNameToProvider[bindingName];

			_bindingNames[_bindingNames.indexOf(bindingName)] = _bindingNames[numBindings];
			_bindingNames.length = numBindings;

			if (changedSignal != null)
				changedSignal.execute(this, bindingName, value, null);
		}

		public function removeAllProviders() : void
		{
			var numProviders : uint = this.numProviders;

			for (var providerId : int = numProviders - 1; providerId >= 0; --providerId)
				removeProvider(getProviderAt(providerId));
		}

		public function hasCallback(bindingName	: String,
									callback	: Function) : Boolean
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			return signal != null && signal.hasCallback(callback);
		}

		public function addCallback(bindingName : String,
									callback	: Function) : void
		{
			_bindingNameToChangedSignal[bindingName] ||= new Signal(
				'DataBindings.changed[' + bindingName + ']'
			);

			Signal(_bindingNameToChangedSignal[bindingName]).add(callback);
		}

		public function removeCallback(bindingName	: String,
									   callback		: Function) : void
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];
			if (!signal)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			signal.remove(callback);

			if (signal.numCallbacks == 0)
				delete _bindingNameToChangedSignal[bindingName];
		}

		public function getProviderAt(index : uint) : IDataProvider
		{
			return _providers[index];
		}

		public function getProviderByBindingName(bindingName : String) : IDataProvider
		{
			if (_bindingNameToProvider[bindingName] == null)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			return _bindingNameToProvider[bindingName];
		}

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

		public function getProperty(bindingName : String) : *
		{
			if (_bindingNames.indexOf(bindingName) < 0)
				throw new Error('The property \'' + bindingName + '\' does not exist.');

			return _bindingNameToValue[bindingName];
		}

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

			return _bindingNames[bindingIndex];
		}

		public function copySharedProvidersFrom(source : DataBindings) : void
		{
			var numProviders : uint = source._providers.length;

			for (var providerId : uint = 0; providerId < numProviders; ++providerId)
			{
				var provider : IDataProvider = source._providers[providerId];

				if (provider.usage == DataProviderUsage.SHARED)
					addProvider(provider);
			}
		}

		private function propertyChangedHandler(source 			: IDataProvider,
												attributeName 	: String) : void
		{
			if (attributeName == null)
				throw new Error('DataProviders must change only one property at a time.');

			var bindingName : String	= source.dataDescriptor[attributeName];
			var oldValue	: Object	= _bindingNameToValue[bindingName];
			var newValue	: Object	= source[attributeName];

			_bindingNameToValue[bindingName] = newValue;

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(
					this,
					bindingName,
					oldValue,
					newValue
				);
		}
	}
}

fix coding style

fix coding style