/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.pivotshare.hls.loader {

    import flash.display.DisplayObject;
    import flash.events.*;
    import flash.net.*;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;
    import flash.utils.Timer;
    import com.pivotshare.hls.loader.FragmentStream;
    import org.mangui.hls.constant.HLSLoaderTypes;
    import org.mangui.hls.demux.Demuxer;
    import org.mangui.hls.demux.DemuxHelper;
    import org.mangui.hls.demux.ID3Tag;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSLoadMetrics;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.model.AudioTrack;
    import org.mangui.hls.model.Fragment;
    import org.mangui.hls.model.FragmentData;
    import org.mangui.hls.utils.AES;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
        import org.mangui.hls.utils.Hex;
    }

    /**
     * HLS Fragment Demuxed Streamer.
     * Tries to parallel URLStream design pattern, but is incomplete.
     *
     * This class encapsulates Demuxing, but in an inefficient way. This is not
     * meant to be performant for sequential Fragments in play.
     *
     * @class    FragmentDemuxedStream
     * @extends  EventDispatcher
     * @author   HGPA
     */
    public class FragmentDemuxedStream extends EventDispatcher {

        /*
         * DisplayObject needed by AES decrypter.
         */
        private var _displayObject : DisplayObject;

        /*
         * Fragment being loaded.
         */
        private var _fragment : Fragment;

        /*
         * URLStream used to download current Fragment.
         */
        private var _fragmentStream : FragmentStream;

        /*
         * Metrics for this stream.
         */
        private var _metrics : HLSLoadMetrics;

        /*
         * Demuxer needed for this Fragment.
         */
        private var _demux : Demuxer;

        /*
         * Options for streaming and demuxing.
         */
        private var _options : Object;

        //
        //
        //
        // Public Methods
        //
        //
        //

        /**
         * Create a FragmentStream.
         *
         * This constructor takes a reference to main DisplayObject, e.g. stage,
         * necessary for AES decryption control.
         *
         * TODO: It would be more appropriate to create a factory that itself
         * takes the DisplayObject (or a more flexible version of AES).
         *
         * @constructor
         * @param  {DisplayObject}  displayObject
         */
        public function FragmentDemuxedStream(displayObject : DisplayObject) : void {
            _displayObject = displayObject;
            _fragment = null;
            _fragmentStream = null;
            _metrics = null;
            _demux = null;
            _options = new Object();
        };

        /*
         * Return FragmentData of Fragment currently being downloaded.
         * Of immediate interest is the `bytes` field.
         *
         * @method getFragment
         * @return  {Fragment}
         */
        public function getFragment() : Fragment {
            return _fragment;
        }

        /*
         * Close the stream.
         *
         * @method  close
         */
        public function close() : void {
            if (_fragmentStream) {
                _fragmentStream.close();
            }

            if (_demux) {
                _demux.cancel();
                _demux = null;
            }
        }

        /**
         * Load a Fragment.
         *
         * This class/methods DOES NOT user reference of parameter. Instead it
         * clones the Fragment and manipulates this internally.
         *
         * @method  load
         * @param   {Fragment}   fragment  -  Fragment with details (cloned)
         * @param   {ByteArray}  key       -  Encryption Key
         * @return  {HLSLoadMetrics}
         */
        public function load(fragment : Fragment, key : ByteArray, options : Object) : HLSLoadMetrics {

            _options = options;

            // Clone Fragment, with new initilizations of deep fields
            // Passing around references is what is causing problems.
            // FragmentData is initialized as part of construction
            _fragment = new Fragment(
                fragment.url,
                fragment.duration,
                fragment.level,
                fragment.seqnum,
                fragment.start_time,
                fragment.continuity,
                fragment.program_date,
                fragment.decrypt_url,
                fragment.decrypt_iv, // We need this reference
                fragment.byterange_start_offset,
                fragment.byterange_end_offset,
                new Vector.<String>()
            )

            _fragmentStream = new FragmentStream(_displayObject);

            _fragmentStream.addEventListener(IOErrorEvent.IO_ERROR, onStreamError);
            _fragmentStream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onStreamError);
            _fragmentStream.addEventListener(ProgressEvent.PROGRESS, onStreamProgress);
            _fragmentStream.addEventListener(Event.COMPLETE, onStreamComplete);

            CONFIG::LOGGING {
                var fragmentString : String = "Fragment[" + _fragment.level + "][" + _fragment.seqnum + "]";
                Log.debug("FragmentDemuxedStream#load: " + fragmentString);
            }

            _metrics = _fragmentStream.load(_fragment, key);

            return _metrics;
        }

        //
        //
        //
        // FragmentStream Event Listeners
        //
        //
        //

        /**
         *
         * @method  onFragmentStreamProgress
         * @param   {ProgressEvent}  evt
         */
        private function onStreamProgress(evt : ProgressEvent) : void {

            _fragment = _fragmentStream.getFragment();

            CONFIG::LOGGING {
                var fragmentString : String = "Fragment[" + _fragment.level + "][" + _fragment.seqnum + "]";
                Log.debug2("FragmentDemuxedStream#onStreamProgress: " +
                    fragmentString + " Progress - " + evt.bytesLoaded + " of " + evt.bytesTotal);

                Log.debug2("FragmentDemuxedStream#onStreamProgress: " +
                    fragmentString + " Fragment status - bytes.position / bytes.length / bytesLoaded " +
                    _fragment.data.bytes.position + " / " +
                    _fragment.data.bytes.length + " / " +
                    _fragment.data.bytesLoaded);
            }

            // If we are loading a partial Fragment then only parse when it has
            // completed loading to desired portion (See onFragmentStreamComplete)
            /*
            if (fragment.byterange_start_offset != -1) {
                return;
            }
            */

            // START PARSING METRICS
            if (_metrics.parsing_begin_time == 0) {
                _metrics.parsing_begin_time = getTimer();
            }

            // Demuxer has not yet been initialized as this is probably first
            // call to onFragmentStreamProgress, but demuxer may also be/remain
            // null due to unknown Fragment type. Probe is synchronous.
            if (_demux == null) {

                // It is possible we have run onFragmentStreamProgress before
                // without having sufficient data to probe
                //bytes.position = bytes.length;
                //bytes.writeBytes(byteArray);
                //byteArray = bytes;
                CONFIG::LOGGING {
                    var fragmentString : String = "Fragment[" + _fragment.level + "][" + _fragment.seqnum + "]";
                    Log.debug2("FragmentDemuxedStream#onStreamProgress: Need a Demuxer for " + fragmentString);
                }

                _demux = DemuxHelper.probe(
                    _fragment.data.bytes,
                    null,
                    _onDemuxAudioTrackRequested,
                    _onDemuxProgress,
                    _onDemuxComplete,
                    _onDemuxVideoMetadata,
                    _onDemuxID3TagFound,
                    false
                    );
            }

            if (_demux) {

                // Demuxer expects the ByteArray delta
                var byteArray : ByteArray = new ByteArray();
                _fragment.data.bytes.readBytes(byteArray, 0, _fragment.data.bytes.length - _fragment.data.bytes.position);
                byteArray.position = 0;

                _demux.append(byteArray);
            }
        }

        /**
         * Called when FragmentStream completes.
         *
         * @method  onStreamComplete
         * @param   {Event}  evt
         */
        private function onStreamComplete(evt : Event) : void {

            // If demuxer is still null, then the Fragment type was invalid
            if (_demux == null) {
                CONFIG::LOGGING {
                    Log.error("FragmentDemuxedStream#onStreamComplete: unknown fragment type");
                    _fragment.data.bytes.position = 0;
                    var bytes2 : ByteArray = new ByteArray();
                    _fragment.data.bytes.readBytes(bytes2, 0, 512);
                    Log.debug2("FragmentDemuxedStream#onStreamComplete: frag dump(512 bytes)");
                    Log.debug2(Hex.fromArray(bytes2));
                }

                var err : ErrorEvent = new ErrorEvent(
                    ErrorEvent.ERROR,
                    false,
                    false,
                    "Unknown Fragment Type"
                    );

                dispatchEvent(err);

            } else {
                _demux.notifycomplete();
            }
        }

        /**
         * Called when FragmentStream has errored.
         *
         * @method  onStreamError
         * @param   {ProgressEvent}  evt
         */
        private function onStreamError(evt : ErrorEvent) : void {
            CONFIG::LOGGING {
                Log.error("FragmentDemuxedStream#onStreamError: " + evt.text);
            }
            dispatchEvent(evt);
        }

        //
        //
        //
        // Demuxer Callbacks
        //
        //
        //

        /**
         * Called when Demuxer needs to know which AudioTrack to parse for.
         *
         * FIXME: This callback simply returns the first audio track!
         * We need to pass this class the appropriate callback propogated from
         * UI layer. Yuck.
         *
         * @method  _onDemuxAudioTrackRequested
         * @param   {Vector<AudioTrack}  audioTrackList  -  List of AudioTracks
         * @return  {AudioTrack}  -  AudioTrack to parse
         */
        private function _onDemuxAudioTrackRequested(audioTrackList : Vector.<AudioTrack>) : AudioTrack {
            if (audioTrackList.length > 0) {
                return audioTrackList[0];
            } else {
                return null;
            }
        }

        /**
         * Called when Demuxer parsed portion of Fragment.
         *
         * @method  _onDemuxProgress
         * @param   {Vector<FLVTag}  tags
         */
        private function _onDemuxProgress(tags : Vector.<FLVTag>) : void {
            CONFIG::LOGGING {
                Log.debug2("FragmentDemuxedStream#_onDemuxProgress");
            }

            _fragment.data.appendTags(tags);

            // TODO: Options to parse only portion of Fragment

            // FIXME: bytesLoaded and bytesTotal represent stats of the current
            // FragmentStream, not Demuxer, which is no longer bytes-relative.
            // What should we define here?
            var progressEvent : ProgressEvent = new ProgressEvent(
                ProgressEvent.PROGRESS,
                false,
                false,
                _fragment.data.bytes.length, // bytesLoaded
                _fragment.data.bytesTotal    // bytesTotal
                );

            dispatchEvent(progressEvent);
        };

        /**
         * Called when Demuxer has finished parsing Fragment.
         *
         * @method  _onDemuxComplete
         */
        private function _onDemuxComplete() : void {
            CONFIG::LOGGING {
                Log.debug2("FragmentDemuxedStream#_onDemuxComplete");
            }

            _metrics.parsing_end_time = getTimer();

            var completeEvent : Event = new ProgressEvent(Event.COMPLETE);
            dispatchEvent(completeEvent);
        };

        /**
         * Called when Video metadata is parsed.
         *
         * Specifically, when Sequence Parameter Set (SPS) is found.
         *
         * @method  _onDemuxVideoMetadata
         * @param   {uint}  width
         * @param   {uint}  height
         */
        private function _onDemuxVideoMetadata(width : uint, height : uint) : void {
            if (_fragment.data.video_width == 0) {
                CONFIG::LOGGING {
                    Log.debug2("FragmentDemuxedStream#_onDemuxVideoMetadata: AVC SPS = " + width + "x" + height);
                }
                _fragment.data.video_width = width;
                _fragment.data.video_height = height;
            }
        }

        /**
         * Called when ID3 tags are found.
         *
         * @method  _onDemuxID3TagFound
         * @param   {Vector.<ID3Tag>}  id3_tags  -  ID3 Tags
         */
        private function _onDemuxID3TagFound(id3_tags : Vector.<ID3Tag>) : void {
            _fragment.data.id3_tags = id3_tags;
        }

    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.pivotshare.hls.loader {

    import flash.display.DisplayObject;
    import flash.events.*;
    import flash.net.*;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;
    import flash.utils.Timer;
    import com.pivotshare.hls.loader.FragmentStream;
    import org.mangui.hls.constant.HLSLoaderTypes;
    import org.mangui.hls.demux.Demuxer;
    import org.mangui.hls.demux.DemuxHelper;
    import org.mangui.hls.demux.ID3Tag;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSLoadMetrics;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.model.AudioTrack;
    import org.mangui.hls.model.Fragment;
    import org.mangui.hls.model.FragmentData;
    import org.mangui.hls.utils.AES;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
        import org.mangui.hls.utils.Hex;
    }

    /**
     * HLS Fragment Demuxed Streamer.
     * Tries to parallel URLStream design pattern, but is incomplete.
     *
     * This class encapsulates Demuxing, but in an inefficient way. This is not
     * meant to be performant for sequential Fragments in play.
     *
     * @class    FragmentDemuxedStream
     * @extends  EventDispatcher
     * @author   HGPA
     */
    public class FragmentDemuxedStream extends EventDispatcher {

        /*
         * DisplayObject needed by AES decrypter.
         */
        private var _displayObject : DisplayObject;

        /*
         * Fragment being loaded.
         */
        private var _fragment : Fragment;

        /*
         * URLStream used to download current Fragment.
         */
        private var _fragmentStream : FragmentStream;

        /*
         * Metrics for this stream.
         */
        private var _metrics : HLSLoadMetrics;

        /*
         * Demuxer needed for this Fragment.
         */
        private var _demux : Demuxer;

        /*
         * Options for streaming and demuxing.
         */
        private var _options : Object;

        /*
         * Whether Fragment has completed processing (loading/demuxing).
         */
        private var _complete : Boolean;

        //
        //
        //
        // Public Methods
        //
        //
        //

        /**
         * Create a FragmentStream.
         *
         * This constructor takes a reference to main DisplayObject, e.g. stage,
         * necessary for AES decryption control.
         *
         * TODO: It would be more appropriate to create a factory that itself
         * takes the DisplayObject (or a more flexible version of AES).
         *
         * @constructor
         * @param  {DisplayObject}  displayObject
         */
        public function FragmentDemuxedStream(displayObject : DisplayObject) : void {
            _displayObject = displayObject;
            _fragment = null;
            _fragmentStream = null;
            _metrics = null;
            _demux = null;
            _options = new Object();
            _complete = false;
        };

        /*
         * Return FragmentData of Fragment currently being downloaded.
         * Of immediate interest is the `bytes` field.
         *
         * @method getFragment
         * @return  {Fragment}
         */
        public function getFragment() : Fragment {
            return _fragment;
        }

        /**
         * Whether Fragment has completed loading and demuxing.
         *
         * @method  complete
         * @return  {Boolean}
         */
        public function get complete():Boolean {
            return _complete;
        }

        /*
         * Close the stream.
         *
         * @method  close
         */
        public function close() : void {
            if (_fragmentStream) {
                _fragmentStream.close();
            }

            if (_demux) {
                _demux.cancel();
                _demux = null;
            }
        }

        /**
         * Load a Fragment.
         *
         * This class/methods DOES NOT user reference of parameter. Instead it
         * clones the Fragment and manipulates this internally.
         *
         * @method  load
         * @param   {Fragment}   fragment  -  Fragment with details (cloned)
         * @param   {ByteArray}  key       -  Encryption Key
         * @return  {HLSLoadMetrics}
         */
        public function load(fragment : Fragment, key : ByteArray, options : Object) : HLSLoadMetrics {

            _options = options;

            // Clone Fragment, with new initilizations of deep fields
            // Passing around references is what is causing problems.
            // FragmentData is initialized as part of construction
            _fragment = new Fragment(
                fragment.url,
                fragment.duration,
                fragment.level,
                fragment.seqnum,
                fragment.start_time,
                fragment.continuity,
                fragment.program_date,
                fragment.decrypt_url,
                fragment.decrypt_iv, // We need this reference
                fragment.byterange_start_offset,
                fragment.byterange_end_offset,
                new Vector.<String>()
            )

            _fragmentStream = new FragmentStream(_displayObject);

            _fragmentStream.addEventListener(IOErrorEvent.IO_ERROR, onStreamError);
            _fragmentStream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onStreamError);
            _fragmentStream.addEventListener(ProgressEvent.PROGRESS, onStreamProgress);
            _fragmentStream.addEventListener(Event.COMPLETE, onStreamComplete);

            CONFIG::LOGGING {
                var fragmentString : String = "Fragment[" + _fragment.level + "][" + _fragment.seqnum + "]";
                Log.debug("FragmentDemuxedStream#load: " + fragmentString);
            }

            _metrics = _fragmentStream.load(_fragment, key);

            return _metrics;
        }

        //
        //
        //
        // FragmentStream Event Listeners
        //
        //
        //

        /**
         *
         * @method  onFragmentStreamProgress
         * @param   {ProgressEvent}  evt
         */
        private function onStreamProgress(evt : ProgressEvent) : void {

            _fragment = _fragmentStream.getFragment();

            CONFIG::LOGGING {
                var fragmentString : String = "Fragment[" + _fragment.level + "][" + _fragment.seqnum + "]";
                Log.debug2("FragmentDemuxedStream#onStreamProgress: " +
                    fragmentString + " Progress - " + evt.bytesLoaded + " of " + evt.bytesTotal);

                Log.debug2("FragmentDemuxedStream#onStreamProgress: " +
                    fragmentString + " Fragment status - bytes.position / bytes.length / bytesLoaded " +
                    _fragment.data.bytes.position + " / " +
                    _fragment.data.bytes.length + " / " +
                    _fragment.data.bytesLoaded);
            }

            // If we are loading a partial Fragment then only parse when it has
            // completed loading to desired portion (See onFragmentStreamComplete)
            /*
            if (fragment.byterange_start_offset != -1) {
                return;
            }
            */

            // START PARSING METRICS
            if (_metrics.parsing_begin_time == 0) {
                _metrics.parsing_begin_time = getTimer();
            }

            // Demuxer has not yet been initialized as this is probably first
            // call to onFragmentStreamProgress, but demuxer may also be/remain
            // null due to unknown Fragment type. Probe is synchronous.
            if (_demux == null) {

                // It is possible we have run onFragmentStreamProgress before
                // without having sufficient data to probe
                //bytes.position = bytes.length;
                //bytes.writeBytes(byteArray);
                //byteArray = bytes;
                CONFIG::LOGGING {
                    var fragmentString : String = "Fragment[" + _fragment.level + "][" + _fragment.seqnum + "]";
                    Log.debug2("FragmentDemuxedStream#onStreamProgress: Need a Demuxer for " + fragmentString);
                }

                _demux = DemuxHelper.probe(
                    _fragment.data.bytes,
                    null,
                    _onDemuxAudioTrackRequested,
                    _onDemuxProgress,
                    _onDemuxComplete,
                    _onDemuxVideoMetadata,
                    _onDemuxID3TagFound,
                    false
                    );
            }

            if (_demux) {

                // Demuxer expects the ByteArray delta
                var byteArray : ByteArray = new ByteArray();
                _fragment.data.bytes.readBytes(byteArray, 0, _fragment.data.bytes.length - _fragment.data.bytes.position);
                byteArray.position = 0;

                _demux.append(byteArray);
            }
        }

        /**
         * Called when FragmentStream completes.
         *
         * @method  onStreamComplete
         * @param   {Event}  evt
         */
        private function onStreamComplete(evt : Event) : void {

            // If demuxer is still null, then the Fragment type was invalid
            if (_demux == null) {
                CONFIG::LOGGING {
                    Log.error("FragmentDemuxedStream#onStreamComplete: unknown fragment type");
                    _fragment.data.bytes.position = 0;
                    var bytes2 : ByteArray = new ByteArray();
                    _fragment.data.bytes.readBytes(bytes2, 0, 512);
                    Log.debug2("FragmentDemuxedStream#onStreamComplete: frag dump(512 bytes)");
                    Log.debug2(Hex.fromArray(bytes2));
                }

                var err : ErrorEvent = new ErrorEvent(
                    ErrorEvent.ERROR,
                    false,
                    false,
                    "Unknown Fragment Type"
                    );

                dispatchEvent(err);

            } else {
                _demux.notifycomplete();
            }
        }

        /**
         * Called when FragmentStream has errored.
         *
         * @method  onStreamError
         * @param   {ProgressEvent}  evt
         */
        private function onStreamError(evt : ErrorEvent) : void {
            CONFIG::LOGGING {
                Log.error("FragmentDemuxedStream#onStreamError: " + evt.text);
            }
            dispatchEvent(evt);
        }

        //
        //
        //
        // Demuxer Callbacks
        //
        //
        //

        /**
         * Called when Demuxer needs to know which AudioTrack to parse for.
         *
         * FIXME: This callback simply returns the first audio track!
         * We need to pass this class the appropriate callback propogated from
         * UI layer. Yuck.
         *
         * @method  _onDemuxAudioTrackRequested
         * @param   {Vector<AudioTrack}  audioTrackList  -  List of AudioTracks
         * @return  {AudioTrack}  -  AudioTrack to parse
         */
        private function _onDemuxAudioTrackRequested(audioTrackList : Vector.<AudioTrack>) : AudioTrack {
            if (audioTrackList.length > 0) {
                return audioTrackList[0];
            } else {
                return null;
            }
        }

        /**
         * Called when Demuxer parsed portion of Fragment.
         *
         * @method  _onDemuxProgress
         * @param   {Vector<FLVTag}  tags
         */
        private function _onDemuxProgress(tags : Vector.<FLVTag>) : void {
            CONFIG::LOGGING {
                Log.debug2("FragmentDemuxedStream#_onDemuxProgress");
            }

            _fragment.data.appendTags(tags);

            // TODO: Options to parse only portion of Fragment

            // FIXME: bytesLoaded and bytesTotal represent stats of the current
            // FragmentStream, not Demuxer, which is no longer bytes-relative.
            // What should we define here?
            var progressEvent : ProgressEvent = new ProgressEvent(
                ProgressEvent.PROGRESS,
                false,
                false,
                _fragment.data.bytes.length, // bytesLoaded
                _fragment.data.bytesTotal    // bytesTotal
                );

            dispatchEvent(progressEvent);
        };

        /**
         * Called when Demuxer has finished parsing Fragment.
         *
         * @method  _onDemuxComplete
         */
        private function _onDemuxComplete() : void {
            CONFIG::LOGGING {
                Log.debug2("FragmentDemuxedStream#_onDemuxComplete");
            }

            _metrics.parsing_end_time = getTimer();
            _complete = true;

            var completeEvent : Event = new ProgressEvent(Event.COMPLETE);
            dispatchEvent(completeEvent);
        };

        /**
         * Called when Video metadata is parsed.
         *
         * Specifically, when Sequence Parameter Set (SPS) is found.
         *
         * @method  _onDemuxVideoMetadata
         * @param   {uint}  width
         * @param   {uint}  height
         */
        private function _onDemuxVideoMetadata(width : uint, height : uint) : void {
            if (_fragment.data.video_width == 0) {
                CONFIG::LOGGING {
                    Log.debug2("FragmentDemuxedStream#_onDemuxVideoMetadata: AVC SPS = " + width + "x" + height);
                }
                _fragment.data.video_width = width;
                _fragment.data.video_height = height;
            }
        }

        /**
         * Called when ID3 tags are found.
         *
         * @method  _onDemuxID3TagFound
         * @param   {Vector.<ID3Tag>}  id3_tags  -  ID3 Tags
         */
        private function _onDemuxID3TagFound(id3_tags : Vector.<ID3Tag>) : void {
            _fragment.data.id3_tags = id3_tags;
        }

    }
}

Add complete state variable

[FragmentDemuxedStream] Add complete state variable


package htmlelements
{
	import flash.display.Sprite;
	import flash.events.*;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.media.Video;
	import flash.media.SoundTransform;
	import flash.utils.Timer;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLVariables;
    import flash.net.URLRequestMethod;
    import flash.display.MovieClip;
	 import flash.display.Loader;
	 import flash.display.DisplayObject;

	import FlashMediaElement;
	import HtmlMediaEvent;

	public class YouTubeElement extends Sprite implements IMediaElement
	{
		private var _currentUrl:String = "";
		private var _autoplay:Boolean = true;
		private var _preload:String = "";

		private var _element:FlashMediaElement;

		// event values
		private var _currentTime:Number = 0;
		private var _duration:Number = 0;
		private var _framerate:Number;
		private var _isPaused:Boolean = true;
		private var _isEnded:Boolean = false;
		private var _volume:Number = 1;
		private var _isMuted:Boolean = false;

		private var _bytesLoaded:Number = 0;
		private var _bytesTotal:Number = 0;
		private var _bufferedTime:Number = 0;
		private var _bufferEmpty:Boolean = false;

		private var _videoWidth:Number = -1;
		private var _videoHeight:Number = -1;

		private var _timer:Timer;

		// YouTube stuff
		private var _playerLoader:Loader;
		private var _player:Object = null;
		private var _playerIsLoaded:Boolean = false;
		private var _youTubeId:String = "";

		//http://code.google.com/p/gdata-samples/source/browse/trunk/ytplayer/actionscript3/com/google/youtube/examples/AS3Player.as
		private static const WIDESCREEN_ASPECT_RATIO:String = "widescreen";
		private static const QUALITY_TO_PLAYER_WIDTH:Object = {
			small: 320,
			medium: 640,
			large: 854,
			hd720: 1280
		};
		private static const STATE_ENDED:Number = 0;
		private static const STATE_PLAYING:Number = 1;
		private static const STATE_PAUSED:Number = 2;
		private static const STATE_CUED:Number = 5;

		public function get player():DisplayObject {
			return _player;
		}

		public function setSize(width:Number, height:Number):void {
			if (_player != null) {
				_player.setSize(width, height);
			}
		}

		public function get videoHeight():Number {
			return _videoHeight;
		}

		public function get videoWidth():Number {
			return _videoWidth;
		}

		public function duration():Number {
			return _duration;
		}

		public function currentProgress():Number {
			if(_bytesTotal> 0) {
				return Math.round(_bytesLoaded/_bytesTotal*100);
			} else {
				return 0;
			}
		}

		public function currentTime():Number {
			return _currentTime;
		}

		public var initHeight:Number;
		public var initWidth:Number;

		// (1) load()
		// calls _connection.connect();
		// waits for NetConnection.Connect.Success
		// _stream gets created

		private var _isChromeless:Boolean = false;

		public function YouTubeElement(element:FlashMediaElement, autoplay:Boolean, preload:String, timerRate:Number, startVolume:Number)
		{
			_element = element;
			_autoplay = autoplay;
			_volume = startVolume;
			_preload = preload;
			initHeight = 0;
			initWidth = 0;

			_playerLoader = new Loader();
			_playerLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, playerLoaderInitHandler);

			// chromeless
			if (_isChromeless) {
				_playerLoader.load(new URLRequest("http://www.youtube.com/apiplayer?version=3&controls=1&rel=0&showinfo=0&iv_load_policy=1"));
			}

			_timer = new Timer(timerRate);
			_timer.addEventListener("timer", timerHandler);
			_timer.start();
		}

		private function playerLoaderInitHandler(event:Event):void {

			trace("yt player init");

			_element.addChild(_playerLoader.content);
			_element.setControlDepth();
			_playerLoader.content.addEventListener("onReady", onPlayerReady);
			_playerLoader.content.addEventListener("onError", onPlayerError);
			_playerLoader.content.addEventListener("onStateChange", onPlayerStateChange);
			_playerLoader.content.addEventListener("onPlaybackQualityChange", onVideoPlaybackQualityChange);
		}

		private function onPlayerReady(event:Event):void {
			_playerIsLoaded = true;

			_player = _playerLoader.content;

			if (initHeight > 0 && initWidth > 0)
				_player.setSize(initWidth, initHeight);

			if (_youTubeId != "") { //  && _isChromeless) {
				if (_autoplay) {
					player.loadVideoById(_youTubeId);
				} else {
					player.cueVideoById(_youTubeId);
				}
				_timer.start();
			}
		}

		private function onPlayerError(event:Event):void {
			// trace("Player error:", Object(event).data);
		}

		private function onPlayerStateChange(event:Event):void {
			trace("State is", Object(event).data);

			_duration = _player.getDuration();

			switch (Object(event).data) {
				case STATE_ENDED:
					_isEnded = true;
					_isPaused = false;

					sendEvent(HtmlMediaEvent.ENDED);

					break;

				case STATE_PLAYING:
					_isEnded = false;
					_isPaused = false;

					sendEvent(HtmlMediaEvent.PLAY);
					sendEvent(HtmlMediaEvent.PLAYING);
					break;

				case STATE_PAUSED:
					_isEnded = false;
					_isPaused = true;

					sendEvent(HtmlMediaEvent.PAUSE);

					break;

				case STATE_CUED:
					sendEvent(HtmlMediaEvent.CANPLAY);

					// resize?

					break;
			}
		}

		private function onVideoPlaybackQualityChange(event:Event):void {
			trace("Current video quality:", Object(event).data);
			//resizePlayer(Object(event).data);
		}

		private function timerHandler(e:TimerEvent) {

			if (_playerIsLoaded) {
				_bytesLoaded = _player.getVideoBytesLoaded();
				_bytesTotal = _player.getVideoBytesTotal();
				_currentTime = player.getCurrentTime();

				if (!_isPaused)
					sendEvent(HtmlMediaEvent.TIMEUPDATE);

				if (_bytesLoaded < _bytesTotal)
					sendEvent(HtmlMediaEvent.PROGRESS);
			}

		}

		private function getYouTubeId(url:String):String {
			// http://www.youtube.com/watch?feature=player_embedded&v=yyWWXSwtPP0
			// http://www.youtube.com/v/VIDEO_ID?version=3
			// http://youtu.be/Djd6tPrxc08

			url = unescape(url);

			var youTubeId:String = "";

			if (url.indexOf("?") > 0) {
				// assuming: http://www.youtube.com/watch?feature=player_embedded&v=yyWWXSwtPP0
				youTubeId = getYouTubeIdFromParam(url);

				// if it's http://www.youtube.com/v/VIDEO_ID?version=3
				if (youTubeId == "") {
					youTubeId = getYouTubeIdFromUrl(url);
				}
			} else {
				youTubeId = getYouTubeIdFromUrl(url);
			}

			return youTubeId;
		}

		// http://www.youtube.com/watch?feature=player_embedded&v=yyWWXSwtPP0
		private function getYouTubeIdFromParam(url:String):String {

			var youTubeId:String = "";
			var parts:Array = url.split('?');
			var parameters:Array = parts[1].split('&');

			for (var i:Number=0; i<parameters.length; i++) {
				var paramParts = parameters[i].split('=');
				if (paramParts[0] == "v") {

					youTubeId = paramParts[1];
					break;
				}

			}

			return youTubeId;
		}

		// http://www.youtube.com/v/VIDEO_ID?version=3
		// http://youtu.be/Djd6tPrxc08
		private function getYouTubeIdFromUrl(url:String):String {

			var youTubeId:String = "";

			// remove any querystring elements
			var parts:Array = url.split('?');
			url = parts[0];

			youTubeId = url.substring(url.lastIndexOf("/")+1);

			return youTubeId;
		}

		// interface members
		public function setSrc(url:String):void {
			trace("yt setSrc()" + url );

			_currentUrl = url;

			_youTubeId = getYouTubeId(url);

			if (!_playerIsLoaded && !_isChromeless) {
				_playerLoader.load(new URLRequest("http://www.youtube.com/v/" + _youTubeId + "?version=3&controls=0&rel=0&showinfo=0&iv_load_policy=1"));
			}
		}

		public function load():void {
			// do nothing
			trace("yt load()");

			if (_playerIsLoaded) {
				player.loadVideoById(_youTubeId);
				_timer.start();
			}  else {
				/*
				if (!_isChromless && _youTubeId != "") {
					_playerLoader.load(new URLRequest("http://www.youtube.com/v/" + _youTubeId + "?version=3&controls=0&rel=0&showinfo=0&iv_load_policy=1"));
				}
				*/
			}
		}

		public function play():void {
			if (_playerIsLoaded) {
				_player.playVideo();
			}

		}

		public function pause():void {
			if (_playerIsLoaded) {
				_player.pauseVideo();
			}
		}

		public function stop():void {
			if (_playerIsLoaded) {
				_player.pauseVideo();
			}
		}

		public function setCurrentTime(pos:Number):void {
			_player.seekTo(pos, false);
		}

		public function setVolume(volume:Number):void {
			_player.setVolume(volume*100);
			_volume = volume;
		}

		public function getVolume():Number {
			return _player.getVolume()*100;
		}

		public function setMuted(muted:Boolean):void {
			if (muted) {
				_player.mute();

			} else {
				_player.unMute();
			}
			_muted = _player.isMuted();
			sendEvent(HtmlMediaEvent.VOLUMECHANGE);
		}

		private function sendEvent(eventName:String) {

			// calculate this to mimic HTML5
			_bufferedTime = _bytesLoaded / _bytesTotal * _duration;

			// build JSON
			var values:String =
							"duration:" + _duration +
							",framerate:" + _framerate +
							",currentTime:" + _currentTime +
							",muted:" + _isMuted +
							",paused:" + _isPaused +
							",ended:" + _isEnded +
							",volume:" + _volume +
							",src:\"" + _currentUrl + "\"" +
							",bytesTotal:" + _bytesTotal +
							",bufferedBytes:" + _bytesLoaded +
							",bufferedTime:" + _bufferedTime +
							",videoWidth:" + _videoWidth +
							",videoHeight:" + _videoHeight +
							"";

			_element.sendEvent(eventName, values);
		}
	}
}

package htmlelements
{
	import flash.display.Sprite;
	import flash.events.*;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.media.Video;
	import flash.media.SoundTransform;
	import flash.utils.Timer;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLVariables;
    import flash.net.URLRequestMethod;
    import flash.display.MovieClip;
	 import flash.display.Loader;
	 import flash.display.DisplayObject;

	import FlashMediaElement;
	import HtmlMediaEvent;

	public class YouTubeElement extends Sprite implements IMediaElement
	{
		private var _currentUrl:String = "";
		private var _autoplay:Boolean = true;
		private var _preload:String = "";

		private var _element:FlashMediaElement;

		// event values
		private var _currentTime:Number = 0;
		private var _duration:Number = 0;
		private var _framerate:Number;
		private var _isPaused:Boolean = true;
		private var _isEnded:Boolean = false;
		private var _volume:Number = 1;
		private var _isMuted:Boolean = false;

		private var _bytesLoaded:Number = 0;
		private var _bytesTotal:Number = 0;
		private var _bufferedTime:Number = 0;
		private var _bufferEmpty:Boolean = false;

		private var _videoWidth:Number = -1;
		private var _videoHeight:Number = -1;

		private var _timer:Timer;

		// YouTube stuff
		private var _playerLoader:Loader;
		private var _player:Object = null;
		private var _playerIsLoaded:Boolean = false;
		private var _youTubeId:String = "";

		//http://code.google.com/p/gdata-samples/source/browse/trunk/ytplayer/actionscript3/com/google/youtube/examples/AS3Player.as
		private static const WIDESCREEN_ASPECT_RATIO:String = "widescreen";
		private static const QUALITY_TO_PLAYER_WIDTH:Object = {
			small: 320,
			medium: 640,
			large: 854,
			hd720: 1280
		};
		private static const STATE_ENDED:Number = 0;
		private static const STATE_PLAYING:Number = 1;
		private static const STATE_PAUSED:Number = 2;
		private static const STATE_CUED:Number = 5;

		public function get player():DisplayObject {
			return _player;
		}

		public function setSize(width:Number, height:Number):void {
			if (_player != null) {
				_player.setSize(width, height);
			} else {
				initHeight = height;
				initWidth = width;
			}
		}

		public function get videoHeight():Number {
			return _videoHeight;
		}

		public function get videoWidth():Number {
			return _videoWidth;
		}

		public function duration():Number {
			return _duration;
		}

		public function currentProgress():Number {
			if(_bytesTotal> 0) {
				return Math.round(_bytesLoaded/_bytesTotal*100);
			} else {
				return 0;
			}
		}

		public function currentTime():Number {
			return _currentTime;
		}

		public var initHeight:Number;
		public var initWidth:Number;

		// (1) load()
		// calls _connection.connect();
		// waits for NetConnection.Connect.Success
		// _stream gets created

		private var _isChromeless:Boolean = false;

		public function YouTubeElement(element:FlashMediaElement, autoplay:Boolean, preload:String, timerRate:Number, startVolume:Number)
		{
			_element = element;
			_autoplay = autoplay;
			_volume = startVolume;
			_preload = preload;
			initHeight = 0;
			initWidth = 0;

			_playerLoader = new Loader();
			_playerLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, playerLoaderInitHandler);

			// chromeless
			if (_isChromeless) {
				_playerLoader.load(new URLRequest("http://www.youtube.com/apiplayer?version=3&controls=1&rel=0&showinfo=0&iv_load_policy=1"));
			}

			_timer = new Timer(timerRate);
			_timer.addEventListener("timer", timerHandler);
			_timer.start();
		}

		private function playerLoaderInitHandler(event:Event):void {

			trace("yt player init");

			_element.addChild(_playerLoader.content);
			_element.setControlDepth();
			_playerLoader.content.addEventListener("onReady", onPlayerReady);
			_playerLoader.content.addEventListener("onError", onPlayerError);
			_playerLoader.content.addEventListener("onStateChange", onPlayerStateChange);
			_playerLoader.content.addEventListener("onPlaybackQualityChange", onVideoPlaybackQualityChange);
		}

		private function onPlayerReady(event:Event):void {
			_playerIsLoaded = true;

			_player = _playerLoader.content;

			if (initHeight > 0 && initWidth > 0)
				_player.setSize(initWidth, initHeight);

			if (_youTubeId != "") { //  && _isChromeless) {
				if (_autoplay) {
					player.loadVideoById(_youTubeId);
				} else {
					player.cueVideoById(_youTubeId);
				}
				_timer.start();
			}
		}

		private function onPlayerError(event:Event):void {
			// trace("Player error:", Object(event).data);
		}

		private function onPlayerStateChange(event:Event):void {
			trace("State is", Object(event).data);

			_duration = _player.getDuration();

			switch (Object(event).data) {
				case STATE_ENDED:
					_isEnded = true;
					_isPaused = false;

					sendEvent(HtmlMediaEvent.ENDED);

					break;

				case STATE_PLAYING:
					_isEnded = false;
					_isPaused = false;

					sendEvent(HtmlMediaEvent.PLAY);
					sendEvent(HtmlMediaEvent.PLAYING);
					break;

				case STATE_PAUSED:
					_isEnded = false;
					_isPaused = true;

					sendEvent(HtmlMediaEvent.PAUSE);

					break;

				case STATE_CUED:
					sendEvent(HtmlMediaEvent.CANPLAY);

					// resize?

					break;
			}
		}

		private function onVideoPlaybackQualityChange(event:Event):void {
			trace("Current video quality:", Object(event).data);
			//resizePlayer(Object(event).data);
		}

		private function timerHandler(e:TimerEvent) {

			if (_playerIsLoaded) {
				_bytesLoaded = _player.getVideoBytesLoaded();
				_bytesTotal = _player.getVideoBytesTotal();
				_currentTime = player.getCurrentTime();

				if (!_isPaused)
					sendEvent(HtmlMediaEvent.TIMEUPDATE);

				if (_bytesLoaded < _bytesTotal)
					sendEvent(HtmlMediaEvent.PROGRESS);
			}

		}

		private function getYouTubeId(url:String):String {
			// http://www.youtube.com/watch?feature=player_embedded&v=yyWWXSwtPP0
			// http://www.youtube.com/v/VIDEO_ID?version=3
			// http://youtu.be/Djd6tPrxc08

			url = unescape(url);

			var youTubeId:String = "";

			if (url.indexOf("?") > 0) {
				// assuming: http://www.youtube.com/watch?feature=player_embedded&v=yyWWXSwtPP0
				youTubeId = getYouTubeIdFromParam(url);

				// if it's http://www.youtube.com/v/VIDEO_ID?version=3
				if (youTubeId == "") {
					youTubeId = getYouTubeIdFromUrl(url);
				}
			} else {
				youTubeId = getYouTubeIdFromUrl(url);
			}

			return youTubeId;
		}

		// http://www.youtube.com/watch?feature=player_embedded&v=yyWWXSwtPP0
		private function getYouTubeIdFromParam(url:String):String {

			var youTubeId:String = "";
			var parts:Array = url.split('?');
			var parameters:Array = parts[1].split('&');

			for (var i:Number=0; i<parameters.length; i++) {
				var paramParts = parameters[i].split('=');
				if (paramParts[0] == "v") {

					youTubeId = paramParts[1];
					break;
				}

			}

			return youTubeId;
		}

		// http://www.youtube.com/v/VIDEO_ID?version=3
		// http://youtu.be/Djd6tPrxc08
		private function getYouTubeIdFromUrl(url:String):String {

			var youTubeId:String = "";

			// remove any querystring elements
			var parts:Array = url.split('?');
			url = parts[0];

			youTubeId = url.substring(url.lastIndexOf("/")+1);

			return youTubeId;
		}

		// interface members
		public function setSrc(url:String):void {
			trace("yt setSrc()" + url );

			_currentUrl = url;

			_youTubeId = getYouTubeId(url);

			if (!_playerIsLoaded && !_isChromeless) {
				_playerLoader.load(new URLRequest("http://www.youtube.com/v/" + _youTubeId + "?version=3&controls=0&rel=0&showinfo=0&iv_load_policy=1"));
			}
		}

		public function load():void {
			// do nothing
			trace("yt load()");

			if (_playerIsLoaded) {
				player.loadVideoById(_youTubeId);
				_timer.start();
			}  else {
				/*
				if (!_isChromless && _youTubeId != "") {
					_playerLoader.load(new URLRequest("http://www.youtube.com/v/" + _youTubeId + "?version=3&controls=0&rel=0&showinfo=0&iv_load_policy=1"));
				}
				*/
			}
		}

		public function play():void {
			if (_playerIsLoaded) {
				_player.playVideo();
			}

		}

		public function pause():void {
			if (_playerIsLoaded) {
				_player.pauseVideo();
			}
		}

		public function stop():void {
			if (_playerIsLoaded) {
				_player.pauseVideo();
			}
		}

		public function setCurrentTime(pos:Number):void {
			//_player.seekTo(pos, false);
			_player.seekTo(pos, true); // works in all places now
		}

		public function setVolume(volume:Number):void {
			_player.setVolume(volume*100);
			_volume = volume;
		}

		public function getVolume():Number {
			return _player.getVolume()*100;
		}

		public function setMuted(muted:Boolean):void {
			if (muted) {
				_player.mute();

			} else {
				_player.unMute();
			}
			_muted = _player.isMuted();
			sendEvent(HtmlMediaEvent.VOLUMECHANGE);
		}

		private function sendEvent(eventName:String) {

			// calculate this to mimic HTML5
			_bufferedTime = _bytesLoaded / _bytesTotal * _duration;

			// build JSON
			var values:String =
							"duration:" + _duration +
							",framerate:" + _framerate +
							",currentTime:" + _currentTime +
							",muted:" + _isMuted +
							",paused:" + _isPaused +
							",ended:" + _isEnded +
							",volume:" + _volume +
							",src:\"" + _currentUrl + "\"" +
							",bytesTotal:" + _bytesTotal +
							",bufferedBytes:" + _bytesLoaded +
							",bufferedTime:" + _bufferedTime +
							",videoWidth:" + _videoWidth +
							",videoHeight:" + _videoHeight +
							"";

			_element.sendEvent(eventName, values);
		}
	}
}

fix for seeking

fix for seeking


package weave.data.Transforms
{
    import weave.api.core.ILinkableHashMap;
    import weave.api.data.ColumnMetadata;
    import weave.api.data.IAttributeColumn;
    import weave.api.data.IColumnReference;
    import weave.api.data.IDataSource;
    import weave.api.data.IWeaveTreeNode;
    import weave.api.newLinkableChild;
    import weave.api.registerLinkableChild;
    import weave.core.LinkableHashMap;
    import weave.data.AttributeColumns.DynamicColumn;
    import weave.data.AttributeColumns.FilteredColumn;
    import weave.data.AttributeColumns.ProxyColumn;
    import weave.data.DataSources.AbstractDataSource;
    import weave.data.KeySets.StringDataFilter;
    import weave.utils.VectorUtils;

    public class PartitionDataTransform extends AbstractDataSource
    {
        public static const PARTITION_VALUE_META:String = "__PartitionValue__";
        public static const PARTITION_COLUMNNAME_META:String = "__PartitionColumnName__";
        WeaveAPI.registerImplementation(IDataSource, PartitionDataTransform, "Partitioned Table");

        public const inputColumns:ILinkableHashMap = registerLinkableChild(this, new LinkableHashMap(IAttributeColumn), inputColumnsChanged);
        public const partitionColumn:DynamicColumn = newLinkableChild(this, DynamicColumn, partitionColumnChanged);

        public var layer_names:Array = [];

        public function PartitionDataTransform()
        {

        }

        public function inputColumnsChanged():void
        {
            refreshHierarchy();
            refreshAllProxyColumns();
        }

        private function partitionColumnChanged():void
        {
            var keys:Array = partitionColumn.keys;
            var layers:Object = {};
            for (var idx:int = keys.length - 1; idx >= 0; idx--)
            {
                var value:String = partitionColumn.getValueFromKey(keys[idx], String);

                layers[value] = true;
            }
            layer_names = VectorUtils.getKeys(layers);

            refreshHierarchy();
            refreshAllProxyColumns();
        }

        public function getInputColumnTitle(name:String):String
        {
            var column:IAttributeColumn = inputColumns.getObject(name) as IAttributeColumn;
            return column.getMetadata(ColumnMetadata.TITLE);
        }

        override protected function generateHierarchyNode(metadata:Object):IWeaveTreeNode
        {
            var transformNode:PartitionTransformNode;
            var valueNode:PartitionValueNode;
            var columnNode:PartitionColumnNode;

            transformNode = new PartitionTransformNode(this);

            if (metadata[PARTITION_VALUE_META] === undefined)
                return transformNode;

            valueNode = new PartitionValueNode(transformNode, metadata[PARTITION_VALUE_META])

            if (metadata[PARTITION_COLUMNNAME_META] === undefined)
                return valueNode;

            return new PartitionColumnNode(valueNode, metadata[PARTITION_COLUMNNAME_META]);
        }

        override public function getHierarchyRoot():IWeaveTreeNode
        {
            if (!_rootNode)
                _rootNode = new PartitionTransformNode(this);
            return _rootNode;
        }

        override protected function requestHierarchyFromSource(subtreeNode:XML = null):void
        {
            // do nothing, as the hierarchy is known ahead of time.
            return;
        }

        override protected function requestColumnFromSource(proxyColumn:ProxyColumn):void
        {

            var metadata:Object = proxyColumn.getProxyMetadata();
            var columnName:String = metadata[PARTITION_COLUMNNAME_META];
            var value:String = metadata[PARTITION_VALUE_META];

            var column:IAttributeColumn = inputColumns.getObject(columnName) as IAttributeColumn;

            var newFilteredColumn:FilteredColumn = new FilteredColumn();
            var filter:StringDataFilter = newFilteredColumn.filter.requestLocalObject(StringDataFilter, false);

            filter.column.requestLocalObjectCopy(partitionColumn);
            filter.stringValue.value = value;

            newFilteredColumn.internalDynamicColumn.requestLocalObjectCopy(column);

            proxyColumn.setInternalColumn(newFilteredColumn);
        }
    }
}

import weave.api.data.ColumnMetadata;
import weave.api.data.IColumnReference;
import weave.api.data.IDataSource;
import weave.api.data.IWeaveTreeNode;
import weave.data.Transforms.PartitionDataTransform;

internal class PartitionTransformNode implements IWeaveTreeNode
{
    private var _source:PartitionDataTransform;
    private var children:Array;

    public function get source():PartitionDataTransform { return _source; }

    public function PartitionTransformNode(source:PartitionDataTransform)
    {
        _source = source;
    }

    public function equals(other:IWeaveTreeNode):Boolean
    {
        var that:PartitionTransformNode = other as PartitionTransformNode;
        return !!that &&
                that.source === this.source;
    }

    public function isBranch():Boolean { return true; }
    public function hasChildBranches():Boolean {return true; }

    public function getChildren():Array
    {
        if (children == null)
        {
            children = [];
            var layer_names:Array = source.layer_names;
            for (var idx:int = layer_names.length - 1; idx >= 0; idx--)
            {
                var layer:String = layer_names[idx];

                children.push(new PartitionValueNode(this, layer));
            }
        }
        return children;
    }

    public function getLabel():String
    {
        return WeaveAPI.globalHashMap.getName(source);
    }

}
internal class PartitionValueNode implements IWeaveTreeNode
{
    private var _parent:PartitionTransformNode;
    private var _partitionValue:String;
    private var children:Array;

    public function get parent():PartitionTransformNode { return _parent; }
    public function get partitionValue():String { return _partitionValue; }

    public function PartitionValueNode(parent:PartitionTransformNode, value:String)
    {
        _parent = parent;
        _partitionValue = value;
    }

    public function equals(other:IWeaveTreeNode):Boolean
    {
        var that:PartitionValueNode = other as PartitionValueNode;
        return !!that &&
                this.partitionValue == that.partitionValue &&
                that.parent.equals(this.parent);
    }

    public function isBranch():Boolean { return true; }
    public function hasChildBranches():Boolean { return false; }

    public function getChildren():Array
    {
        if (children == null)
        {
            children = [];
            var column_names:Array = parent.source.inputColumns.getNames();
            for (var idx:int = column_names.length - 1; idx >= 0; idx--)
            {
                children.push(new PartitionColumnNode(this, column_names[idx]));
            }
        }
        return children;
    }
    public function getLabel():String
    {
        return _partitionValue;
    }
}
internal class PartitionColumnNode implements IWeaveTreeNode, IColumnReference
{
    private var _parent:PartitionValueNode;
    private var _columnName:String;

    public function get parent():PartitionValueNode { return _parent; }
    public function get columnName():String { return _columnName; }

    public function PartitionColumnNode(parent:PartitionValueNode, columnName:String)
    {
        _parent = parent;
        _columnName = columnName;
    }

    public function equals(other:IWeaveTreeNode):Boolean
    {
        var that:PartitionColumnNode = other as PartitionColumnNode;
        return !!that &&
                this.columnName == that.columnName &&
                that.parent.equals(this.parent);
    }

    public function isBranch():Boolean {return false;}
    public function hasChildBranches():Boolean {return false;}
    public function getChildren():Array {return null; }

    public function getLabel():String
    {
        return parent.parent.source.getInputColumnTitle(_columnName) +
                " (" + parent.partitionValue + ")";
    }

    public function getDataSource():IDataSource
    {
        return parent.parent.source;
    }

    public function getColumnMetadata():Object
    {
        var metadata:Object = {};

        metadata[PartitionDataTransform.PARTITION_COLUMNNAME_META] = columnName;
        metadata[PartitionDataTransform.PARTITION_VALUE_META] = parent.partitionValue;

        return metadata;
    }
}

package weave.data.Transforms
{
    import weave.api.core.ILinkableHashMap;
    import weave.api.data.ColumnMetadata;
    import weave.api.data.IAttributeColumn;
    import weave.api.data.IColumnReference;
    import weave.api.data.IDataSource;
    import weave.api.data.IWeaveTreeNode;
    import weave.api.newLinkableChild;
    import weave.api.registerLinkableChild;
    import weave.core.LinkableHashMap;
    import weave.data.AttributeColumns.DynamicColumn;
    import weave.data.AttributeColumns.FilteredColumn;
    import weave.data.AttributeColumns.ProxyColumn;
    import weave.data.DataSources.AbstractDataSource;
    import weave.data.KeySets.StringDataFilter;
    import weave.utils.VectorUtils;

    public class PartitionDataTransform extends AbstractDataSource
    {
        public static const PARTITION_VALUE_META:String = "__PartitionValue__";
        public static const PARTITION_COLUMNNAME_META:String = "__PartitionColumnName__";
        WeaveAPI.registerImplementation(IDataSource, PartitionDataTransform, "Partitioned Table");

        public const inputColumns:ILinkableHashMap = registerLinkableChild(this, new LinkableHashMap(IAttributeColumn), inputColumnsChanged);
        public const partitionColumn:DynamicColumn = newLinkableChild(this, DynamicColumn, partitionColumnChanged);

        public var layer_names:Array = [];

        public function PartitionDataTransform()
        {

        }

        public function inputColumnsChanged():void
        {
            refreshHierarchy();
            refreshAllProxyColumns();
        }

        private function partitionColumnChanged():void
        {
            var keys:Array = partitionColumn.keys;
            var layers:Object = {};
            for (var idx:int = keys.length - 1; idx >= 0; idx--)
            {
                var value:String = partitionColumn.getValueFromKey(keys[idx], String);

                layers[value] = true;
            }
            layer_names = VectorUtils.getKeys(layers);

            refreshHierarchy();
            refreshAllProxyColumns();
        }

        public function getInputColumnTitle(name:String):String
        {
            var column:IAttributeColumn = inputColumns.getObject(name) as IAttributeColumn;
            return column ? column.getMetadata(ColumnMetadata.TITLE) : '';
        }

        override protected function generateHierarchyNode(metadata:Object):IWeaveTreeNode
        {
            var transformNode:PartitionTransformNode;
            var valueNode:PartitionValueNode;
            var columnNode:PartitionColumnNode;

            transformNode = new PartitionTransformNode(this);

            if (metadata[PARTITION_VALUE_META] === undefined)
                return transformNode;

            valueNode = new PartitionValueNode(transformNode, metadata[PARTITION_VALUE_META])

            if (metadata[PARTITION_COLUMNNAME_META] === undefined)
                return valueNode;

            return new PartitionColumnNode(valueNode, metadata[PARTITION_COLUMNNAME_META]);
        }

        override public function getHierarchyRoot():IWeaveTreeNode
        {
            if (!_rootNode)
                _rootNode = new PartitionTransformNode(this);
            return _rootNode;
        }

        override protected function requestColumnFromSource(proxyColumn:ProxyColumn):void
        {

            var metadata:Object = proxyColumn.getProxyMetadata();
            var columnName:String = metadata[PARTITION_COLUMNNAME_META];
            var value:String = metadata[PARTITION_VALUE_META];

            var column:IAttributeColumn = inputColumns.getObject(columnName) as IAttributeColumn;

            var newFilteredColumn:FilteredColumn = new FilteredColumn();
            var filter:StringDataFilter = newFilteredColumn.filter.requestLocalObject(StringDataFilter, false);

            filter.column.requestLocalObjectCopy(partitionColumn);
            filter.stringValue.value = value;

            newFilteredColumn.internalDynamicColumn.requestLocalObjectCopy(column);

            proxyColumn.setInternalColumn(newFilteredColumn);
        }
    }
}

import weave.api.data.ColumnMetadata;
import weave.api.data.IColumnReference;
import weave.api.data.IDataSource;
import weave.api.data.IWeaveTreeNode;
import weave.data.Transforms.PartitionDataTransform;

internal class PartitionTransformNode implements IWeaveTreeNode
{
    private var _source:PartitionDataTransform;
    private var children:Array;

    public function get source():PartitionDataTransform { return _source; }

    public function PartitionTransformNode(source:PartitionDataTransform)
    {
        _source = source;
    }

    public function equals(other:IWeaveTreeNode):Boolean
    {
        var that:PartitionTransformNode = other as PartitionTransformNode;
        return !!that &&
                that.source === this.source;
    }

    public function isBranch():Boolean { return true; }
    public function hasChildBranches():Boolean {return true; }

    public function getChildren():Array
    {
        if (children == null)
        {
            children = [];
            var layer_names:Array = source.layer_names;
            for (var idx:int = layer_names.length - 1; idx >= 0; idx--)
            {
                var layer:String = layer_names[idx];

                children.push(new PartitionValueNode(this, layer));
            }
        }
        return children;
    }

    public function getLabel():String
    {
        return WeaveAPI.globalHashMap.getName(source);
    }

}
internal class PartitionValueNode implements IWeaveTreeNode
{
    private var _parent:PartitionTransformNode;
    private var _partitionValue:String;
    private var children:Array;

    public function get parent():PartitionTransformNode { return _parent; }
    public function get partitionValue():String { return _partitionValue; }

    public function PartitionValueNode(parent:PartitionTransformNode, value:String)
    {
        _parent = parent;
        _partitionValue = value;
    }

    public function equals(other:IWeaveTreeNode):Boolean
    {
        var that:PartitionValueNode = other as PartitionValueNode;
        return !!that &&
                this.partitionValue == that.partitionValue &&
                that.parent.equals(this.parent);
    }

    public function isBranch():Boolean { return true; }
    public function hasChildBranches():Boolean { return false; }

    public function getChildren():Array
    {
        if (children == null)
        {
            children = [];
            var column_names:Array = parent.source.inputColumns.getNames();
            for (var idx:int = column_names.length - 1; idx >= 0; idx--)
            {
                children.push(new PartitionColumnNode(this, column_names[idx]));
            }
        }
        return children;
    }
    public function getLabel():String
    {
        return _partitionValue;
    }
}
internal class PartitionColumnNode implements IWeaveTreeNode, IColumnReference
{
    private var _parent:PartitionValueNode;
    private var _columnName:String;

    public function get parent():PartitionValueNode { return _parent; }
    public function get columnName():String { return _columnName; }

    public function PartitionColumnNode(parent:PartitionValueNode, columnName:String)
    {
        _parent = parent;
        _columnName = columnName;
    }

    public function equals(other:IWeaveTreeNode):Boolean
    {
        var that:PartitionColumnNode = other as PartitionColumnNode;
        return !!that &&
                this.columnName == that.columnName &&
                that.parent.equals(this.parent);
    }

    public function isBranch():Boolean {return false;}
    public function hasChildBranches():Boolean {return false;}
    public function getChildren():Array {return null; }

    public function getLabel():String
    {
        return parent.parent.source.getInputColumnTitle(_columnName) +
                " (" + parent.partitionValue + ")";
    }

    public function getDataSource():IDataSource
    {
        return parent.parent.source;
    }

    public function getColumnMetadata():Object
    {
        var metadata:Object = {};

        metadata[PartitionDataTransform.PARTITION_COLUMNNAME_META] = columnName;
        metadata[PartitionDataTransform.PARTITION_VALUE_META] = parent.partitionValue;

        return metadata;
    }
}

fix null pointer error

fix null pointer error

Change-Id: I0e44faee096bf245825f75d76dc3b127ed8dfc64


package harayoki.app.bitmapfont.export
{
	import flash.display.BitmapData;
	import flash.display.PNGEncoderOptions;
	import flash.events.Event;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;

	import harayoki.app.data.FontData;

	import org.osflash.signals.Signal;

	public class FntExporter
	{

		private var _onResult:Signal = new Signal();
		private var _errorMessage:String;
		private var _isSuccess:Boolean = false;
		private var _canceled:Boolean = false;

		public function FntExporter()
		{
			fntFileMaker = XmlFntFileMaker.getInstance();
		}

		private var _fntFileMaker:IFntFileMaker;

		public function get fntFileMaker():IFntFileMaker
		{
			return _fntFileMaker;
		}

		public function set fntFileMaker(value:IFntFileMaker):void
		{
			_fntFileMaker = value;
		}

		public function export(fntFileName:String,fontData:FontData,bitmapData:BitmapData):void
		{
			var bytes:ByteArray;
			var data:String = _fntFileMaker.makeFntFile(fontData,bitmapData);
			trace(data);

			_errorMessage = "";
			_isSuccess = false;

			var file:File = File.desktopDirectory;
			file.browseForDirectory("select save dirctory");
			file.addEventListener(Event.SELECT,onSelectDir);
			file.addEventListener(Event.CANCEL,onCancelSelectDir);

			function clearListener():void
			{
				file.removeEventListener(Event.SELECT,onSelectDir);
				file.removeEventListener(Event.CANCEL,onCancelSelectDir);
			}

			function onSelectDir(ev:Event):void
			{
				clearListener();
				writeFntData();
			}

			function onCancelSelectDir(ev:Event):void
			{
				clearListener();
				_isSuccess = false;
				_canceled = true;
				_onResult.dispatch();
			}

			function writeFntData():void
			{
				try
				{
					var fntFile:File = file.resolvePath(fntFileName)
					var fileStream:FileStream = new FileStream();
					fileStream.open(fntFile,FileMode.WRITE);
					fileStream.writeUTFBytes(data);
					fileStream.close();

					flash.utils.setTimeout(makePngData,1);

				}
				catch(err:Error)
				{
					_errorMessage = err.message;
					_isSuccess = false;
					_onResult.dispatch();
				}
			}

			function makePngData():void
			{
				try
				{
					bytes = new ByteArray();
					var option:PNGEncoderOptions = new PNGEncoderOptions();
					bitmapData.encode(bitmapData.rect,option,bytes);
					flash.utils.setTimeout(writePngData,1);
				}
				catch(err:Error)
				{
					_errorMessage = err.message;
					_isSuccess = false;
					_onResult.dispatch();
				}

			}

			function writePngData():void
			{
				try
				{
					var pngFile:File = file.resolvePath(fontData.file)
					var fileStream:FileStream = new FileStream();
					fileStream.open(pngFile,FileMode.WRITE);
					fileStream.writeBytes(bytes);
					fileStream.close();
					flash.utils.setTimeout(finish,1);

				}
				catch(err:Error)
				{
					_errorMessage = err.message;
					_isSuccess = false;
					_onResult.dispatch();
				}
			}

			function finish():void
			{
				_isSuccess = true;
				_onResult.dispatch();
			}

		}

		public function get onResult():Signal
		{
			return _onResult;
		}

		public function get successed():Boolean
		{
			return _isSuccess;
		}

		public function get canceled():Boolean
		{
			return _canceled;
		}

		public function get errorMessage():String
		{
			return _errorMessage;
		}

	}
}

package harayoki.app.bitmapfont.export
{
	import flash.display.BitmapData;
	import flash.display.PNGEncoderOptions;
	import flash.events.Event;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.net.SharedObject;
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;

	import harayoki.app.data.FontData;

	import org.osflash.signals.Signal;

	public class FntExporter
	{
		private static const SO_KEY:String = "BitmapFOntExporter";
		private static const LAST_SAVED:String = "lastsaved";

		private var _onResult:Signal = new Signal();
		private var _errorMessage:String;
		private var _isSuccess:Boolean = false;
		private var _canceled:Boolean = false;

		public function FntExporter()
		{
			fntFileMaker = XmlFntFileMaker.getInstance();
		}

		private var _fntFileMaker:IFntFileMaker;

		public function get fntFileMaker():IFntFileMaker
		{
			return _fntFileMaker;
		}

		public function set fntFileMaker(value:IFntFileMaker):void
		{
			_fntFileMaker = value;
		}

		public function export(fntFileName:String,fontData:FontData,bitmapData:BitmapData):void
		{
			var bytes:ByteArray;
			var data:String = _fntFileMaker.makeFntFile(fontData,bitmapData);
			trace(data);

			_errorMessage = "";
			_isSuccess = false;

			var file:File;
			var so:SharedObject = SharedObject.getLocal(SO_KEY);
			var path:String = so.data[LAST_SAVED];
			if(path)
			{
				file = new File(path);
				if(!file.exists)
				{
					file = File.userDirectory;
				}
			}
			else
			{
				file = File.userDirectory;
			}

			file.browseForDirectory("select save dirctory");
			file.addEventListener(Event.SELECT,onSelectDir);
			file.addEventListener(Event.CANCEL,onCancelSelectDir);

			function clearListener():void
			{
				file.removeEventListener(Event.SELECT,onSelectDir);
				file.removeEventListener(Event.CANCEL,onCancelSelectDir);
			}

			function onSelectDir(ev:Event):void
			{
				clearListener();
				writeFntData();
			}

			function onCancelSelectDir(ev:Event):void
			{
				clearListener();
				_isSuccess = false;
				_canceled = true;
				_onResult.dispatch();
			}

			function writeFntData():void
			{
				try
				{
					var fntFile:File = file.resolvePath(fntFileName)
					var fileStream:FileStream = new FileStream();
					fileStream.open(fntFile,FileMode.WRITE);
					fileStream.writeUTFBytes(data);
					fileStream.close();

					flash.utils.setTimeout(makePngData,1);

				}
				catch(err:Error)
				{
					_errorMessage = err.message;
					_isSuccess = false;
					_onResult.dispatch();
				}
			}

			function makePngData():void
			{
				try
				{
					bytes = new ByteArray();
					var option:PNGEncoderOptions = new PNGEncoderOptions();
					bitmapData.encode(bitmapData.rect,option,bytes);
					flash.utils.setTimeout(writePngData,1);
				}
				catch(err:Error)
				{
					_errorMessage = err.message;
					_isSuccess = false;
					_onResult.dispatch();
				}

			}

			function writePngData():void
			{
				try
				{
					var pngFile:File = file.resolvePath(fontData.file)
					var fileStream:FileStream = new FileStream();
					fileStream.open(pngFile,FileMode.WRITE);
					fileStream.writeBytes(bytes);
					fileStream.close();
					flash.utils.setTimeout(finish,1);

				}
				catch(err:Error)
				{
					_errorMessage = err.message;
					_isSuccess = false;
					_onResult.dispatch();
				}
			}

			function finish():void
			{
				so.data[LAST_SAVED] = file.nativePath;
				trace(file.nativePath);
				_isSuccess = true;
				_onResult.dispatch();
			}

		}

		public function get onResult():Signal
		{
			return _onResult;
		}

		public function get successed():Boolean
		{
			return _isSuccess;
		}

		public function get canceled():Boolean
		{
			return _canceled;
		}

		public function get errorMessage():String
		{
			return _errorMessage;
		}

	}
}

save file path

save file path


package aerys.minko.type.binding
{
	import aerys.minko.render.resource.texture.TextureResource;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.compiler.Serializer;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.BindableConstant;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.BindableSampler;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Constant;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Sampler;

	/**
	 * The wrapper used to expose scene/mesh data bindings in ActionScript shaders.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class ShaderDataBindingsProxy extends DataBindingsProxy
	{
		private var _serializer		: Serializer;

		public function ShaderDataBindingsProxy(bindings		: DataBindings,
												signature		: Signature,
												signatureFlags	: uint)
		{
			super(bindings, signature, signatureFlags);

			_serializer = new Serializer();
		}

		public function getParameter(name			: String,
									 size			: uint,
									 defaultValue	: Object = null) : SFloat
		{
			if (defaultValue != null && !propertyExists(name))
			{
				var constantValue : Vector.<Number> = new Vector.<Number>();

				_serializer.serializeKnownLength(defaultValue, constantValue, 0, size);

				return new SFloat(new Constant(constantValue));
			}

			return new SFloat(new BindableConstant(name, size));
		}

		public function getTextureParameter(bindingName		: String,
											filter			: uint				= 1,
											mipmap			: uint				= 0,
											wrapping		: uint				= 1,
											dimension		: uint				= 0,
											format          : uint              = 0,
											defaultValue	: TextureResource	= null) : SFloat
		{
			if (defaultValue != null && !propertyExists(bindingName))
				return new SFloat(new Sampler(defaultValue, filter, mipmap, wrapping, dimension, format));

			return new SFloat(
				new BindableSampler(bindingName, filter, mipmap, wrapping, dimension, format)
			);
		}
	}
}

package aerys.minko.type.binding
{
	import aerys.minko.render.resource.texture.TextureResource;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.compiler.Serializer;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.BindableConstant;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.BindableSampler;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Constant;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Sampler;

	/**
	 * The wrapper used to expose scene/mesh data bindings in ActionScript shaders.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class ShaderDataBindingsProxy extends DataBindingsProxy
	{
		public function ShaderDataBindingsProxy(bindings		: DataBindings,
												signature		: Signature,
												signatureFlags	: uint)
		{
			super(bindings, signature, signatureFlags);
		}

		public function getParameter(name			: String,
									 size			: uint,
									 defaultValue	: Object = null) : SFloat
		{
			if (defaultValue != null && !propertyExists(name))
			{
				var constantValue 	: Vector.<Number> 	= new Vector.<Number>();
				var serializer 		: Serializer		= new Serializer();

				serializer.serializeKnownLength(defaultValue, constantValue, 0, size);

				return new SFloat(new Constant(constantValue));
			}

			return new SFloat(new BindableConstant(name, size));
		}

		public function getTextureParameter(bindingName		: String,
											filter			: uint				= 1,
											mipmap			: uint				= 0,
											wrapping		: uint				= 1,
											dimension		: uint				= 0,
											format          : uint              = 0,
											defaultValue	: TextureResource	= null) : SFloat
		{
			if (defaultValue != null && !propertyExists(bindingName))
				return new SFloat(new Sampler(defaultValue, filter, mipmap, wrapping, dimension, format));

			return new SFloat(
				new BindableSampler(bindingName, filter, mipmap, wrapping, dimension, format)
			);
		}
	}
}

use a local variable for Serializer to avoid useless memory consumption


package com.threerings.ezgame.client {

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;

import flash.events.Event;

import flash.utils.Dictionary;

import mx.containers.Canvas;
import mx.containers.VBox;

import mx.core.Container;
import mx.core.IChildList;

import mx.utils.DisplayUtil;

import com.threerings.util.MediaContainer;

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

import com.threerings.ezgame.data.EZGameConfig;
import com.threerings.ezgame.data.EZGameObject;

public class EZGamePanel extends VBox
    implements PlaceView
{
    /** The game object backend. */
    public var backend :GameControlBackend;

    public function EZGamePanel (ctx :CrowdContext, ctrl :EZGameController)
    {
        _ctx = ctx;
        _ctrl = ctrl;
    }

    // from PlaceView
    public function willEnterPlace (plobj :PlaceObject) :void
    {
        var cfg :EZGameConfig = (_ctrl.getPlaceConfig() as EZGameConfig);

        _ezObj = (plobj as EZGameObject);
        backend = createBackend();

        _gameView = new GameContainer(cfg.configData); // TODO?
        backend.setSharedEvents(
            Loader(_gameView.getMediaContainer().getMedia()).
            contentLoaderInfo.sharedEvents);
        backend.setContainer(_gameView);
        addChild(_gameView);
    }

    // from PlaceView
    public function didLeavePlace (plobj :PlaceObject) :void
    {
        _gameView.getMediaContainer().shutdown(true);
        removeChild(_gameView);

        backend.shutdown();
    }

    /**
     * Creates the backend object that will handle requests from user code.
     */
    protected function createBackend () :GameControlBackend
    {
        return new GameControlBackend(_ctx, _ezObj);
    }

    protected var _ctx :CrowdContext;
    protected var _ctrl :EZGameController;

    protected var _gameView :GameContainer;

    protected var _ezObj :EZGameObject;
}
}

package com.threerings.ezgame.client {

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;

import flash.events.Event;

import flash.utils.Dictionary;

import mx.containers.Canvas;

import mx.core.Container;
import mx.core.IChildList;

import mx.utils.DisplayUtil;

import com.threerings.util.MediaContainer;

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

import com.threerings.ezgame.data.EZGameConfig;
import com.threerings.ezgame.data.EZGameObject;

public class EZGamePanel extends Canvas
    implements PlaceView
{
    /** The game object backend. */
    public var backend :GameControlBackend;

    public function EZGamePanel (ctx :CrowdContext, ctrl :EZGameController)
    {
        _ctx = ctx;
        _ctrl = ctrl;
    }

    // from PlaceView
    public function willEnterPlace (plobj :PlaceObject) :void
    {
        var cfg :EZGameConfig = (_ctrl.getPlaceConfig() as EZGameConfig);

        _ezObj = (plobj as EZGameObject);
        backend = createBackend();

        _gameView = new GameContainer(cfg.configData); // TODO?
        _gameView.percentWidth = 100;
        _gameView.percentHeight = 100;
        backend.setSharedEvents(
            Loader(_gameView.getMediaContainer().getMedia()).
            contentLoaderInfo.sharedEvents);
        backend.setContainer(_gameView);
        addChild(_gameView);
    }

    // from PlaceView
    public function didLeavePlace (plobj :PlaceObject) :void
    {
        _gameView.getMediaContainer().shutdown(true);
        removeChild(_gameView);

        backend.shutdown();
    }

    /**
     * Creates the backend object that will handle requests from user code.
     */
    protected function createBackend () :GameControlBackend
    {
        return new GameControlBackend(_ctx, _ezObj);
    }

    protected var _ctx :CrowdContext;
    protected var _ctrl :EZGameController;

    protected var _gameView :GameContainer;

    protected var _ezObj :EZGameObject;
}
}

Extend from Canvas so that the chat overlay scrollbar works.

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


/*****************************************************
*
*  Copyright 2009 Adobe Systems Incorporated.  All Rights Reserved.
*
*****************************************************
*  The contents of this file are subject to the Mozilla Public License
*  Version 1.1 (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.mozilla.org/MPL/
*
*  Software distributed under the License is distributed on an "AS IS"
*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
*  License for the specific language governing rights and limitations
*  under the License.
*
*
*  The Initial Developer of the Original Code is Adobe Systems Incorporated.
*  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
*  Incorporated. All Rights Reserved.
*
*  Contributor(s): Eyewonder, LLC
*
*****************************************************/
package org.osmf.vast.loader
{
	import flash.events.EventDispatcher;

	import org.osmf.utils.HTTPLoader;
	import org.osmf.vast.model.VASTDataObject;
	import org.osmf.vast.model.VASTDocument;
	import org.osmf.vast.parser.base.VAST2TrackingData;

	CONFIG::LOGGING
	{
	import org.osmf.logging.Logger;
	import org.osmf.logging.Log;
	}

	[Event("processed")]
	[Event("processingFailed")]

	internal class VASTDocumentProcessor extends EventDispatcher
	{
		/**
		 * @private Exposes the same interface as VAST1DocumentProcessor and VAST2DocumentProcessor.
		 * Determines which processor to use based on the VAST version stored in the VASTDataObject.
		 */
		public function VASTDocumentProcessor(maxNumWrapperRedirects:Number, httpLoader:HTTPLoader)
		{
			super();

			this.maxNumWrapperRedirects = maxNumWrapperRedirects;
			this.httpLoader = httpLoader;
		}
		/**
		 * @private
		 */
		public function processVASTDocument(documentContents:String, trackingData:VAST2TrackingData = null):void
		{
			var processingFailed:Boolean = false;
			var vastDocument:VASTDocument = null;
			var documentXML:XML = null;

			try
			{
				documentXML = new XML(documentContents);
			}
			catch (error:TypeError)
			{
				processingFailed = true;
			}

			if (documentXML != null)
			{
				var vastVersion:Number;
				if(documentXML.localName() == VAST_1_ROOT){
					vastVersion = VASTDataObject.VERSION_1_0;
				}else if(documentXML.localName() == VAST_2_ROOT){
					vastVersion = documentXML.@version;
				}

				switch(vastVersion)
				{
					case VASTDataObject.VERSION_1_0:
						var vast1DocumentProcessor:VAST1DocumentProcessor = new VAST1DocumentProcessor(maxNumWrapperRedirects, httpLoader);
						vast1DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
						vast1DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
						vast1DocumentProcessor.processVASTDocument(documentContents);
					break;
					case VASTDataObject.VERSION_2_0:
						var vast2DocumentProcessor:VAST2DocumentProcessor = new VAST2DocumentProcessor(maxNumWrapperRedirects, httpLoader);
						vast2DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
						vast2DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
						vast2DocumentProcessor.processVASTDocument(documentContents, trackingData);
						break;
					case VASTDataObject.VERSION_3_0:
						var vast3DocumentProcessor:VAST3DocumentProcessor = new VAST3DocumentProcessor(maxNumWrapperRedirects, httpLoader);
						vast3DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneVast3DocumentProcessorEvent);
						vast3DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneVast3DocumentProcessorEvent);
						vast3DocumentProcessor.processVASTDocument(documentContents, trackingData);
					break;
					default:
						processingFailed = true;
					break;
				}
			}
			if (processingFailed)
			{
				CONFIG::LOGGING
				{
					logger.debug("[VAST] Processing failed for document with contents: " + documentContents);
				}

				dispatchEvent(new VASTDocumentProcessedEvent(VASTDocumentProcessedEvent.PROCESSING_FAILED));
			}
		}

		private function cloneDocumentProcessorEvent(event:VASTDocumentProcessedEvent):void
		{
			var parser:EventDispatcher = event.target as EventDispatcher;
			parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
			parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
			dispatchEvent(event.clone());
		}

		private function cloneVast3DocumentProcessorEvent(event:VASTDocumentProcessedEvent):void
		{
			if (event.target is VAST3DocumentProcessor)
			{
				var parser:EventDispatcher = event.target as EventDispatcher;
				parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
				parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
				dispatchEvent(event.clone());
			}

		}

		private var maxNumWrapperRedirects:Number;
		private var httpLoader:HTTPLoader;

		private static const VAST_1_ROOT:String = "VideoAdServingTemplate";
		private static const VAST_2_ROOT:String = "VAST";

		CONFIG::LOGGING
		private static const logger:Logger = Log.getLogger("org.osmf.vast.loader.VASTDocumentProcessor");
	}
}

/*****************************************************
*
*  Copyright 2009 Adobe Systems Incorporated.  All Rights Reserved.
*
*****************************************************
*  The contents of this file are subject to the Mozilla Public License
*  Version 1.1 (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.mozilla.org/MPL/
*
*  Software distributed under the License is distributed on an "AS IS"
*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
*  License for the specific language governing rights and limitations
*  under the License.
*
*
*  The Initial Developer of the Original Code is Adobe Systems Incorporated.
*  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
*  Incorporated. All Rights Reserved.
*
*  Contributor(s): Eyewonder, LLC
*
*****************************************************/
package org.osmf.vast.loader
{
	import flash.events.EventDispatcher;

	import org.osmf.utils.HTTPLoader;
	import org.osmf.vast.model.VASTDataObject;
	import org.osmf.vast.model.VASTDocument;
	import org.osmf.vast.parser.base.VAST2TrackingData;

	CONFIG::LOGGING
	{
	import org.osmf.logging.Logger;
	import org.osmf.logging.Log;
	}

	[Event("processed")]
	[Event("processingFailed")]

	internal class VASTDocumentProcessor extends EventDispatcher
	{
		/**
		 * @private Exposes the same interface as VAST1DocumentProcessor and VAST2DocumentProcessor.
		 * Determines which processor to use based on the VAST version stored in the VASTDataObject.
		 */
		public function VASTDocumentProcessor(maxNumWrapperRedirects:Number, httpLoader:HTTPLoader)
		{
			super();

			this.maxNumWrapperRedirects = maxNumWrapperRedirects;
			this.httpLoader = httpLoader;
		}
		/**
		 * @private
		 */
		public function processVASTDocument(documentContents:String, trackingData:VAST2TrackingData = null):void
		{
			var processingFailed:Boolean = false;
			var vastDocument:VASTDocument = null;
			var documentXML:XML = null;

			try
			{
				documentXML = new XML(documentContents);
			}
			catch (error:TypeError)
			{
				processingFailed = true;
			}

			if (documentXML != null)
			{
				var vastVersion:Number;
				if(documentXML.localName() == VAST_1_ROOT){
					vastVersion = VASTDataObject.VERSION_1_0;
				}else if(documentXML.localName() == VAST_2_ROOT){
					vastVersion = documentXML.@version;
					if ( documentXML.@version == "2.0.1" ) {
						vastVersion = VASTDataObject.VERSION_2_0;
					}
				}

				switch(vastVersion)
				{
					case VASTDataObject.VERSION_1_0:
						var vast1DocumentProcessor:VAST1DocumentProcessor = new VAST1DocumentProcessor(maxNumWrapperRedirects, httpLoader);
						vast1DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
						vast1DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
						vast1DocumentProcessor.processVASTDocument(documentContents);
					break;
					case VASTDataObject.VERSION_2_0:
						var vast2DocumentProcessor:VAST2DocumentProcessor = new VAST2DocumentProcessor(maxNumWrapperRedirects, httpLoader);
						vast2DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
						vast2DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
						vast2DocumentProcessor.processVASTDocument(documentContents, trackingData);
						break;
					case VASTDataObject.VERSION_3_0:
						var vast3DocumentProcessor:VAST3DocumentProcessor = new VAST3DocumentProcessor(maxNumWrapperRedirects, httpLoader);
						vast3DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneVast3DocumentProcessorEvent);
						vast3DocumentProcessor.addEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneVast3DocumentProcessorEvent);
						vast3DocumentProcessor.processVASTDocument(documentContents, trackingData);
					break;
					default:
						processingFailed = true;
					break;
				}
			}
			if (processingFailed)
			{
				CONFIG::LOGGING
				{
					logger.debug("[VAST] Processing failed for document with contents: " + documentContents);
				}

				dispatchEvent(new VASTDocumentProcessedEvent(VASTDocumentProcessedEvent.PROCESSING_FAILED));
			}
		}

		private function cloneDocumentProcessorEvent(event:VASTDocumentProcessedEvent):void
		{
			var parser:EventDispatcher = event.target as EventDispatcher;
			parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
			parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
			dispatchEvent(event.clone());
		}

		private function cloneVast3DocumentProcessorEvent(event:VASTDocumentProcessedEvent):void
		{
			if (event.target is VAST3DocumentProcessor)
			{
				var parser:EventDispatcher = event.target as EventDispatcher;
				parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSED, cloneDocumentProcessorEvent);
				parser.removeEventListener(VASTDocumentProcessedEvent.PROCESSING_FAILED, cloneDocumentProcessorEvent);
				dispatchEvent(event.clone());
			}

		}

		private var maxNumWrapperRedirects:Number;
		private var httpLoader:HTTPLoader;

		private static const VAST_1_ROOT:String = "VideoAdServingTemplate";
		private static const VAST_2_ROOT:String = "VAST";

		CONFIG::LOGGING
		private static const logger:Logger = Log.getLogger("org.osmf.vast.loader.VASTDocumentProcessor");
	}
}

Support VAST 2.0.1

Support VAST 2.0.1