package krewfw.utils.as3 {

    import flash.events.Event;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.media.SoundMixer;
    import flash.media.SoundTransform;
    import flash.utils.getDefinitionByName;

    import krewfw.KrewConfig;
    import krewfw.utils.krew;

    public class KrewSoundPlayer {

        private var _bgmChannel:SoundChannel;
        private var _seChannel :SoundChannel;

        private var _soundTransform:SoundTransform;
        private var _bgmVolume:Number = 1.0;  // master volume for BGM
        private var _seVolume:Number  = 1.0;  // master volume for SE

        private var _currentBgmId:String;
        private var _currentBgm:Sound;
        private var _pausePosition:int = 0;

        /**
         * BGM と SE の再生ユーティリティー。
         * ToDo: 現状は非常にシンプルなもの。BGM は全てループ再生
         * ToDO: 同じ SE 鳴らし過ぎちゃわない対応
         */
        //------------------------------------------------------------
        public function KrewSoundPlayer() {
            _soundTransform = new SoundTransform();

            _followDeviceMuteButton();
        }

        //------------------------------------------------------------
        // accessors
        //------------------------------------------------------------

        public function get bgmVolume():Number { return _bgmVolume; }

        public function set bgmVolume(vol:Number):void {
            _bgmVolume = vol;
            _bgmVolume = krew.within(_bgmVolume, 0, 1);
        }

        public function get seVolume():Number { return _seVolume; }

        public function set seVolume(vol:Number):void {
            _seVolume = vol;
            _seVolume = krew.within(_seVolume, 0, 1);
        }

        //------------------------------------------------------------
        // interfaces for BGM
        //------------------------------------------------------------

        /**
         * 渡した Sound を BGM として再生。
         * 前回渡した bgmId と同じものをすでに再生中の場合は、再生し直しを行わない。
         * bgmId に null を渡した場合は常に再生し直す
         */
        public function playBgm(sound:Sound, bgmId:String=null, vol:Number=NaN, startTime:Number=0):void
        {
            if (_bgmChannel) {
                if (bgmId != null  &&  bgmId == _currentBgmId) { return; }
                _bgmChannel.stop();
            }
            _currentBgmId = bgmId;

            _soundTransform.volume = (!isNaN(vol)) ? vol : _bgmVolume;
            var loops:int = 1;
            _bgmChannel = sound.play(startTime, loops, _soundTransform);
            _currentBgm = sound;

            _bgmChannel.addEventListener(Event.SOUND_COMPLETE, _onBgmComplete);
        }

        public function replayBgm(vol:Number=NaN, startTime:Number=0):void {
            if (!_currentBgm) { return; }

            playBgm(_currentBgm, _currentBgmId, vol, startTime);
        }

        public function pauseBgm():void {
            if (!_bgmChannel) { return; }

            _pausePosition = _bgmChannel.position;
            _bgmChannel.stop();
        }

        public function resumeBgm():void {
            if (!_bgmChannel) { return; }

            playBgm(_currentBgm, _currentBgmId, NaN, _pausePosition);
        }

        public function stopBgm():void {
            if (_bgmChannel) {
                _bgmChannel.stop();
                _bgmChannel = null;
            }
        }

        //------------------------------------------------------------
        // interfaces for SE
        //------------------------------------------------------------

        public function playSe(sound:Sound, pan:Number=0, loops:int=0,
                               vol:Number=NaN, startTime:Number=0):void
        {
            _soundTransform.pan    = pan;
            _soundTransform.volume = (!isNaN(vol)) ? vol : _seVolume;
            _seChannel = sound.play(startTime, loops, _soundTransform);
        }

        public function stopSe():void {
            if (_seChannel) {
                _seChannel.stop();
                _seChannel = null;
            }
        }

        //------------------------------------------------------------
        // interfaces for Entire Sound
        //------------------------------------------------------------

        public function stopAll():void {
            stopBgm();
            stopSe();
        }

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

        /**
         * 端末のミュートボタンに従う。IS_AIR が true にセットされていなければ何もしない。
         * クラスを動的に取得しているのは Flash にこのクラスが無いため
         */
        private function _followDeviceMuteButton():void {
            if (!KrewConfig.IS_AIR) { return; }

            var AudioPlaybackMode:Class  = getDefinitionByName("flash.media.AudioPlaybackMode") as Class;
            SoundMixer.audioPlaybackMode = AudioPlaybackMode.AMBIENT;
        }

        /**
         * pause & resume してしまうと、Sound.play() の loops に 2 以上の値を入れて
         * ループさせた場合に、resume した位置から再生が再開されてしまうようだ。
         * ひとまず再生終了のコールバックで 0 位置から再生し直すことでループ再生する
         */
        private function _onBgmComplete(event:Event):void {
            replayBgm();
        }

    }
}

package krewfw.utils.as3 {

    import flash.events.Event;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.media.SoundMixer;
    import flash.media.SoundTransform;
    import flash.utils.getDefinitionByName;

    import krewfw.KrewConfig;
    import krewfw.utils.krew;

    public class KrewSoundPlayer {

        private var _bgmChannel:SoundChannel;
        private var _seChannel :SoundChannel;

        private var _soundTransform:SoundTransform;
        private var _bgmVolume:Number = 1.0;  // master volume for BGM
        private var _seVolume:Number  = 1.0;  // master volume for SE

        private var _currentBgmId:String;
        private var _currentBgm:Sound;
        private var _pausePosition:int = 0;

        /**
         * BGM と SE の再生ユーティリティー。
         * ToDo: 現状は非常にシンプルなもの。BGM は全てループ再生
         * ToDO: 同じ SE 鳴らし過ぎちゃわない対応
         */
        //------------------------------------------------------------
        public function KrewSoundPlayer() {
            _soundTransform = new SoundTransform();

            _followDeviceMuteButton();
        }

        //------------------------------------------------------------
        // accessors
        //------------------------------------------------------------

        public function get bgmVolume():Number { return _bgmVolume; }

        public function set bgmVolume(vol:Number):void {
            _bgmVolume = vol;
            _bgmVolume = krew.within(_bgmVolume, 0, 1);
        }

        public function get seVolume():Number { return _seVolume; }

        public function set seVolume(vol:Number):void {
            _seVolume = vol;
            _seVolume = krew.within(_seVolume, 0, 1);
        }

        //------------------------------------------------------------
        // interfaces for BGM
        //------------------------------------------------------------

        /**
         * 渡した Sound を BGM として再生。
         * 前回渡した bgmId と同じものをすでに再生中の場合は、再生し直しを行わない。
         * bgmId に null を渡した場合は常に再生し直す
         */
        public function playBgm(sound:Sound, bgmId:String=null, vol:Number=NaN, startTime:Number=0):void
        {
            if (_bgmChannel) {
                if (bgmId != null  &&  bgmId == _currentBgmId) { return; }
                _bgmChannel.stop();
            }
            _currentBgmId = bgmId;

            _soundTransform.volume = (!isNaN(vol)) ? vol : _bgmVolume;
            var loops:int = 1;
            _bgmChannel = sound.play(startTime, loops, _soundTransform);
            _currentBgm = sound;

            _bgmChannel.addEventListener(Event.SOUND_COMPLETE, _onBgmComplete);
        }

        public function replayBgm(vol:Number=NaN, startTime:Number=0):void {
            if (!_currentBgm) { return; }

            playBgm(_currentBgm, _currentBgmId, vol, startTime);
        }

        public function pauseBgm():void {
            if (!_bgmChannel) { return; }

            _pausePosition = _bgmChannel.position;
            _bgmChannel.stop();
        }

        public function resumeBgm():void {
            if (!_bgmChannel) { return; }

            stopBgm();
            playBgm(_currentBgm, _currentBgmId, NaN, _pausePosition);
        }

        public function stopBgm():void {
            if (_bgmChannel) {
                _bgmChannel.stop();
                _bgmChannel = null;
            }
        }

        //------------------------------------------------------------
        // interfaces for SE
        //------------------------------------------------------------

        public function playSe(sound:Sound, pan:Number=0, loops:int=0,
                               vol:Number=NaN, startTime:Number=0):void
        {
            _soundTransform.pan    = pan;
            _soundTransform.volume = (!isNaN(vol)) ? vol : _seVolume;
            _seChannel = sound.play(startTime, loops, _soundTransform);
        }

        public function stopSe():void {
            if (_seChannel) {
                _seChannel.stop();
                _seChannel = null;
            }
        }

        //------------------------------------------------------------
        // interfaces for Entire Sound
        //------------------------------------------------------------

        public function stopAll():void {
            stopBgm();
            stopSe();
        }

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

        /**
         * 端末のミュートボタンに従う。IS_AIR が true にセットされていなければ何もしない。
         * クラスを動的に取得しているのは Flash にこのクラスが無いため
         */
        private function _followDeviceMuteButton():void {
            if (!KrewConfig.IS_AIR) { return; }

            var AudioPlaybackMode:Class  = getDefinitionByName("flash.media.AudioPlaybackMode") as Class;
            SoundMixer.audioPlaybackMode = AudioPlaybackMode.AMBIENT;
        }

        /**
         * pause & resume してしまうと、Sound.play() の loops に 2 以上の値を入れて
         * ループさせた場合に、resume した位置から再生が再開されてしまうようだ。
         * ひとまず再生終了のコールバックで 0 位置から再生し直すことでループ再生する
         */
        private function _onBgmComplete(event:Event):void {
            replayBgm();
        }

    }
}

Modify bug of sound player

Modify bug of sound player


import mx.transitions.easing.*;
import com.mosesSupposes.fuse.*;

class menuColouridiomes extends MoviieClip

	{
		public var cat, es, en ,fr:MovieClip;
		public var arrayLang_arr:Array = new Array ("cat", "es", "en", "fr");

		function menuColourIdiomes ():Void

		{

		this.onRollOver = this.mOver;
		this.onRollOut = this.mOut;
		this.onRelease =this.mOver;

		}

		private function mOver():Void

		{

                arrayLang_arr[i].colorTo("#95C60D", 0.3, "easeOutCirc");

		}

		private function mOut():Void

		{

                arrayLang_arr[i].colorTo("#010101", 0.3, "easeInCirc");

		}

	}

import mx.transitions.easing.*;
import com.mosesSupposes.fuse.*;

class com.segonquart.menuColouridiomes extends MoviieClip

	{
		public var cat, es, en ,fr:MovieClip;
		public var arrayLang_arr:Array = new Array ("cat", "es", "en", "fr");

		function menuColourIdiomes ():Void

		{

		this.onRollOver = this.mOver;
		this.onRollOut = this.mOut;
		this.onRelease =this.mOver;

		}

		private function mOver():Void

		{

                arrayLang_arr[i].colorTo("#95C60D", 0.3, "easeOutCirc");

		}

		private function mOut():Void

		{

                arrayLang_arr[i].colorTo("#010101", 0.3, "easeInCirc");

		}

	}

Update menuColourIdiomes.as

Update menuColourIdiomes.as


/**
 * User: MerlinDS
 * Date: 18.07.2014
 * Time: 17:42
 */
package com.merlinds.miracle_tool.viewer {
	import com.bit101.components.List;
	import com.bit101.components.Window;
	import com.merlinds.debug.log;
	import com.merlinds.miracle.Miracle;
	import com.merlinds.miracle.animations.Animation;
	import com.merlinds.miracle.display.MiracleDisplayObject;
	import com.merlinds.miracle.display.MiracleImage;
	import com.merlinds.miracle.utils.Asset;
	import com.merlinds.miracle.utils.MafReader;
	import com.merlinds.miracle_tool.models.AppModel;

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;

	[SWF(backgroundColor="0x333333", frameRate=60)]
	public class ViewerView extends Sprite {

		private var _model:AppModel;
		private var _assets:Vector.<Asset>;
		private var _window:Window;
		private var _name:String;

		public function ViewerView(model:AppModel = null) {
			super();
			_model = model;
			if(_model == null){
				_model = new AppModel();
			}

			this.addEventListener(Event.ADDED_TO_STAGE, this.initialize);
		}
		//==============================================================================
		//{region							PUBLIC METHODS
		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		private function initialize(event:Event):void {
			this.removeEventListener(event.type, this.initialize);
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			this.stage.align = StageAlign.TOP_LEFT;
			Miracle.start(this.stage, this.createHandler, true);
		}

		private function choseAnimation():void {
			//find animation asset and add all of animations to chose list
			var n:int = _assets.length;
			for(var i:int = 0; i < n; i++){
				var asset:Asset = _assets[i];
				if(asset.type == Asset.TIMELINE_TYPE){
					//parse output
					_window = new Window(this, 0, 0, "Chose mesh");
					var list:List = new List(_window, 0, 0, this.getAnimations(asset.output));
					_window.x = this.stage.stageWidth - _window.width;
					list.addEventListener(Event.SELECT, this.selectAnimationHandler);
				}
			}
		}

		[Inline]
		private function getAnimations(bytes:ByteArray):Array {
			var result:Array = [];
			var reader:MafReader = new MafReader();
			reader.execute(bytes);
			var animations:Vector.<Animation> = reader.animations;
			for each(var animation:Animation in animations){
				result.push(animation.name);
			}
			return result;
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		private function createHandler(animation:Boolean = false):void {
			//TODO: Show view if it exit
			if(animation || _model.viewerInput == null){
				_model.viewerInput = _model.lastFileDirection;
				_model.viewerInput.addEventListener(Event.SELECT, this.selectFileHandler);
				_model.viewerInput.browseForOpen("Open " + animation ? "Animation" : "Sprite"
						+ "file that you want to view");
			}
		}

		private function selectFileHandler(event:Event):void {
			_model.viewerInput.removeEventListener(event.type, this.selectFileHandler);
			_model.lastFileDirection = _model.viewerInput.parent;
			var byteArray:ByteArray = new ByteArray();
			var stream:FileStream = new FileStream();
			stream.open(_model.viewerInput, FileMode.READ);
			stream.readBytes(byteArray);
			stream.close();
			//parse
			if(_assets == null)_assets = new <Asset>[];
			var asset:Asset = new Asset(_model.viewerInput.name, byteArray);
			if(asset.type == Asset.TIMELINE_TYPE){
				_name = asset.name;
			}
			_assets.push(asset);
//			var name:String = asset.name;
			if(_assets.length > 1){
				this.choseAnimation();
				Miracle.createScene(_assets, 1);
				Miracle.resume();
			}else{
				this.createHandler(true);
			}
		}

		private function selectAnimationHandler(event:Event):void {
			var list:List = event.target as List;
			//add animation to miracle
			var animation:String = list.selectedItem.toString();
			log(this, "selectAnimationHandler", animation);
			Miracle.currentScene.createAnimation(_name, animation)
					.addEventListener(Event.ADDED_TO_STAGE, this.imageAddedToStage);
		}

		private function imageAddedToStage(event:Event):void {
			var target:MiracleDisplayObject = event.target as MiracleDisplayObject;
			target.moveTO(
					this.stage.stageWidth - target.width >> 1,
					this.stage.stageHeight - target.height >> 1
			);
		}
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

/**
 * User: MerlinDS
 * Date: 18.07.2014
 * Time: 17:42
 */
package com.merlinds.miracle_tool.viewer {
	import com.bit101.components.List;
	import com.bit101.components.Window;
	import com.merlinds.debug.log;
	import com.merlinds.miracle.Miracle;
	import com.merlinds.miracle.animations.AnimationHelper;
	import com.merlinds.miracle.display.MiracleDisplayObject;
	import com.merlinds.miracle.display.MiracleImage;
	import com.merlinds.miracle.utils.Asset;
	import com.merlinds.miracle.utils.MafReader;
	import com.merlinds.miracle_tool.models.AppModel;

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;

	[SWF(backgroundColor="0x333333", frameRate=60)]
	public class ViewerView extends Sprite {

		private var _model:AppModel;
		private var _assets:Vector.<Asset>;
		private var _window:Window;
		private var _name:String;

		public function ViewerView(model:AppModel = null) {
			super();
			_model = model;
			if(_model == null){
				_model = new AppModel();
			}

			this.addEventListener(Event.ADDED_TO_STAGE, this.initialize);
		}
		//==============================================================================
		//{region							PUBLIC METHODS
		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		private function initialize(event:Event):void {
			this.removeEventListener(event.type, this.initialize);
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			this.stage.align = StageAlign.TOP_LEFT;
			Miracle.start(this.stage, this.createHandler, true);
		}

		private function choseAnimation():void {
			//find animation asset and add all of animations to chose list
			var n:int = _assets.length;
			for(var i:int = 0; i < n; i++){
				var asset:Asset = _assets[i];
				if(asset.type == Asset.TIMELINE_TYPE){
					//parse output
					_window = new Window(this, 0, 0, "Chose mesh");
					var list:List = new List(_window, 0, 0, this.getAnimations(asset.output));
					_window.x = this.stage.stageWidth - _window.width;
					list.addEventListener(Event.SELECT, this.selectAnimationHandler);
				}
			}
		}

		[Inline]
		private function getAnimations(bytes:ByteArray):Array {
			var result:Array = [];
			var reader:MafReader = new MafReader();
			reader.execute(bytes);
			var animations:Vector.<AnimationHelper> = reader.animations;
			for each(var animation:AnimationHelper in animations){
				result.push(animation.name);
			}
			return result;
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		private function createHandler(animation:Boolean = false):void {
			//TODO: Show view if it exit
			if(animation || _model.viewerInput == null){
				_model.viewerInput = _model.lastFileDirection;
				_model.viewerInput.addEventListener(Event.SELECT, this.selectFileHandler);
				_model.viewerInput.browseForOpen("Open " + animation ? "Animation" : "Sprite"
						+ "file that you want to view");
			}
		}

		private function selectFileHandler(event:Event):void {
			_model.viewerInput.removeEventListener(event.type, this.selectFileHandler);
			_model.lastFileDirection = _model.viewerInput.parent;
			var byteArray:ByteArray = new ByteArray();
			var stream:FileStream = new FileStream();
			stream.open(_model.viewerInput, FileMode.READ);
			stream.readBytes(byteArray);
			stream.close();
			//parse
			if(_assets == null)_assets = new <Asset>[];
			var asset:Asset = new Asset(_model.viewerInput.name, byteArray);
			if(asset.type == Asset.TIMELINE_TYPE){
				_name = asset.name;
			}
			_assets.push(asset);
//			var name:String = asset.name;
			if(_assets.length > 1){
				this.choseAnimation();
				Miracle.createScene(_assets, 1);
				Miracle.resume();
			}else{
				this.createHandler(true);
			}
		}

		private function selectAnimationHandler(event:Event):void {
			var list:List = event.target as List;
			//add animation to miracle
			var animation:String = list.selectedItem.toString();
			log(this, "selectAnimationHandler", animation);
			Miracle.currentScene.createAnimation(_name, animation)
					.addEventListener(Event.ADDED_TO_STAGE, this.imageAddedToStage);
		}

		private function imageAddedToStage(event:Event):void {
			var target:MiracleDisplayObject = event.target as MiracleDisplayObject;
			target.moveTO(
					this.stage.stageWidth - target.width >> 1,
					this.stage.stageHeight - target.height >> 1
			);
		}
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

Add animations to scene

Add animations to scene


package com.kaltura.kdpfl.view.controls
{
	import com.kaltura.kdpfl.controller.media.LiveStreamCommand;
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.media.KMediaPlayerMediator;

	import flash.events.TimerEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	import flash.utils.Timer;
	import flash.utils.getQualifiedClassName;

	import org.osmf.media.MediaPlayerState;
	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;

	/**
	 * Mediator for the buffer indicator animation. Controls the appearance and removal of the animation from the display list.
	 * @author Hila
	 *
	 */
	public class BufferAnimationMediator extends Mediator
	{
		public static const NAME:String = "spinnerMediator";
		public static const SPINNER_CLASS : String = "kspin";
		public var applicationLoadStyleName:String = '';
		public var bufferingStyleName:String = '';
		private var zeroPoint : Point = new Point( 0 , 0);

		/**
		 * flag indicating whether the active media has reached its end.
		 */
		private var _reachedEnd:Boolean=false;
		private var _currConfig : ConfigProxy;
		private var _notBuffering : Boolean = true;
		private var _prevStatePaused:Boolean = true;
		private var _lastPlayheadPos:Number;

		/**
		 * indicate if "bufferChange" event was sent, we started buffering
		 */
		private var _bufferChangeStart:Boolean = false;

		/**
		 * Constructor.
		 * @param viewComponent - the component controlled by the mediator.
		 *
		 */
		public function BufferAnimationMediator(viewComponent:Object=null)
		{
			super(NAME, viewComponent);
			_currConfig = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
		}

		private var d:Date;

		override public function handleNotification(note:INotification):void
		{
			var flashvars : Object = _currConfig.vo.flashvars;
			var noteName : String = note.getName();
			switch(noteName)
			{
				case NotificationType.SKIN_LOADED:
					//					spinner.swapLoadingWithAnimation( applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					fadeOutSpinner();
					break;
				case NotificationType.LAYOUT_READY:
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && flashvars.preloader)
					{
						spinner.setBufferingAnimation(getQualifiedClassName(flashvars.preloader));
					}
					else
					{
						spinner.setBufferingAnimation(applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					}
					break;
				case NotificationType.KDP_READY:
				case NotificationType.READY_TO_PLAY:
					var spinnerColor:Number = -1;
					if(flashvars.spinnerColorAttribute && flashvars[flashvars.spinnerColorAttribute] )
					{
						spinnerColor = flashvars[flashvars.spinnerColorAttribute]
					}
					if(flashvars.spinnerColor)
					{
						spinnerColor = flashvars.spinnerColor;
					}

					if(spinnerColor> -1 )
					{
						var color_transform:ColorTransform= new ColorTransform;
						color_transform.color= spinnerColor ;
						spinner.transform.colorTransform=color_transform;

					}
					if(flashvars.spinnerFadeTime)
						_animationTime = Number(flashvars.spinnerFadeTime) * 1000;

					fadeOutSpinner();
					_reachedEnd = false;
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && flashvars.preloader)
					{
						spinner.setBufferingAnimation(getQualifiedClassName(flashvars.preloader));
					}
					else
					{
						spinner.setBufferingAnimation(applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					}
					break;
				case NotificationType.PLAYER_STATE_CHANGE:
					if( note.getBody() == MediaPlayerState.BUFFERING)
					{
						_notBuffering = false;
						if(_reachedEnd)
						{
							fadeOutSpinner();
						}
						else if (!_prevStatePaused)
						{
							//fix OSMF bug: sometimes "buffering" was sent after player paused, the spinner shouldn't be visible in this case
							fadeInSpinner();
						}

					}
					else
					{
						//only relevant if not rtmp
						/* if( note.getBody() == MediaPlayerState.READY ){
						spinner.visible = false;
						}
						if(_flashvars.streamerType != StreamerType.RTMP){
						spinner.visible = false;
						} */
						_notBuffering = true;
						if( note.getBody() == MediaPlayerState.PLAYING)
						{
							_reachedEnd = false;
							_prevStatePaused = false;
						//	spinner.visible = false;
						}
						if(note.getBody() == MediaPlayerState.PAUSED)
						{
							fadeOutSpinner();
							_prevStatePaused = true;
						}
						if( note.getBody() == MediaPlayerState.READY)
						{
							fadeOutSpinner();
						}
					}

					break;

				/**
				 * Until OSMF for seeking in rtmp is fixed (buffering-wise)
				 * add a new case for update playhead notification
				 * */
				case NotificationType.PLAYER_UPDATE_PLAYHEAD:
					//if _bufferChangeStart the next bufferChange event will make the spinner invisible
					if(_notBuffering && !_bufferChangeStart)
					{
						fadeOutSpinner();
					}
					//fix another OSMF bug: we are buffering even though movie plays
					else if (spinner.visible)
					{
						var updateIntervalInSeconds:Number = (facade.retrieveMediator(KMediaPlayerMediator.NAME) as KMediaPlayerMediator).player.currentTimeUpdateInterval / 1000;
						var curPos:Number = parseFloat(Number(note.getBody()).toFixed(2));
						if ((curPos - _lastPlayheadPos)<=updateIntervalInSeconds)
						{
							fadeOutSpinner();
						}
						_lastPlayheadPos = curPos;
					}

					break;
				case NotificationType.BUFFER_CHANGE:
					_bufferChangeStart = note.getBody();
					if(_bufferChangeStart)
					{
						fadeInSpinner()
					}
					if(!_bufferChangeStart)
					{
						fadeOutSpinner();
					}
					break;
				case NotificationType.PLAYER_PLAY_END:
					_reachedEnd=true;
					fadeOutSpinner();
					break;
				case NotificationType.KDP_EMPTY:
				case NotificationType.READY_TO_LOAD:
					fadeOutSpinner();
					_reachedEnd=false;
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && flashvars.preloader)
					{
						spinner.setBufferingAnimation(getQualifiedClassName(flashvars.preloader));
					}
					else
					{
						spinner.setBufferingAnimation(applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					}
					break;
				case NotificationType.CHANGE_MEDIA:
					//spinner.visible = true;

					break;

				//in case we are trying to connect to a live stream but it is not on air yet
				case NotificationType.LIVE_ENTRY:
					if (flashvars.hideSpinnerOnOffline=="true")
					{
						fadeOutSpinner();
					}
					else
					{
						fadeInSpinner();
					}
					break;

				//in case the live stream we are trying to connect to was found to be on air
				case LiveStreamCommand.LIVE_STREAM_READY:
					fadeOutSpinner();
					break;

				case NotificationType.PRELOADER_LOADED:
					var preObj:Object = note.getBody().preloader;
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && preObj)
					{
						//save to flashvars, we might need it in the future
						_currConfig.vo.flashvars["preloader"] = preObj;
						spinner.setBufferingAnimation(getQualifiedClassName(preObj));
					}
					break;

			}
		}

		private var _fadeInTimer:Timer;
		private var _fadeOutTimer:Timer;
		private var _animationTime:Number = 0;

		private function fadeOutSpinner():void
		{
			_bufferChangeStart = true;

			if (!spinner.visible)
				return;

			if(_animationTime == 0)
			{
				spinner.visible = false;
				return;
			}
			//trace("^^^^^^^^^   fadeOutSpinner start >>>>>>>>>>");
			spinner.visible = true;
			spinner.alpha = 1;
			_fadeOutTimer = new Timer(_animationTime/10,10);
			_fadeOutTimer.addEventListener(TimerEvent.TIMER , onFadeOutTimer);
			_fadeOutTimer.addEventListener(TimerEvent.TIMER_COMPLETE , onFadeOutComplete);
			_fadeOutTimer.start();
		}
		private function onFadeOutTimer(event:TimerEvent):void
		{
			spinner.alpha -=0.1;
		}
		private function onFadeOutComplete(event:TimerEvent):void
		{
			//trace("^^^^^^^^^   fadeOutSpinner END >>>>>>>>>>");
			_fadeOutTimer.removeEventListener(TimerEvent.TIMER , onFadeOutTimer);
			_fadeOutTimer.removeEventListener(TimerEvent.TIMER_COMPLETE , onFadeOutComplete);
			spinner.visible = false;
		}
		private function fadeInSpinner():void
		{
			if (spinner.visible)
				return;

			if(_animationTime == 0)
			{
				spinner.visible = true;
				return;
			}
			spinner.alpha = 0;
			spinner.visible = true;
			_fadeInTimer = new Timer(_animationTime/10,10);
			_fadeInTimer.addEventListener(TimerEvent.TIMER , onFadeInTimer);
			_fadeInTimer.addEventListener(TimerEvent.TIMER_COMPLETE , onFadeInComplete);
			_fadeInTimer.start();
		}

		private function onFadeInTimer(event:TimerEvent):void
		{
			spinner.alpha +=0.1;
		}
		private function onFadeInComplete(event:TimerEvent):void
		{
			_fadeInTimer.removeEventListener(TimerEvent.TIMER , onFadeInTimer);
			_fadeInTimer.removeEventListener(TimerEvent.TIMER_COMPLETE , onFadeInComplete);
		}

		override public function listNotificationInterests():Array
		{
			return [
				NotificationType.SKIN_LOADED,
				NotificationType.PLAYER_STATE_CHANGE,
				NotificationType.PLAYER_PLAYED,
				NotificationType.PLAYER_UPDATE_PLAYHEAD, //TODO: REMOVE THIS WHEN I FIND A BETTER WORK AROUND TO THE BUG
				NotificationType.ENTRY_FAILED,
				NotificationType.CHANGE_MEDIA,
				NotificationType.PLAYER_PLAY_END,
				NotificationType.KDP_EMPTY,
				NotificationType.KDP_READY,
				NotificationType.LAYOUT_READY,
				NotificationType.LIVE_ENTRY,
				LiveStreamCommand.LIVE_STREAM_READY,
				NotificationType.READY_TO_PLAY,
				NotificationType.READY_TO_LOAD,
				NotificationType.ROOT_RESIZE,
				NotificationType.BUFFER_CHANGE,
				NotificationType.PRELOADER_LOADED
			];
		}
		/**
		 * the buffer indicator animation controlled by the mediator.
		 * @return
		 *
		 */
		public function get spinner():BufferAnimation
		{
			return viewComponent as BufferAnimation;
		}

	}
}

package com.kaltura.kdpfl.view.controls
{
	import com.kaltura.kdpfl.controller.media.LiveStreamCommand;
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.media.KMediaPlayerMediator;

	import flash.events.TimerEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	import flash.utils.Timer;
	import flash.utils.getQualifiedClassName;

	import org.osmf.media.MediaPlayerState;
	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;

	/**
	 * Mediator for the buffer indicator animation. Controls the appearance and removal of the animation from the display list.
	 * @author Hila
	 *
	 */
	public class BufferAnimationMediator extends Mediator
	{
		public static const NAME:String = "spinnerMediator";
		public static const SPINNER_CLASS : String = "kspin";
		public var applicationLoadStyleName:String = '';
		public var bufferingStyleName:String = '';
		private var zeroPoint : Point = new Point( 0 , 0);

		/**
		 * flag indicating whether the active media has reached its end.
		 */
		private var _reachedEnd:Boolean=false;
		private var _currConfig : ConfigProxy;
		private var _notBuffering : Boolean = true;
		private var _prevStatePaused:Boolean = true;
		private var _lastPlayheadPos:Number;

		/**
		 * indicate if "bufferChange" event was sent, we started buffering
		 */
		private var _bufferChangeStart:Boolean = false;

		/**
		 * Constructor.
		 * @param viewComponent - the component controlled by the mediator.
		 *
		 */
		public function BufferAnimationMediator(viewComponent:Object=null)
		{
			super(NAME, viewComponent);
			_currConfig = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
		}

		private var d:Date;

		override public function handleNotification(note:INotification):void
		{
			var flashvars : Object = _currConfig.vo.flashvars;
			var noteName : String = note.getName();
			switch(noteName)
			{
				case NotificationType.SKIN_LOADED:
					//					spinner.swapLoadingWithAnimation( applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					fadeOutSpinner();
					break;
				case NotificationType.LAYOUT_READY:
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && flashvars.preloader)
					{
						spinner.setBufferingAnimation(getQualifiedClassName(flashvars.preloader));
					}
					else
					{
						spinner.setBufferingAnimation(applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					}
					break;
				case NotificationType.KDP_READY:
				case NotificationType.READY_TO_PLAY:
					var spinnerColor:Number = -1;
					if(flashvars.spinnerColorAttribute && flashvars[flashvars.spinnerColorAttribute] )
					{
						spinnerColor = flashvars[flashvars.spinnerColorAttribute]
					}
					if(flashvars.spinnerColor)
					{
						spinnerColor = flashvars.spinnerColor;
					}

					if(spinnerColor> -1 )
					{
						var color_transform:ColorTransform= new ColorTransform;
						color_transform.color= spinnerColor ;
						spinner.transform.colorTransform=color_transform;

					}
					if(flashvars.spinnerFadeTime)
						_animationTime = Number(flashvars.spinnerFadeTime) * 1000;

					fadeOutSpinner();
					_reachedEnd = false;
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && flashvars.preloader)
					{
						spinner.setBufferingAnimation(getQualifiedClassName(flashvars.preloader));
					}
					else
					{
						spinner.setBufferingAnimation(applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					}
					break;
				case NotificationType.PLAYER_STATE_CHANGE:
					if( note.getBody() == MediaPlayerState.BUFFERING)
					{
						_notBuffering = false;
						if(_reachedEnd)
						{
							fadeOutSpinner();
						}
						else if (!_prevStatePaused)
						{
							//fix OSMF bug: sometimes "buffering" was sent after player paused, the spinner shouldn't be visible in this case
							fadeInSpinner();
						}

					}
					else
					{
						//only relevant if not rtmp
						/* if( note.getBody() == MediaPlayerState.READY ){
						spinner.visible = false;
						}
						if(_flashvars.streamerType != StreamerType.RTMP){
						spinner.visible = false;
						} */
						_notBuffering = true;
						if( note.getBody() == MediaPlayerState.PLAYING)
						{
							_reachedEnd = false;
							_prevStatePaused = false;
						//	spinner.visible = false;
						}
						if(note.getBody() == MediaPlayerState.PAUSED)
						{
							fadeOutSpinner();
							_prevStatePaused = true;
						}
						if( note.getBody() == MediaPlayerState.READY)
						{
							fadeOutSpinner();
						}
					}

					break;

				/**
				 * Until OSMF for seeking in rtmp is fixed (buffering-wise)
				 * add a new case for update playhead notification
				 * */
				case NotificationType.PLAYER_UPDATE_PLAYHEAD:
					//if _bufferChangeStart the next bufferChange event will make the spinner invisible
					if(_notBuffering && !_bufferChangeStart)
					{
						fadeOutSpinner();
					}
					//fix another OSMF bug: we are buffering even though movie plays
					else if (spinner.visible)
					{
						var updateIntervalInSeconds:Number = (facade.retrieveMediator(KMediaPlayerMediator.NAME) as KMediaPlayerMediator).player.currentTimeUpdateInterval / 1000;
						var curPos:Number = parseFloat(Number(note.getBody()).toFixed(2));
						if ((curPos - _lastPlayheadPos)<=updateIntervalInSeconds)
						{
							fadeOutSpinner();
						}
						_lastPlayheadPos = curPos;
					}

					break;
				case NotificationType.BUFFER_CHANGE:
					_bufferChangeStart = note.getBody();
					if(_bufferChangeStart)
					{
						fadeInSpinner()
					}
					if(!_bufferChangeStart)
					{
						fadeOutSpinner();
					}
					break;
				case NotificationType.PLAYER_PLAY_END:
					_reachedEnd=true;
					fadeOutSpinner();
					break;
				case NotificationType.KDP_EMPTY:
				case NotificationType.READY_TO_LOAD:
					fadeOutSpinner();
					_reachedEnd=false;
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && flashvars.preloader)
					{
						spinner.setBufferingAnimation(getQualifiedClassName(flashvars.preloader));
					}
					else
					{
						spinner.setBufferingAnimation(applicationLoadStyleName == '' ? SPINNER_CLASS : applicationLoadStyleName );
					}
					break;
				case NotificationType.CHANGE_MEDIA:
					//spinner.visible = true;

					break;

				//in case we are trying to connect to a live stream but it is not on air yet
				case NotificationType.LIVE_ENTRY:
					if (flashvars.hideSpinnerOnOffline=="true")
					{
						fadeOutSpinner();
					}
					else
					{
						fadeInSpinner();
					}
					break;

				//in case the live stream we are trying to connect to was found to be on air
				case LiveStreamCommand.LIVE_STREAM_READY:
					fadeOutSpinner();
					break;

				case NotificationType.PRELOADER_LOADED:
					var preObj:Object = note.getBody().preloader;
					if (flashvars.usePreloaderBufferAnimation && flashvars.usePreloaderBufferAnimation=='true' && preObj)
					{
						//save to flashvars, we might need it in the future
						_currConfig.vo.flashvars["preloader"] = preObj;
						spinner.setBufferingAnimation(getQualifiedClassName(preObj));
					}
					break;

			}
		}

		private var _fadeInTimer:Timer;
		private var _fadeOutTimer:Timer;
		private var _animationTime:Number = 0;

		private function fadeOutSpinner():void
		{
			if (!spinner.visible)
				return;

			if(_animationTime == 0)
			{
				spinner.visible = false;
				return;
			}
			//trace("^^^^^^^^^   fadeOutSpinner start >>>>>>>>>>");
			spinner.visible = true;
			spinner.alpha = 1;
			_fadeOutTimer = new Timer(_animationTime/10,10);
			_fadeOutTimer.addEventListener(TimerEvent.TIMER , onFadeOutTimer);
			_fadeOutTimer.addEventListener(TimerEvent.TIMER_COMPLETE , onFadeOutComplete);
			_fadeOutTimer.start();
		}
		private function onFadeOutTimer(event:TimerEvent):void
		{
			spinner.alpha -=0.1;
		}
		private function onFadeOutComplete(event:TimerEvent):void
		{
			//trace("^^^^^^^^^   fadeOutSpinner END >>>>>>>>>>");
			_fadeOutTimer.removeEventListener(TimerEvent.TIMER , onFadeOutTimer);
			_fadeOutTimer.removeEventListener(TimerEvent.TIMER_COMPLETE , onFadeOutComplete);
			spinner.visible = false;
		}
		private function fadeInSpinner():void
		{
			if (spinner.visible)
				return;

			if(_animationTime == 0)
			{
				spinner.visible = true;
				return;
			}
			spinner.alpha = 0;
			spinner.visible = true;
			_fadeInTimer = new Timer(_animationTime/10,10);
			_fadeInTimer.addEventListener(TimerEvent.TIMER , onFadeInTimer);
			_fadeInTimer.addEventListener(TimerEvent.TIMER_COMPLETE , onFadeInComplete);
			_fadeInTimer.start();
		}

		private function onFadeInTimer(event:TimerEvent):void
		{
			spinner.alpha +=0.1;
		}
		private function onFadeInComplete(event:TimerEvent):void
		{
			_fadeInTimer.removeEventListener(TimerEvent.TIMER , onFadeInTimer);
			_fadeInTimer.removeEventListener(TimerEvent.TIMER_COMPLETE , onFadeInComplete);
		}

		override public function listNotificationInterests():Array
		{
			return [
				NotificationType.SKIN_LOADED,
				NotificationType.PLAYER_STATE_CHANGE,
				NotificationType.PLAYER_PLAYED,
				NotificationType.PLAYER_UPDATE_PLAYHEAD, //TODO: REMOVE THIS WHEN I FIND A BETTER WORK AROUND TO THE BUG
				NotificationType.ENTRY_FAILED,
				NotificationType.CHANGE_MEDIA,
				NotificationType.PLAYER_PLAY_END,
				NotificationType.KDP_EMPTY,
				NotificationType.KDP_READY,
				NotificationType.LAYOUT_READY,
				NotificationType.LIVE_ENTRY,
				LiveStreamCommand.LIVE_STREAM_READY,
				NotificationType.READY_TO_PLAY,
				NotificationType.READY_TO_LOAD,
				NotificationType.ROOT_RESIZE,
				NotificationType.BUFFER_CHANGE,
				NotificationType.PRELOADER_LOADED
			];
		}
		/**
		 * the buffer indicator animation controlled by the mediator.
		 * @return
		 *
		 */
		public function get spinner():BufferAnimation
		{
			return viewComponent as BufferAnimation;
		}

	}
}

fix buffer animtation

fix buffer animtation


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.html.beads.layouts
{
	import flash.display.DisplayObject;
	import flash.geom.Rectangle;

	import org.apache.flex.core.IBeadLayout;
	import org.apache.flex.core.IBorderModel;
	import org.apache.flex.core.ILayoutParent;
	import org.apache.flex.core.IParentIUIBase;
	import org.apache.flex.core.IScrollBarModel;
	import org.apache.flex.core.IScrollingLayoutParent;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.IUIBase;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;
	import org.apache.flex.html.supportClasses.Border;
	import org.apache.flex.html.supportClasses.ScrollBar;

    /**
     *  The NonVirtualVerticalScrollingLayout class is a layout
     *  bead that displays a set of children vertically in one row,
     *  separating them according to CSS layout rules for margin and
     *  vertical-align styles and lays out a vertical ScrollBar
     *  to the right of the children.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class NonVirtualVerticalScrollingLayout implements IBeadLayout
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function NonVirtualVerticalScrollingLayout()
		{
		}

        private var vScrollBar:ScrollBar;

		private var _strand:IStrand;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
			_strand = value;

			IEventDispatcher(value).addEventListener("heightChanged", changeHandler);
			IEventDispatcher(value).addEventListener("widthChanged", changeHandler);
			IEventDispatcher(value).addEventListener("itemsCreated", changeHandler);
		}

		private function changeHandler(event:Event):void
		{
            var layoutParent:IScrollingLayoutParent =
                _strand.getBeadByType(IScrollingLayoutParent) as IScrollingLayoutParent;
            var contentView:IParentIUIBase = layoutParent.contentView as IParentIUIBase;
			var border:Border = layoutParent.border;
			var borderModel:IBorderModel = border.model as IBorderModel;

            var ww:Number = layoutParent.resizableView.width;
            var hh:Number = layoutParent.resizableView.height;
            border.width = ww;
            border.height = hh;

			contentView.width = ww - borderModel.offsets.left - borderModel.offsets.right;
			contentView.height = hh - borderModel.offsets.top - borderModel.offsets.bottom;
			contentView.x = borderModel.offsets.left;
			contentView.y = borderModel.offsets.top;

			var n:int = contentView.numElements;
			var yy:Number = 0;

			for (var i:int = 0; i < n; i++)
			{
				var ir:IUIBase = contentView.getElementAt(i) as IUIBase;
				ir.y = yy;
				ir.width = contentView.width;
				yy += ir.height;
			}
			if (yy > contentView.height)
			{
				vScrollBar = layoutParent.vScrollBar;
				contentView.width -= vScrollBar.width;
				IScrollBarModel(vScrollBar.model).maximum = yy;
				IScrollBarModel(vScrollBar.model).pageSize = contentView.height;
				IScrollBarModel(vScrollBar.model).pageStepSize = contentView.height;
				vScrollBar.visible = true;
				vScrollBar.height = contentView.height;
				vScrollBar.y = contentView.y;
				vScrollBar.x = contentView.width;
                var vpos:Number = IScrollBarModel(vScrollBar.model).value;
                DisplayObject(contentView).scrollRect = new Rectangle(0, vpos, contentView.width, vpos + contentView.height);
                vScrollBar.addEventListener("scroll", scrollHandler);
			}
			else if (vScrollBar)
			{
                DisplayObject(contentView).scrollRect = null;
				vScrollBar.visible = false;
			}

			IEventDispatcher(_strand).dispatchEvent(new Event("layoutComplete"));
		}

        private function scrollHandler(event:Event):void
        {
			var layoutParent:ILayoutParent = _strand.getBeadByType(ILayoutParent) as ILayoutParent;
			var contentView:IParentIUIBase = layoutParent.contentView;

            var vpos:Number = IScrollBarModel(vScrollBar.model).value;
			DisplayObject(contentView).scrollRect = new Rectangle(0, vpos, contentView.width, vpos + contentView.height);
        }
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.html.beads.layouts
{
	import flash.display.DisplayObject;
	import flash.geom.Rectangle;

	import org.apache.flex.core.IBeadLayout;
	import org.apache.flex.core.IBorderModel;
	import org.apache.flex.core.ILayoutParent;
	import org.apache.flex.core.IParentIUIBase;
	import org.apache.flex.core.IScrollBarModel;
	import org.apache.flex.core.IScrollingLayoutParent;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.IUIBase;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;
	import org.apache.flex.html.supportClasses.Border;
	import org.apache.flex.html.supportClasses.ScrollBar;

    /**
     *  The NonVirtualVerticalScrollingLayout class is a layout
     *  bead that displays a set of children vertically in one row,
     *  separating them according to CSS layout rules for margin and
     *  vertical-align styles and lays out a vertical ScrollBar
     *  to the right of the children.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class NonVirtualVerticalScrollingLayout implements IBeadLayout
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function NonVirtualVerticalScrollingLayout()
		{
		}

        private var vScrollBar:ScrollBar;

		private var _strand:IStrand;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
			_strand = value;

			IEventDispatcher(value).addEventListener("heightChanged", changeHandler);
			IEventDispatcher(value).addEventListener("widthChanged", changeHandler);
			IEventDispatcher(value).addEventListener("itemsCreated", changeHandler);
		}

		private function changeHandler(event:Event):void
		{
            var layoutParent:IScrollingLayoutParent =
                _strand.getBeadByType(IScrollingLayoutParent) as IScrollingLayoutParent;
            var contentView:IParentIUIBase = layoutParent.contentView as IParentIUIBase;
			var border:Border = layoutParent.border;
   			var borderModel:IBorderModel;
            if (border)
                borderModel = border.model as IBorderModel;
            var ww:Number = layoutParent.resizableView.width;
            var hh:Number = layoutParent.resizableView.height;
            if (border)
            {
                border.width = ww;
                border.height = hh;
            }

			contentView.width = ww - ((border) ? borderModel.offsets.left + borderModel.offsets.right : 0);
			contentView.height = hh - ((border) ? borderModel.offsets.top - borderModel.offsets.bottom : 0);
			contentView.x = (border) ? borderModel.offsets.left : 0;
			contentView.y = (border) ? borderModel.offsets.top : 0;

			var n:int = contentView.numElements;
			var yy:Number = 0;

			for (var i:int = 0; i < n; i++)
			{
				var ir:IUIBase = contentView.getElementAt(i) as IUIBase;
				ir.y = yy;
				ir.width = contentView.width;
				yy += ir.height;
			}
			if (yy > contentView.height)
			{
				vScrollBar = layoutParent.vScrollBar;
				contentView.width -= vScrollBar.width;
				IScrollBarModel(vScrollBar.model).maximum = yy;
				IScrollBarModel(vScrollBar.model).pageSize = contentView.height;
				IScrollBarModel(vScrollBar.model).pageStepSize = contentView.height;
				vScrollBar.visible = true;
				vScrollBar.height = contentView.height;
				vScrollBar.y = contentView.y;
				vScrollBar.x = contentView.width;
                var vpos:Number = IScrollBarModel(vScrollBar.model).value;
                DisplayObject(contentView).scrollRect = new Rectangle(0, vpos, contentView.width, vpos + contentView.height);
                vScrollBar.addEventListener("scroll", scrollHandler);
			}
			else if (vScrollBar)
			{
                DisplayObject(contentView).scrollRect = null;
				vScrollBar.visible = false;
			}

			IEventDispatcher(_strand).dispatchEvent(new Event("layoutComplete"));
		}

        private function scrollHandler(event:Event):void
        {
			var layoutParent:ILayoutParent = _strand.getBeadByType(ILayoutParent) as ILayoutParent;
			var contentView:IParentIUIBase = layoutParent.contentView;

            var vpos:Number = IScrollBarModel(vScrollBar.model).value;
			DisplayObject(contentView).scrollRect = new Rectangle(0, vpos, contentView.width, vpos + contentView.height);
        }
	}
}

handle no border

handle no border


package stdio {
  internal class BufferedStream implements InputStream, OutputStream {
    public var buffer: String = ""
    public var closed: Boolean = false

    public function get ready(): Boolean {
      return ready_for({ gets: false })
    }

    public function read(callback: Function): void {
      push({ callback: callback, gets: false })
    }

    public function gets(callback: Function): void {
      push({ callback: callback, gets: true })
    }

    public function puts(value: *): void {
      write(value + "\n")
    }

    public function write(value: *): void {
      buffer += value; feed()
    }

    public function close(): void {
      closed = true; feed()
    }

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

    private const requests: Array = []

    private function push(request: Object): void {
      requests.push(request); feed()
    }

    private function feed(): void {
      while (requests.length > 0 && ready_for(requests[0])) {
        satisfy(requests.shift())
      }
    }

    private function ready_for(request: Object): Boolean {
      return closed || (
        request.gets ? buffer.indexOf("\n") !== -1 : buffer !== ""
      )
    }

    private function satisfy(request: Object): void {
      if (closed && buffer.length === 0) {
        (request.callback)(null)
      } else if (request.gets) {
        satisfy_gets(request.callback)
      } else {
        satisfy_read(request.callback)
      }
    }

    private function satisfy_gets(callback: Function): void {
      const next: int = buffer.indexOf("\n") === -1
        ? buffer.length : buffer.indexOf("\n") + 1
      const line: String = buffer.slice(0, next)

      buffer = buffer.slice(next)

      callback(line.replace(/\n$/, ""))
    }

    private function satisfy_read(callback: Function): void {
      const result: String = buffer

      buffer = ""

      callback(result)
    }
  }
}

package stdio {
  public class BufferedStream implements InputStream, OutputStream {
    public var buffer: String = ""
    public var closed: Boolean = false

    public function get ready(): Boolean {
      return ready_for({ gets: false })
    }

    public function read(callback: Function): void {
      push({ callback: callback, gets: false })
    }

    public function gets(callback: Function): void {
      push({ callback: callback, gets: true })
    }

    public function puts(value: *): void {
      write(value + "\n")
    }

    public function write(value: *): void {
      buffer += value; feed()
    }

    public function close(): void {
      closed = true; feed()
    }

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

    private const requests: Array = []

    private function push(request: Object): void {
      requests.push(request); feed()
    }

    private function feed(): void {
      while (requests.length > 0 && ready_for(requests[0])) {
        satisfy(requests.shift())
      }
    }

    private function ready_for(request: Object): Boolean {
      return closed || (
        request.gets ? buffer.indexOf("\n") !== -1 : buffer !== ""
      )
    }

    private function satisfy(request: Object): void {
      if (closed && buffer.length === 0) {
        (request.callback)(null)
      } else if (request.gets) {
        satisfy_gets(request.callback)
      } else {
        satisfy_read(request.callback)
      }
    }

    private function satisfy_gets(callback: Function): void {
      const next: int = buffer.indexOf("\n") === -1
        ? buffer.length : buffer.indexOf("\n") + 1
      const line: String = buffer.slice(0, next)

      buffer = buffer.slice(next)

      callback(line.replace(/\n$/, ""))
    }

    private function satisfy_read(callback: Function): void {
      const result: String = buffer

      buffer = ""

      callback(result)
    }
  }
}

Make BufferedStream public.

Make BufferedStream public.


package org.pyamf.examples.socket
{
	/**
	 * Copyright (c) 2007-2009 The PyAMF Project.
	 * See LICENSE.txt for details.
	 */

	import flash.errors.IOError;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.ObjectEncoding;
	import flash.net.Socket;
	import flash.system.Capabilities;

	[Event(name="connected", type="flash.events.Event")]
	[Event(name="disconnected", type="flash.events.Event")]
	[Event(name="logUpdate", type="flash.events.Event")]
	/**
	 * Socket connection to read and write raw binary data.
	 *
	 * @see http://livedocs.adobe.com/flex/3/langref/flash/net/Socket.html
	 * @since 0.1.0
	 */
	public class PythonSocket extends Socket
	{
		private var _response			: String;
		private var _log				: String;
		private var _host				: String;
		private var _port				: int;

		public static const CONNECTED	: String = "connected";
		public static const DISCONNECTED: String = "disconnected";
		public static const LOG_UPDATE	: String = "logUpdate";

		public function PythonSocket(host:String='localhost', port:int=8000)
		{
			super(host, port);

			_host = host;
			_port = port;
			_log = "Using Flash Player " + Capabilities.version + "\n";

			objectEncoding = ObjectEncoding.AMF0;
			configureListeners();

			logger("Connecting to socket server on " + _host + ":" + _port);
		}

		public function get log():String
		{
			return _log;
		}

		public function set log(val:String):void
		{
			_log = val;
		}

		private function configureListeners():void
		{
	        addEventListener(Event.CLOSE, closeHandler);
	        addEventListener(Event.CONNECT, connectHandler);
	        addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
	        addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
	        addEventListener(ProgressEvent.SOCKET_DATA, readResponse);
	    }

		public function write(msg:String): void
		{
			sendRequest(msg);
		}

	    private function writeln(str:String):void
	    {
	        str += "\n";

	        try {
	            writeUTFBytes(str);
	        }
	        catch(e:IOError) {
	        	switch (e.errorID) {
	        		case 2002:
	        			// reconnect when connection timed out
	        			if (!connected) {
	        				logger("Reconnecting...");
	        				connect( _host, _port );
	        			}
	        			break;

	        		default:
	        			logger(e.toString());
	        			break;
	        	}
	        }
	    }

	    private function sendRequest(str:String):void
	    {
	        logger("sendRequest: " + str);
	        _response = "";
	        writeln(str);
	        flush();
	    }

	    private function readResponse(event:ProgressEvent):void
	    {
			var result:Object = this.readObject();

	        _response += result;
	        logger(result.toString());
	    }

	    private function logger(msg:String):void
		{
			var newMsg:String = msg + "\n";
			_log += newMsg;

			dispatchEvent(new Event(LOG_UPDATE));
		}

	    private function connectHandler(event:Event):void
	    {
	        logger("Connected to server.\n");

	        dispatchEvent(new Event(CONNECTED));
	    }

	    private function closeHandler(event:Event):void
	    {
	        logger("Connection closed.");

	        dispatchEvent(new Event(DISCONNECTED));
	    }

	    private function ioErrorHandler(event:IOErrorEvent):void
	    {
	        logger("ioErrorHandler: " + event.text);
	    }

	    private function securityErrorHandler(event:SecurityErrorEvent):void
	    {
	        logger("securityErrorHandler: " + event.text);
	    }

	}
}

package org.pyamf.examples.socket
{
	/**
	 * Copyright (c) 2007-2009 The PyAMF Project.
	 * See LICENSE.txt for details.
	 */

	import flash.errors.IOError;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.system.Capabilities;

	[Event(name="connected", type="flash.events.Event")]
	[Event(name="disconnected", type="flash.events.Event")]
	[Event(name="logUpdate", type="flash.events.Event")]
	/**
	 * Socket connection to read and write raw binary data.
	 *
	 * @see http://livedocs.adobe.com/flex/3/langref/flash/net/Socket.html
	 * @since 0.1.0
	 */
	public class PythonSocket extends Socket
	{
		private var _response			: String;
		private var _log				: String;
		private var _host				: String;
		private var _port				: int;

		public static const CONNECTED	: String = "connected";
		public static const DISCONNECTED: String = "disconnected";
		public static const LOG_UPDATE	: String = "logUpdate";

		public function PythonSocket(host:String='localhost', port:int=8000)
		{
			super(host, port);

			_host = host;
			_port = port;
			_log = "Using Flash Player " + Capabilities.version + "\n";

			configureListeners();

			logger("Connecting to socket server on " + _host + ":" + _port);
		}

		public function get log():String
		{
			return _log;
		}

		public function set log(val:String):void
		{
			_log = val;
		}

		private function configureListeners():void
		{
	        addEventListener(Event.CLOSE, closeHandler);
	        addEventListener(Event.CONNECT, connectHandler);
	        addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
	        addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
	        addEventListener(ProgressEvent.SOCKET_DATA, readResponse);
	    }

		public function write(msg:String): void
		{
			sendRequest(msg);
		}

	    private function writeln(str:String):void
	    {
	        str += "\n";

	        try {
	            writeUTFBytes(str);
	        }
	        catch(e:IOError) {
	        	switch (e.errorID) {
	        		case 2002:
	        			// reconnect when connection timed out
	        			if (!connected) {
	        				logger("Reconnecting...");
	        				connect( _host, _port );
	        			}
	        			break;

	        		default:
	        			logger(e.toString());
	        			break;
	        	}
	        }
	    }

	    private function sendRequest(str:String):void
	    {
	        logger("sendRequest: " + str);
	        _response = "";
	        writeln(str);
	        flush();
	    }

	    private function readResponse(event:ProgressEvent):void
	    {
			var result:Object = this.readObject();

	        _response += result;
	        logger(result.toString());
	    }

	    private function logger(msg:String):void
		{
			var newMsg:String = msg + "\n";
			_log += newMsg;

			dispatchEvent(new Event(LOG_UPDATE));
		}

	    private function connectHandler(event:Event):void
	    {
	        logger("Connected to server.\n");

	        dispatchEvent(new Event(CONNECTED));
	    }

	    private function closeHandler(event:Event):void
	    {
	        logger("Connection closed.");

	        dispatchEvent(new Event(DISCONNECTED));
	    }

	    private function ioErrorHandler(event:IOErrorEvent):void
	    {
	        logger("ioErrorHandler: " + event.text);
	    }

	    private function securityErrorHandler(event:SecurityErrorEvent):void
	    {
	        logger("securityErrorHandler: " + event.text);
	    }

	}
}

fix the flex example to use AMF3 by default


//
// Flump - Copyright 2012 Three Rings Design

package flump.export {

import com.threerings.util.F;
import com.threerings.util.Log;
import com.threerings.util.StringUtil;

import flash.desktop.NativeApplication;
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.display.NativeWindow;
import flash.display.Stage;
import flash.display.StageQuality;
import flash.events.Event;
import flash.events.InvokeEvent;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.utils.IDataOutput;

import flump.executor.Executor;
import flump.executor.Future;
import flump.xfl.ParseError;
import flump.xfl.XflLibrary;

import mx.events.PropertyChangeEvent;

import spark.components.DataGrid;
import spark.events.GridSelectionEvent;

import starling.display.Sprite;

public class ExporterController
{
    public static const NA :NativeApplication = NativeApplication.nativeApplication;

    public function ExporterController (win :ExporterWindow, configFile :File = null) {
        Log.setLevel("", Log.INFO);
        _win = win;
        _errorsGrid = _win.errors;
        _flashDocsGrid = _win.libraries;

        _confFile = configFile;
        if (_confFile != null) {
            openConf();
        }

        var curSelection :DocStatus = null;
        _flashDocsGrid.addEventListener(GridSelectionEvent.SELECTION_CHANGE, function (..._) :void {
            log.info("Changed", "selected", _flashDocsGrid.selectedIndices);
            updatePreviewAndExport();

            if (curSelection != null) {
                curSelection.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, updatePreviewAndExport);
            }
            var newSelection :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
            if (newSelection != null) {
                newSelection.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, updatePreviewAndExport);
            }
            curSelection = newSelection;
        });
        _win.reload.addEventListener(MouseEvent.CLICK, F.callback(reloadNow));
        _win.export.addEventListener(MouseEvent.CLICK, function (..._) :void {
            for each (var status :DocStatus in _flashDocsGrid.selectedItems) {
                exportFlashDocument(status);
            }
        });
        _win.preview.addEventListener(MouseEvent.CLICK, function (..._) :void {
            showPreviewWindow(_flashDocsGrid.selectedItem.lib);
        });
        _importChooser = new DirChooser(null, _win.importRoot, _win.browseImport);
        _importChooser.changed.add(setImportDirectory);
        _exportChooser = new DirChooser(null, _win.exportRoot, _win.browseExport);
        _exportChooser.changed.add(reloadNow);

        _importChooser.changed.add(F.callback(updateWindowTitle, true));
        _exportChooser.changed.add(F.callback(updateWindowTitle, true));

        var editFormatsController :EditFormatsController = null;
        _win.editFormats.addEventListener(MouseEvent.CLICK, function (..._) :void {
            if (editFormatsController == null || editFormatsController.closed) {
                editFormatsController = new EditFormatsController(_conf);
                editFormatsController.formatsChanged.add(updateUiFromConf);
            } else {
                editFormatsController.show();
            }
        });

        updateUiFromConf();
        updateWindowTitle(false);
        _win.addEventListener(Event.CLOSE, function (..._) :void { NA.exit(0); });
    }

    protected function setupMenus () :void {

        var fileMenuItem :NativeMenuItem;
        if (NativeApplication.supportsMenu) {
            // Grab the existing menu on macs. Use an index to get it as it's not going to be
            // 'File' in all languages
            fileMenuItem = NA.menu.getItemAt(1);
            // Add a separator before the existing close command
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Sep", /*separator=*/true), 0);
        } else {
            _win.nativeWindow.menu = new NativeMenu();
            fileMenuItem = _win.nativeWindow.menu.addSubmenu(new NativeMenu(), "File");
        }

        // Add save and save as by index to work with the existing items on Mac
        // Mac menus have an existing "Close" item, so everything we add should go ahead of that
        var newMenuItem :NativeMenuItem = fileMenuItem.submenu.addItemAt(new NativeMenuItem("New Project"), 0);
        newMenuItem.keyEquivalent = "n";
        newMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            _confFile = null;
            _conf = new FlumpConf();
            setImportDirectory(null);
            updateUiFromConf();
        });

        var openMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Open Project..."), 1);
        openMenuItem.keyEquivalent = "o";
        openMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            var file :File = new File();
            file.addEventListener(Event.SELECT, function (..._) :void {
                open(file);
            });
            file.browseForOpen("Open Flump Project", [
                new FileFilter("Flump project (*.flump)", "*.flump") ]);
        });
        fileMenuItem.submenu.addItemAt(new NativeMenuItem("Sep", /*separator=*/true), 2);

        NA.addEventListener(InvokeEvent.INVOKE, function (event :InvokeEvent) :void {
            if (event.arguments.length > 0) {
                open(new File(event.arguments[0]));
            }
        });

        const saveMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project"), 3);
        saveMenuItem.keyEquivalent = "s";
        function saveConf () :void {
            Files.write(_confFile, function (out :IDataOutput) :void {
                // Set directories relative to where this file is being saved. Fall back to absolute
                // paths if relative paths aren't possible.
                if (_importChooser.dir != null) {
                    _conf.importDir = _confFile.parent.getRelativePath(_importChooser.dir, /*useDotDot=*/true);
                    if (_conf.importDir == null) _conf.importDir = _importChooser.dir.nativePath;
                }

                if (_exportChooser.dir != null) {
                    _conf.exportDir = _confFile.parent.getRelativePath(_exportChooser.dir, /*useDotDot=*/true);
                    if (_conf.exportDir == null) _conf.exportDir = _exportChooser.dir.nativePath;
                }

                out.writeUTFBytes(JSON.stringify(_conf, null, /*space=*/2));
                updateWindowTitle(false);
            });
        };
        function saveAs (..._) :void {
            var file :File = new File();
            file.addEventListener(Event.SELECT, function (..._) :void {
                // Ensure the filename ends with .flump
                if (!StringUtil.endsWith(file.name.toLowerCase(), ".flump")) {
                    file = file.parent.resolvePath(file.name + ".flump");
                }

                _confFile = file;
                saveConfFilePath();
                saveConf();
            });
            file.browseForSave("Save Flump Configuration");
        };
        saveMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            if (_confFile == null) saveAs();
            else saveConf();
        });

        const saveAsMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project As..."), 4);
        saveAsMenuItem.keyEquivalent = "S";
        saveAsMenuItem.addEventListener(Event.SELECT, saveAs);
    }

    protected function updateWindowTitle (modified :Boolean) :void {
        var name :String = (_confFile != null) ? _confFile.name.replace(/\.flump$/i, "") : "Untitled Project";
        if (modified) name += "*";
        _win.title = name;
    }

    protected function openConf () :void {
        try {
            _conf = FlumpConf.fromJSON(JSONFormat.readJSON(_confFile));
            var dir :String = _confFile.parent.resolvePath(_conf.importDir).nativePath;
            setImportDirectory(new File(dir));
        } catch (e :Error) {
            log.warning("Unable to parse conf", e);
            _errorsGrid.dataProvider.addItem(new ParseError(_confFile.nativePath,
                ParseError.CRIT, "Unable to read configuration"));
            _confFile = null;
        }
    }

    protected function open (file :File) :void {
        _confFile = file;
        saveConfFilePath();
        openConf();
        updateUiFromConf();
        updateWindowTitle(false);
    }

    protected function reloadNow () :void {
        setImportDirectory(_importChooser.dir);
        updatePreviewAndExport();
    }

    protected function updateUiFromConf (..._) :void {
        if (_confFile != null) {
            _importChooser.dir = (_conf.importDir != null) ? _confFile.parent.resolvePath(_conf.importDir) : null;
            _exportChooser.dir = (_conf.exportDir != null) ? _confFile.parent.resolvePath(_conf.exportDir) : null;
        } else {
            _importChooser.dir = null;
            _exportChooser.dir = null;
        }

        var formatNames :Array = [];
        for each (var export :ExportConf in _conf.exports) formatNames.push(export.description);
        _win.formatOverview.text = formatNames.join(", ");

        updateWindowTitle(true);
    }

    protected function updatePreviewAndExport (..._) :void {
        _win.export.enabled = _exportChooser.dir != null && _flashDocsGrid.selectionLength > 0 &&
            _flashDocsGrid.selectedItems.some(function (status :DocStatus, ..._) :Boolean {
                return status.isValid;
            });

        var status :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
        _win.preview.enabled = status != null && status.isValid;

        if (_exportChooser.dir == null) return;
        _conf.exportDir = _confFile == null ? _exportChooser.dir.nativePath : _confFile.parent.getRelativePath(_exportChooser.dir, /*useDotDot=*/true);
    }

    protected function createPublisher () :Publisher {
        if (_exportChooser.dir == null || _conf.exports.length == 0) return null;
        return new Publisher(_exportChooser.dir, _conf);
    }

    protected function saveConfFilePath () :void {
        trace("Conf file is now " + _confFile.nativePath);
        FlumpSettings.configFilePath = _confFile.nativePath;
    }

    protected function setImportDirectory (dir :File) :void {
        _importDirectory = dir;
        _flashDocsGrid.dataProvider.removeAll();
        _errorsGrid.dataProvider.removeAll();
        if (dir == null) {
            return;

        }
        if (_docFinder != null) {
            _docFinder.shutdownNow();
        }
        _docFinder = new Executor();
        findFlashDocuments(dir, _docFinder, true);
        _win.reload.enabled = true;
    }

    protected function showPreviewWindow (lib :XflLibrary) :void {
        if (_previewController == null || _previewWindow.closed || _previewControls.closed) {
            _previewWindow = new PreviewWindow();
            _previewControls = new PreviewControlsWindow();
            _previewWindow.started = function (container :Sprite) :void {
                _previewController = new PreviewController(lib, container, _previewWindow,
                    _previewControls);
            }

            _previewWindow.open();
            _previewControls.open();

            preventWindowClose(_previewWindow.nativeWindow);
            preventWindowClose(_previewControls.nativeWindow);

        } else {
            _previewController.lib = lib;
            _previewWindow.nativeWindow.visible = true;
            _previewControls.nativeWindow.visible = true;
        }

        _previewWindow.orderToFront();
        _previewControls.orderToFront();
    }

    // Causes a window to be hidden, rather than closed, when its close box is clicked
    protected static function preventWindowClose (window :NativeWindow) :void {
        window.addEventListener(Event.CLOSING, function (e :Event) :void {
            e.preventDefault();
            window.visible = false;
        });
    }

    protected function findFlashDocuments (base :File, exec :Executor, ignoreXflAtBase :Boolean = false) :void {
        Files.list(base, exec).succeeded.add(function (files :Array) :void {
            if (exec.isShutdown) return;
            for each (var file :File in files) {
                if (Files.hasExtension(file, "xfl")) {
                    if (ignoreXflAtBase) {
                        _errorsGrid.dataProvider.addItem(new ParseError(base.nativePath,
                            ParseError.CRIT, "The import directory can't be an XFL directory, did you mean " +
                            base.parent.nativePath + "?"));
                    } else addFlashDocument(file);
                    return;
                }
            }
            for each (file in files) {
                if (StringUtil.startsWith(file.name, ".", "RECOVER_")) {
                    continue; // Ignore hidden VCS directories, and recovered backups created by Flash
                }
                if (file.isDirectory) findFlashDocuments(file, exec);
                else addFlashDocument(file);
            }
        });
    }

    protected function exportFlashDocument (status :DocStatus) :void {
        const stage :Stage = NA.activeWindow.stage;
        const prevQuality :String = stage.quality;

        stage.quality = StageQuality.BEST;
        createPublisher().publish(status.lib);

        stage.quality = prevQuality;
        status.updateModified(Ternary.FALSE);
    }

    protected function addFlashDocument (file :File) :void {
        var importPathLen :int = _importDirectory.nativePath.length + 1;
        var name :String = file.nativePath.substring(importPathLen).replace(
            new RegExp("\\" + File.separator, "g"), "/");

        var load :Future;
        switch (Files.getExtension(file)) {
        case "xfl":
            name = name.substr(0, name.lastIndexOf("/"));
            load = new XflLoader().load(name, file.parent);
            break;
        case "fla":
            name = name.substr(0, name.lastIndexOf("."));
            load = new FlaLoader().load(name, file);
            break;
        default:
            // Unsupported file type, ignore
            return;
        }

        const status :DocStatus = new DocStatus(name, Ternary.UNKNOWN, Ternary.UNKNOWN, null);
        _flashDocsGrid.dataProvider.addItem(status);

        load.succeeded.add(function (lib :XflLibrary) :void {
            var pub :Publisher = createPublisher();
            status.lib = lib;
            status.updateModified(Ternary.of(pub == null || pub.modified(lib)));
            for each (var err :ParseError in lib.getErrors()) _errorsGrid.dataProvider.addItem(err);
            status.updateValid(Ternary.of(lib.valid));
        });
        load.failed.add(function (error :Error) :void {
            trace("Failed to load " + file.nativePath + ": " + error);
            status.updateValid(Ternary.FALSE);
            throw error;
        });
    }

    protected var _importDirectory :File;

    protected var _docFinder :Executor;
    protected var _win :ExporterWindow;
    protected var _flashDocsGrid :DataGrid;
    protected var _errorsGrid :DataGrid;
    protected var _exportChooser :DirChooser;
    protected var _importChooser :DirChooser;
    protected var _conf :FlumpConf = new FlumpConf();
    protected var _confFile :File;

    protected var _previewController :PreviewController;
    protected var _previewWindow :PreviewWindow;
    protected var _previewControls :PreviewControlsWindow;

    private static const log :Log = Log.getLog(ExporterController);
}
}

import flash.events.EventDispatcher;

import flump.export.Ternary;
import flump.xfl.XflLibrary;

import mx.core.IPropertyChangeNotifier;
import mx.events.PropertyChangeEvent;

class DocStatus extends EventDispatcher implements IPropertyChangeNotifier {
    public var path :String;
    public var modified :String;
    public var valid :String = PENDING;
    public var lib :XflLibrary;

    public function DocStatus (path :String, modified :Ternary, valid :Ternary, lib :XflLibrary) {
        this.lib = lib;
        this.path = path;
        _uid = path;

        updateModified(modified);
        updateValid(valid);
    }

    public function updateValid (newValid :Ternary) :void {
        changeField("valid", function (..._) :void {
            if (newValid == Ternary.TRUE) valid = YES;
            else if (newValid == Ternary.FALSE) valid = ERROR;
            else valid = PENDING;
        });
    }

    public function get isValid () :Boolean { return valid == YES; }

    public function updateModified (newModified :Ternary) :void {
        changeField("modified", function (..._) :void {
            if (newModified == Ternary.TRUE) modified = YES;
            else if (newModified == Ternary.FALSE) modified = " ";
            else modified = PENDING;
        });
    }

    protected function changeField(fieldName :String, modifier :Function) :void {
        const oldValue :Object = this[fieldName];
        modifier();
        const newValue :Object = this[fieldName];
        dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, fieldName, oldValue, newValue));
    }

    public function get uid () :String { return _uid; }
    public function set uid (uid :String) :void { _uid = uid; }

    protected var _uid :String;

    protected static const PENDING :String = "...";
    protected static const ERROR :String = "ERROR";
    protected static const YES :String = "Yes";
}

//
// Flump - Copyright 2012 Three Rings Design

package flump.export {

import com.threerings.util.F;
import com.threerings.util.Log;
import com.threerings.util.StringUtil;

import flash.desktop.NativeApplication;
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.display.NativeWindow;
import flash.display.Stage;
import flash.display.StageQuality;
import flash.events.Event;
import flash.events.InvokeEvent;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.utils.IDataOutput;

import flump.executor.Executor;
import flump.executor.Future;
import flump.xfl.ParseError;
import flump.xfl.XflLibrary;

import mx.events.PropertyChangeEvent;

import spark.components.DataGrid;
import spark.events.GridSelectionEvent;

import starling.display.Sprite;

public class ExporterController
{
    public static const NA :NativeApplication = NativeApplication.nativeApplication;

    public function ExporterController (win :ExporterWindow, configFile :File = null) {
        Log.setLevel("", Log.INFO);
        _win = win;
        _errorsGrid = _win.errors;
        _flashDocsGrid = _win.libraries;

        _confFile = configFile;
        if (_confFile != null) {
            openConf();
        }

        var curSelection :DocStatus = null;
        _flashDocsGrid.addEventListener(GridSelectionEvent.SELECTION_CHANGE, function (..._) :void {
            log.info("Changed", "selected", _flashDocsGrid.selectedIndices);
            updatePreviewAndExport();

            if (curSelection != null) {
                curSelection.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, updatePreviewAndExport);
            }
            var newSelection :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
            if (newSelection != null) {
                newSelection.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, updatePreviewAndExport);
            }
            curSelection = newSelection;
        });
        _win.reload.addEventListener(MouseEvent.CLICK, F.callback(reloadNow));
        _win.export.addEventListener(MouseEvent.CLICK, function (..._) :void {
            for each (var status :DocStatus in _flashDocsGrid.selectedItems) {
                exportFlashDocument(status);
            }
        });
        _win.preview.addEventListener(MouseEvent.CLICK, function (..._) :void {
            showPreviewWindow(_flashDocsGrid.selectedItem.lib);
        });
        _importChooser = new DirChooser(null, _win.importRoot, _win.browseImport);
        _importChooser.changed.add(setImportDirectory);
        _exportChooser = new DirChooser(null, _win.exportRoot, _win.browseExport);
        _exportChooser.changed.add(reloadNow);

        _importChooser.changed.add(F.callback(updateWindowTitle, true));
        _exportChooser.changed.add(F.callback(updateWindowTitle, true));

        var editFormatsController :EditFormatsController = null;
        _win.editFormats.addEventListener(MouseEvent.CLICK, function (..._) :void {
            if (editFormatsController == null || editFormatsController.closed) {
                editFormatsController = new EditFormatsController(_conf);
                editFormatsController.formatsChanged.add(updateUiFromConf);
            } else {
                editFormatsController.show();
            }
        });

        updateUiFromConf();
        updateWindowTitle(false);
        _win.addEventListener(Event.CLOSE, function (..._) :void { NA.exit(0); });
    }

    protected function setupMenus () :void {
        var fileMenuItem :NativeMenuItem;
        if (NativeApplication.supportsMenu) {
            // Grab the existing menu on macs. Use an index to get it as it's not going to be
            // 'File' in all languages
            fileMenuItem = NA.menu.getItemAt(1);
            // Add a separator before the existing close command
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Sep", /*separator=*/true), 0);
        } else {
            _win.nativeWindow.menu = new NativeMenu();
            fileMenuItem = _win.nativeWindow.menu.addSubmenu(new NativeMenu(), "File");
        }

        // Add save and save as by index to work with the existing items on Mac
        // Mac menus have an existing "Close" item, so everything we add should go ahead of that
        var newMenuItem :NativeMenuItem = fileMenuItem.submenu.addItemAt(new NativeMenuItem("New Project"), 0);
        newMenuItem.keyEquivalent = "n";
        newMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            _confFile = null;
            _conf = new FlumpConf();
            setImportDirectory(null);
            updateUiFromConf();
        });

        var openMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Open Project..."), 1);
        openMenuItem.keyEquivalent = "o";
        openMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            var file :File = new File();
            file.addEventListener(Event.SELECT, function (..._) :void {
                open(file);
            });
            file.browseForOpen("Open Flump Project", [
                new FileFilter("Flump project (*.flump)", "*.flump") ]);
        });
        fileMenuItem.submenu.addItemAt(new NativeMenuItem("Sep", /*separator=*/true), 2);

        NA.addEventListener(InvokeEvent.INVOKE, function (event :InvokeEvent) :void {
            if (event.arguments.length > 0) {
                open(new File(event.arguments[0]));
            }
        });

        const saveMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project"), 3);
        saveMenuItem.keyEquivalent = "s";
        saveMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            if (_confFile == null) saveConfAs();
            else saveConf();
        });

        const saveAsMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project As..."), 4);
        saveAsMenuItem.keyEquivalent = "S";
        saveAsMenuItem.addEventListener(Event.SELECT, saveConfAs);
    }

    protected function updateWindowTitle (modified :Boolean) :void {
        var name :String = (_confFile != null) ? _confFile.name.replace(/\.flump$/i, "") : "Untitled Project";
        if (modified) name += "*";
        _win.title = name;
    }

    protected function openConf () :void {
        try {
            _conf = FlumpConf.fromJSON(JSONFormat.readJSON(_confFile));
            var dir :String = _confFile.parent.resolvePath(_conf.importDir).nativePath;
            setImportDirectory(new File(dir));
        } catch (e :Error) {
            log.warning("Unable to parse conf", e);
            _errorsGrid.dataProvider.addItem(new ParseError(_confFile.nativePath,
                ParseError.CRIT, "Unable to read configuration"));
            _confFile = null;
        }
    }

    protected function saveConf () :void {
        Files.write(_confFile, function (out :IDataOutput) :void {
            // Set directories relative to where this file is being saved. Fall back to absolute
            // paths if relative paths aren't possible.
            if (_importChooser.dir != null) {
                _conf.importDir = _confFile.parent.getRelativePath(_importChooser.dir, /*useDotDot=*/true);
                if (_conf.importDir == null) _conf.importDir = _importChooser.dir.nativePath;
            }

            if (_exportChooser.dir != null) {
                _conf.exportDir = _confFile.parent.getRelativePath(_exportChooser.dir, /*useDotDot=*/true);
                if (_conf.exportDir == null) _conf.exportDir = _exportChooser.dir.nativePath;
            }

            out.writeUTFBytes(JSON.stringify(_conf, null, /*space=*/2));
            updateWindowTitle(false);
        });
    }

    protected function saveConfAs (..._) :void {
        var file :File = new File();
        file.addEventListener(Event.SELECT, function (..._) :void {
            // Ensure the filename ends with .flump
            if (!StringUtil.endsWith(file.name.toLowerCase(), ".flump")) {
                file = file.parent.resolvePath(file.name + ".flump");
            }

            _confFile = file;
            saveConfFilePath();
            saveConf();
        });
        file.browseForSave("Save Flump Configuration");
    };

    protected function open (file :File) :void {
        _confFile = file;
        saveConfFilePath();
        openConf();
        updateUiFromConf();
        updateWindowTitle(false);
    }

    protected function reloadNow () :void {
        setImportDirectory(_importChooser.dir);
        updatePreviewAndExport();
    }

    protected function updateUiFromConf (..._) :void {
        if (_confFile != null) {
            _importChooser.dir = (_conf.importDir != null) ? _confFile.parent.resolvePath(_conf.importDir) : null;
            _exportChooser.dir = (_conf.exportDir != null) ? _confFile.parent.resolvePath(_conf.exportDir) : null;
        } else {
            _importChooser.dir = null;
            _exportChooser.dir = null;
        }

        var formatNames :Array = [];
        for each (var export :ExportConf in _conf.exports) formatNames.push(export.description);
        _win.formatOverview.text = formatNames.join(", ");

        updateWindowTitle(true);
    }

    protected function updatePreviewAndExport (..._) :void {
        _win.export.enabled = _exportChooser.dir != null && _flashDocsGrid.selectionLength > 0 &&
            _flashDocsGrid.selectedItems.some(function (status :DocStatus, ..._) :Boolean {
                return status.isValid;
            });

        var status :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
        _win.preview.enabled = status != null && status.isValid;

        if (_exportChooser.dir == null) return;
        _conf.exportDir = _confFile == null ? _exportChooser.dir.nativePath : _confFile.parent.getRelativePath(_exportChooser.dir, /*useDotDot=*/true);
    }

    protected function createPublisher () :Publisher {
        if (_exportChooser.dir == null || _conf.exports.length == 0) return null;
        return new Publisher(_exportChooser.dir, _conf);
    }

    protected function saveConfFilePath () :void {
        trace("Conf file is now " + _confFile.nativePath);
        FlumpSettings.configFilePath = _confFile.nativePath;
    }

    protected function setImportDirectory (dir :File) :void {
        _importDirectory = dir;
        _flashDocsGrid.dataProvider.removeAll();
        _errorsGrid.dataProvider.removeAll();
        if (dir == null) {
            return;

        }
        if (_docFinder != null) {
            _docFinder.shutdownNow();
        }
        _docFinder = new Executor();
        findFlashDocuments(dir, _docFinder, true);
        _win.reload.enabled = true;
    }

    protected function showPreviewWindow (lib :XflLibrary) :void {
        if (_previewController == null || _previewWindow.closed || _previewControls.closed) {
            _previewWindow = new PreviewWindow();
            _previewControls = new PreviewControlsWindow();
            _previewWindow.started = function (container :Sprite) :void {
                _previewController = new PreviewController(lib, container, _previewWindow,
                    _previewControls);
            }

            _previewWindow.open();
            _previewControls.open();

            preventWindowClose(_previewWindow.nativeWindow);
            preventWindowClose(_previewControls.nativeWindow);

        } else {
            _previewController.lib = lib;
            _previewWindow.nativeWindow.visible = true;
            _previewControls.nativeWindow.visible = true;
        }

        _previewWindow.orderToFront();
        _previewControls.orderToFront();
    }

    // Causes a window to be hidden, rather than closed, when its close box is clicked
    protected static function preventWindowClose (window :NativeWindow) :void {
        window.addEventListener(Event.CLOSING, function (e :Event) :void {
            e.preventDefault();
            window.visible = false;
        });
    }

    protected function findFlashDocuments (base :File, exec :Executor, ignoreXflAtBase :Boolean = false) :void {
        Files.list(base, exec).succeeded.add(function (files :Array) :void {
            if (exec.isShutdown) return;
            for each (var file :File in files) {
                if (Files.hasExtension(file, "xfl")) {
                    if (ignoreXflAtBase) {
                        _errorsGrid.dataProvider.addItem(new ParseError(base.nativePath,
                            ParseError.CRIT, "The import directory can't be an XFL directory, did you mean " +
                            base.parent.nativePath + "?"));
                    } else addFlashDocument(file);
                    return;
                }
            }
            for each (file in files) {
                if (StringUtil.startsWith(file.name, ".", "RECOVER_")) {
                    continue; // Ignore hidden VCS directories, and recovered backups created by Flash
                }
                if (file.isDirectory) findFlashDocuments(file, exec);
                else addFlashDocument(file);
            }
        });
    }

    protected function exportFlashDocument (status :DocStatus) :void {
        const stage :Stage = NA.activeWindow.stage;
        const prevQuality :String = stage.quality;

        stage.quality = StageQuality.BEST;
        createPublisher().publish(status.lib);

        stage.quality = prevQuality;
        status.updateModified(Ternary.FALSE);
    }

    protected function addFlashDocument (file :File) :void {
        var importPathLen :int = _importDirectory.nativePath.length + 1;
        var name :String = file.nativePath.substring(importPathLen).replace(
            new RegExp("\\" + File.separator, "g"), "/");

        var load :Future;
        switch (Files.getExtension(file)) {
        case "xfl":
            name = name.substr(0, name.lastIndexOf("/"));
            load = new XflLoader().load(name, file.parent);
            break;
        case "fla":
            name = name.substr(0, name.lastIndexOf("."));
            load = new FlaLoader().load(name, file);
            break;
        default:
            // Unsupported file type, ignore
            return;
        }

        const status :DocStatus = new DocStatus(name, Ternary.UNKNOWN, Ternary.UNKNOWN, null);
        _flashDocsGrid.dataProvider.addItem(status);

        load.succeeded.add(function (lib :XflLibrary) :void {
            var pub :Publisher = createPublisher();
            status.lib = lib;
            status.updateModified(Ternary.of(pub == null || pub.modified(lib)));
            for each (var err :ParseError in lib.getErrors()) _errorsGrid.dataProvider.addItem(err);
            status.updateValid(Ternary.of(lib.valid));
        });
        load.failed.add(function (error :Error) :void {
            trace("Failed to load " + file.nativePath + ": " + error);
            status.updateValid(Ternary.FALSE);
            throw error;
        });
    }

    protected var _importDirectory :File;

    protected var _docFinder :Executor;
    protected var _win :ExporterWindow;
    protected var _flashDocsGrid :DataGrid;
    protected var _errorsGrid :DataGrid;
    protected var _exportChooser :DirChooser;
    protected var _importChooser :DirChooser;
    protected var _conf :FlumpConf = new FlumpConf();
    protected var _confFile :File;

    protected var _previewController :PreviewController;
    protected var _previewWindow :PreviewWindow;
    protected var _previewControls :PreviewControlsWindow;

    private static const log :Log = Log.getLog(ExporterController);
}
}

import flash.events.EventDispatcher;

import flump.export.Ternary;
import flump.xfl.XflLibrary;

import mx.core.IPropertyChangeNotifier;
import mx.events.PropertyChangeEvent;

class DocStatus extends EventDispatcher implements IPropertyChangeNotifier {
    public var path :String;
    public var modified :String;
    public var valid :String = PENDING;
    public var lib :XflLibrary;

    public function DocStatus (path :String, modified :Ternary, valid :Ternary, lib :XflLibrary) {
        this.lib = lib;
        this.path = path;
        _uid = path;

        updateModified(modified);
        updateValid(valid);
    }

    public function updateValid (newValid :Ternary) :void {
        changeField("valid", function (..._) :void {
            if (newValid == Ternary.TRUE) valid = YES;
            else if (newValid == Ternary.FALSE) valid = ERROR;
            else valid = PENDING;
        });
    }

    public function get isValid () :Boolean { return valid == YES; }

    public function updateModified (newModified :Ternary) :void {
        changeField("modified", function (..._) :void {
            if (newModified == Ternary.TRUE) modified = YES;
            else if (newModified == Ternary.FALSE) modified = " ";
            else modified = PENDING;
        });
    }

    protected function changeField(fieldName :String, modifier :Function) :void {
        const oldValue :Object = this[fieldName];
        modifier();
        const newValue :Object = this[fieldName];
        dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, fieldName, oldValue, newValue));
    }

    public function get uid () :String { return _uid; }
    public function set uid (uid :String) :void { _uid = uid; }

    protected var _uid :String;

    protected static const PENDING :String = "...";
    protected static const ERROR :String = "ERROR";
    protected static const YES :String = "Yes";
}

Move save()/saveAs() into the class body