// Simple MochiKit-based ComboBox
// Eric Waldheim
// Cobbled together from bits and pieces of:
//   ComboBox By Jared Nuzzolillo
//   ComboBox Updated by Erik Arvidsson
//   DHTML Widgets by Barney Boisvert
//   Dojo ComboBox

if (typeof(My) == 'undefined')
{
	My = {};
}

My.ComboBox = function(id, options)
{
	this.__init__(id, options);
};

My.ComboBox.prototype =
{
	__class__: My.ComboBox,

	__init__: function(id, /* optional */config)
	{
		this.node = $(id);
		this.exposed_options = [];

		this.config = MochiKit.Base.update(
			{
				maxListLength:10,
				options:[],
				optionStringGetter:itemgetter(0)
			},
			config || {});

		this.textedit = INPUT({type:'text', 'class':"cbox-input"});
		var button = BUTTON({'class':'cbox-button'},
								  IMG({src:'combo_box_arrow.png',
										 alt:'show options'}));
		this.optionslist = DIV({'class':'cbox-list invisible'});
		appendChildNodes(this.node,
							  this.textedit, button, this.optionslist);

  		connect(button, "onfocus", function () { button.blur(); });
		connect(button, 'onclick', bind(this.toggle, this));
		connect(this.optionslist, "onselectstart", function(){return false});
		connect(this.optionslist, "onclick", bind(this.clickOption, this));
		connect(this.textedit, "onkeydown", bind(this.handleKey, this));
		connect(this.textedit, "onkeyup", bind(this.handleKeyUp, this));
		connect(document, "onmousedown", bind(this.mouseDown, this));
	},

	mouseDown: function(event)
	{
		var l = event.target();
		var found;
		while (l && !found)
		{
			found = (l == this.node);
			l = l.parentNode;
		}

		if (found)
		{
			this.textedit.focus();
			this.moveCaretToEnd();
		}
		else
		{
			makeInvisible(this.optionslist);
		}
	},

	_prevTexteditValue: "",
	handleKey: function(event)
	{
		log("keydown");
		this._prevTexteditValue = this.textedit.value;
		var key  = event.key();
		switch(key.code) {
		case 38: // up arrow
			this.highlightPrevOption();
			event.stopPropagation();
			break;
		case 40: // down arrow
			if (!isVisible(this.optionslist))
				this.toggle();
			else
				this.highlightNextOption();
			event.stopPropagation();
			break;
		case 27: // escape
			makeInvisible(this.optionslist);
			event.stopPropagation();
			break;
		case 9: // KEY_TAB
		case 13: //KEY_ENTER:				
			if (isVisible(this.optionslist) && this._highlighted_node)
			{
				this.selectOption();
				makeInvisible(this.optionslist);
			}
			event.stopPropagation();
			break;
		}
	},

	handleKeyUp: function(event)
	{
		if (this._prevTexteditValue != this.textedit.value)
		{
			this.update();
			this.build(this.exposed_options);
		}
	},

	selectOption: function()
	{
		var tgt = this._highlighted_node;
		this.textedit.value = tgt.getAttribute("Desc");
		this.value = tgt.getAttribute("Data");
	},

	moveCaretToEnd: function()
	{
		var t = this.textedit;
		if (t.createTextRange) {
			var range = t.createTextRange();
			range.collapse(false);
			range.select();
		} else if (t.setSelectionRange) {
			t.focus();
			var length = t.value.length;
			t.setSelectionRange(length, length);
		}
	},

	update: function()
	{
		var textedit_value = this.textedit.value.toLowerCase();
		var vlen = textedit_value.length
		var options = this.config.options;
		var get = this.config.optionStringGetter;
		this.exposed_options = [];
		for(i=0;i<options.length;i++)
		{
			var opt_substr = get(options[i]).toLowerCase().substring(0,vlen)
			if (textedit_value == opt_substr)
				this.exposed_options.push(options[i]);
		}
	},

	_connect_ids: [],
	build: function(options)
	{
		while (this._connect_ids.length) { disconnect(this._connect_ids.pop()); }
		var onmouseover = bind(this.itemMouseOver, this);
		var onmouseout = bind(this.itemMouseOut, this);
		var divs = [];
		for (var i = 0; i < options.length; ++i)
		{
			var data = options[i];
			var string = this.config.optionStringGetter(data);
			var div = DIV({'class':'cbox-item'}, string);
			this._connect_ids.push(connect(div, 'onmouseover', onmouseover),
										  connect(div, 'onmouseout', onmouseout));

			div.setAttribute('Desc', string);
			div.setAttribute('Data', data);
			divs.push(div);
		}
		replaceChildNodes(this.optionslist, divs);

		makeVisible(this.optionslist);
		var visibleCount = Math.min(options.length, this.config.maxListLength);
		if (!visibleCount)
		{
			makeInvisible(this.optionslist);
			this.blurHighlightedNode();
			return;
		}
		var item_dims = getElementDimensions(this.optionslist.firstChild);
		var textedit_pos = getElementPosition(this.textedit);
		var textedit_dims = getElementDimensions(this.textedit);
		var h = visibleCount ? (visibleCount * item_dims.h) : 0;
		setElementDimensions(this.optionslist, {w:textedit_dims.w, h:h});
		setElementPosition(this.optionslist, {x:textedit_pos.x,
														  y:textedit_pos.y + textedit_dims.h});

		this.highlightNode(divs[0]);
	},

	clickOption: function(event)
	{
		this.highlightNode(event.target());
		this.selectOption();
		makeInvisible(this.optionslist);
	},

	toggle: function()
	{
		if (!this.optionslist || !isVisible(this.optionslist))
		{
			this.update();
			this.build(this.config.options);
			this.textedit.focus();
		}
		else
		{
			makeInvisible(this.optionslist)
		}
	},

	highlightNode: function(node)
	{
		this.focusOptionNode(node);
		node.scrollIntoView(false);
	},

	focusOptionNode: function(node)
	{
		if (this._highlighted_node != node)
		{
			this.blurHighlightedNode();
			this._highlighted_node = node;
			addElementClass(this._highlighted_node, "cbox-hilite");
		}
	},

	blurHighlightedNode: function()
	{
		if (this._highlighted_node)
		{
			removeElementClass(this._highlighted_node, "cbox-hilite");
			this._highlighted_node = null;
		}
	},

	highlightNextOption: function()
	{
		if ((!this._highlighted_node) || !this._highlighted_node.parentNode)
		{
			this.focusOptionNode(this.optionsListNode.firstChild);
		}
		else if (this._highlighted_node.nextSibling)
		{
			this.focusOptionNode(this._highlighted_node.nextSibling);
		}
		this._highlighted_node.scrollIntoView(false);
	},

	highlightPrevOption: function()
	{
		if (this._highlighted_node && this._highlighted_node.previousSibling)
		{
			this.focusOptionNode(this._highlighted_node.previousSibling);
		}
		this._highlighted_node.scrollIntoView(false);
	},

	itemMouseOver: function(event)
	{
		this.focusOptionNode(event.target());
	},

	itemMouseOut: function(event)
	{
		this.blurHighlightedNode();
	},

	setOptions: function(d)
	{
		this.config.options = d;
	},

	setText: function(d)
	{
		this.textedit.value = d;
	}

};


document.write('<link rel="STYLESHEET" type="text/css" href="my_combobox.css">')