if (typeof MOTO === 'undefined') { var MOTO = {}; MOTO.modulesLoaded = []; }
if (typeof MOTO.StyledForm === 'undefined') { MOTO.StyledForm = {}; }

MOTO.StyledForm.Dropdown = new Class(function() {
	var SelectOption = function(display,value,selected,displayElement,optionElement) {
		this.display = display;
		this.value = value;
		this.selected = selected;
		this.displayElement = displayElement;
		this.optionElement = optionElement;
		this.disable = function() {
			this.displayElement.addClass('disabled');
		},
		this.enable = function() {
			this.displayElement.removeClass('disabled');
		}
		this.destroy = function() {
			this.displayElement = null;
			this.optionElement = null;
		}
	}
	
	return {
		Implements: [Events, Options],
		options: {
			activeClass: 'active',
			defaultValue: null,
			dropdownClass: 'dropdown',
			dropdownBackgroundClass: 'dropdownBg',
			selectionClass: 'selection',
			selectionDivClass: 'dropdownSelection',
			defaultSelectionClass: 'defaultSelection',
			menuClass: 'menu',
			listClass: 'list',
			optionsClass: 'options',
			optionTag: 'p',
			optionClass: 'option',
			transition: Fx.Transitions.Sine.easeOut,
			duration: 250,
			highlightStyle: 'backgroundColor',
			highlightState: '#8ECFF4',
			defaultState: '#F2F2F2',
			typeDelay: 500,
			mouseLeaveDelay: 500,
			initialValue: null,
			animate: true
		},
		input: null,
		dropdown: null,
		list: null,
		listHeight: null,
		listOptions: {},
		listEffect: null,
		selection: null,
		selectedOption: null,
		highlighted: null,
		typed: null,
		clearTimer: null,
		collapseInterval: null,
		events: {},
		initialize: function(el, options) {
			this.setOptions(options);
			this.defaultValue = this.options.defaultValue;
			var i;
			var select = $(el);
			function mouseenterDropdown() {
				$clear(this.collapseInterval);
			}
			function mouseleaveDropdown() {
				this.collapseInterval = this.collapse.delay(this.options.mouseLeaveDelay, this);
			}
			this.events.mouseenter = mouseenterDropdown.bind(this);
			this.events.mouseleave = mouseleaveDropdown.bind(this);
			var selectOptions = select.getElements('option');
			this.dropdown = new Element('div',{
				'class':this.options.dropdownClass
			});
			el.get('class').split(' ').each(function(clazz) {
				this.dropdown.addClass(clazz);
			},this);
			var id = select.get('id');
			if (id && id.trim() !== '') {
				select.set('id','');
				this.dropdown.set('id',id);
			}
			// create options list
			var menu = new Element('div',{'class':this.options.menuClass});
			this.list = new Element('div',{'class':this.options.listClass});
			var optionsDiv = new Element('div',{'class':this.options.optionsClass});
			var listOptions = this.listOptions;
			var option, display, value, selected, selectedOption, optionElement, selectOption, optionClass;
			for (i = 0; i < selectOptions.length; i = i + 1) {
				option = selectOptions[i];
				optionClass = option.get('class') + ' ' + this.options.optionClass;
				optionClass = optionClass.trim();
				display = option.get('html');
				value = option.getProperty('value');
				optionElement = new Element(this.options.optionTag,{
					'class':optionClass,
					'html':display,
					'events': {
						'mouseup': this.listOptionClicked.bind(this),
						'click': function(e) { new Event(e).stop() },
						'mousedown': function(e) { new Event(e).stop() },
						'mouseenter': this.mouseenter.bind(this)
					}
				});
				selectOption = new SelectOption(display,value,selected,optionElement,option);
				optionElement.store('optionData',selectOption);
				selected = option.getProperty('selected');
				if (selected || (this.options.initialValue && this.options.initialValue === selectOption.value)) {
					this.selectedOption = selectOption;
					this.defaultValue = option.get('value');
				}
				listOptions[value] = selectOption;
				optionElement.inject(optionsDiv);
			}
			if (!this.selectedOption) {
				this.defaultValue = selectOptions[0].get('value');
				this.selectedOption = this.listOptions[this.defaultValue];
			}
			optionsDiv.inject(this.list);
			this.list.inject(menu);
			// create dropdown selection div and children
			var dropdownSelection = new Element('div',{'class':this.options.selectionDivClass});
			var dropdownBackground = new Element('div',{'class':this.options.dropdownBackgroundClass});
			var selectedText = selectedOption ? selectedOption.value : selectOptions[0].value;
			this.selection = new Element('span',{
				'class':this.options.selectionClass,
				'html':selectedText,
				'events': {
					'mousedown': this.selectionClicked.bind(this)
				}
			});
			this.input = new Element('input',{
				'type':'text',
				'value':this.selectedOption.optionElement.value,
				'name':select.getProperty('name'),
				'events': {
					'focus': this.focus.bind(this),
					'blur': Browser.Engine.trident ? $empty : this.blur.bind(this),
					'keydown': this.keydown.bind(this)
				}
			})
			dropdownBackground.inject(dropdownSelection);
			this.selectListOption(this.selectedOption.displayElement);
			this.selection.inject(dropdownSelection);
			this.input.inject(dropdownSelection);
			// clone the original select
			var selectClone = select.clone();
			// inject everything
			dropdownSelection.inject(this.dropdown);
			menu.inject(this.dropdown);
			this.dropdown.replaces(select);
			
			this.listHeight = this.list.getSize().y;
			this.listEffect = this.list.effect('height', {
				duration: this.options.duration,
				link: 'cancel',
				transition: this.options.transition
			}).set(0);
			
			if (this.options.initialValue && this.options.initialValue !== this.options.defaultValue) {
				this.fireEvent('onChange',{target:this});
			}
			
			document.addEvent('mousedown', this.clickOutside.bind(this));
		},
		selectionClicked: function(e) {
			this.fireEvent('click');
			new Event(e).stop();
			this.expand();
		},
		toggle: function() {
			$clear(this.collapseInterval);
			var height = this.dropdown.getElement('.options').getSize().y;
			var start = this.open ? height : 0;
			var end = this.open ? 0 : height;
			if (this.options.animate) {
				this.listEffect.start(start, end);
			} else {
				this.listEffect.set(end);
			}
			this.open = this.open ? false : true;
			if (this.open) {
				this.dropdown.addClass(this.options.activeClass).addClass(this.options.dropdownClass + '-' + this.options.activeClass);
				this.highlightOption(this.selectedOption.displayElement);
				this.dropdown.addEvent('mouseenter',this.events.mouseenter);
				this.dropdown.addEvent('mouseleave',this.events.mouseleave);
				this.input.focus();
			} else {
				this.dropdown.removeClass(this.options.activeClass).removeClass(this.options.dropdownClass + '-' + this.options.activeClass);
				this.dropdown.removeEvent('mouseenter',this.events.mouseenter);
				this.dropdown.removeEvent('mouseleave',this.events.mouseleave);
				this.removeHighlightOption(this.highlighted.displayElement);
				this.input.blur();
			}
		},
		listOptionClicked: function(e) {
			var evt = new Event(e).stop();
			var option = evt.target;
			this.selectListOption(option);
			this.collapse();
			if (this.options.hasLinks) {
				location.href = option.getProperty('href');
			}
		},
		selectListOption: function(option) {
			var option = $(option);
			var selectOption = option.retrieve('optionData');
			this.selection.empty();
			var firstChild = selectOption.displayElement.getFirst();
			if (firstChild) {
				this.selection.adopt(selectOption.displayElement.clone());
			} else {
				this.selection.set('html',selectOption.displayElement.get('html'));
			}
			this.selection.setStyle('display', 'block');
			var oldValue = this.value;
			this.value = selectOption.value;
			this.input.setProperty('value',this.value);
			this.selectedOption = selectOption;
			this.fireEvent('onSelect',selectOption);
			var changeEvent = {target:this};
			if ($defined(oldValue) && oldValue !== this.value) {
				this.fireEvent('onChange',changeEvent);
			}
		},
		collapse: function() {
			if (this.open) {
				this.toggle();
			}
			this.fireEvent('onCollapse');
		},
		expand: function() {
			if (!this.open) {
				this.toggle();
			}
			this.fireEvent('onExpand');
		},
		highlightOption: function(option) {
			if (this.highlighted) {
				this.removeHighlightOption(this.highlighted.displayElement);
			}
			option.get('tween',{duration:this.options.duration,transition:this.options.transition});
			option.tween(this.options.highlightStyle,this.options.highlightState);
			this.highlighted = option.retrieve('optionData');
			this.fireEvent('onHighlight');
		},
		removeHighlightOption: function(option) {
			option.get('tween',{duration:this.options.duration,transition:this.options.transition});
			option.tween(this.options.highlightStyle,this.options.defaultState);
			this.fireEvent('onRemoveHighlight');
		},
		mouseenter: function(e) {
			var evt = new Event(e);
			var option = evt.target;
			this.highlightOption(option);
		},
		mouseleave: function(e) {
			var evt = new Event(e);
			var option = evt.target;
			this.removeHighlightOption(option);
		},
		clickOutside: function(e) {
			this.collapse();
		},
		focus: function(e) {
			this.expand();
		},
		blur: function(e) {
			this.collapse();
		},
		keydown: function(e) {
			var evt = new Event(e).stop();
			$clear(this.clearTimer); // clear the typing timer
			var current = this.highlighted.displayElement;
			var previous, next;
			var code = evt.code;
			var key = evt.key;
			switch(code) {
				case 38: // up
				case 37: // left
					previous = current.getPrevious();
					if (!previous) {
						previous = current.getParent().getLast();
					}
					this.highlightOption(previous);
					this.removeHighlightOption(current);
					break;
				case 40: // down
				case 39: // right
					next = current.getNext();
					if (!next) {
						next = current.getParent().getFirst();
					}
					this.highlightOption(next);
					this.removeHighlightOption(current);
					break;
				case 13: // enter
				case 32: // space
					evt.target = current;
					this.listOptionClicked(evt);
					break;
				case 27: // esc
					this.toggle();
					break;
				default: // anything else
					if (code >= 48 && code <= 122 && (code <= 57 || (code >= 65 && code <= 90) || code >=97)) {
						// alphanumeric
					}
					break;
			}
		},
		destroy: function() {
			var listOptions = this.listOptions;
			var listOption;
			for (listOption in listOptions) {
				listOptions[listOption].destroy();
			}
			if (this.dropdown && this.dropdown.parentNode) {
				this.dropdown.destroy();
			}	
			this.dropdown = null;
			this.input = null;
			this.list = null;
			this.listEffect = null;
			this.selectedOption = null;
			this.selection = null;
		}
	};
}());
