
if (!Function.prototype.bind) Function.prototype.bind = function() {
	var args = []
	for (var i = 1; i < arguments.length; i++) args.push(arguments[i])
	var __method = this, object = arguments[0]
	return function() {
		return __method.apply(object, args.concat(arguments))
	}
}

if (!Function.prototype.bindAsEventListener) Function.prototype.bindAsEventListener = function(object) {
	var __method = this;
	return function(event) {
		return __method.call(object, event || window.event)
	}
}

function TypeAhead() {
	this.control = null
	this.transport = null
	this.message = ''
	this.timer = null
	this.delay = 700
	this.url = '/typeahead.php'
	this.current = -1
	this.items = []
	this._divIsShown = false
}

TypeAhead.prototype.attach = function(control) {
	control = document.getElementById(control)
	if (control) {
		if (1 < arguments.length && arguments[1] && 'object' == typeof(arguments[1])) {
			for (var name in arguments[1]) {
				if ('function' != typeof(arguments[1][name])) this[name] = arguments[1][name]
			}
		}
		this.control = control
		this.control.onkeydown = this.onkeydown.bindAsEventListener(this)
		this.control.onkeypress = this.onkeypress.bindAsEventListener(this)
		this.control.onkeyup = this.onkeyup.bindAsEventListener(this)
		this.oldOnBlur = this.control.onblur
		this.control.onblur = this.onblur.bindAsEventListener(this)
		this.control.autocomplete = 'off'
		this.control.form.autocomplete = 'off'
		this._class(this.control, 'taControl', true)
		this._initDiv()
	}
	return null != this.control
}
TypeAhead.prototype.getTransport = function() {
	var variants = [function() {return new ActiveXObject('Msxml2.XMLHTTP')}, function() {return new ActiveXObject('Microsoft.XMLHTTP')}, function() {return new XMLHttpRequest()}]
	this.transport = null
	for (var i = 0; i < variants.length; i++) {
		try {
			this.transport = variants[i]()
		}
		catch(e) {
			this.transport = null
		}
	}
	if (this.transport) {
		this.transport.open('POST', this.url, true)
		this.transport.onreadystatechange = this.onStateChange.bind(this)

		var requestHeaders = ['X-Requested-With', 'XMLHttpTypeAhead', 'Content-type', 'application/x-www-form-urlencoded']

		if (this.transport.overrideMimeType) requestHeaders.push('Connection', 'close');

		for (var i = 0; i < requestHeaders.length; i += 2) this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i + 1])
	}
	return this.transport
}
TypeAhead.prototype.onStateChange = function() {
	if (4 == this.transport.readyState) {
		this._class(this.control, 'taLoading', false)
		var data = []
		if (200 == this.transport.status) try {
			data = eval('(' + this.transport.responseText + ')')
		}
		catch (e) {
		}
		this.createContent(data)
		this.transport = null
		if (0 < this.items.length) this.showDiv(); else this.hideDiv()
	}
}
TypeAhead.prototype.post = function() {
	this.abort()
	if (this.getTransport()) {
		this.transport.send('m=' + encodeURIComponent(this.message = this.control.value))
		this._class(this.control, 'taLoading', true)
	}
}
TypeAhead.prototype.abort = function() {
	if (this.transport && 0 != this.transport.readyState) {
		this.transport.abort()
	}
	this.transport = null
	this._class(this.control, 'taLoading', false)
}
TypeAhead.prototype.onkeydown = function(event) {
	var key = event.which ? event.which : event.keyCode
	if (27 == key) {
		if (this.divIsShown()) {
			this.stopEvent(event)
			this.hideDiv()
			return false
		} else if (this.transport) {
			this.stopEvent(event)
			this.abort()
			return false
		}
	}
	return true
}
TypeAhead.prototype.onkeypress = function(event) {
	var key = event.which ? event.which : event.keyCode
	if (this.divIsShown() && 13 == key && (0 <= this.current && this.current < this.items.length)) {
		this.stopEvent(event)
		if (this.current < 15) this.control.value = this._clearText(this.items[this.current].text)
		this.hideDiv()
		this.control.form.submit()
		return false
	}
	return true
}
TypeAhead.prototype.onkeyup = function(event) {
	this.stopEvent(event)
	var key = event.which ? event.which : event.keyCode
	if (38 == key || 40 == key) {
		if (this.divIsShown()) {
			this.stop()
			if (0 < this.items.length) {
				this.current += (38 == key ? -1 : 1)
				if (0 > this.current) this.current = -1;
				else if (this.current >= this.items.length) this.current = this.items.length - 1
			} else this.current = -1
			this.selectItem()
		} else this.fire()
	} else if (this.control.value != this.message) {
		this.stop()
		this.timer = setTimeout(this.fire.bind(this), this.delay)
	}
	return false
}
TypeAhead.prototype.onblur = function(event) {
	this.stopEvent(event)
	setTimeout(this.hideDiv.bind(this), 300)
	try {
		this.oldOnBlur.apply(this.control)
	}
	catch (e) {}
	return false
}
TypeAhead.prototype.onclick = function(index) {
	if (0 <= index && index < this.items.length) {
		this.control.value = this._clearText(this.items[index].text)
		this.hideDiv()
		this.control.form.submit()
	} else if (-1 == index) {
		this.hideDiv()
		this.control.form.submit()
	}
}
TypeAhead.prototype.stop = function() {
	if (this.timer) {
		try { clearTimeout(this.timer) } catch(e) { }
		this.timer = null
	}
}
TypeAhead.prototype.fire = function() {
	if (this.control.value.match(/\S/)) {
		if (!this.div.firstChild || 'taProgress' != this.div.firstChild.className) {
			var div = document.createElement('div')
			div.innerHTML = '&nbsp;'
			div.className = 'taProgress'
			this.div.insertBefore(div, this.div.firstChild)
		}
		this.showDiv();
		this.post();
	} else this.hideDiv()
}
TypeAhead.prototype.createContent = function(data) {
	this.current = -1
	this.div.innerHTML = ''
	this.items = []
	for (var i = 0; i < data.length; i++) {
		if (null == data[i]) {
			if (0 < this.items.length) {
				this.items[this.items.length - 1].div.className += ' taSeparator'
			}
		} else {
			var div = document.createElement('div')
			div.innerHTML = data[i]
			div.className = 'taNormal'
			div.onclick = this.onclick.bind(this, i)
			this.div.appendChild(div)
			this.items.push({ text: data[i], div: div })
			if (data[i].toLowerCase() == this.message.toLowerCase()) this.current = i
		}
	}
	if (15 <= this.items.length && false) {
		var div = document.createElement('div')
		div.innerHTML = 'MORE...'
		div.className = 'taMore'
		div.onclick = this.onclick.bind(this, -1)
		this.div.appendChild(div)
		this.items.push({ text: div.innerHTML, div: div })
	}
	this.selectItem()
}
TypeAhead.prototype.stopEvent = function(event) {
	if (event) {
		if (event.preventDefault) {
			event.preventDefault()
			event.stopPropagation()
		} else {
			event.returnValue = false
			event.cancelBubble = true
		}
	}
}
TypeAhead.prototype.selectItem = function() {
	for (var i = 0; i < this.items.length; i++) {
		this._class(this.items[i].div, 'taSelection', i == this.current)
	}
}
TypeAhead.prototype._class = function(control, className, add) {
	if (control) {
		var classes = control.className.split(/\s+/)
		for (var i = 0; i < classes.length; i++) {
			if (className == classes[i]) if (add) return true; else classes[i] = ''
		}
		if (add) classes.push(className)
		control.className = classes.join(' ')
		return true
	}
	return false
}
TypeAhead.prototype.divIsShown = function() {
	return this._divIsShown
	return 'none' != this.div.style.display
}
TypeAhead.prototype.showDiv = function() {
	this._divIsShown = true
	var x = this.control.offsetLeft, y = this.control.offsetTop, el = this.control
	while ((el = el.offsetParent) != null) {
 		x += el.offsetLeft
		y += el.offsetTop
	}
	this.div.style.left = x + 'px'
	this.div.style.top = (y + this.control.offsetHeight + 1) + 'px'
	this.div.style.width = this.control.offsetWidth + 'px'
	this.div.style.display = 'block'
	if (this.iframe) {
		setTimeout(this._showIframe.bind(this), 300)
	}
}
TypeAhead.prototype._showIframe = function() {
	this.iframe.style.left = this.div.style.left
	this.iframe.style.top = this.div.style.top
	this.iframe.style.width = this.div.style.width
	this.iframe.style.height = this.div.clientHeight + 'px'
	if (this.divIsShown()) {
		this.iframe.style.display = 'block'
	}
}
TypeAhead.prototype.hideDiv = function() {
	this._divIsShown = false
	this.current = -1
	this.message = this.control.value
	this.stop()
	this.abort()
	if (this.iframe) this.iframe.style.display = 'none'
	this.div.style.display = 'none'
}
TypeAhead.prototype._initDiv = function() {
	this.div = document.createElement('div')
	this.div.style.position = 'absolute'
	//this.div.style.height = '200px'
	this.div.style.zIndex = 1000
	this.div.className = 'taArea'
	this.div.style.display = 'none'
	this.div.style.cursor = 'pointer'
	document.body.appendChild(this.div)
	if (/msie/i.test(navigator.userAgent)) {
		this.iframe = document.createElement('iframe')
		this.iframe.style.display = 'none'
		this.iframe.style.position = 'absolute'
		this.iframe.style.zIndex = this.div.style.zIndex - 1
		document.body.appendChild(this.iframe)
	}
	this._divIsShown = false
}
TypeAhead.prototype._clearText = function(source) {
	var i = source.lastIndexOf(' [')
	if (-1 < i) {
		source = source.substr(0, i)
	}
	return source
}
