package krewfw.builtin_actor.world {

    import flash.geom.Rectangle;

    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.Sprite;

    import krewfw.core.KrewActor;

    //------------------------------------------------------------
    public class KrewWorldLayer extends KrewActor {

        private var _halfScreenWidth:Number;
        private var _halfScreenHeight:Number;
        private var _screenOriginX:Number;
        private var _screenOriginY:Number;
        private var _label:String;

        private var _tree:QuadTreeSprite;

        private var _focusX:Number = 0;
        private var _focusY:Number = 0;
        private var _baseZoomScale:Number = 1.0;
        private var _zoomScale:Number     = 1.0;
        private var _viewport:Rectangle;

        //----- debug stat
        public var debug_numObjectByDepth:Array;
        public var debug_countDrawActor:int;
        public var debug_countDrawActorPrev:int;
        public var debug_countDrawDObj:int;
        public var debug_countDrawDObjPrev:int;
        public var debug_countVisiblePrev:int;
        public var debug_countVisible:int;

        //------------------------------------------------------------
        public function KrewWorldLayer(worldWidth:Number, worldHeight:Number,
                                       screenWidth:Number, screenHeight:Number,
                                       baseZoomScale:Number=1.0,
                                       maxQuadTreeDepth:int=6, subNodeMargin:Number=0.2,
                                       screenOriginX:Number=0, screenOriginY:Number=0,
                                       label:String="")
        {
            _halfScreenWidth  = screenWidth  / 2;
            _halfScreenHeight = screenHeight / 2;
            _screenOriginX    = screenOriginX;
            _screenOriginY    = screenOriginY;
            _baseZoomScale    = baseZoomScale;
            _label = label;

            _tree = new QuadTreeSprite(
                worldWidth, worldHeight, 0, 0, 0,
                maxQuadTreeDepth, subNodeMargin, label
            );
            super.addChild(_tree);

            _viewport = new Rectangle();
        }

        //------------------------------------------------------------
        // Actor's event handlers
        //------------------------------------------------------------

        public override function init():void {
            listen(QuadTreeSprite.DEBUG_EVENT_ADD, _debug_onObjAdd);
            listen(QuadTreeSprite.DEBUG_EVENT_DRAW_ACTOR, _debug_onActorDraw);
            listen(QuadTreeSprite.DEBUG_EVENT_DRAW_DOBJ, _debug_onDObjDraw);
            listen(QuadTreeSprite.DEBUG_EVENT_ENABLE_NODE, _debug_onNodeEnabled);
        }

        protected override function onDispose():void {
            _tree.dispose();
            _tree     = null;
            _viewport = null;
        }

        public function updateWorld(passedTime:Number):void {
            _tree.updateActors(passedTime);
        }

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

        public function get treeRoot():QuadTreeSprite { return _tree; }
        public function get viewport():Rectangle { return _viewport; }

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

        /** width と height はアンカーが中心にあることを想定 */
        public function putActor(actor:KrewActor, width:Number=NaN, height:Number=NaN):void {
            _tree.addActor(actor, width, height);

            if (!actor.hasInitialized) {
                actor.setUp(sharedObj, applyForNewActor, layer, layerName);
            }
        }

        /** width と height はアンカーが中心にあることを想定 */
        public function putDisplayObj(child:DisplayObject, width:Number=NaN, height:Number=NaN,
                                      anchorX:Number=0.5, anchorY:Number=0.5):void
        {
            if (child is Image) {
                var image:Image = child as Image;
                image.pivotX = image.texture.width  * anchorX;
                image.pivotY = image.texture.height * anchorY;
            }

            _tree.addDisplayObj(child, width, height);
        }

        public function setFocusPos(x:Number, y:Number):void {
            _focusX = x;
            _focusY = y;
        }

        public function setZoomScale(scale:Number):void {
            _zoomScale = scale;
            if (_zoomScale <= 0) { _zoomScale = 0.001; }
        }

        public function updateViewport():void {
            var zoomScale:Number = _zoomScale * _baseZoomScale;
            x = -(_focusX * zoomScale) + _screenOriginX + _halfScreenWidth;
            y = -(_focusY * zoomScale) + _screenOriginY + _halfScreenHeight;

            scaleX = zoomScale;
            scaleY = zoomScale;

            _viewport.setTo(
                _focusX - (_halfScreenWidth  / zoomScale),
                _focusY - (_halfScreenHeight / zoomScale),
                _halfScreenWidth  * 2 / zoomScale,
                _halfScreenHeight * 2 / zoomScale
            );

            _tree.updateVisibility(_viewport);
        }

        public function updateScreenSize(screenWidth:Number, screenHeight:Number,
                                         screenOriginX:Number=0, screenOriginY:Number=0):void
        {
            _halfScreenWidth  = screenWidth  / 2;
            _halfScreenHeight = screenHeight / 2;
            _screenOriginX    = screenOriginX;
            _screenOriginY    = screenOriginY;
        }

        //------------------------------------------------------------
        // debug
        //------------------------------------------------------------

        public function startRecDebugStat():void {
            debug_countDrawActorPrev = debug_countDrawActor;
            debug_countDrawDObjPrev  = debug_countDrawDObj;
            debug_countVisiblePrev   = debug_countVisible;
            debug_countDrawActor = 0;
            debug_countDrawDObj  = 0;
            debug_countVisible   = 0;
        }

        private function _debug_onObjAdd(args:Object):void {
            if (args.label != _label) { return; }

            if (!debug_numObjectByDepth) {
                debug_numObjectByDepth = [];
                for (var i:int = 0;  i <= _tree.maxDepth;  ++i) {
                    debug_numObjectByDepth[i] = 0;
                }
            }

            debug_numObjectByDepth[args.depth] += 1;
        }

        private function _debug_onActorDraw(args:Object):void {
            if (args.label != _label) { return; }
            debug_countDrawActor += args.num;
        }

        private function _debug_onDObjDraw(args:Object):void {
            if (args.label != _label) { return; }
            debug_countDrawDObj += args.num;
        }

        private function _debug_onNodeEnabled(args:Object):void {
            if (args.label != _label) { return; }
            debug_countVisible += 1;
        }

    }
}

package krewfw.builtin_actor.world {

    import flash.geom.Rectangle;

    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.Sprite;

    import krewfw.core.KrewActor;

    //------------------------------------------------------------
    public class KrewWorldLayer extends KrewActor {

        private var _halfScreenWidth:Number;
        private var _halfScreenHeight:Number;
        private var _screenOriginX:Number;
        private var _screenOriginY:Number;
        private var _label:String;

        private var _tree:QuadTreeSprite;

        private var _focusX:Number = 0;
        private var _focusY:Number = 0;
        private var _baseZoomScale:Number = 1.0;
        private var _zoomScale:Number     = 1.0;
        private var _viewport:Rectangle;

        //----- debug stat
        public var debug_numObjectByDepth:Array;
        public var debug_countDrawActor:int;
        public var debug_countDrawActorPrev:int;
        public var debug_countDrawDObj:int;
        public var debug_countDrawDObjPrev:int;
        public var debug_countVisiblePrev:int;
        public var debug_countVisible:int;

        //------------------------------------------------------------
        public function KrewWorldLayer(worldWidth:Number, worldHeight:Number,
                                       screenWidth:Number, screenHeight:Number,
                                       baseZoomScale:Number=1.0,
                                       maxQuadTreeDepth:int=6, subNodeMargin:Number=0.2,
                                       screenOriginX:Number=0, screenOriginY:Number=0,
                                       label:String="")
        {
            _halfScreenWidth  = screenWidth  / 2;
            _halfScreenHeight = screenHeight / 2;
            _screenOriginX    = screenOriginX;
            _screenOriginY    = screenOriginY;
            _baseZoomScale    = baseZoomScale;
            _label = label;

            _tree = new QuadTreeSprite(
                worldWidth, worldHeight, 0, 0, 0,
                maxQuadTreeDepth, subNodeMargin, label
            );
            super.addChild(_tree);

            _viewport = new Rectangle();
        }

        //------------------------------------------------------------
        // Actor's event handlers
        //------------------------------------------------------------

        public override function init():void {
            listen(QuadTreeSprite.DEBUG_EVENT_ADD, _debug_onObjAdd);
            listen(QuadTreeSprite.DEBUG_EVENT_DRAW_ACTOR, _debug_onActorDraw);
            listen(QuadTreeSprite.DEBUG_EVENT_DRAW_DOBJ, _debug_onDObjDraw);
            listen(QuadTreeSprite.DEBUG_EVENT_ENABLE_NODE, _debug_onNodeEnabled);
        }

        protected override function onDispose():void {
            _tree.dispose();
            _tree     = null;
            _viewport = null;
        }

        public function updateWorld(passedTime:Number):void {
            _tree.updateActors(passedTime);
        }

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

        public function get treeRoot():QuadTreeSprite { return _tree; }
        public function get viewport():Rectangle { return _viewport; }

        public function get baseZoomScale():Number { return _baseZoomScale; }
        public function set baseZoomScale(scale:Number):void { _baseZoomScale = scale; }

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

        /** width と height はアンカーが中心にあることを想定 */
        public function putActor(actor:KrewActor, width:Number=NaN, height:Number=NaN):void {
            _tree.addActor(actor, width, height);

            if (!actor.hasInitialized) {
                actor.setUp(sharedObj, applyForNewActor, layer, layerName);
            }
        }

        /** width と height はアンカーが中心にあることを想定 */
        public function putDisplayObj(child:DisplayObject, width:Number=NaN, height:Number=NaN,
                                      anchorX:Number=0.5, anchorY:Number=0.5):void
        {
            if (child is Image) {
                var image:Image = child as Image;
                image.pivotX = image.texture.width  * anchorX;
                image.pivotY = image.texture.height * anchorY;
            }

            _tree.addDisplayObj(child, width, height);
        }

        public function setFocusPos(x:Number, y:Number):void {
            _focusX = x;
            _focusY = y;
        }

        public function setZoomScale(scale:Number):void {
            _zoomScale = scale;
            if (_zoomScale <= 0) { _zoomScale = 0.001; }
        }

        public function updateViewport():void {
            var zoomScale:Number = _zoomScale * _baseZoomScale;
            x = -(_focusX * zoomScale) + _screenOriginX + _halfScreenWidth;
            y = -(_focusY * zoomScale) + _screenOriginY + _halfScreenHeight;

            scaleX = zoomScale;
            scaleY = zoomScale;

            _viewport.setTo(
                _focusX - (_halfScreenWidth  / zoomScale),
                _focusY - (_halfScreenHeight / zoomScale),
                _halfScreenWidth  * 2 / zoomScale,
                _halfScreenHeight * 2 / zoomScale
            );

            _tree.updateVisibility(_viewport);
        }

        public function updateScreenSize(screenWidth:Number, screenHeight:Number,
                                         screenOriginX:Number=0, screenOriginY:Number=0):void
        {
            _halfScreenWidth  = screenWidth  / 2;
            _halfScreenHeight = screenHeight / 2;
            _screenOriginX    = screenOriginX;
            _screenOriginY    = screenOriginY;
        }

        //------------------------------------------------------------
        // debug
        //------------------------------------------------------------

        public function startRecDebugStat():void {
            debug_countDrawActorPrev = debug_countDrawActor;
            debug_countDrawDObjPrev  = debug_countDrawDObj;
            debug_countVisiblePrev   = debug_countVisible;
            debug_countDrawActor = 0;
            debug_countDrawDObj  = 0;
            debug_countVisible   = 0;
        }

        private function _debug_onObjAdd(args:Object):void {
            if (args.label != _label) { return; }

            if (!debug_numObjectByDepth) {
                debug_numObjectByDepth = [];
                for (var i:int = 0;  i <= _tree.maxDepth;  ++i) {
                    debug_numObjectByDepth[i] = 0;
                }
            }

            debug_numObjectByDepth[args.depth] += 1;
        }

        private function _debug_onActorDraw(args:Object):void {
            if (args.label != _label) { return; }
            debug_countDrawActor += args.num;
        }

        private function _debug_onDObjDraw(args:Object):void {
            if (args.label != _label) { return; }
            debug_countDrawDObj += args.num;
        }

        private function _debug_onNodeEnabled(args:Object):void {
            if (args.label != _label) { return; }
            debug_countVisible += 1;
        }

    }
}

Add baseZoomeScale accessor to KrewWorldLayer


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

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

    private const finishingListeners : Array = []

    private var _movie : Movie
    private var connection : FlashNetConnection

    private var _started : Boolean = false
    private var _finished : Boolean = false
    private var triedStandardRTMP : Boolean = false
    private var rtmpAvailable : Boolean = false

    private var measuredBandwidth : Bitrate = null
    private var measuredLatency : Duration = null

    private var stream : FlashNetStream = null
    private var metadata : Object = null

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

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

      connection.listener = this
    }

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

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

    public function get finished() : Boolean
    { return _finished }

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

    public function get started() : Boolean
    { return _started }

    public function start() : void
    {
      _started = true

      if (movie.rtmpURL)
        connectUsingRTMP()
      else
        connectUsingHTTP()
    }

    public function handleConnectionFailed() : void
    {
      if (movie.rtmpURL.hasPort && !triedStandardRTMP)
        handleCustomRTMPUnavailable()
      else
        handleRTMPUnavailable()
    }

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

    private function handleRTMPUnavailable() : void
    {
      debug("Could not connect using RTMP; trying plain HTTP.")
      connectUsingHTTP()
    }

    private function connectUsingRTMP() : void
    { connection.connect(movie.rtmpURL) }

    private function connectUsingStandardRTMP() : void
    {
      triedStandardRTMP = true
      connection.connect(movie.rtmpURL.withoutPort)
    }

    private function connectUsingHTTP() : void
    {
      rtmpAvailable = false
      connection.dontConnect()
      startPlaying()
    }

    public function handleConnectionClosed() : void
    {}

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

    public function handleConnectionEstablished() : void
    {
      rtmpAvailable = true

      if (DETERMINE_BANDWIDTH)
        connection.determineBandwidth()
      else
        startPlaying()
    }

    public function handleBandwidthDetermined
      (bandwidth : Bitrate, latency : Duration) : void
    {
      measuredBandwidth = bandwidth
      measuredLatency = latency

      startPlaying()
    }

    private function get bandwidthDetermined() : Boolean
    { return measuredBandwidth != null }

    private function startPlaying() : void
    {
      stream = connection.getNetStream()
      stream.listener = this
      stream.volume = volume

      useStartBuffer()

      if (rtmpAvailable)
        playRTMPStream()
      else
        playHTTPStream()

      if (paused)
        stream.paused = true
    }

    private function playRTMPStream() : void
    { stream.playRTMP(bestStream, streams) }

    private function playHTTPStream() : void
    { stream.playHTTP(movie.httpURL) }

    private function get bestStream() : RTMPStream
    { return new RTMPStreamPicker(streams, maxBitrate).bestStream }

    private function get streams() : Array
    { return movie.rtmpStreams }

    private function get maxBitrate() : Bitrate
    { return bandwidthDetermined ? measuredBandwidth.scaledBy(.8) : null }

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

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

    public function handleStreamingStarted() : void
    {
      useStartBuffer()
      _buffering = true
      _finished = false
    }

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

    private function handleBufferingFinished() : void
    {
      _buffering = false

      // Give the backend a chance to resume playback before we go
      // ahead and raise the buffer size further.
      later($handleBufferingFinished)
    }

    private function $handleBufferingFinished() : void
    {
      // Make sure playback was not interrupted.
      if (!buffering && !finished)
        useLargeBuffer()
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    public function set paused(value : Boolean) : void
    {
      // Allow pausing before the stream has been created.
      _paused = value

      if (stream)
        stream.paused = value
    }

    public function togglePaused() : void
    {
      // Forbid pausing before the stream has been created.
      if (stream)
        paused = !paused
    }

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

    public function get playheadPosition() : Duration
    {
      return stream != null && stream.playheadPosition != null
        ? stream.playheadPosition : Duration.ZERO
    }

    public function get playheadRatio() : Number
    { return getRatio(playheadPosition.seconds, streamLength.seconds) }

    public function get bufferRatio() : Number
    { return getRatio(bufferPosition.seconds, streamLength.seconds) }

    private function getRatio
      (numerator : Number, denominator : Number) : Number
    { return Math.min(1, numerator / denominator) }

    private function get bufferPosition() : Duration
    { return playheadPosition.plus(bufferLength) }

    public function set playheadPosition(value : Duration) : void
    {
      if (!stream) return
      useStartBuffer()
      _finished = false
      stream.playheadPosition = value
    }

    public function set playheadRatio(value : Number) : void
    { playheadPosition = streamLength.scaledBy(value) }

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

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

    public function get playing() : Boolean
    { return started && !paused && !finished }

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

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

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

      if (stream)
        stream.volume = value
    }

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

    public function mute() : void
    {
      savedVolume = volume
      volume = 0
    }

    public function unmute() : void
    { volume = savedVolume == 0 ? DEFAULT_VOLUME : savedVolume }

    public function get muted() : Boolean
    { return volume == 0 }

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

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

    public function get bufferTime() : Duration
    {
      return stream != null && stream.bufferTime != null
        ? stream.bufferTime : START_BUFFER
    }

    public function get bufferLength() : Duration
    {
      return stream != null && stream.bufferLength != null
        ? stream.bufferLength : Duration.ZERO
    }

    public function get bufferFillRatio() : Number
    { return getRatio(bufferLength.seconds, bufferTime.seconds) }

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

    public function get currentBitrate() : Bitrate
    {
      return stream != null && stream.bitrate != null
        ? stream.bitrate : Bitrate.ZERO
    }

    public function get currentBandwidth() : Bitrate
    {
      return stream != null && stream.bandwidth != null
        ? stream.bandwidth : Bitrate.ZERO
    }

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

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

      return result
    }
  }
}

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

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

    private const finishingListeners : Array = []

    private var _movie : Movie
    private var connection : FlashNetConnection

    private var _started : Boolean = false
    private var _finished : Boolean = false
    private var triedStandardRTMP : Boolean = false
    private var rtmpAvailable : Boolean = false

    private var measuredBandwidth : Bitrate = null
    private var measuredLatency : Duration = null

    private var stream : FlashNetStream = null
    private var metadata : Object = null

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

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

      connection.listener = this
    }

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

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

    public function get finished() : Boolean
    { return _finished }

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

    public function get started() : Boolean
    { return _started }

    public function start() : void
    {
      _started = true

      if (movie.rtmpURL)
        connectUsingRTMP()
      else
        connectUsingHTTP()
    }

    public function handleConnectionFailed() : void
    {
      if (movie.rtmpURL.hasPort && !triedStandardRTMP)
        handleCustomRTMPUnavailable()
      else
        handleRTMPUnavailable()
    }

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

    private function handleRTMPUnavailable() : void
    {
      debug("Could not connect using RTMP; trying plain HTTP.")
      connectUsingHTTP()
    }

    private function connectUsingRTMP() : void
    { connection.connect(movie.rtmpURL) }

    private function connectUsingStandardRTMP() : void
    {
      triedStandardRTMP = true
      connection.connect(movie.rtmpURL.withoutPort)
    }

    private function connectUsingHTTP() : void
    {
      rtmpAvailable = false
      connection.dontConnect()
      startPlaying()
    }

    public function handleConnectionClosed() : void
    {}

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

    public function handleConnectionEstablished() : void
    {
      rtmpAvailable = true

      if (DETERMINE_BANDWIDTH)
        connection.determineBandwidth()
      else
        startPlaying()
    }

    public function handleBandwidthDetermined
      (bandwidth : Bitrate, latency : Duration) : void
    {
      measuredBandwidth = bandwidth
      measuredLatency = latency

      startPlaying()
    }

    private function get bandwidthDetermined() : Boolean
    { return measuredBandwidth != null }

    private function startPlaying() : void
    {
      stream = connection.getNetStream()
      stream.listener = this
      stream.volume = volume

      useStartBuffer()

      if (rtmpAvailable)
        playRTMPStream()
      else
        playHTTPStream()

      if (paused)
        stream.paused = true
    }

    private function playRTMPStream() : void
    { stream.playRTMP(bestStream, streams) }

    private function playHTTPStream() : void
    { stream.playHTTP(movie.httpURL) }

    private function get bestStream() : RTMPStream
    { return new RTMPStreamPicker(streams, maxBitrate).bestStream }

    private function get streams() : Array
    { return movie.rtmpStreams }

    private function get maxBitrate() : Bitrate
    { return bandwidthDetermined ? measuredBandwidth.scaledBy(.8) : null }

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

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

    public function handleStreamingStarted() : void
    {
      useStartBuffer()
      _buffering = true
      _finished = false
    }

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

    private function handleBufferingFinished() : void
    {
      _buffering = false

      // Give the backend a chance to resume playback before we go
      // ahead and raise the buffer size further.
      later($handleBufferingFinished)
    }

    private function $handleBufferingFinished() : void
    {
      // Make sure playback was not interrupted.
      if (!buffering && !finished)
        useLargeBuffer()
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    public function set paused(value : Boolean) : void
    {
      // Allow pausing before the stream has been created.
      _paused = value

      if (stream)
        stream.paused = value
    }

    public function togglePaused() : void
    {
      // Forbid pausing before the stream has been created.
      if (stream)
        paused = !paused
    }

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

    public function get playheadPosition() : Duration
    {
      return stream != null && stream.playheadPosition != null
        ? stream.playheadPosition : Duration.ZERO
    }

    public function get playheadRatio() : Number
    { return getRatio(playheadPosition.seconds, streamLength.seconds) }

    public function get bufferRatio() : Number
    { return getRatio(bufferPosition.seconds, streamLength.seconds) }

    private function getRatio
      (numerator : Number, denominator : Number) : Number
    { return Math.min(1, numerator / denominator) }

    private function get bufferPosition() : Duration
    { return playheadPosition.plus(bufferLength) }

    public function set playheadPosition(value : Duration) : void
    {
      if (!stream) return
      useStartBuffer()
      _finished = false
      stream.playheadPosition = value
    }

    public function set playheadRatio(value : Number) : void
    { playheadPosition = streamLength.scaledBy(value) }

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

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

    public function get playing() : Boolean
    { return started && !paused && !finished }

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

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

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

      if (stream)
        stream.volume = value
    }

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

    public function mute() : void
    {
      savedVolume = volume
      volume = 0
    }

    public function unmute() : void
    { volume = savedVolume == 0 ? DEFAULT_VOLUME : savedVolume }

    public function get muted() : Boolean
    { return volume == 0 }

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

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

    public function get bufferTime() : Duration
    {
      return stream != null && stream.bufferTime != null
        ? stream.bufferTime : START_BUFFER
    }

    public function get bufferLength() : Duration
    {
      return stream != null && stream.bufferLength != null
        ? stream.bufferLength : Duration.ZERO
    }

    public function get bufferFillRatio() : Number
    { return getRatio(bufferLength.seconds, bufferTime.seconds) }

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

    public function get currentBitrate() : Bitrate
    {
      return stream != null && stream.bitrate != null
        ? stream.bitrate : Bitrate.ZERO
    }

    public function get currentBandwidth() : Bitrate
    {
      return stream != null && stream.bandwidth != null
        ? stream.bandwidth : Bitrate.ZERO
    }

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

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

      return result
    }
  }
}

Change buffer sizes from .1/10/60 to 1/5/60.


package aerys.minko.render.shader.compiler.graph
{
	import aerys.minko.Minko;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.render.shader.Signature;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Extract;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Instruction;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Interpolate;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Overwriter;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.VariadicExtract;
	import aerys.minko.render.shader.compiler.graph.visitors.*;
	import aerys.minko.render.shader.compiler.register.Components;
	import aerys.minko.render.shader.compiler.sequence.AgalInstruction;
	import aerys.minko.type.log.DebugLevel;
	import aerys.minko.type.stream.format.VertexComponent;

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

	/**
	 * @private
	 * @author Romain Gilliotte
	 *
	 */
	public class ShaderGraph
	{
		private static const SPLITTER				: SplitterVisitor						= new SplitterVisitor();
		private static const REMOVE_EXTRACT			: RemoveExtractsVisitor					= new RemoveExtractsVisitor();
		private static const MERGER					: MergeVisitor							= new MergeVisitor();
		private static const OVERWRITER_CLEANER		: OverwriterCleanerVisitor				= new OverwriterCleanerVisitor();
		private static const RESOLVE_CONSTANT		: ResolveConstantComputationVisitor		= new ResolveConstantComputationVisitor();
		private static const CONSTANT_PACKER		: ConstantPackerVisitor					= new ConstantPackerVisitor();
		private static const CONSTANT_GROUPER		: ConstantGrouperVisitor				= new ConstantGrouperVisitor();
		private static const RESOLVE_PARAMETRIZED	: ResolveParametrizedComputationVisitor	= new ResolveParametrizedComputationVisitor();
		private static const REMOVE_USELESS			: RemoveUselessComputation				= new RemoveUselessComputation();
		private static const COPY_INSERTER			: CopyInserterVisitor					= new CopyInserterVisitor();
		private static const ALLOCATOR				: AllocationVisitor						= new AllocationVisitor();
		private static const INTERPOLATE_FINDER		: InterpolateFinder						= new InterpolateFinder();
		private static const WRITE_DOT				: WriteDot								= new WriteDot();
		private static const MATRIX_TRANSFORMATION	: MatrixTransformationGrouper			= new MatrixTransformationGrouper();

		private var _position				: AbstractNode;
		private var _positionComponents		: uint;
		private var _interpolates			: Vector.<AbstractNode>;
		private var _color					: AbstractNode;
		private var _colorComponents		: uint;
		private var _kills					: Vector.<AbstractNode>;
		private var _killComponents			: Vector.<uint>;
		private var _computableConstants	: Object;

		private var _isCompiled				: Boolean;

		private var _vertexSequence			: Vector.<AgalInstruction>;
		private var _fragmentSequence		: Vector.<AgalInstruction>;
		private var _bindings				: Object;

		private var _vertexComponents		: Vector.<VertexComponent>;
		private var _vertexIndices			: Vector.<uint>;
		private var _vsConstants			: Vector.<Number>;
		private var _fsConstants			: Vector.<Number>;
		private var _textures				: Vector.<ITextureResource>;

		public function get position() : AbstractNode
		{
			return _position;
		}
		public function set position(v : AbstractNode)	: void
		{
			_position = v;
		}

		public function get interpolates() : Vector.<AbstractNode>
		{
			return _interpolates;
		}

		public function get color() : AbstractNode
		{
			return _color;
		}
		public function set color(v : AbstractNode)	: void
		{
			_color = v;
		}

		public function get kills() : Vector.<AbstractNode>
		{
			return _kills;
		}

		public function get positionComponents() : uint
		{
			return _positionComponents;
		}
		public function set positionComponents(v : uint) : void
		{
			_positionComponents = v;
		}

		public function get colorComponents() : uint
		{
			return _colorComponents;
		}
		public function set colorComponents(v : uint) : void
		{
			_colorComponents = v;
		}

		public function get killComponents() : Vector.<uint>
		{
			return _killComponents;
		}

		public function get computableConstants() : Object
		{
			return _computableConstants;
		}

		public function ShaderGraph(position	: AbstractNode,
									color		: AbstractNode,
									kills		: Vector.<AbstractNode>)
		{
			_isCompiled				= false;

			_position				= position;
			_positionComponents		= Components.createContinuous(0, 0, 4, position.size);
			_interpolates			= new Vector.<AbstractNode>();
			_color					= color;
			_colorComponents		= Components.createContinuous(0, 0, 4, color.size);
			_kills					= kills;
			_killComponents			= new Vector.<uint>();
			_computableConstants	= new Object();

			var numKills : uint = kills.length;
			for (var killId : uint = 0; killId < numKills; ++killId)
				_killComponents[killId] = Components.createContinuous(0, 0, 1, 1);
		}

		public function generateProgram(name : String, signature : Signature) : Program3DResource
		{
			if (!_isCompiled)
				compile();

			if (Minko.debugLevel & DebugLevel.SHADER_AGAL)
				Minko.log(DebugLevel.SHADER_AGAL, generateAGAL(name));

			var vsProgram	: ByteArray = computeBinaryProgram(_vertexSequence, true);
			var fsProgram	: ByteArray = computeBinaryProgram(_fragmentSequence, false);

			var program : Program3DResource = new Program3DResource(
				name,
				signature,
				vsProgram,
				fsProgram,
				_vertexComponents,
				_vertexIndices,
				_vsConstants,
				_fsConstants,
				_textures,
				_bindings
			);

			return program;
		}

		public function generateAGAL(name : String) : String
		{
			var instruction	: AgalInstruction;
			var shader		: String = name + "\n";

			if (!_isCompiled)
				compile();

			shader += "- vertex shader\n";
			for each (instruction in _vertexSequence)
				shader += instruction.getAgal(true);

			shader += "- fragment shader\n";
			for each (instruction in _fragmentSequence)
				shader += instruction.getAgal(false);

			return shader;
		}

		private function compile() : void
		{
			// execute consecutive visitors to optimize the shader graph.
			// Warning: the order matters, do not swap lines.
			MERGER					.process(this);	// merge duplicate nodes
			REMOVE_EXTRACT			.process(this);	// remove all extract nodes
			OVERWRITER_CLEANER		.process(this);	// remove nested overwriters
			RESOLVE_CONSTANT		.process(this);	// resolve constant computation
			CONSTANT_PACKER			.process(this);	// pack constants [0,0,0,1] => [0,1].xxxy
			REMOVE_USELESS			.process(this);	// remove some useless operations (add 0, mul 0, mul 1...)
			RESOLVE_PARAMETRIZED	.process(this);	// replace computations that depend on parameters by evalexp parameters
//			MATRIX_TRANSFORMATION	.process(this);	// replace ((vector * matrix1) * matrix2) by vector * (matrix1 * matrix2) to save registers on GPU
			COPY_INSERTER			.process(this);	// ensure there are no operations between constants
			SPLITTER				.process(this);	// clone nodes that are shared between vertex and fragment shader
			CONSTANT_GROUPER		.process(this);	// group constants [0,1] & [0,2] => [0, 1, 2]

			if (Minko.debugLevel & DebugLevel.SHADER_DOTTY)
			{
				WRITE_DOT.process(this);
				Minko.log(DebugLevel.SHADER_DOTTY, WRITE_DOT.result);
				WRITE_DOT.clear();
			}

			// generate final program
			INTERPOLATE_FINDER		.process(this);	// find interpolate nodes. We may skip that in the future.
			ALLOCATOR				.process(this);	// allocate memory and generate final code.

			// retrieve program
			_vertexSequence		= ALLOCATOR.vertexSequence;
			_fragmentSequence	= ALLOCATOR.fragmentSequence;
			_bindings			= ALLOCATOR.parameterBindings;
			_vertexComponents	= ALLOCATOR.vertexComponents;
			_vertexIndices		= ALLOCATOR.vertexIndices;
			_vsConstants		= ALLOCATOR.vertexConstants;
			_fsConstants		= ALLOCATOR.fragmentConstants;
			_textures			= ALLOCATOR.textures;

			ALLOCATOR.clear();

			_isCompiled = true;
		}

		private function computeBinaryProgram(sequence			: Vector.<AgalInstruction>,
											  isVertexShader	: Boolean) : ByteArray
		{
			var program : ByteArray = new ByteArray();

			program.endian = Endian.LITTLE_ENDIAN;
			program.writeByte(0xa0);					// tag version
			program.writeUnsignedInt(1);				// AGAL version, big endian, bit pattern will be 0x01000000
			program.writeByte(0xa1);					// tag program id
			program.writeByte(isVertexShader ? 0 : 1);	// vertex or fragment

			for each (var instruction : AgalInstruction in sequence)
				instruction.getBytecode(program);

			return program;
		}
	}
}

package aerys.minko.render.shader.compiler.graph
{
	import aerys.minko.Minko;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.render.shader.Signature;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Extract;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Instruction;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Interpolate;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Overwriter;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.VariadicExtract;
	import aerys.minko.render.shader.compiler.graph.visitors.*;
	import aerys.minko.render.shader.compiler.register.Components;
	import aerys.minko.render.shader.compiler.sequence.AgalInstruction;
	import aerys.minko.type.log.DebugLevel;
	import aerys.minko.type.stream.format.VertexComponent;

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

	/**
	 * @private
	 * @author Romain Gilliotte
	 *
	 */
	public class ShaderGraph
	{
		private static const SPLITTER				: SplitterVisitor						= new SplitterVisitor();
		private static const REMOVE_EXTRACT			: RemoveExtractsVisitor					= new RemoveExtractsVisitor();
		private static const MERGER					: MergeVisitor							= new MergeVisitor();
		private static const OVERWRITER_CLEANER		: OverwriterCleanerVisitor				= new OverwriterCleanerVisitor();
		private static const RESOLVE_CONSTANT		: ResolveConstantComputationVisitor		= new ResolveConstantComputationVisitor();
		private static const CONSTANT_PACKER		: ConstantPackerVisitor					= new ConstantPackerVisitor();
		private static const CONSTANT_GROUPER		: ConstantGrouperVisitor				= new ConstantGrouperVisitor();
		private static const RESOLVE_PARAMETRIZED	: ResolveParametrizedComputationVisitor	= new ResolveParametrizedComputationVisitor();
		private static const REMOVE_USELESS			: RemoveUselessComputation				= new RemoveUselessComputation();
		private static const COPY_INSERTER			: CopyInserterVisitor					= new CopyInserterVisitor();
		private static const ALLOCATOR				: AllocationVisitor						= new AllocationVisitor();
		private static const INTERPOLATE_FINDER		: InterpolateFinder						= new InterpolateFinder();
		private static const WRITE_DOT				: WriteDot								= new WriteDot();
		private static const MATRIX_TRANSFORMATION	: MatrixTransformationGrouper			= new MatrixTransformationGrouper();

		private var _position				: AbstractNode;
		private var _positionComponents		: uint;
		private var _interpolates			: Vector.<AbstractNode>;
		private var _color					: AbstractNode;
		private var _colorComponents		: uint;
		private var _kills					: Vector.<AbstractNode>;
		private var _killComponents			: Vector.<uint>;
		private var _computableConstants	: Object;

		private var _isCompiled				: Boolean;

		private var _vertexSequence			: Vector.<AgalInstruction>;
		private var _fragmentSequence		: Vector.<AgalInstruction>;
		private var _bindings				: Object;

		private var _vertexComponents		: Vector.<VertexComponent>;
		private var _vertexIndices			: Vector.<uint>;
		private var _vsConstants			: Vector.<Number>;
		private var _fsConstants			: Vector.<Number>;
		private var _textures				: Vector.<ITextureResource>;

		public function get position() : AbstractNode
		{
			return _position;
		}
		public function set position(v : AbstractNode)	: void
		{
			_position = v;
		}

		public function get interpolates() : Vector.<AbstractNode>
		{
			return _interpolates;
		}

		public function get color() : AbstractNode
		{
			return _color;
		}
		public function set color(v : AbstractNode)	: void
		{
			_color = v;
		}

		public function get kills() : Vector.<AbstractNode>
		{
			return _kills;
		}

		public function get positionComponents() : uint
		{
			return _positionComponents;
		}
		public function set positionComponents(v : uint) : void
		{
			_positionComponents = v;
		}

		public function get colorComponents() : uint
		{
			return _colorComponents;
		}
		public function set colorComponents(v : uint) : void
		{
			_colorComponents = v;
		}

		public function get killComponents() : Vector.<uint>
		{
			return _killComponents;
		}

		public function get computableConstants() : Object
		{
			return _computableConstants;
		}

		public function ShaderGraph(position	: AbstractNode,
									color		: AbstractNode,
									kills		: Vector.<AbstractNode>)
		{
			_isCompiled				= false;

			_position				= position;
			_positionComponents		= Components.createContinuous(0, 0, 4, position.size);
			_interpolates			= new Vector.<AbstractNode>();
			_color					= color;
			_colorComponents		= Components.createContinuous(0, 0, 4, color.size);
			_kills					= kills;
			_killComponents			= new Vector.<uint>();
			_computableConstants	= new Object();

			var numKills : uint = kills.length;
			for (var killId : uint = 0; killId < numKills; ++killId)
				_killComponents[killId] = Components.createContinuous(0, 0, 1, 1);
		}

		public function generateProgram(name : String, signature : Signature) : Program3DResource
		{
			if (!_isCompiled)
				compile();

			if (Minko.debugLevel & DebugLevel.SHADER_AGAL)
				Minko.log(DebugLevel.SHADER_AGAL, generateAGAL(name));

			var vsProgram	: ByteArray = computeBinaryProgram(_vertexSequence, true);
			var fsProgram	: ByteArray = computeBinaryProgram(_fragmentSequence, false);

			var program : Program3DResource = new Program3DResource(
				name,
				signature,
				vsProgram,
				fsProgram,
				_vertexComponents,
				_vertexIndices,
				_vsConstants,
				_fsConstants,
				_textures,
				_bindings
			);

			return program;
		}

		public function generateAGAL(name : String) : String
		{
			var instruction	: AgalInstruction;
			var shader		: String = name + "\n";

			if (!_isCompiled)
				compile();

			shader += "- vertex shader\n";
			for each (instruction in _vertexSequence)
				shader += instruction.getAgal(true);

			shader += "- fragment shader\n";
			for each (instruction in _fragmentSequence)
				shader += instruction.getAgal(false);

			return shader;
		}

		private function compile() : void
		{
			// execute consecutive visitors to optimize the shader graph.
			// Warning: the order matters, do not swap lines.
			MERGER					.process(this);	// merge duplicate nodes
			REMOVE_EXTRACT			.process(this);	// remove all extract nodes
			OVERWRITER_CLEANER		.process(this);	// remove nested overwriters
			RESOLVE_CONSTANT		.process(this);	// resolve constant computation
			CONSTANT_PACKER			.process(this);	// pack constants [0,0,0,1] => [0,1].xxxy
//			REMOVE_USELESS			.process(this);	// remove some useless operations (add 0, mul 0, mul 1...)
			RESOLVE_PARAMETRIZED	.process(this);	// replace computations that depend on parameters by evalexp parameters
//			MATRIX_TRANSFORMATION	.process(this);	// replace ((vector * matrix1) * matrix2) by vector * (matrix1 * matrix2) to save registers on GPU
			COPY_INSERTER			.process(this);	// ensure there are no operations between constants
			SPLITTER				.process(this);	// clone nodes that are shared between vertex and fragment shader
			CONSTANT_GROUPER		.process(this);	// group constants [0,1] & [0,2] => [0, 1, 2]

			if (Minko.debugLevel & DebugLevel.SHADER_DOTTY)
			{
				WRITE_DOT.process(this);
				Minko.log(DebugLevel.SHADER_DOTTY, WRITE_DOT.result);
				WRITE_DOT.clear();
			}

			// generate final program
			INTERPOLATE_FINDER		.process(this);	// find interpolate nodes. We may skip that in the future.
			ALLOCATOR				.process(this);	// allocate memory and generate final code.

			// retrieve program
			_vertexSequence		= ALLOCATOR.vertexSequence;
			_fragmentSequence	= ALLOCATOR.fragmentSequence;
			_bindings			= ALLOCATOR.parameterBindings;
			_vertexComponents	= ALLOCATOR.vertexComponents;
			_vertexIndices		= ALLOCATOR.vertexIndices;
			_vsConstants		= ALLOCATOR.vertexConstants;
			_fsConstants		= ALLOCATOR.fragmentConstants;
			_textures			= ALLOCATOR.textures;

			ALLOCATOR.clear();

			_isCompiled = true;
		}

		private function computeBinaryProgram(sequence			: Vector.<AgalInstruction>,
											  isVertexShader	: Boolean) : ByteArray
		{
			var program : ByteArray = new ByteArray();

			program.endian = Endian.LITTLE_ENDIAN;
			program.writeByte(0xa0);					// tag version
			program.writeUnsignedInt(1);				// AGAL version, big endian, bit pattern will be 0x01000000
			program.writeByte(0xa1);					// tag program id
			program.writeByte(isVertexShader ? 0 : 1);	// vertex or fragment

			for each (var instruction : AgalInstruction in sequence)
				instruction.getBytecode(program);

			return program;
		}
	}
}

Comment shadergraph visitor

Comment shadergraph visitor

M Remove_useless visitor creates errors inside the shader graph


////////////////////////////////////////////////////////////////////////////////
//
//  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
{
    import org.apache.flex.core.BeadViewBase;
    import org.apache.flex.core.ContainerBase;
    import org.apache.flex.core.IBead;
    import org.apache.flex.core.IBeadLayout;
    import org.apache.flex.core.IBeadView;
    import org.apache.flex.core.ILayoutParent;
    import org.apache.flex.core.IParentIUIBase;
    import org.apache.flex.core.IStrand;
    import org.apache.flex.core.IUIBase;
    import org.apache.flex.core.UIBase;
    import org.apache.flex.core.ValuesManager;
    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.ContainerContentArea;
    import org.apache.flex.html.supportClasses.ScrollBar;

    /**
     *  The ContainerView class is the default view for
     *  the org.apache.flex.core.ContainerBase classes.
     *  It lets you use some CSS styles to manage the border, background
     *  and padding around the content area.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class ContainerView extends BeadViewBase implements IBeadView, ILayoutParent
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function ContainerView()
		{
		}

        /**
         *  The actual parent that parents the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		protected var actualParent:UIBase;

        /**
         *  The layout.  The layout may actually layout
         *  the children of the internal content area
         *  and not the pieces of the "chrome" like titlebars
         *  and borders.  The ContainerView or its subclass will have some
         *  baked-in logic for handling the chrome.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        protected var layout:IBeadLayout;

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

            var host:UIBase = value as UIBase;

            if (host.isWidthSizedToContent() || host.isHeightSizedToContent())
            {
                // if both dimensions are sized to content, then only draw the
                // borders, etc, after a child is added.  The children in an MXML
                // document don't send this event until the last child is added.
                host.addEventListener("childrenAdded", changeHandler);
                host.addEventListener("layoutNeeded", changeHandler);
                // listen for width and height changes as well in case the app
                // switches away from content sizing via binding or other code
                host.addEventListener("widthChanged", changeHandler);
                host.addEventListener("heightChanged", changeHandler);
            }
            else
            {
                // otherwise, listen for size changes before drawing
                // borders and laying out children.
                host.addEventListener("widthChanged", changeHandler);
                host.addEventListener("heightChanged", changeHandler);
                host.addEventListener("sizeChanged", sizeChangeHandler);
                // if we have fixed size in both dimensions, listen for children
				// being added, but also force an initial display to get the
				// background, border, etc.
                if (!isNaN(host.explicitWidth) && !isNaN(host.explicitHeight)) {
                    host.addEventListener("childrenAdded", changeHandler);
					displayBackgroundAndBorder(host);
				}
            }
            checkActualParent();
        }

        private function checkActualParent():Boolean
        {
            var host:UIBase = UIBase(_strand);
            if (contentAreaNeeded())
            {
                if (actualParent == null || actualParent == host)
                {
                    actualParent = new ContainerContentArea();
					actualParent.className = "ActualParent";
                    host.addElement(actualParent, false);
                    ContainerBase(host).setActualParent(actualParent);
                }
                return true;
            }
            else
            {
                actualParent = host;
            }
            return false;
        }

        private function sizeChangeHandler(event:Event):void
        {
            var host:UIBase = UIBase(_strand);
            host.addEventListener("childrenAdded", changeHandler);
            host.addEventListener("layoutNeeded", changeHandler);
            host.addEventListener("itemsCreated", changeHandler);
            changeHandler(event);
        }

        private var inChangeHandler:Boolean;

        /**
         *  React if the size changed or content changed
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        protected function changeHandler(event:Event):void
        {
            if (inChangeHandler) return;

            inChangeHandler = true;

            var host:UIBase = UIBase(_strand);
			var originalHostWidth:Number = host.width;
			var originalHostHeight:Number = host.height;

            if (layout == null)
            {
                layout = _strand.getBeadByType(IBeadLayout) as IBeadLayout;
                if (layout == null)
                {
                    var c:Class = ValuesManager.valuesImpl.getValue(host, "iBeadLayout");
                    if (c)
                    {
                        layout = new c() as IBeadLayout;
                        _strand.addBead(layout);
                    }
                }
            }

			var padding:Object = determinePadding();

			if (checkActualParent())
			{
				actualParent.x = padding.paddingLeft;
				actualParent.y = padding.paddingTop;
            }
            var pb:Number = padding.paddingBottom;
            if (isNaN(pb))
                pb = 0;
            var pr:Number = padding.paddingRight;
            if (isNaN(pr))
                pr = 0;

			var sizeChangedByLayout:Boolean;

            // if the width is dictated by the parent
            if (!host.isWidthSizedToContent())
            {
                if (actualParent != host)
                {
                    // force the width of the internal content area as desired.
                    actualParent.setWidth(host.width - padding.paddingLeft - pr);
                }
                // run the layout
                sizeChangedByLayout = layout.layout();
            }
            else
            {
                // if the height is dictated by the parent
                if (!host.isHeightSizedToContent())
                {
                    if (actualParent != host)
                    {
                        // force the height
                        actualParent.setHeight(host.height - padding.paddingTop - pb);
                    }
                }
                sizeChangedByLayout = layout.layout();
                if (actualParent != host)
                {
                    // actualParent.width should be the new width after layout.
                    // set the host's width.  This should send a widthChanged event and
                    // have it blocked at the beginning of this method
                    host.setWidth(padding.paddingLeft + pr + actualParent.width);
                }
            }
            // and if the height is sized to content, set the height now as well.
            if (host != actualParent)
	        {
                if (host.isHeightSizedToContent()) {
                    host.setHeight(padding.paddingTop + pb + actualParent.height);
				}
                else
                    actualParent.setHeight(host.height - padding.paddingTop - pb);
		    }
			// if host and actualParent are the same, determine if the layout changed
			// the size and if, dispatch events based on what changed
			else if (sizeChangedByLayout)
			{
				if (originalHostWidth != host.width)
					host.dispatchEvent(new Event("widthChanged"));
				if (originalHostHeight != host.height)
					host.dispatchEvent(new Event("heightChanged"));
			}

			displayBackgroundAndBorder(host);

            inChangeHandler = false;
		}

		protected function displayBackgroundAndBorder(host:UIBase) : void
		{
			var backgroundColor:Object = ValuesManager.valuesImpl.getValue(host, "background-color");
			var backgroundImage:Object = ValuesManager.valuesImpl.getValue(host, "background-image");
			if (backgroundColor != null || backgroundImage != null)
			{
				if (host.getBeadByType(IBackgroundBead) == null)
					host.addBead(new (ValuesManager.valuesImpl.getValue(host, "iBackgroundBead")) as IBead);
			}

			var borderStyle:String;
			var borderStyles:Object = ValuesManager.valuesImpl.getValue(host, "border");
			if (borderStyles is Array)
			{
				borderStyle = borderStyles[1];
			}
			if (borderStyle == null)
			{
				borderStyle = ValuesManager.valuesImpl.getValue(host, "border-style") as String;
			}
			if (borderStyle != null && borderStyle != "none")
			{
				if (host.getBeadByType(IBorderBead) == null)
					host.addBead(new (ValuesManager.valuesImpl.getValue(host, "iBorderBead")) as IBead);
			}
		}

		/**
		 *  Determines the top and left padding values, if any, as set by
		 *  padding style values. This includes "padding" for all padding values
		 *  as well as "padding-left" and "padding-top".
		 *
		 *  Returns an object with paddingLeft and paddingTop properties.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		protected function determinePadding():Object
		{
			var paddingLeft:Object;
			var paddingTop:Object;
			var paddingRight:Object;
			var paddingBottom:Object;
			var padding:Object = ValuesManager.valuesImpl.getValue(_strand, "padding");
			if (padding is Array)
			{
				if (padding.length == 1)
					paddingLeft = paddingTop = padding[0];
				else if (padding.length <= 3)
				{
					paddingLeft = padding[1];
					paddingTop = padding[0];
				}
				else if (padding.length == 4)
				{
					paddingLeft = padding[3];
					paddingTop = padding[0];
				}
			}
			else if (padding == null)
			{
				paddingLeft = ValuesManager.valuesImpl.getValue(_strand, "padding-left");
				paddingTop = ValuesManager.valuesImpl.getValue(_strand, "padding-top");
				paddingRight = ValuesManager.valuesImpl.getValue(_strand, "padding-right");
				paddingBottom = ValuesManager.valuesImpl.getValue(_strand, "padding-bottom");
			}
			else
			{
				paddingLeft = paddingTop = padding;
				paddingRight = paddingBottom = padding;
			}
			var pl:Number = Number(paddingLeft);
			var pt:Number = Number(paddingTop);
			var pr:Number = Number(paddingRight);
			var pb:Number = Number(paddingBottom);

			return {paddingLeft:pl, paddingTop:pt, paddingRight:pr, paddingBottom:pb};
		}

		/**
		 *  Returns true if container to create a separate ContainerContentArea.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		protected function contentAreaNeeded():Boolean
		{
			var padding:Object = determinePadding();

			return (!isNaN(padding.paddingLeft) && padding.paddingLeft > 0 ||
				    !isNaN(padding.paddingTop) && padding.paddingTop > 0);
		}

        /**
         *  The parent of the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get contentView():IParentIUIBase
		{
			return actualParent;
		}

        /**
         *  The host component, which can resize to different slots.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get resizableView():IUIBase
		{
			return _strand as IUIBase;
		}

        private var inGetViewHeight:Boolean;

		/**
		 *  @copy org.apache.flex.core.IBeadView#viewHeight
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		override public function get viewHeight():Number
		{
            if (inGetViewHeight)
            {
                //trace("ContainerView: no height set for " + host);
                return host["$height"];
            }
            inGetViewHeight = true;
			var vh:Number = contentView.height;
            inGetViewHeight = false;
            return vh;
		}

        private var inGetViewWidth:Boolean;

		/**
		 *  @copy org.apache.flex.core.IBeadView#viewWidth
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		override public function get viewWidth():Number
		{
            if (inGetViewWidth)
            {
                //trace("ContainerView: no width set for " + host);
                return host["$width"];
            }
            inGetViewWidth = true;
			var vw:Number = contentView.width;
            inGetViewWidth = false;
			return vw;
		}

	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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
{
    import org.apache.flex.core.BeadViewBase;
    import org.apache.flex.core.ContainerBase;
    import org.apache.flex.core.IBead;
    import org.apache.flex.core.IBeadLayout;
    import org.apache.flex.core.IBeadView;
    import org.apache.flex.core.ILayoutParent;
    import org.apache.flex.core.IParentIUIBase;
    import org.apache.flex.core.IStrand;
    import org.apache.flex.core.IUIBase;
    import org.apache.flex.core.UIBase;
    import org.apache.flex.core.ValuesManager;
    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.ContainerContentArea;
    import org.apache.flex.html.supportClasses.ScrollBar;

    /**
     *  The ContainerView class is the default view for
     *  the org.apache.flex.core.ContainerBase classes.
     *  It lets you use some CSS styles to manage the border, background
     *  and padding around the content area.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class ContainerView extends BeadViewBase implements IBeadView, ILayoutParent
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function ContainerView()
		{
		}

        /**
         *  The actual parent that parents the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		protected var actualParent:UIBase;

        /**
         *  The layout.  The layout may actually layout
         *  the children of the internal content area
         *  and not the pieces of the "chrome" like titlebars
         *  and borders.  The ContainerView or its subclass will have some
         *  baked-in logic for handling the chrome.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        protected var layout:IBeadLayout;

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

            var host:UIBase = value as UIBase;

            if (host.isWidthSizedToContent() && host.isHeightSizedToContent())
            {
                // if both dimensions are sized to content, then only draw the
                // borders, etc, after a child is added.  The children in an MXML
                // document don't send this event until the last child is added.
                host.addEventListener("childrenAdded", changeHandler);
                host.addEventListener("layoutNeeded", changeHandler);
                // listen for width and height changes as well in case the app
                // switches away from content sizing via binding or other code
                host.addEventListener("widthChanged", changeHandler);
                host.addEventListener("heightChanged", changeHandler);
            }
            else
            {
                // otherwise, listen for size changes before drawing
                // borders and laying out children.
                host.addEventListener("widthChanged", changeHandler);
                host.addEventListener("heightChanged", changeHandler);
                host.addEventListener("sizeChanged", sizeChangeHandler);
                // if we have fixed size in both dimensions, listen for children
				// being added, but also force an initial display to get the
				// background, border, etc.
                if (!isNaN(host.explicitWidth) && !isNaN(host.explicitHeight)) {
                    host.addEventListener("childrenAdded", changeHandler);
					displayBackgroundAndBorder(host);
				}
            }
            checkActualParent();
        }

        private function checkActualParent():Boolean
        {
            var host:UIBase = UIBase(_strand);
            if (contentAreaNeeded())
            {
                if (actualParent == null || actualParent == host)
                {
                    actualParent = new ContainerContentArea();
					actualParent.className = "ActualParent";
                    host.addElement(actualParent, false);
                    ContainerBase(host).setActualParent(actualParent);
                }
                return true;
            }
            else
            {
                actualParent = host;
            }
            return false;
        }

        private function sizeChangeHandler(event:Event):void
        {
            var host:UIBase = UIBase(_strand);
            host.addEventListener("childrenAdded", changeHandler);
            host.addEventListener("layoutNeeded", changeHandler);
            host.addEventListener("itemsCreated", changeHandler);
            changeHandler(event);
        }

        private var inChangeHandler:Boolean;

        /**
         *  React if the size changed or content changed
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        protected function changeHandler(event:Event):void
        {
            if (inChangeHandler) return;

            inChangeHandler = true;

            var host:UIBase = UIBase(_strand);
			var originalHostWidth:Number = host.width;
			var originalHostHeight:Number = host.height;

            if (layout == null)
            {
                layout = _strand.getBeadByType(IBeadLayout) as IBeadLayout;
                if (layout == null)
                {
                    var c:Class = ValuesManager.valuesImpl.getValue(host, "iBeadLayout");
                    if (c)
                    {
                        layout = new c() as IBeadLayout;
                        _strand.addBead(layout);
                    }
                }
            }

			var padding:Object = determinePadding();

			if (checkActualParent())
			{
				actualParent.x = padding.paddingLeft;
				actualParent.y = padding.paddingTop;
            }
            var pb:Number = padding.paddingBottom;
            if (isNaN(pb))
                pb = 0;
            var pr:Number = padding.paddingRight;
            if (isNaN(pr))
                pr = 0;

			var sizeChangedByLayout:Boolean;

            // if the width is dictated by the parent
            if (!host.isWidthSizedToContent())
            {
                if (actualParent != host)
                {
                    // force the width of the internal content area as desired.
                    actualParent.setWidth(host.width - padding.paddingLeft - pr);
                }
                // run the layout
                sizeChangedByLayout = layout.layout();
            }
            else
            {
                // if the height is dictated by the parent
                if (!host.isHeightSizedToContent())
                {
                    if (actualParent != host)
                    {
                        // force the height
                        actualParent.setHeight(host.height - padding.paddingTop - pb);
                    }
                }
                sizeChangedByLayout = layout.layout();
                if (actualParent != host)
                {
                    // actualParent.width should be the new width after layout.
                    // set the host's width.  This should send a widthChanged event and
                    // have it blocked at the beginning of this method
                    host.setWidth(padding.paddingLeft + pr + actualParent.width);
                }
            }
            // and if the height is sized to content, set the height now as well.
            if (host != actualParent)
	        {
                if (host.isHeightSizedToContent()) {
                    host.setHeight(padding.paddingTop + pb + actualParent.height);
				}
                else
                    actualParent.setHeight(host.height - padding.paddingTop - pb);
		    }
			// if host and actualParent are the same, determine if the layout changed
			// the size and if, dispatch events based on what changed
			else if (sizeChangedByLayout)
			{
				if (originalHostWidth != host.width)
					host.dispatchEvent(new Event("widthChanged"));
				if (originalHostHeight != host.height)
					host.dispatchEvent(new Event("heightChanged"));
			}

			displayBackgroundAndBorder(host);

            inChangeHandler = false;
		}

		protected function displayBackgroundAndBorder(host:UIBase) : void
		{
			var backgroundColor:Object = ValuesManager.valuesImpl.getValue(host, "background-color");
			var backgroundImage:Object = ValuesManager.valuesImpl.getValue(host, "background-image");
			if (backgroundColor != null || backgroundImage != null)
			{
				if (host.getBeadByType(IBackgroundBead) == null)
					host.addBead(new (ValuesManager.valuesImpl.getValue(host, "iBackgroundBead")) as IBead);
			}

			var borderStyle:String;
			var borderStyles:Object = ValuesManager.valuesImpl.getValue(host, "border");
			if (borderStyles is Array)
			{
				borderStyle = borderStyles[1];
			}
			if (borderStyle == null)
			{
				borderStyle = ValuesManager.valuesImpl.getValue(host, "border-style") as String;
			}
			if (borderStyle != null && borderStyle != "none")
			{
				if (host.getBeadByType(IBorderBead) == null)
					host.addBead(new (ValuesManager.valuesImpl.getValue(host, "iBorderBead")) as IBead);
			}
		}

		/**
		 *  Determines the top and left padding values, if any, as set by
		 *  padding style values. This includes "padding" for all padding values
		 *  as well as "padding-left" and "padding-top".
		 *
		 *  Returns an object with paddingLeft and paddingTop properties.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		protected function determinePadding():Object
		{
			var paddingLeft:Object;
			var paddingTop:Object;
			var paddingRight:Object;
			var paddingBottom:Object;
			var padding:Object = ValuesManager.valuesImpl.getValue(_strand, "padding");
			if (padding is Array)
			{
				if (padding.length == 1)
					paddingLeft = paddingTop = padding[0];
				else if (padding.length <= 3)
				{
					paddingLeft = padding[1];
					paddingTop = padding[0];
				}
				else if (padding.length == 4)
				{
					paddingLeft = padding[3];
					paddingTop = padding[0];
				}
			}
			else if (padding == null)
			{
				paddingLeft = ValuesManager.valuesImpl.getValue(_strand, "padding-left");
				paddingTop = ValuesManager.valuesImpl.getValue(_strand, "padding-top");
				paddingRight = ValuesManager.valuesImpl.getValue(_strand, "padding-right");
				paddingBottom = ValuesManager.valuesImpl.getValue(_strand, "padding-bottom");
			}
			else
			{
				paddingLeft = paddingTop = padding;
				paddingRight = paddingBottom = padding;
			}
			var pl:Number = Number(paddingLeft);
			var pt:Number = Number(paddingTop);
			var pr:Number = Number(paddingRight);
			var pb:Number = Number(paddingBottom);

			return {paddingLeft:pl, paddingTop:pt, paddingRight:pr, paddingBottom:pb};
		}

		/**
		 *  Returns true if container to create a separate ContainerContentArea.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		protected function contentAreaNeeded():Boolean
		{
			var padding:Object = determinePadding();

			return (!isNaN(padding.paddingLeft) && padding.paddingLeft > 0 ||
				    !isNaN(padding.paddingTop) && padding.paddingTop > 0);
		}

        /**
         *  The parent of the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get contentView():IParentIUIBase
		{
			return actualParent;
		}

        /**
         *  The host component, which can resize to different slots.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get resizableView():IUIBase
		{
			return _strand as IUIBase;
		}

        private var inGetViewHeight:Boolean;

		/**
		 *  @copy org.apache.flex.core.IBeadView#viewHeight
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		override public function get viewHeight():Number
		{
            if (inGetViewHeight)
            {
                //trace("ContainerView: no height set for " + host);
                return host["$height"];
            }
            inGetViewHeight = true;
			var vh:Number = contentView.height;
            inGetViewHeight = false;
            return vh;
		}

        private var inGetViewWidth:Boolean;

		/**
		 *  @copy org.apache.flex.core.IBeadView#viewWidth
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		override public function get viewWidth():Number
		{
            if (inGetViewWidth)
            {
                //trace("ContainerView: no width set for " + host);
                return host["$width"];
            }
            inGetViewWidth = true;
			var vw:Number = contentView.width;
            inGetViewWidth = false;
			return vw;
		}

	}
}

switch this back to && so we only go down this path when sized to content


package stdio {
  import flash.display.Sprite
  import flash.events.*
  import flash.net.*

  public class Process extends Sprite {
    public static var instance: Process = null

    private const stdin_buffer: StreamBuffer = new StreamBuffer
    private const stdin_socket: SocketStream = new SocketStream
    private const stdout_socket: SocketStream = new SocketStream
    private const stderr_socket: SocketStream = new SocketStream

    public function Process() {
      instance = this
      connect(function (): void {
        listen_for_uncaught_errors()
        main()
      })
    }

    public function main(): void {
      warn("override public function main(): void {}")
    }

    public function exit(status: int = 0): void {
      post("exit", status.toString())
    }

    private function get base_url(): String {
      // XXX: Not very robust.
      return loaderInfo.loaderURL.replace(/\?.*/, "") + "/"
    }

    private function post(
      path: String,
      content: String,
      callback: Function = null
    ): void {
      const request: URLRequest = new URLRequest(base_url + path)

      request.method = "POST"
      request.data = content

      const loader: URLLoader = new URLLoader

      if (callback !== null) {
        loader.addEventListener(
          Event.COMPLETE,
          function (event: Event): void {
            callback(loader.data)
          }
        )
      }

      loader.load(request)
    }

    public function get argv(): Array {
      return get_parameter("argv").split(" ").map(
        function (argument: String, ...rest: Array): String {
          return decodeURIComponent(argument)
        }
      )
    }

    private function get_parameter(name: String): String {
      return loaderInfo.parameters[name]
    }

    private function get_int_parameter(name: String): int {
      return parseInt(get_parameter(name))
    }

    public function gets(callback: Function): void {
      stdin.read_line(callback)
    }

    public function puts(value: Object): void {
      stdout.write_line(value)
    }

    public function warn(value: Object): void {
      stderr.write_line(value)
    }

    public function get stdin(): InputStream { return stdin_buffer }
    public function get stdout(): OutputStream { return stdout_socket }
    public function get stderr(): OutputStream { return stderr_socket }

    private function listen_for_uncaught_errors(): void {
      loaderInfo.uncaughtErrorEvents.addEventListener(
        UncaughtErrorEvent.UNCAUGHT_ERROR,
        handle_uncaught_error_event
      )
    }

    protected function handle_uncaught_error_event(
      event: UncaughtErrorEvent
    ): void {
      event.preventDefault()

      post(
        "error",
        event.error is Error
          ? (event.error as Error).getStackTrace()
          : event.error,
        function (response: String): void {
          exit(1)
        }
      )
    }

    private function connect(callback: Function): void {
      stdin_socket.onopen = onopen
      stdout_socket.onopen = onopen
      stderr_socket.onopen = onopen

      stdin_socket.ondata = stdin_buffer.write
      stdin_socket.onclose = stdin_buffer.close

      stdin_socket.connect("localhost", get_int_parameter("stdin"))
      stdout_socket.connect("localhost", get_int_parameter("stdout"))
      stderr_socket.connect("localhost", get_int_parameter("stderr"))

      var n: int = 0

      function onopen(): void {
        if (++n === 3) {
          callback()
        }
      }
    }
  }
}

package stdio {
  import flash.display.Sprite
  import flash.events.*
  import flash.net.*

  public class Process extends Sprite {
    public static var instance: Process = null

    private const stdin_buffer: StreamBuffer = new StreamBuffer
    private const stdin_socket: SocketStream = new SocketStream
    private const stdout_socket: SocketStream = new SocketStream
    private const stderr_socket: SocketStream = new SocketStream

    public function Process() {
      instance = this

      if (get_parameter("stdin")) {
        connect(function (): void {
          listen_for_uncaught_errors()
          main()
        })
      } else {
        main()
      }
    }

    public function main(): void {
      warn("override public function main(): void {}")
    }

    public function exit(status: int = 0): void {
      post("exit", status.toString())
    }

    private function get base_url(): String {
      // XXX: Not very robust.
      return loaderInfo.loaderURL.replace(/\?.*/, "") + "/"
    }

    private function post(
      path: String,
      content: String,
      callback: Function = null
    ): void {
      const request: URLRequest = new URLRequest(base_url + path)

      request.method = "POST"
      request.data = content

      const loader: URLLoader = new URLLoader

      if (callback !== null) {
        loader.addEventListener(
          Event.COMPLETE,
          function (event: Event): void {
            callback(loader.data)
          }
        )
      }

      loader.load(request)
    }

    public function get argv(): Array {
      return get_parameter("argv").split(" ").map(
        function (argument: String, ...rest: Array): String {
          return decodeURIComponent(argument)
        }
      )
    }

    private function get_parameter(name: String): String {
      return loaderInfo.parameters[name]
    }

    private function get_int_parameter(name: String): int {
      return parseInt(get_parameter(name))
    }

    public function gets(callback: Function): void {
      stdin.read_line(callback)
    }

    public function puts(value: Object): void {
      stdout.write_line(value)
    }

    public function warn(value: Object): void {
      stderr.write_line(value)
    }

    public function get stdin(): InputStream { return stdin_buffer }
    public function get stdout(): OutputStream { return stdout_socket }
    public function get stderr(): OutputStream { return stderr_socket }

    private function listen_for_uncaught_errors(): void {
      loaderInfo.uncaughtErrorEvents.addEventListener(
        UncaughtErrorEvent.UNCAUGHT_ERROR,
        handle_uncaught_error_event
      )
    }

    protected function handle_uncaught_error_event(
      event: UncaughtErrorEvent
    ): void {
      event.preventDefault()

      post(
        "error",
        event.error is Error
          ? (event.error as Error).getStackTrace()
          : event.error,
        function (response: String): void {
          exit(1)
        }
      )
    }

    private function connect(callback: Function): void {
      stdin_socket.onopen = onopen
      stdout_socket.onopen = onopen
      stderr_socket.onopen = onopen

      stdin_socket.ondata = stdin_buffer.write
      stdin_socket.onclose = stdin_buffer.close

      stdin_socket.connect("localhost", get_int_parameter("stdin"))
      stdout_socket.connect("localhost", get_int_parameter("stdout"))
      stderr_socket.connect("localhost", get_int_parameter("stderr"))

      var n: int = 0

      function onopen(): void {
        if (++n === 3) {
          callback()
        }
      }
    }
  }
}

Enable processes to run outside flashplayer-stdio envionment.


package core.utils
{
	import dragonBones.utils.ConstValues;

	public class OptimizeDataUtils
	{
		private static var animationPropertyArray:Array = [ConstValues.A_FADE_IN_TIME, ConstValues.A_SCALE, ConstValues.A_LOOP];
		private static var animationValueArray:Array = [0,1,1];

		private static var timelinePropertyArray:Array = [ConstValues.A_SCALE, ConstValues.A_OFFSET, ConstValues.A_PIVOT_X, ConstValues.A_PIVOT_Y];
		private static var timelineValueArray:Array = [1,0,0,0];

		private static var framePropertyArray:Array = [ConstValues.A_TWEEN_SCALE, ConstValues.A_TWEEN_ROTATE, ConstValues.A_HIDE, ConstValues.A_DISPLAY_INDEX, ConstValues.A_SCALE_X_OFFSET, ConstValues.A_SCALE_Y_OFFSET];
		private static var frameValueArray:Array = [1,0,0,0,0,0];

		private static var transformPropertyArray:Array = [ConstValues.A_X, ConstValues.A_Y, ConstValues.A_SKEW_X, ConstValues.A_SKEW_Y, ConstValues.A_SCALE_X, ConstValues.A_SCALE_Y, ConstValues.A_PIVOT_X, ConstValues.A_PIVOT_Y];
		private static var transformValueArray:Array = [0,0,0,0,1,1,0,0];

		private static var colorTransformPropertyArray:Array = [ConstValues.A_ALPHA_OFFSET, ConstValues.A_RED_OFFSET, ConstValues.A_GREEN_OFFSET, ConstValues.A_BLUE_OFFSET, ConstValues.A_ALPHA_MULTIPLIER,  ConstValues.A_RED_MULTIPLIER,  ConstValues.A_GREEN_MULTIPLIER,  ConstValues.A_BLUE_MULTIPLIER];
		private static var colorTransformValueArray:Array = [0,0,0,0,1,1,1,1];

		public static function optimizeData(dragonBonesData:Object):void
		{
			for each(var armatureData:Object in dragonBonesData.armature)
			{
				var boneDataList:Array = armatureData.bone;

				for each(var boneData:Object in boneDataList)
				{
					optimizeTransform(boneData.transform);
				}

				var skinList:Array = armatureData.skin;
				var slotList:Array;
				var displayList:Array;
				for each(var skinData:Object in skinList)
				{
					slotList = skinData.slot;
					for each(var slotData:Object in slotList)
					{
						displayList = slotData.display;
						for each(var displayData:Object in displayList)
						{
							optimizeTransform(displayData.transform);
						}
					}
				}

				var animationList:Array = armatureData.animation;
				var timelineList:Array;
				var frameList:Array;
				for each(var animationData:Object in animationList)
				{
					optimizeAnimation(animationData);

					timelineList = animationData.timeline;
					for each(var timelineData:Object in timelineList)
					{
						optimizeTimeline(timelineData);

						frameList = timelineData.frame;
						for each(var frameData:Object in frameList)
						{
							optimizeFrame(frameData);
							optimizeTransform(frameData.transform);
							optimizeColorTransform(frameData.colorTransform);
						}
					}
				}
			}
		}

		private static function optimizeAnimation(animationData:Object):void
		{
			optimizeItem(animationData, animationPropertyArray, animationValueArray, 4);
		}

		private static function optimizeTimeline(timelineData:Object):void
		{
			optimizeItem(timelineData, timelinePropertyArray, timelineValueArray, 4);
		}

		private static function optimizeFrame(frameData:Object):void
		{
			optimizeItem(frameData, framePropertyArray, frameValueArray, 4);
		}

		private static function optimizeTransform(transform:Object):void
		{
			optimizeItem(transform, transformPropertyArray, transformValueArray, 4);
		}

		private static function optimizeColorTransform(colorTransform:Object):void
		{
			optimizeItem(colorTransform, transformPropertyArray, transformValueArray, 4);
		}

		private static function optimizeItem(item:Object, propertyArray:Array, valueArray:Array, prec:uint = 4):void
		{
			if(!item)
			{
				return;
			}

			var i:int = propertyArray.length;
			var property:String;
			var value:Number;
			while(i--)
			{
				property = propertyArray[i];
				value = valueArray[i];
				if(!item.hasOwnProperty(property))
				{
					continue;
				}
				if(compareWith(item[property], value, prec))
				{
					delete item[property];
				}
				else
				{
					item[property] = Number(Number(item[property]).toFixed(prec));
				}
			}
		}

		private static function compareWith(source:Number, target:Number, prec:uint):Boolean
		{
			var delta:Number = 1 / Math.pow(10, prec);
			if(source >= target - delta && source <= target + delta)
			{
				return true;
			}
			return false;
		}
	}
}

package core.utils
{
	import dragonBones.utils.ConstValues;

	public class OptimizeDataUtils
	{
		private static var animationPropertyArray:Array = [ConstValues.A_FADE_IN_TIME, ConstValues.A_SCALE, ConstValues.A_LOOP];
		private static var animationValueArray:Array = [0,1,1];

		private static var timelinePropertyArray:Array = [ConstValues.A_SCALE, ConstValues.A_OFFSET, ConstValues.A_PIVOT_X, ConstValues.A_PIVOT_Y];
		private static var timelineValueArray:Array = [1,0,0,0];

		private static var framePropertyArray:Array = [ConstValues.A_TWEEN_SCALE, ConstValues.A_TWEEN_ROTATE, ConstValues.A_HIDE, ConstValues.A_DISPLAY_INDEX, ConstValues.A_SCALE_X_OFFSET, ConstValues.A_SCALE_Y_OFFSET];
		private static var frameValueArray:Array = [1,0,0,0,0,0];

		private static var transformPropertyArray:Array = [ConstValues.A_X, ConstValues.A_Y, ConstValues.A_SKEW_X, ConstValues.A_SKEW_Y, ConstValues.A_SCALE_X, ConstValues.A_SCALE_Y, ConstValues.A_PIVOT_X, ConstValues.A_PIVOT_Y];
		private static var transformValueArray:Array = [0,0,0,0,1,1,0,0];

		private static var colorTransformPropertyArray:Array = [ConstValues.A_ALPHA_OFFSET, ConstValues.A_RED_OFFSET, ConstValues.A_GREEN_OFFSET, ConstValues.A_BLUE_OFFSET, ConstValues.A_ALPHA_MULTIPLIER,  ConstValues.A_RED_MULTIPLIER,  ConstValues.A_GREEN_MULTIPLIER,  ConstValues.A_BLUE_MULTIPLIER];
		private static var colorTransformValueArray:Array = [0,0,0,0,100,100,100,100];

		public static function optimizeData(dragonBonesData:Object):void
		{
			for each(var armatureData:Object in dragonBonesData.armature)
			{
				var boneDataList:Array = armatureData.bone;

				for each(var boneData:Object in boneDataList)
				{
					optimizeTransform(boneData.transform);
				}

				var skinList:Array = armatureData.skin;
				var slotList:Array;
				var displayList:Array;
				for each(var skinData:Object in skinList)
				{
					slotList = skinData.slot;
					for each(var slotData:Object in slotList)
					{
						displayList = slotData.display;
						for each(var displayData:Object in displayList)
						{
							optimizeTransform(displayData.transform);
						}
					}
				}

				var animationList:Array = armatureData.animation;
				var timelineList:Array;
				var frameList:Array;
				for each(var animationData:Object in animationList)
				{
					optimizeAnimation(animationData);

					timelineList = animationData.timeline;
					for each(var timelineData:Object in timelineList)
					{
						optimizeTimeline(timelineData);

						frameList = timelineData.frame;
						for each(var frameData:Object in frameList)
						{
							optimizeFrame(frameData);
							optimizeTransform(frameData.transform);
							optimizeColorTransform(frameData.colorTransform);
						}
					}
				}
			}
		}

		private static function optimizeAnimation(animationData:Object):void
		{
			optimizeItem(animationData, animationPropertyArray, animationValueArray, 4);
		}

		private static function optimizeTimeline(timelineData:Object):void
		{
			optimizeItem(timelineData, timelinePropertyArray, timelineValueArray, 4);
		}

		private static function optimizeFrame(frameData:Object):void
		{
			optimizeItem(frameData, framePropertyArray, frameValueArray, 4);
		}

		private static function optimizeTransform(transform:Object):void
		{
			optimizeItem(transform, transformPropertyArray, transformValueArray, 4);
		}

		private static function optimizeColorTransform(colorTransform:Object):void
		{
			optimizeItem(colorTransform, transformPropertyArray, transformValueArray, 4);
		}

		private static function optimizeItem(item:Object, propertyArray:Array, valueArray:Array, prec:uint = 4):void
		{
			if(!item)
			{
				return;
			}

			var i:int = propertyArray.length;
			var property:String;
			var value:Number;
			while(i--)
			{
				property = propertyArray[i];
				value = valueArray[i];
				if(!item.hasOwnProperty(property))
				{
					continue;
				}
				if(compareWith(item[property], value, prec))
				{
					delete item[property];
				}
				else
				{
					item[property] = Number(Number(item[property]).toFixed(prec));
				}
			}
		}

		private static function compareWith(source:Number, target:Number, prec:uint):Boolean
		{
			var delta:Number = 1 / Math.pow(10, prec);
			if(source >= target - delta && source <= target + delta)
			{
				return true;
			}
			return false;
		}
	}
}

fix a DesignPanel colorTransform default value bug