//------------------------------------------------------------------------------
//
//   Copyright 2016 www.fenhongxiang.com
//   All rights reserved.
//   By :ljh
//
//------------------------------------------------------------------------------

package com.fenhongxiang.cue
{
	import com.fenhongxiang.event.FResizeEvent;
	import com.fenhongxiang.view.FSprite;

	import flash.events.MouseEvent;

	public class CuePoint extends FSprite
	{
		public function CuePoint()
		{
			this.buttonMode = true;
			this.useHandCursor = true;

			border = false;
			backgroudColor = 0xA6A6A6;
			resize(3, 10);
			addEventListeners();
		}

		private var _container:FSprite;
		private var _data:CueData;

		public function get data():CueData
		{
			return _data;
		}

		public function set toogleTip(value:Function):void
		{
			_toogleTip = value;
		}

		private var _toogleTip:Function;

		public function set container(value:FSprite):void
		{
			_container = value;

			if (_container != null)
			{
				_container.addEventListener(FResizeEvent.SIZE_CHANGE, onContainerResize, false, 0, true);

				this.x = (_data == null ? 0 : _data.pos * _container.width) + _container.x;
				this.y = 0;
				this.height = _container.height;

				_container.addChild(this);
			}
		}

		private function onContainerResize(e:FResizeEvent):void
		{
			this.x = (_data == null ? 0 : _data.pos * _container.width) + _container.x;
			this.y = 0;
			this.height = _container.height;
		}

		public function set data(value:CueData):void
		{
			_data = value;
		}

		private function addEventListeners():void
		{
			this.addEventListener(MouseEvent.MOUSE_OUT, hideTip, false, 0, true);
			this.addEventListener(MouseEvent.MOUSE_OVER, showTip, false, 0, true);
		}

		private function hideTip(e:MouseEvent):void
		{
			backgroudColor = 0xA6A6A6;

			if(_toogleTip != null)
			{
				_toogleTip(null);
			}
		}

		private function showTip(e:MouseEvent):void
		{
			backgroudColor = 0xFF0000;

			if(_toogleTip != null)
			{
				_toogleTip(this);
			}
		}
	}
}

//------------------------------------------------------------------------------
//
//   Copyright 2016 www.fenhongxiang.com
//   All rights reserved.
//   By :ljh
//
//------------------------------------------------------------------------------

package com.fenhongxiang.cue
{
	import com.fenhongxiang.event.FResizeEvent;
	import com.fenhongxiang.view.FSprite;

	import flash.events.MouseEvent;

	public class CuePoint extends FSprite
	{
		public function CuePoint()
		{
			this.buttonMode = true;
			this.useHandCursor = true;

			border = false;
			backgroudColor = 0xffb81d;
			resize(3, 10);
			addEventListeners();
		}

		private var _container:FSprite;
		private var _data:CueData;

		public function get data():CueData
		{
			return _data;
		}

		public function set toogleTip(value:Function):void
		{
			_toogleTip = value;
		}

		private var _toogleTip:Function;

		public function set container(value:FSprite):void
		{
			_container = value;

			if (_container != null)
			{
				_container.addEventListener(FResizeEvent.SIZE_CHANGE, onContainerResize, false, 0, true);

				this.x = (_data == null ? 0 : _data.pos * _container.width) + _container.x;
				this.y = 0;
				this.height = _container.height;

				_container.addChild(this);
			}
		}

		private function onContainerResize(e:FResizeEvent):void
		{
			this.x = (_data == null ? 0 : _data.pos * _container.width) + _container.x;
			this.y = 0;
			this.height = _container.height;
		}

		public function set data(value:CueData):void
		{
			_data = value;
		}

		private function addEventListeners():void
		{
			this.addEventListener(MouseEvent.MOUSE_OUT, hideTip, false, 0, true);
			this.addEventListener(MouseEvent.MOUSE_OVER, showTip, false, 0, true);
		}

		private function hideTip(e:MouseEvent):void
		{
			backgroudColor = 0xffb81d;

			if(_toogleTip != null)
			{
				_toogleTip(null);
			}
		}

		private function showTip(e:MouseEvent):void
		{
			backgroudColor = 0xffaf00;

			if(_toogleTip != null)
			{
				_toogleTip(this);
			}
		}
	}
}

update style

update style


//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.ezgame.client {

import flash.events.Event;

import com.threerings.util.Name;

import com.threerings.presents.dobj.AttributeChangedEvent;

import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;

import com.threerings.parlor.game.client.GameController;
import com.threerings.parlor.game.data.GameObject;

import com.threerings.parlor.turn.client.TurnGameController;
import com.threerings.parlor.turn.client.TurnGameControllerDelegate;

import com.threerings.ezgame.data.EZGameObject;

/**
 * A controller for flash games.
 */
public class EZGameController extends GameController
    implements TurnGameController
{
    public function EZGameController ()
    {
        addDelegate(_turnDelegate = new TurnGameControllerDelegate(this));
    }

    /**
     * This is called by the GameControlBackend once it has initialized and made contact with
     * usercode.
     */
    public function userCodeIsConnected (autoReady :Boolean) :void
    {
        if (autoReady) {
            playerIsReady();
        }
    }

    /**
     * Called by the GameControlBackend when the game is ready to start. If the game has ended,
     * this can be called by all clients to start the game anew.
     */
    public function playerIsReady () :void
    {
        playerReady();
    }

    // from PlaceController
    override public function willEnterPlace (plobj :PlaceObject) :void
    {
        _ezObj = (plobj as EZGameObject);

        super.willEnterPlace(plobj);
    }

    // from PlaceController
    override public function didLeavePlace (plobj :PlaceObject) :void
    {
        super.didLeavePlace(plobj);

        _ezObj = null;
    }

    // from TurnGameController
    public function turnDidChange (turnHolder :Name) :void
    {
        _panel.backend.turnDidChange();
    }

    // from GameController
    override public function attributeChanged (event :AttributeChangedEvent) :void
    {
        var name :String = event.getName();
        if (EZGameObject.CONTROLLER_OID == name) {
            _panel.backend.controlDidChange();
        } else if (GameObject.ROUND_ID == name) {
            if ((event.getValue() as int) > 0) {
                _panel.backend.roundStateChanged(true);
            } else {
                _panel.backend.roundStateChanged(false);
            }
        } else {
            super.attributeChanged(event);
        }
    }

    // from GameController
    override protected function playerReady () :void
    {
        // we require the user to be connected, and we redundantly only do this if the user is in
        // the players array
        if (_panel.backend.isConnected()) {
            var bobj :BodyObject = (_ctx.getClient().getClientObject() as BodyObject);
            if (_gobj.getPlayerIndex(bobj.getVisibleName()) != -1) {
                super.playerReady();
            }

        } else {
            log.debug("Waiting to call playerReady, userCode not yet connected.");
        }
    }

    // from GameController
    override protected function gameDidStart () :void
    {
        super.gameDidStart();
        _panel.backend.gameStateChanged(true);
    }

    // from GameController
    override protected function gameDidEnd () :void
    {
        super.gameDidEnd();
        _panel.backend.gameStateChanged(false);
    }

    // from PlaceController
    override protected function createPlaceView (ctx :CrowdContext) :PlaceView
    {
        return new EZGamePanel(ctx, this);
    }

    // from PlaceController
    override protected function didInit () :void
    {
        super.didInit();

        // we can't just assign _panel in createPlaceView() for some exciting reason
        _panel = (_view as EZGamePanel);
    }

    protected var _ezObj :EZGameObject;
    protected var _turnDelegate :TurnGameControllerDelegate;
    protected var _panel :EZGamePanel;
}
}

//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.ezgame.client {

import flash.events.Event;

import com.threerings.util.Name;

import com.threerings.presents.dobj.AttributeChangedEvent;

import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;

import com.threerings.parlor.game.client.GameController;
import com.threerings.parlor.game.data.GameObject;

import com.threerings.parlor.turn.client.TurnGameController;
import com.threerings.parlor.turn.client.TurnGameControllerDelegate;

import com.threerings.ezgame.data.EZGameObject;

/**
 * A controller for flash games.
 */
public class EZGameController extends GameController
    implements TurnGameController
{
    public function EZGameController ()
    {
        addDelegate(_turnDelegate = new TurnGameControllerDelegate(this));
    }

    /**
     * This is called by the GameControlBackend once it has initialized and made contact with
     * usercode.
     */
    public function userCodeIsConnected (autoReady :Boolean) :void
    {
        if (autoReady) {
            // let the game manager know that we're here and we want to start the game now
            playerIsReady();
        } else {
            // let the game manager know that we're here even though we don't want the game to
            // start yet
            _gobj.manager.invoke("playerInRoom");
        }
    }

    /**
     * Called by the GameControlBackend when the game is ready to start. If the game has ended,
     * this can be called by all clients to start the game anew.
     */
    public function playerIsReady () :void
    {
        playerReady();
    }

    // from PlaceController
    override public function willEnterPlace (plobj :PlaceObject) :void
    {
        _ezObj = (plobj as EZGameObject);

        super.willEnterPlace(plobj);
    }

    // from PlaceController
    override public function didLeavePlace (plobj :PlaceObject) :void
    {
        super.didLeavePlace(plobj);

        _ezObj = null;
    }

    // from TurnGameController
    public function turnDidChange (turnHolder :Name) :void
    {
        _panel.backend.turnDidChange();
    }

    // from GameController
    override public function attributeChanged (event :AttributeChangedEvent) :void
    {
        var name :String = event.getName();
        if (EZGameObject.CONTROLLER_OID == name) {
            _panel.backend.controlDidChange();
        } else if (GameObject.ROUND_ID == name) {
            if ((event.getValue() as int) > 0) {
                _panel.backend.roundStateChanged(true);
            } else {
                _panel.backend.roundStateChanged(false);
            }
        } else {
            super.attributeChanged(event);
        }
    }

    // from GameController
    override protected function playerReady () :void
    {
        // we require the user to be connected, and we redundantly only do this if the user is in
        // the players array
        if (_panel.backend.isConnected()) {
            var bobj :BodyObject = (_ctx.getClient().getClientObject() as BodyObject);
            if (_gobj.getPlayerIndex(bobj.getVisibleName()) != -1) {
                super.playerReady();
            }

        } else {
            log.debug("Waiting to call playerReady, userCode not yet connected.");
        }
    }

    // from GameController
    override protected function gameDidStart () :void
    {
        super.gameDidStart();
        _panel.backend.gameStateChanged(true);
    }

    // from GameController
    override protected function gameDidEnd () :void
    {
        super.gameDidEnd();
        _panel.backend.gameStateChanged(false);
    }

    // from PlaceController
    override protected function createPlaceView (ctx :CrowdContext) :PlaceView
    {
        return new EZGamePanel(ctx, this);
    }

    // from PlaceController
    override protected function didInit () :void
    {
        super.didInit();

        // we can't just assign _panel in createPlaceView() for some exciting reason
        _panel = (_view as EZGamePanel);
    }

    protected var _ezObj :EZGameObject;
    protected var _turnDelegate :TurnGameControllerDelegate;
    protected var _panel :EZGamePanel;
}
}

Use playerInRoom() to let the game manager know we've arrived but aren't ready
to start the game yet.

git-svn-id: a3e1eb16dde062992de22c830ed8045c8013209a@419 c613c5cb-e716-0410-b11b-feb51c14d237


/*
#
# Copyright (c) 2008-2010 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any warranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
*/

package com.rpath.raf.util
{
    import flash.utils.Dictionary;

    import mx.core.FlexGlobals;

    public class LifecycleObject
    {
        public function LifecycleObject()
        {
            super();
        }

        // INVALIDATION AND COMMITPROPERTIES PATTERN

        private var invalidatePropertiesFlag:Boolean;

        public function invalidateProperties():void
        {
            if (!invalidatePropertiesFlag)
            {
                invalidatePropertiesFlag = true;
                invalidateObject(this);
            }
        }

        protected function commitProperties():void
        {
            // override this
        }

        // -- INVALIDATION SUPPORT
        private static var invalidObjects:Dictionary = new Dictionary(true);
        private static var validatePending:Boolean = false;

        public static function invalidateObject(obj:LifecycleObject):void
        {
            invalidObjects[obj] = true;

            if (!validatePending)
            {
                validatePending = true;
                FlexGlobals.topLevelApplication.callLater(validateObjects);
            }
        }

        protected static function validateObjects():void
        {
            var invalidQueue:Dictionary = invalidObjects;

            // start a fresh tracker for further invalidations
            // that are a side effect of this pass
            invalidObjects = new Dictionary(true);
            // ready to receive another call
            validatePending = false;

            for (var o:* in invalidQueue)
            {
                var rm:LifecycleObject = o as LifecycleObject;
                if (rm)
                {
                    // clear the flag first, in case we're reentrant
                    // on any given instance
                    rm.invalidatePropertiesFlag = false;
                    rm.commitProperties();
                }
            }

        }

    }
}

/*
#
# Copyright (c) 2008-2010 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any warranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
*/

package com.rpath.raf.util
{
    import flash.utils.Dictionary;

    import mx.core.FlexGlobals;
    import flash.events.EventDispatcher;

    public class LifecycleObject extends EventDispatcher
    {
        public function LifecycleObject()
        {
            super();
        }

        // INVALIDATION AND COMMITPROPERTIES PATTERN

        private var invalidatePropertiesFlag:Boolean;

        public function invalidateProperties():void
        {
            if (!invalidatePropertiesFlag)
            {
                invalidatePropertiesFlag = true;
                invalidateObject(this);
            }
        }

        protected function commitProperties():void
        {
            // override this
        }

        // -- INVALIDATION SUPPORT
        private static var invalidObjects:Dictionary = new Dictionary(true);
        private static var validatePending:Boolean = false;

        public static function invalidateObject(obj:LifecycleObject):void
        {
            invalidObjects[obj] = true;

            if (!validatePending)
            {
                validatePending = true;
                FlexGlobals.topLevelApplication.callLater(validateObjects);
            }
        }

        protected static function validateObjects():void
        {
            var invalidQueue:Dictionary = invalidObjects;

            // start a fresh tracker for further invalidations
            // that are a side effect of this pass
            invalidObjects = new Dictionary(true);
            // ready to receive another call
            validatePending = false;

            for (var o:* in invalidQueue)
            {
                var rm:LifecycleObject = o as LifecycleObject;
                if (rm)
                {
                    // clear the flag first, in case we're reentrant
                    // on any given instance
                    rm.invalidatePropertiesFlag = false;
                    rm.commitProperties();
                }
            }

        }

    }
}

Make LifeCycleObject subclass of EventDispatcher


package com.phylake.fsm.impl
{
    import com.phylake.fsm.core.*;
    import flash.errors.IllegalOperationError;
    import flash.utils.Dictionary;

    public class Fsm implements IFsm
    {
        private var _events:Vector.<IEvent> = new Vector.<IEvent>();
        private var _currentState:IState;
        public var unmappedEventException:Boolean;

        /*
          key: IEvent.name
        value: [ITransition]
        */
        private var _eventMap:Object = {};

        public function init(value:IState):void
        {
            _currentState = value;
        }

        public function get currentState():IState
        {
            return _currentState;
        }

        // TODO validate reachability of states
        public function set states(value:Vector.<IState>):void {}

        public function mapEvent(ie:IEvent, it:ITransition):void
        {
            _eventMap[ie.name] ||= [];
            _eventMap[ie.name].push(it);
        }

        public function pushEvent(value:IEvent):void
        {
            _events.push(value);
            eventLoop();
        }

        protected function executeAction(action:IAction, event:IEvent):void
        {
            if (action)
            {
                action.execute(this, event);
            }
        }

        protected function executeSubmachines(state:IState, event:IEvent):void
        {
            for each ( var fsm:IFsm in state.subMachines )
            {
                fsm.pushEvent(event);
            }
        }

        protected function eventLoop():void
        {
            // with this loop I'm leaving the possibility open to altering
            // _events outside of pushEvent while still clearing the queue
            var event:IEvent;
            var transition:ITransition;
            var foundTransition:ITransition;
            var guard:IGuard;
            var trueGuard:IGuard;//the guard allowing a state transition
            var submachine:IFsm;

            while (event = _events.shift())
            {
                if (!(event.name in _eventMap))
                {
                    if (unmappedEventException)
                    {
                        throw new IllegalOperationError("event" + event.name + " isn't mapped");
                    }
                    continue;
                }

                trueGuard = null;
                foundTransition = null;

                outer: for each (transition in _eventMap[event.name])
                {
                    if (transition.from == currentState)
                    {
                        for each (guard in transition.guards)
                        {
                            if (guard.evaluate(this, event))
                            {
                                if (trueGuard)
                                {
                                    throw new IllegalOperationError("> 1 unguarded transition for " + currentState.id);
                                    // while this is an exception we have a valid transition from which to continue
                                    continue outer;// TODO consider returning instead
                                }

                                trueGuard = guard;
                                foundTransition = transition;
                            }
                        }
                    }
                }

                if (foundTransition)
                {
                    // execute transition action
                    executeAction(foundTransition.action, event);

                    // complete transition
                    _currentState = foundTransition.to;

                    // execute enterState action for the new state
                    executeAction(currentState.enterState, event);
                }

                // execute submachines with current event
                executeSubmachines(currentState, event);
            }
        }
    }
}

package com.phylake.fsm.impl
{
    import com.phylake.fsm.core.*;
    import flash.errors.IllegalOperationError;
    import flash.utils.Dictionary;

    public class Fsm implements IFsm
    {
        private var _events:Vector.<IEvent> = new Vector.<IEvent>();
        private var _currentState:IState;
        public var unmappedEventException:Boolean;

        /*
          key: IEvent.name
        value: [ITransition]
        */
        private var _eventMap:Object = {};

        public function init(value:IState):void
        {
            _currentState = value;
        }

        public function get currentState():IState
        {
            return _currentState;
        }

        // TODO validate reachability of states
        public function set states(value:Vector.<IState>):void {}

        public function mapEvent(ie:IEvent, it:ITransition):void
        {
            _eventMap[ie.name] ||= [];
            _eventMap[ie.name].push(it);
        }

        public function pushEvent(value:IEvent):void
        {
            _events.push(value);
            eventLoop();
        }

        protected function executeAction(action:IAction, event:IEvent):void
        {
            if (action)
            {
                action.execute(this, event);
            }
        }

        protected function executeSubmachines(state:IState, event:IEvent):void
        {
            for each ( var fsm:IFsm in state.subMachines )
            {
                fsm.pushEvent(event);
            }
        }

        protected function eventLoop():void
        {
            // with this loop I'm leaving the possibility open to altering
            // _events outside of pushEvent while still clearing the queue
            var event:IEvent;
            var transition:ITransition;
            var foundTransition:ITransition;
            var guard:IGuard;
            var trueGuard:IGuard;//the guard allowing a state transition
            var submachine:IFsm;

            while (event = _events.shift())
            {
                if (!(event.name in _eventMap))
                {
                    if (unmappedEventException)
                    {
                        throw new IllegalOperationError("event" + event.name + " isn't mapped");
                    }
                    continue;
                }

                trueGuard = null;
                foundTransition = null;

                outer: for each (transition in _eventMap[event.name])
                {
                    if (transition.from == currentState)
                    {
                        for each (guard in transition.guards)
                        {
                            if (guard.evaluate(this, event))
                            {
                                if (trueGuard)
                                {
                                    throw new IllegalOperationError("> 1 unguarded transition for " + currentState.id);
                                    // while this is an exception we have a valid transition from which to continue
                                    break outer;// TODO consider returning instead
                                }

                                trueGuard = guard;
                                foundTransition = transition;
                            }
                        }
                    }
                }

                if (foundTransition)
                {
                    // execute transition action
                    executeAction(foundTransition.action, event);

                    // complete transition
                    _currentState = foundTransition.to;

                    // execute enterState action for the new state
                    executeAction(currentState.enterState, event);
                }

                // execute submachines with current event
                executeSubmachines(currentState, event);
            }
        }
    }
}

break instead

break instead


package aerys.minko.scene.controller
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.animation.TimeLabel;
	import aerys.minko.type.animation.timeline.ITimeline;

	import flash.utils.getTimer;

	public class AnimationController extends AbstractController
	{
		public static var DEFAULT_TIME_FUNCTION	: Function = getTimer;

		private var _timelines		: Vector.<ITimeline>	= new Vector.<ITimeline>();
		private var _isPlaying		: Boolean				= false;
		private var _loopBeginTime	: int					= 0;
		private var _loopEndTime	: int					= 0;
		private var _looping		: Boolean				= true;
		private var _currentTime	: int					= 0;
		private var _totalTime		: int					= 0;

		private var _timeFunction	: Function				= DEFAULT_TIME_FUNCTION;
		private var _labels			: Vector.<TimeLabel>	= null;

		private var _lastTime		: Number				= 0;

		private var _looped			: Signal 				= new Signal();
		private var _started		: Signal				= new Signal();
		private var _stopped		: Signal				= new Signal();

		public function get started():Signal
		{
			return _started;
		}

		public function get stopped() : Signal
		{
			return _stopped;
		}

		public function get looped():Signal
		{
			return _looped;
		}

		public function get totalTime() : int
		{
			return _totalTime;
		}

		public function get looping() : Boolean
		{
			return _looping;
		}
		public function set looping(value : Boolean) : void
		{
			_looping = value;
		}

		public function AnimationController(timelines	: Vector.<ITimeline>)
		{
			super();

			_timelines = timelines;

			initialize();
		}

		public function getTimeline(index : uint = 0) : ITimeline
		{
			return _timelines[index];
		}

		private function initialize() : void
		{
			var numTimelines : uint = _timelines.length;

			for (var timelineId : uint = 0; timelineId < numTimelines; ++timelineId)
				if (_totalTime < _timelines[timelineId].duration)
					_totalTime = _timelines[timelineId].duration;

			setPlaybackWindow(0, _totalTime);
			gotoAndPlay(0);
		}

		public function gotoAndPlay(time : Object) : void
		{
			var timeValue : uint = getAnimationTime(time);

			if (timeValue < _loopBeginTime || timeValue > _loopEndTime)
				throw new Error('Time value is outside of playback window. To reset playback window, call resetPlaybackWindow.');

			_currentTime = timeValue;
			_lastTime = _timeFunction != null ? _timeFunction() : getTimer();
			play();
		}

		public function gotoAndStop(time : Object) : void
		{
			var timeValue : uint = getAnimationTime(time);

			if (timeValue < _loopBeginTime || timeValue > _loopEndTime)
				throw new Error('Time value is outside of playback window. To reset playback window, call resetPlaybackWindow.');

			_currentTime = timeValue;
			_lastTime = _timeFunction != null ? _timeFunction() : getTimer();
			stop();
		}

		public function play() : void
		{
			_isPlaying = true;
			_started.execute(this);
		}

		public function stop() : void
		{
			_isPlaying = false;
			_stopped.execute(this);
		}

		public function setPlaybackWindow(beginTime	: Object = null,
										  endTime	: Object = null) : void
		{
			_loopBeginTime	= beginTime != null ? getAnimationTime(beginTime) : 0;
			_loopEndTime	= endTime != null ? getAnimationTime(endTime) : _totalTime;

			if (_currentTime < _loopBeginTime || _currentTime > _loopEndTime)
				_currentTime = _loopBeginTime;
		}

		public function resetPlaybackWindow() : void
		{
			setPlaybackWindow();
		}

		private function getAnimationTime(time : Object) : uint
		{
			var timeValue : uint;

			if (time is uint || time is int || time is Number)
			{
				timeValue = uint(time);
			}
			else if (time is String)
			{
				var labelCount : uint = _labels.length;

				for (var labelId : uint = 0; labelId < labelCount; ++labelId)
					if (_labels[labelId].name == time)
						timeValue = _labels[labelId].time;
			}
			else
			{
				throw new Error('Invalid argument type. Must be uint or String');
			}

			return timeValue;
		}

		override protected function updateOnTime(time : Number) : Boolean
		{
			if (_isPlaying)
			{
				if (_timeFunction != null)
					time = _timeFunction();

				var deltaT 			: Number = time - _lastTime;
				var lastCurrentTime : Number = _currentTime;

				_currentTime = _loopBeginTime
					+ (_currentTime + deltaT - _loopBeginTime)
					% (_loopEndTime - _loopBeginTime);

				if (lastCurrentTime > _currentTime)
				{
					if (_looping)
						_looped.execute(this);
					else
					{
						_currentTime = _totalTime;
						stop();

						return true;
					}
				}
			}

			_lastTime = time;

			return _isPlaying;
		}

		override protected function updateTarget(target : ISceneNode) : void
		{
			var numTimelines 	: int 	= _timelines.length;
			var group			: Group	= target as Group;

			for (var i : int = 0; i < numTimelines; ++i)
			{
				var timeline : ITimeline = _timelines[i] as ITimeline;

				timeline.updateAt(
					_currentTime % (timeline.duration + 1),
					target
				);
			}
		}
	}
}

package aerys.minko.scene.controller
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.animation.TimeLabel;
	import aerys.minko.type.animation.timeline.ITimeline;

	import flash.utils.getTimer;

	public class AnimationController extends AbstractController
	{

		public function get currentTime():int
		{
			return _currentTime;
		}

		public function get isPlaying():Boolean
		{
			return _isPlaying;
		}

		public static var DEFAULT_TIME_FUNCTION	: Function = getTimer;

		private var _timelines		: Vector.<ITimeline>	= new Vector.<ITimeline>();
		private var _isPlaying		: Boolean				= false;
		private var _loopBeginTime	: int					= 0;
		private var _loopEndTime	: int					= 0;
		private var _looping		: Boolean				= true;
		private var _currentTime	: int					= 0;
		private var _totalTime		: int					= 0;

		private var _timeFunction	: Function				= DEFAULT_TIME_FUNCTION;
		private var _labels			: Vector.<TimeLabel>	= null;

		private var _lastTime		: Number				= 0;

		private var _looped			: Signal 				= new Signal();
		private var _started		: Signal				= new Signal();
		private var _stopped		: Signal				= new Signal();

		public function get started():Signal
		{
			return _started;
		}

		public function get stopped() : Signal
		{
			return _stopped;
		}

		public function get looped():Signal
		{
			return _looped;
		}

		public function get totalTime() : int
		{
			return _totalTime;
		}

		public function get looping() : Boolean
		{
			return _looping;
		}
		public function set looping(value : Boolean) : void
		{
			_looping = value;
		}

		public function AnimationController(timelines	: Vector.<ITimeline>)
		{
			super();

			_timelines = timelines;

			initialize();
		}

		public function getTimeline(index : uint = 0) : ITimeline
		{
			return _timelines[index];
		}

		private function initialize() : void
		{
			var numTimelines : uint = _timelines.length;

			for (var timelineId : uint = 0; timelineId < numTimelines; ++timelineId)
				if (_totalTime < _timelines[timelineId].duration)
					_totalTime = _timelines[timelineId].duration;

			setPlaybackWindow(0, _totalTime);
			gotoAndPlay(0);
		}

		public function gotoAndPlay(time : Object) : void
		{
			var timeValue : uint = getAnimationTime(time);

			if (timeValue < _loopBeginTime || timeValue > _loopEndTime)
				throw new Error('Time value is outside of playback window. To reset playback window, call resetPlaybackWindow.');

			_currentTime = timeValue;
			_lastTime = _timeFunction != null ? _timeFunction() : getTimer();
			play();
		}

		public function gotoAndStop(time : Object) : void
		{
			var timeValue : uint = getAnimationTime(time);

			if (timeValue < _loopBeginTime || timeValue > _loopEndTime)
				throw new Error('Time value is outside of playback window. To reset playback window, call resetPlaybackWindow.');

			_currentTime = timeValue;
			_lastTime = _timeFunction != null ? _timeFunction() : getTimer();
			stop();
		}

		public function play() : void
		{
			_isPlaying = true;
			_started.execute(this);
		}

		public function stop() : void
		{
			_isPlaying = false;
			_stopped.execute(this);
		}

		public function setPlaybackWindow(beginTime	: Object = null,
										  endTime	: Object = null) : void
		{
			_loopBeginTime	= beginTime != null ? getAnimationTime(beginTime) : 0;
			_loopEndTime	= endTime != null ? getAnimationTime(endTime) : _totalTime;

			if (_currentTime < _loopBeginTime || _currentTime > _loopEndTime)
				_currentTime = _loopBeginTime;
		}

		public function resetPlaybackWindow() : void
		{
			setPlaybackWindow();
		}

		private function getAnimationTime(time : Object) : uint
		{
			var timeValue : uint;

			if (time is uint || time is int || time is Number)
			{
				timeValue = uint(time);
			}
			else if (time is String)
			{
				var labelCount : uint = _labels.length;

				for (var labelId : uint = 0; labelId < labelCount; ++labelId)
					if (_labels[labelId].name == time)
						timeValue = _labels[labelId].time;
			}
			else
			{
				throw new Error('Invalid argument type. Must be uint or String');
			}

			return timeValue;
		}

		override protected function updateOnTime(time : Number) : Boolean
		{
			if (_isPlaying)
			{
				if (_timeFunction != null)
					time = _timeFunction();

				var deltaT 			: Number = time - _lastTime;
				var lastCurrentTime : Number = _currentTime;

				_currentTime = _loopBeginTime
					+ (_currentTime + deltaT - _loopBeginTime)
					% (_loopEndTime - _loopBeginTime);

				if (lastCurrentTime > _currentTime)
				{
					if (_looping)
						_looped.execute(this);
					else
					{
						_currentTime = _totalTime;
						stop();

						return true;
					}
				}
			}

			_lastTime = time;

			return _isPlaying;
		}

		override protected function updateTarget(target : ISceneNode) : void
		{
			var numTimelines 	: int 	= _timelines.length;
			var group			: Group	= target as Group;

			for (var i : int = 0; i < numTimelines; ++i)
			{
				var timeline : ITimeline = _timelines[i] as ITimeline;

				timeline.updateAt(
					_currentTime % (timeline.duration + 1),
					target
				);
			}
		}
	}
}

Add getter on totalTime, currentTime and isPlaying properties


package aerys.minko.render.shader.part.phong
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;

	public class LightAwareDiffuseShaderPart extends LightAwareShaderPart
	{
		public function LightAwareDiffuseShaderPart(main:Shader)
		{
			super(main);
		}

		public function getDiffuseColor() : SFloat
		{
			var diffuseColor : SFloat	= null;
			var uv			 : SFloat	= fsUV;

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_MAP))
			{
				var diffuseMap	: SFloat	= meshBindings.getTextureParameter(
					BasicProperties.DIFFUSE_MAP,
					meshBindings.getConstant(BasicProperties.DIFFUSE_MAP_FILTERING, SamplerFiltering.LINEAR),
					meshBindings.getConstant(BasicProperties.DIFFUSE_MAP_MIPMAPPING, SamplerMipMapping.LINEAR),
					meshBindings.getConstant(BasicProperties.DIFFUSE_MAP_WRAPPING, SamplerWrapping.REPEAT)
				);

				diffuseColor = sampleTexture(diffuseMap, uv);
			}
			else if (meshBindings.propertyExists(BasicProperties.DIFFUSE_COLOR))
			{
				diffuseColor = meshBindings.getParameter(BasicProperties.DIFFUSE_COLOR, 4);
			}
			else
			{
				diffuseColor = float4(0., 0., 0., 1.);
			}

			if (meshBindings.propertyExists(BasicProperties.ALPHA_MAP))
			{
				var alphaMap 	: SFloat 	= meshBindings.getTextureParameter(BasicProperties.ALPHA_MAP);
				var alphaSample	: SFloat	= sampleTexture(alphaMap, uv);

				diffuseColor = float4(diffuseColor.rgb, alphaSample.r);
			}

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_TRANSFORM))
			{
				diffuseColor = multiply4x4(
					diffuseColor,
					meshBindings.getParameter(BasicProperties.DIFFUSE_TRANSFORM, 16)
				);
			}

			if (meshBindings.propertyExists(BasicProperties.ALPHA_THRESHOLD))
			{
				var alphaThreshold : SFloat = meshBindings.getParameter(
					BasicProperties.ALPHA_THRESHOLD, 1
				);

				kill(subtract(0.5, lessThan(diffuseColor.w, alphaThreshold)));
			}

			return diffuseColor;
		}
	}
}

package aerys.minko.render.shader.part.phong
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerFormat;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;

	public class LightAwareDiffuseShaderPart extends LightAwareShaderPart
	{
		public function LightAwareDiffuseShaderPart(main:Shader)
		{
			super(main);
		}

		public function getDiffuseColor() : SFloat
		{
			var diffuseColor : SFloat	= null;
			var uv			 : SFloat	= fsUV;

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_MAP))
			{
				var diffuseMap	: SFloat	= meshBindings.getTextureParameter(
					BasicProperties.DIFFUSE_MAP,
					meshBindings.getConstant(BasicProperties.DIFFUSE_MAP_FILTERING, SamplerFiltering.LINEAR),
					meshBindings.getConstant(BasicProperties.DIFFUSE_MAP_MIPMAPPING, SamplerMipMapping.LINEAR),
					meshBindings.getConstant(BasicProperties.DIFFUSE_MAP_WRAPPING, SamplerWrapping.REPEAT),
                    0,
                    meshBindings.getConstant(BasicProperties.DIFFUSE_MAP_FORMAT, SamplerFormat.RGBA)
				);

				diffuseColor = sampleTexture(diffuseMap, uv);
			}
			else if (meshBindings.propertyExists(BasicProperties.DIFFUSE_COLOR))
			{
				diffuseColor = meshBindings.getParameter(BasicProperties.DIFFUSE_COLOR, 4);
			}
			else
			{
				diffuseColor = float4(0., 0., 0., 1.);
			}

			if (meshBindings.propertyExists(BasicProperties.ALPHA_MAP))
			{
				var alphaMap 	: SFloat 	= meshBindings.getTextureParameter(BasicProperties.ALPHA_MAP);
				var alphaSample	: SFloat	= sampleTexture(alphaMap, uv);

				diffuseColor = float4(diffuseColor.rgb, alphaSample.r);
			}

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_TRANSFORM))
			{
				diffuseColor = multiply4x4(
					diffuseColor,
					meshBindings.getParameter(BasicProperties.DIFFUSE_TRANSFORM, 16)
				);
			}

			if (meshBindings.propertyExists(BasicProperties.ALPHA_THRESHOLD))
			{
				var alphaThreshold : SFloat = meshBindings.getParameter(
					BasicProperties.ALPHA_THRESHOLD, 1
				);

				kill(subtract(0.5, lessThan(diffuseColor.w, alphaThreshold)));
			}

			return diffuseColor;
		}
	}
}

fix LightAwareDiffuseShaderPart.getDiffuseColor() to use the BasicProperties.DIFFUSE_MAP_FORMAT mesh binding


package {

  import flash.display.Loader;
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.NetStatusEvent;
  import flash.external.ExternalInterface;
  import flash.media.SoundTransform;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.net.URLRequest;
  import flash.system.Security;

  [SWF(backgroundColor="0x000000")]
  public class Player extends Sprite {

    private var posterUrl: String;
    private var videoUrl: String;
    private var loop: Boolean;
    private var muted: Boolean;
    private var autoplay: Boolean;

    private var videoPlaying: Boolean;

    private var poster: Loader;
    private var video: Video;
    private var posterContainer: Sprite;

    private var connection: NetConnection;
    private var stream: NetStream;

    public function Player() {
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;

      Security.allowDomain("*");
      Security.allowInsecureDomain("*");

      registerExternalMethods();

      posterUrl = loaderInfo.parameters.poster;
      videoUrl = loaderInfo.parameters.video;
      loop = loaderInfo.parameters.loop == "true";
      muted = loaderInfo.parameters.muted == "true";
      autoplay = loaderInfo.parameters.autoplay == "true";

      if (posterUrl) {
        loadPoster();
      }

      loadVideo();
    }

    private function play(): void {
      videoPlaying = true;
      stream.resume();
    }

    private function pause(): void {
      videoPlaying = false;
      stream.pause();
    }

    private function isPaused(): Boolean {
      return !videoPlaying;
    }

    private function mute(): void {
      stream.soundTransform = new SoundTransform(0);
    }

    private function unmute(): void {
      stream.soundTransform = new SoundTransform(1);
    }

    private function isMuted(): Boolean {
      return stream.soundTransform.volume == 0;
    }

    private function registerExternalMethods(): void {
      ExternalInterface.addCallback("play", play);
      ExternalInterface.addCallback("pause", pause);
      ExternalInterface.addCallback("paused", isPaused);
      ExternalInterface.addCallback("mute", mute);
      ExternalInterface.addCallback("unmute", unmute);
      ExternalInterface.addCallback("muted", isMuted);
    }

    private function loadPoster(): void {
      posterContainer = new Sprite();
      posterContainer.width = stage.stageWidth;
      posterContainer.height = stage.stageHeight;
      addChildAt(posterContainer, 0);

      poster = new Loader();
      poster.contentLoaderInfo.addEventListener(Event.COMPLETE, function(): void {
        posterContainer.addChild(poster);
      });

      poster.load(new URLRequest(posterUrl));
    }

    private function loadVideo(): void {
      connection = new NetConnection();
      connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
      connection.connect(null);
    }

    private function netStatusHandler(event: NetStatusEvent): void {
      switch (event.info.code) {
        case "NetConnection.Connect.Success": connectStream(); break;
        case "NetStream.Play.Stop": if (loop) stream.seek(0); break;
      }
    }

    private function connectStream(): void {
      video = new Video(stage.stageWidth, stage.stageHeight);
      addChildAt(video, 1);

      stream = new NetStream(connection);
      stream.bufferTime = 0.5;
      stream.soundTransform = new SoundTransform(muted ? 0 : 1);
      stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
      video.attachNetStream(stream);

      stream.play(videoUrl);
      if (!autoplay) pause();
    }

  }

}

package {

  import flash.display.Loader;
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.NetStatusEvent;
  import flash.external.ExternalInterface;
  import flash.media.SoundTransform;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;
  import flash.net.URLRequest;
  import flash.system.Security;

  [SWF(backgroundColor="0x000000")]
  public class Player extends Sprite {

    private var posterUrl: String;
    private var videoUrl: String;
    private var loop: Boolean;
    private var muted: Boolean;
    private var autoplay: Boolean;

    private var videoPlaying: Boolean;

    private var poster: Loader;
    private var video: Video;
    private var posterContainer: Sprite;

    private var connection: NetConnection;
    private var stream: NetStream;

    public function Player() {
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;

      Security.allowDomain("*");
      Security.allowInsecureDomain("*");

      registerExternalMethods();

      posterUrl = loaderInfo.parameters.poster;
      videoUrl = loaderInfo.parameters.video;
      loop = loaderInfo.parameters.loop == "true";
      muted = loaderInfo.parameters.muted == "true";
      autoplay = loaderInfo.parameters.autoplay == "true";

      if (posterUrl) {
        loadPoster();
      }

      loadVideo();

      if (loaderInfo.parameters.onReady) {
        ExternalInterface.call(loaderInfo.parameters.onReady);
      }
    }

    private function play(): void {
      videoPlaying = true;
      stream.resume();
    }

    private function pause(): void {
      videoPlaying = false;
      stream.pause();
    }

    private function isPaused(): Boolean {
      return !videoPlaying;
    }

    private function mute(): void {
      stream.soundTransform = new SoundTransform(0);
    }

    private function unmute(): void {
      stream.soundTransform = new SoundTransform(1);
    }

    private function isMuted(): Boolean {
      return stream.soundTransform.volume == 0;
    }

    private function registerExternalMethods(): void {
      ExternalInterface.addCallback("play", play);
      ExternalInterface.addCallback("pause", pause);
      ExternalInterface.addCallback("paused", isPaused);
      ExternalInterface.addCallback("mute", mute);
      ExternalInterface.addCallback("unmute", unmute);
      ExternalInterface.addCallback("muted", isMuted);
    }

    private function loadPoster(): void {
      posterContainer = new Sprite();
      posterContainer.width = stage.stageWidth;
      posterContainer.height = stage.stageHeight;
      addChildAt(posterContainer, 0);

      poster = new Loader();
      poster.contentLoaderInfo.addEventListener(Event.COMPLETE, function(): void {
        posterContainer.addChild(poster);
      });

      poster.load(new URLRequest(posterUrl));
    }

    private function loadVideo(): void {
      connection = new NetConnection();
      connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
      connection.connect(null);
    }

    private function netStatusHandler(event: NetStatusEvent): void {
      switch (event.info.code) {
        case "NetConnection.Connect.Success": connectStream(); break;
        case "NetStream.Play.Stop": if (loop) stream.seek(0); break;
      }
    }

    private function connectStream(): void {
      video = new Video(stage.stageWidth, stage.stageHeight);
      addChildAt(video, 1);

      stream = new NetStream(connection);
      stream.bufferTime = 0.5;
      stream.soundTransform = new SoundTransform(muted ? 0 : 1);
      stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
      video.attachNetStream(stream);

      stream.play(videoUrl);
      if (autoplay) {
        videoPlaying = true;
      } else {
        videoPlaying = false;
        pause();
      }
    }

  }

}

add onReady callback

add onReady callback


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.loader {
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.utils.clearTimeout;
    import flash.utils.getTimer;
    import flash.utils.setTimeout;
    import org.mangui.hls.constant.HLSLoaderTypes;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.constant.HLSTypes;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSLoadMetrics;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.model.Fragment;
    import org.mangui.hls.model.Level;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.playlist.DataUri;
    import org.mangui.hls.playlist.Manifest;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    public class LevelLoader {
        /** Reference to the hls framework controller. **/
        private var _hls : HLS;
        /** levels vector. **/
        private var _levels : Vector.<Level>;
        /** Object that fetches the manifest. **/
        private var _urlloader : URLLoader;
        /** Link to the M3U8 file. **/
        private var _url : String;
        /** are all playlists filled ? **/
        private var _canStart : Boolean;
        /** Timeout ID for reloading live playlists. **/
        private var _timeoutID : uint;
        /** Streaming type (live, ondemand). **/
        private var _type : String;
        /** last reload manifest time **/
        private var _reloadPlaylistTimer : uint;
        /** current load level **/
        private var _loadLevel : int;
        /** reference to manifest being loaded **/
        private var _manifestLoading : Manifest;
        /** is this loader closed **/
        private var _closed : Boolean = false;
        /* playlist retry timeout */
        private var _retryTimeout : Number;
        private var _retryCount : int;
        /* alt audio tracks */
        private var _altAudioTracks : Vector.<AltAudioTrack>;
        /* manifest load metrics */
        private var _metrics : HLSLoadMetrics;

        /** Setup the loader. **/
        public function LevelLoader(hls : HLS) {
            _hls = hls;
            _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _levels = new Vector.<Level>();
        }

        public function dispose() : void {
            _close();
            if(_urlloader) {
                _urlloader.removeEventListener(Event.COMPLETE, _loadCompleteHandler);
                _urlloader.removeEventListener(ProgressEvent.PROGRESS, _loadProgressHandler);
                _urlloader.removeEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
                _urlloader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
                _urlloader = null;
            }
            _hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
        }

        /** Loading failed; return errors. **/
        private function _errorHandler(event : ErrorEvent) : void {
            var txt : String;
            var code : int;
            if (event is SecurityErrorEvent) {
                code = HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR;
                txt = "Cannot load M3U8: crossdomain access denied:" + event.text;
            } else if (event is IOErrorEvent && (HLSSettings.manifestLoadMaxRetry == -1 || _retryCount < HLSSettings.manifestLoadMaxRetry)) {
                CONFIG::LOGGING {
                    Log.warn("I/O Error while trying to load Playlist, retry in " + _retryTimeout + " ms");
                }
                if(_levels.length) {
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, _retryTimeout);
                } else {
                    _timeoutID = setTimeout(_loadManifest, _retryTimeout);
                }
                /* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
                _retryTimeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retryTimeout);
                _retryCount++;
                return;
            } else {
                // if we have redundant streams left for that level, switch to it
                if(_loadLevel < _levels.length && _levels[_loadLevel].redundantStreamId < _levels[_loadLevel].redundantStreamsNb) {
                    CONFIG::LOGGING {
                        Log.warn("max load retry reached, switch to redundant stream");
                    }
                    _levels[_loadLevel].redundantStreamId++;
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, 0);
                    _retryTimeout = 1000;
                    _retryCount = 0;
                    return;
                } else {
                    code = HLSError.MANIFEST_LOADING_IO_ERROR;
                    txt = "Cannot load M3U8: " + event.text;
                }
            }
            var hlsError : HLSError = new HLSError(code, _url, txt);
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
        }

        /** Return the current manifest. **/
        public function get levels() : Vector.<Level> {
            return _levels;
        }

        /** Return the stream type. **/
        public function get type() : String {
            return _type;
        }

        public function get altAudioTracks() : Vector.<AltAudioTrack> {
            return _altAudioTracks;
        }

        /** Load the manifest file. **/
        public function load(url : String) : void {
            if(!_urlloader) {
                //_urlloader = new URLLoader();
                var urlLoaderClass : Class = _hls.URLloader as Class;
                _urlloader = (new urlLoaderClass()) as URLLoader;
                _urlloader.addEventListener(Event.COMPLETE, _loadCompleteHandler);
                _urlloader.addEventListener(ProgressEvent.PROGRESS, _loadProgressHandler);
                _urlloader.addEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
                _urlloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
            }
            _close();
            _closed = false;
            _url = url;
            _levels = new Vector.<Level>();
            _canStart = false;
            _reloadPlaylistTimer = getTimer();
            _retryTimeout = 1000;
            _retryCount = 0;
            _altAudioTracks = null;
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADING, url));
            _metrics = new HLSLoadMetrics(HLSLoaderTypes.MANIFEST);
            _metrics.loading_request_time = getTimer();
            if (DataUri.isDataUri(url)) {
                CONFIG::LOGGING {
                    Log.debug("Identified main manifest <" + url + "> as a data URI.");
                }
                _metrics.loading_begin_time = getTimer();
                var data : String = new DataUri(url).extractData();
                _metrics.loading_end_time = getTimer();
                _parseManifest(data || "");
            } else {
                _urlloader.load(new URLRequest(url));
            }
        }

        /** loading progress handler, use to determine loading latency **/
        private function _loadProgressHandler(event : Event) : void {
            if(_metrics.loading_begin_time == 0) {
                _metrics.loading_begin_time = getTimer();
            }
        }

        /** Manifest loaded; check and parse it **/
        private function _loadCompleteHandler(event : Event) : void {
             _metrics.loading_end_time = getTimer();
            // successful loading, reset retry counter
            _retryTimeout = 1000;
            _retryCount = 0;
            _parseManifest(String(_urlloader.data));
        }

        /** parse a playlist **/
        private function _parseLevelPlaylist(string : String, url : String, level : int, metrics : HLSLoadMetrics) : void {
            var frags : Vector.<Fragment>;
            if (string != null) {
                CONFIG::LOGGING {
                    Log.debug("level " + level + " playlist:\n" + string);
                }
                frags = Manifest.getFragments(string, url, level);
            }

            if(frags && frags.length) {
                // successful loading, reset retry counter
                _retryTimeout = 1000;
                _retryCount = 0;
                // set fragment and update sequence number range
                _levels[level].updateFragments(frags);
                _levels[level].targetduration = Manifest.getTargetDuration(string);
                _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYLIST_DURATION_UPDATED, _levels[level].duration));
            } else {
                if(HLSSettings.manifestLoadMaxRetry == -1 || _retryCount < HLSSettings.manifestLoadMaxRetry) {
                    CONFIG::LOGGING {
                        Log.warn("empty level Playlist, retry in " + _retryTimeout + " ms");
                    }
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, _retryTimeout);
                    /* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
                    _retryTimeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retryTimeout);
                    _retryCount++;
                    return;
                } else {
                    var hlsError : HLSError = new HLSError(HLSError.MANIFEST_LOADING_IO_ERROR, _url, "no fragments in playlist");
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
                    return;
                }
            }
            // Check whether the stream is live or not finished yet
            if (Manifest.hasEndlist(string)) {
                setType(HLSTypes.VOD);
                _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_ENDLIST, level));
            } else {
                setType(HLSTypes.LIVE);
                /* in order to determine playlist reload timer,
                    check playback position against playlist duration.
                    if we are near the edge of a live playlist, reload playlist quickly
                    to discover quicker new fragments and avoid buffer starvation.
                */
                var _reloadInterval : Number = 1000*Math.min((_levels[level].duration - _hls.position)/2,_levels[level].averageduration);
                // avoid spamming the server if we are at the edge ... wait 500ms between 2 reload at least
                var timeout : int = Math.max(500, _reloadPlaylistTimer + _reloadInterval - getTimer());
                CONFIG::LOGGING {
                    Log.debug("Level " + level + " Live Playlist parsing finished: reload in " + timeout + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, timeout);
            }
            if (!_canStart) {
                _canStart = (_levels[level].fragments.length > 0);
                if (_canStart) {
                    CONFIG::LOGGING {
                        Log.debug("first level filled with at least 1 fragment, notify event");
                    }
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADED, _levels, _metrics));
                }
            }
            metrics.id  = _levels[level].start_seqnum;
            metrics.id2 = _levels[level].end_seqnum;
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADED, metrics));
            _manifestLoading = null;
        }

        /** Parse First Level Playlist **/
        private function _parseManifest(string : String) : void {
            var errorTxt : String = null;
            // Check for M3U8 playlist or manifest.
            if (string.indexOf(Manifest.HEADER) == 0) {
                // 1 level playlist, create unique level and parse playlist
                if (string.indexOf(Manifest.FRAGMENT) > 0) {
                    var level : Level = new Level();
                    level.urls = new Vector.<String>();
                    level.urls.push(_url);
                    _levels.push(level);
                    _metrics.parsing_end_time = getTimer();
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, 0));
                    CONFIG::LOGGING {
                        Log.debug("1 Level Playlist, load it");
                    }
                    _loadLevel = 0;
                    _metrics.type = HLSLoaderTypes.LEVEL_MAIN;
                    _parseLevelPlaylist(string, _url, 0,_metrics);
                } else if (string.indexOf(Manifest.LEVEL) > 0) {
                    CONFIG::LOGGING {
                        Log.debug("adaptive playlist:\n" + string);
                    }
                    // adaptative playlist, extract levels from playlist, get them and parse them
                    _levels = Manifest.extractLevels(string, _url);
                    if (_levels.length) {
                        _metrics.parsing_end_time = getTimer();
                        _loadLevel = -1;
                        _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                        if (string.indexOf(Manifest.ALTERNATE_AUDIO) > 0) {
                            CONFIG::LOGGING {
                                Log.debug("alternate audio level found");
                            }
                            // parse alternate audio tracks
                            _altAudioTracks = Manifest.extractAltAudioTracks(string, _url);
                            CONFIG::LOGGING {
                                if (_altAudioTracks.length > 0) {
                                    Log.debug(_altAudioTracks.length + " alternate audio tracks found");
                                }
                            }
                        }
                    } else {
                        errorTxt = "No level found in Manifest";
                    }
                } else {
                    // manifest start with correct header, but it does not contain any fragment or level info ...
                    errorTxt = "empty Manifest";
                }
            } else {
                errorTxt = "Manifest is not a valid M3U8 file";
            }
            if(errorTxt) {
                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, new HLSError(HLSError.MANIFEST_PARSING_ERROR, _url, errorTxt)));
            }
        }

        /** load/reload manifest **/
        private function _loadManifest() : void {
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADING, _url));
            _metrics = new HLSLoadMetrics(HLSLoaderTypes.MANIFEST);
            _metrics.loading_request_time = getTimer();
            if (DataUri.isDataUri(_url)) {
                CONFIG::LOGGING {
                    Log.debug("Identified manifest <" + _url + "> as a data URI.");
                }
                _metrics.loading_begin_time = getTimer();
                var data : String = new DataUri(_url).extractData();
                _metrics.loading_end_time = getTimer();
                _parseManifest(data || "");
            } else {
                _urlloader.load(new URLRequest(_url));
            }
        }

        /** load/reload active M3U8 playlist **/
        private function _loadActiveLevelPlaylist() : void {
            if (_closed) {
                return;
            }
            _reloadPlaylistTimer = getTimer();
            // load active M3U8 playlist only
            _manifestLoading = new Manifest();
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, _loadLevel));
            _manifestLoading.loadPlaylist(_hls,_levels[_loadLevel].url, _parseLevelPlaylist, _errorHandler, _loadLevel, _type, HLSSettings.flushLiveURLCache);
        }

        /** When level switch occurs, assess the need of (re)loading new level playlist **/
        private function _levelSwitchHandler(event : HLSEvent) : void {
            if (_loadLevel != event.level || _levels[_loadLevel].fragments.length == 0) {
                _loadLevel = event.level;
                CONFIG::LOGGING {
                    Log.debug("switch to level " + _loadLevel);
                }
                if (type == HLSTypes.LIVE || _levels[_loadLevel].fragments.length == 0) {
                    _closed = false;
                    CONFIG::LOGGING {
                        Log.debug("(re)load Playlist");
                    }
                    if(_manifestLoading) {
                       _manifestLoading.close();
                       _manifestLoading = null;
                    }
                    clearTimeout(_timeoutID);
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, 0);
                }
            }
        }

        private function _close() : void {
            CONFIG::LOGGING {
                Log.debug("cancel any manifest load in progress");
            }
            _closed = true;
            clearTimeout(_timeoutID);
            try {
                _urlloader.close();
                if (_manifestLoading) {
                    _manifestLoading.close();
                }
            } catch(e : Error) {
            }
        }

        /** When the framework idles out, stop reloading manifest **/
        private function _stateHandler(event : HLSEvent) : void {
            if (event.state == HLSPlayStates.IDLE) {
                _close();
            }
        }

        private function setType(value: String):void{
            if(value != _type){
                _type = value;

                CONFIG::LOGGING {
                    Log.debug("Stream type did change to " + value);
                }

                _hls.dispatchEvent(new HLSEvent(HLSEvent.STREAM_TYPE_DID_CHANGE, _type));
            }
        }
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.loader {
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.utils.clearTimeout;
    import flash.utils.getTimer;
    import flash.utils.setTimeout;
    import org.mangui.hls.constant.HLSLoaderTypes;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.constant.HLSTypes;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSLoadMetrics;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.model.Fragment;
    import org.mangui.hls.model.Level;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.playlist.DataUri;
    import org.mangui.hls.playlist.Manifest;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    public class LevelLoader {
        /** Reference to the hls framework controller. **/
        private var _hls : HLS;
        /** levels vector. **/
        private var _levels : Vector.<Level>;
        /** Object that fetches the manifest. **/
        private var _urlloader : URLLoader;
        /** Link to the M3U8 file. **/
        private var _url : String;
        /** are all playlists filled ? **/
        private var _canStart : Boolean;
        /** Timeout ID for reloading live playlists. **/
        private var _timeoutID : uint;
        /** Streaming type (live, ondemand). **/
        private var _type : String;
        /** last reload manifest time **/
        private var _reloadPlaylistTimer : uint;
        /** current load level **/
        private var _loadLevel : int;
        /** reference to manifest being loaded **/
        private var _manifestLoading : Manifest;
        /** is this loader closed **/
        private var _closed : Boolean = false;
        /* playlist retry timeout */
        private var _retryTimeout : Number;
        private var _retryCount : int;
        /* alt audio tracks */
        private var _altAudioTracks : Vector.<AltAudioTrack>;
        /* manifest load metrics */
        private var _metrics : HLSLoadMetrics;

        /** Setup the loader. **/
        public function LevelLoader(hls : HLS) {
            _hls = hls;
            _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _levels = new Vector.<Level>();
        }

        public function dispose() : void {
            _close();
            if(_urlloader) {
                _urlloader.removeEventListener(Event.COMPLETE, _loadCompleteHandler);
                _urlloader.removeEventListener(ProgressEvent.PROGRESS, _loadProgressHandler);
                _urlloader.removeEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
                _urlloader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
                _urlloader = null;
            }
            _hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
        }

        /** Loading failed; return errors. **/
        private function _errorHandler(event : ErrorEvent) : void {
            var txt : String;
            var code : int;
            if (event is SecurityErrorEvent) {
                code = HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR;
                txt = "Cannot load M3U8: crossdomain access denied:" + event.text;
            } else if (event is IOErrorEvent && (HLSSettings.manifestLoadMaxRetry == -1 || _retryCount < HLSSettings.manifestLoadMaxRetry)) {
                CONFIG::LOGGING {
                    Log.warn("I/O Error while trying to load Playlist, retry in " + _retryTimeout + " ms");
                }
                if(_levels.length) {
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, _retryTimeout);
                } else {
                    _timeoutID = setTimeout(_loadManifest, _retryTimeout);
                }
                /* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
                _retryTimeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retryTimeout);
                _retryCount++;
                return;
            } else {
                // if we have redundant streams left for that level, switch to it
                if(_loadLevel < _levels.length && _levels[_loadLevel].redundantStreamId < _levels[_loadLevel].redundantStreamsNb) {
                    CONFIG::LOGGING {
                        Log.warn("max load retry reached, switch to redundant stream");
                    }
                    _levels[_loadLevel].redundantStreamId++;
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, 0);
                    _retryTimeout = 1000;
                    _retryCount = 0;
                    return;
                } else {
                    code = HLSError.MANIFEST_LOADING_IO_ERROR;
                    txt = "Cannot load M3U8: " + event.text;
                }
            }
            var hlsError : HLSError = new HLSError(code, _url, txt);
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
        }

        /** Return the current manifest. **/
        public function get levels() : Vector.<Level> {
            return _levels;
        }

        /** Return the stream type. **/
        public function get type() : String {
            return _type;
        }

        public function get altAudioTracks() : Vector.<AltAudioTrack> {
            return _altAudioTracks;
        }

        /** Load the manifest file. **/
        public function load(url : String) : void {
            if(!_urlloader) {
                //_urlloader = new URLLoader();
                var urlLoaderClass : Class = _hls.URLloader as Class;
                _urlloader = (new urlLoaderClass()) as URLLoader;
                _urlloader.addEventListener(Event.COMPLETE, _loadCompleteHandler);
                _urlloader.addEventListener(ProgressEvent.PROGRESS, _loadProgressHandler);
                _urlloader.addEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
                _urlloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
            }
            _close();
            _closed = false;
            _url = url;
            _levels = new Vector.<Level>();
            _canStart = false;
            _reloadPlaylistTimer = getTimer();
            _retryTimeout = 1000;
            _retryCount = 0;
            _altAudioTracks = null;
            _loadManifest();
        }

        /** loading progress handler, use to determine loading latency **/
        private function _loadProgressHandler(event : Event) : void {
            if(_metrics.loading_begin_time == 0) {
                _metrics.loading_begin_time = getTimer();
            }
        }

        /** Manifest loaded; check and parse it **/
        private function _loadCompleteHandler(event : Event) : void {
             _metrics.loading_end_time = getTimer();
            // successful loading, reset retry counter
            _retryTimeout = 1000;
            _retryCount = 0;
            _parseManifest(String(_urlloader.data));
        }

        /** parse a playlist **/
        private function _parseLevelPlaylist(string : String, url : String, level : int, metrics : HLSLoadMetrics) : void {
            var frags : Vector.<Fragment>;
            if (string != null) {
                CONFIG::LOGGING {
                    Log.debug("level " + level + " playlist:\n" + string);
                }
                frags = Manifest.getFragments(string, url, level);
            }

            if(frags && frags.length) {
                // successful loading, reset retry counter
                _retryTimeout = 1000;
                _retryCount = 0;
                // set fragment and update sequence number range
                _levels[level].updateFragments(frags);
                _levels[level].targetduration = Manifest.getTargetDuration(string);
                _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYLIST_DURATION_UPDATED, _levels[level].duration));
            } else {
                if(HLSSettings.manifestLoadMaxRetry == -1 || _retryCount < HLSSettings.manifestLoadMaxRetry) {
                    CONFIG::LOGGING {
                        Log.warn("empty level Playlist, retry in " + _retryTimeout + " ms");
                    }
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, _retryTimeout);
                    /* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
                    _retryTimeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retryTimeout);
                    _retryCount++;
                    return;
                } else {
                    var hlsError : HLSError = new HLSError(HLSError.MANIFEST_LOADING_IO_ERROR, _url, "no fragments in playlist");
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
                    return;
                }
            }
            // Check whether the stream is live or not finished yet
            if (Manifest.hasEndlist(string)) {
                setType(HLSTypes.VOD);
                _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_ENDLIST, level));
            } else {
                setType(HLSTypes.LIVE);
                /* in order to determine playlist reload timer,
                    check playback position against playlist duration.
                    if we are near the edge of a live playlist, reload playlist quickly
                    to discover quicker new fragments and avoid buffer starvation.
                */
                var _reloadInterval : Number = 1000*Math.min((_levels[level].duration - _hls.position)/2,_levels[level].averageduration);
                // avoid spamming the server if we are at the edge ... wait 500ms between 2 reload at least
                var timeout : int = Math.max(500, _reloadPlaylistTimer + _reloadInterval - getTimer());
                CONFIG::LOGGING {
                    Log.debug("Level " + level + " Live Playlist parsing finished: reload in " + timeout + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, timeout);
            }
            if (!_canStart) {
                _canStart = (_levels[level].fragments.length > 0);
                if (_canStart) {
                    CONFIG::LOGGING {
                        Log.debug("first level filled with at least 1 fragment, notify event");
                    }
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADED, _levels, _metrics));
                }
            }
            metrics.id  = _levels[level].start_seqnum;
            metrics.id2 = _levels[level].end_seqnum;
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADED, metrics));
            _manifestLoading = null;
        }

        /** Parse First Level Playlist **/
        private function _parseManifest(string : String) : void {
            var errorTxt : String = null;
            // Check for M3U8 playlist or manifest.
            if (string.indexOf(Manifest.HEADER) == 0) {
                // 1 level playlist, create unique level and parse playlist
                if (string.indexOf(Manifest.FRAGMENT) > 0) {
                    var level : Level = new Level();
                    level.urls = new Vector.<String>();
                    level.urls.push(_url);
                    _levels.push(level);
                    _metrics.parsing_end_time = getTimer();
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, 0));
                    CONFIG::LOGGING {
                        Log.debug("1 Level Playlist, load it");
                    }
                    _loadLevel = 0;
                    _metrics.type = HLSLoaderTypes.LEVEL_MAIN;
                    _parseLevelPlaylist(string, _url, 0,_metrics);
                } else if (string.indexOf(Manifest.LEVEL) > 0) {
                    CONFIG::LOGGING {
                        Log.debug("adaptive playlist:\n" + string);
                    }
                    // adaptative playlist, extract levels from playlist, get them and parse them
                    _levels = Manifest.extractLevels(string, _url);
                    if (_levels.length) {
                        _metrics.parsing_end_time = getTimer();
                        _loadLevel = -1;
                        _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                        if (string.indexOf(Manifest.ALTERNATE_AUDIO) > 0) {
                            CONFIG::LOGGING {
                                Log.debug("alternate audio level found");
                            }
                            // parse alternate audio tracks
                            _altAudioTracks = Manifest.extractAltAudioTracks(string, _url);
                            CONFIG::LOGGING {
                                if (_altAudioTracks.length > 0) {
                                    Log.debug(_altAudioTracks.length + " alternate audio tracks found");
                                }
                            }
                        }
                    } else {
                        errorTxt = "No level found in Manifest";
                    }
                } else {
                    // manifest start with correct header, but it does not contain any fragment or level info ...
                    errorTxt = "empty Manifest";
                }
            } else {
                errorTxt = "Manifest is not a valid M3U8 file";
            }
            if(errorTxt) {
                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, new HLSError(HLSError.MANIFEST_PARSING_ERROR, _url, errorTxt)));
            }
        }

        /** load/reload manifest **/
        private function _loadManifest() : void {
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADING, _url));
            _metrics = new HLSLoadMetrics(HLSLoaderTypes.MANIFEST);
            _metrics.loading_request_time = getTimer();
            if (DataUri.isDataUri(_url)) {
                CONFIG::LOGGING {
                    Log.debug("Identified manifest <" + _url + "> as a data URI.");
                }
                _metrics.loading_begin_time = getTimer();
                var data : String = new DataUri(_url).extractData();
                _metrics.loading_end_time = getTimer();
                _parseManifest(data || "");
            } else {
                _urlloader.load(new URLRequest(_url));
            }
        }

        /** load/reload active M3U8 playlist **/
        private function _loadActiveLevelPlaylist() : void {
            if (_closed) {
                return;
            }
            _reloadPlaylistTimer = getTimer();
            // load active M3U8 playlist only
            _manifestLoading = new Manifest();
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, _loadLevel));
            _manifestLoading.loadPlaylist(_hls,_levels[_loadLevel].url, _parseLevelPlaylist, _errorHandler, _loadLevel, _type, HLSSettings.flushLiveURLCache);
        }

        /** When level switch occurs, assess the need of (re)loading new level playlist **/
        private function _levelSwitchHandler(event : HLSEvent) : void {
            if (_loadLevel != event.level || _levels[_loadLevel].fragments.length == 0) {
                _loadLevel = event.level;
                CONFIG::LOGGING {
                    Log.debug("switch to level " + _loadLevel);
                }
                if (type == HLSTypes.LIVE || _levels[_loadLevel].fragments.length == 0) {
                    _closed = false;
                    CONFIG::LOGGING {
                        Log.debug("(re)load Playlist");
                    }
                    if(_manifestLoading) {
                       _manifestLoading.close();
                       _manifestLoading = null;
                    }
                    clearTimeout(_timeoutID);
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, 0);
                }
            }
        }

        private function _close() : void {
            CONFIG::LOGGING {
                Log.debug("cancel any manifest load in progress");
            }
            _closed = true;
            clearTimeout(_timeoutID);
            try {
                _urlloader.close();
                if (_manifestLoading) {
                    _manifestLoading.close();
                }
            } catch(e : Error) {
            }
        }

        /** When the framework idles out, stop reloading manifest **/
        private function _stateHandler(event : HLSEvent) : void {
            if (event.state == HLSPlayStates.IDLE) {
                _close();
            }
        }

        private function setType(value: String):void{
            if(value != _type){
                _type = value;

                CONFIG::LOGGING {
                    Log.debug("Stream type did change to " + value);
                }

                _hls.dispatchEvent(new HLSEvent(HLSEvent.STREAM_TYPE_DID_CHANGE, _type));
            }
        }
    }
}

fix wrong merge commit https://github.com/mangui/flashls/commit/533d1336dd0be7f586a0b709d67f9fcebddc46e3#diff-ea71d040ba29d01d23b9f8d40a0174b0R136


/*
 * Copyright 2014 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *totalMemory
 *     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 flash.utils {

  import flash.events.Event;
  import flash.events.TimerEvent;

  internal final class SetIntervalTimer extends Timer {

    internal var reference:uint;
    private var closure:Function;
    private var rest:Array;
    private static var intervalArray:Array = [];

    public function SetIntervalTimer(closure:Function, delay:Number, repeats:Boolean, rest:Array) {

      super(delay, repeats ? 1 : 0);

      this.closure = closure;
      this.rest = rest;
      reference = intervalArray.push(this);

      addEventListener(TimerEvent.TIMER, onTimer);
      start();
    }

    private function onTimer(event:Event):void {
      closure.apply(null, rest);

      if (1 == repeatCount)
      {
        if (this == intervalArray[reference-1])
        {
          delete intervalArray[reference-1];
        }
      }
    }

    internal static function _clearInterval(id:uint):void {
      var index = id - 1;
      if (intervalArray[index] is SetIntervalTimer) {
        intervalArray[index].stop();
        delete intervalArray[index];
      }
    }
  }

  public function setTimeout(closure:Function, delay:Number, ... args):uint
  {
    return new SetIntervalTimer(closure, delay, false, args).reference;
  }

  public function clearTimeout(id:uint):void {
    SetIntervalTimer._clearInterval(id);
  }

  public function setInterval(closure:Function, delay:Number, ... args):uint
  {
    return new SetIntervalTimer(closure, delay, true, args).reference;
  }

  public function clearInterval(id:uint):void {
    SetIntervalTimer._clearInterval(id);
  }

}

/*
 * Copyright 2014 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *totalMemory
 *     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 flash.utils {

  import flash.events.Event;
  import flash.events.TimerEvent;

  internal final class SetIntervalTimer extends Timer {

    internal var reference:uint;
    private var closure:Function;
    private var rest:Array;
    private static var intervalArray:Array = [];

    public function SetIntervalTimer(closure:Function, delay:Number, repeats:Boolean, rest:Array) {

      super(delay, repeats ? 0 : 1);

      this.closure = closure;
      this.rest = rest;
      reference = intervalArray.push(this);

      addEventListener(TimerEvent.TIMER, onTimer);
      start();
    }

    private function onTimer(event:Event):void {
      closure.apply(null, rest);

      if (1 == repeatCount)
      {
        if (this == intervalArray[reference-1])
        {
          delete intervalArray[reference-1];
        }
      }
    }

    internal static function _clearInterval(id:uint):void {
      var index = id - 1;
      if (intervalArray[index] is SetIntervalTimer) {
        intervalArray[index].stop();
        delete intervalArray[index];
      }
    }
  }

  public function setTimeout(closure:Function, delay:Number, ... args):uint
  {
    return new SetIntervalTimer(closure, delay, false, args).reference;
  }

  public function clearTimeout(id:uint):void {
    SetIntervalTimer._clearInterval(id);
  }

  public function setInterval(closure:Function, delay:Number, ... args):uint
  {
    return new SetIntervalTimer(closure, delay, true, args).reference;
  }

  public function clearInterval(id:uint):void {
    SetIntervalTimer._clearInterval(id);
  }

}

Fix SetIntervalTimer to not switch behavior of setTimout and setInterval