

var QvAjax = {};
QvAjax.Effect = {};

/**************************************************************************************
 *
 * Creates an AJAX container where all elements that contain a .ajax classname
 * are selected and patched in a way that all navigation is done with AJAX calls.
 *
 *************************************************************************************/
QvAjax.Container = Class.create(
{
	element: null,
	options: {},
	
	initialize: function(element, options)
	{	
		this.element = $(element);		
		Object.extend(this.options, options);
		
		if (options.wait && options.wait == true)
		{
			return;
		}
		
		this.open();
	},
	
	open: function()
	{
		// Get URL from options. If available, fill the element through AJAX
		// first and then patch that data.
		var self = this;
		if (this.options.url && this.options.url != null)
		{
			new Ajax.Request(this.options.url,
			{
				onComplete: function(transport)
				{
					self.__ajax_complete(transport);
				}
			});
		}
		else
		{
			this.patch();
		}	
	},
	
	close: function()
	{
		if (this.options.effect && this.options.effect.remove)
		{
			this.options.effect.remove();
		}
		else
		{
			this.element.update("");
		}
	},

	/** Alias for effects **/
	repatch: function() { this.patch(); },

	/**
	 * Patch the whole DOM, searching for ajax classnames
	 */
	patch: function()
	{		
		var self = this;
		
		// Select all .ajax classed elements
		this.element.select(".ajax").each(function(e) 
		{		

			// this is an anchor with ajax class
			if (e.tagName.toLowerCase() == "a")
			{
				// skip javascript links
				if (e.href.indexOf("javascript:") >= 0)
				{
					return;
				}
			
				e.observe("click", function(event) 
				{
					self.__ajax_link(event);
				});				
			}

			// this is a form with ajax class
			if (e.tagName.toLowerCase() == "form")
			{
				// skip javascript FORM actions
				if (e.action.indexOf("javascript:") >= 0)
				{
					return;
				}

				e.observe("submit", function(event) 
				{
					self.__ajax_form(event);
				});				
			}
		});
	},
	
	/**
	 * Callback for click event of AJAX links
	 */
	__ajax_link: function(event)
	{
		Event.stop(event);	
	
		var anchor = Event.element(event);
		var self = this;
		
		while(anchor != null && anchor.tagName.toLowerCase() != "a")
		{
			anchor = anchor.parentNode;
		}
		
		new Ajax.Request(anchor.href,
		{
			onComplete: function(transport) 
			{ 
				self.__ajax_complete(transport);
			}
		});
	},

	/**
	 * Callback for ajax FORM elements
	 */	
	__ajax_form: function(event)
	{
		Event.stop(event);
		
		var form = Event.element(event);
		var self = this;
			
		new Ajax.Request(form.action,
		{
			parameters: form.serialize(),
			method: form.method ? "POST" : form.method,
			onComplete: function(transport) 
			{
				self.__ajax_complete(transport);
			}
		});
	},
	
	__ajax_complete: function(transport)
	{
		var html = transport.responseText;
	
		if (this.options.onLoad)
		{
			this.element.update(html);
			this.patch();
			
			this.options.onLoad(html);
		}
		else if (this.options.effect && this.options.effect.animate)
		{
			this.options.effect.animate(this.element, html, this);
		}
		else
		{
			this.element.update(html);
			this.patch();
		}
	}
});

/**************************************************************************************
 *
 * Creates a master / slave like AJAX
 *
 *************************************************************************************/
QvAjax.Pair = Class.create(
{
	master: null,
	slave: null,
	options: {},
	
	initialize: function(options)
	{
		Object.extend(this.options, options);		
		if(options.master && options.slave)
		{
			this.patch(options.master, options.slave);		
		}
	},
	
	patch: function(master, slave)
	{
		this.master = $(master);
		this.slave = $(slave);

		if (this.master.id && this.slave.id)
		{
			this.patchElement(this.master);
		}
	},	
	
	patchElement: function(elm)
	{
		var self = this;
		var selector = ".ajax";
		selector = this.options.selector ? this.options.selector : selector;
		
		elm.select(selector).each(function(ajaxElement) 
		{			
			if (ajaxElement.tagName.toLowerCase() == "a")
			{	
				Event.stopObserving(ajaxElement, "click");
				Event.observe(ajaxElement, "click", function(event) 
				{
					self.__ajax_link(event);						
				});
			}
		});	
	},
	
	repatch: function()
	{
		this.patch(this.master.id, this.slave.id);
	},
	
	/**
	 * Callback for click event of AJAX links
	 */
	__ajax_link: function(event)
	{		
		Event.stop(event);
			
		var anchor = Event.element(event);		
		while(anchor != null && anchor.tagName.toLowerCase() != "a")
		{
			anchor = anchor.parentNode;
		}
			
		// No anchor found in event
		if (anchor == null)
		{
			return;
		}
		
		var self = this;		
		var url = anchor.href;
				
		new Ajax.Request(url,
		{
			method: 'get',
			onComplete: function(transport)
			{
				self.__ajax_link_complete(transport, anchor);
			}
		});		
	},
	
	__ajax_link_complete: function(transport, anchor)
	{
		// Ok now patch the whole thing
		var html = transport.responseText;
		var rnd = "T" + (Math.random()+"").substring(2);
		
		// Filter body				
		html = html.replace(/[\S\s]*<body[^>]*/i, "");
		html = html.replace(/<\/body>/i, "");
		html = html.replace(/<\/html>/i, "");
		html = html.stripScripts();
		html = html.replace(/ id=\"/gi, ' id="' + rnd);
		html = html.replace(/ id='/gi, " id='" + rnd);
		
		// Find with temporary div
		var div = new Element("div", { "style": "display:none"});
		div.update(html);		
		Element.insert(document.body, {"bottom": div});
		
		// Select the HTML parts needed				
		var masterHTML = $(rnd + this.master.id).innerHTML;
		var slaveHTML = $(rnd + this.slave.id).innerHTML;
				
		// replace tmp id
		masterHTML = masterHTML.replace(rnd, "");
		slaveHTML = slaveHTML.replace(rnd, "");
			
		// Remove temporary div
		div.remove();
		
		// Update & patch master first
		this.master.update(masterHTML);
		this.patchElement(this.master);
						
		// We could now animate
		// The master link dictates the slave's animation
		var effect = this.findEffect(this.slave, null);
		effect = this.findEffect(anchor, effect);
		
		if (effect != null)
		{
			effect.animate(this.slave, slaveHTML, this);
		}
		else
		{
			this.slave.update(slaveHTML);					
		}							
	},

	
	findEffect: function(element, defaultValue)
	{
		for(var effect in QvAjax.Effect.effects)
		{
			if (element.hasClassName)
			{
				if (element.hasClassName("effect-" + effect))
				{
					return QvAjax.Effect.effects[effect];
				}
			}
		}
		
		return defaultValue;
	}
});

/**
 * A-Symmetric effect
 */
QvAjax.Effect.LeftToRight = Class.create(
{
	initialize: function(reverse) { this.reverse = reverse; },
	reverse: false,
	animate: function(container, newHTML, animator)
	{
		try
		{
			container.setStyle({
				"overflow": "hidden",
				"position": "relative"			
			});
	
			var width = container.getWidth();
			var height = container.getHeight();
			
			var innerOld = new Element("div").update(container.innerHTML);		
			var innerNew = new Element("div").update(newHTML);

			innerNew.setStyle({
				"position": "absolute",
				"z-index": "4",
				"left": (this.reverse ? width: -width) + "px",
				"width": width + "px"
			});
			
			container.update(innerOld);
			container.insert({"top": innerNew});
			
			new Effect.Morph(container, { style: 'height:'+innerNew.getHeight()+'px;', duration: 1 });
			new Effect.Move(innerOld, { x: (this.reverse ? -width:width), y: 0, mode: 'absolute', duration: 1 });
			new Effect.Move(innerNew, 
			{ 
				x: 0, 
				y: 0, 
				mode: 'absolute',
				duration: 1,
				afterFinish: function()
				{
					container.update(newHTML);
					animator.repatch();
				}				 
			});
		}
		catch(e)
		{
			container.update(newHTML);
			animator.repatch();			
		}
	}
});

/**
 * Symmetric effect
 */
QvAjax.Effect.Fade = Class.create(
{
	initialize: function() {},
	animate: function(container, newHTML, animator)
	{
		try
		{
			container.setStyle({
				"overflow": "hidden",
				"position": "relative"			
			});
	
			var width = container.getWidth();
			var height = container.getHeight();
			
			var innerOld = new Element("div").update(container.innerHTML);		
			var innerNew = new Element("div").update(newHTML);
			
			innerNew.setStyle({
				"position": "absolute",
				"z-index": "4",
				"width": width + "px",
				"display": "none"
			});
			
			container.update(innerOld);
			container.insert({"top": innerNew});
			
			new Effect.Morph(container, { style: 'height:'+innerNew.getHeight()+'px;', duration: 1 });
			new Effect.Fade(innerOld, { duration: 1 });
			new Effect.Appear(innerNew, 
			{ 
				duration: 1,
				afterFinish: function()
				{
					container.update(newHTML);
					animator.repatch();
				}				 
			});
		}			
		catch(e)
		{
			container.update(newHTML);
			animator.repatch();			
		}
	}
});

/**
 * Ajax window effect.
 */
QvAjax.Effect.Window = Class.create(
{
	windowOptions: null,	
	window: null,
	animated: false,
	
	initialize: function(options) 
	{
		if (options)
		{
			this.windowOptions = Object.extend(
			{
				id: "win" + (Math.random() + "").substring(2),
				className: "popin", 
				width: 200,
				height: 100,
				maximizable: false,
				minimizable: false,
				extraPadding: 10
				
			}, options);
			
			this.window = new Window(this.windowOptions);	
		}		
	},
	
	animate: function(container, newHTML, animator)
	{				
		var win = this.window;

		container.setStyle({
			"padding": this.windowOptions.extraPadding+"px"
		});
		
		container.update(newHTML);
		animator.repatch();
		
		if (!win.isVisible())
		{
			win.setContent(container);
			win.setZIndex(100);
			win.showCenter(true);
		}
		
		if (this.windowOptions.onComplete)
		{
			this.windowOptions.onComplete();
		}		
	},
	
	remove: function(container, animator)
	{	
		Windows.closeAll();	
	}
});

QvAjax.Effect.effects = {
	"ltr": new QvAjax.Effect.LeftToRight(),
	"rtl": new QvAjax.Effect.LeftToRight(true),
	"fade": new QvAjax.Effect.Fade()
};
