// ///////////////////////
// constructor
// container: element to appendChild DDL tto
// id: id of DDL
// items: array of objects, objects have the following properties
// 		id: ID of item - could be DB id, must be unique to the DDL
// 		val: value that winds up in text box - NO HTML
// 		htmlVal: value displayed in the DDL, can ues HTML
// ///////////////////////
function DDL(container, id, items, inputElement)
{
	bindMethods(this);
	this.list = UL({'id': id});
	this.c = $(container);
	this.items = items;
	this.selected = -1;
	this.shown = 0;
	this.hasFocus = false;
	
	if (!this.items)
		return;

	if ($(inputElement))
		this.attachToInput(inputElement, null);
	
	this.loadItems();
	this.connectFocusTrack();

	
	this.show();
	this.addScroll();
}

// ///////////////////////
// data load and add DOM nodes
// ///////////////////////
DDL.prototype.loadItems = function() {
	var cls = 'ddlitem selected';
	for (var i = 0; i < this.items.length; i++)
	{
		//appendChildNodes(this.list, LI({'class': cls}, SPAN({'style': 'display: none;'}, this.items[i].id), this.items[i].val));
		appendChildNodes(this.list, LI({'class': cls}, SPAN({'style': 'display: none;'}, this.items[i].id), this.items[i].htmlVal));
		connect(this.list.childNodes[i], 'onclick', this, 'onClickItem');
		cls = 'ddlitem';
		this.selected = 0;
	}
	appendChildNodes(this.c, this.list)
}

// ///////////////////////
// detect if scroll bars are needed and add if necessary
// ///////////////////////
DDL.prototype.addScroll = function() {
	var ldims = getElementDimensions(this.list);
	var cdims = getElementDimensions(this.c);
	
	// if height of list is greater than container height
	// add scroll bar
	if (ldims.h > cdims.h)
	{
		setStyle(this.c, {'overflow': 'auto'});
		this.c.scrollTop = 0;
	}
		
}

// ///////////////////////
// move selected item up or down
// unit can be pos or neg
// ///////////////////////
DDL.prototype.moveSelected = function(unit) {
	// we are at top, don't move
	if (unit + this.selected < 0)
		return;
	// we are at bottom, don't move
	if (unit + this.selected >= this.items.length)
		return;
	// we never loaded items
	if (this.selected == -1)
		return;

	removeElementClass(this.list.childNodes[this.selected], 'selected');
	this.selectItem(this.selected + unit);
	this.adjustScroll();
}

// ///////////////////////
// handle an item being clicked on
// ///////////////////////
DDL.prototype.onClickItem = function(ev) {
	for (var i = 0; i < this.list.childNodes.length; i++)
	{
		// compare the SPAN elements innerHTML as these should be the ID
		if (this.list.childNodes[i].innerHTML == ev.src().innerHTML)
		{
			this.selectItem(i);
			this.hide();
			this.inputAttached.value = this.items[this.selected].val;
			signal(this, 'onSelectItem');
			break;
		}
	}
}

// ///////////////////////
// select the item
// ///////////////////////
DDL.prototype.selectItem = function(ind) {
	addElementClass(this.list.childNodes[ind], 'selected');
	this.selected = ind;
}

// ///////////////////////
// adjust scroll bar to current item visible
// ///////////////////////
DDL.prototype.adjustScroll = function () {
	// container dims
	var cdim = getElementDimensions(this.c);
	
	// selected element pos and dims
	var selpos = getElementPosition(this.list.childNodes[this.selected], this.c);
	var seldim = getElementPosition(this.list.childNodes[this.selected]);

	if ((selpos.y + this.c.scrollTop) > this.c.scrollTop && (selpos.y + this.c.scrollTop) < ((this.c.scrollTop + (cdim.h - 7)) - 12))
	{
		// item is visible
	}else if (selpos.y >= 0) {
		// need to move down
		this.c.scrollTop = this.c.scrollTop + (cdim.h - 7) - 12;
	}else if (selpos.y < 0) {
		// need to move up
		this.c.scrollTop = this.c.scrollTop - (cdim.h / 2) - 12;
	}
	
}

// ///////////////////////
// show the DDL
// ///////////////////////
DDL.prototype.show = function() {
	showElement(this.c);
	setStyle(this.c, {'visibility': 'visible'});
	this.shown = 1;
}

// ///////////////////////
// hide the DDL
// ///////////////////////
DDL.prototype.hide = function() {
	hideElement(this.c);
	setStyle(this.c, {'visibility': 'hidden'});
	this.shown = 0;
}

// ///////////////////////
// attach key strokes and positioning to an input box
// ///////////////////////
DDL.prototype.attachToInput = function(el, relativeTo) {
	this.inputAttached = $(el);

	var inputPos = getElementPosition(this.inputAttached, relativeTo);
	var inputDims = getElementDimensions(this.inputAttached);


	// get bottom of input element, should check padding and border as well
	inputPos.y = inputPos.y + inputDims.h;
	setElementPosition(this.c, inputPos);

	// wire up keypresses
	disconnectAll(this.inputAttached, 'onkeydown');
	connect(this.inputAttached, 'onkeydown', this, 'handleKeyPress');

	connect(this.inputAttached, 'onblur', this, 'handlelostfocus');
}

// ///////////////////////
// handle keys pressed from attached input
// ///////////////////////
DDL.prototype.handleKeyPress = function(ev) {
	// only operate on TAB UP DOWN
	var code = ev.key().code;
	if (code != 9 && code != 40 && code != 38)
		return;
	
	if (this.shown == 0)
		return;

	switch (code)
	{
		// tab
		case 9:
			this.hide();
			this.inputAttached.value = this.items[this.selected].val;
			signal(this, 'onSelectItem');
			break;
		// up
		case 38:
			this.moveSelected(-1);
			break;
		// down
		case 40:
			this.moveSelected(1);
			break;
		default:
			break;
	}
}

DDL.prototype.connectFocusTrack = function()
{
	if (!this.c)
		return;

	connect(this.c, 'onmouseover', this, 'gotfocus');
	connect(this.c, 'onmouseout', this, 'lostfocus');
}

DDL.prototype.gotfocus = function() 
{
	this.hasFocus = true;
}
DDL.prototype.lostfocus = function() 
{
	this.hasFocus = false; 
}

DDL.prototype.handlelostfocus = function()
{
	if (this.hasFocus)
		return;
	
	signal(this, 'onblur');
	this.hide();
}

