/*===============================================================================
	SelectInput.js
	John Larson
	5/13/08
	
	Component for augmenting standard text inputs (<input type="text">) with
	<select> behavior (i.e. a collection of options nada a down arrow button
	to reveal them).
	
	Example:
	<input type="text" id="timeInput" name="time" value="6:30pm">
	
	var timeOptionValues = ['7:00pm ', '7:30pm', '8:00pm'];
	var timeOptionLabels = ['7:00pm (30 min)', '7:30pm (1 hour)', '8:00pm (1 hour, 30 min)'];
	new SelectInput('timeInput', timeOptionValues, {labelSet: timeOptionLabels});
	
	would cause timeInput to behave like a select box with 3 options,
	yet still freely editable by direct typing.
===============================================================================*/

var SelectInput = new Class({
	
	
	options: {
		inheritWidth:					true,
		dropDownWidth:					'200px;',
		displaySelectArrow:				true,
		displayOptionsOnInputClick:		true,
		className:						'SelectInput',
		zIndex:							50,
		maxDropDownHeight:				0
	},

	initialize: function(inputElement, valueSet, options){
		this.setOptions(options)
		this.inputElement = $(inputElement);
		if(!this.inputElement)
			throw('Non-existent input passed to SelectInput constructor.');
		
		this.optionValueSet = valueSet;
		this.optionLabelSet = this.options.labelSet || valueSet;
		
		this.holder = new Element('span', {
			'styles': {position: 'relative'}
		});
		this.inputElement.replaceWith(this.holder);
		this.holder.adopt(this.inputElement);
		
		this.optionDiv = new Element('div', {
			'class': this.options.className + '-options',
			'styles': {position: 'absolute', zIndex: this.options.zIndex, display: 'none'}
			}).injectInside(document.body);
		
		this.optionList = new Element('ul', {
			'class': this.options.className + '-options'
			}).injectInside(this.optionDiv);
		this.optionListShim = new IframeShim({element: this.optionList});
		
		if(this.options.maxDropDownHeight)
			this.optionDiv.setStyles({'max-height': this.options.maxDropDownHeight,
				overflow: 'auto'});
		
		this.buildOptionList();
		
		if(this.options.displaySelectArrow) {
			this.arrowButton = new Element('span', {
				'class': this.options.className + '-arrowImage',
				'styles': {position: 'absolute', right: '0px', top: '-2px'}
			})
			
			
			this.holder.adopt(this.arrowButton);
			
			this.arrowButton.addEvent('click', function() {
				this.toggleDisplayOptionList();
			//	(function(){ this.inputElement.focus() }).delay(20, this);
			//	this.inputElement.focus();
			}.bindWithEvent(this));
			this.container = this.holder;
		}
		else
			this.container = this.inputElement;
		
		if(this.options.displayOptionsOnInputClick)
			this.inputElement.addEvent('click', function() {
				this.displayOptionList();
			}.bind(this));
		
		this.inputElement.addEvent(window.ie ? 'keydown' : 'keypress', 
									this.onCommand.bindWithEvent(this));
		this.inputElement.addEvent('keyup', 
									this.respondToInputTyping.bindWithEvent(this));
		this.inputElement.addEvent('mousedown', this.onCommand.bindWithEvent(this, [true]));
		this.inputElement.addEvent('blur', this.hideOptionList.bind(this));
	},
	
	setNewOptions: function(newValueSet, newLabelSet) {
		this.optionValueSet = newValueSet;
		this.optionLabelSet = newLabelSet || newValueSet;
		this.buildOptionList();
	},
	
	buildOptionList: function() {
		this.optionList.empty();
		this.selected = null;
		if (!this.optionValueSet || !this.optionValueSet.length) return;
		this.optionValueSet.each(function(optionValue, i){
			var optionItem = new Element('li').setHTML(this.optionLabelSet[i]);
			optionItem.inputValue = optionValue;
			this.addOptionEvents(optionItem).injectInside(this.optionList);
		}, this);
	},
	
	toggleDisplayOptionList: function(e) {
		if(e) e.stop();
		this.visible ? this.hideOptionList() : this.displayOptionList();
	},
	
	displayOptionList: function() {
		if (this.visible || !this.optionList.getFirst()) return;
		this.visible = true;
		var pos = this.inputElement.getCoordinates();
		this.optionDiv.setStyles({'left': pos.left, 'top': pos.bottom});
	//	this.optionDiv.setStyles({'top': 22});
		this.optionDiv.setStyle('width', (this.options.inheritWidth)?pos.width-2:this.options.dropDownWidth);
		this.optionDiv.setStyle('display', 'block');
		
	//	dbug.log('this.selected? ' + this.selected);
	//	if (!this.selected)
			this.highlightMatchingOption();
	//	else
	//		this.optionHoverAction(this.optionList.getFirst());
		
		this.ensureSelectedOptionIsVisible();
		this.fireEvent('onShow', [this.inputElement, this.optionList]);
		this.inputElement.focus();
		
	},
	
	hideOptionList: function() {
		if (!this.visible) return;
		this.visible = this.value = false;
		this.optionDiv.setStyle('display', 'none');
	},
	
	
	addOptionEvents: function(el) {
		return el.addEvents({
			'mouseover': this.optionHoverAction.bind(this, [el]),
			'mousedown': this.optionClickAction.bind(this, [el])
		});
	},

	optionHoverAction: function(el) {
		if (this.selected) this.selected.removeClass(this.options.className + '-selected');
		this.selected = el.addClass(this.options.className + '-selected');
	},

	optionClickAction: function(optionItem) {
		this.inputElement.value = optionItem.inputValue;
		this.hideOptionList();
		this.fireEvent('onSelect', [this.inputElement, optionItem.inputValue], 20);
		this.inputElement.fireEvent('change');
	},
	
	onCommand: function(e, mouse) {
	//	dbug.log('onCommand (' + e.key + ')');
		if (e.key) switch (e.key) {
			case 'enter':
				if (this.selected && this.visible) {
					this.optionClickAction(this.selected);
					e.stop();
				} return;
			case 'up': case 'down':
				if (this.optionValueSet.length == 0)
					break;
				else if (!this.visible)
					this.displayOptionList();
				else {
					this.optionHoverAction((e.key == 'up')
						? (this.selected ? this.selected.getPrevious() || this.optionList.getLast() : this.optionList.getLast())
						: (this.selected ? this.selected.getNext() || this.optionList.getFirst() : this.optionList.getFirst()));
					this.ensureSelectedOptionIsVisible();
				}
				e.stop(); return;
			case 'esc':
				this.hideOptionList(); 
				return;
			case 'tab':
				if(this.selected && this.visible)
					this.optionClickAction(this.selected);
				else
					this.hideOptionList(); 
				return;
		}
		this.value = false;
	},
	
	ensureSelectedOptionIsVisible: function() {
		
		if (!this.selected)
			return;
		
		var listSize = this.optionDiv.getSize();
		var listScrollSize = this.optionDiv.getScrollSize();
		var optionHeight = this.optionList.getChildren()[0].getSize().y + 5;
		
		var lowY = listScrollSize.y;
		var highY = lowY + listSize.y;
		var targetY = this.selected.getTop() - this.optionDiv.getTop() + 1;
		if(targetY < lowY)  // need to scroll further up
			this.optionDiv.scrollTo(0, Math.max(targetY - 2, 0));
		else if(targetY > (highY-optionHeight))  // need to scroll further down
			this.optionDiv.scrollTo(0, Math.min(targetY - listSize.y + optionHeight, listScrollSize.y));
		
	},
	
	respondToInputTyping: function(e) {
	//	dbug.log('respondToInputTyping (' + e.key + ')');
		if(!['enter', 'up', 'down', 'esc', 'tab'].contains(e.key)) {
			if(this.highlightMatchingOption())
				this.displayOptionList();
			else
				this.hideOptionList();
			return;
			
			
			/////
			var theValue = (this.inputElement.value || '');
			var matchingOptionSet = this.optionList.getChildren().filter(function(theOption) {
				return theOption.getText().match(new RegExp('^' + theValue.escapeRegExp(), 'i'));
			});
			if(matchingOptionSet.length) {
				this.displayOptionList();
				this.optionHoverAction(matchingOptionSet[0]);
			}
			else
				this.hideOptionList();
		}
	},
	
	highlightMatchingOption: function() {
		var theValue = (this.inputElement.value || '');
		var matchingOptionSet = this.optionList.getChildren().filter(function(theOption) {
			return theOption.getText().match(new RegExp('^' + theValue.escapeRegExp(), 'i'));
		});
		if(matchingOptionSet.length) {
			this.optionHoverAction(matchingOptionSet[0]);
			return true;
		}
		return false;
	},
	
	show: function() {
		if(this.holder)
			this.holder.style.display = '';
		else
			this.inputElement.style.display = '';
	},
	
	hide: function() {
		if(this.holder)
			this.holder.style.display = 'none';
		else
			this.inputElement.style.display = 'none';
	}
});
	
SelectInput.implement(new Options, new Events);