// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Extends the native element in order to add the container properties. The
 * container is basically the reference of the class which is holding an
 * element.
 * @package core
 * @subpackage element
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
Element.implement
({
	/**
	 * @var array The array of object used as actors.
	 */
	actors : null,
	
	/**
	 * @var int The count of actors.
	 */
	actorsCount : 0,

	/**
	 * Set the id of an element. This method is simply a shortcut to the common
	 * set property method allready in place.
	 * @param string The id.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	setId : function(id) {
		this.set('id', id);
	},

	/**
	 * Return the id of an element. This method is simply a shortcut to the common
	 * get property method allready in place.
	 * @return string The id.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getId : function() {
		return this.get('id');
	},
	
	/**
	 * Return the text of an element.
	 * @return string The text.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getText : function() {
		return this.get('text');
	},
	
	/**
	 * Set the text of an element.
	 * @param string The text.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	setText : function(text) {
		this.set('text', text);
	},
	
	/**
	 * Set the inside html of an element.
	 * @param array The html.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	setHtml : function(html) {
		return this.set('html', html);
	},
	
	/**
	 * Return the inside html of an element.
	 * @return array The html.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getHtml : function() {
		return this.get('html');
	},

	/**
	 * Show teh current element.
	 * @param string The display mode.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	show : function(method) { 
		this.setStyle('display', method === undefined ? 'inline' : method); 
	},
	
	/**
	 * Hide teh current element.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	hide : function() { 
		this.setStyle('display', 'none'); 
	},
	
	/**
	 * Set the row id for this element. The row id allows to store the id which
	 * is related to this element in the database.
	 * @param int The row id.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	setRowId : function(id) {
		this.store('rowid', id);
	},
	
	/**
	 * Return the row id for this element. The row id allows to store the id which
	 * is related to this element in the database.
	 * @param int The row id.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getRowId : function() {
		return this.retrieve('rowid');
	},
	
	/**
	 * This method will dispose of an attribute but the attribute will be stored
	 * in the internal class properties using the store method.
	 * @param string The property name.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	disposeProperty : function(property) {
		this.store(property, null);
		this.store(property, this.getProperty(property));
		this.removeProperty(property);
	},
	
	/**
	 * Return the property that was disposed before it was removed.
	 * @param string The property name.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getDisposedProperty : function(property) {
		return this.retrieve(property);
	},
	
	/**
	 * This method will dispose the href attribute but the attribute will be stored
	 * in the internal class properties using the store method.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	disposeHrefProperty : function() {
		this.disposeProperty('href');
		this.setStyle('cursor', 'pointer');
	},
	
	/**
	 * Return the href property that was disposed before it was removed.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getDisposedHrefProperty : function() {
		return this.getDisposedProperty('href');
	},
	
	/**
	 * Initialize the actor array.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initializeActors : function() {
		if (this.actors == null) this.actors = new Array();
	},
	
	/**
	 * Add an actor to the current element. An actor is basically a class
	 * which will act as a complex event.
	 * @param object The actor object.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	addActor : function(actor) {
		this.initializeActors();
		this.actors.push(actor);
		this.actorsCount++;
		actor.applyOn(this);
	},
	
	/**
	 * Return an actor based on the instance name.
	 * @param object The actor class.
	 * @return object The actor.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getActor : function(actorName) {
		this.initializeActors();
		for (var i = 0; i < this.actors.length; i++) {
			if (this.actors[i] instanceof actorName) {
				return this.actors[i];
			}
		}
		return null;
	},
	
	/**
	 * Return the ammount of actors used on this element.
	 * @return int The actor count.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	getActorCount : function() {
		return this.actorCount;
	},
	
	/**
	 * Indicate whether or not a given actor is acting in this object.
	 * @param object The actor class.
	 * @return bool True if the actor is present.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	hasActor : function(actorName) {
		this.initializeActors();
		for (var i = 0; i < this.actors.length; i++) {
			if (this.actors[i] instanceof actorName) {
				return true;
			}
		}
		return false;
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * An actor is a class that will basically activate itself upon an element using
 * the actAs method of the element.
 * @package core
 * @subpackage actor
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
var MooActor = new Class
({
	/**
	 * @var implements MooElement, Event, Options
	 */
	Implements : [Events, Options],
	
	/**
	 * @var object The element on which the actor will act on.
	 */
	element : null,
	
	/**
	 * Initialize the actor by setting the options.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initialize : function(options) {
		this.setOptions(options);
		return this;
	},
	
	/**
	 * This method is called when an object is given as an actor to an element.
	 * @param object The element to apply everything's on.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	applyOn : function(element) {
		this.element = element;
		return this;
	}	
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * This actor will handle the look of the different navigation element.
 * @package core
 * @subpackage actor
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
var MooNavigationElementActor = new Class
({
	/**
	 * @extends MooActor
	 */
	Extends : MooActor,
	
	/**
	 * @var object The options.
	 */
	options : {
		onEmphasize : $empty,
		onNormalize : $empty,
		css : {
			current : 'cur',
			alternate : 'alt'
		}
	},
		
	/**
	 * This method is called when an object is given as an actor to an element.
	 * @param object The element to apply everything's on.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	applyOn : function(element) {
		this.parent(element);
		this.element.addEvent('mouseenter', this.onEmphasize.bind(this));
		this.element.addEvent('mouseleave', this.onNormalize.bind(this));
		return this;
	},
	/**
	 * This event is called when the mouse moves over the tab for the first
	 * time. This method will basically highlight the tab.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	onEmphasize : function() {
		if (this.isCurrent() == false) {
			this.element.addClass(this.options.css.alternate);
			this.fireEvent('onEmphasize');
		}
	},

	/**
	 * This event is called when the mouse moves out the tab for the first
	 * time. This method will basically unhighlight the tab.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	onNormalize : function() {
		if (this.isCurrent() == false) {
			this.element.removeClass(this.options.css.alternate);
			this.fireEvent('onNormalize');
		}
	},

	/**
	 * Indicate wheter or not the tab is considered has the current tab. This
	 * is given by the class named current.
	 * @return bool True if the tab is a current one.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	isCurrent : function() {
		return this.element.hasClass(this.options.css.current);
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * MooFader is a class used to handling fading in and out.
 * @package core
 * @subpackage finder
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
Fx.Fade = new Class
({
	/**
	 * @extends Fx.Tween.
	 */
	Extends: Fx.Tween,
		
	/**
	 * Initialize the fading tween.
	 * @param object The element.
	 * @return object The object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initialize : function(element) {
		this.parent(element, {'property' : 'opacity', 'duration' : 'short'});
	}
});	// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * MooSelector is a mootools implementation of the original event selector.
 * This been ported to mootools by rossco.
 * @package core
 * @subpackage selector
 * @author Ross Lawley
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Ross Lawley
 * @since 1.0.0
 * @version 1.0.0
 */
var MooSelector = new Class
({
	/**
	 * Apply the selector rules when the dom ready event is called. This event
	 * is similar to the load event except it does not wait until the image are
	 * fully loaded. This is much faster.
	 * @param object The rules.
	 * @return object This class.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	start : function(rules) {
		window.addEvent('domready', function() {
			this.assign(rules);
		}.bind(this));
		return this;
	},

	/**
	 * Assign the selector rules when the dom ready event is called. This event
	 * is similar to the load event except it does not wait until the image are
	 * fully loaded. This is much faster.
	 * @param object The rules.
	 * @return object This class.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	assign : function(rules) {
		for (var key in rules) {
			var rule = rules[key];
			key.clean().split(',').each(function(selector) {
				var pair = selector.split('::');
				$$(pair[0]).each(function(elem) {
					if (pair.length == 1) return rule(elem);
					// attach the event on the selector
					elem.addEvent(pair[1], rule.pass(elem));
				});
			});
		}
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * MooElementFinder is a class used to find an element using a selector from
 * a given context. The context can be a dom tree or a text tree given by an
 * ajax response.
 * @package core
 * @subpackage finder
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
var MooElementFinder = new Class
({
	/**
	 * @var object The context to search in.
	 */
	context : null,

	/**
	 * Constructor. Set the context used to search in. The context may be a
	 * simple dom object or an ajax response object. This object will search
	 * in both type.
	 * @param object The context.
	 * @return object This class.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initialize : function(context) {
		this.setContext(context);
		return this;
	},

	/**
	 * Set the context used to search in. The context may be a simple dom object
	 * or an ajax response object. This object will search in both type.
	 * @param object The context.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	setContext : function(context) {
		if (context) { 
			if (typeof context == 'string') { 			
				var html = context.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
				html = (html) ? html[1] : context;		
				this.context = new Element('div').setHtml(html);
			}
		}
		return this;
	},

	/**
	 * Return the context used to search in. The context may be a simple dom object
	 * or an ajax response object. This object will search in both type.
	 * @return object The context.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getContext : function() {
		return this.context;
	},

	/**
	 * Try to find an element in the current context. It's important to specify
	 * both the element id and tag so the search will be successfully in a
	 * ajax response type of context.
	 * @param string The search id.
	 * @param string The search type.
	 * @return object The result.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	find : function(selector) {
		if (this.context == null) return document.getElement(selector);
		// the getElement method will only for some reason find class selectors and
		// not selectors with an id. If we are looking for an id we must search it
		// the old string way
		if (selector.search('#') > -1) {
			selector = selector.replace('#', '');
			selector = selector.replace(' ', '');
			return this.context.getElement('*[id=' + selector + ']');
		} else {
			return this.context.getElement(selector);
		}
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * MooForm is a class which sends a form through an ajax query and handle
 * the result. 
 * @package core
 * @subpackage finder
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
var MooForm = new Class
({
	/**
	 * @implements Events Options The basic element methods.
	 */
	Implements : [Events, Options],
	
	/**
	 * @var object The main element used.
	 */
	element : null,
	
	/**
	 * @var object The options.
	 */
	options : {
		onRequest : $empty,     // this event is called when the request is sent.
		onSuccess : $empty,     // this event is called when the request is completed.
		onError   : $empty,     // this event is called when the form have errors on the response.
		updateOnSuccess : true, // replace the current form with the success selector on success.
		inputSelector : '',     // the selector used to find the input form.
		successSelector : ''    // the selector used to find the success element.
	},
	
	/**
	 * Constructor. Initialize the form handler.
	 * @param object The form element.
	 * @param object The options. 
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initialize : function(form, options) {
		this.setOptions(options);
		this.element = $(form);
		return this;
	},

	/**
	 * Submit the form by creating the ajax request.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	submit : function() {
		this.fireEvent('request');
		this.element.set('send', { method : 'post', onComplete : this.onComplete.bind(this), evalScripts : true });
		this.element.send();
		return this;
	},

	/**
	 * Find the errors or the success page in the response and update it.
	 * @param object The response. 
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	complete : function(response) {
		var error = false;			
		var oldFormFinder = new MooElementFinder();
		var newFormFinder = new MooElementFinder(response);
		var oldForm = oldFormFinder.find(this.options.inputSelector);
		var newForm = newFormFinder.find(this.options.inputSelector);	
		if (newForm) {			
			var newFormLabels = newForm.getElements('label');
			var oldFormLabels = oldForm.getElements('label');			
			newFormLabels.each(function(label, index) {
				var newLabel = newFormLabels[index];
				var oldLabel = oldFormLabels[index];												
				var newField = newForm.getElement('*[id=' + newLabel.get('for') + ']');
				var oldField = oldForm.getElement('*[id=' + oldLabel.get('for') + ']');				 								 				
				if (newLabel.hasClass('error')) {
					oldLabel.addClass('error');
					error = true;							
					if (oldField) {
						oldField.addClass('error');
					}				
					// add the error message under the field if a new message is set
					var newError = newField.getNext('div.errors');
					var oldError = oldField.getNext('div.errors');
					if (newError) {
						var update = true;
						if (oldError) {
							if (oldError.getText() == newError.getText()) {
								update = false;
							}
						}
						if (update) {
							if (oldError) {
								newError.fade('hide');
								newError.replaces(oldError);
								newError.fade(1);	
							} else {
								newError.fade('hide');
								newError.inject(oldField, 'after');
								newError.fade(1);	
							}							
						}						
					}									
				} else {				
					oldLabel.removeClass('error');							
					if (oldField) {
						oldField.removeClass('error');
					}				
					// remove the error message if it exists
					var oldError = oldField.getNext('div.errors');
					if (oldError) {
						oldError.fade(0);
						oldError.dispose();
					}
				}					
			});			
		} 
		// at this point the form does not contain any errors, we assume it's been submited
		// successfully so we show the success part using the selector
		if (error == false) {			
			if (this.options.updateOnSuccess) {
				var successFinder = new MooElementFinder(response);
				var successFinderElement = successFinder.find(this.options.successSelector);
				if (successFinderElement) {
					oldForm.fade('hide');
					successFinderElement.replaces(oldForm);
					successFinderElement.fade(1);
				}
			}
		} else {
			this.fireEvent('error', response);
		}
		this.fireEvent('success', response);
	},
		
	/**
	 * This method gets called when the request is completed.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	onComplete : function(response) {
		this.complete(response);
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+


var MooControl = new Class
({
	/**
	 * @var implements MooElement, Event, Options
	 */
	Implements : [Events, Options],

	/**
	 * @var object The root element of the control.
	 */
	root : null,

	/**
	 * @var array The array of child elements.
	 */	
	child : {},
	
	/**
	 * @var bool Indicate whether or not the control has been drawn.
	 */
	injected : false,

	/**
	 * This method inject the control into a given object or the document. It 
	 * does not nececerally mean that the object will be visible. 
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	inject : function() {
		this.injected = true;
		return this;
	},
	
	/**
	 * Remove the control from the dom tree and by doing so make the element
	 * invisible. You must override this method. 
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	dispose : function() {
		return this;
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * MooWindow represents an empty window. The content can be obtained by a simple
 * element or for more complex window, a MooElementContent object can be used. In
 * this case, the inject method will have to return the element to insert inside
 * the window.
 * @package mootools
 * @subpackage window
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
var MooWindow = new Class
({
	/**
	 * @extends MooControl The base class for controls.
	 */
	Extends : MooControl,

	/**
	 * @var string The window title
	 */
	title : '',
	
	/**
	 * @var object The window cnotent.
	 */
	content : null,

	/**
	 * @var object The child used to render this control.
	 */
	child : {
		window : null,
		windowFrame : null,
		windowContent : null,
		windowTitleBarClose : null,
		windowTitleBar : null,
		windowOverlay : null
	},
		
	/**
	 * @var object The options.
	 */
	options : {
		onShow : $empty,
		onHide : $empty,
		modal : true,
		movable : false,
		position : 'center',
		sizey : null,
		sizex : 500,
		closeButtonLabel : 'close',
		css : {
			window : 'window',
			windowOverlay : 'window-overlay',
			windowFrame : 'window-frame',
			windowContent : 'window-content',
			windowTitleBar : 'window-title',
			windowTitleBarClose : 'window-close'
		}
	},
	
	/**
	 * Constructor. Set the child required to make the window work.
	 * @param object The window content.
	 * @param string The window title.
	 * @param object The window option.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initialize : function(content, title, options) {
		this.setOptions(options);
		this.title = title;
		this.content = content;
		this.content.setWindow(this);
		this.inject();
		this.initializeEvents();
	},
	
	/**
	 * Initialize all the events.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	 
	initializeEvents : function() {
		window.addEvent('resize', this.onWindowResize.bind(this));
		this.child.windowTitleBarClose.addEvent('click', this.onWindowTitleBarCloseClick.bind(this));
		return this;
	},
	
	/**
	 * This method inject the control into a given object or the document. It 
	 * does not nececerally mean that the object will be visible. 
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	inject : function() {
		if (this.injected == false) {
			this.parent();
			if (this.options.modal) this.createModalOverlay();
			this.createWindow();
		}
		return this;
	},
		
	/**
	 * This method injects the modal windowOverlay into the document. 
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	createModalOverlay : function() {
		this.child.windowOverlay = new Element('div');
		this.child.windowOverlay.addClass(this.options.css.windowOverlay);
		this.child.windowOverlay.setStyle('top', '0');
		this.child.windowOverlay.setStyle('left', '0');
		this.child.windowOverlay.setStyle('width', '100%');
		this.child.windowOverlay.setStyle('position', 'absolute');
		this.child.windowOverlay.hide();
		this.child.windowOverlay.inject(document.body);
		this.adjustModalOverlay();
		return this;
	},
	
	/**
	 * This method injects the window frame into the document. 
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	 
	 createWindow : function() {
	 	this.createWindowFrame();
	 	this.root.setStyle('position', 'absolute');
	 	if (this.options.sizex != '') this.root.setStyle('width', this.options.sizex);
	 	if (this.options.sizey != '') this.root.setStyle('height', this.options.sizey);
	 	if (this.options.modal == true) this.root.addClass('modal');
	 	if (this.options.movable == true) this.root.addClass('movable');
	 	this.root.hide();
	 	this.root.inject(document.body);
	 	this.adjustWindowPosition();
	 	if (this.options.movable) this.root.makeDraggable();
	 	return this;
	 },
	 
	/**
	 * Draw the window frame element.
	 * @return object A reference to the created element.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	createWindowFrame : function() {
	 	this.root = new Element('div');
	 	this.root.addClass(this.options.css.windowFrame);
	 	this.createWindowTitleBar();
	 	this.createWindowTitleBarClose();
	 	this.createWindowContent();
	 },
	 
	/**
	 * Draw the window windowTitle bar.
	 * @return object A reference to the created element.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	createWindowTitleBar : function() {
		this.child.windowTitleBar = new Element('div');
		this.child.windowTitleBar.addClass(this.options.css.windowTitleBar);
		this.child.windowTitleBar.setHtml(this.title);
		this.child.windowTitleBar.inject(this.root);
		return this;
	},
	
	/**
	 * Draw the window windowTitle bar close button.
	 * @return object A reference to the created element.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	createWindowTitleBarClose : function() {
		this.child.windowTitleBarClose = new Element('div');
		this.child.windowTitleBarClose.addClass(this.options.css.windowTitleBarClose);
		this.child.windowTitleBarClose.setText(this.options.closeButtonLabel);
		this.child.windowTitleBarClose.inject(this.child.windowTitleBar);
		return this;
	},

	/**
	 * Draw the window windowContent element.
	 * @return object A reference to the created element.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	 createWindowContent : function() {
		this.child.windowContent = new Element('div');
		this.child.windowContent.addClass(this.options.css.windowContent);
		this.child.windowContent.inject(this.root);
		this.content.inject(this.child.windowContent);
	},
	
	/**
	 * Adjust the window position based on the position option.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	adjustWindowPosition : function() {
		switch (this.options.position) {
			case 'center' :
			case 'right' :
			case 'left' :
				var l = (this.getViewableSize().x / 2) - (this.root.getSize().x / 2);
		 		var t = (this.getViewableSize().y / 2) - (this.root.getSize().y / 2);
		 		var scroll = window.getScroll();
		 		if (scroll.y > 0) t = t + scroll.y;
		 		this.root.setStyle('top', t + 'px');
	 			this.root.setStyle('left', l + 'px');
	 			break;
		} 
		return this;		
	},
	
	/**
	 * Adjust the window windowContent size.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	adjustWindowSize : function() {
		if (this.options.height != null) {
			var head1 = this.child.windowTitleBar.getSize().y;
			var head2 = this.child.windowTitleBarClose.getSize().y;
			var size  = head1 > head2 ? head1 : head2; 
			var paddingTop = parseInt(this.child.windowContent.getStyle('padding-top'));
			var paddingBot = parseInt(this.child.windowContent.getStyle('padding-bottom'));
			var height = this.options.height - size - paddingTop - paddingBot;
			this.child.windowContent.setStyle('height', height);
		}
	},
	
	/**
	 * This methods adjust the windowOverlay size on the screen.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	adjustModalOverlay : function() {
		this.child.windowOverlay.setStyle('height', this.getDocumentSize().y);
		return this;
	},
		
	/**
	 * Return the viewable size of the current screen.
	 * @return object The viewable size.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	getViewableSize : function() {
		return window.getSize();
	},			
	
	/**
	 * Return the entire document size.
	 * @return object The document size.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	getDocumentSize : function() {
		return window.getScrollSize();
	},
	
	/**
	 * Show the window and the windowOverlay if this window is modal.
	 * @return object A reference to the this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	show : function() {
		if (this.injected) {
			this.fireEvent('show');
			this.root.show('block');
			if (this.options.modal == true) this.child.windowOverlay.show('block');
			if (this.options.movable == false) this.adjustWindowPosition();
			else if (this.options.movable == true && this.adjusted == false) {
				this.adjustWindowPosition();
				this.adjusted = true;
			}			
			this.adjustWindowSize();		
		}
		return this;
	},
	
	/**
	 * Hide the window and the windowOverlay if this window is modal.
	 * @return object A reference to the this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	hide : function() {
		if (this.injected) {
			this.fireEvent('hide');
			this.root.hide();
			if (this.options.modal) this.child.windowOverlay.hide();
		}
		return this;	
	},
	
	/**
	 * Remove the control from the dom tree and by doing so make the element
	 * invisible. You must override this method. 
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	dispose : function() {
		if (this.injected) {
			this.root.dispose();
			if (this.options.modal) this.child.windowOverlay.dispose();
		}
		return this;
	},
	
	/**
	 * This event is called when the window or the browse gets resized.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	onWindowResize : function() {
		if (this.options.modal == true) this.adjustModalOverlay();
		if (this.options.movable == false) this.adjustWindowPosition();
	},
		
	/**
	 * This event is called when the window or the close button gets clicked.
	 * @return void
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	onWindowTitleBarCloseClick : function() {
		this.hide();
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * MooWindow content is the base class of any content that will be displayed 
 * inside a window. 
 * @package mootools
 * @subpackage window
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
var MooWindowContent = new Class
({
	/**
	 * @extends MooControl The base class for controls.
	 */
	Extends : MooControl, 
	
	/**
	 * @var object The window.
	 */ 
	window : null,

	/**
	 * Initialize the object by setting the root element.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initialize : function(options) {
		this.setOptions(options);
		this.root = new Element('div');
		return this;
	},

	/**
	 * In the case of a window content, the inject method will return the root
	 * element which contains the window content.
	 * @param object The window.
	 * @return object The root element.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	inject : function(inside) {
		this.root.inject(inside, 'inside');
		return this;
	},
	
	/**
	 * Set the window containing this content.
	 * @param object The window.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	setWindow : function(win) {
		this.window = win;
		return this;
	},
	
	/**
	 * Return the window containing this content.
	 * @return object The window.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getWindow : function() {
		return this.window;
	},
	
	/**
	 * Return the root element of the window content.
	 * @return object The root element.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	getRoot : function() {
		return this.root;
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Cotravel project.                                |
// | Copyright (C) Jean-Philippe Dery                                          |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

var MOO_GMAP_LAT = 1;
var MOO_GMAP_LNG = 0;

/**
 * MooGMap is a class that automate the process of showing a google map.
 * @package cotravel
 * @subpackage map
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @copyright Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @since 1.0.0
 * @version 1.0.0
 */
var MooGMap = new Class
({
	/**
	 * @implements Event, Options.
	 */	 
	Implements : [Events, Options],
	
	/**
	 * @var object The element where the map is rendered.
	 */
	element : null,
	
	/**
	 * @var object The google map object.
	 */
	map : null,
	
	/**
	 * @var object The google map geocoder object.
	 */
	geo : null,
	
	/**
	 * @var object The options.
	 */
	options : {
		onSearchRequest : $empty,
		onSearchSuggest : $empty,
		onSearchSuccess : $empty,
		onSearchError : $empty
	},
	
	/**
	 * Initialize the google map object.
	 * @param string The map element.
	 * @param object The options,
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initialize : function(map, options, sizex, sizey) {
		this.setOptions(options);
		this.element = $(map);
		this.initializeMap(sizex, sizey);
		this.initializeGeoCoder();
		this.setCenter(20, 0, 5);
		return this;
	},
	
	/**
	 * Initialize the geo coder client.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initializeMap : function(sizex, sizey) {
		if (sizex && sizey) {
			// sometime it's required to speciy a size, especially if the map is loaded
			// inside a hidden div. In this case google map cannot find the real size
			// of the map thus centering is not possibe
			this.map = new GMap2(this.element, { size : new GSize(sizex, sizey) });		
		} else {
			this.map = new GMap2(this.element);				
		}
		this.map.addControl(new GLargeMapControl());
		this.map.addControl(new GMapTypeControl());
		return this;
	},
	
	/**
	 * Initialize the geo coder client.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	initializeGeoCoder : function() {
		this.geo = new GClientGeocoder();
		return this;
	},
			
	/**
	 * Search for an address and trigger the right event.
	 * @param string The address to search.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	search : function(address) {
		this.fireEvent('search');
		this.geo.getLocations(address, function(result) {
			if (result.Status.code == G_GEO_SUCCESS) {
				if (result.Placemark.length > 1) { 
					// if there was more than one result we fire the 
					//suggest event to build a  did you mean service
					this.fireEvent('searchSuggest', [result.Placemark]);
				} else {
					// we have a single marker do we fire the success 
					// event with the placemark
					this.fireEvent('searchSuccess', result.Placemark[0]);
				}
			} else {
				// at this point we have an error. This fires up 
				// the error event with the error code as parameter 
				// so we can treat the error	
				this.fireEvent('searchError', result.Status.code);
            }
		}.bind(this));
		return this;
	},
	
	/**
	 * Center the map from a latitude and a longitude.
	 * @param float The latitude.
	 * @param float The longitude.
	 * @param int The zoom level.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */
	setCenter : function(lat, lng, zoom) {
		var point = new GLatLng(lat, lng);
		var level = zoom ? zoom : 1;
		this.map.setCenter(point, level);
		return this;
	},
	
	/**
	 * Add a marker at a latitude and a longitude.
	 * @param float The latitude.
	 * @param float The longitude.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	addMarker : function(lat, lng, options) {
		var point = new GLatLng(lat, lng);
		var markr = new GMarker(point, options);
		this.map.addOverlay(markr);
		return markr;
	},
	
	/**
	 *
	 */
	addPolygon : function(points, strokeColor, strokeWeight, strokeOpacity, fillColor, fillOpacity) {
		this.map.addOverlay(new GPolygon(this.convertPointArrayToLatLngArray(points), strokeColor, strokeWeight, strokeOpacity, fillColor, fillOpacity));
	},
	
	/**
	 *
	 */
	convertPointArrayToLatLngArray : function(arr) {
		var points = new Array();
		for (var i = 0; i < arr.length; i += 2) {
			var lat = arr[i + 0];
			var lng = arr[i + 1];
			points.push(new GLatLng(lat, lng));
		}
		return points;
	},
	
	/**
	 * Remove all the overlays.
	 * @param float The latitude.
	 * @param float The longitude.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since 1.0.0
	 */	
	clearOverlays : function() {
		this.map.clearOverlays();
		return this;
	},

	setMapTypeToNormal : function() {
		this.map.setMapType(G_NORMAL_MAP);
	},
	
	setMapTypeToSatellite : function() {
		this.map.setMapType(G_SATELLITE_MAP);
	},
	
	setMapTypeToHybrid : function() {
		this.map.setMapType(G_HYBRID_MAP);
	},
	
	getMap : function() {
		return this.map;
	}
});
