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_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_aThisParam;
var m_sThisHash;
var mp = null;
var mpText = null;
var mf = null;
var mfText = null;

var m_battleData = {
	refMobs : {},
	refEffects : {}
};

/*************************************************************************************************************
 * 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() {
		var args = $A(arguments);

		var autoLaunch = true;
		if ('boolean' == typeof(args[0])) {
			autoLaunch = args[0];
			args = args.slice(1);
		}

		this.fOnLoad = args[0];

		if (2 == args.length && Object.isArray(args[1])) {
			this.aLiDocToLoad = args[1];
		} else {
			this.aLiDocToLoad = [];
			for (var i = 1; i < args.length; i++) {
				this.aLiDocToLoad.push(args[i]);
			}
		}

		if (autoLaunch) {
			this.launch();
		}
	},

	launch : function() {
		if (this.aLiDocToLoad.length > 0) {
			this.process();
		} else {
			if (Object.isFunction(this.fOnLoad)) {
				this.fOnLoad();
			}
		}
	},

	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) {
								var oImg = Event.element(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)) {
						if (Object.isArray(oDoc.param)) {
							$A(oDoc.param).each(function(param) {
										oHttp.addParam(param[0], param[1]);
									});
						} else if ('object' == typeof(oDoc.param)) {
							$H(oDoc.param).each(function(pair) {
										oHttp.addParam(pair.key, pair.value);
									});
						}
					}
					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;

				case 'process' :
					oDoc.fileName.process(this.docLoaded.bind(this, iDocIndex), oDoc.param);
					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["_appIsInactive"]) {
						self.location.href = 'inactive.html';
					}
					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);
			this.fOnLoad.apply(this, aArgs);
			SnReader.read();
		}

	}
};

function DocToLoad(sType, sFileName, vParam) {
	this.fileName = sFileName;
	this.type = sType;
	this.param = vParam;
	this.content = null;
}
/** ************************************************************************** */

/*************************************************************************************************************
 * ClientContext
 ************************************************************************************************************/
var ClientContext = {
	genMenuItems : [],
	headerMenuItems : null,
	extMenuItems : [{
				id : "facebook",
				link : "http://www.facebook.com/pages/Lands-of-Magic/106903459337082",
				subtitle : true
			}],

	webTracking : function(url, customUrl) {
		if (PIWIK_PATH && PIWIK_SITEID && !(m_session && m_session.log && m_session.admin)) {
			try {
				var piwikTracker = Piwik.getTracker(PIWIK_PATH + "piwik.php", PIWIK_SITEID);
				var customUrl = customUrl || url;
				if (m_sReferrer) {
					customUrl += '?pk_campaign=' + m_sReferrer
				}
				piwikTracker.setCustomUrl(customUrl);
				piwikTracker.trackPageView(url);
			} catch (err) {
			}
		}
	},

	init : function() {
		m_sReferrer = gup("referrer") || null;

		// versioning
		m_versionMin = VERSION.substr(0, VERSION.lastIndexOf('.'));

		{
			var ua = navigator.userAgent;
			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;
				}
			}

			Prototype.Browser.Touch = Prototype.Browser.MobileSafari || new RegExp("Android").test(ua);
		}

		// init Windows API
		this.initWindow();

		// define UserPref for default value
		woUserPref = null;
		try {
			if (window.opener) {
				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));

			// add link to o/home on header
			$('header').observe('click', function(ev) {
						if ("header" == ev.element().getAttribute('role')) {
							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.hasEffectLib = false;
		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, {
					getShortMonthName : function(month) {
						return this.monthShortnames[month];
					}
				});
		Object.extend(Date.prototype, {
					getSuffix : function() {
						return Date.dateSuffix[this.getDate()] || "";
					}
				});

		if (!bPopup) {
			$('__copyright').innerHTML = COPYRIGHT;
			$('__version').innerHTML = VERSION;
			$('__contact').innerHTML = '<a href="javascript:void(PageManager.navigateToSupport())">'
					+ ti('support.title') + '</a>';

			// 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
			this.initMenuDef();
			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;

		var goTo;
		switch (gup('q')) {
			case 'lostPassword' :
				PageManager.goTo('js://ClientContext.loginRetrieve()');
				break;
			default :
				goTo = PageManager.getHashFromLocation();
				break;
		}
		PageManager.goTo(goTo || 'o/home');
		window.setInterval(PageManager.detectLocationChange.bind(PageManager), 500);

		if (!bPopup) {
			Event.observe(window, 'resize', function() {
						if (this.resizeTimeout) {
							window.clearTimeout(this.resizeTimeout);
						}
						this.resizeTimeout = window.setTimeout(this.onWindowResize.bindAsEventListener(this), 303);
					}.bind(this));
			this.onWindowResize();
		}
	},

	initMenuDef : function() {
		this.headerMenuItems = [{
					id : "register",
					link : "o/accountRegister",
					log : false
				}, {
					id : "login",
					link : "js://ClientContext.login()",
					log : false
				}, {
					id : "me",
					link : "r/me",
					subtitle : true,
					log : true,
					items : [{
								// id : "me_me",
								// link : "r/me"
								// }, {
								// id : "myAccount",
								// link : "r/playerProfile"
								// }, {
								id : "logout",
								link : "js://ClientContext.logout()"
							}]
				}, {
					id : "battle",
					link : "r/battleList",
					html : '<div id="menuBattleList"></div>',
					subtitle : true,
					// items : [{
					// id : "tnmt",
					// link : "r/tnmt"
					// }],
					log : true
				}, {
					id : "business",
					subtitle : true,
					html : '<div id="menuMeRecap"></div>',
					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 : "forum",
					link : "js://PageManager.navigateToForum()"
					// }, {
				// id : "chat",
				// link : "js://PageManager.navigateToChat()"
			}	, {
					id : "gameHelp",
					items : [{
								id : "h_pres",
								link : "o/gameInfo"
							}, {
								id : "h_wiki",
								link : "wiki://"
							}].concat($H(Tutorial.items).keys().collect(function(tuto, i) {
								return {
									id : "h_tuto" + i,
									text : ti('menu.header.h_tuto') + ' - ' + Tutorial.itemName(tuto),
									link : "tuto://" + tuto
								};
							}))
				}, {
					id : "world",
					subtitle : true,
					items : [{
								id : "w_mobs",
								link : "o/mobList"
							}, {
								id : "w_players",
								link : "o/playerList"
								// }, {
							// id : "w_hfps",
							// link : "o/heroeFightTop"
						}	, {
								id : "w_heroes",
								link : "o/heroeList"
							}, {
								id : "w_partners",
								link : "o/partnerList"
							}, {
								id : "a_stat",
								link : "a/statList",
								admin : true
							}, {
								id : "a_noob",
								link : "a/noobList",
								admin : true
							}, {
								id : "a_news",
								link : "a/newsList",
								admin : true
							}, {
								id : "a_partner",
								link : "a/partnerList",
								admin : true
							}, {
								id : "a_mobs",
								link : "a/mobList",
								admin : true
							}, {
								id : "a_effects",
								link : "a/effectList",
								admin : true
							}, {
								id : "a_bf",
								link : "a/mapList",
								admin : true
							}]
				}];
	},

	menuMake : function(fMenuLabel, items, prefix, lvl) {
		lvl = (lvl || 1);
		return $A(items).collect(function(item) {
			var label = item.text || fMenuLabel(prefix, item.id);
			var link = (item.link ? (item.link.startsWith('js://')
					? item.link.substr(5)
					: "PageManager.goTo(&quot;" + item.link + "&quot;)") : null);
			return ('<li id="' + prefix + 'Menu_' + item.id + '"' + (1 == lvl ? ' style="display:none;"' : '') + '>')
					+ (('<span class="lnkLevel' + lvl + '">')
							+ (link ? ('<a class="lnkLevel' + lvl + '" href="javascript:void(0);" onclick="' + link
									+ ';this.blur();">' + label + '</a>') : label) + '</span>')
					+ (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.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) {
		Windows.windows.each(function(w) {
					if (w.constraint && w.constraintPad.autoUpdateLeft) {
						PageManager.constraintPopup(w);
					}
				});

		{ // place bg
			var barre2 = $('barre2');
			var imgWidth = 460;
			var imgHeight = 600;
			var viewportDim = document.viewport.getDimensions();
			var barre2Dim = barre2.getDimensions();
			var barre2Pos = barre2.cumulativeOffset();
			var availableWidth = Math.min(450, (viewportDim.width - barre2Dim.width) / 2);
			var availableHeight = Math.max(0, (viewportDim.height - imgHeight - 5));

			['bgBarreRight', 'bgBarreLeft'].each(function(id) {
						var elm = $(id);
						if (elm) {
							elm.remove();
						}
					});

			if (availableWidth > 100) {
				var pos = (availableWidth + (imgWidth - availableWidth) / 2);
				document.body.appendChild(new Element('img', {
							id : 'bgBarreRight',
							src : m_currentSkin + '/img/bgBarreRight.png'
						}).setStyle({
							position : 'fixed',
							zIndex : '-10',
							top : availableHeight + 'px',
							left : (barre2Pos.left - pos) + 'px'
						}));
				document.body.appendChild(new Element('img', {
							id : 'bgBarreLeft',
							src : m_currentSkin + '/img/bgBarreLeft.png'
						}).setStyle({
							position : 'fixed',
							zIndex : '-10',
							top : availableHeight + 'px',
							left : (barre2Pos.left + barre2Dim.width + pos - imgWidth) + 'px'
						}));
			}
		}
	},

	onPageContentMouseUp : function(e) {
		if (e.button >= 2 && !Object.isUndefined(mp) && !Object.isUndefined(mp.onContextMenu)) {
			return mp.onContextMenu(e);
		} else {
			return true;
		}
	},

	onBodyKeyPress : function(e) {
		if (!Object.isUndefined(mp) && null != mp && Object.isFunction(mp.onKeyPress)) {
			var res = mp.onKeyPress(e);
			if (!Object.isUndefined(res)) {
				return res;
			}
		}
		switch (e.keyCode) {
			case Event.KEY_ESC :
				var win = Windows.focusedWindow;
				if (win) {
					var cont = false;
					if (Object.isFunction("cancelCallback")) {
						win.cancelCallback();
					} else if (Object.isFunction("okCallback")) {
						win.okCallback();
					} else if (win.options.closable) {
						win.close();
					} else {
						cont = true;
					}
					if (!cont) {
						e.stop();
						return false;
					}
				}
				break;
		}
		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', 'itemDetail', '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();
		}
	},

	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",
						onOk : function() {
							PageManager.navigateTo('o/home');
						}
					});
			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;

		var sessionChanged = !m_session || session.log !== m_session.log;

		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) {
					var f = null;
					if (!(session.state2 & PLAYER_STATE2.tuto)) {
						(function() {
							PageManager.goTo('tuto://battle1');
						}).delay(0.05);
					} else if (session.state == PLAYER_STATE.vacation) {
						Dialog.confirm(ti('vacationAlert'), {
									closeOnNavigate : false,
									onOk : function(win) {
										win.close();
										PageManager.goTo('r/playerProfile{activeTab:"vacation"}');
									}
								});
					}

					Chat.restore.bind(Chat).delay(0.5);

					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('menuBattleItem', 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>';
				}, this).join('');
				$('menuBattleList').innerHTML = html ? ('<ul>' + html + '</ul>') : ti('menuBattleNone');
			}
			{
				var html = ti('menuMeRecap', null, {
							gold : m_session.gold
						});
				$('menuMeRecap').innerHTML = html;
			}
		}
	},

	i18nswitch : function(i18n) {
		var params = {
			i18n : i18n
		};
		$H(m_aThisParam).each(function(param) {
					if ('i18n' != param.key) {
						params[param.key] = param.value;
					}
				});
		self.location.hash = m_sThisPage + Object.toJSON(params);
		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();">'
								+ ti("loginForm.retrieve") + '</a>]') + '</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() {
		var html = '<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" style="width:300px;"/></div>')
				+ ('<div class="bottomSpace">' + ti("loginRetrieveForm.or") + ' ' + ti("loginRetrieveForm.mail") + ' : <input type="text" name="mail" class="text" style="width:300px;"/></div>')
				+ ('<input type="button" class="validate w100" value="' + ti('loginRetrieveForm.submit') + '" name="validate"/>')
				+ '</form>';

		var win = new Window({
					id : "formLoginRetrieveWin",
					width : 400,
					height : 95,
					title : ti("loginRetrieveForm.desc"),
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true,
					closeOnNavigate : false
				});
		win.setHTMLContent(html);
		win.showCenter(true);
		form_parseAll();
	},

	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(('<div>' + ti('haveToBeLog.text') + '</div>')
							+ ('<div style="clear:both;margin:20px;">'
									+ ('<div style="width:50%;float:left;text-align:center;">'
											+ ('[<a href="javascript:void(0);" onclick="Windows.focusedWindow.close();ClientContext.login();">'
													+ ti('haveToBeLog.login') + '</a>]') + '</div>')
									+ ('<div style="width:50%;float:left;text-align:center;">'
											+ ('[<a href="javascript:void(0);" onclick="Windows.focusedWindow.close();PageManager.goTo(\'o/accountRegister\');">'
													+ ti('haveToBeLog.register') + '</a>]') + '</div>') + '</div>'));
			return false;
		}
		return true;
	}

};
Event.observe(window, 'load', ClientContext.init.bind(ClientContext));
/** ************************************************************************** */

/*************************************************************************************************************
 * Navigation et Affichage
 ************************************************************************************************************/
var PageManager = {
	intervals : {},
	timeouts : {},

	constraintPopup : function(win) {
		var p = win.options.parent;
		var pp = $(p).cumulativeOffset();
		var pd = $(p).getDimensions();
		var vpDim = document.viewport.getDimensions();
		win.setConstraint(true, {
					autoUpdateLeft : true,
					top : -1 * pp.top,
					left : -1 * pp.left,
					right : -1 * (vpDim.width - pp.left - pd.width),
					bottom : -1 * Math.max(0, (win.height + win.heightN + win.heightS - 50))
				});
	},

	unloadPageContent : function() {
		if (!bPopup) {
			Tutorial.stop();
		}
		{
			for (var key in this.intervals) {
				window.clearInterval(this.intervals[key]);
			}
			this.intervals = {};
		}
		{
			for (var key in this.timeouts) {
				window.clearTimeout(this.timeouts[key]);
			}
			this.timeouts = {};
		}
		if (mp && mp.onunload && Object.isFunction(mp.onunload)) {
			try {
				mp.onunload();
			} catch (e) {
			}
		}
	},

	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);
		if (Object.isArray(html)) {
			html = html.join('');
		}
		ctn.innerHTML = html.replace(/(\\r)?\\n/g, '<br/>') + ('<div style="clear:both;"></div>');
		form_parseAll();
	},

	registerInterval : function(interval, key) {
		this.intervals[key || interval] = interval;
		return interval;
	},

	registerTimeout : function(timeout, key) {
		this.timeouts[key] = timeout;
		return timeout;
	},

	detectLocationChange : function() {
		var hash = PageManager.getHashFromLocation();
		if (hash && hash != m_sThisHash) {
			this.goTo(hash);
		}
	},

	getHashFromLocation : function() {
		var hash = window.location.hash || null;
		if (hash) {
			hash = hash.substr(1);
			if (hash.startsWith('?url=')) {
				hash = hash.substr(5);
			}
			hash = unescape(hash);
		}
		return hash || "";
	},

	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('/');
					if (-1 < pos2) {
						this.navigateToForum(action.substring(0, pos2), action.substring(pos2 + 1));
					} else {
						this.navigateToForum(action);
					}
					break;
				case 'wiki' :
					this.navigateToWiki(action);
					break;
				case 'tuto' :
					this.navigateToTuto(action);
					break;
				case 'js' :
					window.setTimeout(action, 0);
					break;
			}
		}
	},

	navigateTo : function(sUrl, sWinName, iWidth, iHeight, scrollBars) {
		if (!sUrl) {
			sUrl = m_sThisPage + Object.toJSON(m_aThisParam || {});
		}

		if (sUrl.startsWith('r/') && !ClientContext.loginVerify()) {
			return false;
		}

		if (!sWinName) {
			this.unloadPageContent();

			var urlSplitIndex;
			var oParam = {};
			if ((urlSplitIndex = sUrl.indexOf('{')) > -1) {
				oParam = eval('(' + sUrl.substr(urlSplitIndex) + ')');
			} else if ((urlSplitIndex = sUrl.indexOf("&")) > -1) {
				sUrl.substr(urlSplitIndex).split('&').each(function(param) {
							var a = param.split('=');
							if (a && a[0]) {
								oParam[a[0]] = a[1] || '';
							}
						});
			}
			if (urlSplitIndex > -1) {
				sUrl = sUrl.substr(0, urlSplitIndex);
			}

			var sParam = Object.toJSON(oParam);
			m_sThisHash = sUrl + (sParam.length > 2 ? sParam : '');
			window.location.hash = m_sThisHash;
			var fJSLoaded = function(text) {
				this.resetPageContent();
				m_sThisPage = sUrl;
				m_aThisParam = oParam;
				mpText = m_text[m_sThisPage];
				document.body.addClassName(m_sThisPage.replace(/\//g, '_'));
				mp.onload();
				if (false !== mp.webTrackerUrl) {
					ClientContext.webTracking(mp.webTrackerUrl || sUrl, (bPopup ? 'popup' : 'index') + '_' + sUrl);
				}
			}.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#" + 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]);
	},

	navigateToWiki : function(page) {
		window.open(WIKI_PATH + page, 'lomWiki');
	},

	navigateToForum : function(action, extra) {
		var redirect = action ? ('action=' + action + ';' + extra) : extra;
		if (m_session.log) {
			new LoadDoc(function(content) {
						window.open(FORUM_PATH_HANDSHAKE.interpolate({
											handshake : content.handshake,
											redirect : (redirect ? escape(redirect) : '')
										}), 'lomForum');
					}, new DocToLoad('api', 'r/getHandshake'));
		} else {
			window.open(FORUM_PATH + (redirect ? ('?' + redirect) : ''), 'lomForum');
		}
	},

	navigateToChat : function() {
		Chat.open.apply(Chat, arguments);
	},

	navigateToSupport : function() {
		var winId = 'support';
		if (Windows.getWindow(winId)) {
			return;
		}

		var html = ('<div class="textLayer">' + ti('support.msg') + '</div>')
				+ ('<form class="textLayer topSpace" name="formSupport" js="o/support" url="o/support">'
						+ (!(m_session && m_session.log) ? ('<div>' + ti('support.mail') + ' '
								+ '<input type="text" class="text" name="mail" style="width:410px" />' + '</div>') : '')
						+ ('<textarea style="width:480px;height:170px;margin:10px 0;" class="text" name="msg"></textarea>')
						+ ('<div class="center">'
								+ ('<input type="button" class="validate" name="validate" value="' + ti('support.send') + '"') + '</div>') + '</form>')

		var win = new Window({
					id : winId,
					width : 500,
					height : 260,
					title : ti("support.title"),
					minimizable : false,
					maximizable : false,
					wiredDrag : true,
					destroyOnClose : true
				});
		win.setHTMLContent(html);
		win.showCenter(false);
		win.updateHeight();
		win.showCenter(false);
		form_parseAll();
	},

	showDetail : function(sType, sId) {
		switch (sType) {
			case 'player' :
				PlayerDetailWin.show(sId);
				break;
			case 'map' :
				this.goTo('o/mapDetail{id:' + sId + '}', 'mapDetail', 1030, 600);
				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{visit:1,id:' + sId + '}', 'battleDetail', 1030, 670);
				} 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 ImgPreLoader = {
	inUse : false,
	callbacks : [],
	wanteds : {},
	docLoader : null,

	process : function(callback, path) {
		var proceed = false;
		$A(path).each(function(path) {
					if (Object.isUndefined(this.wanteds[path])) {
						this.wanteds[path] = false;
						proceed = true;
					}
				}, this);

		if (Object.isFunction(callback)) {
			this.callbacks.push(callback);
		}

		if (!this.inUse) {
			if (proceed) {
				this.load();
			} else {
				this.finished();
			}
		}
	},

	load : function() {
		this.inUse = true;

		var docToLoads = [];
		$H(this.wanteds).each(function(pair) {
					if (!pair.value) {
						docToLoads.push(new DocToLoad('img', pair.key));
					}
				});

		if (docToLoads.length > 0) {
			this.docLoader = new LoadDoc(false, this.loaded.bind(this), docToLoads);
			this.docLoader.launch();
		} else {
			this.finished();
		}
	},

	loaded : function() {
		$A(this.docLoader.aLiDocToLoad).each(function(docToLoad) {
					this.wanteds[docToLoad.fileName] = true;
				}, this);
		this.docLoader = null;
		this.load();
	},

	finished : function() {
		var callbacks = this.callbacks;
		this.callbacks = [];
		this.inUse = false;
		callbacks.each(function(f) {
					f();
				}, this);
	}
};

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 "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://pm\')">'
									+ 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();
	}
}
/** ************************************************************************** */

/*************************************************************************************************************
 *
 ************************************************************************************************************/
var MobList = Class.create();
MobList.prototype = {
	options : null,
	style : null,
	data : null,
	htmlPagerFn : 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,
								this.htmlPagerFn) : '&#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%" class="grid">'
							+ '<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>');
							}, 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="lbSmallImg '
																+ 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>';
					}, 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 && this.options.emptyResText) {
			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, this.render.bind(this, type, id, options || {}));
		}
	},

	render : function(type, id, options) {
		var realContent = $(this.tmpDiv.childNodes[0]);
		delete options.title;
		var win = new Window(Object.extend({
					id : this.winName + type + '_' + id,
					className : 'dialogItemDetail',
					width : 865,
					height : 600,
					minimizable : false,
					maximizable : false,
					resizable : false,
					wiredDrag : true,
					recenterAuto : false,
					destroyOnClose : true
				}, options));
		win.setContent(realContent.remove());
		win.showCenter(true);
	}

}

var LivingBeingDetail = {
	show : function(type, id, container, renderCallback) {
		var cbBinderParams = [type, container, renderCallback];
		switch (type) {
			case (ENTITY_TYPE.mapElm) :
				new LoadDoc(LivingBeingDetail.display.bind.apply(LivingBeingDetail.display, [LivingBeingDetail]
										.concat(cbBinderParams)), new DocToLoad('api', 'o/mapElmDetail', [["id", id]]));
				break;
			case (ENTITY_TYPE.heroe) :
				new LoadDoc(LivingBeingDetail.display.bind.apply(LivingBeingDetail.display, [LivingBeingDetail]
										.concat(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]]));
				break;
		}
	},

	display : function(type, container, renderCallback, data, oImg) {
		var lbRef = null;
		var isMapElm = false;
		var isHeroe = false;
		var isMob = false;
		switch (type) {
			case (ENTITY_TYPE.mapElm) :
				ClientContext.webTracking('extra/livingBeingDetail/mapElm');
				lbRef = data.refMapElm;
				isMapElm = true;
				break;
			case (ENTITY_TYPE.heroe) :
				ClientContext.webTracking('extra/livingBeingDetail/heroe');
				lbRef = data.heroe;
				isHeroe = true;
				break;
			case (ENTITY_TYPE.mob) :
				ClientContext.webTracking('extra/livingBeingDetail/mob/' + data.refMob.name);
				lbRef = data.refMob;
				isMob = true;
				break;
		}

		var base = new Element("div", {
					"class" : "itemDetail_base"
				});
		{
			var o = base.appendChild(new Element("div", {
						"class" : "itemDetail_ctna"
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn1" + (isMob || isHeroe ? ' itemDetail_ctn1_' + lbRef.refFamily_id : '')
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn2"
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn3"
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn4"
					}));
			if (isHeroe && lbRef.isValid) {
				o = o.appendChild(new Element("div", {
							"class" : "itemDetail_ctn5 itemDetail_ctn5_" + lbRef.refFamily_id
						}));
				o.appendChild((new Element("div", {
							"class" : "itemDetail_value",
							"onmouseover" : 'Overlib.open(event, \''
									+ ti("heroeStat.pow").capitalize().replace(/'/g, '\\\'') + '\');'
						})).update(lbRef.pow));
			}
			if (isMob && m_data.families[lbRef.refFamily_id].availableForPlayer) {
				o = o.appendChild(new Element("div", {
							"class" : "itemDetail_ctn5 itemDetail_ctn5_" + lbRef.refFamily_id
						}));
				o.appendChild((new Element("div", {
							"class" : "itemDetail_value",
							"onmouseover" : 'Overlib.open(event, \''
									+ ti("mobStat.value").capitalize().replace(/'/g, '\\\'') + '\');'
						})).update(lbRef.value));
			}
			o.appendChild((new Element("div", {
						"class" : "itemDetail_title"
					})).update(lbRef.name));
			{
				var src;
				if (oImg) {
					src = oImg.src;
				} else {
					if (isMob) {
						src = STATIC_PATH + 'open/img/mob/' + lbRef.id + '.png';
					} else if (isHeroe) {
						src = STATIC_PATH + 'open/img/heroe/' + lbRef.refFamily_id + '.png';
					} else if (isMapElm) {
						src = STATIC_PATH + 'open/img/mapElm/' + lbRef.id + '.png';
					}
				}
				if (src) {
					o.appendChild(new Element("img", {
								"class" : "itemDetail_img",
								src : src
							}));
				}
			}
			{
				var subTitle;
				if (isMob) {
					subTitle = lbRef.refFamily_name + ' / ' + lbRef.refScarcity_name;
				} else if (isHeroe) {
					subTitle = lbRef.refFamily_name + ' / ' + '<a href="javascript:void(detail(\'player\', '
							+ lbRef.player_id + '));">' + lbRef.player_name + '</a>';
				}
				if (subTitle) {
					o.appendChild(new Element("div", {
								"class" : "itemDetail_subTitle"
							}).update(subTitle));
				}
			}
			o.appendChild(new Element("div", {
						"class" : "itemDetail_credit"
					}).update(COPYRIGHT));
		}

		{
			var o = base.appendChild(new Element("div", {
						"class" : "itemDetail_ctnb"
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn1" + (isMob || isHeroe ? ' itemDetail_ctn1_' + lbRef.refFamily_id : '')
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn2"
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn3"
					})).appendChild(new Element("div", {
						"class" : "itemDetail_ctn4"
					}));
			var dataCtn = o.appendChild(new Element("div", {
						"class" : "itemDetail_data"
					}));

			if (isHeroe) {
				var metaCtn = dataCtn.appendChild(new Element("div", {
							"class" : "itemDetail_meta"
						}));

				metaCtn.appendChild(new Element("div").update(ti('heroeStat.xpUsedAvailable').capitalize() + ' : '
						+ (lbRef.xpUsed) + ' / ' + lbRef.xpAvailable));
				metaCtn.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" : "itemDetail_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 info = EffectDetail.getInfos(false, data.refEffects, effect.id, iLvl);

					var costHtml = null;
					var iCost = info.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;
						}
						costHtml = 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>';
					}

					var effectDescCtn = effectCtn.appendChild((new Element("div", {
								"class" : "effectType" + effectRef.refEffectType_id
							})).update('<span class="effectName">' + info.name + '</span>[' + iLvl + '] : ' + info.typeS
							+ (costHtml ? ', ' + costHtml : '') + '. '));

					var effectDescCtnSub = new Element("span");
					effectDescCtnSub.innerHTML = info.desc;
					if (10 > effectRef.visibility) {
						var link = (new Element("a", {
									"href" : "javascript:void(0);"
								})).update(ti("itemDetail.toggleDesc"));
						link.observe("click", LivingBeingDetail.toogleDesc.bindAsEventListener(LivingBeingDetail));
						var tmp = effectDescCtn.appendChild(new Element("span"));
						tmp.appendChild(document.createTextNode("["));
						tmp.appendChild(link);
						tmp.appendChild(document.createTextNode("]"));
						effectDescCtnSub.hide();
					}
					effectDescCtn.appendChild(effectDescCtnSub);
				});
			}

			dataCtn.appendChild((new Element("div", {
						"class" : "itemDetail_description"
					})).update(htmlEscape(lbRef.description)));

			if (0 == dataCtn.childNodes.length) {
				dataCtn.hide();
			}

			{
				var charactCtn = o.appendChild(new Element("div", {
							"class" : "itemDetail_charact"
						}));
				if (isMapElm) {
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_mapElmWalk",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("mapElmStat.walk").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(lbRef.walk));
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_mapElmFlyover",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("mapElmStat.flyover").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(lbRef.flyover));
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_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" : "itemDetail_agility",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("mobStat.agility").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(lbRef.agilityRef));
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_discipline",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("mobStat.discipline").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(lbRef.disciplineRef));
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_health",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("mobStat.health").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(lbRef.healthRef));
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_regen",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("mobStat.regen").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(lbRef.regenRef));
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_recovery",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("heroeStat.recovery").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(isMob ? 30 : lbRef.recoveryRef));
					charactCtn.appendChild((new Element("div", {
								"class" : "itemDetail_stamina",
								"onmouseover" : 'Overlib.open(event, \''
										+ ti("heroeStat.stamina").capitalize().replace(/'/g, '\\\'') + '\');'
							})).update(isMob ? 100 : lbRef.staminaRef));
				}
			}

			o.appendChild(new Element("div", {
						"class" : "itemDetail_credit"
					}).update(COPYRIGHT));
		}

		container.appendChild(base);

		if (renderCallback) {
			renderCallback(data);
		}
	},

	toogleDesc : function(e) {
		var o = Event.element(e).parentNode;
		Element.toggle(o);
		Element.toggle(o.nextSibling);
	}
};

var EffectDetail = {

	getInfos : function(combined, refEffects, refEffectId, iLvl) {
		var ids = [refEffectId];
		if (combined) {
			for (var i = 0; i < ids.length; i++) {
				$A(refEffects[ids[i]].dependencies).each(function(depId) {
							if (-1 == ids.indexOf(depId)) {
								ids.push(depId);
							}
						});
			}
		}

		var infos = ids.collect(function(subId) {
					var refEffect = refEffects[subId];
					return {
						id : subId,
						name : refEffect.name,
						desc : refEffect.description,
						typeS : refEffect.refEffectType_shortName,
						typeL : refEffect.refEffectType_longName,
						cost : null == refEffect.cost ? undefined : refEffect.cost,
						dependencies : refEffect.dependencies,
						lvl : subId == refEffectId && null != iLvl && !Object.isUndefined(iLvl) ? iLvl : undefined
					};
				});

		infos = infos.collect(function(info) {
					info.desc = htmlEscape(info.desc);

					var lvlStr;
					if (!Object.isUndefined(info.lvl)) { // compute
						// mathematical
						// expression with
						// ##lvl## in
						// description
						var exprs = [];
						var reg = new RegExp('([\(\)0-9\*\+\/\-]|##lvl##){2,}', 'g');
						var result;
						while (null != (result = reg.exec(info.desc))) {
							if (/[\*\+\/\-]/.test(result[0])) {
								exprs.push({
											expr : result[0],
											index : result.index
										});
							}
						}
						$A(exprs).sortBy(function(expr) {
									return -1 * expr.index;
								}).each(function(expr) {
							try {
								var newVal = expr.expr + '=' + Math.round(eval(expr.expr.replace(/##lvl##/g, info.lvl)));
							} catch (e) {
								throw "unable to eval mathematical expression: " + expr;
							}
							info.desc = info.desc.substr(0, expr.index) + newVal
									+ info.desc.substr(expr.index + expr.expr.length);
						});
						lvlStr = String(iLvl)
					} else {
						lvlStr = ti("effect.lvl").capitalize();
					}

					// replace ##lvl## and ##effect:this## in description
					info.desc = info.desc.replace(/##lvl##/g, '<span class="italic">' + lvlStr + '</span>');
					info.desc = info.desc.replace(/##effect:this##/g, '<span class="italic">' + info.name + '</span>');

					$A(info.dependencies).each(function(depId) {
						var depName = refEffects[depId].name;
						depHtml = combined
								? ('<span class="italic">' + depName + '</span>')
								: ('<span class="italic overliber" '
										+ htmlOverliber(EffectDetail.getHtml(true, refEffects, depId).collect(function(parts) {
											return parts.title + (Object.isUndefined(parts.cost) ? '' : ', ' + parts.cost)
													+ '.<br/>' + parts.desc;
										}).join('<div class="hr"><hr/></div>')) + '>' + depName + '</span>')
						info.desc = info.desc.replace(new RegExp('##effect:' + depId + '##', 'g'), depHtml);
					});

					return info;
				});

		return combined ? infos : infos[0];
	},

	getHtml : function(combined, refEffects, refEffectId, iLvl) {
		var infos = this.getInfos(combined, refEffects, refEffectId, iLvl);
		if (!combined) {
			infos = [infos];
		}
		var html = infos.collect(function(info) {
					return {
						info : info,
						title : '<span class="bold">' + info.name + '</span>[<span class="italic">'
								+ (Object.isUndefined(info.lvl) ? ti("effect.lvl").capitalize() : info.lvl) + '</span>] : '
								+ info.typeS,
						cost : (Object.isUndefined(info.cost) ? undefined : ti("effect.baseCost") + ' ' + info.cost),
						desc : info.desc
					};
				});
		return combined ? html : html[0];
	}

};

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://pm/sa=send;u=' + data.forumId + '\'))">'
								+ ti('playerDetail.sendMp') + '</a>') + '</div>'))
				+ '<div class="textLayer">'
				+ '<table width="100%" class="grid">'
				+ '<col width="150"/>'
				+ '<col/>'
				+ (data.description ? ('<tr>'
						+ ('<th class="header">' + ti("playerStat.desc").capitalize() + '</th>')
						+ ('<td class="border">' + htmlEscape(data.description) + '</td>') + '</tr>') : '')
				+ ('<tr>'
						+ ('<th class="header">' + ti("playerStat.lastCon").capitalize() + '</th>')
						+ ('<td class="border">' + 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 class="header">' + ti("playerStat.rank").capitalize() + '</th>')
						+ ('<td class="border">#' + data.rank + ' <span class="size-1">(' + ti("playerStat.score") + ' '
								+ data.score + ')</span>' + '</td>') + '</tr>') : '')
				+ ('<tr>'
						+ ('<th class="header">' + ti("playerStat.stat").capitalize() + '</th>')
						+ ('<td class="border">' + data.noVictory + ' ' + ti("playerStat.victory", data.noVictory) + ', '
								+ data.noDefeat + ' ' + ti("playerStat.defeat", data.noDefeat) + '</td>') + '</tr>')
				+ ('<tr>' + ('<th class="header">' + ti("playerStat.noMob").capitalize() + '</th>')
						+ ('<td class="border">' + data.noMob + '</td>') + '</tr>')
				+ ('<tr>' + ('<th class="header">' + ti("heroe_").capitalize() + '</th>')
						+ ('<td class="border"><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 class="header">' + ti("playerStat.lastBattle_").capitalize() + '</th>')
						+ ('<td class="border">'
								+ ('<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.'
													+ (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'] || (m_session && m_session.admin)) {
												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,
					className : "dialogLite",
					width : 600,
					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) {
		return false;
		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'));
	},

	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) {
		this.refProducts = refProductList.refProducts;
		var owned = [];
		if (m_session.gold) {
			owned.push({
						no : m_session.gold,
						name : ti('gold', m_session.gold)
					});
		}
		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">'
						+ ('<table width="100%" class="grid">'
								+ ('<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 = $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();
	},

	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 || Prototype.Browser.WebKit) {
			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 fitWinToContentSize() {
	if (bPopup) {
		window.scrollTo(9999, 9999);
		var sos = document.viewport.getScrollOffsets();
		window.resizeBy(sos.left, sos.top);
		window.scrollTo(0, 0);
	}
}

function htmlOverliber(s, w) {
	return 'onmouseover="Overlib.open(event, \'' + s.replace(/'/g, '\\\'').replace(/"/g, '&quot;') + '\''
			+ (w ? ',' + w : '') + ');"';
}

function htmlPlayerHonnor(rank, lvl) {
	return '';
	// 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) {
	options = options || {};
	var past = false;
	if (noSeconds <= 0) {
		if (options.enablePast) {
			past = true;
			noSeconds = Math.abs(noSeconds);
		} else {
			return false;
		}
	}

	startingNoSeconds = noSeconds;

	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 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>' + ('<div class="h2Shadower"></div>') + 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>';
}

function htmlSignNumber(n) {
	return n > 0 ? '+' + n : n;
}

/**
 * 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);
};

Array.fromV = function(v) {
	if (null === v || Object.isUndefined(v)) {
		return [];
	} else if (Object.isArray(v)) {
		return v;
	} else {
		return [v];
	}
};

Element.prototype.keepElmInWindow = function(yMargin, xMargin) {
	yMargin = yMargin | 0;
	xMargin = xMargin | 0;

	var scrollbarWidth = 18;
	var vpDim = document.viewport.getDimensions();
	var vpScroll = document.viewport.getScrollOffsets();
	var elmDim = this.getDimensions();
	var elmPos = this.cumulativeOffset();

	var iScrollX = 0;
	if (elmPos.left < vpScroll.left + xMargin) {
		iScrollX += vpScroll.left + xMargin - elmPos.left;
	} else if (elmPos.left + elmDim.width > vpScroll.left + vpDim.width - xMargin) {
		iScrollX -= elmPos.left + elmDim.width - (vpScroll.left + vpDim.width - xMargin) + scrollbarWidth;
	}

	if (iScrollX != 0) {
		this.style.left = (elmPos.left + iScrollX) + 'px';
	}

	var iScrollY = 0;
	if (elmPos.top < vpScroll.top + yMargin) {
		iScrollY += vpScroll.top + yMargin - elmPos.top;
	} else if (elmPos.top + elmDim.height > vpScroll.top + vpDim.height - yMargin) {
		iScrollY -= elmPos.top + elmDim.height - (vpScroll.top + vpDim.height - yMargin) + scrollbarWidth;
	}

	if (iScrollY != 0) {
		this.style.top = (elmPos.top + iScrollY) + 'px';
	}
};
/** ********************************************************************************************************* */

/*************************************************************************************************************
 * 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();
}
/** ************************************************************************** */

