// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux Bédard Communication                                |
// |                                                                           |
// | 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	getHtml : function() {
		return this.get('html');
	},
	
	/**
	 * 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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	getDisposedHrefProperty : function() {
		return this.getDisposedProperty('href');
	},
	
	/**
	 * Initialize the actor array.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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;
	}
});String.implement
({
	toNumber: function() {
		return this.replace(/[^0-9]/g, '');
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

var Moo = {};// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * A command is basically an ajax request with extra options.
 * @package core
 * @subpackage command
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 1.0.0
 * @version 1.0.0
 */
Moo.Command = new Class
({	
	/**
	 * @var implements MooElement, Event, Options
	 */
	Implements : [Events, Options],

	/**
	 * @var string The url to request the command.
	 */
	url: null,

	/**
	 * @var object The ajax request.
	 */
	request: null,

	/**
	 * @var hash The ajax request headers.
	 */
	headers: null,
	
	/**
	 * @var hash The ajax request parameters.
	 */
	parameters: null,
	
	/**
	 * @var object The options.
	 */
	options: {
		onRequest: $empty,
		onComplete: $empty,
		evalScripts: false,
		method: 'post'
	},
	
	/**
	 * Initialize the actor by setting the options.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	initialize : function(url, options) {
		this.setUrl(url);
		this.setOptions(options);
		this.headers = new Hash();
		this.parameters = new Hash();
		return this;
	},
	
	/**
	 * Set the url to execute the command.
	 * @param string The url.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	setUrl: function(url) {
		this.url = url;
	},

	/**
	 * Set an header to send to the request.
	 * @param string The header name.
	 * @param string The header value.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	setHeader: function(name, value) {
		this.headers.set(name, value);
	},
	
	/**
	 * Set the content request header which will be handled by the server in order to
	 * return the content or the content with the decorator.
	 * @param string content or decorator.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	setContentRequestHeader: function(value) {
		this.setHeader('X-Content-Request', value);
	},
	
	/**
	 * Set an parameter to send to the request.
	 * @param string The parameter name.
	 * @param string The parameter value.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	setParameter: function(name, value) {
		this.parameters.set(name, value);
	},
	
	/**
	 * Clear all the parameters.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	clearParameters: function() {
		this.parameters = null;
		this.parameters = new Hash();
	},
	
	/**
	 * Prepare the ajax request.
	 * @param string The mode
	 * @param string The parameter value.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	prepare: function(mode) {
		this.fireEvent('request');
		switch (mode) {
			case 'json': this.request = new Request.JSON({url: this.url, method: this.options.method, evalScripts: this.options.evalScripts, data: this.parameters.toQueryString(), headers: this.headers.getClean(), onComplete: this.jsonRequestCompleted.bind(this)}); break;
			case 'html': this.request = new Request.HTML({url: this.url, method: this.options.method, evalScripts: this.options.evalScripts, data: this.parameters.toQueryString(), headers: this.headers.getClean(), onComplete: this.htmlRequestCompleted.bind(this)}); break;
			default:     this.request = new Request.HTML({url: this.url, method: this.options.method, evalScripts: this.options.evalScripts, data: this.parameters.toQueryString(), headers: this.headers.getClean(), onComplete: this.htmlRequestCompleted.bind(this)}); break;  
		}
	},
	
	/**
	 * Execute the command and request for an html response.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	executeHtml: function() {
		this.setContentRequestHeader('content');
		this.prepare('html');
		this.request.send();
	},
	
	/**
	 * Execute the command and request for a json response.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	executeJson: function() {
		this.prepare('json');
		this.request.send();
	},

	/**
	 * This is called when the html request is completed.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	htmlRequestCompleted: function(responseTree, responseElements, responseHtml, responseJavaScript) {
		this.fireEvent('complete', [responseTree, responseElements, responseHtml, responseJavaScript]);
	},
	
	/**
	 * This is called when the html request is completed.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	jsonRequestCompleted: function(responseJson, responseText) {
		this.fireEvent('complete', [responseJson, responseText]);
	},
	
	/**
	 * This is called when the html request is completed.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	isError: function() {
		return false;
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Finder
 * This class is 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 1.0.0
 * @version 1.0.0
 */
Moo.Finder = 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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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 (jean-philippe.dery@lemieuxbedard.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) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Actor = new Class
({
	/**
	 * @var object The options the element will inherit.
	 */
	options: null,
	
	/**
	 * @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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	initialize: function(options) {
		this.options = 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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	applyOn: function(element) {
		this.element = element;
		return this;
	},
	
	/**
	 * Return the element.
	 * @return object The element.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	getElement: function() {
		return this.element;
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Animable
 * This is an interface that simplify the animation handling process.
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Animable = new Class
({
	/**
	 * This method will execute an animation with a given element and handle the 
	 * animation response. If the animation wants the default behavior to be
	 * executed after the process, it will use the behavior parameter as this
	 * default behavior.
	 * @param object The element to animate.
	 * @param object The animation.
	 * @param function The default behavior.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	animate: function(element, animation, behavior) {
		// execute the animation if such thing is specified othewise the
		// default behavior will be executed
		if (animation) {
			if (animation.run(element) == MOO_ANIMATION_EXEC_DEFAULT_BEHAVIOR) {
				animation.end(function() { 
					behavior.run(); 
				});
			}
			return;
		}
		// at this point we know there is no animations so we execute
		// the default behavior 
		behavior.run();
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * @var string Skip the default behavior.
 */
var MOO_ANIMATION_SKIP_DEFAULT_BEHAVIOR = 'skip';

/**
 * @var string Execute the default behavior.
 */	
var MOO_ANIMATION_EXEC_DEFAULT_BEHAVIOR = 'exec';

/**
 * Moo.Animation 
 * This class is the base class of all animations. This class, once extended will
 * be used with several other class where simple methods such as show and hide 
 * can be animated differently instead of using the default behavior.
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Animation = new Class
({
	/**
	 * @implements Events Options The basic element methods.
	 */
	Implements: [Events, Options],
			
	/**
	 * @var object The options.
	 */
	options: {
		onStart: $empty,
		onFinish: $empty,
		behavior: MOO_ANIMATION_EXEC_DEFAULT_BEHAVIOR
	},
	
	/**
	 * @var object The element that gets animated.
	 */
	element: null,
	
	/**
	 * @var function The function that gets called when the animation reaches the end.
	 */
	callback: null,
	
	/**
	 * Initialize the animation.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	initialize: function(options) {
		this.setOptions(options);
		return this;
	},
	
	/**
	 * Runs the animation.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	run: function(element) {
		this.element = element;
		this.fireEvent('start', this.element);
		return this.animate(this.element);
	},
	
	/**
	 * Set the function that will be called when the animation finishes.
	 * @param function The callback function.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	end: function(callback) {
		this.callback = callback;
	},
	
	/**
	 * Override this method to add your own implementation of the animation. Don't
	 * forget to call the finish method when the animation's tween completes.
	 * @param object The element to animate. 
	 * @return string A constant about the behavior of the sub executions.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	animate: function(element) {
		return this.options.behavior;
	},
	
	/**
	 * This method is called from your overridden animate method to indicate
	 * the animation finished.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	finish: function() {
		if (this.callback) {
			this.callback.run(null, this);
			this.fireEvent('finish', this.element);
		} 
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Animation.FadeIn
 * A simple fade in animation.
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Animation.FadeIn = new Class
({
	/**
	 * @extends Moo.Animation.
	 */
	Extends: Moo.Animation,
	
	/**
	 * @var object The options.
	 */
	options: {
		duration: 'long',
		from: 0,
		to: 1
	},
	
	/**
	 * Execute the animation.
	 * @param object The element to animate. 
	 * @return string A constant about the behavior of the sub executions.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	animate: function(element) {

		if (element.isVisible() == false) {
			element.fade('hide');
			element.show();
		}

		var fx =  new Fx.Tween(element, {
			duration: this.options.duration,
			onComplete: function() {
				this.finish();
			}.bind(this)
		})
		
		fx.start('opacity', this.options.from, this.options.to);

		return this.options.behavior;
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Animation.FadeIn
 * A simple fade in animation.
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Animation.FadeOut = new Class
({
	/**
	 * @extends Moo.Animation.
	 */
	Extends: Moo.Animation,
	
	/**
	 * @var object The options.
	 */
	options: {
		duration: 'short'
	},
	
	/**
	 * Execute the animation.
	 * @param object The element to animate. 
	 * @return string A constant about the behavior of the sub executions.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	animate: function(element) {
		
		if (element.isVisible() == true) {
			element.fade('show');
			element.show();
		}

		var fx =  new Fx.Tween(element, {
			duration: this.options.duration,
			onComplete: function() {
				this.finish();
			}.bind(this)
		})
		
		fx.start('opacity', this.options.from, this.options.to);

		return this.options.behavior;
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BŽdard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Selector 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Ross Lawley
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Selector = 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 Ross Lawley
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.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 Ross Lawley	 
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.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) Lemieux BĹ˝dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

Moo.Swiff = {};// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĹ˝dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Swiff.Uploader - Flash FileReference Control
 * @version 3.0
 * @license MIT License
 * @author Harald Kirschner <http://digitarald.de>
 * @author Valerio Proietti, <http://mad4milk.net>
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Authors
 * @since 2.0.0
 * @version 2.0.0 
 */
Moo.Swiff.Uploader = new Class({

	Extends: Swiff,

	Implements: Events,

	options: {
		path: 'swf/Moo.Swiff.Uploader.swf',
		target: null,
		zIndex: 9999,
		height: 30,
		width: 100,
		callBacks: null,
		params: {
			wMode: 'opaque',
			menu: 'false',
			allowScriptAccess: 'always'
		},

		typeFilter: null,
		multiple: true,
		queued: true,
		verbose: false,

		url: null,
		method: null,
		data: null,
		mergeData: true,
		fieldName: null,

		fileSizeMin: 1,
		fileSizeMax: null, // Official limit is 100 MB for FileReference, but I tested up to 2Gb!
		allowDuplicates: false,
		timeLimit: (Browser.Platform.linux) ? 0 : 30,

		buttonImage: null,
		policyFile: null,
		
		fileListMax: 0,
		fileListSizeMax: 0,

		instantStart: false,
		appendCookieData: false,
		
		fileClass: null
		/*
		onLoad: $empty,
		onFail: $empty,
		onStart: $empty,
		onQueue: $empty,
		onComplete: $empty,
		onBrowse: $empty,
		onDisabledBrowse: $empty,
		onCancel: $empty,
		onSelect: $empty,
		onSelectSuccess: $empty,
		onSelectFail: $empty,
		
		onButtonEnter: $empty,
		onButtonLeave: $empty,
		onButtonDown: $empty,
		onButtonDisable: $empty,
		
		onFileStart: $empty,
		onFileStop: $empty,
		onFileRequeue: $empty,
		onFileOpen: $empty,
		onFileProgress: $empty,
		onFileComplete: $empty,
		onFileRemove: $empty,
		
		onBeforeStart: $empty,
		onBeforeStop: $empty,
		onBeforeRemove: $empty
		*/
	},

	initialize: function(options) {
		// protected events to control the class, added
		// before setting options (which adds own events)
		this.addEvent('load', this.initializeSwiff, true)
			.addEvent('select', this.processFiles, true)
			.addEvent('complete', this.update, true)
			.addEvent('fileRemove', function(file) {
				this.fileList.erase(file);
			}.bind(this), true);

		this.setOptions(options);

		// callbacks are no longer in the options, every callback
		// is fired as event, this is just compat
		if (this.options.callBacks) {
			Hash.each(this.options.callBacks, function(fn, name) {
				this.addEvent(name, fn);
			}, this);
		}

		this.options.callBacks = {
			fireCallback: this.fireCallback.bind(this)
		};

		var path = this.options.path;
		if (!path.contains('?')) path += '?noCache=' + $time(); // cache in IE

		// container options for Swiff class
		this.options.container = this.box = new Element('span', {'class': 'swiff-uploader-box'}).inject($(this.options.container) || document.body);

		// target 
		this.target = $(this.options.target);
		if (this.target) {
			var scroll = window.getScroll();
			this.box.setStyles({
				position: 'absolute',
				visibility: 'visible',
				zIndex: this.options.zIndex,
				overflow: 'hidden',
				height: 1, width: 1,
				top: scroll.y, left: scroll.x
			});
			
			// we force wMode to transparent for the overlay effect
			this.parent(path, {
				params: {
					wMode: 'transparent'
				},
				height: '100%',
				width: '100%'
			});
			
			this.target.addEvent('mouseenter', this.reposition.bind(this, []));
			
			// button interactions, relayed to to the target
			this.addEvents({
				buttonEnter: this.targetRelay.bind(this, ['mouseenter']),
				buttonLeave: this.targetRelay.bind(this, ['mouseleave']),
				buttonDown: this.targetRelay.bind(this, ['mousedown']),
				buttonDisable: this.targetRelay.bind(this, ['disable'])
			});
			
			this.reposition();
			window.addEvent('resize', this.reposition.bind(this, []));
		} else {
			this.parent(path);
		}

		this.inject(this.box);

		this.fileList = [];
		
		this.size = this.uploading = this.bytesLoaded = this.percentLoaded = 0;
		
		if (Browser.Plugins.Flash.version < 9) {
			this.fireEvent('fail', ['flash']);
		} else {
			this.verifyLoad.delay(1000, this);
		}
	},
	
	verifyLoad: function() {
		if (this.loaded) return;
		if (!this.object.parentNode) {
			this.fireEvent('fail', ['disabled']);
		} else if (this.object.style.display == 'none') {
			this.fireEvent('fail', ['hidden']);
		} else if (!this.object.offsetWidth) {
			this.fireEvent('fail', ['empty']);
		}
	},

	fireCallback: function(name, args) {
		// file* callbacks are relayed to the specific file
		if (name.substr(0, 4) == 'file') {
			// updated queue data is the second argument
			if (args.length > 1) this.update(args[1]);
			var data = args[0];
			
			var file = this.findFile(data.id);
			this.fireEvent(name, file || data, 5);
			if (file) {
				var fire = name.replace(/^file([A-Z])/, function($0, $1) {
					return $1.toLowerCase();
				});
				file.update(data).fireEvent(fire, [data], 10);
			}
		} else {
			this.fireEvent(name, args, 5);
		}
	},

	update: function(data) {
		// the data is saved right to the instance 
		$extend(this, data);
		this.fireEvent('queue', [this], 10);
		return this;
	},

	findFile: function(id) {
		for (var i = 0; i < this.fileList.length; i++) {
			if (this.fileList[i].id == id) return this.fileList[i];
		}
		return null;
	},

	initializeSwiff: function() {
		// extracted options for the swf 
		this.remote('initialize', {
			width: this.options.width,
			height: this.options.height,
			typeFilter: this.options.typeFilter,
			multiple: this.options.multiple,
			queued: this.options.queued,
			url: this.options.url,
			method: this.options.method,
			data: this.options.data,
			mergeData: this.options.mergeData,
			fieldName: this.options.fieldName,
			verbose: this.options.verbose,
			fileSizeMin: this.options.fileSizeMin,
			fileSizeMax: this.options.fileSizeMax,
			allowDuplicates: this.options.allowDuplicates,
			timeLimit: this.options.timeLimit,
			buttonImage: this.options.buttonImage,
			policyFile: this.options.policyFile
		});

		this.loaded = true;

		this.appendCookieData();
	},
	
	targetRelay: function(name) {
		if (this.target) this.target.fireEvent(name);
	},

	reposition: function(coords) {
		// update coordinates, manual or automatically
		coords = coords || (this.target && this.target.offsetHeight)
			? this.target.getCoordinates(this.box.getOffsetParent())
			: {top: window.getScrollTop(), left: 0, width: 40, height: 40}
		this.box.setStyles(coords);
		this.fireEvent('reposition', [coords, this.box, this.target]);
	},

	setOptions: function(options) {
		if (options) {
			if (options.url) options.url = Moo.Swiff.Uploader.qualifyPath(options.url);
			if (options.buttonImage) options.buttonImage = Moo.Swiff.Uploader.qualifyPath(options.buttonImage);
			this.parent(options);
			if (this.loaded) this.remote('setOptions', options);
		}
		return this;
	},

	setEnabled: function(status) {
		this.remote('setEnabled', status);
	},

	start: function() {
		this.fireEvent('beforeStart');
		this.remote('start');
	},

	stop: function() {
		this.fireEvent('beforeStop');
		this.remote('stop');
	},

	remove: function() {
		this.fireEvent('beforeRemove');
		this.remote('remove');
	},

	fileStart: function(file) {
		this.remote('fileStart', file.id);
	},

	fileStop: function(file) {
		this.remote('fileStop', file.id);
	},

	fileRemove: function(file) {
		this.remote('fileRemove', file.id);
	},

	fileRequeue: function(file) {
		this.remote('fileRequeue', file.id);
	},

	appendCookieData: function() {
		var append = this.options.appendCookieData;
		if (!append) return;
		
		var hash = {};
		document.cookie.split(/;\s*/).each(function(cookie) {
			cookie = cookie.split('=');
			if (cookie.length == 2) {
				hash[decodeURIComponent(cookie[0])] = decodeURIComponent(cookie[1]);
			}
		});

		var data = this.options.data || {};
		if ($type(append) == 'string') data[append] = hash;
		else $extend(data, hash);

		this.setOptions({data: data});
	},

	processFiles: function(successraw, failraw, queue) {
		var cls = this.options.fileClass || Moo.Swiff.Uploader.File;

		var fail = [], success = [];

		if (successraw) {
			successraw.each(function(data) {
				var ret = new cls(this, data);
				if (!ret.validate()) {
					ret.remove.delay(10, ret);
					fail.push(ret);
				} else {
					this.size += data.size;
					this.fileList.push(ret);
					success.push(ret);
					ret.render();
				}
			}, this);

			this.fireEvent('selectSuccess', [success], 10);
		}

		if (failraw || fail.length) {
			fail.extend((failraw) ? failraw.map(function(data) {
				return new cls(this, data);
			}, this) : []).each(function(file) {
				file.invalidate().render();
			});

			this.fireEvent('selectFail', [fail], 10);
		}

		this.update(queue);

		if (this.options.instantStart && success.length) this.start();
	}

});

$extend(Moo.Swiff.Uploader, {

	STATUS_QUEUED: 0,
	STATUS_RUNNING: 1,
	STATUS_ERROR: 2,
	STATUS_COMPLETE: 3,
	STATUS_STOPPED: 4,

	log: function() {
		if (window.console && console.info) console.info.apply(console, arguments);
	},

	unitLabels: {
		b: [{min: 1, unit: 'B'}, {min: 1024, unit: 'kB'}, {min: 1048576, unit: 'MB'}, {min: 1073741824, unit: 'GB'}],
		s: [{min: 1, unit: 's'}, {min: 60, unit: 'm'}, {min: 3600, unit: 'h'}, {min: 86400, unit: 'd'}]
	},

	formatUnit: function(base, type, join) {
		var labels = Moo.Swiff.Uploader.unitLabels[(type == 'bps') ? 'b' : type];
		var append = (type == 'bps') ? '/s' : '';
		var i, l = labels.length, value;

		if (base < 1) return '0 ' + labels[0].unit + append;

		if (type == 's') {
			var units = [];

			for (i = l - 1; i >= 0; i--) {
				value = Math.floor(base / labels[i].min);
				if (value) {
					units.push(value + ' ' + labels[i].unit);
					base -= value * labels[i].min;
					if (!base) break;
				}
			}

			return (join === false) ? units : units.join(join || ', ');
		}

		for (i = l - 1; i >= 0; i--) {
			value = labels[i].min;
			if (base >= value) break;
		}

		return (base / value).toFixed(1) + ' ' + labels[i].unit + append;
	}

});

Moo.Swiff.Uploader.qualifyPath = (function() {
	
	var anchor;
	
	return function(path) {
		(anchor || (anchor = new Element('a'))).href = path;
		return anchor.href;
	};

})();

Moo.Swiff.Uploader.File = new Class({

	Implements: Events,

	initialize: function(base, data) {
		this.base = base;
		this.update(data);
	},

	update: function(data) {
		return $extend(this, data);
	},

	validate: function() {
		var options = this.base.options;
		
		if (options.fileListMax && this.base.fileList.length >= options.fileListMax) {
			this.validationError = 'fileListMax';
			return false;
		}
		
		if (options.fileListSizeMax && (this.base.size + this.size) > options.fileListSizeMax) {
			this.validationError = 'fileListSizeMax';
			return false;
		}
		
		return true;
	},

	invalidate: function() {
		this.invalid = true;
		this.base.fireEvent('fileInvalid', this, 10);
		return this.fireEvent('invalid', this, 10);
	},

	render: function() {
		return this;
	},

	setOptions: function(options) {
		if (options) {
			if (options.url) options.url = Moo.Swiff.Uploader.qualifyPath(options.url);
			this.base.remote('fileSetOptions', this.id, options);
			this.options = $merge(this.options, options);
		}
		return this;
	},

	start: function() {
		this.base.fileStart(this);
		return this;
	},

	stop: function() {
		this.base.fileStop(this);
		return this;
	},

	remove: function() {
		this.base.fileRemove(this);
		return this;
	},

	requeue: function() {
		this.base.fileRequeue(this);
	} 

});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

Moo.Request = {};
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Request.HTMLForm
 * This class handles a form by sending the form contents using a json request.
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Request.HTMLForm = new Class
({
	/**
	 * @implements Events Options The basic element methods.
	 */
	Implements: [Events, Options],
	
	/**
	 * @var object The options.
	 */
	options: {
		onSubmit: $empty,
		onReceive: $empty
	},
		
	/**
	 * @var object The form element.
	 */
	element: null,
	
	/**
	 * Initialize the form.
	 * @param object The form element.
	 * @param object The options.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	initialize: function(element, options) {
		this.setOptions(options);
		this.element = $(element);
		this.element.addEvent('submit', this.onSubmit.bind(this));
		var request = this.element.get('send');
		request.addEvent('request', this.onSubmitRequest.bind(this));
		request.addEvent('complete', this.onSubmitComplete.bind(this));
	},

	/**
	 * Send the form.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	submit: function() {
		this.element.send();
	},	
	
	/**
	 * Fired when the form is submited.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	onSubmit: function(e) {
		e.stop();
		this.submit();
		return false;
	},
	
	/**
	 * Fired when the form is requested.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	onSubmitRequest: function() {
		this.fireEvent('submit');
	},
	
	/**
	 * Fired when the form sending is received.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	onSubmitComplete: function(responseHtml) {
		this.fireEvent('receive', [responseHtml]);
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Request.HTMLFormActor = new Class
({
	/**
	 * @extends Moo.Actor
	 */
	Extends: Moo.Actor,
	
	/**
	 * 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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	applyOn: function(element) {
		this.element = new Moo.Request.HTMLForm(element, this.options);
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * This class handles a form by sending the form contents using a json 
 * request. In this case, the response returned will be valid json.
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Request.JSONForm = new Class
({
	/**
	 * @extend Moo.Request.HTMLForm
	 */
	Extends: Moo.Request.HTMLForm,

	/**
	 * Send the form.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	submit: function() {
		var request = this.element.get('send');
		request.setHeader('Accept', 'application/json');
		request.setHeader('X-Request', 'JSON');
		this.element.send();
	},	
	
	/**
	 * Fired when the form sending is received.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	onSubmitComplete: function(responseJson) {
		this.fireEvent('receive', [JSON.decode(responseJson)]);
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.Request.JSONFormActor = new Class
({
	/**
	 * @extends Moo.Actor
	 */
	Extends: Moo.Actor,
	
	/**
	 * 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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	applyOn: function(element) {
		this.element = new Moo.Request.JSONForm(element, this.options);
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĹ˝dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

Moo.Request.Upload = {};// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĹ˝dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Request.Upload.Fancy
 * FancyUpload - Flash meets Ajax for powerful and elegant uploads.
 * Updated to latest 3.0 API. Hopefully 100% compat!
 * @version 1.1
 * @license MIT License
 * @author Harald Kirschner <mail [at] digitarald [dot] de>
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Authors
 * @since 2.0.0
 * @version 2.0.0  
 */
Moo.Request.Upload.Fancy = new Class({

	/**
	 * @extends Moo.Swiff.Uploader.
	 */
	Extends: Moo.Swiff.Uploader,
	
	/**
	 * @var object The options.
	 */
	options: {
		queued: 1,
		// compat
		limitSize: 0,
		limitFiles: 0,
		validateFile: $lambda(true)
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	initialize: function(status, list, options) {
		this.status = $(status);
		this.list = $(list);

		// compat
		options.fileClass = options.fileClass || Moo.Request.Upload.Fancy.File;
		options.fileSizeMax = options.limitSize || options.fileSizeMax;
		options.fileListMax = options.limitFiles || options.fileListMax;

		this.parent(options);

		this.addEvents({
			'load': this.render,
			'select': this.onSelect,
			'cancel': this.onCancel,
			'start': this.onStart,
			'queue': this.onQueue,
			'complete': this.onComplete
		});
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	render: function() {
		this.overallTitle = this.status.getElement('.overall-title');
		this.currentTitle = this.status.getElement('.current-title');
		this.currentText = this.status.getElement('.current-text');
		var progress = this.status.getElement('.overall-progress');
		this.overallProgress = new Moo.UI.Progress(progress, {
			text: new Element('span', {'class': 'progress-text'}).inject(progress, 'after')
		});
		progress = this.status.getElement('.current-progress')
		this.currentProgress = new Moo.UI.Progress(progress, {
			text: new Element('span', {'class': 'progress-text'}).inject(progress, 'after')
		});
				
		this.updateOverall();
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onSelect: function() {
		this.status.removeClass('status-browsing');
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onCancel: function() {
		this.status.removeClass('file-browsing');
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onStart: function() {
		this.status.addClass('file-uploading');
		this.overallProgress.set(0);
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onQueue: function() {
		this.updateOverall();
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onComplete: function() {
		this.status.removeClass('file-uploading');
		if (this.size) {
			this.overallProgress.start(100);
		} else {
			this.overallProgress.set(0);
			this.currentProgress.set(0);
		}
		
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	updateOverall: function() {
		this.overallTitle.set('html', MooTools.lang.get('FancyUpload', 'progressOverall').substitute({
			total: Moo.Swiff.Uploader.formatUnit(this.size, 'b')
		}));
		if (!this.size) {
			this.currentTitle.set('html', MooTools.lang.get('FancyUpload', 'currentTitle'));
			this.currentText.set('html', '');
		}
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	upload: function() {
		this.start();
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	removeFile: function() {
		return this.remove();
	}

});

Moo.Request.Upload.Fancy.File = new Class({
	
	Extends: Moo.Swiff.Uploader.File,

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	render: function() {
		if (this.invalid) {
			if (this.validationError) {
				var msg = MooTools.lang.get('FancyUpload', 'validationErrors')[this.validationError] || this.validationError;
				this.validationErrorMessage = msg.substitute({
					name: this.name,
					size: Moo.Swiff.Uploader.formatUnit(this.size, 'b'),
					fileSizeMin: Moo.Swiff.Uploader.formatUnit(this.base.options.fileSizeMin || 0, 'b'),
					fileSizeMax: Moo.Swiff.Uploader.formatUnit(this.base.options.fileSizeMax || 0, 'b'),
					fileListMax: this.base.options.fileListMax || 0,
					fileListSizeMax: Moo.Swiff.Uploader.formatUnit(this.base.options.fileListSizeMax || 0, 'b')
				});
			}
			this.remove();
			return;
		}
		
		this.addEvents({
			'start': this.onStart,
			'progress': this.onProgress,
			'complete': this.onComplete,
			'error': this.onError,
			'remove': this.onRemove
		});
		
		this.info = new Element('span', {'class': 'file-info'});
		this.element = new Element('li', {'class': 'file'}).adopt(
			new Element('span', {'class': 'file-size', 'html': Moo.Swiff.Uploader.formatUnit(this.size, 'b')}),
			new Element('a', {
				'class': 'file-remove',
				href: '#',
				html: MooTools.lang.get('FancyUpload', 'remove'),
				title: MooTools.lang.get('FancyUpload', 'removeTitle'),
				events: {
					click: function() {
						this.remove();
						return false;
					}.bind(this)
				}
			}),
			new Element('span', {'class': 'file-name', 'html': MooTools.lang.get('FancyUpload', 'fileName').substitute(this)}),
			this.info
		).inject(this.base.list);
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	validate: function() {
		return (this.parent() && this.base.options.validateFile(this));
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	onStart: function() {
		this.element.addClass('file-uploading');
		this.base.currentProgress.cancel().set(0);
		this.base.currentTitle.set('html', MooTools.lang.get('FancyUpload', 'currentFile').substitute(this));
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onProgress: function() {
		this.base.overallProgress.start(this.base.percentLoaded);
		this.base.currentText.set('html', MooTools.lang.get('FancyUpload', 'currentProgress').substitute({
			rate: (this.progress.rate) ? Moo.Swiff.Uploader.formatUnit(this.progress.rate, 'bps') : '- B',
			bytesLoaded: Moo.Swiff.Uploader.formatUnit(this.progress.bytesLoaded, 'b'),
			timeRemaining: (this.progress.timeRemaining) ? Moo.Swiff.Uploader.formatUnit(this.progress.timeRemaining, 's') : '-'
		}));
		this.base.currentProgress.start(this.progress.percentLoaded);
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	onComplete: function() {
		this.element.removeClass('file-uploading');
		this.base.currentText.set('html', 'Upload completed');
		this.base.currentProgress.start(100);
		if (this.response.error) {
			var msg = MooTools.lang.get('FancyUpload', 'errors')[this.response.error] || '{error} #{code}';
			this.errorMessage = msg.substitute($extend({
				name: this.name,
				size: Moo.Swiff.Uploader.formatUnit(this.size, 'b')
			}, this.response));
			var args = [this, this.errorMessage, this.response];
			this.fireEvent('error', args).base.fireEvent('fileError', args);
		} else {
			this.base.fireEvent('fileSuccess', [this, this.response.text || '']);
		}
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onError: function() {
		this.element.addClass('file-failed');
		var error = MooTools.lang.get('FancyUpload', 'fileError').substitute(this);
		this.info.set('html', '<strong>' + error + ':</strong> ' + this.errorMessage);
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onRemove: function() {
		this.element.getElements('a').setStyle('visibility', 'hidden');
		this.element.fade('out').retrieve('tween').chain(Element.destroy.bind(Element, this.element));
	}
});

// avoiding MooTools.lang dependency

(function() {
	var phrases = {
		'progressOverall': 'Overall Progress ({total})',
		'currentTitle': 'File Progress',
		'currentFile': 'Uploading "{name}"',
		'currentProgress': 'Upload: {bytesLoaded} with {rate}, {timeRemaining} remaining.',
		'fileName': '{name}',
		'remove': 'Remove',
		'removeTitle': 'Click to remove this entry.',
		'fileError': 'Upload failed',
		'validationErrors': {
			'duplicate': 'File <em>{name}</em> is already added, duplicates are not allowed.',
			'sizeLimitMin': 'File <em>{name}</em> (<em>{size}</em>) is too small, the minimal file size is {fileSizeMin}.',
			'sizeLimitMax': 'File <em>{name}</em> (<em>{size}</em>) is too big, the maximal file size is <em>{fileSizeMax}</em>.',
			'fileListMax': 'File <em>{name}</em> could not be added, amount of <em>{fileListMax} files</em> exceeded.',
			'fileListSizeMax': 'File <em>{name}</em> (<em>{size}</em>) is too big, overall filesize of <em>{fileListSizeMax}</em> exceeded.'
		},
		'errors': {
			'httpStatus': 'Server returned HTTP-Status <code>#{code}</code>',
			'securityError': 'Security error occured ({text})',
			'ioError': 'Error caused a send or load operation to fail ({text})'
		}
	};
	
	if (MooTools.lang) {
		MooTools.lang.set('en-US', 'FancyUpload', phrases);
	} else {
		MooTools.lang = {
			get: function(from, key) {
				return phrases[key];
			}
		};
	}
})();
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĹ˝dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * FancyUpload.Attach - Flash meets Ajax for powerful and elegant uploads.
 * @version 3.0 rc3
 * @license MIT License
 * @author Harald Kirschner <mail [at] digitarald [dot] de>
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Authors
 */
Moo.Request.Upload.Fancy.Attach = new Class({

	Extends: Moo.Swiff.Uploader,
	
	options: {
		queued: false,
		instantStart: true
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	initialize: function(list, selects, options) {
		this.list = $(list);
		this.selects = $(selects) ? $$($(selects)) : $$(selects);
				
		options.target = this.selects[0];
		options.fileClass = options.fileClass || Moo.Request.Upload.Fancy.Attach.File;
		
		this.parent(options);

		var self = this;
		
		this.selects.addEvents({
			click: function() {
				return false;
			},
			mouseenter: function() {
				this.addClass('hover');
				self.reposition();
			},
			mouseleave: function() {
				this.removeClass('hover');
				this.blur();
			},
			mousedown: function() {
				this.focus();
			}
		});
		
		if (this.selects.length == 2) {
			this.selects[1].setStyle('display', 'none');
			this.addEvents({
				'selectSuccess': this.onSelectSuccess,
				'fileRemove': this.onFileRemove
			});
		}
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	onSelectSuccess: function() {
		if (this.fileList.length > 0) {
			this.selects[0].setStyle('display', 'none');
			this.selects[1].setStyle('display', 'inline');
			this.target = this.selects[1];
			this.reposition();
		}
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	onFileRemove: function() {
		if (this.fileList.length == 0) {
			this.selects[0].setStyle('display', 'inline');
			this.selects[1].setStyle('display', 'none');
			this.target = this.selects[0];
			this.reposition();
		}
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	start: function() {
		if (Browser.Platform.linux && window.confirm(MooTools.lang.get('FancyUpload', 'linuxWarning'))) return this;
		return this.parent();
	}
	
});

Moo.Request.Upload.Fancy.Attach.File = new Class({

	Extends: Moo.Swiff.Uploader.File,

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	render: function() {
		if (this.invalid) {
			if (this.validationError) {
				var msg = MooTools.lang.get('FancyUpload', 'validationErrors')[this.validationError] || this.validationError;
				this.validationErrorMessage = msg.substitute({
					name: this.name,
					size: Moo.Swiff.Uploader.formatUnit(this.size, 'b'),
					fileSizeMin: Moo.Swiff.Uploader.formatUnit(this.base.options.fileSizeMin || 0, 'b'),
					fileSizeMax: Moo.Swiff.Uploader.formatUnit(this.base.options.fileSizeMax || 0, 'b'),
					fileListMax: this.base.options.fileListMax || 0,
					fileListSizeMax: Moo.Swiff.Uploader.formatUnit(this.base.options.fileListSizeMax || 0, 'b')
				});
			}
			this.remove();
			return;
		}
		
		this.addEvents({
			'open': this.onOpen,
			'remove': this.onRemove,
			'requeue': this.onRequeue,
			'progress': this.onProgress,
			'stop': this.onStop,
			'complete': this.onComplete,
			'error': this.onError
		});
		
		this.ui = {};
		
		this.ui.element = new Element('li', {'class': 'file', id: 'file-' + this.id});
		this.ui.title = new Element('span', {'class': 'file-title', text: this.name});
		this.ui.size = new Element('span', {'class': 'file-size', text: Moo.Swiff.Uploader.formatUnit(this.size, 'b')});
		
		this.ui.cancel = new Element('a', {'class': 'file-cancel', text: 'Cancel', href: '#'});
		this.ui.cancel.addEvent('click', function() {
			this.remove();
			return false;
		}.bind(this));
		
		this.ui.element.adopt(
			this.ui.title,
			this.ui.size,
			this.ui.cancel
		).inject(this.base.list).highlight();
		
		var progress = new Element('img', {'class': 'file-progress', src: '../../assets/progress-bar/bar.gif'}).inject(this.ui.size, 'after');
		this.ui.progress = new Moo.UI.Progress(progress, {
			fit: true
		}).set(0);
					
		this.base.reposition();

		return this.parent();
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onOpen: function() {
		this.ui.element.addClass('file-uploading');
		if (this.ui.progress) this.ui.progress.set(0);
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onRemove: function() {
		this.ui = this.ui.element.destroy();
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onProgress: function() {
		if (this.ui.progress) this.ui.progress.start(this.progress.percentLoaded);
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onStop: function() {
		this.remove();
	},
	
	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	onComplete: function() {
		this.ui.element.removeClass('file-uploading');

		if (this.response.error) {
			var msg = MooTools.lang.get('FancyUpload', 'errors')[this.response.error] || '{error} #{code}';
			this.errorMessage = msg.substitute($extend({name: this.name}, this.response));
			
			this.base.fireEvent('fileError', [this, this.response, this.errorMessage]);
			this.fireEvent('error', [this, this.response, this.errorMessage]);
			return;
		}
		
		if (this.ui.progress) this.ui.progress = this.ui.progress.cancel().element.destroy();
		this.ui.cancel = this.ui.cancel.destroy();
		
		var response = this.response.text || '';
		this.base.fireEvent('fileSuccess', [this, response]);
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onError: function() {
		this.ui.element.addClass('file-failed');		
	}

});

// avoiding MooTools.lang dependency

(function() {
	var phrases = {
		'fileName': '{name}',
		'cancel': 'Cancel',
		'cancelTitle': 'Click to cancel and remove this entry.',
		'validationErrors': {
			'duplicate': 'File <em>{name}</em> is already added, duplicates are not allowed.',
			'sizeLimitMin': 'File <em>{name}</em> (<em>{size}</em>) is too small, the minimal file size is {fileSizeMin}.',
			'sizeLimitMax': 'File <em>{name}</em> (<em>{size}</em>) is too big, the maximal file size is <em>{fileSizeMax}</em>.',
			'fileListMax': 'File <em>{name}</em> could not be added, amount of <em>{fileListMax} files</em> exceeded.',
			'fileListSizeMax': 'File <em>{name}</em> (<em>{size}</em>) is too big, overall filesize of <em>{fileListSizeMax}</em> exceeded.'
		},
		'errors': {
			'httpStatus': 'Server returned HTTP-Status #{code}',
			'securityError': 'Security error occured ({text})',
			'ioError': 'Error caused a send or load operation to fail ({text})'
		},
		'linuxWarning': 'Warning: Due to a misbehaviour of Adobe Flash Player on Linux,\nthe browser will probably freeze during the upload process.\nDo you want to start the upload anyway?'
	};
	
	if (MooTools.lang) {
		MooTools.lang.set('en-US', 'FancyUpload', phrases);
	} else {
		MooTools.lang = {
			get: function(from, key) {
				return phrases[key];
			}
		};
	}
})();// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĹ˝dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Request.Upload.File
 *	<div class="file-upload">
 *		<div class="button">Choisir</div>
 *		<div class="status">Aucun fichier sĂ©lectionnĂ©</div>
 *		<div class="clear"></div>
 *		<div class="progress">
 *			<span class="track"><img src="images/progress-track.png" /></span>
 *			<span class="value">33%</span>
 *		</div>
 *	</div>	
 * @version 1.1
 * @license MIT License
 * @author Harald Kirschner <mail [at] digitarald [dot] de>
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Authors
 * @since 2.0.0
 * @version 2.0.0  
 */
Moo.Request.Upload.File = new Class
({
	/**
	 * @implements Events, Options
	 */
	Implements: [Events, Options],
	
	/**
	 * @var object The options.
	 */
	options: {
		onBegin: $empty,
		onProgress: $empty,
		onComplete: $empty,
		onError: $empty,
		path: 'swf/Moo.Swiff.Uploader.swf'			
	},

	/**
	 * @var string The url to post to.
	 */
	url: null,

	/**
	 * @var object The uplaoder.
	 */
	uploader: null,
	
	/**
	 * @var object The progress bar controller.
	 */
	progress: null,
	
	/**
	 * @var object The differents elements.
	 */
	buttonElement: null,
	statusElement: null,
	progressElement: null,
	
	/**
	 * Initialize the uploader
	 * @param object The root file upload element.
	 * @param string The url to post to.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	initialize: function(element, url, options) {
		this.setOptions(options);
		this.buttonElement = element.getElement('.button');
		this.statusElement = element.getElement('.status');
		this.progressElement = element.getElement('.progress');
		this.progress = new Moo.UI.Progress(this.progressElement.getElement('.track img'), {
			text: element.getElement('.progress .value')
		});
		this.progressElement.fade('hide');
		// initialize the swiff uploader	
		this.uploader = new Moo.Swiff.Uploader({
			path: 'swf/Moo.Swiff.Uploader.swf',
			url: url,
			queued: false,
			multiple: false,
			target: this.buttonElement,
			instantStart: true,
			fileSizeMax: 1000 * 1024 * 1024,
			onSelectSuccess: this.onBegin.bind(this),
			onFileComplete: this.onComplete.bind(this),
			onQueue: this.onProgress.bind(this)
		});
		return this;
	},
	
	/**
	 * This event happens when a file start to upload.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	onBegin: function() {
		this.buttonElement.fade(0);
		this.statusElement.fade(0);
		this.progressElement.fade(1);
		this.fireEvent('begin');
	},
	
	/**
	 * This event happens when the progress bar get updated.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */		
	onProgress: function() {
		if (this.uploader.uploading) {
			this.progress.set(this.uploader.percentLoaded);
		}
	},
	
	/**
	 * This event happens when the progress bar get updated.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	onComplete: function(file) {
		
		if (file.response.error) {
			this.statusElement.set('text', 'Erreur');
			this.fireEvent('error', file.response.error);
			return;
		}

		var file = JSON.decode(file.response.text);
		if (file) {
			this.statusElement.set('text', file.name);
		}
		
		this.progressElement.fade(0);				
		this.buttonElement.fade(1);
		this.statusElement.fade(1);
	}	
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

Moo.UI = {};
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.UI.Waiter
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.UI.Waiter = new Class
({
	/**
	 * @implements Events Options The basic element methods.
	 */
	Implements: [Events, Options],
	
	/**
	 * @var object The element injected.
	 */
	element: null,
	
	/**
	 * @var string The path to the loading image.
	 */
	image: null,
		
	/**
	 * @var object The options.
	 */
	options: {
		onShow: $empty,
		onHide: $empty,
		parent: null,
		inject: 'after',
		classname: '',
		offset: {
			x: 0, 
			y: 0
		}
	},
	
	/**
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	initialize: function(image, options) {
		this.setOptions(options);
		this.image = image;
	},
	
	/**
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	show: function(animation) {
		this.createElement();
		this.injectElement();
	},
	
	/**
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	hide: function(animation) {
		if (this.element) {
			this.disposeElement();
		}
	},
	
	/** Protected **/
	
	/**
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	createElement: function() {
		this.element = new Element('img', {'src': this.image});
		this.element.addClass(this.options.classname);
		this.element.setStyle('position', 'absolute');
		if (this.options.offset.x) this.image.setStyle('margin-left', this.options.offset.x);
		if (this.options.offset.y) this.image.setStyle('margin-top', this.options.offset.y);
	},
	
	/**
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	injectElement: function() {
		var receiver = this.options.parent ? this.options.parent : document;
		this.element.inject(receiver, this.options.inject);
	},
	
	/**
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	disposeElement: function() {
		this.element.dispose();
		this.element = null;
	}	
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.UI.Dock. 
 * This class represents a dock where menus can be attached at runtime. To use 
 * this dock you must have the html on your site since the base look and feel 
 * can change. However, you must have an element with the class dock-items.
 * The html should look like this.
 *	<div id="dock">
 *		<div class="wrap">
 *			<div class="dock-title">Lemieux BĂ©dard</div>
 *			<div class="dock-items">
 *				<ul>
 *					<li class="dock-item">
 *						<div class="dock-item-title">Interface</div>
 *						<div class="dock-item-child">
 *							<ol>
 *								<li>Item 1</li>
 *								<li>Item 2</li>
 *								<li>Item 3</li>	
 *							</ol>
 *						</div>
 *					</li>
 *					<li class="dock-item">
 *						<div class="dock-item-title">PrĂ©fĂ©rences</div>
 *						<div class="dock-item-child">
 *							<ol>
 *								<li>Item 1</li>
 *								<li>Item 2</li>
 *								<li>Item 3</li>	
 *								<li>Item 4</li>
 *							</ol>
 *						</div>
 *					</li>
 *				</ul>
 *			</div>
 *		</div>
 *	</div>
 * @package core
 * @subpackage selector
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.UI.Dock = new Class
({
	/**
	 * @implements Events Options The basic element methods.
	 */
	Implements: [Events, Options, Moo.Animable],

	/**
	 * @var object The root element of the dock
	 */
	element: null,
	
	/**
	 * @var object The element containing the title.
	 */
	dockTitleElement: null,
	
	/**
	 * @var object The element containing the childs element.
	 */
	dockItemsElement: null,
	
	/**
	 * @var int The height of the dock.
	 */
	dockHeight: 0,
	
	/**
	 * @var bool Whether or not the dock is expanding.
	 */
	dockResizing: false,
	
	/**
	 * @var object The dock item that's currently expanded.
	 */
	expandedDockItem: null,
	
	/**
	 * @var object The options.
	 */	
	options: {
		dockItemWillCreate: $empty,
		dockItemDidCreate: $empty,
		dockItemWillRemove: $empty,
		dockItemDidRemove: $empty,
		dockItemWillShow: $empty,
		dockItemDidShow: $empty,
		dockItemWillHide: $empty,
		dockItemDidHide: $empty
	},
		
	/**
	 * Initialize the dock.
	 * @param object The root dock element.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	initialize: function(dock, options) {
		this.setOptions(options);
		this.element = $(dock);
		this.dockTitleElement = this.element.getElement('.dock-title');
		this.dockItemsElement = this.element.getElement('.dock-items');
		this.dockHeight = this.dockItemsElement.getSize().y;
		this.initializeDockItems();
		return this;
	},

	/**
	 * Initialize all the dock items.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */					
	initializeDockItems: function() {
		var dockItems = this.element.getElements('.dock-items > ul > li');
		if (dockItems) {
			dockItems.each(function(item) {
				var dockItemChild = item.getElement('.dock-item-child');
				if (dockItemChild) dockItemChild.fade('hide');
				item.addEvent('click', function(e) {
					this.toggleDockItem(dockItemChild);
				}.bind(this));
			}, this);
		}
	},
	
	/**
	 * Create a new doc item. 
	 * @param string The dock item id.
	 * @param string The dock item title.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	createDockItem: function(id, title) {
		
		// create the title element
		var dockItemTitle = new Element('div');
		dockItemTitle.addClass('dock-item-title');
		
		// create the child container element
		var dockItemChild = new Element('div');
		dockItemChild.addClass('dock-item-child');

		// create the dock item element
		var dockItem = new Element('li');
		dockItem.setId(id);
		dockItem.addClass('dock-item');
		dockItem.getChildCount = function() { return this.retrieve('dock-item-child-count', 0); };
		dockItem.incrementChildCount = function() { this.store('dock-item-child-count', this.retrieve('dock-item-child-count', 0) + 1); };
		dockItem.decrementChildCount = function() { this.store('dock-item-child-count', this.retrieve('dock-item-child-count', 0) - 1); };
		dockItem.grab(dockItemTitle);
		dockItem.grab(dockItemChild);
						
		var ul = this.dockItemsElement.getElement('ul');
		if (ul == null) {
			ul = new Element('ul');
			ul.inject(this.dockItemsElement);
		}
		
		this.fireEvent('dockItemWillCreate', [dockItem]);
		ul.grab(dockItem);		
		this.fireEvent('dockItemDidCreate', [dockItem]);
		
		// create an empty list of items
		dockItemChild.grab(new Element('ol'));
	},
	
	/**
	 * Remove a given dock item from the list. 
	 * @param string The dock item id.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */		
	removeDockItem: function(id) {
		var dockItem = $(id);
		if (dockItem) {
			this.fireEvent('dockItemWillRemove', [dockItem]);
			dockItem.dispose();
			this.fireEvent('dockItemDidRemove', [dockItem]);
		}
	},

	/**
	 * Show a dock item.
	 * @param string The dock item id.
	 * @param object The possible animation.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	showDockItem: function(id, animation) {
		var dockItem = $(id);
		if (dockItem) {
			this.animate(dockItem, animation, function() {
				// the default behavior
				dockItem.show();
			});			
		}
	},

	/**
	 * hide a dock item.
	 * @param string The dock item id.
	 * @param object The possible animation.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	hideDockItem: function(id, animation) {
		var dockItem = $(id);
		if (dockItem) {
			this.animate(dockItem, animation, function() {
				// the default behavior
				dockItem.hide();
			});	
		}
	},

	/**
	 * Either show or hide a sub menu.
	 * @param object The parent of the submenu to show or hide.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */					
	toggleDockItem: function(item) {
		if (this.dockResizing == false) {
			var expanded = item.retrieve('expanded', false);			
			if (expanded == false) {
				if (this.expandedDockItem) {
					// hide the previously opened sub menu. This will also
					// reset the expandedDockItem variable to a null value
					this.shortenDockItem(this.expandedDockItem);
				} 
				this.expandDockItem(item);
				this.expandDock(item);
			} else {	
				this.shortenDockItem(item);
				this.shortenDock(item);
			}
		}
	},
	
	/**
	 * Open a sub menu.
	 * @param object The sub menu to show.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	expandDockItem: function(item) {
		item.store('expanded', true);
		item.fade(1);
		this.expandedDockItem = item;
	},	

	/**
	 * Hide a sub menu.
	 * @param object The sub menu to hide.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */				
	shortenDockItem: function(item) {
		item.store('expanded', false);
		item.fade(0);
		this.expandedDockItem = null;
	},

	/**
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */			
	expandDock: function(sm) {
		this.dockResizing = true;
		var curr = this.footerHeight;
		var next = this.expandedDockItem.getSize().y + 40; // <-- This is an unknown constant
		new Fx.Tween(this.dockItemsElement, {duration:'short'}).start('height', curr, next).chain(function() {
			this.dockHeight = next;
			this.dockResizing = false;
		}.bind(this));	
	},
	
	/**
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */			
	shortenDock: function(sm) {
		this.dockResizing = true;
		var curr = this.footerHeight;
		var next = 35; // once again an unknown constant
		new Fx.Tween(this.dockItemsElement, {duration:'short'}).start('height', curr, next).chain(function() {
			this.dockHeight = next;
			this.dockResizing = false;
		}.bind(this));
	},
	
	addElementToDockItem: function(menuId, element) {
	
	}
});	// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĹ˝dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.UI.Progress
 * @version 1.1
 * @license MIT License
 * @author Harald Kirschner <mail [at] digitarald [dot] de>
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Authors
 * @since 2.0.0
 * @version 2.0.0  
 */
Moo.UI.Progress = new Class
({
	/**
	 * @extends Fx
	 */
	Extends: Fx,

	/**
	 * @var object The options.
	 */
	options: {
		text: null,
		url: null,
		transition: Fx.Transitions.Circ.easeOut,
		fit: true,
		link: 'cancel'
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	initialize: function(element, options) {
		this.element = $(element);
		this.parent(options);
				
		var url = this.options.url;
		if (url) {

			this.element.setStyles({
				'background-image': 'url(' + url + ')',
				'background-repeat': 'no-repeat'
			});
		}
		
		if (this.options.fit) {
			url = url || this.element.getStyle('background-image').replace(/^url\(["']?|["']?\)$/g, '');
			if (url) {
				var fill = new Image();
				fill.onload = function() {
					this.fill = fill.width;
					fill = fill.onload = null;
					this.set(this.now || 0);
				}.bind(this);
				fill.src = url;
				if (!this.fill && fill.width) fill.onload();
			}
		} else {
			this.set(0);
		}
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	start: function(to, total) {
		return this.parent(this.now, (arguments.length == 1) ? to.limit(0, 100) : to / total * 100);
	},

	/**
	 * @author Harald Kirschner <mail [at] digitarald [dot] de>
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	set: function(to) {
		this.now = to;
		var css = (this.fill)
			? (((this.fill / -2) + (to / 100) * (this.element.width || 1) || 0).round() + 'px')
			: ((100 - to) + '%');
		
		this.element.setStyle('backgroundPosition', css + ' 0px').title = Math.round(to) + '%';
		
		var text = $(this.options.text);
		if (text) text.set('text', Math.round(to) + '%');
		
		return this;
	}

});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.UI.Form
 * This class handles a form with ajax only. onError and onSuccess events are
 * only available when the json request is selected because it's just not possible
 * to really detect errors otherwise.
 * @package UI.Form
 * @author Jean-Philippe Dery
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.UI.Form = new Class
({
	/**
	 * @implements Events, Options.
	 */
	Implements: [Events, Options],
	
	/**
	 * @var object The options.
	 */
	options: {
		onSubmit: $empty,
		onComplete: $empty,
		onError: $empty,
		onSuccess: $empty,
		onRedirect: $empty,
		request: 'json',
		errorHandling: true,
		errorHandler: null,
		animationOnShowError: null,
		animationOnHideError: null
	},
	
	/**
	 * @var object The form element.
	 */
	element: null,
	
	/**
	 * @var object The request handler.
	 */
	request: null,
	
	/**
	 * Initialize the form.
	 * @param object The form.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	initialize: function(element, options) {
		this.element = $(element);
		this.setOptions(options);
		
		var requestHandlerOptions = {
			'onSubmit': this.onFormSubmit.bind(this),
			'onReceive': this.onFormReceive.bind(this)
		};
		
		switch (this.options.request) {
			case 'json': this.request = new Moo.Request.JSONForm(this.element, requestHandlerOptions); break;
			case 'html': this.request = new Moo.Request.HTMLForm(this.element, requestHandlerOptions); break;
		}

		// look whether we have to use a custom error handling 
		// class instead of the default one		
		if (this.options.errorHandling) {
			this.options.errorHandler = this.options.errorHandler
				? this.options.errorHandler
				: new Moo.UI.Form.ErrorHandler(this, {
					'animationOnShowError': this.options.animationOnShowError,
					'animationOnHideError': this.options.animationOnHideError
				});
		}
		
		return this;
	},

	/**
	 * Return a field from the form using the name.
	 * @param string The field name.
	 * @return object The field.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	getFieldByName: function(name) {
		return this.element.getElement('*[name="' + name + '"]');
	},
	
	/**
	 * Return a field from the form using the id.
	 * @param string The field id.
	 * @return object The field.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	getFieldById: function(id) {
		return this.element.getElement('*[id="' + id + '"]');
	},

	/**
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	onFormSubmit: function() {
		this.fireEvent('submit');
	},
	
	/**
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */	
	onFormReceive: function(response) {
		this.fireEvent('complete', [response]);
		// it's only possible to handle errors when the request is a json type
		// otherwise there is no perfect way to know if an error was triggered
		if (this.options.request == 'json') {
			switch (response.status) {
				// handles a simple success event where the data is
				// retransmited to the success event
				case 'success':
					this.fireEvent('success', [response.message, response.code]);
					break;
				// handle the error by possibly managing the error display and 
				// transmiting the error data to the error event 
				case 'error':
					if (this.options.errorHandling) {
						this.options.errorHandler.handleErrors(response.message);
					}
					this.fireEvent('error', [response.message, response.code]);
					break;
				// handle a redirect response by simply redirecting
				case 'redirect':
					window.location = response.message;
					break;
				// handle an unknown status. In this case we simply trigger a new event
				// based on the name of the status and fire it
				default:
					this.fireEvent(response.status, [response.message, response.code, response.status]);
					break;
			}
		}
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 2.0.0
 * @version 2.0.0
 */
Moo.UI.FormActor = new Class
({
	/**
	 * @extends Moo.Actor
	 */
	Extends: Moo.Actor,
	
	/**
	 * 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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	applyOn: function(element) {
		this.element = new Moo.UI.Form(element, this.options);		
	}
});
// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

Moo.UI.Form.ErrorHandler = new Class
({
	/**
	 * @implements Events, Options
	 */
	Implements: [Events, Options, Moo.Animable],
	
	/**
	 * @var object The options.
	 */
	 options: {
	 	injectInputError: 'after',
	 	injectFormError: 'top',
	 	animationOnShowError: null,
	 	animationOnHideError: null
	 },
	
	/**
	 * @var object The form object which handles the form element.
	 */
	form: null,
	
	/**
	 * @var array The errors.
	 */
	errors: null,
	
	/**
	 * Initialize the form error handler.
	 * @param object The form.
	 * @param object The options.
	 * @return object A reference to this object.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0
	 */
	initialize: function(form, options) {
		this.setOptions(options);
		this.form = form;
		this.errors = [];
	},
				
	/**
	 * Handle the errors when they are received. This will clear the current
	 * errors and inject the new errors.
	 * @param array The errors.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0	 
	 */
	handleErrors: function(incidents) {
		this.removeErrors();
		incidents.each(function(incident) {
			var error = incident.error;
			var field = incident.field[0];
			var input = this.form.getFieldByName(field);
			this.addError(error, input);
		}.bind(this));
	},

	/**
	 * Remove all the errors from the form.
	 * @param array The errors.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0	 
	 */		
	removeErrors: function() {
		for (var i = 0; i < this.errors.length; i++) {
			this.errors[i].dispose();
			this.errors[i] = null;	
		}
		this.errors = this.errors.clean();
	},
	
	/**
	 * Handle the errors when they are received.
	 * @param array The errors.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0	 
	 */	
	addError: function(message, input) {
		if (input)	this.addErrorToInput(message, input);
		else 		this.addErrorToForm(message);
	},
	
	/**
	 * Add an error next to an input.
	 * @param object The error message.
	 * @param object The associated input.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0	 
	 */		
	addErrorToInput: function(message, input) {
		var div1 = new Element('div', {'class': 'errors'});
		var div2 = new Element('div', {'class': 'error', 'html': message});
		div1.grab(div2);
		div1.hide();
		div1.inject(input, this.options.injectInputError);
		this.errors.push(div1);
		this.animate(div1, this.options.animationOnShowError, 
			function() {
				div1.show();
			}
		);
	},
	
	/**
	 * Add an error inside the form.
	 * @param object The error message.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0	 
	 */	
	addErrorToForm: function(message) {
		var div1 = this.form.element.getElement('div.errors');
		if (div1 == null) {
			var div1 = new Element('div', {'class': 'errors'});
			var div2 = new Element('div', {'class': 'error', 'html': message});
			div1.grab(div2);
			div1.hide();
			div1.inject(this.form.element, this.options.injectFormError);			
			this.errors.push(div1);
			this.animate(div1, this.options.animationOnShowError, 
				function() {
					div1.show();
				}
			);
		} else {
			var div2 = new Element('div', {'class': 'error', 'html': message});
			div1.grab(div2);
			div2.hide();
			this.animate(div2, this.options.animationOnShowError, 
				function() {
					div2.show();
				}
			);			
		}
	},
	
	/**
	 * Find whether or not an input has an error displayed.
	 * @param string The error message.
	 * @param object The input element.
	 * @return bool True if the input has the error.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 2.0.0	 
	 */		
	hasError: function(message, input) {
		var element = null;
		if (element == null) element = input.getNext('div.errors div.error:contains("' + error + '")');
		if (element == null) element = input.getPrevious('div.errors div.error:contains("' + error + '")');
		return element == null;
	}
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

Moo.UI.Navigation = {};// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.UI.Navigation.Tab
 * This class handles a navigation tab.
 * @package core
 * @subpackage actor
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 1.0.0
 * @version 2.0.0
 */
Moo.UI.Navigation.Tab = new Class
({
	/**
	 * @implements Events Options.
	 */
	Implements: [Events, Options],

	/**
	 * @var object The options.
	 */
	options : {
		onEmphasize : $empty,
		onNormalize : $empty,
		cssOnEmphasizeCurrent: 'cur',
		cssOnEmphasizeAlternate: 'alt'
	},
	
	/**
	 * @var object The root tab eleement.
	 */
	element: null,

	/**
	 * 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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	initialize: function(element, options) {
		this.setOptions(options);
		this.element = $(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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onEmphasize: function() {
		if (this.isCurrent() == false) {
			this.element.addClass(this.options.cssOnEmphasizeAlternate);
			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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	onNormalize: function() {
		if (this.isCurrent() == false) {
			this.element.removeClass(this.options.cssOnEmphasizeAlternate);
			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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	isCurrent : function() {
		return this.element.hasClass(this.options.cssOnEmphasizeCurrent);
	}	
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | 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 (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 1.0.0
 * @version 2.0.0
 */
Moo.UI.Navigation.TabActor = new Class
({
	/**
	 * @extends Moo.Actor
	 */
	Extends: Moo.Actor,
		
	/**
	 * 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 (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	applyOn: function(element) {
		this.element = new Moo.UI.Navigation.Tab(element, this.options);
	}	
});// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

Moo.Filter = {};// +---------------------------------------------------------------------------+
// | This file is part of the Mootools Addon project.                          |
// | Copyright (C) Lemieux BĂ©dard Communication                                |
// |                                                                           |
// | For the full copyright and license information, please view the LICENSE   |
// | file that was distributed with this source code.                          |
// +---------------------------------------------------------------------------+

/**
 * Moo.Filter.Text
 * This class allow a user to perform on the fly filter based on the text 
 * content of a web page.
 * @package core
 * @subpackage filter
 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
 * @copyright Lemieux Bedard Communication (francois.patry@lemieuxbedard.com)
 * @since 2.0.0
 * @version 1.0.0
 */
Moo.Filter.Text = new Class
({
	/**
	 * @implements Events, Options.
	 */
	Implements: [Events, Options],
	
	/**
	 * @var object The options.
	 */
	options: {
		onBegin: $empty,
		onFinish: $empty,
		onMatch: $empty,
		onMismatch: $empty,
		onReset: $empty,
		cssOnParentMatch: 'filter-parent-match',
		cssOnElementMatch: 'filter-element-match',
		emphasize: true
	},
	
	/**
	 * @var array The array to search for paths.
	 */
	paths: null,

	/**
	 * Initialize the text filter.
	 * @param object The root element of each searchable items.
	 * @param object The text input element used to filter.
	 * @param object The options.
	 * @author Jean-Philippe DĂ©ry (jean-philippe.dery@lemieuxbedard.com)
 	 * @since 2.0.0
	 */
	initialize: function(root, paths, options) {
		this.setOptions(options);
		this.root = root;
		this.paths = paths;
	},
	
	/**
	 * Add a path to seach.
	 * @param string The path to search.
	 * @return void
	 * @author Jean-Philippe DĂ©ry (jean-philippe.dery@lemieuxbedard.com)
 	 * @since 2.0.0
	 */
	addPath: function(path) {
		this.path.push(path);
	},
	
	/**
	 * Execute the filter and emphasize the element that were founds.
	 * @param string The path to search.
	 * @return void
	 * @author Jean-Philippe DĂ©ry (jean-philippe.dery@lemieuxbedard.com)
 	 * @since 2.0.0
	 */	
	filter: function(str) {
		this.clear();		
		if (str.length > 0) {
			str = str.escapeRegExp();
			var regex = new RegExp(str, 'i');
			var roots = $$(this.root);
			this.fireEvent('begin', regex);
			roots.each(function(root) {
				var found = false;
				// starts by looking at each root element and find whether or not
				// the node contains the searched text
				this.paths.each(function(path) {
					var nodes = root.getElements(path);
					if (nodes) {
						nodes.each(function(node) {
							var text = node.get('text');
							if (text.match(regex)) {
								found = true;
								// empahsize each parts of the text that matched the regular
								// expression if this option is selected.
								if (this.options.emphasize) this.emphasize(node, regex);
							}
						}, this);	
					}
				}, this);
				// finally we look whether or not the parent node contains the text 
				// we are looking for. In this case we apply a special css class and 
				// trigger an event
				if (found) {
					root.addClass(this.options.cssOnParentMatch);
					this.fireEvent('match', [root]);
				} else {
					if (root.hasClass(this.options.cssOnParentMatch)) {
						root.removeClass(this.options.cssOnParentMatch);
					}
					this.fireEvent('mismatch', [root]);
				}
			}, this);
			this.fireEvent('finish');
		} else {
			var roots = $$(this.root);
			roots.each(function(root) {
				this.fireEvent('reset', [root]);
			}, this);
		}
	},

	/**
	 * Emphasize the text of a node which match a given regular expression.
	 * @param object The first node.
	 * @param object The regular expression.
	 * @return void.
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	emphasize: function(node, regex) {
		if (node.nodeType === 3) {
			var match = node.data.match(regex);
			if (match) {
				var textnode = node.splitText(match.index);
				textnode.splitText(match[0].length);
				var cloneTextnode = textnode.cloneNode(true);
				var span = new Element('span');
				span.addClass(this.options.cssOnElementMatch);
				span.appendChild(cloneTextnode);
				textnode.parentNode.replaceChild(span, textnode);
				return 1;
			}
		} else {
			if (node.nodeType === 1 && node.childNodes) {
				if ((node.tagName === 'SPAN' && node.className === this.options.cssOnElementMatch) == false) {
					for (var i = 0; i < node.childNodes.length; i++) {
						i += this.emphasize(node.childNodes[i],regex);
					}
				}
			}
		}
		return 0;
	},
	
	/**
	 * Clear all the matches that were found inside the paths.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */
	clear: function() {
		var klass = this.root + '.' + this.options.cssOnParentMatch;
		var roots = $$(klass);
		roots.each(function(root) {
			this.normalize(root);
		}, this);
	},
	
	/**
	 * Remove all the highlights on the searchable pats inside a root
	 * element. This will also rebuild the text node tree.
	 * @return void
	 * @author Jean-Philippe Dery (jean-philippe.dery@lemieuxbedard.com)
	 * @since 1.0.0
	 */	
	normalize: function(root) {
		this.paths.each(function(path) {
			var nodes = root.getElements(path);
			if (nodes) {
				nodes.each(function(node) {
					var klass = '.' + this.options.cssOnElementMatch;
					var highlights = node.getElements(klass);
					if (highlights) {
						highlights.each(function(highlight) {
							highlight.appendText(highlight.get('html'), 'after').destroy(); 
						}, this);					
					}
					// rebuild the textnode tree otherwise we won't be table to search
					// for string like admin and then administrator
					node.set('html', node.get('html'));
				}, this);
			}
		}, this);
	}
});


