
	/* loadingIndicator */
	
	function loadingIndicator() {}

	loadingIndicator.prototype.show = function(parentElement)
	{
		this.element = parentElement.ownerDocument.createElement('div');
		this.element.className = 'loadingIndicator';
		this.element.style.left = parentElement.offsetLeft + parentElement.offsetWidth/2 + 'px';
		this.element.style.top = parentElement.offsetTop + parentElement.offsetHeight/2 + 'px';
		parentElement.parentNode.insertBefore(this.element, parentElement.nextSibling);
	}

	loadingIndicator.prototype.hide = function()
	{
		this.element.parentNode.removeChild(this.element);
	}

	function ajaxLoadDocument(link, callback)
	{
		link.setParameter('outputMimeType', 'application/xml', true);

		var xsltUrl = styleUrl +  'main.xsl';
		
		if(callback == undefined)
		{
			var xmlDocument = link.loadXml();
			return xmlDocument ? transform(xmlDocument, xsltUrl) : null;
		}
		else link.loadXml(function(xmlDocument) {callback(xmlDocument ? transform(xmlDocument, xsltUrl) : null)});
	}
	
	function ajaxLoadComponent(componentId, link, callback)
	{
		link.setParameter('componentFilter', /* 'page' */ componentId, false);
		if(callback == undefined)
		{
			var htmlDocument = ajaxLoadDocument(link);
			return htmlDocument ? selectNode("//*[@component='"+ componentId +"']", htmlDocument.documentElement) : null;
		}
		else ajaxLoadDocument(link, function(htmlDocument){callback(htmlDocument ? selectNode("//*[@component='"+ componentId +"']", htmlDocument.documentElement) : null)});
	}
	
	function importComponent(component, document)
	{
		var head = document.getElementsByTagName('head')[0];
		
		var scripts = component.ownerDocument.getElementsByTagName('script');
		var existedScripts = head.getElementsByTagName('script');
		for(var i = 0; i < scripts.length; i++)
		{
			var script = scripts[i];			
			if(script.src)
			{
				var exist = false;
				for(var j = 0; j < existedScripts.length; j++)
					if(existedScripts[j].src == script.src) { exist = true; break; }
				if(!exist) head.appendChild(importElement(script, document, false));
			}
		}
		
		var links = component.ownerDocument.getElementsByTagName('link');
		var existedLinks = head.getElementsByTagName('link');
		for(var i = 0; i < links.length; i++)
		{
			var link = links[i];
			if(link.href)
			{
				var exist = false;			
				for(var j = 0; j < existedLinks.length; j++)
					if(existedLinks[j].href == link.href) { exist = true; break; }
				if(!exist) head.appendChild(importElement(link, document, false));
			}
		}

		return importElement(component, document, false);
	}

	function ajaxReplaceComponent(sourceComponent, targetComponent)
	{		
		// TODO: IE XML and HTML misunderstanding
		if(sourceComponent.ownerDocument != targetComponent.ownerDocument)
			targetComponent = importComponent(targetComponent, sourceComponent.ownerDocument);

		var container = sourceComponent.parentNode;
		
		if(targetComponent.outerHTML) sourceComponent.outerHTML = targetComponent.outerHTML;
		else container.replaceChild(targetComponent, sourceComponent);
		
		ajaxSetLinkHandlers(container);
		ajaxSetFormSubmitHandlers(container);
		ajaxSetUpdaters(container);		

		return targetComponent;
	}

	function ajaxRemoveComponent(component)
	{
		component.parentNode.removeChild(component);
	}
	

	function ajaxUpdateComponent(component, link)
	{
		if(link == undefined) link = new ajaxLink(window.location.href);
				
		if(typeof component == 'string')
		{
			var componentId = component;
			var element = link.a;
			if(element == undefined) element = link.form;
			var rootNode = document.documentElement;
			if(element != undefined)
			{
				rootNode = selectNode("ancestor::*[contains(@class, 'dialog')]", element);
				if(!rootNode) rootNode = element.ownerDocument.documentElement;
			}
			var sourceComponent =  selectNode("descendant-or-self::*[@component='"+ componentId +"']", rootNode);
		}
		else
		{
			var sourceComponent = component;
			var componentId = sourceComponent.getAttribute('component');
		}
		
		if(!sourceComponent.fading) sourceComponent.fading = new fading(sourceComponent);
		sourceComponent.fading.fade(0.03);

		var indicator = new loadingIndicator();
		indicator.show(sourceComponent);

		var callback = function(targetComponent)
		{
			if(targetComponent != null)
			{
				targetComponent = ajaxReplaceComponent(sourceComponent, targetComponent);
				if(!targetComponent.fading) targetComponent.fading = new fading(targetComponent);
				targetComponent.fading.value = sourceComponent.fading.value;
				targetComponent.fading.unfade(0.06);
			}
			else
			{
				var win = selectNode("ancestor::*[contains(@class, 'dialog')]", sourceComponent);
				ajaxRemoveComponent(sourceComponent);
				if(win && win.dialog) win.dialog.close();
			}
			indicator.hide();
		}
		
		ajaxLoadComponent(componentId, link, callback);
		
		return true;
	}

	function ajaxUpdate(link)
	{
		var targetComponentId = link.getParameter('targetComponent');
		if(targetComponentId != undefined)
			return ajaxUpdateComponent(targetComponentId, link);
			
		return false;
	}

	function ajaxOpen(link)
	{
		var targetComponentId = link.getParameter('targetComponent');
		if(targetComponentId != undefined && targetComponentId.length)
		{
			var win = new dialog(undefined, link.getParameter('window.mode'));
			win.autoDestroy = true;
			win.href = link.assembleRef();
			win.open();
			
			var indicator = new loadingIndicator();
			indicator.show(win.contentContainer);

			var component = ajaxLoadComponent(targetComponentId, link);

			indicator.hide();
			
			win.setContent(importComponent(component, win.contentContainer.ownerDocument, false));
			ajaxSetLinkHandlers(win.contentContainer);
			ajaxSetFormSubmitHandlers(win.contentContainer);

			return true;
		}
		return false;
	}	

	function ajaxLoad(link, currentRef)
	{
		if(currentRef == undefined) currentRef = window.location.href;

		var oldLink = new ajaxLink(currentRef);
		try
		{
			if(link.protocol == 'http:' && oldLink.host == link.host)
			{
				var target = link.getParameter('target');
				if(target == undefined) target = oldLink.path != link.path ? 'new' : 'this';
				return !(target == 'new' ? ajaxOpen(link) : ajaxUpdate(link));
			}

		}
		catch(e) {
			//alert('AJAX handler: ' + e.message)
		}

		return true;
	}

	function ajaxHandleLink(a)
	{
		var win = selectNode("ancestor::*[contains(@class, 'dialog')]", a);
		var currentRef = win && win.dialog ? win.dialog.href : window.location.href;
		
		var link = new ajaxLink();
		link.assignA(a);
		return ajaxLoad(link, currentRef);
	}

	function ajaxHandleFormSubmit(submit)
	{
		var form = selectNode("ancestor::form|ancestor::FORM", submit);

		var win = selectNode("ancestor::*[contains(@class, 'dialog')]", form);
		var currentRef = win && win.dialog ? win.dialog.href : window.location.href;

		var link = new ajaxLink();
		link.assignForm(form, submit);	
		
		var oldLink = new ajaxLink(currentRef);
		if(link.protocol == 'http:' && oldLink.host == link.host && oldLink.path == link.path)
			link.setParameter('targetComponent', selectNode('(ancestor-or-self::*[@component])[last()]', form).getAttribute('component'), false);

		return ajaxLoad(link, currentRef);
	}

	function ajaxLinkHandler()
	{
		var returnValue = ajaxHandleLink(this);
		if(window.event) window.event.returnValue = returnValue;
		return returnValue;
	}
	
	function ajaxSetLinkHandlers(node)
	{
		var nodes = selectNodes("descendant-or-self::a", node);
		for(var i = 0; i < nodes.length; i++)
			if(nodes[i].onclick == undefined)
				nodes[i].onclick = ajaxLinkHandler;
	}

	function ajaxFormSubmitHandler()
	{
		var returnValue = ajaxHandleFormSubmit(this);
		if(window.event) window.event.returnValue = returnValue;
		return returnValue;	
	}
	
	function ajaxSetFormSubmitHandlers(node)
	{
		var nodes = selectNodes("descendant-or-self::form//input[@type='submit' or @type='image']", node);
		for(var i = 0; i < nodes.length; i++)
			if(nodes[i].onclick == undefined)
				nodes[i].onclick = ajaxFormSubmitHandler;
	}
	
	function ajaxSetUpdaters(node)
	{
	/*
		var nodes = selectNodes("descendant-or-self::*[@component]", node);
		for(var i = 0; i < nodes.length; i++)
		{
			if(nodes[i].onmouseover == undefined)
			{
				nodes[i].onmouseover = function()
				{ 
					this.style.border='1px red dashed';
					var div = (this.ownerDocument ? this.ownerDocument : node).createElement('div');
					div.style.width = '16px';
					div.style.height = '16px'; 
					div.style.border = '1px green solid';					
					div.style.position = 'absolute';
					div.style.background = 'red';
					div.style.top = 0;
					div.style.left = (this.offsetWidth - 16) + 'px';
					div.style.zIndex = '99';
					this.appendChild(div);
					this.div = div;
					div.onclick = function(event){ ajaxUpdateComponent(this.parentNode.getAttribute('component')); event.stopPropagation();};
				};
				nodes[i].onmouseout = function()
				{ 
					this.style.border='none';
					if(this.div) this.removeChild(this.div);
				};
			}
		}
		*/
	}
	
	function ajaxInit()
	{
		ajaxSetLinkHandlers(document.documentElement);
		ajaxSetFormSubmitHandlers(document.documentElement);
		ajaxSetUpdaters(document.documentElement);		
	}

	if(!window.ActiveXObject) // for IE (TEMPORARRY SWITCHED OFF)
		document.eventer.addListener('onLoad', ajaxInit);

	// TOFIX:
	// 1. concurent requests