/* 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 {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;

    public final class HLSSettings extends Object {
        /**
         * autoStartLoad
         *
         * if set to true,
         *      start level playlist and first fragments will be loaded automatically,
         *      after triggering of HlsEvent.MANIFEST_PARSED event
         * if set to false,
         *      an explicit API call (hls.startLoad()) will be needed
         *      to start quality level/fragment loading.
         *
         * Default is true
         */
        public static var autoStartLoad : Boolean = true;
        /**
         * capLevelToStage
         *
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;

        /**
         * maxLevelCappingMode
         *
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;

        // // // // // // /////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // // /////////////////////////////////
        /**
         * minBufferLength
         *
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;

        /**
         * minBufferLengthCapping
         *
         * Defines minimum buffer length capping value (max value) if minBufferLength is set to -1
         *
         * Default is -1 = no capping
         */
        public static var minBufferLengthCapping : Number = -1;

        /**
         * maxBufferLength
         *
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 120
         */
        public static var maxBufferLength : Number = 120;

        /**
         * maxBackBufferLength
         *
         * Defines maximum back buffer length in seconds.
         * (0 means infinite back buffering)
         *
         * Default is 30
         */
        public static var maxBackBufferLength : Number = 30;

        /**
         * lowBufferLength
         *
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3
         */
        public static var lowBufferLength : Number = 3;

        /**
         * mediaTimeUpdatePeriod
         *
         * time update period in ms
         * period at which HLSEvent.MEDIA_TIME will be triggered
         * Default is 100 ms
         */
        public static var mediaTimePeriod : int = 100;

        /**
         * fpsDroppedMonitoringPeriod
         *
         * dropped FPS Monitor Period in ms
         * period at which nb dropped FPS will be checked
         * Default is 5000 ms
         */
        public static var fpsDroppedMonitoringPeriod : int = 5000;

        /**
         * fpsDroppedMonitoringThreshold
         *
         * dropped FPS Threshold
         * every fpsDroppedMonitoringPeriod, dropped FPS will be compared to displayed FPS.
         * if during that period, ratio of (dropped FPS/displayed FPS) is greater or equal
         * than fpsDroppedMonitoringThreshold, HLSEvent.FPS_DROP event will be fired
         * Default is 0.2 (20%)
         */
        public static var fpsDroppedMonitoringThreshold : Number = 0.2;

        /**
         * capLevelonFPSDrop
         *
         * Limit levels usable in auto-quality when FPS drop is detected
         * i.e. if frame drop is detected on level 5, auto level will be capped to level 4 a
         *      true -  enabled
         *      false - disabled
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is true
         */
        public static var capLevelonFPSDrop : Boolean = true;

        /**
         * smoothAutoSwitchonFPSDrop
         *
         * force a smooth level switch Limit when FPS drop is detected in auto-quality
         * i.e. if frame drop is detected on level 5, it will trigger an auto quality level switch
         * to level 4 for next fragment
         *      true -  enabled
         *      false - disabled
         *
         * Note: this setting is active only if capLevelonFPSDrop==true
         *
         * Default is true
         */
        public static var smoothAutoSwitchonFPSDrop : Boolean = true;

        /**
         * seekMode
         *
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *
         * Default is HLSSeekMode.KEYFRAME_SEEK
         */
        public static var seekMode : String = HLSSeekMode.KEYFRAME_SEEK;

        /**
         * keyLoadMaxRetry
         *
         * Max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         *
         * Default is 3
         */
        public static var keyLoadMaxRetry : int = 3;

        /**
         * keyLoadMaxRetryTimeout
         *
         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;

        /**
         * fragmentLoadMaxRetry
         *
         * Max number of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         *
         * Default is 3
         */
        public static var fragmentLoadMaxRetry : int = 3;

        /**
         * fragmentLoadMaxRetryTimeout
         *
         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 4000
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 4000

        /**
         * fragmentLoadSkipAfterMaxRetry
         *
         * control behaviour in case fragment load still fails after max retry timeout
         * if set to true, fragment will be skipped and next one will be loaded.
         * If set to false, an I/O Error will be raised.
         *
         * Default is true.
         */
        public static var fragmentLoadSkipAfterMaxRetry : Boolean = true;

        /**
         * flushLiveURLCache
         *
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;

        /**
         * manifestLoadMaxRetry
         *
         * max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = 3;

        /**
         * manifestLoadMaxRetryTimeout
         *
         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;

        /**
         * startFromBitrate
         *
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         *
         * Default is -1
         */
        public static var startFromBitrate : Number = -1;

        /**
         * startFromLevel
         *
         * start level :
         *  from 0 to 1 : indicates the "normalized" preferred level. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         * -2 : playback will start from the first level appearing in Manifest (not sorted by bitrate)
         *
         * Default is -1
         */
        public static var startFromLevel : Number = -1;

        /**
         * autoStartMaxDuration
         *
         * max fragment loading duration in automatic start level selection mode (in ms)
         * -1 : max duration not capped
         * other : max duration is capped to avoid long playback starting time
         *
         * Default is -1
         */
        public static var autoStartMaxDuration : Number = -1;

        /**
         * seekFromLevel
         *
         * Seek level:
         *  from 0 to 1: indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         *
         * Default is -1
         */
        public static var seekFromLevel : Number = -1;

        /**
         * useHardwareVideoDecoder
         *
         * Use hardware video decoder:
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         *
         * Default is true
         */
        public static var useHardwareVideoDecoder : Boolean = true;

        /**
         * logInfo
         *
         * Defines whether INFO level log messages will will appear in the console
         *
         * Default is true
         */
        public static var logInfo : Boolean = true;

        /**
         * logDebug
         *
         * Defines whether DEBUG level log messages will will appear in the console
         *
         * Default is false
         */
        public static var logDebug : Boolean = false;

        /**
         * logDebug2
         *
         * Defines whether DEBUG2 level log messages will will appear in the console
         *
         * Default is false
         */
        public static var logDebug2 : Boolean = false;

        /**
         * logWarn
         *
         * Defines whether WARN level log messages will will appear in the console
         *
         * Default is true
         */
        public static var logWarn : Boolean = true;

        /**
         * logError
         *
         * Defines whether ERROR level log messages will will appear in the console
         *
         * Default is true
         */
        public static var logError : Boolean = true;
    }
}

/* 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 {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;

    public final class HLSSettings extends Object {
        /**
         * autoStartLoad
         *
         * if set to true,
         *      start level playlist and first fragments will be loaded automatically,
         *      after triggering of HlsEvent.MANIFEST_PARSED event
         * if set to false,
         *      an explicit API call (hls.startLoad()) will be needed
         *      to start quality level/fragment loading.
         *
         * Default is true
         */
        public static var autoStartLoad : Boolean = true;
        /**
         * capLevelToStage
         *
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;

        /**
         * maxLevelCappingMode
         *
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;

        // // // // // // /////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // // /////////////////////////////////
        /**
         * minBufferLength
         *
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;

        /**
         * minBufferLengthCapping
         *
         * Defines minimum buffer length capping value (max value) if minBufferLength is set to -1
         *
         * Default is -1 = no capping
         */
        public static var minBufferLengthCapping : Number = -1;

        /**
         * maxBufferLength
         *
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 120
         */
        public static var maxBufferLength : Number = 120;

        /**
         * maxBackBufferLength
         *
         * Defines maximum back buffer length in seconds.
         * (0 means infinite back buffering)
         *
         * Default is 30
         */
        public static var maxBackBufferLength : Number = 30;

        /**
         * lowBufferLength
         *
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3
         */
        public static var lowBufferLength : Number = 3;

        /**
         * mediaTimeUpdatePeriod
         *
         * time update period in ms
         * period at which HLSEvent.MEDIA_TIME will be triggered
         * Default is 100 ms
         */
        public static var mediaTimePeriod : int = 100;

        /**
         * fpsDroppedMonitoringPeriod
         *
         * dropped FPS Monitor Period in ms
         * period at which nb dropped FPS will be checked
         * Default is 5000 ms
         */
        public static var fpsDroppedMonitoringPeriod : int = 5000;

        /**
         * fpsDroppedMonitoringThreshold
         *
         * dropped FPS Threshold
         * every fpsDroppedMonitoringPeriod, dropped FPS will be compared to displayed FPS.
         * if during that period, ratio of (dropped FPS/displayed FPS) is greater or equal
         * than fpsDroppedMonitoringThreshold, HLSEvent.FPS_DROP event will be fired
         * Default is 0.2 (20%)
         */
        public static var fpsDroppedMonitoringThreshold : Number = 0.2;

        /**
         * capLevelonFPSDrop
         *
         * Limit levels usable in auto-quality when FPS drop is detected
         * i.e. if frame drop is detected on level 5, auto level will be capped to level 4 a
         *      true -  enabled
         *      false - disabled
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelonFPSDrop : Boolean = false;

        /**
         * smoothAutoSwitchonFPSDrop
         *
         * force a smooth level switch Limit when FPS drop is detected in auto-quality
         * i.e. if frame drop is detected on level 5, it will trigger an auto quality level switch
         * to level 4 for next fragment
         *      true -  enabled
         *      false - disabled
         *
         * Note: this setting is active only if capLevelonFPSDrop==true
         *
         * Default is true
         */
        public static var smoothAutoSwitchonFPSDrop : Boolean = true;

        /**
         * seekMode
         *
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *
         * Default is HLSSeekMode.KEYFRAME_SEEK
         */
        public static var seekMode : String = HLSSeekMode.KEYFRAME_SEEK;

        /**
         * keyLoadMaxRetry
         *
         * Max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         *
         * Default is 3
         */
        public static var keyLoadMaxRetry : int = 3;

        /**
         * keyLoadMaxRetryTimeout
         *
         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;

        /**
         * fragmentLoadMaxRetry
         *
         * Max number of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         *
         * Default is 3
         */
        public static var fragmentLoadMaxRetry : int = 3;

        /**
         * fragmentLoadMaxRetryTimeout
         *
         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 4000
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 4000

        /**
         * fragmentLoadSkipAfterMaxRetry
         *
         * control behaviour in case fragment load still fails after max retry timeout
         * if set to true, fragment will be skipped and next one will be loaded.
         * If set to false, an I/O Error will be raised.
         *
         * Default is true.
         */
        public static var fragmentLoadSkipAfterMaxRetry : Boolean = true;

        /**
         * flushLiveURLCache
         *
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;

        /**
         * manifestLoadMaxRetry
         *
         * max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = 3;

        /**
         * manifestLoadMaxRetryTimeout
         *
         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;

        /**
         * startFromBitrate
         *
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         *
         * Default is -1
         */
        public static var startFromBitrate : Number = -1;

        /**
         * startFromLevel
         *
         * start level :
         *  from 0 to 1 : indicates the "normalized" preferred level. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         * -2 : playback will start from the first level appearing in Manifest (not sorted by bitrate)
         *
         * Default is -1
         */
        public static var startFromLevel : Number = -1;

        /**
         * autoStartMaxDuration
         *
         * max fragment loading duration in automatic start level selection mode (in ms)
         * -1 : max duration not capped
         * other : max duration is capped to avoid long playback starting time
         *
         * Default is -1
         */
        public static var autoStartMaxDuration : Number = -1;

        /**
         * seekFromLevel
         *
         * Seek level:
         *  from 0 to 1: indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         *
         * Default is -1
         */
        public static var seekFromLevel : Number = -1;

        /**
         * useHardwareVideoDecoder
         *
         * Use hardware video decoder:
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         *
         * Default is true
         */
        public static var useHardwareVideoDecoder : Boolean = true;

        /**
         * logInfo
         *
         * Defines whether INFO level log messages will will appear in the console
         *
         * Default is true
         */
        public static var logInfo : Boolean = true;

        /**
         * logDebug
         *
         * Defines whether DEBUG level log messages will will appear in the console
         *
         * Default is false
         */
        public static var logDebug : Boolean = false;

        /**
         * logDebug2
         *
         * Defines whether DEBUG2 level log messages will will appear in the console
         *
         * Default is false
         */
        public static var logDebug2 : Boolean = false;

        /**
         * logWarn
         *
         * Defines whether WARN level log messages will will appear in the console
         *
         * Default is true
         */
        public static var logWarn : Boolean = true;

        /**
         * logError
         *
         * Defines whether ERROR level log messages will will appear in the console
         *
         * Default is true
         */
        public static var logError : Boolean = true;
    }
}

disable capLevelonFPSDrop feature

disable capLevelonFPSDrop feature

it is currently broken


package ember.core
{
	import mocks.MockComponent;
	import mocks.MockNode;
	import mocks.MockOptionalComponent;
	import mocks.MockOptionalNode;

	import org.hamcrest.assertThat;
	import org.hamcrest.object.equalTo;
	import org.hamcrest.object.isTrue;
	import org.hamcrest.object.notNullValue;
	import org.hamcrest.object.nullValue;
	import org.hamcrest.object.sameInstance;

	public class NodesManagerTests
	{
		private var _entities:Entities;
		private var _manager:NodesManager;

		[Before]
		public function before():void
		{
			_entities = new Entities();
			_manager = new NodesManager(_entities);
		}

		[After]
		public function after():void
		{
			_entities = null;
			_manager = null;
		}

		[Test]
		public function you_create_a_set_by_node_class():void
		{
			assertThat(_manager.get(MockNode), notNullValue());
		}

		[Test]
		public function nodes_contains_a_node_that_represents_each_member_entity():void
		{
			var list:Array = [];
			for (var i:int = 0; i < 7; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
				list[i] = entity;
			}

			var nodes:Nodes = _manager.get(MockNode);
			var foundCount:uint = 0;
			for (var node:MockNode = nodes.head; node; node = node.next)
				list.indexOf(node.entity) != -1 && ++foundCount;

			assertThat(foundCount, equalTo(7));
		}

		[Test]
		public function nodes_does_not_contain_a_node_that_misses_component():void
		{
			for (var i:int = 0; i < 8; i++)
				_entities.create();

			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.count, equalTo(0));
		}

		[Test]
		public function if_an_enititys_components_no_longer_satisfy_requirements_it_is_removed():void
		{
			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);
			var wasAdded:Boolean = nodes.count == 1;

			entity.removeComponent(MockComponent);
			var wasRemoved:Boolean = nodes.count == 0;

			assertThat(wasAdded && wasRemoved, isTrue());
		}

		[Test]
		public function once_nodes_is_defined_if_an_entity_later_satisfies_requirements_it_is_added():void
		{
			var entity:Entity = _entities.create();

			var nodes:Nodes = _manager.get(MockNode);
			var noNodes:Boolean = nodes.count == 0;

			entity.addComponent(new MockComponent());
			var nodeAdded:Boolean = nodes.count == 1;

			assertThat(noNodes && nodeAdded, isTrue());
		}

		[Test]
		public function nodes_are_not_duplicated_per_request():void
		{
			var nodes:Nodes = _manager.get(MockNode);
			assertThat(_manager.get(MockNode), sameInstance(nodes));
		}

		[Test]
		public function count_is_0_when_clear_is_called():void
		{
			for (var i:int = 0; i < 11; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			_manager.clear();
			assertThat(nodes.count, equalTo(0));
		}

		[Test]
		public function all_nodes_are_removed_when_clear_is_called():void
		{
			for (var i:int = 0; i < 11; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			_manager.clear();
			assertThat(nodes.head, nullValue());
		}

		[Test]
		public function optional_component_is_added_to_existing_node_when_added_to_component():void
		{
			var component:MockOptionalComponent = new MockOptionalComponent();

			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());
			entity.addComponent(component);

			var nodes:Nodes = _manager.get(MockOptionalNode);

			assertThat(nodes.head.optional, sameInstance(component));
		}

		[Test]
		public function optional_component_is_removed_from_existing_node_when_removed_from_component():void
		{
			var component:MockOptionalComponent = new MockOptionalComponent();

			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());
			entity.addComponent(component);

			var nodes:Nodes = _manager.get(MockOptionalNode);

			entity.removeComponent(MockOptionalComponent);
			assertThat(nodes.head.optional, nullValue());
		}

		[Test]
		public function an_empty_nodes_reports_0_count():void
		{
			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.count, equalTo(0));
		}

		[Test]
		public function nodes_reports_accurate_count_after_construction():void
		{
			for (var i:int = 0; i < 5; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.count, equalTo(5));
		}

		[Test]
		public function nodes_count_is_maintained_as_entities_are_added():void
		{
			var entity:Entity, i:int;

			var count:uint = 5;
			for (i = 0; i < count; i++)
			{
				entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			for (i = 0; i < 3; i++)
			{
				entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			assertThat(nodes.count, equalTo(5 + 3));
		}

		[Test]
		public function nodes_count_is_maintained_as_entities_are_removed():void
		{
			var entity:Entity, i:int;

			var count:uint = 10;
			for (i = 0; i < count; i++)
			{
				entity = _entities.create("id" + i);
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			var removed:uint = 3;
			for (i = 0; i < removed; i++)
			{
				entity = _entities.get("id" + i);
				entity.removeComponent(MockComponent);
			}

			assertThat(nodes.count, equalTo(10 - 3));
		}

		[Test]
		public function a_node_can_be_referenced_in_nodes_by_entity():void
		{
			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.get(entity), notNullValue());
		}

		[Test]
		public function when_an_entity_is_removed_any_corresponding_nodes_are_removed():void
		{
			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);
			_manager.removeEntity(entity);

			assertThat(nodes.get(entity), nullValue());
		}

		[Test]
		public function when_a_node_is_removed_during_a_loop_the_loop_isnt_disrupted():void
		{
			var a:Entity = _entities.create("a");
			a.addComponent(new MockComponent());

			var b:Entity = _entities.create("b");
			b.addComponent(new MockComponent());

			var c:Entity = _entities.create("c");
			c.addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);
			var count:uint = 0;
			for (var node:MockNode = nodes.head as MockNode; node; node = node.next)
			{
				node.entity.removeComponent(MockComponent);
				++count;
			}

			assertThat(count, equalTo(3));
		}

	}

}

package ember.core
{
	import mocks.MockComponent;
	import mocks.MockNode;
	import mocks.MockOptionalComponent;
	import mocks.MockOptionalNode;

	import org.hamcrest.assertThat;
	import org.hamcrest.object.equalTo;
	import org.hamcrest.object.isTrue;
	import org.hamcrest.object.notNullValue;
	import org.hamcrest.object.nullValue;
	import org.hamcrest.object.sameInstance;

	public class NodesManagerTests
	{
		private var _entities:Entities;
		private var _manager:NodesManager;

		[Before]
		public function before():void
		{
			_entities = new Entities();
			_manager = new NodesManager(_entities);
		}

		[After]
		public function after():void
		{
			_entities = null;
			_manager = null;
		}

		[Test]
		public function you_create_a_set_by_node_class():void
		{
			assertThat(_manager.get(MockNode), notNullValue());
		}

		[Test]
		public function nodes_contains_a_node_that_represents_each_member_entity():void
		{
			var list:Array = [];
			for (var i:int = 0; i < 7; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
				list[i] = entity;
			}

			var nodes:Nodes = _manager.get(MockNode);
			var foundCount:uint = 0;
			for (var node:MockNode = nodes.head; node; node = node.next)
				list.indexOf(node.entity) != -1 && ++foundCount;

			assertThat(foundCount, equalTo(7));
		}

		[Test]
		public function nodes_does_not_contain_a_node_that_misses_component():void
		{
			for (var i:int = 0; i < 8; i++)
				_entities.create();

			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.count, equalTo(0));
		}

		[Test]
		public function if_an_enititys_components_no_longer_satisfy_requirements_it_is_removed():void
		{
			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);
			var wasAdded:Boolean = nodes.count == 1;

			entity.removeComponent(MockComponent);
			var wasRemoved:Boolean = nodes.count == 0;

			assertThat(wasAdded && wasRemoved, isTrue());
		}

		[Test]
		public function once_nodes_is_defined_if_an_entity_later_satisfies_requirements_it_is_added():void
		{
			var entity:Entity = _entities.create();

			var nodes:Nodes = _manager.get(MockNode);
			var noNodes:Boolean = nodes.count == 0;

			entity.addComponent(new MockComponent());
			var nodeAdded:Boolean = nodes.count == 1;

			assertThat(noNodes && nodeAdded, isTrue());
		}

		[Test]
		public function nodes_are_not_duplicated_per_request():void
		{
			var nodes:Nodes = _manager.get(MockNode);
			assertThat(_manager.get(MockNode), sameInstance(nodes));
		}

		[Test]
		public function count_is_0_when_clear_is_called():void
		{
			for (var i:int = 0; i < 11; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			_manager.clear();
			assertThat(nodes.count, equalTo(0));
		}

		[Test]
		public function all_nodes_are_removed_when_clear_is_called():void
		{
			for (var i:int = 0; i < 11; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			_manager.clear();
			assertThat(nodes.head, nullValue());
		}

		[Test]
		public function optional_component_is_added_to_existing_node_when_added_to_component():void
		{
			var component:MockOptionalComponent = new MockOptionalComponent();

			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());
			entity.addComponent(component);

			var nodes:Nodes = _manager.get(MockOptionalNode);

			assertThat(nodes.head.optional, sameInstance(component));
		}

		[Test]
		public function optional_component_is_removed_from_existing_node_when_removed_from_component():void
		{
			var component:MockOptionalComponent = new MockOptionalComponent();

			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());
			entity.addComponent(component);

			var nodes:Nodes = _manager.get(MockOptionalNode);

			entity.removeComponent(MockOptionalComponent);
			assertThat(nodes.head.optional, nullValue());
		}

		[Test]
		public function an_empty_nodes_reports_0_count():void
		{
			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.count, equalTo(0));
		}

		[Test]
		public function nodes_reports_accurate_count_after_construction():void
		{
			for (var i:int = 0; i < 5; i++)
			{
				var entity:Entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.count, equalTo(5));
		}

		[Test]
		public function nodes_count_is_maintained_as_entities_are_added():void
		{
			var entity:Entity, i:int;

			var count:uint = 5;
			for (i = 0; i < count; i++)
			{
				entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			for (i = 0; i < 3; i++)
			{
				entity = _entities.create();
				entity.addComponent(new MockComponent());
			}

			assertThat(nodes.count, equalTo(5 + 3));
		}

		[Test]
		public function nodes_count_is_maintained_as_entities_are_removed():void
		{
			var entity:Entity, i:int;

			var count:uint = 10;
			for (i = 0; i < count; i++)
			{
				entity = _entities.create("id" + i);
				entity.addComponent(new MockComponent());
			}

			var nodes:Nodes = _manager.get(MockNode);

			var removed:uint = 3;
			for (i = 0; i < removed; i++)
			{
				entity = _entities.get("id" + i);
				entity.removeComponent(MockComponent);
			}

			assertThat(nodes.count, equalTo(10 - 3));
		}

		[Test]
		public function a_node_can_be_referenced_in_nodes_by_entity():void
		{
			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);
			assertThat(nodes.get(entity), notNullValue());
		}

		[Test]
		public function when_an_entity_is_removed_any_corresponding_nodes_are_removed():void
		{
			var entity:Entity = _entities.create();
			entity.addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);
			_manager.removeEntity(entity);

			assertThat(nodes.get(entity), nullValue());
		}

		[Test]
		public function when_a_node_is_removed_during_a_loop_the_loop_isnt_disrupted():void
		{
			for (var i:int = 0; i < 3; i++)
				_entities.create().addComponent(new MockComponent());

			var nodes:Nodes = _manager.get(MockNode);

			var count:uint = 0;
			for (var node:MockNode = nodes.head as MockNode; node; node = node.next)
			{
				node.entity.removeComponent(MockComponent);
				++count;
			}

			assertThat(count, equalTo(3));
		}

	}

}

Simplify test for #11

Simplify test for #11


package aerys.minko.render.geometry.primitive
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.IndexStream;
	import aerys.minko.render.geometry.stream.StreamUsage;
	import aerys.minko.render.geometry.stream.VertexStream;
	import aerys.minko.render.geometry.stream.format.VertexComponent;
	import aerys.minko.render.geometry.stream.format.VertexFormat;
	import aerys.minko.type.math.Vector4;

	public class TorusGeometry extends Geometry
	{
		private static const EPSILON	: Number	= 0.00001;
		private static const DTOR 		: Number	= 0.01745329252;

		private var _radius		: Number;
		private var _tube		: Number;
		private var _segmentsR	: uint;
		private var _segmentsT	: uint;
		private var _arc		: Number;

		public function TorusGeometry(radius				: Number	= 100.,
									  tube					: Number	= 40.,
									  segmentsR				: uint		= 8,
									  segmentsT				: uint		= 6,
									  arc					: Number	= Math.PI * 2.,
									  withUVs				: Boolean	= true,
									  withNormals			: Boolean	= true,
									  vertexStreamUsage		: uint		= StreamUsage.STATIC,
									  indexStreamUsage		: uint		= StreamUsage.STATIC)
		{
			_radius = radius;
			_tube = tube;
			_segmentsR = segmentsR;
			_segmentsT = segmentsT;
			_arc = arc;

			super(
				new <IVertexStream>[buildVertexStream(vertexStreamUsage, withUVs, withNormals)],
				buildIndexStream(indexStreamUsage)
			);
		}

		private function buildVertexStream(usage : uint, withUVs : Boolean, withNormals : Boolean) : VertexStream
		{
			var vertexData	: Vector.<Number>	= new <Number>[];

			for (var j : uint = 0; j <= _segmentsR; ++j)
			{
				for (var i : uint = 0; i <= _segmentsT; ++i)
				{
					var u 		: Number 	= i / _segmentsT * _arc;
					var v 		: Number 	= j / _segmentsR * _arc;
					var cosU	: Number	= Math.cos(u);
					var sinU	: Number	= Math.sin(u);
					var cosV	: Number	= Math.cos(v);
					var x		: Number	= (_radius + _tube * cosV) * cosU;
					var y		: Number	= (_radius + _tube * cosV) * sinU;
					var z		: Number	= _tube * Math.sin(v);

					vertexData.push(x, y, z);

					if (withUVs)
						vertexData.push(i / _segmentsT, 1 - j / _segmentsR);

					if (withNormals)
					{
						var normalX	: Number	= x - _radius * cosU;
						var normalY	: Number	= y - _radius * sinU;
						var normalZ	: Number	= z;
						var mag		: Number	= normalX * normalX + normalY * normalY
							+ normalZ * normalZ;

						normalX /= mag;
						normalY /= mag;
						normalZ /= mag;

						vertexData.push(normalX, normalY, normalZ);
					}
				}
			}

			var format : VertexFormat = new VertexFormat(VertexComponent.XYZ);

			if (withUVs)
				format.addComponent(VertexComponent.UV);

			if (withNormals)
				format.addComponent(VertexComponent.NORMAL);

			return new VertexStream(usage, format, vertexData);
		}

		private function buildIndexStream(usage : uint) : IndexStream
		{
			var indices : Vector.<uint> = new <uint>[];

			for (var j : uint = 1; j <= _segmentsR; ++j)
			{
				for (var i : uint = 1; i <= _segmentsT; ++i)
				{
					var a : uint = (_segmentsT + 1) * j + i - 1;
					var b : uint = (_segmentsT + 1) * (j - 1) + i - 1;
					var c : uint = (_segmentsT + 1) * (j - 1) + i;
					var d : uint = (_segmentsT + 1) * j + i;

					indices.push(a, c, b, a, d, c);
				}
			}

			return new IndexStream(usage, indices);
		}
	}
}

add TorusGeometry

add TorusGeometry


/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * 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 Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2007-2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
/**
   Description:
   Runtime specialization
   Specialization of the Vector type can be done at runtime
   instead of at compile time.
*/

function getVector() { return Vector; }

var CODE = 1007; //  Instantiation attempted on a non-constructor.

startTest();
var TITLE="Runtime specialization";
writeHeaderToLog(TITLE);

var x = getVector().<int>;
y = new x();

y.push(1);
y.push(2);
y.push(3);

AddTestCase(    "Vector constructed via runtime specialization",
                3,
                y.length);

AddTestCase(    "Vector constructed via runtime specialization",
                2,
                y[1]);

var neg_result = "Failed to catch invalid construct";

try {
   unspecialized = getVector();
   var z = new unspecialized();
} catch ( ex )
{
    neg_result = String(ex);
}

AddTestCase ( "Invalid use of unspecialized type in constructor", TYPEERROR + CODE,  typeError(neg_result));

test();

Fix 525654 - added test for runtime Vector specialization


package dolly {

import dolly.core.dolly_internal;
import dolly.data.PropertyLevelCopyableCloneableClass;

import org.as3commons.reflect.Type;

use namespace dolly_internal;

public class CloningOfPropertyLevelCopyableCloneableClassTest {

	private var propertyLevelCopyableCloneable:PropertyLevelCopyableCloneableClass;
	private var propertyLevelCopyableCloneableType:Type;

	[Before]
	public function before():void {
		propertyLevelCopyableCloneable = new PropertyLevelCopyableCloneableClass();
		propertyLevelCopyableCloneable.property1 = "property1 value";
		propertyLevelCopyableCloneable.writableField1 = "writableField1 value";
		propertyLevelCopyableCloneableType = Type.forInstance(propertyLevelCopyableCloneable);
	}

	[After]
	public function after():void {
		propertyLevelCopyableCloneable = null;
		propertyLevelCopyableCloneableType = null;
	}

	/**
	 * Cloner.findingAllWritableFieldsForType() method will throw CloningError in this case
	 * because PropertyLevelCopyableCloneableClass class is not cloneable indeed.
	 */
	[Test(expects="dolly.core.errors.CloningError")]
	public function findingAllWritableFieldsForType():void {
		Cloner.findAllWritableFieldsForType(propertyLevelCopyableCloneableType);
	}

	/**
	 * Cloner.clone() method will throw CloningError in this case
	 * because PropertyLevelCopyableCloneableClass class is not cloneable indeed.
	 */
	[Test(expects="dolly.core.errors.CloningError")]
	public function cloningByCloner():void {
		Cloner.clone(propertyLevelCopyableCloneable);
	}

	/**
	 * Method clone() will throw CloningError in this case
	 * because PropertyLevelCopyableCloneableClass class is not cloneable indeed.
	 */
	[Test(expects="dolly.core.errors.CloningError")]
	public function cloningByCloneFunction():void {
		clone(propertyLevelCopyableCloneable);
	}
}
}

Test class for cloning of PropertyLevelCopyableCloneableClass.


package com.threerings.util {

import flash.display.Loader;
import flash.errors.IllegalOperationError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.utils.ByteArray;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;

/**
 * Allows you to load an embeded SWF stored as a Byte Array then access any stored classes
 * within the SWF.
 */
public class EmbededClassLoader extends EventDispatcher
{
    public static var SWF_LOADED :String = "swfLoaded";
    public static var LOAD_ERROR :String = "loadError";

    public function EmbededClassLoader ()
    {
        _loader = new Loader();
        _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
        _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
    }

    /**
     * Load the SWF from a Byte Array.  A CLASS_LOADED event will be dispatched on successful
     * completion of the load.  If any errors occur, a LOAD_ERROR event will be dispatched.
     */
    public function load (byteArray :ByteArray) :void
    {
        var context :LoaderContext = new LoaderContext();
        context.applicationDomain = ApplicationDomain.currentDomain;
        _loader.loadBytes(byteArray, context);
    }

    /**
     * Retrieves a class definition from the loaded swf.
     *
     * @throws IllegalOperationError when the swf has not completed loading or the class does
     * not exist
     */
    public function getClass (className :String) :Class
    {
        if (_loader.contentLoaderInfo.bytesLoaded != _loader.contentLoaderInfo.bytesTotal) {
            throw new IllegalOperationError("SWF has not completed loading");
        }
        try {
            return _loader.contentLoaderInfo.applicationDomain.getDefinition(className) as Class;
        } catch (e: Error) {
            throw new IllegalOperationError(className + " definition not found");
        }
        return null;
    }

    /**
     * Checks if a class exists in the library.
     *
     * @throws IllegalOperationError when the swf has not completed loading.
     */
    public function isClass (className :String) :Boolean
    {
        if (_loader.contentLoaderInfo.bytesLoaded != _loader.contentLoaderInfo.bytesTotal) {
            throw new IllegalOperationError("SWF has not completed loading");
        }
        return _loader.contentLoaderInfo.applicationDomain.hasDefinition(className);
    }

    protected function completeHandler (e :Event) :void
    {
        dispatchEvent(new Event(SWF_LOADED));
    }

    protected function ioErrorHandler (E :Event) :void
    {
        dispatchEvent(new Event(LOAD_ERROR));
    }

    protected var _loader :Loader;
}
}

Use the EmbededClassLoader to get full access to an embeded swf file, including all classes,
functions and namespaces defined in it.

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@4502 542714f4-19e9-0310-aa3c-eee0fc999fb1


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

import org.apache.flex.events.Event;

[Event(name="timer", type="org.apache.flex.events.Event")]

public class Timer extends flash.utils.Timer
{
    public function Timer(delay:Number, repeatCount:int = 0)
    {
        super(delay, repeatCount);
		addEventListener("timer", interceptor, false, 9999);
    }

	private function interceptor(event:flash.events.Event):void
	{
		if (event is TimerEvent)
		{
			event.stopImmediatePropagation();
			dispatchEvent(new Event("timer"));
		}
	}
}
}

add Timer class

add Timer class


// Urho2D tile map example.
// This sample demonstrates:
//     - Creating a 2D scene with tile map
//     - Displaying the scene using the Renderer subsystem
//     - Handling keyboard to move and zoom 2D camera

#include "Scripts/Utilities/Sample.as"

void Start()
{
    // Execute the common startup for samples
    SampleStart();

    // Create the scene content
    CreateScene();

    // Create the UI content
    CreateInstructions();

    // Setup the viewport for displaying the scene
    SetupViewport();

    // Hook up to the frame update events
    SubscribeToEvents();
}

void CreateScene()
{
    scene_ = Scene();

    // Create the Octree component to the scene. This is required before adding any drawable components, or else nothing will
    // show up. The default octree volume will be from (-1000, -1000, -1000) to (1000, 1000, 1000) in world coordinates; it
    // is also legal to place objects outside the volume but their visibility can then not be checked in a hierarchically
    // optimizing manner
    scene_.CreateComponent("Octree");

    // Create a scene node for the camera, which we will move around
    // The camera will use default settings (1000 far clip distance, 45 degrees FOV, set aspect ratio automatically)
    cameraNode = scene_.CreateChild("Camera");
    // Set an initial position for the camera scene node above the plane
    cameraNode.position = Vector3(0.0f, 0.0f, -10.0f);

    Camera@ camera = cameraNode.CreateComponent("Camera");
    camera.orthographic = true;
    camera.orthoSize = graphics.height * PIXEL_SIZE;

    // Get tmx file
    TmxFile2D@ tmxFile = cache.GetResource("TmxFile2D", "Urho2D/isometric_grass_and_water.tmx");
    if (tmxFile == null)
        return;

    Node@ tileMapNode = scene_.CreateChild("TileMap");
    tileMapNode.position = Vector3(0.0f, 0.0f, -1.0f);

    TileMap2D@ tileMap = tileMapNode.CreateComponent("TileMap2D");
    tileMap.tmxFile = tmxFile;

    // Set camera's position;
    float x = tileMap.width * tileMap.tileWidth * 0.5f;
    float y = tileMap.height * tileMap.tileHeight * 0.5f;
    cameraNode.position = Vector3(x, y, -10.0f);
}

void CreateInstructions()
{
    // Construct new Text object, set string to display and font to use
    Text@ instructionText = ui.root.CreateChild("Text");
    instructionText.text = "Use WASD keys and mouse to move, Use PageUp PageDown to zoom.";
    instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);

    // Position the text relative to the screen center
    instructionText.horizontalAlignment = HA_CENTER;
    instructionText.verticalAlignment = VA_CENTER;
    instructionText.SetPosition(0, ui.root.height / 4);
}

void SetupViewport()
{
    // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen. We need to define the scene and the camera
    // at minimum. Additionally we could configure the viewport screen size and the rendering path (eg. forward / deferred) to
    // use, but now we just use full screen and default render path configured in the engine command line options
    Viewport@ viewport = Viewport(scene_, cameraNode.GetComponent("Camera"));
    renderer.viewports[0] = viewport;
}

void MoveCamera(float timeStep)
{
    // Do not move if the UI has a focused element (the console)
    if (ui.focusElement !is null)
        return;

    // Movement speed as world units per second
    const float MOVE_SPEED = 4.0f;

    // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
    if (input.keyDown['W'])
        cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
    if (input.keyDown['S'])
        cameraNode.Translate(Vector3(0.0f, -1.0f, 0.0f) * MOVE_SPEED * timeStep);
    if (input.keyDown['A'])
        cameraNode.Translate(Vector3(-1.0f, 0.0f, 0.0f) * MOVE_SPEED * timeStep);
    if (input.keyDown['D'])
        cameraNode.Translate(Vector3(1.0f, 0.0f, 0.0f) * MOVE_SPEED * timeStep);

    if (input.keyDown[KEY_PAGEUP])
    {
        Camera@ camera = cameraNode.GetComponent("Camera");
        camera.zoom = camera.zoom * 1.01f;
    }

    if (input.keyDown[KEY_PAGEDOWN])
    {
        Camera@ camera = cameraNode.GetComponent("Camera");
        camera.zoom = camera.zoom * 0.99f;
    }
}

void SubscribeToEvents()
{
    // Subscribe HandleUpdate() function for processing update events
    SubscribeToEvent("Update", "HandleUpdate");

    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
    UnsubscribeFromEvent("SceneUpdate");
}

void HandleUpdate(StringHash eventType, VariantMap& eventData)
{
    // Take the frame time step, which is stored as a float
    float timeStep = eventData["TimeStep"].GetFloat();

    // Move the camera, scale movement with time step
    MoveCamera(timeStep);
}

// Create XML patch instructions for screen joystick layout specific to this sample app
String patchInstructions =
        "<patch>" +
        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
        "        <element type=\"Text\">" +
        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
        "            <attribute name=\"Text\" value=\"PAGEUP\" />" +
        "        </element>" +
        "    </add>" +
        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
        "        <element type=\"Text\">" +
        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
        "        </element>" +
        "    </add>" +
        "</patch>";

Add tile map AngelScript sample.

Add tile map AngelScript sample.


package org.denivip.osmf.utility
{
	/*
	 * Contains Utility functions for relative / absolute url.
	 * Main differnce is if relative url is starting with slash "/" or without "/"
	 *
	 * Example:
	 * m3u8:
		http://www.example.com/playlist/first/video.m3u8

	 * relative path starting without "/":
			part-1.ts
		absolute path:
			http://www.example.com/playlist/first/part-1.ts

	 * relative path starting with "/":
			/part-1.ts
		absolute path:
			http://www.example.com/part-1.ts
	 */
	public class Url
	{
		public static function absolute(rootUrl:String, url:String):String {
			if (url.search(/(ftp|file|https?):\/\//) == 0)
				return url;

			if (url.charAt(0) == '/') {
				var urlParts:Array = rootUrl.split('/', 4);
				rootUrl = urlParts[2]
				? urlParts[0] + '//' + urlParts[2]
				: urlParts[0] + '///' + urlParts[3];
				//*		'//' = http || https || ftp,	 '///' = file
				return rootUrl + url;
			};

			if (rootUrl.lastIndexOf('/') != rootUrl.length - 1) {
				rootUrl = rootUrl.substr(rootUrl.lastIndexOf('/')).indexOf('.') > -1
				? rootUrl.substr(0, rootUrl.lastIndexOf('/') + 1)
				: rootUrl + '/';
			};
			return rootUrl + url;
		}
	}
}

Create Url.as

Create Url.as

Relative to absolute path.


package com.threerings.ezgame {

import flash.display.DisplayObject;
import flash.display.Sprite;

import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;

/**
 * A sample component that displays the players of a game.
 * If the game has a turn holder, the current turn holder will be highlighted.
 *
 * This class demonstrates that the 'Game' interface may be implemented
 * by any DisplayObject that want access to the GameObject, not just the
 * actual DisplayObject that is displaying the game. Here, all we are
 * interested in is the names of the players and the current turn holder.
 *
 * You may use this, with any modifications you desire, in your game. Feel
 * free to copy/modify or extend this class.
 */
public class PlayersDisplay extends Sprite
    implements StateChangedListener
{
    /**
     * Set the game control that will be used with this display.
     */
    public function setGameControl (gameCtrl :EZGameControl) :void
    {
        _gameCtrl = gameCtrl;
        _gameCtrl.registerListener(this);

        configureInterface();
    }

    // from StateChangedListener
    public function stateChanged (event :StateChangedEvent) :void
    {
        displayCurrentTurn();
    }

    /**
     * Set up the player labels and configure the look of the entire UI.
     */
    protected function configureInterface () :void
    {
        var border :int = getBorderSpacing();
        var pad :int = getInternalSpacing();
        var y :Number = border;
        var maxWidth :Number = 0;
        var label :TextField;
        var icon :DisplayObject;

        // create a label at the top, above the player names
        label = createHeader();
        if (label != null) {
            label.x = border;
            label.y = y;
            addChild(label);
            y += label.textHeight + pad;
            maxWidth = label.textWidth;
        }

        var players :Array = _gameCtrl.getPlayers();

        // create a label for each player
        for each (var playerId :int in players) {
            var name :String = _gameCtrl.getOccupantName(playerId);
            label = createPlayerLabel(playerId, name);
            icon = createPlayerIcon(playerId, name);
            var iconW :int = 0;
            var iconH :int = 0;
            if (icon != null) {
                iconW = icon.width + pad;
                iconH = icon.height;

                icon.x = border;
                icon.y = y;
                addChild(icon);
            }
            label.x = border + iconW;
            label.y = y;
            addChild(label);
            y += Math.max(label.textHeight, iconH) + pad;
            maxWidth = Math.max(maxWidth, iconW + label.textWidth);

            _playerLabels.push(label);
        }

        // make all the player labels the same width
        // (looks nice when highlighted)
        for each (label in _playerLabels) {
            label.autoSize = TextFieldAutoSize.NONE;
            label.width = maxWidth - (label.x - border);
        }

        // y has a pad at the end, we want border instead
        y += border - pad;

        // draw a blue rectangle around everything
        graphics.clear();
        graphics.lineStyle(1, 0x0000FF);
        graphics.drawRect(0, 0, maxWidth + (border * 2), y);

        displayCurrentTurn();
    }

    protected function createHeader () :TextField
    {
        var label :TextField = createHeader();
// damn stylesheet doesn't seem to actually -work-
//        var style :StyleSheet = new StyleSheet();
//        style.fontWeight = "bold";
//        style.color = "#0000FF";
//        style.fontFamily = "serif";
//        style.fontSize = 18;
//        label.styleSheet = style;
        label.autoSize = TextFieldAutoSize.LEFT;
        label.selectable = false;
        label.text = "Players";
        return label;
    }

    /**
     * Create a TextArea that will be used to display player names.
     */
    protected function createPlayerLabel (playerId :int, name :String) :TextField
    {
        var label :TextField = new TextField();
        label.autoSize = TextFieldAutoSize.LEFT;
        label.background = true;
        label.selectable = false;
        label.text = name;
        return label;
    }

    protected function createPlayerIcon (playerId :int, name :String) :DisplayObject
    {
        return null;
    }

    protected function getBackground (isTurn :Boolean) :uint
    {
        return isTurn ? 0xFF9999 : 0xFFFFFF;
    }

    protected function getBorderSpacing () :int
    {
        return 6;
    }

    protected function getInternalSpacing () :int
    {
        return 2;
    }

    /**
     * Re-set the background color for every player label, highlighting
     * only the player who has the turn.
     */
    protected function displayCurrentTurn () :void
    {
        var idx :int = _gameCtrl.isInPlay() ? _gameCtrl.getPlayerPosition(_gameCtrl.getTurnHolder()) : -1;
        for (var ii :int = 0; ii < _playerLabels.length; ii++) {
            var label :TextField = (_playerLabels[ii] as TextField);
            label.backgroundColor = getBackground(ii == idx);
        }
    }

    /** Our game Control. */
    protected var _gameCtrl :EZGameControl;

    /** An array of labels, one for each player name. */
    protected var _playerLabels :Array = [];
}
}

package com.threerings.ezgame {

import flash.display.DisplayObject;
import flash.display.Sprite;

import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;

/**
 * A sample component that displays the players of a game.
 * If the game has a turn holder, the current turn holder will be highlighted.
 *
 * This class demonstrates that the 'Game' interface may be implemented
 * by any DisplayObject that want access to the GameObject, not just the
 * actual DisplayObject that is displaying the game. Here, all we are
 * interested in is the names of the players and the current turn holder.
 *
 * You may use this, with any modifications you desire, in your game. Feel
 * free to copy/modify or extend this class.
 */
public class PlayersDisplay extends Sprite
    implements StateChangedListener
{
    /**
     * Set the game control that will be used with this display.
     */
    public function setGameControl (gameCtrl :EZGameControl) :void
    {
        _gameCtrl = gameCtrl;
        _gameCtrl.registerListener(this);

        configureInterface();
    }

    // from StateChangedListener
    public function stateChanged (event :StateChangedEvent) :void
    {
        displayCurrentTurn();
    }

    /**
     * Set up the player labels and configure the look of the entire UI.
     */
    protected function configureInterface () :void
    {
        var border :int = getBorderSpacing();
        var pad :int = getInternalSpacing();
        var y :Number = border;
        var maxWidth :Number = 0;
        var label :TextField;
        var icon :DisplayObject;

        // create a label at the top, above the player names
        label = createHeader();
        if (label != null) {
            label.x = border;
            label.y = y;
            addChild(label);
            y += label.textHeight + pad;
            maxWidth = label.textWidth + TEXTWIDTH_ADD;
        }

        var players :Array = _gameCtrl.getPlayers();

        // create a label for each player
        for each (var playerId :int in players) {
            var name :String = _gameCtrl.getOccupantName(playerId);
            label = createPlayerLabel(playerId, name);
            icon = createPlayerIcon(playerId, name);
            var iconW :int = 0;
            var iconH :int = 0;
            if (icon != null) {
                iconW = icon.width + pad;
                iconH = icon.height;

                icon.x = border;
                icon.y = y;
                addChild(icon);
            }
            label.x = border + iconW;
            label.y = y;
            addChild(label);
            y += Math.max(label.textHeight, iconH) + pad;
            maxWidth = Math.max(maxWidth, iconW + label.textWidth + TEXTWIDTH_ADD);

            _playerLabels.push(label);
        }

        // make all the player labels the same width
        // (looks nice when highlighted)
        for each (label in _playerLabels) {
            label.autoSize = TextFieldAutoSize.NONE;
            label.width = maxWidth - (label.x - border);
        }

        // y has a pad at the end, we want border instead
        y += border - pad;

        // draw a blue rectangle around everything
        graphics.clear();
        graphics.lineStyle(1, 0x0000FF);
        graphics.drawRect(0, 0, maxWidth + (border * 2), y);

        displayCurrentTurn();
    }

    protected function createHeader () :TextField
    {
        var label :TextField = createHeader();
// damn stylesheet doesn't seem to actually -work-
//        var style :StyleSheet = new StyleSheet();
//        style.fontWeight = "bold";
//        style.color = "#0000FF";
//        style.fontFamily = "serif";
//        style.fontSize = 18;
//        label.styleSheet = style;
        label.autoSize = TextFieldAutoSize.LEFT;
        label.selectable = false;
        label.text = "Players";
        return label;
    }

    /**
     * Create a TextArea that will be used to display player names.
     */
    protected function createPlayerLabel (playerId :int, name :String) :TextField
    {
        var label :TextField = new TextField();
        label.autoSize = TextFieldAutoSize.LEFT;
        label.background = true;
        label.selectable = false;
        label.text = name;
        return label;
    }

    protected function createPlayerIcon (playerId :int, name :String) :DisplayObject
    {
        return null;
    }

    protected function getBackground (isTurn :Boolean) :uint
    {
        return isTurn ? 0xFF9999 : 0xFFFFFF;
    }

    protected function getBorderSpacing () :int
    {
        return 6;
    }

    protected function getInternalSpacing () :int
    {
        return 2;
    }

    /**
     * Re-set the background color for every player label, highlighting
     * only the player who has the turn.
     */
    protected function displayCurrentTurn () :void
    {
        var idx :int = _gameCtrl.isInPlay() ? _gameCtrl.getPlayerPosition(_gameCtrl.getTurnHolder()) : -1;
        for (var ii :int = 0; ii < _playerLabels.length; ii++) {
            var label :TextField = (_playerLabels[ii] as TextField);
            label.backgroundColor = getBackground(ii == idx);
        }
    }

    /** Our game Control. */
    protected var _gameCtrl :EZGameControl;

    /** An array of labels, one for each player name. */
    protected var _playerLabels :Array = [];

    /** These are fucking ridiculous. */
    protected static const TEXTWIDTH_ADD :int = 5;
    protected static const TEXTHEIGHT_ADD :int = 4;
}
}

Use some of my acquired special knowledge.

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


package aerys.minko.render.material.realistic
{
	import aerys.minko.render.Effect;
	import aerys.minko.render.material.environment.EnvironmentMappingProperties;
	import aerys.minko.render.material.phong.PhongMaterial;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.scene.node.Scene;

	import flash.utils.Dictionary;

	public class RealisticMaterial extends PhongMaterial
	{
		private static const DEFAULT_NAME		: String		= 'RealisticMaterial';
		private static const SCENE_TO_EFFECT	: Dictionary	= new Dictionary(true);

		public function get environmentMap() : ITextureResource
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP) as ITextureResource;
		}
		public function set environmentMap(value : ITextureResource) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP, value);
		}

		public function get reflectivity() : Number
		{
			return getProperty(EnvironmentMappingProperties.REFLECTIVITY) as Number;
		}
		public function set reflectivity(value : Number) : void
		{
			setProperty(EnvironmentMappingProperties.REFLECTIVITY, value);
		}

		public function get environmentMappingType() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE) as uint;
		}
		public function set environmentMappingType(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE, value);
		}

		public function get environmentBlending() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING) as uint;
		}
		public function set environmentBlending(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING, value);
		}

		public function RealisticMaterial(scene			: Scene,
										  properties	: Object	= null,
										  effect		: Effect	= null,
										  name			: String	= DEFAULT_NAME)
		{
			effect ||= SCENE_TO_EFFECT[scene] || (SCENE_TO_EFFECT[scene] = new RealisticEffect(scene));

			super(scene, properties, effect, name);
		}
	}
}

package aerys.minko.render.material.realistic
{
	import aerys.minko.render.Effect;
	import aerys.minko.render.material.environment.EnvironmentMappingProperties;
	import aerys.minko.render.material.phong.PhongMaterial;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.binding.IDataProvider;

	import flash.utils.Dictionary;

	public class RealisticMaterial extends PhongMaterial
	{
		private static const DEFAULT_NAME		: String		= 'RealisticMaterial';
		private static const SCENE_TO_EFFECT	: Dictionary	= new Dictionary(true);

		public function get environmentMap() : ITextureResource
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP) as ITextureResource;
		}
		public function set environmentMap(value : ITextureResource) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAP, value);
		}

		public function get reflectivity() : Number
		{
			return getProperty(EnvironmentMappingProperties.REFLECTIVITY) as Number;
		}
		public function set reflectivity(value : Number) : void
		{
			setProperty(EnvironmentMappingProperties.REFLECTIVITY, value);
		}

		public function get environmentMappingType() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE) as uint;
		}
		public function set environmentMappingType(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_MAPPING_TYPE, value);
		}

		public function get environmentBlending() : uint
		{
			return getProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING) as uint;
		}
		public function set environmentBlending(value : uint) : void
		{
			setProperty(EnvironmentMappingProperties.ENVIRONMENT_BLENDING, value);
		}

		public function RealisticMaterial(scene			: Scene,
										  properties	: Object	= null,
										  effect		: Effect	= null,
										  name			: String	= DEFAULT_NAME)
		{
			effect ||= SCENE_TO_EFFECT[scene] || (SCENE_TO_EFFECT[scene] = new RealisticEffect(scene));

			super(scene, properties, effect, name);
		}

        override public function clone() : IDataProvider
        {
            return new RealisticMaterial((effect as RealisticEffect).scene, this, effect, name);
        }
	}
}

add missing RealisticMaterial.clone()

add missing RealisticMaterial.clone()


/* RTMFPSocket abstraction
 * Author: Nate Hardison, May 2011
 *
 * This code is heavily based off of BelugaFile, an open-source
 * Air file-transfer application written by Nicholas Bliyk.
 * Website: http://www.belugafile.com/
 * Source: http://code.google.com/p/belugafile/
 *
 */

package rtmfp
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.NetStatusEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.utils.ByteArray;
    import flash.utils.clearInterval;
    import flash.utils.setInterval;
    import flash.utils.setTimeout;

    import rtmfp.RTMFPSocketClient;
    import rtmfp.events.RTMFPSocketEvent;

    [Event(name="connectSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="connectFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="publishStart", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peerConnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peeringSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peeringFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peerDisconnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
    public class RTMFPSocket extends EventDispatcher
    {
        /* The name of the "media" to pass between peers */
        private static const DATA:String = "data";
		    private static const DEFAULT_CIRRUS_ADDRESS:String = "rtmfp://p2p.rtmfp.net";
		    private static const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;
		    private static const DEFAULT_CONNECT_TIMEOUT:uint = 4000;

        /* Connection to the Cirrus rendezvous service */
        private var connection:NetConnection;

		    /* ID of the peer to connect to */
		    private var peerID:String;

		    /* Data streams to be established with peer */
		    private var sendStream:NetStream;
		    private var recvStream:NetStream;

		    /* Timeouts */
		    private var connectionTimeout:int;
		    private var peerConnectTimeout:uint;

        public function RTMFPSocket(){}

        public function connect(addr:String = DEFAULT_CIRRUS_ADDRESS, key:String = DEFAULT_CIRRUS_KEY):void
        {
          connection = new NetConnection();
			    connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
			    connection.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEvent);
			    connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityErrorEvent);
          connection.connect(addr + "/" + key);
			    connectionTimeout = setInterval(fail, DEFAULT_CONNECT_TIMEOUT);
        }

        public function get id():String
        {
          if (connection != null && connection.connected) {
            return connection.nearID;
          }

          return null;
        }

        public function get connected():Boolean
        {
          return (connection != null && connection.connected);
        }

        public function readBytes(bytes:ByteArray):void
        {
          recvStream.client.bytes.readBytes(bytes);
        }

        public function writeBytes(bytes:ByteArray):void
        {
          sendStream.send("dataAvailable", bytes);
        }

        public function get peer():String
        {
          return this.peerID;
        }

        public function set peer(peerID:String):void
        {
          if (peerID == null || peerID.length == 0) {
            throw new Error("Peer ID is null/empty.")
    			} else if (peerID == connection.nearID) {
    				throw new Error("Peer ID cannot be the same as our ID.");
    			} else if (this.peerID == peerID) {
    			  throw new Error("Already connected to peer " + peerID + ".");
    			} else if (this.recvStream != null) {
            throw new Error("Cannot connect to a second peer.");
          }

          this.peerID = peerID;

    			recvStream = new NetStream(connection, peerID);
    			var client:RTMFPSocketClient = new RTMFPSocketClient();
    			client.addEventListener(ProgressEvent.SOCKET_DATA, onDataAvailable, false, 0, true);
    			client.addEventListener(RTMFPSocketClient.PEER_CONNECT_ACKNOWLEDGED, onPeerConnectAcknowledged, false, 0, true);
    			recvStream.client = client;
    			recvStream.addEventListener(NetStatusEvent.NET_STATUS, onRecvStreamEvent);
    			recvStream.play(DATA);
    			setTimeout(onPeerConnectTimeout, peerConnectTimeout, recvStream);
        }

        private function startPublishStream():void
        {
          sendStream = new NetStream(connection, NetStream.DIRECT_CONNECTIONS);
    			sendStream.addEventListener(NetStatusEvent.NET_STATUS, onSendStreamEvent);
    			var o:Object = new Object();
    			o.onPeerConnect = onPeerConnect;
    			sendStream.client = o;
    			sendStream.publish(DATA);
        }

        private function fail():void
        {
          clearInterval(connectionTimeout);
    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_FAIL));
        }

        private function onDataAvailable(event:ProgressEvent):void
        {
          dispatchEvent(event);
        }

        private function onIOErrorEvent(event:IOErrorEvent):void
        {
          fail();
        }

        private function onNetStatusEvent(event:NetStatusEvent):void
        {
          switch (event.info.code) {
      			case "NetConnection.Connect.Success" :
      				clearInterval(connectionTimeout);
      				startPublishStream();
      				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_SUCCESS));
      				break;
      			case "NetStream.Connect.Success" :
      				break;
      			case "NetStream.Publish.BadName" :
      			  fail();
      				break;
      			case "NetStream.Connect.Closed" :
      			  // we've disconnected from the peer
      			  // can reset to accept another
      			  // clear the publish stream and re-publish another
      			  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_DISCONNECTED, recvStream));
      				break;
      		}
        }

        private function onPeerConnect(peer:NetStream):Boolean
        {
          // establish a bidirectional stream with the peer
          if (peerID == null) {
            this.peer = peer.farID;
          }

          // disallow additional peers connecting to us
          if (peer.farID != peerID) return false;

          peer.send("setPeerConnectAcknowledged");
          dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_CONNECTED, peer));

          return true;
        }

        private function onPeerConnectAcknowledged(event:Event):void
        {
    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_SUCCESS, recvStream));
        }

        private function onPeerConnectTimeout(peer:NetStream):void
        {
          if (!recvStream.client) return;
    			if (!RTMFPSocketClient(recvStream.client).peerConnectAcknowledged) {
    				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_FAIL, recvStream));
    			}
        }

        private function onSecurityErrorEvent(event:SecurityErrorEvent):void
        {
          fail();
        }

        private function onSendStreamEvent(event:NetStatusEvent):void
        {
    			switch (event.info.code) {
    				case ("NetStream.Publish.Start") :
    				  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PUBLISH_START));
    				  break;
    				case ("NetStream.Play.Reset") :
    				case ("NetStream.Play.Start") :
    					break;
    			}
    		}
    		private function onRecvStreamEvent(event:NetStatusEvent):void
    		{
    			switch (event.info.code) {
    				case ("NetStream.Publish.Start") :
    				case ("NetStream.Play.Reset") :
    				case ("NetStream.Play.Start") :
    					break;
    			}
    		}
    }
}

/* RTMFPSocket abstraction
 * Author: Nate Hardison, May 2011
 *
 * This code is heavily based off of BelugaFile, an open-source
 * Air file-transfer application written by Nicholas Bliyk.
 * Website: http://www.belugafile.com/
 * Source: http://code.google.com/p/belugafile/
 *
 */

package rtmfp
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.NetStatusEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.utils.ByteArray;
    import flash.utils.clearInterval;
    import flash.utils.setInterval;
    import flash.utils.setTimeout;

    import rtmfp.RTMFPSocketClient;
    import rtmfp.events.RTMFPSocketEvent;

    [Event(name="connectSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="connectFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="publishStart", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peerConnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peeringSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peeringFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
  	[Event(name="peerDisconnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
    public class RTMFPSocket extends EventDispatcher
    {
        /* The name of the "media" to pass between peers */
        private static const DATA:String = "data";
		    private static const DEFAULT_CIRRUS_ADDRESS:String = "rtmfp://p2p.rtmfp.net";
		    private static const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;
		    private static const DEFAULT_CONNECT_TIMEOUT:uint = 4000;

        /* Connection to the Cirrus rendezvous service */
        private var connection:NetConnection;

		    /* ID of the peer to connect to */
		    private var peerID:String;

		    /* Data streams to be established with peer */
		    private var sendStream:NetStream;
		    private var recvStream:NetStream;

		    /* Timeouts */
		    private var connectionTimeout:int;
		    private var peerConnectTimeout:uint;

        public function RTMFPSocket(){}

        public function connect(addr:String = DEFAULT_CIRRUS_ADDRESS, key:String = DEFAULT_CIRRUS_KEY):void
        {
          connection = new NetConnection();
			    connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
			    connection.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEvent);
			    connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityErrorEvent);
          connection.connect(addr + "/" + key);
			    connectionTimeout = setInterval(fail, DEFAULT_CONNECT_TIMEOUT);
        }

        public function close():void
        {
          connection.close();
        }

        public function get id():String
        {
          if (connection != null && connection.connected) {
            return connection.nearID;
          }

          return null;
        }

        public function get connected():Boolean
        {
          return (connection != null && connection.connected);
        }

        public function readBytes(bytes:ByteArray):void
        {
          recvStream.client.bytes.readBytes(bytes);
        }

        public function writeBytes(bytes:ByteArray):void
        {
          sendStream.send("dataAvailable", bytes);
        }

        public function get peer():String
        {
          return this.peerID;
        }

        public function set peer(peerID:String):void
        {
          if (peerID == null || peerID.length == 0) {
            throw new Error("Peer ID is null/empty.")
    			} else if (peerID == connection.nearID) {
    				throw new Error("Peer ID cannot be the same as our ID.");
    			} else if (this.peerID == peerID) {
    			  throw new Error("Already connected to peer " + peerID + ".");
    			} else if (this.recvStream != null) {
            throw new Error("Cannot connect to a second peer.");
          }

          this.peerID = peerID;

    			recvStream = new NetStream(connection, peerID);
    			var client:RTMFPSocketClient = new RTMFPSocketClient();
    			client.addEventListener(ProgressEvent.SOCKET_DATA, onDataAvailable, false, 0, true);
    			client.addEventListener(RTMFPSocketClient.PEER_CONNECT_ACKNOWLEDGED, onPeerConnectAcknowledged, false, 0, true);
    			recvStream.client = client;
    			recvStream.addEventListener(NetStatusEvent.NET_STATUS, onRecvStreamEvent);
    			recvStream.play(DATA);
    			setTimeout(onPeerConnectTimeout, peerConnectTimeout, recvStream);
        }

        private function startPublishStream():void
        {
          sendStream = new NetStream(connection, NetStream.DIRECT_CONNECTIONS);
    			sendStream.addEventListener(NetStatusEvent.NET_STATUS, onSendStreamEvent);
    			var o:Object = new Object();
    			o.onPeerConnect = onPeerConnect;
    			sendStream.client = o;
    			sendStream.publish(DATA);
        }

        private function fail():void
        {
          clearInterval(connectionTimeout);
    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_FAIL));
        }

        private function onDataAvailable(event:ProgressEvent):void
        {
          dispatchEvent(event);
        }

        private function onIOErrorEvent(event:IOErrorEvent):void
        {
          fail();
        }

        private function onNetStatusEvent(event:NetStatusEvent):void
        {
          switch (event.info.code) {
      			case "NetConnection.Connect.Success" :
      				clearInterval(connectionTimeout);
      				startPublishStream();
      				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_SUCCESS));
      				break;
      			case "NetStream.Connect.Success" :
      				break;
      			case "NetStream.Publish.BadName" :
      			  fail();
      				break;
      			case "NetStream.Connect.Closed" :
      			  // we've disconnected from the peer
      			  // can reset to accept another
      			  // clear the publish stream and re-publish another
      			  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_DISCONNECTED, recvStream));
      				break;
      		}
        }

        private function onPeerConnect(peer:NetStream):Boolean
        {
          // establish a bidirectional stream with the peer
          if (peerID == null) {
            this.peer = peer.farID;
          }

          // disallow additional peers connecting to us
          if (peer.farID != peerID) return false;

          peer.send("setPeerConnectAcknowledged");
          dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_CONNECTED, peer));

          return true;
        }

        private function onPeerConnectAcknowledged(event:Event):void
        {
    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_SUCCESS, recvStream));
        }

        private function onPeerConnectTimeout(peer:NetStream):void
        {
          if (!recvStream.client) return;
    			if (!RTMFPSocketClient(recvStream.client).peerConnectAcknowledged) {
    				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_FAIL, recvStream));
    			}
        }

        private function onSecurityErrorEvent(event:SecurityErrorEvent):void
        {
          fail();
        }

        private function onSendStreamEvent(event:NetStatusEvent):void
        {
    			switch (event.info.code) {
    				case ("NetStream.Publish.Start") :
    				  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PUBLISH_START));
    				  break;
    				case ("NetStream.Play.Reset") :
    				case ("NetStream.Play.Start") :
    					break;
    			}
    		}
    		private function onRecvStreamEvent(event:NetStatusEvent):void
    		{
    			switch (event.info.code) {
    				case ("NetStream.Publish.Start") :
    				case ("NetStream.Play.Reset") :
    				case ("NetStream.Play.Start") :
    					break;
    			}
    		}
    }
}

Add an RTMFPSocket.close method.

Add an RTMFPSocket.close method.