/**
 * Interface dropdown list. Simple widget used to replace default select elements.
 *
 * @package classes
 * @copyright Copyright 2009, Lastbyte VOF
 * @version 1.0
 */

var Dropdown = new Class(
{

	Implements: [Options, Events],

	options:
	{
		name: null,
		container: null,
		replace: null,
		multiple: false,
		multipleSelectMessage: '{count} selectie(s)',
		className: 'CustomList',
		useMooScroller: false,
		onChange: $empty,
		onShow: $empty,
		onHide: $empty
	},

	/**
	 * Constructor for this class.
	 *
	 * @param element element
	 * @param object options
	 * @return Interface.List
	 */
	initialize: function (options)
	{
		this.setOptions(options);
		if (this.options.replace)
			this.originalElement = $(this.options.replace);
		if (!this.originalElement || this.originalElement.get('tag') != 'select')
			this.originalElement = null;
			
		Dropdown.instances.push(this);

		this.items = [];
		this.values = [];
		this.currentSelection = this.options.multiple ? [] : -1;
		this.isShown = false;

		this.wrapper = null;
		this.selectedPanel = null;
		this.list = null;
		this.formField = null;
		
		this.build();
		return this;
	},
	
	getName: function ()
	{
		return this.options.name;
	},

	/**
	 * Builds the HTML.
	 *
	 * @return Interface.List
	 */
	build: function ()
	{
		// If there was a replaced element, disable its view
		if (this.originalElement)
			this.originalElement.setStyle('display', 'none');

		// Create element
		this.wrapper = new Element('span', {styles: {position: 'relative'}}).addClass(this.options.className + 'Wrapper');
		this.list = new Element('span',
		{
			styles:
			{
				position: 'absolute',
				visibility: 'hidden'
			}
		}).addClass(this.options.className + 'DropDown').setStyle('opacity', 0).inject(this.wrapper);
		this.selectedPanel = new Element('span').addClass(this.options.className + 'Selection').inject(this.wrapper);
		this.selectedPanel.addEvent('click', this.toggle.bind(this));
		
		// Wrapper for items in the dropdown
		this.itemWrapper = new Element('span').addClass(this.options.className + 'ItemWrapper');
		this.itemWrapper.inject(this.list);
		
		// Add MooScroller if so specified and available
		if (this.options.useMooScroller && window.MooScroller)
		{
			// Create scroll area 
			var area = new Element('div').addClass('scrollarea').inject(this.list);
			
			// Scroll up button
			var scrollBack = new Element('div').addClass('scrollBack').inject(area);
			
			// Create scroll-bar container
			var scrollContainer = new Element('div').addClass('scrollBarContainer').inject(area);
			
			// Create scroll knob
			var scrollKnob = new Element('div').addClass('scrollKnob').inject(scrollContainer);
			
			// Scroll down button
			var scrollForward = new Element('div').addClass('scrollForward').inject(area);
			
			// Initiate MooScroller
			this.scroller = new MooScroller(this.itemWrapper, scrollKnob,
			{
				scrollLinks:
				{
					forward: scrollForward,
					back: scrollBack
				},
				hideWhenNoOverflow: false
			});
		}

		// Create hidden form field to contain the selected value
		if (this.options.name)
			this.formField = new Element('input', {type: 'hidden', name: this.options.name}).inject(this.wrapper);
		else if (this.options.replace)
		{
			this.formField = new Element('input', {type: 'hidden', name: this.originalElement.get('name')}).inject(this.wrapper);
			this.options.name = this.originalElement.get('name');
		}

		// If a container was specified, inject the element
		if (this.options.container)
			this.wrapper.inject($(this.options.container));
		// Otherwise, replace the original element and automatically add options
		else if (this.originalElement)
		{
			this.originalElement.getElements('option').each(function (option, index)
			{
				this.addItem(option.get('text'), option.get('value'));
				if (option.get('selected'))
				{
					if (this.options.multiple)
					{
						this.currentSelection = this.currentSelection.combine([index]);
						this.itemWrapper.getElements('span.' + this.options.className + 'Item')[index].addClass('selected');
					}
					else
						this.currentSelection = index;
				}
			}.bind(this));
			this.wrapper.inject(this.originalElement, 'before');
			this.originalElement = this.originalElement.destroy();
		}
		
		// Update the form field
		if (this.formField)
			this.formField.set('value', this.getCurrentValue());
		
		// Check current selections
		if (this.options.multiple)
			this.selectedPanel.set('text', this.options.multipleSelectMessage.replace('{count}', this.currentSelection.length));

		// Bind event to body for custom focus/blur events
		window.addEvent('mousedown', function (event)
		{
			var match = $(event.target).getParent('.' + this.options.className + 'Wrapper');
			if (match != this.wrapper && this.isShown)
				this.blur();
		}.bind(this));
	},

	/**
	 * Returns the element.
	 *
	 * @return Element
	 */
	toElement: function ()
	{
		return this.wrapper;
	},

	/**
	 * Adds an item to the list, accepts an element or a string.
	 *
	 * @param mixed content
	 * @param mixed value
	 * @param bool selected
	 */
	addItem: function (content, value, selected)
	{
		var item = new Element('span').addClass(this.options.className + 'Item');
		if ($type(content) == 'element')
			item.adopt(content);
		else
			item.set('html', content);

		this.items.push(content);
		this.values.push(value);

		item.addEvent('click', this.select.pass(this.items.length - 1, this));
		item.inject(this.itemWrapper);
		
		if (selected)
			this.select(this.items.length - 1);		
		else if (this.items.length == 1 && !this.options.multiple)
			this.select(0);
		return this;
	},

	/**
	 * Toggles between an opened and closed list.
	 *
	 * @return Interface.List
	 */
	toggle: function ()
	{
		if (this.isShown)
			this.hide();
		else
			this.show();
		return this;
	},

	/**
	 * Select items. This method injects the selected element in the selectedPanel element if the content is of type element,
	 * if not, it sets the inner HTML of the selectedPanel.
	 *
	 * @param int index
	 * @return bool
	 */
	select: function (index)
	{
		if (!this.options.multiple && index == this.currentSelection)
		{
			this.hide();
			return false;
		}

		if (this.options.multiple)
		{
			if (this.currentSelection.indexOf(index) >= 0)
			{
				this.itemWrapper.getElements('span.' + this.options.className + 'Item')[index].removeClass('selected');
				this.currentSelection.splice(this.currentSelection.indexOf(index), 1);
			}
			else
			{
				this.currentSelection = this.currentSelection.combine([index]);
				this.itemWrapper.getElements('span.' + this.options.className + 'Item')[index].addClass('selected');
			}
			this.selectedPanel.set('text', this.options.multipleSelectMessage.replace('{count}', this.currentSelection.length));
		}
		else
		{
			this.currentSelection = index;

			if ($type(this.items[this.currentSelection]) == 'element')
			{
				this.selectedPanel.empty();
				this.selectedPanel.adopt(this.items[this.currentSelection]);
			}
			else
				this.selectedPanel.set('html', this.items[this.currentSelection]);
				
			if (this.itemWrapper.getElement('span.selected'))
				this.itemWrapper.getElement('span.selected').removeClass('selected');
			
			this.itemWrapper.getElements('span.' + this.options.className + 'Item')[index].addClass('selected');
			this.hide();
		}

		// Update the value for the form field
		if (this.formField)
			this.formField.set('value', this.getCurrentValue());

		this.fireEvent('change', index);
		return true;
	},

	/**
	 * Returns the value(s) of the currently selected item(s).
	 *
	 * @return mixed
	 */
	getCurrentValue: function ()
	{
		if (this.options.multiple)
		{
			var values = [];
			this.currentSelection.each(function (index)
			{
				values.push(this.values[index]);
			}.bind(this));
			return values;
		}
		else
			return this.values[this.currentSelection];
	},

	/**
	 * Shows the dropdown list.
	 *
	 * @return Interface.List
	 */
	show: function()
	{
		if (this.isShown)
			return false;
			
		this.wrapper.addClass('open');
			
		// Exception for Internet Explorer <= 7
		if (Browser.Engine.trident && Browser.Engine.version <= 5)
			new Fx.Tween(this.list, {property: 'opacity'}).set(1);
		else
			new Fx.Tween(this.list, {duration: 200, property: 'opacity', link: 'cancel'}).start(0, 1);
			
		if (this.scroller)
			this.scroller.update.delay(200, this.scroller);
		
		this.isShown = true;
		this.fireEvent('show');
		return this;
	},

	/**
	 * Hides the dropdown list.
	 *
	 * @return Interface.List
	 */
	hide: function ()
	{
		if (!this.isShown)
			return false;
			
		this.wrapper.removeClass('open');

		// Exception for Internet Explorer <= 7
		if (Browser.Engine.trident && Browser.Engine.version <= 5)
			new Fx.Tween(this.list, {property: 'opacity'}).set(0);
		else
			new Fx.Tween(this.list, {duration: 200, property: 'opacity', link: 'cancel'}).start(1, 0);
		
		this.isShown = false;
		this.fireEvent('hide');
		return this;
	},

	/**
	 * Fires the blur event.
	 */
	blur: function ()
	{
		this.fireEvent('blur');
		this.hide();
	}

});

$extend(Dropdown,
{

	instances: [],
	
	getInstance: function (name)
	{
		var result = false;
		Dropdown.instances.each(function (object)
		{
			if (object.getName() == name)
				result = object;
		});
		return result;
	}

});

