package org.mangui.hls.demux {
    import org.mangui.hls.model.AudioTrack;
    import org.mangui.hls.flv.FLVTag;

    import flash.utils.ByteArray;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Constants and utilities for the AAC audio format. **/
    public class AACDemuxer implements Demuxer {
        /** ADTS Syncword (111111111111), ID (MPEG4), layer (00) and protection_absent (1).**/
        private static const SYNCWORD : uint = 0xFFF1;
        /** ADTS Syncword with MPEG2 stream ID (used by e.g. Squeeze 7). **/
        private static const SYNCWORD_2 : uint = 0xFFF9;
        /** ADTS Syncword with MPEG2 stream ID (used by e.g. Envivio 4Caster). **/
        private static const SYNCWORD_3 : uint = 0xFFF8;
        /** ADTS/ADIF sample rates index. **/
        private static const RATES : Array = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
        /** ADIF profile index (ADTS doesn't have Null). **/
        private static const PROFILES : Array = ['Null', 'Main', 'LC', 'SSR', 'LTP', 'SBR'];
        /** Byte data to be read **/
        private var _data : ByteArray;
        /* callback functions for audio selection, and parsing progress/complete */
        private var _callback_audioselect : Function;
        private var _callback_progress : Function;
        private var _callback_complete : Function;

        /** append new data */
        public function append(data : ByteArray) : void {
            if (_data == null) {
                _data = new ByteArray();
            }
            _data.writeBytes(data);
        }

        /** cancel demux operation */
        public function cancel() : void {
            _data = null;
        }

        public function notifycomplete() : void {
            CONFIG::LOGGING {
                Log.debug("AAC: extracting AAC tags");
            }
            var audioTags : Vector.<FLVTag> = new Vector.<FLVTag>();
            /* parse AAC, convert Elementary Streams to TAG */
            _data.position = 0;
            var id3 : ID3 = new ID3(_data);
            // AAC should contain ID3 tag filled with a timestamp
            var frames : Vector.<AudioFrame> = AACDemuxer.getFrames(_data, _data.position);
            var adif : ByteArray = getADIF(_data, id3.len);
            var adifTag : FLVTag = new FLVTag(FLVTag.AAC_HEADER, id3.timestamp, id3.timestamp, true);
            adifTag.push(adif, 0, adif.length);
            audioTags.push(adifTag);

            var audioTag : FLVTag;
            var stamp : uint;
            var i : int = 0;

            while (i < frames.length) {
                stamp = Math.round(id3.timestamp + i * 1024 * 1000 / frames[i].rate);
                audioTag = new FLVTag(FLVTag.AAC_RAW, stamp, stamp, false);
                if (i != frames.length - 1) {
                    audioTag.push(_data, frames[i].start, frames[i].length);
                } else {
                    audioTag.push(_data, frames[i].start, _data.length - frames[i].start);
                }
                audioTags.push(audioTag);
                i++;
            }
            var audiotracks : Vector.<AudioTrack> = new Vector.<AudioTrack>();
            audiotracks.push(new AudioTrack('AAC ES', AudioTrack.FROM_DEMUX, 0, true));
            // report unique audio track. dont check return value as obviously the track will be selected
            _callback_audioselect(audiotracks);
            CONFIG::LOGGING {
                Log.debug("AAC: all tags extracted, callback demux");
            }
            _data = null;
            _callback_progress(audioTags);
            _callback_complete();
        }

        public function AACDemuxer(callback_audioselect : Function, callback_progress : Function, callback_complete : Function) : void {
            _callback_audioselect = callback_audioselect;
            _callback_progress = callback_progress;
            _callback_complete = callback_complete;
        };

        public static function probe(data : ByteArray) : Boolean {
            var pos : uint = data.position;
            var id3 : ID3 = new ID3(data);
            // AAC should contain ID3 tag filled with a timestamp
            if (id3.hasTimestamp) {
                while (data.bytesAvailable > 1) {
                    // Check for ADTS header
                    var short : uint = data.readUnsignedShort();
                    if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                        // rewind to sync word
                        data.position -= 2;
                        return true;
                    } else {
                        data.position--;
                    }
                }
                data.position = pos;
            }
            return false;
        }

        /** Get ADIF header from ADTS stream. **/
        public static function getADIF(adts : ByteArray, position : uint) : ByteArray {
            adts.position = position;
            var short : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while ((adts.bytesAvailable > 5) && (short != SYNCWORD) && (short != SYNCWORD_2) && (short != SYNCWORD_3)) {
                short = adts.readUnsignedShort();
            }

            if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                var profile : uint = (adts.readByte() & 0xF0) >> 6;
                // Correcting zero-index of ADIF and Flash playing only LC/HE.
                if (profile > 3) {
                    profile = 5;
                } else {
                    profile = 2;
                }
                adts.position--;
                var srate : uint = (adts.readByte() & 0x3C) >> 2;
                adts.position--;
                var channels : uint = (adts.readShort() & 0x01C0) >> 6;
            } else {
                throw new Error("Stream did not start with ADTS header.");
            }
            // 5 bits profile + 4 bits samplerate + 4 bits channels.
            var adif : ByteArray = new ByteArray();
            adif.writeByte((profile << 3) + (srate >> 1));
            adif.writeByte((srate << 7) + (channels << 3));
            CONFIG::LOGGING {
                Log.debug('AAC: ' + PROFILES[profile] + ', ' + RATES[srate] + ' Hz ' + channels + ' channel(s)');
            }
            // Reset position and return adif.
            adts.position -= 4;
            adif.position = 0;
            return adif;
        };

        /** Get a list with AAC frames from ADTS stream. **/
        public static function getFrames(adts : ByteArray, position : uint) : Vector.<AudioFrame> {
            var frames : Vector.<AudioFrame> = new Vector.<AudioFrame>();
            var frame_start : uint;
            var frame_length : uint;
            var id3 : ID3 = new ID3(adts);
            position += id3.len;
            // Get raw AAC frames from audio stream.
            adts.position = position;
            var samplerate : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while (adts.bytesAvailable > 5) {
                // Check for ADTS header
                var short : uint = adts.readUnsignedShort();
                if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                    // Store samplerate for offsetting timestamps.
                    if (!samplerate) {
                        samplerate = RATES[(adts.readByte() & 0x3C) >> 2];
                        adts.position--;
                    }
                    // Store raw AAC preceding this header.
                    if (frame_start) {
                        frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                    }
                    if (short == SYNCWORD_3) {
                        // ADTS header is 9 bytes.
                        frame_length = ((adts.readUnsignedInt() & 0x0003FFE0) >> 5) - 9;
                        frame_start = adts.position + 3;
                        adts.position += frame_length + 3;
                    } else {
                        // ADTS header is 7 bytes.
                        frame_length = ((adts.readUnsignedInt() & 0x0003FFE0) >> 5) - 7;
                        frame_start = adts.position + 1;
                        adts.position += frame_length + 1;
                    }
                } else {
                    CONFIG::LOGGING {
                        Log.debug("no ADTS header found, probing...");
                    }
                    adts.position--;
                }
            }
            if (frame_start) {
                // check if we have a complete frame available at the end, i.e. last found frame is fitting in this PES packet
                var overflow : int = frame_start + frame_length - adts.length;
                if (overflow <= 0 ) {
                    // no overflow, Write raw AAC after last header.
                    frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                } else {
                    CONFIG::LOGGING {
                        Log.debug2("ADTS overflow at the end of PES packet, missing " + overflow + " bytes to complete the ADTS frame");
                    }
                }
            } else if (frames.length == 0) {
                null;
                CONFIG::LOGGING {
                    Log.warn("No ADTS headers found in this stream.");
                }
            }
            adts.position = position;
            return frames;
        };
    }
}

package org.mangui.hls.demux {
    import org.mangui.hls.model.AudioTrack;
    import org.mangui.hls.flv.FLVTag;

    import flash.utils.ByteArray;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Constants and utilities for the AAC audio format, refer to
     *  http://wiki.multimedia.cx/index.php?title=ADTS
     **/
    public class AACDemuxer implements Demuxer {
        /** ADTS Syncword (0xFFF), ID:0 (MPEG4), layer (00) and protection_absent (1:no CRC).**/
        private static const SYNCWORD : uint = 0xFFF1;
        /** ADTS Syncword (0xFFF), ID:1 (MPEG2), layer (00) and protection_absent (1: no CRC).**/
        private static const SYNCWORD_2 : uint = 0xFFF9;
        /** ADTS Syncword (0xFFF), ID:1 (MPEG2), layer (00) and protection_absent (0: CRC).**/
        private static const SYNCWORD_3 : uint = 0xFFF8;
        /** ADTS/ADIF sample rates index. **/
        private static const RATES : Array = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
        /** ADIF profile index (ADTS doesn't have Null). **/
        private static const PROFILES : Array = ['Null', 'Main', 'LC', 'SSR', 'LTP', 'SBR'];
        /** Byte data to be read **/
        private var _data : ByteArray;
        /* callback functions for audio selection, and parsing progress/complete */
        private var _callback_audioselect : Function;
        private var _callback_progress : Function;
        private var _callback_complete : Function;

        /** append new data */
        public function append(data : ByteArray) : void {
            if (_data == null) {
                _data = new ByteArray();
            }
            _data.writeBytes(data);
        }

        /** cancel demux operation */
        public function cancel() : void {
            _data = null;
        }

        public function notifycomplete() : void {
            CONFIG::LOGGING {
                Log.debug("AAC: extracting AAC tags");
            }
            var audioTags : Vector.<FLVTag> = new Vector.<FLVTag>();
            /* parse AAC, convert Elementary Streams to TAG */
            _data.position = 0;
            var id3 : ID3 = new ID3(_data);
            // AAC should contain ID3 tag filled with a timestamp
            var frames : Vector.<AudioFrame> = AACDemuxer.getFrames(_data, _data.position);
            var adif : ByteArray = getADIF(_data, id3.len);
            var adifTag : FLVTag = new FLVTag(FLVTag.AAC_HEADER, id3.timestamp, id3.timestamp, true);
            adifTag.push(adif, 0, adif.length);
            audioTags.push(adifTag);

            var audioTag : FLVTag;
            var stamp : uint;
            var i : int = 0;

            while (i < frames.length) {
                stamp = Math.round(id3.timestamp + i * 1024 * 1000 / frames[i].rate);
                audioTag = new FLVTag(FLVTag.AAC_RAW, stamp, stamp, false);
                if (i != frames.length - 1) {
                    audioTag.push(_data, frames[i].start, frames[i].length);
                } else {
                    audioTag.push(_data, frames[i].start, _data.length - frames[i].start);
                }
                audioTags.push(audioTag);
                i++;
            }
            var audiotracks : Vector.<AudioTrack> = new Vector.<AudioTrack>();
            audiotracks.push(new AudioTrack('AAC ES', AudioTrack.FROM_DEMUX, 0, true));
            // report unique audio track. dont check return value as obviously the track will be selected
            _callback_audioselect(audiotracks);
            CONFIG::LOGGING {
                Log.debug("AAC: all tags extracted, callback demux");
            }
            _data = null;
            _callback_progress(audioTags);
            _callback_complete();
        }

        public function AACDemuxer(callback_audioselect : Function, callback_progress : Function, callback_complete : Function) : void {
            _callback_audioselect = callback_audioselect;
            _callback_progress = callback_progress;
            _callback_complete = callback_complete;
        };

        public static function probe(data : ByteArray) : Boolean {
            var pos : uint = data.position;
            var id3 : ID3 = new ID3(data);
            // AAC should contain ID3 tag filled with a timestamp
            if (id3.hasTimestamp) {
                while (data.bytesAvailable > 1) {
                    // Check for ADTS header
                    var short : uint = data.readUnsignedShort();
                    if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                        // rewind to sync word
                        data.position -= 2;
                        return true;
                    } else {
                        data.position--;
                    }
                }
                data.position = pos;
            }
            return false;
        }

        /** Get ADIF header from ADTS stream. **/
        public static function getADIF(adts : ByteArray, position : uint) : ByteArray {
            adts.position = position;
            var short : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while ((adts.bytesAvailable > 5) && (short != SYNCWORD) && (short != SYNCWORD_2) && (short != SYNCWORD_3)) {
                short = adts.readUnsignedShort();
                adts.position--;
            }
            adts.position++;
            if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                var profile : uint = (adts.readByte() & 0xF0) >> 6;
                // Correcting zero-index of ADIF and Flash playing only LC/HE.
                if (profile > 3) {
                    profile = 5;
                } else {
                    profile = 2;
                }
                adts.position--;
                var srate : uint = (adts.readByte() & 0x3C) >> 2;
                adts.position--;
                var channels : uint = (adts.readShort() & 0x01C0) >> 6;
            } else {
                throw new Error("Stream did not start with ADTS header.");
            }
            // 5 bits profile + 4 bits samplerate + 4 bits channels.
            var adif : ByteArray = new ByteArray();
            adif.writeByte((profile << 3) + (srate >> 1));
            adif.writeByte((srate << 7) + (channels << 3));
            CONFIG::LOGGING {
                Log.debug('AAC: ' + PROFILES[profile] + ', ' + RATES[srate] + ' Hz ' + channels + ' channel(s)');
            }
            // Reset position and return adif.
            adts.position -= 4;
            adif.position = 0;
            return adif;
        };

        /** Get a list with AAC frames from ADTS stream. **/
        public static function getFrames(adts : ByteArray, position : uint) : Vector.<AudioFrame> {
            var frames : Vector.<AudioFrame> = new Vector.<AudioFrame>();
            var frame_start : uint;
            var frame_length : uint;
            var id3 : ID3 = new ID3(adts);
            position += id3.len;
            // Get raw AAC frames from audio stream.
            adts.position = position;
            var samplerate : uint;
            // we need at least 6 bytes, 2 for sync word, 4 for frame length
            while (adts.bytesAvailable > 5) {
                // Check for ADTS header
                var short : uint = adts.readUnsignedShort();
                if (short == SYNCWORD || short == SYNCWORD_2 || short == SYNCWORD_3) {
                    // Store samplerate for offsetting timestamps.
                    if (!samplerate) {
                        samplerate = RATES[(adts.readByte() & 0x3C) >> 2];
                        adts.position--;
                    }
                    // Store raw AAC preceding this header.
                    if (frame_start) {
                        frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                    }
                    // protection_absent=1, crc_len = 0,protection_absent=0,crc_len=2
                    var crc_len : int = (1 - (short & 0x1)) << 1;
                    // ADTS header is 7+crc_len bytes.
                    frame_length = ((adts.readUnsignedInt() & 0x0003FFE0) >> 5) - 7 - crc_len;
                    frame_start = adts.position + 1 + crc_len;
                    adts.position += frame_length + 1 + crc_len;
                } else {
                    CONFIG::LOGGING {
                        Log.debug2("no ADTS header found, probing...");
                    }
                    adts.position--;
                }
            }
            if (frame_start) {
                // check if we have a complete frame available at the end, i.e. last found frame is fitting in this PES packet
                var overflow : int = frame_start + frame_length - adts.length;
                if (overflow <= 0 ) {
                    // no overflow, Write raw AAC after last header.
                    frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate));
                } else {
                    CONFIG::LOGGING {
                        Log.debug2("ADTS overflow at the end of PES packet, missing " + overflow + " bytes to complete the ADTS frame");
                    }
                }
            } else if (frames.length == 0) {
                null;
                CONFIG::LOGGING {
                    Log.warn("No ADTS headers found in this stream.");
                }
            }
            adts.position = position;
            return frames;
        };
    }
}

simplify ADTS header parsing code related to #135


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

	import org.apache.flex.core.IBeadLayout;
    import org.apache.flex.core.ILayoutChild;
	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.ValuesManager;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;

    /**
     *  The NonVirtualVerticalLayout class is a simple layout
     *  bead.  It takes the set of children and lays them out
     *  vertically in one column, separating them according to
     *  CSS layout rules for margin and horizontal-align styles.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class NonVirtualVerticalLayout implements IBeadLayout
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function NonVirtualVerticalLayout()
		{
		}

		private var _strand:IStrand;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
			_strand = value;
			IEventDispatcher(value).addEventListener("heightChanged", changeHandler);
			IEventDispatcher(value).addEventListener("childrenAdded", changeHandler);
			IEventDispatcher(value).addEventListener("itemsCreated", changeHandler);
			IEventDispatcher(value).addEventListener("beadsAdded", changeHandler);
		}

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

			var n:int = contentView.numElements;
			var hasHorizontalFlex:Boolean;
			var flexibleHorizontalMargins:Array = [];
            var ilc:ILayoutChild;
			var marginLeft:Object;
			var marginRight:Object;
			var marginTop:Object;
			var marginBottom:Object;
			var margin:Object;
			var maxWidth:Number = 0;
			for (var i:int = 0; i < n; i++)
			{
				var child:IUIBase = contentView.getElementAt(i) as IUIBase;
				margin = ValuesManager.valuesImpl.getValue(child, "margin");
				if (margin is Array)
				{
					if (margin.length == 1)
						marginLeft = marginTop = marginRight = marginBottom = margin[0];
					else if (margin.length <= 3)
					{
						marginLeft = marginRight = margin[1];
						marginTop = marginBottom = margin[0];
					}
					else if (margin.length == 4)
					{
						marginLeft = margin[3];
						marginBottom = margin[2];
						marginRight = margin[1];
						marginTop = margin[0];
					}
				}
				else if (margin == null)
				{
					marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
					marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
					marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
					marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
				}
				else
				{
					marginLeft = marginTop = marginBottom = marginRight = margin;
				}
				var ml:Number;
				var mr:Number;
				var mt:Number;
				var mb:Number;
				var lastmb:Number;
				mt = Number(marginTop);
				if (isNaN(mt))
					mt = 0;
				mb = Number(marginBottom);
				if (isNaN(mb))
					mb = 0;
				var yy:Number;
				if (i == 0)
					child.y = mt;
				else
					child.y = yy + Math.max(mt, lastmb);
                if (child is ILayoutChild)
                {
                    ilc = child as ILayoutChild;
                    if (!isNaN(ilc.percentHeight))
                        ilc.setHeight(contentView.height * ilc.percentHeight / 100);
                }
				yy = child.y + child.height;
				lastmb = mb;
				flexibleHorizontalMargins[i] = {};
				if (marginLeft == "auto")
				{
					ml = 0;
					flexibleHorizontalMargins[i].marginLeft = marginLeft;
					hasHorizontalFlex = true;
				}
				else
				{
					ml = Number(marginLeft);
					if (isNaN(ml))
					{
						ml = 0;
						flexibleHorizontalMargins[i].marginLeft = marginLeft;
					}
					else
						flexibleHorizontalMargins[i].marginLeft = ml;
				}
				if (marginRight == "auto")
				{
					mr = 0;
					flexibleHorizontalMargins[i].marginRight = marginRight;
					hasHorizontalFlex = true;
				}
				else
				{
					mr = Number(marginRight);
					if (isNaN(mr))
					{
						mr = 0;
						flexibleHorizontalMargins[i].marginRight = marginRight;
					}
					else
						flexibleHorizontalMargins[i].marginRight = mr;
				}
				child.x = ml;
				maxWidth = Math.max(maxWidth, ml + child.width + mr);
			}
			if (hasHorizontalFlex)
			{
				for (i = 0; i < n; i++)
				{
					child = contentView.getElementAt(i) as IUIBase;
                    if (child is ILayoutChild)
                    {
                        ilc = child as ILayoutChild;
                        if (!isNaN(ilc.percentWidth))
                            ilc.setWidth(contentView.width * ilc.percentWidth / 100);
                    }
					var obj:Object = flexibleHorizontalMargins[i];
					if (obj.marginLeft == "auto" && obj.marginRight == "auto")
						child.x = maxWidth - child.width / 2;
					else if (obj.marginLeft == "auto")
						child.x = maxWidth - child.width - obj.marginRight;
				}
			}
		}
	}
}

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

	import org.apache.flex.core.IBeadLayout;
    import org.apache.flex.core.ILayoutChild;
	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.ValuesManager;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;

    /**
     *  The NonVirtualVerticalLayout class is a simple layout
     *  bead.  It takes the set of children and lays them out
     *  vertically in one column, separating them according to
     *  CSS layout rules for margin and horizontal-align styles.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class NonVirtualVerticalLayout implements IBeadLayout
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function NonVirtualVerticalLayout()
		{
		}

		private var _strand:IStrand;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
			_strand = value;
			IEventDispatcher(value).addEventListener("heightChanged", changeHandler);
			IEventDispatcher(value).addEventListener("childrenAdded", changeHandler);
			IEventDispatcher(value).addEventListener("itemsCreated", changeHandler);
            IEventDispatcher(value).addEventListener("layoutNeeded", changeHandler);
			IEventDispatcher(value).addEventListener("beadsAdded", changeHandler);
		}

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

			var n:int = contentView.numElements;
			var hasHorizontalFlex:Boolean;
			var flexibleHorizontalMargins:Array = [];
            var ilc:ILayoutChild;
			var marginLeft:Object;
			var marginRight:Object;
			var marginTop:Object;
			var marginBottom:Object;
			var margin:Object;
			var maxWidth:Number = 0;
			for (var i:int = 0; i < n; i++)
			{
				var child:IUIBase = contentView.getElementAt(i) as IUIBase;
				margin = ValuesManager.valuesImpl.getValue(child, "margin");
				if (margin is Array)
				{
					if (margin.length == 1)
						marginLeft = marginTop = marginRight = marginBottom = margin[0];
					else if (margin.length <= 3)
					{
						marginLeft = marginRight = margin[1];
						marginTop = marginBottom = margin[0];
					}
					else if (margin.length == 4)
					{
						marginLeft = margin[3];
						marginBottom = margin[2];
						marginRight = margin[1];
						marginTop = margin[0];
					}
				}
				else if (margin == null)
				{
					marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
					marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
					marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
					marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
				}
				else
				{
					marginLeft = marginTop = marginBottom = marginRight = margin;
				}
				var ml:Number;
				var mr:Number;
				var mt:Number;
				var mb:Number;
				var lastmb:Number;
				mt = Number(marginTop);
				if (isNaN(mt))
					mt = 0;
				mb = Number(marginBottom);
				if (isNaN(mb))
					mb = 0;
				var yy:Number;
				if (i == 0)
					child.y = mt;
				else
					child.y = yy + Math.max(mt, lastmb);
                if (child is ILayoutChild)
                {
                    ilc = child as ILayoutChild;
                    if (!isNaN(ilc.percentHeight))
                        ilc.setHeight(contentView.height * ilc.percentHeight / 100);
                }
				yy = child.y + child.height;
				lastmb = mb;
				flexibleHorizontalMargins[i] = {};
				if (marginLeft == "auto")
				{
					ml = 0;
					flexibleHorizontalMargins[i].marginLeft = marginLeft;
					hasHorizontalFlex = true;
				}
				else
				{
					ml = Number(marginLeft);
					if (isNaN(ml))
					{
						ml = 0;
						flexibleHorizontalMargins[i].marginLeft = marginLeft;
					}
					else
						flexibleHorizontalMargins[i].marginLeft = ml;
				}
				if (marginRight == "auto")
				{
					mr = 0;
					flexibleHorizontalMargins[i].marginRight = marginRight;
					hasHorizontalFlex = true;
				}
				else
				{
					mr = Number(marginRight);
					if (isNaN(mr))
					{
						mr = 0;
						flexibleHorizontalMargins[i].marginRight = marginRight;
					}
					else
						flexibleHorizontalMargins[i].marginRight = mr;
				}
				child.x = ml;
                if (child is ILayoutChild)
                {
                    ilc = child as ILayoutChild;
                    if (!isNaN(ilc.percentWidth))
                        ilc.setWidth(contentView.width * ilc.percentWidth / 100);
                }
				maxWidth = Math.max(maxWidth, ml + child.width + mr);
			}
			if (hasHorizontalFlex)
			{
				for (i = 0; i < n; i++)
				{
					child = contentView.getElementAt(i) as IUIBase;
					var obj:Object = flexibleHorizontalMargins[i];
					if (obj.marginLeft == "auto" && obj.marginRight == "auto")
						child.x = maxWidth - child.width / 2;
					else if (obj.marginLeft == "auto")
						child.x = maxWidth - child.width - obj.marginRight;
				}
			}
		}
	}
}

fix when we set the width of children

fix when we set the width of children


package utils
{
	import flash.geom.Rectangle;

	/**
	 * For place texture
	 */
	public final class TextureUtil
	{
		private static const HIGHEST:uint = 0xFFFFFFFF;

		/**
		 * Place textures by textureAtlasXML data
		 */
		public static function packTextures(widthDefault:uint, padding:uint, rectMap:Object, verticalSide:Boolean = false):Rectangle
		{
			for each(var rect:Rectangle in rectMap)
			{
				break;
			}
			if(!rect)
			{
				return null;
			}

			var dimensions:uint = 0;
			var rectList:Vector.<Rectangle> = new Vector.<Rectangle>;
			for each(rect in rectMap)
			{
				dimensions += rect.width * rect.height;
				rectList.push(rect);
			}
			//sort texture by size
			rectList.sort(sortRectList);

			if(widthDefault == 0)
			{
				//calculate width for Auto size
				widthDefault = Math.sqrt(dimensions);
			}

			widthDefault = getNearest2N(Math.max(int(rectList[0].width) + padding, widthDefault));

			var heightMax:uint = HIGHEST;
			var remainAreaList:Vector.<Rectangle> = new Vector.<Rectangle>;
			remainAreaList.push(new Rectangle(0, 0, widthDefault, heightMax));

			var isFit:Boolean;
			var width:int;
			var height:int;

			var area:Rectangle;
			var areaPrev:Rectangle;
			var areaNext:Rectangle;
			var areaID:int;
			var rectID:int;
			do
			{
				//Find highest blank area
				area = getHighestArea(remainAreaList);
				areaID = remainAreaList.indexOf(area);
				isFit = false;
				rectID = 0;
				for each(rect in rectList)
				{
					//check if the area is fit
					width = int(rect.width) + padding;
					height = int(rect.height) + padding;
					if (area.width >= width && area.height >= height)
					{
						//place portrait texture
						if(
							verticalSide?
							(
								height > width * 4?
								(
									areaID > 0?
									(area.height - height >= remainAreaList[areaID - 1].height):
									true
								):
								true
							):
							true
						)
						{
							isFit = true;
							break;
						}
					}
					rectID ++;
				}

				if(isFit)
				{
					//place texture if size is fit
					rect.x = area.x;
					rect.y = area.y;
					rectList.splice(rectID, 1);
					remainAreaList.splice(
						areaID + 1,
						0,
						new Rectangle(area.x + width, area.y, area.width - width, area.height)
					);
					area.y += height;
					area.width = width;
					area.height -= height;
				}
				else
				{
					//not fit, don't place it, merge blank area to others toghther
					if(areaID == 0)
					{
						areaNext = remainAreaList[areaID + 1];
					}
					else if(areaID == remainAreaList.length - 1)
					{
						areaNext = remainAreaList[areaID - 1];
					}
					else
					{
						areaPrev = remainAreaList[areaID - 1];
						areaNext = remainAreaList[areaID + 1];
						areaNext = areaPrev.height <= areaNext.height?areaNext:areaPrev;
					}
					if(area.x < areaNext.x)
					{
						areaNext.x = area.x;
					}
					areaNext.width = area.width + areaNext.width;
					remainAreaList.splice(areaID, 1);
				}
			}
			while (rectList.length > 0);

			heightMax = getNearest2N(heightMax - getLowestArea(remainAreaList).height);

			return new Rectangle(0, 0, widthDefault, heightMax);
		}

		private static function sortRectList(rect1:Rectangle, rect2:Rectangle):int
		{
			var v1:uint = rect1.width + rect1.height;
			var v2:uint = rect2.width + rect2.height;
			if (v1 == v2)
			{
				return rect1.width > rect2.width?-1:1;
			}
			return v1 > v2?-1:1;
		}

		private static function getNearest2N(_n:uint):uint
		{
			return _n & _n - 1?1 << _n.toString(2).length:_n;
		}

		private static function getHighestArea(areaList:Vector.<Rectangle>):Rectangle
		{
			var height:uint = 0;
			var areaHighest:Rectangle;
			for each(var area:Rectangle in areaList)
			{
				if (area.height > height)
				{
					height = area.height;
					areaHighest = area;
				}
			}
			return areaHighest;
		}

		private static function getLowestArea(areaList:Vector.<Rectangle>):Rectangle
		{
			var height:uint = HIGHEST;
			var areaLowest:Rectangle;
			for each(var area:Rectangle in areaList)
			{
				if (area.height < height)
				{
					height = area.height;
					areaLowest = area;
				}
			}
			return areaLowest;
		}
	}
}

package utils
{
	import flash.geom.Rectangle;

	/**
	 * For place texture
	 */
	public final class TextureUtil
	{
		private static const HIGHEST:uint = 0xFFFFFFFF;

		/**
		 * Place textures by textureAtlasXML data
		 */
		public static function packTextures(widthDefault:uint, padding:uint, rectMap:Object, verticalSide:Boolean = false):Rectangle
		{
			for each(var rect:Rectangle in rectMap)
			{
				break;
			}
			if(!rect)
			{
				return null;
			}

			var dimensions:uint = 0;
			var maxWidth:Number = 0;
			var rectList:Vector.<Rectangle> = new Vector.<Rectangle>;
			for each(rect in rectMap)
			{
				dimensions += rect.width * rect.height;
				rectList.push(rect);

				maxWidth = Math.max(rect.width, maxWidth);
			}
			//sort texture by size
			rectList.sort(sortRectList);

			if(widthDefault == 0)
			{
				//calculate width for Auto size
				widthDefault = Math.sqrt(dimensions);
			}

			widthDefault = getNearest2N(Math.max(maxWidth + padding, widthDefault));

			var heightMax:uint = HIGHEST;
			var remainAreaList:Vector.<Rectangle> = new Vector.<Rectangle>;
			remainAreaList.push(new Rectangle(0, 0, widthDefault, heightMax));

			var isFit:Boolean;
			var width:int;
			var height:int;

			var area:Rectangle;
			var areaPrev:Rectangle;
			var areaNext:Rectangle;
			var areaID:int;
			var rectID:int;
			do
			{
				//Find highest blank area
				area = getHighestArea(remainAreaList);
				areaID = remainAreaList.indexOf(area);
				isFit = false;
				rectID = 0;
				for each(rect in rectList)
				{
					//check if the area is fit
					width = int(rect.width) + padding;
					height = int(rect.height) + padding;
					if (area.width >= width && area.height >= height)
					{
						//place portrait texture
						if(
							verticalSide?
							(
								height > width * 4?
								(
									areaID > 0?
									(area.height - height >= remainAreaList[areaID - 1].height):
									true
								):
								true
							):
							true
						)
						{
							isFit = true;
							break;
						}
					}
					rectID ++;
				}

				if(isFit)
				{
					//place texture if size is fit
					rect.x = area.x;
					rect.y = area.y;
					rectList.splice(rectID, 1);
					remainAreaList.splice(
						areaID + 1,
						0,
						new Rectangle(area.x + width, area.y, area.width - width, area.height)
					);
					area.y += height;
					area.width = width;
					area.height -= height;
				}
				else
				{
					//not fit, don't place it, merge blank area to others toghther
					if(areaID == 0)
					{
						areaNext = remainAreaList[areaID + 1];
					}
					else if(areaID == remainAreaList.length - 1)
					{
						areaNext = remainAreaList[areaID - 1];
					}
					else
					{
						areaPrev = remainAreaList[areaID - 1];
						areaNext = remainAreaList[areaID + 1];
						areaNext = areaPrev.height <= areaNext.height?areaNext:areaPrev;
					}
					if(area.x < areaNext.x)
					{
						areaNext.x = area.x;
					}
					areaNext.width = area.width + areaNext.width;
					remainAreaList.splice(areaID, 1);
				}
			}
			while (rectList.length > 0);

			heightMax = getNearest2N(heightMax - getLowestArea(remainAreaList).height);

			return new Rectangle(0, 0, widthDefault, heightMax);
		}

		private static function sortRectList(rect1:Rectangle, rect2:Rectangle):int
		{
			var v1:uint = rect1.width + rect1.height;
			var v2:uint = rect2.width + rect2.height;
			if (v1 == v2)
			{
				return rect1.width > rect2.width?-1:1;
			}
			return v1 > v2?-1:1;
		}

		private static function getNearest2N(_n:uint):uint
		{
			return _n & _n - 1?1 << _n.toString(2).length:_n;
		}

		private static function getHighestArea(areaList:Vector.<Rectangle>):Rectangle
		{
			var height:uint = 0;
			var areaHighest:Rectangle;
			for each(var area:Rectangle in areaList)
			{
				if (area.height > height)
				{
					height = area.height;
					areaHighest = area;
				}
			}
			return areaHighest;
		}

		private static function getLowestArea(areaList:Vector.<Rectangle>):Rectangle
		{
			var height:uint = HIGHEST;
			var areaLowest:Rectangle;
			for each(var area:Rectangle in areaList)
			{
				if (area.height < height)
				{
					height = area.height;
					areaLowest = area;
				}
			}
			return areaLowest;
		}
	}
}

fix get TextureAtlas width bug

fix get TextureAtlas width bug


//
// Flump - Copyright 2013 Flump Authors

package flump.display {

import flump.mold.MovieMold;

import react.Signal;

import starling.animation.IAnimatable;
import starling.display.DisplayObject;
import starling.display.Sprite;
import starling.events.Event;

/**
 * A movie created from flump-exported data. It has children corresponding to the layers in the
 * movie in Flash, in the same order and with the same names. It fills in those children
 * initially with the image or movie of the symbol on that exported layer. After the initial
 * population, it only applies the keyframe-based transformations to the child at the index
 * corresponding to the layer. This means it's safe to swap in other DisplayObjects at those
 * positions to have them animated in place of the initial child.
 *
 *
A Movie will not animate unless it's added to a Juggler (or its advanceTime() function
 * is otherwise called). When the movie is added to a juggler, it advances its playhead with the
 * frame ticks if isPlaying is true. It will automatically remove itself from its juggler when
 * removed from the stage.

 *
 * @see Library and LibraryLoader to create instances of Movie.
 */
public class Movie extends Sprite
    implements IAnimatable
{
    /** A label fired by all movies when entering their first frame. */
    public static const FIRST_FRAME :String = "flump.movie.FIRST_FRAME";

    /** A label fired by all movies when entering their last frame. */
    public static const LAST_FRAME :String = "flump.movie.LAST_FRAME";

    /** Fires the label string whenever it's passed in playing. */
    public const labelPassed :Signal = new Signal(String);

    /** @private */
    public function Movie (src :MovieMold, frameRate :Number, library :Library) {
        this.name = src.id;
        _labels = src.labels;
        _frameRate = frameRate;
        if (src.flipbook) {
            _layers = new Vector.<Layer>(1, true);
            _layers[0] = new Layer(this, src.layers[0], library, /*flipbook=*/true);
            _numFrames = src.layers[0].frames;
        } else {
            _layers = new Vector.<Layer>(src.layers.length, true);
            for (var ii :int = 0; ii < _layers.length; ii++) {
                _layers[ii] = new Layer(this, src.layers[ii], library, /*flipbook=*/false);
                _numFrames = Math.max(src.layers[ii].frames, _numFrames);
            }
        }
        _duration = _numFrames / _frameRate;
        updateFrame(0, 0);

        // When we're removed from the stage, remove ourselves from any juggler animating us.
        addEventListener(Event.REMOVED_FROM_STAGE, function (..._) :void {
            dispatchEventWith(Event.REMOVE_FROM_JUGGLER);
        });
    }

    /** @return the frame being displayed. */
    public function get frame () :int { return _frame; }

    /** @return the number of frames in the movie. */
    public function get numFrames () :int { return _numFrames; }

    /** @return true if the movie is currently playing. */
    public function get isPlaying () :Boolean { return _playing; }

    /** @return true if the movie contains the given label. */
    public function hasLabel (label :String) :Boolean {
        return getFrameForLabel(label) >= 0;
    }

    /** Plays the movie from its current frame. The movie will loop forever.  */
    public function loop () :Movie {
        _playing = true;
        _stopFrame = NO_FRAME;
        return this;
    }

    /** Plays the movie from its current frame, stopping when it reaches its last frame. */
    public function playOnce () :Movie { return playTo(LAST_FRAME); }

    /**
     * Moves to the given String label or int frame. Doesn't alter playing status or stop frame.
     * If there are labels at the given position, they're fired as part of the goto, even if the
     * current frame is equal to the destination. Labels between the current frame and the
     * destination frame are not fired.
     *
     * @param position the int frame or String label to goto.
     *
     * @return this movie for chaining
     *
     * @throws Error if position isn't an int or String, or if it is a String and that String isn't
     * a label on this movie
     */
    public function goTo (position :Object) :Movie {
        const frame :int = extractFrame(position);
        updateFrame(frame, 0);
        return this;
    }

   /**
    * Plays the movie from its current frame. The movie will stop when it reaches the given label
    * or frame.
    *
    * @param position to int frame or String label to stop at
    *
    * @return this movie for chaining
    *
    * @throws Error if position isn't an int or String, or if it is a String and that String isn't
    * a label on this movie.
    */
    public function playTo (position :Object) :Movie {
       _stopFrame = extractFrame(position);
       // don't play if we're already at the stop frame
       _playing = (_frame != _stopFrame);
       return this;
    }

    /** Stops playback if it's currently active. Doesn't alter the current frame or stop frame. */
    public function stop () :Movie {
        _playing = false;
        return this;
    }

    /** Advances the playhead by the give number of seconds. From IAnimatable. */
    public function advanceTime (dt :Number) :void {
        if (dt < 0) throw new Error("Invalid time [dt=" + dt + "]");
        if (_skipAdvanceTime) { _skipAdvanceTime = false; return; }
        if (!_playing) return;

        if (_numFrames > 1) {
            _playTime += dt;
            var actualPlaytime :Number = _playTime;
            if (_playTime >= _duration) _playTime %= _duration;

            // If _playTime is very close to _duration, rounding error can cause us to
            // land on lastFrame + 1. Protect against that.
            var newFrame :int = clamp(int(_playTime * _frameRate), 0, _numFrames - 1);

            // If the update crosses or goes to the stopFrame:
            // go to the stopFrame, stop the movie, clear the stopFrame
            if (_stopFrame != NO_FRAME) {
                // how many frames remain to the stopframe?
                var framesRemaining :int =
                    (_frame <= _stopFrame ? _stopFrame - _frame : _numFrames - _frame + _stopFrame);
                var framesElapsed :int = int(actualPlaytime * _frameRate) - _frame;
                if (framesElapsed >= framesRemaining) {
                    _playing = false;
                    newFrame = _stopFrame;
                    _stopFrame = NO_FRAME;
                }
            }
            updateFrame(newFrame, dt);
        }

        for (var ii :int = this.numChildren - 1; ii >= 0; --ii) {
            var child :DisplayObject = getChildAt(ii);
            if (child is Movie) {
                Movie(child).advanceTime(dt);
            }
        }
    }

    /** notify this Movie that it has been added to the Layer after initialization */
    public function addedToLayer() :void
    {
        goTo(0);
        _skipAdvanceTime = true;
    }

    /** @private */
    protected function extractFrame (position :Object) :int {
        if (position is int) return int(position);
        if (!(position is String)) throw new Error("Movie position must be an int frame or String label");
        const label :String = String(position);
        var frame :int = getFrameForLabel(label);
        if (frame < 0) {
            throw new Error("No such label '" + label + "'");
        }
        return frame;
    }

    /**
     * @private
     *
     * Returns the frame index for the given label, or -1 if the label doesn't exist.
     */
    protected function getFrameForLabel (label :String) :int {
        for (var ii :int = 0; ii < _labels.length; ii++) {
            if (_labels[ii] != null && _labels[ii].indexOf(label) != -1) return ii;
        }
        return -1;
    }

    /**
     * @private
     *
     * @param dt the timeline's elapsed time since the last update. This should be 0
     * for updates that are the result of a "goTo" call.
     */
    protected function updateFrame (newFrame :int, dt :Number) :void {

        if (newFrame < 0 || newFrame >= _numFrames) {
            throw new Error("Invalid frame [frame=" + newFrame,
                " validRange=0-" + (_numFrames - 1) + "]");
        }

        if (_isUpdatingFrame) {
            _pendingFrame = newFrame;
            return;
        } else {
            _pendingFrame = NO_FRAME;
            _isUpdatingFrame = true;
        }

        const isGoTo :Boolean = (dt <= 0);
        const wrapped :Boolean = (dt >= _duration) || (newFrame < _frame);

        if (newFrame != _frame) {
            if (wrapped) {
                for each (var layer :Layer in _layers) {
                    layer.movieLooped();
                }
            }
            for each (layer in _layers) layer.drawFrame(newFrame);
        }

        if (isGoTo) _playTime = newFrame / _frameRate;

        // Update the frame before firing frame label signals, so if firing changes the frame,
        // it sticks.
        const oldFrame :int = _frame;
        _frame = newFrame;

        // determine which labels to fire signals for
        var startFrame :int;
        var frameCount :int;
        if (isGoTo) {
            startFrame = newFrame;
            frameCount = 1;
        } else {
            startFrame = (oldFrame + 1 < _numFrames ? oldFrame + 1 : 0);
            frameCount = (_frame - oldFrame);
            if (wrapped) frameCount += _numFrames;
        }

        // Fire signals. Stop if pendingFrame is updated, which indicates that the client
        // has called goTo()
        var frameIdx :int = startFrame;
        for (var ii :int = 0; ii < frameCount; ++ii) {
            if (_pendingFrame != NO_FRAME) break;

            if (_labels[frameIdx] != null) {
                for each (var label :String in _labels[frameIdx]) {
                    labelPassed.emit(label);
                    if (_pendingFrame != NO_FRAME) break;
                }
            }

            // avoid modulo division by updating frameIdx each time through the loop
            if (++frameIdx == _numFrames) {
                frameIdx = 0;
            }
        }

        _isUpdatingFrame = false;
        // If we were interrupted by a goTo(), update to that frame now.
        if (_pendingFrame != NO_FRAME) {
            newFrame = _pendingFrame;
            updateFrame(newFrame, 0);
        }
    }

    protected static function clamp (n :Number, min :Number, max :Number) :Number {
        return Math.min(Math.max(n, min), max);
    }

    /** @private */
    protected var _isUpdatingFrame :Boolean;
    /** @private */
    protected var _pendingFrame :int = NO_FRAME;
    /** @private */
    protected var _frame :int = NO_FRAME, _stopFrame :int = NO_FRAME;
    /** @private */
    protected var _playing :Boolean = true;
    /** @private */
    protected var _playTime :Number, _duration :Number;
    /** @private */
    protected var _layers :Vector.<Layer>;
    /** @private */
    protected var _numFrames :int;
    /** @private */
    protected var _frameRate :Number;
    /** @private */
    protected var _labels :Vector.<Vector.<String>>;
    /** @private */
    private var _skipAdvanceTime :Boolean = false;
    /** @private */
    internal var _playerData :MoviePlayerNode;

    private static const NO_FRAME :int = -1;
}
}

remove errant newline

remove errant newline


package krewfw.core {

    import flash.geom.Rectangle;

    import starling.animation.Transitions;
    import starling.animation.Tween;
    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.text.TextField;
    import starling.textures.Texture;

    import krewfw.KrewConfig;
    import krewfw.builtin_actor.display.ColorRect;
    import krewfw.core_internal.KrewSharedObjects;
    import krewfw.core_internal.ProfileData;
    import krewfw.core_internal.StageLayer;
    import krewfw.core_internal.StuntAction;
    import krewfw.core_internal.StuntActionInstructor;
    import krewfw.utils.as3.KrewTimeKeeper;

    //------------------------------------------------------------
    public class KrewActor extends KrewGameObject {

        private var _hasInitialized:Boolean = false;
        private var _hasDisposed:Boolean    = false;
        private var _markedForDeath:Boolean = false;  // 死亡フラグ

        protected var _checkDisplayArea:Boolean = false;
        protected var _cachedWidth :Number;
        protected var _cachedHeight:Number;

        private var _initFuncList  :Vector.<Function>      = new Vector.<Function>();
        private var _imageList     :Vector.<Image>         = new Vector.<Image>();
        private var _displayObjList:Vector.<DisplayObject> = new Vector.<DisplayObject>();
        private var _textList      :Vector.<TextField>     = new Vector.<TextField>();
        private var _childActors   :Vector.<KrewActor>     = new Vector.<KrewActor>();

        private var _color:uint = 0xffffff;
        public  var applyForNewActor:Function;
        public  var layer:StageLayer;  // reference of layer this actor belonged to
        public  var layerName:String;

        private var _actionInstructors:Vector.<StuntActionInstructor> = new Vector.<StuntActionInstructor>();
        private var _timeKeeper:KrewTimeKeeper = new KrewTimeKeeper();

        /** high is front */
        public var displayOrder:int = 0;

        /**
         * addActor 前に false にすると addActor 時に addChild を行わない
         * (Starling の DisplayList にのせない.)
         * 見た目を持たずに仕事をする Actor はこれを false にすればよい
         */
        public var displayable:Boolean = true;

        /** false にすると CollisionShape が衝突判定を行わない */
        public var collidable:Boolean = true;

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

        /**
         * [CAUTION] starling.display.DisplayObjectContainer の
         * width / height の getter は重い行列計算が走るので滅多なことでもない限り使うな
         */
        public function get cachedWidth():Number {
            return _cachedWidth;
        }

        /** @see cachedWidth */
        public function get cachedHeight():Number {
            return _cachedHeight;
        }

        public function get color():uint {
            return _color;
        }

        /** Actor が持つ Image や TextField 全てに色をセットする */
        public function set color(color:uint):void {
            _color = color;
            for each (var image:Image in _imageList) {
                image.color = color;
            }
            for each (var text:TextField in _textList) {
                text.color = color;
            }
        }

        public function get childActors():Vector.<KrewActor> {
            return _childActors;
        }

        public function get numActor():int {
            return _childActors.length;
        }

        public function get isDead():Boolean {
            return _markedForDeath;
        }

        public function get hasInitialized():Boolean {
            return _hasInitialized;
        }

        //------------------------------------------------------------
        // constructors
        //------------------------------------------------------------

        public function KrewActor() {}

        /**
         * init が呼ばれる時（KrewScene.setUpActor に渡された時、または
         * Actor から addActor されたとき）に、init 後に呼ばれる関数を登録。
         * コンストラクタでの使用を想定.
         *
         * コンストラクタで引数を渡して init に用いたい場合、クラスメンバに値を
         * 保持しておくなどの手間がかかるため、それを楽にするためのもの。
         * 登録した関数は init 実行後に、登録した順番で呼ばれる。
         */
        public function addInitializer(initFunc:Function):void {
            _initFuncList.push(initFunc);
        }

        /** @private */
        public function setUp(sharedObj:KrewSharedObjects, applyForNewActor:Function,
                              layer:StageLayer, layerName:String):void
        {
            if (_hasInitialized) {
                krew.fwlog('[Warning] KrewActor has initialized twice.');
                return;
            }
            _hasInitialized = true;

            this.sharedObj        = sharedObj;
            this.applyForNewActor = applyForNewActor;
            this.layer            = layer;
            this.layerName        = layerName;

            _doInit();

            ProfileData.countActor(1, this.layerName);
        }

        private function _doInit():void {
            init();
            for each (var initFunc:Function in _initFuncList) {
                initFunc();
            }
        }

        //------------------------------------------------------------
        // destructors
        //------------------------------------------------------------

        /** @private */
        public override function dispose():void {
            if (_hasDisposed) { return; }
            _hasDisposed = true;

            if (_hasInitialized) {
                _hasInitialized = false;
                ProfileData.countActor(-1, this.layerName);
            }

            for each (var child:KrewActor in _childActors) {
                child.dispose();
            }

            removeChildren(0, -1, true);
            removeCollision();
            removeTweens();
            _disposeImageTextures();
            _disposeTexts();
            _disposeDisplayObjs();
            _timeKeeper.dispose();

            _initFuncList      = null;
            _imageList         = null;
            _textList          = null;
            _childActors       = null;
            _actionInstructors = null;
            _timeKeeper        = null;

            onDispose();
            super.dispose();
        }

        /** @private */
        protected function _disposeImageTextures():void {
            for (var i:uint=0;  i < _imageList.length;  ++i) {
                _imageList[i].texture.dispose();
                _imageList[i].dispose();
            }
        }

        /** @private */
        protected function _disposeTexts():void {
            for each (var text:TextField in _textList) {
                text.dispose();
            }
        }

        /** @private */
        protected function _disposeDisplayObjs():void {
            for each (var obj:DisplayObject in _displayObjList) {
                obj.dispose();
            }
        }

        /** @private */
        protected function removeCollision():void {
            if (!sharedObj) { return; }
            sharedObj.collisionSystem.removeShapeWithActor(this);
        }

        //------------------------------------------------------------
        // public interface
        //------------------------------------------------------------

        /**
         * addChild の代わりに addImage を呼ぶことで破棄時に Image.texture の dispose が
         * 呼ばれるようになる。また、KrewActor.color の指定で全 Image に色がかかるようになる
         */
        public function addImage(image:Image,
                                 width:Number=NaN, height:Number=NaN,
                                 x:Number=0, y:Number=0,
                                 anchorX:Number=0.5, anchorY:Number=0.5):void
        {
            image.x = x;
            image.y = y;
            if (!isNaN(width )) { image.width  = width;  }
            if (!isNaN(height)) { image.height = height; }

            // pivotX, Y は回転の軸だけでなく座標指定にも影響する
            // そして何故かソースの画像の解像度に対する座標で指定してやらないといけないようだ
            var textureRect:Rectangle = image.texture.frame;
            image.pivotX = (textureRect.width  * anchorX);
            image.pivotY = (textureRect.height * anchorY);

            _cachedWidth  = width;
            _cachedHeight = height;

            super.addChild(image);
            _imageList.push(image);
        }

        public function changeImage(image:Image, imageName:String):void {
            var newTexture:Texture = sharedObj.resourceManager.getTexture(imageName);
            image.texture.dispose();
            image.texture = newTexture;
        }

        /**
         * Actor 全体の color に影響させたい場合は addChild ではなく addText で足す
         */
        public function addText(text:TextField, x:Number=NaN, y:Number=NaN):void {
            if (!isNaN(x)) { text.x = x; }
            if (!isNaN(y)) { text.y = y; }

            super.addChild(text);
            _textList.push(text);
        }

        /**
         * addChild したものは Actor 破棄時に勝手に dispose が呼ばれる
         */
        public override function addChild(child:DisplayObject):DisplayObject {
            super.addChild(child);
            _displayObjList.push(child);
            return child;
        }

        /**
         * krewFramework のシステムに Actor を登録し、同時に Starling の DisplayList に追加する.
         * 見た目を持たずに仕事をするような Actor は putOnDisplayList に false を渡すか、
         * Actor のコンストラクタで displayable に false を設定しておくと Starling の DisplayList
         * には追加しない
         */
        public function addActor(actor:KrewActor, putOnDisplayList:Boolean=true):void {
            _childActors.push(actor);
            if (putOnDisplayList  &&  actor.displayable) {
                addChild(actor);
            }

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

        public function createActor(newActor:KrewActor, layerName:String=null):void {
            if (!newActor) {
                throw new Error("[Error] [KrewActor :: createActor] newActor is required.");
            }

            // layerName 省略時は自分と同じ layer に出す
            if (layerName == null) {
                layerName = this.layerName;
            }
            applyForNewActor(newActor, layerName);
        }

        public function passAway():void {
            _markedForDeath = true;
        }

        public function setVertexColor(color1:int=0, color2:int=0,
                                       color3:int=0, color4:int=0):void {
            for each (var image:Image in _imageList) {
                image.setVertexColor(0, color1);
                image.setVertexColor(1, color2);
                image.setVertexColor(2, color3);
                image.setVertexColor(3, color4);
            }
        }

        //------------------------------------------------------------
        // Helpers for Tween
        //------------------------------------------------------------

        public function addTween(tween:Tween):void {
            if (!layer) {
                krew.fwlog('[Error] This actor does not belong to any layer.');
                return;
            }

            layer.juggler.add(tween);
        }

        public function removeTweens():void {
            if (!layer) {
                krew.fwlog('[Error] This actor does not belong to any layer.');
                return;
            }

            layer.juggler.removeTweens(this);
        }

        public function enchant(duration:Number, transition:String=Transitions.LINEAR):Tween {
            var tween:Tween = new Tween(this, duration, transition);
            addTween(tween);
            return tween;
        }

        //------------------------------------------------------------
        // Multi Tasker
        //------------------------------------------------------------

        public function act(action:StuntAction=null):StuntAction {
            var actionInstructor:StuntActionInstructor = new StuntActionInstructor(this, action);
            _actionInstructors.push(actionInstructor);
            return actionInstructor.action;
        }

        // purge actions
        public function react():void {
            _actionInstructors.length = 0;
            removeTweens();
        }

        private function _updateAction(passedTime:Number):void {
            for (var i:int=0;  i <_actionInstructors.length;  ++i) {
                var actionInstructor:StuntActionInstructor = _actionInstructors[i];
                actionInstructor.update(passedTime);

                if (actionInstructor.isAllActionFinished) {
                    _actionInstructors.splice(i, 1);  // remove instructor from Array
                    --i;
                }
            }
        }

        /** Equivalent to setTimeout() */
        public function addScheduledTask(timeout:Number, task:Function):void {
            if (timeout <= 0) {
                task();
                return;
            }
            _timeKeeper.addPeriodicTask(timeout, task, 1);
        }

        /** Alias for addScheduledTask */
        public function delayed(timeout:Number, task:Function):void {
            addScheduledTask(timeout, task);
        }

        /** Equivalent to setInterval() */
        public function addPeriodicTask(interval:Number, task:Function, times:int=-1):void {
            _timeKeeper.addPeriodicTask(interval, task, times);
        }

        /** Alias for addPeriodicTask */
        public function cyclic(interval:Number, task:Function, times:int=-1):void {
            addPeriodicTask(interval, task, times);
        }

        // for touch action
        public function addTouchMarginNode(touchWidth:Number=0, touchHeight:Number=0):void {
            var margin:ColorRect = new ColorRect(touchWidth, touchHeight);
            margin.touchable = true;
            margin.alpha = 0;
            margin.x = -touchWidth  / 2;
            margin.y = -touchHeight / 2;
            addActor(margin);
        }

        /**
         * displayOrder の値でツリーをソート。 children が皆 KrewActor である前提。
         * tkActor.displayOrder = 1;  のように設定した上で
         * getLayer('hoge').sortDisplayOrder(); のように使う
         */
        public function sortDisplayOrder():void {
            sortChildren(function(a:KrewActor, b:KrewActor):int {
                if (a.displayOrder < b.displayOrder) { return -1; }
                if (a.displayOrder > b.displayOrder) { return  1; }
                return 0;
            });
        }

        //------------------------------------------------------------
        // Called by framework
        //------------------------------------------------------------

        /** @private */
        public function update(passedTime:Number):void {
            if (_hasDisposed) { return; }

            // update children actors
            for (var i:int=0;  i < _childActors.length;  ++i) {
                var child:KrewActor = _childActors[i];
                if (child.isDead) {
                    _childActors.splice(i, 1);  // remove dead actor from Array
                    removeChild(child);
                    child.dispose();
                    --i;
                    continue;
                }
                child.update(passedTime);
            }

            onUpdate(passedTime);
            _updateAction(passedTime);
            _timeKeeper.update(passedTime);
            _disappearInOutside();
        }

        /**
         * _checkDisplayArea = true の Actor は、画面外にいるときは表示を off にする。
         * これをやってあげないと少なくとも Flash では
         * 画面外でも普通に描画コストがかかってしまうようだ.
         *
         * いずれにせよ visible false でなければ Starling は描画のための
         * 計算とかをしだすので、それを避けるにはこれをやった方がいい
         *
         * つまるところ、数が多く画像のアンカーが中央にあるような Actor を
         * _checkDisplayArea = true にすればよい
         */
        private function _disappearInOutside():void {
            if (!_checkDisplayArea) { return; }

            // starling.display.DisplayObjectContainer の width / height は
            // getBounds という重い処理が走るのでうかつにさわってはいけない

            // オーバヘッドを抑えるため、ラフに計算。回転を考慮して大きめにとる。
            // 横長の画像などについては考えてないのでどうしようもないときは
            // _checkDisplayArea を false のままにしておくか、
            // _cachedWidth とかを書き換えるか、もしくは override してくれ

            var halfWidth :Number = (_cachedWidth  / 1.5) * scaleX;
            var halfHeight:Number = (_cachedHeight / 1.5) * scaleY;
            if (x + halfWidth  > 0  &&  x - halfWidth  < KrewConfig.SCREEN_WIDTH  &&
                y + halfHeight > 0  &&  y - halfHeight < KrewConfig.SCREEN_HEIGHT) {
                visible = true;
            } else {
                visible = false;
            }
        }
    }
}

package krewfw.core {

    import flash.geom.Rectangle;

    import starling.animation.Transitions;
    import starling.animation.Tween;
    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.text.TextField;
    import starling.textures.Texture;

    import krewfw.KrewConfig;
    import krewfw.builtin_actor.display.ColorRect;
    import krewfw.core_internal.KrewSharedObjects;
    import krewfw.core_internal.ProfileData;
    import krewfw.core_internal.StageLayer;
    import krewfw.core_internal.StuntAction;
    import krewfw.core_internal.StuntActionInstructor;
    import krewfw.utils.as3.KrewTimeKeeper;

    //------------------------------------------------------------
    public class KrewActor extends KrewGameObject {

        private var _hasInitialized:Boolean = false;
        private var _hasDisposed:Boolean    = false;
        private var _markedForDeath:Boolean = false;  // 死亡フラグ

        protected var _checkDisplayArea:Boolean = false;
        protected var _cachedWidth :Number;
        protected var _cachedHeight:Number;

        private var _initFuncList  :Vector.<Function>      = new Vector.<Function>();
        private var _imageList     :Vector.<Image>         = new Vector.<Image>();
        private var _displayObjList:Vector.<DisplayObject> = new Vector.<DisplayObject>();
        private var _textList      :Vector.<TextField>     = new Vector.<TextField>();
        private var _childActors   :Vector.<KrewActor>     = new Vector.<KrewActor>();

        private var _color:uint = 0xffffff;
        public  var applyForNewActor:Function;
        public  var layer:StageLayer;  // reference of layer this actor belonged to
        public  var layerName:String;

        private var _actionInstructors:Vector.<StuntActionInstructor> = new Vector.<StuntActionInstructor>();
        private var _timeKeeper:KrewTimeKeeper = new KrewTimeKeeper();

        /** high is front */
        public var displayOrder:int = 0;

        /**
         * addActor 前に false にすると addActor 時に addChild を行わない
         * (Starling の DisplayList にのせない.)
         * 見た目を持たずに仕事をする Actor はこれを false にすればよい
         */
        public var displayable:Boolean = true;

        /** false にすると CollisionShape が衝突判定を行わない */
        public var collidable:Boolean = true;

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

        /**
         * [CAUTION] starling.display.DisplayObjectContainer の
         * width / height の getter は重い行列計算が走るので滅多なことでもない限り使うな
         */
        public function get cachedWidth():Number {
            return _cachedWidth;
        }

        /** @see cachedWidth */
        public function get cachedHeight():Number {
            return _cachedHeight;
        }

        public function get color():uint {
            return _color;
        }

        /** Actor が持つ Image や TextField 全てに色をセットする */
        public function set color(color:uint):void {
            _color = color;
            for each (var image:Image in _imageList) {
                image.color = color;
            }
            for each (var text:TextField in _textList) {
                text.color = color;
            }
        }

        public function get childActors():Vector.<KrewActor> {
            return _childActors;
        }

        public function get numActor():int {
            return _childActors.length;
        }

        public function get isDead():Boolean {
            return _markedForDeath;
        }

        public function get hasInitialized():Boolean {
            return _hasInitialized;
        }

        //------------------------------------------------------------
        // constructors
        //------------------------------------------------------------

        public function KrewActor() {}

        /**
         * init が呼ばれる時（KrewScene.setUpActor に渡された時、または
         * Actor から addActor されたとき）に、init 後に呼ばれる関数を登録。
         * コンストラクタでの使用を想定.
         *
         * コンストラクタで引数を渡して init に用いたい場合、クラスメンバに値を
         * 保持しておくなどの手間がかかるため、それを楽にするためのもの。
         * 登録した関数は init 実行後に、登録した順番で呼ばれる。
         */
        public function addInitializer(initFunc:Function):void {
            _initFuncList.push(initFunc);
        }

        /** @private */
        public function setUp(sharedObj:KrewSharedObjects, applyForNewActor:Function,
                              layer:StageLayer, layerName:String):void
        {
            if (_hasInitialized) {
                krew.fwlog('[Warning] KrewActor has initialized twice.');
                return;
            }
            _hasInitialized = true;

            this.sharedObj        = sharedObj;
            this.applyForNewActor = applyForNewActor;
            this.layer            = layer;
            this.layerName        = layerName;

            _doInit();

            ProfileData.countActor(1, this.layerName);
        }

        private function _doInit():void {
            init();
            for each (var initFunc:Function in _initFuncList) {
                initFunc();
            }
        }

        //------------------------------------------------------------
        // destructors
        //------------------------------------------------------------

        /** @private */
        public override function dispose():void {
            if (_hasDisposed) { return; }
            _hasDisposed = true;

            if (_hasInitialized) {
                _hasInitialized = false;
                ProfileData.countActor(-1, this.layerName);
            }

            for each (var child:KrewActor in _childActors) {
                child.dispose();
            }

            removeChildren(0, -1, true);
            removeCollision();
            removeTweens();
            _disposeImageTextures();
            _disposeTexts();
            _disposeDisplayObjs();
            _timeKeeper.dispose();

            _initFuncList      = null;
            _imageList         = null;
            _textList          = null;
            _displayObjList    = null;
            _childActors       = null;
            _actionInstructors = null;
            _timeKeeper        = null;

            onDispose();
            super.dispose();
        }

        /** @private */
        protected function _disposeImageTextures():void {
            for (var i:uint=0;  i < _imageList.length;  ++i) {
                _imageList[i].texture.dispose();
                _imageList[i].dispose();
            }
        }

        /** @private */
        protected function _disposeTexts():void {
            for each (var text:TextField in _textList) {
                text.dispose();
            }
        }

        /** @private */
        protected function _disposeDisplayObjs():void {
            for each (var obj:DisplayObject in _displayObjList) {
                obj.dispose();
            }
        }

        /** @private */
        protected function removeCollision():void {
            if (!sharedObj) { return; }
            sharedObj.collisionSystem.removeShapeWithActor(this);
        }

        //------------------------------------------------------------
        // public interface
        //------------------------------------------------------------

        /**
         * addChild の代わりに addImage を呼ぶことで破棄時に Image.texture の dispose が
         * 呼ばれるようになる。また、KrewActor.color の指定で全 Image に色がかかるようになる
         */
        public function addImage(image:Image,
                                 width:Number=NaN, height:Number=NaN,
                                 x:Number=0, y:Number=0,
                                 anchorX:Number=0.5, anchorY:Number=0.5):void
        {
            image.x = x;
            image.y = y;
            if (!isNaN(width )) { image.width  = width;  }
            if (!isNaN(height)) { image.height = height; }

            // pivotX, Y は回転の軸だけでなく座標指定にも影響する
            // そして何故かソースの画像の解像度に対する座標で指定してやらないといけないようだ
            var textureRect:Rectangle = image.texture.frame;
            image.pivotX = (textureRect.width  * anchorX);
            image.pivotY = (textureRect.height * anchorY);

            _cachedWidth  = width;
            _cachedHeight = height;

            super.addChild(image);
            _imageList.push(image);
        }

        public function changeImage(image:Image, imageName:String):void {
            var newTexture:Texture = sharedObj.resourceManager.getTexture(imageName);
            image.texture.dispose();
            image.texture = newTexture;
        }

        /**
         * Actor 全体の color に影響させたい場合は addChild ではなく addText で足す
         */
        public function addText(text:TextField, x:Number=NaN, y:Number=NaN):void {
            if (!isNaN(x)) { text.x = x; }
            if (!isNaN(y)) { text.y = y; }

            super.addChild(text);
            _textList.push(text);
        }

        /**
         * addChild したものは Actor 破棄時に勝手に dispose が呼ばれる
         */
        public override function addChild(child:DisplayObject):DisplayObject {
            super.addChild(child);
            _displayObjList.push(child);
            return child;
        }

        /**
         * krewFramework のシステムに Actor を登録し、同時に Starling の DisplayList に追加する.
         * 見た目を持たずに仕事をするような Actor は putOnDisplayList に false を渡すか、
         * Actor のコンストラクタで displayable に false を設定しておくと Starling の DisplayList
         * には追加しない
         */
        public function addActor(actor:KrewActor, putOnDisplayList:Boolean=true):void {
            _childActors.push(actor);
            if (putOnDisplayList  &&  actor.displayable) {
                addChild(actor);
            }

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

        public function createActor(newActor:KrewActor, layerName:String=null):void {
            if (!newActor) {
                throw new Error("[Error] [KrewActor :: createActor] newActor is required.");
            }

            // layerName 省略時は自分と同じ layer に出す
            if (layerName == null) {
                layerName = this.layerName;
            }
            applyForNewActor(newActor, layerName);
        }

        public function passAway():void {
            _markedForDeath = true;
        }

        public function setVertexColor(color1:int=0, color2:int=0,
                                       color3:int=0, color4:int=0):void {
            for each (var image:Image in _imageList) {
                image.setVertexColor(0, color1);
                image.setVertexColor(1, color2);
                image.setVertexColor(2, color3);
                image.setVertexColor(3, color4);
            }
        }

        //------------------------------------------------------------
        // Helpers for Tween
        //------------------------------------------------------------

        public function addTween(tween:Tween):void {
            if (!layer) {
                krew.fwlog('[Error] This actor does not belong to any layer.');
                return;
            }

            layer.juggler.add(tween);
        }

        public function removeTweens():void {
            if (!layer) {
                krew.fwlog('[Error] This actor does not belong to any layer.');
                return;
            }

            layer.juggler.removeTweens(this);
        }

        public function enchant(duration:Number, transition:String=Transitions.LINEAR):Tween {
            var tween:Tween = new Tween(this, duration, transition);
            addTween(tween);
            return tween;
        }

        //------------------------------------------------------------
        // Multi Tasker
        //------------------------------------------------------------

        public function act(action:StuntAction=null):StuntAction {
            var actionInstructor:StuntActionInstructor = new StuntActionInstructor(this, action);
            _actionInstructors.push(actionInstructor);
            return actionInstructor.action;
        }

        // purge actions
        public function react():void {
            _actionInstructors.length = 0;
            removeTweens();
        }

        private function _updateAction(passedTime:Number):void {
            for (var i:int=0;  i <_actionInstructors.length;  ++i) {
                var actionInstructor:StuntActionInstructor = _actionInstructors[i];
                actionInstructor.update(passedTime);

                if (actionInstructor.isAllActionFinished) {
                    _actionInstructors.splice(i, 1);  // remove instructor from Array
                    --i;
                }
            }
        }

        /** Equivalent to setTimeout() */
        public function addScheduledTask(timeout:Number, task:Function):void {
            if (timeout <= 0) {
                task();
                return;
            }
            _timeKeeper.addPeriodicTask(timeout, task, 1);
        }

        /** Alias for addScheduledTask */
        public function delayed(timeout:Number, task:Function):void {
            addScheduledTask(timeout, task);
        }

        /** Equivalent to setInterval() */
        public function addPeriodicTask(interval:Number, task:Function, times:int=-1):void {
            _timeKeeper.addPeriodicTask(interval, task, times);
        }

        /** Alias for addPeriodicTask */
        public function cyclic(interval:Number, task:Function, times:int=-1):void {
            addPeriodicTask(interval, task, times);
        }

        // for touch action
        public function addTouchMarginNode(touchWidth:Number=0, touchHeight:Number=0):void {
            var margin:ColorRect = new ColorRect(touchWidth, touchHeight);
            margin.touchable = true;
            margin.alpha = 0;
            margin.x = -touchWidth  / 2;
            margin.y = -touchHeight / 2;
            addActor(margin);
        }

        /**
         * displayOrder の値でツリーをソート。 children が皆 KrewActor である前提。
         * tkActor.displayOrder = 1;  のように設定した上で
         * getLayer('hoge').sortDisplayOrder(); のように使う
         */
        public function sortDisplayOrder():void {
            sortChildren(function(a:KrewActor, b:KrewActor):int {
                if (a.displayOrder < b.displayOrder) { return -1; }
                if (a.displayOrder > b.displayOrder) { return  1; }
                return 0;
            });
        }

        //------------------------------------------------------------
        // Called by framework
        //------------------------------------------------------------

        /** @private */
        public function update(passedTime:Number):void {
            if (_hasDisposed) { return; }

            // update children actors
            for (var i:int=0;  i < _childActors.length;  ++i) {
                var child:KrewActor = _childActors[i];
                if (child.isDead) {
                    _childActors.splice(i, 1);  // remove dead actor from Array
                    removeChild(child);
                    child.dispose();
                    --i;
                    continue;
                }
                child.update(passedTime);
            }

            onUpdate(passedTime);
            _updateAction(passedTime);
            _timeKeeper.update(passedTime);
            _disappearInOutside();
        }

        /**
         * _checkDisplayArea = true の Actor は、画面外にいるときは表示を off にする。
         * これをやってあげないと少なくとも Flash では
         * 画面外でも普通に描画コストがかかってしまうようだ.
         *
         * いずれにせよ visible false でなければ Starling は描画のための
         * 計算とかをしだすので、それを避けるにはこれをやった方がいい
         *
         * つまるところ、数が多く画像のアンカーが中央にあるような Actor を
         * _checkDisplayArea = true にすればよい
         */
        private function _disappearInOutside():void {
            if (!_checkDisplayArea) { return; }

            // starling.display.DisplayObjectContainer の width / height は
            // getBounds という重い処理が走るのでうかつにさわってはいけない

            // オーバヘッドを抑えるため、ラフに計算。回転を考慮して大きめにとる。
            // 横長の画像などについては考えてないのでどうしようもないときは
            // _checkDisplayArea を false のままにしておくか、
            // _cachedWidth とかを書き換えるか、もしくは override してくれ

            var halfWidth :Number = (_cachedWidth  / 1.5) * scaleX;
            var halfHeight:Number = (_cachedHeight / 1.5) * scaleY;
            if (x + halfWidth  > 0  &&  x - halfWidth  < KrewConfig.SCREEN_WIDTH  &&
                y + halfHeight > 0  &&  y - halfHeight < KrewConfig.SCREEN_HEIGHT) {
                visible = true;
            } else {
                visible = false;
            }
        }
    }
}

Modify KrewActor.dispose

Modify KrewActor.dispose


package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Constant;
	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.Overwriter;
	import aerys.minko.render.shader.compiler.register.Components;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * SFloat (Shader Float) objects are GPU-side computed value proxies
	 * declared, defined and used in ActionScript shaders.
	 *
	 *

	 * ActionScript shaders define what operations will be
	 * performed on the GPU. Those operations take arguments and return
	 * values that will be computed and accessible on the graphics hardware
	 * only. Those values are represented by SFloat objects.
	 *

	 *
	 *

	 * Because SFloat objects are just hardware memory proxies, it is not
	 * possible (and does not make sense to try) to read their actual value
	 * using CPU-side code. For the very same reason, most of the errors will
	 * be detected at runtime only. The only available property is the size
	 * (number of components) of the corresponding value (ie. 3D vector
	 * operations will return SFloat objects of size 3, dot-product will
	 * return a scalar SFloat object of size 1, ...).
	 *

	 *
	 *

	 * SFloat objects also provide OOP shader programming by encapsulating
	 * common operations (add, multiply, ...). They also allow the use of
	 * dynamic properties in order to read or write sub-components of
	 * non-scalar values. Example:
	 *

	 *
	 *

	 * public function getOutputColor() : void
	 * {
	 * 	var diffuse : SFloat = sampleTexture(BasicStyle.DIFFUSE_MAP);
	 *
	 * 	// use the RGB components of the diffuse map but use a
	 * 	// fixed alpha = 0.5
	 *  return combine(diffuse.rgb, 0.5);
	 * }
	 *

	 *
	 *

	 * Each SFloat object wraps a shader graph node that will be evaluated
	 * by the compiler in order to create the corresponding AGAL bytecode.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public dynamic final class SFloat extends Proxy
	{
		use namespace minko_shader;

		minko_shader var _node	: AbstractNode	= null;

		public final function get size() : uint
		{
			return _node.size;
		}

		public function SFloat(value : Object)
		{
			_node = getNode(value);
		}

		public final function scaleBy(arg : Object) : SFloat
		{
			_node = new Instruction(Instruction.MUL, _node, getNode(arg));

			return this;
		}

		public final function incrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.ADD, _node, getNode(value));

			return this;
		}

		public final function decrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.SUB, _node, getNode(value));

			return this;
		}

		public final function normalize() : SFloat
		{
			_node = new Instruction(Instruction.NRM, _node);

			return this;
		}

		public final function negate() : SFloat
		{
			_node = new Instruction(Instruction.NEG, _node);

			return this;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return new SFloat(new Extract(_node, Components.stringToComponent(name)));
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			var nodeComponent		: uint 			= Components.createContinuous(0, 0, _node.size, _node.size);
			var propertyComponent	: uint			= Components.stringToComponent(name);
			var propertyNode		: AbstractNode	= getNode(value);

			_node = new Overwriter(
				new <AbstractNode>[_node, propertyNode],
				new <uint>[nodeComponent, propertyComponent]
			);
		}

		private function getNode(value : Object) : AbstractNode
		{
			if (value is AbstractNode)
				return value as AbstractNode;

			if (value is SFloat)
				return (value as SFloat)._node;

			if (value is uint || value is int || value is Number)
				return new Constant(new <Number>[Number(value)]);

			if (value is Matrix4x4)
				return new Constant(Matrix4x4(value).getRawData(null, 0, false));

			throw new Error('This type cannot be casted to a shader value.');
		}
	}
}

package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Constant;
	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.Overwriter;
	import aerys.minko.render.shader.compiler.register.Components;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * SFloat (Shader Float) objects are GPU-side computed value proxies
	 * declared, defined and used in ActionScript shaders.
	 *
	 *

	 * ActionScript shaders define what operations will be
	 * performed on the GPU. Those operations take arguments and return
	 * values that will be computed and accessible on the graphics hardware
	 * only. Those values are represented by SFloat objects.
	 *

	 *
	 *

	 * Because SFloat objects are just hardware memory proxies, it is not
	 * possible (and does not make sense to try) to read their actual value
	 * using CPU-side code. For the very same reason, most of the errors will
	 * be detected at runtime only. The only available property is the size
	 * (number of components) of the corresponding value (ie. 3D vector
	 * operations will return SFloat objects of size 3, dot-product will
	 * return a scalar SFloat object of size 1, ...).
	 *

	 *
	 *

	 * SFloat objects also provide OOP shader programming by encapsulating
	 * common operations (add, multiply, ...). They also allow the use of
	 * dynamic properties in order to read or write sub-components of
	 * non-scalar values. Example:
	 *

	 *
	 *

	 * public function getOutputColor() : void
	 * {
	 * 	var diffuse : SFloat = sampleTexture(BasicStyle.DIFFUSE_MAP);
	 *
	 * 	// use the RGB components of the diffuse map but use a
	 * 	// fixed alpha = 0.5
	 *  return combine(diffuse.rgb, 0.5);
	 * }
	 *

	 *
	 *

	 * Each SFloat object wraps a shader graph node that will be evaluated
	 * by the compiler in order to create the corresponding AGAL bytecode.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public dynamic final class SFloat extends Proxy
	{
		use namespace minko_shader;

		minko_shader var _node	: AbstractNode	= null;

		public final function get size() : uint
		{
			return _node.size;
		}

		public function SFloat(value : Object)
		{
			_node = getNode(value);
		}

		public final function scaleBy(arg : Object) : SFloat
		{
			_node = new Instruction(Instruction.MUL, _node, getNode(arg));

			return this;
		}

		public final function incrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.ADD, _node, getNode(value));

			return this;
		}

		public final function decrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.SUB, _node, getNode(value));

			return this;
		}

		public final function normalize() : SFloat
		{
			_node = new Instruction(Instruction.NRM, _node);

			return this;
		}

		public final function negate() : SFloat
		{
			_node = new Instruction(Instruction.NEG, _node);

			return this;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return new SFloat(new Extract(_node, Components.stringToComponent(name)));
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			var propertyName		: String		= String(name);
			var propertyComponent	: uint			= getPropertyWriteComponentMask(propertyName);
			var propertyNode		: AbstractNode	= getNode(value);
			var nodeComponent		: uint 			= Components.createContinuous(0, 0, _node.size, _node.size);

			propertyNode = new Overwriter(
				new <AbstractNode>[propertyNode],
				new <uint>[Components.createContinuous(0, 0, 4, propertyNode.size)]
			);

			_node = new Overwriter(
				new <AbstractNode>[_node, propertyNode],
				new <uint>[nodeComponent, propertyComponent]
			);
		}

		public static function getPropertyWriteComponentMask(string : String) : uint
		{
			var result : uint = 0x04040404;

			for (var i : uint = 0; i < 4; ++i)
				if (i < string.length)
					switch (string.charAt(i))
					{
						case 'x': case 'X': case 'r': case 'R':
							result = (0xffffff00 & result) | i;
							result |= i << (8 * 0);
							break;

						case 'y': case 'Y': case 'g': case 'G':
							result = (0xffff00ff & result) | i << 8;
							break;

						case 'z': case 'Z': case 'b': case 'B':
							result = (0xff00ffff & result) | i << 16;
							break;

						case 'w': case 'W': case 'a': case 'A':
							result = (0x00ffffff & result) | i << 24;
							break;

						default:
							throw new Error('Invalid string.');
					}

			return result;
		}

		private function getNode(value : Object) : AbstractNode
		{
			if (value is AbstractNode)
				return value as AbstractNode;

			if (value is SFloat)
				return (value as SFloat)._node;

			if (value is uint || value is int || value is Number)
				return new Constant(new <Number>[Number(value)]);

			if (value is Matrix4x4)
				return new Constant(Matrix4x4(value).getRawData(null, 0, false));

			throw new Error('This type cannot be casted to a shader value.');
		}
	}
}

fix SFloat.setProperty()

fix SFloat.setProperty()