var m_init = false;
var m_versionMin = null;
var m_warnOnVersionChanged = true;
var m_sThisI18n = null;
var m_currentSkin = null;
var m_sReferrer = null;
var m_sSponsor = null;

var m_session = null;
var m_oDate = new Date();

var m_data = null;
var m_menus = null;

var m_effectDef = null;
var m_text = null;
var m_sThisPage;
var m_sThisParams;
var m_aThisParam;
var m_sThisHash;
var mp = null;
var mpText = null;
var mf = null;
var mfText = null;

/*************************************************************************************************************
 * FnLauncher
 ************************************************************************************************************/
FnLauncher = {
	toLaunch : {},
	uid : 0,
	register : function(fn, args, scope) {
		var uid = this.uid++;
		this.toLaunch[uid] = {
			fn : fn,
			args : args,
			scope : scope
		};
		return uid;
	},
	launch : function(uid) {
		var a = this.toLaunch[uid];
		delete this.toLaunch[uid];
		a.fn.apply(a.scope, a.args)
	},
	launchAsync : function(uid, interval) {
		window.setTimeout('FnLauncher.launch(' + uid + ');', interval || 0);
	}
};
/** ************************************************************************** */

/*************************************************************************************************************
 * LoadDoc
 ************************************************************************************************************/
var LoadDoc = Class.create();
LoadDoc.noInstance = 0;
LoadDoc.prototype = {
	fOnLoad : null,
	bStoped : false,
	iNoDocLoaded : 0,

	initialize : function(fOnLoad) {
		this.fOnLoad = fOnLoad;

		if (2 == arguments.length && Object.isArray(arguments[1])) {
			this.aLiDocToLoad = arguments[1];
		} else {
			this.aLiDocToLoad = [];
			for (var i = 1; i < arguments.length; i++) {
				this.aLiDocToLoad.push(arguments[i]);
			}
		}

		this.process();
	},

	process : function() {
		if ($("loadingMsg") != null) {
			$("loadingMsg").className = 'active';
		}

		LoadDoc.noInstance++;

		var reloadParam = (NOCACHE ? Math.round(Math.random() * 999999) : VERSION);
		for (var iDocIndex = 0; iDocIndex < this.aLiDocToLoad.length; iDocIndex++) {
			var oDoc = this.aLiDocToLoad[iDocIndex];
			switch (oDoc.type) {
				case 'img' :
					var oImg = $(new Image());
					oImg.setAttribute("docIndex", iDocIndex);
					oImg.observe("load", function(event) {
								oImg.stopObserving("load");
								this.docLoaded(Number(oImg.getAttribute("docIndex")), oImg);
							}.bindAsEventListener(this));
					oImg.src = oDoc.fileName;
					break;

				case 'js' :
					var url = ('js/' == oDoc.fileName.substr(0, 3) ? '' : 'page/js/') + oDoc.fileName + '.js' + '?r='
							+ reloadParam;
					if (LOADJSDEBUG) {
						var oScript = $(document.createElement("script"));
						oScript.setAttribute("docIndex", iDocIndex);
						oScript.observe("load", function(event) {
									var o = Event.element(event);
									o.stopObserving("load");
									this.docLoaded(Number(o.getAttribute("docIndex")));
								}.bindAsEventListener(this));
						oScript.type = "text/javascript";
						oScript.src = url;
						document.getElementsByTagName("head")[0].appendChild(oScript);
					} else {
						var oHttp = new XmlHttp(url, 'GET', 'UTF-8', false);
						oHttp.contentFinished = this.docLoadedJs.bind(this, iDocIndex);
						oHttp.call();
					}
					break;

				case 'api' :
					var oHttp;
					if (DEVMODE) {
						oHttp = new XmlHttp(SERVER_PATH + 'page/' + oDoc.fileName, 'POST', 'UTF-8', false);
					} else {
						oHttp = new XmlHttp(SERVER_PATH + 'index.php5', 'POST', 'UTF-8', false);
						oHttp.addParam('url', oDoc.fileName);
					}
					if (!Object.isUndefined(oDoc.param)) {
						$A(oDoc.param).each(function(param) {
									oHttp.addParam(param[0], param[1]);
								});
					}
					oHttp.addParam('timezone', ClientContext.computeTimezone());
					if (m_init && !bPopup) {
						oHttp.addParam('readSn', 1);
						if (!m_session || !m_session.log) {
							oHttp.addParam('readPrefs', 1);
							oHttp.addParam('readBattles', 1);
						}
					}
					if (m_sReferrer) {
						oHttp.addParam('referrer', m_sReferrer);
					}
					oHttp.contentFinished = this.docLoaded.bind(this, iDocIndex);
					oHttp.call();
					break;

				case 'json' :
					var url = STATIC_PATH + oDoc.fileName + '.json' + "?r=" + reloadParam;
					var oHttp = new XmlHttp(url, 'GET', 'UTF-8', false);
					oHttp.contentFinished = this.docLoaded.bind(this, iDocIndex);
					oHttp.call();
					break;

				case 'text' :
					var url = STATIC_PATH + 'page/i18n/' + m_sThisI18n + '.json' + "?r=" + reloadParam;
					var oHttp = new XmlHttp(url, 'GET', 'UTF-8', false);
					oHttp.contentFinished = this.docLoaded.bind(this, iDocIndex);
					oHttp.call();
					break;
			}
		}
	},

	docLoadedJs : function(iIndex, vContent) {
		var alUid = FnLauncher.register(this.docLoaded, arguments, this);
		window.setTimeout(vContent.responseText + ";FnLauncher.launch(" + alUid + ");", 0);
	},

	docLoaded : function(iIndex, vContent) {
		if (this.bStoped) {
			return false;
		}

		var genericData = null;
		var sType = this.aLiDocToLoad[iIndex].type;
		if ('api' == sType) {
			var error = false;
			if (!vContent.onError || 0 == vContent.status) {
				var content = null;
				try {
					if (0 != vContent.status) {
						content = eval('(' + vContent.responseText + ')');
					}
				} catch (e) {
					error = true;
				}
				if (null != content) {
					genericData = content;
					if (content["_stopHack"] || content["_accessDenied"]) {
						this.bStoped = true;
					}
				}
				this.aLiDocToLoad[iIndex].content = content;
			} else {
				error = true;
			}
			if (error) {
				this.bStoped = true;
				var sErr = "Une erreur s'est produite lors du chargement d'une page :<br/><br/>" + "-type : " + sType
						+ "<br/>" + "-url : " + this.aLiDocToLoad[iIndex].fileName + "<br/>" + "<br/>";
				try {
					sErr += "-responseStatus : " + vContent.status + "<br/>" + "-responseText : "
							+ vContent.responseText.replace(/(\r)?\n/g, "<br/>") + "<br/>";
				} catch (e) {
					sErr += "-impossible de récupérer le détail de l'erreur";
				}
				PageManager.resetPageContent(true);
				PageManager.setPageContent('<div class="textLayer">' + sErr + '</div>');
				mp = null;
			}
		} else if ('img' == sType) {
			this.aLiDocToLoad[iIndex].content = vContent;
		} else if ('json' == sType || 'text' == sType) {
			this.aLiDocToLoad[iIndex].content = eval('(' + vContent.responseText + ')');
		}

		if (null != genericData) {
			ClientContext.updateContext(genericData);
		}

		this.iNoDocLoaded++;
		if (this.iNoDocLoaded == this.aLiDocToLoad.length || this.bStoped) {
			LoadDoc.noInstance--;
			if (LoadDoc.noInstance <= 0 && $("loadingMsg") != null) {
				$("loadingMsg").className = 'inactive';
			}
		}

		// when all is loaded launch fOnLoad
		if (!this.bStoped && this.iNoDocLoaded == this.aLiDocToLoad.length && Object.isFunction(this.fOnLoad)) {
			var aArgs = $A(this.aLiDocToLoad).collect(function(doc) {
						return doc.content;
					});
			window.setTimeout("FnLauncher.launch(" + FnLauncher.register(this.fOnLoad, aArgs, this) + ");", 0);
			SnReader.read();
		}

	}
};

function DocToLoad(sType, sFileName, vParam) {
	this.fileName = sFileName;
	this.type = sType;
	this.param = vParam;
	this.content = null;
}
/** ************************************************************************** */

/*************************************************************************************************************
 * ClientContext
 ************************************************************************************************************/
var ClientContext = {
	genMenuItems : [{
				id : "home",
				link : "o/home"
			}, {
				id : "forum",
				link : "js://PageManager.navigateToForum()"
			}, {
				id : "chat",
				link : "js://PageManager.navigateToChat()"
			}, {
				id : "gameInfo",
				link : "o/gameHelp&chap=pres"
			}, {
				id : "gameHelp",
				link : "o/gameHelp"
				// link : "http://./" + STATIC_PATH + "open/Manuel_v1_5.pdf"
		}	, {
				id : "faq",
				link : "js://PageManager.navigateToFAQ()"
			}, {
				id : "myAccount",
				link : "r/playerProfile",
				log : true
			}, {
				id : "register",
				link : "o/accountRegister",
				log : false
			}, {
				id : "logout",
				link : "js://ClientContext.logout()",
				logLite : true
			}, {
				id : "login",
				link : "js://ClientContext.login()",
				log : false
			}],
	headerMenuItems : [{
				id : "me",
				link : "r/me",
				subtitle : true,
				log : true
			}, {
				id : "battle",
				link : "r/battleList",
				html : '<div id="menuBattleList"></div>',
				subtitle : true,
				// items : [{
				// id : "tnmt",
				// link : "r/tnmt"
				// }],
				log : true
			}, {
				id : "business",
				subtitle : true,
				items : [{
							id : "b_buyProduct",
							link : "js://BuyProduct.start()"
						}, {
							id : "b_mobBuy",
							link : "r/mobBuy"
						}, {
							id : "b_sales",
							link : "r/saleList"
						}, {
							id : "b_recycle",
							link : "r/recycle"
						}, {
							id : "b_heroeRecruit",
							link : "r/heroeRecruit"
						}],
				log : true
				// }, {
			// id : "guild",
			// link : "r/guildHome",
			// subtitle : true,
			// log : true,
			// admin : true
		}	, {
				id : "world",
				subtitle : true,
				items : [{
							id : "w_mobs",
							link : "o/mobList"
						}, {
							id : "w_players",
							link : "o/playerList"
						}, {
							id : "w_heroes",
							link : "o/heroeList"
						}]
			}, {
				id : "admin",
				subtitle : true,
				items : [{
							id : "a_stat",
							link : "a/statList"
						}, {
							id : "a_news",
							link : "a/newsList"
						}, {
							id : "a_mobs",
							link : "a/mobList"
						}, {
							id : "a_effects",
							link : "a/effectList"
						}, {
							id : "a_bf",
							link : "a/mapList"
						}],
				admin : true
			}],
	extMenuItems : [{
				id : "facebook",
				link : "http://www.facebook.com/pages/Lands-of-Magic/106903459337082",
				subtitle : true
			}],

	init : function() {
		m_sReferrer = gup("referrer") || null;
		m_sSponsor = gup("sponsor") || null;

		// versioning
		m_versionMin = VERSION.substr(0, VERSION.lastIndexOf('.'));
		if (!bPopup) {
			$('__version').innerHTML = VERSION;
		}

		Prototype.Browser.IE6 = Prototype.Browser.IE7 = Prototype.Browser.IE8 = false;
		if (Prototype.Browser.IE) {
			var ieVersion = parseInt(navigator.appVersion.substr(navigator.appVersion.indexOf('MSIE') + 5, 1));
			if (6 == ieVersion) {
				Prototype.Browser.IE6 = true;
			} else if (7 == ieVersion) {
				Prototype.Browser.IE7 = true;
			} else if (8 == ieVersion) {
				Prototype.Browser.IE8 = true;
			}
		}

		// init Windows API
		this.initWindow();

		// define UserPref for default value
		woUserPref = null;
		try {
			woUserPref = window.opener.UserPref;
		} catch (e) {
		}
		if (woUserPref) {
			UserPref = woUserPref
		} else {
			UserPref.define(USER_PREF);
		}

		// apply right skin
		this.updateSkin();

		// overwrite contextmenu handler
		$('page_content').observe("mouseup", this.onPageContentMouseUp.bindAsEventListener(this));
		document.observe("keypress", this.onBodyKeyPress.bindAsEventListener(this));

		if (!bPopup) {
			Event.observe(window, 'beforeunload', this.onWindowBeforeUnload.bind(this));
			Event.observe(window, 'resize', function() {
						if (this.resizeTimeout) {
							window.clearTimeout(this.resizeTimeout);
						}
						this.resizeTimeout = window.setTimeout(this.onWindowResize.bindAsEventListener(this), 301);
					}.bind(this));
			this.onWindowResize();

			// add link to o/home on header
			$('header').observe('click', function(ev) {
						if ("header" == ev.element().id) {
							PageManager.goTo('o/home');
						}
					});
		}

		new LoadDoc(this.initStep2.bind(this), [
						new DocToLoad('api', 'o/getRefDataForMob', [["i18n", (gup("i18n") || "")]]),
						new DocToLoad('json', 'page/js/b/effect')]);
	},

	initWindow : function() {
		Window.warnIfExists = DEVMODE;
		Window.prototype.setZIndex = function(zindex) {
			var currentZIndex = this.element.style.zIndex;
			if (currentZIndex && currentZIndex >= zindex) {
				return;
			}
			this.element.setStyle({
						zIndex : zindex
					});
			Windows.updateZindex(zindex, this);
		};

		if (Prototype.Browser.IE
				&& (parseInt(navigator.appVersion.substr(navigator.appVersion.indexOf('MSIE') + 5, 1)) > 6)) {
			Window.prototype._checkIEOverlapping = Prototype.emptyFunction;
		}

		Windows.maxZIndex = 1000;
		Windows.unsetOverflow = function(except) {
			this.windows.each(function(d) {
						if (d.getContent().tagName.toLowerCase() == 'iframe') {
							return;
						}
						d.oldOverflow = d.getContent().getStyle("overflow") || "auto";
						d.getContent().setStyle({
									overflow : "hidden"
								})
					});
			if (except && except.oldOverflow) {
				except.getContent().setStyle({
							overflow : except.oldOverflow
						});
			}
		};
		Windows.updateZindex = function(zindex, win) {
			if (zindex >= Dialog.maxZIndex) {
				this.focusedWindow = win;
			} else {
				if (zindex > this.maxZIndex) {
					this.maxZIndex = zindex;
					if (this.focusedWindow)
						this.blur(this.focusedWindow.getId())
				}
				this.focusedWindow = win;
				if (this.focusedWindow)
					this.focus(this.focusedWindow.getId())
			}
		};

		WindowUtilities.setCookie = function(value, cookieParam) {
			UserPref.set("win_" + cookieParam[0], value);
		};
		WindowUtilities.delCookie = function(cookieParam) {
			UserPref.set("win_" + cookieParam[0], null);
		};
		WindowUtilities.getCookie = function(cookieName) {
			return UserPref.get("win_" + cookieName);
		};

		Dialog.maxZIndex = 11000;
		Dialog.alert = Dialog.alert.wrap(function(callOriginal, content, parameters) {
					parameters = Object.extend({
								zIndex : Dialog.maxZIndex,
								buttonClass : "validate topSpace",
								title : ti("alert.title"),
								onShow : function() {
									$(Windows.getWindow(this.id).getContent()).select("input.ok_button")[0].focus();
								},
								options : {}
							}, parameters);
					parameters.options.parent = parameters.options.parent || null;
					callOriginal(content, parameters);
				});
		Dialog.confirm = Dialog.confirm.wrap(function(callOriginal, content, parameters) {
					var focusCancel = parameters.focusCancel;
					parameters = Object.extend({
								zIndex : Dialog.maxZIndex,
								buttonClass : "validate topSpace",
								title : ti("question.title"),
								okLabel : ti("question.okButton"),
								cancelLabel : ti("question.cancelButton"),
								onShow : function() {
									$(Windows.focusedWindow.getContent()).select("input." + (focusCancel ? "cancel" : "ok")
											+ "_button")[0].focus();
								},
								options : {}
							}, parameters);
					parameters.options.parent = parameters.options.parent || null;
					var win = callOriginal(content, parameters);
				});
	},

	initStep2 : function(refDataForMob, effectDef) {
		m_data = Object.extend(m_data || {}, refDataForMob);
		m_effectDef = effectDef;

		new LoadDoc(this.initStep3.bind(this), [new DocToLoad('text'), new DocToLoad('api', '')]);
	},

	initStep3 : function(text) {
		m_text = text;
		Object.extend(Date, ti("date"));
		Object.extend(Date.prototype, ti("dateProto"));

		if (!bPopup) {
			// Tips
			Tipshower.init(ti('tips'), 30);

			// i18nSwitch
			$('i18nSwitch').innerHTML = '<ul>' + $H(I18N_CODE).collect(function(pair) {
				return '<li class="' + pair.key + '"><a href="javascript:void(i18nswitch(\'' + pair.key + '\'));">'
						+ pair.value + '</a></li>';
			}).join('') + '</ul>';

			// text for page
			var texts = ti("index");
			for (var text in texts) {
				var value = texts[text];
				switch (text) {
					case "loadingMsg" :
						$(text).title = value;
						break;
					default :
						$(text).innerHTML = value;
						break;
				}
			}

			// make menu
			m_menus = {};
			var fMenuLabel = function(prefix, itemId) {
				return ti('menu.' + prefix + '.' + itemId);
			}
			$("genMenu").innerHTML = '<ul>' + this.menuMake(fMenuLabel, this.genMenuItems, "gen") + '</ul>';

			$("headerMenu").innerHTML = '<ul id="headerMenuRoot" class="level1">'
					+ this.menuMake(fMenuLabel, this.headerMenuItems, "header") + '</ul>';
			m_menus.header = new Menu('headerMenuRoot', 'm_menus.header', function() {
						this.closeDelayTime = 300;
					});

			$("extMenu").innerHTML = '<ul id="extMenuRoot" class="level1">'
					+ this.menuMake(fMenuLabel, this.extMenuItems, "ext") + '</ul>';
			m_menus.ext = new Menu('extMenuRoot', 'm_menus.ext', function() {
						this.closeDelayTime = 300;
					});
		}

		$(document.body).show();
		m_init = true;

		PageManager.goTo(PageManager.getHashFromLocation() || 'o/home');
		window.setInterval(PageManager.detectLocationChange.bind(PageManager), 500);
	},

	menuMake : function(fMenuLabel, items, prefix, lvl) {
		lvl = (lvl || 1);
		return $A(items).collect(function(item) {
			var label = fMenuLabel(prefix, item.id);
			var link = (item.link ? "PageManager.goTo(&quot;" + item.link + "&quot;)" : "0");
			return ('<li id="' + prefix + 'Menu_' + item.id + '"' + (1 == lvl ? ' style="display:none;"' : '') + '>')
					+ (('<a href="javascript:void(0);" onclick="' + link + ';this.blur();" class="lnkLevel' + lvl + '">')
							+ '<span>' + label + '</span></a>')
					+ (item.items || item.subtitle ? '<ul class="level' + (lvl + 1) + '">'
							+ (item.subtitle ? '<li class="subtitle">' + label + '</li>' : '')
							+ (item.items ? this.menuMake(fMenuLabel, item.items, prefix, (lvl + 1)) : '')
							+ (item.html || '') + '</ul>' : '') + '</li>';
		}, this).join('');
	},

	menuDisplay : function(items, prefix) {
		$A(items).each(function(item) {
					var b = true;
					if (!Object.isUndefined(item.log)) {
						b = false !== b && (item.log == m_session.log);
					}
					if (!Object.isUndefined(item.logLite)) {
						b = false !== b && (item.logLite == m_session.logLite);
					}
					if (!Object.isUndefined(item.admin)) {
						b = false !== b && (item.admin == m_session.admin);
					}
					$(prefix + 'Menu_' + item.id)[b ? 'show' : 'hide']();
					if (item.items && false !== b) {
						this.menuDisplay(item.items, prefix);
					}
				}, this);
	},

	onWindowBeforeUnload : function(e) {
		return ClientContext.saveUserPref();
	},

	onWindowResize : function(e) {
		$(document.body)[(document.viewport.getWidth() > 1570 ? "add" : "remove") + "ClassName"]("centered");
	},

	onPageContentMouseUp : function(e) {
		if (e.button >= 2 && !Object.isUndefined(mp) && !Object.isUndefined(mp.onContextMenu)) {
			return mp.onContextMenu(e);
		} else {
			return true;
		}
	},

	onBodyKeyPress : function(e) {
		switch (e.keyCode) {
			case Event.KEY_ESC :
				var win = Windows.focusedWindow;
				if (win) {
					if (Object.isFunction("cancelCallback")) {
						win.cancelCallback();
					} else if (Object.isFunction("okCallback")) {
						win.okCallback();
					} else if (win.options.closable) {
						win.close();
					}
					e.stop();
					return false;
				}
				break;
		}
		if (!Object.isUndefined(mp) && null != mp && Object.isFunction(mp.onKeyPress)) {
			return mp.onKeyPress(e);
		}
		return true;
	},

	updateSkin : function(skin) {
		skin = skin || UserPref.get('skin');
		if (m_currentSkin == skin) {
			return;
		}
		var skinOld = m_currentSkin;
		m_currentSkin = skin;
		UserPref.set('skin', skin);

		var body = $(document.body);
		var bodyVisible = body.visible();

		if (bodyVisible) {
			body.hide();
		}

		ContextMenu.imgPath = skin + 'img/icon/';

		// remove old stylesheets
		var oLiLink = document.getElementsByTagName('link');
		for (var i = oLiLink.length - 1; i >= 0; i--) {
			var oLink = oLiLink[i];
			if (oLink.type == "text/css") {
				oLink.parentNode.removeChild(oLink);
			}
		}

		var csss = [(bPopup ? 'popup' : 'index'), 'design', 'map', 'mobDetail', 'page', 'menu'];
		var reloadParam = (NOCACHE ? Math.round(Math.random() * 999999) : VERSION);
		if (Prototype.Browser.IE6) {
			createCss(STATIC_PATH + 'open/style/ieHack/pngfix/iepngfix.css?r=' + reloadParam, "skin_ieHack_pngfix");
			csss.push('ie6');
		} else if (Prototype.Browser.IE7) {
			csss.push('ie7');
		} else if (Prototype.Browser.IE8) {
			csss.push('ie8');
		}
		csss.each(function(s) {
					createCss(skin + 'css/' + s + '.css?r=' + reloadParam, "skin_" + s.replace(/\//g, '_'))
				});

		// modify all img
		if (skinOld) {
			var oLiImg = document.getElementsByTagName('img');
			for (var i = 0; i < oLiImg.length; i++) {
				var oImg = oLiImg[i];
				if (oImg.src.indexOf(skinOld) != -1) {
					oImg.src = oImg.src.replace(skinOld, skin);
				}
			}
		}

		if (bodyVisible) {
			body.show();
		}
	},

	fullscreen : function() {
		$(document.body).toggleClassName('fullscreen');
	},

	updateContext : function(data) {

		var context = data['_context'];

		m_sThisI18n = context['i18n'];

		if (!m_init) {
			return;
		}

		this.loginRefresh(data);

		if (!bPopup) {
			// $('__noSqlQueries').innerHTML = Number($('noSqlQueries').innerHTML) + data['_sql'];
			m_oDate = Date.fromServer(context.hour);
			$('__serverTime').innerHTML = Date.fromServerToHtml(context.hour, Date.patterns.ISODT);

			if (0 < context.sn.length) {
				SnReader.parse(context.sn);
			}
		}

		if (DEVMODEFIRE && data['_debug'] && data['_debug'].length > 0) {
			console.log.apply(console, data['_debug']);
		}

		if (data['_accessDenied']) {
			Dialog.alert(ti('accessDenied.msg'), {
						title : ti('accessDenied.title'),
						className : "warn alert"
					});
			return;
		}

		if (data['_stopHack']) {
			var html = htmlTitle(ti("stopHack.title")) + '<div class="textLayer">' + ti("stopHack.t1") + '<br/>'
					+ '<ul>' + '<li>' + ti("stopHack.t2") + '</li>' + '<li>' + ti("stopHack.t3") + '</li>' + '<li>'
					+ ti("stopHack.t4") + '</li>' + '<li>' + ti("stopHack.t5") + '</li>' + '</ul>' + '<br/>' + '<br/>'
					+ ti("stopHack.t6") + '<br/>' + '<ul>' + '<li>' + ti("stopHack.t7") + ' = '
					+ data['_stopHack'].fileName + '</li>' + '<li>' + ti("stopHack.t8") + ' = '
					+ data['_stopHack'].lineNumber + '</li>' + '</ul>' + '</div>';
			PageManager.resetPageContent(true);
			PageManager.setPageContent(html);
			mp = null;
			return;
		}

		if (m_warnOnVersionChanged && context.version.substr(0, context.version.lastIndexOf('.')) != m_versionMin) {
			Dialog.confirm(ti("versionChanged"), {
						className : "warn alert",
						onOk : function(win) {
							win.close();
							self.location.reload(true);
						},
						onCancel : function() {
							m_warnOnVersionChanged = false;
						}
					});
		}
	},

	loginRefresh : function(data, bForce) {
		var session = data["_context"].session || {};
		session.id = session.id || null;
		session.log = !!session.id;
		session.logLite = !!session.name;
		session.newbie = session.bonusPoint < 15;

		var sessionChanged = !m_session || session.log !== m_session.log || session.logLite !== m_session.logLite;

		if (!session.battles && m_session) {
			session.battles = m_session.battles;
		}
		m_session = session;

		if (bForce || sessionChanged) {
			if (!session.log) {
				UserPref.reset();
			} else if (m_session.prefs) {
				UserPref.setFlat(m_session.prefs);
			}
			this.updateSkin();

			if (!bPopup) {
				if (session.log) {
					Chat.restore.bind(Chat).delay(0.5);
					if (UserPref.get('quickStart')) {
						PageManager.navigateToQuickStart(true);
					}

					var link = document.createElement("link");
					link.id = "header_rss_playerBattle";
					link.rel = "alternate";
					link.type = "application/rss+xml";
					link.title = ti('rss.playerBattle');
					link.href = "rss/playerBattle/" + session.name;
					document.getElementsByTagName('head')[0].appendChild(link);
				} else {
					var rssLink = $('header_rss_playerBattle');
					if (rssLink) {
						rssLink.remove();
					}
				}

				this.menuDisplay(this.genMenuItems, "gen");
				this.menuDisplay(this.headerMenuItems, "header");
				this.menuDisplay(this.extMenuItems, "ext");
			}
		}

		if (!bPopup && m_session.log) {
			var html = $A(m_session.battles).collect(function(battle) {
				var txt = ti('manuBattleItem', null, {
							player : battle.enemys.collect(function(team) {
										return team.name || ti('ai.name');
									}).join(' & '),
							turnNum : battle.turnNum,
							turnPlayer : battle.active_name,
							turnPlayerOnline : ('<span class="' + (battle.active_online ? "online" : "offline") + '"> </span>')
						});
				var className = 'active' + (battle.active_id == m_session.id ? 1 : 0);
				return '<li class="'
						+ className
						+ '">'
						+ (('<a href="javascript:void(0);" onclick="detail(\'battle\', ' + battle.id + ');">')
								+ ('<img src="' + m_currentSkin + '/img/battleType' + battle.type + 'Smallest.png" />') + txt + '</a>')
						+ '</li>';
			}.bind(this)).join('');
			$('menuBattleList').innerHTML = html ? ('<ul>' + html + '</ul>') : '';
		}
	},

	i18nswitch : function(i18n) {
		var s = "?url=" + m_sThisPage + "&i18n=" + i18n;
		$H(m_aThisParam).each(function(param) {
					if ('i18n' != param.key) {
						s += "&" + param.key + "=" + param.value;
					}
				});
		self.location.hash = s;
		self.location.reload();
	},

	login : function() {
		var html = ('<form name="formLoginPopup" js="o/accountLogin" url="o/accountLogin" style="margin:10px;">'
				+ ('<input type="text" class="text fullWidth" style="margin-bottom:3px;" name="name" /><br/>')
				+ ('<input type="password" class="text fullWidth bottomSpace" name="password" style="margin-bottom:3px;" /><br/>')
				+ ('<div class="checkList">'
						+ '<input type="checkbox" name="rememberMe" id="popupLogin_rememberMe" value="1" />'
						+ ('<label for="popupLogin_rememberMe" class="size-1">' + ti('loginForm.rememberMe') + '</label>') + '</div>')
				+ ('<div class="topSpace center">'
						+ ('<input type="button" class="validate w100" value="' + ti('loginForm.submit') + '" name="validate"/>') + '</div>') + '</form>')
				+ ('<div class="topSpace bottom right" style="margin-right:5px;">'
						+ ('[<a href="javascript:void(PageManager.goTo(\'o/accountRegister\'));">'
								+ ti("accountRegister") + '</a>]') + '</div>')
				+ ('<div class="topSpace right" style="margin-right:5px;">'
						+ ('[<a href="javascript:void(0);" onclick="ClientContext.loginRetrieve(event);">'
								+ ti("loginForm.retrieve") + '</a>]') + '</div>')
				+ ('<div style="display:none">'
						+ '<form name="formLoginRetrieve" id="formLoginRetrieve" js="o/accountRetrieve" url="o/accountRetrieve" style="margin:5px;" class="center">'
						+ ('<div class="bottomSpace">' + ti("loginRetrieveForm.login") + ' : <input type="text" name="login" class="text"/></div>')
						+ ('<div class="bottomSpace">' + ti("loginRetrieveForm.or") + ' ' + ti("loginRetrieveForm.mail") + ' : <input type="text" name="mail" class="text"/></div>')
						+ ('<input type="button" class="validate w100" value="' + ti('loginRetrieveForm.submit') + '" name="validate"/>')
						+ '</form>' + '</div>');

		var win = new Window({
					id : "formLoginWin",
					width : 210,
					height : 190,
					title : ti('loginForm.title'),
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true
				});
		win.setHTMLContent(html);
		win.showCenter(true);
		form_parseAll();

		var oForm = new Form('formLoginPopup');
		Form.setEmptyValue(oForm.getElm('name', true), ti('loginForm.login'));
		Form.setEmptyValue(oForm.getElm('password', true), "********");
	},

	loginRetrieve : function(e) {
		var win = new Window({
					id : "formLoginRetrieveWin",
					width : 300,
					height : 95,
					top : Event.pointerY(e),
					left : Event.pointerX(e),
					title : ti("loginRetrieveForm.desc"),
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true
				});
		win.setContent($("formLoginRetrieve"));
		win.showCenter(true);
	},

	saveUserPref : function(callback) {
		callback = callback || function() {
		};
		if (m_session.log) {
			var prefs = UserPref.getFlat();
			try {
				new LoadDoc(callback, new DocToLoad('api', 'o/accountEditPrefs', [["prefs", prefs]]));
			} catch (e) {
				callback();
			}
		} else {
			callback();
		}
	},

	computeTimezone : function() {
		return (new Date().getTimezoneOffset()) / 60 * -1;
	},

	logout : function() {
		PageManager.goTo('o/accountLogout');
	},

	loginVerify : function() {
		if (m_session && !m_session.log) {
			Dialog.alert(ti('haveToBeLog'));
			return false;
		}
		return true;
	}

};
Event.observe(window, 'load', ClientContext.init.bind(ClientContext));
/** ************************************************************************** */

/*************************************************************************************************************
 * Navigation et Affichage
 ************************************************************************************************************/
var PageManager = {
	intervals : [],

	unloadPageContent : function() {
		var iIntervalId;
		while (iIntervalId = this.intervals.pop()) {
			window.clearInterval(iIntervalId);
		}
		if (mp && mp.onunload && Object.isFunction(mp.onunload)) {
			mp.onunload();
		}
	},

	resetPageContent : function(bForce) {
		if (bForce) {
			this.unloadPageContent();
		}
		if (m_menus) {
			$H(m_menus).each(function(pair) {
						pair.value.rootContainer.closeAll();
					})
		}
		Windows.windows.each(function(w) {
					closeOnNavigate = w.options.closeOnNavigate;
					if (Object.isUndefined(closeOnNavigate)) {
						closeOnNavigate = true;
					} else if (Object.isNumber(closeOnNavigate)) {
						closeOnNavigate = (--w.options.closeOnNavigate < 0);
					}
					if (closeOnNavigate) {
						w.close();
					}
				});
		Tabgroup.cleanAll();
		this.setPageContent("");
		if (m_sThisPage) {
			document.body.removeClassName(m_sThisPage.replace(/\//g, '_'));
		}
	},

	setPageContent : function(html, containerId) {
		var pageContentId = 'page_content';
		var ctn = $(containerId || pageContentId);
		ctn.innerHTML = html.replace(/(\\r)?\\n/g, '<br/>');
		form_parseAll();
	},

	registerInterval : function(interval) {
		this.intervals.push(interval);
	},

	detectLocationChange : function() {
		var hash = PageManager.getHashFromLocation();
		if (hash && hash != m_sThisHash) {
			this.goTo(hash);
		}
	},

	getHashFromLocation : function() {
		var sUrl = window.location.href;
		sUrl = (sUrl.indexOf("url=") > -1 ? sUrl.substr(sUrl.indexOf("url=") + 4) : null);
		return sUrl;
	},

	goTo : function(link) {
		var res;
		var pos = link.indexOf('://');
		if (-1 == pos) {
			this.navigateTo.apply(this, arguments);
		} else {
			var action = link.substring(pos + 3);
			var protocol = link.substr(0, pos);
			switch (protocol) {
				case 'http' :
				case 'https' :
					window.open(action.startsWith('./') ? action : link);
					break;
				case 'forum' :
					var pos2 = action.indexOf('/');
					this.navigateToForum(action.substring(0, pos2), action.substring(pos2 + 1));
					break;
				case 'tuto' :
					this.navigateToTuto(action);
					break;
				case 'js' :
					window.setTimeout(action, 0);
					break;
			}
		}
	},

	navigateTo : function(sUrl, sWinName, iWidth, iHeight, scrollBars) {
		sUrl = sUrl || m_sThisPage + m_sThisParams;
		sWinName = sWinName || null;

		if (sUrl.startsWith('r/') && !ClientContext.loginVerify()) {
			return false;
		}

		if (null == sWinName) {
			this.unloadPageContent();

			var sParams = (sUrl.indexOf("&") == -1 ? "" : sUrl.substr(sUrl.indexOf("&")));
			sUrl = (sUrl.indexOf("&") == -1 ? sUrl : sUrl.substr(0, sUrl.indexOf("&")));

			var aParam = {};
			var aTmp = sParams.split('&');
			for (var i = 0; i < aTmp.length; i++) {
				var a = aTmp[i].split('=');
				if (a && a[0]) {
					aParam[a[0]] = (a.length > 0 ? a[1] : '');
				}
			}

			m_sThisHash = sUrl + sParams;
			window.location.hash = "?url=" + m_sThisHash;
			var fJSLoaded = function(text) {
				this.resetPageContent();
				m_sThisPage = sUrl;
				m_sThisParams = sParams;
				m_aThisParam = aParam;
				mpText = m_text[m_sThisPage];
				document.body.addClassName(m_sThisPage.replace(/\//g, '_'));
				if (!Object.isUndefined(mp.XML_AUTO) && mp.XML_AUTO == true) {
					new LoadDoc(mp.onload.bind(mp), new DocToLoad('api', m_sThisPage, $H(m_aThisParam).collect(
											function(pair) {
												return [pair.key, pair.value];
											})));
				} else if (Object.isFunction(mp.onload)) {
					mp.onload();
				}
			}.bind(this);
			new LoadDoc(fJSLoaded, new DocToLoad('js', sUrl));
		} else {
			iWidth = (!iWidth ? (sUrl.substr(0, 2) == 'a/' ? 850 : 750) : Math.min(iWidth, screen.width));
			iHeight = (!iHeight ? (sUrl.substr(0, 2) == 'a/' ? 800 : 550) : Math.min(iHeight, screen.height));
			window.open("popup.html#?url=" + sUrl, sWinName, "top=" + ((screen.height - iHeight) / 2) + ",left="
							+ ((screen.width - iWidth) / 2) + ",width=" + iWidth + ",height=" + iHeight + ",scrollBars="
							+ (false !== scrollBars ? "yes" : "no") + ",resizable=yes");
		}
	},

	navigateToTuto : function(id) {
		this.goTo(Tutorial.items[id]);
	},

	navigateToForum : function(action, extension) {
		if (m_session.log) {
			new LoadDoc(function(content) {
						window.open(FORUM_PATH_HANDSHAKE.interpolate({
									handshake : content.handshake,
									action : action || "",
									extension : extension ? escape(extension) : ""
								}));
					}, new DocToLoad('api', 'r/getHandshake'));
		} else {
			window.open(FORUM_PATH);
		}
	},

	navigateToChat : function() {
		Chat.open.apply(Chat, arguments);
	},

	navigateToAvantajeux : function() {
		if (!m_session.log) {
			return;
		}
		if (Prototype.Browser.Gecko) {
			var win = new Window({
						width : 230,
						height : 650,
						title : "Kredz",
						minimizable : false,
						maximizable : false,
						wiredDrag : true,
						destroyOnClose : true,
						onClose : function() {
							PageManager.goTo('');
						}
					});
			win.setHTMLContent('<div style="overflow:hidden;height:100%">'
					+ '<iframe style="width:100%;height:100%;border:none;" name="avantajeuxIframe"></iframe>'
					+ '</div>');
			win.showCenter(true);
		}
		this.navigateToColyseoSSO('Kredz', function(url) {
					window.open(AVANTAJEUX_PATH.interpolate({
										url : url
									}), 'avantajeuxIframe', 'width=230,height=650');
				});
	},

	navigateToColyseoSSO : function(gotoId, f) {
		if (!Object.isFunction(f)) {
			f = function(url) {
				window.open(url);
			}
		}
		new LoadDoc(function(content) {
					var url = content.url;
					if (url) {
						f(url);
					} else {
						alert('error');
					}
				}, new DocToLoad('api', 'r/getColyseoSSOUrl', [['goto', gotoId]]));
	},

	navigateToFAQ : function() {
		var html = ('<div style="margin:10px;"><ul>' + $A(ti('faq.items')).collect(function(item, index) {
					return '<li><a href="#faq' + index + '" class="size-1">' + item.q + '</a></li>';
				}).join('') + '</ul></div>') + '<br/>'
				+ ('<div>' + $A(ti('faq.items')).collect(function(item, index) {
					return '<a name="faq' + index + '"></a><h3>' + item.q + '</h3>' + '<div class="textLayer">'
							+ item.r + '</div>';
				}).join('<br/>') + '<br/>' + '</div>');
		var win = new Window({
					width : 900,
					height : 500,
					title : ti("faq.title"),
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true
				});
		win.setHTMLContent(html);
		win.showCenter(true);
	},

	navigateToQuickStart : function(preventFirstClose) {
		var winId = 'quickStart';
		if (Windows.getWindow(winId)) {
			return;
		}

		var textParam = {
			tutos : ('<ul>' + $H(Tutorial.items).keys().collect(function(tutoId) {
				return '<li style="float:left;margin-right:40px;">'
						+ ('<a href="javascript:void(PageManager.goTo(\'tuto://' + tutoId + '\'));">'
								+ Tutorial.itemName(tutoId) + '</a>') + '</li>';
			}).join('') + '<div style="clear:left;"></div></ul>'),
			link : {
				rules : '<a href="javascript:void(PageManager.goTo(\'o/gameHelp\'));">',
				chat : '<a href="javascript:void(PageManager.navigateToChat());">',
				forum : '<a href="javascript:void(PageManager.navigateToForum());">',
				newBattle : '<a href="javascript:void(PageManager.goTo(\'r/battleList&new=1\'));">',
				newBattleAi : '<a href="javascript:void(PageManager.goTo(\'r/battleList&newAi=1\'));">',
				end : '</a>'
			}
		};
		var html = ('<div class="textLayer">' + ti('quickStart.pres') + '</div>')
				+ ('<div>' + $A(ti('quickStart.items')).collect(function(item, index) {
					return ('<h3>' + item.q + '</h3>')
							+ ('<div class="textLayer">' + item.r.interpolate(textParam) + '</div>');
				}).join('') + '</div>') + '<br/>';

		html += ('<div class="topSpace bottomSpace center" style="position:relative;">'
				+ (m_session.log ? ('<div style="position:absolute;left:5px;top:2px;" class="size-1">'
						+ ('<input type="checkbox" onclick="UserPref.set(\'quickStart\',!UserPref.get(\'quickStart\'))" '
								+ (!UserPref.get('quickStart') ? 'checked="checked"' : '') + ' id="quickStartPref"/>') + ' '
						+ ('<label for="quickStartPref">' + ti('quickStart.pref') + '</label>') + '</div>') : '')
				+ ('<input type="button" class="button" value="' + ti('quickStart.close')
						+ '" onclick="Windows.close(\'' + winId + '\', event);"/>') + '</div>')

		var win = new Window({
					id : winId,
					width : 720,
					title : ti("quickStart.title"),
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					closeOnNavigate : (preventFirstClose ? 1 : true),
					destroyOnClose : true
				});
		win.setHTMLContent(html);
		win.showCenter(false);
		win.updateHeight();
		win.showCenter(false);
	},

	showDetail : function(sType, sId) {
		switch (sType) {
			case 'player' :
				PlayerDetailWin.show(sId);
				break;
			case 'team' :
				TeamDetailWin.show(sId);
				break;
			case 'map' :
				this.goTo('o/mapDetail&id=' + sId, 'mapDetail', 900, 500);
				break;
			case 'heroe' :
				LivingBeingDetailWin.showHeroe(sId, "object" == typeof(arguments[2]) ? arguments[2] : {});
				break;
			case 'mob' :
				LivingBeingDetailWin.showMob(sId, "object" == typeof(arguments[2]) ? arguments[2] : {});
				break;
			case 'mapElm' :
				LivingBeingDetailWin.showMapElm(sId, "object" == typeof(arguments[2]) ? arguments[2] : {});
				break;
			case 'battle' :
				if (true === arguments[2]) {
					this.goTo('b/map&replay=1&id=' + sId, 'battleDetail', 1030, 670, false);
				} else {
					this.goTo('b/map&id=' + sId);
				}
				break;
			default :
				alert('Détail de : ' + sType + ' ; id=' + sId);
				break;
		}
	}

};
setPageContent = PageManager.setPageContent.bind(PageManager);
// goToLink = PageManager.goTo.bind(PageManager);
// navigateTo = PageManager.navigateTo.bind(PageManager);
detail = PageManager.showDetail.bind(PageManager);

var Chat = {
	userPrefKey : "chatState",
	existingWindow : null,
	existingPopup : null,
	existingPopupInterval : null,

	open : function(mode) {
		if (!ClientContext.loginVerify()) {
			return false;
		}
		this.close();
		mode = mode || "popup";
		UserPref.set(this.userPrefKey, mode);
		switch (mode) {
			case "window" :
				this.openWindow();
				break;
			case "popup" :
				this.openPopup();
				break;
		}
	},

	restore : function() {
		this.open(UserPref.get(this.userPrefKey));
	},

	close : function() {
		if (this.existingWindow) {
			try {
				this.existingWindow.close();
			} catch (e) {
			};
		}
		if (this.existingPopup) {
			try {
				this.existingPopup.close();
			} catch (e) {
			};
		}
		this.markClosed(true);
	},

	markClosed : function(noSave) {
		this.existingWindow = null;
		this.existingPopup = null;
		window.clearTimeout(this.existingPopupInterval);
		if (true !== noSave) {
			UserPref.set(this.userPrefKey, "none");
		}
	},

	getChatUrl : function() {
		return CHAT_PATH.interpolate({
					timezone : ClientContext.computeTimezone()
				});
	},

	openWindow : function() {
		this.existingWindow = window.open(this.getChatUrl(), "lomChat",
				"left=200,top=150,width=700,height=500,scrollBars=no,resizable=yes");
	},

	openPopup : function() {
		this.existingPopup = new Window({
			closeOnNavigate : false,
			top : 5,
			left : 5,
			minWidth : 300,
			width : 450,
			minHeight : 150,
			height : 210,
			title : ti('chat.title')
					+ '<span id="chat_noNews"></span>'
					+ " "
					+ ('<span class="miniLink" style="margin-left:50px;">['
							+ '<a href="javascript:void(Chat.open(\'window\'));">' + ti('chat.realPopup') + '</a>' + ']</span>'),
			minimizable : true,
			maximizable : false,
			wiredDrag : true,
			destroyOnClose : true,
			onDestroy : function(ev) {
				Chat.markClosed();
			},
			onBeforeMove : function(ev) {
				return Event.findElement(ev, '.miniLink') ? false : true;
			},
			onMinimize : function(win) {
				if (win.isMinimized()) {
					this.existingPopupInterval = window.setInterval(this.updatePopupTitle.bind(this), 1000);
				} else {
					window.clearTimeout(this.existingPopupInterval);
					this.updatePopupTitleNews(0);
				}
			}.bind(this)
		});
		this.existingPopup.setCookie("chatPopupState");
		this.existingPopup.setURL(this.getChatUrl());
		this.existingPopup.show(false);
	},

	updatePopupTitle : function() {
		try {
			var title = window.frames[this.existingPopup.content.name].document.title;
			var noNew = title.startsWith("[") ? title.substr(1, title.indexOf(']') - 1) : 0;
			this.updatePopupTitleNews(noNew);
		} catch (e) {
		}
	},

	updatePopupTitleNews : function(no) {
		$("chat_noNews").innerHTML = no > 0 ? ' * ' + no + ' *' : '';
	}
};

var SnReader = {
	stack : {},
	stackChanged : false,
	win : null,

	parse : function(liSn) {
		if (0 == liSn.length) {
			return;
		}
		SnReader.stackChanged = true;
		$A(liSn).each(function(sn) {
					SnReader.stack[sn.id] = sn;
				});
	},

	read : function() {
		if (!SnReader.stackChanged) {
			return;
		}
		SnReader.stackChanged = false;

		var liMpToDisplay = $H(SnReader.stack).values().select(function(sn) {
					return (mp && Object.isFunction(mp.ignoreSn)) ? !mp.ignoreSn(sn) : true;
				}).sortBy(function(sn) {
					return sn.id;
				});
		if (0 == liMpToDisplay.length) {
			SnReader.stack = {};
			return;
		}
		var html = '<ul>'
				+ liMpToDisplay.collect(function(sn) {
					var message = sn.message;
					switch (sn.type) {
						case "battleValidate" :
						case "battleNewTurn" :
						case "battleEnd" :
							var bid = sn.ident.split(";")[0];
							message += ' [<a href="javascript:void(0);" class="size-1" onclick="SnReader.win.close();detail(\'battle\', '
									+ bid + ');">' + ti('snReader.seeLink') + '</a>]';
							break;
						case "battleNew" :
							message += ' [<a href="javascript:void(0);" class="size-1" onclick="SnReader.win.close();PageManager.goTo(\'r/battleList&activeTab=standbyMe\');">'
									+ ti('snReader.seeLink') + '</a>]';
							break;
						case "saleWin" :
							var sid = sn.ident.split(";")[0];
							message += ' [<a href="javascript:void(0);" class="size-1" onclick="SnReader.win.close();PageManager.goTo(\'r/saleList&saleDetail='
									+ sid + '\');">' + ti('snReader.seeLink') + '</a>]';
							break;
						case "newPm" :
							var noMp = parseInt(message);
							message = ti('snReader.newMp', noMp, {
										no : noMp
									})
									+ ' [<a href="javascript:void(0);" class="size-1" onclick="SnReader.win.close();PageManager.goTo(\'forum://privmsg/folder=inbox\')">'
									+ ti('snReader.seeLink') + '</a>]';
							break;
					}
					return '<li>'
							+ (sn.date ? '<span class="size-1">' + Date.fromServerToHtml(sn.date) + '</span> : ' : '')
							+ message + '</li>'
				}).join('') + '</ul>' + '<div class="topSpace bottomSpace center">'
				+ '<input type="button" class="validate" value="Ok" onclick="SnReader.win.close();"/>' + '</div>';

		if (null == SnReader.win) {
			SnReader.win = new Window({
						title : ti("alert.mpTitle"),
						closeOnNavigate : false,
						width : 500,
						height : 50,
						top : 2,
						right : 2,
						minimizable : false,
						maximizable : false,
						onClose : function() {
							SnReader.stack = {};
						},
						wiredDrag : true
					});
		}
		SnReader.win.setHTMLContent(html);
		if (SnReader.win.isMinimized()) {
			SnReader.win.minimize();
		}
		SnReader.win.toFront();
		SnReader.win.showCenter(false);
		SnReader.win.updateHeight();
	}
}

Tipshower = {
	messages : null,
	interval : 30,

	currentIndex : null,
	timer : null,

	init : function(message, interval) {
		if (bPopup) {
			return false;
		}

		this.messages = message || this.messages;
		if (!Object.isArray(this.messages) || 0 == this.messages.length) {
			this.messages = [""];
		}
		this.interval = interval || this.interval;

		this.currentIndex = Math.floor(Math.random() * this.messages.length) - 1;

		this.showOne();
	},

	showOne : function() {
		window.clearInterval(this.timer);

		this.currentIndex++;
		if (this.currentIndex > this.messages.length - 1) {
			this.currentIndex = 0;
		}
		$('headerSubtitle').innerHTML = '<span onclick="Tipshower.showOne();" class="hand">'
				+ this.messages[this.currentIndex] + '</a>';

		if (this.messages.length > 1) {
			this.timer = window.setTimeout(this.showOne.bind(this), this.interval * 1000);
		}
	}
}
/** ************************************************************************** */

/*************************************************************************************************************
 *
 ************************************************************************************************************/
var MobList = Class.create();
MobList.prototype = {
	options : null,
	style : null,
	data : null,
	currentPage : null,
	noElmShown : null,
	initialize : function(options) {
		this.options = Object.extend({
					emptyResText : ti("mobList.emptyRes")
				}, options || {});
		this.options.styleColumns = Object.extend({
					charact : true
				}, this.options.styleColumns || {});
		this.options.styleDetail = Object.extend({
					width : '19%'
				}, this.options.styleDetail || {});
		this.style = UserPref.get("listViewStyle");
	},
	setData : function(data) {
		this.data = data;
		this.show();
	},
	setStyle : function(style) {
		if (this.style != style) {
			if (Prototype.Browser.IE6 && style == "detail") {
				style = "columns";
				Dialog.alert(ti('browserCompatibilityError'));
			}
			this.style = style;
			UserPref.set("listViewStyle", style);
			this.show();
		}
	},
	show : function() {
		var liMob = this.data.result;
		var pageNum = this.data.pageNum;
		var hasMore = (!Object.isUndefined(this.data.hasMore) ? this.data.hasMore : true);
		this.currentPage = (!pageNum || pageNum == 1 && liMob.length == 0 ? 0 : pageNum);
		var htmlArround = '<div style="margin-bottom:3px;">'
				+ ('<div style="float:right;margin-bottom:3px;" class="listViewStyleSelector">'
						+ ('<a href="javascript:void(0);" title="" forStyle="detail">'
								+ ('<img src="' + m_currentSkin + 'img/view-detail.png" align="absmiddle"/>') + '</a>')
						+ ' '
						+ ('<a href="javascript:void(0);" title="" forStyle="columns">'
								+ ('<img src="' + m_currentSkin + 'img/view-columns.png" align="absmiddle"/>') + '</a>') + '</div>')
				+ ('<div style="float:left">'
						+ (this.currentPage > 1 || this.data.hasMore ? htmlPager(this.currentPage, hasMore) : '&#160;') + '</div>')
				+ '</div>' + ('<div style="clear:both;"></div>');
		var html;
		this.noElmShown = 0;
		if (liMob.length > 0) {
			switch (this.style) {
				case "columns" :
					var htmlHeader = ''
							+ ('<td class="header" width="100">' + ti("mobStat.family").capitalize() + '</td>')
							+ ('<td class="header">' + ti("mobStat.name").capitalize() + '</td>')
							+ ('<td class="header" width="40">' + ti("mobStat.value").capitalize() + '</td>')
							+ ('<td class="header" width="100">' + ti("mobStat.scarcity").capitalize() + '</td>')
							+ (this.options.styleColumns.charact ? ('<td class="header" width="25" title="'
									+ ti("mobStat.discipline").capitalize() + '">' + ti("mobStat.dis") + '</td>')
									+ ('<td class="header" width="25" title="' + ti("mobStat.agility").capitalize() + '">'
											+ ti("mobStat.agi") + '</td>')
									+ ('<td class="header" width="25" title="' + ti("mobStat.regen").capitalize() + '">'
											+ ti("mobStat.reg") + '</td>')
									+ ('<td class="header" width="25" title="' + ti("mobStat.health").capitalize() + '">'
											+ ti("mobStat.hea") + '</td>') : '');
					html = '<table style="width:100%">'
							+ '<tr>'
							+ (Object.isFunction(this.options.headerFn)
									? this.options.headerFn(this, htmlHeader)
									: htmlHeader) + '</tr>' + $A(liMob).collect(function(mob) {
								var htmlRow = ''
										+ ('<td class="border">' + mob.refFamily_name + '</td>')
										+ ('<td class="border">'
												+ ('<a href="javascript:void(detail(\'mob\', ' + mob.id + '));">' + mob.name + '</a>') + '</td>')
										+ ('<td class="border right">' + mob.value + '</td>')
										+ ('<td class="border">' + mob.refScarcity_name + '</td>')
										+ (this.options.styleColumns.charact
												? ('<td class="border right">' + mob.disciplineRef + '</td>')
														+ ('<td class="border right">' + mob.agilityRef + '</td>')
														+ ('<td class="border right">' + mob.regenRef + '</td>')
														+ ('<td class="border right">' + mob.healthRef + '</td>')
												: '');
								htmlRow = (Object.isFunction(this.options.rowFn)
										? this.options.rowFn(this, htmlRow, mob)
										: htmlRow);
								if (htmlRow) {
									this.noElmShown++;
								}
								return !htmlRow ? '' : ('<tr>' + htmlRow + '</tr>');
							}.bind(this)).join('') + '</table>';
					break;
				case "detail" :
					html = '<div style="position:relative;clear:both;">' + $A(liMob).collect(function(mob) {
						var htmlRow = ('<div class="name">' + '<a href="javascript:void(detail(\'mob\', ' + mob.id
								+ '));">' + mob.name + '</a></div>')
								+ ('<div>' + mob.refFamily_name + '</div>')
								+ ('<div>' + mob.refScarcity_name + '</div>')
								+ ('<div>' + ti("mobStat.value").capitalize() + ' ' + mob.value + '</div>');
						htmlRow = (Object.isFunction(this.options.rowFn)
								? this.options.rowFn(this, htmlRow, mob)
								: htmlRow);
						if (htmlRow) {
							this.noElmShown++;
						}
						return !htmlRow
								? ''
								: ('<div class="boxMob" style="width:' + this.options.styleDetail.width + ';">')
										+ ('<div class="avatar">'
												+ (('<a href="javascript:void(detail(\'mob\', ' + mob.id + '));">')
														+ ('<img class="' + htmlClassMobBorder(mob.refScarcity_id, mob.refFamily_id)
																+ '" src="' + STATIC_PATH + 'open/img/mob/' + mob.id + 'l.png" align="absmiddle"/>') + '</a>') + '</div>')
										+ ('<div class="info">' + htmlRow + '</div>') + '</div>';
					}.bind(this)).join('') + '<div style="clear:both;"> </div></div>';
					break;
				default :
					html = 'undefined style.<br/>';
					break;
			}
			html += (this.options.rq ? '<div class="size-1" style="clear:both;">' + this.options.rq + '</div>' : '');
		}
		if (0 == this.noElmShown) {
			html = this.options.emptyResText;
		}
		html = htmlArround + html/* + htmlArround */;

		$(this.options.ctn).innerHTML = html;

		$(this.options.ctn).select(".listViewStyleSelector a").each(function(elm) {
					elm.observe("click", this.setStyle.bind(this, elm.getAttribute("forStyle")));
				}.bind(this));
		if (Object.isFunction(this.options.afterShowFn)) {
			this.options.afterShowFn(this);
		}
	}
};

var LivingBeingDetailWin = {
	winName : "livingBeingDetailWin",
	tmpDiv : null,

	retrieveOne : function(type, id) {
		var win = Windows.getWindow(this.winName + type + '_' + id);
		if (!Object.isUndefined(win) && null != win) {
			win.toFront();
			return true;
		}
		return false;
	},

	showMapElm : function(id, options) {
		this.show(ENTITY_TYPE.mapElm, id, Object.extend({
							title : ti("mapElm").capitalize()
						}, options || {}))
	},

	showHeroe : function(id, options) {
		this.show(ENTITY_TYPE.heroe, id, Object.extend({
							title : ti("heroe").capitalize()
						}, options || {}))
	},

	showMob : function(id, options) {
		this.show(ENTITY_TYPE.mob, id, Object.extend({
							title : ti("mob").capitalize()
						}, options || {}))
	},

	show : function(type, id, options) {
		if (!this.retrieveOne(type, id)) {
			if (null == this.tmpDiv) {
				this.tmpDiv = new Element('div');
				document.body.appendChild(this.tmpDiv);
				this.tmpDiv.setStyle({
							visibility : "hidden",
							zIndex : -100000,
							position : "absolute",
							top : 0,
							left : 0
						});
			} else {
				this.tmpDiv.update();
			}
			LivingBeingDetail.show(type, id, this.tmpDiv, 70, this.render.bind(this, type, id, options || {}),
					this.resizeWin.bind(this, type, id));
		}
	},

	render : function(type, id, options, data) {
		var realContent = $(this.tmpDiv.childNodes[0]);
		var dim = realContent.getDimensions();
		dim.width += 8;
		dim.height += 8;

		var win = new Window(Object.extend({
					id : this.winName + type + '_' + id,
					width : dim.width,
					height : dim.height,
					minimizable : false,
					maximizable : false,
					resizable : false,
					wiredDrag : true,
					recenterAuto : false,
					destroyOnClose : true
				}, options));
		win.setContent(realContent.remove());
		win.setSize(dim.width, dim.height);
		win.showCenter(true);
		window.setTimeout('LivingBeingDetailWin.resizeWin(' + id + ');', 100);
	},

	resizeWin : function(type, id) {
		var win = Windows.getWindow(this.winName + type + '_' + id);
		if (!Object.isUndefined(win) && null != win) {
			win.updateHeight();
			win._center();
		}
	}

}

var LivingBeingDetail = {
	show : function(type, id, container, heightBonus, renderCallback, resizeCallback) {
		var cbBinderParams = [type, container, heightBonus, renderCallback, resizeCallback];
		switch (type) {
			case (ENTITY_TYPE.mapElm) :
				new LoadDoc(LivingBeingDetail.display.bind.apply(LivingBeingDetail.display, [LivingBeingDetail]
										.concat(cbBinderParams)), new DocToLoad('api', 'o/mapElmDetail', [["id", id]]),
						new DocToLoad('img', STATIC_PATH + 'open/img/mapElm/' + id + '.jpg'));
				break;
			case (ENTITY_TYPE.heroe) :
				new LoadDoc(LivingBeingDetail.loadHeroeImg.bind(LivingBeingDetail, cbBinderParams), new DocToLoad(
								'api', 'o/heroeDetail', [["id", id]]));
				break;
			case (ENTITY_TYPE.mob) :
				new LoadDoc(LivingBeingDetail.display.bind.apply(LivingBeingDetail.display, [LivingBeingDetail]
										.concat(cbBinderParams)), new DocToLoad('api', 'o/mobDetail', [["id", id]]),
						new DocToLoad('img', STATIC_PATH + 'open/img/mob/' + id + '.jpg'));
				break;
		}
	},

	loadHeroeImg : function(cbBinderParams, data) {
		cbBinderParams.push(data);
		var img = (data.heroe.img ? SERVER_PATH + data.heroe.img : STATIC_PATH + 'open/img/heroe/'
				+ data.heroe.refFamily_id + '.jpg');
		new LoadDoc(LivingBeingDetail.display.bind.apply(LivingBeingDetail.display, [LivingBeingDetail]
								.concat(cbBinderParams)), new DocToLoad('img', img));
	},

	display : function(type, container, heightBonus, renderCallback, resizeCallback, data, oImg) {
		var lbRef = null;
		var isMapElm = false;
		var isHeroe = false;
		var isMob = false;
		switch (type) {
			case (ENTITY_TYPE.mapElm) :
				lbRef = data.refMapElm;
				isMapElm = true;
				break;
			case (ENTITY_TYPE.heroe) :
				lbRef = data.heroe;
				isHeroe = true;
				break;
			case (ENTITY_TYPE.mob) :
				lbRef = data.refMob;
				isMob = true;
				break;
		}

		var base = new Element("div", {
					"class" : "mobDetail" + (isMob ? " family" + lbRef.refFamily_id : "")
				});
		var imgCtn2 = base.appendChild(new Element("div", {
					"class" : "img2",
					"style" : (Prototype.Browser.IE ? null : "float:right;")
				}));
		var o = base.appendChild(new Element("span", {})).appendChild(new Element("div", {
					"class" : "principal",
					"style" : "display:block;"
				}));
		o.appendChild((new Element("div", {
					"class" : "title"
				})).update('<div class="name">' + lbRef.name + '</div>'));
		if (isHeroe) {
			o.appendChild((new Element("div", {
						"class" : "owner"
					})).update('<div class="owner">' + ti('heroeStat.owner').capitalize()
					+ ' <a href="javascript:void(detail(\'player\', ' + lbRef.player_id + '));">' + lbRef.player_name
					+ '</a></div>'));

		}
		var imgCtn1 = o.appendChild(new Element("div", {
					"class" : "img1"
				}));
		o.appendChild(new Element("div", {
					"class" : "middle"
				}).update((isMob || isHeroe ? ('<div class="family" title="' + ti("mobStat.family").capitalize()
				+ '">' + lbRef.refFamily_name + '</div>') : '')
				+ (isMob ? ('<div class="value">' + ti("mobStat.value").capitalize() + ' ' + lbRef.value + '</div>')
						+ ('<div class="scarcity" title="' + ti("mobStat.scarcity").capitalize() + '">'
								+ lbRef.refScarcity_name + '</div>') : '')
				+ (isHeroe && lbRef.refAward_name ? '<div class="award" '
						+ htmlOverliber(ti("heroeStat.award").capitalize() + ' :<ul>'
								+ $A(lbRef.awards).collect(function(award) {
											return '<li>' + HTMLEscape(award.name) + '</li>'
										}).join('') + '</ul>') + '>[' + lbRef.refAward_name + ']</div>' : '')));
		var dataCtn = o.appendChild(new Element("div", {
					"class" : "data"
				}));

		if (isHeroe) {
			dataCtn.appendChild(new Element("div").update(ti('heroeStat.xpUsedAvailable').capitalize() + ' : '
					+ (lbRef.xp - lbRef.xpAvailable) + ' / ' + lbRef.xp));
			dataCtn.appendChild(new Element("div").update(lbRef.noVictory + ' '
					+ ti('heroeStat.victory', lbRef.noVictory) + ' / ' + lbRef.noDefeat + ' '
					+ ti('heroeStat.defeat', lbRef.noDefeat)));
		}

		if (lbRef.effects.length > 0) {
			var effectCtn = dataCtn.appendChild(new Element("div", {
						"class" : "effectContainer"
					}));

			$A(lbRef.effects).sortBy(function(effect) {
						return data.refEffects[effect.id].visibility;
					}).each(function(effect) {
				var iLvl = effect.lvl;
				var effectRef = data.refEffects[effect.id];

				var costType = effectRef.refEffectType_shortName;
				var iCost = effectRef.cost;
				if (null != iCost) {
					var iCostForLb = Math.min(100, Math.round(iCost
									- (iCost * Math.min(COST_REDUCTION_CAP, lbRef.agilityRef / 100))));
					var iCostForMob, iCostForHeroe;
					if (isMob) {
						iCostForMob = iCostForLb;
						iCostForHeroe = Math.round(iCost
								- (iCost * Math.min(COST_REDUCTION_CAP, lbRef.disciplineRef / 100)));
					} else {
						iCostForHeroe = iCostForLb;
					}
					costType += ', '
							+ ti("effect.cost")
							+ ' <span class="overliber" '
							+ htmlOverliber(ti("effect.baseCost").capitalize() + ': ' + iCost + ', '
									+ (!Object.isUndefined(iCostForMob) ? ti("mob") + ': ' + iCostForMob + ', ' : '')
									+ (isHeroe ? lbRef.name : ti("heroe")) + ': ' + iCostForHeroe) + '>'
							+ (!Object.isUndefined(iCostForMob) ? iCostForMob + '|' : '') + iCostForHeroe + '</span>';
				}
				costType += '. ';

				var effectDescCtn = effectCtn.appendChild((new Element("div", {
							"class" : "effectType" + effectRef.refEffectType_id
						})).update('<span class="effectName">' + effectRef.name + '</span>[' + iLvl + '] : ' + costType));

				var sDescription = HTMLEscape(effectRef.description);

				// compute mathematical expression with ##lvl## in description
				var exprs = [];
				var reg = new RegExp('([\(\)0-9\*\+\/\-]|##lvl##){2,}', 'g');
				while (null != (result = reg.exec(sDescription))) {
					if (/[\*\+\/\-]/.test(result[0])) {
						exprs.push({
									expr : result[0],
									index : result.index
								});
					}
				}
				$A(exprs).sortBy(function(expr) {
							return -1 * expr.index;
						}).each(function(expr) {
					var newVal = expr.expr + '=' + Math.round(eval(expr.expr.replace(/##lvl##/g, iLvl)));
					sDescription = sDescription.substr(0, expr.index) + newVal
							+ sDescription.substr(expr.index + expr.expr.length);
				});

				// replace ##lvl## and ##effect:this## in description
				sDescription = sDescription.replace(/##lvl##/g, '<span class="overliber" '
								+ htmlOverliber(ti("effect.lvl").capitalize()) + '>' + iLvl + '</span>');
				sDescription = sDescription.replace(/##effect:this##/g, '<span class="italic">' + effectRef.name
								+ '</span>');

				// make dependencies descriptions and replace dependencies in description
				$A(effectRef.dependencies).each(function(depId) {
					var depInfos = EffectDependencies.getInfo(data.refEffects, [depId]);
					var depHtml = $H(depInfos).collect(function(pair) {
						var infos = pair.value;
						return '<span class="bold">' + infos.name + '</span>[<span class="italic">'
								+ ti("effect.lvl").capitalize() + '</span>] : ' + infos.typeS
								+ (infos.cost ? ', ' + ti("effect.baseCost") + ' ' + infos.cost : '') + '.<br/>' + infos.desc;
					}).join('<div class="hr"><hr/></div>');

					sDescription = sDescription.replace(new RegExp('##effect:' + depId + '##', 'g'),
							'<span class="overliber italic" ' + htmlOverliber(depHtml) + '>' + depInfos[depId].name
									+ '</span>');
				});

				var effectDescCtnSub = new Element("span");
				effectDescCtnSub.innerHTML = sDescription;
				if (10 > effectRef.visibility) {
					var link = (new Element("a", {
								"href" : "javascript:void(0);"
							})).update(ti("mobDetail.toggleDesc"));
					link.observe("click", LivingBeingDetail.toogleDesc.bindAsEventListener(LivingBeingDetail,
									resizeCallback));
					var tmp = effectDescCtn.appendChild(new Element("span"));
					tmp.appendChild(document.createTextNode("["));
					tmp.appendChild(link);
					tmp.appendChild(document.createTextNode("]"));
					effectDescCtnSub.hide();
				}
				effectDescCtn.appendChild(effectDescCtnSub);
			});
		}

		if (0 == dataCtn.childNodes.length) {
			dataCtn.hide();
		}

		o.appendChild((new Element("div", {
					"class" : "description"
				})).update(HTMLEscape(lbRef.description)));
		var charactCtn = o.appendChild(new Element("div", {
					"class" : "charact"
				}));
		if (isMapElm) {
			charactCtn.appendChild((new Element("div", {
						"class" : "mapElmWalk",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("mapElmStat.walk").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(lbRef.walk));
			charactCtn.appendChild((new Element("div", {
						"class" : "mapElmFlyover",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("mapElmStat.flyover").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(lbRef.flyover));
			charactCtn.appendChild((new Element("div", {
						"class" : "mapElmHealth",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("mapElmStat.health").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(lbRef.healthRef || ti('na')));
		} else if (isHeroe || isMob) {
			charactCtn.appendChild((new Element("div", {
						"class" : "agility",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("mobStat.agility").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(lbRef.agilityRef));
			charactCtn.appendChild((new Element("div", {
						"class" : "discipline",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("mobStat.discipline").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(lbRef.disciplineRef));
			charactCtn.appendChild((new Element("div", {
						"class" : "health",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("mobStat.health").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(lbRef.healthRef));
			charactCtn.appendChild((new Element("div", {
						"class" : "regen",
						"onmouseover" : 'Overlib.open(event, \'' + ti("mobStat.regen").capitalize().replace(/'/g, '\\\'')
								+ '\');'
					})).update(lbRef.regenRef));
			charactCtn.appendChild((new Element("div", {
						"class" : "recovery",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("heroeStat.recovery").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(isMob ? 30 : lbRef.recoveryRef));
			charactCtn.appendChild((new Element("div", {
						"class" : "stamina",
						"onmouseover" : 'Overlib.open(event, \''
								+ ti("heroeStat.stamina").capitalize().replace(/'/g, '\\\'') + '\');'
					})).update(isMob ? 100 : lbRef.staminaRef));
		}

		o.appendChild(new Element("div", {
					"class" : "end"
				}));

		container.appendChild(base);

		if (oImg) {
			var maxWidth = 300;
			if (oImg.width > maxWidth) {
				var ratio = maxWidth / oImg.width;
				oImg.width = maxWidth;
				oImg.height = oImg.height * ratio;
			}
			var ho = o.getHeight();
			var hi = oImg.height;
			if (heightBonus + ho + hi < document.viewport.getHeight()) {
				imgCtn1.appendChild(oImg);
			} else {
				var baseDim = base.getDimensions();
				base.setStyle({
							width : (baseDim.width + oImg.width) + 'px',
							height : Math.max(hi, ho) + 'px'
						});
				imgCtn2.appendChild(oImg);
				if (Prototype.Browser.IE) {
					imgCtn2.style.styleFloat = "right";
				}
			}
		}

		renderCallback(data);
	},

	toogleDesc : function(e, resizeCallback) {
		var o = Event.element(e).parentNode;
		Element.toggle(o);
		Element.toggle(o.nextSibling);
		if (Object.isFunction(resizeCallback)) {
			resizeCallback();
		}
	}
};

var EffectDependencies = {

	listDependences : function(hRefEffects, idToScan, ids) {
		ids.push(idToScan);
		$A(hRefEffects[idToScan].dependencies).each(function(depId) {
					if (ids.indexOf(depId) == -1) {
						EffectDependencies.listDependences(hRefEffects, depId, ids);
					}
				});
	},

	getInfo : function(hRefEffects, idsToScan) {
		var ids = [];
		$A(idsToScan).each(function(idToScan) {
					EffectDependencies.listDependences(hRefEffects, idToScan, ids);
				});

		var hNames = {};
		var hInfo = {};
		$A(ids).each(function(id) {
					var refEffect = hRefEffects[id];
					hInfo[id] = {
						name : refEffect.name,
						desc : refEffect.description,
						typeS : refEffect.refEffectType_shortName,
						typeL : refEffect.refEffectType_longName,
						cost : refEffect.cost
					};
				});

		$H(hInfo).each(function(pair) {
			var desc = pair.value.desc;
			desc = desc.replace(/##lvl##/g, '<span class="italic">' + ti("effect.lvl").capitalize() + '</span>');
			var reg = new RegExp('##effect:([^#]+)##', 'g');
			while (null != (result = reg.exec(desc))) {
				var foundId = result[1];
				var name = (foundId == "this" ? pair.value.name : hInfo[foundId].name);
				desc = desc.replace(new RegExp('##effect:' + foundId + '##', 'g'), '<span class="italic">' + name
								+ '</span>');
			}
			pair.value.desc = desc.replace(/(\r)?\n/g, '<br/>');

			return pair.value;
		});

		return hInfo;
	}
};

var PlayerDetailWin = {
	winName : "playerDetailWin",

	retrieveOne : function(id) {
		var win = Windows.getWindow(this.winName + id);
		if (!Object.isUndefined(win) && null != win) {
			win.toFront();
			return true;
		}
		return false;
	},

	show : function(id) {
		if (!this.retrieveOne(id)) {
			new LoadDoc(this.dataLoaded, new DocToLoad('api', 'o/playerDetail', [['id', id]]));
		}
	},

	dataLoaded : function(data) {
		var html = htmlTitle(data.name
				+ ' '
				+ htmlPlayerHonnor(data.honnorRank, data.honnorLvl)
				+ ('<div class="subtitle">'
						+ ('<a href="javascript:void(PageManager.goTo(\'forum://privmsg/mode=post&u=' + data.forumId
								+ '\'))">' + ti('playerDetail.sendMp') + '</a>') + '</div>'))
				+ '<div class="textLayer">'
				+ '<table width="100%" class="tabAutoPopup">'
				+ '<col width="150"/>'
				+ '<col/>'
				+ (data.description ? ('<tr>' + ('<th>' + ti("playerStat.desc").capitalize() + '</th>')
						+ ('<td>' + HTMLEscape(data.description) + '</td>') + '</tr>') : '')
				+ ('<tr>'
						+ ('<th>' + ti("playerStat.lastCon").capitalize() + '</th>')
						+ ('<td>' + Date.fromServerToHtml(data.lastCon, Date.patterns.at) + ' <span class="size-1">('
								+ ti("playerStat.createDate") + ' '
								+ Date.fromServerToHtml(data.createDate, Date.patterns.ISOD) + ')</span>' + '</td>') + '</tr>')
				+ (null != data.rank ? ('<tr>'
						+ ('<th>' + ti("playerStat.rank").capitalize() + '</th>')
						+ ('<td>#' + data.rank + ' <span class="size-1">(' + ti("playerStat.score") + ' ' + data.score
								+ ')</span>' + '</td>') + '</tr>') : '')
				+ ('<tr>'
						+ ('<th>' + ti("playerStat.stat").capitalize() + '</th>')
						+ ('<td>' + data.noVictory + ' ' + ti("playerStat.victory", data.noVictory) + ', '
								+ data.noDefeat + ' ' + ti("playerStat.defeat", data.noDefeat) + '</td>') + '</tr>')
				+ ('<tr>' + ('<th>' + ti("playerStat.noMob").capitalize() + '</th>')
						+ ('<td>' + data.noMob + '</td>') + '</tr>')
				+ ('<tr>' + ('<th>' + ti("heroe_").capitalize() + '</th>')
						+ ('<td><ul class="inlineList">' + $A(data.heroes).collect(function(heroe) {
							return '<li><a href="javascript:void(detail(\'heroe\', ' + heroe.id + '));">' + heroe.name
									+ '</a> [' + heroe.refFamily_name + ']</li>';
						}).join(' - ') + '</ul></td>') + '</tr>')
				+ ('<tr>' + ('<th>' + ti("team_").capitalize() + '</th>')
						+ ('<td><ul class="inlineList">' + $A(data.teams).collect(function(team) {
							return '<li><a href="javascript:void(detail(\'team\', ' + team.id + '));">' + team.name
									+ '</a> [' + team.refAllegiance_name + ']</li>';
						}).join(' - ') + '</ul></td>') + '</tr>')
				+ ('<tr>'
						+ ('<th>' + ti("playerStat.lastBattle_").capitalize() + '</th>')
						+ ('<td>'
								+ ('<ul style="list-style-image:none;list-style-type:none;padding:0px;">'
										+ $A(data.battles).collect(function(battle) {
											var txt = (2 == battle.state ? ti('battleStat.state2') : (ti('playerDetail.'
													+ (0 == battle.win ? 'null' : (1 == battle.win ? 'win' : 'loose')))
													+ ' ' + '(' + Date.fromServerToHtml(battle.endDate, Date.patterns.ISOD) + ')'))
													+ ' ' + (ti('playerDetail.against') + ' ' + battle.enemys.collect(function(team) {
																return team.name || ti('ai.name');
															}).join(' & '));
											if (!battle['private']) {
												txt = ('<a href="javascript:void(detail(\'battle\', ' + battle.id + ', true));">')
														+ txt + '</a>';
											}
											return ('<li>')
													+ ('<img src="' + m_currentSkin + '/img/battleType' + battle.type + 'Smallest.png" style="vertical-align:top;margin-right:5px" />')
													+ txt + '</li>';
										}).join('') + '</ul>') + '</td>') + '</tr>') + '</table>' + '</div>';
		var win = new Window({
					id : this.winName + data.id,
					title : ti("player").capitalize(),
					width : 600,
					height : 50,
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true
				});
		win.setHTMLContent(html);
		win.showCenter(false);
		win.updateHeight();
	}

}

var TeamDetailWin = {
	winName : "teamDetailWin",

	retrieveOne : function(id) {
		var win = Windows.getWindow(this.winName + id);
		if (!Object.isUndefined(win) && null != win) {
			win.toFront();
			return true;
		}
		return false;
	},

	show : function(id) {
		if (!this.retrieveOne(id)) {
			new LoadDoc(this.dataLoaded, new DocToLoad('api', 'o/teamDetail', [['id', id]]));
		}
	},

	dataLoaded : function(data) {
		var team = data.team;
		var html = htmlTitle(team.name)
				+ '<div class="textLayer">'
				+ '<table width="100%" class="tabAutoPopup">'
				+ '<col width="150"/>'
				+ '<col/>'
				+ ('<tr>'
						+ ('<th>' + ti("teamStat.owner").capitalize() + '</th>')
						+ ('<td>'
								+ ('<a href="javascript:void(detail(\'player\', ' + team.player_id + '));">'
										+ team.player_name + '</a>') + ' '
								+ htmlPlayerHonnor(team.player_honnorRank, team.player_honnorLvl) + '</td>') + '</tr>')
				+ ('<tr>' + ('<th>' + ti("teamStat.allegiance").capitalize() + '</th>')
						+ ('<td>' + team.refAllegiance_name + '</td>') + '</tr>')
				+ ('<tr>' + ('<th>' + ti("teamStat.pow").capitalize() + '</th>') + ('<td>' + team.pow + '</td>') + '</tr>')
				+ ('<tr>'
						+ ('<th>' + ti("teamStat.noHeroe").capitalize() + '</th>')
						+ ('<td>' + team.noHeroe + ' (' + ti("teamStat.heroeSumXp").capitalize() + ' ' + team.heroeSumXp
								+ ')' + '</td>') + '</tr>')
				+ ('<tr>'
						+ ('<th>' + ti("teamStat.noMob").capitalize() + '</th>')
						+ ('<td>' + team.noMob + ' (' + ti("teamStat.mobAvgValue").capitalize() + ' ' + team.mobAvgValue
								+ ')' + '</td>') + '</tr>')
				+ (team.description ? ('<tr>' + ('<th>' + ti("teamStat.desc").capitalize() + '</th>')
						+ ('<td>' + HTMLEscape(team.description) + '</td>') + '</tr>') : '') + '</table>' + '</div>';
		var win = new Window({
					id : this.winName + team.id,
					title : ti("team").capitalize(),
					width : 500,
					height : 50,
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true
				});
		win.setHTMLContent(html);
		win.showCenter(false);
		win.updateHeight();
	}

}

var BuyProduct = {
	refProducts : null,
	win : null,
	callback : null,

	getTxt : function(keyName, no, templateValues) {
		return ti('buyProduct.' + keyName, no, templateValues);
	},

	start : function(callback) {
		if (!ClientContext.loginVerify()) {
			return false;
		}
		if (this.win) {
			this.win.toFront();
			return;
		}
		this.callback = callback;
		new LoadDoc(this.dataLoaded.bind(this), new DocToLoad('api', 'o/refProductList'), new DocToLoad('api',
						'r/playerForProduct'), new DocToLoad('api', 'r/battleFeeList'));
	},

	stop : function(winAlreadyClosed) {
		if (!this.win) {
			return;
		}
		if (!winAlreadyClosed) {
			this.win.close();
		}
		var callback = this.callback;

		this.win = null;
		this.callback = null;

		if (Object.isFunction(callback)) {
			callback();
		}
	},

	dataLoaded : function(refProductList, data, battleFeeList) {
		this.refProducts = refProductList.refProducts;
		var avantajeuxLinkParam = {
			startTag : '<a href="javascript:void(PageManager.navigateToAvantajeux());">',
			endTag : '</a>'
		};
		var owned = [];
		if (m_session.gold) {
			owned.push({
						no : m_session.gold,
						name : ti('gold', m_session.gold)
					});
		}
		owned = owned.concat(this.groupBattleFees(battleFeeList.battleFees));
		var html = ('<h3>' + this.getTxt('productOwnedTitle') + '</h3>' + '<div class="textLayer"><ul>'
				+ $A(owned).collect(function(bf) {
							return '<li>' + bf.no + ' x ' + bf.name + '</li>';
						}).join('') + '</ul></div>')
				+ (('<h3>' + this.getTxt('productAvailableTitle') + '</h3>')
						+ '<div class="textLayer">'
						+ '<form name="formBuyProduct" js="r/formBuyProduct" url="r/buyProduct">'
						+ '<div>'
						+ this.getTxt('desc', null, avantajeuxLinkParam)
						+ '</div>'
						+ ('<table width="100%">'
								+ ('<tr>' + ('<td class="header">' + this.getTxt('pack') + '</td>')
										+ ('<td class="header center">' + this.getTxt('price') + '</td>')
										+ ('<td class="header center" width="80">' + this.getTxt('quantity') + '</td>') + '</tr>')
								+ $H(this.refProducts).values().collect(function(refProduct) {
									var elms = [];
									if (refProduct.gold) {
										elms.push({
													quantity : refProduct.gold,
													name : ti("gold_", refProduct.gold)
												});
									}
									elms = elms.concat($A(refProduct.battleFees).collect(function(battleFee) {
												return {
													id : battleFee.id,
													quantity : battleFee.quantity,
													name : battleFee.name,
													duration : battleFee.duration
												};
											}));
									elms = $A(elms).collect(function(elm) {
										return elm.quantity + ' x ' + elm.name + (1 == elm.id ? ' *' : '')
												+ (elm.duration ? ' ' + this.getTxt('duration', null, {
															duration : elm.duration
														}) : '');
									}, this);
									return '<tr>'
											+ (('<td class="border">')
													+ ((!refProduct.name && 1 == elms.length) ? elms[0] : ((refProduct.name
															? refProduct.name
															: '')
															+ '<ul>' + $A(elms).collect(function(elm) {
																		return '<li>' + elm + '</li>';
																	}).join('') + '</ul>')) + '</td>')
											+ ('<td class="border right" valign="top">'
													+ this.formatPrice(refProduct.priceKR, refProduct.discount) + '</td>')
											+ ('<td class="noPadding" valign="top">'
													+ ('<input type="text" class="text center" style="width:80px;" name="product['
															+ refProduct.id + ']" value="0" onchange="BuyProduct.quantityChange(this);" />') + '</td>')
											+ '</tr>';
								}, this).join('')
								+ ('<tr>' + ('<td valign="top" class="size-1">' + ti('infinityPackDesc') + '</td>')
										+ ('<td class="header center">' + this.getTxt('total') + '</td>') + ('<td>' + '</td>') + '</tr>')
								+ ('<tr>'
										+ ('<td valign="bottom" class="center">' + this.getTxt('youHave', null, {
													KR : m_session.KR,
													avjl : avantajeuxLinkParam
												}) + '</td>')
										+ ('<td class="border right" id="total_KR">' + '0' + '</td>')
										+ ('<td class="center">'
												+ ('<input type="button" name="validate" class="validate basic" style="width:80px;" value="'
														+ this.getTxt('submit') + '" />') + '</td>') + '</tr>') + '</table>') + '</form>' + '</div>');
		this.win = new Window({
					title : this.getTxt("title").capitalize(),
					width : 700,
					height : 500,
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true,
					onClose : function() {
						BuyProduct.stop(true);
					}
				});
		this.win.setHTMLContent(html);
		this.win.showCenter(true);
		form_parseAll();
	},

	groupBattleFees : function(battleFees) {
		var grouped = [];
		$A(battleFees).each(function(bf) {
					if (bf.maxDate) {
						grouped.push({
									no : 1,
									id : null,
									name : bf.refBattleFee_name + ' (' + this.getTxt('maxDate', null, {
												date : Date.fromServerToHtml(bf.maxDate, Date.patterns.ISOD)
											}) + ')'
								});
					} else {
						var existing = $A(grouped).find(function(bfTmp) {
									return bfTmp.id == bf.refBattleFee_id;
								})
						if (existing) {
							existing.no++;
						} else {
							grouped.push({
										no : 1,
										id : bf.refBattleFee_id,
										name : bf.refBattleFee_name
									});
						}
					}
				}.bind(this));
		return grouped;
	},

	quantityChange : function(input) {
		if (!Object.isUndefined(input)) {
			var q = parseInt(input.value, 10);
			input.value = (isNaN(q) || q < 0 ? 0 : q);
		}
		var oForm = new Form("formBuyProduct");
		var total = {
			KR : 0
		}
		$H(this.refProducts).each(function(pair) {
			var q = Number(oForm.val("product[" + pair.key + "]"));
			$H(total).keys().each(function(key) {
				total[key] += q
						* Math.floor(pair.value['price' + key.capitalize()] * (1 - (pair.value['discount'] / 100)));
			});
		}, this);
		$H(total).each(function(pair) {
					$('total_' + pair.key).innerHTML = pair.value;
				}, this);
	},

	formatPrice : function(price, discount) {
		return (price ? price
				+ (discount ? '<span class="bold">-' + discount + '%</span>='
						+ (Math.floor(price * (1 - (discount / 100)))) : '') : '');
	}

};
/** ************************************************************************** */

/*************************************************************************************************************
 * html toolbox
 ************************************************************************************************************/
/**
 *
 * @param {Object|String}
 *          rules object for rules or String for src link
 * @param {}
 *          stylesheetId
 */
function createCss(rules, stylesheetId) {
	stylesheetId = "__css__" + stylesheetId;

	if (Object.isString(rules)) {

		var link = $(stylesheetId);
		if (link) {
			link.parentNode.removeChild(link);
		}

		var link = document.createElement("link");
		link.rel = "stylesheet";
		link.type = "text/css";
		link.href = rules;
		document.getElementsByTagName('head')[0].appendChild(link);

	} else {

		// bug in FF with insertRule ... so use brutal method
		if (Prototype.Browser.Gecko || Prototype.Browser.Opera) {
			var ss = $(stylesheetId);
			if (ss) {
				ss.parentNode.removeChild(ss);
			}
			ss = document.createElement("style");
			ss.setAttribute('type', "text/css");
			ss.setAttribute('media', "screen");
			ss.setAttribute('id', stylesheetId);
			document.getElementsByTagName('head')[0].appendChild(ss);
			ss.appendChild(document.createTextNode($H(rules).collect(function(pair) {
						return pair.key + '{' + pair.value + '}';
					}).join(' ')));
		} else {
			var ss = null;
			ss = $A(document.styleSheets).find(function(ss) {
						return stylesheetId == ss.title;
					});
			if (ss) {
				if (ss.rules) {
					while (ss.rules.length > 0) {
						ss.removeRule(0);
					}
				} else {
					while (ss.cssRules.length > 0) {
						ss.deleteRule(0);
					}
				}
			} else {
				ss = document.createElement("style");
				ss.setAttribute('type', "text/css");
				ss.setAttribute('media', "screen");
				ss.setAttribute('title', stylesheetId);
				document.getElementsByTagName('head')[0].appendChild(ss);
				ss = document.styleSheets[document.styleSheets.length - 1];
			}
			$H(rules).each(function(pair) {
						if (ss.addRule) {
							ss.addRule(pair.key, pair.value);
						} else {
							ss.insertRule(pair.key + '{' + pair.value + '}');
						}
					});
		}

	}
}

function htmlOverliber(s, w) {
	return 'onmouseover="Overlib.open(event, \'' + s.replace(/'/g, '\\\'').replace(/"/g, '&quot;') + '\''
			+ (w ? ',' + w : '') + ');"';
}

function htmlPlayerHonnor(rank, lvl) {
	var liImgRank = ["medal_silver", "star_silver", "medal_gold", "star_gold"];
	var overlib = ti('playerHonnor.lvl.' + lvl).capitalize() + '<br/>'
			+ ti('playerHonnor.rank.' + rank).capitalize();
	return ('<img src="' + m_currentSkin + 'img/honnor/' + liImgRank[rank] + '_' + (lvl + 1) + '.png" '
			+ htmlOverliber(overlib, 400) + ' align="top"/>');
}

function htmlClassMobBorder(scarcity, family) {
	var li = ["white", "black", "grey", "gold"];
	return "mobBorder-" + li[scarcity || 0];
}

function htmlDelay(noSeconds, options) {
	if (noSeconds <= 0) {
		return false;
	}

	startingNoSeconds = noSeconds;
	options = options || {};

	var sTimerText = '';
	if (!options.disableDay) {
		var iDay = Math.floor(noSeconds / 86400);
		if (iDay > 0 || sTimerText.length > 0) {
			noSeconds = noSeconds % 86400;
			sTimerText += iDay + ti("date.accr.day");
		}
	}
	if (!options.disableHour) {
		var iHour = Math.floor(noSeconds / 3600);
		if (iHour > 0 || sTimerText.length > 0) {
			noSeconds = noSeconds % 3600;
			sTimerText += (options.shorter || 0 == sTimerText.length ? '' : ' ') + iHour + ti("date.accr.hour");
		}
	}
	if (!options.disableMinute) {
		var iMinute = Math.floor(noSeconds / 60);
		if (iMinute > 0 || sTimerText.length > 0) {
			noSeconds = noSeconds % 60;
			sTimerText += (options.shorter || 0 == sTimerText.length ? '' : ' ') + iMinute + ti("date.accr.minute");
		}
	}
	if (startingNoSeconds <= 300) {
		sTimerText += (options.shorter || 0 == sTimerText.length ? '' : ' ') + noSeconds + ti("date.accr.second");
	}

	return sTimerText;
}

/**
 * encode string for HTML display if it come from JSON
 *
 * @param {String}
 *          s
 * @return {String}
 */
function HTMLEscape(s, notHtml) {
	if (!s) {
		return s;
	}
	s = s.replace(/&/g, "&amp;");
	if (true !== notHtml) {
		s = s.replace(/</g, "&lt;").replace(/>/g, "&gt;");
	}
	s = s.replace(/(\r)?\n/g, '<br/>');
	return s;
}

function rollCss(o) {
	o.className = o.className.replace(/_1/g, "_@");
	o.className = o.className.replace(/_2/g, "_1");
	o.className = o.className.replace(/_@/g, "_2");
}

// function getDateObject(s) {
// var o = null;
// if (s.match(/^\d{2}\/\d{2}\/\d{4}$/) != null) {
// var aJMA = s.split("/");
// var d = new Date(aJMA[2], parseInt(aJMA[1], 10) - 1, aJMA[0]);
//
// if (d.getDate() == parseInt(aJMA[0], 10) && d.getMonth() == parseInt(aJMA[1], 10) - 1
// && d.getFullYear() == parseInt(aJMA[2], 10) && d.getFullYear() < 2080) {
// o = d;
// }
// }
// return o;
// }

function htmlTitle(title) {
	return '<h2 id="pageTitle">' + title + '</h2>';
}

function htmlPager(currentPage, hasMore, fnName, extra) {
	hasMore = Object.isUndefined(hasMore) ? true : hasMore;
	fnName = fnName || "mp.changePage";
	return ('<table align="center" cellpadding="0" cellspacing="0"' + (extra ? ' width="100%"' : '') + '>')
			+ '<tr>'
			+ ('<td width="30" align="center">'
					+ (currentPage > 1 ? '<a href="javascript:' + fnName + '(-1);" title="' + ti("pagePrev")
							+ '"><img src="' + m_currentSkin + 'img/arrow-left.png" align="absmiddle"/></a>' : '') + '</td>')
			+ ('<td width="80" align="center">'
					+ (currentPage == 1 && !hasMore ? '' : (ti("page") + ' ' + currentPage)) + '</td>')
			+ ('<td width="30" align="right">'
					+ (hasMore ? '<a href="javascript:' + fnName + '(1);" title="' + ti("pageNext") + '"><img src="'
							+ m_currentSkin + 'img/arrow-right.png" align="absmiddle"/></a>' : '') + '</td>')
			+ (extra ? ('<td align="right">' + extra + '</td>') : '') + '</tr>' + '</table>';
}

/**
 * renvoie un text dans la langue en cours à partie d'une clef. Si <code>no > 1</code> renvoie le texte au
 * pluriel correspondant a la meme clef + "_"
 *
 * @param {String}
 *          keyName
 * @param {Number}
 *          no
 * @return {String}
 */
function tp(keyName, no, templateValues) {
	return getText(mpText, keyName, no, templateValues)
}

function tf(keyName, no, templateValues) {
	return getText(mfText, keyName, no, templateValues)
}

function ti(keyName, no, templateValues) {
	return getText(m_text.index, keyName, no, templateValues)
}

function getText(base, keyName, no, templateValues) {
	no = no || 0;
	var value = base;
	if (keyName) {
		var keyNameParts = keyName.split('.');
		for (var i = 0, iMax = keyNameParts.length - 1; i <= iMax; i++) {
			value = value[keyNameParts[i] + (i == iMax && no > 1 ? '_' : '')];
		}
	}
	if (Object.isString(value) && -1 < value.indexOf("#{")) {
		templateValues = Object.clone(templateValues || {});
		templateValues.index = ti();
		value = value.interpolate(templateValues);
	}
	return value;
}

function gup(name) {
	name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
	var regexS = "[\\?&]" + name + "=([^&#]*)";
	var regex = new RegExp(regexS);
	var results = regex.exec(window.location.href);
	return results == null ? "" : results[1];
}
/** ************************************************************************** */

/*************************************************************************************************************
 * Prototype override
 ************************************************************************************************************/
String.prototype.capitalize = function() {
	return this.charAt(0).toUpperCase() + this.substring(1);
};
/** ********************************************************************************************************* */

/*************************************************************************************************************
 * Date extension
 ************************************************************************************************************/
Date.fromServer = function(s) {
	var matches = /^(\d{4})\-(\d{2})\-(\d{2})( (\d{2}):(\d{2}):(\d{2}))?$/.exec(s);
	var d = new Date();
	d.setUTCDate(10);
	d.setUTCFullYear(parseInt(matches[1], 10));
	d.setUTCMonth(parseInt(matches[2], 10) - 1);
	d.setUTCDate(parseInt(matches[3], 10));
	if (matches[4]) {
		d.setUTCHours(parseInt(matches[5], 10));
		d.setUTCMinutes(parseInt(matches[6], 10));
		d.setUTCSeconds(parseInt(matches[7], 10));
	} else {
		d.setUTCHours(0);
		d.setUTCMinutes(0);
		d.setUTCSeconds(0);
	}
	d.setMilliseconds(0);
	return d;
};

Date.fromServerToHtml = function(s, pattern, force) {
	if (s == null || s == '') {
		return s;
	}
	var d = Date.fromServer(s);
	return Date.toHtml(d, pattern, force);
};

Date.toHtml = function(d, pattern, force) {
	pattern = pattern || Date.patterns.ISODT;
	if (true !== force) {
		switch (pattern) {
			case Date.patterns.battle :
			case Date.patterns.at :
				var now = new Date();
				if (now.getDate() == d.getDate() && Math.abs(d.valueOf() - now.valueOf()) < 1000 * 3600 * 24) {
					switch (pattern) {
						case Date.patterns.battle :
							pattern = Date.patterns.battleShort;
							break;
						case Date.patterns.at :
							pattern = Date.patterns.atShort;
							break;
					}
				}
				break;
		}
	}
	return d.format(pattern || Date.patterns.ISODT);
};
/** ********************************************************************************************************* */

/*************************************************************************************************************
 * Date obj from Ext
 ************************************************************************************************************/
Ext = {
	escapeRe : function(s) {
		return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1")
	},
	isSafari : (/webkit|khtml/).test(navigator.userAgent.toLowerCase())
};
String.escape = function(A) {
	return A.replace(/('|\\)/g, "\\$1")
};
String.leftPad = function(D, B, C) {
	var A = new String(D);
	if (C === null || C === undefined || C === "") {
		C = " "
	}
	while (A.length < B) {
		A = C + A
	}
	return A
};

Date.parseFunctions = {
	count : 0
};
Date.parseRegexes = [];
Date.formatFunctions = {
	count : 0
};

Date.prototype.dateFormat = function(format) {
	if (Date.formatFunctions[format] == null) {
		Date.createNewFormat(format);
	}
	var func = Date.formatFunctions[format];
	return this[func]();
};

Date.prototype.format = Date.prototype.dateFormat;

Date.createNewFormat = function(format) {
	var funcName = "format" + Date.formatFunctions.count++;
	Date.formatFunctions[format] = funcName;
	var code = "Date.prototype." + funcName + " = function(){return ";
	var special = false;
	var ch = '';
	for (var i = 0; i < format.length; ++i) {
		ch = format.charAt(i);
		if (!special && ch == "\\") {
			special = true;
		} else if (special) {
			special = false;
			code += "'" + String.escape(ch) + "' + ";
		} else {
			code += Date.getFormatCode(ch);
		}
	}
	eval(code.substring(0, code.length - 3) + ";}");
};

Date.getFormatCode = function(character) {
	switch (character) {
		case "d" :
			return "String.leftPad(this.getDate(), 2, '0') + ";
		case "D" :
			return "Date.getShortDayName(this.getDay()) + ";
		case "j" :
			return "this.getDate() + ";
		case "l" :
			return "Date.dayNames[this.getDay()] + ";
		case "N" :
			return "(this.getDay() ? this.getDay() : 7) + ";
		case "S" :
			return "this.getSuffix() + ";
		case "w" :
			return "this.getDay() + ";
		case "z" :
			return "this.getDayOfYear() + ";
		case "W" :
			return "String.leftPad(this.getWeekOfYear(), 2, '0') + ";
		case "F" :
			return "Date.monthNames[this.getMonth()] + ";
		case "m" :
			return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
		case "M" :
			return "Date.getShortMonthName(this.getMonth()) + ";
		case "n" :
			return "(this.getMonth() + 1) + ";
		case "t" :
			return "this.getDaysInMonth() + ";
		case "L" :
			return "(this.isLeapYear() ? 1 : 0) + ";
		case "o" :
			return "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0))) + ";
		case "Y" :
			return "this.getFullYear() + ";
		case "y" :
			return "('' + this.getFullYear()).substring(2, 4) + ";
		case "a" :
			return "(this.getHours() < 12 ? 'am' : 'pm') + ";
		case "A" :
			return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
		case "g" :
			return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
		case "G" :
			return "this.getHours() + ";
		case "h" :
			return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
		case "H" :
			return "String.leftPad(this.getHours(), 2, '0') + ";
		case "i" :
			return "String.leftPad(this.getMinutes(), 2, '0') + ";
		case "s" :
			return "String.leftPad(this.getSeconds(), 2, '0') + ";
		case "u" :
			return "String.leftPad(this.getMilliseconds(), 3, '0') + ";
		case "O" :
			return "this.getGMTOffset() + ";
		case "P" :
			return "this.getGMTOffset(true) + ";
		case "T" :
			return "this.getTimezone() + ";
		case "Z" :
			return "(this.getTimezoneOffset() * -60) + ";
		case "c" :
			for (var df = Date.getFormatCode, c = "Y-m-dTH:i:sP", code = "", i = 0, l = c.length; i < l; ++i) {
				var e = c.charAt(i);
				code += e == "T" ? "'T' + " : df(e);
			}
			return code;
		case "U" :
			return "Math.round(this.getTime() / 1000) + ";
		default :
			return "'" + String.escape(character) + "' + ";
	}
};

Date.parseDate = function(input, format) {
	if (Date.parseFunctions[format] == null) {
		Date.createParser(format);
	}
	var func = Date.parseFunctions[format];
	return Date[func](input);
};

Date.createParser = function(format) {
	var funcName = "parse" + Date.parseFunctions.count++;
	var regexNum = Date.parseRegexes.length;
	var currentGroup = 1;
	Date.parseFunctions[format] = funcName;

	var code = "Date." + funcName + " = function(input){\n"
			+ "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, ms = -1, o, z, u, v;\n"
			+ "var d = new Date();\n" + "y = d.getFullYear();\n" + "m = d.getMonth();\n" + "d = d.getDate();\n"
			+ "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
			+ "if (results && results.length > 0) {";
	var regex = "";

	var special = false;
	var ch = '';
	for (var i = 0; i < format.length; ++i) {
		ch = format.charAt(i);
		if (!special && ch == "\\") {
			special = true;
		} else if (special) {
			special = false;
			regex += String.escape(ch);
		} else {
			var obj = Date.formatCodeToRegex(ch, currentGroup);
			currentGroup += obj.g;
			regex += obj.s;
			if (obj.g && obj.c) {
				code += obj.c;
			}
		}
	}

	code += "if (u)\n" + "{v = new Date(u * 1000);}"
			+ "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0 && ms >= 0)\n"
			+ "{v = new Date(y, m, d, h, i, s, ms);}\n"
			+ "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
			+ "{v = new Date(y, m, d, h, i, s);}\n" + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
			+ "{v = new Date(y, m, d, h, i);}\n" + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
			+ "{v = new Date(y, m, d, h);}\n" + "else if (y >= 0 && m >= 0 && d > 0)\n"
			+ "{v = new Date(y, m, d);}\n" + "else if (y >= 0 && m >= 0)\n" + "{v = new Date(y, m);}\n"
			+ "else if (y >= 0)\n" + "{v = new Date(y);}\n" + "}return (v && (z || o))?\n"
			+ "    (z ? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n"
			+ "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" + ";}";

	Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$", "i");
	eval(code);
};

Date.formatCodeToRegex = function(character, currentGroup) {

	switch (character) {
		case "d" :
			return {
				g : 1,
				c : "d = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{2})"
			};
		case "D" :
			for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i);
			return {
				g : 0,
				c : null,
				s : "(?:" + a.join("|") + ")"
			};
		case "j" :
			return {
				g : 1,
				c : "d = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{1,2})"
			};
		case "l" :
			return {
				g : 0,
				c : null,
				s : "(?:" + Date.dayNames.join("|") + ")"
			};
		case "N" :
			return {
				g : 0,
				c : null,
				s : "[1-7]"
			};
		case "S" :
			return {
				g : 0,
				c : null,
				s : "(?:st|nd|rd|th)"
			};
		case "w" :
			return {
				g : 0,
				c : null,
				s : "[0-6]"
			};
		case "z" :
			return {
				g : 0,
				c : null,
				s : "(?:\\d{1,3}"
			};
		case "W" :
			return {
				g : 0,
				c : null,
				s : "(?:\\d{2})"
			};
		case "F" :
			return {
				g : 1,
				c : "m = parseInt(Date.getMonthNumber(results[" + currentGroup + "]), 10);\n",
				s : "(" + Date.monthNames.join("|") + ")"
			};
		case "m" :
			return {
				g : 1,
				c : "m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
				s : "(\\d{2})"
			};
		case "M" :
			for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i);
			return {
				g : 1,
				c : "m = parseInt(Date.getMonthNumber(results[" + currentGroup + "]), 10);\n",
				s : "(" + a.join("|") + ")"
			};
		case "n" :
			return {
				g : 1,
				c : "m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
				s : "(\\d{1,2})"
			};
		case "t" :
			return {
				g : 0,
				c : null,
				s : "(?:\\d{2})"
			};
		case "L" :
			return {
				g : 0,
				c : null,
				s : "(?:1|0)"
			};
		case "o" :
		case "Y" :
			return {
				g : 1,
				c : "y = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{4})"
			};
		case "y" :
			return {
				g : 1,
				c : "var ty = parseInt(results[" + currentGroup + "], 10);\n"
						+ "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
				s : "(\\d{1,2})"
			};
		case "a" :
			return {
				g : 1,
				c : "if (results[" + currentGroup + "] == 'am') {\n" + "if (h == 12) { h = 0; }\n"
						+ "} else { if (h < 12) { h += 12; }}",
				s : "(am|pm)"
			};
		case "A" :
			return {
				g : 1,
				c : "if (results[" + currentGroup + "] == 'AM') {\n" + "if (h == 12) { h = 0; }\n"
						+ "} else { if (h < 12) { h += 12; }}",
				s : "(AM|PM)"
			};
		case "g" :
		case "G" :
			return {
				g : 1,
				c : "h = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{1,2})"
			};
		case "h" :
		case "H" :
			return {
				g : 1,
				c : "h = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{2})"
			};
		case "i" :
			return {
				g : 1,
				c : "i = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{2})"
			};
		case "s" :
			return {
				g : 1,
				c : "s = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{2})"
			};
		case "u" :
			return {
				g : 1,
				c : "ms = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(\\d{3})"
			};
		case "O" :
			return {
				g : 1,
				c : ["o = results[", currentGroup, "];\n", "var sn = o.substring(0,1);\n",
						"var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n",
						"var mn = o.substring(3,5) % 60;\n",
						"o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
						"    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"].join(""),
				s : "([+\-]\\d{4})"
			};
		case "P" :
			return {
				g : 1,
				c : ["o = results[", currentGroup, "];\n", "var sn = o.substring(0,1);\n",
						"var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
						"var mn = o.substring(4,6) % 60;\n",
						"o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
						"    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"].join(""),
				s : "([+\-]\\d{2}:\\d{2})"
			};
		case "T" :
			return {
				g : 0,
				c : null,
				s : "[A-Z]{1,4}"
			};
		case "Z" :
			return {
				g : 1,
				c : "z = results[" + currentGroup + "] * 1;\n" + "z = (-43200 <= z && z <= 50400)? z : null;\n",
				s : "([+\-]?\\d{1,5})"
			};
		case "c" :
			var df = Date.formatCodeToRegex, calc = [];
			var arr = [df("Y", 1), df("m", 2), df("d", 3), df("h", 4), df("i", 5), df("s", 6), df("P", 7)];
			for (var i = 0, l = arr.length; i < l; ++i) {
				calc.push(arr[i].c);
			}
			return {
				g : 1,
				c : calc.join(""),
				s : arr[0].s + "-" + arr[1].s + "-" + arr[2].s + "T" + arr[3].s + ":" + arr[4].s + ":" + arr[5].s
						+ arr[6].s
			};
		case "U" :
			return {
				g : 1,
				c : "u = parseInt(results[" + currentGroup + "], 10);\n",
				s : "(-?\\d+)"
			};
		default :
			return {
				g : 0,
				c : null,
				s : Ext.escapeRe(character)
			};
	}
};

Date.prototype.getTimezone = function() {
	return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2")
			.replace(/[^A-Z]/g, "");
};

Date.prototype.getGMTOffset = function(colon) {
	return (this.getTimezoneOffset() > 0 ? "-" : "+")
			+ String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0") + (colon ? ":" : "")
			+ String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
};

Date.prototype.getDayOfYear = function() {
	var num = 0;
	Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
	for (var i = 0; i < this.getMonth(); ++i) {
		num += Date.daysInMonth[i];
	}
	return num + this.getDate() - 1;
};

Date.prototype.getWeekOfYear = function() {
	var ms1d = 864e5;
	var ms7d = 7 * ms1d;
	var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d;
	var AWN = Math.floor(DC3 / 7);
	var Wyr = new Date(AWN * ms7d).getUTCFullYear();
	return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
};

Date.prototype.isLeapYear = function() {
	var year = this.getFullYear();
	return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
};

Date.prototype.getFirstDayOfMonth = function() {
	var day = (this.getDay() - (this.getDate() - 1)) % 7;
	return (day < 0) ? (day + 7) : day;
};

Date.prototype.getLastDayOfMonth = function() {
	var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
	return (day < 0) ? (day + 7) : day;
};

Date.prototype.getFirstDateOfMonth = function() {
	return new Date(this.getFullYear(), this.getMonth(), 1);
};

Date.prototype.getLastDateOfMonth = function() {
	return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
};

Date.prototype.getDaysInMonth = function() {
	Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
	return Date.daysInMonth[this.getMonth()];
};

Date.daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

Date.getShortMonthName = function(month) {
	return Date.monthNames[month].substring(0, 3);
}

Date.getShortDayName = function(day) {
	return Date.dayNames[day].substring(0, 3);
}

Date.y2kYear = 50;

Date.monthNumbers = {
	Jan : 0,
	Feb : 1,
	Mar : 2,
	Apr : 3,
	May : 4,
	Jun : 5,
	Jul : 6,
	Aug : 7,
	Sep : 8,
	Oct : 9,
	Nov : 10,
	Dec : 11
};

Date.getMonthNumber = function(name) {
	return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
}

Date.prototype.clone = function() {
	return new Date(this.getTime());
};

Date.prototype.clearTime = function(clone) {
	if (clone) {
		return this.clone().clearTime();
	}
	this.setHours(0);
	this.setMinutes(0);
	this.setSeconds(0);
	this.setMilliseconds(0);
	return this;
};

if (Ext.isSafari) {
	Date.brokenSetMonth = Date.prototype.setMonth;
	Date.prototype.setMonth = function(num) {
		if (num <= -1) {
			var n = Math.ceil(-num);
			var back_year = Math.ceil(n / 12);
			var month = (n % 12) ? 12 - n % 12 : 0;
			this.setFullYear(this.getFullYear() - back_year);
			return Date.brokenSetMonth.call(this, month);
		} else {
			return Date.brokenSetMonth.apply(this, arguments);
		}
	};
}

Date.MILLI = "ms";

Date.SECOND = "s";

Date.MINUTE = "mi";

Date.HOUR = "h";

Date.DAY = "d";

Date.MONTH = "mo";

Date.YEAR = "y";

Date.prototype.add = function(interval, value) {
	var d = this.clone();
	if (!interval || value === 0) {
		return d;
	}
	switch (interval.toLowerCase()) {
		case Date.MILLI :
			d.setMilliseconds(this.getMilliseconds() + value);
			break;
		case Date.SECOND :
			d.setSeconds(this.getSeconds() + value);
			break;
		case Date.MINUTE :
			d.setMinutes(this.getMinutes() + value);
			break;
		case Date.HOUR :
			d.setHours(this.getHours() + value);
			break;
		case Date.DAY :
			d.setDate(this.getDate() + value);
			break;
		case Date.MONTH :
			var day = this.getDate();
			if (day > 28) {
				day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
			}
			d.setDate(day);
			d.setMonth(this.getMonth() + value);
			break;
		case Date.YEAR :
			d.setFullYear(this.getFullYear() + value);
			break;
	}
	return d;
};

Date.prototype.between = function(start, end) {
	var t = this.getTime();
	return start.getTime() <= t && t <= end.getTime();
}
/** ************************************************************************** */
