/**
 * Dynamic replacement of select boxes so that they can have desired style.
 * 
 * TODO: Need to add function to dynamicly add/remove elemnts from list.
 */
var SelectBox = new Class({
	
	Implements: [Events, Options],
	
	options: {
		listLength: 	5,
		customScrollBar:false, //true false
		scrollOptions:	{},
		listWidthAdj:	6,
		offset:			0,
		button:			{ 'isImage': false, 'open': '+', 'close': '-' },
		ignoreKeys:		['tab','enter'],
		listStyles:		false,
		listClass:		false,
		roundingStyle:	false,
		roundingObj:	false,
		zIndex:			1
	},
	
	/**
	 * Initialize the SelectBox class.
	 * 
	 * @constructor test
	 * 
	 * @example
	 * var box = new SelectBox('box1', {
	 * 		'button': { 'isImage': true, 'open': '/images/open.png', 'close': '/images/close.png' },
	 * 		onChange: function () { alert('item changed'); }
	 * });
	 * 
	 * @param {Object} element Element to swap out for styled box.
	 * @param {Object} options Options to pass in to box.
	 */
	initialize: function(element, options){
		if (Browser.Engine.webkit419) return; //safari 2 is a pile of junk and none of this works there.
		this.element = $(element);
		this.setOptions(options);
		this.element.getParent().setStyle('position','relative');
		this.createWrapper();
		this.setListStyles();
		this.createList();
		this.setButton();
		this.setText("");
		this.fillList();
		this.addEvent('reset', this.reset.bind(this));
		this.element.setStyle('visiblity', 'hidden').setStyle('display', 'none');
	},
	
	/**
	 * Builds the click button on the right
	 */
	setButton: function () {
		var click = function () {
			if (this.options.button.isImage)
				this.button.getFirst().setProperty('src', (this.button.getFirst().getProperty('src').replace(location.protocol + '//' + location.host, "") == this.options.button.close) ? this.options.button.open : this.options.button.close);
			else
				this.button.set('text', (this.button.get('text') == this.options.button.close)? this.options.button.open : this.options.button.close);
			this.list.slide.toggle();
			if (!this.hasFocus) this.wrapper.focus();
		}
		this.button = new Element('div', {
						'class': 'ddButton',
						'styles': {
							'position': 'absolute',
							'top': 	2,
							'right': 0,
							'width': 16,
							'height': this.element.offsetHeight,
							'background-color': '#000',
							'text-align': 'center',
							'line-height': this.element.offsetHeight,
							'font-weight': 'bold',
							'cursor': 'pointer'
						},
						'events': {
							'click': click.bind(this)
						}
					}).inject(this.wrapper, 'inside');
		if (this.options.button.isImage)
		{
			new Element('img', {
				'src': this.options.button.open
			}).inject(this.button, 'inside');
			this.button.setStyle('background-color', 'transparent');
		}
		else
			this.button.set('text','+');
	},
	
	setListStyles: function() {
		var selected = new Hash();
		this.element.getChildren().each(function(item){
			if (item.selected) 
			{
				selected.highlightBGColor = item.getStyle('background-color');
				selected.highlightColor = item.getStyle('color');
			}
			else if (selected.listBGColor == null)
			{
				selected.listBGColor = item.getStyle('background-color');
				selected.listColor = item.getStyle('color');
				selected.height = item.getCoordinates().height;
			}
		});
		this.listStyles = new Hash({ 
			'color': this.options.listStyles.color || '#000', 
			'backgroundColor': this.options.listStyles.backgroundColor || ((Browser.Engine.trident) ? '#fff' : 'transparent'), 
			'overColor': this.options.listStyles.overColor || ((selected.highlightBGColor == 'transparent') ? '#fff' : selected.highlightColor), 
			'overBackgroundColor': this.options.listStyles.overBackgroundColor || ((selected.highlightBGColor == 'transparent') ?  '#5e81bc' : selected.highlightBGColor), 
			'height': this.options.listStyles.height || ((selected.height == 0) ? 14 : selected.height),
			'background': this.options.listStyles.background,
			'baseBackground': this.options.listStyles.baseBackground ? this.options.listStyles.baseBackground : '#fff'
		});
	},

	createWrapper: function() {
		document.addEvent('mousedown', this.checkClose.bind(this));
		this.wrapper = new Element('div', {
			'class': 'wrapper',
			'styles': {
				'position': 'absolute',
				'top': 	this.element.offsetTop,
				'left': this.element.offsetLeft,
				'width': this.element.offsetWidth,
				'height': this.element.offsetHeight,
				'background-color': this.element.getStyle('background-color'),
				'z-index': 5
			},
			'tabindex': this.element.getProperty('tabindex'),
			'events': {
				'focus': this.focus.bind(this),
				'keydown': this.keypress.bind(this),
				'blur': this.blur.bind(this),
				'mouseenter': this.mouseenter.bind(this),
				'mouseleave': this.mouseleave.bind(this)
			}
		}).inject(this.element, 'before');
	}, 
	
	mouseenter: function() {
		//console.log('mouse enter')
		this.mouseOver = true;
	},
	
	mouseleave: function() {
		//console.log('mouse leave')
		this.mouseOver = false;
	},
	
	focus: function() {
		//console.log('focus');
		this.hasFocus = true;
	},
	
	blur: function() {
		//console.log('blur');
		var close = function() {	
			this.hasFocus = false;
			if(this.list.slide.open)
				this.buttonClick();
		}
		if (!this.mouseOver)
			this.closeID = close.delay(250, this);
	},
	
	checkClose: function(e) {
		//debugger;
		//var parent = e.target.getParent('div.holder') || e.target.getParent('div.wrapper');
		if (!this.mouseOver/* && !$chk(parent)*/)
			this.blur();
		return true;
	},
	
	keypress: function(e) {
		if (this.hasFocus || this.list.slide.open)
		{
			
			if (!this.options.ignoreKeys.contains(e.key))
				e.stop();
			if (e.key == 'up')
				this.getNext('up');
			else if (e.key == 'down')
				this.getNext('down');
			else
				this.findItem(e.key);
		}
	},
	
	buttonClick: function() {
		this.button.fireEvent('click');
	},
		
	/**
	 * Handles user pushing down key
	 */
	getNext: function(direction) {
		//console.log('direction ' + direction)
		if (direction == 'up' && this.list.items[0].hasClass('selected'))return;
		if (direction == 'down' && this.list.items[this.list.items.length - 1].hasClass('selected')) return;
			
		var update = -1
		var item = new Object(); 
		
		this.list.items.each(function(item, index){
			if (item.hasClass('selected')) update = (direction == 'up') ? index - 1: index + 1;
		});
		item.target = this.list.items[update];
		this.list.items[update].fireEvent('mouseover', item);
		this.list.items[update].fireEvent('click', item);
	},
	
	/**
	 * Handles user typing in characters to find correct item.
	 * 
	 * @param {Object} key	Next key to add to sequance.
	 */
	findItem: function(key) {
		$clear(this.clearID);
		if (this.chars == null) this.chars = '';
		this.chars += key;
		this.list.items.each(function(item){
			var regex = new RegExp('^' + this.chars, 'gi');
			if (item.get('text').match(regex))
			{
				var obj = new Object(); 
				obj.target = item;
				this.clearID = this.charClear.delay(500, this);
				item.fireEvent('mouseover', obj);
				item.fireEvent('click', obj);
				return;
			}
		}, this);
		this.clearID = this.charClear.delay(500);
	},
	
	/**
	 * clear characters typed in search
	 */
	charClear: function() {
		this.chars = '';
	},
	
	/**
	 * Creates the ul list
	 */
	createList: function() {
		this.list = new Element('ul', {
			'styles': {
				'list-style': 'none',
				'events': {
					'mouseenter': this.mouseenter.bind(this),
					'mouseleave': this.mouseleave.bind(this)	
				}
			}
		}).inject(new Element('div', {
			'class': 'holder',
			'styles': {
				'position': 'absolute',
				'top': 	this.element.offsetTop + this.element.offsetHeight + this.options.offset,
				'left': this.element.offsetLeft + (this.options.listWidthAdj / 2),
				'width': this.element.offsetWidth - this.options.listWidthAdj,
				'z-index': this.options.zIndex,
				'overflow': 'hidden'
			}
		}).inject(this.wrapper, 'after'), 'inside');
		(this.options.listClass) ? this.list.addClass(this.options.listClass) : this.list.setStyle('background', '#fff');
		
		//add scroll bar functionallity
		var slideObj = this.checkScrolls();
		
		//style wrapper ( used for adding rounding )
		slideObj = this.rounding(slideObj);
		
		var slideComplete = function() { 
			this.clicked = false; 
		};
		
		this.list.slide = new Fx.Slide(slideObj, { 
			duration: 200,
			onComplete: slideComplete.bind(this)
		}).hide();
		
	},
	
	checkScrolls: function() {
		var slideObj = this.list;
		if (this.element.getChildren().length > this.options.listLength)
		{
			if (this.options.customScrollBar )  
				slideObj = this.createCustomScroll(this.listStyles.height * this.options.listLength);
			else
			{
				this.list.setStyle('overflow', 'auto');
				this.list.setStyle('height', this.listStyles.height * this.options.listLength);
			}
		}
		return slideObj;
	},
	
	createCustomScroll: function(height) {
		var scrollWrapper = new Element('div', {
			'class': 'scrollHolder',
			'styles': {
				'height': height
			}
		}).inject(this.list, 'before').adopt(this.list);
		this.list.setStyle('width', this.element.offsetWidth - this.options.listWidthAdj);
		this.list.setStyle('height', this.element.getChildren().length * this.listStyles.height);
		var scroll = new ScrollPanel(scrollWrapper, this.options.scrollOptions);
		
		return scroll.panel;
	},
	
	rounding: function(slideObj) {
		if (this.options.roundingStyle || this.options.roundingObj)
		{
			slideObj = new Element('div', this.options.roundingStyle).wraps(slideObj);
			if (this.options.roundingObj) this.options.roundingObj.clone().inject(slideObj, 'inside');
		}
		return slideObj;
	},
	
	/**
	 * Fills the list with items from select box.
	 */
	fillList: function() {
		this.list.items = new Array();
		var groups = this.element.getChildren('optgroup');
		if (groups.length > 0){
			this.withoutGroups(this.element.getChildren('option'));
			groups.each(function(item){
				new Element('li', {
					'class': 'optGroup',
					'styles': {
						'font-weight': 'bold',
						'padding-left': 5
						}, 
					'events': {
						'mouseover': this.optGroupOver.bind(this),
						'mouseout': this.optGroupOut.bind(this)
						
					}}).set('text', item.get('label')).adopt(
					new Element('ul', {'styles': {'padding-left': 0}}).adopt(this.withGroups(item.getChildren()))
				).inject(this.list, 'inside')
			}, this);
		}
		else
			this.withoutGroups(this.element.getChildren());
	},
	
	withGroups: function(children){
		var list = new Array();
		children.each(function(item){
			if (item.selected) 	{
				this.setText(item.get('text'));
				this.value = item.value;
			}
			list.push(new Element('li', {
				'class': (item.selected) ? 'selected' : '',
				'id' : item.value,
				'styles': {
					'font-weight': 'normal',
					'padding-left': 5,
					'height': this.listStyles.height,
					'line-height': this.listStyles.height,
					'color': (item.selected) ? this.listStyles.overColor : this.listStyles.color, 
					'background-color': (item.selected) ? this.listStyles.overBackgroundColor : this.listStyles.backgroundColor,
					'cursor': 'pointer',
					'overflow': 'hidden'
				}, 
				'events': {
					'mouseover': this.highlightOver.bind(this),
					'mouseout': this.highlightOut.bind(this),
					'click': this.updateText.bindWithEvent(this, item)
				}
			}).set('text', item.get('text') ).inject(this.list, 'inside'));
			if(item.selected && this.listStyles.background) this.list.items[this.list.items.length - 1].setStyle('background', this.listStyles.background);
		}, this);
		this.list.items.merge(list);
		return list;
	},
	
	withoutGroups: function(children){
		children.each(function(item){
			if (item.selected) 	{
				this.setText(item.get('text'));
				this.value = item.value;
			}
			this.list.items.push(new Element('li', {
				'class': (item.selected) ? 'selected' : '',
				'id' : item.value,
				'styles': {
					'padding-left': 5,
					'height': this.listStyles.height,
					'line-height': this.listStyles.height,
					'color': (item.selected) ? this.listStyles.overColor : this.listStyles.color, 
					'background-color': (item.selected) ? this.listStyles.overBackgroundColor : this.listStyles.backgroundColor,
					'cursor': 'pointer',
					'overflow': 'hidden'
				}, 
				'events': {
					'mouseover': this.highlightOver.bind(this),
					'mouseout': this.highlightOut.bind(this),
					'click': this.updateText.bindWithEvent(this, item),
					'mouseenter': this.mouseenter.bind(this),
					'mouseleave': this.mouseleave.bind(this)
				}
			}).set('text', item.get('text') ).inject(this.list, 'inside'));
			if(item.selected && this.listStyles.background) this.list.items[this.list.items.length - 1].setStyle('background', this.listStyles.background);
		}, this);
	},
	
	updateText: function(e, item){
		//console.log('item clicked')
		$clear(this.closeID);
		this.list.items.each(function(item) {
			if (item.hasClass('selected'))
				item.removeClass('selected');
		});
		e.target.addClass('selected');
		this.clicked = true;
		this.selectedText.set('text', e.target.get('text')); 
		if (this.list.slide.open)
			this.buttonClick();
		//console.log(item.selected)
		item.selected = "selected";
		item.getParent().value = item.value;
		this.value = item.value;
		//console.log(item.selected)
		this.fireEvent('onChange');
	},
	
	/**
	 * Event when user puts mouse over item in list.
	 * 
	 * @param {Object} e	Event object
	 */
	highlightOver: function(e) {
		//console.log('highlightover')
		if (this.clicked) return;
		this.list.items.each(function(item) {
			if (this.listStyles.background && !Browser.Engine.trident)	item.setStyle('background', '');
			item.setStyle('background-color', this.listStyles.backgroundColor);		
			item.setStyle('color', this.listStyles.color);
		}, this);
		if (this.listStyles.background && !Browser.Engine.trident)
			e.target.setStyle('background', this.listStyles.background)
		e.target.setStyle('background-color', this.listStyles.overBackgroundColor);
		e.target.setStyle('color', this.listStyles.overColor);
	},
	/**
	 * Event when user rolls off list item.
	 * 
	 * @param {Object} e	Event object
	 */
	highlightOut: function(e) {
		//console.log('highlightout')
		if (this.clicked) return;
		this.list.items.each(function(item) {
			if (item.hasClass('selected'))
			{
				if (this.listStyles.background && !Browser.Engine.trident)	item.setStyle('background', this.listStyles.background);
				item.setStyle('background-color', this.listStyles.overBackgroundColor);
				item.setStyle('color', this.listStyles.overColor);
			}
		}, this);
		if (this.listStyles.background && !Browser.Engine.trident)	e.target.setStyle('background', '');
		e.target.setStyle('background-color', this.listStyles.backgroundColor);
		e.target.setStyle('color', this.listStyles.color);
	},
	optGroupOver: function(e) {
		///console.log('optGroupOver')
		//debugger;
		if (this.clicked || !e.target.hasClass('optGroup')){
			var test = 'test';
			return;
		}
		this.list.items.each(function(item) {
			if (this.listStyles.background && !Browser.Engine.trident)	item.setStyle('background', '');
			item.setStyle('background-color', this.listStyles.backgroundColor);		
			item.setStyle('color', this.listStyles.color);
		}, this);
	},
	optGroupOut: function(e){
		//console.log('optGroupOut')
		if (this.clicked || !e.target.hasClass('optGroup')) {
			var test = 'test';
			return;
		}
		this.list.items.each(function(item) {
			if (item.hasClass('selected'))
			{
				if (this.listStyles.background && !Browser.Engine.trident)	item.setStyle('background', this.listStyles.background);
				item.setStyle('background-color', this.listStyles.overBackgroundColor);
				item.setStyle('color', this.listStyles.overColor);
			}
		}, this);
	},
	/**
	 * Update the text displayed as selected.
	 * 
	 * @param {Object} text	The new text to show.
	 */
	setText: function(text) {
		if (this.selectedText == null)
			this.selectedText = new Element('span', {
				'styles': {
					'display': 'block',
					'padding-left': 5,
					'color': this.element.getStyle('color'),
					'line-height': this.element.offsetHeight
				},
				'events': {
					'click': this.buttonClick.bind(this)
				}
			}).inject(this.wrapper, 'inside');
		this.selectedText.set('text', text);
	},
	
	reset: function() {
		//console.log('reset')
		var e = new Object();
		e.target = this.list.items[0];
		this.list.items[0].fireEvent('click', e);
	},
	
	/**
	 * For dynamicly interacting with the dropdown box.
	 * 
	 * This would be used if the box selected value needs to change based on if something like a 
	 * different input element being changed.
	 * 
	 * If the SelectBoxes class was used to swap out all the select boxes on the page then they can 
	 * be accessed by window.boxes.{original object name}
	 * 
	 * @param {Object} value
	 */
	setValue: function(value) {
		this.list.items.each(function(item){
			if (item.get('id') == value) {
				var e = new Object();
				e.target = item;
				item.fireEvent('click', e);
			}
		});
	}

	/**
	 * TODO: still need to be able to add and remove items dynamicly.
	 */
});

/**
 * Short cut to swapping out all select boxes on a page.  This has all the same options as
 * the SelectBox class, as they are passed directly to the SelectBox class.
 * 
 * @example
 * var boxes = new SelectBoxes('select', {
 * 		'button': { 'isImage': true, 'open': '/images/open.png', 'close': '/images/close.png' },
 * 		onChange: function () { alert('item changed'); }
 * });
 */
var SelectBoxes = new Class({
	
	/**
	 * All the objects that are sent in are stored in window.boxes.{original object name}, where {original object name} is the 
	 * name property of the original select box.
	 * 
	 * @param {Object} elements
	 * This is the select boxes that are going to be converted.
	 * 
	 * @param {Object} options
	 * See the options for the SelectBox class
	 */
	initialize: function(elements, options){
		this.elements = $$(elements);
		window.boxes = new Hash();
		var boxNum = 0;
		this.elements.each(function(item, count) {
			var select = new SelectBox(item, options);
			window.boxes.set(item.get('id') || 'box' + boxNum++, select);
		});
	}
	
});


