package {

import com.marstonstudio.crossusermedia.encoder.Encoder;

import flash.display.DisplayObjectContainer;
import flash.display.Sprite;

import flash.events.Event;
import flash.utils.ByteArray;
import flash.utils.getTimer;

import mx.core.ByteArrayAsset;
import mx.core.UIComponent;

import org.flexunit.asserts.assertNotNull;

import org.flexunit.asserts.assertTrue;
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;

public class TestApp {

        public function TestApp() {}

        [Embed(source="../resources/audio.raw",mimeType="application/octet-stream")]
        public var AudioPcm:Class;

        public var container:DisplayObjectContainer;

        [Before(async,ui)]
        public function initialize():void {
            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            trace("test::start " + new Date().toLocaleString());
            trace("");
            container = new UIComponent();
            Async.proceedOnEvent(this, container, Event.ADDED_TO_STAGE, 100);
            UIImpersonator.addChild(container);
        }

        [Test(description="Load audio asset")]
        public function testAudioLoad():void {

            var audioPcmAsset:ByteArrayAsset = new AudioPcm();
            var inputBytesAvailable:int = audioPcmAsset.bytesAvailable;
            assertTrue("embedded bytesAvailable", inputBytesAvailable > 0);

            assertNotNull(container.stage);
            var rootSprite:Sprite = new Sprite();
            container.addChild(rootSprite);

            // ffmpeg -loglevel debug -f f32be -acodec pcm_f32be -ar 16000 -ac 1 -channel_layout mono -i audio.raw -b:a 32000 -f mp4 -acodec aac audio.mp4

            /*
             ffmpeg version 3.0 Copyright (c) 2000-2016 the FFmpeg developers
             built with Apple LLVM version 7.0.2 (clang-700.1.81)
             configuration: --prefix=/usr/local/Cellar/ffmpeg/3.0 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libxvid --enable-libfontconfig --enable-libfreetype --enable-libtheora --enable-libvorbis --enable-librtmp --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libass --enable-libspeex --enable-libfdk-aac --enable-libopus --enable-libx265 --enable-libdcadec --enable-nonfree --enable-vda
             libavutil      55. 17.103 / 55. 17.103
             libavcodec     57. 24.102 / 57. 24.102
             libavformat    57. 25.100 / 57. 25.100
             libavdevice    57.  0.101 / 57.  0.101
             libavfilter     6. 31.100 /  6. 31.100
             libavresample   3.  0.  0 /  3.  0.  0
             libswscale      4.  0.100 /  4.  0.100
             libswresample   2.  0.101 /  2.  0.101
             libpostproc    54.  0.100 / 54.  0.100
             Splitting the commandline.
             Reading option '-loglevel' ... matched as option 'loglevel' (set logging level) with argument 'debug'.
             Reading option '-f' ... matched as option 'f' (force format) with argument 'f32be'.
             Reading option '-acodec' ... matched as option 'acodec' (force audio codec ('copy' to copy stream)) with argument 'pcm_f32be'.
             Reading option '-ar' ... matched as option 'ar' (set audio sampling rate (in Hz)) with argument '16000'.
             Reading option '-ac' ... matched as option 'ac' (set number of audio channels) with argument '1'.
             Reading option '-channel_layout' ... matched as option 'channel_layout' (set channel layout) with argument 'mono'.
             Reading option '-i' ... matched as input file with argument 'audio.raw'.
             Reading option '-b:a' ... matched as option 'b' (video bitrate (please use -b:v)) with argument '32000'.
             Reading option '-f' ... matched as option 'f' (force format) with argument 'mp4'.
             Reading option '-acodec' ... matched as option 'acodec' (force audio codec ('copy' to copy stream)) with argument 'aac'.
             Reading option 'audio.mp4' ... matched as output file.
             Finished splitting the commandline.
             Parsing a group of options: global .
             Applying option loglevel (set logging level) with argument debug.
             Successfully parsed a group of options.
             Parsing a group of options: input file audio.raw.
             Applying option f (force format) with argument f32be.
             Applying option acodec (force audio codec ('copy' to copy stream)) with argument pcm_f32be.
             Applying option ar (set audio sampling rate (in Hz)) with argument 16000.
             Applying option ac (set number of audio channels) with argument 1.
             Applying option channel_layout (set channel layout) with argument mono.
             Successfully parsed a group of options.
             Opening an input file: audio.raw.
             [file @ 0x7f97b9d17360] Setting default whitelist 'file'
             [f32be @ 0x7f97ba00fa00] Before avformat_find_stream_info() pos: 0 bytes read:32768 seeks:0
             [f32be @ 0x7f97ba00fa00] All info found
             [f32be @ 0x7f97ba00fa00] Estimating duration from bitrate, this may be inaccurate
             [f32be @ 0x7f97ba00fa00] After avformat_find_stream_info() pos: 204800 bytes read:229376 seeks:0 frames:50
             Input #0, f32be, from 'audio.raw':
             Duration: 00:00:04.32, bitrate: 512 kb/s
             Stream #0:0, 50, 1/16000: Audio: pcm_f32be, 16000 Hz, mono, flt, 512 kb/s
             Successfully opened the file.
             Parsing a group of options: output file audio.mp4.
             Applying option b:a (video bitrate (please use -b:v)) with argument 32000.
             Applying option f (force format) with argument mp4.
             Applying option acodec (force audio codec ('copy' to copy stream)) with argument aac.
             Successfully parsed a group of options.
             Opening an output file: audio.mp4.
             [file @ 0x7f97b9d17dc0] Setting default whitelist 'file'
             Successfully opened the file.
             detected 8 logical cores
             [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'time_base' to value '1/16000'
             [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'sample_rate' to value '16000'
             [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'sample_fmt' to value 'flt'
             [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'channel_layout' to value '0x4'
             [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] tb:1/16000 samplefmt:flt samplerate:16000 chlayout:0x4
             [audio format for output stream 0:0 @ 0x7f97b9d1bb20] Setting 'sample_fmts' to value 'fltp'
             [audio format for output stream 0:0 @ 0x7f97b9d1bb20] Setting 'sample_rates' to value '96000|88200|64000|48000|44100|32000|24000|22050|16000|12000|11025|8000|7350'
             [audio format for output stream 0:0 @ 0x7f97b9d1bb20] auto-inserting filter 'auto-inserted resampler 0' between the filter 'Parsed_anull_0' and the filter 'audio format for output stream 0:0'
             [AVFilterGraph @ 0x7f97b9d16f40] query_formats: 4 queried, 6 merged, 3 already done, 0 delayed
             [auto-inserted resampler 0 @ 0x7f97b9d1c260] [SWR @ 0x7f97ba05fc00] Using fltp internally between filters
             [auto-inserted resampler 0 @ 0x7f97b9d1c260] ch:1 chl:mono fmt:flt r:16000Hz -> ch:1 chl:mono fmt:fltp r:16000Hz
             Output #0, mp4, to 'audio.mp4':
             Metadata:
             encoder         : Lavf57.25.100
             Stream #0:0, 0, 1/16000: Audio: aac (LC) ([64][0][0][0] / 0x0040), 16000 Hz, mono, fltp, 32 kb/s
             Metadata:
             encoder         : Lavc57.24.102 aac
             Stream mapping:
             Stream #0:0 -> #0:0 (pcm_f32be (native) -> aac (native))
             Press [q] to stop, [?] for help
             cur_dts is invalid (this is harmless if it occurs once at the start per stream)
             Last message repeated 1 times
             [output stream 0:0 @ 0x7f97b9d1b960] EOF on sink link output stream 0:0:default.
             No more output streams to write to, finishing.
             [aac @ 0x7f97ba057000] Trying to remove 512 more samples than there are in the queue
             size=      18kB time=00:00:04.35 bitrate=  34.7kbits/s speed=64.1x
             video:0kB audio:17kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 5.758254%
             Input file #0 (audio.raw):
             Input stream #0:0 (audio): 68 packets read (276480 bytes); 68 frames decoded (69120 samples);
             Total: 68 packets (276480 bytes) demuxed
             Output file #0 (audio.mp4):
             Output stream #0:0 (audio): 68 frames encoded (69120 samples); 69 packets muxed (17870 bytes);
             Total: 69 packets (17870 bytes) muxed
             68 frames successfully decoded, 0 decoding errors
             [AVIOContext @ 0x7f97b9d1b260] Statistics: 30 seeks, 92 writeouts
             [aac @ 0x7f97ba057000] Qavg: 107.061
             [AVIOContext @ 0x7f97b9d17400] Statistics: 276480 bytes read, 0 seeks
             */

            // audio.raw = 276480 bytes, audio.mp4 = 18899 bytes

            const inputCodec:String = 'pcm_f32be';
            const inputSampleRate:int = 16000;
            const inputChannels:int = 1;
            const outputCodec:String = 'aac';
            const outputFormat:String = 'mp4';
            const outputSampleRate:int = 16000;
            const outputChannels:int = 1;
            const outputBitRate:int = 32000;

            var encoder:Encoder = new Encoder(rootSprite);
            encoder.init(inputCodec, inputSampleRate, inputChannels, outputCodec, outputFormat, outputSampleRate, outputChannels, outputBitRate);
            encoder.load(audioPcmAsset);
            var output:ByteArray = encoder.flush();
            var outputBytesAvailable:int = output.bytesAvailable;

            var encoderOutputFormat:String = encoder.getOutputFormat();
            var encoderOutputSampleRate:int = encoder.getOutputSampleRate();
            var encoderOutputLength:int = encoder.getOutputLength();
            var compressionRatio:Number = Math.round(1000 * outputBytesAvailable / inputBytesAvailable) / 10;

            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            trace("inputBytesAvailable = " + inputBytesAvailable);
            trace("outputBytesAvailable = " + outputBytesAvailable);
            trace("expected output filesize 6.8% of input, actual filesize:" + compressionRatio + "%");
            trace("encoder.getOutputFormat() = " + encoderOutputFormat);
            trace("encoder.getOutputSampleRate() = " + encoderOutputSampleRate);
            trace("encoder.getOutputLength() = " + encoderOutputLength);
            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            sleep(1000);

            assertTrue("outputFormat set to " + outputFormat, encoderOutputFormat == outputFormat);
            assertTrue("outputSampleRate set to " + outputSampleRate, encoderOutputSampleRate == outputSampleRate);
            assertTrue("encoder.getOutputLength() > 0", encoderOutputLength > 0);
            assertTrue("outputBytesAvailable > 0", outputBytesAvailable > 0);
            assertTrue("outputBytesAvailable = encoder.getOutputLength()", outputBytesAvailable == encoderOutputLength);
            assertTrue("outputBytesAvailable * 10 < inputBytesAvailable", outputBytesAvailable * 10 < inputBytesAvailable);
            assertTrue("outputBytesAvailable * 20 > inputBytesAvailable", outputBytesAvailable * 20 > inputBytesAvailable);

            encoder.dispose(0);
        }

        [After(ui)]
        public function finalize():void {
            UIImpersonator.removeAllChildren();
            container = null;

            trace("");
            trace("test::complete " + new Date().toLocaleString());
            trace("=======================================================");
            sleep(1000);
        }

        private function sleep(ms:int):void {
            var init:int = getTimer();
            while(true) {
                if(getTimer() - init >= ms) {
                    break;
                }
            }
        }

    }
}

package {

import com.marstonstudio.crossusermedia.encoder.Encoder;

import flash.display.DisplayObjectContainer;
import flash.display.Sprite;

import flash.events.Event;
import flash.utils.ByteArray;
import flash.utils.getTimer;

import mx.core.ByteArrayAsset;
import mx.core.UIComponent;

import org.flexunit.asserts.assertNotNull;

import org.flexunit.asserts.assertTrue;
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;

// ffmpeg -loglevel debug -f f32be -acodec pcm_f32be -ar 16000 -ac 1 -channel_layout mono -i audio.raw -b:a 32000 -f mp4 -acodec aac audio.mp4
// audio.raw = 276480 bytes, audio.mp4 = 18899 bytes

/*
 ffmpeg version 3.0 Copyright (c) 2000-2016 the FFmpeg developers
 built with Apple LLVM version 7.0.2 (clang-700.1.81)
 configuration: --prefix=/usr/local/Cellar/ffmpeg/3.0 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libxvid --enable-libfontconfig --enable-libfreetype --enable-libtheora --enable-libvorbis --enable-librtmp --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libass --enable-libspeex --enable-libfdk-aac --enable-libopus --enable-libx265 --enable-libdcadec --enable-nonfree --enable-vda
 libavutil      55. 17.103 / 55. 17.103
 libavcodec     57. 24.102 / 57. 24.102
 libavformat    57. 25.100 / 57. 25.100
 libavdevice    57.  0.101 / 57.  0.101
 libavfilter     6. 31.100 /  6. 31.100
 libavresample   3.  0.  0 /  3.  0.  0
 libswscale      4.  0.100 /  4.  0.100
 libswresample   2.  0.101 /  2.  0.101
 libpostproc    54.  0.100 / 54.  0.100
 Splitting the commandline.
 Reading option '-loglevel' ... matched as option 'loglevel' (set logging level) with argument 'debug'.
 Reading option '-f' ... matched as option 'f' (force format) with argument 'f32be'.
 Reading option '-acodec' ... matched as option 'acodec' (force audio codec ('copy' to copy stream)) with argument 'pcm_f32be'.
 Reading option '-ar' ... matched as option 'ar' (set audio sampling rate (in Hz)) with argument '16000'.
 Reading option '-ac' ... matched as option 'ac' (set number of audio channels) with argument '1'.
 Reading option '-channel_layout' ... matched as option 'channel_layout' (set channel layout) with argument 'mono'.
 Reading option '-i' ... matched as input file with argument 'audio.raw'.
 Reading option '-b:a' ... matched as option 'b' (video bitrate (please use -b:v)) with argument '32000'.
 Reading option '-f' ... matched as option 'f' (force format) with argument 'mp4'.
 Reading option '-acodec' ... matched as option 'acodec' (force audio codec ('copy' to copy stream)) with argument 'aac'.
 Reading option 'audio.mp4' ... matched as output file.
 Finished splitting the commandline.
 Parsing a group of options: global .
 Applying option loglevel (set logging level) with argument debug.
 Successfully parsed a group of options.
 Parsing a group of options: input file audio.raw.
 Applying option f (force format) with argument f32be.
 Applying option acodec (force audio codec ('copy' to copy stream)) with argument pcm_f32be.
 Applying option ar (set audio sampling rate (in Hz)) with argument 16000.
 Applying option ac (set number of audio channels) with argument 1.
 Applying option channel_layout (set channel layout) with argument mono.
 Successfully parsed a group of options.
 Opening an input file: audio.raw.
 [file @ 0x7f97b9d17360] Setting default whitelist 'file'
 [f32be @ 0x7f97ba00fa00] Before avformat_find_stream_info() pos: 0 bytes read:32768 seeks:0
 [f32be @ 0x7f97ba00fa00] All info found
 [f32be @ 0x7f97ba00fa00] Estimating duration from bitrate, this may be inaccurate
 [f32be @ 0x7f97ba00fa00] After avformat_find_stream_info() pos: 204800 bytes read:229376 seeks:0 frames:50
 Input #0, f32be, from 'audio.raw':
 Duration: 00:00:04.32, bitrate: 512 kb/s
 Stream #0:0, 50, 1/16000: Audio: pcm_f32be, 16000 Hz, mono, flt, 512 kb/s
 Successfully opened the file.
 Parsing a group of options: output file audio.mp4.
 Applying option b:a (video bitrate (please use -b:v)) with argument 32000.
 Applying option f (force format) with argument mp4.
 Applying option acodec (force audio codec ('copy' to copy stream)) with argument aac.
 Successfully parsed a group of options.
 Opening an output file: audio.mp4.
 [file @ 0x7f97b9d17dc0] Setting default whitelist 'file'
 Successfully opened the file.
 detected 8 logical cores
 [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'time_base' to value '1/16000'
 [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'sample_rate' to value '16000'
 [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'sample_fmt' to value 'flt'
 [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] Setting 'channel_layout' to value '0x4'
 [graph 0 input from stream 0:0 @ 0x7f97b9d1b640] tb:1/16000 samplefmt:flt samplerate:16000 chlayout:0x4
 [audio format for output stream 0:0 @ 0x7f97b9d1bb20] Setting 'sample_fmts' to value 'fltp'
 [audio format for output stream 0:0 @ 0x7f97b9d1bb20] Setting 'sample_rates' to value '96000|88200|64000|48000|44100|32000|24000|22050|16000|12000|11025|8000|7350'
 [audio format for output stream 0:0 @ 0x7f97b9d1bb20] auto-inserting filter 'auto-inserted resampler 0' between the filter 'Parsed_anull_0' and the filter 'audio format for output stream 0:0'
 [AVFilterGraph @ 0x7f97b9d16f40] query_formats: 4 queried, 6 merged, 3 already done, 0 delayed
 [auto-inserted resampler 0 @ 0x7f97b9d1c260] [SWR @ 0x7f97ba05fc00] Using fltp internally between filters
 [auto-inserted resampler 0 @ 0x7f97b9d1c260] ch:1 chl:mono fmt:flt r:16000Hz -> ch:1 chl:mono fmt:fltp r:16000Hz
 Output #0, mp4, to 'audio.mp4':
 Metadata:
 encoder         : Lavf57.25.100
 Stream #0:0, 0, 1/16000: Audio: aac (LC) ([64][0][0][0] / 0x0040), 16000 Hz, mono, fltp, 32 kb/s
 Metadata:
 encoder         : Lavc57.24.102 aac
 Stream mapping:
 Stream #0:0 -> #0:0 (pcm_f32be (native) -> aac (native))
 Press [q] to stop, [?] for help
 cur_dts is invalid (this is harmless if it occurs once at the start per stream)
 Last message repeated 1 times
 [output stream 0:0 @ 0x7f97b9d1b960] EOF on sink link output stream 0:0:default.
 No more output streams to write to, finishing.
 [aac @ 0x7f97ba057000] Trying to remove 512 more samples than there are in the queue
 size=      18kB time=00:00:04.35 bitrate=  34.7kbits/s speed=64.1x
 video:0kB audio:17kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 5.758254%
 Input file #0 (audio.raw):
 Input stream #0:0 (audio): 68 packets read (276480 bytes); 68 frames decoded (69120 samples);
 Total: 68 packets (276480 bytes) demuxed
 Output file #0 (audio.mp4):
 Output stream #0:0 (audio): 68 frames encoded (69120 samples); 69 packets muxed (17870 bytes);
 Total: 69 packets (17870 bytes) muxed
 68 frames successfully decoded, 0 decoding errors
 [AVIOContext @ 0x7f97b9d1b260] Statistics: 30 seeks, 92 writeouts
 [aac @ 0x7f97ba057000] Qavg: 107.061
 [AVIOContext @ 0x7f97b9d17400] Statistics: 276480 bytes read, 0 seeks
 */

public class TestApp {

        public function TestApp() {}

        [Embed(source="../resources/audio.raw",mimeType="application/octet-stream")]
        public var AudioPcm:Class;

        public var container:DisplayObjectContainer;

        [Before(async,ui)]
        public function initialize():void {
            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            trace("test::start " + new Date().toLocaleString());
            trace("");
            container = new UIComponent();
            Async.proceedOnEvent(this, container, Event.ADDED_TO_STAGE, 100);
            UIImpersonator.addChild(container);
        }

        [Test(description="Load audio asset")]
        public function testAudioLoad():void {

            var audioPcmAsset:ByteArrayAsset = new AudioPcm();
            var inputBytesAvailable:int = audioPcmAsset.bytesAvailable;
            assertTrue("embedded bytesAvailable", inputBytesAvailable > 0);

            assertNotNull(container.stage);
            var rootSprite:Sprite = new Sprite();
            container.addChild(rootSprite);

            const inputCodec:String = 'pcm_f32be';
            const inputSampleRate:int = 16000;
            const inputChannels:int = 1;
            const outputCodec:String = 'aac';
            const outputFormat:String = 'mp4';
            const outputSampleRate:int = 16000;
            const outputChannels:int = 1;
            const outputBitRate:int = 32000;

            var encoder:Encoder = new Encoder(rootSprite);
            encoder.init(inputCodec, inputSampleRate, inputChannels, outputCodec, outputFormat, outputSampleRate, outputChannels, outputBitRate);
            encoder.load(audioPcmAsset);
            var output:ByteArray = encoder.flush();
            var outputBytesAvailable:int = output.bytesAvailable;

            var encoderOutputFormat:String = encoder.getOutputFormat();
            var encoderOutputSampleRate:int = encoder.getOutputSampleRate();
            var encoderOutputLength:int = encoder.getOutputLength();
            var compressionRatio:Number = Math.round(1000 * outputBytesAvailable / inputBytesAvailable) / 10;

            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            trace("inputBytesAvailable = " + inputBytesAvailable);
            trace("outputBytesAvailable = " + outputBytesAvailable);
            trace("expected output filesize 6.8% of input, actual filesize:" + compressionRatio + "%");
            trace("encoder.getOutputFormat() = " + encoderOutputFormat);
            trace("encoder.getOutputSampleRate() = " + encoderOutputSampleRate);
            trace("encoder.getOutputLength() = " + encoderOutputLength);
            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            sleep(1000);

            assertTrue("outputFormat set to " + outputFormat, encoderOutputFormat == outputFormat);
            assertTrue("outputSampleRate set to " + outputSampleRate, encoderOutputSampleRate == outputSampleRate);
            assertTrue("encoder.getOutputLength() > 0", encoderOutputLength > 0);
            assertTrue("outputBytesAvailable > 0", outputBytesAvailable > 0);
            assertTrue("outputBytesAvailable = encoder.getOutputLength()", outputBytesAvailable == encoderOutputLength);
            assertTrue("outputBytesAvailable * 10 < inputBytesAvailable", outputBytesAvailable * 10 < inputBytesAvailable);
            assertTrue("outputBytesAvailable * 20 > inputBytesAvailable", outputBytesAvailable * 20 > inputBytesAvailable);

            encoder.dispose(0);
        }

        [After(ui)]
        public function finalize():void {
            UIImpersonator.removeAllChildren();
            container = null;

            trace("");
            trace("test::complete " + new Date().toLocaleString());
            trace("=======================================================");
            sleep(1000);
        }

        private function sleep(ms:int):void {
            var init:int = getTimer();
            while(true) {
                if(getTimer() - init >= ms) {
                    break;
                }
            }
        }

    }
}

clean up test

clean up test


package ValueObjects
{
	public class AGORAParameters
	{
		public static var reference:AGORAParameters;
		public var listMapsURL:String;
		public var myMapsURL:String;
		public var loginURL:String;
		public var registrationURL:String;
		public var mapRemoveURL:String;
		public var insertURL:String;
		public var loadMapURL:String;

		public var gridWidth:int;
		public var version:String;

		//prompts
		public var MAP_LOADING_FAILED:String;
		public var STATEMENT_TOGGLE_FAILED:String;
		public var NETWORK_ERROR:String;

		public var IF:String = "If";
		public var THEN:String = "then";
		public var OR:String = "or";
		public var AND:String = "and";

		//constants
		public  var MOD_PON:String = "Modus Ponens";
		public  var MOD_TOL:String = "Modus Tollens";
		public  var COND_SYLL:String = "Conditional Syllogism";
		public  var DIS_SYLL:String = "Disjunctive Syllogism";
		public  var NOT_ALL_SYLL:String = "Not-All Syllogism";
		public  var CONST_DILEM:String = "Constructive Dilemma";

		public  var IF_THEN:String = "If-then";
		public  var IMPLIES:String = "Implies";
		public  var WHENEVER:String = "Whenever";
		public  var ONLY_IF:String = "Only if";
		public  var ONLY_IF_OR:String = "OnlyIfOR";
		public  var ONLY_IF_AND:String = "OnlyIfAnd";
		public  var PROVIDED_THAT:String = "Provided that";
		public  var SUFFICIENT_CONDITION:String = "Sufficient condition";
		public  var NECESSARY_CONDITION:String = "Necessary Condition";

		public  const DB_IF_THEN:String = "ifthen";
		public  const DB_IMPLIES:String = "implies";
		public  const DB_WHENEVER:String = "whenever";
		public  const DB_ONLY_IF:String = "onlyif";
		public  const DB_PROVIDED_THAT:String = "providedthat";
		public  const DB_SUFFICIENT:String = "sufficient";
		public  const DB_NECESSARY:String = "necessary";

		public  const MPIfThen:String = "MPifthen";
		public  const MPimplies:String = "MPimplies";
		public  const MPwhenever:String = "MPwhenever";
		public  const MPonlyif:String = "MPonlyif";
		public  const MPprovidedthat:String = "MPprovidedthat";
		public  const MPsufficient:String = "MPsufficient";
		public  const MPnecessary:String = "MPnecessary";
		public  const dbMP:String = "MP";
		public  const MTifthen:String = "MTifthen";
		public  const MTimplies:String = "MTimplies";
		public  const MTwhenever:String = "MTwhenever";
		public  const MTonlyif:String = "MTonlyif";
		public  const MTonlyiffor:String = "MTonlyiffor";
		public  const MTprovidedthat:String = "MTprovidedthat";
		public  const MTsufficient:String = "MTsufficient";
		public  const MTnecessary:String = "MTnecessary";
		public  const dbMT:String = "MT";
		public  const DisjSyl:String = "DisjSyl";
		public  const dbDisjSyl:String = "DisjSyl";
		public  const NotAllSyll:String = "NotAllSyl";
		public  const dbNotAllSyll:String = "NotAllSyl";
		public  const EQiff:String = "EQiff";
		public  const EQnecsuf:String = "EQnecsuf";
		public  const EQ:String = "EQ";
		public  const CSifthen:String = "CSifthen";

		public  const CSimplies:String = "CSimplies";
		public  const dbCS:String = "CS";
		public  const CDaltclaim:String = "CDaltclaim";
		public  const CDpropclaim:String = "CDpropclaim";
		public  const dbCD:String = "CD";
		public  const Unset:String = "Unset";

		public function AGORAParameters()
		{
			listMapsURL = "http://agora.gatech.edu/dev/list_maps.php";
			myMapsURL = "http://agora.gatech.edu/dev/my_maps.php";
			loginURL = "http://agora.gatech.edu/dev/login.php";
			registrationURL = "http://agora.gatech.edu/dev/register.php";
			mapRemoveURL = "http://agora.gatech.edu/dev/remove_map.php";
			insertURL = "http://agora.gatech.edu/dev/insert.php";
			loadMapURL = "http://agora.gatech.edu/dev/load_map.php";

			MAP_LOADING_FAILED = "Error occured when loading map";
			STATEMENT_TOGGLE_FAILED = "Error occurred when trying to toggle the type of statement";
			NETWORK_ERROR = "Unable to reach server. Please check your Internet connection...";

			gridWidth = 25;
			version = "11.8.30";
			reference = this;
		}

		public static function getInstance():AGORAParameters{
			if(!reference){
				reference = new AGORAParameters;
			}
			return reference;
		}
	}
}

Remove unnecessary duplicate spaces to clean up code


package com.axis.httpclient {
  import com.axis.ClientEvent;
  import com.axis.NetStreamClient;
  import com.axis.ErrorManager;
  import com.axis.IClient;
  import com.axis.Logger;

  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.NetStatusEvent;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;

  public class HTTPClient extends NetStreamClient implements IClient {
    private var urlParsed:Object;
    private var nc:NetConnection;

    public function HTTPClient(urlParsed:Object) {
      this.urlParsed = urlParsed;
    }

    public function start(options:Object):Boolean {
      Logger.log('HTTPClient: playing:', urlParsed.full);

      nc = new NetConnection();
      nc.connect(null);
      nc.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus);

      this.ns = new NetStream(nc);
      this.setupNetStream();

      this.ns.play(urlParsed.full);
      return true;
    }

    public function stop():Boolean {
      this.ns.dispose();
      this.nc.close();
      this.currentState = 'stopped';
      return true;
    }

    public function seek(position:Number):Boolean {
      this.ns.seek(position);
      return true;
    }

    public function pause():Boolean {
      if (this.currentState !== 'playing') {
        ErrorManager.dispatchError(800);
        return false;
      }
      this.ns.pause();
      return true;
    }

    public function resume():Boolean {
      if (this.currentState !== 'paused') {
        ErrorManager.dispatchError(801);
        return false;
      }
      this.ns.resume();
      return true;
    }

    public function setFrameByFrame(frameByFrame:Boolean):Boolean {
      return false;
    }

    public function playFrames(timestamp:Number):void {}

    public function setBuffer(seconds:Number):Boolean {
      this.ns.bufferTime = seconds;
      this.ns.pause();
      this.ns.resume();
      return true;
    }

    private function onConnectionStatus(event:NetStatusEvent):void {
      if ('NetConnection.Connect.Closed' === event.info.code) {
        this.currentState = 'stopped';
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
        this.ns.dispose();
      }
    }
  }
}

package com.axis.httpclient {
  import com.axis.ClientEvent;
  import com.axis.NetStreamClient;
  import com.axis.ErrorManager;
  import com.axis.IClient;
  import com.axis.Logger;

  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.NetStatusEvent;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;

  public class HTTPClient extends NetStreamClient implements IClient {
    private var urlParsed:Object;
    private var nc:NetConnection;

    public function HTTPClient(urlParsed:Object) {
      this.urlParsed = urlParsed;
    }

    public function start(options:Object):Boolean {
      Logger.log('HTTPClient: playing:', urlParsed.full);

      nc = new NetConnection();
      nc.connect(null);
      nc.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus);

      this.ns = new NetStream(nc);
      this.setupNetStream();

      this.ns.play(urlParsed.full);
      return true;
    }

    public function stop():Boolean {
      this.ns.dispose();
      this.nc.close();
      this.currentState = 'stopped';
      dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
      return true;
    }

    public function seek(position:Number):Boolean {
      this.ns.seek(position);
      return true;
    }

    public function pause():Boolean {
      if (this.currentState !== 'playing') {
        ErrorManager.dispatchError(800);
        return false;
      }
      this.ns.pause();
      return true;
    }

    public function resume():Boolean {
      if (this.currentState !== 'paused') {
        ErrorManager.dispatchError(801);
        return false;
      }
      this.ns.resume();
      return true;
    }

    public function setFrameByFrame(frameByFrame:Boolean):Boolean {
      return false;
    }

    public function playFrames(timestamp:Number):void {}

    public function setBuffer(seconds:Number):Boolean {
      this.ns.bufferTime = seconds;
      this.ns.pause();
      this.ns.resume();
      return true;
    }

    private function onConnectionStatus(event:NetStatusEvent):void {
      if ('NetConnection.Connect.Closed' === event.info.code && this.currentState !== 'stopped') {
        this.currentState = 'stopped';
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
        this.ns.dispose();
      }
    }
  }
}

Fix STOPPED event not triggered in a timely fashion

The stopped event from the http client was not triggered immediately on
clean up but after the connection was closed. If a call to streamStatus
would be made between clean up and connection closed event it would
throw an exception.


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.loader {
    import org.mangui.hls.playlist.DataUri;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.playlist.Manifest;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.constant.HLSTypes;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.model.Level;
    import org.mangui.hls.model.Fragment;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;

    import flash.events.*;
    import flash.net.*;
    import flash.utils.*;

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

        /** Setup the loader. **/
        public function LevelLoader(hls : HLS) {
            _hls = hls;
            _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _levels = new Vector.<Level>();
            _urlloader = new URLLoader();
            _urlloader.addEventListener(Event.COMPLETE, _loaderHandler);
            _urlloader.addEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
            _urlloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
        };

        public function dispose() : void {
            _close();
            _urlloader.removeEventListener(Event.COMPLETE, _loaderHandler);
            _urlloader.removeEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
            _urlloader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
            _hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
        }

        /** Loading failed; return errors. **/
        private function _errorHandler(event : ErrorEvent) : void {
            var txt : String;
            var code : int;
            if (event is SecurityErrorEvent) {
                code = HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR;
                txt = "Cannot load M3U8: crossdomain access denied:" + event.text;
            } else if (event is IOErrorEvent && _levels.length && (HLSSettings.manifestLoadMaxRetry == -1 || _retry_count < HLSSettings.manifestLoadMaxRetry)) {
                CONFIG::LOGGING {
                    Log.warn("I/O Error while trying to load Playlist, retry in " + _retry_timeout + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, _retry_timeout);
                /* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
                _retry_timeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retry_timeout);
                _retry_count++;
                return;
            } else {
                code = HLSError.MANIFEST_LOADING_IO_ERROR;
                txt = "Cannot load M3U8: " + event.text;
            }
            var hlsError : HLSError = new HLSError(code, _url, txt);
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
        };

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

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

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

        /** Load the manifest file. **/
        public function load(url : String) : void {
            _close();
            _closed = false;
            _url = url;
            _levels = new Vector.<Level>();
            _canStart = false;
            _reload_playlists_timer = getTimer();
            _retry_timeout = 1000;
            _retry_count = 0;
            _alt_audio_tracks = null;
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADING, url));

            if (DataUri.isDataUri(url)) {
                CONFIG::LOGGING {
                    Log.debug("Identified main manifest <" + url + "> as a data URI.");
                }
                var data : String = new DataUri(url).extractData();
                _parseManifest(data || "");
            } else {
                _urlloader.load(new URLRequest(url));
            }
        };

        /** Manifest loaded; check and parse it **/
        private function _loaderHandler(event : Event) : void {
            // successful loading, reset retry counter
            _retry_timeout = 1000;
            _retry_count = 0;
            var loader : URLLoader = URLLoader(event.target);
            _parseManifest(String(loader.data));
        };

        /** parse a playlist **/
        private function _parseLevelPlaylist(string : String, url : String, level : int) : void {
            if (string != null && string.length != 0) {
                CONFIG::LOGGING {
                    Log.debug("level " + level + " playlist:\n" + string);
                }
                var frags : Vector.<Fragment> = Manifest.getFragments(string, url, level);
                // set fragment and update sequence number range
                _levels[level].updateFragments(frags);
                _levels[level].targetduration = Manifest.getTargetDuration(string);
                _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYLIST_DURATION_UPDATED, _levels[level].duration));
            }

            // Check whether the stream is live or not finished yet
            if (Manifest.hasEndlist(string)) {
                _type = HLSTypes.VOD;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_ENDLIST, level));
            } else {
                _type = HLSTypes.LIVE;
                var timeout : Number = Math.max(100, _reload_playlists_timer + 1000 * _levels[level].averageduration - getTimer());
                CONFIG::LOGGING {
                    Log.debug("Level " + level + " Live Playlist parsing finished: reload in " + timeout.toFixed(0) + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, timeout);
            }
            if (!_canStart) {
                _canStart = (_levels[level].fragments.length > 0);
                if (_canStart) {
                    CONFIG::LOGGING {
                        Log.debug("first level filled with at least 1 fragment, notify event");
                    }
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADED, _levels));
                }
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADED, level));
            _manifest_loading = null;
        };

        /** Parse First Level Playlist **/
        private function _parseManifest(string : String) : void {
            // Check for M3U8 playlist or manifest.
            if (string.indexOf(Manifest.HEADER) == 0) {
                // 1 level playlist, create unique level and parse playlist
                if (string.indexOf(Manifest.FRAGMENT) > 0) {
                    var level : Level = new Level();
                    level.url = _url;
                    _levels.push(level);
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, 0));
                    CONFIG::LOGGING {
                        Log.debug("1 Level Playlist, load it");
                    }
                    _current_level = 0;
                    _parseLevelPlaylist(string, _url, 0);
                } else if (string.indexOf(Manifest.LEVEL) > 0) {
                    CONFIG::LOGGING {
                        Log.debug("adaptive playlist:\n" + string);
                    }
                    // adaptative playlist, extract levels from playlist, get them and parse them
                    _levels = Manifest.extractLevels(_hls, string, _url);
                    // retrieve start level from helper function
                    _current_level = _hls.startlevel;
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _loadActiveLevelPlaylist();
                    if (string.indexOf(Manifest.ALTERNATE_AUDIO) > 0) {
                        CONFIG::LOGGING {
                            Log.debug("alternate audio level found");
                        }
                        // parse alternate audio tracks
                        _alt_audio_tracks = Manifest.extractAltAudioTracks(string, _url);
                        CONFIG::LOGGING {
                            if (_alt_audio_tracks.length > 0) {
                                Log.debug(_alt_audio_tracks.length + " alternate audio tracks found");
                            }
                        }
                    }
                }
            } else {
                var hlsError : HLSError = new HLSError(HLSError.MANIFEST_PARSING_ERROR, _url, "Manifest is not a valid M3U8 file");
                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
            }
        };

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

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

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

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

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.loader {
    import org.mangui.hls.playlist.DataUri;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.playlist.Manifest;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.constant.HLSTypes;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.model.Level;
    import org.mangui.hls.model.Fragment;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;

    import flash.events.*;
    import flash.net.*;
    import flash.utils.*;

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

        /** Setup the loader. **/
        public function LevelLoader(hls : HLS) {
            _hls = hls;
            _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _levels = new Vector.<Level>();
            _urlloader = new URLLoader();
            _urlloader.addEventListener(Event.COMPLETE, _loaderHandler);
            _urlloader.addEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
            _urlloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
        };

        public function dispose() : void {
            _close();
            _urlloader.removeEventListener(Event.COMPLETE, _loaderHandler);
            _urlloader.removeEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
            _urlloader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
            _hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
        }

        /** Loading failed; return errors. **/
        private function _errorHandler(event : ErrorEvent) : void {
            var txt : String;
            var code : int;
            if (event is SecurityErrorEvent) {
                code = HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR;
                txt = "Cannot load M3U8: crossdomain access denied:" + event.text;
            } else if (event is IOErrorEvent && _levels.length && (HLSSettings.manifestLoadMaxRetry == -1 || _retry_count < HLSSettings.manifestLoadMaxRetry)) {
                CONFIG::LOGGING {
                    Log.warn("I/O Error while trying to load Playlist, retry in " + _retry_timeout + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, _retry_timeout);
                /* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
                _retry_timeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retry_timeout);
                _retry_count++;
                return;
            } else {
                code = HLSError.MANIFEST_LOADING_IO_ERROR;
                txt = "Cannot load M3U8: " + event.text;
            }
            var hlsError : HLSError = new HLSError(code, _url, txt);
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
        };

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

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

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

        /** Load the manifest file. **/
        public function load(url : String) : void {
            _close();
            _closed = false;
            _url = url;
            _levels = new Vector.<Level>();
            _canStart = false;
            _reload_playlists_timer = getTimer();
            _retry_timeout = 1000;
            _retry_count = 0;
            _alt_audio_tracks = null;
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADING, url));

            if (DataUri.isDataUri(url)) {
                CONFIG::LOGGING {
                    Log.debug("Identified main manifest <" + url + "> as a data URI.");
                }
                var data : String = new DataUri(url).extractData();
                _parseManifest(data || "");
            } else {
                _urlloader.load(new URLRequest(url));
            }
        };

        /** Manifest loaded; check and parse it **/
        private function _loaderHandler(event : Event) : void {
            // successful loading, reset retry counter
            _retry_timeout = 1000;
            _retry_count = 0;
            var loader : URLLoader = URLLoader(event.target);
            _parseManifest(String(loader.data));
        };

        /** parse a playlist **/
        private function _parseLevelPlaylist(string : String, url : String, level : int) : void {
            if (string != null && string.length != 0) {
                CONFIG::LOGGING {
                    Log.debug("level " + level + " playlist:\n" + string);
                }
                var frags : Vector.<Fragment> = Manifest.getFragments(string, url, level);
                // set fragment and update sequence number range
                _levels[level].updateFragments(frags);
                _levels[level].targetduration = Manifest.getTargetDuration(string);
                _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYLIST_DURATION_UPDATED, _levels[level].duration));
            }

            // Check whether the stream is live or not finished yet
            if (Manifest.hasEndlist(string)) {
                _type = HLSTypes.VOD;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_ENDLIST, level));
            } else {
                _type = HLSTypes.LIVE;
                /* in order to determine playlist reload timer,
                    check playback position against playlist duration.
                    if we are near the edge of a live playlist, reload playlist quickly
                    to discover quicker new fragments and avoid buffer starvation.
                */
                var reload_interval : Number = 1000*Math.min((_levels[level].duration - _hls.position)/2,_levels[level].averageduration);
                // avoid spamming the server if we are at the edge ... wait 500ms between 2 reload at least
                var timeout : Number = Math.max(500, _reload_playlists_timer + reload_interval - getTimer());
                CONFIG::LOGGING {
                    Log.debug("Level " + level + " Live Playlist parsing finished: reload in " + timeout.toFixed(0) + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, timeout);
            }
            if (!_canStart) {
                _canStart = (_levels[level].fragments.length > 0);
                if (_canStart) {
                    CONFIG::LOGGING {
                        Log.debug("first level filled with at least 1 fragment, notify event");
                    }
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADED, _levels));
                }
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADED, level));
            _manifest_loading = null;
        };

        /** Parse First Level Playlist **/
        private function _parseManifest(string : String) : void {
            // Check for M3U8 playlist or manifest.
            if (string.indexOf(Manifest.HEADER) == 0) {
                // 1 level playlist, create unique level and parse playlist
                if (string.indexOf(Manifest.FRAGMENT) > 0) {
                    var level : Level = new Level();
                    level.url = _url;
                    _levels.push(level);
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, 0));
                    CONFIG::LOGGING {
                        Log.debug("1 Level Playlist, load it");
                    }
                    _current_level = 0;
                    _parseLevelPlaylist(string, _url, 0);
                } else if (string.indexOf(Manifest.LEVEL) > 0) {
                    CONFIG::LOGGING {
                        Log.debug("adaptive playlist:\n" + string);
                    }
                    // adaptative playlist, extract levels from playlist, get them and parse them
                    _levels = Manifest.extractLevels(_hls, string, _url);
                    // retrieve start level from helper function
                    _current_level = _hls.startlevel;
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _loadActiveLevelPlaylist();
                    if (string.indexOf(Manifest.ALTERNATE_AUDIO) > 0) {
                        CONFIG::LOGGING {
                            Log.debug("alternate audio level found");
                        }
                        // parse alternate audio tracks
                        _alt_audio_tracks = Manifest.extractAltAudioTracks(string, _url);
                        CONFIG::LOGGING {
                            if (_alt_audio_tracks.length > 0) {
                                Log.debug(_alt_audio_tracks.length + " alternate audio tracks found");
                            }
                        }
                    }
                }
            } else {
                var hlsError : HLSError = new HLSError(HLSError.MANIFEST_PARSING_ERROR, _url, "Manifest is not a valid M3U8 file");
                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
            }
        };

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

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

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

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

adjust live playlist reload timer when play position is near the edge of the live playlist related to #198


/*
 *  FRESteamWorks.as
 *  This file is part of FRESteamWorks.
 *
 *  Created by Ventero <http://github.com/Ventero>
 *  Copyright (c) 2012-2013 Level Up Labs, LLC. All rights reserved.
 */

package com.amanitadesign.steam {
	import flash.desktop.NativeProcess;
	import flash.desktop.NativeProcessStartupInfo;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.StatusEvent;
	import flash.filesystem.File;
	import flash.utils.ByteArray;
	import flash.utils.IDataInput;
	import flash.utils.IDataOutput;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;

	public class FRESteamWorks extends EventDispatcher {
		[Event(name="steam_response", type="com.amanitadesign.steam.SteamEvent")]

		private static const PATH:String = "NativeApps/Linux/APIWrapper";

		private var _file:File;
		private var _process:NativeProcess;
		private var _tm:int;
		private var _init:Boolean = false;
		private var _error:Boolean = false;
		private var _crashHandlerArgs:Array = null;

		public var isReady:Boolean = false;

		private static const AIRSteam_Init:int = 0;
		private static const AIRSteam_RunCallbacks:int = 1;
		private static const AIRSteam_RequestStats:int = 2;
		private static const AIRSteam_SetAchievement:int = 3;
		private static const AIRSteam_ClearAchievement:int = 4;
		private static const AIRSteam_IsAchievement:int = 5;
		private static const AIRSteam_GetStatInt:int = 6;
		private static const AIRSteam_GetStatFloat:int = 7;
		private static const AIRSteam_SetStatInt:int = 8;
		private static const AIRSteam_SetStatFloat:int = 9;
		private static const AIRSteam_StoreStats:int = 10;
		private static const AIRSteam_ResetAllStats:int = 11;
		private static const AIRSteam_GetFileCount:int = 12;
		private static const AIRSteam_GetFileSize:int = 13;
		private static const AIRSteam_FileExists:int = 14;
		private static const AIRSteam_FileWrite:int = 15;
		private static const AIRSteam_FileRead:int = 16;
		private static const AIRSteam_FileDelete:int = 17;
		private static const AIRSteam_IsCloudEnabledForApp:int = 18;
		private static const AIRSteam_SetCloudEnabledForApp:int = 19;
		private static const AIRSteam_GetUserID:int = 20;
		private static const AIRSteam_GetPersonaName:int = 21;
		private static const AIRSteam_UseCrashHandler:int = 22;

		public function FRESteamWorks (target:IEventDispatcher = null) {
			_file = File.applicationDirectory.resolvePath(PATH);
			_process = new NativeProcess();
			_process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, eventDispatched);
			_process.addEventListener(IOErrorEvent.STANDARD_INPUT_IO_ERROR, errorCallback);
			super(target);
		}

		public function dispose():void {
			if(_process.running) {
				_process.closeInput();
				_process.exit();
			}
			clearInterval(_tm);
			isReady = false;
		}

		private function startProcess():void {
			var startupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
			startupInfo.executable = _file;

			_process.start(startupInfo);
		}

		public function init():Boolean {
			if(!_file.exists) return false;

			try {
				startProcess();
			} catch(e:Error) {
				return false;
			}

			// try to start the process a second time, but this time ignoring any
			// potential errors. this seems to give the runtime enough time to check
			// for any stderr output of the initial call to startProcess(), so that we
			// now can check if it actually started. process.running is unreliable,
			// in that it's always set to true, even if the process didn't start at all
			// in case it couldn't load one of the libraries it depends on, it prints
			// something like "error while loading shared libraries" to stderr,
			// which we're trying to detect here
			try {
				startProcess();
			} catch(e:Error) {
				// no-op
			}
			if(!_process.running) return false;
			if(_process.standardError.bytesAvailable > 0) return false;

			// initialization seems to be successful
			_init = true;

			// UseCrashHandler has to be called before Steam_Init
			// but we still have to make sure the process is actually running
			// so FRESteamWorks#useCrashHandler just sets _crashHandlerArgs
			// and the actual call is handled here
			if(_crashHandlerArgs) {
				if(!callWrapper(AIRSteam_UseCrashHandler, _crashHandlerArgs))
					return false;
			}

			if(!callWrapper(AIRSteam_Init)) return false;
			isReady = readBoolResponse();
			if(isReady) _tm = setInterval(runCallbacks, 100);
			return isReady;
		}

		private function errorCallback(e:IOErrorEvent):void {
			_error = true;
			// the process doesn't accept our input anymore, so just stop it
			clearInterval(_tm);

			if(_process.running) {
				try {
					_process.closeInput();
				} catch(e:*) {
					// no-op
				}

				_process.exit();
			}
		}

		private function callWrapper(funcName:int, params:Array = null):Boolean {
			_error = false;
			if(!_process.running) return false;

			var stdin:IDataOutput = _process.standardInput;
			stdin.writeUTFBytes(funcName + "\n");

			if (params) {
				for(var i:int = 0; i < params.length; ++i) {
					if(params[i] is ByteArray) {
						var length:uint = params[i].length;
						// length + 1 for the added newline
						stdin.writeUTFBytes(String(length + 1) + "\n");
						stdin.writeBytes(params[i]);
						stdin.writeUTFBytes("\n");
					} else {
						stdin.writeUTFBytes(String(params[i]) + "\n");
					}
				}
			}

			return !_error;
		}

		private function waitForData(output:IDataInput):uint {
			while(!output.bytesAvailable) {
				// wait
				if(!_process.running) return 0;
			}

			return output.bytesAvailable;
		}

		private function readBoolResponse():Boolean {
			if(!_process.running) return false;
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(1);
			return (response == "t");
		}

		private function readIntResponse():int {
			if(!_process.running) return 0;
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(avail);
			return parseInt(response, 10);
		}

		private function readFloatResponse():Number {
			if(!_process.running) return 0.0;
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(avail)
			return parseFloat(response);
		}

		private function readStringResponse():String {
			if(!_process.running) return "";
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(avail)
			return response;
		}

		private function eventDispatched(e:ProgressEvent):void {
			var stderr:IDataInput = _process.standardError;
			var avail:uint = stderr.bytesAvailable;
			var data:String = stderr.readUTFBytes(avail);

			var pattern:RegExp = /__event__<(\d+),(\d+)>/g;
			var result:Object;
			while((result = pattern.exec(data))) {
				var req_type:int = new int(result[1]);
				var response:int = new int(result[2]);
				var steamEvent:SteamEvent = new SteamEvent(SteamEvent.STEAM_RESPONSE, req_type, response);
				dispatchEvent(steamEvent);
			}
		}

		public function requestStats():Boolean {
			if(!callWrapper(AIRSteam_RequestStats)) return false;
			return readBoolResponse();
		}

		public function runCallbacks():Boolean {
			if(!callWrapper(AIRSteam_RunCallbacks)) return false;
			return true;
		}

		public function getUserID():String {
			if(!callWrapper(AIRSteam_GetUserID)) return "";
			return readStringResponse();
		}

		public function getPersonaName():String {
			if(!callWrapper(AIRSteam_GetPersonaName)) return "";
			return readStringResponse();
		}

		public function useCrashHandler(appID:uint, version:String, date:String, time:String):Boolean {
			// only allow calls before SteamAPI_Init was called
			if(_init) return false;
			_crashHandlerArgs = [appID, version, date, time];
			return true;
		}

		public function setAchievement(id:String):Boolean {
			if(!callWrapper(AIRSteam_SetAchievement, [id])) return false;
			return readBoolResponse();
		}

		public function clearAchievement(id:String):Boolean {
			if(!callWrapper(AIRSteam_ClearAchievement, [id])) return false;
			return readBoolResponse();
		}

		public function isAchievement(id:String):Boolean {
			if(!callWrapper(AIRSteam_IsAchievement, [id])) return false;
			return readBoolResponse();
		}

		public function getStatInt(id:String):int {
			if(!callWrapper(AIRSteam_GetStatInt, [id])) return 0;
			return readIntResponse();
		}

		public function getStatFloat(id:String):Number {
			if(!callWrapper(AIRSteam_GetStatFloat, [id])) return 0.0;
			return readFloatResponse();
		}

		public function setStatInt(id:String, value:int):Boolean {
			if(!callWrapper(AIRSteam_SetStatInt, [id, value])) return false;
			return readBoolResponse();
		}

		public function setStatFloat(id:String, value:Number):Boolean {
			if(!callWrapper(AIRSteam_SetStatFloat, [id, value])) return false;
			return readBoolResponse();
		}

		public function storeStats():Boolean {
			if(!callWrapper(AIRSteam_StoreStats)) return false;
			return readBoolResponse();
		}

		public function resetAllStats(bAchievementsToo:Boolean):Boolean {
			if(!callWrapper(AIRSteam_ResetAllStats, [bAchievementsToo])) return false;
			return readBoolResponse();
		}

		public function getFileCount():int {
			if(!callWrapper(AIRSteam_GetFileCount)) return 0;
			return readIntResponse();
		}

		public function getFileSize(fileName:String):int {
			if(!callWrapper(AIRSteam_GetFileSize, [fileName])) return 0;
			return readIntResponse();
		}

		public function fileExists(fileName:String):Boolean {
			if(!callWrapper(AIRSteam_FileExists, [fileName])) return false;
			return readBoolResponse();
		}

		public function fileWrite(fileName:String, data:ByteArray):Boolean {
			if(!callWrapper(AIRSteam_FileWrite, [fileName, data])) return false;
			return readBoolResponse();
		}

		public function fileRead(fileName:String, data:ByteArray):Boolean {
			if(!callWrapper(AIRSteam_FileRead, [fileName])) return false;

			var success:Boolean = readBoolResponse();
			if(success) {
				var content:String = readStringResponse();
				data.writeUTFBytes(content);
				data.position = 0;
			}

			return success;
		}

		public function fileDelete(fileName:String):Boolean {
			if(!callWrapper(AIRSteam_FileDelete, [fileName])) return false;
			return readBoolResponse();
		}

		public function isCloudEnabledForApp():Boolean {
			if(!callWrapper(AIRSteam_IsCloudEnabledForApp)) return false;
			return readBoolResponse();
		}

		public function setCloudEnabledForApp(enabled:Boolean):Boolean {
			if(!callWrapper(AIRSteam_SetCloudEnabledForApp, [enabled])) return false;
			return readBoolResponse();
		}
	}
}

/*
 *  FRESteamWorks.as
 *  This file is part of FRESteamWorks.
 *
 *  Created by Ventero <http://github.com/Ventero>
 *  Copyright (c) 2012-2013 Level Up Labs, LLC. All rights reserved.
 */

package com.amanitadesign.steam {
	import flash.desktop.NativeProcess;
	import flash.desktop.NativeProcessStartupInfo;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.StatusEvent;
	import flash.filesystem.File;
	import flash.utils.ByteArray;
	import flash.utils.IDataInput;
	import flash.utils.IDataOutput;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;

	public class FRESteamWorks extends EventDispatcher {
		[Event(name="steam_response", type="com.amanitadesign.steam.SteamEvent")]

		private static const PATH:String = "NativeApps/Linux/APIWrapper";

		private var _file:File;
		private var _process:NativeProcess;
		private var _tm:int;
		private var _init:Boolean = false;
		private var _error:Boolean = false;
		private var _crashHandlerArgs:Array = null;

		public var isReady:Boolean = false;

		private static const AIRSteam_Init:int = 0;
		private static const AIRSteam_RunCallbacks:int = 1;
		private static const AIRSteam_RequestStats:int = 2;
		private static const AIRSteam_SetAchievement:int = 3;
		private static const AIRSteam_ClearAchievement:int = 4;
		private static const AIRSteam_IsAchievement:int = 5;
		private static const AIRSteam_GetStatInt:int = 6;
		private static const AIRSteam_GetStatFloat:int = 7;
		private static const AIRSteam_SetStatInt:int = 8;
		private static const AIRSteam_SetStatFloat:int = 9;
		private static const AIRSteam_StoreStats:int = 10;
		private static const AIRSteam_ResetAllStats:int = 11;
		private static const AIRSteam_GetFileCount:int = 12;
		private static const AIRSteam_GetFileSize:int = 13;
		private static const AIRSteam_FileExists:int = 14;
		private static const AIRSteam_FileWrite:int = 15;
		private static const AIRSteam_FileRead:int = 16;
		private static const AIRSteam_FileDelete:int = 17;
		private static const AIRSteam_IsCloudEnabledForApp:int = 18;
		private static const AIRSteam_SetCloudEnabledForApp:int = 19;
		private static const AIRSteam_GetUserID:int = 20;
		private static const AIRSteam_GetPersonaName:int = 21;
		private static const AIRSteam_UseCrashHandler:int = 22;

		public function FRESteamWorks (target:IEventDispatcher = null) {
			_file = File.applicationDirectory.resolvePath(PATH);
			_process = new NativeProcess();
			_process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, eventDispatched);
			_process.addEventListener(IOErrorEvent.STANDARD_INPUT_IO_ERROR, errorCallback);
			super(target);
		}

		public function dispose():void {
			if(_process.running) {
				_process.closeInput();
				_process.exit();
			}
			clearInterval(_tm);
			isReady = false;
		}

		private function startProcess():void {
			var startupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
			startupInfo.executable = _file;

			_process.start(startupInfo);
		}

		public function init():Boolean {
			if(!_file.exists) return false;

			try {
				startProcess();
			} catch(e:Error) {
				return false;
			}

			// try to start the process a second time, but this time ignoring any
			// potential errors since we will definitely get one about the process
			// already running. this seems to give the runtime enough time to check
			// for any stderr output of the initial call to startProcess(), so that we
			// now can check if it actually started: in case it couldn't one of the
			// libraries it depends on, it prints something like "error while loading
			// shared libraries" to stderr, which we're trying to detect here.
			// process.running is unreliable, in that it's always set to true,
			// even if the process didn't start at all
			try {
				startProcess();
			} catch(e:Error) {
				// no-op
			}
			if(!_process.running) return false;
			if(_process.standardError.bytesAvailable > 0) return false;

			// initialization seems to be successful
			_init = true;

			// UseCrashHandler has to be called before Steam_Init
			// but we still have to make sure the process is actually running
			// so FRESteamWorks#useCrashHandler just sets _crashHandlerArgs
			// and the actual call is handled here
			if(_crashHandlerArgs) {
				if(!callWrapper(AIRSteam_UseCrashHandler, _crashHandlerArgs))
					return false;
			}

			if(!callWrapper(AIRSteam_Init)) return false;
			isReady = readBoolResponse();
			if(isReady) _tm = setInterval(runCallbacks, 100);
			return isReady;
		}

		private function errorCallback(e:IOErrorEvent):void {
			_error = true;
			// the process doesn't accept our input anymore, so just stop it
			clearInterval(_tm);

			if(_process.running) {
				try {
					_process.closeInput();
				} catch(e:*) {
					// no-op
				}

				_process.exit();
			}
		}

		private function callWrapper(funcName:int, params:Array = null):Boolean {
			_error = false;
			if(!_process.running) return false;

			var stdin:IDataOutput = _process.standardInput;
			stdin.writeUTFBytes(funcName + "\n");

			if (params) {
				for(var i:int = 0; i < params.length; ++i) {
					if(params[i] is ByteArray) {
						var length:uint = params[i].length;
						// length + 1 for the added newline
						stdin.writeUTFBytes(String(length + 1) + "\n");
						stdin.writeBytes(params[i]);
						stdin.writeUTFBytes("\n");
					} else {
						stdin.writeUTFBytes(String(params[i]) + "\n");
					}
				}
			}

			return !_error;
		}

		private function waitForData(output:IDataInput):uint {
			while(!output.bytesAvailable) {
				// wait
				if(!_process.running) return 0;
			}

			return output.bytesAvailable;
		}

		private function readBoolResponse():Boolean {
			if(!_process.running) return false;
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(1);
			return (response == "t");
		}

		private function readIntResponse():int {
			if(!_process.running) return 0;
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(avail);
			return parseInt(response, 10);
		}

		private function readFloatResponse():Number {
			if(!_process.running) return 0.0;
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(avail)
			return parseFloat(response);
		}

		private function readStringResponse():String {
			if(!_process.running) return "";
			var stdout:IDataInput = _process.standardOutput;
			var avail:uint = waitForData(stdout);

			var response:String = stdout.readUTFBytes(avail)
			return response;
		}

		private function eventDispatched(e:ProgressEvent):void {
			var stderr:IDataInput = _process.standardError;
			var avail:uint = stderr.bytesAvailable;
			var data:String = stderr.readUTFBytes(avail);

			var pattern:RegExp = /__event__<(\d+),(\d+)>/g;
			var result:Object;
			while((result = pattern.exec(data))) {
				var req_type:int = new int(result[1]);
				var response:int = new int(result[2]);
				var steamEvent:SteamEvent = new SteamEvent(SteamEvent.STEAM_RESPONSE, req_type, response);
				dispatchEvent(steamEvent);
			}
		}

		public function requestStats():Boolean {
			if(!callWrapper(AIRSteam_RequestStats)) return false;
			return readBoolResponse();
		}

		public function runCallbacks():Boolean {
			if(!callWrapper(AIRSteam_RunCallbacks)) return false;
			return true;
		}

		public function getUserID():String {
			if(!callWrapper(AIRSteam_GetUserID)) return "";
			return readStringResponse();
		}

		public function getPersonaName():String {
			if(!callWrapper(AIRSteam_GetPersonaName)) return "";
			return readStringResponse();
		}

		public function useCrashHandler(appID:uint, version:String, date:String, time:String):Boolean {
			// only allow calls before SteamAPI_Init was called
			if(_init) return false;
			_crashHandlerArgs = [appID, version, date, time];
			return true;
		}

		public function setAchievement(id:String):Boolean {
			if(!callWrapper(AIRSteam_SetAchievement, [id])) return false;
			return readBoolResponse();
		}

		public function clearAchievement(id:String):Boolean {
			if(!callWrapper(AIRSteam_ClearAchievement, [id])) return false;
			return readBoolResponse();
		}

		public function isAchievement(id:String):Boolean {
			if(!callWrapper(AIRSteam_IsAchievement, [id])) return false;
			return readBoolResponse();
		}

		public function getStatInt(id:String):int {
			if(!callWrapper(AIRSteam_GetStatInt, [id])) return 0;
			return readIntResponse();
		}

		public function getStatFloat(id:String):Number {
			if(!callWrapper(AIRSteam_GetStatFloat, [id])) return 0.0;
			return readFloatResponse();
		}

		public function setStatInt(id:String, value:int):Boolean {
			if(!callWrapper(AIRSteam_SetStatInt, [id, value])) return false;
			return readBoolResponse();
		}

		public function setStatFloat(id:String, value:Number):Boolean {
			if(!callWrapper(AIRSteam_SetStatFloat, [id, value])) return false;
			return readBoolResponse();
		}

		public function storeStats():Boolean {
			if(!callWrapper(AIRSteam_StoreStats)) return false;
			return readBoolResponse();
		}

		public function resetAllStats(bAchievementsToo:Boolean):Boolean {
			if(!callWrapper(AIRSteam_ResetAllStats, [bAchievementsToo])) return false;
			return readBoolResponse();
		}

		public function getFileCount():int {
			if(!callWrapper(AIRSteam_GetFileCount)) return 0;
			return readIntResponse();
		}

		public function getFileSize(fileName:String):int {
			if(!callWrapper(AIRSteam_GetFileSize, [fileName])) return 0;
			return readIntResponse();
		}

		public function fileExists(fileName:String):Boolean {
			if(!callWrapper(AIRSteam_FileExists, [fileName])) return false;
			return readBoolResponse();
		}

		public function fileWrite(fileName:String, data:ByteArray):Boolean {
			if(!callWrapper(AIRSteam_FileWrite, [fileName, data])) return false;
			return readBoolResponse();
		}

		public function fileRead(fileName:String, data:ByteArray):Boolean {
			if(!callWrapper(AIRSteam_FileRead, [fileName])) return false;

			var success:Boolean = readBoolResponse();
			if(success) {
				var content:String = readStringResponse();
				data.writeUTFBytes(content);
				data.position = 0;
			}

			return success;
		}

		public function fileDelete(fileName:String):Boolean {
			if(!callWrapper(AIRSteam_FileDelete, [fileName])) return false;
			return readBoolResponse();
		}

		public function isCloudEnabledForApp():Boolean {
			if(!callWrapper(AIRSteam_IsCloudEnabledForApp)) return false;
			return readBoolResponse();
		}

		public function setCloudEnabledForApp(enabled:Boolean):Boolean {
			if(!callWrapper(AIRSteam_SetCloudEnabledForApp, [enabled])) return false;
			return readBoolResponse();
		}
	}
}

Clarify comment

Clarify comment


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

package com.threerings.flash {

import flash.events.Event;
import flash.events.MouseEvent;

import flash.filters.GlowFilter;

import flash.system.Capabilities;
import flash.system.System;

import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;

import com.threerings.util.Util;

public class TextFieldUtil
{
    /** A fudge factor that must be added to a TextField's textWidth when setting the width. */
    public static const WIDTH_PAD :int = 5;

    /** A fudge factor that must be added to a TextField's textHeight when setting the height. */
    public static const HEIGHT_PAD :int = 4;

    /**
     * Create a TextField.
     *
     * @param initProps contains properties with which to initialize the TextField.
     * Additionally it may contain the following properties:
     *    outlineColor: uint
     *
     * initProps may be destructively modified.
     */
    public static function createField (
        text :String, initProps :Object = null, clazz :Class = null) :TextField
    {
        var tf :TextField = (clazz == null) ? new TextField() : TextField(new clazz());

        if ("outlineColor" in initProps) {
            tf.filters = [ new GlowFilter(uint(initProps["outlineColor"]), 1, 2, 2, 255) ];
        }

        Util.init(tf, initProps, null, MASK_FIELD_PROPS);
        tf.text = text;
        if (tf.autoSize != TextFieldAutoSize.NONE) {
            tf.width = tf.textWidth + WIDTH_PAD;
            tf.height = tf.textHeight + HEIGHT_PAD;
        }

        return tf;
    }

    /**
     * Create a TextFormat using initProps.
     * If unspecified, the following properties have default values:
     *  size: 18
     *  font: _sans
     */
    public static function createFormat (initProps :Object) :TextFormat
    {
        var f :TextFormat = new TextFormat();
        Util.init(f, initProps, DEFAULT_FORMAT_PROPS);
        return f;
    }

    /**
     * Include the specified TextField in a set of TextFields in which only
     * one may have a selection at a time.
     */
    public static function trackSingleSelectable (textField :TextField) :void
    {
        textField.addEventListener(MouseEvent.MOUSE_MOVE, handleTrackedSelection);

        // immediately put the kibosh on any selection
        textField.setSelection(0, 0);
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleTrackedSelection (event :MouseEvent) :void
    {
        if (event.buttonDown) {
            var field :TextField = event.target as TextField;
            if (field == _lastSelected) {
                updateSelection(field);

            } else if (field.selectionBeginIndex != field.selectionEndIndex) {
                // clear the last one..
                if (_lastSelected != null) {
                    handleLastSelectedRemoved();
                }
                _lastSelected = field;
                _lastSelected.addEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
                updateSelection(field);
            }
        }
    }

    /**
     * Process the selection.
     */
    protected static function updateSelection (field :TextField) :void
    {
        if (-1 != Capabilities.os.indexOf("Linux")) {
            var str :String = field.text.substring(
                field.selectionBeginIndex, field.selectionEndIndex);
            System.setClipboard(str);
        }
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleLastSelectedRemoved (... ignored) :void
    {
        _lastSelected.setSelection(0, 0);
        _lastSelected.removeEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
        _lastSelected = null;
    }

    /** The last tracked TextField to be selected. */
    protected static var _lastSelected :TextField;

    protected static const MASK_FIELD_PROPS :Object = { outlineColor: true };

    protected static const DEFAULT_FORMAT_PROPS :Object = { size: 18, font: "_sans" };
}
}

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

package com.threerings.flash {

import flash.events.Event;
import flash.events.MouseEvent;

import flash.filters.GlowFilter;

import flash.system.Capabilities;
import flash.system.System;

import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;

import com.threerings.util.Util;

public class TextFieldUtil
{
    /** A fudge factor that must be added to a TextField's textWidth when setting the width. */
    public static const WIDTH_PAD :int = 5;

    /** A fudge factor that must be added to a TextField's textHeight when setting the height. */
    public static const HEIGHT_PAD :int = 4;

    /**
     * Create a TextField.
     *
     * @param initProps contains properties with which to initialize the TextField.
     * Additionally it may contain the following properties:
     *    outlineColor: uint
     * @param formatProps contains properties with which to initialize the defaultTextFormat.
     */
    public static function createField (
        text :String, initProps :Object = null, formatProps :Object = null, clazz :Class = null)
        :TextField
    {
        var tf :TextField = (clazz == null) ? new TextField() : TextField(new clazz());

        if ("outlineColor" in initProps) {
            tf.filters = [ new GlowFilter(uint(initProps["outlineColor"]), 1, 2, 2, 255) ];
        }

        Util.init(tf, initProps, null, MASK_FIELD_PROPS);
        if (formatProps != null) {
            tf.defaultTextFormat = createFormat(formatProps);
        }
        tf.text = text;
        if (tf.autoSize != TextFieldAutoSize.NONE) {
            tf.width = tf.textWidth + WIDTH_PAD;
            tf.height = tf.textHeight + HEIGHT_PAD;
        }

        return tf;
    }

    /**
     * Create a TextFormat using initProps.
     * If unspecified, the following properties have default values:
     *  size: 18
     *  font: _sans
     */
    public static function createFormat (initProps :Object) :TextFormat
    {
        var f :TextFormat = new TextFormat();
        Util.init(f, initProps, DEFAULT_FORMAT_PROPS);
        return f;
    }

    /**
     * Include the specified TextField in a set of TextFields in which only
     * one may have a selection at a time.
     */
    public static function trackSingleSelectable (textField :TextField) :void
    {
        textField.addEventListener(MouseEvent.MOUSE_MOVE, handleTrackedSelection);

        // immediately put the kibosh on any selection
        textField.setSelection(0, 0);
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleTrackedSelection (event :MouseEvent) :void
    {
        if (event.buttonDown) {
            var field :TextField = event.target as TextField;
            if (field == _lastSelected) {
                updateSelection(field);

            } else if (field.selectionBeginIndex != field.selectionEndIndex) {
                // clear the last one..
                if (_lastSelected != null) {
                    handleLastSelectedRemoved();
                }
                _lastSelected = field;
                _lastSelected.addEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
                updateSelection(field);
            }
        }
    }

    /**
     * Process the selection.
     */
    protected static function updateSelection (field :TextField) :void
    {
        if (-1 != Capabilities.os.indexOf("Linux")) {
            var str :String = field.text.substring(
                field.selectionBeginIndex, field.selectionEndIndex);
            System.setClipboard(str);
        }
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleLastSelectedRemoved (... ignored) :void
    {
        _lastSelected.setSelection(0, 0);
        _lastSelected.removeEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
        _lastSelected = null;
    }

    /** The last tracked TextField to be selected. */
    protected static var _lastSelected :TextField;

    protected static const MASK_FIELD_PROPS :Object = { outlineColor: true };

    protected static const DEFAULT_FORMAT_PROPS :Object = { size: 18, font: "_sans" };
}
}

Allow the defaultTextFormat initProps to be specified more conveniently.

git-svn-id: b675b909355d5cf946977f44a8ec5a6ceb3782e4@478 ed5b42cb-e716-0410-a449-f6a68f950b19