package org.mangui.flowplayer {
    import flash.display.DisplayObject;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.utils.Dictionary;
    import flash.media.Video;

    import org.mangui.HLS.HLS;
    import org.mangui.HLS.HLSEvent;
    import org.mangui.HLS.HLSStates;
    import org.mangui.HLS.HLSTypes;
    import org.mangui.HLS.utils.Log;
    import org.flowplayer.model.Plugin;
    import org.flowplayer.model.PluginModel;
    import org.flowplayer.view.Flowplayer;
    import org.flowplayer.controller.StreamProvider;
    import org.flowplayer.controller.TimeProvider;
    import org.flowplayer.controller.VolumeController;
    import org.flowplayer.model.Clip;
    import org.flowplayer.model.ClipType;
    import org.flowplayer.model.ClipEvent;
    import org.flowplayer.model.ClipEventType;
    import org.flowplayer.model.Playlist;
    import org.flowplayer.view.StageVideoWrapper;

    public class HLSProvider  implements StreamProvider,Plugin {
        private var _volumecontroller : VolumeController;
        private var _playlist : Playlist;
        private var _timeProvider : TimeProvider;
        private var _model : PluginModel;
        private var _player : Flowplayer;
        private var _clip : Clip;
        private var _video : Video;
        /** reference to the framework. **/
        private var _hls : HLS;
        private var _hlsState : String = HLSStates.IDLE;
        // event values
        private var _position : Number = 0;
        private var _duration : Number = 0;
        private var _bufferedTime : Number = 0;
        private var _videoWidth : Number = -1;
        private var _videoHeight : Number = -1;
        private var _isManifestLoaded : Boolean = false;
        private var _pauseAfterStart : Boolean;
        private var _seekable : Boolean = false;

        public function getDefaultConfig() : Object {
            return null;
        }

        public function onConfig(model : PluginModel) : void {
            Log.info("onConfig()");
            _model = model;
        }

        public function onLoad(player : Flowplayer) : void {
            Log.info("onLoad()");
            _player = player;
            _hls = new HLS();
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _completeHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
            _hls.addEventListener(HLSEvent.STATE, _stateHandler);

            var cfg:Object = _model.config;
            var value : Object;

            // parse configuration parameters
            value = cfg.hls_debug;
            if (value != null) {
                Log.info("hls_debug:" + value);
                Log.LOG_DEBUG_ENABLED = value as Boolean;
            }

            value = cfg.hls_debug2;
            if (value != null) {
                Log.info("hls_debug2:" + value);
                Log.LOG_DEBUG2_ENABLED = value as Boolean;
            }

            value = cfg.hls_minbufferlength;
            if (value != null) {
                Log.info("hls_minbufferlength:" + value);
                _hls.minBufferLength = value as Number;
            }

            value = cfg.hls_maxbufferlength;
            if (value != null) {
                Log.info("hls_maxbufferlength:" + value);
                _hls.maxBufferLength = value as Number;
            }

            value = cfg.hls_startfromlowestlevel;
            if (value != null) {
                Log.info("hls_startfromlowestlevel:" + value);
                _hls.startFromLowestLevel = value as Boolean;
            }

            value = cfg.hls_seekfromlowestlevel;
            if (value != null) {
                Log.info("hls_seekfromlowestlevel:" + value);
                _hls.seekFromLowestLevel = value as Boolean;
            }

            value = cfg.hls_live_flushurlcache;
            if (value != null) {
                Log.info("hls_live_flushurlcache:" + value);
                _hls.flushLiveURLCache = value as Boolean;
            }
            _model.dispatchOnLoad();
        }

        private function _completeHandler(event : HLSEvent) : void {
            _clip.dispatch(ClipEventType.FINISH);
        };

        private function _errorHandler(event : HLSEvent) : void {
        };

        private function _manifestHandler(event : HLSEvent) : void {
            _duration = event.levels[0].duration;
            _isManifestLoaded = true;
            _clip.duration = _duration;
            _clip.dispatch(ClipEventType.METADATA);
            _seekable = true;
            //if (_hls.type == HLSTypes.LIVE) {
            //    _seekable = false;
            //} else {
            //    _seekable = true;
            //}
            if (_pauseAfterStart == false) {
                _hls.stream.play();
            }
        };

        private function _mediaTimeHandler(event : HLSEvent) : void {
            _position = Math.max(0, event.mediatime.position);
            _duration = event.mediatime.duration;
            _clip.duration = _duration;
            _bufferedTime = event.mediatime.buffer + event.mediatime.position;
            var videoWidth : Number = _video.videoWidth;
            var videoHeight : Number = _video.videoHeight;
            if (videoWidth && videoHeight) {
                var changed : Boolean = _videoWidth != videoWidth || _videoHeight != videoHeight;
                if (changed) {
                    Log.info("video size changed to " + videoWidth + "/" + videoHeight);
                    _videoWidth = videoWidth;
                    _videoHeight = videoHeight;
                    _clip.originalWidth = videoWidth;
                    _clip.originalHeight = videoHeight;
                    _clip.dispatch(ClipEventType.START);
                    _clip.dispatch(ClipEventType.METADATA_CHANGED);
                }
            }
        };

        private function _stateHandler(event : HLSEvent) : void {
            _hlsState = event.state;
            // Log.txt("state:"+ _hlsState);
            switch(event.state) {
                case HLSStates.IDLE:
                case HLSStates.PLAYING:
                    _clip.dispatch(ClipEventType.BUFFER_FULL);
                    break;
                case HLSStates.PLAYING_BUFFERING:
                    _clip.dispatch(ClipEventType.BUFFER_EMPTY);
                    break;
                case HLSStates.PAUSED_BUFFERING:
                    _clip.dispatch(ClipEventType.BUFFER_EMPTY);
                    _clip.dispatch(ClipEventType.PAUSE);
                    break;
                case HLSStates.PAUSED:
                    _clip.dispatch(ClipEventType.BUFFER_FULL);
                    _clip.dispatch(ClipEventType.PAUSE);
                    break;
            }
        };

        /**
         * Starts loading the specivied clip. Once video data is available the provider
         * must set it to the clip using clip.setContent(). Typically the video
         * object passed to the clip is an instance of flash.media.Video.
         *
         * @param event the event that this provider should dispatch once loading has successfully started,
         * once dispatched the player will call getVideo()
         * @param clip the clip to load
         * @param pauseAfterStart if true the playback is paused on first frame and
         * buffering is continued
         * @see Clip#setContent()
         * @see #getVideo()
         */
        public function load(event : ClipEvent, clip : Clip, pauseAfterStart : Boolean = true) : void {
            _clip = clip;
            Log.info("load()" + clip.completeUrl);
            _hls.load(clip.completeUrl);
            _pauseAfterStart = pauseAfterStart;
            clip.type = ClipType.VIDEO;
            clip.dispatch(ClipEventType.BEGIN);
            return;
        }

        /**
         * Gets the Video object.
         * A stream will be attached to the returned video object using attachStream().
         * @param clip the clip for which the Video object is queried for
         * @see #attachStream()
         */
        public function getVideo(clip : Clip) : DisplayObject {
            Log.debug("getVideo()");
            if (clip.useStageVideo) {
                Log.debug("useStageVideo");
                _video = new StageVideoWrapper(clip);
            } else {
                _video = new Video();
                _video.smoothing = clip.smoothing;
            }
            return _video;
        }

        /**
         * Attaches a stream to the specified display object.
         * @param video the video object that was originally retrieved using getVideo().
         * @see #getVideo()
         */
        public function attachStream(video : DisplayObject) : void {
            Log.debug("attachStream()");
            Video(video).attachNetStream(_hls.stream);
            return;
        }

        /**
         * Pauses playback.
         * @param event the event that this provider should dispatch once loading has been successfully paused
         */
        public function pause(event : ClipEvent) : void {
            Log.info("pause()");
            _hls.stream.pause();
            return;
        }

        /**
         * Resumes playback.
         * @param event the event that this provider should dispatch once loading has been successfully resumed
         */
        public function resume(event : ClipEvent) : void {
            Log.info("resume()");
            _hls.stream.resume();
            _clip.dispatch(ClipEventType.RESUME);
            return;
        }

        /**
         * Stops and rewinds to the beginning of current clip.
         * @param event the event that this provider should dispatch once loading has been successfully stopped
         */
        public function stop(event : ClipEvent, closeStream : Boolean = false) : void {
            Log.info("stop()");
            _hls.stream.close();
            return;
        }

        /**
         * Seeks to the specified point in the timeline.
         * @param event the event that this provider should dispatch once the seek is in target
         * @param seconds the target point in the timeline
         */
        public function seek(event : ClipEvent, seconds : Number) : void {
            Log.info("seek()");
            _hls.stream.seek(seconds);
            _position = seconds;
            _bufferedTime = seconds;
            _clip.dispatch(ClipEventType.SEEK);
            return;
        }

        /**
         * File size in bytes.
         */
        public function get fileSize() : Number {
            return 0;
        }

        /**
         * Current playhead time in seconds.
         */
        public function get time() : Number {
            return _position;
        }

        /**
         * The point in timeline where the buffered data region begins, in seconds.
         */
        public function get bufferStart() : Number {
            return 0;
        }

        /**
         * The point in timeline where the buffered data region ends, in seconds.
         */
        public function get bufferEnd() : Number {
            return _bufferedTime;
        }

        /**
         * Does this provider support random seeking to unbuffered areas in the timeline?
         */
        public function get allowRandomSeek() : Boolean {
            // Log.info("allowRandomSeek()");
            return _seekable;
        }

        /**
         * Volume controller used to control the video volume.
         */
        public function set volumeController(controller : VolumeController) : void {
            _volumecontroller = controller;
            _volumecontroller.netStream = _hls.stream;
            return;
        }

        /**
         * Is this provider in the process of stopping the stream?
         * When stopped the provider should not dispatch any events resulting from events that
         * might get triggered by the underlying streaming implementation.
         */
        public function get stopping() : Boolean {
            Log.info("stopping()");
            return false;
        }

        /**
         * The playlist instance.
         */
        public function set playlist(playlist : Playlist) : void {
            // Log.debug("set playlist()");
            _playlist = playlist;
            return;
        }

        public function get playlist() : Playlist {
            Log.debug("get playlist()");
            return _playlist;
        }

        /**
         * Adds a callback public function to the NetConnection instance. This public function will fire ClipEvents whenever
         * the callback is invoked in the connection.
         * @param name
         * @param listener
         * @return
         * @see ClipEventType#CONNECTION_EVENT
         */
        public function addConnectionCallback(name : String, listener : Function) : void {
            Log.debug("addConnectionCallback()");
            return;
        }

        /**
         * Adds a callback public function to the NetStream object. This public function will fire a ClipEvent of type StreamEvent whenever
         * the callback has been invoked on the stream. The invokations typically come from a server-side app running
         * on RTMP server.
         * @param name
         * @param listener
         * @return
         * @see ClipEventType.NETSTREAM_EVENT
         */
        public function addStreamCallback(name : String, listener : Function) : void {
            Log.debug("addStreamCallback()");
            return;
        }

        /**
         * Get the current stream callbacks.
         * @return a dictionary of callbacks, keyed using callback names and values being the callback functions
         */
        public function get streamCallbacks() : Dictionary {
            Log.debug("get streamCallbacks()");
            return null;
        }

        /**
         * Gets the underlying NetStream object.
         * @return the netStream currently in use, or null if this provider has not started streaming yet
         */
        public function get netStream() : NetStream {
            Log.debug("get netStream()");
            return _hls.stream;
        }

        /**
         * Gets the underlying netConnection object.
         * @return the netConnection currently in use, or null if this provider has not started streaming yet
         */
        public function get netConnection() : NetConnection {
            Log.debug("get netConnection()");
            return null;
        }

        /**
         * Sets a time provider to be used by this StreamProvider. Normally the playhead time is queried from
         * the NetStream.time property.
         *
         * @param timeProvider
         */
        public function set timeProvider(timeProvider : TimeProvider) : void {
            Log.debug("set timeProvider()");
            _timeProvider = timeProvider;
            return;
        }

        /**
         * Gets the type of StreamProvider either http, rtmp, psuedo.
         */
        public function get type() : String {
            return "httpstreaming";
        }

        /**
         * Switch the stream in realtime with / without dynamic stream switching support
         *
         * @param event ClipEvent the clip event
         * @param clip Clip the clip to switch to
         * @param netStreamPlayOptions Object the NetStreamPlayOptions object to enable dynamic stream switching
         */
        public function switchStream(event : ClipEvent, clip : Clip, netStreamPlayOptions : Object = null) : void {
            Log.info("switchStream()");
            return;
        }
    }
}

package org.mangui.flowplayer {
    import flash.display.DisplayObject;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.utils.Dictionary;
    import flash.media.Video;

    import org.mangui.HLS.HLS;
    import org.mangui.HLS.HLSEvent;
    import org.mangui.HLS.HLSStates;
    import org.mangui.HLS.utils.Log;
    import org.flowplayer.model.Plugin;
    import org.flowplayer.model.PluginModel;
    import org.flowplayer.view.Flowplayer;
    import org.flowplayer.controller.StreamProvider;
    import org.flowplayer.controller.TimeProvider;
    import org.flowplayer.controller.VolumeController;
    import org.flowplayer.model.Clip;
    import org.flowplayer.model.ClipType;
    import org.flowplayer.model.ClipEvent;
    import org.flowplayer.model.ClipEventType;
    import org.flowplayer.model.Playlist;
    import org.flowplayer.view.StageVideoWrapper;

    public class HLSProvider  implements StreamProvider,Plugin {
        private var _volumecontroller : VolumeController;
        private var _playlist : Playlist;
        private var _timeProvider : TimeProvider;
        private var _model : PluginModel;
        private var _player : Flowplayer;
        private var _clip : Clip;
        private var _video : Video;
        /** reference to the framework. **/
        private var _hls : HLS;
        private var _hlsState : String = HLSStates.IDLE;
        // event values
        private var _position : Number = 0;
        private var _duration : Number = 0;
        private var _bufferedTime : Number = 0;
        private var _videoWidth : Number = -1;
        private var _videoHeight : Number = -1;
        private var _isManifestLoaded : Boolean = false;
        private var _pauseAfterStart : Boolean;
        private var _seekable : Boolean = false;

        public function getDefaultConfig() : Object {
            return null;
        }

        public function onConfig(model : PluginModel) : void {
            Log.info("onConfig()");
            _model = model;
        }

        public function onLoad(player : Flowplayer) : void {
            Log.info("onLoad()");
            _player = player;
            _hls = new HLS();
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _completeHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
            _hls.addEventListener(HLSEvent.STATE, _stateHandler);

            var cfg:Object = _model.config;
            var value : Object;

            // parse configuration parameters
            value = cfg.hls_debug;
            if (value != null) {
                Log.info("hls_debug:" + value);
                Log.LOG_DEBUG_ENABLED = value as Boolean;
            }

            value = cfg.hls_debug2;
            if (value != null) {
                Log.info("hls_debug2:" + value);
                Log.LOG_DEBUG2_ENABLED = value as Boolean;
            }

            value = cfg.hls_minbufferlength;
            if (value != null) {
                Log.info("hls_minbufferlength:" + value);
                _hls.minBufferLength = value as Number;
            }

            value = cfg.hls_maxbufferlength;
            if (value != null) {
                Log.info("hls_maxbufferlength:" + value);
                _hls.maxBufferLength = value as Number;
            }

            value = cfg.hls_startfromlowestlevel;
            if (value != null) {
                Log.info("hls_startfromlowestlevel:" + value);
                _hls.startFromLowestLevel = value as Boolean;
            }

            value = cfg.hls_seekfromlowestlevel;
            if (value != null) {
                Log.info("hls_seekfromlowestlevel:" + value);
                _hls.seekFromLowestLevel = value as Boolean;
            }

            value = cfg.hls_live_flushurlcache;
            if (value != null) {
                Log.info("hls_live_flushurlcache:" + value);
                _hls.flushLiveURLCache = value as Boolean;
            }
            _model.dispatchOnLoad();
        }

        private function _completeHandler(event : HLSEvent) : void {
            _clip.dispatch(ClipEventType.FINISH);
        };

        private function _errorHandler(event : HLSEvent) : void {
        };

        private function _manifestHandler(event : HLSEvent) : void {
            _duration = event.levels[0].duration;
            _isManifestLoaded = true;
            _clip.duration = _duration;
            _clip.dispatch(ClipEventType.METADATA);
            _seekable = true;
            //if (_hls.type == HLSTypes.LIVE) {
            //    _seekable = false;
            //} else {
            //    _seekable = true;
            //}
            if (_pauseAfterStart == false) {
                _hls.stream.play();
            }
        };

        private function _mediaTimeHandler(event : HLSEvent) : void {
            _position = Math.max(0, event.mediatime.position);
            _duration = event.mediatime.duration;
            _clip.duration = _duration;
            _bufferedTime = event.mediatime.buffer + event.mediatime.position;
            var videoWidth : Number = _video.videoWidth;
            var videoHeight : Number = _video.videoHeight;
            if (videoWidth && videoHeight) {
                var changed : Boolean = _videoWidth != videoWidth || _videoHeight != videoHeight;
                if (changed) {
                    Log.info("video size changed to " + videoWidth + "/" + videoHeight);
                    _videoWidth = videoWidth;
                    _videoHeight = videoHeight;
                    _clip.originalWidth = videoWidth;
                    _clip.originalHeight = videoHeight;
                    _clip.dispatch(ClipEventType.START);
                    _clip.dispatch(ClipEventType.METADATA_CHANGED);
                }
            }
        };

        private function _stateHandler(event : HLSEvent) : void {
            _hlsState = event.state;
            // Log.txt("state:"+ _hlsState);
            switch(event.state) {
                case HLSStates.IDLE:
                case HLSStates.PLAYING:
                    _clip.dispatch(ClipEventType.BUFFER_FULL);
                    break;
                case HLSStates.PLAYING_BUFFERING:
                    _clip.dispatch(ClipEventType.BUFFER_EMPTY);
                    break;
                case HLSStates.PAUSED_BUFFERING:
                    _clip.dispatch(ClipEventType.BUFFER_EMPTY);
                    _clip.dispatch(ClipEventType.PAUSE);
                    break;
                case HLSStates.PAUSED:
                    _clip.dispatch(ClipEventType.BUFFER_FULL);
                    _clip.dispatch(ClipEventType.PAUSE);
                    break;
            }
        };

        /**
         * Starts loading the specivied clip. Once video data is available the provider
         * must set it to the clip using clip.setContent(). Typically the video
         * object passed to the clip is an instance of flash.media.Video.
         *
         * @param event the event that this provider should dispatch once loading has successfully started,
         * once dispatched the player will call getVideo()
         * @param clip the clip to load
         * @param pauseAfterStart if true the playback is paused on first frame and
         * buffering is continued
         * @see Clip#setContent()
         * @see #getVideo()
         */
        public function load(event : ClipEvent, clip : Clip, pauseAfterStart : Boolean = true) : void {
            _clip = clip;
            Log.info("load()" + clip.completeUrl);
            _hls.load(clip.completeUrl);
            _pauseAfterStart = pauseAfterStart;
            clip.type = ClipType.VIDEO;
            clip.dispatch(ClipEventType.BEGIN);
            return;
        }

        /**
         * Gets the Video object.
         * A stream will be attached to the returned video object using attachStream().
         * @param clip the clip for which the Video object is queried for
         * @see #attachStream()
         */
        public function getVideo(clip : Clip) : DisplayObject {
            Log.debug("getVideo()");
            if (clip.useStageVideo) {
                Log.debug("useStageVideo");
                _video = new StageVideoWrapper(clip);
            } else {
                _video = new Video();
                _video.smoothing = clip.smoothing;
            }
            return _video;
        }

        /**
         * Attaches a stream to the specified display object.
         * @param video the video object that was originally retrieved using getVideo().
         * @see #getVideo()
         */
        public function attachStream(video : DisplayObject) : void {
            Log.debug("attachStream()");
            Video(video).attachNetStream(_hls.stream);
            return;
        }

        /**
         * Pauses playback.
         * @param event the event that this provider should dispatch once loading has been successfully paused
         */
        public function pause(event : ClipEvent) : void {
            Log.info("pause()");
            _hls.stream.pause();
            return;
        }

        /**
         * Resumes playback.
         * @param event the event that this provider should dispatch once loading has been successfully resumed
         */
        public function resume(event : ClipEvent) : void {
            Log.info("resume()");
            _hls.stream.resume();
            _clip.dispatch(ClipEventType.RESUME);
            return;
        }

        /**
         * Stops and rewinds to the beginning of current clip.
         * @param event the event that this provider should dispatch once loading has been successfully stopped
         */
        public function stop(event : ClipEvent, closeStream : Boolean = false) : void {
            Log.info("stop()");
            _hls.stream.close();
            return;
        }

        /**
         * Seeks to the specified point in the timeline.
         * @param event the event that this provider should dispatch once the seek is in target
         * @param seconds the target point in the timeline
         */
        public function seek(event : ClipEvent, seconds : Number) : void {
            Log.info("seek()");
            _hls.stream.seek(seconds);
            _position = seconds;
            _bufferedTime = seconds;
            _clip.dispatch(ClipEventType.SEEK);
            return;
        }

        /**
         * File size in bytes.
         */
        public function get fileSize() : Number {
            return 0;
        }

        /**
         * Current playhead time in seconds.
         */
        public function get time() : Number {
            return _position;
        }

        /**
         * The point in timeline where the buffered data region begins, in seconds.
         */
        public function get bufferStart() : Number {
            return 0;
        }

        /**
         * The point in timeline where the buffered data region ends, in seconds.
         */
        public function get bufferEnd() : Number {
            return _bufferedTime;
        }

        /**
         * Does this provider support random seeking to unbuffered areas in the timeline?
         */
        public function get allowRandomSeek() : Boolean {
            // Log.info("allowRandomSeek()");
            return _seekable;
        }

        /**
         * Volume controller used to control the video volume.
         */
        public function set volumeController(controller : VolumeController) : void {
            _volumecontroller = controller;
            _volumecontroller.netStream = _hls.stream;
            return;
        }

        /**
         * Is this provider in the process of stopping the stream?
         * When stopped the provider should not dispatch any events resulting from events that
         * might get triggered by the underlying streaming implementation.
         */
        public function get stopping() : Boolean {
            Log.info("stopping()");
            return false;
        }

        /**
         * The playlist instance.
         */
        public function set playlist(playlist : Playlist) : void {
            // Log.debug("set playlist()");
            _playlist = playlist;
            return;
        }

        public function get playlist() : Playlist {
            Log.debug("get playlist()");
            return _playlist;
        }

        /**
         * Adds a callback public function to the NetConnection instance. This public function will fire ClipEvents whenever
         * the callback is invoked in the connection.
         * @param name
         * @param listener
         * @return
         * @see ClipEventType#CONNECTION_EVENT
         */
        public function addConnectionCallback(name : String, listener : Function) : void {
            Log.debug("addConnectionCallback()");
            return;
        }

        /**
         * Adds a callback public function to the NetStream object. This public function will fire a ClipEvent of type StreamEvent whenever
         * the callback has been invoked on the stream. The invokations typically come from a server-side app running
         * on RTMP server.
         * @param name
         * @param listener
         * @return
         * @see ClipEventType.NETSTREAM_EVENT
         */
        public function addStreamCallback(name : String, listener : Function) : void {
            Log.debug("addStreamCallback()");
            return;
        }

        /**
         * Get the current stream callbacks.
         * @return a dictionary of callbacks, keyed using callback names and values being the callback functions
         */
        public function get streamCallbacks() : Dictionary {
            Log.debug("get streamCallbacks()");
            return null;
        }

        /**
         * Gets the underlying NetStream object.
         * @return the netStream currently in use, or null if this provider has not started streaming yet
         */
        public function get netStream() : NetStream {
            Log.debug("get netStream()");
            return _hls.stream;
        }

        /**
         * Gets the underlying netConnection object.
         * @return the netConnection currently in use, or null if this provider has not started streaming yet
         */
        public function get netConnection() : NetConnection {
            Log.debug("get netConnection()");
            return null;
        }

        /**
         * Sets a time provider to be used by this StreamProvider. Normally the playhead time is queried from
         * the NetStream.time property.
         *
         * @param timeProvider
         */
        public function set timeProvider(timeProvider : TimeProvider) : void {
            Log.debug("set timeProvider()");
            _timeProvider = timeProvider;
            return;
        }

        /**
         * Gets the type of StreamProvider either http, rtmp, psuedo.
         */
        public function get type() : String {
            return "httpstreaming";
        }

        /**
         * Switch the stream in realtime with / without dynamic stream switching support
         *
         * @param event ClipEvent the clip event
         * @param clip Clip the clip to switch to
         * @param netStreamPlayOptions Object the NetStreamPlayOptions object to enable dynamic stream switching
         */
        public function switchStream(event : ClipEvent, clip : Clip, netStreamPlayOptions : Object = null) : void {
            Log.info("switchStream()");
            return;
        }
    }
}

remove unused import

remove unused import


package laml.display {
	import flash.display.DisplayObject;
	import flash.display.Sprite;

	public class Skin extends Sprite implements ISkin {

		public function getBitmapByName(alias:String):DisplayObject {
			if(hasOwnProperty(alias)) {
				return new this[alias]() as DisplayObject;
			}

			return null;
		}
	}
}

package laml.display {
	import flash.display.DisplayObject;
	import flash.display.Sprite;

	import mx.core.BitmapAsset;
	import mx.core.FontAsset;
	import mx.core.IFlexAsset;
	import mx.core.IFlexDisplayObject;
	import mx.core.SpriteAsset;

	public class Skin extends Sprite implements ISkin {
 		private var bitmapAsset:BitmapAsset;
		private var fontAsset:FontAsset;
		private var iFlexAsset:IFlexAsset;
		private var iFlexDisplayObject:IFlexDisplayObject;
		private var spriteAsset:SpriteAsset;

		public function getBitmapByName(alias:String):DisplayObject {
			if(hasOwnProperty(alias)) {
				return new this[alias]() as DisplayObject;
			}

			return null;
		}
	}
}

Put MX dependencies in skin

Put MX dependencies in skin

git-svn-id: 02605e11d3a461bc7e12f3f3880edf4b0c8dcfd0@4281 3e7533ad-8678-4d30-8e38-00a379d3f0d0


/**
  * LzMouseKernel.as
  *
  * @copyright Copyright 2001-2008 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 (LzMouseKernel.__callback) LzMouseKernel.__scope[LzMouseKernel.__callback](eventname, view);
        //Debug.write('LzMouseKernel event', eventname);
    }
    static var __callback = null;
    static var __scope = null;
    static var __lastMouseDown = null;
    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) {
        LzMouseKernel.__scope = scope;
        LzMouseKernel.__callback = funcname;
        if (LzMouseKernel.__listeneradded == false) {
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE,     __mouseLeavesHandler);
            LzMouseKernel.__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
            LzMouseKernel.__lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            LzMouseKernel.__sendEvent(null, eventname);
        }
    }

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

    /**
    * 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) {
        LzMouseKernel.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 ( LzMouseKernel.__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 ( LzMouseKernel.__amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        globalCursorResource = null;
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal ( ){
        if ( LzMouseKernel.__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 (){
        LzMouseKernel.__amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock (){
        LzMouseKernel.__amLocked = false;
        LzMouseKernel.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 (LzMouseKernel.__callback) LzMouseKernel.__scope[LzMouseKernel.__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) {
        LzMouseKernel.__scope = scope;
        LzMouseKernel.__callback = funcname;
        if (LzMouseKernel.__listeneradded == false) {
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE,     __mouseLeavesHandler);
            LzMouseKernel.__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
            LzMouseKernel.__lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            if (__mouseLeft && event.buttonDown && LzMouseKernel.__lastMouseDown != null) {
                __mouseLeft = false;
                //Debug.write(eventname, event.buttonDown, LzMouseKernel.__lastMouseDown);
                var ev = new MouseEvent('mouseup');
                LzMouseKernel.__lastMouseDown.__globalmouseup(ev);
                LzMouseKernel.__lastMouseDown = null;
            }
            LzMouseKernel.__sendEvent(null, eventname);
        }
    }

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

    /**
    * 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) {
        LzMouseKernel.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 ( LzMouseKernel.__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 ( LzMouseKernel.__amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        globalCursorResource = null;
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal ( ){
        if ( LzMouseKernel.__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 (){
        LzMouseKernel.__amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock (){
        LzMouseKernel.__amLocked = false;
        LzMouseKernel.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 20090206-maxcarlson-y by maxcarlson@Bank.local on 2009-02-06 19:03:11 PST
    in /Users/maxcarlson/openlaszlo/trunk-clean
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: Send mouseup events when mouse goes up outside the app in swf9

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

Technical Reviewer: andre.bargull@udo.edu
QA Reviewer: hminsky

Details: Add mouseLeft flag to track whether the mouse left the app.  Send a mouseup event if the mouse left the app, the button is still down and there is a view to receive the event (lastMouseDown)

Tests: See LPP-7724.

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


package goplayer
{
  public class Player
    implements FlashNetConnectionListener, FlashNetStreamListener
  {
    private const DEFAULT_VOLUME : Number = .8

    private const START_BUFFER : Duration = Duration.seconds(.1)
    private const SMALL_BUFFER : Duration = Duration.seconds(3)
    private const LARGE_BUFFER : Duration = Duration.seconds(60)

    private const finishingListeners : Array = []

    private var _movie : Movie
    private var connection : FlashNetConnection

    private var triedConnectingUsingDefaultPorts : Boolean = false
    private var stream : FlashNetStream = null
    private var metadata : Object = null

    private var _volume : Number = DEFAULT_VOLUME
    private var _paused : Boolean = false
    private var _buffering : Boolean = false

    public function Player
      (movie : Movie, connection : FlashNetConnection)
    {
      _movie = movie, this.connection = connection

      connection.listener = this
    }

    public function get movie() : Movie
    { return _movie }

    public function addFinishingListener
      (value : PlayerFinishingListener) : void
    { finishingListeners.push(value) }

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

    public function start() : void
    { connection.connect(movie.rtmpURL) }

    public function handleConnectionFailed() : void
    {
      if (movie.rtmpURL.hasPort && !triedConnectingUsingDefaultPorts)
        connectUsingDefaultPorts()
      else
        debug("Could not connect using RTMP; giving up.")
    }

    private function connectUsingDefaultPorts() : void
    {
      debug("Trying to connect using default RTMP ports.")

      triedConnectingUsingDefaultPorts = true
      connection.connect(movie.rtmpURL.withoutPort)
    }

    public function handleConnectionClosed() : void
    {}

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

    public function handleConnectionEstablished() : void
    {
      stream = connection.getNetStream()
      stream.listener = this

      stream.bufferTime = START_BUFFER
      stream.volume = volume

      stream.play(movie.rtmpStreams)

      _buffering = true

      if (paused)
        stream.paused = true
    }

    public function handleNetStreamMetadata(data : Object) : void
    { metadata = data }

    public function handleBufferFilled() : void
    { handleBufferingFinished() }

    private function handleBufferingFinished() : void
    {
      useLargeBuffer()
      _buffering = false
    }

    public function handleBufferEmptied() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
      else
        handleBufferingStarted()
    }

    private function handleBufferingStarted() : void
    {
      useSmallBuffer()
      _buffering = true
    }

    private function useSmallBuffer() : void
    { stream.bufferTime = SMALL_BUFFER }

    private function useLargeBuffer() : void
    { stream.bufferTime = LARGE_BUFFER }

    public function get buffering() : Boolean
    { return _buffering }

    public function handleStreamingStopped() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
    }

    private function handleFinishedPlaying() : void
    {
      debug("Finished playing.")

      for each (var listener : PlayerFinishingListener in finishingListeners)
        listener.handleMovieFinishedPlaying()
    }

    private function get finishedPlaying() : Boolean
    { return timeRemaining.seconds < 1 }

    private function get timeRemaining() : Duration
    { return streamLength.minus(playheadPosition) }

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

    public function get paused() : Boolean
    { return _paused }

    public function set paused(value : Boolean) : void
    {
      _paused = value

      if (stream)
        stream.paused = value
    }

    public function togglePaused() : void
    { paused = !paused }

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

    public function get playheadPosition() : Duration
    { return stream ? stream.playheadPosition : Duration.ZERO }

    public function set playheadPosition(value : Duration) : void
    { stream.playheadPosition = value }

    public function seekBy(delta : Duration) : void
    { playheadPosition = playheadPosition.plus(delta) }

    public function rewind() : void
    { playheadPosition = Duration.ZERO }

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

    public function get volume() : Number
    { return _volume }

    public function set volume(value : Number) : void
    {
      _volume = value

      if (stream)
        stream.volume = value
    }

    public function changeVolumeBy(delta : Number) : void
    { volume = volume + delta }

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

    public function get aspectRatio() : Number
    { return movie.aspectRatio }

    public function get bufferTime() : Duration
    { return stream ? stream.bufferTime : Duration.ZERO }

    public function get bufferLength() : Duration
    { return stream ? stream.bufferLength : Duration.ZERO }

    public function get bufferFillRatio() : Number
    { return Math.min(1, bufferLength.seconds / bufferTime.seconds) }

    public function get streamLength() : Duration
    {
      return metadata
        ? Duration.seconds(metadata.duration)
        : movie.duration
    }

    public function get bitrate() : Bitrate
    { return stream ? stream.bitrate : Bitrate.ZERO }

    public function get bandwidth() : Bitrate
    { return stream ? stream.bandwidth : Bitrate.ZERO }

    public function get highQualityDimensions() : Dimensions
    {
      var result : Dimensions = Dimensions.ZERO

      for each (var stream : RTMPStream in movie.rtmpStreams)
        if (stream.dimensions.isGreaterThan(result))
          result = stream.dimensions

      return result
    }
  }
}

package goplayer
{
  public class Player
    implements FlashNetConnectionListener, FlashNetStreamListener
  {
    private const DEFAULT_VOLUME : Number = .8

    private const START_BUFFER : Duration = Duration.seconds(.1)
    private const SMALL_BUFFER : Duration = Duration.seconds(3)
    private const LARGE_BUFFER : Duration = Duration.seconds(60)

    private const finishingListeners : Array = []

    private var _movie : Movie
    private var connection : FlashNetConnection

    private var triedConnectingUsingDefaultPorts : Boolean = false
    private var stream : FlashNetStream = null
    private var metadata : Object = null

    private var _volume : Number = DEFAULT_VOLUME
    private var _paused : Boolean = false
    private var _buffering : Boolean = false

    public function Player
      (movie : Movie, connection : FlashNetConnection)
    {
      _movie = movie, this.connection = connection

      connection.listener = this
    }

    public function get movie() : Movie
    { return _movie }

    public function addFinishingListener
      (value : PlayerFinishingListener) : void
    { finishingListeners.push(value) }

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

    public function start() : void
    { connection.connect(movie.rtmpURL) }

    public function handleConnectionFailed() : void
    {
      if (movie.rtmpURL.hasPort && !triedConnectingUsingDefaultPorts)
        connectUsingDefaultPorts()
      else
        debug("Could not connect using RTMP; giving up.")
    }

    private function connectUsingDefaultPorts() : void
    {
      debug("Trying to connect using default RTMP ports.")

      triedConnectingUsingDefaultPorts = true
      connection.connect(movie.rtmpURL.withoutPort)
    }

    public function handleConnectionClosed() : void
    {}

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

    public function handleConnectionEstablished() : void
    {
      stream = connection.getNetStream()
      stream.listener = this

      stream.bufferTime = START_BUFFER
      stream.volume = volume

      stream.play(movie.rtmpStreams)

      _buffering = true

      if (paused)
        stream.paused = true
    }

    public function handleNetStreamMetadata(data : Object) : void
    { metadata = data }

    public function handleBufferFilled() : void
    { handleBufferingFinished() }

    private function handleBufferingFinished() : void
    {
      _buffering = false

      // Make sure to let playback resume first.
      later(useLargeBuffer)
    }

    public function handleBufferEmptied() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
      else
        handleBufferingStarted()
    }

    private function handleBufferingStarted() : void
    {
      useSmallBuffer()
      _buffering = true
    }

    private function useStartBuffer() : void
    { stream.bufferTime = START_BUFFER }

    private function useSmallBuffer() : void
    { stream.bufferTime = SMALL_BUFFER }

    private function useLargeBuffer() : void
    { stream.bufferTime = LARGE_BUFFER }

    public function get buffering() : Boolean
    { return _buffering }

    public function handleStreamingStopped() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
    }

    private function handleFinishedPlaying() : void
    {
      debug("Finished playing.")

      for each (var listener : PlayerFinishingListener in finishingListeners)
        listener.handleMovieFinishedPlaying()
    }

    private function get finishedPlaying() : Boolean
    { return timeRemaining.seconds < 1 }

    private function get timeRemaining() : Duration
    { return streamLength.minus(playheadPosition) }

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

    public function get paused() : Boolean
    { return _paused }

    public function set paused(value : Boolean) : void
    {
      _paused = value

      if (stream)
        stream.paused = value
    }

    public function togglePaused() : void
    { paused = !paused }

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

    public function get playheadPosition() : Duration
    { return stream ? stream.playheadPosition : Duration.ZERO }

    public function set playheadPosition(value : Duration) : void
    {
      useStartBuffer()
      stream.playheadPosition = value
    }

    public function seekBy(delta : Duration) : void
    { playheadPosition = playheadPosition.plus(delta) }

    public function rewind() : void
    { playheadPosition = Duration.ZERO }

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

    public function get volume() : Number
    { return _volume }

    public function set volume(value : Number) : void
    {
      _volume = value

      if (stream)
        stream.volume = value
    }

    public function changeVolumeBy(delta : Number) : void
    { volume = volume + delta }

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

    public function get aspectRatio() : Number
    { return movie.aspectRatio }

    public function get bufferTime() : Duration
    { return stream ? stream.bufferTime : Duration.ZERO }

    public function get bufferLength() : Duration
    { return stream ? stream.bufferLength : Duration.ZERO }

    public function get bufferFillRatio() : Number
    { return Math.min(1, bufferLength.seconds / bufferTime.seconds) }

    public function get streamLength() : Duration
    {
      return metadata
        ? Duration.seconds(metadata.duration)
        : movie.duration
    }

    public function get bitrate() : Bitrate
    { return stream ? stream.bitrate : Bitrate.ZERO }

    public function get bandwidth() : Bitrate
    { return stream ? stream.bandwidth : Bitrate.ZERO }

    public function get highQualityDimensions() : Dimensions
    {
      var result : Dimensions = Dimensions.ZERO

      for each (var stream : RTMPStream in movie.rtmpStreams)
        if (stream.dimensions.isGreaterThan(result))
          result = stream.dimensions

      return result
    }
  }
}

Tweak buffering strategy.

Tweak buffering strategy.


package aerys.minko.scene.node.camera
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.data.CameraDataProvider;
	import aerys.minko.scene.node.AbstractSceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Ray;
	import aerys.minko.type.math.Vector4;

	import flash.geom.Point;

	use namespace minko_scene;

	public class Camera extends AbstractCamera
	{
		public static const DEFAULT_FOV		: Number	= Math.PI * .25;

		private var _tmpVector:Vector4 = new Vector4();

		public function get fieldOfView() : Number
		{
			return _cameraData.fieldOfView;
		}
		public function set fieldOfView(value : Number) : void
		{
			_cameraData.fieldOfView = value;
		}

		public function Camera(fieldOfView	: Number = DEFAULT_FOV,
							   zNear		: Number = AbstractCamera.DEFAULT_ZNEAR,
							   zFar			: Number = AbstractCamera.DEFAULT_ZFAR)
		{
			super(zNear, zFar);

			_cameraData.fieldOfView = fieldOfView;
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Camera = new Camera(fieldOfView, zNear, zFar);

			clone.transform.copyFrom(this.transform);

			return clone as AbstractSceneNode;
		}

		override public function unproject(x : Number, y : Number, out : Ray = null) : Ray
		{
			if (!(root is Scene))
				throw new Error('Camera must be in the scene to unproject vectors.');

			out ||= new Ray();

			var sceneBindings	: DataBindings	= (root as Scene).bindings;
			var zNear			: Number		= _cameraData.zNear;
			var zFar			: Number		= _cameraData.zFar;
			var fovDiv2			: Number		= _cameraData.fieldOfView * 0.5;
			var width			: Number		= sceneBindings.getProperty('viewportWidth');
			var height			: Number		= sceneBindings.getProperty('viewportHeight');
			var xPercent		: Number		= 2.0 * (x / width - 0.5);
			var yPercent 		: Number		= 2.0 * (y / height - 0.5);
			var dx				: Number 		= Math.tan(fovDiv2) * xPercent * (width / height);
			var dy				: Number 		= -Math.tan(fovDiv2) * yPercent;

			out.origin.set(dx * zNear, dy * zNear, zNear);
			out.direction.set(dx * zNear, dy * zNear, zNear).normalize();

			localToWorld(out.origin, out.origin);
			localToWorld(out.direction, out.direction, true);

			return out;
		}

		override public function project(localToWorld : Matrix4x4, output : Point = null) : Point
		{
			output ||= new Point();

			var sceneBindings	: DataBindings	= (root as Scene).bindings;
			var width			: Number		= sceneBindings.getProperty('viewportWidth');
			var height			: Number		= sceneBindings.getProperty('viewportHeight');
			var translation		: Vector4		= localToWorld.getTranslation();
			var screenPosition	: Vector4		= _cameraData.worldToScreen.projectVector(translation, _tmpVector);
			output.x 							= width * ((screenPosition.x + 1.0) * .5);
			output.y							= height * ((1.0 - ((screenPosition.y + 1.0) * .5)));

			return output;
		}

		public function projectPoint(point : Vector4, output : Point = null) : Point
		{
			output ||= new Point();

			var sceneBindings	: DataBindings	= (root as Scene).bindings;
			var width			: Number		= sceneBindings.getProperty('viewportWidth');
			var height			: Number		= sceneBindings.getProperty('viewportHeight');
			var screenPosition	: Vector4		= _cameraData.worldToScreen.projectVector(point, _tmpVector);
			output.x 							= width * ((screenPosition.x + 1.0) * .5);
			output.y							= height * ((1.0 - ((screenPosition.y + 1.0) * .5)));

			return output;
		}
	}
}

package aerys.minko.scene.node.camera
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.data.CameraDataProvider;
	import aerys.minko.scene.node.AbstractSceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Ray;
	import aerys.minko.type.math.Vector4;

	import flash.geom.Point;

	use namespace minko_scene;

	public class Camera extends AbstractCamera
	{
		public static const DEFAULT_FOV		: Number	= Math.PI * .25;

        private static const TMP_VECTOR4    : Vector4   = new Vector4();

		public function get fieldOfView() : Number
		{
			return _cameraData.fieldOfView;
		}
		public function set fieldOfView(value : Number) : void
		{
			_cameraData.fieldOfView = value;
		}

		public function Camera(fieldOfView	: Number = DEFAULT_FOV,
							   zNear		: Number = AbstractCamera.DEFAULT_ZNEAR,
							   zFar			: Number = AbstractCamera.DEFAULT_ZFAR)
		{
			super(zNear, zFar);

			_cameraData.fieldOfView = fieldOfView;
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Camera = new Camera(fieldOfView, zNear, zFar);

			clone.transform.copyFrom(this.transform);

			return clone as AbstractSceneNode;
		}

		override public function unproject(x : Number, y : Number, out : Ray = null) : Ray
		{
			if (!(root is Scene))
				throw new Error('Camera must be in the scene to unproject vectors.');

			out ||= new Ray();

			var sceneBindings	: DataBindings	= (root as Scene).bindings;
			var zNear			: Number		= _cameraData.zNear;
			var zFar			: Number		= _cameraData.zFar;
			var fovDiv2			: Number		= _cameraData.fieldOfView * 0.5;
			var width			: Number		= sceneBindings.getProperty('viewportWidth');
			var height			: Number		= sceneBindings.getProperty('viewportHeight');
			var xPercent		: Number		= 2.0 * (x / width - 0.5);
			var yPercent 		: Number		= 2.0 * (y / height - 0.5);
			var dx				: Number 		= Math.tan(fovDiv2) * xPercent * (width / height);
			var dy				: Number 		= -Math.tan(fovDiv2) * yPercent;

			out.origin.set(dx * zNear, dy * zNear, zNear);
			out.direction.set(dx * zNear, dy * zNear, zNear).normalize();

			localToWorld(out.origin, out.origin);
			localToWorld(out.direction, out.direction, true);

			return out;
		}

		override public function project(localToWorld : Matrix4x4, output : Point = null) : Point
		{
			output ||= new Point();

			var sceneBindings	: DataBindings	= (root as Scene).bindings;
			var width			: Number		= sceneBindings.getProperty('viewportWidth');
			var height			: Number		= sceneBindings.getProperty('viewportHeight');
			var translation		: Vector4		= localToWorld.getTranslation();
			var screenPosition	: Vector4		= _cameraData.worldToScreen.projectVector(
                translation, TMP_VECTOR4
            );

			output.x = width * ((screenPosition.x + 1.0) * .5);
			output.y = height * ((1.0 - ((screenPosition.y + 1.0) * .5)));

			return output;
		}
	}
}

remove duplicate definition of Camera.project() and set the tmp Vector4 as a private static const


package krewfw.builtin_actor.system {

    import krewfw.core.KrewActor;

    /**
     * Base class for event-driven command player.
     */
    //------------------------------------------------------------
    public class KrewScenarioPlayer extends KrewActor {

        private var _eventList:Array;
        private var _eventsByTrigger:Object;

        private var _triggers:Object = {};
        private var _methods:Object  = {};

        //------------------------------------------------------------
        public function KrewScenarioPlayer() {
            displayable = false;
        }

        protected override function onDispose():void {
            _eventList       = null;
            _eventsByTrigger = null;
            _triggers        = null;
            _methods         = null;
        }

        //------------------------------------------------------------
        // public
        //------------------------------------------------------------

        /**
         * @params eventList Example:
         *

         * [
         *     {
         *         "trigger_key" : "start_turn",
         *         "trigger_params": {"turn": 1},
         *         "method": "dialog",
         *         "params": {
         *             "messages": [ ... ]
         *         },
         *         "next": [
         *             {
         *                 "method": "overlay",
         *                 "params": { ... }
         *             }
         *         ]
         *     },
         *     ...
         * ]
         *

         */
        public function setData(eventList:Array):void {
            _eventList = eventList;

            _eventsByTrigger = {};
            for each (var eventData:Object in _eventList) {
                var triggerKey:String = eventData.trigger_key;
                if (!triggerKey) { continue; }

                if (!_eventsByTrigger[triggerKey]) {
                    _eventsByTrigger[triggerKey] = [];
                }
                _eventsByTrigger[triggerKey].push(eventData);
            }
        }

        /**
         * @param checker Schema: function(eventArgs;Object, triggerParams:Object):Boolean
         */
        public function addTrigger(triggerKey:String, eventName:String, checker:Function):void {
            _triggers[triggerKey] = new TriggerInfo(eventName, checker);
        }

        /**
         * @param triggerInfoList Example:
         *

         * [
         *     ["triggerKey", "eventName", checkerFunc],
         *     ...
         * ]
         *

         */
        public function addTriggers(triggerInfoList:Array):void {
            for each (var info:Array in triggerInfoList) {
                addTrigger(info[0], info[1], info[2]);
            }
        }

        /**
         * @param handler Schema: function(params:Object, onComplete:Function):void
         */
        public function addMethod(methodName:String, handler:Function):void {
            _methods[methodName] = handler;
        }

        /**
         * @param triggerInfoList Example:
         *

         * [
         *     ["methodName", methodFunc],
         *     ...
         * ]
         *

         */
        public function addMethods(methodList:Array):void {
            for each (var info:Array in methodList) {
                addMethod(info[0], info[1]);
            }
        }

        /**
         * Activate player.
         * Please call this after setData(), addTriggers(), addMethods(), and init().
         */
        public function activate():void {
            for (var triggerKey:String in _eventsByTrigger) {
                _listenEvent(triggerKey);
            }
        }

        //------------------------------------------------------------
        // private
        //------------------------------------------------------------

        private function _listenEvent(triggerKey:String):void {
            var info:TriggerInfo = _triggers[triggerKey];
            if (!info) {
                throw new Error('[KrewEventPlayer] trigger not registered: ' + triggerKey);
                return;
            }

            listen(info.eventName, function(eventArgs:Object):void {
                for each (var eventData:Object in _eventsByTrigger[triggerKey]) {

                    var triggerParams:Object = eventData.trigger_params;
                    if (!info.checker(eventArgs, triggerParams)) { continue; }

                    _callMethod(eventData);
                }
            });
        }

        private function _callMethod(eventData:Object):void {
            var methodName:String = eventData.method;
            if (!methodName) {
                throw new Error('[KrewEventPlayer] method name not found. (trigger: '
                                + eventData.trigger_key + ')');
                return;
            }

            var method:Function = _methods[methodName];
            if (method == null) {
                throw new Error('[KrewEventPlayer] method not registered: ' + methodName);
                return;
            }

            method(eventData.params, function():void {
                _callNextMethod(eventData);
            });
        }

        private function _callNextMethod(parentEventData:Object):void {
            if (!parentEventData.next) { return; }

            for each (var nextEventData:Object in parentEventData.next) {
                _callMethod(nextEventData);
            }
        }

    }
}

class TriggerInfo {

    public var eventName:String;
    public var checker:Function;

    public function TriggerInfo(eventName:String, checker:Function) {
        this.eventName = eventName;
        this.checker   = checker;
    }

}

package krewfw.builtin_actor.system {

    import krewfw.core.KrewActor;

    /**
     * Base class for event-driven command player.
     */
    //------------------------------------------------------------
    public class KrewScenarioPlayer extends KrewActor {

        // You can customize object keys.
        public var TRIGGER_KEY    :String = "trigger_key";
        public var TRIGGER_PARAMS :String = "trigger_params";
        public var METHOD         :String = "method";
        public var PARAMS         :String = "params";
        public var NEXT           :String = "next";

        private var _eventList:Array;
        private var _eventsByTrigger:Object;

        private var _triggers:Object = {};
        private var _methods:Object  = {};

        //------------------------------------------------------------
        public function KrewScenarioPlayer() {
            displayable = false;
        }

        protected override function onDispose():void {
            _eventList       = null;
            _eventsByTrigger = null;
            _triggers        = null;
            _methods         = null;
        }

        //------------------------------------------------------------
        // public
        //------------------------------------------------------------

        /**
         * @params eventList Example:
         *

         * [
         *     {
         *         "trigger_key" : "start_turn",
         *         "trigger_params": {"turn": 1},
         *         "method": "dialog",
         *         "params": {
         *             "messages": [ ... ]
         *         },
         *         "next": [
         *             {
         *                 "method": "overlay",
         *                 "params": { ... }
         *             }
         *         ]
         *     },
         *     ...
         * ]
         *

         */
        public function setData(eventList:Array):void {
            _eventList = eventList;

            _eventsByTrigger = {};
            for each (var eventData:Object in _eventList) {
                var triggerKey:String = eventData[TRIGGER_KEY];
                if (!triggerKey) { continue; }

                if (!_eventsByTrigger[triggerKey]) {
                    _eventsByTrigger[triggerKey] = [];
                }
                _eventsByTrigger[triggerKey].push(eventData);
            }
        }

        /**
         * @param checker Schema: function(eventArgs;Object, triggerParams:Object):Boolean
         */
        public function addTrigger(triggerKey:String, eventName:String, checker:Function):void {
            _triggers[triggerKey] = new TriggerInfo(eventName, checker);
        }

        /**
         * @param triggerInfoList Example:
         *

         * [
         *     ["triggerKey", "eventName", checkerFunc],
         *     ...
         * ]
         *

         */
        public function addTriggers(triggerInfoList:Array):void {
            for each (var info:Array in triggerInfoList) {
                addTrigger(info[0], info[1], info[2]);
            }
        }

        /**
         * @param handler Schema: function(params:Object, onComplete:Function):void
         */
        public function addMethod(methodName:String, handler:Function):void {
            _methods[methodName] = handler;
        }

        /**
         * @param triggerInfoList Example:
         *

         * [
         *     ["methodName", methodFunc],
         *     ...
         * ]
         *

         */
        public function addMethods(methodList:Array):void {
            for each (var info:Array in methodList) {
                addMethod(info[0], info[1]);
            }
        }

        /**
         * Activate player.
         * Please call this after setData(), addTriggers(), addMethods(), and init().
         */
        public function activate():void {
            for (var triggerKey:String in _eventsByTrigger) {
                _listenEvent(triggerKey);
            }
        }

        //------------------------------------------------------------
        // private
        //------------------------------------------------------------

        private function _listenEvent(triggerKey:String):void {
            var info:TriggerInfo = _triggers[triggerKey];
            if (!info) {
                throw new Error('[KrewEventPlayer] trigger not registered: ' + triggerKey);
                return;
            }

            listen(info.eventName, function(eventArgs:Object):void {
                for each (var eventData:Object in _eventsByTrigger[triggerKey]) {

                    var triggerParams:Object = eventData[TRIGGER_PARAMS];
                    if (!info.checker(eventArgs, triggerParams)) { continue; }

                    _callMethod(eventData);
                }
            });
        }

        private function _callMethod(eventData:Object):void {
            var methodName:String = eventData[METHOD];
            if (!methodName) {
                throw new Error('[KrewEventPlayer] method name not found. (trigger: '
                                + eventData[TRIGGER_KEY] + ')');
                return;
            }

            var method:Function = _methods[methodName];
            if (method == null) {
                throw new Error('[KrewEventPlayer] method not registered: ' + methodName);
                return;
            }

            method(eventData[PARAMS], function():void {
                _callNextMethod(eventData);
            });
        }

        private function _callNextMethod(parentEventData:Object):void {
            if (!parentEventData.next) { return; }

            for each (var nextEventData:Object in parentEventData.next) {
                _callMethod(nextEventData);
            }
        }

    }
}

class TriggerInfo {

    public var eventName:String;
    public var checker:Function;

    public function TriggerInfo(eventName:String, checker:Function) {
        this.eventName = eventName;
        this.checker   = checker;
    }

}

Modify KrewScenarioPlayer

Modify KrewScenarioPlayer


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

	public final class CameraController extends AbstractController
	{
		use namespace minko_scene;

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

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

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

		public function CameraController()
		{
			super(AbstractCamera);

			initialize();
		}

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

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

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

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

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

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

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

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

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

			_data.changed.add(cameraPropertyChangedHandler);

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

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

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

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

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

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

			_data.changed.remove(cameraPropertyChangedHandler);

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

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

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

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

			worldToView
				.copyFrom(localToWorld)
				.invert()

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

			cameraData.frustum.updateFromMatrix(worldToScreen);

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

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

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

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

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

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

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

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

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

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

				cameraData.frustum.updateFromMatrix(worldToScreen);

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

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

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

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

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

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

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

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

	public final class CameraController extends AbstractController
	{
		use namespace minko_scene;

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

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

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

		public function CameraController()
		{
			super(AbstractCamera);

			initialize();
		}

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

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

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

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

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

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

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

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

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

			_data.changed.add(cameraPropertyChangedHandler);

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

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

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

			if (!scene)
				return;

			var sceneBindings : DataBindings = scene.bindings;

			resetSceneCamera(scene);

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

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

			_data.changed.remove(cameraPropertyChangedHandler);

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

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

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

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

			worldToView
				.copyFrom(localToWorld)
				.invert();

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

			cameraData.frustum.updateFromMatrix(worldToScreen);

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

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

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

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

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

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

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

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

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

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

				cameraData.frustum.updateFromMatrix(worldToScreen);

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

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

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

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

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

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

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

fix wrong update of worldToScreen matrix when the viewport is resized


package flails.resource {
  import mx.core.IMXMLObject;
  import flash.utils.Dictionary;

  public class Resources implements IMXMLObject {
    [DefaultProperty("resources")]

    private static var _resources:Dictionary = new Dictionary();

    public static function getResource(name:String):Resource {
      return _resources[name] as Resource;
    }

    public function set resources(rs:Array):void {
      for each (var r:Resource in rs) {
        _resources[r.name] = r;
      }
    }

    public function initialized(parent:Object, id:String):void {
    }
  }
}

package flails.resource {
  import mx.core.IMXMLObject;
  import flash.utils.Dictionary;

  [DefaultProperty("resources")]
  public class Resources implements IMXMLObject {
    private static var _resources:Dictionary = new Dictionary();

    public static function getResource(name:String):Resource {
      return _resources[name] as Resource;
    }

    public function set resources(rs:Array):void {
      for each (var r:Resource in rs) {
        _resources[r.name] = r;
      }
    }

    public function initialized(parent:Object, id:String):void {
    }
  }
}

Correct DefaultPropery placement.

Correct DefaultPropery placement.


package server {
	import flash.events.EventDispatcher;

	dynamic public class FMSClient extends EventDispatcher {
		public function FMSClient() {
			super();
		}

		public function onRegisterUser(user:String, result:Object):void {
			trace('FMSClient.onRegisterUser:', user, result);
		}

		public function onSigninUser(user:String, result:Object):void {
			trace('FMSClient.onSigninUser:', user, result);
		}

		public function onStartGame(user:String):void {
			trace('FMSClient.onStartGame:', user);
		}

		public function onChangePlayer(user:String, player:uint):void {
			trace('FMSClient.onChangePlayer:', user, player);
		}

		public function onUpdateScore(user:String, score:uint):void {
			trace('FMSClient.onUpdateScore:', user, score);
		}

		public function onDisconnect(user:String):void {
			trace('FMSClient.onDisconnect:', user);
		}
	}
}

package server {
	import flash.events.EventDispatcher;

	dynamic public class FMSClient extends EventDispatcher {
		public function FMSClient() {
			super();
		}

		public function onRegisterUser(user:String, result:Object):void {
			trace('FMSClient.onRegisterUser:', user, result);
		}

		public function onSigninUser(user:String, result:Object):void {
			trace('FMSClient.onSigninUser:', user, result);
		}

		public function onStartGame(user:String):void {
			trace('FMSClient.onStartGame:', user);
		}

		public function onChangePlayer(user:String, player:uint):void {
			trace('FMSClient.onChangePlayer:', user, player);
		}

		public function onUpdateScore(user:String, score:uint):void {
			trace('FMSClient.onUpdateScore:', user, score);
		}

		public function onDisconnect(user:String):void {
			trace('FMSClient.onDisconnect:', user);
		}

		public function dispose():void {
			trace('FMSClient.diposed');
		}
	}
}

Update FMSClient.as

Update FMSClient.as