/**
 * Lebegő panelek kezelését segítő modul és dragging helper.
 *
 * http://wiki.deepskeye.gotdns.org/index.php/Flpanel.js
 */
function _HTDrag(x0, y0, inClient) {
	this.inClient = inClient ? true : false;
	if(!this.inClient) {
		with(document.documentElement) { // ff és ie
			x0 += scrollLeft;
			y0 += scrollTop;
		}
		with(document.body) { // webkit
			x0 += scrollLeft;
			y0 += scrollTop;
		}
	}
	this.x0 = x0;
	this.y0 = y0;
}

_HTDrag.prototype = {
	drag: function(fmove, fup) {
		var _this = this;

		function mmv(e) {
			if(!e) e = event;
			jgtc.discardEvent(e);

			var xm = e.clientX;
			var ym = e.clientY;
			if(!_this.inClient) {
				with(document.documentElement) { // ff és ie
					xm += scrollLeft;
					ym += scrollTop;
				}
				with(document.body) { // webkit
					xm += scrollLeft;
					ym += scrollTop;
				}

			}
			var x = xm - _this.x0;
			var y = ym - _this.y0;

			fmove(x, y, xm, ym);
		}

		function mmv2(e) {
			if(!e) e = event;
			jgtc.discardEvent(e);

			var element;

			if(is.ie) {
				try {
					element = e.srcElement.ownerDocument.parentWindow.frameElement;
				} catch(err) {
					return;
				}
			} else {
				element = e.view.frameElement;
			}

			var b = jgtc.getBounds(element);
			var x = b.x + e.clientX - _this.x0;
			var y = b.y + e.clientY - _this.y0;
			if(_this.inClient) {
				// a b.x és b.y koordináták tartalmazzák a scroll pozíciót is, ezért itt az el kell tüntetni, ha nem szükséges.
				with(document.documentElement) { // ff és ie
					x -= scrollLeft;
					y -= scrollTop;
				}
				with(document.body) { // webkit
					x -= scrollLeft;
					y -= scrollTop;
				}
			}

			fmove(x, y);
		}

		function mup(e) {
			if(!e) e = event;
			jgtc.discardEvent(e);

			jgtc.releaseEvent(document, "mousemove", mmv, true);
			jgtc.releaseEvent(document, "mouseup", mup, true);
			for(var i = 0; i < ifrms.length; i++) {
				jgtc.releaseEvent(ifrms[i].contentWindow.document, "mousemove", mmv2, true);
				jgtc.releaseEvent(ifrms[i].contentWindow.document, "mouseup", mup, true);
			}

			fup();
		}

		jgtc.captureEvent(document, "mousemove", mmv, true);
		jgtc.captureEvent(document, "mouseup", mup, true);

		var ifrms = document.getElementsByTagName("IFRAME");
		for(var i = 0; i < ifrms.length; i++) {
			jgtc.captureEvent(ifrms[i].contentWindow.document, "mousemove", mmv2, true);
			jgtc.captureEvent(ifrms[i].contentWindow.document, "mouseup", mup, true);
		}
	},

	setAnchor: function(x0, y0) {
		this.x0 = x0;
		this.y0 = y0;
	}
}

var HTAfl = {
	zmax: 0, // deprecated!!!
	ztop: 1000,
	uid: 1,
	msgListeners: new Array(),
	widgets: new Array(),
	idPrefix: "htf",

	/**
	 * Lebegőablak megnyitása. Ha a wname paraméter egy név, akkor megnyílik az ablak, vagy fókuszba kerül, ha már létezik az ablak, ha értéke null,
	 * akkor mindenképpen új ablak nyílik.
	 *
	 * A funkció egy DOM element objektumot ad vissza, ami a böngésző által biztosított funkciókon és propertyken kívül a következő funkciókat tartalmazza:
	 *   htfGetName
	 *   htfFocus
	 *   htfClose
	 *   htfReload
	 *   htfSendMessage
	 *   htfAddMessageListener
	 *   htfRemoveMessageListener
	 *
	 * Bejövő paraméterek:
	 *   wname:  ablak neve, vagy null.
	 *   params:  parméter objektum. kötelező paraméter, ez a megnyitandó ablak leírója.
	 *     type:  string, az ablak típusa, 'url', 'html' vagy 'json' lehet az értéke. (default: 'url')
	 *     ref:  string vagy object. Az érték értelmezése a 'type' paramétertől függ, ha a 'type' parméter értéke 'url', akkor ez a megnyitandó ablak URL-je,
	 *             ha 'html', akkor a tartalom html stringje, ha 'json' akkor egy jsonML objektum.
	 *     view:  egy funkció (konstruktor), egy FlAbstractView-ből származtatott objektum, a megjelenítés vezérlése. (default: FlDefaultView)
	 *
	 *     width:

	 *
	 *   return value: a fő ablak root dom elementje.
	 *
	 */
	open: function(wname, params) {
		params = jgtc.validateObject(params, [
			[ 'type', 'string', 'url' ],
			[ 'view', 'function', FlDefaultView ],
			[ 'autoBounds', 'boolean', false ],
			[ 'swf', 'object', {} ]
		], false);

		params.swf = jgtc.validateObject(params.swf, [
			[ 'movie', 'string', params.ref ],
			[ 'quality', 'string', "high" ],
			[ 'bgcolor', 'string', "#000000" ],
			[ 'play', 'string', "true" ],
			[ 'loop', 'string', "true" ],
			[ 'wmode', 'string', "opaque" ],
			[ 'scale', 'string', "showall" ],
			[ 'menu', 'string', "false" ],
			[ 'devicefont', 'string', "false" ],
			[ 'salign', 'string', "" ],
			[ 'allowScriptAccess', 'string', "sameDomain" ]
		], false);

		var widget = this.getWidgetByName(wname);
		if(widget == null) {
			widget = this.__create(wname, params);
		}

		return widget;
	},

	__create: function(wname, params) {
		var resizelock = 0;
		var draglock = 0;
		var activeBounds = false;
		var activeCursor = false;
		var zalloc = 1;
		var iframeInEvents = [ ];
		var loaderObj;

		var helper = {
			getParam: function(name) {
				return params[name];
			},

			sendMessage: function(msgName, msg) {
				HTAfl.__dispatchMsg(wname, msgName, msg);
			},

			disableRebound: function() {
				this.disableDrag();
				this.disableResize();
			},

			enableRebound: function() {
				this.enableDrag();
				this.enableResize();
			},

			disableResize: function() {
				resizelock ++;
			},

			enableResize: function() {
				if(resizelock == 0) {
					console.warn("enableResize: túl sok enable");
				}
				resizelock --;
			},

			disableDrag: function() {
				draglock ++;
			},

			enableDrag: function() {
				if(draglock == 0) {
					console.warn("enableDrag: túl sok enable");
				}
				draglock --;
			},

			/**
			 * DOM element regisztrálása, drag forrásként. Az itt megadott elementen lenyomva a bal egérgombot, a rendszer, követi az egérmozgást és
			 * a view objektum setBounds() funkcióhívásaival segíti az ablak felhasználó általi mozgatását.
			 *
			 * element -
			 * cursor - string ha szeretnénk ha a helper megjelenítene kurzort mikor az element, vagy null, ha nincs szükség kurzor kezelésre
			 * area - aktív terület [ <top>, <top_anchor>, <right>, <right_anchor>, <bottom>, <bottom_anchor>, <left>, <left_anchor> ]
			 *    top, right, bottom, left
			 *    top_anchor, bottom_anchor -
			 *
			 */
			addDragElement: function(element, cursor, area) {
				_rebound(element, 1, 1, 0, 0, cursor, area,
					function() { widgetView.setDragState(true) }, 
					function() { widgetView.setDragState(false); },
					function() { return draglock != 0; }
				);
			},

			/**
			 *
			 */
			addResizeElement: function(element, dir, cursor, area) {
				var dl = {
					//      x  y  w  h
					'n':  [ 0, 1, 0,-1 ], // N
					'ne': [ 0, 1, 1,-1 ], // NE
					'e':  [ 0, 0, 1, 0 ], // E
					'se': [ 0, 0, 1, 1 ], // SE
					's':  [ 0, 0, 0, 1 ], // S
					'sw': [ 1, 0,-1, 1 ], // SW
					'w':  [ 1, 0,-1, 0 ], // W
					'nw': [ 1, 1,-1,-1 ]  // NW
				};

				if(!dir in dl) {
					console.error("addResizeElement: invalid dir: %s", dir);
					return;
				}

				var d = dl[dir];
				_rebound(element, d[0], d[1], d[2], d[3], cursor, area,
					function() { widgetView.setResizeState(true) },
					function() { widgetView.setResizeState(false); },
					function() { return resizelock != 0; }
				);
			},

			addFocusElement: function(element, area) {
				if(area != null) {
					throw "Not supported (area).";
				}

				function _focus(e) {
					widget.htfFocus();
				}

				if(element.nodeName.toLowerCase() == 'iframe') {
					if(element !== widgetView.getContentElement()) {
						throw "Focus element nem lehet más IFRAME, csak a 'contentElement'.";
					}

					iframeInEvents.push([ 'mousedown', _focus, false ]);
				} else {
					jgtc.captureEvent(element, "mousedown", _focus, true);
				}
			},

			clientToView: function(returnOnly, bound) {
				if(bound == null) bound = widgetView.getBounds();
				var x = bound.x - document.documentElement.scrollLeft - document.body.scrollLeft;
				var y = bound.y - document.documentElement.scrollTop - document.body.scrollTop;
				var w = bound.w;
				var h = bound.h;

				if(!returnOnly) {
					widgetView.setBounds(x, y, w, h);
				}

				return { 'x': x, 'y': y, 'w': w, 'h': h };
			},

			viewToClient: function(returnOnly, bound) {
				if(bound == null) bound = widgetView.getBounds();
				var x = bound.x + document.documentElement.scrollLeft + document.body.scrollLeft;
				var y = bound.y + document.documentElement.scrollTop + document.body.scrollTop;
				var w = bound.w;
				var h = bound.h;

				if(!returnOnly) {
					widgetView.setBounds(x, y, w, h);
				}

				return { 'x': x, 'y': y, 'w': w, 'h': h };
			},

			allocateZDepth: function(z) {
				if(z < 0 || z > 10) {
					console.error("allocateZDepth: invalid z value: %d", z);
					return;
				}

				zalloc = z;
			}
		};

		function _rebound(element, rb_x, rb_y, rb_w, rb_h, cursor, area, fn_start, fn_end, fn_lock_chk) {
			var inIframe = false;
			var oldCursor = '';

			function _in_area(x, y) {
//				if(!widgetView.isFixed()) {
					with(document.documentElement) { // ff és ie
						x += scrollLeft;
						y += scrollTop;
					}
					with(document.body) { // webkit
						x += scrollLeft;
						y += scrollTop;
					}
//				}

				var b = jgtc.getBounds(element);
				if('width' in area) {
					if('left' in area) {
						b.x += area.left;
					} else if('right' in area) {
						b.x += b.w - area.width - area.right;
					}
					b.w = area.width;
				} else {
					if('left' in area) {
						b.x += area.left;
						b.w -= area.left;
					}
					if('right' in area) {
						b.w -= area.right;
					}
				}

				if('height' in area) {
					if('top' in area) {
						b.y += area.top;
					} else if('bottom' in area) {
						b.y += b.h - area.bottom - area.height;
					}
					b.h = area.height;
				} else {
					if('top' in area) {
						b.y += area.top;
						b.h -= area.top;
					}
					if('bottom' in area) {
						b.h -= area.bottom;
					}
				}

				return x >= b.x && x < b.x + b.w && y >= b.y && y < b.y + b.h;
			}

			function _mousedown(e) {
				if(!e) e = event;
				var x0 = e.clientX;
				var y0 = e.clientY;

				if(fn_lock_chk() || activeBounds !== false || area && !_in_area(x0, y0)) return;

				jgtc.discardEvent(e);

//				console.dir(e);

				activeBounds = widgetView.getBounds();
				if(inIframe) {
					var b = jgtc.getBounds(is.ie ? e.srcElement.ownerDocument.parentWindow.frameElement : e.view.frameElement);
					x0 += b.x;
					y0 += b.y;
					with(document.documentElement) { // ff és ie
						x0 -= scrollLeft;
						y0 -= scrollTop;
					}
					with(document.body) { // webkit
						x0 -= scrollLeft;
						y0 -= scrollTop;
					}
				}

				widget.htfFocus();
				fn_start();

				new _HTDrag(x0, y0, widgetView.isFixed()).drag(function(x, y) {
//					console.log('x: %d, y: %d', x, y);

					var x1 = activeBounds.x;
					var y1 = activeBounds.y;
					var w1 = activeBounds.w;
					var h1 = activeBounds.h;

					if(rb_x == 1) x1 += x;
					if(rb_y == 1) y1 += y;
					if(rb_w == 1) w1 += x;
					else if(rb_w == -1) w1 -= x;
					if(rb_h == 1) h1 += y;
					else if(rb_h == -1) h1 -= y;

					if(x1 < 0) {
						if(rb_w) {
							w1 += x1;
						}
						x1 = 0;
					}
					if(y1 < 0) {
						if(rb_h) {
							h1 += y1;
						}
						y1 = 0;
					}

					widgetView.setBounds(x1, y1, w1, h1);
				}, function() {
					activeBounds = false;
					fn_end();
				});
			}

			function _mousemove(e) {
				if(activeBounds !== false) return;

				if(!e) e = event;
				var x0 = e.clientX;
				var y0 = e.clientY;

				if(activeCursor === cursor) {
					if(!_in_area(x0, y0) || fn_lock_chk()) {
						element.style.cursor = oldCursor;
						activeCursor = false;
					}
				} else if(!fn_lock_chk() && _in_area(x0, y0)) {
					element.style.cursor = cursor;
					activeCursor = cursor;
				}
			}

			if(element.nodeName.toLowerCase() == 'iframe') {
				if(element !== widgetView.getContentElement()) {
					throw "Drag element nem lehet más IFRAME, csak a 'contentElement'.";
				}

				inIframe = true;
				iframeInEvents.push([ 'mousedown', _mousedown, true ]);
			} else {
				jgtc.captureEvent(element, "mousedown", _mousedown, true);
				if(cursor && area) {
					oldCursor = element.style.cursor;
					jgtc.captureEvent(element, "mousemove", _mousemove, true);
				}
			}

		}

		function _loaded(e) {
			if(!e) e = event;

			console.log("flpanel loaded (new)...");

			var contentElement = widgetView.getContentElement();

			var lparams = { 'staus': true };

			if(params.type == 'img') {
				lparams.width = loaderObj.width;
				lparams.height = loaderObj.height;

				if(contentElement.nodeName.toLowerCase() == 'img') {
					contentElement.src = loaderObj.src;
					if(is.ie) {
						contentElement.width = loaderObj.width;
						contentElement.height = loaderObj.height;
					}
				} else {
					contentElement.style.backgroundImage = 'url(' + loaderObj.src + ')';
					contentElement.htfImageWidth = loaderObj.width;
					contentElement.htfImageHeight = loaderObj.height;
				}
			} else if(params.type == 'url') {
				with(contentElement.contentWindow.document) {
					lparams.width = Math.max(documentElement.scrollWidth, body.scrollWidth);
					lparams.height = Math.max(documentElement.scrollHeight, body.scrollHeight);
				}

				jgtc.captureEvent(contentElement.contentWindow, "beforeunload", _unloaded, false);
			} else if(params.type == 'swf') {
				if(!params.width || !params.height) {
					throw "HTAfl: swf üzemmódban mindenképpen kell a paramétereknek tartalmaznia, a 'width' és 'height' paramétereket.";
				}
				lparams.width = params.width;
				lparams.height = params.height;

				if(!is.ie) {
					var jswf = [ "object", { 'type': "application/x-shockwave-flash", 'data': params.ref, 'width': lparams.width, 'height': lparams.height } ];
					for(var pname in params.swf) {
						jswf.push([ 'param', { 'name': pname, 'value': params.swf[pname] } ]);
					}

					contentElement.appendChild(jgtc.jsonML().build(jswf));
				} else {
					// IE hack
					var hswf = '<object type="application/x-shockwave-flash" width="' + lparams.width + '" height="' + lparams.height + '">';
					for(var pname in params.swf) {
						hswf += '<param name="' + pname + '" value="' + params.swf[pname] + '"/>';
					}
					hswf += '</object>';

					contentElement.innerHTML = hswf;
				}
			}

			// Iframe eventek feltrigelése az iframe body-jára.
			if(iframeInEvents.length > 0) {
				for(var i = 0; i < iframeInEvents.length; i++) {
					var ed = iframeInEvents[i];
					jgtc.captureEvent(contentElement.contentWindow.document, ed[0], ed[1], ed[2]);
				}
			}

			widgetView.setLoadState(true, lparams);
			HTAfl.__dispatchMsg(wname, 'load', null);
		}

		function _unloaded(e) {
			if(!e) e = event;

			console.log("unload: %o", e);

			// A HTAfl message listenerek közül töröljük azokat, amelyeknek a widget iframe-jében van a kódja.
			_msg_filter(function(l) { return l.origin == wname; });
			widgetView.setLoadState(false, null);
			HTAfl.__dispatchMsg(wname, 'unload', null);
		}

		function _msg_filter(f) {
			for(var i = 0; i < HTAfl.msgListeners.length;) {
				if(f(HTAfl.msgListeners[i])) {
					HTAfl.msgListeners.splice(i, 1);
				} else {
					i++;
				}
			}
		}

		function _top() {
			widgetView.setZIndex(HTAfl.ztop);
			HTAfl.ztop += zalloc;
		}

//		console.dir(params);

		// Widget setup és és megjelenítés
		var widgetView = new (params.view)(helper);
		var wValidator = widgetView.getParamsValidator();
		if(wValidator !== false) jgtc.validateObject(params, wValidator, false);
		var widget = widgetView.open();
		widgetView.setLoadState(false, null);

		if(!params.autoBounds) {
			params = jgtc.validateObject(params, [
				[ 'width', 'number', 500 ],
				[ 'height', 'number', 500 ],
				[ 'left', function(p) {
					if(typeof p.left != 'number') {
						var html = document.compatMode == "BackCompat" ? document.body : document.documentElement;
						return Math.round(html.scrollLeft + html.clientWidth * 2 / 3 - p.width / 2);
					} else {
						return p.left;
					}
				} ],
				[ 'top', function(p) {
					if(typeof p.top != 'number') {
						var html = document.compatMode == "BackCompat" ? document.body : document.documentElement;
						return Math.max(Math.round(html.scrollTop + html.clientHeight / 2 - p.height / 2), html.scrollTop);
					} else {
						return p.top;
					}
				} ]
			], false);
			widgetView.setBounds(params.left, params.top, params.width, params.height);
		}
		_top();

		// Widget dom element végső setupolása
		widget.id = this.idPrefix + "_" + wname;

		widget.htfGetName = function() {
			return wname;
		}

		widget.htfFocus = function() {
			if(widgetView.getZIndex() + zalloc < HTAfl.ztop) _top();
		}

		widget.htfClose = function() {
			HTAfl.__dispatchMsg(wname, 'close', null);

			_msg_filter(function(l) { return l.wname == wname; });

			if(is.ie && params.type == 'url') { // Különben elveszhet a fókusz bazze!
				var iframe = widgetView.getContentElement();
				iframe.contentWindow.location.href = 'about:blank';
				jgtc.releaseEvent(iframe, 'load', _loaded, true);
			}

			widgetView.close();
		}

		widget.htfReload = function(url) {
			if(params.type != 'url') {
				console.warn("Reload csak 'url' típusú widget esetén van.");
				return;
			}

			_unloaded(true);

			var iframe = widgetView.getContentElement();
			jgtc.releaseEvent(iframe.contentWindow, 'beforeunload', _unloaded, false);

			var ilocation = iframe.contentWindow.location;
			if(!url) {
				ilocation.reload();
			} else {
				ilocation.href = String(url);
			}
		}

		widget.htfSendMessage = function(msgName, msg) {
			HTAfl.sendMessage(wname, msgName, msg);
		}

		widget.htfAddMessageListener = function(msgName, fn) {
			HTAfl.addMessageListener(wname, msgName, fn);
		}

		widget.htfRemoveMessageListener = function(msgName, fn) {
			HTAfl.removeMessageListener(wname, msgName, fn);
		}

		widget.htfGetBounds = function() {
			with(widgetView.getBounds()) {
				return { 'x': x, 'y': y, 'w': w, 'h': h };
			}
		}

		// Ablak tartalmának betöltése és kezelése
		switch(params.type) {
			case 'url':
				var iframe = widgetView.getContentElement();
				iframe.contentWindow.location.href = params.ref;
				jgtc.captureEvent(iframe, "load", _loaded, false);
				break;
			case 'img':
				loaderObj = new Image();
				jgtc.captureEvent(loaderObj, "load", _loaded, false);
				loaderObj.src = params.ref;
				break;
			case 'swf':
				setTimeout(_loaded, 1);
				break;
			default:
				console.error("Unsupported type: '" + params.type + "'");
		}

		return widget;
	},

	getWidgetByName: function(wname) {
		return document.getElementById(this.idPrefix + '_' + wname);
	},

	closeWidget: function(wname) {
		if(typeof wname == 'string') {
			wname = HTAfl.getWidgetByName(wname);
		}

		if(wname) wname.htfClose();
	},

	closeAllWidget: function() {

	},

	/**
	 * Egy message listener beállítása.
	 *
	 * wname  string, az ablak neve. minden üzenet ami ennek az ablaknak megy, el lesz kapva. Ha ez az érték null, akkor minden ablak üzenetei el lesznek kapva.
	 * msgName  string, az üzenet neve, amit el szeretnénk kapni, vagy null, ha minden üzenetet szeretnénk elkapni.
	 * fn  function(wname, msgName, msg), a funkció, ami meghívódik, ha egy megfelelő üzenet érkezik.
	 */
	addMessageListener: function(wname, msgName, fn) {
		if(!wname) wname = null;
		if(!msgName) msgName = null;
		if(wname == '_self') {
			var w = this.getSelfWidget();
			if(!w) {
				console.error("HTAfl rootnak nincs message queue-ja.");
				return;
			} else {
				wname = w.htfGetName();
			}
		}

		var man = this.getManager();
		var origin = null;
		if(man != this) {
			origin = wname;
		}

		man.msgListeners.push({
			'wname': wname,
			'msgName': msgName,
			'fn': fn,
			'origin': origin
		});
	},

	/**
	 * Message listener eltávolítása.
	 */
	removeMessageListener: function(wname, msgName, fn) {
		if(!wname) wname = null;
		if(!msgName) msgName = null;
		if(wname == '_self') {
			var w = this.getSelfWidget();
			if(!w) {
				console.error("HTAfl rootnak nincs message queue-ja.");
				return;
			} else {
				wname = w.htfGetName();
			}
		}

		var man = this.getManager();

		for(var i = 0; i < man.msgListeners.length; i++) {
			var l = man.msgListeners[i];
			if(l.wname == wname && l.msgName == msgName && l.fn === fn) {
				man.msgListeners.splice(i, 1);
				break;
			}
		}
	},

	/**
	 * Üzenet küldése egy ablaknak.
	 *
	 * wname  string, az ablak neve, aminek az üzenetet küldeni szeretnénk
	 * msgName  string, az üzenet neve, amit el szeretnénk küldeni
	 * msg  object, az üzenet objektum (ez egy szabadon használható változó, a rendszert nem befolyásolja az értéke, de kötelező object típusúnak vagy null -nak lennie).
	 */
	sendMessage: function(wname, msgName, msg) {
		if(wname == '_parent' || wname == '_self') {
			if(wname == '_parent') {
				console.warn("deprecated use: HTAfl.sendMessage('_parent'), használd helyette: HTAfl.sendMessage('_self')");
			}

			var w = this.getSelfWidget();
			if(!w) {
				console.error("HTAfl rootnak nincs message queue-ja.");
				return false;
			} else {
				wname = w.htfGetName();
			}
		}

		this.getManager().__dispatchMsg(wname, msgName, msg);
		return true;
	},

	getSelfWidget: function() {
		if(typeof window.frameElement != 'undefined' && typeof window.parent.HTAfl != 'undefined') {
			for(var e = window.frameElement; e != null; e = e.parentNode) {
				if(e.htfGetName) return e;
			}
		}

		return null;
	},

	getManager: function() {
		if(window.parent && 'HTAfl' in window.parent) {
			return window.parent.HTAfl;
		}
		return HTAfl;
	},

	getManagerWindow: function() {
		if(window.parent && 'HTAfl' in window.parent) {
			return window.parent;
		}
		return window;
	},

	/**
	 * Belső funkció
	 */
	__dispatchMsg: function(wname, msgName, msg) {
//		console.log("__dispatchMsg('%s', '%s', %o)", wname, msgName, msg);

		for(var i = 0; i < this.msgListeners.length; i++) {
			var l = this.msgListeners[i];
			if((l.wname == null || l.wname == wname) && (l.msgName == null || l.msgName == msgName)) {
				try {
					l.fn(wname, msgName, msg);
				} catch(err) {
					console.error("Error in a message: %o", err);
				}
			}
		}
	}
}

/**
 * Abstract view osztály. Egy widget megjelenítését kell elvégeznie. A bejövő paraméter, a 'helper' változó, a manager osztály által biztosított segítő
 * funkcionalitásokat biztosító objektum. A helper objektum a view működése alatt, a this.helper propertyvel érhető el, ha lefut ez az alapértelmezett
 * konstruktor.
 *
 * Ez az osztály egy absztrakt osztály, ami azért van, hogy egységbe zárva láthatóak legyenek a viewben, a controller által használt metódusok. Tehát,
 * ez az osztály azért létezik, hogy ha valaki a nulláról szeretne felépíteni egy, az HTAfl által működtetett widgetet, akkor lássa mi az, amit ehhez 
 * implementálnia kell.
 *
 * @param helper {Object} a kontroller által biztosított segítő objektum.
 *
 */
function FlAbstractView(helper) {
	this.helper = helper;
}

/**
 *
 */
FlAbstractView.prototype.getBounds = function() {
}

/**
 * A controller a következő esetekben hívja meg ezt a függvényt: felhasználó által kezdeményezett drag vagy resize esetén illetve a helper objektum 
 * fixed / absolute pozícionálást segíő funkciói.
 */
FlAbstractView.prototype.setBounds = function(x, y, w, h) {
	this.helper.sendMessage('rebound', { 'x': x, 'y': y, 'w': w, 'h': h });
}

/**
 *
 */
FlAbstractView.prototype.getZIndex = function() {
}

/**
 *
 */
FlAbstractView.prototype.setZIndex = function(z) {
}

/**
 *
 */
FlAbstractView.prototype.isFixed = function() {
	return false;
}

/**
 * A controller a tartalom betöltődésekor 
 */
FlAbstractView.prototype.setLoadState = function(loaded, params) {
}

/**
 *
 */
FlAbstractView.prototype.setDragState = function(dragging) {
}

/**
 *
 */
FlAbstractView.prototype.setResizeState = function(resizing) {
}

/**
 * Ez a metódus adja vissza azt a DOM elementet, amibe a külső tartalmat be kell tölteni.
 * 
 */
FlAbstractView.prototype.getContentElement = function() {
	return null;
}

/**
 * A view speciális paramétereinek leíróját adja vissza 
 * Ez a metódus az egyetlen, ami még az FlAbstractView#open() metódus meghívása előtt meghívódik.
 *
 * @return a view által használt extra paraméterek validator leírója, vagy false, ha nincsenek a view által használt extra paraméterek.
 *
 * TODO Kell dokumentálni és esetleg a common.js -be áthelyezni a paraméter validatort.
 */
FlAbstractView.prototype.getParamsValidator = function() {
	return false;
}

/**
 * A view életciklusa alatt, a controller által egyszer hívódik meg, közvetlenül a konstruktor meghívása után. A konstruktor és az open() metódus meghívása között
 * más metódusai is meghívódhatnak a viewnek, de ez a metódus dokumentációjában fel van tüntetve.
 *
 * Jelenleg egy olyan metódus van, ami a konstuktor és az open metódus meghívása között meghívódhat, @see FlAbstractView#getParamsValidator().
 * 
 * @return az elkészítet ablak fő DOM elementje.
 */
FlAbstractView.prototype.open = function() {
	return null;
}

/**
 * A view életciklusa alatt, a controller által egyszer hívódik meg, az ablak lezárásakor. Ha ez a funkció meghívódott, akkor a kontroller több funkcióját
 * nem hívja meg a view objektumnak.
 * Ennek a funkciónak a meghívása jelzi, hogy az ablak a felhasználó kezdeményezésére, vagy valamilyen esemény hatására be kell, hogy záródjon, de ha a view
 * saját magát akarja lezárni, akkor azt a helper objektum megfelelő metódusának meghívásával teheti, a close közvetlen meghívása hibás működést eredményezhet,
 * mert így a controller nem szerez tudomást a widget bezáródásáról.
 */
FlAbstractView.prototype.close = function() {
}

/*
FlAbstractView.prototype.postInit = function() {
}
*/


/**
 * Alapértelmezett ablaknézet (view) osztály.
 * Ez a view osztály egy működő, és könnyen módosítható illetve kiterjeszthető megjelenítő osztály implementáció.
 */
function FlDefaultView(helper) {
	FlAbstractView.call(this, helper);

	this.fixed = false;
	this.minimized = false;
	this.loaded = true;
	this.bounds = { 'x': 0, 'y': 0, 'w': 100, 'h': 100 };
	this.elementBenders = [];
	this.welements = {};
	this.title = "";

	var _this = this;
	this.twWidget = new HTTween(function(x, mode) { _this.widgetFadeAnim(x, mode); }, 'sinoidal', 300).init(0);
	this.twContent = new HTTween(function(x, mode) { _this.contentFadeAnim(x, mode); }, 'sinoidal', 200).init(0);
}

jgtc.__extends(FlDefaultView, FlAbstractView);

/**
 *
 */
FlDefaultView.prototype.widgetFadeAnim = function(x, mode) {
	if(is.ie) this.widget.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + (x * 100) + ")";
	else this.widget.style.opacity = x;

	if(x == 0 && mode == HTTween.T_END) {
		this.destroy();
	} else if(x == 1 && mode == HTTween.T_END) {
		if(is.ie) this.widget.style.filter = '';
		else this.widget.style.opacity = '';
	}
}

/**
 *
 */
FlDefaultView.prototype.contentFadeAnim = function(x, mode) {
/*	if(is.ie) this.welements.content.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + (x * 100) + ")";
	else this.welements.content.style.opacity = x; */

	if(mode == -1 || x) {
		if(is.ie) this.welements.content.style.filter = '';
		else this.welements.content.style.visibility = '';
	}
}

/**
 * @see FlAbstractView#getBounds()
 */
FlDefaultView.prototype.getBounds = function() {
	console.log("FlDefaultView.prototype.getBounds()");

	return this.bounds;
}

/**
 * @see FlAbstractView#setBounds(x, y, w, h)
 */
FlDefaultView.prototype.setBounds = function(x, y, w, h) {
//	console.log("FlDefaultView.prototype.setBounds(%d, %d, %d, %d)", x, y, w, h);

	this.bounds = { 'x': x, 'y': y, 'w': w, 'h': h };

	this.runElementBenders('bounds');

	FlAbstractView.prototype.setBounds.call(this, x, y, w, h);
}

/**
 * @see FlAbstractView#getZIndex()
 */
FlDefaultView.prototype.getZIndex = function() {
	console.log("FlDefaultView.prototype.getZIndex()");

	return Number(this.widget.style.zIndex);
}

/**
 * @see FlAbstractView#setZIndex()
 */
FlDefaultView.prototype.setZIndex = function(z) {
	console.log("FlDefaultView.prototype.setZIndex(%d)", z);

	this.widget.style.zIndex = z;
}

/**
 * @see FlAbstractView#isFixed()
 */
FlDefaultView.prototype.isFixed = function() {
//	console.log("FlDefaultView.prototype.isFixed()");

	return this.fixed;
}

/**
 * @see FlAbstractView#setLoadState()
 */
FlDefaultView.prototype.setLoadState = function(loaded, params) {
	console.log("FlDefaultView.prototype.setLoadState(%s, %o)", loaded, params);

	// Alap státuszállítás
	if(this.loaded == loaded) return;
	this.loaded = loaded;

	// Title kezelése
	if(loaded) {
		var title = "";

		if(this.helper.getParam('type') == 'url') {
			var titles = this.welements.content.contentWindow.document.getElementsByTagName("title");
			for(var i = 0; i < titles.length; i++) {
				if(titles[i].parentNode.nodeName.toLowerCase() == 'head') {
					title = titles[i].innerHTML;
					break;
				}
			}
		}

		this.title = title || this.helper.getParam('title');
	} else {
		this.title = this.helper.getParam("titleLoading");
	}

	this.runElementBenders(loaded ? 'loaded' : 'unloaded');
}

FlDefaultView.prototype.setDragState = function(dragging) {
	console.log("FlDefaultView.prototype.setDragState(%s)", dragging);

	if(this.helper.getParam('fancy')) {
		this.twWidget.cont(dragging ? this.helper.getParam('resizeOpacity') : 1);
	}
}

FlDefaultView.prototype.setResizeState = function(resizing) {
	console.log("FlDefaultView.prototype.setResizeState(%s)", resizing);

	if(this.helper.getParam('fancy')) {
		this.twWidget.cont(resizing ? this.helper.getParam('resizeOpacity') : 1);
	}
}

FlDefaultView.prototype.getContentElement = function() {
	console.log("FlDefaultView.prototype.getContentElement()");

	return this.welements.content;
}

/**
 * Ez egy FlDefaultView objektumban definiált funkció. 
 *
 *
 *
 */
FlDefaultView.prototype.elementBender = function(evName, bName, value, element) {
//	console.log("-FlDefaultView.prototype.elementBender('%s', '%s', '%s', %o)", evName, bName, value, element);

	function _type_style(styleAttr) {
		for(var i = 1; i < arguments.length; i+= 2) {
			if(evName == arguments[i]) {
				element.style[styleAttr] = arguments[i + 1];
				break;
			}
		}
	}

	function _view() {
		return document.compatMode == "BackCompat" ? document.body : document.documentElement;
	}

	switch(bName) {
		case "width":
			element.style.width = (this.bounds.w + (value == 'client' ? _view().clientWidth : Number(value))) + "px";
			break;
		case "height":
			element.style.height = (this.bounds.h + (value == 'client' ? _view().clientHeight : Number(value))) + "px";
			break;
		case "left":
			element.style.left = (this.bounds.x + Number(value)) + "px";
			break;
		case "top":
			element.style.top = (this.bounds.y + Number(value)) + "px";
			break;
		case "element":
			if(value == "title") {
				element.innerHTML = this.title;
			}
			break;
		case "display":
			switch(value) {
				case "minimized":
					_type_style("display", "minimize", "", "maximize", "none");
					break;
				case "maximized":
					_type_style("display", "minimize", "none", "maximize", "");
					break;
				case "fix":
					_type_style("display", "fix", "", "absolute", "none");
					break;
				case "absolute":
					_type_style("display", "fix", "none", "absolute", "");
					break;
				case "loaded":
					_type_style("display", "loaded", "", "unloaded", "none");
					break;
				case "unloaded":
					_type_style("display", "loaded", "none", "unloaded", "");
					break;
			}
			break;
		case "visibility":
			switch(value) {
				case "minimized":
					_type_style("visibility", "minimize", "visible", "maximize", "hidden");
					break;
				case "maximized":
					_type_style("visibility", "minimize", "hidden", "maximize", "visible");
					break;
				case "fix":
					_type_style("visibility", "fix", "visible", "absolute", "hidden");
					break;
				case "absolute":
					_type_style("visibility", "fix", "hidden", "absolute", "visible");
					break;
				case "loaded":
					_type_style("visibility", "loaded", "visible", "unloaded", "hidden");
					break;
				case "unloaded":
					_type_style("visibility", "loaded", "hidden", "unloaded", "visible");
					break;
			}
			break;
		default:
			console.warn("Unhandled elementBender type: evName = '%s', bName = '%s', element = %o, value: %o", evName, bName, element, value);
	}

	return true;
}

FlDefaultView.prototype.initElementBender = function(bName, value, element) {
//	console.log("-FlDefaultView.prototype.initElementBender('%s', '%s', %o)", bName, value, element);

	var _this = this;

	function btn(f) {
		jgtc.captureEvent(element, "mouseover", function() { _this.helper.disableRebound(); }, false);
		jgtc.captureEvent(element, "mouseout", function() { _this.helper.enableRebound(); }, false);
		jgtc.captureEvent(element, "click", f, false);
	}

	function resize() {
		for(var i = 0; i < value.length; i++) {
			var v = value[i];
			_this.helper.addResizeElement(element, v.dir, v.cursor, v.area);
		}
	}

	switch(bName) {
		case "width":
		case "height":
		case "left":
		case "top":
			return [ "bounds" ];
		case "display":
		case "visibility":
			return [ "minimize", "maximize", "fix", "absolute", "loaded", "unloaded" ];
		case "element":
			switch(value) {
				case "title":
					return [ "loaded", "unloaded", "init" ];
				case "content":
					if('content' in this.welements) {
						console.error("HTAfl: többszörös content: %o", element);
					} else {
						this.welements.content = element;
					}
					break;
				case "loader":
					if('loader' in this.welements) {
						console.error("HTAfl: többszörös loader: %o", element);
					} else {
						this.welements.loader = element;
					}
					break;
				default:
					console.warn("HTAfl: ismeretlen element: '%s'", value);
			}
			return false;
		case "drag":
			this.helper.addDragElement(element, null, null);
			return false;
		case "resize":
			resize();
			return false;
		case "focus":
			this.helper.addFocusElement(element, null);
			return false;
		case "button":
			switch(value) {
				case "close":
					if(!this.helper.getParam('close')) {
						element.style.display = 'none';
					} else {
						btn(function() { _this.widget.htfClose(); });
					}
					break;
				case "reload":
					if(!this.helper.getParam('reload')) {
						element.style.display = 'none';
					} else {
						btn(function() { _this.widget.htfReload(); });
					}
					break;
				case "minimize":
				case "maximize":
					if(!this.helper.getParam('minmax')) {
						element.style.display = 'none';
					} else {
						btn(value == "minimize" ? function() { _this.minimize(); } : function() { _this.maximize(); });
					}
					break;
				case "fix":
				case "absolute":
					if(!this.helper.getParam('fixabs') || is.ie6) {
						element.style.display = 'none';
					} else {
						btn(value == "fix" ? function() { _this.fix(); } : function() { _this.absolute(); });
					}
					break;
				default:
					console.warn("HTAfl: ismeretlen button: '%s'", value);
			}
			return false;
		default:
			console.warn("HTAfl: unhandled elementBender bName = '%s', element = %o, value: '%s'", bName, element, value);
	}

	return false;
}

FlDefaultView.prototype.runElementBenders = function(evName) {
//	console.log("-FlDefaultView.prototype.runElementBenders('%s')", evName);

	for(var i = 0; i < this.elementBenders.length; i++) {
		var e = this.elementBenders[i];
		if(e[0] === true || e[0].indexOf(evName) != -1) {
			this.elementBender(evName, e[1], e[2], e[3]);
		}
	}
}

FlDefaultView.prototype.addElementBender = function(evNames, bName, value, element) {
//	console.log("-FlDefaultView.prototype.addElementBender('%s', '%s', '%s', %o)", evNames, bName, value, element);

	this.elementBenders.push([ evNames, bName, value, element ]);
}

FlDefaultView.prototype.createJsonMLUtil = function() {
	var _this = this;

	return jgtc.jsonML().addUserAttribute('fl', function(element, prefix, attrName, value, jsonml) {
		var eb = _this.initElementBender(attrName, value, element);
		if(eb !== false) {
			_this.addElementBender(eb, attrName, value, element);
		}
	});
}

FlDefaultView.prototype.createJsonML = function() {
	var jsonml = this.helper.getParam('skin');
	if(!jsonml) {
		var resize = [
			{ 'dir': "ne", 'cursor': "ne-resize", 'area': { 'width': 15, 'height': 15, 'right': 0, 'top': 0 } },
			{ 'dir': "e",  'cursor': "e-resize",  'area': { 'width': 10, 'right': 0, 'top': 15, 'bottom': 15 } },
			{ 'dir': "se", 'cursor': "se-resize", 'area': { 'width': 15, 'height': 15, 'right': 0, 'bottom': 0 } },
			{ 'dir': "s",  'cursor': "s-resize",  'area': { 'height': 10, 'left': 15, 'right': 15, 'bottom': 0 } },
			{ 'dir': "sw", 'cursor': "sw-resize", 'area': { 'width': 15, 'height': 15, 'bottom': 0 } },
			{ 'dir': "w",  'cursor': "w-resize",  'area': { 'width': 10, 'top': 15, 'bottom': 15 } },
			{ 'dir': "nw", 'cursor': "nw-resize", 'area': { 'width': 15, 'height': 15 } }
		];

		jsonml = [ "div", { 'class': "adm_flpanel", 'style': "position: absolute;", 'fl:left': 0, 'fl:top': 0, 'fl:width': -22, 'fl:focus': true, 'fl:resize': resize },
			[ "div", { 'class': "head", 'fl:drag': true },
				[ "img", { 'src': jgtc.rootImgURL + "adm3fl-close.png", 'alt': "Bezárás", 'title': "Bezárás", 'fl:button': "close" } ],
				[ "img", { 'src': jgtc.rootImgURL + "adm3fl-reload.png", 'alt': "Újratöltés", 'title': "Újratöltés", 'fl:button': "reload" } ],
				[ "img", { 'src': jgtc.rootImgURL + "adm3fl-min.png", 'alt': "Kicsinyítés", 'title': "Kicsinyítés", 'fl:button': "minimize", 'fl:display': "maximized" } ],
				[ "img", { 'src': jgtc.rootImgURL + "adm3fl-max.png", 'alt': "Nagyítás", 'title': "Nagyítás", 'fl:button': "maximize", 'fl:display': "minimized" } ],
				[ "img", { 'src': jgtc.rootImgURL + "adm3fl-fix.png", 'alt': "Ablakhoz rögzít", 'title': "Ablakhoz rögzít", 'fl:button': "fix", 'fl:display': "absolute" } ],
				[ "img", { 'src': jgtc.rootImgURL + "adm3fl-abs.png", 'alt': "Oldalhoz rögzít", 'title': "Oldalhoz rögzít", 'fl:button': "absolute", 'fl:display': "fix" } ],
				[ "span", { 'element': this.welements, 'fl:element': "title" } ]
			],
			[ "ins",
				[ "div", { 'fl:display': "maximized" },
					[ "div", { 'class': "loader", 'fl:element': 'loader', 'fl:display': "unloaded", 'fl:width': -24, 'fl:height': -43 } ],
					[ "iframe", { 'src' : "about:blank", 'frameBorder': 0, 'style': "visibility: hidden;", 'fl:element': "content", 'fl:visibility': "loaded", 'fl:width': -24, 'fl:height': -43, 'fl:focus': true } ]
				]
			]
		];
	}
	return jsonml;
}

FlDefaultView.prototype.create = function() {
	if([ "url", "img" ].indexOf(this.helper.getParam('type')) == -1) {
		throw "FlDefaultView. Unsupported type: " + helper.getParam('type');
	}

	var jsonml = this.createJsonML();
	this.widget = this.createJsonMLUtil().build(jsonml);
}

FlDefaultView.prototype.minimize = function() {
	if(this.minimized === false) {
		this.minimized = true;
		this.helper.disableResize();
	}
	this.runElementBenders('minimize');
}

FlDefaultView.prototype.maximize = function() {
	if(this.minimized === true) {
		this.minimized = false;
		this.helper.enableResize();
	}
	this.runElementBenders('maximize');
}

FlDefaultView.prototype.fix = function() {
	this.helper.clientToView(false, null);
	this.widget.style.position = "fixed";

	this.fixed = true;
	this.runElementBenders('fix');
}

FlDefaultView.prototype.absolute = function() {
	this.helper.viewToClient(false, null);
	this.widget.style.position = "absolute";

	this.fixed = false;
	this.runElementBenders('absolute');
}

FlDefaultView.prototype.initWidget = function() {
	this.title = this.helper.getParam("titleLoading");
	this.runElementBenders('init');

	if(this.helper.getParam('minmax')) {
		if(this.minimized) this.minimize();
		else this.maximize();
	}

	if(this.helper.getParam('fixabs') && !is.ie6) {
		if(this.fixed) this.fix();
		else this.absolute();
	}
}

/**
 * A FlDefaultView támogat néhány extap paramétert, amivel az ablakkezelést segítjük elő.
 *
 * @see FlAbstractView#getParamsValidator()
 */
FlDefaultView.prototype.getParamsValidator = function() {
	console.log("FlDefaultView.prototype.getParamsValidator()");

	return [
		[ 'close', 'boolean', true ],
		[ 'reload', 'boolean', true ],
		[ 'minmax', 'boolean', true ],
		[ 'fixabs', 'boolean', true ],
		[ 'fancy', 'boolean', true ],
		[ 'resizeOpacity', 'number', 0.75 ],
		[ 'skin', 'object', null ],
		[ 'title', 'string', "" ],
		[ 'titleLoading', 'string', "loading..." ]
	];
}

/**
 * @see FlAbstractView#open()
 */
FlDefaultView.prototype.open = function() {
	console.log("FlDefaultView.prototype.open()");

	this.create();
	document.body.appendChild(this.widget);
	if(this.helper.getParam('fancy')) {
		this.twWidget.cont(1);

		if('loader' in this.welements) {
			this.contentFadeAnim(0, -1);
		}
	}
	this.initWidget();

	return this.widget;
}
/**
 * Segédfunkció
 */
FlDefaultView.prototype.destroy = function() {
	document.body.removeChild(this.widget);
}

/**
 * @see FlAbstractView#close()
 */
FlDefaultView.prototype.close = function() {
	console.log("FlDefaultView.prototype.close()");

	if(this.helper.getParam('fancy')) {
		this.twWidget.cont(0);
	} else {
		this.destroy();
	}
}


