/**
 * @author alex
 */
if (typeof LT == "undefined") {
	var LT = {};
}
LT.baseUrl = "";
/*
 * Uless re-defined use logo images as four-frames progress animation
 */
LT.animatedProgressElement = "logo"; 
LT.animatedProgressFrames = 4; 
LT.debug = false;
LT.s = function(noun, num) {
	num = 1*num;
	switch (num) {
		case 0:
			return LT.subs(LT.L('no_things'), {"num":num, "noun": noun});
		case 1:	
			return LT.subs(LT.L('one_thing'), {"num":num, "noun": noun});
		default:
			return LT.subs(LT.L('many_things'), {"num":num, "noun": noun});
	}
}
LT.d = function(ds) {
	return ds;
}
LT.init = function(page) {
	if (LT.locale.s)
		LT.s = LT.locale.s;
	if (LT.locale.d)
		LT.d = LT.locale.d;		
	// IE6 patch
	if (typeof XMLHttpRequest == "undefined")
		XMLHttpRequest = function() {
			try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch(e) {};
		    try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch(e) {};
		    try { return new ActiveXObject("Msxml2.XMLHTTP"); }     catch(e) {};
		    try { return new ActiveXObject("Microsoft.XMLHTTP"); }  catch(e) {};
		    return null;
		}	
	LT.params = LT.parseUrlString(document.location.href);
	LT.pi = page;

	if (LT.cookie.read("username")) {
		LT.username = LT.cookie.read("username");	
	} else {
		if (!LT.inArray(LT.pi, ["help", "index", "home"]) && !LT.debug)
			LT.redirect("index.htm?from=" + LT.pi + ".htm");
		else
			LT.username = "";
	}
	if (LT.isCloud) {
		if (LT.network_name == "" && LT.pi != "home" && !LT.debug) {
			LT.redirect("home.htm");
		}	
		if (LT.network_name != "" && LT.pi == "home" && !LT.debug) {
			LT.redirect("index.htm");
		}
	}
	if (LT.$(LT.animatedProgressElement)) {
		LT.progress.init(LT.$(LT.animatedProgressElement), LT.animatedProgressFrames);
	}
	LT.tokens = {
		"username": LT.username,
		"account_name": LT.network_name,
		"domain": LT.domain
	}
	LT.renderMetadata(); 
	LT.currentPage = LT.pages[LT.pi];
	if (LT.currentPage && LT.currentPage.init) {
		LT.currentPage.init();
	}
}

LT.merge = function(where, what){
	for (var i in what){
		if (!where[i])
			where[i] = what[i];
	}
}

LT.renderMetadata = function() {
	if (LT.$("network_name"))
		LT.$("network_name").innerHTML = LT.network_name;
	if (LT.$("logout"))
		LT.$("logout").innerHTML = LT.L('logged_in_as') + "<a href=\"user.htm?u=" + LT.username + "\">" + LT.username + "</a>. <a href=\"#\" onclick=\"LT.API.logout(); return false;\">" + LT.L('logout') + "</a>";
}
LT.pages = LT.pages || {};

LT.merge(LT.pages, {
	index: {
		init: function() {
			var _this = this;
			LT.$("login_form").onsubmit = function() {
				_this.doLogin();
				return false;
			}
			if (LT.params.guid) {
				this.changePassword(LT.params.guid);
			}
			if (LT.params.forgot_password) {
				this.forgotPassword();
			}
		},
		doLogin: function() {
			var user = LT.$("username").value;
			var pass = LT.$("password").value;
			// validate
			if (user == "") {
				LT.message.show(LT.L('empty_username'), 3000);
				LT.$("username").focus();
				return;
			}
			if (pass == "") {
				LT.message.show(LT.L('empty_password'), 3000);
				LT.$("password").focus();
				return;
			}
			LT.API.login(user, pass);
		},
		forgotPassword: function() {
			var _this = this;
			var dialog = {
				title: LT.L('password_wizard_1_title'),
				submitCaption: LT.L('retrieve_password'),
				form: {
					"email": {
						caption: LT.L('form_email'),
						attributes: {type: "text"}
					}
				},
				action: function() {
					if (this.getField("email").value == "") {
						this.showError(LT.L('empty_email_error'));
						this.getField("email").focus();
						return false;
					}

					LT.API.forgotPassword(function(){
						LT.message.show(LT.L('password_email_sent'));
					}, this.getField("email").value);
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},
		changePassword: function(guid) {
			var _this = this;
			var dialog = {
				title: LT.L('password_wizard_2_title'),
				submitCaption: LT.L('change_password'),
				form: {
					"password": {
						caption: LT.L('form_new_password'),
						attributes: {type: "password"}
					},
					"password2": {
						caption: LT.L('form_repeat_new_password'),
						attributes: {type: "password"}
					}
				},
				action: function() {
					if (this.getField("password").value.length < 4) {
						this.showError(LT.L('short_password_error'));
						this.getField("password").focus();
						return false;
					}
					if (this.getField("password2").value != this.getField("password").value) {
						this.showError(LT.L('passwords_match_error'));
						this.getField("password2").focus();
						return false;
					}
					LT.API.changePassword(function(){
						LT.message.show(LT.L('password_updated'));
					}, guid, this.getField("password").value);
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		}
	},
	users: {
		_users: null,
		_channels: null,
		init: function() {
			var _this = this;
			
			this._view = new LT.tableView(LT.$("data_blocks"));
			this._view.init({
				rowTemplate: ' <a title="%full_name%" href="user.htm?u=%name%">%name%%full_name? (%full_name%):%</a> %admin?<img src="/img/boss.png" title="' + LT.L('admin') + '"/>:%',
				itemToString: function(item){
					if (item.toText == undefined)
						item.toText = "n:" + item.name + ",e:" + item.email + ",fn:" + item.full_name + ",j:" + item.job + ",c:" + (item.channels.join != undefined ? item.channels.join(",c:") : "");
					return item.toText;
				},
				searchBox: {
					container: LT.$("users_search"),
					defaultText: LT.L('defaultSearch')
				},
				selectors: {
					"admins": function(){return this.admin},
					"non-admins": function(){return !this.admin}
				},
				onclick: function(item){
					LT.redirect('user.htm?u=' + encodeURI(item.name));
				},
				onfiltered: function(count){
					LT.$("search_summary").innerHTML = LT.subs(LT.L('users_count'), {
						total: LT.s(LT.L('user'), _this._users.length), 
						displayed: LT.s(LT.L('user'), count)
					});
					if (_this._users.length > count) {
						LT.$("search_summary").innerHTML += ' <a href="#" onmousedown="LT.currentPage._view.resetFilters(); return false;">' + LT.L('show_all') + '</a>';
					}
					if (!count)
						LT.$("data_blocks").innerHTML = "<div>" + LT.L('no_users_found') + "</div>";	
				}
			});
			
			LT.$("top_c_list").onchange = function() {
				if (this.selectedIndex != 0)
					_this.addToChannel(this.value);
			}	
			LT.$("bottom_c_list").onchange = LT.$("top_c_list").onchange;
			
			LT.API.getUsers(function(data) {
				_this._users = data.users;
				_this._canAdd = data.canAddUsers;
				_this._view.render(data.users);
				if (LT.params["search"]) {
					_this._view.filter(decodeURI(LT.params["search"]));
					LT.$("users_search").value = decodeURI(LT.params["search"]);
					if (LT.params["select"]) {
						_this._view.select(function(){
							return true
						});
					}
				}
				if (LT.params.add) {
					_this.newUser();
				}
				if (LT.params.import_users) {
					_this.importUsers();
				}
			});
			LT.API.getChannels(function(data) {
				_this._channels = data.channels;
				_this.renderChannels(data.channels);
			});
		},
		addToChannel: function(channel) {
			var target = this._view.getSelection();
			if (!target.length) {
				LT.message.show(LT.L('no_users_selected_error'), 3000);
				LT.$("top_c_list").selectedIndex = 0;
				LT.$("bottom_c_list").selectedIndex = 0;
				return;
			}
			var users = [];
			for (var u in target) {
				users.push(this._users[target[u]].name);
			}
			LT.API.addToChannel(function(data) {
				LT.message.show(LT.s(LT.L('user'), data.count) + LT.L('updated'), 3000);
				LT.$("top_c_list").selectedIndex = 0;
				LT.$("bottom_c_list").selectedIndex = 0;
			}, channel, users);
		},
		deleteUsers: function() {
			var target = this._view.getSelection();
			if (!target.length) {
				LT.message.show(LT.L('no_users_selected_error'), 3000);
				return;
			}
			if (!confirm(LT.s(LT.L('user'), target.length) + LT.L('confirm_delete')))
				return true;
			var users = [], _this = this;
			for (var u in target) {
				users.push(this._users[target[u]].name);
			}
			
			LT.API.deleteUsers(function(data) {
				LT.message.show(LT.s(LT.L('user'), data.count) + LT.L('deleted'), 3000);
				LT.API.getUsers(function(data) {
					_this._users = data.users;
					_this._canAdd = data.canAddUsers;
					_this._view.render(data.users);
				});
			}, users);
		},

		renderChannels: function(clist) {
			var oTop = LT.$("top_c_list");
			var oBottom = LT.$("bottom_c_list");
			var count = 0;
			oTop.innerHTML = "";
			oBottom.innerHTML = "";
			
			oTop.options[count] = new Option(LT.L('select_channel'), "");
			oBottom.options[count] = new Option(LT.L('select_channel'), "");

			for (var c in clist) {
				count++;
				oTop.options[count] = new Option(clist[c].name, clist[c].name);
				oBottom.options[count] = new Option(clist[c].name, clist[c].name);
			}
		},
		importUsers: function() {
			var _this = this;
			if (!this._canAdd) {	
				LT.popup.show({
					title: LT.L('import_users'),
					submitCaption: LT.L('view_upgrade_options'),
					preface: LT.L('reached_users_limit'),
					form: {},
					action: function() {
						this.showError("");
						this.close();
						LT.redirect(LT.isCloud ? "account.htm" : "dashboard.htm");
						return false;
					}
				});
				return;
			}			
			var dialog = {
				title: LT.L('import_users'),
				preface: LT.L('import_intro'),
				afterface: LT.L('import_afterface'),
				submitCaption: LT.L('import_now'),
				form: {
					"separator": {
						caption: "",
						attributes: {type: "separator"}
					},					
					"users_list": {
						caption: LT.L('import_users_list'),
						attributes: {
							type: "textarea", 
							name: "users_list",
							rows: "5",
							value: "",
							title: LT.L('enter_emails')
						},
						init: function() {
							this.onchange = function() {
								var users = _this.parseUsers(this.value);
								var val = "";
								if (users.length) {
									val = "";
									for (var i = 0; i < users.length; i++) {
										if (i > 4) {
											val += '<div>+ ' + LT.s(LT.L('more_user'), users.length - i).toLowerCase() + '</div>';
											break;
										}
										val += '<div class="accept">' + users[i].full_name + ' <a href="mailto:' + users[i].email + '">' + users[i].email + '</a></div>';
							
									}
									LT.popup.showError("");
								} else {
									val = '<div class="error">' + LT.L('import_instructions') + '</div>';
								}
								LT.$("popup_afterface").innerHTML = val;
							}
							this.onkeyup = this.onchange;
							this.onmouseup = this.onchange;
							this.onpaste = this.onchange;
						}
					}
				},
				action: function() {
					var users = _this.parseUsers(this.getField("users_list").value, 10);
					if (users.length < 1) {
						this.showError(LT.L('no_users_import_error'));
						this.getField("users_list").focus();
						return false;
					}
					LT.API.importUsers(function(response){
						LT.message.show(LT.L('import_completed'), 3000);
						LT.API.getUsers(function(data) {
							_this._users = data.users;
							_this._canAdd = data.canAddUsers;
							_this._view.render(data.users);
						});
					}, users);
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},		
		parseUsers: function(text, limit) {
			var out = [];
			if (limit == undefined) limit = 100;
			var re = /(?:["]*([^"<>,;@\r\t\n]*)[\s"<]+)*([\.-_a-zA-Z0-9!$%^&*|\/:~#]+@(?:[-_a-zA-Z0-9\.]+\.)+?[a-zA-Z]{2,4})[>\s,;\r\t\n\.\\\/]*/ig;
			var matches = re.exec(text);
			while (matches) {
				var user = {
					username: /^([\.-_a-zA-Z0-9!$%^&*|\/:~#]+)@/.exec(matches[2])[1],
					full_name: matches[1],
					email: matches[2]
				}
				if (!user.full_name)
					user.full_name = user.username;
					
				out.push(user);
				matches = re.exec(text);
				limit--;
				if (limit <= 0) 
					break;
			}
			return out;
		},
		newUser: function() {
			var _this = this;
			if (!this._canAdd) {	
				LT.popup.show({
					title: LT.L('new_user'),
					submitCaption: LT.L('view_upgrade_options'),
					preface: LT.L('reached_users_limit'),
					form: {},
					action: function() {
						this.showError("");
						this.close();
						LT.redirect(LT.isCloud ? "account.htm" : "dashboard.htm");
						return false;
					}
				});
				return;
			}
			var adminOption = {
				template: LT.L('form_user_is_admin'),
				options: [
					{
						"name": LT.L('is_allowed'),
						"value": "true" 
					},
					{
						"name": LT.L('is_not_allowed'),
						"value": "false" 
					}
				]
			};
			var managerOption = {
				template: LT.L('form_user_access'),
				options: [
					{
						"name": LT.L('can'),
						"value": "false" 
					},
					{
						"name": LT.L('cant'),
						"value": "true" 
					}
				]
			};
			var dialog = {
				title: LT.L('new_user'),
				submitCaption: LT.L('create_new_user'),
				form: {
					"name": {
						caption: LT.L('form_username'),
						attributes: {type: "text", maxlength: 64},
						required: true
					},
					"password": {
						caption: LT.L('form_password'),
						attributes: {type: "password", maxlength: 26},
						required: true
					},
					"password2": {
						caption: LT.L('form_repeat_password'),
						attributes: {type: "password", maxlength: 26},
						required: true
					},
					"full_name": {
						caption: LT.L('form_full_name'),
						attributes: {type: "text", maxlength: 254}
					},
					"job": {
						caption: LT.L('form_position'),
						attributes: {type: "text", maxlength: 254}
					},
					"email": {
						caption: LT.L('form_email'),
						attributes: {type: "text"}
					},
					"separator": {
						caption: "",
						attributes: {type: "separator"}
					},
					"admin": {
						caption: "",
						attributes: {type: "textoption"},
						data: adminOption
					},
					"limited_access": {
						caption: "",
						attributes: {type: "textoption"},
						data: managerOption
					}
				},
				
				action: function() {
					if (this.getField("name").value == "") {
						this.showError(LT.L('empty_username_error'));
						this.getField("name").focus();
						return false;
					}
					if (this.getField("password").value.length < 4) {
						this.showError(LT.L('short_password_error'));
						this.getField("password").focus();
						return false;
					}
					if (this.getField("password").value != this.getField("password2").value) {
						this.showError(LT.L('passwords_match_error'));
						this.getField("password2").focus();
						return false;
					}
					LT.API.saveUser(function(response){
						LT.message.show(LT.L('user_created'), 3000);
						LT.API.getUsers(function(data) {
							_this._users = data.users;
							_this._canAdd = data.canAddUsers;
							_this._view.render(data.users);
						});
					}, {
						name: this.getField("name").value,
						full_name: this.getField("full_name").value,
						job: this.getField("job").value,
						email: this.getField("email").value,
						password: hex_md5(this.getField("password").value),
						limited_access: this.getField("limited_access").value,
						admin: this.getField("admin").value,
						gateway: "false",
						add: "true"
					});
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		}
	},
	user: {
		_user: null,
		_myself: false,
		init: function() {
			LT.$("data").style.visibility = "hidden";
			if (!LT.params.u) {
				LT.message.show(LT.L('unknown_user'));
				return;
			}
			var _this = this;
			LT.API.getUsers(function(data){
				if (data.users && data.users.length == 1) {
					_this._user = data.users[0];
					_this._myself = (LT.username == _this._user.name);
					_this.renderUser(data.users[0]);
					LT.$("data").style.visibility = "visible";
					if (LT.params.change_password) {
						_this.changePassword();
					}
				} else {
					LT.message.show(LT.L('user_not_found'));
				}
			}, decodeURI(LT.params.u));
		},
		renderUser: function(user) {
			LT.$("user_name").innerHTML = user.name;
			document.title = LT.L('serviceName') + LT.L('user_page_title') + user.name;

			LT.$("user_full_name").innerHTML = (user.full_name != undefined && user.full_name != null && user.full_name != '') ? user.full_name : '<span class="empty">&lt;' + LT.L('empty') + '&gt;</span>';
			LT.$("user_job").innerHTML = (user.job != undefined && user.job != null && user.job != '') ? user.job : '<span class="empty">&lt;' + LT.L('empty') + '&gt;</span>';
			
			if (this._myself) {
				LT.$("you_notice").style.display = "block";
				LT.$("user_delete_btn").style.display = "none";
				LT.$("user_web_access").style.display = "none";
			} else {
				LT.$("you_notice").style.display = "none";
				LT.$("user_delete_btn").style.display = "block";
			}
			if (!user.limited_access) {
				LT.$("user_role").innerHTML = LT.L('unrestricted_access');
				LT.$("user_role").title = LT.L('unrestricted_access_title');
				LT.$("user_role").className = "";
			} else {
				LT.$("user_role").innerHTML = LT.L('antiabuse_access');
				LT.$("user_role").title = LT.L('antiabuse_access_title');
				LT.$("user_role").className = "limited_access";	
			}
			if (user.admin) {
				LT.$("user_access").innerHTML = LT.L('web_admin');
				LT.$("user_access").className = "boss";
			} else {
				LT.$("user_access").innerHTML = LT.L('no_web_access');
				LT.$("user_access").className = "";	
			}

			LT.$("user_email").innerHTML = (user.email && user.email != "" && user.email != null) ? '<a href="mailto:' + user.email + '">' + user.email + "</a>" : "<span class='empty'>&lt;" + LT.L('empty') + "&gt;</span>";

			if (!user.channels || (user.channels && user.channels.length == 0)) {
				LT.$("user_channels").innerHTML = LT.L('no_user_channels');
				return;
			} else {
				var out = "<span>" + LT.L('member_of_channels') + "</span><br/>";
				var cc = [];
				for (var c in user.channels) {
					cc.push('<a href="users.htm?search=c:' + user.channels[c] + '">' + user.channels[c] + '</a> <a href="#" onclick="LT.pages.user.removeFrom(\'' + user.channels[c] + '\'); return false;"><img src="/img/delete.png"/></a>');
				}
				LT.$("user_channels").innerHTML = out + cc.join(', ');
			}
		},
		deleteUser: function() {
			if (confirm(LT.L('confirm_user_delete')))
				LT.API.deleteUsers(function(){
					LT.redirect("users.htm");
				}, [this._user.name]);
		},
		switchRole: function() {
			this._user.limited_access = !this._user.limited_access;
			this.saveUser();
		},	
		switchAccess: function() {
			this._user.admin = !this._user.admin;
			this.saveUser();
		},		
		changePassword: function() {
			var _this = this;
			var dialog = {
				title: LT.L('enter_new_password'),
				submitCaption: LT.L('change_password'),
				form: {
					"password": {
						caption: LT.L('form_new_password'),
						attributes: {type: "password"}
					},
					"password2": {
						caption: LT.L('form_repeat_new_password'),
						attributes: {type: "password"}
					}
				},
				action: function() {
					if (this.getField("password").value.length < 4) {
						this.showError(LT.L('short_password_error'));
						this.getField("password").focus();
						return false;
					}
					if (this.getField("password2").value != this.getField("password").value) {
						this.showError(LT.L('passwords_match_error'));
						this.getField("password2").focus();
						return false;
					}
					_this._user.password = hex_md5(this.getField("password").value);
					_this.saveUser();
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},	
		editEmail: function() {
			var _this = this;
			var dialog = {
				title: LT.L('enter_new_email'),
				submitCaption: LT.L('update_email'),
				form: {
					"email": {
						caption: LT.L('form_email'),
						attributes: {type: "text", value: _this._user.email}
					}
				},
				action: function() {
					if (this.getField("email").value.length < 6) {
						this.showError(LT.L('email_validation_email'));
						this.getField("email").focus();
						return false;
					}
					_this._user.email = this.getField("email").value;
					_this.saveUser();
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},
		editFullName: function() {
			var _this = this;
			var full_name = (_this._user.full_name != null) ? _this._user.full_name : '';
			var dialog = {
				title: LT.L('enter_new_alias'),
				submitCaption: LT.L('update_name'),
				form: {
					"full_name": {
						caption: LT.L('form_full_name'),
						attributes: {type: "text", value: full_name}
					}
				},
				action: function() {
					_this._user.full_name = (this.getField("full_name").value != null) ? this.getField("full_name").value : '';
					_this.saveUser();
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},					
		editJob: function() {
			var _this = this;
			var job = (_this._user.job != null) ? _this._user.job : '';
			var dialog = {
				title: LT.L('enter_new_position'),
				submitCaption: LT.L('update_position'),
				form: {
					"job": {
						caption: LT.L('form_position'),
						attributes: {type: "text", value: job}
					}
				},
				action: function() {
					_this._user.job = this.getField("job").value;
					_this.saveUser();
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},					
		removeFrom: function(channel) {
			var _this = this;
			LT.API.removeFromChannel(function(){
				_this.init();
			}, channel, [this._user.name]);
		},		
		assignToChannel: function(channel) {
			//LT.redirect("users.htm?search=n:" + this._user.name + "&select=true");
			var _this = this;
			if (channel == undefined) {
				var dialog = {
					title: LT.L('assign_to_channel'),
					submitCaption: LT.L('assign_to_channel_action'),
					form: {
						"channel": {
							caption: LT.L('form_channel'),
							attributes: {
								type: "select",
								style: "width: 150px"
							},
							init: function() { // load options dynamically
								var __this = this;
								LT.API.getChannels(function(data) {
									var count = 0;
									__this.innerHTML = "";
									for (var c in data.channels) {
										__this.options[count] = new Option(data.channels[c].name, data.channels[c].name);
										count++;
									}
								});
							}
						}
					},
					action: function(){
						_this.assignToChannel(this.getField("channel").value);
						this.showError("");
						this.close();
						return false;
					}
				};
				LT.popup.show(dialog);
			} else {
				var users = [];
				users.push(this._user.name);
				LT.API.addToChannel(function(data) {
					LT.message.show(LT.s("user", data.count) + LT.L('updated'), 3000);
					if (data.count)
						_this._user.channels.push(channel);
					_this.renderUser(_this._user);
				}, channel, users);	
			}
		},
		saveUser: function() {
			var _this = this;
			LT.API.saveUser(function(data){
				LT.message.show(LT.L('user_updated'));
				_this.renderUser(_this._user);
			}, this._user);
		}
	},
	gateways: {
		_users: null,
		_channels: null,
		init: function() {
			var _this = this;
			
			this._view = new LT.tableView(LT.$("data_blocks"));
			this._view.init({
				rowTemplate: ' <a title="%full_name%" href="gateway.htm?u=%name%">%name%%full_name? (%full_name%):%</a><img src="/img/radio.png"/>',
				emptyText: function(){
					return (LT.pages.gateways._canAdd ? 
							LT.L('no_gateways_configured') : 
							LT.L('gateways_not_allowed'))
				},
				onclick: function(item){
					LT.redirect('gateway.htm?u=' + encodeURI(item.name));
				}
			});
			
			LT.API.getChannels(function(data) {
				_this._channels = data.channels;
				_this.renderChannels(data.channels);
			});
			LT.$("top_c_list").onchange = function() {
				if (this.selectedIndex != 0)
					_this.addToChannel(this.value);
				
			}
			LT.$("bottom_c_list").onchange = LT.$("top_c_list").onchange;
			if (LT.params.add) {
				this.newGateway();
			}

			LT.API.getUsers(function(data) {
				_this._users = data.users;
				_this._canAdd = data.canAddGateways;
				_this._view.render(data.users);
			}, 0, true);			
		},
		addToChannel: function(channel) {
			var target = this._view.getSelection();
			if (!target.length) {
				LT.message.show(LT.L('no_gateways_selected'), 3000);
				LT.$("top_c_list").selectedIndex = 0;
				LT.$("bottom_c_list").selectedIndex = 0;
				return;
			}
			var users = [];
			for (var u in target) {
				users.push(this._users[target[u]].name);
			}
			LT.API.addToChannel(function(data) {
				LT.message.show(LT.s("gateway", data.count) + LT.L('updated'), 3000);
				LT.$("top_c_list").selectedIndex = 0;
				LT.$("bottom_c_list").selectedIndex = 0;
			}, channel, users);
		},
		deleteGateways: function() {
			var target = this._view.getSelection();
			if (!target.length) {
				LT.message.show(LT.L('no_gateways_selected'), 3000);
				return;
			}
			if (!confirm(LT.s(LT.L('gateway'), target.length) + LT.L('confirm_delete')))
				return true;
			var users = [], _this = this;
			for (var u in target) {
				users.push(this._users[target[u]].name);
			}
			
			LT.API.deleteUsers(function(data) {
				LT.message.show(LT.s(LT.L('gateway'), data.count) + LT.L('deleted'), 3000);
				// to do: handle the case, when not all users were deleted
				for (var u in target) {
					delete _this._users[target[u]];
				}
				_this._view.render(_this._users);
			}, users);
		},

		renderChannels: function(clist) {
			var oTop = LT.$("top_c_list");
			var oBottom = LT.$("bottom_c_list");
			var count = 0;
			oTop.innerHTML = "";
			oBottom.innerHTML = "";
			
			oTop.options[count] = new Option(LT.L('select_channel'), "");
			oBottom.options[count] = new Option(LT.L('select_channel'), "");

			for (var c in clist) {
				count++;
				oTop.options[count] = new Option(clist[c].name, clist[c].name);
				oBottom.options[count] = new Option(clist[c].name, clist[c].name);
			}
		},
		newGateway: function() {
			var _this = this;
			if (!this._canAdd) {	
				LT.popup.show({
					title: LT.L('new_gateway'),
					submitCaption: LT.L('view_upgrade_options'),
					preface: LT.L('reached_gateways_limit'),
					form: {},
					action: function() {
						this.showError("");
						this.close();
						LT.redirect(LT.isCloud ? "account.htm" : "dashboard.htm");
						return false;
					}
				});
				return;
			}
			var dialog = {
				title: LT.L('new_gateway'),
				submitCaption: LT.L('create_new_gateway'),
				form: {
					"name": {
						caption: LT.L('form_login'),
						attributes: {type: "text", maxlength: 64},
						required: true
					},
					"password": {
						caption: LT.L('form_password'),
						attributes: {type: "password", maxlength: 26},
						required: true
					},
					"password2": {
						caption: LT.L('form_repeat_password'),
						attributes: {type: "password", maxlength: 26},
						required: true
					},
					"full_name": {
						caption: LT.L('form_alias'),
						attributes: {type: "text", maxlength: 254}
					}
				},
				
				action: function() {
					if (this.getField("name").value == "") {
						this.showError(LT.L('empty_login_error'));
						this.getField("name").focus();
						return false;
					}
					if (this.getField("password").value.length < 4) {
						this.showError(LT.L('short_password_error'));
						this.getField("password").focus();
						return false;
					}
					if (this.getField("password").value != this.getField("password2").value) {
						this.showError(LT.L('passwords_match_error'));
						this.getField("password2").focus();
						return false;
					}
					LT.API.saveUser(function(response){
						LT.message.show(LT.L('gateway_created'), 3000);
						LT.API.getUsers(function(data) {
							_this._users = data.users;
							_this._canAdd = data.canAddGateways;
							_this._view.render(data.users);
						}, 0, true);
					}, {
						name: this.getField("name").value,
						full_name: this.getField("full_name").value,
						job: '',
						email: '',
						password: hex_md5(this.getField("password").value),
						limited_access: "false",
						admin: "false",
						gateway: "true",
						add: "true"
					});
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		}
	},	
	gateway: {
		_user: null,
		_myself: false,
		init: function() {
			LT.$("data").style.visibility = "hidden";
			if (!LT.params.u) {
				LT.message.show(LT.L('unknown_gateway'));
				return;
			}
			var _this = this;
			LT.API.getUsers(function(data){
				if (data.users && data.users.length == 1) {
					_this._user = data.users[0];
					_this._user.gateway = true;
					_this._myself = (LT.username == _this._user.name);
					_this.renderUser(data.users[0]);
					LT.$("data").style.visibility = "visible";
					if (LT.params.change_password) {
						_this.changePassword();
					}
				} else {
					LT.message.show(LT.L('gateway_not_found'));
				}
			}, decodeURI(LT.params.u), true);
		},
		renderUser: function(user) {
			LT.$("user_name").innerHTML = user.name;
			document.title = LT.L('user_page_title') + user.name;

			LT.$("user_full_name").innerHTML = (user.full_name != undefined && user.full_name != null && user.full_name != '') ? user.full_name : '<span class="empty">&lt;Empty&gt;</span>';

			if (this._myself) {
				LT.$("user_delete_btn").style.display = "none";
			} else {
				LT.$("user_delete_btn").style.display = "block";
			}
			if (!user.channels || (user.channels && user.channels.length == 0)) {
				LT.$("user_channels").innerHTML = LT.L('gateway_not_linked');
				return;
			} else {
				var out = "<span>" + LT.L('linked_to') + "</span><br/>";
				var cc = [];
				for (var c in user.channels) {
					cc.push('<a href="users.htm?search=c:' + user.channels[c] + '">' + user.channels[c] + '</a> <a href="#" onclick="LT.pages.gateway.removeFrom(\'' + user.channels[c] + '\'); return false;"><img src="/img/delete.png"/></a>');
				}
				LT.$("user_channels").innerHTML = out + cc.join(', ');
			}
		},
		deleteGateway: function() {
			if (confirm(LT.L('confirm_delete_gateway')))
				LT.API.deleteUsers(function(){
					LT.redirect("gateways.htm");
				}, [this._user.name]);
		},
		changePassword: function() {
			var _this = this;
			var dialog = {
				title: LT.L('enter_new_password'),
				submitCaption: LT.L('change_password'),
				form: {
					"password": {
						caption: LT.L('form_new_password'),
						attributes: {type: "password"}
					},
					"password2": {
						caption: LT.L('form_repeat_new_password'),
						attributes: {type: "password"}
					}
				},
				action: function() {
					if (this.getField("password").value.length < 4) {
						this.showError(LT.L('short_password_error'));
						this.getField("password").focus();
						return false;
					}
					if (this.getField("password2").value != this.getField("password").value) {
						this.showError(LT.L('passwords_match_error'));
						this.getField("password2").focus();
						return false;
					}
					_this._user.password = hex_md5(this.getField("password").value);
					_this.saveUser();
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},	

		editFullName: function() {
			var _this = this;
			var full_name = (_this._user.full_name != null) ? _this._user.full_name : '';
			var dialog = {
				title: LT.L('enter_new_alias'),
				submitCaption: LT.L('update_gateway_alias'),
				form: {
					"full_name": {
						caption: LT.L('form_alias'),
						attributes: {type: "text", value: full_name}
					}
				},
				action: function() {
					_this._user.full_name = (this.getField("full_name").value != null) ? this.getField("full_name").value : '';
					_this.saveUser();
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},							
		removeFrom: function(channel) {
			var _this = this;
			LT.API.removeFromChannel(function(){
				_this.init();
			}, channel, [this._user.name]);
		},		
		assignToChannel: function(channel) {
			var _this = this;
			if (channel == undefined) {
				var dialog = {
					title: LT.L('assign_to_channel'),
					submitCaption: LT.L('assign_to_channel_action'),
					form: {
						"channel": {
							caption: LT.L('form_channel'),
							attributes: {
								type: "select",
								style: "width: 150px"
							},
							init: function() { // load options dynamically
								var __this = this;
								LT.API.getChannels(function(data) {
									var count = 0;
									__this.innerHTML = "";
									for (var c in data.channels) {
										__this.options[count] = new Option(data.channels[c].name, data.channels[c].name);
										count++;
									}
								});
							}
						}
					},
					action: function(){
						_this.assignToChannel(this.getField("channel").value);
						this.showError("");
						this.close();
						return false;
					}
				};
				LT.popup.show(dialog);
			} else {
				var users = [];
				users.push(this._user.name);
				LT.API.addToChannel(function(data) {
					LT.message.show(LT.s(LT.L('gateway'), data.count) + LT.L('updated'), 3000);
					if (data.count)
						_this._user.channels.push(channel);
					_this.renderUser(_this._user);
				}, channel, users);	
			}
		},
		saveUser: function() {
			var _this = this;
			LT.API.saveUser(function(data){
				LT.message.show(LT.L('gateway_updated'));
				_this.renderUser(_this._user);
			}, this._user);
		}
	},
	channels: {
		init: function() {
			var _this = this;

			this._view = new LT.tableView(LT.$("data_blocks"));
			this._view.init({
				rowTemplate: ' <a href="users.htm?search=c:%name%">%name%</a> <span style="padding-right:20px">%count%</span>%is_shared?<img src="/img/group_channel.png" title="Group channel"/>:<img src="/img/dynamic_channel.png" title="Dynamic channel"/>%',
				emptyText: 'No channels is configured',
				itemToString: function(item){
					return item.name;
				},
				searchBox: {
					container: LT.$("channels_search"),
					defaultText: LT.L('channelsDefaultSearch')
				},
				onclick: function(item){
					LT.redirect('users.htm?search=c:' + encodeURI(item.name));
				},
				onfiltered: function(count){
					LT.$("search_summary").innerHTML = LT.subs(LT.L('channels_count'), {
						total: LT.s(LT.L('channel', _this._channels.length)), 
						displayed: LT.s(LT.L('channel', count))
					});
					if (!count)
						LT.$("data_blocks").innerHTML = "<div>" + LT.L('no_channels_found') + "</div>";
				}
			});
			LT.API.getChannels(function(data) {
				_this._channels = data.channels;
				_this._view.render(data.channels);
			});
		},
		newChannel: function() {
			var _this = this;
			var channelTypes = {
				template: LT.L('form_channel_type'),
				options: [
					{
						"name": LT.L('group_channel'),
						"value": 1 
					},
					{
						"name": LT.L('dynamic_channel'),
						"value": 0 
					}
				]
			};
			var dialog = {
				title: LT.L('new_channel'),
				submitCaption: LT.L('create_new_channel'),
				form: {
					"name": {
						caption: LT.L('form_name'),
						attributes: {type: "text"}
					},
					"is_shared": {
						caption: "",
						attributes: {type: "textoption"},
						data: channelTypes
					}
				},
				action: function() {
					if (this.getField("name").value == "") {
						this.showError(LT.L('channel_name_empty_error'));
						this.getField("name").focus();
						return false;
					}
					LT.API.addChannel(function(response){
						LT.message.show(LT.L('channel_created'), 3000);
						LT.API.getChannels(function(data) {
							_this._channels = data.channels;
							_this._view.render(data.channels);
						});
					}, this.getField("name").value, this.getField("is_shared").value);
					this.showError("");
					this.close();
					return false;
				}
			};
			LT.popup.show(dialog);
		},
		deleteChannels: function() {
			var target = this._view.getSelection();
			if (!target.length) {
				LT.message.show(LT.L('no_channels_selected'), 3000);
				return;
			}
			if (!confirm(LT.s(LT.L('channel'), target.length) + LT.L('confirm_delete')))
				return true;
			var channels = [], _this = this;
			for (var c in target) {
				channels.push(this._channels[target[c]].name);
			}
			
			LT.API.deleteChannels(function(data) {
				LT.message.show(LT.s(LT.L('channel'), data.count) + LT.L('deleted'), 3000);
				for (var c in target) {
					delete _this._channels[target[c]];
				}
				_this._view.render(_this._channels);
			}, channels);
		}	
	},
	settings: {
		init: function() {
			var _this = this;
			LT.API.getClientSettings(function(data){
				_this.loadSettings(data.settings);
			});
		},
		loadSettings: function(settings) {
			this._settings = settings;
			this.matchPresets();
			this.renderPlatforms(this._settings.platforms);
			this.triggerPlatform(this._settings.platforms[0]);
			
		},
		renderPlatforms: function(platforms) {
			var out = '';
			for (var i = 0; i < platforms.length; i++) {
				out += LT.subs('<a id="platform_%platform%" \
				onclick="LT.currentPage.triggerPlatform(\'%platform%\'); return false;" href="#">' + LT.L(platforms[i]) + '</a>', {
					platform: platforms[i]
				});
			}
			LT.$("platform_selector").innerHTML = out;
		},
		renderSettings: function(values) {
			var groups = {};
			for (var vi in values) {
				// The setting is not for this platfrom -- ignore
				if (values[vi].platforms && !LT.inArray(this._platform, values[vi].platforms))
					continue;				
				values[vi].id = vi;

				if (values[vi].data && (typeof values[vi].data[this._platform] != "undefined")) {
					values[vi].value = values[vi].data[this._platform];
					values[vi].enabled = true;
				} else {
					values[vi].enabled = false;
					if (values[vi].defaults && (typeof values[vi].defaults[this._platform] != "undefined")){
						values[vi].value = values[vi].defaults[this._platform];
					}
				}
				values[vi].has_value = typeof values[vi].value != "undefined";
				if (!groups[values[vi].group]){
					groups[values[vi].group] = [];
				}
				groups[values[vi].group].push(values[vi]);
			}
			var out = ''
			for (var gi in groups) {
				out += this.renderSettingsGroup(gi, groups[gi]);
			}
			LT.$("platform_settings").innerHTML = out;
		},
		renderSettingsGroup: function(groupId, group) {
			var out = '';
			out += '<h2>' + LT.L(groupId) + '</h2>';
			if (this._settings.presets[groupId]) {
				out += this.renderGroupPresets(groupId, this._settings.presets[groupId]);
			}
			for (var si in group) {
				out += this.renderSetting(group[si]);
			}
			return out;
		},
		renderGroupPresets: function(group, presets) {
			var out = '';
			if (presets.description) {
				out += '<div class="p">' + LT.L(presets.description) + '</div>';
			}
			out += '<div class="p filters">' + LT.L('presets') + ': '
			for (var i in presets.data) {
				presets.data[i].id = i;
				out += LT.subs('<a id="preset_link_%id%" href="#" %selected?class="active":% onclick="LT.currentPage.applyPreset(\'' + group + '\', \'%id%\'); return false;">' + LT.L(presets.data[i].id)+ '</a> ', presets.data[i]);
			}
			out += '</div>';
			return out;
		},
		applyPreset: function(group, pid) {
			if (this._settings.presets[group] && this._settings.presets[group].data[pid]) {

				var vals = this._settings.presets[group].data[pid].values;
				for (var vi in vals) {
					if (typeof this._settings.values[vi].data == "undefined") {
						this._settings.values[vi].data = {};
					}
					this._settings.values[vi].data[this._platform] = vals[vi];
				}
				this.matchPresets();
				this.renderSettings(this._settings.values);
			}
		},
		matchPresets: function() {
			var somethingChanged = false;
			for (var i in this._settings.presets) {
				for (var pid in this._settings.presets[i].data) {
					var vals = this._settings.presets[i].data[pid].values;
					var match = true;
					for (var ii in vals) {
						if (!this._settings.values[ii].data) {
							match = false;
							break;
						}
						if (typeof this._settings.values[ii].data[this._platform] == "undefined") {
							match = false;
							break;
						}
						if (this._settings.values[ii].data[this._platform] != vals[ii]) {
							match = false;
							break;
						}						
					}
					somethingChanged = somethingChanged || 
					(
						typeof this._settings.presets[i].data[pid].selected != "undefined" && 
						this._settings.presets[i].data[pid].selected != match
					) || (
						typeof this._settings.presets[i].data[pid].selected == "undefined" && 
						match
					);
					this._settings.presets[i].data[pid].selected = match;
					LT.$("preset_link_" + pid).className = match ? "active" : "";
				}
			}
			return somethingChanged;
		},
		renderSetting: function(setting) {
			var out = '<div  class="p">';	
			out += LT.subs('<div class="setting_enabler">\
			<input id="setting_enabler_%id%" type="checkbox" %enabled?checked:% onclick="LT.currentPage.enableSetting(\'%id%\', this);"/>\
			 ' + (LT.locale[setting.id] || setting.name) + '</div>', setting);
			out += '<div class="setting_edit">'; 
			switch (setting.type) {
				case "enum":
					out += 
		LT.subs('<select id="setting_edit_%id%" onblur="LT.currentPage.applySetting(\'%id%\', this);" value="%has_value?%value%:%" %enabled?:disabled%>', setting);
					for (var oi in setting.options) {
						out += '<option value="' + oi + '"';
						if (setting.value == oi) {
							out += ' selected';
						}
						out +='>' + LT.L(setting.options[oi]) + '</option>';
					}
					out += '</select>';
					break;
				case "bool":
					out += 
		LT.subs('<input id="setting_edit_%id%"  onclick="LT.currentPage.applySetting(\'%id%\', this);" type="checkbox" %value?checked:% %enabled?:disabled%>', setting);
					break;	
				case "int":
					//break;
					
				default:
					out += 
		LT.subs('<input id="setting_edit_%id%"  onblur="LT.currentPage.applySetting(\'%id%\', this);" type="text" value="%has_value?%value%:%" %enabled?:disabled% title="%min?%min%:% ... %max?%max%:% "/>', setting);
			}
			out += '</div><div style="clear:both;"></div></div>';
			return out;
		},
		applySetting: function(id, sender) {
			var setting = this._settings.values[id];
			switch (setting.type) {
				case "enum":
					setting.data[this._platform] = sender.value;
					break;
				case "bool":
					setting.data[this._platform] = sender.checked;
					break;
				case "int":
					var val = parseInt(sender.value);
					if (isNaN(val)) {
						val = setting.defaults[this._platform];
					}
					if (val > setting.max){
						val = setting.max;
					}	
					if (val < setting.min){
						val = setting.min;
					}			
					sender.value = val;
				default:
					setting.data[this._platform] = sender.value;
			}
			//? watch the performance here
			this.matchPresets();
		},
		enableSetting: function(id, sender) {
			LT.$("setting_edit_" + id).disabled = !sender.checked;
			if (sender.checked) {
				if (!this._settings.values[id].data) {
					this._settings.values[id].data = {};
				}
				this.applySetting(id, LT.$("setting_edit_" + id));
			} else {
				delete this._settings.values[id].data[this._platform];
			}
			this.matchPresets();
		},
		triggerPlatform: function(platform) {
			var ch = LT.$("platform_selector").childNodes;
			for (var i = 0; i < ch.length; i++) {
				if (ch[i].id == ("platform_" + platform)) {
					ch[i].className = "active_menu";
				} else {
					ch[i].className = "";
				}
			}
			this._platform = platform;
			this.renderSettings(this._settings.values);
			this.matchPresets();
		},
		applySettings: function() {

			var values = this._settings.values;
			var out = {};
			for (var vi in values) {
				if (values[vi].data) {
					out[vi] = values[vi].data;
				}
			}
			LT.API.saveClientSettings(function(data){
				LT.message.show(LT.L('new_settings_applied'), 5000);
			}, out);
		}
	},
	downloads: {
		init: function() {
			var _this = this;
			LT.API.getDownloads(function(data){
				_this.displayDownloads(data);
			});
		},
		compareVersionStrings: function(a, b) {
			var pA = a.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
			var pB = b.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
			for (var i = 1; i < pA.length; i++) {
				if (parseInt(pA[i], 10) > parseInt(pB[i],10)){
					return 1;
				}
				if (parseInt(pA[i], 10) < parseInt(pB[i],10)){
					return -1;
				}
			}
			return 0;
		},
		displayDownloads: function(data){
			var out = '', i, suffix = '', versions;
			if (data.desktop.archive){
				out += '<h3>Desktop</h3>';
				versions = data.desktop.archive;
				versions.sort(LT.currentPage.compareVersionStrings);
				versions.reverse();
				for (i in versions){
					switch (this.compareVersionStrings(versions[i], data.desktop.current)){
						case 0:
							suffix = 'current';
							break;
						case 1:
							suffix = 'pre-release';
							break;
						case -1:
							suffix = '';
							break;
					}
					
					out += '<p><a class="package" href="/download/' + versions[i] + '/' + data.names.desktop +'">Download ' + data.names.desktop + ' (version ' + versions[i] + ')</a> ' + suffix + '</p>';
				}
			}
			if (data.mobile.archive){
				out += '<h3>Mobile</h3>';
				versions = data.mobile.archive;
				versions.sort(LT.currentPage.compareVersionStrings);
				versions.reverse();
				for (i in versions){
					switch (this.compareVersionStrings(versions[i], data.mobile.current)){
						case 0:
							suffix = 'current';
							break;
						case 1:
							suffix = 'pre-release';
							break;
						case -1:
							suffix = '';
							break;
					}					
					out += '<p><a class="package" href="/download/' + versions[i] + '/' + data.names.mobile +'">Download ' + data.names.mobile + ' (version ' + versions[i] + ')</a> ' + suffix + '</p>';
				}
			}
			if (out === '') {
				out = 'No files are available';
			}
			LT.$("releases_list").innerHTML = out;
		}
	},
	dashboard: {
		_state: null,
		init: function() {
			var _this = this;
			LT.$("change_password_link").href = "user.htm?u=" + LT.username + "&change_password=1";
			LT.API.getSystemStatus(function(data) {
				_this._state = data.state;
				_this.displayStatus(data.state);
			});
		},
		displayStatus: function(state) {
			if (state.client_version) {
				LT.$("release_notes_link").href = LT.L('release_notes_base') + state.client_version + ".txt";
			}
			if (LT.isCloud) {
				if (LT.billingType != "none") {
					if (state.network.price == "0") {
						LT.$("system_account_type").innerHTML = LT.L('using_free_account') + ' <a href="account.htm">' + LT.L('details') + '</a>';
					} else {
						LT.$("system_account_type").innerHTML = LT.L('using_premium_account') + ' <a href="account.htm">' + LT.L('details') + '</a>';
						
						if (state.network.is_beta) {
							LT.$("system_account_type").innerHTML += "<br/>(" + LT.L('free_beta_normally') + " " + LT.render.core.price(state.network.price) + ")";
						} else {
							LT.$("system_account_type").innerHTML += "<br/>(" + LT.L('subscription_rate_is') + " " + LT.render.core.price(state.network.price) + ")";
						}
						
					}
				} else {
					LT.$("system_account_type").innerHTML = LT.subs('%form_service_plan% <b>%service_name%</b> (%service_description%) <a href="account.htm">%details%</a>', state.network);
				}
				if (!state.network.enabled) {
					var accountDisabledMessage = LT.subs(LT.L('account_disabled'), {});
					LT.$("system_account_type").innerHTML += '<br/><br/><span class="error">' + accountDisabledMessage + '</span>';
					LT.message.show(accountDisabledMessage);
				}
			}
			if (state.network.api_key) {
				LT.$('api_status').innerHTML = LT.L("api_key") + ": " + state.network.api_key;
			} else {
				LT.$('api_status').innerHTML = LT.L('api_disabled') + ' <a href="#" onclick="LT.currentPage.getAPIkey(); return false;">' + LT.L('get_api_key') + '</a><br/>';
			}
			if (LT.domain == "loudtalks.net") {
				LT.$('api_status').innerHTML += '<a class="info" href="http://loudtalks.com/api/" target="_blank">' + LT.L('about_api') + '</a>';
			}
			if (state.network.usersNumber == 1) {
				LT.$("network_state").innerHTML = LT.L('the_only_user');
			} else {
				LT.$("network_state").innerHTML = LT.subs(LT.L('network_summary'), {
					users_count: 	'<a href="users.htm">' + 
									LT.s(LT.L('user'), state.network.usersNumber).toLowerCase() + 
									'</a>',
					channels_count: '<a href="channels.htm">' + 
									LT.s(LT.L('channel'), state.network.channelsNumber).toLowerCase() + 
									'</a>',
					gateways_count: '<a href="gateways.htm">' + 
									LT.s(LT.L('gateway'), state.network.gatewaysNumber).toLowerCase() + 
									'</a>'
				});

				LT.$("network_state").innerHTML += '<br/><br/><a href="users.htm?add=1" class="add">' + LT.L('add_new_user') + '</a>';
			}

			if (state.server == "OK" && state.supernode == "OK") {
				LT.$("system_state").innerHTML = LT.L('system_running');
				LT.$("system_state").className = "accept";
			} else {
				LT.$("system_state").innerHTML = "Server: " + state.server + ", supernode: " + state.supernode;
				LT.$("system_state").className = "error";
			}
			var clientVerText;
			if (state.client_version) {
				clientVerText = LT.L('current_client_version') + 
							" <b>" + state.client_version + "</b> (" + LT.L("desktop") + ")," +
							" <b>" + state.mobile_client_version + "</b> (" + LT.L("mobile") + ")";
			} else {
				clientVerText = LT.L('client_is_not_uploaded');
			}
			
			LT.$("system_info").innerHTML = clientVerText;
		},
		getAPIkey: function() {
			LT.API.requestAPIkey(function(data){
				LT.$('api_status').innerHTML = LT.L("api_key") + ": " + data.api_key;
				if (LT.domain == "loudtalks.net") {
					LT.$('api_status').innerHTML += '<a class="info" href="http://loudtalks.com/api/" target="_blank">' + LT.L('about_api') + '</a>';
				}
			});
		},
		sendFeedback: function(text) {
			if (text == "") {
				LT.message.show(LT.L('empty_message_error'));
				return;
			}
			if (LT.$("feedback_text").disabled) {
				LT.message.show(LT.L('busy_sending_error'));
				return;
			}
			LT.$("feedback_text").disabled = true;
			LT.API.sendFeedback(function(){
				LT.message.show(LT.L('support_message_sent'));
				LT.$("feedback_text").value = "";
				LT.$("feedback_text").disabled = false;
			}, LT.createUrlString(this._state) + "text=" + text);
		}
	}
});

LT.popup  = {
	_data: null,
	show: function(data) {
		var selects = document.getElementsByTagName("select");
		for (var i=0; i < selects.length; i++) {
			selects[i].style.visibility = "hidden";
		}
		LT.$("popup_name").innerHTML = data.title; //popup_submit
		LT.$("popup_submit").value = data.submitCaption;
		var out = "", _this = this;
		if (data.preface)
			out += '<div id="popup_preface">' + data.preface + '</div>';
		// Build form HTML
		for (var i in data.form) {
			data.form[i].attributes.id = 'popup_form_field_' + i;
			var reqClass = (data.form[i].required != undefined && data.form[i].required == true) ? 'required' : '';
			switch (data.form[i].attributes.type) {
				case "textarea":
					out += '<div style=\"height:auto;\" class="' + reqClass + '"><div class="form_label no-float">';
					out += data.form[i].caption;
					out += "</div><textarea ";
					for (var j in data.form[i].attributes) {
						if (j == "type") continue;
						out += j + '="' + data.form[i].attributes[j] + '" ';
					}
					out += ">";
					if (data.form[i].attributes.value)
						out += data.form[i].attributes.value;
					out += "</textarea></div>";
					break;
				case "select":
					out += '<div class="' + reqClass + '"><div class="form_label">';
					out += data.form[i].caption;
					out += "</div><select ";
					for (var j in data.form[i].attributes) {
						out += j + '="' + data.form[i].attributes[j] + '" ';
					}
					out += "/></select>";
					break;	
				case "textoption":
					out += '<div><div class="form_label no-float" id="_' + data.form[i].attributes.id + '">';
					out += '</div><input type="hidden" id="' + data.form[i].attributes.id + '" /></div>';
					break;	
				case "separator":
					out += '<div class="form_separator"></div>';
					break;	
				default:
					out += '<div class="' + reqClass + '"><div class="form_label">';
					out += data.form[i].caption;
					out += "</div>";
					out += LT.render.core.input(data.form[i].attributes);
					out += "</div>";
			}
		}
		if (data.afterface)
			out += '<div id="popup_afterface">' + data.afterface + '</div>';
		LT.$("popup_contents").innerHTML = out;
		

		for (var i in data.form) {
			switch (data.form[i].attributes.type) {

				case "textoption":
					var d = data.form[i].data;
					var inputEl = LT.$(data.form[i].attributes.id);
					var placeholderEl = LT.$('_' + data.form[i].attributes.id);
					this.createTextOption(inputEl, placeholderEl, d);
					break;	
				default:

			}
		}
		
		LT.$("popup_form").onsubmit = function() {
			return _this._data.action.call(_this);
		}
		// Call form fields init functions, where present, pass input element as this
		for (var i in data.form) {
			if (data.form[i].init) {
				data.form[i].init.call(LT.$(data.form[i].attributes.id));
			}
		}
		
		this._data = data;
				
		LT.$show("popup");
		LT.$show("cover");
		LT.$("cover").style.height = LT.$("wrapper").offsetHeight + "px";
		
		LT.$("popup").style.top = Math.max(0, (Math.min(LT.$("wrapper").offsetHeight, document.body.offsetHeight) - LT.$("popup").offsetHeight)/2) + "px";

		LT.$("popup_form").elements[0].focus();
	},
	createTextOption: function(inputElement, parentElement, data) {
		data.inputElementId = inputElement.id;
		inputElement.value = data.options[0].value;
		opTxt = '<a href="#" title="' + LT.L('click_to_change') + '">' + data.options[0].name + '</a>';
		parentElement.innerHTML = data.template.replace("%%", opTxt);
		var links = parentElement.getElementsByTagName("a");
		var _this = this;
		if (links) {
			links[0].onclick = function() {
				_this.switchTextOption(data, this);
				return false;
			}
		}
	},
	switchTextOption: function(data, el) {
		var nextIndex = 0;
		for (var i = 0; i < data.options.length; i++) {
			if (data.options[i].name == el.innerHTML) { //found current value
				if (i < data.options.length-1) {
					nextIndex = i + 1; 
				} //else 0, which is default
				break;
			}
		}
		LT.$(data.inputElementId).value = data.options[nextIndex].value;
		el.innerHTML = data.options[nextIndex].name;
	},
	getForm: function() {
		return LT.$("popup_form");
	},
	getField: function(name) {
		return LT.$(this._data.form[name].attributes.id);
	},
	showError: function(message) {
		if (message != "")
			LT.$show("popup_error");
		else 
			LT.$hide("popup_error");
		LT.$("popup_error").innerHTML = message;
		
	},
	close: function() {
		LT.$hide("popup");
		LT.$hide("cover");
		this.showError("");
		var selects = document.getElementsByTagName("select");
		for (var i=0; i < selects.length; i++) {
			selects[i].style.visibility = "visible";
		}
	}
}
LT.API = LT.API || {};
LT.merge(LT.API, {
	login: function(user, pass) {
		LT.username = user;
		LT.progress.start(LT.L('login_phase_1')); //phase I
		LT.loader.sendRequest(LT.baseUrl + "user/gettoken", "", 
			function(res) {

				LT.progress.start(LT.L('login_phase_2')); //phase II
				LT.loader.sendRequest(LT.baseUrl + "user/login", 
					"username=" + user + "&password=" + hex_md5(hex_md5(pass) + res.token + LT.nonce),
					function(data){
						LT.cookie.save("username", LT.username, 1);
						LT.redirect(LT.params.from != undefined ? LT.params.from : "dashboard.htm");
		
					});	
	
			});

	},
	logout: function() {
		LT.progress.start(LT.L('logging_out'));
		LT.loader.sendRequest(LT.baseUrl + "user/logout", "",
			function(data){
				LT.username = "";
				LT.cookie.clear("username");
				LT.redirect("index.htm");
			});
	},
	getUsers: function(callback, username, is_gateway, maxusers, startuser) {
		LT.progress.start(LT.L('loading_users'));
		var req = LT.baseUrl + "user/get";
		if (username) {
			req += "/login/" + encodeURI(username); 
		}
		if (is_gateway) {
			req += "/gateway/true"; 
		}
		if (maxusers) {
			req += "/max/" + maxusers;
		}
		if (startuser) {
			req += "/start/" + startuser;
		}
		LT.loader.sendRequest(req, "",
			callback);
	},
	getChannels: function(callback, name, maxresults, startresult) {
		LT.progress.start(LT.L('loading_channels'));
		var req = LT.baseUrl + "channel/get";
		if (name) {
			req += "/name/" + encodeURI(name);
		}
		if (maxresults) {
			req += "/max/" + maxresults;
		}
		if (startresult) {
			req += "/start/" + startresult;
		}
		LT.loader.sendRequest(req, "",
			callback);
	},
	addToChannel: function(callback, where, who) {
		if (who.length > 1)
			LT.progress.start(LT.L('adding_users_to_channel'));
		else 
			LT.progress.start(LT.L('adding_user_to_channel'));
		LT.loader.sendRequest(LT.baseUrl + 'user/addto/' + encodeURI(where), 
							  'login[]=' + who.join('&login[]='),
			callback);
	},
	removeFromChannel: function(callback, where, who) {
		LT.progress.start(LT.L('removing_users_from_channel'));
		LT.loader.sendRequest(LT.baseUrl + 'user/removefrom/' + encodeURI(where), 
							  'login[]=' + who.join('&login[]='),
			callback);
	},	
	saveUser: function(callback, user) {
		LT.progress.start(LT.L('saving_user'));
		
		LT.loader.sendRequest(LT.baseUrl + 'user/save', LT.createUrlString(user),
			callback);
	},
	deleteUsers: function(callback, who) {
		LT.progress.start(LT.L('deleting_users'));
		LT.loader.sendRequest(LT.baseUrl + 'user/delete', 
							  'login[]=' + who.join('&login[]='),
			callback);
	},
	addChannel: function(callback, name, is_shared) {
		LT.progress.start(LT.L('creating_channel'));
		LT.loader.sendRequest(LT.baseUrl + 'channel/add/name/' + encodeURI(name) + '/shared/' + is_shared, '',
			callback);
	},
	deleteChannels: function(callback, what) {
		LT.progress.start(LT.L('deleting_channels'));
		LT.loader.sendRequest(LT.baseUrl + 'channel/delete', 
							  'name[]=' + what.join('&name[]='),
			callback);
	},
	getSystemStatus: function(callback) {
		LT.progress.start(LT.L('getting_system_status'));
		LT.loader.sendRequest(LT.baseUrl + 'system/statusget', '',
			callback);
	},
	getDownloads: function(callback){
		LT.progress.start(LT.L('getting_system_status'));
		LT.loader.sendRequest(LT.baseUrl + 'system/downloadsget', '',
			callback);
	},
	addLicense: function(callback, key) {
		LT.progress.start(LT.L('validating_license'));
		LT.loader.sendRequest(LT.baseUrl + 'system/applylicense', "license_code=" + key,
			callback);
	},
	checkUpdates: function(callback, currentVersion) {
		LT.progress.start(LT.L('checking_updates'));
		LT.loader.sendRequest(LT.baseUrl + 'system/checkupdates' , 'current_version=' + currentVersion,
			callback);
	},
	restartServers: function(callback) {
		LT.progress.start(LT.L('restarting_servers'));
		LT.loader.sendRequest(LT.baseUrl + 'system/restart', '',
			callback);
	},
	sendFeedback: function(callback, message) {
		LT.progress.start(LT.L('emailing_support'));
		LT.loader.sendRequest(LT.baseUrl + 'system/sendfeedback', message,
			callback);
	},
	getClientSettings: function(callback) {
		LT.progress.start(LT.L('loading_network_settings'));
		LT.loader.sendRequest(LT.baseUrl + 'system/getclientsettings', '',
			callback);
	},
	saveClientSettings: function(callback, values) {
		LT.progress.start(LT.L('saving_network_settings'));
		LT.loader.sendRequest(LT.baseUrl + 'system/saveclientsettings', 
		'settings=' + LT.JSON.toString(values),
		callback);
	},
	forgotPassword: function(callback, email) {
		LT.progress.start(LT.L('sending_password_reminder'));
		LT.loader.sendRequest(LT.baseUrl + 'user/forgot', 'email=' + email,
			callback);
	},
	changePassword: function(callback, guid, newPassword) {
		LT.progress.start(LT.L('updating_password'));
		LT.loader.sendRequest(LT.baseUrl + 'user/recall', 'guid=' + guid + '&password_hash=' + hex_md5(newPassword),
			callback);
	},
	requestAPIkey: function(callback){
		LT.progress.start(LT.L('requesting_api_key'));
		LT.loader.sendRequest(LT.baseUrl + 'system/requestapikey', '',
			callback);
	}
});
/*
 * Table view helper class - copypaste must die!
 */
LT.tableView = function(container, config) {
	this._container = container;
	this._prefix = LT.makeId();
	if (config) {
		this.init(config);
	}
}
LT.tableView.prototype.init = function(config) {
/*
var config = {
	rowTemplate: '',
	itemToString: function(item){},
	emptyText: 'No data found',
	onclick: function(item){},
	onselect: function(item, selected){},
	onfiltered: function(count){},
	filters: [],
	selectors: []
}
 */	
	if (!config.selectors)
		config.selectors = [];
	config.selectors['all'] = function(){return true};
	config.selectors['none'] = function(){return false};		
	this._config = config;
	this._searchString = '';
	var _this = this;
	if (config.filtersContainer) {
		config.filtersContainer.innerHTML = 'Filters:' + this.renderFilters();
		config.filtersContainer.onclick = function() {
			_this.toggleFilter(this);
			return false;
		}
	}
	if (config.searchBox) {
		config.searchBox.container.value = config.searchBox.defaultText;
		config.searchBox.container.onfocus = function(){
			if (this.value == config.searchBox.defaultText) 
				this.value = "";
			else 
				this.selectionStart = 0; // doesn't work in IE
			this.className = "active";
		}
		config.searchBox.container.onblur = function(){
			_this.filter(this.value);
			if (this.value == "") 
				this.value = config.searchBox.defaultText;
			this.className = "";
			return true;
		}
		config.searchBox.container.onkeyup = function(){
			_this.filter(this.value);
		}
	}
}

LT.tableView.prototype.render = function(data){
	var out = "", st ="";
	this._data = data;

	if (data.length < 1) {
		if (typeof this._config.emptyText == 'function')
			this._container.innerHTML = this._config.emptyText();
		else if (typeof this._config.emptyText == 'string')
			this._container.innerHTML = this._config.emptyText;
		else 
			this._container.innerHTML = '';
		return;
	}
	for (var u in data) {
		if (data[u].hide) continue;
		if (this._config.style) {
			st = this._config.style.call(data[u]);
		} else {
			st = ""
		}
		out += '<div id="' + this._prefix + '_row_' + u +'" style="' + st + '">';
		out += '<input id="' + this._prefix + '_cb_' + u + '" type="checkbox"/>';
		out += LT.subs(this._config.rowTemplate, data[u]);
		out += '</div>';
	}

	this._container.innerHTML = out;
	var _this = this;
	for (u in data) {
		// need this trick to create extra closure and avoid 
		// getting the last array item in every callback
		var s = function(u){
			LT.$(_this._prefix + '_row_' + u).onclick = function(){
				return _this._onClick(this, u);
			}
			if (LT.$(_this._prefix + '_row_' + u).style) 
				LT.$(_this._prefix + '_row_' + u).style.cursor = "pointer";
			LT.$(_this._prefix + '_cb_' + u).onclick = function(){
				return _this._onSelect(this, u);
			}
		}(u);
	}
}
LT.tableView.prototype._onClick = function(caller, itemIndex){
	if (this._disableClick) {
		this._disableClick = false;
		return true;
	}
	if (this._config.onclick)
		this._config.onclick(this._data[itemIndex]);
	return true;
}
LT.tableView.prototype._onSelect = function(caller, itemIndex){	
	if (this._config.onselect)
		this._config.onselect(this._data[itemIndex], caller.checked);
	this._disableClick = true;
	return true;
}
LT.tableView.prototype.getSelection = function(){
	var out = [];
	for (var u in this._data) {
		if (LT.$(this._prefix + '_cb_' + u) && LT.$(this._prefix + '_cb_' + u).checked)
			out.push(u);
	}
	return out;
}
LT.tableView.prototype.select = function(condition){
	if (typeof condition == 'string' && this._config.selectors && this._config.selectors[condition]) {
		this.select(this._config.selectors[condition]);
		return;
	}
	for (var u in this._data) {
		if (LT.$(this._prefix + '_cb_' + u).id)
			LT.$(this._prefix + '_cb_' + u).checked = condition.call(this._data[u]);
	}
}
LT.tableView.prototype.toggleFilter = function(elem){
	if (!this._config.filters) return;
	var filter = this._config.filters[elem.innerHTML];
	if (!filter) return;
	if (filter.group && !filter.active) {
		for (var fi in this._config.filters) {
			if (this._config.filters[fi].group == filter.group) {
				this._config.filters[fi].active = false;
			}
		}
		for (var el in elem.parentNode.childNodes) {
			var child = elem.parentNode.childNodes[el];
			if (child && child.innerHTML)
				child.className = this._config.filters[child.innerHTML].active ? 'active' : '';
		}
	}
	filter.active = !filter.active;
	elem.className = filter.active ? 'active' : '';
	this.filter(this._searchString);
}
LT.tableView.prototype.renderFilters = function(){
	if (!this._config.filters) return '';
	var out = '';
	for (var fi in this._config.filters) {
		out += ' <a href="#" ';
		if (this._config.filters[fi].active) {
			out += 'class="active" ';
		}
		out += 'onclick="this.parentNode.onclick.call(this); return false;">' + fi + '</a>';
	}
	return out;
}
LT.tableView.prototype.resetFilters = function(value){
	for (var fi in this._config.filters) {
		this._config.filters[fi].active = false;
	}
	if (this._config.filtersContainer)
		this._config.filtersContainer.innerHTML = 'Filters:' + this.renderFilters();
	this._config.searchBox.container.blur();
	this.filter('');
	this._config.searchBox.container.value = this._config.searchBox.defaultText;
}
LT.tableView.prototype.filter = function(value){
	var count = 0;

	if (!this._config.itemToString) 
		return;
	this._searchString = value;
	var inverse = false;
	if (value.indexOf("!") == 0) {
		inverse = true;
		value = value.substr(1);
	}
	for (var u in this._data) {
		var t = this._config.itemToString(this._data[u]).toLowerCase();
		var tohide = true;
		if (inverse ? t.indexOf(value.toLowerCase()) < 0 : t.indexOf(value.toLowerCase()) > -1) {
			tohide = false;
			for (var fi in this._config.filters) {
				if (this._config.filters[fi].active)
					tohide = tohide || !this._config.filters[fi].check.call(this._data[u]);
			}
			
		}
		this._data[u].hide = tohide;
		if (!tohide) 
			count++;
	}

	if (this._config.onfiltered) {
		this._config.onfiltered(count);
	}
	if (count)
		this.render(this._data);
}
/*
 * Progress animation helper
 */
LT.progress = {
	_interval: -1,
	speed: 20, // fps
	_currentFrame: 0,
	_element: null,
	init: function(element, frames) {
		this._element = element;
		this._frames = frames;
		this._step = element.offsetWidth;
	},
	start: function(message){
		this.stop();
		var _this = this;
		this._interval = setInterval(function(){
			_this._update();
		}, 1000/this.speed);
		if (message != undefined) {
			LT.message.show(message);
		}
	},
	_update: function() {
		if (!this._element || !this._element.style) return;
		this._element.style.backgroundPosition = 
			this._step*(this._frames - 1 - this._currentFrame % this._frames) + "px 0px";
		this._currentFrame++;
	},
	stop: function(message){
		clearInterval(this._interval);
		if (message != undefined) {
			LT.message.show(message);
		} else {
			LT.message.show('');
		}
	}
}

LT.message = {
	show: function(text, timeout) {
		if (text == "") {
			LT.$hide("message");
		} else {
			LT.$("message").innerHTML = text;
			LT.$show("message");
		}
		if (timeout != undefined) 
			setTimeout(function(){LT.message.show('');}, timeout);
	}
	
}
LT.handlers = {
	301: function(data) {
		LT.message.show(data.status);
		LT.username = "";
		LT.cookie.clear("username");
		LT.redirect("index.htm");
		return true;
	},
	302: function(data) {
		LT.progress.stop();
		LT.message.show(LT.L('permissions_error'));
		
		return true;
	},
	303: function(data) {
		LT.progress.stop();
		LT.message.show(LT.L('invalid_credentials'));
		return true;
	},
	601: function() {
		LT.progress.stop();
		LT.popup.showError(LT.L('missing_setup_file'));
		return true;
	},
	701: function() {
		LT.progress.stop();
		LT.$("network_hint").innerHTML = LT.L('network_name_is_taken');
		LT.$("network_hint").className = "hint error";
		return true;
	}
}
LT.defaultResponseHandler = function(data) {
	LT.message.show(LT.L('error') + " (" + data.code + ") " + data.status);
}
LT.loader = {
	_requestTimeout: 10, // in seconds
	_cache: {},
	requests: {},
	handleResponse: function(data, func) {
		LT.progress.stop();
		if (data.code == 200 && func)
			func.call(this, data);
		else 
			LT.defaultResponseHandler(data);
	},
	makeSecureUrl: function(req) {
		req = LT.baseSecureUrl + '/a/' + LT.network_name + '.' + LT.domain + '/' + req;
		if (req.indexOf('?') > 0) {
			req += '&';
		} else {
			req += '?';
		}
		req += 'PHPSESSID=' + LT.cookie.read('PHPSESSID');
		return req;
	},
	sendRequest: function(req, body, success, usecache) {
		if (usecache != undefined && usecache) {
			if (this._cache[req] != undefined) {
				this.handleResponse(this._cache[req], success);
				return;
			}
		}
		if (LT.debug) {
			if (this._cache[req] != undefined) {
				if (success) {
					var _this = this;
					setTimeout(function(){
						_this.handleResponse(_this._cache[req], success);
					}, 1000);
				}
				return;
			}
		}
		var reqId = LT.makeId();

		this.requests[reqId] = {
			_req: req,
			_body: body,
			xhr: new XMLHttpRequest(),
			_callback: success != undefined ? success : null,
			_reqUrl: req + "?rnd=" + reqId,
			init: function(response) {
				var data = {};
				try {
					eval("data = " + response);
				} catch (e) {}
				if (typeof data.code == "undefined") {
					data = {code: 801, status: LT.L('bad_response') + ' <a href="#" onclick="alert(unescape(\'' + escape(response) + '\')); return false;">' + LT.L('details') + '</a>'};
					LT.loader.handleResponse(data, this._callback);
				}
				
				// use default error handlers if exist
				if (data.code != 200 && LT.handlers[data.code]) {
					if (LT.handlers[data.code].call(this, data))
						return;
				}
				LT.loader._cache[this._req] = data;
				LT.loader.handleResponse(data, this._callback);
			}
		};

		if (!this.requests[reqId].xhr) return false;
		try {
			if (!body)
				this.requests[reqId].xhr.open("GET", this.requests[reqId]._reqUrl);
			else 
				this.requests[reqId].xhr.open("POST", this.requests[reqId]._reqUrl);
			var _this = this;
			this.requests[reqId].xhr.onreadystatechange = function() {
			    if (_this.requests[reqId].xhr.readyState == 4 && (_this.requests[reqId].xhr.status == 200 || _this.requests[reqId].xhr.status == 302)) {
			    	_this.requests[reqId].init.call(_this.requests[reqId], _this.requests[reqId].xhr.responseText);
			    	delete _this.requests[reqId]; // clean up 
			    } else if (_this.requests[reqId].xhr.readyState == 4) {
					_this.requests[reqId].init.call(_this.requests[reqId], 
					'{"code": "802", "status":  "' + LT.L('request_error') + ' (' + _this.requests[reqId].xhr.status + ')"}');
				}
			}
			this.requests[reqId].xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		
			if (!body)
				this.requests[reqId].xhr.send("");
			else 
				this.requests[reqId].xhr.send(body);
		} catch (e) {
			this.requests[reqId].init.call(this.requests[reqId], "{'code': 500, 'status': '" + e + "'}");
		}
	}
}
LT.exists = function(name) {
	return !!document.getElementById(name);
}
LT.$ = function(name) {
	var el = document.getElementById(name);
	if (!el) {
		el = {}; 
		//if (typeof window["console"] != "undefined") 
		//	console.log('Element "', name, '" is not found');
	}
	return el;
}
LT.$hide = function(name) {
	var el = LT.$(name);
	if (el.style)
		el.style.display = "none";
}
LT.$show = function(name) {
	var el = LT.$(name);
	if (el.style)
		el.style.display = "block";
}
/*
 * Get localized string
 */
LT.L = function(token) {
	return LT.locale[token] || token;
}
LT.makeId = function() {
	return 1*(Math.random().toString().replace('.', ''));
}
/*
 * IE-friendly way of setting innerHTML on tbody
 */
LT.setTableBodyHTML = function(tObject, html){
	var tBody = tObject.getElementsByTagName("tbody");
	tBody = (typeof tBody != "undefined") ? tBody[0] : null;
	if (!tBody) return;

	var tempDiv = document.createElement("div");
	tempDiv.innerHTML = "<table><tbody>" + html + "</tbody></table>";
	tObject.removeChild(tBody);
	tObject.appendChild(tempDiv.firstChild.firstChild);

} 
LT.redirect = function(url) {
	document.location.href = url;	
}
/*
 * HTML rendering helper
 */
/*
 * 		formatPrice: function(price) {
			if (price == "0")
				return "Free";
			else	
				return "$" + price + "/month";
		},
		formatNumber: function(number) {
			if (number == "0")
				return "&mdash;";
			else	
				return number;
		},
 */
LT.render = {
	core: {
		table: function(data, tmpl, shallow) {
			var out = '';
			out += '<table>';
			var template = tmpl || '<tr><td>%name%:</td><td>%value%</td></tr>';
			var value;
			for (var item in data) {
				switch (typeof data[item]) {
					case 'object':
						value = this.table(data[item]); // recursion
						break;
					case 'string':
						if (!shallow) {
							value = LT.subs(data[item], LT.tokens);
							break;
						}
					default:
						value = data[item];
				}
				out += LT.subs(template, {
					'name': item,
					'value': value
				});
			}
			out += '</table>';
			return out;
		},
		input: function(attribs) {
			var out = '';
			if (attribs.length) {
				for (var i=0; i< attribs.length; i++){
					out += this.input(attribs[i]);
				}
			} else {
				out = '<input ';
				if (!attribs.type)
					attribs.type = 'hidden';
				for (var j in attribs) {
					out += j + '="' + attribs[j] + '" ';
				}
				out += '/>';
			}
			return out;
		},
		price: function(price) {
			price = 1*price;
			if (!price)
				return LT.L('free');
			else	
				return this.currency(price) + LT.L('per_month'); 
		},
		currency: function(val) {
			return LT.subs(LT.L('currency_pattern'), {
				"value": this.num(val, 2, "0.00")
				});
		},
		num: function(num, fixed, zeroSign) {
			num = 1*num;
			if (!num) {
				if (zeroSign)
					return zeroSign;
				else
					return '&mdash;';
			} else {
				if (fixed) {
					return num.toFixed(fixed);
				} else {
					return num;
				}
			}
		}
	},
	element: {
		order: function(order, showCancel){
			var content = "";
			content += '<div class="order_list_item_top">';
			content += '<b>' + order.service_name + '</b> (';
			content += LT.render.core.price(order.service_price); 
			if (order.is_beta) {
				content += ' ' + LT.L('beta');
			}
			content += ') ' + LT.L('service_plan');

			content += '<div class="order_list_status ' + order.status + '">';
			content += order.status;
			if (order.status == "pending" && showCancel) {
				var temp = ' <a href="#" class="cross left25px" onclick="LT.pages.%page%.cancelOrder(\'%order_number%\');return false;">' + LT.L('cancel') + '</a>';
				content += LT.subs(temp, {
					"page": LT.pi,
					"order_number": order.order_number
				});
			}				
			content += ' <a href="account_order.htm?order=' + order.order_number  + '" class="info">' + LT.L('view') + '</a>';
			content += '</div>';
			content += '</div>'
			content += '<div class="order_list_item_bottom">';
			content += LT.d(order.date);
			content += ', ';
			content += LT.L('order') + ' #' + order.order_number;
			content += '</div>';
			return content;
		},
		transaction: function(trans){
			
		},
		billTo: function(order){
			var billToText = '';
			if (order.billing_name) {
				billToText += order.billing_name + '<br/>';
			}
			if (order.billing_email) {
				billToText += '<a href="mailto:' + order.billing_email + '">' + order.billing_email + '</a><br/>';
			}
			if (order.billing_address) {
				billToText += order.billing_address + '<br/>';
			}
			if (order.billing_address2) {
				billToText += order.billing_address2 + '<br/>';
			}
			if (order.billing_city) {
				billToText += order.billing_city;
			}
			if (order.billing_city && order.billing_state) {
				billToText += ', ';
			}
			if (order.billing_state) {
				billToText += order.billing_state + '<br/>';
			} else {
				billToText += '<br/>';
			}
			if (order.billing_zip) {
				if (order.billing_country) {
					billToText += order.billing_zip + ', ' + order.billing_country + '<br/>';
				} else {
					billToText += order.billing_zip + '<br/>';
				}
			} else {
				if (order.billing_country) {
					billToText += order.billing_country + '<br/>';
				}
			}
			if (!billToText) billToText = '&lt;Not available&gt;';
			return billToText;
		}
	}	
}
LT.parseUrlString = function(url) {
	url += "&";
	if (url.indexOf("?") > -1)
	url = url.split("?")[1];
	var out = {};
	var re = /([^=&]+)=([^&]+)&/ig;
	var matches = re.exec(url);
	while (matches) {
		out[matches[1]] = matches[2];
		matches = re.exec(url);
	}
	return out;
}

/**
 * Creates url-string from simple JSON object
 */
LT.createUrlString = function(obj) {
	var out = "";
	for (var key in obj) {
		switch (typeof obj[key]) {
			case "string":
			case "number":
			case "boolean":
				out += key + "=" + encodeURI(obj[key]) + "&";
				break;
		}
	}
	return out;
}
LT.inArray = function(what, where) {
	for (var i in where) {
		if (where[i] == what) {
			return true;
		}
	}
	return false;
}
LT.parseName = function(name) {
	var out = {"first": "", "last":""};
	//name = name.trim();
	var spacePos = name.indexOf(" ");
	out.first = name.substring(0, spacePos);
	out.last = name.substring(spacePos + 1);
	return out;
}
/*
 * Substitutes %something% tokens in a string with the values
 * passed in pairs 
 */
LT.subs = function(str, pairs) {
	// substitute vars
	var out = str.replace(/%(\w+)%/ig, function(match, p1){
		if (typeof pairs[p1] != 'undefined') {
			return pairs[p1];
		}
		else {
			if (p1.indexOf("label_") == 0) {
				p1 = p1.replace('label_', '');
				
				if (typeof pairs[p1] != 'undefined' && typeof LT.locale["label_" + pairs[p1]] != 'undefined') {
					return LT.locale["label_" + pairs[p1]];
				}
				else {
					return "";
				}
			} else {
				return LT.locale[p1] || "";
			}
		}
	});
	// handle contitional statements %<condition>?<value if true>:<value if false>
	out = out.replace(/%(\w+)\?([^:%]*)\:([^%]*)%/ig, function(match, p1, p2, p3){
		if (pairs[p1])
			return p2;
		else
			return p3;
	});
	return out;
}
LT.JSON = {
	toString: function(s) {
	    var src = LT.clone(s);
		switch (typeof src) {
		case "number":
			src = src.toString();
			break;
		case "string":
		    src = '"' + src.replace(/"/g, '\\"') +'"'; 
		    break;
		case "object":
		    var out = [];
			if (typeof src.length == "undefined") {
				for (i in src) {
					out.push('"' + i + '":' + LT.JSON.toString(src[i]));
				}
				src = "{" + out.join(",") + "}";
				break;
			}
		case "array":
		    for (var i = 0; i< src.length; i++)
		    {
				src[i] = LT.JSON.toString(src[i]);
		    }
			src = "[" + src.join(",") + "]";
		    break;
	    }
	    return src;
	},
	fromString: function(str) {
		try {
			eval("var res = " + str);
		} catch (e) {
			return false;
		}
		return res;
	}
}
LT.cookie = {
	save: function (name, value, days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime() + (days*24*60*60*1000));
			var expires = "; expires=" + date.toGMTString();
		}
		else var expires = "";
		document.cookie = name + "=" + encodeURI(value) + expires + "; path=/";
	},
	read: function (name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0; i < ca.length; i++) {
			var c = ca[i];
			while (c.charAt(0) == ' ') 
				c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) 
				return decodeURI(c.substring(nameEQ.length,c.length));
		}
		return null;
	},
	clear: function (name) {
		this.save(name, "", -1);
	}
}
var AIM = {

    frame : function(c) {

        var n = 'f' + Math.floor(Math.random() * 99999);
        var d = document.createElement('DIV');
        d.innerHTML = '<iframe style="display:none" src="about:blank" id="'+n+'" name="'+n+'" onload="AIM.loaded(\''+n+'\')"></iframe>';
        document.body.appendChild(d);

        var i = document.getElementById(n);
        if (c && typeof(c.onComplete) == 'function') {
            i.onComplete = c.onComplete;
        }

        return n;
    },

    form : function(f, name) {
        f.setAttribute('target', name);
    },

    submit : function(f, c) {
        AIM.form(f, AIM.frame(c));
        if (c && typeof(c.onStart) == 'function') {
            return c.onStart();
        } else {
            return true;
        }
    },

    loaded : function(id) {
        var i = document.getElementById(id);
        if (i.contentDocument) {
            var d = i.contentDocument;
        } else if (i.contentWindow) {
            var d = i.contentWindow.document;
        } else {
            var d = window.frames[id].document;
        }
        if (d.location.href == "about:blank") {
            return;
        }

        if (typeof(i.onComplete) == 'function') {
            i.onComplete(d.body.innerHTML);
        }
    }

}
LT.clone = function(from){
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);
		
    var to = new from.constructor();
    for (var name in from){
        to[name] = this.clone(from[name]);
    }
    return to;
}
