var WebApp = (function() {
	var L2R = +1;
	var R2L = -1;

	var HEAD = 0;
	var HOME = 1;
	var BACK = 2;
	var LEFT = 3;
	var RIGHT = 4;
	var TITLE = 5;

	var _def, _headView, _head;
	var _webapp, _group, _bdo, _bdy;
	var _maxw, _maxh;
	var _scrID, _scrolling, _scrAmount;
	var _opener;

	var _prev		= -1;	// for beginslide/endslide event (SlideInfo)
	var _historyPos	= -1;	// warning: must order properly var names for reduction script
	var _history	= [];
	var _loader		= [];
	var _fading		= [];
	var _header		= [];
	var _ajax		= [];
	var _initialNav	= history.length;
	var _sliding	= 0;
	var _hold		= false;
	var _baseTitle	= "";
	var _baseBack	= "";
	var _lockCnt	= 0;
	var _width		= 0;
	var _height		= 0;
	var _lastScroll	= 1;
	var _dialog		= null;
	var _scrolled	= true;
	var _proxy		= "";
	
	// RFC 2397 (http://www.scalora.org/projects/uriencoder/)
	var _blank		= "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";

	var _v2			= !(!document.getElementsByClassName) && UA("WebKit");
	var _fullscreen	= IsMobile() && !UA("Safari");
//	var _touch		= 0 && (window.ontouchstart !== undefined);

	var _handler	= {};
	_handler.load				= [];
	_handler.beginslide			= [];
	_handler.endslide			= [];
	_handler.error				= [];
	_handler.success			= [];
	_handler.orientationchange	= [];
	_handler.tabchange			= [];
//	_handler.contentchange		= [];

/* Public */
	__p = {
		Proxy: function(url) { _proxy = url; },

		Opener: function(func) {
			_opener = func ? func : function(u) { location = u };
		},

		Refresh: function(i) {
			// TODO: refresh checkbox and radio by id or all
		},

		HideBar: function() {
			if (_scrolled && IsMobile()) {
				_scrolled = false;
				ToTop(1); setTimeout(ToTop, 0);
			}
			return false;
		},

		Header: function(show, what) {
			Buttons(show);
			Display(_headView, 0);
			_headView = $(what);
			Display(_headView, !show);
			_header[HEAD].style.zIndex = show ? 2 : "";
			return false;
		},

		Tab: function(id, active) {
			var o = $(id);
			ShowTab(o, $$("li", o)[active]);
		},

		AddEventListener: function(evt, handler) {
			if (typeof _handler[evt] != "undefined")
				if (_handler[evt].indexOf(handler) == -1)
					_handler[evt].push(handler);
		},
/*
		RemoveEventListener: function(evt, handler) {
			if (typeof _handler[evt] != "undefined")
				_handler[evt].splice(_handle.lastIndexOf(handler), 1);
		},
*/
		Toggle: function() {
			if (_history.length > 1) {
				if (_historyPos == _history.length - 1) {	// TODO: check this, _history.length should be greater than _historyPos
					AdjustView();
					history.back();
				} else {
					AdjustView();
					history.forward();
				}
			}
			return false;
		},

		Back: function() {
			if (_hold)
				return (_hold = false);

			if (history.length - _initialNav == _historyPos) {
				AdjustView();
				history.back();
			} else {
				AdjustView();
				_opener(_history[_historyPos - 1][1] || "#");
			}
			return false;
		},

		Home: function() {
			if (history.length - _initialNav == _historyPos) {
				AdjustView();
				history.go(-_historyPos);
			} else {
				AdjustView();
				_opener("#");
			}
			return (_hold = false);
		},

		Form: function(frm) {
			var s, a, b, c, o, k, f;

			a = $(frm);
			b = $(_history[_historyPos][0]);
			s = (a.style.display != "block");

			f = GetName(a) == "form" ? a : GetParent(a, "form");

			// WARNING: this variable must not be changed, else onsumbit will not point to the function anymore!
			k = f.onsubmit;
			if (!s) {
				f.onsubmit = f.onsubmit(null, true);

			} else {
				a.style.top = _group.offsetTop - 1 + "px";

				f.onsubmit = function(e, b) {
					if (b) return k;
					if (k) k(e);

					e.preventDefault();
					__p.Submit(this);
				};
			}

			AdjustView();
			Shadow(s, _group.offsetTop);
			Display(a, s);

			o = $$("legend", a)[0];
			SetTitle(s && o ? o.innerHTML : null);

			_dialog = (s) ? a : null;
			if (s) { c = a; a = b; b = c }

			DelLayerButtons(a);
			AddLayerButtons(b, s);

			if (s)	__p.Header(s);
			else	Buttons(!s);

			return false;
		},

		Submit: function(frm) {
			var a = arguments[1];	// submiter object to help focus fixer, should not be used by client code!
			var e = arguments[2];

			var f = $(frm);
			if(f && GetName(f) != "form") f = GetParent(f, "form");
			if (f) {
				var _ = function(i, f) {
					var q = "";
					for (var n = 0; n < i.length; n++)
						if (i[n].name && !i[n].disabled && (f ? f(i[n]) : 1))
							q += "&" + i[n].name + "=" + encodeURIComponent(i[n].value);
					return q;
				}

				var q  = _($$("input", f),
					function(i) {
						with(i) return ((Contains(type, ["text", "password", "hidden", "search"]) ||
										(Contains(type, ["radio", "checkbox"]) && checked)))
					});
					q += _($$("select", f));
					q += _($$("textarea", f));

				e  = e || event;
				a  = !a ? GetLink(e.target) : a;
				if (!a) FocusFixer();
				q += "&" + (a && a.id ? a.id : "__submit") + "=1";
				q  = q.substr(1);

				BasicAsync(f.action || self.location.href, null, q);
				if (_dialog) __p.Form(_dialog);
			}
			return false;
		},

		Postable: function(keys, values) {
			var q = "";
			for (var i = 1; i < values.length && i <= keys.length; i++)
				q += "&" + keys[i - 1] + "=" + encodeURIComponent(values[i]);
			return q.replace(/&=/g, "&").substr(1);
		},

		Request: function(url, prms, cb, async, loader) {
			cb = cb == -1 ? DefaultCallback() : cb;

			var o = new XMLHttpRequest();
			var c = function() { __callback(o, cb, loader) };
			var m = prms ? "post" : "get";

			async = !!async;
			if (loader) __p.Loader(loader, true);
			_ajax[_ajax.length] = [o, url, prms, arguments[5]];

			url = AddParam(url, "__async", "true");
			url = AddParam(url, "__source", _history[_historyPos][0]);
			url = SetURL(url);

			o.open(m, url, async);
			if (prms) o.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			o.onreadystatechange = (async) ? c : null;
			o.send(prms);

			if (!async) c();
		},

		Loader: function(obj, show) {
			var o = obj;
			var h = HasClass(o, "__lod");
			if (h == show)
				return h;

			if (show) {
				AddClass(o, "__lod");
				_loader.push(o);
			} else DelClass(o, "__lod");

			ApplyMore(o);
			return h;
		},

		Player: function(src) {
			src = src || GetLink(event.target).href;

			if (!IsMobile()) {
				window.open(src);
			} else {
				// prevent the back request sent by the internal player
				if (_v2) _opener("#" + Math.random());

				var w = $("__wa_media");
				var o = NewItem("iframe");
				o.id = "__wa_media";
				o.src = src;			// must be before appendChild to prevent Safari weird behavior

				_webapp.appendChild(o);
				if (w) _webapp.removeChild(w);
			}
			return false;
		}
	}
	
	function ToTop(h) {
		h = h ? h : 0;
		_webapp.style.minHeight = (_height + h) + "px";
		window.scrollTo(0, h);
	}

	function CalcEaseOut(s, w, dir, step, mn) {
		s += Math.max((w - s) / step, mn || 4);
		return [s, (w + w * dir) / 2 - Math.min(s, w) * dir];
	}

	function ApplyMore(o) {
		if (HasClass(o, "iMore")) {
			var a = $$("a", o)[0];
			if (a && a.title) {
				o = a.innerHTML;
				a.innerHTML = a.title;
				a.title = o;
			}
		}
	}

	function FocusFixer() {
		var i = NewItem("input");
		_group.appendChild(i);
		i.type = "text";
		i.focus();
		Display(i, 0);
		setTimeout(_group.removeChild, 5, i);
	}

	function Buttons(s) {
		if (_head) {
			for (var i = 1; i < _header.length; i++)
				Display(_header[i], s);

			Display(_header[BACK], s && !_header[LEFT] && _historyPos);
			Display(_header[HOME], s && !_header[RIGHT] && !_hold && _historyPos > 1);
		}
	}

	function AddLayerButtons(lay, ignore) {
		if (_head) {
			var a = $$("a", lay);
			var p = RIGHT;
	
			for (var i = 0; i < a.length && p >= LEFT; i++) {
				if (_header[p] && !ignore) { i--; p--; continue; }
	
				if (HasToken(a[i].rel, "action") ||
					HasToken(a[i].rel, "back")) {
	
					AddClass(a[i], p == RIGHT ? "iRightButton" : "iLeftButton");
					Display(a[i], 1);
					_header[p--] = a[i];
					_head.appendChild(a[i--]);
				}
			}
		}
	}

	function DelLayerButtons(lay) {
		if (_head) {
			for (var i = LEFT; i <= RIGHT; i++) {
				var a = _header[i];
				if (a && (	HasToken(a.rel, "action") ||
							HasToken(a.rel, "back")) ) {
	
					Display(a, 0);
					DelClass(a, i == RIGHT ? "iRightButton" : "iLeftButton");
					lay.insertBefore(a, lay.firstChild);
				}
			}
			_header[RIGHT] = $("waRightButton");
			_header[LEFT] = $("waLeftButton");
		}
	}

/* Private */
	function NoTag(s) { return s.replace(/<.+?>/g, "").replace(/^\s+|\s+$/g, "").replace(/\s{2,}/, " "); }
	function Contains(o, a) { return a.indexOf(o) != -1 }
	function IsAsync(o) { return HasToken(o.rev, "async") || HasToken(o.rev, "async:np"); }

	function $(i) { return typeof i == "string" ? document.getElementById(i) : i; }
	function $$(t, o) { return (o || document).getElementsByTagName(t); }
	function NewItem(t, c) {
		var o = document.createElement(t);
		if (c) o.innerHTML = c;
		return o;
	}
	function GetLink(o) { return GetName(o) == "a" ? o : GetParent(o, "a") }
	function GetName(o) { return o.localName.toLowerCase() }
	function HasToken(o, t) { return o && Contains(t, o.toLowerCase().split(" ")); }

	function HasClass(o, c) { return o && Contains(c, GetClass(o)); }
	function GetClass(o) { return o.className.split(" "); }
	function AddClass(o, c) {
		var h = HasClass(o, c);
		if (!h) o.className += " " + c;
		return h;
	}
	function DelClass(o) {
		var c = GetClass(o);
		var a = arguments;
		for (var i = 1; i < a.length; i++) {
			var p = c.indexOf(a[i]);
			if (p != -1) c.splice(p, 1);
		}
		o.className = c.join(" ");
	}
	function GetParent(o, t) {
		while ((o = o.parentNode) && (o.nodeType != 1 || GetName(o) != t));
		return o;
	}	
	function AnyOf(o, c) {
		while ((o = o.parentNode) && (o.nodeType != 1 || !HasClass(o, c)));
		return o;
	}

	function GetText(o) {
		var o = o.childNodes;
		for (var i = 0; i < o.length; i++)
			if (o[i].nodeType == 3)
				return o[i].nodeValue.replace(/^\s+|\s+$/g, "");
		return null;
	}

	function InitBlocks() {
		if (!_webapp || !_group) {
			_webapp	= $("WebApp");
			_group	= $("iGroup");
		}
	}

	function InitVars() {
		InitBlocks();

		_header[HEAD] = $("iHeader");
		_header[BACK] = $("waBackButton");
		_header[HOME] = $("waHomeButton");
		_header[RIGHT] = $("waRightButton");
		_header[LEFT] = $("waLeftButton");
		_header[TITLE] = $("waHeadTitle");

		_bdy = document.body;
		_bdo = (_bdy.dir == "rtl") ? -1 : +1;
	}

	function Display(o, s) { if (o) o.style.display = s ? "block" : "none" }

	function ShowLayer(o) {
		Display(o, 1);
		o.style.width = "100%";
	}

	// TODO: any way to do this with CSS??
	function AdjustLayer(o) {
		o = o || $(GetActive());

		if (o) {
			var z = $$("div", o);
			if (z[0] && HasClass(z[0], "iList")) {
				AddClass(o, "__lay");
				o.style.minHeight = parseInt(_webapp.style.minHeight) - _group.offsetTop + "px";
			} else DelClass(o, "__lay");
		}
	}

	function Shadow(s, p) {
		var o = $("__wa_shadow");
		o.style.top = p + "px";
		Display(o, s);
	}

	function Lock() {
		if (!_lockCnt++)
			Display($("__wa_noclick"), 1);
	}
	function Unlock() {
		if (!--_lockCnt)
			Display($("__wa_noclick"), 0);
	}

	function Historize(o, l) {
		if (o) {
			// TODO: push an array with [o, l ? location.hash : null, _lastScroll]
			// then use _history[x][0]...[x][2]			
			_history.splice(++_historyPos, _history.length);
			_history.push([o, l ? location.hash : null, _lastScroll]);
			
/*			_history.splice(++_historyPos, _history.length);
			_history.push(o);

			_location.splice(_historyPos, _location.length);
			_location.push(l ? location.hash : null);

			_scrPos.splice(_historyPos, _scrPos.length);
			_scrPos.push(_lastScroll);*/
		}
	}

	// We need to remove scripts from the clone to prevent execution
	function PrepareClone(o) {
		// prevent execution of script tag
		var s = $$("script", o);
		while(s.length)
			s[0].parentNode.removeChild(s[0]);

		// HACK: prevent invalidation of radio selection (Firefox) useful only for generated content...  (FIXME)
		s = $$("input", o);
		for (var i = 0; i < s.length; i++)
			if (s[i].type == "radio"){
				s[i].name += "_cloned";
			}
		return o;
	}

	function Cleanup() {
		var s, i, c;

		while (s = _loader.pop())
			__p.Loader(s, 0);

		s = $$("li");
		for (i = 0; i < s.length; i++)
			if (HasClass(s[i], "__sel"))
				DelClass(s[i], "__sel");
	}

	function ParseParams(s, np) {
		var ed = s.indexOf("#_");
		if (ed == -1)
			return null;

		var rs = "";
		var bs = SplitURL(s);
		if (!np) for (var i = 0; i < bs[1].length; i++)
					rs += "/" + bs[1][i].split("=").pop();

		return bs[2] + rs;
	}

	function FadeWait(o, cb) {
		setTimeout(function() {
			if (_fading.indexOf(o) != -1)
				FadeWait(o, cb);
			else cb();
		}, 5);
	}

	function FadeItem(o, show, cb, sp, nx) {
		// if we're already fading the same oject, exits
		if (!nx) {
			if (!o) {
				if (cb) cb();
				return;

			} else if (_fading.indexOf(o) != -1)
				return;

			else
				_fading.push(o);
		}

		// set default step if not defined
		if (!sp) sp = 0.5;

		// process fading effect
		with (o.style) {
			if ((!show && opacity > 0) || (show && opacity < 1)) {
				if (show) display = "block";
				opacity = parseFloat(opacity) + (show ? +sp : -sp);
				setTimeout(FadeItem, 0, o, show, cb, sp, 1);
			} else {
				display = (opacity == 0) ? "none" : "block";
				_fading.splice(_fading.indexOf(o), 1);
				if (cb) cb();
			}
		}
	}

	function IsMobile() {
		return (UA("iPhone") || UA("iPod") || UA("Aspen"));
	}

	function UA(s) {
		return Contains(s, navigator.userAgent);
	}

	function Resizer() {
		InitBlocks();
		if (_sliding || !_webapp || !_group)
			return;

		var w = (window.innerWidth >= _maxh) ? _maxh : _maxw;
		if (w != _width) {
			_width = w;
			_webapp.className = (w == _maxw) ? "portrait" : "landscape";
			AdjustLayer();
			CallListeners("orientationchange");
		}

		var h = window.innerHeight;
		var m = ((_width == _maxw) ? 416 : 268);	// TODO
			h = (h < m) ? m : h;
		if (h != _height) {
			_height = h;
			_webapp.style.minHeight = h + "px";
		}
		
		//if (_touch) __p.HideBar();
	}

	function Locator() {
		if (_sliding || _hold == location.href)
			return;
		_hold = false;

		var act = GetActive();
		if (act == null)
			if (location.hash.length > 0)	// there is a simple anchor, jump to it
				return;
			else							// No? should slide back to home
				act = _history[0][0];

		var cur = _history[_historyPos][0];
		if (act != cur) {
			var i, pos = -1;
			for (i in _history) {
				if (_history[i][0] == act) {
					pos = parseInt(i); break;
				}
			}
			if (pos != -1 && pos < _historyPos) {
				_historyPos = pos + 1;
				InitSlide(cur, act, L2R);
			} else {
				SlideTo(act);
			}
		}
	}

	function CallListeners(evt, ctx, obj) {
		// Do not waste time and memory if no handler have been defined for the given event
		var l = _handler[evt].length;
		if (l == 0) {
			return true;
		}

		var e = {};
		e.type = evt;
		e.target = obj || null;
		e.context = ctx || Explode(_history[_historyPos][1]);
		e.windowWidth = _width;
		e.windowHeight = _webapp.offsetHeight;

		var k = true;
		for (var i = 0; i < l; i++) {
			k = k && (_handler[evt][i](e) == false ? false : true);
		}
		return k;
	}

var _msy, _moy, _mty = 0, _ty = 0;

	function Init() {
/*
if (_touch) {
	_group.addEventListener("touchstart", function(e) {
		$(GetActive()).style.webkitTransitionProperty = "none";
		e.preventDefault();
	}, false);

	_group.addEventListener("touchend", function(e) {
		_moy = null;
		_ty += _mty;
		
		if (_ty > 0) {
			$(GetActive()).style.webkitTransition = "-webkit-transform 0.35s ease-out";
			$(GetActive()).style.webkitTransform = "translateY(0)";
			_ty = 0;
		}
			
		
	}, false);

	_group.addEventListener("touchmove", function(e) {
		_msy = e.touches[0].pageY;
		_moy = _moy || _msy;
		_mty = (_msy - _moy);
		
		if (_ty + _mty > 0)
			_mty = -_ty + (_mty + _ty) * 0.4;

		var xx = parseInt(_webapp.style.minHeight) - $(GetActive()).offsetHeight;

		if (_ty + _mty < xx)
			_mty = (-_ty + xx) + (_mty - (-_ty + xx)) * 0.3;

		$(GetActive()).style.webkitTransform = "translateY(" + (_ty + _mty) + "px)";

		e.preventDefault();
	}, false);
}
*/

		InitVars();
		Forms.Init();
		InitTab();
		InitHeader();
		InitObj("div", "__wa_noclick");
		InitObj("div", "__wa_shadow");
		
		
		__p.Opener(_opener);

		// Display loader if any
		var l = $("iLoader");
		if (l) { Display(l, 0); }

		// Add preloader
		l = NewItem("div");
		l.id = "iPL";
		_bdy.appendChild(l);

		// get screen size
		_maxw = screen.width;
		_maxh = screen.height;
		if (_maxw > _maxh) { l = _maxh; _maxh = _maxw; _maxw = l; }

		// Get the default layer
		_def = GetLayers()[0].id;
		Historize(_def);

		var a = GetActive();
		if (a != _def)	{ Historize(a, true); }
		if (!a)			{ a = _def; }

		Images.Init($(a));
		ShowLayer($(a));
		AddLayerButtons($(a));

		Display(_header[BACK], (!_header[LEFT] && _historyPos));
		Display(_header[HOME], (!_header[RIGHT] && _historyPos > 1 && a != _def));

		if (_header[BACK]) {
			_baseBack = _header[BACK].innerHTML;
		}
		if (_header[TITLE]) {
			_baseTitle = _header[TITLE].innerHTML;
			_header[TITLE].innerHTML = GetTitle($(a));
		}

		setInterval(Locator, 250);
		setTimeout(Images.Show, 300);
		setTimeout(AdjustView, 500);
		setTimeout(CallListeners, 0, "load");

/*		_group.addEventListener("touchstart", function() {
			alert("ok");
		}, false);
*/
		(_fullscreen ? _group : document).addEventListener(_fullscreen ? "touchmove" : "scroll", Images.Listener, false);
	}

/* Event */
	function ShowTab(ul, li, h, ev) {
		var c, s, al = $$("li", ul);
		for (var i = 0; i < al.length; i++) {
			c = (al[i] == li);
			if (c) s = i;

			Display($(ul.id + i), (!h && c));
			DelClass(al[i], "__act");
		}
		AddClass(li, "__act");
		if (ev) CallListeners("tabchange", [s], ul);
	}
	
	function ListenClick(e, b) {
		if (_sliding) {
			e.preventDefault();
			return;
		}

		/* Checkbox label */
		var o = e.target;
		var n = GetName(o);
		if (n == "label") {
			var f = $(o.getAttribute("for"));
			if (HasClass(f, "iToggle"))
				setTimeout(FlipCheck, 1, f.previousSibling.childNodes[1], true);
			return;
		}

		/* Radio parent */
		var li = GetParent(o, "li");		
		if (li && HasClass(li, "iRadio")) {
			AddClass(li, "__sel");
			ShowRadio(li);
			return;
		}

		/* handle onclick event on links */
		var a  = GetLink(o);
/*        if (a && a.onclick) {
			var old = a.onclick;
			a.onclick = null;
			var val = old(e);
			setTimeout(function() { a.onclick = old }, 0);
			if (val === false) {
				e.preventDefault();
				return;
			}
        }
*/

		/* Tab */
		var ul = GetParent(o, "ul");
		var pr = !ul ? null : ul.parentNode;
		var ax = a && IsAsync(a);
		if (ul && HasClass(pr, "iTab")) {
			var h = $(ul.id + "-loader");
			Display(h, 0);

			if (ax) {
				Display(h, 1);
				BasicAsync(a, function(o) {
					Display(h, 0);
					Display($(ShowAsync(o)[0]), 1);
					ShowTab(ul, li, null, 1);
				});

			} else { h = null }
			ShowTab(ul, li, h, !ax);
			e.preventDefault();
			return;
		}

		/* Common button */
		if (a && /*!a.onclick &&*/
			Contains(a.id, ["waBackButton", "waHomeButton"])) {

			if (a.id == "waBackButton")	__p.Back();
			else						__p.Home();

			e.preventDefault();
			return;			
		}

		/* Radio list */
		if (ul && HasClass(ul, "iCheck")) {
			var al = $$("li", ul);
			for (var i = 0; i < al.length; i++)
				DelClass(al[i], "__act", "__sel");
			AddClass(li, "__act __sel");
			setTimeout(DelClass, 1000, li, "__sel");
			e.preventDefault();
			return;
		}

		/* Menu and list */
		if (ul && !HasClass(li, "iMore") &&
			((HasClass(ul, "iMenu") || HasClass(pr, "iMenu")) ||
			 (HasClass(ul, "iList") || HasClass(pr, "iList"))) ) {

			if (a && !HasClass(a, "iButton")) {
				var c = AddClass(li, "__sel");
				if (ax) {
					if (!c) BasicAsync(a);
					e.preventDefault();
					return;
				}
			}
		}

		/* More */
		var dv = AnyOf(o, "iMore");
		if (dv) {
			if (!__p.Loader(dv, 1) && IsAsync(a))
				BasicAsync(a);
			e.preventDefault();
			return;
		}

		/* Top form button */
		if (a && _dialog /*&& !a.onclick*/) {
			if (HasToken(a.rel, "back"))
				__p.Form(_dialog, a);
			if (HasToken(a.rel, "action"))
				__p.Submit(_dialog, a);
			e.preventDefault();
			return;
		}

		/* Media player */
		if (a && HasToken(a.rev, "media")) {
			__p.Player(a.href);
			Unselect(li);
			e.preventDefault();
			return;
		}

		/* Basic async link */
		if (ax) {
			BasicAsync(a);
			e.preventDefault();

		} else if (a) {
			/* Basic go layer */
			var l = Contains("#_", a.href);
			if (li || l) {
				AdjustView();
				setTimeout(function() { _opener(a.href) } , !l ? 1000 : 0);
				if (!l)	Unselect(li);
				e.preventDefault();
/*
			} else if (li) {
				Unselect(li);
				e.preventDefault();*/
			}
		}
	}

	function Unselect(li) {
		if (li) setTimeout(DelClass, 500, li, "__sel");
	}

/* Animate */

	function SlideTo(to) {
		if (_history[_historyPos][0] != to)
			InitSlide(_history[_historyPos][0], to);
		return false;
	}

	function InitSlide(src, dst, dir) {
		if (_sliding)
			return;

		Lock();

		if (dst == _history[0][0])
			_initialNav = history.length;

		dir = dir || R2L;
		src = $(src);
		dst = $(dst);
		AdjustLayer(dst);

//		Display($("iFooter"), 0);
		_sliding = 1;

		var h, p;

		if (typeof _bdy.style.webkitTransform != "undefined") {
			p = XY(_head);
			h = PrepareClone(_head.cloneNode(true));
			h.className = "__head";
			h.style.position = "absolute";
			h.style.top = p.y + "px";
			h.style.left = p.x + "px";
		}

// New title bar
		DelLayerButtons(src);
		AddLayerButtons(dst);

		_prev = _historyPos;
		if (dir == R2L)
			Historize(dst.id, true);
		else
			while (_historyPos && _history[--_historyPos][0] != dst.id) {}

		Buttons(1);
		if (h) _header[HEAD].appendChild(h);

		if (_header[BACK]) {
			var txt;
			if (dir == R2L)
				txt = (_hold ? "" : NoTag(src.title)) || _baseBack;
			else if (_historyPos)
				txt = NoTag($(_history[_historyPos - 1][0]).title) || _baseBack;
			if (txt) _header[BACK].innerHTML = txt;
		}
		SetTitle(_hold ? dst.title : null);
///////////////////
/*
		h.style.opacity = 1;
		h.style.webkitTransform="translate(0,0)";
		_head.style.opacity = 0;
		_head.style.webkitTransform="translate(" + (-dir * _bdo) + "00px,0)";
*/
/*
		setTimeout(function() {
			AddClass(_header[HEAD], "__go");

			h.style.opacity = 0;
			h.style.webkitTransform="translate(" + (dir * _bdo) + "00px,0)";
			_head.style.opacity = 1;
			_head.style.webkitTransform="translate(0,0)";

			setTimeout(function() {
				DelClass(_header[HEAD], "__go");
				_header[HEAD].removeChild(h);
			}, 350);
		}, 0);
*/
//		_head.style.webkitTransform="translate(" + (dir * _bdo) + "00px,0)";
//		_head.style.opacity = 0;

		
//		HideHeader(0, function() {
			DoSlide(src, dst, dir);
//		});
	}

	function SlideInfo(d) {
		return [Explode(_history[_prev][1]) , Explode(location.hash), d];
	}
	

	function DoSlide(src, dst, dir) {
		CallListeners("beginslide", SlideInfo(dir));

//		DelLayerButtons(src);
//		AddLayerButtons(dst);
		Forms.Init(dst);

		var b = _group;
		var go = dir * _bdo;

		// default effect if not webkit TODO: IsDefined(...)
		if (typeof _bdy.style.webkitTransform == "undefined") {
			Display(src, 0);
			ShowLayer(dst);
			EndSlide(src, dst, dir);
			return;
		}

		ShowLayer(src);
		ShowLayer(dst);
		var h1 = src.offsetHeight;
		var h2 = dst.offsetHeight;
		AddClass(b, "__slide");

		b.style.height = (h1 > h2 ? h1 : h2) + "px";
		src.style.webkitTransform = "translate(0,0)";
		dst.style.webkitTransform = "translate(" + (-go) + "00%,0)";

		// header anim
		var h = _header[HEAD].lastChild;
		h.style.opacity = 1;
		h.style.webkitTransform="translate(0,0)";
		_head.style.opacity = 0;
		_head.style.webkitTransform="translate(" + (-dir * _bdo * 3) + "0%,0)";
		_header[TITLE].style.webkitTransform="translate(" + (-dir * _bdo * 7) + "0%,0)";
		AddClass(_header[HEAD], "__slide");

		setTimeout(function() {
			AddClass(b, "__go");
			src.style.webkitTransform = "translate(" + (+go) + "00%,0)";
			dst.style.webkitTransform = "translate(0,0)";

			AddClass(_header[HEAD], "__go");

			h.style.opacity = 0;
			h.style.webkitTransform="translate(" + (dir * _bdo * 3) + "0%,0)";
			_head.style.opacity = 1;
			_head.style.webkitTransform="translate(0,0)";
			_header[TITLE].style.webkitTransform="translate(0,0)";

			setTimeout(function() {
				DelClass(_header[HEAD], "__slide");
				DelClass(_header[HEAD], "__go");
				_header[HEAD].removeChild(h);
				b.style.webkitTransform = "";

				Display(src, 0);
				b.style.height = "";
				DelClass(b, "__slide");
				DelClass(b, "__go");
				EndSlide(src, dst, dir);
			}, 350);

		}, 0);
	}

	function EndSlide(src, dst, dir) {
		Cleanup();
/*
		if (_header[BACK]) {
			var txt;
			if (dir == R2L)
				txt = (_hold ? "" : NoTag(src.title)) || _baseBack;
			else if (_historyPos)
				txt = NoTag($(_history[_historyPos - 1]).title) || _baseBack;
			if (txt) _header[BACK].innerHTML = txt;
		}
*/
//		Display($("iFooter"), 1);
//		SetTitle(_hold ? dst.title : null);
//		DelClass(_group, "__wa_slideV1");
//		ShowLayer(dst);
//		ShowHeader(null, function() {
			CleanSlide(dir);
			CallListeners("endslide", SlideInfo(dir));
			setTimeout(Images.Show, 0);
			_prev = -1;
//		});
	}

	function CleanSlide(dir) {
		Unlock();
		setTimeout(AdjustView, 0, (dir == L2R) ? _history[_historyPos + 1][2] : null);
		_sliding = 0;
	}

	function SetTitle(title/*, hide*/) {
		var o;
		if (o = _header[TITLE]) {
			o.innerHTML = title || GetTitle($(GetActive())) || _baseTitle;
			//Display(o, !hide);
		}
	}

	function HideHeader(s, cb) {
		FadeItem(_head, 0, function() {
			if (cb) cb();
			if (_dialog) __p.Form(_dialog);
			Display(_headView, 0);
		}, s ? 1 : null);
	}

	function ShowHeader(s, cb) {
/*		FadeWait(_head, function() {
			Display(_header[BACK], !_header[LEFT] && _historyPos);
			Display(_header[HOME], !_header[RIGHT] && _historyPos > 1);
			Display(_header[LEFT], 1);
			Display(_header[RIGHT], 1);
			Buttons(1);
			FadeItem(_head, 1, cb, s ? 1 : null);
		});*/
	}

	function FlipCheck(o, dontChange) {
		var i = $(o.parentNode.title);
		var txt = i.title.split("|");
		if (!dontChange)
			i.click();

		with (o.nextSibling) {
			innerHTML = txt[i.checked ? 0 : 1];
			if (i.checked) {
				o.style.left = "";
				o.style.right = "-1px";
				o.parentNode.className = "iToggleOn";
				style.left = 0;
				style.right = "";
			} else {
				o.style.left = "-1px";
				o.style.right = "";
				o.parentNode.className = "iToggle";
				style.left = "";
				style.right = 0;
			}
		}
	}

	function AdjustView(to) {
		_lastScroll = window.pageYOffset;

		var h = to ? to : Math.min(50, _lastScroll);
		var s = to ? Math.max(1, to - 50) : 1;
		var d = to ? -1 : +1;

		while (s <= h) {
//			if (_scrolling) console.log("ok");
			var z = CalcEaseOut(s, h, d, 6, 2);
			s = z[0]; window.scrollTo(0, z[1]);
		}
		if (!to) __p.HideBar();
	}

	function Explode(loc) {
		// WARNING: with classic anchors the returned value of this function will be wrong
		if (loc) {
			var pos = loc.indexOf("#_");
			var vis = [];

			if (pos != -1) {
				loc = loc.substring(pos + 2).split("/");
				vis = GetLayers().filter(
						function(l) { return l.id == "wa" + loc[0] });
			}
			if (vis.length) {
				loc[0] = vis[0].id;
				return loc;
			}
		}
		return [];
	}

	function GetLayers() {
		var lay = [];
		var src = _group.childNodes;
		for (var i = 0; i < src.length; i++)
			if (src[i].nodeType == 1 && HasClass(src[i], "iLayer"))
				lay.push(src[i]);
		return lay;
	}

	function GetTitle(o) {
		return (!_historyPos && _baseTitle) ? _baseTitle : o.title;
	}

	function GetActive() {
		// FIXME: should always return the active layer even if the hash is incorrect or use a classic anchor?
		var h = location.hash;
		return !h ? _def : Explode(h)[0];
	}

	function SetURL(url) {
		var d = url.match(/[a-z]+:\/\/(.+:.*@)?([a-z0-9-\.]+)((:\d+)?\/.*)?/i);
		return (!_proxy || !d || d[2] == location.hostname)
			? url : AddParam(_proxy, "__url=", url);
	}

	function SplitURL(u) {
		var s, q, d;

		s = u.replace(/&amp;/g, "&");
		d = s.indexOf("#");
		d = s.substr(d != -1 ? d : s.length);
		s = s.substr(0, s.length - d.length);
		q = s.indexOf("?");
		q = s.substr(q != -1 ? q : s.length);
		s = s.substr(0, s.length - q.length);
		q = !q ? [] : q.substr(1).split("&");

		return [s, q, d];
	}

	function AddParam(u, k, v) {
		u = SplitURL(u);
		var q = u[1].filter(
				function(o) { return o && o.indexOf(k + "=") != 0 });	// != 0 => any parameter not starting with (k + "=")
		q.push(k + "=" + encodeURIComponent(v));
		return u[0] + "?" + q.join("&") + u[2];
	}

	function BasicAsync(item, cb, q) {
		var h, o, u, i;

		i = (typeof item == "object");
		u = (i ? item.href : item);
		o = GetParent(item, "li");	// get loader

		if (!cb) cb = DefaultCallback(u, HasToken(item.rev, "async:np"));
		__p.Request(u, q, cb, true, o, (i ? item : null));
	}

	function DefaultCallback(i, np) {
		return function(o) {
			var u = i ? ParseParams(i, np) : null;
			var g = ShowAsync(o);

			if (g && (g[1] || u)) {
				AdjustView();
				_opener(g[1] || u);
			} else {
				setTimeout(Cleanup, 250);
			}

			return null;
		};
	}
	
	function ReadTextNodes(o) {
		var nds = o.childNodes;
		var txt = "";
		for (var y = 0; y < nds.length; y++) 
			txt += nds[y].nodeValue;
		return txt;
	}

	function ShowAsync(o) {
		if (o.responseXML) {
			o = o.responseXML.documentElement;

			var s, t, k, a = GetActive();

			/* force jump to a given layer */
			var g = $$("go", o);
			g = (g.length != 1) ? null : g[0].getAttribute("to");

			/* get all parts to update */
			var f, p = $$("part", o);
			if (p.length == 0) p = [o];

			for (var z = 0; z < p.length; z++) {
				var dst = $$("destination", p[z])[0];
				if (!dst) break;

				var mod = dst.getAttribute("mode");
				var txt = ReadTextNodes($$("data", p[z])[0]);

				var i = dst.getAttribute("zone");
				if (dst.getAttribute("create") == "true" &&
					i.substr(0, 2) == "wa" && !$(i)) {

					var n = NewItem("div");
					n.className = "iLayer";
					n.id = i;
					_group.appendChild(n);
				}

				f = f || i;
				g = g || dst.getAttribute("go");		// For compatibility with older version
				i = $(i || dst.firstChild.nodeValue);	// For compatibility with older version

//				if (!CallListeners("contentchange", [mod, txt], i))
//					continue;

				/* if we target the active layer, remove buttons */
				if (!k && a == i.id) {
					HideHeader(1);
					DelLayerButtons(i);
					k = i;
				}

				/* update content */
				SetContent(i, txt, mod);
			}

			/* Custom title for the given layer */
			if (t = $$("title", o)[0]) {
				var s = t.getAttribute("set");
				$(s).title = ReadTextNodes(t);
				if (a == s) SetTitle(null, 1);
			}

			/* active layer is targeted? show new header */
			if (k) {
				AddLayerButtons(k);
				ShowHeader(1);
			}

			/* script to execute */
			var e = $$("script", o)[0];
			if (e) eval(ReadTextNodes(e));

			return [f, g ? "#_" + g.substr(2) : null];
		}

		throw "Invalid asynchronous response received.";
	}

	function SetContent(o, c, m) {
		// Store content in a temp <DIV> to prepare script execution
		c = NewItem("div", c);
		// Clone the <DIV> so that Safari properly recognize <SCRIPT> tags
		c = c.cloneNode(true);
		// replace images with blank content to speed up loading!
		Images.Init(c);

		// Append content to webapp
		if (m == "replace" || m == "append") {
			if (m != "append")
				while (o.hasChildNodes())
					o.removeChild(o.firstChild);

			while (c.hasChildNodes())
				o.appendChild(c.firstChild);
		} else {
			var p = o.parentNode;
			var w = (m == "before") ? o : o.nextSibling;

			if (m == "self")
				p.removeChild(o);
			while (c.hasChildNodes())
				p.insertBefore(c.firstChild, w);
		}
	}

	function __callback(o, cb, lr) {
		if (o.readyState != 4)
			return;

		var er, ld, ob;
		er = (o.status != 200 && o.status != 0);

		if (!er) {
			try { if (cb) ld = cb(o, lr); } catch (ex) { er = ex; console.error(er); }
		}

		if (lr) {
			__p.Loader(lr, 0);
			if (er) DelClass(lr, "__sel");
		}

		if (ob = _ajax.filter(function(a) { return o == a[0] })[0]) {
			CallListeners(er ? "error" : "success", ob, ob.pop());
			_ajax.splice(_ajax.indexOf(ob), 1);
		}
	}

/* Render */
	function InitHeader() {
		var hd = _header[HEAD];
		if (hd) {
			var dv = NewItem("div");
			dv.style.opacity = 1;
			while (hd.hasChildNodes())
				dv.appendChild(hd.firstChild);
			hd.appendChild(dv);
			_head = dv;
		}
	}

	function InitTab() {	// TODO: use showtab?
		var o = $$("ul");
		for(var i = 0; i < o.length; i++) {
			var p = o[i].parentNode;
			if (p && HasClass(p, "iTab")) {
				AddClass($$("li", o[i])[0], "__act");
				if (p = $(o[i].id + "0"))
					Display(p, 1);
			}
		}
	}

	function InitRadio(p) {
		var s = "wa__radio";
		var o = $$("li", p);	// TODO: use getElementsByClassName

		// search each <li> for unprocessed iRadio
		for (var i = 0; i < o.length; i++) {
			if (HasClass(o[i], "iRadio") && !HasClass(o[i], "__done")) {
				var lnk = NewItem("a");
				var sel = NewItem("span");
				var inp = $$("input", o[i]);

				for (var j = 0; j < inp.length; j++) {
					with (inp[j]) if (type == "radio" && checked) {
						sel.innerHTML = GetText(parentNode);
						break;
					}
				}
				lnk.appendChild(sel);
				while (o[i].hasChildNodes())
					lnk.appendChild(o[i].firstChild);
				o[i].appendChild(lnk);
				lnk.href = "#";
				lnk.onclick = function() { _hold = location.href; return SlideTo(s) };
				AddClass(o[i], "__done");
			}
		}

		if (!$(s)) {
			var d = NewItem("div");
			d.className = "iLayer";
			d.id = s;
			_group.appendChild(d);
		}
	}

	function ClickRadio(a, p, u) {
		var x = $$("input", p);
		var y = $$("a", u);
		var b;

		for (var i = 0; i < y.length; i++) {
			if (y[i] == a) {
				x[i].checked = true;
				$$("span", p)[0].innerHTML = GetText(x[i].parentNode);
				b = p.getAttribute("value");
				if (b && b.toLowerCase() == "autoback")
					setTimeout(__p.Back, 0);
				break;
			}
		}
	}

	function ShowRadio(p) {
		var o = $$("input", p);
		var dv = NewItem("div");
		var ul = NewItem("ul");
		ul.className = "iCheck";

		for (var i = 0; i < o.length; i++) {
			if (o[i].type == "radio") {
				var li = NewItem("li");
				var a = NewItem("a", o[i].nextSibling.nodeValue); // TODO: is that correct???
				a.href = "#";
				a.onclick = function(e) { ClickRadio(this, p, ul) };
				li.appendChild(a);
				ul.appendChild(li);
				if (o[i].checked) li.className = "__act";
			}
		}

		dv.className = "iMenu";
		dv.appendChild(ul);

		o = $("wa__radio");
		if (o.firstChild)
			o.removeChild(o.firstChild);
		o.title = GetText(p.firstChild);
		o.appendChild(dv);
	}

	function InitObj(t, i) {
		var o = NewItem(t);
		o.id = i;
		_webapp.appendChild(o);
		return o;
	}

	function InitCheck(p) {
		var o = $$("input", p);

		for (var i = 0; i < o.length; i++) {
			if (o[i].type == "checkbox" && HasClass(o[i], "iToggle") && !HasClass(o[i], "__done")) {
				if (!o[i].id)
					o[i].id = "__" + Math.random();
				if (!o[i].title)
					o[i].title = "ON|OFF";

				var txt = o[i].title.split("|");

				var b1 = NewItem("b", "&nbsp;");
				var b2 = NewItem("b");
				var i1 = NewItem("i", txt[1]);

				b1.className = "iToggle";
				b1.title = o[i].id;
				b1.appendChild(b2);
				b1.appendChild(i1);
				o[i].parentNode.insertBefore(b1, o[i]);
				b2.onclick = function() { FlipCheck(this) };
				FlipCheck(b2, true);
				AddClass(o[i], "__done");
			}
		}
	}

	function XY(elm) {
		var mx = 0;
		var my = 0;

		while (elm) {
			mx += elm.offsetLeft;
			my += elm.offsetTop;
			elm = elm.offsetParent;
		}

		return {x:mx, y:my};
	}

	function IsViewable(o) {
		var x11, x12, y11, y12;
		var x21, x22, y21, y22;

		var p = XY(o);

		x11 = p.x;
		y11 = p.y;
		x12 = x11 + o.offsetWidth - 1;
		y12 = y11 + o.offsetHeight - 1;

		x21 = window.pageXOffset;
		y21 = window.pageYOffset;
		x22 = x21 + _width - 1;
		y22 = y21 + _height - 1;

		return !(x11 > x22 || x12 < x21 || y11 > y22 || y12 < y21);
	}

/* Form custom elements */
	var Forms = {
		Init: function(l) {
			InitCheck(l);
			InitRadio(l);
		}
	}

/* Progressive images loading */
	var Images = {
		Init: function(c) {
			var p, tmp = $$("img", c);
			for (var i = 0; i < tmp.length; i++) {
				// Ignore elements with button parent
				if ( (p = GetParent(tmp[i], "a")) &&
						(	HasToken(p.rel, "action") ||
							HasToken(p.rel, "back")) )
					continue;
				tmp[i].setAttribute("load", tmp[i].src);
				tmp[i].src = _blank;
			}
		},

		Check: function() {
			if (_scrAmount - window.pageYOffset == 0) {
				_scrID = clearInterval(_scrID);	// no return = disable _scrID
				Images.Show();
			}
		},

		Show: function() {
			var img = $$("img", $(GetActive()));
			for (var i = 0; i < img.length; i++) {
				var o = img[i].getAttribute("load");
				if (o && IsViewable(img[i])) {
					img[i].src = o;
					img[i].removeAttribute("load");
				}
			}
		},

		Listener: function() {
			_scrolled = true;
			if (!_sliding) {
				if (!_scrolling) {
					_scrolling = true;
					setTimeout(function() {
						_scrAmount = window.pageYOffset;
						_scrolling = false;
					}, 500);
				}
				if (!_scrID) _scrID = setInterval(Images.Check, 1000);
			}
		}
	}

/* PreLoad */
	setInterval(Resizer, 100);
//	window.addEventListener("orientationchange", Resizer, false);

	addEventListener("load", Init, true);
	addEventListener("click", ListenClick, true);

/* Static */
	return __p;
})();

var WA = WebApp;
