
function FeedbackAdmin(sAppid) {

	function id(sId) { return document.getElementById(sId); }
	
	var sVersion = "1_0_3",
		nUid,
		sClientAppid = "",
		sClientId = "",
		sClientName = "";

	var sSession = Cookies.Get("session"),
		sEmail = Cookies.Get("email"),
		sPassword = Cookies.Get("password");

	function MessageOutput() {
		this.Error = function(sText, oResponse) {
			_sendError(sText, oResponse);
			sText = GFeedbackAdminLocalize[sText] || sText;
			if(oResponse) {
				if(oResponse.result && oResponse.result != 0) {
					var sLocaleText = GFeedbackAdminLocalize["LT_E_Platform_" + oResponse.result];
					sText += " [" + oResponse.result + "]: " + (sLocaleText || oResponse.error);
				}
			}
			oMessage.innerHTML = sText;
			oMessage.className = "error";
			oMessage.style.display = "block";
			if(nTimeout) clearTimeout(nTimeout);
			nTimeout = setTimeout(function() { oMessage.style.display = "none"; nTimeout = 0; }, 2500);
		}
		this.Notify = function(sText) {
			sText = GFeedbackAdminLocalize[sText] || sText;
			oMessage.innerHTML = sText;
			oMessage.className = "notify";
			oMessage.style.display = "block";
			if(nTimeout) clearTimeout(nTimeout);
			nTimeout = setTimeout(function() { oMessage.style.display = "none"; nTimeout = 0; }, 2500);
		}

		var _sendError = function(sText, oResponse) {
			var oError = {
				sModuleType : "FeedbackAdmin",
				sClientAppid : sClientAppid,
				sClientId : sClientId,
				sClientName : sClientName,
				sResult : oResponse ? oResponse.result : "locale",
				sError : oResponse ? oResponse.error : "locale",
				sLocaleConst : GFeedbackAdminLocalize[sText] ? sText : "empty", 
				sLocaleText : GFeedbackAdminLocalize[sText] || sText
			};
			Data.Base.CreateObject(sAppid, sSession, "GErrorCollector", toJSON(oError), function() {});
		}

		// Constructor.
		var nTimeout, oMessage = id("message");
		oMessage.onclick = function() { this.style.display = "none"; nTimeout = 0; }
		return this;
	}

	var GOut = new MessageOutput();

	function ClearInputs(aInputs) { for(var i=0; i<aInputs.length; i++) if(id(aInputs[i])) id(aInputs[i]).value = ""; }

	function RegistrationBlock() {
		id("signin-block").style.display = "none";
		id("reg-block").style.display = "block";
		id("cancel").onclick = function() {
			id("reg-block").style.display = "none";
			id("signin-block").style.display = "block";
			ClearInputs([ "reg-email", "reg-name", "reg-password", "reg-re-password" ]);
		}
		id("createaccount").onclick = function() {
			if(!id("reg-email").value || !id("reg-password").value) {
				GOut.Error("LT_E_InputEmailAndPassword");
				return false;
			}
			if(id("reg-password").value != id("reg-re-password").value) {
				GOut.Error("LT_E_PasswordRetype");
				return false;
			}
			Users.Registration.CreateAccount(sAppid, id("reg-email").value, id("reg-password").value, id("reg-name").value, true, function(oResponse) {
				if(oResponse.result === 0) {		
					id("reg-block").style.display = "none";
					id("signin-block").style.display = "block";
					ClearInputs([ "reg-email", "reg-name", "reg-password", "reg-re-password" ]);
					GOut.Notify("LT_N_RegisterSuccess");
				} else {
					GOut.Error("LT_E_CreateAccount", oResponse);		
				}
			});
		}
		return false;
	}

	function RecoverBlock() {
		id("signin-block").style.display = "none";
		id("recover-block").style.display = "block";
		id("recover-cancel").onclick = function() {
			id("recover-block").style.display = "none";
			id("signin-block").style.display = "block";
			ClearInputs([ "recover-email" ]);
		}
		id("recover-password").onclick = function() {
			if(!id("recover-email").value) {
				GOut.Error("LT_E_InputEmailRecover");
				return false;
			}
			Users.Account.RecoverPassword(sAppid, id("recover-email").value, function(oResponse) {
				if(oResponse.result == 0) {
					id("recover-block").style.display = "none";
					id("signin-block").style.display = "block";
					ClearInputs([ "recover-email" ]);
				} else {
					GOut.Error("LT_E_PasswordRecover", oResponse);
				}
			});
		}
	}

	function SigninBlock() {
		if(!id("email").value || !id("password").value) {		
			GOut.Error("LT_E_InputEmailAndPassword");
			return false;
		}
		Users.Authentication.Signin(sAppid, id("email").value, id("password").value, function(oResponse) {
			if(oResponse.result === 0) {
				id("signin-block").style.display = "none";
				id("signout-block").style.display = "block";
				sSession = oResponse.session;
				nUid = oResponse.uid;
				Cookies.Set("session", sSession);
				Cookies.Set("email", id("email").value);
				Cookies.Set("password", id("password").value);

				sEmail = id("email").value;
				sPassword = id("password").value;

				if(oResponse.name) id("welcome").innerHTML = GFeedbackAdminLocalize["LT_Welcome"] + " <b>" + oResponse.name + "</b>";
				else id("welcome").innerHTML = id("email").value;

				//
				// Auth ok.
				//

				id("welcome-block").style.display = "none";
				id("main-block").style.display = "block";

				GetClient();

			} else {
				GOut.Error("Signin", oResponse);
			}
			id("password").value = "";
		});

		return true;
	}

	function SignoutBlock() {
		Users.Authentication.Signout(sAppid, sSession, function(oResponse) {
			if(oResponse.result === 0) {
				id("signin-block").style.display = "block";
				id("signout-block").style.display = "none";
				Cookies.Clear("session");
				Cookies.Clear("email");
				sSession = '';

				//
				// Cleanup.
				//

				id("main-block").style.display = "none";
				id("welcome-block").style.display = "block";

				var oSelect = id("client-list");
				oSelect.options.length = 0;
				var oOption = document.createElement("option");
				oOption.innerHTML = GFeedbackAdminLocalize["LT_NoClients"];
				oOption.value = 0;
				oSelect.appendChild(oOption);
				id("content-list").innerHTML = "";
				oSelect.onchange = function() {}
				id("client-actions").style.display = "none";
				id("suggestion-menu").style.display = "none";
				id("client-result").style.display = "none";
				sClientAppid = sClientId = sClientName = "";

			} else {
				GOut.Error("Signout", oResponse);
			}
		});
	}

	id("signin-block").getElementsByTagName('a')[0].onclick = function() { return RegistrationBlock(); }
	id("signin-block").getElementsByTagName('a')[1].onclick = function() { return RecoverBlock(); }
	id("signin").onclick = function() { return SigninBlock(); }
	id("signout").onclick = function() { return SignoutBlock(); }

	id("construct").onclick = function() {
		id("client-result").style.display = "none";
		var sClientName = id("client-name").value;
		if(!sClientName) {
			GOut.Error("LT_E_InputClientName");
			return;
		}
		this.disabled = true;
		id("client-result").innerHTML = "";
		if(!sClientAppid) {
			id("client-result").style.display = "block";
			id("client-result").innerHTML = GFeedbackAdminLocalize["LT_Config_Start"] + " " + sClientName + "<br />----------------------------------------<br />";
			id("client-result").innerHTML += GFeedbackAdminLocalize["LT_Config_GenerateApp"];
			Development.Applications.GenerateApp(sAppid, sSession, "Feedback Client [" + sVersion + "]", "Feedback Client [" + sVersion + "]", "*", function(oResponse) {			
				if(oResponse.result == 0) {
					sClientAppid = oResponse.appid;
					Data.Base.CreateObject(sAppid, sSession, "GUserList", "{sClientAppid:'" + sClientAppid + "'}", function(oResponse) {
						if(oResponse.result != 0) {
							GOut.Error("CreateObject(type=GUserList)->GenerateApp()->id('construct').onclick", oResponse);					
						}
					});

					//
					// Allow the admin application access to client with common session [ sAppid -> sClientAppid ].
					//

					Development.Applications.AllowAppAccess(sAppid, sSession, sClientAppid, sAppid, function(oResponse) {
					//Users.Authentication.Signin(sClientAppid, sEmail, sPassword, function(oResponse) {
						if(oResponse.result == 0) {
							//sSession = oResponse.session;
							Data.Base.DefineType(sClientAppid, sSession, "GClientList", "{sClientId:'string',sClientName:'string'}", function(oResponse) {
								if(oResponse.result == 0) {
									id("client-result").innerHTML += ": OK<br />";
									CreateClient(sClientName);
								} else {
									// Show error.
									GOut.Error("DefineType(type=GClientList)->AllowAppAccess()->GenerateApp()->id('construct').onclick", oResponse);
									id("construct").disabled = false;
								}
							});

						} else {
							// Show error.
							GOut.Error("AllowAppAccess()->GenerateApp()->id('construct').onclick", oResponse);
							id("construct").disabled = false;
						} 
					});

				} else {
					// Show error.
					GOut.Error("GenerateApp()->id('construct').onclick", oResponse);
					id("construct").disabled = false;
				}
			});
		} else {
			Data.Base.GetObjectsCount(sClientAppid, sSession, "GClientList", function(oResponse) {
				if(oResponse.result == 0) {
					if(oResponse.count < 3) {
						id("client-result").style.display = "block";
						id("client-result").innerHTML = GFeedbackAdminLocalize["LT_Config_Start"] + " " + sClientName + "<br />----------------------------------------<br />";
						CreateClient(sClientName);
					} else {
						GOut.Error("LT_E_MaxClients");
						id("construct").disabled = false;
					}
				} else {
					GOut.Error("GetObjectsCount(type=GClientList)->id('construct').onclick", oResponse);
					id("construct").disabled = false;
				}
			});

		}
	}

	function GetClient() {

		Data.Base.GetObjectsByCriteria(sAppid, sSession, "GUserList", "nUid=" + nUid, 0, 1, function(oResponse) {
			if(oResponse.result == 0 && oResponse.objects) {
				if(oResponse.objects.length) {
					sClientAppid = oResponse.objects[0].sClientAppid;

					//
					// Allow admin application access to client with common session [ sAppid -> sClientAppid ]. 
					//

					Development.Applications.AllowAppAccess(sAppid, sSession, sClientAppid, sAppid, function(oResponse) {
						if(oResponse.result == 0) {
							ViewClient();
						} else {
							// Show error.
							GOut.Error("AllowAppAccess()->GetObjectsByCriteria(type=GUserList)->GetClient()", oResponse);								
						}
					});
				} else {
					id("constructor-block").className = "block-show";
				}
			} else {
				// Show error.
				GOut.Error("GetObjectsByCriteria(type=GUserList)->GetClient()", oResponse);			
			}
		});
	}

	function ViewClient() {

		var sCurrentCode = "sMainTemplate";
		sClientId = sClientName = "";

		Data.Base.GetObjectsByCriteria(sClientAppid, sSession, "GClientList", "ORDER BY id DESC", function(oResponse) {
			if(oResponse.result == 0 && oResponse.objects) {
				var oSelect = id("client-list");
				oSelect.options.length = 0;
				if(oResponse.objects.length) {
					for(var i=0; i<oResponse.objects.length; i++) {
						var oOption = document.createElement("option");
						oOption.innerHTML = oResponse.objects[i].sClientName;
						oOption.value = oResponse.objects[i].sClientId;
						oSelect.appendChild(oOption);
					}
					oSelect.onchange = function() {
						sClientId = this.options[this.options.selectedIndex].value;
						sClientName = this.options[this.options.selectedIndex].innerHTML;
						var oBatchRequest = {
							version : "1.0",
							alias : { m : "Data.Base.GetObject" },
							global : { session : sSession, id : 1 },
							methods : [
								{ m : { type : "ClientInfo_" + sClientId } },
								{ m : { type : "ClientTheme_" + sClientId } }
							]
						};
						GBatchHttpSwitcher = true; // Get mode.
						Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
							if(oResponse.result == 0) {
								id("widget-header").value = oResponse.response[0].object.sHeaderTitle;
								id("settings-domain").value = oResponse.response[0].object.sClientDomain;
								if(oResponse.response[0].object.nModerateMode != undefined) {
									id("moderate-mode").value = oResponse.response[0].object.nModerateMode ?
										GFeedbackAdminLocalize["LT_ModerateOff"] : GFeedbackAdminLocalize["LT_ModerateOn"];
								} else { // Moderate mode - first init.
									Data.Base.AddField(sClientAppid, sSession, "ClientInfo_" + sClientId, "nModerateMode", "int", function(oResponse) {
										if(oResponse.result == 0) {
											id("moderate-mode").value = GFeedbackAdminLocalize["LT_ModerateOn"];
										}
									});
								}
								if(oResponse.response[1].object) {
									sCurrentCode = "sMainTemplate";
									id("widget-code").value = oResponse.response[1].object[sCurrentCode];
								} else id("widget-code").value = "";

								id("preview-client").href = "http://code.hivext.ru/applications/feedback/preview"
									+ (oResponse.response[0].object.sVersion ? "_" + oResponse.response[0].object.sVersion : "")
									+ ".html?client=" + sClientAppid + ":" + sClientId;  
                        		id("embed-code").innerHTML = EmbedCode(sClientAppid, sClientId, oResponse.response[0].object.sVersion);
								id("readonly-code").checked = "true";
								id("widget-code").disabled = "true";
							}
						});
						GBatchHttpSwitcher = false; // Post mode.
						var oMenuItems = id("suggestion-menu").getElementsByTagName("a");
						for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
						oMenuItems[2].parentNode.className = "active";
						GetContent(sClientId, 0);
					}
					oSelect.onchange();

					id("client-settings").onclick = function() {
						if(id("settings-block").style.display == "none" || id("settings-block").style.display == "") {
							id("settings-block").style.display = "block";
							this.className = "client-select";
						} else {
							id("settings-block").style.display = "none";
							this.className = "";
						}
					}

					id("apply-header").onclick = function() {
						var sClientId = oSelect.options[oSelect.options.selectedIndex].value;
						Data.Base.SetProperty(sClientAppid, sSession, "ClientInfo_" + sClientId, 1, "sHeaderTitle",
							id("widget-header").value, function(oResponse) {
							if(oResponse.result == 0) {
								GOut.Notify("LT_N_WidgetHeaderSaved");
							} else {
								GOut.Error("LT_E_DataSave");
							}
						});			
					}

					id("apply-domain").onclick = function() {
						var sClientId = oSelect.options[oSelect.options.selectedIndex].value;
						Data.Base.SetProperty(sClientAppid, sSession, "ClientInfo_" + sClientId, 1, "sClientDomain",
							id("settings-domain").value, function(oResponse) {
							if(oResponse.result == 0) {
								GOut.Notify("LT_N_WidgetDomainSaved");
							} else {
								GOut.Error("LT_E_DataSave");
							}
						});
					}

					id("moderate-mode").onclick = function() {
						id("moderate-mode").disabled = "true";
						Data.Base.GetProperty(sClientAppid, sSession, "ClientInfo_" + sClientId, 1, "nModerateMode", function(oResponse) {
							if(oResponse.result == 0) {
								id("moderate-mode").value = oResponse.value ?
										GFeedbackAdminLocalize["LT_ModerateOn"] : GFeedbackAdminLocalize["LT_ModerateOff"];
								Data.Base.SetProperty(sClientAppid, sSession, "ClientInfo_" + sClientId, 1, "nModerateMode",
									 oResponse.value ? 0 : 1, function(oResponse) {});
							}
							id("moderate-mode").disabled = "";
						});
					}

					// Developer.

					function GetWidgetCode(sCode) {					
						sCurrentCode = sCode;
						var sClientId = oSelect.options[oSelect.options.selectedIndex].value;
						id("widget-code").value = GFeedbackAdminLocalize["LT_Loading"];
						Data.Base.GetProperty(sClientAppid, sSession, "ClientTheme_" + sClientId, 1, sCurrentCode, function(oResponse) {
							if(oResponse.result == 0) {
								id("widget-code").value = oResponse.value;
							}
						});
					}

					id("common-template").onclick = function() { GetWidgetCode("sMainTemplate"); }
					id("suggestion-template").onclick = function() { GetWidgetCode("sSuggestionTemplate"); }
					id("comment-template").onclick = function() { GetWidgetCode("sCommentTemplate"); }
					id("main-css").onclick = function() { GetWidgetCode("sMainCSS"); }

					id("readonly-code").onclick = function() { id("widget-code").disabled = this.checked; }

					id("apply-code").onclick = function() {
						var sClientId = oSelect.options[oSelect.options.selectedIndex].value; 
						Data.Base.SetProperty(sClientAppid, sSession, "ClientTheme_" + sClientId, 1,
							sCurrentCode, id("widget-code").value, function(oResponse) {
							if(oResponse.result == 0) { GOut.Notify("LT_CodeSaved"); }
						});
					}

					id("remove-client").onclick = function() {
						if(!confirm(GFeedbackAdminLocalize["LT_Confirm_DeleteClient"])) return;
						id("settings-block").style.display = "none";
						id("client-settings").className = "";
						id("client-actions").style.display = "none";
						id("suggestion-menu").style.display = "none";
						id("content-list").innerHTML = GFeedbackAdminLocalize["LT_DeleteClientProcess"];

						oSelect.disabled = true;
						var sClientId = oSelect.options[oSelect.options.selectedIndex].value;
						if(!sClientId) return;
						var oBatchRequest = {
							version : "1.0",
							alias : { m : "Data.Base.UndefineType" },
							global : { session : sSession },
							methods : [
								{ m : { type : "ClientInfo_" + sClientId } },
								{ m : { type : "ClientTheme_" + sClientId } },
								{ m : { type : "SuggestionList_" + sClientId } },
								{ m : { type : "VoteList_" + sClientId } },
								{ m : { type : "CommentList_" + sClientId } },
								{ "Security.AccessControl.DeleteRole" : { role : "ClientUsers_" + sClientId } },
								{ "Data.Base.DeleteObjectsByCriteria" : { type : "GClientList", criteria : "sClientId=" + sClientId } }
							]
						};
						GBatchHttpSwitcher = true;
						Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
							if(oResponse.result == 0) {
								GOut.Notify("LT_N_ClientRemoved");
								oSelect.disabled = false;
								ViewClient();
							} else {
								oSelect.disabled = false;
								GOut.Error("Batch.Call(appid=" + sClientAppid + ")->id('remove-client').onclick", oResponse);
							}
						});
						GBatchHttpSwitcher = false;
					}
					id("client-actions").style.display = "inline";
					id("suggestion-menu").style.display = "block";
					id("client-block").className = "block-show";
				} else {
					oSelect.options.length = 0;
					var oOption = document.createElement("option");
					oOption.innerHTML = GFeedbackAdminLocalize["LT_NoClients"];
					oOption.value = 0;
					oSelect.appendChild(oOption);
					id("content-list").innerHTML = "";
					oSelect.onchange = function() {}
					id("client-actions").style.display = "none";
					id("suggestion-menu").style.display = "none";
					id("client-block").className = "block-hide";
					id("client-result").style.display = "none";
					id("constructor-block").className = "block-show";
				}
			}
		});
	}

	function FormatDate(nDate) {
		var oDate = new Date(nDate);
		function fn(str) { str += ""; if(str.length == 1) str = "0" + str; return str; }
		return fn(oDate.getDate()) + "." + fn(oDate.getMonth() + 1) + "." + (oDate.getFullYear() + "").substr(2)
			+ "&nbsp;&nbsp;" + fn(oDate.getHours()) + ":" + fn(oDate.getMinutes());
	}

	function SetOptionByValue(oSelect, value) {
		var aOptions = oSelect.options;
		for(var i=0; i<aOptions.length; i++) {
			if(aOptions[i].value == value) {
				aOptions.selectedIndex = i;
				return true;
			}
		}
		return false;
	}

	function SafeEscape(sData) {
		if(typeof sData == "string")
			sData = sData.replace(/</g, "&lt;").replace(/>/g, "&gt;");
		return sData;
	}

	function XTemplate(sTemplate, oData) {
		var fn = function(t, p) { return (oData[p] != undefined) ? SafeEscape(oData[p]) : t; }
		return sTemplate.replace(/%\(([A-Za-z0-9_|.]*)\)/g, fn);
	}

	function EmbedCode(sClientAppid, sClientId, sVersion) {

		var sCode = '<div id="client-code">&#60;!-- insert after and near &#60;/BODY&#62; (this comment can be removed) --&#62;<br />'
			+ '&#60;script charset="utf-8" src="http://code.hivext.ru/applications/feedback/FeedbackWidget'
			+ (sVersion ? '_' + sVersion : '') + '.js" type="text/javascript"&#62;&#60;/script&#62;<br />'
			+ '&#60;script charset="utf-8" type="text/javascript"&#62;<br />'
			+ '&nbsp;&nbsp;FeedbackWidget("%(sClientAppid):%(sClientId)", { sWidth : 600, sHeight : 420, sButtonSide : "left", sLanguage : "ru" });<br />'
			+ '&#60;/script&#62;</div>';

		return XTemplate(sCode, { sClientAppid : sClientAppid, sClientId : sClientId });
	}

	function Init() {

		var oMenuItems = id("suggestion-menu").getElementsByTagName("a");
		oMenuItems[0].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus <> 'LT_Denied' AND sStatus <> 'LT_Moderate' ORDER BY id DESC");
		}
		oMenuItems[1].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus <> 'LT_Denied' AND sStatus <> 'LT_Moderate' ORDER BY nScore DESC, id DESC");
		}
		oMenuItems[2].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus = 'LT_New' OR sStatus = 'LT_Moderate' ORDER BY id DESC");
		}
		oMenuItems[3].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus = 'LT_UnderReview' ORDER BY id DESC");
		}
		oMenuItems[4].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus = 'LT_InProcess' ORDER BY id DESC");
		}
		oMenuItems[5].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus = 'LT_Complited' ORDER BY id DESC");
		}
		oMenuItems[6].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus = 'LT_Moderate' ORDER BY id DESC");
		}
		/*oMenuItems[7].onclick = function() {
			for(var i=0; i<oMenuItems.length; oMenuItems[i++].parentNode.className = "");
			this.parentNode.className = "active";
			GetContent(sCurrentClientId, 0, "sStatus = 'LT_Denied' ORDER BY id DESC");
		}*/

	}

	var sSuggestionTemplate =
		"<div id='suggestion-%(id)' class='%(sClassName)'>"
		+ "<div class='vote-score'>"
			+ "<span id='score-%(id)' class='score'>%(nScore)</span>"
		+ "</div>"
		+ "<div><h3>"
			+ "<select id='status-%(id)'>"
				+ "<option value='LT_New'>%(LT_New)</option>"
				+ "<option value='LT_UnderReview'>%(LT_UnderReview)</option>"
				+ "<option value='LT_InProcess'>%(LT_InProcess)</option>"
				+ "<option value='LT_Complited'>%(LT_Complited)</option>"
				+ "<option value='LT_Moderate'>%(LT_Moderate)</option></select>"
				/*+ "<option value='LT_Denied'>%(LT_Denied)</option></select>"*/
			+ "&nbsp;&nbsp;<a id='title-%(id)' href='javascript:void(0);'>%(sTitle)</a>"
			+ "</h3>%(sDescription)</div>"
		+ "<div class='meta'>%(sFormatDate) от %(sAuthor) | <a id='comments-%(id)' href='javascript:void(0)'>%(LT_Comments) (%(nComments))</a> | "
		+ "<a id='reset-%(id)' href='javascript:void(0)'>%(LT_Action_VoteClear)</a> | <a id='deletesuggestion-%(id)' href='javascript:void(0)'>%(LT_Action_Delete)</a>"
		+ "</div>"
		+ "<div class='clearfix'></div>"
		+ "</div>";
	sSuggestionTemplate = XTemplate(sSuggestionTemplate, GFeedbackAdminLocalize); 

	var sCommentTemplate = "<div id='comment'><div id='comment-meta'>%(sFormatDate) %(LT_CommentBy) %(sAuthor)</div><div>%(sComment)</div>"
		+ "<div class='actions'><a id='deletecomment-%(id)-%(nSuggestion)' href='javascript:void(0)'>%(LT_Action_DeleteComment)</a></div><div class='clearfix'></div></div>";
	sCommentTemplate = XTemplate(sCommentTemplate, GFeedbackAdminLocalize);

	function GetContent(sClientId, nType, sCriteria, nFrom) {
		var nCount, sDataType, sTemplate, sContentEmpty, aContentObjects;
		sCurrentClientId = sClientId; 
		sCurrentList = sCriteria = sCriteria || "sStatus = 'LT_New' OR sStatus = 'LT_Moderate' ORDER BY id DESC";
		nCurrentContent = nType;
		nFrom = nFrom || 0;
		if(nType == 0) { // Suggestion.

			id("suggestion-one").style.display = "none";

			nCount = 5; // Max on page.
			sContentType = "SuggestionList_";
			sTemplate = sSuggestionTemplate;
			sContentEmpty = "LT_SuggestionEmpty";
			id("content-list").className = "suggestion-list";
		} else if(nType == 1) { // Comments.
			nCount = 8; // Max on page.
			sContentType = "CommentList_";
			sTemplate = sCommentTemplate;
			sContentEmpty = "LT_CommentEmpty";
			id("content-list").className = "comment-list";
		} else {
			return;
		}
		sContentType += sClientId;
		id("content-list").innerHTML = "<div class='content-loading'>" + GFeedbackAdminLocalize["LT_Loading"] + "</div>";

		var sBatchRequest = "{version:'1.0',global:{session:'" + sSession + "',type:'" + sContentType + "',criteria:\"" + sCriteria + "\"},methods:["
			+ "{'Data.Base.GetObjectsCount':{}},{'Data.Base.GetObjectsByCriteria':{from:" + (nFrom ? nFrom : 0) + ",count:" + nCount + "}}]}";

		Utils.Batch.Call(sClientAppid, sBatchRequest, "true", function(oResponse) {

			if(oResponse.result == 0) {
				var nContentCount = oResponse.response[0].count || 0;
				if(!nContentCount) {
					id("content-list").innerHTML = "<div class='content-empty'>" + GFeedbackAdminLocalize[sContentEmpty] + "</div>";				
					return;
				}
				aContentObjects = oResponse.response[1].objects;				
				if(nType == 0) aSuggestionObjects = aContentObjects; 
				// Output data.
				var sContentListHtml = "", sPageHtml = "";
				for(var i=0, n=aContentObjects.length; i<n; i++) {
					if(nType == 0) {
						i % 2 ? aContentObjects[i].sClassName = "suggestion dark" : aContentObjects[i].sClassName = "suggestion";
						aContentObjects[i].sStatusText = GFeedbackAdminLocalize[aContentObjects[i].sStatus];
					}
					aContentObjects[i].sFormatDate = FormatDate(aContentObjects[i].nDate);
					sContentListHtml += XTemplate(sTemplate, aContentObjects[i]);
				}
				if(nContentCount > nCount) {
					sPageHtml += "<div id='list-select' align='right'>";
					var n = parseInt(nContentCount/nCount) + (nContentCount%nCount ? 1 : 0);
					for(var i=0; i<n; i++) {			
						sPageHtml += "<span id='" + i + "' class='" + (nFrom == (i*nCount) ? "page" : "page-select") + "'><a id='" + i + "' href='javascript:void(0);'>" + (i+1) + "</a></span>";
					}
					sPageHtml += "</div>";
				}
				id("content-list").innerHTML = sContentListHtml + sPageHtml;

				if(nType == 0) {
					var aSelect = id("content-list").getElementsByTagName("select");
					for(var i=0; i<aContentObjects.length; i++) SetOptionByValue(aSelect[i], aContentObjects[i].sStatus);
				}

				if(nContentCount > nCount) {
					id("list-select", true).onclick = function(oEvent) {
						var oElement = GetTarget(oEvent);
						if(oElement == this) return;
						if(oElement.id) GetContent(sClientId, nType, sCriteria, parseInt(oElement.id)*nCount);
					}
				}
			} else {
				var sErrorText = "Batch.Call(appid=" + sClientAppid + ")->GetContent(sClientId="
					+ sClientId + ",nType=" + nType + ",sCriteria=" + sCriteria + ",nFrom=" + nFrom + ")";
				GOut.Error(sErrorText, oResponse);
			}
		});

		// =======================================================================

		function FindContent(aContentObjects, nId) {
			for(var i=0, n=aContentObjects.length; i<n; i++) {
				if(aContentObjects[i].id == nId)
					return aContentObjects[i];
			}
		}

		id("content-list").onclick = id("suggestion-one").onclick = function(oEvent) {
			var oSuggestion, nSid, oElement = GetTarget(oEvent);

			if(oElement.id.indexOf("title") != -1 || oElement.id.indexOf("comments") != -1) {
				var nId = oElement.id.split("-")[1];
				oSuggestion = FindContent(aContentObjects, nId);
				oSuggestion.sClassName = "suggestion";
				id("suggestion-one").innerHTML = XTemplate(sSuggestionTemplate, oSuggestion); 
				var aSelect = id("suggestion-one").getElementsByTagName("select");
				SetOptionByValue(aSelect[0], oSuggestion.sStatus);
				id("suggestion-one").style.display = "block";
				GetContent(sClientId, 1, "nSuggestion=" + nId + " ORDER BY id DESC");
			} else if(oElement.id.indexOf("reset") != -1) {
				if(!confirm(GFeedbackAdminLocalize["LT_Confirm_Reset"])) return;
				nId = oElement.id.split("-")[1];
				var oBatchRequest = {
					version : "1.0",
					global : { session : sSession },
					methods : [
						{ "Data.Base.SetProperty" : { type : "SuggestionList_" + sClientId, id : nId, field : "nScore", value : 0 }},
						{ "Data.Base.DeleteObjectsByCriteria" : { type : "VoteList_" + sClientId, criteria : "nSuggestion=" + nId } }
					]
				};
				Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
					if(oResponse.result == 0) {
						id("score-" + nId).innerHTML = 0;
						GOut.Notify("LT_N_VoteClear");
					}
				});

			} else if(oElement.id.indexOf("deletesuggestion") != -1) {
				if(!confirm(GFeedbackAdminLocalize["LT_Confirm_DeleteSuggestion"])) return;
				nId = oElement.id.split("-")[1];
				var oBatchRequest = {
					version : "1.0",
					global : { session : sSession },
					methods : [
						{ "Data.Base.DeleteObject" : { type : "SuggestionList_" + sClientId, id : nId }},
						{ "Data.Base.DeleteObjectsByCriteria" : { type : "CommentList_" + sClientId, criteria : "nSuggestion=" + nId } },
						{ "Data.Base.DeleteObjectsByCriteria" : { type : "VoteList_" + sClientId, criteria : "nSuggestion=" + nId } }
					]
				};
				Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
					if(oResponse.result == 0) {
						GOut.Notify("LT_N_SuggestionRemoved");
						GetContent(sClientId, 0, "ORDER BY id DESC", nFrom);
					}
				});
			} else if(oElement.id.indexOf("deletecomment") != -1) {
				if(!confirm(GFeedbackAdminLocalize["LT_Confirm_DeleteComment"])) return;
				nId = oElement.id.split("-")[1];
				nSuggestion = oElement.id.split("-")[2];
				var oBatchRequest = {
					version : "1.0",
					global : { session : sSession },
					methods : [
						{ "Data.Base.DeleteObject" : { type : "CommentList_" + sClientId, id : nId }},
						{ "Data.Base.GetObjectsCount" : { type : "CommentList_" + sClientId, criteria : "nSuggestion=" + nSuggestion } }
					]
				};
				Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
					if(oResponse.result == 0) {
						GOut.Notify("LT_N_CommentRemoved");
						GetContent(sClientId, 1, sCriteria, nFrom);
						var nComments = oResponse.response[1].count;					
						var oBatchRequest = {
							version : "1.0",
							global : { session : sSession },
							methods : [
								{ "Data.Base.SetProperty" : { type : "SuggestionList_" + sClientId, id : nSuggestion, field : "nComments", value : nComments }},
								{ "Data.Base.GetObject" : { type : "SuggestionList_" + sClientId, id : nSuggestion } }
							]
						};
						Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
							if(oResponse.result == 0) {
								oSuggestion = oResponse.response[1].object;							
								oSuggestion.sClassName = "suggestion";
								oSuggestion.sStatusText = GFeedbackAdminLocalize[oSuggestion.sStatus];
								oSuggestion.sFormatDate = FormatDate(oSuggestion.nDate);
								id("suggestion-one").innerHTML = XTemplate(sSuggestionTemplate, oSuggestion);
								var aSelect = id("suggestion-one").getElementsByTagName("select");
								SetOptionByValue(aSelect[0], oSuggestion.sStatus);
							}
						});
					}
				});
			}

		}

		id("content-list").onchange = id("suggestion-one").onchange = function(oEvent) {
			var nSid, sStatus, oElement = GetTarget(oEvent);
			if(oElement.id.indexOf("status") != -1) {
				nSid = oElement.id.split("-")[1];
				sStatus = oElement.options[oElement.options.selectedIndex].value;
				Data.Base.SetProperty(sClientAppid, sSession, "SuggestionList_" + sClientId, nSid, "sStatus", sStatus, function(oResponse) {
					if(oResponse.result == 0) {
						GOut.Notify("LT_N_StatusChanged");
					} else {}
				});
			}
			return true;
		}

	}

	function CreateClient(sClientName) {

		var sClientId = parseInt(Math.random()*1000000) + "";
		var oDefaultTheme;

		id("client-result").innerHTML += GFeedbackAdminLocalize["LT_Config_DataBase"];

		var oBatchRequest = {
			version : "1.0",
			alias : { m : "Data.Base.DefineType" },
			global : { session : sSession },
			methods : [
				{ m : {
					type : "ClientInfo_" + sClientId,
					fields : toJSON({
						sClientDomain : "string",	// max 255
						sHeaderTitle : "string",
						nModerateMode : "int",
						sVersion : "string"
					})
				}},
				{ m : {
					type : "ClientTheme_" + sClientId,
					fields : toJSON({
						sThemeName : "string",
						sThemeDescription : "string",
						sMainTemplate : "text",	// max 16000
						sSuggestionTemplate : "text",
						sCommentTemplate : "text",
						sMainCSS : "text"
					})
				}},
				{ m : {
					type : "SuggestionList_" + sClientId,
					fields : toJSON({
						sTitle : "string(100)", 	// max 100
						sDescription : "string(500)",
						sAuthor : "string(32)", 	// max 32
						sStatus : "string",
						nScore : "int",
						nDate : "date",
						nComments : "int",
						nModerate : "int"
					})
				}},
				{ m : {
					type : "VoteList_" + sClientId,
					fields : toJSON({
						sSignature : "signature",
						nSuggestion : "int",
						nVote : "int"
					})
				}},
				{ m : {
					type : "CommentList_" + sClientId,
					fields : toJSON({
						nSuggestion : "int",
						sComment : "string(500)",	// max 500
						sAuthor : "string(32)",		// max 32
						nDate : "date",
						nModerate : "int"
					})
				}},
				{ "Data.Base.GetObject" : { appid : sAppid, session : sSession, type : "GThemeList", id : 1 }}
			]
		};

		Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
			if(oResponse.result == 0) {

				oDefaultTheme = oResponse.response[5].object;

				id("client-result").innerHTML += ": OK<br />";
				id("client-result").innerHTML += GFeedbackAdminLocalize["LT_Config_Access"];

				var oBatchRequest = {
					version : "1.0",
					alias : { m : "Security.AccessControl.AddPolicy" },
					global : { session : sSession, role : "ClientUsers_" + sClientId },
					methods : [
						{ "Security.AccessControl.CreateRole" : {}},
						{ m : { object : "ClientInfo_" + sClientId + "/*", rights : "read" }},
						{ m : { object : "ClientTheme_" + sClientId + "/*", rights : "read" }},
						{ m : { object : "SuggestionList_" + sClientId, rights : "create" }},
						{ m : { object : "SuggestionList_" + sClientId + "/*", rights : "create, read, update" }},
						{ m : { object : "VoteList_" + sClientId, rights : "create" }},
						{ m : { object : "VoteList_" + sClientId + "/*", rights : "create, read, update" }},
						{ m : { object : "CommentList_" + sClientId, rights : "create" }},
						{ m : { object : "CommentList_" + sClientId + "/*", rights : "create, read" }},
						{ "Security.AccessControl.ApplyRole" : { subject : "*,0" }}
					]
				};

				Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {
					if(oResponse.result == 0) {

						id("client-result").innerHTML += ": OK<br />";
						id("client-result").innerHTML += GFeedbackAdminLocalize["LT_Config_ClientSave"];

						var oBatchRequest = {
							version : "1.0",
							alias : { m : "Data.Base.CreateObject" },
							global : { session : sSession },
							methods : [
								{ m : {
									type : "ClientInfo_" + sClientId,
									data : toJSON({
										sHeaderTitle : GFeedbackAdminLocalize["LT_ClientHeader"],
										sClientDomain : "",
										nModerateMode : 1,
										sVersion : sVersion
									})
								}},
								{ m : {
									type : "ClientTheme_" + sClientId,
									data : toJSON({
										sThemeName : "default",
										sThemeDescription : "default",
										sMainTemplate : oDefaultTheme.sMainTemplate,
										sSuggestionTemplate : oDefaultTheme.sSuggestionTemplate,
										sCommentTemplate : oDefaultTheme.sCommentTemplate,
										sMainCSS : oDefaultTheme.sMainCSS
									})
								}},
								{ m : {
									type : "GClientList",
									data : toJSON({
										sClientId : sClientId,
										sClientName : sClientName
									})
								}}
							]
						};

						Utils.Batch.Call(sClientAppid, toJSON(oBatchRequest), "true", function(oResponse) {

							if(oResponse.result == 0) {

								id("client-result").innerHTML += ": OK<br />----------------------------------------<br />";
								id("client-result").innerHTML += GFeedbackAdminLocalize["LT_Config_Success"];

								id("settings-block").style.display = "block";
								id("client-settings").className = "client-select";

								id("constructor-block").className = "block-hide";
								id("construct").disabled = false;
								ClearInputs([ "client-name" ]);

								GOut.Notify("LT_N_ClientCreated");

								ViewClient();

							} else {
								GOut.Error("LT_E_Config_ClientSave", oResponse);
								id("construct").disabled = false;
							}
						});

					} else {
						GOut.Error("LT_E_Config_Access", oResponse);				
						id("construct").disabled = false;
					}
				});

			} else {
				// Show error.
				GOut.Error("LT_E_Config_DataBase", oResponse);
				id("construct").disabled = false;			
			}

		});

	}

	id("user-change-password").onclick = function() {
		if(id("user-new-password").value != id("user-re-new-password").value) {
			GOut.Error("LT_E_PasswordConfirm");
			return false;
		}
		Users.Account.ChangePassword(sClientAppid ? sClientAppid : sAppid, sSession, id("user-old-password").value, id("user-new-password").value, function(oResponse) {
			if(oResponse.result == 0) {
				GOut.Notify("LT_N_NewPasswordSetup");
				id("profile-block").className = "block-hide";
				ClearInputs([ "user-old-password", "user-new-password", "user-re-new-password" ]);
			} else { GOut.Error("ChangePassword", oResponse); }
		});
	}

	Users.Authentication.CheckSign(sAppid, sSession, function(oResponse) {
		if(oResponse.result === 0) {
			if(oResponse.name) id("welcome").innerHTML = GFeedbackAdminLocalize["LT_Welcome"] + " <b>" + oResponse.name + "</b>";
			else id("welcome").innerHTML = Cookies.Get("email");
			id("signin-block").style.display = "none";
			id("signout-block").style.display = "block";
			sSession = oResponse.session;
			nUid = oResponse.uid;
			Cookies.Set("session", sSession);

			//
			// Auth ok.
			//

			id("welcome-block").style.display = "none";
			id("main-block").style.display = "block";

			GetClient();

		} else {
			id("signin-block").style.display = "block";
			id("signout-block").style.display = "none";
		}
	});

	Init();

}
