//Claim.isFunction(ClientMgr,"Missing script reference: /Javascript/ClientMgr.js\n required for Profile.js");
if(!ClientMgr) throw new Error('Missing script reference: src="/Javascript/ClientMgr.js\"n required for Profile.js');

if(!window.Const) Const = {};
Object.extend(	Const,	
				{	UNSET_STRING: "-UNSET-STRING-"
				,	UNSET_NUMBER: NaN	
				}
			 );
/**
 * ProfileMgr
 * 
 * ProfileMgr.sendToAdapter(sCommand)
 * ProfileMgr.sendToProfile()
 * ProfileMgr.openGameHighScore(iSku)
 * ProfileMgr.getUserDetails(p_propsDictionary)
 * ProfileMgr.getAvatar(sHtmlElementID,eAvatarSize,sNoDataFoundHTML,sNotAvailableHTML)
 * ProfileMgr.getGameBestScore(iSku, sScoreElementID, sNoDataFoundHTML, sNotAvailableHTML)
 * ProfileMgr.getUserBestScore(iSku, sStringTemplate, oPlaceHoldersDictionary, sTargetElementID, sNoDataFoundHTML, sNotAvailableHTML)
 * ProfileMgr.getUserOnlineGames(sTemplatesString, oPlaceHoldersDictionary, sTargetElementID, iMaxShownGames)
 * ProfileMgr.getUserDownloadGames( sTemplatesString, oPlaceHoldersDictionary, sTargetElementID, iMaxShownGames))
 * ProfileMgr.getUserWornBadge(sBadgeTemplateString, oPlaceHoldersDictionary, sTargetElementID, sNoDataFoundHTML, sNotAvailableHTML)
 *
 * TODO - ProfileMgr.getInstalledGames(...) - TODO
 */
ProfileMgr = new ClientMgr(	 {}
						  ,	 "ProfileMgr");
/**
 * contains default settings untill the ProfileMgr is initiated.
 * Initiation must contain channel, and authenticationAdapter
 * @type Object
 */
ProfileMgr.settings = 	{}
/**
 * The channel. Its expected to be overide by ProfileMgr.init(settings)
 * @type Number
 */
ProfileMgr.settings.channel = Const.UNSET_NUMBER;
/**
 * The URL for the authentication adapter
 * Its expected to be overide by ProfileMgr.init(settings)
 * @type String
 */
ProfileMgr.settings.authenticationAdapter =	Const.UNSET_STRING;
/**
 * The URL of the page that the redirection to the profile sends to
 * @type string
 */
ProfileMgr.settings.userProfile = "/Profile/ProfileSummary.aspx";
/**
 * The size of the avatar in the page the ProfileMgr runs in
 * @type UA.User.Avatar
 */
ProfileMgr.settings.avatarSize = "Size150x200";
/**
 *
 */
ProfileMgr.settings.loginTarget = "_top";
/**
 * A general handler for Failure on asynchronous calls from ProfileMgr
 * @type Function
 */
ProfileMgr.settings.onProfileCallFailure = null;
/**
 * A general handler for Timeout on asynchronous calls from ProfileMgr
 * @type Function
 */
ProfileMgr.settings.onProfileCallTimeout = null;
/**
 * The Url of the channel Homepage.
 * Used when the Mgr sends to the adapter from a popup, while the parent window is closed.
 */
ProfileMgr.settings.channelHomePage = Const.UNSET_STRING;
/**
 * A general error message, used when a connection error or server error occures.
 */
ProfileMgr.settings.noDataMessage = "<b>No data available</b>";
/**
 * The current User - access to UserAccounts API
 */
ProfileMgr.user = new UA.User();
/**
 * Returns true if the current user is logged as member, otherwise - false
 * 
 * @type boolean
 */
ProfileMgr.isMember = function()
{
	return Clearance.isMember();
}

/**
 * @param {object} oSettingsDictionary
 * A settings dictionary specifying at least "channel" and "authenticationAdapter"
 * can include any of the documented properties on the Profile.settings inner object
 */
ProfileMgr.init = function(oSettingsDictionary)
{
	if(!this.isInitiated)
	{
		Claim.isObject(oSettingsDictionary, "ProfileMgr.init(oSettingsDictionary) - oSettingsDictionary must be an object");
		Claim.isNumber(oSettingsDictionary.channel, "ProfileMgr.init(oSettingsDictionary) - oSettingsDictionary.channel must be a number - describing the channel-id");
		Claim.isString(oSettingsDictionary.authenticationAdapter, "ProfileMgr.init(oSettingsDictionary) - oSettingsDictionary.authenticationAdapter must be a string - describing the authentication-adapter URL");
		Claim.isString(oSettingsDictionary.channelHomePage, "ProfileMgr.init(oSettingsDictionary) - oSettingsDictionary.channelHomePage must be a string - expected to be : the  URL for the homepage of the channel");

		this.log = new Log4Js.Logger("ProfileMgr")
		this.isInitiated = true;
	}

	this.set(oSettingsDictionary);
	
}
/**
 * Logs-Off the current User, and redirects
 * to the current page to dispose all cookies
 */
ProfileMgr.logOffCurrentUser = function()
{
	this.log.info("logging off current user...");
	
	// clear any cookie-related QS parts
	var oUrl = Url.parse(location.href);
	var oRemovedQS = String("ui,cx,rx,ux,ax,cn,rn,un,an").split(",");
	for(var i = 0 ; i < oRemovedQS.length; i++)
		delete oUrl.params[oRemovedQS[i]];

	//patch over bug of Url.parse on no QS URLs
	delete oUrl.params[""]

	var sUrl = Url.appendParams(oUrl.base, oUrl.params);
	this.log.info("sending to URL: " + sUrl );
	
	Clearance.forget(sUrl);
}
/**
 * @param {object} oSettingsDictionary
 * can include any of the documented properties on the ProfileMgr.settings inner object
 */
ProfileMgr.set = function(oSettingsDictionary)
{
	Object.extend(this.settings, oSettingsDictionary);	
}
/**
 * Sends the current page to the Authentication Adapter
 *
 * @param {string} sCommand
 * Command passed to the adapter
 */
ProfileMgr.sendToAdapter = function(sCommand)
{
	//assure userProfile 
	Claim.check(this.settings.authenticationAdapter != Const.UNSET_STRING, "ProfileMgr.sendToAdapter used without defining ProfileMgr.settings.authenticationAdapter");
	if(this.settings.authenticationAdapter == Const.UNSET_STRING)
		return alert("sendToAdapter used without defining settings.authenticationAdapter");

	if (sCommand == null || typeof(sCommand) != 'string'){
		sCommand = "login";
	}
	
	var submitSettings = {	action : this.settings.authenticationAdapter
						 ,	method : "GET"	
						 ,  target : this.settings.loginTarget
						 } 

	var sUrl =  Url.parse( this.getReturnUrl() );
	delete sUrl.params.channel;
	delete sUrl.params.lc;
	sUrl = Url.appendParams( sUrl.base, sUrl.params );

	var params =	{	command: sCommand.toLowerCase()
					,	retUrl : sUrl 
					,	channel: this.settings.channel
					};

	//applicative submit to TOP or Opener-window without conflicting IFrames/Windows security
	this.submitToPage(	submitSettings,	params );
	
	//in case the method is called from A.onclick
	return false;
}
/**
 * Sends the current page to the User-Profile page
 */
ProfileMgr.sendToProfile = function()
{
	//assure userProfile 
	Claim.check(this.settings.userProfile != Const.UNSET_STRING, "ProfileMgr.settings.userProfile","ProfileMgr.settings.userProfile is not set");

	//applicative submit to TOP or Opener-window  
	//without conflicting IFrames/Windows security
	this.submitToPage(	{	action : this.settings.userProfile
						,	target : this.settings.loginTarget }
					 );
}		
/**
 * @private
 * When the Mgr runs in the GC - returns the location of the current page.
 * When the Mgr runs in popup - tries to retrieve the Url of the parent, 
 * or returns ProfileMgr.settings.channelHomePage on failure.
 * Used when the sendToAdapter is called on the popup, to retrieve the return URL.
 *
 * Also - @private.
 * wrap function - in cirtain browser versions and settings security issues 
 * hide inner attributes of variables on parent and opener windows.
 * This wrap allows calling a function on the window itself.
 */
ProfileMgr_getReturnUrl = function(){	return ProfileMgr.getReturnUrl(); }
ProfileMgr.getReturnUrl = function()
{
	Claim.isTrue( this.isInitiated , "ProfileMgr.getReturnUrl() was called before initiating ProfileMgr. \n\nSee ProfileMgr.init(oSettingsDictionary)");
	// when running in popup - use opener
	if( window.opener )
	{
		try
		{
			return window.opener.ProfileMgr_getReturnUrl()
		}
		catch(ex)
		{
			this.log.warn("ProfileMgr.getReturnUrl() failed to retrieve location from the opener window. channel-home used instead: " + this.settings.channelHomePage + ". Exception: " + Object.serialize(ex));
			return this.settings.channelHomePage;
		}	
	}
	// Use highest IFRAME with a ProfileMgr instance
	if( window.parent != window)
	{
		this.log.info("ProfileMgr.getReturnUrl() - Parent window detected");
		try
		{
			return window.parent.ProfileMgr_getReturnUrl()
		}
		catch(ex)
		{
			switch(typeof(ex))
			{
				case 'object': /*explorer*/
					this.log.debug("ProfileMgr.getReturnUrl() - Parent window detected, assumed Explorer-based browser ");
					if	(	ex.number == -2146827850 /*doesn't support this property*/
						||	ex.number == -2146823281 /*is null or not an object*/
						)
					{
						this.log.warn("widow.parent.ProfileMgr_getReturnUrl() is not found on parent window. Code runs in IFrame on a page of the partner. Exception: " + Object.serialize(ex) );
					}
					else
					{			
						this.log.warn("ProfileMgr.getReturnUrl() failed to retrieve location from the parent window for unexpected reason. Channel-home used instead: " + this.settings.channelHomePage + ". Exception: " + Object.serialize(ex));
						return this.settings.channelHomePage;
					}
				break;
				case 'string': /* FireFox/Mozilla */ 
					this.log.debug("ProfileMgr.getReturnUrl() - Parent window detected, assumed Mozilla-based browser ");
	
					/** 
					 * the identifier of the exception is the message FF/Mozila use for permission denied:
					 * "Permission denied to get property Window.ProfileMgr_getReturnUrl"
					 * However, the words "Permission denied to get property" can be localized, 
					 * that leaves us only the property name...
					 */
					if	(  ex.indexOf("Window.ProfileMgr_getReturnUrl") != -1 ) 
					{
						this.log.warn("widow.parent.ProfileMgr_getReturnUrl() is not found on parent window. Code runs in IFrame on a page of the partner. Exception: " + ex);
					}
					else
					{			
						this.log.warn("ProfileMgr.getReturnUrl() failed to retrieve location from the parent window for unexpected reason. Channel-home used instead: " + this.settings.channelHomePage + ". Exception: " + ex);
						return this.settings.channelHomePage;
					}
				break;
			}
		}		
	}

	var sUrl = this.Url.full;
	//PATCH: - remove #anchor - overcome a bug of the infrastructure
	i =	sUrl.lastIndexOf("#");
	if(   i > -1 && i > sUrl.lastIndexOf("?") && i > sUrl.lastIndexOf("=") )
	{
		sUrl = sUrl.substr(0,i);
	}		
	return sUrl;
	

}
/**
 * High-Score - to open popup of user high-score of his worn badge
 */
ProfileMgr.addManagedPage( "HighScore", "/Orange2.0/App/GameHighScore.aspx", "width=611,height=440,status=no,scrollbars=yes,toolbar=no,menubar=no,location=no,resizeable=no");  							  
/**
 * Opens the high-score Page
 */
ProfileMgr.openGameHighScore = function(iSku)
{
	var oParams = Object.extend({}, this.Url.params);
	if(iSku)
	{
		oParams.sku = iSku;
	}
	this.openWindow("HighScore",oParams, {method: "GET"});
}
/**
 * Fetches and Populates the User-Details to the HTML elements, 
 * as specified by the provided dictionary.
 * The dictionary is expected to contain HTML element-IDs as keys, 
 * and User-Details properties as values.
 *
 * @param {object} p_propsDictionary
 * Supported properties as values 
 * according to the product UA.User.getDetails.
 * Known by the time of this update:
 *  - age            - gender
 *  - nickname       - birthDayOfMonth
 *  - birthMonth     - birthYear
 *  - country        - zipCode
 *
 * Uses a subclass of UA.USer as worker class.
 * It is described right bellow.
 */
ProfileMgr.getUserDetails = function(p_propsDictionary,oEvents)
{
	var ud = new ProfileMgr.UserDetails( p_propsDictionary );
	ud = Object.extend(ud,oEvents);
	ud.apply();
}
//--ProfileMgr.UserDetails-----------------------------------------------------------------
  /**
   * A worker class that fetches user details.
   */
	ProfileMgr.UserDetails = Class.create("UA.User");
	ProfileMgr.UserDetails = Class.enhance(ProfileMgr.UserDetails, IEventDispatcher);
	ProfileMgr.UserDetails.prototype.initialize = function(pPropsDictionary, fFailure, fTimeout)
	{
		Claim.isObject(pPropsDictionary, "UserDetails.get(pPropsDictionary) - pPropsDictionary is expected to be an object");
		
		this.log = new Log4Js.Logger("ProfileMgr.UserDetails");

		this.detailsPropDictionary = pPropsDictionary;
	}
	ProfileMgr.UserDetails.prototype.apply = function(){
		this.GetUserDetails(  this.onSuccess
							, this.onFailure 
							, this.onTimeout);
	}
	ProfileMgr.UserDetails.prototype.onSuccess = function(data)
	{
		this.log.debug("onSuccess(" + Object.serialize(data) + ")");

		this.dispatch("onDataBind", data);

		ProfileMgr.populateElements (/*oElementsDictionary*/ this.detailsPropDictionary
									,/*oDataSource		  */ data
									);
		this.log.debug("onSuccess is complete");
	}
	ProfileMgr.UserDetails.prototype.onFailure = function(r){ this.log.error('FAILURE on ProfileMgr.UserDetails.get()'); }
	ProfileMgr.UserDetails.prototype.onTimeout = function(r){ this.log.error('TIMEOUT on ProfileMgr.UserDetails.get()'); }
	ProfileMgr.UserDetails.get = function(pPropsDictionary, fFailure, fTimeout, oUser)
	{
		new ProfileMgr.UserDetails(pPropsDictionary, fFailure, fTimeout, oUser);
	}
//--/ProfileMgr.UserDetails------------------------------------------------------------
/**
 * ProfileMgr.getAvatar
 * gets the avatar of the user, and sets it's HTML to the provided sHtmlElementID.
 * The size of the avatar can be specified by the optional eAvatarSize
 *
 * @param {string} sHtmlElementID
 *
 * @param {Object(optional)} oSettings
 * Any settings, configurations, HTML-attributes or Flash-Params we want to pass on.
 *
 */
ProfileMgr.getAvatar = function(sHtmlElementID, oSettings, oEvents)
{
	Claim.isString(sHtmlElementID, "sHtmlElementID is expected to be the ID of the target element");
	oSettings = oSettings || {};

   ua = new GameCatalog.AvatarViewer( oSettings );
   ua = Object.extend(ua,oEvents);
   ua.render(sHtmlElementID);
}

/**
 * ProfileMgr.getAvatarStudio
 * gets the avatar of the user, and sets it's HTML to the provided sHtmlElementID.
 * The size of the avatar can be specified by the optional eAvatarSize
 *
 * @param {string} sHtmlElementID
 *
 * @param {Object(optional)} oSettings
 * Any settings, configurations, HTML-attributes or Flash-Params we want to pass on.
 *
 */
ProfileMgr.getAvatarStudio = function(sHtmlElementID, oSettings,oEvents)
{
	Claim.isString(sHtmlElementID, "sHtmlElementID is expected to be the ID of the target element");
	oSettings = oSettings || {};

   ua = new GameCatalog.AvatarStudio( sHtmlElementID, oSettings );
   ua = Object.extend(ua,oEvents);
   ua.apply();

}

/**
 * ProfileMgr.getGameBestScore
 * Populates the cummunity best score of the provided SKU in to the element specified by ID.
 *
 * @param {number} iSku
 * The Sku of the queried game
 *
 * @param {string} sElementID
 * The HTML element ID to feed the top-score into 
 *
 *  @param {string(optional)} sNoDataFoundHTML
 *  HTML to populate target-element when server returms with no data
 *  - optional: When not provided, ProfileMgr.settings.noDataMessage is used
 *
 *  @param {string(optional)} sNotAvailableHTML
 *  HTML to populate target-element when there is a problem with the server's response
 *  - optional: When not provided, ProfileMgr.settings.noDataMessage is used
 */
ProfileMgr.getGameBestScore = function(iSku, sScoreElementID, sNoDataFoundHTML, sNotAvailableHTML, oEvents)
{	
	var gbs = new ProfileMgr.GameBestScore( iSku
										  , sScoreElementID
										  , sNoDataFoundHTML  || this.settings.noDataMessage
										  , sNotAvailableHTML || this.settings.noDataMessage
										  );
	gbs = Object.extend(gbs,oEvents);
	gbs.apply();
}
//--ProfileMgr.GameBestScore------------------------------------------------------------
	/**
	 * A worker class that feches game community high-score
	 */
	ProfileMgr.GameBestScore = Class.create("UA.Game");
	ProfileMgr.GameBestScore = Class.enhance(ProfileMgr.GameBestScore, IEventDispatcher);
	ProfileMgr.GameBestScore.prototype.toString = function()
	{
		return "[UA.Game] extension: GameBestScore";
	}
	ProfileMgr.GameBestScore.prototype.initialize = function(iSku, sScoreElementID, sNoDataFoundHTML, sNotAvailableHTML)
	{
		Claim.isString(sScoreElementID,"ProfileMgr.GameBestScore(sScoreElementID,...) - sScoreElementID must be a string")	
		Claim.isObject($(sScoreElementID),"ProfileMgr.GameBestScore(sScoreElementID,...) - sScoreElementID must be a string representing an HTML element ID")	
		Claim.isString(sNoDataFoundHTML	, "ProfileMgr.GameBestScore(...,sNoDataFoundHTML,...) - must be a string, expected HTML for no-data-found case")
		Claim.isString(sNotAvailableHTML, "ProfileMgr.GameBestScore(...,sNotAvailableHTML) - must be a string, expected HTML for no-data-available case")

		this.log = new Log4Js.Logger("ProfileMgr.GameBestScore");

		this.oTargetElement = $(sScoreElementID);		
		this.noDataFound	= sNoDataFoundHTML;
		this.notAvailable	= sNotAvailableHTML;
		
	}
	ProfileMgr.GameBestScore.prototype.apply = function()
	{
		this.GetGameHighScores  ( /*mode	*/	""	
								, /*iAmount	*/	1			
								, /*period	*/	UA.Game.Scores.Periods.EVER
								, /*fSuccess*/	this.onSuccess
								, /*fFailure*/	this.onFailureOrTimeout
								, /*fTimeout*/	this.onFailureOrTimeout);
	}
	ProfileMgr.GameBestScore.prototype.onSuccess = function(data)
	{
		this.log.info("onSuccess with this data: " + Object.serialize(data));
		if(!data || !data.TopScores || !data.TopScores[0] || !data.TopScores[0].Score)
		{
			this.log.error("GameBestScore did not get game best-score data. Object.serialize(data): " + Object.serialize(data));
			Element.setText(this.oTargetElement, this.noDataFound);
			return;
		}

		this.dispatch("onDataBind", data);
		
		Element.setText(this.oTargetElement, Number(data.TopScores[0].Score).format());
	}
	ProfileMgr.GameBestScore.prototype.onFailureOrTimeout = function(r)
	{ 
		this.log.error('FAILURE or TIMEOUT on ProfileMgr.GameBestScore.get()');
		Element.setText(this.oTargetElement, this.sNotAvailableHTML);
	}
//--/ProfileMgr.GameBestScore------------------------------------------------------------

/**
 * ProfileMgr.getUserBestScore
 * Gets the user's best score to the provided game, and populates the target element
 * using the provided template
 *	
 * @param {number} iSku
 * The Sku of the queried game
 *
 * @param {string} sStringTemplate
 * The template of the populated element
 * 
 * @param {object} oPlaceHoldersDictionary
 * A dictionary with place-holders in the template as keys, 
 * and the expected value-propreties for these places from the server's response as values
 * 
 * @param {string} sTargetElementID
 * The HTML element ID to feed the top-score into 
 *
 * @param {string(optional)} sNoDataFoundHTML
 * HTML to populate target-element when server returms with no data
 * - optional: When not provided, ProfileMgr.settings.noDataMessage is used
 *
 * @param {string(optional)} sNotAvailableHTML
 * HTML to populate target-element when there is a problem with the server's response
 * - optional: When not provided, ProfileMgr.settings.noDataMessage is used
 */
ProfileMgr.getUserBestScore  = function(iSku, sStringTemplate, oPlaceHoldersDictionary, sTargetElementID, sNoDataFoundHTML, sNotAvailableHTML, oEvents)
{
	var ubs = new ProfileMgr.UserBestScore( iSku
											, sStringTemplate
											, oPlaceHoldersDictionary
											, sTargetElementID
											, sNoDataFoundHTML  || this.settings.noDataMessage
											, sNotAvailableHTML || this.settings.noDataMessage
											);
	ubs = Object.extend(ubs, oEvents);
	ubs.apply();
}
//--ProfileMgr.getUserBestScore------------------------------------------------------------
	/**
	 * A worker class that feches game user high-score
	 */
	ProfileMgr.UserBestScore = Class.create("UA.Game");
    ProfileMgr.UserBestScore = Class.enhance(ProfileMgr.UserBestScore, IEventDispatcher);
	ProfileMgr.UserBestScore.prototype.toString = function()
	{
		return "[UA.Game] extension: UserBestScore";
	}
	ProfileMgr.UserBestScore.prototype.initialize = function(iSku, sStringTemplate, oPlaceHoldersDictionary, sTargetElementID, sNoDataFoundHTML, sNotAvailableHTML)
	{
		Claim.isString(sStringTemplate, "UserBestScore.initialize - sStringTemplate must be a string");
		Claim.isObject(oPlaceHoldersDictionary, "UserBestScore.initialize - oPlaceHoldersDictionary  must be an object");
		Claim.isString(sTargetElementID, "UserBestScore.prototype.initialize - sTargetElementID must be a string");
		Claim.isObject($(sTargetElementID), "UserBestScore.prototype.initialize - sTargetElementID must be a string describing an HTML element ID");
		Claim.isString(sNoDataFoundHTML	, "ProfileMgr.UserBestScore(...,sNoDataFoundHTML,...) - must be a string, expected HTML for no-data-found case")
		Claim.isString(sNotAvailableHTML, "ProfileMgr.UserBestScore(...,sNotAvailableHTML) - must be a string, expected HTML for no-data-available case")
		
		this.log = new Log4Js.Logger("ProfileMgr.UserBestScore");
		
		
		this.userBestScoreTemplate = sStringTemplate;
		this.userBestScorePlaceHoldersDictionary = oPlaceHoldersDictionary;
		this.oTargetElement = $(sTargetElementID);		
		this.noDataFound	= sNoDataFoundHTML;
		this.notAvailable	= sNotAvailableHTML;
	

	}
	/**
	 * sends the request
	 */
	ProfileMgr.UserBestScore.prototype.apply = function()
	{
		this.GetUserHighScore( /* mode:		*/ null
							 , /* fSuccess: */ this.onSuccess
							 , /* fFailure: */ this.onFailureOrTimeout
							 , /* fTimeout: */ this.onFailureOrTimeout
							 );
	}
	/**
	* @param {object} data
	* expeced properties on data (provided by infra): 
	*   - userHighScore    - userRanking
	*   - userPosition
	*/
	ProfileMgr.UserBestScore.prototype.onSuccess = function(dataUA)
	{
		
		this.log.info("onSuccess with this Data: " + Object.serialize(dataUA) );

		if(!dataUA.userPosition && !dataUA.userHighScore && !dataUA.userRanking)
		{
			Element.setText(this.oTargetElement, this.noDataFound);
			return;
		}
		
		var data = Object.extend({}, dataUA);
		
		data.userPosition		= (!data.userPosition	   ) ? "&nbsp" : "(" + data.userPosition + ")";
		data.userRankingImgUrl  = (data.userRanking == null) ? ""	   : TC.UserMedals.getMedalByRanking( data.userRanking ).img;
		data.userHighScoreFormatted = Number(data.userHighScore).format();
		
		this.log.debug("pre populating template with : " + Object.serialize(data) );
	
		this.dispatch("onDataBind", data);
		
		var strHTML = ProfileMgr.populateStringTemplate (	this.userBestScoreTemplate
														,	this.userBestScorePlaceHoldersDictionary
														,   data
														);
		Element.setText(this.oTargetElement, strHTML );
	}
	ProfileMgr.UserBestScore.prototype.onFailureTimeout = function()
	{
		this.log.error('FAILURE or TIMEOUT on UserBestScore.get()');
		Element.setText(this.oTargetElement, this.notAvailable );
	}
//--/ProfileMgr.GameBestScore------------------------------------------------------------
/**
 * ProfileMgr.getUserOnlineGames
 * Gets the user's recent online games, and populates them into the target element, 
 * in the format of the provided game-template. 
 *
 * @param {string} sTemplatesString
 * The template for each found game 
 * 
 * @param {object} oPlaceHoldersDictionary
 * A dictionary with place-holders in the template as keys, 
 * and the expected value-propreties for these places from the server's response as values
 *
 * @param {string} sTargetElementID
 * The HTML element ID to feed the top-score into 
 *
 * @param {number(optional)} oParams
 * The max displayed games.
 * - optiona: When not provided - all fetched games are shown.
 *
 */
ProfileMgr.getUserOnlineGames = function(sTemplatesString, oPlaceHoldersDictionary, sTargetElementID, oParams, oEvents)
{
	var uog = new ProfileMgr.UserOnlineGames( sTemplatesString
											, oPlaceHoldersDictionary
											, sTargetElementID
											, oParams
											);
	uog = Object.extend(uog, oEvents);
	uog.apply();
}
//--ProfileMgr.UserOnlineGames------------------------------------------------------------
	ProfileMgr.UserOnlineGames = Class.create("UA.User");
    ProfileMgr.UserOnlineGames = Class.enhance(ProfileMgr.UserOnlineGames, IEventDispatcher);
	ProfileMgr.UserOnlineGames.prototype.initialize = function(sTemplatesString, oPlaceHoldersDictionary, sTargetElementID, oParams )
	{
		Claim.isString(sTemplatesString, "ProfileMgr.UserOnlineGames(GameTemplateString,...) must be a string");
		Claim.isObject(oPlaceHoldersDictionary, "ProfileMgr.UserOnlineGames(...,oPlaceHoldersDictionary,...) must be an object");
		Claim.isString(sTargetElementID, "ProfileMgr.UserOnlineGames(...,sTargetElementID,...) must be a string");
		Claim.isObject($(sTargetElementID), "ProfileMgr.UserOnlineGames(...,sTargetElementID,...) must be a string describing an HTML element");

		if(!oParams) oParams = {};
		
		//backward compatibility - iMaxShownGames as a "default" parameter
		if(parseInt(oParams)) oParams = {maxGames: parseInt(oParams)};

		this.params = Object.extend( this.params, oParams );
		
		if(oParams.maxGames) this.maxDisplayedGames = oParams.maxGames;
		
		this.log = new Log4Js.Logger("ProfileMgr.UserOnlineGames");
		
		this.repeater = new TC.Controls.Repeater( sTemplatesString 
												, this.log
												, {error: "error"} 
												);
		this.repeater.boss = this;
		this.repeater._dispatch = this.repeater.dispatch;
		this.repeater.dispatch = function()
		{
			this.log.info("ProfileMgr.UserOnlineGames.repeater - dispatching " + arguments[0] );
			if(this[arguments[0]])
				return this._dispatch.apply(this, arguments);

			return this.boss.dispatch.apply(this.boss, arguments);
		}

		this.templates = this.repeater.templates;

		if(0 == this.templates.noData.length) this.templates.noData = ProfileMgr.settings.noDataMessage;
		if(0 == this.templates.error.length	) this.templates.error		  = ProfileMgr.settings.noDataMessage;

		this.templatePlaceHoldersDictionary = oPlaceHoldersDictionary;
		this.oTargetElement = $(sTargetElementID);
	}
	ProfileMgr.UserOnlineGames.prototype.apply = function()
	{	
		this.GetUserGames( this.onSuccess
						 , this.onFailureOrTimeout
						 , this.onFailureOrTimeout);
	}
	ProfileMgr.UserOnlineGames.prototype.toString = function()
	{
		return "[UA.User] extension: UserOnlineGames";
	}
	ProfileMgr.UserOnlineGames.prototype.prv_repeater_onItemDataBound = function(item)
	{
		if( !TC.SKUs.extendDataFromSKU(item.dataItem, item.dataItem.gameSku) )
		{
			item.skipItem = true;
			this.log.info(item.dataItem.sku + " was reported as recent user online game, but is not found in games catalog");
		}
		
		this.boss.dispatch("onItemDataBound", item);
	}
	ProfileMgr.UserOnlineGames.prototype.onSuccess = function(data)
	{
		this.log.info("onSuccess with this Data: " + Object.serialize(data) );
		
		var arrGames = data.Games;
		if ( this.maxDisplayedGames && arrGames.length > this.maxDisplayedGames )
			arrGames = arrGames.slice(0, this.maxDisplayedGames);

		this.repeater.onItemDataBound = this.prv_repeater_onItemDataBound;
		this.repeater.dataBind( this.templatePlaceHoldersDictionary, arrGames );

		this.repeater.render( this.oTargetElement );
		return;
	}
	ProfileMgr.UserOnlineGames.prototype.onFailureOrTimeout = function()
	{ 
		this.log.error("FAILURE or TIMEOUT on ProfileMgr.UserOnlineGames");
		Element.setText(this.oTargetElement, this.templates.error);
	}
//--/ProfileMgr.UserOnlineGames------------------------------------------------------------

/**
 *
 */
ProfileMgr.getUserDownloadGames = function(sTemplatesString, oPlaceHoldersDictionary, sTargetElementID, oSettings, oEvents)
{
	if(!oSettings.channel) oSettings.channel = this.settings.channel;

	var udg = new ProfileMgr.UserDownloadGames(sTemplatesString, oPlaceHoldersDictionary, sTargetElementID, oSettings);
	udg = Object.extend(udg, oEvents);
	udg.apply();
}
 
//--ProfileMgr.UserDownloadGames------------------------------------------------------------
    ProfileMgr.UserDownloadGames = Class.create();
    ProfileMgr.UserDownloadGames = Class.enhance(ProfileMgr.UserDownloadGames, IEventDispatcher);
	ProfileMgr.UserDownloadGames.prototype.toString = function()
	{
		return "[ProfileMgr.UserDownloadGames]";
	}
    ProfileMgr.UserDownloadGames.prototype.initialize = function(sTemplatesString, oPlaceHoldersDictionary, sTargetElementID, oSettings)
    {
		Claim.isString(sTemplatesString, "ProfileMgr.UserDownloadGames(...,GameTemplateString,...) must be a string");
		Claim.isObject(oPlaceHoldersDictionary, "ProfileMgr.UserDownloadGames(...,oPlaceHoldersDictionary,...) must be an object");
		Claim.isString(sTargetElementID, "ProfileMgr.UserDownloadGames(...,sTargetElementID,...) must be a string");
		Claim.isObject($(sTargetElementID), "ProfileMgr.UserDownloadGames(...,sTargetElementID,...) must be a string describing an HTML element");

		if(!oSettings) oSettings = {};
		Claim.isScalar(oSettings.channel, "ProfileMgr.UserDownloadGames(.../oSettings.channel) must be the channel-code or codes");
		this.channel = oSettings.channel;

		this.log = new Log4Js.Logger("ProfileMgr.UserDownloadGames");

		this.maxDisplayedGames = oSettings.showMaxGames || 10;

		this.repeater = new TC.Controls.Repeater( sTemplatesString , this.log );
		this.repeater.boss = this;
		this.repeater._dispatch = this.repeater.dispatch;
		this.repeater.dispatch = function()
		{
			this.log.info("ProfileMgr.UserOnlineGames.repeater - dispatching " + arguments[0] );
			if(this[arguments[0]])
				return this._dispatch.apply(this, arguments);

			return this.boss.dispatch.apply(this.boss, arguments);
		}
		
		this.templatePlaceHoldersDictionary = oPlaceHoldersDictionary;
		this.oTargetElement = $(sTargetElementID);
    }
    ProfileMgr.UserDownloadGames.prototype.apply = function()
    {
		var data = ProfileMgr.getInstalledGames(this.channel)
		if (!data.games.length)
		{
			this.log.info("No downloadable games are found.");
		}
		this.onPopulate(data);
    }
    ProfileMgr.UserDownloadGames.prototype.prv_repeater_onItemDataBound = function(item)
	{
		if( !TC.SKUs.extendDataFromSKU(item.dataItem) ){
			this.log.info(item.dataItem.sku + " is installed for channel " + ProfileMgr.settings.channel + " but is not found in games catalog");
			item.skipItem = true;
		}

		this.boss.dispatch("onItemDataBound", item);
	}
    ProfileMgr.UserDownloadGames.prototype.onPopulate = function(data)
    {
    
		this.log.info("populating downloadable games with this Data: " + Object.serialize(data) );
		
		var arrGames = data.games;
		if ( this.maxDisplayedGames && arrGames.length > this.maxDisplayedGames )
			arrGames = arrGames.slice(0, this.maxDisplayedGames);

		this.repeater.onItemDataBound = this.prv_repeater_onItemDataBound;
		this.repeater.dataBind( this.templatePlaceHoldersDictionary, arrGames );

		this.repeater.render( this.oTargetElement );
		return;
    }
//--/ProfileMgr.UserDownloadGames------------------------------------------------------------

/**
 * ProfileMgr.getUserWornBadge
 * Gets the user worn badge, and populates the target element using the worn 
 * badge's properties and the badge HTML template.
 *
 * @param {string} sBadgeTemplateString
 * The template for the user's worn badge 
 *
 * @param {object} oPlaceHoldersDictionary
 * A dictionary with place-holders in the template as keys, 
 * and the expected value-propreties for these places from the server's response as values

 * @param {string} sTargetElementID
 * The HTML element ID to feed the top-score into 
 *
 * @param {string(optional)} sNoDataFoundHTML
 * HTML to populate target-element when server returms with no data
 * - optional: When not provided, ProfileMgr.settings.noDataMessage is used
 *
 * @param {string(optional)} sNotAvailableHTML
 * HTML to populate target-element when there is a problem with the server's response
 * - optional: When not provided, ProfileMgr.settings.noDataMessage is used
 */
ProfileMgr.getUserWornBadge = function(sBadgeTemplateString, oPlaceHoldersDictionary, sTargetElementID, sNoDataFoundHTML, sNotAvailableHTML,oEvents)
{
	var ub = new ProfileMgr.UserBadge(sBadgeTemplateString, oPlaceHoldersDictionary, sTargetElementID
									, sNoDataFoundHTML  || this.settings.noDataMessage
									, sNotAvailableHTML || this.settings.noDataMessage
									);
	ub = Object.extend(ub, oEvents);									
	ub.apply();
}
//--ProfileMgr.UserBadge------------------------------------------------------------
	ProfileMgr.UserBadge = Class.create("UA.User");
    ProfileMgr.UserBadge = Class.enhance(ProfileMgr.UserBadge, IEventDispatcher);
	ProfileMgr.UserBadge.prototype.toString = function()
	{
		return "[UA.User] extension: UserBadge";
	}
	ProfileMgr.UserBadge.prototype.initialize = function(sBadgeTemplateString, oPlaceHoldersDictionary, sTargetElementID, sNoDataFoundHTML, sNotAvailableHTML)
	{
		Claim.isString(sBadgeTemplateString, "UserBadge.prototype.initialize(sTargetElementID, oPlaceHoldersDictionary, sBadgeTargetElementID) - sBadgeTemplateString must be a string");
		Claim.isObject(oPlaceHoldersDictionary, "UserBadge.prototype.initialize(sTargetElementID, oPlaceHoldersDictionary, sBadgeTargetElementID) - oPlaceHoldersDictionary must be an object");
		Claim.isString(sTargetElementID, "UserBadge.prototype.initialize(sTargetElementID, oPlaceHoldersDictionary, sBadgeTargetElementID) - sBadgeTargetElementID must be a string");
		Claim.isObject($(sTargetElementID), "UserBadge.prototype.initialize(sTargetElementID, oPlaceHoldersDictionary, sBadgeTargetElementID) - sBadgeTargetElementID must be a string describing an HTML element ID");
		Claim.isString(sNoDataFoundHTML	, "ProfileMgr.UserBadge(...,sNoDataFoundHTML,...) - must be a string, expected HTML for no-data-found case")
		Claim.isString(sNotAvailableHTML, "ProfileMgr.UserBadge(...,sNotAvailableHTML) - must be a string, expected HTML for no-data-available case")
		
		this.log = new Log4Js.Logger("ProfileMgr.UserBadge");
		
		this.oTargetElement = $(sTargetElementID);		
		this.noDataFound	= sNoDataFoundHTML;
		this.notAvailable	= sNotAvailableHTML;
		this.medalsStringTemplate = sBadgeTemplateString;
		this.medalsPlaceHoldersDictionary = oPlaceHoldersDictionary;
		
	}
	ProfileMgr.UserBadge.prototype.apply = function()
	{
		this.GetHighestMedal( this.onSuccess
							, this.onFailure
							, this.onTimeout);
	}
	ProfileMgr.UserBadge.prototype.onSuccess = function(data)
	{
		this.log.info("ProfileMgr.serBadge.onSuccess - got data: " + Object.serialize(data));

		var game = TC.SKUs[data.gameSku];
		if(!game){
			this.log.error("Game sku " + data.gameSku + " is not found.");
			Element.setText (this.oTargetElement, this.notAvailable);
			return;
		}
		this.log.info("getUserWornBadge - user badge game: " + Object.serialize(game));
		this.log.info("data.userRanking:"+ data.userRanking + ", TC.UserMedals.getMedalByRanking(data.userRanking):" + Object.serialize(TC.UserMedals.getMedalByRanking(data.userRanking)));
		
		data.badgeGameSku	=	game.sku;
		data.badgeGameName	=	game.name;
		data.badgeGameUrl   =   game.gamePageURL;
		data.badgeImgUrl	=	TC.UserMedals.getMedalByRanking(data.userRanking).img;

		this.dispatch("onDataBind", data);
	
		var sHTML = ProfileMgr.populateStringTemplate( this.medalsStringTemplate
													 , this.medalsPlaceHoldersDictionary
													 , data
													 );
		Element.setText (this.oTargetElement,	sHTML);
	}
	ProfileMgr.UserBadge.prototype.onFailure = function(response)
	{ 
		this.log.error("FAILURE on ProfileMgr.UserBadge");

		if( response.Status == 'PERSONAL_DETAILS_NOT_FOUND')
		{
			this.log.warn("PERSONAL_DETAILS_NOT_FOUND on UserBadge");
			Element.setText (this.oTargetElement,	this.noDataFound);
		}
		else
		{
			this.log.warn("Error on UserBadge: " + response.Status);
			Element.setText (this.oTargetElement,	this.notAvailable);
		}
	}
	ProfileMgr.UserBadge.prototype.onTimeout = function(request)
	{ 
		this.log.error("TIMEOUT on ProfileMgr.UserBadge");
		Element.setText (this.oTargetElement,	this.notAvailable);
	}
//--/ProfileMgr.UserBadge------------------------------------------------------------
/**
 * ProfileMgr.getInstalledGames
 * 
 * @param {Number|String(optional)} The channel-code or comma-delimited-string of channel-codes
 * When not provided - the channel that is initiated into ProfileMgr is used.
 */
ProfileMgr.getInstalledGames = function(channel)
{
	channel = channel || this.settings.channel;
	return getGamesListByChannel(channel)
}
//----------------------------------------------------------------------------------

/**
 * Centralizes the applicativity of the user's Avatar
 */
ProfileMgr.Avatar = { }
/**
 * id of the HTML id
 */
ProfileMgr.Avatar.id = "mainFAV"
/**
 * supported emotions array
 */
ProfileMgr.Avatar.Emotions = [ "Happy"
							 , "Sad"
							 ];
ProfileMgr.Avatar.Emotions.Happy = 0
ProfileMgr.Avatar.Emotions.Sad = 1


ProfileMgr.Avatar = {};


/**
 * initiates the ProfileMgr.Avatar object
 * 
 * @param {string(optional)} sAvatarID
 * - optional: when not provided, the default in ProfileMgr.Avatar.id is used.
 */
ProfileMgr.Avatar.init = function(sAvatarID)
{
	if(!this.log) this.log = new Log4Js.Logger("ProfileMgr.Avatar");

	if(sAvatarID) this.id = sAvatarID;
	if(!this.id) 
	{
		this.log.warn("avatar object ID is not set");
		return;
	}
	if( navigator.appName.indexOf("Netscape") != -1 )
	{
		this.log.info("netscape detected.");
		this.objFAV = document.embeds[this.id];
		return;
	}
	else
	{
		this.objFAV = $(this.id);
	}
}
ProfileMgr.addOnLoadHandler( 
	function()
	{ 
		ProfileMgr.Avatar.init(); 
	}
);
/**
 * Sets emotion to the active avater
 *
 * @param {string} sEmotion
 * The emotion be to set
 */
ProfileMgr.Avatar.be = function(sEmotion)
{
	Claim.isString(sEmotion, "ProfileMgr.Avatar.be(sEmotion) - sEmotion must be a string, expectedly - describing a supported emotion");
	if(null == this.Emotions[sEmotion])
	{
		this.log.warn("ProfileMgr.Avatar.be(sEmotion) - invalid emotion is used: " + sEmotion);
		return;
	}
	this.objFAV.SetVariable("val", sEmotion);
	this.objFAV.SetVariable("command","changeEmotion");
}
/**
 * sets the zoom of the avatar
 * 
 * @param {string} sZoom
 * The zoom to be set
 */
ProfileMgr.Avatar.zoom = function(sZoom)
{
	Claim.isString(zoom, "ProfileMgr.Avatar.be(zoom) - zoom must be a string, expectedly - describing a supported zoom rate");

	this.objFAV.SetVariable("val", sZoom);
	this.objFAV.SetVariable("command","setZoom");
}
/**
 * Sets the avatar emotion to Happy
 */
ProfileMgr.Avatar.beHappy = function()
{
	this.be("Happy");
}
/**
 * Sets the avatar emotion to Sad
 */
ProfileMgr.Avatar.beSad = function()
{
	this.be("Sad");
}



//--------------------------------------------------------
// SEATLE PORTLET AVATAR ON CLICK API
avatarOnClick = function(){}