#Include "MathLib" as MathLib
#Include "TextLib" as TextLib
#RequireContext CMlScript 

//file: Debug.Script.txtm

declare Boolean G_Debug_Debug;

Void Debug_Log(Text Message)
{
	if(G_Debug_Debug)
	{
		log(Message);
	}
}

Void Debug_Log(Text[] Message)
{
	if(G_Debug_Debug)
	{
		log(Message);
	}
}
	
Void Debug_Log(Text[Text] Message)
{
	if(G_Debug_Debug)
	{
		log(Message);
	}
}

Void Debug_LogLine()
{
	Debug_Log("---------------------------------------------");
}

Void Debug_Init(Boolean _DebugMode)
{
	G_Debug_Debug = _DebugMode;
    if(G_Debug_Debug)
    {
        Debug_LogLine();
    }
}

//file: Manipulation.Script.txtm

Void Manipulation_DisableLinks() {
	Page.LinksInhibited = True;
}

Void Manipulation_EnableLinks() {
	Page.LinksInhibited = False;
}

Void Manipulation_Hide(CMlControl _Control) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _Hide: Control is Null");
		return;
	}
	_Control.Hide();
}

Void Manipulation_Hide(Text _ControlId) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _Hide: Control "^_ControlId^" does not exists");
		return;
	}
	Manipulation_Hide(Control);
}

Void Manipulation_Hide(Text[] _ControlIds) {
	foreach(ControlId in _ControlIds) {
		Manipulation_Hide(ControlId);
	}
}

Void Manipulation_Show(CMlControl _Control) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _Show: Control is Null");
		return;
	}
	_Control.Show();
}

Void Manipulation_Show(Text _ControlId) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _Show: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_Show(Control);
}

Void Manipulation_Show(Text[] _ControlIds) {
	foreach(ControlId in _ControlIds) {
		Manipulation_Show(ControlId);
	}
}

Void Manipulation_Toggle(CMlControl _Control) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _Toggle: Control is Null");
		return;
	}
	if (_Control.Visible) {
		Manipulation_Hide(_Control);
	}
	else {
		Manipulation_Show(_Control);
	}
}

Void Manipulation_Toggle(Text _ControlId) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _Toggle: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_Toggle(Control);
}

Boolean Manipulation_GetVisibility(CMlControl _Control) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _GetVisibility: Control is Null");
		return False;
	}
	return _Control.Visible;
}

Boolean Manipulation_GetVisibility(Text _ControlId) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _GetVisibility: Control "^_ControlId^" does not exist");
		return False;
	}
	return Manipulation_GetVisibility(Control);
}

Void Manipulation_SetSizeY(CMlControl _Control, Real _PosY) {
	if (_Control == Null) {
		Debug_Log("[Manipulation] _SetSizeYY: Control is Null");
		return;
	}
	_Control.Size.Y = _PosY;
}

Void Manipulation_SetSizeY(Text _ControlId, Real _PosY) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetSizeYY: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_SetSizeY(Control, _PosY);
}


Void Manipulation_SetPosX(CMlControl _Control, Real _PosX) {
	if (_Control == Null) {
		Debug_Log("[Manipulation] _SetPosX: Control is Null");
		return;
	}
	_Control.RelativePosition.X = _PosX;
}

Void Manipulation_SetPosX(Text _ControlId, Real _PosX) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetPosX: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_SetPosX(Control, _PosX);
}

Void Manipulation_SetPosY(CMlControl _Control, Real _PosY) {
	if (_Control == Null) {
		Debug_Log("[Manipulation] _SetPosY: Control is Null");
		return;
	}
	_Control.RelativePosition.Y = _PosY;
}

Void Manipulation_SetPosY(Text _ControlId, Real _PosY) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetPosY: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_SetPosY(Control, _PosY);
}

Void Manipulation_SetPosZ(CMlControl _Control, Real _PosZ) {
	if (_Control == Null) {
		Debug_Log("[Manipulation] _SetPosZ: Control is Null");
		return;
	}
	_Control.RelativePosition.Z = _PosZ;
}

Void Manipulation_SetPosZ(Text _ControlId, Real _PosZ) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetPosZ: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_SetPosZ(Control, _PosZ);
}

Void Manipulation_SetScale(CMlControl _Control, Real _Scale) {
	if (_Control == Null) {
		Debug_Log("[Manipulation] _SetScale: Control is Null");
		return;
	}
	_Control.RelativeScale = _Scale;
}

Void Manipulation_SetScale(Text _ControlId, Real _Scale) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetScale: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_SetScale(Control, _Scale);
}

Real Manipulation_GetScale(CMlControl _Control) {
	if (_Control == Null) {
		Debug_Log("[Manipulation] _SetScale: Control is Null");
		return 0.;
	}
	return _Control.RelativeScale;
}

Real Manipulation_GetScale(Text _ControlId) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetScale: Control "^_ControlId^" does not exist");
		return 0.;
	}
	return Manipulation_GetScale(Control);
}

Void Manipulation_SetOpacity(CMlControl _Control, Real _Opacity) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _SetOpacity: Control is Null");
		return;
	}
	switchtype(_Control) {
		case CMlLabel: (_Control as CMlLabel).Opacity = _Opacity;
		case CMlQuad: (_Control as CMlQuad).Opacity = _Opacity;
		default: Debug_Log("[Manipulation] _SetOpacity: Control "^_Control.Id^" has no Opacity property");
	}
}

Void Manipulation_SetOpacity(Text _ControlId, Real _Opacity) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetOpacity: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_SetOpacity(Control, _Opacity);
}

Text Manipulation_GetEntryValue(CMlControl _Control) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _GetEntryValue: Control is Null");
		return "";
	}
	switchtype(_Control) {
		case CMlEntry:
			return (_Control as CMlEntry).Value;
		case CMlFileEntry:
			return (_Control as CMlFileEntry).Value;
		default: Debug_Log("[Manipulation] _GetEntryValue: Control "^_Control.Id^" is not an entry");
	}
	return "";
}

Text Manipulation_GetEntryValue(Text _ControlId) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _GetEntryValue: Control "^_ControlId^" does not exist");
		return "";
	}
	return Manipulation_GetEntryValue(Control);
}

Void Manipulation_SetEntryValue(CMlControl _Control, Text _Value) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _SetEntryValue: Control is Null");
		return;
	}
	switchtype(_Control) {
		case CMlEntry:
			(_Control as CMlEntry).Value = _Value;
		case CMlFileEntry:
			(_Control as CMlFileEntry).Value = _Value;
		default: Debug_Log("[Manipulation] _SetEntryValue: Control "^_Control.Id^" is not an entry");
	}
}

Void Manipulation_SetEntryValue(Text _ControlId, Text _Value) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if(Control == Null) {
		Debug_Log("[Manipulation] _SetEntryValue: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_SetEntryValue(Control, _Value);
}

Void Manipulation_Focus(CMlControl _Control) {
	if(_Control == Null) {
		Debug_Log("[Manipulation] _Focus: Control is Null");
		return;
	}
	_Control.Focus();
}

Void Manipulation_Focus(Text _ControlId) {
	declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _Focus: Control "^_ControlId^" does not exist");
		return;
	}
	Manipulation_Focus(Control);
}

Void Manipulation_SetStyle(CMlQuad _Quad, Text _Style) {
	if(_Quad == Null) {
		Debug_Log("[[Manipulation] _SetStyle: Quad is Null");
		return;
	}
	_Quad.Style = _Style;
}

Void Manipulation_SetStyle(CMlLabel _Label, Text _Style) {
	if(_Label == Null) {
		Debug_Log("[[Manipulation] _SetStyle: Label is Null");
		return;
	}
	_Label.Style = _Style;
}

Void Manipulation_SetStyle(Text _QuadId, Text _Style) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetStyle: Control "^_QuadId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlQuad: Manipulation_SetStyle((Control as CMlQuad), _Style);
		case CMlLabel: Manipulation_SetStyle((Control as CMlLabel), _Style);
		default:
			Debug_Log("[Manipulation] _SetStyle: Control "^_QuadId^" is not a Quad nor a Label");
	}
}

Void Manipulation_SetSubstyle (CMlQuad _Quad, Text _Substyle) {
	if(_Quad == Null) {
		Debug_Log("[Manipulation] _SetSubStyle: _Quad is Null");
		return;
	}
	_Quad.Substyle = _Substyle;
}

Void Manipulation_SetSubstyle(Text _QuadId, Text _Style) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetSubstyle: Control "^_QuadId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlQuad: Manipulation_SetSubstyle((Control as CMlQuad), _Style);
		default:
			Debug_Log("[Manipulation] _SetSubStyle: Control "^_QuadId^" is not a Quad");
	}
}

Void Manipulation_SetSubstyle(Text[] _QuadIds, Text _SubStyle) {
	foreach(QuadId in _QuadIds) {
		Manipulation_SetSubstyle(QuadId, _SubStyle);
	}
}


Void Manipulation_SetImageURL(CMlQuad _Quad, Text _Image) {
	if(_Quad == Null) {
		Debug_Log("[Manipulation] _SetImageURL: Control is Null");
		return;
	}
	_Quad.ImageUrl = _Image;
}

Void Manipulation_SetImageURL(Text _QuadId, Text _Image) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetImageURL: Control "^_QuadId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlQuad: {
			Manipulation_SetImageURL((Control as CMlQuad), _Image);
		}
		default:
			Debug_Log("[Manipulation] _SetImageURL: Control "^_QuadId^" is not a Quad");
	}
}

Text Manipulation_GetImageURL(CMlQuad _Quad) {
	if(_Quad == Null) {
		Debug_Log("[Manipulation] _GetImageURL: Control is Null");
		return "";
	}
	return _Quad.ImageUrl;
}

Text Manipulation_GetImageURL(Text _QuadId) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _GetImageURL: Control "^_QuadId^" does not exist");
	}
	switchtype(Control) {
		case CMlQuad: {
			return Manipulation_GetImageURL((Control as CMlQuad));
		}
		default:
			Debug_Log("[Manipulation] _SetImageURL: Control "^_QuadId^" is not a Quad");
	}
	return "";
}

Void Manipulation_SetImageURLFocus(CMlQuad _Quad, Text _ImageFocus) {
	if(_Quad == Null) {
		Debug_Log("[Manipulation] _SetImageURLFocus: Control is Null");
		return;
	}
	_Quad.ImageUrlFocus = _ImageFocus;
}

Void Manipulation_SetImageURLFocus(Text _QuadId, Text _ImageFocus) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetImageURLFocus: Control "^_QuadId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlQuad: Manipulation_SetImageURLFocus((Control as CMlQuad), _ImageFocus);
		default:
			Debug_Log("[Manipulation] _SetImageURLFocus: Control "^_QuadId^" is not a Quad");
	}
}

Void Manipulation_SetModulateColor(CMlQuad _Quad, Vec3 _ModulateColor) {
	if(_Quad == Null) {
		Debug_Log("[Manipulation] _SetModulateColor: Control is Null");
		return;
	}
	_Quad.ModulateColor = _ModulateColor;
}

Void Manipulation_SetModulateColor(CMlQuad _Quad, Text _ModulateColor) {
	Manipulation_SetModulateColor(_Quad, TextLib::ToColor(_ModulateColor));
}

Void Manipulation_SetModulateColor(Text _QuadId, Text _ModulateColor) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetModulateColor: Control "^_QuadId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlQuad: Manipulation_SetModulateColor((Control as CMlQuad), _ModulateColor);
		default:
			Debug_Log("[Manipulation] _SetModulateColor: Control "^_QuadId^" is not a Quad");
	}
}

Void Manipulation_SetModulateColor(Text _QuadId, Vec3 _ModulateColor) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetModulateColor: Control "^_QuadId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlQuad: Manipulation_SetModulateColor((Control as CMlQuad), _ModulateColor);
		default:
			Debug_Log("[Manipulation] _SetModulateColor: Control "^_QuadId^" is not a Quad");
	}
}

Void Manipulation_FitToText(CMlLabel _Label) {
	if (_Label == Null) {
		Debug_Log("[Manipulation] _FitToText: Label is Null");
		return;
	}
	_Label.Size.X = _Label.ComputeWidth(_Label.Value);
}

Void Manipulation_FitToText(Text _LabelId) {
	declare CMlControl Control <=> Page.GetFirstChild(_LabelId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _FitToText: Control "^_LabelId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlLabel: Manipulation_FitToText((Control as CMlLabel));
		default:
			Debug_Log("[Manipulation] _FitToText: Control "^_LabelId^" is not a Label");
	}
}

Void Manipulation_FitToText() {
	Page.GetClassChildren("FitToText", Page.MainFrame, True);
	foreach(Control in Page.GetClassChildren_Result) {
		switchtype(Control) {
			case CMlLabel: Manipulation_FitToText((Control as CMlLabel));
		}
	}
}

Text Manipulation_GetText(CMlLabel _Label) {
	if (_Label == Null) {
		Debug_Log("[Manipulation] _GetText: Label is Null");
		return "";
	}
	return _Label.Value;
}

Text Manipulation_GetText(Text _LabelId) {
	declare CMlControl Control <=> Page.GetFirstChild(_LabelId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _GetText: Control "^_LabelId^" does not exists");
		return "";
	}
	switchtype(Control) {
		case CMlLabel: return Manipulation_GetText((Control as CMlLabel));
		default:
			Debug_Log("[Manipulation] _GetText: Control "^_LabelId^" is not a Label");
	}
	return "";
}

Void Manipulation_SetText(CMlLabel _Label, Text _Text) {
	if (_Label == Null) {
		Debug_Log("[Manipulation] _SetText: Label is Null");
		return;
	}
	_Label.SetText(_Text);
}

Void Manipulation_SetText(Text _LabelId, Text _Text) {
	declare CMlControl Control <=> Page.GetFirstChild(_LabelId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetText: Control "^_LabelId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlLabel: Manipulation_SetText((Control as CMlLabel), _Text);
		default:
			Debug_Log("[Manipulation] _SetText: Control "^_LabelId^" is not a Label");
	}
}

Void Manipulation_SetTextColor(CMlLabel _Label, Vec3 _TextColor) {
	if (_Label == Null) {
		Debug_Log("[Manipulation] _SetTextColor: Label is Null");
		return;
	}
	_Label.TextColor = _TextColor;
}

Void Manipulation_SetTextColor (Text _LabelId, Vec3 _TextColor) {
	declare CMlControl Control <=> Page.GetFirstChild(_LabelId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetTextColor: Control "^_LabelId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlLabel: Manipulation_SetTextColor((Control as CMlLabel), _TextColor);
		default:
			Debug_Log("[Manipulation] _SetTextColor: Control "^_LabelId^" is not a Label");
	}
}

Void Manipulation_SetTextColor (CMlLabel _Label, Text _TextColor) {
	Manipulation_SetTextColor(_Label, TextLib::ToColor(_TextColor));
}

Void Manipulation_SetTextColor (Text _LabelId, Text _TextColor) {
	declare CMlControl Control <=> Page.GetFirstChild(_LabelId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetTextColor: Control "^_LabelId^" does not exist");
		return;
	}
	switchtype(Control) {
		case CMlLabel: Manipulation_SetTextColor((Control as CMlLabel), _TextColor);
		default:
			Debug_Log("[Manipulation] _SetTextColor: Control "^_LabelId^" is not a Label");
	}
}
Void Manipulation_SetTextColor(Text[] _LabelIds, Text _TextColor) {
	foreach(LabelId in _LabelIds) {
		Manipulation_SetTextColor(LabelId, _TextColor);
	}
}

Void Manipulation_SetTextSize(CMlLabel _Label, Integer _Size) {
	if (_Label == Null) {
		Debug_Log("[Manipulation] _SetTextSize: Label is Null");
		return;
	}
	_Label.TextSize = _Size;
}

Void Manipulation_SetTextSize(Text _LabelId, Integer _Size) {
	declare CMlControl Control <=> Page.GetFirstChild(_LabelId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetTextSize: Control "^_LabelId^" does not exists");
		return;
	}
	switchtype(Control) {
		case CMlLabel: Manipulation_SetTextSize((Control as CMlLabel), _Size);
		default:
			Debug_Log("[Manipulation] _SetTextSize: Control "^_LabelId^" is not a Label");
	}
}

Void Manipulation_SetStyleSelected(CMlQuad _Quad, Boolean _Selected) {
	if (_Quad == Null) {
		Debug_Log("[Manipulation] _SetStyleSelected: Label is Null");
		return;
	}
	_Quad.StyleSelected = _Selected;
}

Void Manipulation_SetStyleSelected(Text _QuadId, Boolean _Selected) {
	declare CMlControl Control <=> Page.GetFirstChild(_QuadId);
	if (Control == Null) {
		Debug_Log("[Manipulation] _SetStyleSelected: Control "^_QuadId^" does not exists");
		return;
	}
	switchtype(Control) {
		case CMlQuad: Manipulation_SetStyleSelected((Control as CMlQuad), _Selected);
		default:
			Debug_Log("[Manipulation] _SetStyleSelected: Control "^_QuadId^" is not a Label");
	}
}


//file: Event.Script.txtm

declare Text[][][] G_Event_PendingEvents;
declare Text[][][] G_Event_UpcomingEvents;

Void Event_Init() {

}

Void Event_DispatchCustomEvent(Text _Type, Text[] _Data) {
    if(_Data.count > 0)
    {
        Debug_Log("[Event] "^_Type^" "^_Data[0]);
    }
    else
    {
        Debug_Log("[Event] "^_Type);
    }
	G_Event_UpcomingEvents.add([[_Type], _Data]);
}

Void Event_DispatchCustomEvent(Text _Type) {
	Event_DispatchCustomEvent(_Type, Text[]);
}

Text[][][] Event_PendingEvents() {
	return G_Event_PendingEvents;
}

Void Event_Yield() {
    G_Event_PendingEvents = G_Event_UpcomingEvents;
	if(G_Event_UpcomingEvents.count > 0) {
		G_Event_UpcomingEvents = Text[][][];
	}
}

//file: Section.Script.txtm

declare Text[] G_Section_FrameIds;
declare Text[][Text] G_Section_SubSectionsFrameIds;
declare Text G_Section_Current;
declare Text G_Section_SubCurrent;

Void Section_Init(Text[] _FrameIds) {
	G_Section_FrameIds = _FrameIds;
}

Void Private_Section_Show(Text _FrameId) {
	Manipulation_Show(_FrameId);
}

Void Private_Section_Hide(Text _FrameId) {
	Manipulation_Hide(_FrameId);
}

Void Section_AddSub(Text _FrameId, Text _SubFrameId) {
	if (G_Section_SubSectionsFrameIds.existskey(_FrameId)) {
		G_Section_SubSectionsFrameIds[_FrameId].add(_SubFrameId);
	}
	else {
		G_Section_SubSectionsFrameIds[_FrameId] = [_SubFrameId];
	}
}

Text Section_GetCurrent() {
	return G_Section_Current;
}

Text Section_GetSubCurrent() {
	return G_Section_SubCurrent;
}

Void Section_Change(Text _FrameId, Boolean _Force) {
	if (G_Section_Current != _FrameId || _Force == True) {
		G_Section_Current = _FrameId;
		Event_DispatchCustomEvent("Section.Changed", [_FrameId]);
		foreach(FrameId in G_Section_FrameIds) {
			if (FrameId == _FrameId) {
				Private_Section_Show(FrameId);
			}
			else {
				Private_Section_Hide(FrameId);
			}
		}
	}
}

Void Section_Reset() {
	foreach(Section in G_Section_FrameIds) {
		Private_Section_Hide(Section);
	}
}

Void Section_Change(Text _FrameId) {
	Section_Change(_FrameId, False);
}

Void Section_SubChange(Text _FrameId, Text _SubFrameId, Boolean _Force) {
	Section_Change(_FrameId, _Force);
	if (G_Section_SubCurrent != _SubFrameId || _Force == True) {
		G_Section_SubCurrent = _SubFrameId;
		if (G_Section_SubSectionsFrameIds.existskey(_FrameId)) {
			Event_DispatchCustomEvent("Section.SubChanged", [_SubFrameId]);
			foreach(SubFrameId in G_Section_SubSectionsFrameIds[_FrameId]) {
				if (SubFrameId == _SubFrameId) {
					Private_Section_Show(SubFrameId);
				}
				else {
					Private_Section_Hide(SubFrameId);
				}
			}
		}
		else {
			Debug_Log("[Section] Cannot find SubSection "^_SubFrameId^" in section "^_FrameId);
		}
	}
}
Void Section_SubChange(Text _FrameId, Text _SubFrameId) {
	Section_SubChange(_FrameId, _SubFrameId, False);
}

//file: URL.Script.txtm

Text Private_URL_BuildQueryString(Text[Text] _Query) {
	declare Text result = "";
	foreach (Key => Value in _Query) {
		result ^= TextLib::URLEncode(Key)^"="^TextLib::URLEncode(Value)^"&";
	}
	return result;
}

Text URL_BuildURL(Text _Url, Text[Text] _Query) {
	return _Url^"?"^Private_URL_BuildQueryString(_Query);
}



//file: HTTP.Script.txtm

declare Ident[Ident] G_HTTP_Requests;
declare Text[Ident] G_HTTP_RequestsResultsSuccess;
declare Text[Ident] G_HTTP_RequestsResultsError;

Ident HTTP_PostAsync(Text _Url, Text _Data) {
	declare Request <=> Http.CreatePost(_Url, _Data);
	if (Request != Null) {
		G_HTTP_Requests[Request.Id] = Request.Id;
	}
	Debug_Log("[HTTP] PostAsync: "^_Url);
	return Request.Id;
}

Ident HTTP_GetAsync(Text _Url) {
	declare Request <=> Http.CreateGet(_Url, False);
	if (Request != Null) {
		G_HTTP_Requests[Request.Id] = Request.Id;
	}
	Debug_Log("[HTTP] GetAsync ("^Request.Id^"): "^_Url);
	return Request.Id;
}

Void Private_HTTP_HandleFinishedRequests() {
	declare Ident[] ToRemove;
	foreach (Request in Http.Requests) {
		if (!G_HTTP_Requests.existskey(Request.Id)) continue; //Ignore other requests
		if (Request.IsCompleted) { 
			if (Request.StatusCode == 200) {
				G_HTTP_RequestsResultsSuccess[Request.Id] = Request.Result;
				//Debug_Log("[HTTP] OK "^Request.Id);
				Event_DispatchCustomEvent("HTTP.Success", [""^Request.Id, Request.Result]);
			}
			else {
				G_HTTP_RequestsResultsError[Request.Id] = Request.Result;
                Event_DispatchCustomEvent("HTTP.Error", [""^Request.Id, Request.Result]);
                Debug_Log("[HTTP] "^Request.Id^": "^Request.StatusCode);
			}
			ToRemove.add(Request.Id);
		}
	}
	foreach (RequestId in ToRemove) {
		G_HTTP_Requests.removekey(RequestId);
		Http.Destroy(Http.Requests[RequestId]);
	}
}

Void Private_HTTP_EmptyResultsSuccess() {
	foreach(RequestId => Request in G_HTTP_RequestsResultsSuccess) {
		G_HTTP_RequestsResultsSuccess.removekey(RequestId);
	}
}

Void Private_HTTP_EmptyResultsError() {
	foreach(RequestId => Request in G_HTTP_RequestsResultsError) {
		G_HTTP_RequestsResultsError.removekey(RequestId);
	}
}

Text[Ident] HTTP_PendingResponses() {
	declare Text[Ident] result;
	Private_HTTP_HandleFinishedRequests();
	result = G_HTTP_RequestsResultsSuccess;
	Private_HTTP_EmptyResultsSuccess();
	return result;
}

Text[Ident] HTTP_PendingErrors() {
	declare Text[Ident] result;
	Private_HTTP_HandleFinishedRequests();
	result = G_HTTP_RequestsResultsError;
	Private_HTTP_EmptyResultsError();
	return result;
}

Void HTTP_Loop() {
    Private_HTTP_HandleFinishedRequests();
}

//file: XML.Script.txtm

Text XML_NodeToText(CXmlNode _Node)
{
	if(_Node == Null)
	{
		Debug_Log("[XML] Undefined node");
		return "";
	}
	return _Node.TextContents;
}

Text[Text] XML_NodeToArray(CXmlNode _Node)
{
	declare Text[Text] result;
    if(_Node == Null)
	{
		Debug_Log("[XML] Undefined node");
		return result;
	}
    foreach(child in _Node.Children)
    {
        result[child.Name] = XML_NodeToText(child);
    }
	return result;
}

Text[Text][] XML_NodeToArray2(CXmlNode _Node)
{
	declare Text[Text][] result;
    if(_Node == Null)
	{
		Debug_Log("[XML] Undefined node");
		return result;
	}
    foreach(child in _Node.Children)
    {
        result.add(XML_NodeToArray(child));
    }
	return result;
}

Text[Text][][] XML_NodeToArray3(CXmlNode _Node)
{
	declare Text[Text][][] result;
    if(_Node == Null)
	{
		Debug_Log("[XML] Undefined node");
		return result;
	}
    foreach(child in _Node.Children)
    {
        result.add(XML_NodeToArray2(child));
    }
	return result;
}

CXmlDocument XML_TextToXMLDocument(Text _XmlText) {
	declare CXmlDocument Xml <=> Xml.Create(_XmlText);
	if(Xml == Null)
	{
		Debug_Log("[XML] Couldn't create CXmlDocument");
	}
	return Xml;
}

//file: ApplicationData.Script.txtm

declare Text G_ApplicationData_BaseUrl;
declare Text G_ApplicationData_Login;
declare Text G_ApplicationData_SID;
declare Text[] G_ApplicationData_RequestIds;
declare Integer G_ApplicationData_InitDate;
declare Boolean G_ApplicationData_SessionExpired;

Text ApplicationData_BuildUrl(Text _Route, Text[Text] _Query) {
	declare Text[Text] Query = _Query;
	if(G_ApplicationData_Login != "")
    {
        Query["login"] = G_ApplicationData_Login;
    }
	if (G_ApplicationData_SID != "") {
		declare Text[] SID = TextLib::Split("=", G_ApplicationData_SID);
		if (SID.count == 2) {
			Query[SID[0]] = SID[1];
		}
	}
	return URL_BuildURL(G_ApplicationData_BaseUrl^_Route, Query);
}

Boolean ApplicationData_HasError(Text _XmlText) {
	declare Text ValidTextStart = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response";
	if (TextLib::SubText(_XmlText, 0, TextLib::Length(ValidTextStart)) != ValidTextStart) {
		Debug_Log("[ApplicationData] Error: "^_XmlText);
		return True;
	}
	return False;
}

Text[Text] ApplicationData_ParseError(Text _XmlErrorText) {
	declare Text[Text] Result;
	declare XMLDocument = XML_TextToXMLDocument(_XmlErrorText);
	if (XMLDocument != Null) {
		Result = XML_NodeToArray(XMLDocument.GetFirstChild("error"));
	}
	Xml.Destroy(XMLDocument);
	return Result;
}

Void Private_ApplicationData_CustomEventListener(Text[][] _Event) {
	switch(_Event[0][0]) {
		case "HTTP.Success": {
			if(G_ApplicationData_RequestIds.exists(_Event[1][0])) {
				if(ApplicationData_HasError(_Event[1][1])) {
					Event_DispatchCustomEvent("ApplicationData.Error", [_Event[1][0], _Event[1][1]]);
					return;
				}
				Event_DispatchCustomEvent("ApplicationData.Success", [_Event[1][0], _Event[1][1]]);
			}
		}
		case "HTTP.Error": {
			if(G_ApplicationData_RequestIds.exists(_Event[1][0])) {
				Event_DispatchCustomEvent("ApplicationData.Error", [_Event[1][0], _Event[1][1]]);
			}
		}
	}
}

Void ApplicationData_Init(Text _BaseUrl, Text _Login, Text _SID) {
	G_ApplicationData_SessionExpired = False;
	G_ApplicationData_InitDate = Now;
	G_ApplicationData_SID = _SID;
	G_ApplicationData_BaseUrl = _BaseUrl;
	G_ApplicationData_Login = _Login;
}

Void ApplicationData_Init(Text _BaseUrl, Text _Login) {
	ApplicationData_Init(_BaseUrl, _Login, "");
}

Void ApplicationData_Register(Ident _RequestId) {
    if(!G_ApplicationData_RequestIds.exists(""^_RequestId)) {
        Debug_Log("[ApplicationData] Registered "^_RequestId);
        G_ApplicationData_RequestIds.add(""^_RequestId);
    }
}

Void ApplicationData_Loop() {
	if (Now - G_ApplicationData_InitDate > 3600000 && !G_ApplicationData_SessionExpired) {
		Event_DispatchCustomEvent("ApplicationData.SessionExpired");
		G_ApplicationData_SessionExpired = True;
	}
    foreach(Event in Event_PendingEvents()) {
        Private_ApplicationData_CustomEventListener(Event);
    }
}


//file: Identifier.Script.txtm

declare Integer[Text] G_Identifier_Ids;

Integer Identifier_GenerateId(Text _LibName) {
	if (!G_Identifier_Ids.existskey(_LibName)) {
		G_Identifier_Ids[_LibName] = 1;
	}
	else {
		G_Identifier_Ids[_LibName] += 1;
	}
	return G_Identifier_Ids[_LibName];
}

Integer Identifier_GenerateId() {
    return Identifier_GenerateId("Default");
}

//file: Pager.Script.txtm

declare Text G_Pager_ButtonsStyle;
declare Text G_Pager_DisabledButtonSubstyle;
declare Text G_Pager_NextPageButtonSubstyle;
declare Text G_Pager_PreviousPageButtonSubstyle;

declare Integer[Integer] G_Pager_CurrentPage;
declare Integer[Integer] G_Pager_ItemsPerPage;
declare Integer[] G_Pager_Ids;

declare Text[Integer] G_Pager_PreviousPageButtonId;
declare Text[Integer] G_Pager_NextPageButtonId;
declare Text[Integer] G_Pager_PageNumberLabelId;

declare Boolean[Integer] G_Pager_LoadData;
declare Boolean[Integer] G_Pager_HasPreviousPage;
declare Boolean[Integer] G_Pager_HasNextPage;
declare Boolean[Integer] G_Pager_Enabled;

Void Private_Pager_DisplayPageNumber(Integer _PagerId) {
	if (G_Pager_PageNumberLabelId[_PagerId] != "") {
		Manipulation_SetText(G_Pager_PageNumberLabelId[_PagerId], ""^G_Pager_CurrentPage[_PagerId]);
	}
}

Integer Pager_GetCurrentPage(Integer _PagerId) {
	if (G_Pager_CurrentPage.existskey(_PagerId)) {
		return G_Pager_CurrentPage[_PagerId];
	}
	Debug_Log("[Pager] _GetCurrentPage: Unknown pager ID:"^_PagerId);
	return 0;
}

Integer Pager_GetItemsPerPage(Integer _PagerId) {
	if (G_Pager_ItemsPerPage.existskey(_PagerId)) {
		return G_Pager_ItemsPerPage[_PagerId];
	}
	Debug_Log("[Pager] _GetItemsPerPage: Unknown pager ID:"^_PagerId);
	return 0;
}

Integer Pager_GetOffset(Integer _PagerId) {
	return (G_Pager_CurrentPage[_PagerId] - 1) * G_Pager_ItemsPerPage[_PagerId];
}

Integer Pager_GetLimit(Integer _PagerId) {
	return G_Pager_ItemsPerPage[_PagerId] + 1;
}

Integer Pager_Init(Integer _ItemsPerPage, Text _PreviousPageButtonId, Text _NextPageButtonId, Text _PageNumberLabelId) {
	declare Integer PagerId = Identifier_GenerateId("Pager");
	G_Pager_ButtonsStyle = "Icons64x64_1";
	G_Pager_DisabledButtonSubstyle = "ArrowDisabled";
	G_Pager_NextPageButtonSubstyle = "ArrowNext";
	G_Pager_PreviousPageButtonSubstyle = "ArrowPrev";

	Manipulation_SetStyle(_PreviousPageButtonId, G_Pager_ButtonsStyle);
	Manipulation_SetSubstyle(_PreviousPageButtonId, G_Pager_DisabledButtonSubstyle);
	Manipulation_SetStyle(_NextPageButtonId, G_Pager_ButtonsStyle);
	Manipulation_SetSubstyle(_NextPageButtonId, G_Pager_DisabledButtonSubstyle);

	G_Pager_CurrentPage[PagerId] = 1;
	G_Pager_ItemsPerPage[PagerId] = _ItemsPerPage;
	G_Pager_PreviousPageButtonId[PagerId] = _PreviousPageButtonId;
	G_Pager_NextPageButtonId[PagerId] = _NextPageButtonId;
	G_Pager_PageNumberLabelId[PagerId] = _PageNumberLabelId;
	G_Pager_LoadData[PagerId] = True;
	G_Pager_HasPreviousPage[PagerId] = False;
	G_Pager_HasNextPage[PagerId] = False;
	G_Pager_Enabled[PagerId] = True;
	G_Pager_Ids.add(PagerId);
	return PagerId;
}

Integer Pager_Init(Integer _ItemsPerPage, Text _PreviousPageButtonId, Text _NextPageButtonId) {
	return Pager_Init(_ItemsPerPage, _PreviousPageButtonId, _NextPageButtonId, "");
}

Integer Pager_Init(Integer _ItemsPerPage, Text _BaseId) {
	return Pager_Init(_ItemsPerPage, _BaseId^":arrowPrev", _BaseId^":arrowNext", _BaseId^":text");
}

Integer Pager_Init(Integer _ItemsPerPage) {
	return Pager_Init(_ItemsPerPage, "PageNavigator:arrowPrev", "PageNavigator:arrowNext", "PageNavigator:text");
}

Boolean Pager_Changed(Integer _PagerId) {
	declare ReturnedValue = G_Pager_LoadData[_PagerId];
	G_Pager_LoadData[_PagerId] = False;
	return ReturnedValue;
}

Void Pager_ForceChange(Integer _PagerId) {
	Debug_Log("[Pager] ForceChange:"^_PagerId);
	G_Pager_LoadData[_PagerId] = True;
}

Void Pager_ResetCurrentPage(Integer _PagerId) {
	G_Pager_CurrentPage[_PagerId] = 1;
	Pager_ForceChange(_PagerId);
}

Void Pager_Disable(Integer _PagerId) {
	G_Pager_Enabled[_PagerId] = False;
}

Void Pager_Enable(Integer _PagerId) {
	G_Pager_Enabled[_PagerId] = True;
}

Void Pager_PagerUpdate(Integer _PagerId, Integer _ListLength) {
	if (G_Pager_CurrentPage[_PagerId] != 1) {
		Manipulation_SetStyle(G_Pager_PreviousPageButtonId[_PagerId], G_Pager_ButtonsStyle);
		Manipulation_SetSubstyle(G_Pager_PreviousPageButtonId[_PagerId], G_Pager_PreviousPageButtonSubstyle);
		G_Pager_HasPreviousPage[_PagerId] = True;
	}
	else {
		Manipulation_SetStyle(G_Pager_PreviousPageButtonId[_PagerId], G_Pager_ButtonsStyle);
		Manipulation_SetSubstyle(G_Pager_PreviousPageButtonId[_PagerId], G_Pager_DisabledButtonSubstyle);
		G_Pager_HasPreviousPage[_PagerId] = False;
	}

	if (_ListLength > G_Pager_ItemsPerPage[_PagerId]) {
		Manipulation_SetStyle(G_Pager_NextPageButtonId[_PagerId], G_Pager_ButtonsStyle);
		Manipulation_SetSubstyle(G_Pager_NextPageButtonId[_PagerId], G_Pager_NextPageButtonSubstyle);
		G_Pager_HasNextPage[_PagerId] = True;
	}
	else {
		Manipulation_SetStyle(G_Pager_NextPageButtonId[_PagerId], G_Pager_ButtonsStyle);
		Manipulation_SetSubstyle(G_Pager_NextPageButtonId[_PagerId], G_Pager_DisabledButtonSubstyle);
		G_Pager_HasNextPage[_PagerId] = False;
	}
	Private_Pager_DisplayPageNumber(_PagerId);
}

Void Pager_EventListener(Integer _PagerId, CMlEvent _Event)
{
	if (G_Pager_Enabled[_PagerId] && _Event.Type == CMlEvent::Type::MouseClick) {
		if (_Event.ControlId == G_Pager_PreviousPageButtonId[_PagerId] && G_Pager_HasPreviousPage[_PagerId]) {
			if (G_Pager_CurrentPage[_PagerId] != 1) {
				G_Pager_CurrentPage[_PagerId] -= 1;
			}
			G_Pager_LoadData[_PagerId] = True;
		}
		else if(_Event.ControlId == G_Pager_NextPageButtonId[_PagerId] && G_Pager_HasNextPage[_PagerId]) {
			G_Pager_CurrentPage[_PagerId] += 1;
			G_Pager_LoadData[_PagerId] = True;
		}
	}
}

Void Pager_EventListener(CMlEvent _Event) {
    foreach (Event in PendingEvents) {
        foreach(PagerId in G_Pager_Ids) {
            Pager_EventListener(PagerId, Event);
        }
    }
}

Void Pager_Loop()
{
    foreach(Id in G_Pager_Ids)
    {
        if(Pager_Changed(Id))
        {
            Event_DispatchCustomEvent("Pager.Changed", [""^Id]);
        }
    }
   
    foreach (Event in PendingEvents) {
        foreach(PagerId in G_Pager_Ids) {
            Pager_EventListener(PagerId, Event);
        }
    }
}

//file: Zone.Script.txtm

declare Text[Integer] G_Zone_Continents;

Void Zone_Init() {
	G_Zone_Continents = [
		118636 => "Africa",
		118642 => "Asia",
		118644 => "Europe",
		118640 => "Middle East",
		118634 => "North America",
		118632 => "Oceania",
		118638 => "South America"
	];
}

Text Zone_GetName(Integer _ZoneId) {
	switch (_ZoneId) {
		case 1:
			return "World";
		default:
			return G_Zone_Continents[_ZoneId];
	}
	return "";
}

Integer Zone_GetId(Text _Name) {
	if (G_Zone_Continents.exists(_Name)) {
		return G_Zone_Continents.keyof(_Name);
	}
	return 1;
}

//file: Controls.Script.txtm

declare Text[Text] G_Controls_Selects;

Void Controls_Select_Init(Text _SelectId) {
	Manipulation_Hide(_SelectId^":options");
	G_Controls_Selects[_SelectId^":bg"] = _SelectId;
}

Void Controls_Select_Close(Text _SelectId) {
	Manipulation_Hide(_SelectId^":options");
}

Void Private_Controls_EventListener(CMlEvent _Event) {
	if (_Event.Type == CMlEvent::Type::MouseClick) {
		if (G_Controls_Selects.existskey(_Event.ControlId)) {
			Manipulation_Toggle(G_Controls_Selects[_Event.ControlId]^":options");
		}
	}
}

Void Controls_Loop() {
	foreach (Event in PendingEvents) {
		Private_Controls_EventListener(Event);
	}
}

//file: Player.Script.txtm

Integer Player_GetEchelon(Real _LadderPoints) {
	declare Integer Echelon = MathLib::FloorInteger(_LadderPoints/10000);
	if (Echelon == 10) {
		return 9;
	}
	return Echelon;
}

Integer Player_GetEchelon(Text _LadderPoints) {
	return Player_GetEchelon(TextLib::ToReal(_LadderPoints));
}

//file: Tooltip.Script.txtm

declare Text[Text][Integer] G_Tooltip_Tooltips;
declare Text[Integer][Text] G_Tooltip_OnMouseOver;
declare Integer G_Tooltip_DefaultTooltip;

Void Private_Tooltip_Position(CMlControl _Control, CMlFrame _Frame, CMlQuad _BoundingBox) {
    // Display it on top
    declare ZDelta = <0., 0., 25.>;

    // Place the "bottom left" corner of the tooltip at the specified coords
    declare BBoxDelta = <_BoundingBox.Size.X/2.,  _BoundingBox.Size.Y/2., 0.>;
    BBoxDelta -= _BoundingBox.RelativePosition;
    
    // Place the "bottom left" corner of the tooltip at the "top left" corner of the control
    declare Vec3 ControlDelta = <0., 0., 0.>;
    if (_Control.HorizontalAlign == CMlControl::AlignHorizontal::HCenter) {
        ControlDelta.X = - _Control.Size.X / 2.;
    } 
    else if (_Control.HorizontalAlign == CMlControl::AlignHorizontal::Right) {
        ControlDelta.X = - _Control.Size.X;
    }
    if (_Control.VerticalAlign == CMlControl::AlignVertical::Bottom) {
        ControlDelta.Y = _Control.Size.Y;
    } else if (_Control.VerticalAlign == CMlControl::AlignVertical::VCenter || _Control.VerticalAlign == CMlControl::AlignVertical::VCenter2) {
        ControlDelta.Y = _Control.Size.Y / 2;
    }

    // Spacer
    declare Spacer = <0., 2., 0.>;

    // Out of screen?
    declare Vec3 Position = _Control.AbsolutePosition;
    declare PosMin = <-160., -90., 0.>;
    declare PosMax = <160., 90., 0.>;
    PosMax.X -= _BoundingBox.Size.X + Spacer.X;
    PosMax.Y -= _BoundingBox.Size.Y + Spacer.Y;
    if (Position.X > PosMax.X) Position.X = PosMax.X;
    if (Position.X < PosMin.X) Position.X = PosMin.X;
    if (Position.Y > PosMax.Y) Position.Y = PosMax.Y;
    if (Position.Y < PosMin.Y) Position.Y = PosMin.Y;

    _Frame.RelativePosition = Position + ZDelta + BBoxDelta + ControlDelta + Spacer;
    return;
}

Void Private_Tooltip_Show(Integer _TooltipId, Text _ControlId, Text _Message) {
    if(!G_Tooltip_Tooltips.existskey(_TooltipId)) {
        Debug_Log("[Tooltip] Unknown tooltip "^_TooltipId);
        return;
    }
    declare CMlControl Control <=> Page.GetFirstChild(_ControlId);
    if(Control == Null) {
        Debug_Log("[Tooltip] Unknown control "^_ControlId);
        return;
    }
    declare CMlQuad BoundingBox <=> (Page.GetFirstChild(G_Tooltip_Tooltips[_TooltipId]["BoundingBoxId"]) as CMlQuad);
    if(BoundingBox == Null) {
        Debug_Log("[Tooltip] Unknown bounding box "^G_Tooltip_Tooltips[_TooltipId]["BoundingBoxId"]);
        return;
    }
    declare CMlFrame Frame <=> (Page.GetFirstChild(G_Tooltip_Tooltips[_TooltipId]["FrameId"]) as CMlFrame);
    if(Frame == Null) {
        Debug_Log("[Tooltip] Unknown Frame "^G_Tooltip_Tooltips[_TooltipId]["FrameId"]);
        return;
    }
    declare CMlLabel Label <=> (Page.GetFirstChild(G_Tooltip_Tooltips[_TooltipId]["LabelId"]) as CMlLabel);
    if(Label == Null) {
        Debug_Log("[Tooltip] Unknown Label "^G_Tooltip_Tooltips[_TooltipId]["LabelId"]);
        return;
    }
    
    declare XIncrement = Label.Size.X;
    Label.Value = _Message;
    Label.Size.X =  Label.ComputeWidth(_Message);
    XIncrement = Label.Size.X - XIncrement;
    BoundingBox.Size.X = BoundingBox.Size.X + XIncrement;
    Private_Tooltip_Position(Control, Frame, BoundingBox);
    Manipulation_Show(G_Tooltip_Tooltips[_TooltipId]["FrameId"]);
}

Void Private_Tooltip_Hide(Integer _TooltipId) {
    if(!G_Tooltip_Tooltips.existskey(_TooltipId)) {
        Debug_Log("[Tooltip] Unknown tooltip "^_TooltipId);
        return;
    }
    Manipulation_Hide(G_Tooltip_Tooltips[_TooltipId]["FrameId"]);
    Manipulation_SetText(G_Tooltip_Tooltips[_TooltipId]["LabelId"], "");
}

Void Private_Tooltip_EventListener(CMlEvent _Event) {
    switch(_Event.Type) {
        case CMlEvent::Type::MouseOver: {
            if(G_Tooltip_OnMouseOver.existskey(_Event.ControlId)) {
                foreach(TooltipId => Message in G_Tooltip_OnMouseOver[_Event.ControlId]) {
                    Private_Tooltip_Show(TooltipId, _Event.ControlId, Message);
                }
            }
        }
        case CMlEvent::Type::MouseOut: {
            if(G_Tooltip_OnMouseOver.existskey(_Event.ControlId)) {
                foreach(TooltipId => Message in G_Tooltip_OnMouseOver[_Event.ControlId]) {
                    Private_Tooltip_Hide(TooltipId);
                }
            }
        }
    }
}

Void Private_Tooltip_CustomEventListener(Text[][] _Event) {

}

Integer Tooltip_Create(Text _FrameId, Text _BoundingBoxId, Text _LabelId) {
    declare Id = Identifier_GenerateId();
    if(G_Tooltip_DefaultTooltip == 0) {
        G_Tooltip_DefaultTooltip = Id;
    }
    G_Tooltip_Tooltips[Id] = ["FrameId" => _FrameId, "BoundingBoxId" => _BoundingBoxId, "LabelId" => _LabelId];
    return Id;
}

Void Tooltip_OnMouseOver(Integer _TooltipId, Text _ElementId, Text _Message) {
    if(!G_Tooltip_OnMouseOver.existskey(_ElementId)) {
        G_Tooltip_OnMouseOver[_ElementId] = Text[Integer];
    }
    G_Tooltip_OnMouseOver[_ElementId][_TooltipId] = _Message;
}

Void Tooltip_OnMouseOver(Text _ElementId, Text _Message) {
    Tooltip_OnMouseOver(G_Tooltip_DefaultTooltip, _ElementId, _Message);
}

Void Tooltip_Init() {
    Tooltip_Create("Tooltip", "Tooltip_Background", "Tooltip_Label");
}

Void Tooltip_Loop() {
    foreach(Event in PendingEvents) {
        Private_Tooltip_EventListener(Event);
    }
    foreach(Event in G_Event_PendingEvents) {
        Private_Tooltip_CustomEventListener(Event);
    }
}

//file: Loading.Script.txtm

declare Text[Integer] G_Loading_Scrobblers;

Void Loading_ShowScrobbler(Integer _ScrobblerId) {
	if (!G_Loading_Scrobblers.existskey(_ScrobblerId)) {
		Debug_Log("[Loading] _ShowScrobbler: unknown scrobbler ID: "^_ScrobblerId);
		return;
	}
	Manipulation_Show(G_Loading_Scrobblers[_ScrobblerId]);
}

Void Loading_HideScrobbler(Integer _ScrobblerId) {
	if (!G_Loading_Scrobblers.existskey(_ScrobblerId)) {
		Debug_Log("[Loading] _HideScrobbler: unknown scrobbler ID: "^_ScrobblerId);
		return;
	}
	Manipulation_Hide(G_Loading_Scrobblers[_ScrobblerId]);
}

Integer Loading_Init(Text _ScrobblerFrameId) {
	declare Integer ScrobblerId = Identifier_GenerateId("Loading");
	G_Loading_Scrobblers[ScrobblerId] = _ScrobblerFrameId;
	Loading_HideScrobbler(ScrobblerId);
	return ScrobblerId;
}

Integer Loading_Init() {
	return Loading_Init("Loading");
}

//file: ErrorMessage.Script.txtm

declare Text G_ErrorMessage_ErrorFrameId;
declare Text G_ErrorMessage_ErrorMessageId;
declare Text G_ErrorMessage_RefreshButtonId;
declare Text G_ErrorMessage_RefreshURL;
declare ::LinkType G_ErrorMessage_LinkType;

Void ErrorMessage_Init(Text _ErrorFrameId, Text _ErrorMessageId, Text _RefreshButtonId, Text _RefreshURL, ::LinkType _LinkType) {
	G_ErrorMessage_ErrorFrameId = _ErrorFrameId;
    G_ErrorMessage_ErrorMessageId = _ErrorMessageId;
	G_ErrorMessage_RefreshButtonId = _RefreshButtonId;
	G_ErrorMessage_RefreshURL = _RefreshURL;
	G_ErrorMessage_LinkType = _LinkType;

	Manipulation_Hide(_ErrorFrameId);
    Manipulation_FitToText(G_ErrorMessage_ErrorMessageId);
}

Void ErrorMessage_Init(Text _RefreshURL, ::LinkType _LinkType) {
	ErrorMessage_Init("ErrorFrame", "ErrorFrame:message", "ErrorFrame:RefreshButton", _RefreshURL, _LinkType);
    
}

Void ErrorMessage_Show() {
	Manipulation_Show(G_ErrorMessage_ErrorFrameId);
	Manipulation_DisableLinks();
}

Void ErrorMessage_EventListener(CMlEvent _Event) {
	if (_Event.Type == CMlEvent::Type::MouseClick && _Event.ControlId == G_ErrorMessage_RefreshButtonId) {
		OpenLink(G_ErrorMessage_RefreshURL, G_ErrorMessage_LinkType);
	}
}

Void ErrorMessage_Loop() {
    foreach (Event in PendingEvents) {
		ErrorMessage_EventListener(Event);
	}
}

//file: ServerBrowser.Script.txtm

declare CUser G_ServerBrowser_User;
declare Real G_ServerBrowser_UserLadderPoints;

/*
 * Filters
 * -------------------------------------------------------------------------------------------------
 */

declare Text[Text] G_ServerBrowser_Filters;

Void ServerBrowser_Filters_Set(Text _Name, Text _Value) {
	G_ServerBrowser_Filters[_Name] = _Value;
}

Text ServerBrowser_Filters_Get(Text _Name) {
	if (G_ServerBrowser_Filters.existskey(_Name)) {
		return G_ServerBrowser_Filters[_Name];
	}
	return "";
}

Void ServerBrowser_Filters_Remove(Text _Name) {
	if (G_ServerBrowser_Filters.existskey(_Name)) {
		G_ServerBrowser_Filters.removekey(_Name);
 
	}
}

Void ServerBrowser_Filters_RemoveZoneId() {
	ServerBrowser_Filters_Remove("zoneId");
}

Void ServerBrowser_Filters_SetMatchmaking(Integer _Value) {
	ServerBrowser_Filters_Set("matchmaking", TextLib::ToText(_Value));
}

Text ServerBrowser_Filters_GetMatchmaking() {
	return ServerBrowser_Filters_Get("matchmaking");
}

Void ServerBrowser_Filters_SetZoneId(Text _Value) {
	ServerBrowser_Filters_Set("zoneId", _Value);
	Manipulation_SetText("country:main", Zone_GetName(TextLib::ToInteger(_Value)));
}

Text ServerBrowser_Filters_GetZoneId() {
	return ServerBrowser_Filters_Get("zoneId");
}

Void ServerBrowser_Filters_SetMode(Text _Value) {
	ServerBrowser_Filters_Set("mode", _Value);
	Manipulation_SetText("multiplayer:filters:mode:label", _Value);
	Manipulation_SetTextColor("multiplayer:filters:mode:label","FFF");
}

Void ServerBrowser_Filters_RemoveMode() {
	ServerBrowser_Filters_Remove("mode");
	Manipulation_SetText("multiplayer:filters:mode:label", "All modes");
	Manipulation_SetTextColor("multiplayer:filters:mode:label","CCC");
}
	

Void ServerBrowser_Filters_SetPrivacy(Text _Value) {
	ServerBrowser_Filters_Set("privacy", _Value);
}

Void ServerBrowser_Filters_RemovePrivacy() {
	ServerBrowser_Filters_Remove("privacy");
}

Text ServerBrowser_Filters_GetPrivacy() {
	return ServerBrowser_Filters_Get("privacy");
}

Void ServerBrowser_Filters_SetHideFull(Text _Value) {
	ServerBrowser_Filters_Set("hideFull", _Value);
}

Text ServerBrowser_Filters_GetHideFull() {
	return ServerBrowser_Filters_Get("hideFull");
}

Void ServerBrowser_Filters_SetSearch(Text _Value) {
	ServerBrowser_Filters_Set("search", _Value);
	ServerBrowser_Filters_RemovePrivacy();
	ServerBrowser_Filters_RemoveZoneId();
}

Text ServerBrowser_Filters_GetSearch() {
	return ServerBrowser_Filters_Get("search");
}

Void ServerBrowser_Filters_SetZoneId(Integer _Value) {
	ServerBrowser_Filters_SetZoneId(TextLib::ToText(_Value));
}

Void ServerBrowser_Filters_ResetZoneId() {
	declare Text[] Zones = TextLib::Split("|", G_ServerBrowser_User.ZonePath);
	if (Zones.count >= 1)
		ServerBrowser_Filters_SetZoneId(Zone_GetId(Zones[1]));
}

Void ServerBrowser_Filters_SetFavorite(Integer _Value) {
	if (_Value == 1) {
		ServerBrowser_Filters_Set("favorite", "1");
	}
	else {
		ServerBrowser_Filters_Set("favorite", "0");
	}
}

Text ServerBrowser_Filters_GetFavorite() {
	return ServerBrowser_Filters_Get("favorite");
}

Void ServerBrowser_Filters_RemoveFavorite() {
	ServerBrowser_Filters_Remove("favorite"); 
}

/*
 * Order
 * -------------------------------------------------------------------------------------------------
 */

declare Text G_ServerBrowser_Order;
declare Text G_ServerBrowser_OrderModifier;

Text ServerBrowser_Order_Get() {
	return G_ServerBrowser_Order;
}

Text ServerBrowser_Order_GetModifier() {
	return G_ServerBrowser_OrderModifier;
}

Void ServerBrowser_Order_Set(Text _Order) {
	G_ServerBrowser_Order = _Order;
}

Void ServerBrowser_Order_SetPlayers() {
	ServerBrowser_Order_Set("players");
}

Void ServerBrowser_Order_SetName() {
	ServerBrowser_Order_Set("name");
}

Void ServerBrowser_Order_SetLevel() {
	ServerBrowser_Order_Set("level");
}

Void ServerBrowser_Order_SetModifier(Text _Modifier) {
	G_ServerBrowser_OrderModifier = _Modifier;
}

Void ServerBrowser_Order_InvertModifier() {
	if (G_ServerBrowser_OrderModifier == "DESC") {
		G_ServerBrowser_OrderModifier = "ASC";
	}
	else {
		G_ServerBrowser_OrderModifier = "DESC";
	}
}

Void ServerBrowser_MetaFilters_Beginners() {
	ServerBrowser_Filters_Set("minimalBuildVersion", "2014-06-05");
    ServerBrowser_Filters_SetMode("Battle");
	ServerBrowser_Filters_SetMatchmaking(-1);
	ServerBrowser_Filters_RemoveFavorite();
	ServerBrowser_Filters_RemovePrivacy();
}

Void ServerBrowser_MetaFilters_Matchmaking() {
	ServerBrowser_Filters_SetMatchmaking(1);
	ServerBrowser_Filters_RemoveMode();
	ServerBrowser_Filters_RemoveFavorite();
	ServerBrowser_Filters_RemovePrivacy();
}

Void ServerBrowser_MetaFilters_Servers() {
	ServerBrowser_Filters_SetMatchmaking(0);
	ServerBrowser_Filters_RemoveMode();
	ServerBrowser_Filters_RemoveFavorite();
	ServerBrowser_Filters_RemovePrivacy();
}

Void ServerBrowser_MetaFilters_Favorites() {
	ServerBrowser_Filters_SetFavorite(1);
	ServerBrowser_Filters_SetMatchmaking(0);
	ServerBrowser_Filters_RemoveMode();
	ServerBrowser_Filters_SetPrivacy("all");
	ServerBrowser_Filters_SetZoneId(1);
}


/*
 * Adequacy
 * -------------------------------------------------------------------------------------------------
 */

Text ServerBrowser_Adequacy_GetSubstyle(Integer _Adequacy) {
	switch (_Adequacy) {
		case 1:
			return "Green";
		case 2:
			return "YellowHigh";
		case 3:
			return "YellowLow";
		case 4:
			return "RedHigh";
		case 5:
			return "RedLow";
	}
	return "";
}

Text ServerBrowser_Adequacy_GetSubstyle(Text _Adequacy) {
	return ServerBrowser_Adequacy_GetSubstyle(TextLib::ToInteger(_Adequacy));
}

Text ServerBrowser_Adequacy_GetHelp(Integer _Adequacy) {
	switch (_Adequacy) {
		case 1:
			return "At your level";
		case 2:
			return "Difficult";
		case 3:
			return "Too easy";
		case 4:
			return "Very difficult";
		case 5:
			return "Way too easy";
	}
	return "";
}

Text ServerBrowser_Adequacy_GetHelp(Text _Adequacy) {
	return ServerBrowser_Adequacy_GetHelp(TextLib::ToInteger(_Adequacy));
}

/*
 * Menu
 * -------------------------------------------------------------------------------------------------
 */

declare Text[Text] G_ServerBrowser_Menu_OrderItems;
declare Text[] G_ServerBrowser_Menu_Items;

Void Private_ServerBrowser_Menu_Update() {
	foreach (Order => ControlId in G_ServerBrowser_Menu_OrderItems) {
		if (Order == ServerBrowser_Order_Get()) {
			Manipulation_SetTextSize(ControlId, 2);
		}
		else {
			Manipulation_SetTextSize(ControlId, 1);
		}
	}
}

/*
 * Misc.
 * -------------------------------------------------------------------------------------------------
 */

Text Private_ServerBrowser_GetURL(Text _TitleUid, Integer _Offset, Integer _Limit) {
	declare Text[Text] Query;
	Query["titleUid"] = _TitleUid;
	Query["offset"] = TextLib::ToText(_Offset);
	Query["length"] = TextLib::ToText(_Limit);
	Query["order"] = ServerBrowser_Order_Get();
	Query["modifier"] = ServerBrowser_Order_GetModifier();
	foreach (Name => Value in G_ServerBrowser_Filters) {
		Query[Name] = Value;
	}
	return ApplicationData_BuildUrl("/server-browser/", Query);
}

Text Private_ServerBrowser_GetServerCardId(Integer _Id, Text _Suffix) {
	declare Text Result;
	Result = "server:";
	Result ^= TextLib::ToText(_Id);
	if (_Suffix != "") {
		Result ^= ":"^_Suffix;
	}
	return Result;
}

Text Private_ServerBrowser_GetServerCardId(Integer _Id) {
	return Private_ServerBrowser_GetServerCardId(_Id, "");
}

/*
 * App
 * -------------------------------------------------------------------------------------------------
 */

declare Text[Text][] G_ServerBrowser_Servers;

Void Private_ServerBrowser_UI_DisplayServer(Integer _Index) {
    if(G_ServerBrowser_Servers.existskey(_Index)) {
        declare Integer Echelon = Player_GetEchelon(G_ServerBrowser_Servers[_Index]["averageLadderPoints"]);
        declare Text Mode = G_ServerBrowser_Servers[_Index]["mode"];
        if (G_ServerBrowser_Servers[_Index]["isLobby"] == "true") {
            Mode ^= " $i(Lobby)";
        }
        Manipulation_Show(Private_ServerBrowser_GetServerCardId(_Index));
        Manipulation_SetText(Private_ServerBrowser_GetServerCardId(_Index, "name"), G_ServerBrowser_Servers[_Index]["name"]);
        Manipulation_SetText(Private_ServerBrowser_GetServerCardId(_Index, "mode"), Mode);
        Tooltip_OnMouseOver(Private_ServerBrowser_GetServerCardId(_Index, "mode"), "Click to filter by $<$o"^Mode^"$>");
        Manipulation_SetImageURL(Private_ServerBrowser_GetServerCardId(_Index, "flag"), G_ServerBrowser_Servers[_Index]["flagURL"]);
		declare Text TextPlayersCount = G_ServerBrowser_Servers[_Index]["playerCount"];
		if (G_ServerBrowser_Servers[_Index]["isLobby"] != "true") {
			TextPlayersCount ^= "/"^G_ServerBrowser_Servers[_Index]["maxPlayerCount"];
		}
        Manipulation_SetText(Private_ServerBrowser_GetServerCardId(_Index, "players"), TextPlayersCount);
        if (TextLib::ToInteger(G_ServerBrowser_Servers[_Index]["playerCount"]) > 0) {
			Manipulation_SetOpacity(Private_ServerBrowser_GetServerCardId(_Index, "name"), 1.);
			Manipulation_SetOpacity(Private_ServerBrowser_GetServerCardId(_Index, "mode"), 1.);
			Manipulation_SetOpacity(Private_ServerBrowser_GetServerCardId(_Index, "players"), 1.);
            Manipulation_SetText(Private_ServerBrowser_GetServerCardId(_Index, "echelon:value"), TextLib::ToText(Echelon));
            Manipulation_SetImageURL(Private_ServerBrowser_GetServerCardId(_Index, "echelon:image"), "file://Media/Manialinks/Common/Echelons/echelon"^TextLib::ToText(Echelon)^".dds");

            Manipulation_SetStyle(Private_ServerBrowser_GetServerCardId(_Index, "adequacy"), "Icons64x64_1");
            Manipulation_SetSubstyle(Private_ServerBrowser_GetServerCardId(_Index, "adequacy"), ServerBrowser_Adequacy_GetSubstyle(G_ServerBrowser_Servers[_Index]["adequacy"]));

            Manipulation_Show(Private_ServerBrowser_GetServerCardId(_Index, "echelon:value"));
            Manipulation_Show(Private_ServerBrowser_GetServerCardId(_Index, "echelon:image"));
            Manipulation_Show(Private_ServerBrowser_GetServerCardId(_Index, "adequacy"));

            Tooltip_OnMouseOver(Private_ServerBrowser_GetServerCardId(_Index, "echelon:image"), "Average echelon on server");
        }
        else {
			Manipulation_SetOpacity(Private_ServerBrowser_GetServerCardId(_Index, "name"), 0.80);
			Manipulation_SetOpacity(Private_ServerBrowser_GetServerCardId(_Index, "mode"), 0.80);
			Manipulation_SetOpacity(Private_ServerBrowser_GetServerCardId(_Index, "players"), 0.80);
            Manipulation_Hide(Private_ServerBrowser_GetServerCardId(_Index, "echelon:value"));
            Manipulation_Hide(Private_ServerBrowser_GetServerCardId(_Index, "echelon:image"));
            Manipulation_Hide(Private_ServerBrowser_GetServerCardId(_Index, "adequacy"));
        }
        if (G_ServerBrowser_Servers[_Index]["isPrivate"] == "true") {
            Manipulation_Show(Private_ServerBrowser_GetServerCardId(_Index, "privacy"));
        }	
        else {
            Manipulation_Hide(Private_ServerBrowser_GetServerCardId(_Index, "privacy"));
        }
		declare Text TooltipText; 
		if (G_ServerBrowser_User.LadderPoints < TextLib::ToInteger(G_ServerBrowser_Servers[_Index]["ladderLimitMin"])) {
			TooltipText = "You need at least "^G_ServerBrowser_Servers[_Index]["ladderLimitMin"]^" LP to play";
			Manipulation_Show(Private_ServerBrowser_GetServerCardId(_Index, "adequacyModifier"));
		}
		else if (G_ServerBrowser_User.LadderPoints > TextLib::ToInteger(G_ServerBrowser_Servers[_Index]["ladderLimitMax"])) {
			TooltipText = "Maximum is "^G_ServerBrowser_Servers[_Index]["ladderLimitMax"]^" LP";
			Manipulation_Show(Private_ServerBrowser_GetServerCardId(_Index, "adequacyModifier"));
		}
		else {
			TooltipText = ServerBrowser_Adequacy_GetHelp(G_ServerBrowser_Servers[_Index]["adequacy"]);
			Manipulation_Hide(Private_ServerBrowser_GetServerCardId(_Index, "adequacyModifier"));
		}
		if (Manipulation_GetVisibility(Private_ServerBrowser_GetServerCardId(_Index, "adequacy"))) {
			Tooltip_OnMouseOver(Private_ServerBrowser_GetServerCardId(_Index, "adequacy"), TooltipText);
		}
    }
    else {
        Manipulation_Hide(Private_ServerBrowser_GetServerCardId(_Index, ""));
    }
}


/*
 * App
 * -------------------------------------------------------------------------------------------------
 */

declare Text G_ServerBrowser_TitleIdString;
declare Integer G_ServerBrowser_LoadingId;
declare Integer G_ServerBrowser_PagerId;
declare Ident G_ServerBrowser_RequestId;

Void ServerBrowser_Update() {
	Private_ServerBrowser_Menu_Update();
	Pager_ResetCurrentPage(G_ServerBrowser_PagerId);
	Pager_ForceChange(G_ServerBrowser_PagerId);
}

Void Private_ServerBrowser_GetAsync()
{
    Loading_ShowScrobbler(G_ServerBrowser_LoadingId);
	Pager_Disable(G_ServerBrowser_PagerId);
	Event_DispatchCustomEvent("ServerBrowser.Update");
	G_ServerBrowser_RequestId = HTTP_GetAsync(Private_ServerBrowser_GetURL(
        G_ServerBrowser_TitleIdString, 
        Pager_GetOffset(G_ServerBrowser_PagerId), 
        Pager_GetLimit(G_ServerBrowser_PagerId)));
	ApplicationData_Register(G_ServerBrowser_RequestId);
}

Void Private_ServerBrowser_HandleResponse(Text _Response)
{
    declare CXmlDocument Document = XML_TextToXMLDocument(_Response);
	G_ServerBrowser_Servers = XML_NodeToArray2(Document.GetFirstChild("servers"));
	Xml.Destroy(Document);
    Pager_PagerUpdate(G_ServerBrowser_PagerId, G_ServerBrowser_Servers.count);
    Pager_Enable(G_ServerBrowser_PagerId);
    Loading_HideScrobbler(G_ServerBrowser_LoadingId);

    if (G_ServerBrowser_Servers.count == 0) {
        Manipulation_Show("empty-list");
		if (ServerBrowser_Filters_GetZoneId() != "1") {
			//Trying to go to World zone
			ServerBrowser_Filters_SetZoneId(1);
			ServerBrowser_Update();
		}
    }
    else {
        Manipulation_Hide("empty-list");
    }

    for(i, 0, Pager_GetItemsPerPage(G_ServerBrowser_PagerId)-1) {
        Private_ServerBrowser_UI_DisplayServer(i);
    }

	Manipulation_SetEntryValue("multiplayer:filters:search:entry", ServerBrowser_Filters_GetSearch());
}

Void Private_ServerBrowser_EventListener(CMlEvent _Event) {
	declare Text[] ControlIdArray = TextLib::Split(":", _Event.ControlId);
	if (_Event.Type == CMlEvent::Type::MouseClick) {
		if (ControlIdArray.count >= 3) {
			if (ControlIdArray[0] == "multiplayer") {
				if (ControlIdArray[1] == "filters") {
					switch(ControlIdArray[2]) {
						case "private": {
							if (ServerBrowser_Filters_GetPrivacy() == "private") {
								ServerBrowser_Filters_SetPrivacy("public");
								Manipulation_SetStyleSelected("multiplayer:filters:private:quad", False);
							}
							else {
								ServerBrowser_Filters_SetPrivacy("private");
								Manipulation_SetStyleSelected("multiplayer:filters:private:quad", True);
							}
							Controls_Select_Close("advanced");
						}
						case "hideFull": {
							if (ServerBrowser_Filters_GetHideFull() == "1") {
								ServerBrowser_Filters_SetHideFull("0");
								Manipulation_SetStyleSelected("multiplayer:filters:hideFull:quad", False);
							}
							else {
								ServerBrowser_Filters_SetHideFull("1");
								Manipulation_SetStyleSelected("multiplayer:filters:hideFull:quad", True);
							}
							Controls_Select_Close("advanced");
						}
						case "zone": {
							ServerBrowser_Filters_SetZoneId(ControlIdArray[3]);
							Controls_Select_Close("country");
						}
						case "matchmaking": {
							ServerBrowser_MetaFilters_Matchmaking();
						}
						case "servers": {
							ServerBrowser_MetaFilters_Servers();
						}
						case "favorites": {
							ServerBrowser_MetaFilters_Favorites();
						}
						case "mode": {
							ServerBrowser_Filters_Remove("mode");
						}
						case "search": {
							ServerBrowser_Filters_SetSearch(Manipulation_GetEntryValue("multiplayer:filters:search:entry"));
						}
						case "refresh": {
							//do nothing, just refresh
						}
					}
				}
				else if (ControlIdArray[1] == "order") {
					if (ServerBrowser_Order_Get() == ControlIdArray[2]) {
						ServerBrowser_Order_InvertModifier();
					}
					else {
						if (ControlIdArray[2] == "players") {
							ServerBrowser_Order_SetPlayers();
						}
						else if (ControlIdArray[2] == "level") {
							ServerBrowser_Order_SetLevel();
						}
						else if (ControlIdArray[2] == "name") {
							ServerBrowser_Order_SetName();
						}
						Manipulation_SetTextSize(_Event.ControlId, 2);
					}
				}
				ServerBrowser_Update();
			}
		}
		if (ControlIdArray.count == 3 && ControlIdArray[0] == "server") {
			declare Integer Index = TextLib::ToInteger(ControlIdArray[1]);
			if (G_ServerBrowser_Servers.existskey(Index)) {
				if (ControlIdArray[2] == "bg") {
					OpenLink(G_ServerBrowser_Servers[Index]["joinLink"], CMlScript::LinkType::ManialinkBrowser);
				}
				else if (ControlIdArray[2] == "mode") {
					ServerBrowser_Filters_SetMode(G_ServerBrowser_Servers[Index]["mode"]);
					ServerBrowser_Update();
				}
			}
		}
	}
	else if(_Event.Type == CMlEvent::Type::MouseOver) {
		if (ControlIdArray.count == 3) {
			if (ControlIdArray[0] == "server") {
				if (ControlIdArray[2] == "bg") {
					Audio.PlaySoundEvent(CAudioManager::ELibSound::Focus, 1, 1.);
				}
//				else if (ControlIdArray[2] == "mode") {
//					Manipulation_Focus("server:"^ControlIdArray[1]^":bg");
//				}
			}
		}
	}
	else if(_Event.Type == CMlEvent::Type::EntrySubmit) {
		ServerBrowser_Filters_SetSearch(Manipulation_GetEntryValue("multiplayer:filters:search:entry"));
		ServerBrowser_Update();
	}
}

Void Private_ServerBrowser_Error() {
	ErrorMessage_Show();
	Loading_HideScrobbler(G_ServerBrowser_LoadingId);
}

Void Private_ServerBrowser_CustomEventListener(Text[][] _Event) {
    switch(_Event[0][0]) {
        case "ApplicationData.Success": {
            if(_Event[1][0] == ""^G_ServerBrowser_RequestId) {
                Private_ServerBrowser_HandleResponse(_Event[1][1]);
            }
		}
		case "ApplicationData.Error": {
			if(_Event[1][0] == ""^G_ServerBrowser_RequestId) {
				Private_ServerBrowser_Error();
			}
		}
		case "HTTP.Error": {
			if(_Event[1][0] == ""^G_ServerBrowser_RequestId) {
				Private_ServerBrowser_Error();
			}
		}
        case "Pager.Changed": {
            if(_Event[1][0] == ""^G_ServerBrowser_PagerId) {
                Private_ServerBrowser_GetAsync();
            }
        }
    }
}

Void ServerBrowser_Init(Text _ServerBrowser_TitleIdString, CUser _User, Real _UserLadderPoints, Integer _ServerCardsCount) {
	Zone_Init();

	G_ServerBrowser_User <=> _User;
	//We can't get _User.ladderPoints because it's not working on the station (need to use CStation)
	G_ServerBrowser_UserLadderPoints = _UserLadderPoints;
    G_ServerBrowser_TitleIdString = _ServerBrowser_TitleIdString;

	G_ServerBrowser_Menu_OrderItems = ["level" => "multiplayer:order:level", "players" => "multiplayer:order:players", "level" => "multiplayer:order:level"];

	if (!_User.IsConnectedToMasterServer)
	{
		G_ServerBrowser_Servers.clear();
		for (I, 0, _ServerCardsCount-1)
			Manipulation_Hide(Private_ServerBrowser_GetServerCardId(I, ""));
		Manipulation_Show("not-connected");
	}
	
	//FIXME: maybe use something to filter with baseID or do a lib or something
	if (_ServerBrowser_TitleIdString == "TMStadium" ||
		_ServerBrowser_TitleIdString == "TMCanyon" ||
		_ServerBrowser_TitleIdString == "TMValley") {
			ServerBrowser_Filters_SetZoneId(1);
	}
	else {
		ServerBrowser_Filters_ResetZoneId();
	}
	ServerBrowser_Order_SetPlayers();

	ServerBrowser_Order_SetModifier("DESC");

    G_ServerBrowser_LoadingId = Loading_Init("Loading:multiplayer");
    G_ServerBrowser_PagerId = Pager_Init(_ServerCardsCount, "pagenavigator:servers:arrowPrev", "pagenavigator:servers:arrowNext", "pagenavigator:servers:text");
    
	Controls_Select_Init("country");
	Controls_Select_Init("advanced");
}

Void ServerBrowser_Loop() {
    foreach (Event in PendingEvents) {
		Private_ServerBrowser_EventListener(Event);
	}
    foreach(Event in Event_PendingEvents()) {
        Private_ServerBrowser_CustomEventListener(Event);
    }
}


//file: Title_ServerBrowser.Script.txtm

Void Private_Title_ServerBrowser_Menu_Init() {
    G_ServerBrowser_Menu_Items = ["multiplayer:menu:recommended:selected", "multiplayer:menu:matchmaking:selected", "multiplayer:menu:servers:selected", "multiplayer:menu:favorites:selected"];
}

Void Private_Title_ServerBrowser_Menu_Select(Text _CurrentButton) {
	foreach (Button in G_ServerBrowser_Menu_Items) {
		if (Button == _CurrentButton) {
			Manipulation_Show(Button);
		}
		else {
			Manipulation_Hide(Button);
		}
	}
}

Void Private_Title_ServerBrowser_SelectRecommended() {
	ServerBrowser_MetaFilters_Beginners();
	Private_Title_ServerBrowser_Menu_Select("multiplayer:menu:recommended:selected");
}
Void Private_Title_ServerBrowser_SelectMatchmaking() {
	ServerBrowser_MetaFilters_Matchmaking();
	Private_Title_ServerBrowser_Menu_Select("multiplayer:menu:matchmaking:selected");
}
Void Private_Title_ServerBrowser_SelectServers() {
	ServerBrowser_MetaFilters_Servers();
	Private_Title_ServerBrowser_Menu_Select("multiplayer:menu:servers:selected");
}
Void Private_Title_ServerBrowser_SelectFavorites() {
	ServerBrowser_MetaFilters_Favorites();
	Private_Title_ServerBrowser_Menu_Select("multiplayer:menu:favorites:selected");
}

Void Private_Title_ServerBrowser_CustomEventListener(Text[][] _Event) {
}

Void Private_Title_ServerBrowser_EventListener(CMlEvent _Event) {
	declare Text[] ControlIdArray = TextLib::Split(":", _Event.ControlId);
	if (_Event.Type == CMlEvent::Type::MouseClick) {
		if (ControlIdArray.count >= 3) {
			if (ControlIdArray[0] == "multiplayer" && ControlIdArray[1] == "menu") {
				switch(ControlIdArray[2]) {
					case "recommended": {
						Private_Title_ServerBrowser_SelectRecommended();
					}
					case "matchmaking": {
						Private_Title_ServerBrowser_SelectMatchmaking();
					}
					case "servers": {
						Private_Title_ServerBrowser_SelectServers();
					}
					case "favorites": {
						Private_Title_ServerBrowser_SelectFavorites();
					}
				}
			}
		}
	}
}

Void Title_ServerBrowser_Loop() {
	foreach (Event in PendingEvents) {
		Private_Title_ServerBrowser_EventListener(Event);
	}
    foreach(Event in Event_PendingEvents()) {
        Private_Title_ServerBrowser_CustomEventListener(Event);
    }
	HTTP_Loop();
	ApplicationData_Loop();
	ServerBrowser_Loop(); 
	Pager_Loop();
	Controls_Loop();
	ErrorMessage_Loop();
	Tooltip_Loop();
	Event_Yield();
}

Void Title_ServerBrowser_Init(Boolean _Debug, Text _ApplicationDataURL, Text _SID, Text _ServerBrowser_TitleIdString, Integer _FilterMatchmaking, CUser _User, Integer _ServerCardsCount) {
	Debug_Init(_Debug);
	ApplicationData_Init(_ApplicationDataURL, _User.Login, _SID);
	Private_Title_ServerBrowser_Menu_Init();
	ServerBrowser_Init(_ServerBrowser_TitleIdString, _User, _User.LadderPoints, _ServerCardsCount);
	if (_ServerBrowser_TitleIdString == "SMStorm" && _User.LadderPoints < 10000.) {
		Private_Title_ServerBrowser_SelectRecommended();
	}
	else {
		Private_Title_ServerBrowser_SelectServers();
	}
	Tooltip_Init();
}