package com.kaltura.kdpfl.controller.media
{
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;

	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.command.SimpleCommand;

	/**
	 * PostSequenceEndCommand is called when the pre-sequence of the player is complete.
	 * The "main-event" media is reloaded into the player and begins to play automatically.
	 * All variables which have to do with the pre-sequence are nullified and the sequence is registered as COMPLETE.
	 */
	public class PreSequenceEndCommand extends SimpleCommand
	{
		override public function execute(notification:INotification):void
		{
			var sequenceProxy : SequenceProxy = facade.retrieveProxy( SequenceProxy.NAME ) as SequenceProxy;
			sequenceProxy.vo.isInSequence = false;
			sequenceProxy.vo.preCurrentIndex = -1;
			sequenceProxy.vo.preSequenceComplete = true;
			var mediaProxy : MediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			sendNotification(NotificationType.DO_PLAY);

		}
	}
}

package com.kaltura.kdpfl.controller.media
{
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.view.media.KMediaPlayer;
	import com.kaltura.kdpfl.view.media.KMediaPlayerMediator;

	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.command.SimpleCommand;

	/**
	 * PostSequenceEndCommand is called when the pre-sequence of the player is complete.
	 * The "main-event" media is reloaded into the player and begins to play automatically.
	 * All variables which have to do with the pre-sequence are nullified and the sequence is registered as COMPLETE.
	 */
	public class PreSequenceEndCommand extends SimpleCommand
	{
		override public function execute(notification:INotification):void
		{
			var sequenceProxy : SequenceProxy = facade.retrieveProxy( SequenceProxy.NAME ) as SequenceProxy;
			sequenceProxy.vo.isInSequence = false;
			sequenceProxy.vo.preCurrentIndex = -1;
			sequenceProxy.vo.preSequenceComplete = true;
			var flashvars:Object = (facade.retrieveProxy( ConfigProxy.NAME ) as ConfigProxy).vo.flashvars;
			if (!flashvars.pauseAfterPreSequence || flashvars.pauseAfterPreSequence=="false")
			{
				sendNotification(NotificationType.DO_PLAY);
			}
			else
			{
				sendNotification(NotificationType.DO_PAUSE);
				var mediaMediator:KMediaPlayerMediator = facade.retrieveMediator(KMediaPlayerMediator.NAME) as KMediaPlayerMediator;
				mediaMediator.cleanMedia();
				//reset timer
				sendNotification(NotificationType.PLAYER_UPDATE_PLAYHEAD, 0);
				mediaMediator.kMediaPlayer.showThumbnail();
			}

		}
	}
}

qnd: support in pauseAfterPreSequence flashvar

git-svn-id: 3f608e5a9a704dd448217c0a64c508e7f145cfa1@85862 6b8eccd3-e8c5-4e7d-8186-e12b5326b719


package aerys.minko.render.effect.basic
{
	import aerys.minko.render.effect.Style;

	public final class BasicStyle
	{
		public static const DIFFUSE_MAP			: int	= Style.getStyleId("basic diffuse map");
		public static const NORMAL_MAP			: int	= Style.getStyleId("basic normal map");

		public static const BLENDING			: int	= Style.getStyleId("basic blending");

		public static const TRIANGLE_CULLING	: int	= Style.getStyleId("basic triangle culling");
		public static const NORMAL_MULTIPLIER	: int	= Style.getStyleId("basic normal multiplier");
	}
}

package aerys.minko.render.effect.basic
{
	import aerys.minko.render.effect.Style;

	public final class BasicStyle
	{
		public static const DIFFUSE_COLOR		: int	= Style.getStyleId("basic diffuse color");
		public static const DIFFUSE_MAP			: int	= Style.getStyleId("basic diffuse map");
		public static const NORMAL_MAP			: int	= Style.getStyleId("basic normal map");

		public static const BLENDING			: int	= Style.getStyleId("basic blending");

		public static const TRIANGLE_CULLING	: int	= Style.getStyleId("basic triangle culling");
		public static const NORMAL_MULTIPLIER	: int	= Style.getStyleId("basic normal multiplier");
	}
}

Create a BasicStyle.DIFFUSE_COLOR parameter


package
{
	import com.kaltura.KalturaClient;
	import com.kaltura.commands.baseEntry.BaseEntryList;
	import com.kaltura.commands.playlist.PlaylistExecute;
	import com.kaltura.events.KalturaEvent;
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.ExternalInterfaceProxy;
	import com.kaltura.kdpfl.model.FuncsProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.ServicesProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.model.vo.ExternalInterfaceVO;
	import com.kaltura.kdpfl.plugin.IPlugin;
	import com.kaltura.kdpfl.plugin.component.RelatedEntriesMediator;
	import com.kaltura.kdpfl.plugin.component.RelatedEntriesNotificationType;
	import com.kaltura.kdpfl.plugin.component.RelatedEntriesSourceType;
	import com.kaltura.kdpfl.plugin.component.RelatedEntryVO;
	import com.kaltura.kdpfl.plugin.component.RelatedItemActionType;
	import com.kaltura.kdpfl.util.Functor;
	import com.kaltura.net.KalturaCall;
	import com.kaltura.types.KalturaStatsFeatureType;
	import com.kaltura.vo.KalturaBaseEntry;
	import com.kaltura.vo.KalturaBaseEntryFilter;
	import com.kaltura.vo.KalturaBaseEntryListResponse;

	import fl.core.UIComponent;
	import fl.data.DataProvider;

	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.IFacade;
	import org.puremvc.as3.patterns.facade.Facade;

	public class relatedEntriesPluginCode extends UIComponent implements IPlugin
	{
		/**
		 * current player's entry
		 */
		public var entryId:String;
		/**
		 * source according to RelatedEntriesSourceType
		 */
		public var sourceType:String;

		//////////////////////////////////////////
		//TODO: the following 3 properties can be replaced with one, but due to appstudio limitaions
		//we will seperate them
		//////////////////////////////////////////
		/**
		 * data for the related entries source.to be used when sourceType is "entryIds"
		 */
		public var entryIdsSourceData:String;
		/**
		 * data for the related entries source.to be used when sourceType is "referenceIds"
		 */
		public var referenceIdsSourceData:String;
		/**
		 * data for the related entries source.to be used when sourceType is "globalPlaylist"
		 */
		public var playlistSourceData:String;

		[Bindable]
		/**
		 * holds the relatedEntries array
		 *
		 */
		public var dataProvider:DataProvider;
		/**
		 * default playlist ID. will be used when sourceType=automatic
		 */
		public var automaticPlaylistId:String;
		/**
		 * should start playing related entry after "autoPlayDelay" seconds
		 */
		public var autoPlay:Boolean;
		/**
		 * time to wait befre related entry is selected
		 */
		public var autoPlayDelay:int = 3;
		/**
		 * action according to RelateditemActionType
		 */
		public var itemClickAction:String = RelatedItemActionType.LOAD_IN_PLAYER;
		/**
		 * URL address to navigate to, in case itemClickAction=GO_TO_URL
		 */
		public var urlAddress:String;
		/**
		 *JS function to call in case itemClickAction=CALL_JS_FUNC
		 */
		public var jsFunc:String;
		/**
		 * parameters of JS Func
		 */
		public var jsParams:Object;
		[Bindable]
		/**
		 * Will be used for the countdown display in the next selected related item renderer
		 */
		public var timeRemaining:int = 0;
		[Bindable]
		/**
		 * the current selected relatedEntryVo object
		 */
		public var selectedRelatedVo:RelatedEntryVO;
		/**
		 * if true, the next up item will be selected randomally
		 */
		public var selectRandomNext:Boolean;

		//////////////////////////////////////
		// private fields
		/////////////////////////////////////
		/**
		 * timer for the timeRemaining countdown
		 */
		private var _timeRemainingTimer:Timer;
		private var _isTimerRunning:Boolean;

		private var _mediator:RelatedEntriesMediator;
		private var _facade:IFacade;

		public function relatedEntriesPluginCode()
		{
		}

		public function initializePlugin(facade:IFacade):void
		{
			_facade = facade;
			_mediator = new RelatedEntriesMediator(this);
			_facade.registerMediator(_mediator);
			var mediaProxy:MediaProxy = _facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			mediaProxy.vo.supportImageDuration = true;
		}

		public function setSkin(styleName:String, setSkinSize:Boolean=false):void
		{
		}

		/**
		 * will send an API call to retrieve entries according to sourceType and sourceData
		 *
		 */
		public function loadEntries():void
		{
			var kc:KalturaClient = (_facade.retrieveProxy(ServicesProxy.NAME) as ServicesProxy).vo.kalturaClient;
			var kalturaCall:KalturaCall;
			var sourceData:String;
			switch (sourceType)
			{
				case RelatedEntriesSourceType.AUTOMATIC:
				case RelatedEntriesSourceType.GLOBAL_PLAYLIST:
					if (sourceType == RelatedEntriesSourceType.AUTOMATIC)
						sourceData = automaticPlaylistId;
					else
						sourceData = playlistSourceData;
					kalturaCall = new PlaylistExecute(sourceData);
					break;
				case RelatedEntriesSourceType.ENTRY_IDS:
				case RelatedEntriesSourceType.REFERENCE_IDS:
					if (sourceType == RelatedEntriesSourceType.ENTRY_IDS)
						sourceData = entryIdsSourceData;
					else
						sourceData = referenceIdsSourceData;
					var filter:KalturaBaseEntryFilter = new KalturaBaseEntryFilter();
					if (sourceType == RelatedEntriesSourceType.ENTRY_IDS)
					{
						filter.idIn = sourceData;
					}
					else //reference IDs list
					{
						filter.referenceIdIn = sourceData;
					}
					kalturaCall = new BaseEntryList(filter);
					break;
			}

			if (!sourceData || !kalturaCall)
			{
				trace ("cannot retrieve related entries, missing information");
			}
			else
			{
				kalturaCall.addEventListener(KalturaEvent.COMPLETE, onEntriesComplete);
				kalturaCall.addEventListener(KalturaEvent.FAILED, onEntriesFailed);
				kc.post(kalturaCall);
			}
		}

		/**
		 * Handler for the entries API request.
		 * Will populate the data provider
		 * @param event
		 *
		 */
		private function onEntriesComplete(event:KalturaEvent):void
		{
			var resultArray:Array;
			var dpArray:Array = new Array();
			if (sourceType == RelatedEntriesSourceType.AUTOMATIC || sourceType == RelatedEntriesSourceType.GLOBAL_PLAYLIST)
			{
				resultArray = event.data as Array;
			}
			else
			{
				resultArray = (event.data as KalturaBaseEntryListResponse).objects;
			}
			if (resultArray)
			{
				for each (var entry:KalturaBaseEntry in resultArray)
				{
					if (entry.id != entryId) //current entry shouldn't be part of related entries
					{
						var relatedEntry:RelatedEntryVO = new RelatedEntryVO(entry);
						dpArray.push(relatedEntry);
					}
				}

				if (!dpArray.length)
				{
					selectedRelatedVo = null;
				}
				else
				{
					if (selectRandomNext)
					{
						var randomIndex:int = Math.round(Math.random() * dpArray.length);
						//set the next up in the first place in the array
						if (randomIndex != 0)
						{
							var selected:RelatedEntryVO = dpArray.splice(randomIndex,1)[0];
							dpArray.splice(0, 0, selected);
						}
					}
					selectedRelatedVo = dpArray[0] as RelatedEntryVO;
					selectedRelatedVo.isUpNext = true;
				}

			}
			dataProvider = new DataProvider(dpArray);

			_facade.sendNotification(RelatedEntriesNotificationType.RELATED_ENTRIES_LOADED);
		}

		/**
		 * Entries API request failed
		 * @param event
		 *
		 */
		private function onEntriesFailed(event:KalturaEvent):void
		{
			trace ("failed to retrieve related entries");
			//fallback to automatic playlist
			if (sourceType!=RelatedEntriesSourceType.AUTOMATIC && automaticPlaylistId)
			{
				trace ("fallback to automtaic playlist:", automaticPlaylistId);
				sourceType = RelatedEntriesSourceType.AUTOMATIC;
				loadEntries();
			}
		}

		public function setUpNext(index:int):void
		{
			if (dataProvider && index >= 0 && index < dataProvider.length)
			{
				//unselect old selected
				if (selectedRelatedVo)
					selectedRelatedVo.isUpNext = false;

				selectedRelatedVo = dataProvider.getItemAt(index) as RelatedEntryVO;
				selectedRelatedVo.isUpNext = true;
			}
			else
			{
				trace ("could not set up next, invalid value:", index);
			}
		}

		/**
		 * start action on selectedRelatedVo according to itemClickAction value
		 *
		 */
		public function startAction():void
		{
			switch (itemClickAction)
			{
				case RelatedItemActionType.LOAD_IN_PLAYER:
					(_facade.retrieveProxy(MediaProxy.NAME) as MediaProxy).vo.singleAutoPlay = true;
					if (!selectedRelatedVo)
					{
						trace ("cannot load related entry, no selected related entry");
					}
					else
					{
						_facade.sendNotification(NotificationType.CHANGE_MEDIA, {entryId: selectedRelatedVo.entry.id, originFeature: KalturaStatsFeatureType.RELATED});
					}
					break;
				case RelatedItemActionType.CALL_JS_FUNC:
					var externalProxy:ExternalInterfaceProxy = _facade.retrieveProxy(ExternalInterfaceProxy.NAME) as ExternalInterfaceProxy;
					if (externalProxy.vo.enabled)
					{
						externalProxy.call(jsFunc, jsParams);
					}
					break;
				case RelatedItemActionType.GO_TO_URL:
					var funcProxy:FuncsProxy = Functor.globalsFunctionsObject as FuncsProxy;
					funcProxy.navigate(urlAddress, "_blank");
					break;
			}
		}

		/**
		 * starts remainingTime timer
		 *
		 */
		public function startTimer():void
		{
			if (autoPlayDelay==0)
			{
				onTimerComplete();
			}
			else
			{
				if (!_timeRemainingTimer)
				{
					_timeRemainingTimer = new Timer(1000, autoPlayDelay);
					_timeRemainingTimer.addEventListener(TimerEvent.TIMER, onTimer, false, 0, true);
					_timeRemainingTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete, false, 0, true);
				}
				else
				{
					_timeRemainingTimer.repeatCount = autoPlayDelay;
					_timeRemainingTimer.reset();
				}

				timeRemaining = autoPlayDelay;
				_timeRemainingTimer.start();
				isTimerRunning = true;
			}
		}

		/**
		 * stops remainingTime timer
		 *
		 */
		public function stopTimer():void
		{
			if (_timeRemainingTimer && _timeRemainingTimer.running)
			{
				_timeRemainingTimer.stop();
				isTimerRunning = false;
			}
		}

		/**
		 * resumes remainingTime timer
		 *
		 */
		public function resumeTimer():void
		{
			if (_timeRemainingTimer && !_timeRemainingTimer.running)
			{
				_timeRemainingTimer.start();
				isTimerRunning = true;
			}
		}

		private function onTimer(event:TimerEvent):void
		{
			timeRemaining = _timeRemainingTimer.repeatCount - _timeRemainingTimer.currentCount;
		}

		private function onTimerComplete(event:TimerEvent = null):void
		{
			timeRemaining = 0;
			isTimerRunning = false;
			startAction();
		}

		[Bindable]
		/**
		 * indicated if timer is currently running
		 * @return
		 *
		 */
		public function get isTimerRunning():Boolean
		{
			return _isTimerRunning;
		}

		public function set isTimerRunning(value:Boolean):void
		{
			_isTimerRunning = value;
		}
	}

}

package
{
	import com.kaltura.KalturaClient;
	import com.kaltura.commands.baseEntry.BaseEntryList;
	import com.kaltura.commands.playlist.PlaylistExecute;
	import com.kaltura.events.KalturaEvent;
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.ExternalInterfaceProxy;
	import com.kaltura.kdpfl.model.FuncsProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.ServicesProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.model.vo.ExternalInterfaceVO;
	import com.kaltura.kdpfl.plugin.IPlugin;
	import com.kaltura.kdpfl.plugin.component.RelatedEntriesMediator;
	import com.kaltura.kdpfl.plugin.component.RelatedEntriesNotificationType;
	import com.kaltura.kdpfl.plugin.component.RelatedEntriesSourceType;
	import com.kaltura.kdpfl.plugin.component.RelatedEntryVO;
	import com.kaltura.kdpfl.plugin.component.RelatedItemActionType;
	import com.kaltura.kdpfl.util.Functor;
	import com.kaltura.net.KalturaCall;
	import com.kaltura.types.KalturaStatsFeatureType;
	import com.kaltura.vo.KalturaBaseEntry;
	import com.kaltura.vo.KalturaBaseEntryFilter;
	import com.kaltura.vo.KalturaBaseEntryListResponse;
	import com.kaltura.kdpfl.util.URLUtils;

	import fl.core.UIComponent;
	import fl.data.DataProvider;

	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.IFacade;
	import org.puremvc.as3.patterns.facade.Facade;

	public class relatedEntriesPluginCode extends UIComponent implements IPlugin
	{
		/**
		 * current player's entry
		 */
		public var entryId:String;
		/**
		 * source according to RelatedEntriesSourceType
		 */
		public var sourceType:String;

		//////////////////////////////////////////
		//TODO: the following 3 properties can be replaced with one, but due to appstudio limitaions
		//we will seperate them
		//////////////////////////////////////////
		/**
		 * data for the related entries source.to be used when sourceType is "entryIds"
		 */
		public var entryIdsSourceData:String;
		/**
		 * data for the related entries source.to be used when sourceType is "referenceIds"
		 */
		public var referenceIdsSourceData:String;
		/**
		 * data for the related entries source.to be used when sourceType is "globalPlaylist"
		 */
		public var playlistSourceData:String;

		[Bindable]
		/**
		 * holds the relatedEntries array
		 *
		 */
		public var dataProvider:DataProvider;
		/**
		 * default playlist ID. will be used when sourceType=automatic
		 */
		public var automaticPlaylistId:String;
		/**
		 * should start playing related entry after "autoPlayDelay" seconds
		 */
		public var autoPlay:Boolean;
		/**
		 * time to wait befre related entry is selected
		 */
		public var autoPlayDelay:int = 3;
		/**
		 * action according to RelateditemActionType
		 */
		public var itemClickAction:String = RelatedItemActionType.LOAD_IN_PLAYER;
		/**
		 * URL address to navigate to, in case itemClickAction=GO_TO_URL
		 */
		public var urlAddress:String;
		/**
		 *JS function to call in case itemClickAction=CALL_JS_FUNC
		 */
		public var jsFunc:String;
		/**
		 * parameters of JS Func
		 */
		public var jsParams:Object;
		[Bindable]
		/**
		 * Will be used for the countdown display in the next selected related item renderer
		 */
		public var timeRemaining:int = 0;
		[Bindable]
		/**
		 * the current selected relatedEntryVo object
		 */
		public var selectedRelatedVo:RelatedEntryVO;
		/**
		 * if true, the next up item will be selected randomally
		 */
		public var selectRandomNext:Boolean;

		//////////////////////////////////////
		// private fields
		/////////////////////////////////////
		/**
		 * timer for the timeRemaining countdown
		 */
		private var _timeRemainingTimer:Timer;
		private var _isTimerRunning:Boolean;

		private var _mediator:RelatedEntriesMediator;
		private var _facade:IFacade;

		public function relatedEntriesPluginCode()
		{
		}

		public function initializePlugin(facade:IFacade):void
		{
			_facade = facade;
			_mediator = new RelatedEntriesMediator(this);
			_facade.registerMediator(_mediator);
			var mediaProxy:MediaProxy = _facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			mediaProxy.vo.supportImageDuration = true;
		}

		public function setSkin(styleName:String, setSkinSize:Boolean=false):void
		{
		}

		/**
		 * will send an API call to retrieve entries according to sourceType and sourceData
		 *
		 */
		public function loadEntries():void
		{
			var kc:KalturaClient = (_facade.retrieveProxy(ServicesProxy.NAME) as ServicesProxy).vo.kalturaClient;
			var kalturaCall:KalturaCall;
			var sourceData:String;
			switch (sourceType)
			{
				case RelatedEntriesSourceType.AUTOMATIC:
				case RelatedEntriesSourceType.GLOBAL_PLAYLIST:
					if (sourceType == RelatedEntriesSourceType.AUTOMATIC)
						sourceData = automaticPlaylistId;
					else
						sourceData = playlistSourceData;
					kalturaCall = new PlaylistExecute(sourceData);
					break;
				case RelatedEntriesSourceType.ENTRY_IDS:
				case RelatedEntriesSourceType.REFERENCE_IDS:
					if (sourceType == RelatedEntriesSourceType.ENTRY_IDS)
						sourceData = entryIdsSourceData;
					else
						sourceData = referenceIdsSourceData;
					var filter:KalturaBaseEntryFilter = new KalturaBaseEntryFilter();
					if (sourceType == RelatedEntriesSourceType.ENTRY_IDS)
					{
						filter.idIn = sourceData;
					}
					else //reference IDs list
					{
						filter.referenceIdIn = sourceData;
					}
					kalturaCall = new BaseEntryList(filter);
					break;
			}

			if (!sourceData || !kalturaCall)
			{
				trace ("cannot retrieve related entries, missing information");
			}
			else
			{
				kalturaCall.addEventListener(KalturaEvent.COMPLETE, onEntriesComplete);
				kalturaCall.addEventListener(KalturaEvent.FAILED, onEntriesFailed);
				kc.post(kalturaCall);
			}
		}

		/**
		 * Handler for the entries API request.
		 * Will populate the data provider
		 * @param event
		 *
		 */
		private function onEntriesComplete(event:KalturaEvent):void
		{
			var resultArray:Array;
			var dpArray:Array = new Array();
			if (sourceType == RelatedEntriesSourceType.AUTOMATIC || sourceType == RelatedEntriesSourceType.GLOBAL_PLAYLIST)
			{
				resultArray = event.data as Array;
			}
			else
			{
				resultArray = (event.data as KalturaBaseEntryListResponse).objects;
			}
			if (resultArray)
			{
				for each (var entry:KalturaBaseEntry in resultArray)
				{
					if (entry.id != entryId) //current entry shouldn't be part of related entries
					{
						if (entry.thumbnailUrl && entry.thumbnailUrl.indexOf( "thumbnail/entry_id" ) != -1)
						{
							entry.thumbnailUrl +=  URLUtils.getThumbURLPostfix((_facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy).vo.flashvars,
								(_facade.retrieveProxy(ServicesProxy.NAME) as ServicesProxy).vo.kalturaClient.ks);
						}
						var relatedEntry:RelatedEntryVO = new RelatedEntryVO(entry);
						dpArray.push(relatedEntry);
					}
				}

				if (!dpArray.length)
				{
					selectedRelatedVo = null;
				}
				else
				{
					if (selectRandomNext)
					{
						var randomIndex:int = Math.round(Math.random() * dpArray.length);
						//set the next up in the first place in the array
						if (randomIndex != 0)
						{
							var selected:RelatedEntryVO = dpArray.splice(randomIndex,1)[0];
							dpArray.splice(0, 0, selected);
						}
					}
					selectedRelatedVo = dpArray[0] as RelatedEntryVO;
					selectedRelatedVo.isUpNext = true;
				}

			}
			dataProvider = new DataProvider(dpArray);

			_facade.sendNotification(RelatedEntriesNotificationType.RELATED_ENTRIES_LOADED);
		}

		/**
		 * Entries API request failed
		 * @param event
		 *
		 */
		private function onEntriesFailed(event:KalturaEvent):void
		{
			trace ("failed to retrieve related entries");
			//fallback to automatic playlist
			if (sourceType!=RelatedEntriesSourceType.AUTOMATIC && automaticPlaylistId)
			{
				trace ("fallback to automtaic playlist:", automaticPlaylistId);
				sourceType = RelatedEntriesSourceType.AUTOMATIC;
				loadEntries();
			}
		}

		public function setUpNext(index:int):void
		{
			if (dataProvider && index >= 0 && index < dataProvider.length)
			{
				//unselect old selected
				if (selectedRelatedVo)
					selectedRelatedVo.isUpNext = false;

				selectedRelatedVo = dataProvider.getItemAt(index) as RelatedEntryVO;
				selectedRelatedVo.isUpNext = true;
			}
			else
			{
				trace ("could not set up next, invalid value:", index);
			}
		}

		/**
		 * start action on selectedRelatedVo according to itemClickAction value
		 *
		 */
		public function startAction():void
		{
			switch (itemClickAction)
			{
				case RelatedItemActionType.LOAD_IN_PLAYER:
					(_facade.retrieveProxy(MediaProxy.NAME) as MediaProxy).vo.singleAutoPlay = true;
					if (!selectedRelatedVo)
					{
						trace ("cannot load related entry, no selected related entry");
					}
					else
					{
						_facade.sendNotification(NotificationType.CHANGE_MEDIA, {entryId: selectedRelatedVo.entry.id, originFeature: KalturaStatsFeatureType.RELATED});
					}
					break;
				case RelatedItemActionType.CALL_JS_FUNC:
					var externalProxy:ExternalInterfaceProxy = _facade.retrieveProxy(ExternalInterfaceProxy.NAME) as ExternalInterfaceProxy;
					if (externalProxy.vo.enabled)
					{
						externalProxy.call(jsFunc, jsParams);
					}
					break;
				case RelatedItemActionType.GO_TO_URL:
					var funcProxy:FuncsProxy = Functor.globalsFunctionsObject as FuncsProxy;
					funcProxy.navigate(urlAddress, "_blank");
					break;
			}
		}

		/**
		 * starts remainingTime timer
		 *
		 */
		public function startTimer():void
		{
			if (autoPlayDelay==0)
			{
				onTimerComplete();
			}
			else
			{
				if (!_timeRemainingTimer)
				{
					_timeRemainingTimer = new Timer(1000, autoPlayDelay);
					_timeRemainingTimer.addEventListener(TimerEvent.TIMER, onTimer, false, 0, true);
					_timeRemainingTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete, false, 0, true);
				}
				else
				{
					_timeRemainingTimer.repeatCount = autoPlayDelay;
					_timeRemainingTimer.reset();
				}

				timeRemaining = autoPlayDelay;
				_timeRemainingTimer.start();
				isTimerRunning = true;
			}
		}

		/**
		 * stops remainingTime timer
		 *
		 */
		public function stopTimer():void
		{
			if (_timeRemainingTimer && _timeRemainingTimer.running)
			{
				_timeRemainingTimer.stop();
				isTimerRunning = false;
			}
		}

		/**
		 * resumes remainingTime timer
		 *
		 */
		public function resumeTimer():void
		{
			if (_timeRemainingTimer && !_timeRemainingTimer.running)
			{
				_timeRemainingTimer.start();
				isTimerRunning = true;
			}
		}

		private function onTimer(event:TimerEvent):void
		{
			timeRemaining = _timeRemainingTimer.repeatCount - _timeRemainingTimer.currentCount;
		}

		private function onTimerComplete(event:TimerEvent = null):void
		{
			timeRemaining = 0;
			isTimerRunning = false;
			startAction();
		}

		[Bindable]
		/**
		 * indicated if timer is currently running
		 * @return
		 *
		 */
		public function get isTimerRunning():Boolean
		{
			return _isTimerRunning;
		}

		public function set isTimerRunning(value:Boolean):void
		{
			_isTimerRunning = value;
		}
	}

}

qnd: add referrer and ks on thumbURl, if needed

git-svn-id: 3f608e5a9a704dd448217c0a64c508e7f145cfa1@85990 6b8eccd3-e8c5-4e7d-8186-e12b5326b719


package aerys.minko.render.shader.node.common
{
	import aerys.minko.render.shader.node.Components;
	import aerys.minko.render.shader.node.Dummy;
	import aerys.minko.render.shader.node.IFragmentNode;
	import aerys.minko.render.shader.node.INode;
	import aerys.minko.render.shader.node.IVertexNode;
	import aerys.minko.render.shader.node.leaf.Attribute;
	import aerys.minko.render.shader.node.leaf.TransformParameter;
	import aerys.minko.render.shader.node.operation.builtin.Divide;
	import aerys.minko.render.shader.node.operation.builtin.Multiply4x4;
	import aerys.minko.render.shader.node.operation.manipulation.Extract;
	import aerys.minko.render.shader.node.operation.manipulation.Interpolate;
	import aerys.minko.scene.visitor.data.TransformData;
	import aerys.minko.type.vertex.format.VertexComponent;

	public class UV extends Dummy implements IVertexNode
	{
		public function get interpolated() : INode
		{
//			var simpleUv : INode = new Multiply4x4(
//				new Interpolate(new Attribute(VertexComponent.XYZ)),
//				new TransformParameter(16, TransformData.LOCAL_TO_UV)
//			);
//
//			var uv : INode = new Divide(
//				simpleUv,
//				new Extract(simpleUv, Components.W)
//			);
//
//			return uv;
			return new Interpolate(this);
		}

		public function UV()
		{
			var simpleUv : INode = new Multiply4x4(
				new Attribute(VertexComponent.XYZ),
				new TransformParameter(16, TransformData.LOCAL_TO_UV)
			);

			var uv : INode = new Divide(
				simpleUv,
				new Extract(simpleUv, Components.W)
			);

			super(uv);
		}
	}
}

package aerys.minko.render.shader.node.common
{
	import aerys.minko.render.shader.node.Components;
	import aerys.minko.render.shader.node.Dummy;
	import aerys.minko.render.shader.node.IFragmentNode;
	import aerys.minko.render.shader.node.INode;
	import aerys.minko.render.shader.node.IVertexNode;
	import aerys.minko.render.shader.node.leaf.Attribute;
	import aerys.minko.render.shader.node.leaf.TransformParameter;
	import aerys.minko.render.shader.node.operation.builtin.Divide;
	import aerys.minko.render.shader.node.operation.builtin.Multiply4x4;
	import aerys.minko.render.shader.node.operation.manipulation.Extract;
	import aerys.minko.render.shader.node.operation.manipulation.Interpolate;
	import aerys.minko.scene.visitor.data.TransformData;
	import aerys.minko.type.vertex.format.VertexComponent;

	public class UV extends Dummy implements IVertexNode
	{
		public function get interpolated() : INode
		{
			var simpleUv : INode = new Interpolate(
				new Multiply4x4(
					new Attribute(VertexComponent.XYZ),
					new TransformParameter(16, TransformData.LOCAL_TO_UV)
				)
			);

			var uv : INode = new Divide(
				simpleUv,
				new Extract(simpleUv, Components.W)
			);

			return uv;
		}

		public function UV()
		{
			var simpleUv : INode = new Multiply4x4(
				new Attribute(VertexComponent.XYZ),
				new TransformParameter(16, TransformData.LOCAL_TO_UV)
			);

			var uv : INode = new Divide(
				simpleUv,
				new Extract(simpleUv, Components.W)
			);

			super(uv);
		}
	}
}

Fix the UV Shader node

Fix the UV Shader node


/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed 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.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 */

package
{
    import com.google.analytics.AnalyticsTracker;
    import com.google.analytics.GATracker;
    import com.google.analytics.core.TrackerMode;

    import flash.display.Sprite;
    import flash.events.Event;

    /* note:
       Basic example

    */
    [SWF(width="800", height="600", backgroundColor='0xffffff', frameRate='24', pageTitle='example', scriptRecursionLimit='1000', scriptTimeLimit='60')]
    [ExcludeClass]
    public class gaforflash_example extends Sprite
    {
        /* You need to define a valid GA ID here */
        private const GA_ID:String = "UA-111-222";

        public var tracker:AnalyticsTracker;

        public function gaforflash_example()
        {
            super();

            if( stage )
            {
                onAddedToStage();
            }
            else
            {
                addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
            }

        }

        private function onAddedToStage( event:Event = null ):void
        {
            removeEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
            main();
        }

        private function _gasetup():void
        {
            /* we prevent the factory to build automatically */
            GATracker.autobuild = false;

            /* instanciation of the Google Analytics tracker */
            tracker = new GATracker( this, GA_ID );

            /* note:
               the 'this' reference need to be a DisplayObject
               properly instancied, eg. added to the display list
               so we can access the 'stage' property.
            */

            /* we configure the tracker */
            tracker.mode = TrackerMode.AS3;
            tracker.config.sessionTimeout = 60;
            tracker.config.conversionTimeout = 180;

            /* we force the factory to build the tracker */
            GATracker(tracker).build();
        }

        private function _gatest():void
        {
            //track pageview test
            tracker.trackPageview( "/test" );

            //track event test
            tracker.trackEvent( "say", "hello world", "test", 123 );
        }

        public function main():void
        {
            /* note:
               any call ot the GA API before its setup
               will be cached temporarily

               and once the GATracker build
               the cached functions will run in batch
            */
            tracker.trackPageview( "/before/setup" );

            _gasetup();

            /* note:
               from this point the GATracker is initialized
               and will send data to the Google Analytics server
            */

            _gatest();

        }

    }
}

/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed 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.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 */

package
{
    import com.google.analytics.AnalyticsTracker;
    import com.google.analytics.GATracker;
    import com.google.analytics.core.TrackerMode;

    import flash.display.Sprite;
    import flash.events.Event;

    /* note:
       Basic example

    */
    [SWF(width="800", height="600", backgroundColor='0xffffff', frameRate='24', pageTitle='example', scriptRecursionLimit='1000', scriptTimeLimit='60')]
    [ExcludeClass]
    public class gaforflash_example extends Sprite
    {
        /* You need to define a valid GA ID here */
        //private const GA_ID:String = "UA-111-222";
        private const GA_ID:String = "UA-94526-19";

        public var tracker:AnalyticsTracker;

        public function gaforflash_example()
        {
            super();

            if( stage )
            {
                onAddedToStage();
            }
            else
            {
                addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
            }

        }

        private function onAddedToStage( event:Event = null ):void
        {
            removeEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
            main();
        }

        private function _gasetup():void
        {
            /* we prevent the factory to build automatically */
            GATracker.autobuild = false;

            /* instanciation of the Google Analytics tracker */
            tracker = new GATracker( this, GA_ID );

            /* note:
               the 'this' reference need to be a DisplayObject
               properly instancied, eg. added to the display list
               so we can access the 'stage' property.
            */

            /* we configure the tracker */
            tracker.mode = TrackerMode.AS3;
            tracker.config.sessionTimeout = 60;
            tracker.config.conversionTimeout = 180;

            /* we force the factory to build the tracker */
            GATracker(tracker).build();
        }

        private function _gatest():void
        {
            //track pageview test
            tracker.trackPageview( "/test" );

            //track event test
            tracker.trackEvent( "say", "hello world", "test", 123 );
        }

        public function main():void
        {
            /* note:
               any call ot the GA API before its setup
               will be cached temporarily

               and once the GATracker build
               the cached functions will run in batch
            */
            //tracker.trackPageview( "/before/setup" );

            _gasetup();

            /* note:
               from this point the GATracker is initialized
               and will send data to the Google Analytics server
            */

            _gatest();

        }

    }
}

update example

update example


package com.esri.builder.supportClasses
{

import com.esri.ags.layers.ArcGISDynamicMapServiceLayer;
import com.esri.ags.layers.ArcGISImageServiceLayer;
import com.esri.ags.layers.ArcGISTiledMapServiceLayer;
import com.esri.ags.layers.CSVLayer;
import com.esri.ags.layers.FeatureLayer;
import com.esri.ags.layers.GeoRSSLayer;
import com.esri.ags.layers.KMLLayer;
import com.esri.ags.layers.Layer;
import com.esri.ags.layers.OpenStreetMapLayer;
import com.esri.ags.layers.WMSLayer;
import com.esri.ags.layers.WMTSLayer;
import com.esri.ags.layers.WebTiledLayer;
import com.esri.ags.layers.supportClasses.Field;
import com.esri.ags.layers.supportClasses.LayerDefinition;
import com.esri.ags.layers.supportClasses.TileInfo;
import com.esri.ags.symbols.PictureMarkerSymbol;
import com.esri.ags.symbols.SimpleFillSymbol;
import com.esri.ags.symbols.SimpleLineSymbol;
import com.esri.ags.symbols.SimpleMarkerSymbol;
import com.esri.ags.symbols.Symbol;
import com.esri.ags.virtualearth.VETiledLayer;

import flash.utils.ByteArray;

import mx.utils.Base64Encoder;
import mx.utils.ObjectUtil;

public class LayerToXMLUtil
{
    public static function basemapLayerToXML(layer:Layer, label:String):XML
    {
        return layerToXML(layer, label);
    }

    public static function operationalLayerToXML(layer:Layer, label:String):XML
    {
        const layerXML:XML = layerToXML(layer, label);

        if (layerXML)
        {
            if (layer.minScale > 0)
            {
                layerXML.@minscale = layer.minScale;
            }
            if (layer.maxScale > 0)
            {
                layerXML.@maxscale = layer.maxScale;
            }
        }

        return layerXML;
    }

    private static function layerToXML(layer:Layer, label:String):XML
    {
        var lyrXML:XML;

        if (layer is ArcGISDynamicMapServiceLayer)
        {
            var dynLyr:ArcGISDynamicMapServiceLayer = layer as ArcGISDynamicMapServiceLayer;
            lyrXML = <layer label={label}
                    type="dynamic"
                    visible={dynLyr.visible}
                    alpha={dynLyr.alpha}
                    url={dynLyr.url}
                    useproxy={dynLyr.proxyURL != null}/>;
            if (dynLyr.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(dynLyr.refreshInterval);
            }
            if (dynLyr.showInLegend)
            {
                if (dynLyr.showInLegendHiddenLayers)
                {
                    lyrXML.@showinlegendhiddenlayers = dynLyr.showInLegendHiddenLayers.join();
                }
            }
            else
            {
                lyrXML.@showinlegend = false;
            }
            if (dynLyr.visibleLayers)
            {
                lyrXML.@visiblelayers = dynLyr.visibleLayers.toArray().join();
            }
            if (dynLyr.layerDefinitions && dynLyr.layerDefinitions.length > 0)
            {
                // <sublayer id="2" definitionexpression="AGBUR = 'BIA'"/>
                for each (var layerDefinition:LayerDefinition in dynLyr.layerDefinitions)
                {
                    lyrXML.appendChild(<sublayer id={layerDefinition.layerId} definitionexpression={layerDefinition.definition}/>);
                }
            }
        }
        else if (layer is ArcGISImageServiceLayer)
        {
            var imgLyr:ArcGISImageServiceLayer = layer as ArcGISImageServiceLayer;
            lyrXML = <layer label={label}
                    type="image"
                    visible={imgLyr.visible}
                    alpha={imgLyr.alpha}
                    url={imgLyr.url}
                    useproxy={imgLyr.proxyURL != null}/>;
            if (imgLyr.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(imgLyr.refreshInterval);
            }
            if (!imgLyr.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
            if (imgLyr.bandIds)
            {
                lyrXML.@bandids = imgLyr.bandIds.join();
            }
        }
        else if (layer is ArcGISTiledMapServiceLayer)
        {
            var tiledLyr:ArcGISTiledMapServiceLayer = layer as ArcGISTiledMapServiceLayer;
            lyrXML = <layer label={label}
                    type="tiled"
                    visible={tiledLyr.visible}
                    alpha={tiledLyr.alpha}
                    url={tiledLyr.url}
                    useproxy={tiledLyr.proxyURL != null}/>;
            if (tiledLyr.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(tiledLyr.refreshInterval);
            }
            if (tiledLyr.showInLegend)
            {
                if (tiledLyr.showInLegendHiddenLayers)
                {
                    lyrXML.@showinlegendhiddenlayers = tiledLyr.showInLegendHiddenLayers.join();
                }
            }
            else
            {
                lyrXML.@showinlegend = false;
            }
            if (tiledLyr.displayLevels)
            {
                lyrXML.@displaylevels = tiledLyr.displayLevels.join();
            }
        }
        else if (layer is CSVLayer)
        {
            var csvLyr:CSVLayer = layer as CSVLayer;
            lyrXML = <layer label={label}
                    type="csv"
                    visible={csvLyr.visible}
                    alpha={csvLyr.alpha}
                    url={csvLyr.url}
                    longitudefieldname={csvLyr.longitudeFieldName}
                    latitudefieldname={csvLyr.latitudeFieldName}/>;
            if (csvLyr.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(csvLyr.refreshInterval);
            }
            if (!csvLyr.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
            if (csvLyr.columnDelimiter != ",")
            {
                lyrXML.@columndelimiter = csvLyr.columnDelimiter;
            }
            if (csvLyr.sourceFields)
            {
                var fields:Array = [];
                for each (var field:Field in csvLyr.sourceFields)
                {
                    fields.push(field.name + "|" + field.alias + "|" + field.type);
                }
                lyrXML.@sourcefields = fields.join();
            }
        }
        else if (layer is FeatureLayer && !(layer is CSVLayer))
        {
            var feaLyr:FeatureLayer = layer as FeatureLayer;
            if (!feaLyr.featureCollection)
            {
                lyrXML = <layer label={label}
                        type="feature"
                        visible={feaLyr.visible}
                        alpha={feaLyr.alpha}
                        mode={feaLyr.mode}
                        url={feaLyr.url}
                        iseditable={feaLyr.isEditable}
                        useproxy={feaLyr.proxyURL != null}/>;
                if (feaLyr.refreshInterval > 0)
                {
                    lyrXML.@autorefresh = minutesToSeconds(feaLyr.refreshInterval);
                }
                if (!feaLyr.showInLegend)
                {
                    lyrXML.@showinlegend = false;
                }
            }
        }
        else if (layer is GeoRSSLayer)
        {
            var geoRSSLayer:GeoRSSLayer = layer as GeoRSSLayer;
            lyrXML = <layer label={label}
                    type="georss"
                    visible={geoRSSLayer.visible}
                    alpha={geoRSSLayer.alpha}
                    url={geoRSSLayer.url}/>;
            if (geoRSSLayer.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(geoRSSLayer.refreshInterval);
            }
            if (!geoRSSLayer.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
            if (geoRSSLayer.serviceURL)
            {
                lyrXML.@serviceurl = geoRSSLayer.serviceURL;
            }
            if (geoRSSLayer.pointSymbol)
            {
                lyrXML.appendChild(getPointSymbolXML(geoRSSLayer.pointSymbol));
            }
            if (geoRSSLayer.polylineSymbol)
            {
                lyrXML.appendChild(getLineSymbolXML(geoRSSLayer.polylineSymbol));
            }
            if (geoRSSLayer.polygonSymbol)
            {
                lyrXML.appendChild(getPolygonSymbolXML(geoRSSLayer.polygonSymbol));
            }
        }
        else if (layer is KMLLayer)
        {
            var kmlLyr:KMLLayer = layer as KMLLayer;
            lyrXML = <layer label={label}
                    type="kml"
                    visible={kmlLyr.visible}
                    alpha={kmlLyr.alpha}
                    url={kmlLyr.url}/>;
            if (kmlLyr.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(kmlLyr.refreshInterval);
            }
            if (!kmlLyr.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
        }
        else if (layer is OpenStreetMapLayer)
        {
            var osmLyr:OpenStreetMapLayer = layer as OpenStreetMapLayer;
            lyrXML = <layer label={label}
                    type="osm"
                    visible={osmLyr.visible}
                    alpha={osmLyr.alpha}/>;
            if (osmLyr.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(osmLyr.refreshInterval);
            }
            if (!osmLyr.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
        }
        else if (layer is VETiledLayer)
        {
            var veLyr:VETiledLayer = layer as VETiledLayer;
            lyrXML = <layer label={label}
                    type="bing"
                    visible={veLyr.visible}
                    alpha={veLyr.alpha}
                    style={veLyr.mapStyle}/>;
            if (veLyr.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(veLyr.refreshInterval);
            }
            if (!veLyr.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
            if (veLyr.displayLevels)
            {
                lyrXML.@displaylevels = veLyr.displayLevels.join();
            }
        }
        else if (layer is WMSLayer)
        {
            var wmsLayer:WMSLayer = layer as WMSLayer;
            lyrXML = <layer label={label}
                    type="wms"
                    visible={wmsLayer.visible}
                    alpha={wmsLayer.alpha}
                    version={wmsLayer.version}
                    skipgetcapabilities={wmsLayer.skipGetCapabilities}
                    imageformat={wmsLayer.imageFormat}
                    url={wmsLayer.url}/>;
            if (wmsLayer.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(wmsLayer.refreshInterval);
            }
            if (!wmsLayer.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
            if (wmsLayer.copyright)
            {
                lyrXML.@copyright = wmsLayer.copyright;
            }
            if (wmsLayer.spatialReference)
            {
                lyrXML.@wkid = wmsLayer.spatialReference.wkid;
            }
            if (wmsLayer.maxImageHeight > 0)
            {
                lyrXML.@maximageheight = wmsLayer.maxImageHeight;
            }
            if (wmsLayer.maxImageWidth > 0)
            {
                lyrXML.@maximagewidth = wmsLayer.maxImageWidth;
            }
            if (wmsLayer.visibleLayers)
            {
                lyrXML.@visiblelayers = wmsLayer.visibleLayers.toArray().join();
            }
        }
        else if (layer is WMTSLayer)
        {
            var wmtsLayer:WMTSLayer = layer as WMTSLayer;
            lyrXML = <layer label={label}
                    type="wmts"
                    visible={wmtsLayer.visible}
                    alpha={wmtsLayer.alpha}
                    version={wmtsLayer.version}
                    imageformat={wmtsLayer.imageFormat}
                    url={wmtsLayer.url}
                    useproxy={wmtsLayer.proxyURL != null}/>;
            if (wmtsLayer.refreshInterval > 0)
            {
                lyrXML.@autorefresh = minutesToSeconds(wmtsLayer.refreshInterval);
            }
            if (!wmtsLayer.showInLegend)
            {
                lyrXML.@showinlegend = false;
            }
            if (wmtsLayer.layerId)
            {
                lyrXML.@layerid = wmtsLayer.layerId;
            }
            if (wmtsLayer.serviceMode)
            {
                lyrXML.@mode = wmtsLayer.serviceMode;
            }
            if (wmtsLayer.style)
            {
                lyrXML.@style = wmtsLayer.style;
            }
        }
        else if (layer is WebTiledLayer)
        {
            var webTiledLayer:WebTiledLayer = layer as WebTiledLayer;
            var defaultWebTiledLayer:WebTiledLayer = new WebTiledLayer();
            var defaultTileInfo:TileInfo = defaultWebTiledLayer.tileInfo;

            var hasSupportedTileInfo:Boolean = ObjectUtil.compare(defaultTileInfo, webTiledLayer.tileInfo) == 0;
            if (hasSupportedTileInfo)
            {
                lyrXML = <layer label={label}
                        type="webtiled"
                        visible={webTiledLayer.visible}
                        alpha={webTiledLayer.alpha}
                        url={webTiledLayer.urlTemplate}/>;
                if (webTiledLayer.refreshInterval > 0)
                {
                    lyrXML.@autorefresh = minutesToSeconds(webTiledLayer.refreshInterval);
                }
                if (!webTiledLayer.showInLegend)
                {
                    lyrXML.@showinlegend = false;
                }
                if (webTiledLayer.copyright)
                {
                    lyrXML.@copyright = webTiledLayer.copyright;
                }
                if (webTiledLayer.displayLevels)
                {
                    lyrXML.@displaylevels = webTiledLayer.displayLevels.join();
                }
                if (webTiledLayer.subDomains)
                {
                    lyrXML.@subdomains = webTiledLayer.subDomains.join();
                }
            }
        }

        return lyrXML;
    }

    private static function minutesToSeconds(minutes:Number):Number
    {
        const SECONDS_IN_MINUTE:int = 60;
        return minutes * SECONDS_IN_MINUTE;
    }

    private static function getPointSymbolXML(pointSymbol:Symbol):XML
    {
        var pointSymbolXML:XML;

        if (pointSymbol)
        {
            if (pointSymbol is SimpleMarkerSymbol)
            {
                var sms:SimpleMarkerSymbol = pointSymbol as SimpleMarkerSymbol;

                pointSymbolXML = <simplemarkersymbol
                        alpha={sms.alpha}
                        color={sms.color}
                        size={sms.size}
                        style={sms.style}/>;

                if (sms.outline)
                {
                    pointSymbolXML.appendChild(<outline
                                                   color={sms.outline.color}
                                                   width={sms.outline.width}
                                                   style={sms.outline.style}/>);
                }
            }
            else if (pointSymbol is PictureMarkerSymbol)
            {
                var pms:PictureMarkerSymbol = pointSymbol as PictureMarkerSymbol;

                pointSymbolXML = <picturemarkersymbol
                        height={pms.height}
                        width={pms.width}
                        xoffset={pms.xoffset}
                        yoffset={pms.yoffset}
                        angle={pms.angle}/>;

                var sourceData:ByteArray = pms.source as ByteArray;
                if (sourceData)
                {
                    var encoder:Base64Encoder = new Base64Encoder();
                    encoder.insertNewLines = false;
                    encoder.encodeBytes(sourceData)
                    pointSymbolXML.@source = encoder.toString();
                }
                else
                {
                    pointSymbolXML.@url = pms.source;
                }
            }
        }

        return pointSymbolXML;
    }

    private static function getLineSymbolXML(polylineSymbol:Symbol):XML
    {
        var polylineSymbolXML:XML;

        if (polylineSymbol)
        {
            var sls:SimpleLineSymbol = polylineSymbol as SimpleLineSymbol;

            polylineSymbolXML = <simplelinesymbol
                    alpha={sls.alpha}
                    color={sls.color}
                    width={sls.width}
                    style={sls.style}/>;
        }

        return polylineSymbolXML;
    }

    private static function getPolygonSymbolXML(polygonSymbol:Symbol):XML
    {
        var polygonSymbolXML:XML;

        if (polygonSymbol)
        {
            var sfs:SimpleFillSymbol = polygonSymbol as SimpleFillSymbol;

            polygonSymbolXML = <simplefillsymbol
                    alpha={sfs.alpha}
                    color={sfs.color}
                    style={sfs.style}/>;

            if (sfs.outline)
            {
                polygonSymbolXML.appendChild(<outline
                                                 color={sfs.outline.color}
                                                 width={sfs.outline.width}
                                                 style={sfs.outline.style}/>);
            }
        }

        return polygonSymbolXML;
    }
}
}

package com.esri.builder.supportClasses
{

import com.esri.ags.layers.ArcGISDynamicMapServiceLayer;
import com.esri.ags.layers.ArcGISImageServiceLayer;
import com.esri.ags.layers.ArcGISTiledMapServiceLayer;
import com.esri.ags.layers.CSVLayer;
import com.esri.ags.layers.FeatureLayer;
import com.esri.ags.layers.GeoRSSLayer;
import com.esri.ags.layers.KMLLayer;
import com.esri.ags.layers.Layer;
import com.esri.ags.layers.OpenStreetMapLayer;
import com.esri.ags.layers.WMSLayer;
import com.esri.ags.layers.WMTSLayer;
import com.esri.ags.layers.WebTiledLayer;
import com.esri.ags.layers.supportClasses.Field;
import com.esri.ags.layers.supportClasses.LayerDefinition;
import com.esri.ags.layers.supportClasses.TileInfo;
import com.esri.ags.symbols.PictureMarkerSymbol;
import com.esri.ags.symbols.SimpleFillSymbol;
import com.esri.ags.symbols.SimpleLineSymbol;
import com.esri.ags.symbols.SimpleMarkerSymbol;
import com.esri.ags.symbols.Symbol;
import com.esri.ags.virtualearth.VETiledLayer;

import flash.utils.ByteArray;

import mx.utils.Base64Encoder;
import mx.utils.ObjectUtil;

public class LayerToXMLUtil
{
    public static function basemapLayerToXML(layer:Layer, label:String):XML
    {
        return layerToXML(layer, label);
    }

    public static function operationalLayerToXML(layer:Layer, label:String):XML
    {
        const layerXML:XML = layerToXML(layer, label);

        if (layerXML)
        {
            if (layer.minScale > 0)
            {
                layerXML.@minscale = layer.minScale;
            }
            if (layer.maxScale > 0)
            {
                layerXML.@maxscale = layer.maxScale;
            }
        }

        return layerXML;
    }

    private static function layerToXML(layer:Layer, label:String):XML
    {
        var layerXML:XML;

        if (layer is ArcGISDynamicMapServiceLayer)
        {
            var dynLayer:ArcGISDynamicMapServiceLayer = layer as ArcGISDynamicMapServiceLayer;

            layerXML = <layer label={label}
                    type="dynamic"
                    visible={dynLayer.visible}
                    alpha={dynLayer.alpha}
                    url={dynLayer.url}
                    useproxy={dynLayer.proxyURL != null}/>;

            if (dynLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(dynLayer.refreshInterval);
            }

            if (dynLayer.showInLegend)
            {
                if (dynLayer.showInLegendHiddenLayers)
                {
                    layerXML.@showinlegendhiddenlayers = dynLayer.showInLegendHiddenLayers.join();
                }
            }
            else
            {
                layerXML.@showinlegend = false;
            }

            if (dynLayer.visibleLayers)
            {
                layerXML.@visiblelayers = dynLayer.visibleLayers.toArray().join();
            }

            if (dynLayer.layerDefinitions && dynLayer.layerDefinitions.length > 0)
            {
                // <sublayer id="2" definitionexpression="AGBUR = 'BIA'"/>
                for each (var layerDefinition:LayerDefinition in dynLayer.layerDefinitions)
                {
                    layerXML.appendChild(<sublayer id={layerDefinition.layerId} definitionexpression={layerDefinition.definition}/>);
                }
            }
        }
        else if (layer is ArcGISImageServiceLayer)
        {
            var imgLayer:ArcGISImageServiceLayer = layer as ArcGISImageServiceLayer;

            layerXML = <layer label={label}
                    type="image"
                    visible={imgLayer.visible}
                    alpha={imgLayer.alpha}
                    url={imgLayer.url}
                    useproxy={imgLayer.proxyURL != null}/>;

            if (imgLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(imgLayer.refreshInterval);
            }

            if (!imgLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }

            if (imgLayer.bandIds)
            {
                layerXML.@bandids = imgLayer.bandIds.join();
            }
        }
        else if (layer is ArcGISTiledMapServiceLayer)
        {
            var tiledLayer:ArcGISTiledMapServiceLayer = layer as ArcGISTiledMapServiceLayer;
            layerXML = <layer label={label}
                    type="tiled"
                    visible={tiledLayer.visible}
                    alpha={tiledLayer.alpha}
                    url={tiledLayer.url}
                    useproxy={tiledLayer.proxyURL != null}/>;

            if (tiledLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(tiledLayer.refreshInterval);
            }

            if (tiledLayer.showInLegend)
            {
                if (tiledLayer.showInLegendHiddenLayers)
                {
                    layerXML.@showinlegendhiddenlayers = tiledLayer.showInLegendHiddenLayers.join();
                }
            }
            else
            {
                layerXML.@showinlegend = false;
            }

            if (tiledLayer.displayLevels)
            {
                layerXML.@displaylevels = tiledLayer.displayLevels.join();
            }
        }
        else if (layer is CSVLayer)
        {
            var csvLayer:CSVLayer = layer as CSVLayer;

            layerXML = <layer label={label}
                    type="csv"
                    visible={csvLayer.visible}
                    alpha={csvLayer.alpha}
                    url={csvLayer.url}
                    longitudefieldname={csvLayer.longitudeFieldName}
                    latitudefieldname={csvLayer.latitudeFieldName}/>;

            if (csvLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(csvLayer.refreshInterval);
            }

            if (!csvLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }

            if (csvLayer.columnDelimiter != ",")
            {
                layerXML.@columndelimiter = csvLayer.columnDelimiter;
            }

            if (csvLayer.sourceFields)
            {
                var fields:Array = [];
                for each (var field:Field in csvLayer.sourceFields)
                {
                    fields.push(field.name + "|" + field.alias + "|" + field.type);
                }
                layerXML.@sourcefields = fields.join();
            }
        }
        else if (layer is FeatureLayer && !(layer is CSVLayer))
        {
            var feaLayer:FeatureLayer = layer as FeatureLayer;

            if (!feaLayer.featureCollection)
            {
                layerXML = <layer label={label}
                        type="feature"
                        visible={feaLayer.visible}
                        alpha={feaLayer.alpha}
                        mode={feaLayer.mode}
                        url={feaLayer.url}
                        iseditable={feaLayer.isEditable}
                        useproxy={feaLayer.proxyURL != null}/>;

                if (feaLayer.refreshInterval > 0)
                {
                    layerXML.@autorefresh = minutesToSeconds(feaLayer.refreshInterval);
                }

                if (!feaLayer.showInLegend)
                {
                    layerXML.@showinlegend = false;
                }
            }
        }
        else if (layer is GeoRSSLayer)
        {
            var geoRSSLayer:GeoRSSLayer = layer as GeoRSSLayer;

            layerXML = <layer label={label}
                    type="georss"
                    visible={geoRSSLayer.visible}
                    alpha={geoRSSLayer.alpha}
                    url={geoRSSLayer.url}/>;

            if (geoRSSLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(geoRSSLayer.refreshInterval);
            }

            if (!geoRSSLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }

            if (geoRSSLayer.serviceURL)
            {
                layerXML.@serviceurl = geoRSSLayer.serviceURL;
            }

            if (geoRSSLayer.pointSymbol)
            {
                layerXML.appendChild(getPointSymbolXML(geoRSSLayer.pointSymbol));
            }

            if (geoRSSLayer.polylineSymbol)
            {
                layerXML.appendChild(getLineSymbolXML(geoRSSLayer.polylineSymbol));
            }

            if (geoRSSLayer.polygonSymbol)
            {
                layerXML.appendChild(getPolygonSymbolXML(geoRSSLayer.polygonSymbol));
            }
        }
        else if (layer is KMLLayer)
        {
            var kmlLayer:KMLLayer = layer as KMLLayer;

            layerXML = <layer label={label}
                    type="kml"
                    visible={kmlLayer.visible}
                    alpha={kmlLayer.alpha}
                    url={kmlLayer.url}/>;

            if (kmlLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(kmlLayer.refreshInterval);
            }

            if (!kmlLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }
        }
        else if (layer is OpenStreetMapLayer)
        {
            var osmLayer:OpenStreetMapLayer = layer as OpenStreetMapLayer;

            layerXML = <layer label={label}
                    type="osm"
                    visible={osmLayer.visible}
                    alpha={osmLayer.alpha}/>;

            if (osmLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(osmLayer.refreshInterval);
            }

            if (!osmLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }
        }
        else if (layer is VETiledLayer)
        {
            var veLayer:VETiledLayer = layer as VETiledLayer;

            layerXML = <layer label={label}
                    type="bing"
                    visible={veLayer.visible}
                    alpha={veLayer.alpha}
                    style={veLayer.mapStyle}/>;

            if (veLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(veLayer.refreshInterval);
            }

            if (!veLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }

            if (veLayer.displayLevels)
            {
                layerXML.@displaylevels = veLayer.displayLevels.join();
            }
        }
        else if (layer is WMSLayer)
        {
            var wmsLayer:WMSLayer = layer as WMSLayer;

            layerXML = <layer label={label}
                    type="wms"
                    visible={wmsLayer.visible}
                    alpha={wmsLayer.alpha}
                    version={wmsLayer.version}
                    skipgetcapabilities={wmsLayer.skipGetCapabilities}
                    imageformat={wmsLayer.imageFormat}
                    url={wmsLayer.url}/>;

            if (wmsLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(wmsLayer.refreshInterval);
            }

            if (!wmsLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }

            if (wmsLayer.copyright)
            {
                layerXML.@copyright = wmsLayer.copyright;
            }

            if (wmsLayer.spatialReference)
            {
                layerXML.@wkid = wmsLayer.spatialReference.wkid;
            }

            if (wmsLayer.maxImageHeight > 0)
            {
                layerXML.@maximageheight = wmsLayer.maxImageHeight;
            }

            if (wmsLayer.maxImageWidth > 0)
            {
                layerXML.@maximagewidth = wmsLayer.maxImageWidth;
            }

            if (wmsLayer.visibleLayers)
            {
                layerXML.@visiblelayers = wmsLayer.visibleLayers.toArray().join();
            }
        }
        else if (layer is WMTSLayer)
        {
            var wmtsLayer:WMTSLayer = layer as WMTSLayer;

            layerXML = <layer label={label}
                    type="wmts"
                    visible={wmtsLayer.visible}
                    alpha={wmtsLayer.alpha}
                    version={wmtsLayer.version}
                    imageformat={wmtsLayer.imageFormat}
                    url={wmtsLayer.url}
                    useproxy={wmtsLayer.proxyURL != null}/>;

            if (wmtsLayer.refreshInterval > 0)
            {
                layerXML.@autorefresh = minutesToSeconds(wmtsLayer.refreshInterval);
            }

            if (!wmtsLayer.showInLegend)
            {
                layerXML.@showinlegend = false;
            }

            if (wmtsLayer.layerId)
            {
                layerXML.@layerid = wmtsLayer.layerId;
            }

            if (wmtsLayer.serviceMode)
            {
                layerXML.@mode = wmtsLayer.serviceMode;
            }

            if (wmtsLayer.style)
            {
                layerXML.@style = wmtsLayer.style;
            }
        }
        else if (layer is WebTiledLayer)
        {
            var webTiledLayer:WebTiledLayer = layer as WebTiledLayer;
            var defaultWebTiledLayer:WebTiledLayer = new WebTiledLayer();
            var defaultTileInfo:TileInfo = defaultWebTiledLayer.tileInfo;

            var hasSupportedTileInfo:Boolean = ObjectUtil.compare(defaultTileInfo, webTiledLayer.tileInfo) == 0;
            if (hasSupportedTileInfo)
            {
                layerXML = <layer label={label}
                        type="webtiled"
                        visible={webTiledLayer.visible}
                        alpha={webTiledLayer.alpha}
                        url={webTiledLayer.urlTemplate}/>;

                if (webTiledLayer.refreshInterval > 0)
                {
                    layerXML.@autorefresh = minutesToSeconds(webTiledLayer.refreshInterval);
                }

                if (!webTiledLayer.showInLegend)
                {
                    layerXML.@showinlegend = false;
                }

                if (webTiledLayer.copyright)
                {
                    layerXML.@copyright = webTiledLayer.copyright;
                }

                if (webTiledLayer.displayLevels)
                {
                    layerXML.@displaylevels = webTiledLayer.displayLevels.join();
                }

                if (webTiledLayer.subDomains)
                {
                    layerXML.@subdomains = webTiledLayer.subDomains.join();
                }
            }
        }

        return layerXML;
    }

    private static function minutesToSeconds(minutes:Number):Number
    {
        const SECONDS_IN_MINUTE:int = 60;
        return minutes * SECONDS_IN_MINUTE;
    }

    private static function getPointSymbolXML(pointSymbol:Symbol):XML
    {
        var pointSymbolXML:XML;

        if (pointSymbol)
        {
            if (pointSymbol is SimpleMarkerSymbol)
            {
                var sms:SimpleMarkerSymbol = pointSymbol as SimpleMarkerSymbol;

                pointSymbolXML = <simplemarkersymbol
                        alpha={sms.alpha}
                        color={sms.color}
                        size={sms.size}
                        style={sms.style}/>;

                if (sms.outline)
                {
                    pointSymbolXML.appendChild(<outline
                                                   color={sms.outline.color}
                                                   width={sms.outline.width}
                                                   style={sms.outline.style}/>);
                }
            }
            else if (pointSymbol is PictureMarkerSymbol)
            {
                var pms:PictureMarkerSymbol = pointSymbol as PictureMarkerSymbol;

                pointSymbolXML = <picturemarkersymbol
                        height={pms.height}
                        width={pms.width}
                        xoffset={pms.xoffset}
                        yoffset={pms.yoffset}
                        angle={pms.angle}/>;

                var sourceData:ByteArray = pms.source as ByteArray;
                if (sourceData)
                {
                    var encoder:Base64Encoder = new Base64Encoder();
                    encoder.insertNewLines = false;
                    encoder.encodeBytes(sourceData)
                    pointSymbolXML.@source = encoder.toString();
                }
                else
                {
                    pointSymbolXML.@url = pms.source;
                }
            }
        }

        return pointSymbolXML;
    }

    private static function getLineSymbolXML(polylineSymbol:Symbol):XML
    {
        var polylineSymbolXML:XML;

        if (polylineSymbol)
        {
            var sls:SimpleLineSymbol = polylineSymbol as SimpleLineSymbol;

            polylineSymbolXML = <simplelinesymbol
                    alpha={sls.alpha}
                    color={sls.color}
                    width={sls.width}
                    style={sls.style}/>;
        }

        return polylineSymbolXML;
    }

    private static function getPolygonSymbolXML(polygonSymbol:Symbol):XML
    {
        var polygonSymbolXML:XML;

        if (polygonSymbol)
        {
            var sfs:SimpleFillSymbol = polygonSymbol as SimpleFillSymbol;

            polygonSymbolXML = <simplefillsymbol
                    alpha={sfs.alpha}
                    color={sfs.color}
                    style={sfs.style}/>;

            if (sfs.outline)
            {
                polygonSymbolXML.appendChild(<outline
                                                 color={sfs.outline.color}
                                                 width={sfs.outline.width}
                                                 style={sfs.outline.style}/>);
            }
        }

        return polygonSymbolXML;
    }
}
}

Clean up.

Clean up.

* Consolidate variable names.

* Improve readability.


//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.util {

import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;

import com.threerings.util.Maps;
import com.threerings.util.maps.ForwardingMap;
import com.threerings.util.maps.HashMap;
import com.threerings.util.maps.ImmutableMap;

/**
 * A Map that can be sent over the wire, bearing in mind that all keys and values must
 * be primitives or implement Streamable.
 *
 * @see com.threerings.util.Map
 * @see com.threerings.io.Streamable
 */
public class StreamableHashMap extends ForwardingMap
    implements Streamable
{
    public function StreamableHashMap (keyClazz :Class = null)
    {
        super(keyClazz == null ? DEFAULT_MAP : Maps.newMapOf(keyClazz));
    }

    // documentation inherited from interface Streamable
    public function writeObject (out :ObjectOutputStream) :void
    {
        out.writeInt(size());
        forEach(function (key :Object, value :Object) :void {
            out.writeObject(key);
            out.writeObject(value);
        });
    }

    // documentation inherited from interface Streamable
    public function readObject (ins :ObjectInputStream) :void
    {
        var ecount :int = ins.readInt();
        if (ecount > 0) {
            // guess the type of map based on the first key
            var key :Object = ins.readObject();
            _source = Maps.newMapOf(ClassUtil.getClass(key));
            put(key, ins.readObject());
            // now read the rest
            for (var ii :int = 1; ii < ecount; ii++) {
                put(ins.readObject(), ins.readObject());
            }

        } else {
            _source = Maps.newMapOf(String); // hope for the best
        }
    }

    protected static const DEFAULT_MAP :Map = new ImmutableMap(new HashMap());
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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.util {

import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;

import com.threerings.util.Maps;
import com.threerings.util.maps.ForwardingMap;
import com.threerings.util.maps.HashMap;
import com.threerings.util.maps.ImmutableMap;

/**
 * A Map that can be sent over the wire, bearing in mind that all keys and values must
 * be primitives or implement Streamable.
 *
 * @see com.threerings.util.Map
 * @see com.threerings.io.Streamable
 */
public class StreamableHashMap extends ForwardingMap
    implements Streamable
{
    /**
     * Creates a new StreamableHashMap.
     *
     * @param keyClazz The class to use as the map key. May be null, but this functionality is
     * provided only for deserialization. A StreamableHashMap that has not been initialized
     * properly by either a non-null keyClazz or via readObject will throw errors on write and
     * always return null on read.
     */
    public function StreamableHashMap (keyClazz :Class = null)
    {
        super(keyClazz == null ? DEFAULT_MAP : Maps.newMapOf(keyClazz));
    }

    // documentation inherited from interface Streamable
    public function writeObject (out :ObjectOutputStream) :void
    {
        out.writeInt(size());
        forEach(function (key :Object, value :Object) :void {
            out.writeObject(key);
            out.writeObject(value);
        });
    }

    // documentation inherited from interface Streamable
    public function readObject (ins :ObjectInputStream) :void
    {
        var ecount :int = ins.readInt();
        if (ecount > 0) {
            // guess the type of map based on the first key
            var key :Object = ins.readObject();
            _source = Maps.newMapOf(ClassUtil.getClass(key));
            put(key, ins.readObject());
            // now read the rest
            for (var ii :int = 1; ii < ecount; ii++) {
                put(ins.readObject(), ins.readObject());
            }

        } else {
            _source = Maps.newMapOf(String); // hope for the best
        }
    }

    protected static const DEFAULT_MAP :Map = new ImmutableMap(new HashMap());
}
}

Add a comment so people don't accidentally new StreamableHashMap(), and expect it to work.

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